Skip to content

Commit 0af7823

Browse files
committed
Dynamic goal reordering
1 parent a4e5e14 commit 0af7823

File tree

9 files changed

+84
-16
lines changed

9 files changed

+84
-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
@@ -56,6 +56,7 @@ module Distribution.Client.Dependency (
5656
setEnableBackjumping,
5757
setMaxScore,
5858
setFindBestSolution,
59+
setDynamicGoalReordering,
5960
addSourcePackages,
6061
hideInstalledPackagesSpecificByUnitId,
6162
hideInstalledPackagesSpecificBySourcePackageId,
@@ -165,7 +166,8 @@ data DepResolverParams = DepResolverParams {
165166
depResolverMaxBackjumps :: Maybe Int,
166167
depResolverEnableBackjumping :: EnableBackjumping,
167168
depResolverMaxScore :: Maybe InstallPlanScore,
168-
depResolverFindBestSolution :: FindBestSolution
169+
depResolverFindBestSolution :: FindBestSolution,
170+
depResolverDynamicGoalReordering :: DynamicGoalReordering
169171
}
170172

171173
showDepResolverParams :: DepResolverParams -> String
@@ -239,7 +241,8 @@ basicDepResolverParams installedPkgIndex sourcePkgIndex =
239241
depResolverMaxBackjumps = Nothing,
240242
depResolverEnableBackjumping = EnableBackjumping True,
241243
depResolverMaxScore = Nothing,
242-
depResolverFindBestSolution = FindBestSolution False
244+
depResolverFindBestSolution = FindBestSolution False,
245+
depResolverDynamicGoalReordering = DynamicGoalReordering False
243246
}
244247

245248
addTargets :: [PackageName]
@@ -326,6 +329,12 @@ setFindBestSolution findBest params =
326329
depResolverFindBestSolution = findBest
327330
}
328331

332+
setDynamicGoalReordering :: DynamicGoalReordering -> DepResolverParams -> DepResolverParams
333+
setDynamicGoalReordering dynGoals params =
334+
params {
335+
depResolverDynamicGoalReordering = dynGoals
336+
}
337+
329338
-- | Some packages are specific to a given compiler version and should never be
330339
-- upgraded.
331340
dontUpgradeNonUpgradeablePackages :: DepResolverParams -> DepResolverParams
@@ -626,7 +635,8 @@ resolveDependencies platform comp pkgConfigDB solver params =
626635
Step (showDepResolverParams finalparams)
627636
$ fmap (uncurry $ validateSolverResult platform comp indGoals)
628637
$ runSolver solver (SolverConfig reorderGoals indGoals noReinstalls
629-
shadowing strFlags maxBkjumps enableBj mScore findBest)
638+
shadowing strFlags maxBkjumps enableBj mScore
639+
findBest dynGoals)
630640
platform comp installedPkgIndex sourcePkgIndex
631641
pkgConfigDB preferences constraints targets
632642
where
@@ -644,7 +654,8 @@ resolveDependencies platform comp pkgConfigDB solver params =
644654
maxBkjumps
645655
enableBj
646656
mScore
647-
findBest) = dontUpgradeNonUpgradeablePackages
657+
findBest
658+
dynGoals) = dontUpgradeNonUpgradeablePackages
648659
-- TODO:
649660
-- The modular solver can properly deal with broken
650661
-- packages and won't select them. So the
@@ -881,7 +892,7 @@ resolveWithoutDependencies (DepResolverParams targets constraints
881892
prefs defpref installedPkgIndex sourcePkgIndex
882893
_reorderGoals _indGoals _avoidReinstalls
883894
_shadowing _strFlags _maxBjumps _enableBj _maxScore
884-
_findBest) =
895+
_findBest _dynGoals) =
885896
collectEithers (map selectPackage targets)
886897
where
887898
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
@@ -380,6 +380,8 @@ planPackages comp platform mSandboxPkgInfo solver
380380

381381
. setFindBestSolution findBest
382382

383+
. setDynamicGoalReordering dynGoals
384+
383385
. setIndependentGoals independentGoals
384386

