It has been recently brought to my attention that some recent version of FlatZinc
supports half-reified predicates:
Half-reified predicates essentially represent constraints that are implied by a Boolean variable rather than being equivalent to one.
source: docs
Q:
- do all global predicates have a
_reif
version now? - do all global predicates have a
_imp
version too?
Looking at the content of share/minizinc/
I can see that inside std
only nosets.mzn
uses _imp
predicates, so my impression is that it is not yet mandatory to support _imp
predicates. Q: Is this correct?
Looking at the docs, I found this:
Solver libraries should therefore provide reified versions of constraints whenever possible. The library contains files
fzn__reif.mzn
for this purpose.source: docs
If I interpret it correctly, it is not necessary to support _reif
predicates because they are already redefined in share/minizinc
(although it can be beneficial for performance). Q: Is this correct?
Most often predicates (calls),
R(...)
, are used as an actual constraint:constraint R(...)
. The question when processing the expressionR(...)
is now if it can be given directly to the solver or if it has a redefinition that needs to be evaluated.If the predicate is used in a more complex expression
B \/ R(...)
, then we cannot just give the solver the callR(...)
because we don't want to enforceR(...)
. Instead we want to know if the relationship described byR(...)
holds. This is what we call a reification.For this reason, there must be a
R_reif(..., r)
predicate that forcesr <-> R(...)
. Essentially this give the truth value ofR(...)
. Again, when the compiler is given the expressionR_reif(...,r)
, then it will see if it can be given directly to the solver or if there is a redefinition available. If both of these are unavailable, then the compiler will try to generate a redefinition from the redefinition ofR(...)
. If this also fails, then the compiler will stop the compilation.If we look back at our example,
B \/ R(...)
, then one might note that usingr <-> R(...)
is more than what is necessary. IfR(...)
is false, then we still needB
to betrue
and ifB
isfalse
we still want to forceR(...)
; however, we know that nothing will forceR(...)
to becomefalse
. We sayR(...)
is in a positive context. In this case that means that havingr -> R(...)
is enough. This is referred to as a half-reification.In MiniZinc it is possible to introduce a
R_imp(..., r)
predicate to provide a separate half-reified predicate. This can again be something that is directly passed into the solver or a redefinition. If the compiler has to reify a expressionR(...)
and sees that it is in a positive context, then it will try first to see if it can findR_imp(..., r)
and otherwise fall-back toR_reif(..., r)
. Note that if a redefinition is generated, then the context of the call propagates inward and half reifications can still occur in the redefinition.To summarise and answer your questions directly:
_reif
can be defined explicitly or can be generated through the generation from their redefinition._reif
predicate that can be implemented as_imp
by the solver (including FlatZinc builtins)._reif
predicates; however, there are several required_reif
predicates in the FlatZinc builtins that do need support. If these predicates are not redefined or implemented by the solver, then the solver cannot support the generated FlatZinc.