Skip to content

Commit d0021a4

Browse files
wip narrowing
1 parent 5d52eac commit d0021a4

File tree

2 files changed

+87
-64
lines changed

2 files changed

+87
-64
lines changed

src/Constrained/Env.hs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{-# LANGUAGE DerivingVia #-}
2+
{-# LANGUAGE ImpredicativeTypes #-}
3+
{-# LANGUAGE ExistentialQuantification #-}
24
{-# LANGUAGE GADTs #-}
35
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
46
{-# LANGUAGE ImportQualifiedPost #-}
@@ -13,6 +15,7 @@ module Constrained.Env (
1315
lookup,
1416
find,
1517
remove,
18+
filterKeys,
1619
) where
1720

1821
import Constrained.Core
@@ -34,7 +37,7 @@ data EnvValue where
3437
deriving instance Show EnvValue
3538

3639
data EnvKey where
37-
EnvKey :: !(Var a) -> EnvKey
40+
EnvKey :: Typeable a => !(Var a) -> EnvKey
3841

3942
instance Eq EnvKey where
4043
EnvKey v == EnvKey v' = nameOf v == nameOf v'
@@ -50,7 +53,7 @@ extend :: (Typeable a, Show a) => Var a -> a -> Env -> Env
5053
extend v a (Env m) = Env $ Map.insert (EnvKey v) (EnvValue a) m
5154

5255
-- | Remove a variable from an environment if it exists
53-
remove :: Var a -> Env -> Env
56+
remove :: Typeable a => Var a -> Env -> Env
5457
remove v (Env m) = Env $ Map.delete (EnvKey v) m
5558

5659
-- | Create a singleton environment
@@ -70,6 +73,11 @@ find env var = do
7073
Just a -> pure a
7174
Nothing -> genError ("Couldn't find " ++ show var ++ " in " ++ show env)
7275

76+
-- | Filter the keys in an env, useful for removing irrelevant variables in
77+
-- error messages
78+
filterKeys :: Env -> (forall a. Typeable a => Var a -> Bool) -> Env
79+
filterKeys (Env m) f = Env $ Map.filterWithKey (\ (EnvKey k) _ -> f k) m
80+
7381
instance Pretty EnvValue where
7482
pretty (EnvValue x) = pretty $ take 80 (show x)
7583

src/Constrained/Generation.hs

Lines changed: 77 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,15 @@ prettyPlan (simplifySpec -> spec)
194194

195195
-- ---------------------- Building a plan -----------------------------------
196196

197-
substStage :: Env -> SolverStage -> SolverStage
198-
substStage env (SolverStage y ps spec) = normalizeSolverStage $ SolverStage y (substPred env <$> ps) spec
197+
substStage :: HasSpec a => Set Name -> Var a -> a -> SolverStage -> SolverStage
198+
substStage rel' x val (SolverStage y ps spec relevant) =
199+
normalizeSolverStage $ SolverStage y (substPred env <$> ps) spec relevant'
200+
where env = Env.singleton x val
201+
relevant' | Name x `appearsIn` ps = rel' <> relevant
202+
| otherwise = relevant
199203

200204
normalizeSolverStage :: SolverStage -> SolverStage
201-
normalizeSolverStage (SolverStage x ps spec) = SolverStage x ps'' (spec <> spec')
205+
normalizeSolverStage (SolverStage x ps spec relevant) = SolverStage x ps'' (spec <> spec') relevant
202206
where
203207
(ps', ps'') = partition ((1 ==) . Set.size . freeVarSet) ps
204208
spec' = fromGESpec $ computeSpec x (And ps')
@@ -228,7 +232,7 @@ prepareLinearization p = do
228232
]
229233
)
230234
$ linearize preds graph
231-
pure $ backPropagation $ SolverPlan plan
235+
pure $ backPropagation mempty $ SolverPlan plan
232236

233237
-- | Flatten nested `Let`, `Exists`, and `And` in a `Pred fn`. `Let` and
234238
-- `Exists` bound variables become free in the result.
@@ -300,7 +304,7 @@ linearize preds graph = do
300304
]
301305
go (n@(Name x) : ns) ps = do
302306
let (nps, ops) = partition (isLastVariable n . fst) ps
303-
(normalizeSolverStage (SolverStage x (map snd nps) mempty) :) <$> go ns ops
307+
(normalizeSolverStage (SolverStage x (map snd nps) mempty mempty):) <$> go ns ops
304308

305309
isLastVariable n set = n `Set.member` set && solvableFrom n (Set.delete n set) graph
306310

@@ -823,7 +827,7 @@ totalWeight = fmap getSum . foldMapList (fmap Semigroup.Sum . weight)
823827

824828
-- | Does nothing if the variable is not in the plan already.
825829
mergeSolverStage :: SolverStage -> [SolverStage] -> [SolverStage]
826-
mergeSolverStage (SolverStage x ps spec) plan =
830+
mergeSolverStage (SolverStage x ps spec relevant) plan =
827831
[ case eqVar x y of
828832
Just Refl ->
829833
SolverStage
@@ -840,51 +844,63 @@ mergeSolverStage (SolverStage x ps spec) plan =
840844
)
841845
(spec <> spec')
842846
)
847+
(relevant <> relevant')
843848
Nothing -> stage
844-
| stage@(SolverStage y ps' spec') <- plan
849+
| stage@(SolverStage y ps' spec' relevant') <- plan
845850
]
846851

847852
isEmptyPlan :: SolverPlan -> Bool
848853
isEmptyPlan (SolverPlan plan) = null plan
849854

850-
stepPlan :: MonadGenError m => Env -> SolverPlan -> GenT m (Env, SolverPlan)
851-
stepPlan env plan@(SolverPlan []) = pure (env, plan)
852-
stepPlan env (SolverPlan (SolverStage (x :: Var a) ps spec : pl)) = do
853-
(spec', specs) <- runGE
854-
$ explain
855-
( show
856-
( "Computing specs for variable "
857-
<> pretty x
858-
/> vsep' (map pretty ps)
859-
)
860-
)
861-
$ do
862-
ispecs <- mapM (computeSpec x) ps
863-
pure $ (fold ispecs, ispecs)
864-
val <-
865-
genFromSpecT
866-
( addToErrorSpec
867-
( NE.fromList
868-
( ( "\nStepPlan for variable: "
869-
++ show x
870-
++ "::"
871-
++ showType @a
872-
++ " fails to produce Specification, probably overconstrained."
873-
++ "PS = "
874-
++ unlines (map show ps)
855+
stepPlan :: MonadGenError m => SolverPlan -> Env -> SolverPlan -> GenT m (Env, SolverPlan)
856+
stepPlan _ env plan@(SolverPlan []) = pure (env, plan)
857+
stepPlan (SolverPlan origStages) env (SolverPlan (stage@(SolverStage (x :: Var a) ps spec relevant) : pl)) = do
858+
let errorMessage = "Failed to step the plan" />
859+
vsep [ "Relevant parts of original plan: " /> pretty narrowedOrigPlan
860+
, "Relevant parts of the env: " /> pretty narrowedEnv
861+
, "Current stage: " /> pretty stage
862+
]
863+
-- TODO: tests for this, including tests for transitive behaviour
864+
narrowedOrigPlan = SolverPlan $ [ st | st@(SolverStage v _ _ _) <- origStages, Name v `Set.member` relevant ]
865+
narrowedEnv = Env.filterKeys env (\v -> nameOf v `Set.member` (Set.map (\ (Name n) -> nameOf n) relevant))
866+
explain (show errorMessage) $ do
867+
(spec', specs) <- runGE
868+
$ explain
869+
( show
870+
( "Computing specs for variable "
871+
<> pretty x
872+
/> vsep' (map pretty ps)
873+
)
874+
)
875+
$ do
876+
ispecs <- mapM (computeSpec x) ps
877+
pure $ (fold ispecs, ispecs)
878+
val <-
879+
genFromSpecT
880+
( addToErrorSpec
881+
( NE.fromList
882+
( ( "\nStepPlan for variable: "
883+
++ show x
884+
++ "::"
885+
++ showType @a
886+
++ " fails to produce Specification, probably overconstrained."
887+
++ "PS = "
888+
++ unlines (map show ps)
889+
)
890+
: ("Relevant variables " ++ show relevant)
891+
: ("Original spec " ++ show spec)
892+
: "Predicates"
893+
: zipWith
894+
(\pred specx -> " pred " ++ show pred ++ " -> " ++ show specx)
895+
ps
896+
specs
875897
)
876-
: ("Original spec " ++ show spec)
877-
: "Predicates"
878-
: zipWith
879-
(\pred specx -> " pred " ++ show pred ++ " -> " ++ show specx)
880-
ps
881-
specs
882-
)
883-
)
884-
(spec <> spec')
885-
)
886-
let env1 = Env.extend x val env
887-
pure (env1, backPropagation $ SolverPlan (substStage env1 <$> pl) )
898+
)
899+
(spec <> spec')
900+
)
901+
let env1 = Env.extend x val env
902+
let relevant' = Set.insert (Name x) relevant
903+
pure (env1, backPropagation relevant' $ SolverPlan (substStage relevant' x val <$> pl) )
888904

889905
-- | Generate a satisfying `Env` for a `p : Pred fn`. The `Env` contains values for
890906
-- all the free variables in `flattenPred p`.
@@ -894,25 +910,22 @@ genFromPreds env0 (optimisePred . optimisePred -> preds) = do
894910
-- NOTE: this is just lazy enough that the work of flattening,
895911
-- computing dependencies, and linearizing is memoized in
896912
-- properties that use `genFromPreds`.
897-
plan <- runGE $ prepareLinearization preds
898-
go env0 plan
913+
origPlan <- runGE $ prepareLinearization preds
914+
let go :: Env -> SolverPlan -> GenT m Env
915+
go env plan | isEmptyPlan plan = pure env
916+
go env plan = do
917+
(env', plan') <- stepPlan origPlan env plan
918+
go env' plan'
919+
go env0 origPlan
899920
where
900-
go :: Env -> SolverPlan -> GenT m Env
901-
go env plan | isEmptyPlan plan = pure env
902-
go env plan = do
903-
(mess :: String) <- (unlines . map NE.head) <$> getMessages
904-
(env', plan') <-
905-
explain (show (fromString (mess ++ "Stepping the plan:") /> vsep [pretty plan, pretty env])) $
906-
stepPlan env plan
907-
go env' plan'
908921

909922
-- | Push as much information we can backwards through the plan.
910-
backPropagation :: SolverPlan -> SolverPlan
911-
backPropagation (SolverPlan initplan) = SolverPlan (go [] (reverse initplan))
923+
backPropagation :: Set Name -> SolverPlan -> SolverPlan
924+
backPropagation relevant (SolverPlan initplan) = SolverPlan (go [] (reverse initplan))
912925
where
913926
go :: [SolverStage] -> [SolverStage] -> [SolverStage]
914927
go acc [] = acc
915-
go acc (s@(SolverStage (x :: Var a) ps spec) : plan) = go (s : acc) plan'
928+
go acc (s@(SolverStage (x :: Var a) ps spec _) : plan) = go (s : acc) plan'
916929
where
917930
newStages = concatMap (newStage spec) ps
918931
plan' = foldr mergeSolverStage plan newStages
@@ -926,12 +939,12 @@ backPropagation (SolverPlan initplan) = SolverPlan (go [] (reverse initplan))
926939
termVarEqCases :: HasSpec b => Specification a -> Var b -> Term b -> [SolverStage]
927940
termVarEqCases (MemberSpec vs) x' t
928941
| Set.singleton (Name x) == freeVarSet t =
929-
[SolverStage x' [] $ MemberSpec (NE.nub (fmap (\v -> errorGE $ runTerm (Env.singleton x v) t) vs))]
942+
[SolverStage x' [] (MemberSpec (NE.nub (fmap (\v -> errorGE $ runTerm (Env.singleton x v) t) vs))) relevant]
930943
termVarEqCases specx x' t
931944
| Just Refl <- eqVar x x'
932945
, [Name y] <- Set.toList $ freeVarSet t
933946
, Result ctx <- toCtx y t =
934-
[SolverStage y [] (propagateSpec specx ctx)]
947+
[SolverStage y [] (propagateSpec specx ctx) relevant]
935948
termVarEqCases _ _ _ = []
936949

937950
-- | Function symbols for `(==.)`
@@ -1025,10 +1038,11 @@ pinnedBy _ _ = Nothing
10251038
-- assert $ just_ x ==. lookup_ y (lit $ Map.fromList [(z, z) | z <- [100 .. 102]])
10261039

10271040
-- Without this code the example wouldn't work because `y` is completely unconstrained during
1028-
-- generation. With this code we essentially rewrite occurences of `just_ A == B` to
1029-
-- `[cJust A == B, case B of Nothing -> False; Just _ -> True]` to add extra information
1041+
-- generation. With this code we _essentially_ rewrite occurences of `just_ A == B` to
1042+
-- `[just_ A == B, case B of Nothing -> False; Just _ -> True]` to add extra information
10301043
-- about the variables in `B`. Consequently, `y` in the example above is
1031-
-- constrained to `MemberSpec [100 .. 102]` in the plan.
1044+
-- constrained to `MemberSpec [100 .. 102]` in the plan. This is implemented using the `saturate`
1045+
-- function in the logic type class - in the example above for `==`.
10321046
saturatePred :: Pred -> [Pred]
10331047
saturatePred p =
10341048
-- [p]
@@ -1307,6 +1321,7 @@ data SolverStage where
13071321
{ stageVar :: Var a
13081322
, stagePreds :: [Pred]
13091323
, stageSpec :: Specification a
1324+
, relevantVariables :: Set Name
13101325
} ->
13111326
SolverStage
13121327

0 commit comments

Comments
 (0)