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