-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
4059 ensures substitution #4060
Conversation
9b4f310
to
bc44927
Compare
2e7f126
to
52d3ad4
Compare
Failed KEVM tests as of 52d3ad4
|
4e34924
to
fdf62fc
Compare
No failed KEVM tests at commit fdf62fc and not much difference in performance:
|
Include substitution in known truth when simplifying
Fix unit tests add test
Format with fourmolu
Handle with care
8e622f9
to
598c8a4
Compare
0c612f3
to
b2ea0f8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big job, well done! I like that we can ditch many of the response*.booster-dev
files.
One open question is the removal of purging the simplifier cache when we have new constraints, I think that should still be done. Please put this back or explain why we don't.
The other things are minor and stylistic. Maybe we should think about the pattern constructor, as mentioned.
booster/library/Booster/JsonRpc.hs
Outdated
let substPat = | ||
Pattern | ||
{ term = substituteInTerm substitution pat.term | ||
, constraints = Set.map (substituteInPredicate substitution) pat.constraints | ||
{ term = Substitution.substituteInTerm pat.substitution pat.term | ||
, constraints = Set.map (Substitution.substituteInPredicate pat.substitution) pat.constraints | ||
, ceilConditions = pat.ceilConditions | ||
, substitution = pat.substitution | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While reading through here, I thought that internaliseTermOrPredicates
, and any internalisation that may return a Pattern
as a whole, should maybe already apply the substitution before returning the result (somewhat "normalising" the pattern before it is returned).
The internalisePattern
, in contrast, returns the parts separately and leaves it to the caller to piece them back together. This is because we use it a lot in the definition internalisation where we need the parts separately.
This is more of a stylistic preference, and IDK how much fall-out it will have, so it is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very good point. I'll try putting this change in.
withPatternContext Pattern{term, constraints} m = | ||
let t' = foldl' AndTerm term $ Set.toList $ Set.map coerce constraints -- FIXME | ||
withPatternContext Pattern{term, constraints, substitution} m = | ||
let t' = foldl' AndTerm term . map coerce $ Set.toList constraints <> asEquations substitution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That FIXME
was referring to the fact that the t' :: Term
is not well-sorted. Probably good enough for logging, though... just that nobody should use the json version of it as input directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we make it well-sorted? Replace AndTerm
with _andBool_
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah no, there's a term there...
Pattern | ||
{ term = substituteInTerm substitutionL patL.term | ||
, constraints = Set.map (substituteInPredicate substitutionL) patL.constraints | ||
{ term = Substitution.substituteInTerm substitutionL patL.term | ||
, constraints = Set.map (Substitution.substituteInPredicate substitutionL) patL.constraints | ||
, ceilConditions = patL.ceilConditions | ||
, substitution = substitutionL | ||
} | ||
substPatR = | ||
Pattern | ||
{ term = substituteInTerm substitutionR patR.term | ||
, constraints = Set.map (substituteInPredicate substitutionR) patR.constraints | ||
{ term = Substitution.substituteInTerm substitutionR patR.term | ||
, constraints = Set.map (Substitution.substituteInPredicate substitutionR) patR.constraints | ||
, ceilConditions = patR.ceilConditions | ||
, substitution = substitutionR | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above, maybe the Pattern
returned from internalisePatternOrTopOrBottom
should already have the substitution applied...
I wonder whether we could just enforce that by a smart constructor for Pattern
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We very often construct a pattern directly, bypassing the Pattern_
smart-constructor. I'd love to have a smart constructor that is used consistently, but I think it may be out-of-scope for this PR. I do agree that it's best to apply the substitution in the internalisation function that return a Pattern
.
toCheck <- | ||
lift $ | ||
filterOutKnownConstraints | ||
(pat.constraints <> (Set.fromList . asEquations $ pat.substitution)) | ||
ruleRequires |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit) maybe worth giving this expression a name:
toCheck <- | |
lift $ | |
filterOutKnownConstraints | |
(pat.constraints <> (Set.fromList . asEquations $ pat.substitution)) | |
ruleRequires | |
let knownConstraints = pat.constraints <> Set.fromList (asEquations pat.substitution) | |
toCheck <- lift $ filterOutKnownConstraints knownConstraints ruleRequires |
and onwards below just inserting knownConstraints
for pat.constraints
in the old code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!
-- if a new constraint is going to be added, the equation cache is invalid | ||
unless (null newConstraints) $ do | ||
withContextFor Equations . logMessage $ | ||
("New path condition ensured, invalidating cache" :: Text) | ||
|
||
lift . RewriteT . lift . modify $ \s -> s{equations = mempty} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Why) Is this not true any more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a similar piece of code in applyRule
, just after the call to checkEnsures
. On master we are invalidating the cache twice: at the end of checkEnsures
(removed here) and right after the call to checkEnsures
in applyRule
.
modifyVariables :: (Variable -> Variable) -> Pattern -> Pattern | ||
modifyVariables f p = | ||
Pattern | ||
{ term = modifyVariablesInT f p.term | ||
, constraints = Set.map (modifyVariablesInP f) p.constraints | ||
, substitution = Map.map (coerce $ modifyVariablesInT f) p.substitution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is currently unused but we should either delete it or also modify the Map
keys (variables).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed, thanks!
-- substitution mining | ||
extractSubsitution, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-- substitution mining | |
extractSubsitution, | |
-- substitution mining | |
extractSubstitution, |
😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦
extractSubsitution :: | ||
[Internal.Predicate] -> (Map Internal.Variable Internal.Term, Set Internal.Predicate) | ||
extractSubsitution ps = | ||
let (potentialSubstituion, otherPreds) = partitionSubstitutionPreds . map mbSubstitution . coerce $ ps | ||
(newSubstitution, leftoverPreds) = mkSubstitution potentialSubstituion | ||
in (newSubstitution, Set.fromList $ leftoverPreds <> map unsafeFromBoolPred otherPreds) | ||
where | ||
partitionSubstitutionPreds = foldl' accumulate ([], []) | ||
where | ||
accumulate (accSubst, accOther) = \case | ||
pSubst@SubstitutionPred{} -> (pSubst : accSubst, accOther) | ||
pOther -> (accSubst, pOther : accOther) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe simpler to just use partition
and a helper that extracts the Term
s from BoolPred
immediately (avoids unsafeFromBoolPred
below)
extractSubsitution :: | |
[Internal.Predicate] -> (Map Internal.Variable Internal.Term, Set Internal.Predicate) | |
extractSubsitution ps = | |
let (potentialSubstituion, otherPreds) = partitionSubstitutionPreds . map mbSubstitution . coerce $ ps | |
(newSubstitution, leftoverPreds) = mkSubstitution potentialSubstituion | |
in (newSubstitution, Set.fromList $ leftoverPreds <> map unsafeFromBoolPred otherPreds) | |
where | |
partitionSubstitutionPreds = foldl' accumulate ([], []) | |
where | |
accumulate (accSubst, accOther) = \case | |
pSubst@SubstitutionPred{} -> (pSubst : accSubst, accOther) | |
pOther -> (accSubst, pOther : accOther) | |
extractSubstitution :: | |
[Internal.Predicate] -> (Map Internal.Variable Internal.Term, Set Internal.Predicate) | |
extractSubstitution ps = | |
let (potentialSubstitution, otherPreds) = partitionSubstitutionPreds . map mbSubstitution . coerce $ ps | |
(newSubstitution, leftoverPreds) = mkSubstitution potentialSubstitution | |
in (newSubstitution, Set.fromList $ leftoverPreds <> otherPreds) | |
where | |
partitionSubstitutionPreds = partitionEithers . unpackBoolPred | |
where | |
unpackBoolPred = \case | |
pSubst@SubstitutionPred{} -> Left pSubst | |
BoolPred p -> Right p | |
other -> error $ "extractSubstitution: unsupported predicate " <> show other |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, done!
|
||
-- if a new constraint is going to be added, the equation cache is invalid | ||
unless (null newConstraints) $ do | ||
unless (null ensuredConditions) $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We invalidate the cache here if a new constraint has been ensured.
Fixes #4059 This PR improves Booster's handling of substitutions. ## Summary Substitutions are special predicates: equations where the LHS is a variable, for example `VAR ==Int TERM`. See `Booster.Syntax.Json.Internalise.mbSubstitution` for the exact specification of what is considered to be a substitution. This PR changes the `Pattern` type to explicitly carry these special substitution predicates: ```diff +type Substitution = Map Variable Term + -- | A term (configuration) constrained by a number of predicates. data Pattern = Pattern { term :: Term , constraints :: !(Set Predicate) + , substitution :: Substitution , ceilConditions :: ![Ceil] } deriving stock (Eq, Ord, Show, Generic, Data) ``` Substitution may appear out of three places: - sent in the request body - ensured as a post condition of a rewrite rule - **NOT IMPLEMENTED** learned from the branching condition --- this is something that will be added as part of #4058 The first source is handled by the pattern internalisation code. The second and third sources are different, as the pattern has already been internalised. All this sources can also produce generic (i.e. non-substitution) constrains that should be added into the `constrains` set of a `Pattern`. Substitutions, when produces, should be applied to the `term` of the `Pattern` and added to the `substitution` field. This PR makes sure we use the same code path for separating substitutions from generic predicates everywhere. We use `Booster.Syntax.Json.Internalise.mbSubstitution.mkSubstitution` to take care of cyclic, which are broken up, and duplicate bindings, which are treated as constraints. With these changes, we can harmonize many (but not all) specialisations of the integrations test responses, i.e. we do not need many of the `*.booster-dev` and `*.kore-rpc-dev` response files. ## Changes to pattern simplification code As the `Pattern` type has changed, we must adapt the `ApplyEquations.evaluatePattern` and `ApplyEquations.evaluatePattern'` functions to: - consider `substitutions` as known truth together with all other constraints (that's what we did before) - simplify the substitution We achieve that by doing the following: - convert the substitution into equalities and assume it as know truth - when constructing the new, simplified pattern, use the same code as when internalising a pattern to partition predicates into the substitution and non-substitution ones ## Changes to rewrite rule application code The `Pattern.Rewrite.applyRule` function has been changed to: - consider `substiontion` as known truth together with all other constraints (that's what we did before) when checking requires/ensures - extract the substitution from the ensured conditions and add it to the rewritten pattern --------- Co-authored-by: github-actions <[email protected]>
Fixes #4059
This PR improves Booster's handling of substitutions.
Summary
Substitutions are special predicates: equations where the LHS is a variable, for example
VAR ==Int TERM
. SeeBooster.Syntax.Json.Internalise.mbSubstitution
for the exact specification of what is considered to be a substitution.This PR changes the
Pattern
type to explicitly carry these special substitution predicates:Substitution may appear out of three places:
The first source is handled by the pattern internalisation code. The second and third sources are different, as the pattern has already been internalised. All this sources can also produce generic (i.e. non-substitution) constrains that should be added into the
constrains
set of aPattern
. Substitutions, when produces, should be applied to theterm
of thePattern
and added to thesubstitution
field. This PR makes sure we use the same code path for separating substitutions from generic predicates everywhere. We useBooster.Syntax.Json.Internalise.mbSubstitution.mkSubstitution
to take care of cyclic, which are broken up, and duplicate bindings, which are treated as constraints.With these changes, we can harmonize many (but not all) specialisations of the integrations test responses, i.e. we do not need many of the
*.booster-dev
and*.kore-rpc-dev
response files.Changes to pattern simplification code
As the
Pattern
type has changed, we must adapt theApplyEquations.evaluatePattern
andApplyEquations.evaluatePattern'
functions to:substitutions
as known truth together with all other constraints (that's what we did before)We achieve that by doing the following:
Changes to rewrite rule application code
The
Pattern.Rewrite.applyRule
function has been changed to:substiontion
as known truth together with all other constraints (that's what we did before) when checking requires/ensures