1
1
module Distribution.Client.Dependency.Modular.Explore
2
2
( backjump
3
- , exploreTreeLog
3
+ , backjumpAndExplore
4
4
) where
5
5
6
- import Control.Applicative as A
7
- import Data.Foldable
8
- import Data.List as L
6
+ import Data.Foldable as F
9
7
import Data.Map as M
10
8
import Data.Set as S
11
9
@@ -16,107 +14,81 @@ import Distribution.Client.Dependency.Modular.Message
16
14
import Distribution.Client.Dependency.Modular.Package
17
15
import qualified Distribution.Client.Dependency.Modular.PSQ as P
18
16
import Distribution.Client.Dependency.Modular.Tree
17
+ import qualified Distribution.Client.Dependency.Types as T
19
18
20
- -- | Backjumping.
19
+ -- | This function takes the variable we're currently considering and a
20
+ -- list of children's logs. Each log yields either a solution or a
21
+ -- conflict set. The result is a combined log for the parent node that
22
+ -- has explored a prefix of the children.
21
23
--
22
- -- A tree traversal that tries to propagate conflict sets
23
- -- up the tree from the leaves, and thereby cut branches.
24
- -- All the tricky things are done in the function 'combine'.
25
- backjump :: Tree a -> Tree (Maybe (ConflictSet QPN ))
26
- backjump = snd . cata go
27
- where
28
- go (FailF c fr) = (Just c, Fail c fr)
29
- go (DoneF rdm ) = (Nothing , Done rdm)
30
- go (PChoiceF qpn _ ts) = (c, PChoice qpn c (P. fromList ts'))
31
- where
32
- ~ (c, ts') = combine (P qpn) (P. toList ts) S. empty
33
- go (FChoiceF qfn _ b m ts) = (c, FChoice qfn c b m (P. fromList ts'))
34
- where
35
- ~ (c, ts') = combine (F qfn) (P. toList ts) S. empty
36
- go (SChoiceF qsn _ b ts) = (c, SChoice qsn c b (P. fromList ts'))
37
- where
38
- ~ (c, ts') = combine (S qsn) (P. toList ts) S. empty
39
- go (GoalChoiceF ts) = (c, GoalChoice (P. fromList ts'))
40
- where
41
- ~ (cs, ts') = unzip $ L. map (\ (k, (x, v)) -> (x, (k, v))) $ P. toList ts
42
- c = case cs of [] -> Nothing
43
- d : _ -> d
44
-
45
- -- | The 'combine' function is at the heart of backjumping. It takes
46
- -- the variable we're currently considering, and a list of children
47
- -- annotated with their respective conflict sets, and an accumulator
48
- -- for the result conflict set. It returns a combined conflict set
49
- -- for the parent node, and a (potentially shortened) list of children
50
- -- with the annotations removed.
51
- --
52
- -- It is *essential* that we produce the results as early as possible.
53
- -- In particular, we have to produce the list of children prior to
54
- -- traversing the entire list -- otherwise we lose the desired behaviour
55
- -- of being able to traverse the tree from left to right incrementally.
56
- --
57
- -- We can shorten the list of children if we find an individual conflict
58
- -- set that does not contain the current variable. In this case, we can
59
- -- just lift the conflict set to the current level, because the current
60
- -- level cannot possibly have contributed to this conflict, so no other
61
- -- choice at the current level would avoid the conflict.
24
+ -- We can stop traversing the children's logs if we find an individual
25
+ -- conflict set that does not contain the current variable. In this
26
+ -- case, we can just lift the conflict set to the current level,
27
+ -- because the current level cannot possibly have contributed to this
28
+ -- conflict, so no other choice at the current level would avoid the
29
+ -- conflict.
62
30
--
63
- -- If any of the children might contain a successful solution
64
- -- (indicated by Nothing), then Nothing will be the combined
65
- -- conflict set. If all children contain conflict sets, we can
31
+ -- If any of the children might contain a successful solution, we can
32
+ -- return it immediately. If all children contain conflict sets, we can
66
33
-- take the union as the combined conflict set.
67
- combine :: Var QPN -> [(a , (Maybe (ConflictSet QPN ), b ))] ->
68
- ConflictSet QPN -> (Maybe (ConflictSet QPN ), [(a , b )])
69
- combine _ [] c = (Just c, [] )
70
- combine var ((k, ( d, v)) : xs) c = (\ ~ (e, ys) -> (e, (k, v) : ys)) $
71
- case d of
72
- Just e | not (simplifyVar var `S.member` e) -> (Just e, [] )
73
- | otherwise -> combine var xs (e `S.union` c)
74
- Nothing -> (Nothing , snd $ combine var xs S. empty)
34
+ backjump :: F. Foldable t => Var QPN -> t (ConflictSetLog a ) -> ConflictSetLog a
35
+ backjump var xs = F. foldr combine backjumpInfo xs S. empty
36
+ where
37
+ combine :: ConflictSetLog a
38
+ -> (ConflictSet QPN -> ConflictSetLog a )
39
+ -> ConflictSet QPN -> ConflictSetLog a
40
+ combine (T. Done x) _ _ = T. Done x
41
+ combine (T. Fail cs) f csAcc
42
+ | not (simplifyVar var `S.member` cs) = backjumpInfo cs
43
+ | otherwise = f (csAcc `S.union` cs)
44
+ combine (T. Step m ms) f cs = T. Step m (combine ms f cs)
45
+
46
+ type ConflictSetLog = T. Progress Message (ConflictSet QPN )
75
47
76
- -- | Version of 'explore' that returns a 'Log'.
77
- exploreLog :: Tree ( Maybe ( ConflictSet QPN )) ->
78
- (Assignment -> Log Message (Assignment , RevDepMap ))
48
+ -- | A tree traversal that simultaneously propagates conflict sets up
49
+ -- the tree from the leaves and creates a log.
50
+ exploreLog :: Tree a -> (Assignment -> ConflictSetLog (Assignment , RevDepMap ))
79
51
exploreLog = cata go
80
52
where
81
- go (FailF c fr) _ = failWith (Failure c fr)
53
+ go :: TreeF a (Assignment -> ConflictSetLog (Assignment , RevDepMap ))
54
+ -> (Assignment -> ConflictSetLog (Assignment , RevDepMap ))
55
+ go (FailF c fr) _ = failWith (Failure c fr) c
82
56
go (DoneF rdm) a = succeedWith Success (a, rdm)
83
- go (PChoiceF qpn c ts) (A pa fa sa) =
84
- backjumpInfo c $
85
- asum $ -- try children in order,
57
+ go (PChoiceF qpn _ ts) (A pa fa sa) =
58
+ backjump (P qpn) $ -- try children in order,
86
59
P. mapWithKey -- when descending ...
87
- (\ i@ (POption k _) r -> tryWith (TryP qpn i) $ -- log and ...
60
+ (\ i@ (POption k _) r -> tryWith (TryP qpn i) $ -- log and ...
88
61
r (A (M. insert qpn k pa) fa sa)) -- record the pkg choice
89
62
ts
90
- go (FChoiceF qfn c _ _ ts) (A pa fa sa) =
91
- backjumpInfo c $
92
- asum $ -- try children in order,
63
+ go (FChoiceF qfn _ _ _ ts) (A pa fa sa) =
64
+ backjump (F qfn) $ -- try children in order,
93
65
P. mapWithKey -- when descending ...
94
66
(\ k r -> tryWith (TryF qfn k) $ -- log and ...
95
67
r (A pa (M. insert qfn k fa) sa)) -- record the pkg choice
96
68
ts
97
- go (SChoiceF qsn c _ ts) (A pa fa sa) =
98
- backjumpInfo c $
99
- asum $ -- try children in order,
69
+ go (SChoiceF qsn _ _ ts) (A pa fa sa) =
70
+ backjump (S qsn) $ -- try children in order,
100
71
P. mapWithKey -- when descending ...
101
72
(\ k r -> tryWith (TryS qsn k) $ -- log and ...
102
73
r (A pa fa (M. insert qsn k sa))) -- record the pkg choice
103
74
ts
104
75
go (GoalChoiceF ts) a =
105
76
P. casePSQ ts
106
- (failWith (Failure S. empty EmptyGoalChoice )) -- empty goal choice is an internal error
107
- (\ k v _xs -> continueWith (Next (close k)) (v a)) -- commit to the first goal choice
77
+ (failWith (Failure S. empty EmptyGoalChoice ) S. empty) -- empty goal choice is an internal error
78
+ (\ k v _xs -> continueWith (Next (close k)) (v a)) -- commit to the first goal choice
108
79
109
80
-- | Add in information about pruned trees.
110
81
--
111
82
-- TODO: This isn't quite optimal, because we do not merely report the shape of the
112
83
-- tree, but rather make assumptions about where that shape originated from. It'd be
113
84
-- better if the pruning itself would leave information that we could pick up at this
114
85
-- point.
115
- backjumpInfo :: Maybe (ConflictSet QPN ) -> Log Message a -> Log Message a
116
- backjumpInfo c m = m <|> case c of -- important to produce 'm' before matching on 'c'!
117
- Nothing -> A. empty
118
- Just cs -> failWith (Failure cs Backjump )
86
+ backjumpInfo :: ConflictSet QPN -> ConflictSetLog a
87
+ backjumpInfo cs = failWith (Failure cs Backjump ) cs
119
88
120
89
-- | Interface.
121
- exploreTreeLog :: Tree (Maybe (ConflictSet QPN )) -> Log Message (Assignment , RevDepMap )
122
- exploreTreeLog t = exploreLog t (A M. empty M. empty M. empty)
90
+ backjumpAndExplore :: Tree a -> Log Message (Assignment , RevDepMap )
91
+ backjumpAndExplore t = toLog $ exploreLog t (A M. empty M. empty M. empty)
92
+ where
93
+ toLog :: T. Progress step fail done -> Log step done
94
+ toLog = T. foldProgress T. Step (const (T. Fail () )) T. Done
0 commit comments