Closed niklasdewally closed 6 days ago
This PR: Detailed Report
lines......: 72.8% (3522 of 4839 lines)
functions..: 59.1% (367 of 621 functions)
branches...: no data found
Main: Detailed Report
lines......: 72.5% (3455 of 4764 lines)
functions..: 58.8% (359 of 611 functions)
branches...: no data found
Lines coverage changed by 0.30% and covered lines changed by 67
Functions coverage changed by 0.30% and covered lines changed by 8
Branches... coverage: No comparison data available
@ozgurakgun ok to merge?
Ready to merge version of #450.
Add generic Minion flattening rules, and implement flattening for division.
These rules will be key to converting nested expressions to auxiliary variables for use with Minion. This PR only implements them for division, but they will apply trivially to any binary or vector operator.
Unlike previous attempts, flattening is now encoded in the type system (as enabled by f931a3d2b407c5f295e7eab181d53bd26a32c6aa). In this patch DivEq has the type
DivEq(Metadata,Factor,Factor,Factor)
.The following related bug-fixes were necessary to implement this:
Fix the rewriter not updating the
next_var
counter, breakingmodel.gensym()
.Previously, we updated the model by extending the symbol table with the one returned by the rule application. This did not update the
gensym()
counter. This patch adds a method,model.extend_sym_table
, to reconcile thegensym
counter with machine names added by rules.Make expressions bubble inside-out, and forbid making a bubble inside a bubble. This is a non-functional change, but the hope is that this will make execution order more predictable, and reduce the number of steps needed to rewrite bubbled expressions. See #450.
Fix domain of SafeDiv expressions to include 0. This is what I think the big bug was.
Do not allow putting an unsafe expression in an aux var. Unsafe expressions need to be bubbled up to the nearest boolean context. Putting them in an auxiliary variable moves them out of their boolean context to the top level of the model. I am unsure whether this was causing issues or not, but this seems like the right thing to do.
See #450.
Flattening approach
Flattening is the conversion between nested expressions to non nestedexpressions through the creation of auxiliary variables. For example:
(y/(x/z)) ~> y/a where a = x/z
Most solvers, including Minion, only support flattened constraints.
Unlike previous flattening code, here I choose to flatten expressions "inside out". This choice allows us to assume that
diveq
constraints are always flat.Outside-in flattening looks like so:
y/(x/z) = a ~> diveq(y,(x/z),a) ~> diveq(y,b,a) , b = x/z
In the first line,
diveq
is non flat, which is a semantically invalid model. Flattening inside-out ensures that our model stays semantically valid after the application of every rule.This means that we apply the rules that turn expressions into their "constraint" form when their arguments are flat.
This evaluation order also keeps things as solver independent arithmetic expressions (e.g. safediv) rather than lower-level constraints (e.g. diveq) for longer. This will allow solver-independent rules and optimisations, which use arithmetic expressions, to be applicable for longer.
I also, arbitrarily, choose to flatten after undefinedness and bubbles have been resolved: i.e. I flatten SafeDivs but not UnsafeDivs.
Flattening Rules
My rules for flattening are generic and will be applicable to all arithmetic binary operators (
flatten_binop
) and arithmetic vector operators (flatten_vecop
).To flatten an operator, it will need to be added to the above rules' allow lists and will need a rule to turn it into a lower-level flat constraint. In the case of division, this is
introduce_diveq
.A special case is flattening equalities. We only need to flatten an equality if both sides are not flat. Note that this rule applies for equality only and we currently treat
!=
as a standard binary operator.See also
450 for more details on changes to bubbling made in order to fix
div_ undefzero-05-nested-noteq
.