Skip to content

Commit a6e621e

Browse files
committed
Dynamic goal reordering
1 parent 22150b7 commit a6e621e

File tree

9 files changed

+85
-16
lines changed

9 files changed

+85
-16
lines changed

cabal-install/Distribution/Client/Config.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ instance Semigroup SavedConfig where
240240
installMaxBackjumps = combine installMaxBackjumps,
241241
installMaxScore = combine installMaxScore,
242242
installFindBestSolution = combine installFindBestSolution,
243+
installDynamicGoalReordering = combine installDynamicGoalReordering,
243244
installReorderGoals = combine installReorderGoals,
244245
installIndependentGoals = combine installIndependentGoals,
245246
installShadowPkgs = combine installShadowPkgs,

cabal-install/Distribution/Client/Dependency.hs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module Distribution.Client.Dependency (
5555
setEnableBackjumping,
5656
setMaxScore,
5757
setFindBestSolution,
58+
setDynamicGoalReordering,
5859
addSourcePackages,
5960
hideInstalledPackagesSpecificByUnitId,
6061
hideInstalledPackagesSpecificBySourcePackageId,
@@ -161,7 +162,8 @@ data DepResolverParams = DepResolverParams {
161162
depResolverMaxBackjumps :: Maybe Int,
162163
depResolverEnableBackjumping :: EnableBackjumping,
163164
depResolverMaxScore :: Maybe InstallPlanScore,
164-
depResolverFindBestSolution :: FindBestSolution
165+
depResolverFindBestSolution :: FindBestSolution,
166+
depResolverDynamicGoalReordering :: DynamicGoalReordering
165167
}
166168

167169
showDepResolverParams :: DepResolverParams -> String
@@ -235,7 +237,8 @@ basicDepResolverParams installedPkgIndex sourcePkgIndex =
235237
depResolverMaxBackjumps = Nothing,
236238
depResolverEnableBackjumping = EnableBackjumping True,
237239
depResolverMaxScore = Nothing,
238-
depResolverFindBestSolution = FindBestSolution False
240+
depResolverFindBestSolution = FindBestSolution False,
241+
depResolverDynamicGoalReordering = DynamicGoalReordering False
239242
}
240243

241244
addTargets :: [PackageName]
@@ -322,6 +325,12 @@ setFindBestSolution findBest params =
322325
depResolverFindBestSolution = findBest
323326
}
324327

328+
setDynamicGoalReordering :: DynamicGoalReordering -> DepResolverParams -> DepResolverParams
329+
setDynamicGoalReordering dynGoals params =
330+
params {
331+
depResolverDynamicGoalReordering = dynGoals
332+
}
333+
325334
-- | Some packages are specific to a given compiler version and should never be
326335
-- upgraded.
327336
dontUpgradeNonUpgradeablePackages :: DepResolverParams -> DepResolverParams
@@ -605,7 +614,8 @@ resolveDependencies platform comp pkgConfigDB solver params =
605614
Step (showDepResolverParams finalparams)
606615
$ fmap (uncurry $ validateSolverResult platform comp indGoals)
607616
$ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls
608-
shadowing strFlags maxBkjumps enableBj mScore findBest)
617+
shadowing strFlags maxBkjumps enableBj mScore
618+
findBest dynGoals)
609619
platform comp installedPkgIndex sourcePkgIndex
610620
pkgConfigDB preferences constraints targets
611621
where
@@ -623,7 +633,8 @@ resolveDependencies platform comp pkgConfigDB solver params =
623633
maxBkjumps
624634
enableBj
625635
mScore
626-
findBest) = dontUpgradeNonUpgradeablePackages
636+
findBest
637+
dynGoals) = dontUpgradeNonUpgradeablePackages
627638
-- TODO:
628639
-- The modular solver can properly deal with broken
629640
-- packages and won't select them. So the
@@ -860,7 +871,7 @@ resolveWithoutDependencies (DepResolverParams targets constraints
860871
prefs defpref installedPkgIndex sourcePkgIndex
861872
_reorderGoals _indGoals _avoidReinstalls
862873
_shadowing _strFlags _maxBjumps _enableBj _maxScore
863-
_findBest) =
874+
_findBest _dynGoals) =
864875
collectEithers (map selectPackage targets)
865876
where
866877
selectPackage :: PackageName -> Either ResolveNoDepsError UnresolvedSourcePackage

cabal-install/Distribution/Client/Install.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,8 @@ planPackages comp platform mSandboxPkgInfo solver
371371

372372
. setFindBestSolution findBest
373373

