Recently, I noticed several behaviors in Z3 with respect to launch that I don’t understand. Unfortunately, the examples are taken from large Boogie files, so I decided to describe them abstractly at the moment, just to see if there are obvious answers. However, if specific files are better, I can attach them.
Basically there are three questions, although the third may be a special case of the second. As far as I understand, none of the behavior is expected, but maybe I missed something. Any help would be greatly appreciated!
First of all . The trivial equalities in my program seem to be ignored in relation to startup. For example, if t1
is a term that should match the template for a quantitative axiom, if I add a line to my Boogie program of the form
assert t1 == t1;
then t1
does not seem to be used as a trigger for quantified axioms. I added the line explicitly to provide t1
as a trigger for checking, which I often do / do in Boogie programs.
If instead I add an extra function, let's say
function f(x : same_type_as_t1) : bool { true }
and now instead add the line
assert f(t1);
for my program, then t1
seems to be used as a trigger on Z3. I checked the Z3 translation of the previous program, and the (trivial) equality on t1
withstood Boogie's translation (i.e.Not Boogie is trying to do something smart).
Second: Secondary patterns do not seem to work properly for me. For example, I have a program in which one axiom has the form
axiom (forall ... :: {t1,t2} {t3,t4,t5} ..... );
and a situation in which t3, t4
and t5
all happened. The program does not check, apparently, because the axiom is not created. However, if I rewrite the axiom as
axiom (forall ... :: {t3,t4,t5} {t1,t2} ..... );
then the program checks. In both cases, Boogie's startup time is approximately 3 seconds, and the patterns survive until the release of Z3.
Third: This may be a symptom of a second problem, but I was surprised by the following behavior:
I wrote axioms of form
axiom (forall .. :: {t1,t2} .... ); axiom (forall ... :: {t2,t4} {t2,t3} ...some expression involving t1... );
and in the situation where t2
and t3
took place, the first axiom did not receive an instance (I expected this to happen, since after creating the second axiom t1
occurs). However, if I rewrote as
axiom (forall .. :: {t2,t3} {t1,t2} .... ); axiom (forall ... :: {t2,t4} {t2,t3} ...some expression involving t1... );
then the first axiom was created. However, if for some reason secondary patterns do not get used at all, this also explains this behavior.
If explicit examples would be useful, I can, of course, attach long ones and try to reduce them a bit (but, of course, the launch problems are a bit delicate, so I could lose my behavior if I made a small example too).
Thanks so much for any advice!
Alex Summers
Edit: Here is an example that partially illustrates the second and third behavior described above. I have attached the Boogie code to make it easier to read here, but I can also copy or email the input of Z3 if it is more useful. I cut out almost all of the original Boogie code, but it seems hard to make it simpler without losing all of the behavior.
Already the code below behaves differently compared to my original example, but I think it is close enough. Basically, the axiom labeled (1) below cannot combine its second set of patterns. If I comment on axiom (1) and replace it with axioms (2) and (3), which are only copies of the original with each of the two sets of templates, the program checks. In fact, it is enough to have axiom (2) in this particular case. In my source code (before I turned it off), it was also enough to reverse the order of the two sets of patterns in axiom (1), but this does not seem to be the case in my smaller playback.
type ref; type HeapType; function vals1(HeapType, ref) returns (ref); function vals2(HeapType, ref) returns (ref); function vals3(HeapType, ref) returns (ref); function heap_trigger(HeapType) returns (bool); function trigger1(ref) returns (bool); function trigger2(ref) returns (bool); axiom (forall Heap: HeapType, this: ref :: {vals1(Heap, this)} (vals1(Heap, this) == vals3(Heap,this))); axiom (forall Heap: HeapType, this: ref :: {vals2(Heap, this)} trigger2(this)); // (1) axiom (forall Heap: HeapType, this: ref :: {vals1(Heap, this)} {trigger1(this), heap_trigger(Heap), trigger2(this)} (vals1(Heap, this) == vals2(Heap, this)));