385387
. setReorderGoals reorderGoals
@@ -443,6 +445,7 @@ planPackages comp platform mSandboxPkgInfo solver
443445
maxBackjumps = fromFlag (installMaxBackjumps installFlags)
444446
maxScore = flagToMaybe (installMaxScore installFlags)
445447
findBest = fromFlag (installFindBestSolution installFlags)
448+
dynGoals = fromFlag (installDynamicGoalReordering installFlags)
446449
upgradeDeps = fromFlag (installUpgradeDeps installFlags)
447450
onlyDeps = fromFlag (installOnlyDeps installFlags)
448451
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
@@ -607,6 +607,7 @@ data FetchFlags = FetchFlags {
607607
fetchMaxBackjumps :: Flag Int,
608608
fetchMaxScore :: Flag InstallPlanScore,
609609
fetchFindBestSolution :: Flag FindBestSolution,
610+
fetchDynamicGoalReordering :: Flag DynamicGoalReordering,
610611
fetchReorderGoals :: Flag ReorderGoals,
611612
fetchIndependentGoals :: Flag IndependentGoals,
612613
fetchShadowPkgs :: Flag ShadowPkgs,
@@ -623,6 +624,7 @@ defaultFetchFlags = FetchFlags {
623624
fetchMaxBackjumps = Flag defaultMaxBackjumps,
624625
fetchMaxScore = mempty,
625626
fetchFindBestSolution = Flag (FindBestSolution False),
627+
fetchDynamicGoalReordering = Flag (DynamicGoalReordering False),
626628
fetchReorderGoals = Flag (ReorderGoals False),
627629
fetchIndependentGoals = Flag (IndependentGoals False),
628630
fetchShadowPkgs = Flag (ShadowPkgs False),
@@ -671,6 +673,7 @@ fetchCommand = CommandUI {
671673
fetchMaxBackjumps (\v flags -> flags { fetchMaxBackjumps = v })
672674
fetchMaxScore (\v flags -> flags { fetchMaxScore = v })
673675
fetchFindBestSolution (\v flags -> flags { fetchFindBestSolution = v })
676+
fetchDynamicGoalReordering (\v flags -> flags { fetchDynamicGoalReordering = v })
674677
fetchReorderGoals (\v flags -> flags { fetchReorderGoals = v })
675678
fetchIndependentGoals (\v flags -> flags { fetchIndependentGoals = v })
676679
fetchShadowPkgs (\v flags -> flags { fetchShadowPkgs = v })
@@ -690,6 +693,7 @@ data FreezeFlags = FreezeFlags {
690693
freezeMaxBackjumps :: Flag Int,
691694
freezeMaxScore :: Flag InstallPlanScore,
692695
freezeFindBestSolution :: Flag FindBestSolution,
696+
freezeDynamicGoalReordering :: Flag DynamicGoalReordering,
693697
freezeReorderGoals :: Flag ReorderGoals,
694698
freezeIndependentGoals :: Flag IndependentGoals,
695699
freezeShadowPkgs :: Flag ShadowPkgs,
@@ -706,6 +710,7 @@ defaultFreezeFlags = FreezeFlags {
706710
freezeMaxBackjumps = Flag defaultMaxBackjumps,
707711
freezeMaxScore = mempty,
708712
freezeFindBestSolution = Flag (FindBestSolution False),
713+
freezeDynamicGoalReordering = Flag (DynamicGoalReordering False),
709714
freezeReorderGoals = Flag (ReorderGoals False),
710715
freezeIndependentGoals = Flag (IndependentGoals False),
711716
freezeShadowPkgs = Flag (ShadowPkgs False),
@@ -753,6 +758,7 @@ freezeCommand = CommandUI {
753758
freezeMaxBackjumps (\v flags -> flags { freezeMaxBackjumps = v })
754759
freezeMaxScore (\v flags -> flags { freezeMaxScore = v })
755760
freezeFindBestSolution (\v flags -> flags { freezeFindBestSolution = v })
761+
freezeDynamicGoalReordering (\v flags -> flags { freezeDynamicGoalReordering = v })
756762
freezeReorderGoals (\v flags -> flags { freezeReorderGoals = v })
757763
freezeIndependentGoals (\v flags -> flags { freezeIndependentGoals = v })
758764
freezeShadowPkgs (\v flags -> flags { freezeShadowPkgs = v })
@@ -1157,6 +1163,7 @@ data InstallFlags = InstallFlags {
11571163
installMaxBackjumps :: Flag Int,
11581164
installMaxScore :: Flag InstallPlanScore,
11591165
installFindBestSolution :: Flag FindBestSolution,
1166+
installDynamicGoalReordering :: Flag DynamicGoalReordering,
11601167
installReorderGoals :: Flag ReorderGoals,
11611168
installIndependentGoals :: Flag IndependentGoals,
11621169
installShadowPkgs :: Flag ShadowPkgs,
@@ -1190,6 +1197,7 @@ defaultInstallFlags = InstallFlags {
11901197
installMaxBackjumps = Flag defaultMaxBackjumps,
11911198
installMaxScore = mempty,
11921199
installFindBestSolution= Flag (FindBestSolution False),
1200+
installDynamicGoalReordering = Flag (DynamicGoalReordering False),
11931201
installReorderGoals = Flag (ReorderGoals False),
11941202
installIndependentGoals= Flag (IndependentGoals False),
11951203
installShadowPkgs = Flag (ShadowPkgs False),
@@ -1336,6 +1344,7 @@ installOptions showOrParseArgs =
13361344
installMaxBackjumps (\v flags -> flags { installMaxBackjumps = v })
13371345
installMaxScore (\v flags -> flags { installMaxScore = v })
13381346
installFindBestSolution (\v flags -> flags { installFindBestSolution = v })
1347+
installDynamicGoalReordering (\v flags -> flags { installDynamicGoalReordering = v })
13391348
installReorderGoals (\v flags -> flags { installReorderGoals = v })
13401349
installIndependentGoals (\v flags -> flags { installIndependentGoals = v })
13411350
installShadowPkgs (\v flags -> flags { installShadowPkgs = v })
@@ -2098,12 +2107,13 @@ optionSolverFlags :: ShowOrParseArgs
20982107
-> (flags -> Flag Int ) -> (Flag Int -> flags -> flags)
20992108
-> (flags -> Flag InstallPlanScore) -> (Flag InstallPlanScore -> flags -> flags)
21002109
-> (flags -> Flag FindBestSolution) -> (Flag FindBestSolution -> flags -> flags)
2110+
-> (flags -> Flag DynamicGoalReordering) -> (Flag DynamicGoalReordering -> flags -> flags)
21012111
-> (flags -> Flag ReorderGoals) -> (Flag ReorderGoals -> flags -> flags)
21022112
-> (flags -> Flag IndependentGoals) -> (Flag IndependentGoals -> flags -> flags)
21032113
-> (flags -> Flag ShadowPkgs) -> (Flag ShadowPkgs -> flags -> flags)
21042114
-> (flags -> Flag StrongFlags) -> (Flag StrongFlags -> flags -> flags)
21052115
-> [OptionField flags]
2106-
optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getrg setrg _getig _setig getsip setsip getstrfl setstrfl =
2116+
optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getdyn setdyn getrg setrg _getig _setig getsip setsip getstrfl setstrfl =
21072117
[ option [] ["max-backjumps"]
21082118
("Maximum number of backjumps allowed while solving (default: " ++ show defaultMaxBackjumps ++ "). Use a negative number to enable unlimited backtracking. Use 0 to disable backtracking completely.")
21092119
getmbj setmbj
@@ -2121,6 +2131,11 @@ optionSolverFlags showOrParseArgs getmbj setmbj getms setms getfb setfb getrg se
21212131
(fmap asBool . getfb)
21222132
(setfb . fmap FindBestSolution)
21232133
(yesNoOpt showOrParseArgs)
2134+
, option [] ["dynamic-goal-reordering"]
2135+
"Prefer goals that were previously involved in a conflict."
2136+
(fmap asBool . getdyn)
2137+
(setdyn . fmap DynamicGoalReordering)
2138+
(yesNoOpt showOrParseArgs)
21242139
, option [] ["reorder-goals"]
21252140
"Try to reorder goals according to certain heuristics. Slows things down on average, but may make backtracking faster for some packages."
21262141
(fmap asBool . getrg)

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

Lines changed: 29 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,9 +127,10 @@ 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)
@@ -136,6 +143,7 @@ explore enableBj maxScore findBest =
136143
put ExploreState {
137144
esBestPlan = Just (a, rdm, ssTotalScore ss)
138145
, esMaxScore = Just $ ssTotalScore ss
146+
, esPreferredGoals = CS.empty
139147
}
140148
if asBool findBest
141149
then fail' (ssConflictSet ss) $
@@ -150,8 +158,12 @@ explore enableBj maxScore findBest =
150158
go (SChoiceF qsn (gr, ss) _ ts) =
151159
maybePrune ss $ backjump enableBj (S qsn) (avoidSet (S qsn) gr) $
152160
W.mapWithKey (\ k r -> tryWith (TryS qsn k) `fmap` r) ts
153-
go (GoalChoiceF ts) =
154-
P.casePSQ ts
161+
go (GoalChoiceF ts) = do
162+
preferred <- gets esPreferredGoals
163+
let ts' = if asBool dynGoals
164+
then P.sortByKeys (goalOrdering preferred) ts
165+
else ts
166+
P.casePSQ ts'
155167
(fail' CS.empty EmptyGoalChoice) -- empty goal choice is an internal error
156168
(\ k v _xs -> continueWith (Next k) `fmap` v) -- commit to the first goal choice
157169

@@ -169,8 +181,15 @@ explore enableBj maxScore findBest =
169181
initES = ExploreState {
170182
esBestPlan = Nothing
171183
, esMaxScore = maxScore
184+
, esPreferredGoals = CS.empty
172185
}
173186

187+
goalOrdering :: ConflictSet QPN -> Goal QPN -> Goal QPN -> Ordering
188+
goalOrdering vars = flip $ comparing $ (`CS.member` vars) . goalToVar
189+
where
190+
goalToVar :: Goal QPN -> Var QPN
191+
goalToVar (Goal var _) = var
192+
174193
-- | Build a conflict set corresponding to the (virtual) option not to
175194
-- choose a solution for a goal at all.
176195
--
@@ -200,16 +219,20 @@ avoidSet var gr =
200219

201220
-- | Fail and record the current best install plan.
202221
fail' :: ConflictSet QPN -> FailReason -> Explore (ConflictSetLog a)
203-
fail' c fr = (\plan -> failWith (Failure c fr plan) c) `fmap` gets esBestPlan
222+
fail' c fr = do
223+
es <- get
224+
put es { esPreferredGoals = c }
225+
return $ failWith (Failure c fr (esBestPlan es)) c
204226

205227
-- | Interface.
206228
backjumpAndExplore :: EnableBackjumping
207229
-> Maybe InstallPlanScore
208230
-> FindBestSolution
231+
-> DynamicGoalReordering
209232
-> Tree ScoringState (QGoalReason, ScoringState)
210233
-> Log Message Plan
211-
backjumpAndExplore enableBj maxScore findBest =
212-
toLog . explore enableBj maxScore findBest . assign
234+
backjumpAndExplore enableBj maxScore findBest dynGoals =
235+
toLog . explore enableBj maxScore findBest dynGoals . assign
213236
where
214237
toLog :: P.Progress step fail done -> Log step done
215238
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.Solver.Types.ConstraintSource
3435
import Distribution.Solver.Types.InstalledPreference
@@ -415,7 +416,12 @@ deferWeakFlagChoices = trav go
415416
preferEasyGoalChoices :: Tree a b -> Tree a b
416417
preferEasyGoalChoices = trav go
417418
where
418-
go (GoalChoiceF xs) = GoalChoiceF (P.dminimumBy dchoices xs)
419+
-- TODO: --dynamic-goal-reordering requires this function to leave all goal
420+
-- choices. If we decide to keep the feature, this function should be
421+
-- refactored to calculate the first element more efficiently, as before:
422+
-- go (GoalChoiceF xs) = GoalChoiceF (P.dminimumBy dchoices xs)
423+
424+
go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing dchoices) xs)
419425
-- (a different implementation that seems slower):
420426
-- GoalChoiceF (P.firstOnly (P.preferOrElse zeroOrOneChoices (P.minimumBy choices) xs))
421427
go x = x

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ data SolverConfig = SolverConfig {
5757
maxBackjumps :: Maybe Int,
5858
enableBackjumping :: EnableBackjumping,
5959
maxInstallPlanScore :: Maybe InstallPlanScore,
60-
findBestSolution :: FindBestSolution
60+
findBestSolution :: FindBestSolution,
61+
dynamicGoalReordering :: DynamicGoalReordering
6162
}
6263

6364
-- | Run all solver phases.
@@ -106,11 +107,12 @@ solve sc cinfo idx pkgConfigDB userPrefs userConstraints userGoals =
106107
explorePhase = backjumpAndExplore (enableBackjumping sc)
107108
(maxInstallPlanScore sc)
108109
(findBestSolution sc)
110+
(dynamicGoalReordering sc)
109111
detectCycles = traceTree "cycles.json" id . detectCyclesPhase
110112
scorePhase = P.scoreTree -- must come after all preferences
111113
heuristicsPhase = (if asBool (preferEasyGoalChoices sc)
112-
then P.preferEasyGoalChoices -- also leaves just one choice
113-
else P.firstGoal) . -- after doing goal-choice heuristics, commit to the first choice (saves space)
114+
then P.preferEasyGoalChoices
115+
else id) .
114116
traceTree "heuristics.json" id .
115117
P.deferWeakFlagChoices .
116118
P.deferSetupChoices .

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)