374+
. setDynamicGoalReordering dynGoals
375+
374376
. setIndependentGoals independentGoals
375377

376378
. setReorderGoals reorderGoals
@@ -434,6 +436,7 @@ planPackages comp platform mSandboxPkgInfo solver
434436
maxBackjumps = fromFlag (installMaxBackjumps installFlags)
435437
maxScore = flagToMaybe (installMaxScore installFlags)
436438
findBest = fromFlag (installFindBestSolution installFlags)
439+
dynGoals = fromFlag (installDynamicGoalReordering installFlags)
437440
upgradeDeps = fromFlag (installUpgradeDeps installFlags)
438441
onlyDeps = fromFlag (installOnlyDeps installFlags)
439442
allowNewer = fromMaybe AllowNewerNone (configAllowNewer configFlags)

cabal-install/Distribution/Client/ProjectConfig/Legacy.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ convertLegacyAllPackageFlags globalFlags configFlags
302302
--installOverrideReinstall = projectConfigOverrideReinstall,
303303
installMaxBackjumps = projectConfigMaxBackjumps,
304304
--installMaxScore = projectConfigMaxScore,
305+
--installDynamicGoalReordering = projectConfigDynamicGoalReordering,
305306
--installUpgradeDeps = projectConfigUpgradeDeps,
306307
installReorderGoals = projectConfigReorderGoals,
307308
--installIndependentGoals = projectConfigIndependentGoals,
@@ -494,6 +495,7 @@ convertToLegacySharedConfig
494495
installOverrideReinstall = mempty, --projectConfigOverrideReinstall,
495496
installMaxBackjumps = projectConfigMaxBackjumps,
496497
installMaxScore = mempty, --projectConfigMaxScore,
498+
installDynamicGoalReordering = mempty, --projectConfigDynamicGoalReordering
497499
installUpgradeDeps = mempty, --projectConfigUpgradeDeps,
498500
installFindBestSolution = mempty, --projectConfigFindBestSolution,
499501
installReorderGoals = projectConfigReorderGoals,

