(One intermediate revision by the same user not shown) | |||
Line 29: | Line 29: | ||
One exception to the above treatment are existential quantifiers of the form <tt>#x.(x=E & P)</tt>. They are recognised by ProB as LET-PREDICATES. This is a good use of the existential quantifier. This quantifier will never "block". | One exception to the above treatment are existential quantifiers of the form <tt>#x.(x=E & P)</tt>. They are recognised by ProB as LET-PREDICATES. This is a good use of the existential quantifier. This quantifier will never "block". | ||
* Tip: use existential quantifiers as LET-PREDICATES <tt>#x.(x=E & P)</tt> to avoid repeated computations (common- | * Tip: use existential quantifiers as LET-PREDICATES <tt>#x.(x=E & P)</tt> to avoid repeated computations ([[Common_Subexpression_Elimination|common-subexpression elimination]]) and to make your predicates more readable | ||
== Universal Quantifiers == | |||
The situation is very similar to the existential quantifier. | |||
In the worst case ProB may delay evaluating a universal quantifier <tt>!x.(P=>Q)</tt> until all variables in <tt>P</tt> are known so as to be able to determine all values of <tt>x</tt> for which <tt>Q</tt> has to be checked. | |||
There are a few optimisations: | |||
* if ProB can determine that the domain of <tt>x</tt> is finite and small, then the universal quantifier will be expanded into a conjunction. E.g., <tt>!x.(x:1..3 & x<y => P(x)</tt> will be expanded into <tt>(1<y => P(1)) & (2<y => P(2)) & (3<y => P(3))</tt> even if <tt>y</tt> is not yet known. Setting the preference <tt>SMT</tt> to true will increase the threshold for which this expansion occurs. | |||
* if the quantifier is of the form <tt>!x.(x:S => P)</tt>, then P will be gradually checked for every new value that is added to <tt>S</tt>; ProB does not wait for <tt>S</tt> to be completely known before checking the quantifier. | |||
Tip: sometimes one can force expansion of a quantifier by using two implications. | |||
E.g., suppose we know that the domain of <tt>s</tt> is a subset of <tt>1..10</tt>, then we can rewrite <tt>!x.(s(x)=5 => P(x)</tt> into <tt>!x.(x:1..10 => (s(x)=5 => P(x))</tt>. This will ensure that the quantifier is checked | |||
== Transitive Closure in Event-B == | == Transitive Closure in Event-B == |
The most common issue is that ProB needs to find values for the constants which satisfy the properties (aka axioms in Event-B). You should read the tutorial pages on this (in particular Understanding the ProB Setup Phases and Tutorial Troubleshooting the Setup)
Existential quantifiers can pose subtle problems when solving constraint problems.
For an existential quantifier #x.P ProB will often wait until all variables in P apart from x are known to evaluate the quantifier. Indeed, if all variables apart from x are known, ProB can stop when it finds one solution for x. Take for example:
#x.(x:0..1000 & x=p) & p:101..104
Here, ProB will wait until p is known (e.g., 101) before enumerating possible values for x. However, it could be that the predicate P is required to instantiate the outside variable, as in this example:
#x.(x:100..101 & x=p) & p:NATURAL
Here, the existential quantifier is required to narrow down the possible values of p. Thus, before enumerating an unbounded variable, ProB will start enumerating the existential variable x. Note, however, that the priority with which it will be enumerated is much lower than if it was a regular variable! Hence:
One exception to the above treatment are existential quantifiers of the form #x.(x=E & P). They are recognised by ProB as LET-PREDICATES. This is a good use of the existential quantifier. This quantifier will never "block".
The situation is very similar to the existential quantifier. In the worst case ProB may delay evaluating a universal quantifier !x.(P=>Q) until all variables in P are known so as to be able to determine all values of x for which Q has to be checked.
There are a few optimisations:
Tip: sometimes one can force expansion of a quantifier by using two implications. E.g., suppose we know that the domain of s is a subset of 1..10, then we can rewrite !x.(s(x)=5 => P(x) into !x.(x:1..10 => (s(x)=5 => P(x)). This will ensure that the quantifier is checked
Classical B contains the transitive closure operator closure1. It is not available by default in Event-B, and axiomatisations of it may be very difficult to treat by ProB. Indeed, if you define the transitive closure in Event-B as a function tclos from relations to relations, ProB will try to find a value for tclos. The search space for this function is (2^n*n)^(2^n*n), where n is the size of the base set (see Tutorial Understanding the Complexity of B Animation). For n=3 this is already way too big too handle (the search space has 1.40e+1387 relations).
Hence, in Event-B, you should use a theory of the transitive closure which contains a special mapping file which instructs ProB to use the classical B operator. See the page on supporting Event-B theories along with the links to theories that can be used efficiently with ProB.