Ho osservato di recente diversi comportamenti in Z3 rispetto al trigger, che non capisco. Sfortunatamente, gli esempi provengono da grandi file Boogie, quindi ho pensato di descriverli in astratto per ora, solo per vedere se ci sono risposte ovvie. Tuttavia, se i file concreti fossero migliori, posso allegarli.Problemi di trigger in Z3
Ci sono fondamentalmente tre problemi, anche se il terzo potrebbe essere un caso speciale del secondo. Per quanto riguarda la mia comprensione, nessuno dei comportamenti è previsto, ma forse mi manca qualcosa. Qualsiasi aiuto sarebbe molto apprezzato!
In primo luogo: uguaglianze Trivial nel mio programma sembrano avere ignorato per quanto scatenante è interessato. Ad esempio, se t1
è un termine che dovrebbe corrispondere al modello per un assioma quantificata, se posso aggiungere una riga nel mio programma Boogie della forma
assert t1 == t1;
poi t1
fa non sembrano abituarsi come trigger per assiomi quantificati. Ho aggiunto esplicitamente la riga per fornire t1
come trigger al prover, cosa che faccio spesso/faccio nei programmi Boogie.
Se invece vi presento una funzione extra, dicono
function f(x : same_type_as_t1) : bool
{ true }
e ora invece aggiungere una riga
assert f(t1);
al mio programma, quindi t1
fa sembrano essere usato come un trigger Z3. Ho controllato la traduzione Z3 del programma precedente e l'uguaglianza (banale) su t1
è sopravvissuta alla traduzione di Boogie (ad esempio, non è Boogie che cerca di fare qualcosa di intelligente).
In secondo luogo: schemi secondari non sembrano funzionare correttamente per me. Ad esempio, ho un programma in cui uno assioma ha la forma
axiom (forall ... :: {t1,t2} {t3,t4,t5} .....);
e una situazione in cui tutti sono verificati t3, t4
e t5
. Il programma non riesce a verificare, apparentemente perché l'assioma non è istanziato. Tuttavia, se riscrivo l'assioma come
axiom (forall ... :: {t3,t4,t5} {t1,t2} .....);
quindi il programma verifica. In entrambi i casi, il tempo di eseguire Boogie è di circa 3 secondi e i pattern sopravvivono all'output Z3.
In terzo luogo: questo potrebbe essere un sintomo del secondo problema, ma sono rimasto sorpreso dal seguente comportamento:
ho scritto assiomi della forma
axiom (forall .. :: {t1,t2} ....);
axiom (forall ... :: {t2,t4} {t2,t3} ...some expression involving t1...);
e in un situazione in cui si era verificato t2
e t3
, il primo assioma non è stato istanziato (mi aspettavo che fosse, poiché dopo l'istanziazione del secondo assioma, si verifica t1
).Tuttavia, se ho riscritto come
axiom (forall .. :: {t2,t3} {t1,t2} ....);
axiom (forall ... :: {t2,t4} {t2,t3} ...some expression involving t1...);
quindi il primo assioma è stato istanziato. Tuttavia, se per qualche motivo gli schemi secondari non vengono utilizzati in generale, ciò spiegherebbe anche questo comportamento.
Se esempi espliciti sarebbero utili, posso certamente attaccare quelli lunghi e provare a ridurli un po '(ma ovviamente i problemi di avvio sono un po' delicati, quindi potrei perdere il comportamento se faccio l'esempio troppo piccolo).
Grazie mille in anticipo per qualsiasi consiglio!
Alex Summers
Edit: qui è un esempio che illustra parzialmente il secondo e terzo comportamenti sopra descritti. Ho allegato il codice Boogie per renderlo più facile da leggere qui, ma posso anche copiare o inviare tramite e-mail l'input Z3 se fosse più utile. Ho tagliato quasi tutto il codice originale di Boogie, ma sembra difficile renderlo più semplice senza perdere completamente il comportamento.
Già il codice qui sotto si comporta in modo leggermente diverso dal mio esempio originale, ma penso che sia abbastanza vicino. Fondamentalmente l'assioma etichettato (1) di seguito non riesce a far corrispondere il suo secondo set di schemi. Se commentassi axiom (1), e invece lo sostituissi con gli assiomi (attualmente commentati) (2) e (3), che sono solo copie dell'originale con ciascuno dei due insiemi di modelli, allora il programma verifica. In realtà, è sufficiente avere assioma (2) in questo caso particolare. Nel mio codice originale (prima che lo riducessi) era anche sufficiente capovolgere l'ordine dei due insiemi di modelli nell'assioma (1), ma ciò non sembra più il caso nella mia versione più piccola.
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)));
// (2)
// axiom (forall Heap: HeapType, this: ref :: {trigger1(this), heap_trigger(Heap), trigger2(this)} (vals1(Heap, this) == vals2(Heap, this)));
// (3)
// axiom (forall Heap: HeapType, this: ref :: {vals1(Heap, this)} (vals1(Heap, this) == vals2(Heap, this)));
procedure test(Heap:HeapType, this:ref)
{
assume trigger1(this); assume heap_trigger(Heap);
assert (vals2(Heap, this) == vals3(Heap,this));
}
Caro Leonardo, Grazie mille per la tua risposta veloce. È molto utile sapere - sono sicuro che lo sai, è molto difficile cercare di tracciare i problemi di innesco attraverso grandi esempi. È possibile configurare il comportamento che descrivi? Per quanto ne sapevo, solo menzionare un termine era un criterio sufficiente per far corrispondere un pattern, e quindi avevamo (erroneamente) assunto che un particolare problema di codifica non fosse dovuto al trigger. In realtà, ho letto il tuo post su http://stackoverflow.com/questions/10949633/z3-real-arithmetic-and-statistics che sono completamente d'accordo - preferirei davvero non perdere i trigger :) –
Mi chiedo, poiché (come suggerito sopra) nessuno dei modi di introdurre manualmente termini extra per il solo scopo di attivare più pattern è garantito che funzioni per sempre, sarebbe possibile avere una sintassi esplicita per fare proprio questo? cioè, potremmo avere un modo di aggiungere un termine candidato all'e-graph matching, senza bisogno di dargli un significato logico particolare nel problema originale. Anche se Z3 non cambia mai il suo comportamento di semplificazione, sarebbe più semplice che introdurre funzioni extra (specialmente in un linguaggio con tipi, come Boogie), e forse anche un po 'più leggibile. –
p.s. repro aggiunto sopra in modifica. –