cabal-install/Distribution/Client/Setup.hs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ data FetchFlags = FetchFlags {
606606
fetchMaxBackjumps :: Flag Int,
607607
fetchMaxScore :: Flag InstallPlanScore,
608608
fetchFindBestSolution :: Flag FindBestSolution,
609+
fetchDynamicGoalReordering :: Flag DynamicGoalReordering,
609610
fetchReorderGoals :: Flag ReorderGoals,
610611
fetchIndependentGoals :: Flag IndependentGoals,
611612
fetchShadowPkgs :: Flag ShadowPkgs,
@@ -622,6 +623,7 @@ defaultFetchFlags = FetchFlags {
622623
fetchMaxBackjumps = Flag defaultMaxBackjumps,
623624
fetchMaxScore = mempty,
624625
fetchFindBestSolution = Flag (FindBestSolution False),
626+
fetchDynamicGoalReordering = Flag (DynamicGoalReordering False),
625627
fetchReorderGoals = Flag (ReorderGoals False),
626628
fetchIndependentGoals = Flag (IndependentGoals False),
627629
fetchShadowPkgs = Flag (ShadowPkgs False),
@@ -670,6 +672,7 @@ fetchCommand = CommandUI {
670672
fetchMaxBackjumps (\v flags -> flags { fetchMaxBackjumps = v })
671673
fetchMaxScore (\v flags -> flags { fetchMaxScore = v })
672674
fetchFindBestSolution (\v flags -> flags { fetchFindBestSolution = v })
675+
fetchDynamicGoalReordering (\v flags -> flags { fetchDynamicGoalReordering = v })
673676
fetchReorderGoals (\v flags -> flags { fetchReorderGoals = v })
674677
fetchIndependentGoals (\v flags -> flags { fetchIndependentGoals = v })
675678
fetchShadowPkgs (\v flags -> flags { fetchShadowPkgs = v })
@@ -689,6 +692,7 @@ data FreezeFlags = FreezeFlags {
689692
freezeMaxBackjumps :: Flag Int,
690693
freezeMaxScore :: Flag InstallPlanScore,
691694
freezeFindBestSolution :: Flag FindBestSolution,
695+
freezeDynamicGoalReordering :: Flag DynamicGoalReordering,
692696
freezeReorderGoals :: Flag ReorderGoals,
693697
freezeIndependentGoals :: Flag IndependentGoals,
694698
freezeShadowPkgs :: Flag ShadowPkgs,
@@ -705,6 +709,7 @@ defaultFreezeFlags = FreezeFlags {
705709
freezeMaxBackjumps = Flag defaultMaxBackjumps,
706710
freezeMaxScore = mempty,
707711
freezeFindBestSolution = Flag (FindBestSolution False),
712+
freezeDynamicGoalReordering = Flag (DynamicGoalReordering False),
708713
freezeReorderGoals = Flag (ReorderGoals False),
709714
freezeIndependentGoals = Flag (IndependentGoals False),
710715
freezeShadowPkgs = Flag (ShadowPkgs False),
@@ -752,6 +757,7 @@ freezeCommand = CommandUI {
752757
freezeMaxBackjumps (\v flags -> flags { freezeMaxBackjumps = v })
753758
freezeMaxScore (\v flags -> flags { freezeMaxScore = v })
754759
freezeFindBestSolution (\v flags -> flags { freezeFindBestSolution = v })
760+
freezeDynamicGoalReordering (\v flags -> flags { freezeDynamicGoalReordering = v })
755761
freezeReorderGoals (\v flags -> flags { freezeReorderGoals = v })
756762
freezeIndependentGoals (\v flags -> flags { freezeIndependentGoals = v })
757763
freezeShadowPkgs (\v flags -> flags { freezeShadowPkgs = v })
@@ -1156,6 +1162,7 @@ data InstallFlags = InstallFlags {
11561162
installMaxBackjumps :: Flag Int,
11571163
installMaxScore :: Flag InstallPlanScore,
11581164
installFindBestSolution :: Flag FindBestSolution,
1165+
installDynamicGoalReordering :: Flag DynamicGoalReordering,
11591166
installReorderGoals :: Flag ReorderGoals,
11601167
installIndependentGoals :: Flag IndependentGoals,
11611168
installShadowPkgs :: Flag ShadowPkgs,
@@ -1189,6 +1196,7 @@ defaultInstallFlags = InstallFlags {
11891196
installMaxBackjumps = Flag defaultMaxBackjumps,
11901197
installMaxScore = mempty,
11911198
installFindBestSolution= Flag (FindBestSolution False),
1199+
installDynamicGoalReordering = Flag (DynamicGoalReordering False),
11921200
installReorderGoals = Flag (ReorderGoals False),
11931201
installIndependentGoals= Flag (IndependentGoals False),
11941202
installShadowPkgs = Flag (ShadowPkgs False),
@@ -1335,6 +1343,7 @@ installOptions showOrParseArgs =
13351343
installMaxBackjumps (\v flags -> flags { installMaxBackjumps = v })
13361344
installMaxScore (\v flags -> flags { installMaxScore = v })
13371345
installFindBestSolution (\v flags -> flags { installFindBestSolution = v })
1346+
installDynamicGoalReordering (\v flags -> flags { installDynamicGoalReordering = v })
13381347
installReorderGoals (\v flags -> flags { installReorderGoals = v })
13391348
installIndependentGoals (\v flags -> flags { installIndependentGoals = v })
13401349
installShadowPkgs (\v flags -> flags { installShadowPkgs = v })
@@ -2089,12 +2098,13 @@ optionSolverFlags :: ShowOrParseArgs
20892098
-> (flags -> Flag Int ) -> (Flag Int -> flags -> flags)
20902099
-> (flags -> Flag InstallPlanScore) -> (Flag InstallPlanScore -> flags -> flags)
20912100
-> (flags -> Flag FindBestSolution) -> (Flag FindBestSolution -> flags -> flags)
2101+
-> (flags -> Flag DynamicGoalReordering) -> (Flag DynamicGoalReordering -> flags -> flags)
20922102
-> (flags -> Flag ReorderGoals) -> (Flag ReorderGoals -> flags -> flags)
20932103
-> (flags -> Flag IndependentGoals) -> (Flag IndependentGoals -> flags -> flags)
20942104
-> (flags -> Flag ShadowPkgs) -> (Flag ShadowPkgs -> flags -> flags)
20952105
-> (flags -> Flag StrongFlags) -> (Flag StrongFlags -> flags -> flags)
20962106
-> [OptionField flags]
2097-
optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getrg setrg _getig _setig getsip setsip getstrfl setstrfl =
2107+
optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getdyn setdyn getrg setrg _getig _setig getsip setsip getstrfl setstrfl =
20982108
[ option [] ["max-backjumps"]
20992109
("Maximum number of backjumps allowed while solving (default: " ++ show defaultMaxBackjumps ++ "). Use a negative number to enable unlimited backtracking. Use 0 to disable backtracking completely.")
21002110
getmbj setmbj
@@ -2111,6 +2121,11 @@ optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getrg se
21112121
(fmap asBool . getfb)
21122122
(setfb . fmap FindBestSolution)
21132123
(yesNoOpt showOrParseArgs)
2124+
, option [] ["dynamic-goal-reordering"]
2125+
"Prefer goals that were previously involved in a conflict."
2126+
(fmap asBool . getdyn)
2127+
(setdyn . fmap DynamicGoalReordering)
2128+
(yesNoOpt showOrParseArgs)
21142129
, option [] ["reorder-goals"]
21152130
"Try to reorder goals according to certain heuristics. Slows things down on average, but may make backtracking faster for some packages."
21162131
(fmap asBool . getrg)

cabal-install/Distribution/Solver/Modular/Explore.hs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Distribution.Solver.Modular.Explore
66
import Control.Monad.State.Lazy
77
import Data.Foldable as F
88
import Data.Map as M
9+
import Data.Ord
910

1011
import Distribution.Solver.Modular.Assignment
1112
import Distribution.Solver.Modular.Dependency
@@ -108,6 +109,11 @@ data ExploreState = ExploreState {
108109
-- specified with --max-score and the score of the best install plan. If
109110
-- neither of those two values exists, 'esMaxScore' is equal to 'Nothing'.
110111
, esMaxScore :: Maybe InstallPlanScore
112+
113+
-- | Variables from the last conflict set. Since they are likely to lead
114+
-- to the same failure in the next subtree, the solver should try them
115+
-- first.
116+
, esPreferredGoals :: ConflictSet QPN
111117
}
112118

113119
-- | A tree traversal that simultaneously prunes nodes based on score,
@@ -121,21 +127,26 @@ data ExploreState = ExploreState {
121127
explore :: EnableBackjumping
122128
-> Maybe InstallPlanScore
123129
-> FindBestSolution
130+
-> DynamicGoalReordering
124131
-> Tree (Assignment, ScoringState) (QGoalReason, ScoringState)
125132
-> ConflictSetLog Plan
126-
explore enableBj maxScore findBest =
133+
explore enableBj maxScore findBest dynGoals =
127134
(`evalState` initES) . cata go
128135
where
129136
go :: TreeF (Assignment, ScoringState)
130137
(QGoalReason, ScoringState)
131138
(Explore (ConflictSetLog Plan))
132139
-> Explore (ConflictSetLog Plan)
133-
go (FailF c fr) = fail' c fr
140+
go (FailF c fr) = do
141+
es <- get
142+
put es { esPreferredGoals = c }
143+
fail' c fr
134144
go (DoneF rdm (a, ss)) =
135145
maybePrune ss $ do
136146
put ExploreState {
137147
esBestPlan = Just (a, rdm, ssTotalScore ss)
138148
, esMaxScore = Just $ ssTotalScore ss
149+
, esPreferredGoals = ssConflictSet ss
139150
}
140151
if asBool findBest
141152
then fail' (ssConflictSet ss) $
@@ -150,8 +161,12 @@ explore enableBj maxScore findBest =
150161
go (SChoiceF qsn (gr, ss) _ ts) =
151162
maybePrune ss $ backjump enableBj (S qsn) (avoidSet (S qsn) gr) $
152163
W.mapWithKey (\ k r -> tryWith (TryS qsn k) `fmap` r) ts
153-
go (GoalChoiceF ts) =
154-
P.casePSQ ts
164+
go (GoalChoiceF ts) = do
165+
preferred <- gets esPreferredGoals
166+
let ts' = if asBool dynGoals
167+
then P.sortByKeys (sortGoals preferred) ts
168+
else ts
169+
P.casePSQ ts'
155170
(fail' CS.empty EmptyGoalChoice) -- empty goal choice is an internal error
156171
(\ k v _xs -> continueWith (Next (close k)) `fmap` v) -- commit to the first goal choice
157172

@@ -169,8 +184,16 @@ explore enableBj maxScore findBest =
169184
initES = ExploreState {
170185
esBestPlan = Nothing
171186
, esMaxScore = maxScore
187+
, esPreferredGoals = CS.empty
172188
}
173189

190+
sortGoals :: ConflictSet QPN -> OpenGoal () -> OpenGoal () -> Ordering
191+
sortGoals vars = flip $ comparing $ (`CS.member` vars) . goalToVar
192+
where
193+
goalToVar :: OpenGoal () -> Var QPN
194+
goalToVar g = let Goal var _ = close g
195+
in var
196+
174197
-- | Build a conflict set corresponding to the (virtual) option not to
175198
-- choose a solution for a goal at all.
176199
--
@@ -206,10 +229,11 @@ fail' c fr = (\plan -> failWith (Failure c fr plan) c) `fmap` gets esBestPlan
206229
backjumpAndExplore :: EnableBackjumping
207230
-> Maybe InstallPlanScore
208231
-> FindBestSolution
232+
-> DynamicGoalReordering
209233
-> Tree ScoringState (QGoalReason, ScoringState)
210234
-> Log Message Plan
211-
backjumpAndExplore enableBj maxScore findBest =
212-
toLog . explore enableBj maxScore findBest . assign
235+
backjumpAndExplore enableBj maxScore findBest dynGoals =
236+
toLog . explore enableBj maxScore findBest dynGoals . assign
213237
where
214238
toLog :: P.Progress step fail done -> Log step done
215239
toLog = P.foldProgress P.Step (const (P.Fail ())) P.Done

cabal-install/Distribution/Solver/Modular/Preference.hs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import Prelude hiding (sequence)
2929
import Control.Monad.Reader hiding (sequence)
3030
import Data.Map (Map)
3131
import Data.Maybe
32+
import Data.Ord (comparing)
3233

3334
import Distribution.Client.Dependency.Types
3435
( PackageConstraint(..), LabeledPackageConstraint(..), ConstraintSource(..)
@@ -413,7 +414,12 @@ deferWeakFlagChoices = trav go
413414
preferEasyGoalChoices :: Tree a b -> Tree a b
414415
preferEasyGoalChoices = trav go
415416
where
416-
go (GoalChoiceF xs) = GoalChoiceF (P.dminimumBy dchoices xs)
417+
-- TODO: --dynamic-goal-reordering requires this function to leave all goal
418+
-- choices. If we decide to keep the feature, this function should be
419+
-- refactored to calculate the first element more efficiently, as before:
420+
-- go (GoalChoiceF xs) = GoalChoiceF (P.dminimumBy dchoices xs)
421+
422+
go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing dchoices) xs)
417423
-- (a different implementation that seems slower):
418424
-- GoalChoiceF (P.firstOnly (P.preferOrElse zeroOrOneChoices (P.minimumBy choices) xs))
419425
go x = x

cabal-install/Distribution/Solver/Modular/Solver.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ data SolverConfig = SolverConfig {
3838
maxBackjumps :: Maybe Int,
3939
enableBackjumping :: EnableBackjumping,
4040
maxInstallPlanScore :: Maybe InstallPlanScore,
41-
findBestSolution :: FindBestSolution
41+
findBestSolution :: FindBestSolution,
42+
dynamicGoalReordering :: DynamicGoalReordering
4243
}
4344

4445
-- | Run all solver phases.
@@ -87,10 +88,11 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
8788
explorePhase = backjumpAndExplore (enableBackjumping sc)
8889
(maxInstallPlanScore sc)
8990
(findBestSolution sc)
91+
(dynamicGoalReordering sc)
9092
scorePhase = P.scoreTree -- must come after all preferences
9193
heuristicsPhase = (if asBool (preferEasyGoalChoices sc)
92-
then P.preferEasyGoalChoices -- also leaves just one choice
93-
else P.firstGoal) . -- after doing goal-choice heuristics, commit to the first choice (saves space)
94+
then P.preferEasyGoalChoices
95+
else id) .
9496
P.deferWeakFlagChoices .
9597
P.deferSetupChoices .
9698
P.preferBaseGoalChoice .

cabal-install/Distribution/Solver/Types/Settings.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
33
module Distribution.Solver.Types.Settings
44
( FindBestSolution(..)
5+
, DynamicGoalReordering(..)
56
, ReorderGoals(..)
67
, IndependentGoals(..)
78
, AvoidReinstalls(..)
@@ -20,6 +21,9 @@ import GHC.Generics (Generic)
2021
newtype FindBestSolution = FindBestSolution Bool
2122
deriving (BooleanFlag, Eq, Generic, Show)
2223

24+
newtype DynamicGoalReordering = DynamicGoalReordering Bool
25+
deriving (BooleanFlag, Eq, Generic, Show)
26+
2327
newtype ReorderGoals = ReorderGoals Bool
2428
deriving (BooleanFlag, Eq, Generic, Show)
2529

@@ -39,6 +43,7 @@ newtype EnableBackjumping = EnableBackjumping Bool
3943
deriving (BooleanFlag, Eq, Generic, Show)
4044

4145
instance Binary FindBestSolution
46+
instance Binary DynamicGoalReordering
4247
instance Binary ReorderGoals
4348
instance Binary IndependentGoals
4449
instance Binary AvoidReinstalls

0 commit comments

Comments
 (0)