From: Guy Date: Wed, 23 Mar 2016 19:07:59 +0000 (-0700) Subject: squash-merge from proof branch X-Git-Tag: cvc5-1.0.0~6049^2~92^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=aa9aa46b77f048f2865c29e40ed946371fd115ef;p=cvc5.git squash-merge from proof branch --- diff --git a/proofs/signatures/Makefile.am b/proofs/signatures/Makefile.am index 82d8c2caa..2b6d16cfd 100644 --- a/proofs/signatures/Makefile.am +++ b/proofs/signatures/Makefile.am @@ -3,7 +3,7 @@ # add support for more theories, just list them here in the same order # you would to the LFSC proof-checker binary. # -CORE_PLFS = sat.plf smt.plf th_base.plf th_arrays.plf th_bv.plf th_bv_bitblast.plf +CORE_PLFS = sat.plf smt.plf th_base.plf th_arrays.plf th_bv.plf th_bv_bitblast.plf th_real.plf th_int.plf noinst_LTLIBRARIES = libsignatures.la diff --git a/proofs/signatures/example-arrays.plf b/proofs/signatures/example-arrays.plf index 600fc11c9..c454a6dfe 100755 --- a/proofs/signatures/example-arrays.plf +++ b/proofs/signatures/example-arrays.plf @@ -9,7 +9,7 @@ ; input : ; ~L1 -; (extenstionality) lemma : +; (extenstionality) lemma : ; L1 or ~L2 ; theory conflicts : @@ -26,13 +26,13 @@ (check (% I sort (% E sort -(% a (term (array I E)) +(% a (term (Array I E)) (% i (term I) ; (1) -------------------- input formula ----------------------------------- -(% A1 (th_holds (not (= (array I E) a (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i))))) +(% A1 (th_holds (not (= (Array I E) a (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i))))) @@ -57,7 +57,7 @@ ; (4) -------------------- map theory literals to boolean variables ; --- maps all theory literals involved in proof to boolean literals -(decl_atom (= (array I E) a (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i))) (\ v1 (\ a1 +(decl_atom (= (Array I E) a (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i))) (\ v1 (\ a1 (decl_atom (= E (apply _ _ (apply _ _ (read I E) a) k) (apply _ _ (apply _ _ (read I E) (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i))) k)) (\ v2 (\ a2 (decl_atom (= I i k) (\ v3 (\ a3 @@ -73,7 +73,7 @@ ; use read over write rule "row" (contra _ (symm _ _ _ (row _ _ _ _ a (apply _ _ (apply _ _ (read I E) a) i) l3)) l2) - + ))))) (\ CT1 ; CT1 is the clause ( v2 V v3 ) @@ -83,9 +83,9 @@ (clausify_false ; use read over write rule "row1" - (contra _ (symm _ _ _ - (trans _ _ _ _ - (symm _ _ _ (cong _ _ _ _ _ _ + (contra _ (symm _ _ _ + (trans _ _ _ _ + (symm _ _ _ (cong _ _ _ _ _ _ (refl _ (apply _ _ (read I E) (apply _ _ (apply _ _ (apply _ _ (write I E) a) i) (apply _ _ (apply _ _ (read I E) a) i)))) l3)) (trans _ _ _ _ @@ -93,7 +93,7 @@ (cong _ _ _ _ _ _ (refl _ (apply _ _ (read I E) a)) l3) ))) l2) - + ))))) (\ CT2 ; CT2 is the clause ( v2 V ~v3 ) diff --git a/proofs/signatures/th_arrays.plf b/proofs/signatures/th_arrays.plf index 8334f51de..5ed3d2f6f 100755 --- a/proofs/signatures/th_arrays.plf +++ b/proofs/signatures/th_arrays.plf @@ -7,44 +7,56 @@ ; sorts -(declare array (! s1 sort (! s2 sort sort))) ; s1 is index, s2 is element +(declare Array (! s1 sort (! s2 sort sort))) ; s1 is index, s2 is element ; functions (declare write (! s1 sort (! s2 sort - (term (arrow (array s1 s2) + (term (arrow (Array s1 s2) (arrow s1 - (arrow s2 (array s1 s2)))))))) + (arrow s2 (Array s1 s2)))))))) (declare read (! s1 sort (! s2 sort - (term (arrow (array s1 s2) + (term (arrow (Array s1 s2) (arrow s1 s2)))))) ; inference rules + +; read( a[i] = b, i ) == b (declare row1 (! s1 sort (! s2 sort - (! t1 (term (array s1 s2)) + (! t1 (term (Array s1 s2)) (! t2 (term s1) (! t3 (term s2) - (th_holds (= _ (apply _ _ (apply _ _ (read s1 s2) (apply _ _ (apply _ _ (apply _ _ (write s1 s2) t1) t2) t3)) t2) - t3)))))))) - + (th_holds (= _ + (apply _ _ (apply _ _ (read s1 s2) (apply _ _ (apply _ _ (apply _ _ (write s1 s2) t1) t2) t3)) t2) t3)))))))) (declare row (! s1 sort (! s2 sort (! t2 (term s1) (! t3 (term s1) - (! t1 (term (array s1 s2)) + (! t1 (term (Array s1 s2)) (! t4 (term s2) (! u (th_holds (not (= _ t2 t3))) (th_holds (= _ (apply _ _ (apply _ _ (read s1 s2) (apply _ _ (apply _ _ (apply _ _ (write s1 s2) t1) t2) t4)) t3) (apply _ _ (apply _ _ (read s1 s2) t1) t3))))))))))) +(declare negativerow (! s1 sort + (! s2 sort + (! t2 (term s1) + (! t3 (term s1) + (! t1 (term (Array s1 s2)) + (! t4 (term s2) + (! u (th_holds (not (= _ + (apply _ _ (apply _ _ (read s1 s2) (apply _ _ (apply _ _ (apply _ _ (write s1 s2) t1) t2) t4)) t3) + (apply _ _ (apply _ _ (read s1 s2) t1) t3)))) + (th_holds (= _ t2 t3)))))))))) + (declare ext (! s1 sort (! s2 sort - (! t1 (term (array s1 s2)) - (! t2 (term (array s1 s2)) + (! t1 (term (Array s1 s2)) + (! t2 (term (Array s1 s2)) (! u1 (! k (term s1) (! u2 (th_holds (or (= _ t1 t2) (not (= _ (apply _ _ (apply _ _ (read s1 s2) t1) k) (apply _ _ (apply _ _ (read s1 s2) t2) k))))) (holds cln))) diff --git a/proofs/signatures/th_base.plf b/proofs/signatures/th_base.plf index 9dd950b5b..d6b283752 100755 --- a/proofs/signatures/th_base.plf +++ b/proofs/signatures/th_base.plf @@ -18,8 +18,8 @@ (! t1 (term (arrow s1 s2)) (! t2 (term s1) (term s2)))))) - - + + ; inference rules : (declare trust (th_holds false)) ; temporary @@ -44,6 +44,28 @@ (! u (th_holds (= _ y z)) (th_holds (= _ x z))))))))) +(declare negsymm (! s sort + (! x (term s) + (! y (term s) + (! u (th_holds (not (= _ x y))) + (th_holds (not (= _ y x)))))))) + +(declare negtrans1 (! s sort + (! x (term s) + (! y (term s) + (! z (term s) + (! u (th_holds (not (= _ x y))) + (! u (th_holds (= _ y z)) + (th_holds (not (= _ x z)))))))))) + +(declare negtrans2 (! s sort + (! x (term s) + (! y (term s) + (! z (term s) + (! u (th_holds (= _ x y)) + (! u (th_holds (not (= _ y z))) + (th_holds (not (= _ x z)))))))))) + (declare cong (! s1 sort (! s2 sort (! a1 (term (arrow s1 s2)) diff --git a/proofs/signatures/th_int.plf b/proofs/signatures/th_int.plf new file mode 100644 index 000000000..9a0a2d63c --- /dev/null +++ b/proofs/signatures/th_int.plf @@ -0,0 +1,25 @@ +(declare Int sort) + +(define arithpred_Int (! x (term Int) + (! y (term Int) + formula))) + +(declare >_Int arithpred_Int) +(declare >=_Int arithpred_Int) +(declare <_Int arithpred_Int) +(declare <=_Int arithpred_Int) + +(define arithterm_Int (! x (term Int) + (! y (term Int) + (term Int)))) + +(declare +_Int arithterm_Int) +(declare -_Int arithterm_Int) +(declare *_Int arithterm_Int) ; is * ok to use? +(declare /_Int arithterm_Int) ; is / ok to use? + +; a constant term +(declare a_int (! x mpz (term Int))) + +; unary negation +(declare u-_Int (! t (term Int) (term Int))) diff --git a/proofs/signatures/th_lra.plf b/proofs/signatures/th_lra.plf new file mode 100644 index 000000000..88118e8d1 --- /dev/null +++ b/proofs/signatures/th_lra.plf @@ -0,0 +1,451 @@ +; 59 loc in side conditions + +(program mpq_ifpos ((x mpq)) bool + (mp_ifneg x ff (mp_ifzero x ff tt))) + +; a real variable +(declare var_real type) +; a real variable term +(declare a_var_real (! v var_real (term Real))) + +;; linear polynomials in the form a_1*x_1 + a_2*x_2 .... + a_n*x_n + +(declare lmon type) +(declare lmonn lmon) +(declare lmonc (! c mpq (! v var_real (! l lmon lmon)))) + +(program lmon_neg ((l lmon)) lmon + (match l + (lmonn l) + ((lmonc c' v' l') (lmonc (mp_neg c') v' (lmon_neg l'))))) + +(program lmon_add ((l1 lmon) (l2 lmon)) lmon + (match l1 + (lmonn l2) + ((lmonc c' v' l') + (match l2 + (lmonn l1) + ((lmonc c'' v'' l'') + (compare v' v'' + (lmonc c' v' (lmon_add l' l2)) + (lmonc c'' v'' (lmon_add l1 l'')))))))) + +(program lmon_mul_c ((l lmon) (c mpq)) lmon + (match l + (lmonn l) + ((lmonc c' v' l') (lmonc (mp_mul c c') v' (lmon_mul_c l' c))))) + +;; linear polynomials in the form (a_1*x_1 + a_2*x_2 .... + a_n*x_n) + c + +(declare poly type) +(declare polyc (! c mpq (! l lmon poly))) + +(program poly_neg ((p poly)) poly + (match p + ((polyc m' p') (polyc (mp_neg m') (lmon_neg p'))))) + +(program poly_add ((p1 poly) (p2 poly)) poly + (match p1 + ((polyc c1 l1) + (match p2 + ((polyc c2 l2) (polyc (mp_add c1 c2) (lmon_add l1 l2))))))) + +(program poly_sub ((p1 poly) (p2 poly)) poly + (poly_add p1 (poly_neg p2))) + +(program poly_mul_c ((p poly) (c mpq)) poly + (match p + ((polyc c' l') (polyc (mp_mul c' c) (lmon_mul_c l' c))))) + +;; code to isolate a variable from a term +;; if (isolate v l) returns (c,l'), this means l = c*v + l', where v is not in FV(t'). + +(declare isol type) +(declare isolc (! r mpq (! l lmon isol))) + +(program isolate_h ((v var_real) (l lmon) (e bool)) isol + (match l + (lmonn (isolc 0/1 l)) + ((lmonc c' v' l') + (ifmarked v' + (match (isolate_h v l' tt) + ((isolc ci li) (isolc (mp_add c' ci) li))) + (match e + (tt (isolc 0/1 l)) + (ff (match (isolate_h v l' ff) + ((isolc ci li) (isolc ci (lmonc c' v' li)))))))))) + +(program isolate ((v var_real) (l lmon)) isol + (do (markvar v) + (let i (isolate_h v l ff) + (do (markvar v) i)))) + +;; determine if a monomial list is constant + +(program is_lmon_zero ((l lmon)) bool + (match l + (lmonn tt) + ((lmonc c v l') + (match (isolate v l) + ((isolc ci li) + (mp_ifzero ci (is_lmon_zero li) ff)))))) + +;; return the constant that p is equal to. If p is not constant, fail. + +(program is_poly_const ((p poly)) mpq + (match p + ((polyc c' l') + (match (is_lmon_zero l') + (tt c') + (ff (fail mpq)))))) + +;; conversion to use polynomials in term formulas + +(declare poly_term (! p poly (term Real))) + +;; create new equality out of inequality + +(declare lra_>=_>=_to_= + (! p1 poly + (! p2 poly + (! f1 (th_holds (>=0_Real (poly_term p1))) + (! f2 (th_holds (>=0_Real (poly_term p2))) + (! i2 (^ (mp_ifzero (is_poly_const (poly_add p1 p2)) tt ff) tt) + (th_holds (=0_Real (poly_term p2)))))))))) + +;; axioms + +(declare lra_axiom_= + (th_holds (=0_Real (poly_term (polyc 0/1 lmonn))))) + +(declare lra_axiom_> + (! c mpq + (! i (^ (mpq_ifpos c) tt) + (th_holds (>0_Real (poly_term (polyc c lmonn))))))) + +(declare lra_axiom_>= + (! c mpq + (! i (^ (mp_ifneg c tt ff) ff) + (th_holds (>=0_Real (poly_term (polyc c lmonn))))))) + +(declare lra_axiom_distinct + (! c mpq + (! i (^ (mp_ifzero c tt ff) ff) + (th_holds (distinct0_Real (poly_term (polyc c lmonn))))))) + +;; contradiction rules + +(declare lra_contra_= + (! p poly + (! f (th_holds (=0_Real (poly_term p))) + (! i (^ (mp_ifzero (is_poly_const p) tt ff) ff) + (holds cln))))) + +(declare lra_contra_> + (! p poly + (! f (th_holds (>0_Real (poly_term p))) + (! i2 (^ (mpq_ifpos (is_poly_const p)) ff) + (holds cln))))) + +(declare lra_contra_>= + (! p poly + (! f (th_holds (>=0_Real (poly_term p))) + (! i2 (^ (mp_ifneg (is_poly_const p) tt ff) tt) + (holds cln))))) + +(declare lra_contra_distinct + (! p poly + (! f (th_holds (distinct0_Real (poly_term p))) + (! i2 (^ (mp_ifzero (is_poly_const p) tt ff) tt) + (holds cln))))) + +;; muliplication by a constant + +(declare lra_mul_c_= + (! p poly + (! p' poly + (! c mpq + (! f (th_holds (=0_Real (poly_term p))) + (! i (^ (poly_mul_c p c) p') + (th_holds (=0_Real (poly_term p'))))))))) + +(declare lra_mul_c_> + (! p poly + (! p' poly + (! c mpq + (! f (th_holds (>0_Real (poly_term p))) + (! i (^ (mp_ifneg c (fail poly) (mp_ifzero c (fail poly) (poly_mul_c p c))) p') + (th_holds (>0_Real (poly_term p')))))))));) + +(declare lra_mul_c_>= + (! p poly + (! p' poly + (! c mpq + (! f (th_holds (>=0_Real (poly_term p))) + (! i (^ (mp_ifneg c (fail poly) (poly_mul_c p c)) p') + (th_holds (>=0_Real (poly_term p')))))))));) + +(declare lra_mul_c_distinct + (! p poly + (! p' poly + (! c mpq + (! f (th_holds (distinct0_Real (poly_term p))) + (! i (^ (mp_ifzero c (fail poly) (poly_mul_c p c)) p') + (th_holds (distinct0_Real (poly_term p')))))))));) + +;; adding equations + +(declare lra_add_=_= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (=0_Real (poly_term p1))) + (! f2 (th_holds (=0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (=0_Real (poly_term p3))))))))))) + +(declare lra_add_>_> + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>0_Real (poly_term p1))) + (! f2 (th_holds (>0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>0_Real (poly_term p3)))))))))) + +(declare lra_add_>=_>= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>=0_Real (poly_term p1))) + (! f2 (th_holds (>=0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>=0_Real (poly_term p3)))))))))) + +(declare lra_add_=_> + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (=0_Real (poly_term p1))) + (! f2 (th_holds (>0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>0_Real (poly_term p3)))))))))) + +(declare lra_add_=_>= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (=0_Real (poly_term p1))) + (! f2 (th_holds (>=0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>=0_Real (poly_term p3)))))))))) + +(declare lra_add_>_>= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>0_Real (poly_term p1))) + (! f2 (th_holds (>=0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (>0_Real (poly_term p3)))))))))) + +(declare lra_add_=_distinct + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (=0_Real (poly_term p1))) + (! f2 (th_holds (distinct0_Real (poly_term p2))) + (! i (^ (poly_add p1 p2) p3) + (th_holds (distinct0_Real (poly_term p3))))))))))) + +;; substracting equations + +(declare lra_sub_=_= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (=0_Real (poly_term p1))) + (! f2 (th_holds (=0_Real (poly_term p2))) + (! i (^ (poly_sub p1 p2) p3) + (th_holds (=0_Real (poly_term p3))))))))))) + +(declare lra_sub_>_= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>0_Real (poly_term p1))) + (! f2 (th_holds (=0_Real (poly_term p2))) + (! i (^ (poly_sub p1 p2) p3) + (th_holds (>0_Real (poly_term p3)))))))))) + +(declare lra_sub_>=_= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (>=0_Real (poly_term p1))) + (! f2 (th_holds (=0_Real (poly_term p2))) + (! i (^ (poly_sub p1 p2) p3) + (th_holds (>=0_Real (poly_term p3)))))))))) + +(declare lra_sub_distinct_= + (! p1 poly + (! p2 poly + (! p3 poly + (! f1 (th_holds (distinct0_Real (poly_term p1))) + (! f2 (th_holds (=0_Real (poly_term p2))) + (! i (^ (poly_sub p1 p2) p3) + (th_holds (distinct0_Real (poly_term p3))))))))))) + + ;; converting between terms and polynomials + +(declare poly_norm (! t (term Real) (! p poly type))) + +(declare pn_let + (! t (term Real) + (! p poly + (! pn (poly_norm t p) + + (! u (! pnt (poly_norm t p) + (holds cln)) + (holds cln)))))) + +(declare pn_const + (! x mpq + (poly_norm (a_real x) (polyc x lmonn)))) + +(declare pn_var + (! v var_real + (poly_norm (a_var_real v) (polyc 0/1 (lmonc 1/1 v lmonn))))) + + +(declare pn_+ + (! x (term Real) + (! px poly + (! y (term Real) + (! py poly + (! pz poly + (! pnx (poly_norm x px) + (! pny (poly_norm y py) + (! a (^ (poly_add px py) pz) + (poly_norm (+_Real x y) pz)))))))))) + +(declare pn_- + (! x (term Real) + (! px poly + (! y (term Real) + (! py poly + (! pz poly + (! pnx (poly_norm x px) + (! pny (poly_norm y py) + (! a (^ (poly_sub px py) pz) + (poly_norm (-_Real x y) pz)))))))))) + +(declare pn_mul_c_L + (! y (term Real) + (! py poly + (! pz poly + (! x mpq + (! pny (poly_norm y py) + (! a (^ (poly_mul_c py x) pz) + (poly_norm (*_Real (a_real x) y) pz)))))))) + +(declare pn_mul_c_R + (! y (term Real) + (! py poly + (! pz poly + (! x mpq + (! pny (poly_norm y py) + (! a (^ (poly_mul_c py x) pz) + (poly_norm (*_Real y (a_real x)) pz)))))))) + +;; for polynomializing other terms, in particular ite's + +(declare term_atom (! v var_real (! t (term Real) type))) + +(declare decl_term_atom + (! t (term Real) + (! u (! v var_real + (! a (term_atom v t) + (holds cln))) + (holds cln)))) + +(declare pn_var_atom + (! v var_real + (! t (term Real) + (! a (term_atom v t) + (poly_norm t (polyc 0/1 (lmonc 1/1 v lmonn))))))) + + +;; conversion between term formulas and polynomial formulas + +(declare poly_formula_norm (! ft formula (! fp formula type))) + +; convert between term formulas and polynomial formulas + +(declare poly_form + (! ft formula + (! fp formula + (! p (poly_formula_norm ft fp) + (! u (th_holds ft) + (th_holds fp)))))) + +(declare poly_form_not + (! ft formula + (! fp formula + (! p (poly_formula_norm ft fp) + (! u (th_holds (not ft)) + (th_holds (not fp))))))) + +; form equivalence between term formula and polynomial formula + +(declare poly_norm_= + (! x (term Real) + (! y (term Real) + (! p poly + (! h (th_holds (= Real x y)) + (! n (poly_norm (-_Real x y) p) + (! u (! pn (th_holds (=0_Real (poly_term p))) + (holds cln)) + (holds cln)))))))) + +(declare poly_norm_> + (! x (term Real) + (! y (term Real) + (! p poly + (! h (th_holds (>_Real x y)) + (! n (poly_norm (-_Real x y) p) + (! u (! pn (th_holds (>0_Real (poly_term p))) + (holds cln)) + (holds cln)))))))) + +(declare poly_norm_< + (! x (term Real) + (! y (term Real) + (! p poly + (! h (th_holds (<_Real x y)) + (! n (poly_norm (-_Real y x) p) + (! u (! pn (th_holds (>0_Real (poly_term p))) + (holds cln)) + (holds cln)))))))) + +(declare poly_norm_>= + (! x (term Real) + (! y (term Real) + (! p poly + (! h (th_holds (>=_Real x y)) + (! n (poly_norm (-_Real x y) p) + (! u (! pn (th_holds (>=0_Real (poly_term p))) + (holds cln)) + (holds cln)))))))) + +(declare poly_norm_<= + (! x (term Real) + (! y (term Real) + (! p poly + (! h (th_holds (<=_Real x y)) + (! n (poly_norm (-_Real y x) p) + (! u (! pn (th_holds (>=0_Real (poly_term p))) + (holds cln)) + (holds cln)))))))) + + diff --git a/proofs/signatures/th_real.plf b/proofs/signatures/th_real.plf new file mode 100644 index 000000000..3478e23ef --- /dev/null +++ b/proofs/signatures/th_real.plf @@ -0,0 +1,25 @@ +(declare Real sort) + +(define arithpred_Real (! x (term Real) + (! y (term Real) + formula))) + +(declare >_Real arithpred_Real) +(declare >=_Real arithpred_Real) +(declare <_Real arithpred_Real) +(declare <=_Real arithpred_Real) + +(define arithterm_Real (! x (term Real) + (! y (term Real) + (term Real)))) + +(declare +_Real arithterm_Real) +(declare -_Real arithterm_Real) +(declare *_Real arithterm_Real) ; is * ok to use? +(declare /_Real arithterm_Real) ; is / ok to use? + +; a constant term +(declare a_real (! x mpq (term Real))) + +; unary negation +(declare u-_Real (! t (term Real) (term Real))) diff --git a/src/Makefile.am b/src/Makefile.am index aa12c7e03..94ddf452a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,9 @@ libcvc4_la_SOURCES = \ printer/cvc/cvc_printer.cpp \ printer/tptp/tptp_printer.h \ printer/tptp/tptp_printer.cpp \ + proof/arith_proof.cpp \ + proof/arith_proof.h \ + proof/array_proof.cpp \ proof/array_proof.h \ proof/bitvector_proof.cpp \ proof/bitvector_proof.h \ @@ -89,6 +92,8 @@ libcvc4_la_SOURCES = \ proof/proof_utils.h \ proof/sat_proof.h \ proof/sat_proof_implementation.h \ + proof/skolemization_manager.cpp \ + proof/skolemization_manager.h \ proof/theory_proof.cpp \ proof/theory_proof.h \ proof/uf_proof.cpp \ diff --git a/src/options/bv_options b/src/options/bv_options index c1180dba3..8edc809e3 100644 --- a/src/options/bv_options +++ b/src/options/bv_options @@ -11,14 +11,14 @@ option bitblastMode bitblast --bitblast=MODE CVC4::theory::bv::BitblastMode :han choose bitblasting mode, see --bitblast=help # Options for eager bit-blasting - + option bitvectorAig --bitblast-aig bool :default false :predicate abcEnabledBuild setBitblastAig :read-write bitblast by first converting to AIG (implies --bitblast=eager) expert-option bitvectorAigSimplifications --bv-aig-simp=COMMAND std::string :default "" :predicate abcEnabledBuild :read-write :link --bitblast-aig :link-smt bitblast-aig abc command to run AIG simplifications (implies --bitblast-aig, default is "balance;drw") # Options for lazy bit-blasting -option bitvectorPropagate --bv-propagate bool :default true :read-write +option bitvectorPropagate --bv-propagate bool :default true :read-write use bit-vector propagation in the bit-blaster option bitvectorEqualitySolver --bv-eq-solver bool :default true :read-write @@ -28,18 +28,18 @@ option bitvectorEqualitySlicer --bv-eq-slicer=MODE CVC4::theory::bv::BvSlicerMod turn on the slicing equality solver for the bit-vector theory (only if --bitblast=lazy) -option bitvectorInequalitySolver --bv-inequality-solver bool :default true :read-write +option bitvectorInequalitySolver --bv-inequality-solver bool :default true :read-write turn on the inequality solver for the bit-vector theory (only if --bitblast=lazy) option bitvectorAlgebraicSolver --bv-algebraic-solver bool :default true :read-write turn on the algebraic solver for the bit-vector theory (only if --bitblast=lazy) - + expert-option bitvectorAlgebraicBudget --bv-algebraic-budget unsigned :default 1500 :read-write :link --bv-algebraic-solver :link-smt bv-algebraic-solver the budget allowed for the algebraic solver in number of SAT conflicts # General options -option bitvectorToBool --bv-to-bool bool :default false :read-write +option bitvectorToBool --bv-to-bool bool :default false :read-write lift bit-vectors of size 1 to booleans when possible option bitvectorDivByZeroConst --bv-div-zero-const bool :default false :read-write @@ -47,11 +47,11 @@ option bitvectorDivByZeroConst --bv-div-zero-const bool :default false :read-wri expert-option bvExtractArithRewrite --bv-extract-arith bool :default false :read-write enable rewrite pushing extract [i:0] over arithmetic operations (can blow up) - + expert-option bvAbstraction --bv-abstraction bool :default false :read-write - mcm benchmark abstraction + mcm benchmark abstraction -expert-option skolemizeArguments --bv-skolemize bool :default false :read-write +expert-option skolemizeArguments --bv-skolemize bool :default false :read-write skolemize arguments for bv abstraction (only does something if --bv-abstraction is on) expert-option bvNumFunc --bv-num-func=NUM unsigned :default 1 @@ -62,8 +62,8 @@ expert-option bvEagerExplanations --bv-eager-explanations bool :default false :r expert-option bitvectorQuickXplain --bv-quick-xplain bool :default false minimize bv conflicts using the QuickXplain algorithm - + expert-option bvIntroducePow2 --bv-intro-pow2 bool :default false introduce bitvector powers of two as a preprocessing pass - + endmodule diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 657d288e7..14f46db84 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -385,6 +385,8 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, // arrays theory case kind::SELECT: case kind::STORE: + case kind::PARTIAL_SELECT_0: + case kind::PARTIAL_SELECT_1: case kind::ARRAY_TYPE: out << smtKindString(k) << " "; break; // string theory @@ -449,7 +451,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::CARDINALITY_CONSTRAINT: out << "fmf.card "; break; case kind::CARDINALITY_VALUE: out << "fmf.card.val "; break; - + // bv theory case kind::BITVECTOR_CONCAT: out << "concat "; forceBinary = true; break; case kind::BITVECTOR_AND: out << "bvand "; forceBinary = true; break; @@ -732,6 +734,8 @@ static string smtKindString(Kind k) throw() { case kind::SELECT: return "select"; case kind::STORE: return "store"; case kind::ARRAY_TYPE: return "Array"; + case kind::PARTIAL_SELECT_0: return "partial_select_0"; + case kind::PARTIAL_SELECT_1: return "partial_select_1"; // bv theory case kind::BITVECTOR_CONCAT: return "concat"; diff --git a/src/proof/arith_proof.cpp b/src/proof/arith_proof.cpp new file mode 100644 index 000000000..abe6cf1d4 --- /dev/null +++ b/src/proof/arith_proof.cpp @@ -0,0 +1,833 @@ +/********************* */ +/*! \file arith_proof.cpp +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none +** Minor contributors (to current version): none +** This file is part of the CVC4 project. +** Copyright (c) 2009-2014 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ + +#include "proof/theory_proof.h" +#include "proof/proof_manager.h" +#include "proof/arith_proof.h" +#include "theory/arith/theory_arith.h" +#include + +namespace CVC4 { + +inline static Node eqNode(TNode n1, TNode n2) { + return NodeManager::currentNM()->mkNode(n1.getType().isBoolean() ? kind::IFF : kind::EQUAL, n1, n2); +} + +// congrence matching term helper +inline static bool match(TNode n1, TNode n2) { + Debug("pf::arith") << "match " << n1 << " " << n2 << std::endl; + if(ProofManager::currentPM()->hasOp(n1)) { + n1 = ProofManager::currentPM()->lookupOp(n1); + } + if(ProofManager::currentPM()->hasOp(n2)) { + n2 = ProofManager::currentPM()->lookupOp(n2); + } + Debug("pf::arith") << "+ match " << n1 << " " << n2 << std::endl; + if(n1 == n2) { + return true; + } + if(n1.getType().isFunction() && n2.hasOperator()) { + if(ProofManager::currentPM()->hasOp(n2.getOperator())) { + return n1 == ProofManager::currentPM()->lookupOp(n2.getOperator()); + } else { + return n1 == n2.getOperator(); + } + } + if(n2.getType().isFunction() && n1.hasOperator()) { + if(ProofManager::currentPM()->hasOp(n1.getOperator())) { + return n2 == ProofManager::currentPM()->lookupOp(n1.getOperator()); + } else { + return n2 == n1.getOperator(); + } + } + if(n1.hasOperator() && n2.hasOperator() && n1.getOperator() != n2.getOperator()) { + return false; + } + for(size_t i = 0; i < n1.getNumChildren() && i < n2.getNumChildren(); ++i) { + if(n1[i] != n2[i]) { + return false; + } + } + return true; +} + + +void ProofArith::toStream(std::ostream& out) { + Trace("theory-proof-debug") << "; Print Arith proof..." << std::endl; + //AJR : carry this further? + LetMap map; + toStreamLFSC(out, ProofManager::getArithProof(), d_proof, map); +} + +void ProofArith::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map) { + Debug("lfsc-arith") << "Printing arith proof in LFSC : " << std::endl; + pf->debug_print("lfsc-arith"); + Debug("lfsc-arith") << std::endl; + toStreamRecLFSC( out, tp, pf, 0, map ); +} + +Node ProofArith::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map) { + Debug("pf::arith") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; + pf->debug_print("pf::arith"); + Debug("pf::arith") << std::endl; + + if(tb == 0) { + Assert(pf->d_id == theory::eq::MERGED_THROUGH_TRANS); + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.size() >= 2); + + int neg = -1; + theory::eq::EqProof subTrans; + subTrans.d_id = theory::eq::MERGED_THROUGH_TRANS; + subTrans.d_node = pf->d_node; + + size_t i = 0; + while (i < pf->d_children.size()) { + // Look for the negative clause, with which we will form a contradiction. + if(!pf->d_children[i]->d_node.isNull() && pf->d_children[i]->d_node.getKind() == kind::NOT) { + Assert(neg < 0); + neg = i; + ++i; + } + + // Handle congruence closures over equalities. + else if (pf->d_children[i]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && pf->d_children[i]->d_node.isNull()) { + Debug("pf::arith") << "Handling congruence over equalities" << std::endl; + + // Gather the sequence of consecutive congruence closures. + std::vector congruenceClosures; + unsigned count; + Debug("pf::arith") << "Collecting congruence sequence" << std::endl; + for (count = 0; + i + count < pf->d_children.size() && + pf->d_children[i + count]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && + pf->d_children[i + count]->d_node.isNull(); + ++count) { + Debug("pf::arith") << "Found a congruence: " << std::endl; + pf->d_children[i+count]->debug_print("pf::arith"); + congruenceClosures.push_back(pf->d_children[i+count]); + } + + Debug("pf::arith") << "Total number of congruences found: " << congruenceClosures.size() << std::endl; + + // Determine if the "target" of the congruence sequence appears right before or right after the sequence. + bool targetAppearsBefore = true; + bool targetAppearsAfter = true; + + if ((i == 0) || (i == 1 && neg == 0)) { + Debug("pf::arith") << "Target does not appear before" << std::endl; + targetAppearsBefore = false; + } + + if ((i + count >= pf->d_children.size()) || + (!pf->d_children[i + count]->d_node.isNull() && + pf->d_children[i + count]->d_node.getKind() == kind::NOT)) { + Debug("pf::arith") << "Target does not appear after" << std::endl; + targetAppearsAfter = false; + } + + // Assert that we have precisely one target clause. + Assert(targetAppearsBefore != targetAppearsAfter); + + // Begin breaking up the congruences and ordering the equalities correctly. + std::vector orderedEqualities; + + + // Insert target clause first. + if (targetAppearsBefore) { + orderedEqualities.push_back(pf->d_children[i - 1]); + // The target has already been added to subTrans; remove it. + subTrans.d_children.pop_back(); + } else { + orderedEqualities.push_back(pf->d_children[i + count]); + } + + // Start with the congruence closure closest to the target clause, and work our way back/forward. + if (targetAppearsBefore) { + for (unsigned j = 0; j < count; ++j) { + if (pf->d_children[i + j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + j]->d_children[0]); + if (pf->d_children[i + j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + j]->d_children[1]); + } + } else { + for (unsigned j = 0; j < count; ++j) { + if (pf->d_children[i + count - 1 - j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + count - 1 - j]->d_children[0]); + if (pf->d_children[i + count - 1 - j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + count - 1 - j]->d_children[1]); + } + } + + // Copy the result into the main transitivity proof. + subTrans.d_children.insert(subTrans.d_children.end(), orderedEqualities.begin(), orderedEqualities.end()); + + // Increase i to skip over the children that have been processed. + i += count; + if (targetAppearsAfter) { + ++i; + } + } + + // Else, just copy the child proof as is + else { + subTrans.d_children.push_back(pf->d_children[i]); + ++i; + } + } + Assert(neg >= 0); + + Node n1; + std::stringstream ss; + //Assert(subTrans.d_children.size() == pf->d_children.size() - 1); + Debug("pf::arith") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; + if(pf->d_children.size() > 2) { + n1 = toStreamRecLFSC(ss, tp, &subTrans, 1, map); + } else { + n1 = toStreamRecLFSC(ss, tp, subTrans.d_children[0], 1, map); + Debug("pf::arith") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; + } + + Node n2 = pf->d_children[neg]->d_node; + Assert(n2.getKind() == kind::NOT); + out << "(clausify_false (contra _ "; + Debug("pf::arith") << "\nhave proven: " << n1 << std::endl; + Debug("pf::arith") << "n2 is " << n2[0] << std::endl; + + if (n2[0].getNumChildren() > 0) { Debug("pf::arith") << "\nn2[0]: " << n2[0][0] << std::endl; } + if (n1.getNumChildren() > 1) { Debug("pf::arith") << "n1[1]: " << n1[1] << std::endl; } + + if(n2[0].getKind() == kind::APPLY_UF) { + out << "(trans _ _ _ _ "; + out << "(symm _ _ _ "; + out << ss.str(); + out << ") (pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; + } else { + Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); + if(n1[1] == n2[0][0]) { + out << "(symm _ _ _ " << ss.str() << ")"; + } else { + out << ss.str(); + } + out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; + } + return Node(); + } + + switch(pf->d_id) { + case theory::eq::MERGED_THROUGH_CONGRUENCE: { + Debug("pf::arith") << "\nok, looking at congruence:\n"; + pf->debug_print("pf::arith"); + std::stack stk; + for(const theory::eq::EqProof* pf2 = pf; pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; pf2 = pf2->d_children[0]) { + Assert(!pf2->d_node.isNull()); + Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF || pf2->d_node.getKind() == kind::BUILTIN || pf2->d_node.getKind() == kind::APPLY_UF || pf2->d_node.getKind() == kind::SELECT || pf2->d_node.getKind() == kind::STORE); + Assert(pf2->d_children.size() == 2); + out << "(cong _ _ _ _ _ _ "; + stk.push(pf2); + } + Assert(stk.top()->d_children[0]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE); + NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), b2(kind::PARTIAL_APPLY_UF); + const theory::eq::EqProof* pf2 = stk.top(); + stk.pop(); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); + Node n1 = toStreamRecLFSC(out, tp, pf2->d_children[0], tb + 1, map); + out << " "; + std::stringstream ss; + Node n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); + Debug("pf::arith") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; + pf2->debug_print("pf::arith"); + Debug("pf::arith") << "looking at " << pf2->d_node << "\n"; + Debug("pf::arith") << " " << n1 << "\n"; + Debug("pf::arith") << " " << n2 << "\n"; + int side = 0; + if(match(pf2->d_node, n1[0])) { + //if(tb == 1) { + Debug("pf::arith") << "SIDE IS 0\n"; + //} + side = 0; + } else { + //if(tb == 1) { + Debug("pf::arith") << "SIDE IS 1\n"; + //} + if(!match(pf2->d_node, n1[1])) { + Debug("pf::arith") << "IN BAD CASE, our first subproof is\n"; + pf2->d_children[0]->debug_print("pf::arith"); + } + Assert(match(pf2->d_node, n1[1])); + side = 1; + } + if(n1[side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::PARTIAL_APPLY_UF || n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::STORE) { + if(n1[side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::PARTIAL_APPLY_UF) { + b1 << n1[side].getOperator(); + } else { + b1 << ProofManager::currentPM()->mkOp(n1[side].getOperator()); + } + b1.append(n1[side].begin(), n1[side].end()); + } else { + b1 << n1[side]; + } + if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || n1[1-side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::STORE) { + if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || n1[1-side].getKind() == kind::APPLY_UF) { + b2 << n1[1-side].getOperator(); + } else { + b2 << ProofManager::currentPM()->mkOp(n1[1-side].getOperator()); + } + b2.append(n1[1-side].begin(), n1[1-side].end()); + } else { + b2 << n1[1-side]; + } + Debug("pf::arith") << "pf2->d_node " << pf2->d_node << std::endl; + Debug("pf::arith") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; + Debug("pf::arith") << "n1 " << n1 << std::endl; + Debug("pf::arith") << "n2 " << n2 << std::endl; + Debug("pf::arith") << "side " << side << std::endl; + if(pf2->d_node[b1.getNumChildren() - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { + b1 << n2[side]; + b2 << n2[1-side]; + out << ss.str(); + } else { + Assert(pf2->d_node[b1.getNumChildren() - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[1-side]); + b1 << n2[1-side]; + b2 << n2[side]; + out << "(symm _ _ _ " << ss.str() << ")"; + } + out << ")"; + while(!stk.empty()) { + if(tb == 1) { + Debug("pf::arith") << "\nMORE TO DO\n"; + } + pf2 = stk.top(); + stk.pop(); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); + out << " "; + ss.str(""); + n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); + Debug("pf::arith") << "\nok, in cong[" << stk.size() << "]" << "\n"; + Debug("pf::arith") << "looking at " << pf2->d_node << "\n"; + Debug("pf::arith") << " " << n1 << "\n"; + Debug("pf::arith") << " " << n2 << "\n"; + Debug("pf::arith") << " " << b1 << "\n"; + Debug("pf::arith") << " " << b2 << "\n"; + if(pf2->d_node[b1.getNumChildren()] == n2[side]) { + b1 << n2[side]; + b2 << n2[1-side]; + out << ss.str(); + } else { + Assert(pf2->d_node[b1.getNumChildren()] == n2[1-side]); + b1 << n2[1-side]; + b2 << n2[side]; + out << "(symm _ _ _ " << ss.str() << ")"; + } + out << ")"; + } + n1 = b1; + n2 = b2; + Debug("pf::arith") << "at end assert, got " << pf2->d_node << " and " << n1 << std::endl; + if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { + Assert(n1 == pf2->d_node); + } + if(n1.getOperator().getType().getNumChildren() == n1.getNumChildren() + 1) { + if(ProofManager::currentPM()->hasOp(n1.getOperator())) { + b1.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst()); + } else { + b1.clear(kind::APPLY_UF); + b1 << n1.getOperator(); + } + b1.append(n1.begin(), n1.end()); + n1 = b1; + Debug("pf::arith") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; + if(pf2->d_node.getKind() == kind::APPLY_UF) { + Assert(n1 == pf2->d_node); + } + } + if(n2.getOperator().getType().getNumChildren() == n2.getNumChildren() + 1) { + if(ProofManager::currentPM()->hasOp(n2.getOperator())) { + b2.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst()); + } else { + b2.clear(kind::APPLY_UF); + b2 << n2.getOperator(); + } + b2.append(n2.begin(), n2.end()); + n2 = b2; + } + Node n = (side == 0 ? eqNode(n1, n2) : eqNode(n2, n1)); + if(tb == 1) { + Debug("pf::arith") << "\ncong proved: " << n << "\n"; + } + return n; + } + + case theory::eq::MERGED_THROUGH_REFLEXIVITY: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + out << "(refl _ "; + tp->printTerm(NodeManager::currentNM()->toExpr(pf->d_node), out, map); + out << ")"; + return eqNode(pf->d_node, pf->d_node); + + case theory::eq::MERGED_THROUGH_EQUALITY: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + out << ProofManager::getLitName(pf->d_node.negate()); + return pf->d_node; + + case theory::eq::MERGED_THROUGH_TRANS: { + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.size() >= 2); + std::stringstream ss; + Debug("pf::arith") << "\ndoing trans proof[[\n"; + pf->debug_print("pf::arith"); + Debug("pf::arith") << "\n"; + Node n1 = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); + Debug("pf::arith") << "\ndoing trans proof, got n1 " << n1 << "\n"; + if(tb == 1) { + Debug("pf::arith") << "\ntrans proof[0], got n1 " << n1 << "\n"; + } + + bool identicalEqualities = false; + bool evenLengthSequence; + Node nodeAfterEqualitySequence; + + std::map childToStream; + + for(size_t i = 1; i < pf->d_children.size(); ++i) { + std::stringstream ss1(ss.str()), ss2; + ss.str(""); + + // It is possible that we've already converted the i'th child to stream. If so, + // use previously stored result. Otherwise, convert and store. + Node n2; + if (childToStream.find(i) != childToStream.end()) + n2 = childToStream[i]; + else { + n2 = toStreamRecLFSC(ss2, tp, pf->d_children[i], tb + 1, map); + childToStream[i] = n2; + } + + // The following branch is dedicated to handling sequences of identical equalities, + // i.e. trans[ a=b, a=b, a=b ]. + // + // There are two cases: + // 1. The number of equalities is odd. Then, the sequence can be collapsed to just one equality, + // i.e. a=b. + // 2. The number of equalities is even. Now, we have two options: a=a or b=b. To determine this, + // we look at the node after the equality sequence. If it needs a, we go for a=a; and if it needs + // b, we go for b=b. If there is no following node, we look at the goal of the transitivity proof, + // and use it to determine which option we need. + if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { + if (((n1[0] == n2[0]) && (n1[1] == n2[1])) || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) { + // We are in a sequence of identical equalities + + Debug("pf::arith") << "Detected identical equalities: " << std::endl << "\t" << n1 << std::endl; + + if (!identicalEqualities) { + // The sequence of identical equalities has started just now + identicalEqualities = true; + + Debug("pf::arith") << "The sequence is just beginning. Determining length..." << std::endl; + + // Determine whether the length of this sequence is odd or even. + evenLengthSequence = true; + bool sequenceOver = false; + size_t j = i + 1; + + while (j < pf->d_children.size() && !sequenceOver) { + std::stringstream dontCare; + nodeAfterEqualitySequence = toStreamRecLFSC(dontCare, tp, pf->d_children[j], tb + 1, map ); + + if (((nodeAfterEqualitySequence[0] == n1[0]) && (nodeAfterEqualitySequence[1] == n1[1])) || + ((nodeAfterEqualitySequence[0] == n1[1]) && (nodeAfterEqualitySequence[1] == n1[0]))) { + evenLengthSequence = !evenLengthSequence; + } else { + sequenceOver = true; + } + + ++j; + } + + if (evenLengthSequence) { + // If the length is even, we need to apply transitivity for the "correct" hand of the equality. + + Debug("pf::arith") << "Equality sequence of even length" << std::endl; + Debug("pf::arith") << "n1 is: " << n1 << std::endl; + Debug("pf::arith") << "n2 is: " << n2 << std::endl; + Debug("pf::arith") << "pf-d_node is: " << pf->d_node << std::endl; + Debug("pf::arith") << "Next node is: " << nodeAfterEqualitySequence << std::endl; + + ss << "(trans _ _ _ _ "; + + // If the sequence is at the very end of the transitivity proof, use pf->d_node to guide us. + if (!sequenceOver) { + if (match(n1[0], pf->d_node[0])) { + n1 = eqNode(n1[0], n1[0]); + ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; + } else if (match(n1[1], pf->d_node[1])) { + n1 = eqNode(n1[1], n1[1]); + ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); + } else { + Debug("pf::arith") << "Error: identical equalities over, but hands don't match what we're proving." + << std::endl; + Assert(false); + } + } else { + // We have a "next node". Use it to guide us. + + Assert(nodeAfterEqualitySequence.getKind() == kind::EQUAL || + nodeAfterEqualitySequence.getKind() == kind::IFF); + + if ((n1[0] == nodeAfterEqualitySequence[0]) || (n1[0] == nodeAfterEqualitySequence[1])) { + + // Eliminate n1[1] + ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; + n1 = eqNode(n1[0], n1[0]); + + } else if ((n1[1] == nodeAfterEqualitySequence[0]) || (n1[1] == nodeAfterEqualitySequence[1])) { + + // Eliminate n1[0] + ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); + n1 = eqNode(n1[1], n1[1]); + + } else { + Debug("pf::arith") << "Error: even length sequence, but I don't know which hand to keep!" << std::endl; + Assert(false); + } + } + + ss << ")"; + + } else { + Debug("pf::arith") << "Equality sequence length is odd!" << std::endl; + ss.str(ss1.str()); + } + + Debug("pf::arith") << "Have proven: " << n1 << std::endl; + } else { + ss.str(ss1.str()); + } + + // Ignore the redundancy. + continue; + } + } + + if (identicalEqualities) { + // We were in a sequence of identical equalities, but it has now ended. Resume normal operation. + identicalEqualities = false; + } + + Debug("pf::arith") << "\ndoing trans proof, got n2 " << n2 << "\n"; + if(tb == 1) { + Debug("pf::arith") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; + Debug("pf::arith") << (n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) << "\n"; + + if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) { + Debug("pf::arith") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; + Debug("pf::arith") << n1[0].getId() << " " << n1[0] << "\n"; + Debug("pf::arith") << n1[1].getId() << " " << n1[1] << "\n"; + Debug("pf::arith") << n2[0].getId() << " " << n2[0] << "\n"; + Debug("pf::arith") << n2[1].getId() << " " << n2[1] << "\n"; + Debug("pf::arith") << (n1[0] == n2[0]) << "\n"; + Debug("pf::arith") << (n1[1] == n2[1]) << "\n"; + Debug("pf::arith") << (n1[0] == n2[1]) << "\n"; + Debug("pf::arith") << (n1[1] == n2[0]) << "\n"; + } + } + ss << "(trans _ _ _ _ "; + + if((n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) && + (n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF)) + // Both elements of the transitivity rule are equalities/iffs + { + if(n1[0] == n2[0]) { + if(tb == 1) { Debug("pf::arith") << "case 1\n"; } + n1 = eqNode(n1[1], n2[1]); + ss << "(symm _ _ _ " << ss1.str() << ") " << ss2.str(); + } else if(n1[1] == n2[1]) { + if(tb == 1) { Debug("pf::arith") << "case 2\n"; } + n1 = eqNode(n1[0], n2[0]); + ss << ss1.str() << " (symm _ _ _ " << ss2.str() << ")"; + } else if(n1[0] == n2[1]) { + if(tb == 1) { Debug("pf::arith") << "case 3\n"; } + n1 = eqNode(n2[0], n1[1]); + ss << ss2.str() << " " << ss1.str(); + if(tb == 1) { Debug("pf::arith") << "++ proved " << n1 << "\n"; } + } else if(n1[1] == n2[0]) { + if(tb == 1) { Debug("pf::arith") << "case 4\n"; } + n1 = eqNode(n1[0], n2[1]); + ss << ss1.str() << " " << ss2.str(); + } else { + Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; + Warning() << "0 proves " << n1 << "\n"; + Warning() << "1 proves " << n2 << "\n\n"; + pf->debug_print("pf::arith",0); + //toStreamRec(Warning.getStream(), pf, 0); + Warning() << "\n\n"; + Unreachable(); + } + Debug("pf::arith") << "++ trans proof[" << i << "], now have " << n1 << std::endl; + } else if(n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF) { + // n1 is an equality/iff, but n2 is a predicate + if(n1[0] == n2) { + n1 = n1[1]; + ss << "(symm _ _ _ " << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; + } else if(n1[1] == n2) { + n1 = n1[0]; + ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; + } else { + Unreachable(); + } + } else if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { + // n2 is an equality/iff, but n1 is a predicate + if(n2[0] == n1) { + n1 = n2[1]; + ss << "(symm _ _ _ " << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; + } else if(n2[1] == n1) { + n1 = n2[0]; + ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; + } else { + Unreachable(); + } + } else { + // Both n1 and n2 are prediacates. Don't know what to do... + Unreachable(); + } + + ss << ")"; + } + out << ss.str(); + Debug("pf::arith") << "\n++ trans proof done, have proven " << n1 << std::endl; + return n1; + } + + default: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + Debug("pf::arith") << "theory proof: " << pf->d_node << " by rule " << int(pf->d_id) << std::endl; + AlwaysAssert(false); + return pf->d_node; + } +} + +ArithProof::ArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* pe) + : TheoryProof(arith, pe), d_realMode(false) +{} + +void ArithProof::registerTerm(Expr term) { + Debug("pf::arith") << "Arith register term: " << term << ". Kind: " << term.getKind() + << ". Type: " << term.getType() << std::endl; + + if (term.getType().isReal() && !term.getType().isInteger()) { + Debug("pf::arith") << "Entering real mode" << std::endl; + d_realMode = true; + } + + // recursively declare all other terms + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + // could belong to other theories + d_proofEngine->registerTerm(term[i]); + } +} + +void LFSCArithProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { + Debug("pf::arith") << "Arith print term: " << term << ". Kind: " << term.getKind() + << ". Type: " << term.getType() + << ". Number of children: " << term.getNumChildren() << std::endl; + + // !d_realMode <--> term.getType().isInteger() + + Assert (theory::Theory::theoryOf(term) == theory::THEORY_ARITH); + switch (term.getKind()) { + + case kind::CONST_RATIONAL: { + Assert (term.getNumChildren() == 0); + Assert (term.getType().isInteger() || term.getType().isReal()); + + const Rational& r = term.getConst(); + bool neg = (r < 0); + + os << (!d_realMode ? "(a_int " : "(a_real "); + + if (neg) { + os << "(~ "; + } + + if (!d_realMode) { + os << r.abs(); + } else { + os << r.abs().getNumerator(); + os << "/"; + os << r.getDenominator(); + } + + if (neg) { + os << ") "; + } + + os << ") "; + return; + } + + case kind::UMINUS: { + Assert (term.getNumChildren() == 1); + Assert (term.getType().isInteger() || term.getType().isReal()); + os << (!d_realMode ? "(u-_Int " : "(u-_Real "); + d_proofEngine->printBoundTerm(term[0], os, map); + os << ") "; + return; + } + + case kind::PLUS: { + Assert (term.getNumChildren() >= 2); + + std::stringstream paren; + for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) { + os << (!d_realMode ? "(+_Int " : "(+_Real "); + d_proofEngine->printBoundTerm(term[i], os, map); + os << " "; + paren << ") "; + } + + d_proofEngine->printBoundTerm(term[term.getNumChildren() - 1], os, map); + os << paren.str(); + return; + } + + case kind::MINUS: { + Assert (term.getNumChildren() >= 2); + + std::stringstream paren; + for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) { + os << (!d_realMode ? "(-_Int " : "(-_Real "); + d_proofEngine->printBoundTerm(term[i], os, map); + os << " "; + paren << ") "; + } + + d_proofEngine->printBoundTerm(term[term.getNumChildren() - 1], os, map); + os << paren.str(); + return; + } + + case kind::MULT: { + Assert (term.getNumChildren() >= 2); + + std::stringstream paren; + for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) { + os << (!d_realMode ? "(*_Int " : "(*_Real "); + d_proofEngine->printBoundTerm(term[i], os, map); + os << " "; + paren << ") "; + } + + d_proofEngine->printBoundTerm(term[term.getNumChildren() - 1], os, map); + os << paren.str(); + return; + } + + case kind::DIVISION: + case kind::DIVISION_TOTAL: { + Assert (term.getNumChildren() >= 2); + + std::stringstream paren; + for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) { + os << (!d_realMode ? "(/_Int " : "(/_Real "); + d_proofEngine->printBoundTerm(term[i], os, map); + os << " "; + paren << ") "; + } + + d_proofEngine->printBoundTerm(term[term.getNumChildren() - 1], os, map); + os << paren.str(); + return; + } + + case kind::GT: + Assert (term.getNumChildren() == 2); + os << (!d_realMode ? "(>_Int " : "(>_Real "); + d_proofEngine->printBoundTerm(term[0], os, map); + os << " "; + d_proofEngine->printBoundTerm(term[1], os, map); + os << ") "; + return; + + case kind::GEQ: + Assert (term.getNumChildren() == 2); + os << (!d_realMode ? "(>=_Int " : "(>=_Real "); + d_proofEngine->printBoundTerm(term[0], os, map); + os << " "; + d_proofEngine->printBoundTerm(term[1], os, map); + os << ") "; + return; + + case kind::LT: + Assert (term.getNumChildren() == 2); + os << (!d_realMode ? "(<_Int " : "(<_Real "); + d_proofEngine->printBoundTerm(term[0], os, map); + os << " "; + d_proofEngine->printBoundTerm(term[1], os, map); + os << ") "; + return; + + case kind::LEQ: + Assert (term.getNumChildren() == 2); + os << (!d_realMode ? "(<=_Int " : "(<=_Real "); + d_proofEngine->printBoundTerm(term[0], os, map); + os << " "; + d_proofEngine->printBoundTerm(term[1], os, map); + os << ") "; + return; + + default: + Debug("pf::arith") << "Default printing of term: " << term << std::endl; + os << term; + return; + } +} + +void LFSCArithProof::printOwnedSort(Type type, std::ostream& os) { + Debug("pf::arith") << "Arith print sort: " << type << std::endl; + + if (type.isInteger() && d_realMode) { + // If in "real mode", don't use type Int for, e.g., equality. + os << "Real "; + } else { + os << type << " "; + } +} + +void LFSCArithProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) { + os << " ;; Arith Theory Lemma \n;;"; + for (unsigned i = 0; i < lemma.size(); ++i) { + os << lemma[i] <<" "; + } + os <<"\n"; + //os << " (clausify_false trust)"; + ArithProof::printTheoryLemmaProof( lemma, os, paren ); +} + +void LFSCArithProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { +} + +void LFSCArithProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { +} + +void LFSCArithProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + +} /* CVC4 namespace */ diff --git a/src/proof/arith_proof.h b/src/proof/arith_proof.h new file mode 100644 index 000000000..a35a5f57e --- /dev/null +++ b/src/proof/arith_proof.h @@ -0,0 +1,82 @@ +/********************* */ +/*! \file arith_proof.h + ** \verbatim + ** Original author: Liana Hadarean + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Arith proof + ** + ** Arith proof + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__ARITH__PROOF_H +#define __CVC4__ARITH__PROOF_H + +#include "expr/expr.h" +#include "proof/proof_manager.h" +#include "proof/theory_proof.h" +#include "theory/uf/equality_engine.h" + +namespace CVC4 { + +//proof object outputted by TheoryArith +class ProofArith : public Proof { +private: + static Node toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map); +public: + ProofArith( theory::eq::EqProof * pf ) : d_proof( pf ) {} + //it is simply an equality engine proof + theory::eq::EqProof * d_proof; + void toStream(std::ostream& out); + static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map); +}; + + +namespace theory { +namespace arith { +class TheoryArith; +} +} + +typedef __gnu_cxx::hash_set TypeSet; + + +class ArithProof : public TheoryProof { +protected: + // std::map d_constRationalString; // all the variable/function declarations + + // TypeSet d_sorts; // all the uninterpreted sorts in this theory + // ExprSet d_declarations; // all the variable/function declarations + + bool d_realMode; + +public: + ArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* proofEngine); + + virtual void registerTerm(Expr term); +}; + +class LFSCArithProof : public ArithProof { +public: + LFSCArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* proofEngine) + : ArithProof(arith, proofEngine) + {} + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedSort(Type type, std::ostream& os); + virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); +}; + + +}/* CVC4 namespace */ + +#endif /* __CVC4__ARITH__PROOF_H */ diff --git a/src/proof/array_proof.cpp b/src/proof/array_proof.cpp new file mode 100644 index 000000000..764028c6b --- /dev/null +++ b/src/proof/array_proof.cpp @@ -0,0 +1,1260 @@ +/********************* */ +/*! \file uf_proof.cpp +** \verbatim +** Original author: Liana Hadarean +** Major contributors: none +** Minor contributors (to current version): none +** This file is part of the CVC4 project. +** Copyright (c) 2009-2014 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ + +#include "proof/theory_proof.h" +#include "proof/proof_manager.h" +#include "proof/array_proof.h" +#include "theory/arrays/theory_arrays.h" +#include + +namespace CVC4 { + +inline static Node eqNode(TNode n1, TNode n2) { + return NodeManager::currentNM()->mkNode(n1.getType().isBoolean() ? kind::IFF : kind::EQUAL, n1, n2); +} + +// congrence matching term helper +inline static bool match(TNode n1, TNode n2) { + Debug("mgd") << "match " << n1 << " " << n2 << std::endl; + if(ProofManager::currentPM()->hasOp(n1)) { + n1 = ProofManager::currentPM()->lookupOp(n1); + } + if(ProofManager::currentPM()->hasOp(n2)) { + n2 = ProofManager::currentPM()->lookupOp(n2); + } + Debug("mgd") << "+ match " << n1 << " " << n2 << std::endl; + Debug("pf::array") << "+ match: step 1" << std::endl; + if(n1 == n2) { + return true; + } + + if(n1.getType().isFunction() && n2.hasOperator()) { + if(ProofManager::currentPM()->hasOp(n2.getOperator())) { + return n1 == ProofManager::currentPM()->lookupOp(n2.getOperator()); + } else { + return n1 == n2.getOperator(); + } + } + + if(n2.getType().isFunction() && n1.hasOperator()) { + if(ProofManager::currentPM()->hasOp(n1.getOperator())) { + return n2 == ProofManager::currentPM()->lookupOp(n1.getOperator()); + } else { + return n2 == n1.getOperator(); + } + } + + if(n1.hasOperator() && n2.hasOperator() && n1.getOperator() != n2.getOperator()) { + if (!((n1.getKind() == kind::SELECT && n2.getKind() == kind::PARTIAL_SELECT_0) || + (n1.getKind() == kind::SELECT && n2.getKind() == kind::PARTIAL_SELECT_1) || + (n1.getKind() == kind::PARTIAL_SELECT_1 && n2.getKind() == kind::SELECT) || + (n1.getKind() == kind::PARTIAL_SELECT_1 && n2.getKind() == kind::PARTIAL_SELECT_0) || + (n1.getKind() == kind::PARTIAL_SELECT_0 && n2.getKind() == kind::SELECT) || + (n1.getKind() == kind::PARTIAL_SELECT_0 && n2.getKind() == kind::PARTIAL_SELECT_1) + )) { + return false; + } + } + + for(size_t i = 0; i < n1.getNumChildren() && i < n2.getNumChildren(); ++i) { + if(n1[i] != n2[i]) { + return false; + } + } + + return true; +} + +void ProofArray::toStream(std::ostream& out) { + Trace("pf::array") << "; Print Array proof..." << std::endl; + //AJR : carry this further? + LetMap map; + toStreamLFSC(out, ProofManager::getArrayProof(), d_proof, map); + Debug("pf::array") << "; Print Array proof done!" << std::endl; +} + +void ProofArray::toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const LetMap& map) { + Debug("pf::array") << "Printing array proof in LFSC : " << std::endl; + pf->debug_print("pf::array"); + Debug("pf::array") << std::endl; + toStreamRecLFSC( out, tp, pf, 0, map ); + Debug("pf::array") << "Printing array proof in LFSC DONE" << std::endl; +} + +void ProofArray::registerSkolem(Node equality, Node skolem) { + d_nodeToSkolem[equality] = skolem; +} + +Node ProofArray::toStreamRecLFSC(std::ostream& out, + TheoryProof* tp, + theory::eq::EqProof* pf, + unsigned tb, + const LetMap& map) { + + Debug("pf::array") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; + pf->debug_print("pf::array"); + Debug("pf::array") << std::endl; + + if(tb == 0) { + Assert(pf->d_id == theory::eq::MERGED_THROUGH_TRANS); + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.size() >= 2); + + int neg = -1; + theory::eq::EqProof subTrans; + subTrans.d_id = theory::eq::MERGED_THROUGH_TRANS; + subTrans.d_node = pf->d_node; + + size_t i = 0; + while (i < pf->d_children.size()) { + // Look for the negative clause, with which we will form a contradiction. + if(!pf->d_children[i]->d_node.isNull() && pf->d_children[i]->d_node.getKind() == kind::NOT) { + Assert(neg < 0); + neg = i; + ++i; + } + + // Handle congruence closures over equalities. + else if (pf->d_children[i]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && pf->d_children[i]->d_node.isNull()) { + Debug("pf::array") << "Handling congruence over equalities" << std::endl; + + // Gather the sequence of consecutive congruence closures. + std::vector congruenceClosures; + unsigned count; + Debug("pf::array") << "Collecting congruence sequence" << std::endl; + for (count = 0; + i + count < pf->d_children.size() && + pf->d_children[i + count]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && + pf->d_children[i + count]->d_node.isNull(); + ++count) { + Debug("pf::array") << "Found a congruence: " << std::endl; + pf->d_children[i+count]->debug_print("pf::array"); + congruenceClosures.push_back(pf->d_children[i+count]); + } + + Debug("pf::array") << "Total number of congruences found: " << congruenceClosures.size() << std::endl; + + // Determine if the "target" of the congruence sequence appears right before or right after the sequence. + bool targetAppearsBefore = true; + bool targetAppearsAfter = true; + + if ((i == 0) || (i == 1 && neg == 0)) { + Debug("pf::array") << "Target does not appear before" << std::endl; + targetAppearsBefore = false; + } + + if ((i + count >= pf->d_children.size()) || + (!pf->d_children[i + count]->d_node.isNull() && + pf->d_children[i + count]->d_node.getKind() == kind::NOT)) { + Debug("pf::array") << "Target does not appear after" << std::endl; + targetAppearsAfter = false; + } + + // Assert that we have precisely one target clause. + Assert(targetAppearsBefore != targetAppearsAfter); + + // Begin breaking up the congruences and ordering the equalities correctly. + std::vector orderedEqualities; + + // Insert target clause first. + if (targetAppearsBefore) { + orderedEqualities.push_back(pf->d_children[i - 1]); + // The target has already been added to subTrans; remove it. + subTrans.d_children.pop_back(); + } else { + orderedEqualities.push_back(pf->d_children[i + count]); + } + + // Start with the congruence closure closest to the target clause, and work our way back/forward. + if (targetAppearsBefore) { + for (unsigned j = 0; j < count; ++j) { + if (pf->d_children[i + j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + j]->d_children[0]); + if (pf->d_children[i + j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + j]->d_children[1]); + } + } else { + for (unsigned j = 0; j < count; ++j) { + if (pf->d_children[i + count - 1 - j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + count - 1 - j]->d_children[0]); + if (pf->d_children[i + count - 1 - j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) + orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + count - 1 - j]->d_children[1]); + } + } + + // Copy the result into the main transitivity proof. + subTrans.d_children.insert(subTrans.d_children.end(), orderedEqualities.begin(), orderedEqualities.end()); + + // Increase i to skip over the children that have been processed. + i += count; + if (targetAppearsAfter) { + ++i; + } + } + + // Else, just copy the child proof as is + else { + subTrans.d_children.push_back(pf->d_children[i]); + ++i; + } + } + Assert(neg >= 0); + + Node n1; + std::stringstream ss, ss2; + //Assert(subTrans.d_children.size() == pf->d_children.size() - 1); + Debug("mgdx") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; + if(pf->d_children.size() > 2) { + n1 = toStreamRecLFSC(ss, tp, &subTrans, 1, map); + } else { + n1 = toStreamRecLFSC(ss, tp, subTrans.d_children[0], 1, map); + Debug("mgdx") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; + } + + Node n2 = pf->d_children[neg]->d_node; + Assert(n2.getKind() == kind::NOT); + Debug("mgdx") << "\nhave proven: " << n1 << std::endl; + Debug("mgdx") << "n2 is " << n2 << std::endl; + Debug("mgdx") << "n2->d_id is " << pf->d_children[neg]->d_id << std::endl; + Debug("mgdx") << "n2[0] is " << n2[0] << std::endl; + + if (n2[0].getNumChildren() > 0) { Debug("mgdx") << "\nn2[0]: " << n2[0][0] << std::endl; } + if (n1.getNumChildren() > 1) { Debug("mgdx") << "n1[1]: " << n1[1] << std::endl; } + + if (pf->d_children[neg]->d_id == theory::eq::MERGED_ARRAYS_EXT) { + // The negative node was created by an EXT rule; e.g. it is a[k]!=b[k], due to a!=b. + + // (clausify_false (contra _ .gl2 (or_elim_1 _ _ .gl1 FIXME))))))) (\ .glemc6 + + out << "(clausify_false (contra _ "; + out << ss.str(); + + toStreamRecLFSC(ss2, tp, pf->d_children[neg], 1, map); + + out << " "; + out << ss2.str(); + out << "))"; + + } else { + // The negative node is, e.g., a pure equality + out << "(clausify_false (contra _ "; + + if(n2[0].getKind() == kind::APPLY_UF) { + out << "(trans _ _ _ _ "; + out << "(symm _ _ _ "; + out << ss.str(); + out << ") (pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; + } else { + Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); + if(n1[1] == n2[0][0]) { + out << "(symm _ _ _ " << ss.str() << ")"; + } else { + out << ss.str(); + } + Debug("pf::array") << "ArrayProof::toStream: getLitName( " << n2[0] << " ) = " << + ProofManager::getLitName(n2[0]) << std::endl; + + out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; + } + } + + return Node(); + } + + switch(pf->d_id) { + case theory::eq::MERGED_THROUGH_CONGRUENCE: { + Debug("mgd") << "\nok, looking at congruence:\n"; + pf->debug_print("mgd"); + std::stack stk; + for(const theory::eq::EqProof* pf2 = pf; pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; pf2 = pf2->d_children[0]) { + Assert(!pf2->d_node.isNull()); + Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF || + pf2->d_node.getKind() == kind::BUILTIN || + pf2->d_node.getKind() == kind::APPLY_UF || + pf2->d_node.getKind() == kind::SELECT || + pf2->d_node.getKind() == kind::PARTIAL_SELECT_0 || + pf2->d_node.getKind() == kind::PARTIAL_SELECT_1 || + pf2->d_node.getKind() == kind::STORE); + + Assert(pf2->d_children.size() == 2); + out << "(cong _ _ _ _ _ _ "; + stk.push(pf2); + } + Assert(stk.top()->d_children[0]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE); + // NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), b2(kind::PARTIAL_APPLY_UF); + NodeBuilder<> b1, b2; + + const theory::eq::EqProof* pf2 = stk.top(); + stk.pop(); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); + Node n1 = toStreamRecLFSC(out, tp, pf2->d_children[0], tb + 1, map); + out << " "; + std::stringstream ss; + Node n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); + + + Debug("mgd") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; + pf2->debug_print("mgd"); + // Temp + Debug("mgd") << "n1 is a proof for: " << pf2->d_children[0]->d_node << ". It is: " << n1 << std::endl; + Debug("mgd") << "n2 is a proof for: " << pf2->d_children[1]->d_node << ". It is: " << n2 << std::endl; + // + Debug("mgd") << "looking at " << pf2->d_node << "\n"; + Debug("mgd") << " " << n1 << "\n"; + Debug("mgd") << " " << n2 << "\n"; + + int side = 0; + if(match(pf2->d_node, n1[0])) { + Debug("mgd") << "SIDE IS 0\n"; + side = 0; + } else { + Debug("mgd") << "SIDE IS 1\n"; + if(!match(pf2->d_node, n1[1])) { + Debug("mgd") << "IN BAD CASE, our first subproof is\n"; + pf2->d_children[0]->debug_print("mgd"); + } + Assert(match(pf2->d_node, n1[1])); + side = 1; + } + + if(n1[side].getKind() == kind::APPLY_UF || + n1[side].getKind() == kind::PARTIAL_APPLY_UF || + n1[side].getKind() == kind::SELECT || + n1[side].getKind() == kind::PARTIAL_SELECT_1 || + n1[side].getKind() == kind::STORE) { + if(n1[side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::PARTIAL_APPLY_UF) { + b1 << kind::PARTIAL_APPLY_UF; + b1 << n1[side].getOperator(); + } else if (n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::PARTIAL_SELECT_1) { + // b1 << n1[side].getKind(); + b1 << kind::SELECT; + } else { + b1 << kind::PARTIAL_APPLY_UF; + b1 << ProofManager::currentPM()->mkOp(n1[side].getOperator()); + } + b1.append(n1[side].begin(), n1[side].end()); + } + else if (n1[side].getKind() == kind::PARTIAL_SELECT_0) { + b1 << kind::PARTIAL_SELECT_1; + } else { + b1 << n1[side]; + } + + if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || + n1[1-side].getKind() == kind::APPLY_UF || + n1[1-side].getKind() == kind::SELECT || + n1[1-side].getKind() == kind::PARTIAL_SELECT_1 || + n1[1-side].getKind() == kind::STORE) { + if(n1[1-side].getKind() == kind::APPLY_UF || + n1[1-side].getKind() == kind::PARTIAL_APPLY_UF) { + b2 << kind::PARTIAL_APPLY_UF; + b2 << n1[1-side].getOperator(); + } else if (n1[1-side].getKind() == kind::SELECT || n1[1-side].getKind() == kind::PARTIAL_SELECT_1) { + // b2 << n1[1-side].getKind(); + b2 << kind::SELECT; + } else { + b2 << kind::PARTIAL_APPLY_UF; + b2 << ProofManager::currentPM()->mkOp(n1[1-side].getOperator()); + } + b2.append(n1[1-side].begin(), n1[1-side].end()); + } else if (n1[1-side].getKind() == kind::PARTIAL_SELECT_0) { + b2 << kind::PARTIAL_SELECT_1; + } else { + b2 << n1[1-side]; + } + Debug("mgd") << "pf2->d_node " << pf2->d_node << std::endl; + Debug("mgd") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; + Debug("mgd") << "n1 " << n1 << std::endl; + Debug("mgd") << "n2 " << n2 << std::endl; + // These debug prints can cause a problem if we're constructing a SELECT node and it doesn't have enough + // children yet. + // Debug("mgd") << "b1 " << b1 << std::endl; + // Debug("mgd") << "b2 " << b2 << std::endl; + Debug("mgd") << "side " << side << std::endl; + Debug("mgd") << "pf2->d_node's number of children: " << pf2->d_node.getNumChildren() << std::endl; + Debug("mgd") << "pf2->d_node's meta kind: " << pf2->d_node.getMetaKind() << std::endl; + Debug("mgd") << "Is this meta kind considered parameterized? " << (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED) << std::endl; + + if(pf2->d_node[b1.getNumChildren() + + (n1[side].getKind() == kind::PARTIAL_SELECT_0 ? 1 : 0) + + (n1[side].getKind() == kind::PARTIAL_SELECT_1 ? 1 : 0) - + (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { + b1 << n2[side]; + b2 << n2[1-side]; + out << ss.str(); + } else { + Assert(pf2->d_node[b1.getNumChildren() + + (n1[side].getKind() == kind::PARTIAL_SELECT_0 ? 1 : 0) + + (n1[side].getKind() == kind::PARTIAL_SELECT_1 ? 1 : 0) - + (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[1-side]); + b1 << n2[1-side]; + b2 << n2[side]; + out << "(symm _ _ _ " << ss.str() << ")"; + } + + Debug("mgd") << "After first insertion:" << std::endl; + Debug("mgd") << "b1 " << b1 << std::endl; + Debug("mgd") << "b2 " << b2 << std::endl; + + out << ")"; + while(!stk.empty()) { + + Debug("mgd") << "\nMORE TO DO\n"; + + pf2 = stk.top(); + stk.pop(); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); + out << " "; + ss.str(""); + n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); + + Debug("mgd") << "\nok, in cong[" << stk.size() << "]" << "\n"; + Debug("mgd") << "looking at " << pf2->d_node << "\n"; + Debug("mgd") << " " << n1 << "\n"; + Debug("mgd") << " " << n2 << "\n"; + Debug("mgd") << " " << b1 << "\n"; + Debug("mgd") << " " << b2 << "\n"; + if(pf2->d_node[b1.getNumChildren()] == n2[side]) { + b1 << n2[side]; + b2 << n2[1-side]; + out << ss.str(); + } else { + Assert(pf2->d_node[b1.getNumChildren()] == n2[1-side]); + b1 << n2[1-side]; + b2 << n2[side]; + out << "(symm _ _ _ " << ss.str() << ")"; + } + out << ")"; + } + n1 = b1; + n2 = b2; + + Debug("mgd") << "at end assert!" << std::endl + << "pf2->d_node = " << pf2->d_node << std::endl + << "n1 (assigned from b1) = " << n1 << std::endl + << "n2 (assigned from b2) = " << n2 << std::endl; + + if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { + Assert(n1 == pf2->d_node); + } + + Debug("mgd") << "n1.getOperator().getType().getNumChildren() = " + << n1.getOperator().getType().getNumChildren() << std::endl; + Debug("mgd") << "n1.getNumChildren() + 1 = " + << n1.getNumChildren() + 1 << std::endl; + + Assert(!((n1.getKind() == kind::PARTIAL_SELECT_0 && n1.getNumChildren() == 2))); + if (n1.getKind() == kind::PARTIAL_SELECT_1 && n1.getNumChildren() == 2) { + Debug("mgd") << "Finished a SELECT. Updating.." << std::endl; + b1.clear(kind::SELECT); + b1.append(n1.begin(), n1.end()); + n1 = b1; + Debug("mgd") << "New n1: " << n1 << std::endl; + // } else if (n1.getKind() == kind::PARTIAL_SELECT_0 && n1.getNumChildren() == 1) { + // Debug("mgd") << "Finished a PARTIAL_SELECT_1. Updating.." << std::endl; + // b1.clear(kind::PARTIAL_SELECT_1); + // b1.append(n1.begin(), n1.end()); + // n1 = b1; + // Debug("mgd") << "New n1: " << n1 << std::endl; + // } else + } else if(n1.getOperator().getType().getNumChildren() == n1.getNumChildren() + 1) { + if(ProofManager::currentPM()->hasOp(n1.getOperator())) { + b1.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst()); + } else { + b1.clear(kind::APPLY_UF); + b1 << n1.getOperator(); + } + b1.append(n1.begin(), n1.end()); + n1 = b1; + Debug("mgd") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; + if(pf2->d_node.getKind() == kind::APPLY_UF) { + Assert(n1 == pf2->d_node); + } + } + + Debug("mgd") << "n2.getOperator().getType().getNumChildren() = " + << n2.getOperator().getType().getNumChildren() << std::endl; + Debug("mgd") << "n2.getNumChildren() + 1 = " + << n2.getNumChildren() + 1 << std::endl; + + Assert(!((n2.getKind() == kind::PARTIAL_SELECT_0 && n2.getNumChildren() == 2))); + if (n2.getKind() == kind::PARTIAL_SELECT_1 && n2.getNumChildren() == 2) { + Debug("mgd") << "Finished a SELECT. Updating.." << std::endl; + b2.clear(kind::SELECT); + b2.append(n2.begin(), n2.end()); + n2 = b2; + Debug("mgd") << "New n2: " << n2 << std::endl; + // } else if (n2.getKind() == kind::PARTIAL_SELECT_0 && n2.getNumChildren() == 1) { + // Debug("mgd") << "Finished a PARTIAL_SELECT_1. Updating.." << std::endl; + // b2.clear(kind::PARTIAL_SELECT_1); + // b2.append(n2.begin(), n2.end()); + // n2 = b2; + // Debug("mgd") << "New n2: " << n2 << std::endl; + // } else + } else if(n2.getOperator().getType().getNumChildren() == n2.getNumChildren() + 1) { + if(ProofManager::currentPM()->hasOp(n2.getOperator())) { + b2.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst()); + } else { + b2.clear(kind::APPLY_UF); + b2 << n2.getOperator(); + } + b2.append(n2.begin(), n2.end()); + n2 = b2; + } + Node n = (side == 0 ? eqNode(n1, n2) : eqNode(n2, n1)); + + Debug("mgdx") << "\ncong proved: " << n << "\n"; + return n; + } + + case theory::eq::MERGED_THROUGH_REFLEXIVITY: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + out << "(refl _ "; + tp->printTerm(NodeManager::currentNM()->toExpr(pf->d_node), out, map); + out << ")"; + return eqNode(pf->d_node, pf->d_node); + + case theory::eq::MERGED_THROUGH_EQUALITY: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + Debug("pf::array") << "ArrayProof::toStream: getLitName( " << pf->d_node.negate() << " ) = " << + ProofManager::getLitName(pf->d_node.negate()) << std::endl; + out << ProofManager::getLitName(pf->d_node.negate()); + return pf->d_node; + + case theory::eq::MERGED_THROUGH_TRANS: { + bool firstNeg = false; + bool secondNeg = false; + + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.size() >= 2); + std::stringstream ss; + Debug("mgd") << "\ndoing trans proof[[\n"; + pf->debug_print("mgd"); + Debug("mgd") << "\n"; + Node n1 = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); + Debug("mgd") << "\ndoing trans proof, got n1 " << n1 << "\n"; + if(tb == 1) { + Debug("mgdx") << "\ntrans proof[0], got n1 " << n1 << "\n"; + } + + bool identicalEqualities = false; + bool evenLengthSequence; + Node nodeAfterEqualitySequence; + + std::map childToStream; + + for(size_t i = 1; i < pf->d_children.size(); ++i) { + std::stringstream ss1(ss.str()), ss2; + ss.str(""); + + // It is possible that we've already converted the i'th child to stream. If so, + // use previously stored result. Otherwise, convert and store. + Node n2; + if (childToStream.find(i) != childToStream.end()) + n2 = childToStream[i]; + else { + n2 = toStreamRecLFSC(ss2, tp, pf->d_children[i], tb + 1, map); + childToStream[i] = n2; + } + + Debug("mgd") << "\ndoing trans proof, got (first) n2 " << n2 << "\n"; + + // The following branch is dedicated to handling sequences of identical equalities, + // i.e. trans[ a=b, a=b, a=b ]. + // + // There are two cases: + // 1. The number of equalities is odd. Then, the sequence can be collapsed to just one equality, + // i.e. a=b. + // 2. The number of equalities is even. Now, we have two options: a=a or b=b. To determine this, + // we look at the node after the equality sequence. If it needs a, we go for a=a; and if it needs + // b, we go for b=b. If there is no following node, we look at the goal of the transitivity proof, + // and use it to determine which option we need. + if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { + if (((n1[0] == n2[0]) && (n1[1] == n2[1])) || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) { + // We are in a sequence of identical equalities + + Debug("pf::array") << "Detected identical equalities: " << std::endl << "\t" << n1 << std::endl; + + if (!identicalEqualities) { + // The sequence of identical equalities has started just now + identicalEqualities = true; + + Debug("pf::array") << "The sequence is just beginning. Determining length..." << std::endl; + + // Determine whether the length of this sequence is odd or even. + evenLengthSequence = true; + bool sequenceOver = false; + size_t j = i + 1; + + while (j < pf->d_children.size() && !sequenceOver) { + std::stringstream dontCare; + nodeAfterEqualitySequence = toStreamRecLFSC(dontCare, tp, pf->d_children[j], tb + 1, map ); + + if (((nodeAfterEqualitySequence[0] == n1[0]) && (nodeAfterEqualitySequence[1] == n1[1])) || + ((nodeAfterEqualitySequence[0] == n1[1]) && (nodeAfterEqualitySequence[1] == n1[0]))) { + evenLengthSequence = !evenLengthSequence; + } else { + sequenceOver = true; + } + + ++j; + } + + if (evenLengthSequence) { + // If the length is even, we need to apply transitivity for the "correct" hand of the equality. + + Debug("pf::array") << "Equality sequence of even length" << std::endl; + Debug("pf::array") << "n1 is: " << n1 << std::endl; + Debug("pf::array") << "n2 is: " << n2 << std::endl; + Debug("pf::array") << "pf-d_node is: " << pf->d_node << std::endl; + Debug("pf::array") << "Next node is: " << nodeAfterEqualitySequence << std::endl; + + ss << "(trans _ _ _ _ "; + + // If the sequence is at the very end of the transitivity proof, use pf->d_node to guide us. + if (!sequenceOver) { + if (match(n1[0], pf->d_node[0])) { + n1 = eqNode(n1[0], n1[0]); + ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; + } else if (match(n1[1], pf->d_node[1])) { + n1 = eqNode(n1[1], n1[1]); + ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); + } else { + Debug("pf::array") << "Error: identical equalities over, but hands don't match what we're proving." + << std::endl; + Assert(false); + } + } else { + // We have a "next node". Use it to guide us. + if (nodeAfterEqualitySequence.getKind() == kind::NOT) { + nodeAfterEqualitySequence = nodeAfterEqualitySequence[0]; + } + + Assert(nodeAfterEqualitySequence.getKind() == kind::EQUAL || + nodeAfterEqualitySequence.getKind() == kind::IFF); + + if ((n1[0] == nodeAfterEqualitySequence[0]) || (n1[0] == nodeAfterEqualitySequence[1])) { + + // Eliminate n1[1] + ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; + n1 = eqNode(n1[0], n1[0]); + + } else if ((n1[1] == nodeAfterEqualitySequence[0]) || (n1[1] == nodeAfterEqualitySequence[1])) { + + // Eliminate n1[0] + ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); + n1 = eqNode(n1[1], n1[1]); + + } else { + Debug("pf::array") << "Error: even length sequence, but I don't know which hand to keep!" << std::endl; + Assert(false); + } + } + + ss << ")"; + + } else { + Debug("pf::array") << "Equality sequence length is odd!" << std::endl; + ss.str(ss1.str()); + } + + Debug("pf::array") << "Have proven: " << n1 << std::endl; + } else { + ss.str(ss1.str()); + } + + // Ignore the redundancy. + continue; + } + } + + if (identicalEqualities) { + // We were in a sequence of identical equalities, but it has now ended. Resume normal operation. + identicalEqualities = false; + } + + Debug("mgd") << "\ndoing trans proof, got n2 " << n2 << "\n"; + if(tb == 1) { + Debug("mgdx") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; + Debug("mgdx") << (n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) << "\n"; + + if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) { + Debug("mgdx") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; + Debug("mgdx") << n1[0].getId() << " " << n1[0] << "\n"; + Debug("mgdx") << n1[1].getId() << " " << n1[1] << "\n"; + Debug("mgdx") << n2[0].getId() << " " << n2[0] << "\n"; + Debug("mgdx") << n2[1].getId() << " " << n2[1] << "\n"; + Debug("mgdx") << (n1[0] == n2[0]) << "\n"; + Debug("mgdx") << (n1[1] == n2[1]) << "\n"; + Debug("mgdx") << (n1[0] == n2[1]) << "\n"; + Debug("mgdx") << (n1[1] == n2[0]) << "\n"; + } + } + + // We can hadnle one of the equalities being negative, but not both + Assert((n1.getKind() != kind::NOT) || (n2.getKind() != kind::NOT)); + + firstNeg = false; + secondNeg = false; + + if (n1.getKind() == kind::NOT) { + Debug("mgdx") << "n1 is negative" << std::endl; + Debug("pf::array") << "n1 = " << n1 << ", n2 = " << n2 << std::endl; + firstNeg = true; + ss << "(negtrans1 _ _ _ _ "; + n1 = n1[0]; + } else if (n2.getKind() == kind::NOT) { + Debug("mgdx") << "n2 is negative" << std::endl; + Debug("pf::array") << "n1 = " << n1 << ", n2 = " << n2 << std::endl; + secondNeg = true; + ss << "(negtrans2 _ _ _ _ "; + n2 = n2[0]; + } else { + ss << "(trans _ _ _ _ "; + } + + if((n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) && + (n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF)) + // Both elements of the transitivity rule are equalities/iffs + { + if(n1[0] == n2[0]) { + if(tb == 1) { Debug("mgdx") << "case 1\n"; } + n1 = eqNode(n1[1], n2[1]); + ss << (firstNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") << ss1.str() << ") " << ss2.str(); + } else if(n1[1] == n2[1]) { + if(tb == 1) { Debug("mgdx") << "case 2\n"; } + n1 = eqNode(n1[0], n2[0]); + ss << ss1.str() << (secondNeg ? " (negsymm _ _ _ " : " (symm _ _ _ " ) << ss2.str() << ")"; + } else if(n1[0] == n2[1]) { + if(tb == 1) { Debug("mgdx") << "case 3\n"; } + if(!firstNeg && !secondNeg) { + n1 = eqNode(n2[0], n1[1]); + ss << ss2.str() << " " << ss1.str(); + } else if (firstNeg) { + n1 = eqNode(n1[1], n2[0]); + ss << " (negsymm _ _ _ " << ss1.str() << ") (symm _ _ _ " << ss2.str() << ")"; + } else { + Assert(secondNeg); + n1 = eqNode(n1[1], n2[0]); + ss << " (symm _ _ _ " << ss1.str() << ") (negsymm _ _ _ " << ss2.str() << ")"; + } + if(tb == 1) { Debug("mgdx") << "++ proved " << n1 << "\n"; } + } else if(n1[1] == n2[0]) { + if(tb == 1) { Debug("mgdx") << "case 4\n"; } + n1 = eqNode(n1[0], n2[1]); + ss << ss1.str() << " " << ss2.str(); + } else { + Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; + Warning() << "0 proves " << n1 << "\n"; + Warning() << "1 proves " << n2 << "\n\n"; + pf->debug_print("mgdx",0); + //toStreamRec(Warning.getStream(), pf, 0); + Warning() << "\n\n"; + Unreachable(); + } + Debug("mgd") << "++ trans proof[" << i << "], now have " << n1 << std::endl; + } else if(n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF) { + // n1 is an equality/iff, but n2 is a predicate + if(n1[0] == n2) { + n1 = n1[1]; + ss << (firstNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") + << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; + } else if(n1[1] == n2) { + n1 = n1[0]; + ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; + } else { + Unreachable(); + } + } else if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { + // n2 is an equality/iff, but n1 is a predicate + if(n2[0] == n1) { + n1 = n2[1]; + ss << (secondNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") + << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; + } else if(n2[1] == n1) { + n1 = n2[0]; + ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; + } else { + Unreachable(); + } + } else { + // Both n1 and n2 are prediacates. Don't know what to do... + Unreachable(); + } + + ss << ")"; + + if (firstNeg || secondNeg) { + n1 = (n1.getKind() == kind::NOT) ? n1[0] : n1.notNode(); + } + } + + out << ss.str(); + Debug("mgd") << "\n++ trans proof done, have proven " << n1 << std::endl; + //return (firstNeg || secondNeg) ? n1.notNode() : n1; + return n1; + } + + case theory::eq::MERGED_ARRAYS_ROW: { + Debug("mgd") << "row lemma: " << pf->d_node << std::endl; + Assert(pf->d_node.getKind() == kind::EQUAL); + + + if (pf->d_node[1].getKind() == kind::SELECT) { + // This is the case where ((a[i]:=t)[k] == a[k]), and the sub-proof explains why (i != k). + TNode t1, t2, t3, t4; + Node ret; + if(pf->d_node[1].getKind() == kind::SELECT && + pf->d_node[1][0].getKind() == kind::STORE && + pf->d_node[0].getKind() == kind::SELECT && + pf->d_node[0][0] == pf->d_node[1][0][0] && + pf->d_node[0][1] == pf->d_node[1][1]) { + t2 = pf->d_node[1][0][1]; + t3 = pf->d_node[1][1]; + t1 = pf->d_node[0][0]; + t4 = pf->d_node[1][0][2]; + ret = pf->d_node[1].eqNode(pf->d_node[0]); + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; + } else { + Assert(pf->d_node[0].getKind() == kind::SELECT && + pf->d_node[0][0].getKind() == kind::STORE && + pf->d_node[1].getKind() == kind::SELECT && + pf->d_node[1][0] == pf->d_node[0][0][0] && + pf->d_node[1][1] == pf->d_node[0][1]); + t2 = pf->d_node[0][0][1]; + t3 = pf->d_node[0][1]; + t1 = pf->d_node[1][0]; + t4 = pf->d_node[0][0][2]; + ret = pf->d_node; + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; + } + + // inner index != outer index + // t3 is the outer index + + + Assert(pf->d_children.size() == 1); + std::stringstream ss; + Node subproof = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); + + out << "(row _ _ "; + tp->printTerm(t2.toExpr(), out, map); + out << " "; + tp->printTerm(t3.toExpr(), out, map); + out << " "; + tp->printTerm(t1.toExpr(), out, map); + out << " "; + tp->printTerm(t4.toExpr(), out, map); + out << " "; + + Debug("pf::array") << "pf->d_children[0]->d_node is: " << pf->d_children[0]->d_node + << ". t3 is: " << t3 << std::endl + << "subproof is: " << subproof << std::endl; + + Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; + + if (subproof[0][1] == t3) { + Debug("pf::array") << "Dont need symmetry!" << std::endl; + out << ss.str(); + } else { + Debug("pf::array") << "Need symmetry!" << std::endl; + out << "(negsymm _ _ _ " << ss.str() << ")"; + } + + out << ")"; + + return ret; + } else { + Debug("pf::array") << "In the case of NEGATIVE ROW" << std::endl; + + Debug("pf::array") << "pf->d_children[0]->d_node is: " << pf->d_children[0]->d_node << std::endl; + + // This is the case where (i == k), and the sub-proof explains why ((a[i]:=t)[k] != a[k]) + + // If we wanted to remove the need for "negativerow", we would need to prove i==k using a new satlem. We would: + // 1. Create a new satlem. + // 2. Assume that i != k + // 3. Apply ROW to show that ((a[i]:=t)[k] == a[k]) + // 4. Contradict this with the fact that ((a[i]:=t)[k] != a[k]), obtaining our contradiction + + TNode t1, t2, t3, t4; + Node ret; + + // pf->d_node is an equality, i==k. + t1 = pf->d_node[0]; + t2 = pf->d_node[1]; + + // pf->d_children[0]->d_node will have the form: (not (= (select (store a_565 i7 e_566) i1) (select a_565 i1))), + // or its symmetrical version. + + unsigned side; + if (pf->d_children[0]->d_node[0][0].getKind() == kind::SELECT && + pf->d_children[0]->d_node[0][0][0].getKind() == kind::STORE) { + side = 0; + } else if (pf->d_children[0]->d_node[0][1].getKind() == kind::SELECT && + pf->d_children[0]->d_node[0][1][0].getKind() == kind::STORE) { + side = 1; + } + else { + Unreachable(); + } + + Debug("pf::array") << "Side is: " << side << std::endl; + + // The array's index and element types will come from the subproof... + t3 = pf->d_children[0]->d_node[0][side][0][0]; + t4 = pf->d_children[0]->d_node[0][side][0][2]; + ret = pf->d_node; + + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; + + Assert(pf->d_children.size() == 1); + std::stringstream ss; + Node subproof = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); + + Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; + + out << "(negativerow _ _ "; + tp->printTerm(t1.toExpr(), out, map); + out << " "; + tp->printTerm(t2.toExpr(), out, map); + out << " "; + tp->printTerm(t3.toExpr(), out, map); + out << " "; + tp->printTerm(t4.toExpr(), out, map); + out << " "; + + + // if (subproof[0][1] == t3) { + Debug("pf::array") << "Dont need symmetry!" << std::endl; + out << ss.str(); + // } else { + // Debug("pf::array") << "Need symmetry!" << std::endl; + // out << "(negsymm _ _ _ " << ss.str() << ")"; + // } + + out << ")"; + + // Unreachable(); + + return ret; + } + } + + case theory::eq::MERGED_ARRAYS_ROW1: { + Debug("mgd") << "row1 lemma: " << pf->d_node << std::endl; + Assert(pf->d_node.getKind() == kind::EQUAL); + TNode t1, t2, t3; + Node ret; + if(pf->d_node[1].getKind() == kind::SELECT && + pf->d_node[1][0].getKind() == kind::STORE && + pf->d_node[1][0][1] == pf->d_node[1][1] && + pf->d_node[1][0][2] == pf->d_node[0]) { + t1 = pf->d_node[1][0][0]; + t2 = pf->d_node[1][0][1]; + t3 = pf->d_node[0]; + ret = pf->d_node[1].eqNode(pf->d_node[0]); + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; + } else { + Assert(pf->d_node[0].getKind() == kind::SELECT && + pf->d_node[0][0].getKind() == kind::STORE && + pf->d_node[0][0][1] == pf->d_node[0][1] && + pf->d_node[0][0][2] == pf->d_node[1]); + t1 = pf->d_node[0][0][0]; + t2 = pf->d_node[0][0][1]; + t3 = pf->d_node[1]; + ret = pf->d_node; + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; + } + out << "(row1 _ _ "; + tp->printTerm(t1.toExpr(), out, map); + out << " "; + tp->printTerm(t2.toExpr(), out, map); + out << " "; + tp->printTerm(t3.toExpr(), out, map); + out << ")"; + return ret; + } + + case theory::eq::MERGED_ARRAYS_EXT: { + theory::eq::EqProof *child_proof; + + Assert(pf->d_node.getKind() == kind::NOT); + Assert(pf->d_node[0].getKind() == kind::EQUAL); + Assert(pf->d_children.size() == 1); + + child_proof = pf->d_children[0]; + Assert(child_proof->d_node.getKind() == kind::NOT); + Assert(child_proof->d_node[0].getKind() == kind::EQUAL); + + Debug("mgd") << "EXT lemma: " << pf->d_node << std::endl; + + TNode t1, t2, t3; + t1 = child_proof->d_node[0][0]; + t2 = child_proof->d_node[0][1]; + t3 = pf->d_node[0][0][1]; + + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; + + out << "(or_elim_1 _ _ "; + out << ProofManager::getLitName(child_proof->d_node[0]); + out << " "; + out << ProofManager::getArrayProof()->skolemToLiteral(t3.toExpr()); + out << ")"; + + return pf->d_node; + } + + default: + Assert(!pf->d_node.isNull()); + Assert(pf->d_children.empty()); + Debug("mgd") << "theory proof: " << pf->d_node << " by rule " << int(pf->d_id) << std::endl; + AlwaysAssert(false); + return pf->d_node; + } +} + +ArrayProof::ArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* pe) + : TheoryProof(arrays, pe) +{} + +void ArrayProof::registerTerm(Expr term) { + // already registered + if (d_declarations.find(term) != d_declarations.end()) + return; + + Type type = term.getType(); + if (type.isSort()) { + // declare uninterpreted sorts + d_sorts.insert(type); + } + + if (term.getKind() == kind::APPLY_UF) { + Expr function = term.getOperator(); + d_declarations.insert(function); + } + + if (term.isVariable()) { + d_declarations.insert(term); + } + + // recursively declare all other terms + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + // could belong to other theories + d_proofEngine->registerTerm(term[i]); + } +} + +std::string ArrayProof::skolemToLiteral(Expr skolem) { + Assert(d_skolemToLiteral.find(skolem) != d_skolemToLiteral.end()); + return d_skolemToLiteral[skolem]; +} + +void LFSCArrayProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { + Debug("pf::array") << std::endl << "(pf::array) LFSCArrayProof::printOwnedTerm: term = " << term << std::endl; + + Assert (theory::Theory::theoryOf(term) == theory::THEORY_ARRAY); + + if (theory::Theory::theoryOf(term) != theory::THEORY_ARRAY) { + // We can get here, for instance, if there's a (select ite ...), e.g. a non-array term + // hiding as a subterm of an array term. In that case, send it back to the dispatcher. + d_proofEngine->printBoundTerm(term, os, map); + return; + } + + if (term.getKind() == kind::VARIABLE || term.getKind() == kind::SKOLEM) { + os << term; + return; + } + + Assert ((term.getKind() == kind::SELECT) || (term.getKind() == kind::PARTIAL_SELECT_0) || (term.getKind() == kind::PARTIAL_SELECT_1) || (term.getKind() == kind::STORE)); + + switch (term.getKind()) { + case kind::SELECT: + Assert(term.getNumChildren() == 2); + os << "(apply _ _ (apply _ _ (read "; + printSort(ArrayType(term[0].getType()).getIndexType(), os); + os << " "; + printSort(ArrayType(term[0].getType()).getConstituentType(), os); + os << ") "; + printTerm(term[0], os, map); + os << ") "; + printTerm(term[1], os, map); + os << ") "; + return; + + case kind::PARTIAL_SELECT_0: + Assert(term.getNumChildren() == 1); + os << "(read "; + printSort(ArrayType(term[0].getType()).getIndexType(), os); + os << " "; + printSort(ArrayType(term[0].getType()).getConstituentType(), os); + os << ") "; + return; + + case kind::PARTIAL_SELECT_1: + Debug("pf::array") << "This branch has not beed tested yet." << std::endl; + Unreachable(); + + Assert(term.getNumChildren() == 1); + os << "(apply _ _ (read "; + printSort(ArrayType(term[0].getType()).getIndexType(), os); + os << " "; + printSort(ArrayType(term[0].getType()).getConstituentType(), os); + os << ") "; + printTerm(term[0], os, map); + os << ") "; + return; + + case kind::STORE: + os << "(apply _ _ (apply _ _ (apply _ _ (write "; + printSort(ArrayType(term[0].getType()).getIndexType(), os); + os << " "; + printSort(ArrayType(term[0].getType()).getConstituentType(), os); + os << ") "; + printTerm(term[0], os, map); + os << ") "; + printTerm(term[1], os, map); + os << ") "; + printTerm(term[2], os, map); + os << ") "; + return; + + default: + Unreachable(); + return; + } +} + +void LFSCArrayProof::printOwnedSort(Type type, std::ostream& os) { + Debug("pf::array") << std::endl << "(pf::array) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; + Assert (type.isArray() || type.isSort()); + os << type <<" "; +} + +void LFSCArrayProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) { + os << " ;; Array Theory Lemma \n;;"; + for (unsigned i = 0; i < lemma.size(); ++i) { + os << lemma[i] <<" "; + } + os <<"\n"; + //os << " (clausify_false trust)"; + ArrayProof::printTheoryLemmaProof(lemma, os, paren); +} + +void LFSCArrayProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { + // declaring the sorts + Debug("pf::array") << "Arrays declaring sorts..." << std::endl; + + for (TypeSet::const_iterator it = d_sorts.begin(); it != d_sorts.end(); ++it) { + if (!ProofManager::currentPM()->wasPrinted(*it)) { + os << "(% " << *it << " sort\n"; + paren << ")"; + ProofManager::currentPM()->markPrinted(*it); + } + } +} + +void LFSCArrayProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::array") << "Arrays declaring terms..." << std::endl; + + for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { + Expr term = *it; + + Assert(term.getType().isArray() || term.isVariable()); + + Debug("pf::array") << "LFSCArrayProof::printDeclarations: term is: " << term + << ". It's type is: " << term.getType() + << std::endl; + + if (term.getType().isArray()){ + ArrayType array_type(term.getType()); + + Debug("pf::array") << "LFSCArrayProof::printDeclarations: term is an array. Index type: " + << array_type.getIndexType() + << ", element type: " << array_type.getConstituentType() << std::endl; + + os << "(% " << ProofManager::sanitize(term) << " "; + os << "(term "; + os << "(Array "; + + printSort(array_type.getIndexType(), os); + os << " "; + printSort(array_type.getConstituentType(), os); + + os << "))\n"; + } else { + Assert(term.isVariable()); + if (ProofManager::getSkolemizationManager()->isSkolem(*it)) { + Debug("pf::array") << "This term is a skoelm!" << std::endl; + d_skolemDeclarations.insert(*it); + } else { + os << "(% " << ProofManager::sanitize(term) << " "; + os << "(term "; + os << term.getType() << ")\n"; + } + } + + paren << ")"; + } + + Debug("pf::array") << "Declaring terms done!" << std::endl; +} + +void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::array") << "Array: print deferred declarations called" << std::endl; + + for (ExprSet::const_iterator it = d_skolemDeclarations.begin(); it != d_skolemDeclarations.end(); ++it) { + Expr term = *it; + Node equality = ProofManager::getSkolemizationManager()->getDisequality(*it); + + Debug("pf::array") << "LFSCArrayProof::printDeferredDeclarations: term is: " << *it << std::endl + << "It is a witness for: " << equality << std::endl; + + std::ostringstream newSkolemLiteral; + newSkolemLiteral << ".sl" << d_skolemToLiteral.size(); + std::string skolemLiteral = newSkolemLiteral.str(); + + d_skolemToLiteral[*it] = skolemLiteral; + + Debug("pf::array") << "LFSCArrayProof::printDeferredDeclarations: new skolem literal is: " << skolemLiteral << std::endl; + + Assert(equality.getKind() == kind::NOT); + Assert(equality[0].getKind() == kind::EQUAL); + + Node array_one = equality[0][0]; + Node array_two = equality[0][1]; + + LetMap map; + + os << "(ext _ _ "; + printTerm(array_one.toExpr(), os, map); + os << " "; + printTerm(array_two.toExpr(), os, map); + os << " (\\ "; + printTerm(*it, os, map); + os << " (\\ "; + os << skolemLiteral.c_str(); + os << "\n"; + + paren << ")))"; + } +} + +} /* CVC4 namespace */ diff --git a/src/proof/array_proof.h b/src/proof/array_proof.h index beaf5194c..9b308dcd8 100644 --- a/src/proof/array_proof.h +++ b/src/proof/array_proof.h @@ -23,53 +23,65 @@ #include "proof/proof_manager.h" #include "proof/theory_proof.h" #include "theory/arrays/theory_arrays.h" +#include "theory/uf/equality_engine.h" namespace CVC4 { +//proof object outputted by TheoryARRAY +class ProofArray : public Proof { +private: + static Node toStreamRecLFSC(std::ostream& out, TheoryProof* tp, + theory::eq::EqProof* pf, + unsigned tb, + const LetMap& map); + + std::hash_map d_nodeToSkolem; +public: + ProofArray(theory::eq::EqProof* pf) : d_proof(pf) {} + //it is simply an equality engine proof + theory::eq::EqProof *d_proof; + void toStream(std::ostream& out); + static void toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const LetMap& map); + + void registerSkolem(Node equality, Node skolem); +}; + namespace theory { namespace arrays{ class TheoryArrays; } /* namespace CVC4::theory::arrays */ } /* namespace CVC4::theory */ +typedef __gnu_cxx::hash_set TypeSet; + class ArrayProof : public TheoryProof { // TODO: whatever goes in this theory +protected: + TypeSet d_sorts; // all the uninterpreted sorts in this theory + ExprSet d_declarations; // all the variable/function declarations + ExprSet d_skolemDeclarations; // all the skolem variable declarations + std::map d_skolemToLiteral; + public: - ArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* proofEngine) - : TheoryProof(arrays, proofEngine) - {} - virtual void registerTerm(Expr term) {} - - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map) = 0; - virtual void printSort(Type type, std::ostream& os) = 0; - /** - * Print a proof for the theory lemma. Must prove - * clause representing lemma to be used in resolution proof. - * - * @param lemma clausal form of lemma - * @param os output stream - */ - virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) = 0; - /** - * Print the variable/sorts declarations for this theory. - * - * @param os - * @param paren - */ - virtual void printDeclarations(std::ostream& os, std::ostream& paren) = 0; + ArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* proofEngine); + + std::string skolemToLiteral(Expr skolem); + + virtual void registerTerm(Expr term); }; class LFSCArrayProof : public ArrayProof { public: - LFSCArrayProof(theory::arrays::TheoryArrays* uf, TheoryProofEngine* proofEngine) - : ArrayProof(uf, proofEngine) + LFSCArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* proofEngine) + : ArrayProof(arrays, proofEngine) {} - // TODO implement - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map) {} - virtual void printSort(Type type, std::ostream& os) {} - virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) {} - virtual void printDeclarations(std::ostream& os, std::ostream& paren) {} + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedSort(Type type, std::ostream& os); + virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); }; diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index a018d8bc5..cc1003b2c 100644 --- a/src/proof/bitvector_proof.cpp +++ b/src/proof/bitvector_proof.cpp @@ -15,8 +15,7 @@ ** \todo document this file **/ -#include "proof/bitvector_proof.h" - +#include "proof/bitvector_proof.h" #include "options/bv_options.h" #include "proof/clause_id.h" #include "proof/proof_utils.h" @@ -131,10 +130,10 @@ void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl expr_confl.push_back(expr_lit); } Expr conflict = utils::mkSortedExpr(kind::OR, expr_confl); - Debug("bv-proof") << "Make conflict for " << conflict << std::endl; + Debug("pf::bv") << "Make conflict for " << conflict << std::endl; if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) { - Debug("bv-proof") << "Abort...already conflict for " << conflict << std::endl; + Debug("pf::bv") << "Abort...already conflict for " << conflict << std::endl; // This can only happen when we have eager explanations in the bv solver // if we don't get to propagate p before ~p is already asserted d_resolutionProof->cancelResChain(); @@ -145,30 +144,33 @@ void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl ClauseId clause_id = d_resolutionProof->registerAssumptionConflict(confl); d_bbConflictMap[conflict] = clause_id; d_resolutionProof->endResChain(clause_id); - Debug("bv-proof") << "BitVectorProof::endBVConflict id"< " << conflict << "\n"; + Debug("pf::bv") << "BitVectorProof::endBVConflict id"< " << conflict << "\n"; d_isAssumptionConflict = false; } void BitVectorProof::finalizeConflicts(std::vector& conflicts) { if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { - Debug("bv-proof") << "Construct full proof." << std::endl; + Debug("pf::bv") << "Construct full proof." << std::endl; d_resolutionProof->constructProof(); return; } for(unsigned i = 0; i < conflicts.size(); ++i) { Expr confl = conflicts[i]; - Debug("bv-proof") << "Finalize conflict " << confl << std::endl; + Debug("pf::bv") << "Finalize conflict " << confl << std::endl; //Assert (d_bbConflictMap.find(confl) != d_bbConflictMap.end()); if(d_bbConflictMap.find(confl) != d_bbConflictMap.end()){ ClauseId id = d_bbConflictMap[confl]; d_resolutionProof->collectClauses(id); }else{ - Debug("bv-proof") << "Do not collect clauses for " << confl << std::endl; + Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl; } } } -void LFSCBitVectorProof::printTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { + Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedTerm( " << term << " ), theory is: " + << Theory::theoryOf(term) << std::endl; + Assert (Theory::theoryOf(term) == THEORY_BV); // peel off eager bit-blasting trick @@ -233,7 +235,7 @@ void LFSCBitVectorProof::printTerm(Expr term, std::ostream& os, const LetMap& ma return; } case kind::BITVECTOR_BITOF : { - printBitOf(term, os); + printBitOf(term, os, map); return; } case kind::VARIABLE: @@ -246,13 +248,25 @@ void LFSCBitVectorProof::printTerm(Expr term, std::ostream& os, const LetMap& ma } } -void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os) { +void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const LetMap& map) { Assert (term.getKind() == kind::BITVECTOR_BITOF); unsigned bit = term.getOperator().getConst().bitIndex; Expr var = term[0]; - Assert (var.getKind() == kind::VARIABLE || - var.getKind() == kind::SKOLEM); - os << "(bitof " << ProofManager::sanitize(var) <<" " << bit <<")"; + + Debug("pf::bv") << "LFSCBitVectorProof::printBitOf( " << term << " ), " + << "bit = " << bit + << ", var = " << var << std::endl; + + os << "(bitof "; + if (var.getKind() == kind::VARIABLE || var.getKind() == kind::SKOLEM) { + // If var is "simple", we can just sanitize and print + os << ProofManager::sanitize(var); + } else { + // If var is "complex", it can belong to another theory. Therefore, dispatch again. + d_proofEngine->printBoundTerm(var, os, map); + } + + os << " " << bit << ")"; } void LFSCBitVectorProof::printConstant(Expr term, std::ostream& os) { @@ -333,7 +347,9 @@ void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, co os <<")"; } -void LFSCBitVectorProof::printSort(Type type, std::ostream& os) { +void LFSCBitVectorProof::printOwnedSort(Type type, std::ostream& os) { + Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedSort( " << type << " )" << std::endl; + Assert (type.isBitVector()); unsigned width = utils::getSize(type); os << "(BitVec "<& lemma, std::os ClauseId lemma_id = d_bbConflictMap[lem]; d_resolutionProof->printAssumptionsResolution(lemma_id, os, lemma_paren); os < +//#include #include #include #include @@ -45,7 +45,7 @@ template class TBitblaster; } /* namespace CVC4::theory::bv */ } /* namespace CVC4::theory */ -class CnfProof; +class CnfProof; } /* namespace CVC4 */ namespace CVC4 { @@ -109,7 +109,7 @@ public: virtual void printTermBitblasting(Expr term, std::ostream& os) = 0; virtual void printAtomBitblasting(Expr term, std::ostream& os) = 0; - + virtual void printBitblasting(std::ostream& os, std::ostream& paren) = 0; virtual void printResolutionProof(std::ostream& os, std::ostream& paren) = 0; @@ -122,17 +122,19 @@ class LFSCBitVectorProof: public BitVectorProof { void printOperatorUnary(Expr term, std::ostream& os, const LetMap& map); void printPredicate(Expr term, std::ostream& os, const LetMap& map); void printOperatorParametric(Expr term, std::ostream& os, const LetMap& map); - void printBitOf(Expr term, std::ostream& os); + void printBitOf(Expr term, std::ostream& os, const LetMap& map); public: LFSCBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) :BitVectorProof(bv, proofEngine) {} - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map); - virtual void printSort(Type type, std::ostream& os); + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedSort(Type type, std::ostream& os); virtual void printTermBitblasting(Expr term, std::ostream& os); virtual void printAtomBitblasting(Expr term, std::ostream& os); virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); - virtual void printDeclarations(std::ostream& os, std::ostream& paren); + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); virtual void printBitblasting(std::ostream& os, std::ostream& paren); virtual void printResolutionProof(std::ostream& os, std::ostream& paren); }; diff --git a/src/proof/cnf_proof.cpp b/src/proof/cnf_proof.cpp index 31c976a63..f9823ea92 100644 --- a/src/proof/cnf_proof.cpp +++ b/src/proof/cnf_proof.cpp @@ -24,16 +24,15 @@ #include "prop/minisat/minisat.h" #include "prop/sat_solver_types.h" -using namespace CVC4::prop; - namespace CVC4 { -CnfProof::CnfProof(CnfStream* stream, +CnfProof::CnfProof(prop::CnfStream* stream, context::Context* ctx, const std::string& name) : d_cnfStream(stream) , d_clauseToAssertion(ctx) , d_assertionToProofRule(ctx) + , d_clauseIdToOwnerTheory(ctx) , d_currentAssertionStack() , d_currentDefinitionStack() , d_clauseToDefinition(ctx) @@ -56,12 +55,13 @@ bool CnfProof::isDefinition(Node node) { return d_definitions.find(node) != d_definitions.end(); } - + ProofRule CnfProof::getProofRule(Node node) { Assert (isAssertion(node)); NodeToProofRule::iterator it = d_assertionToProofRule.find(node); return (*it).second; } + ProofRule CnfProof::getProofRule(ClauseId clause) { TNode assertion = getAssertionForClause(clause); return getProofRule(assertion); @@ -96,13 +96,14 @@ void CnfProof::registerConvertedClause(ClauseId clause, bool explanation) { Node current_assertion = getCurrentAssertion(); Node current_expr = getCurrentDefinition(); - + Debug("proof:cnf") << "CnfProof::registerConvertedClause " << clause << " assertion = " << current_assertion << clause << " definition = " << current_expr << std::endl; setClauseAssertion(clause, current_assertion); setClauseDefinition(clause, current_expr); + registerExplanationLemma(clause); } void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { @@ -114,7 +115,7 @@ void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { // that since b is top level, it is not cached by the CnfStream) if (d_clauseToAssertion.find(clause) != d_clauseToAssertion.end()) return; - + d_clauseToAssertion.insert (clause, expr); } @@ -128,7 +129,7 @@ void CnfProof::setClauseDefinition(ClauseId clause, Node definition) { d_clauseToDefinition.insert(clause, definition); d_definitions.insert(definition); } - + void CnfProof::registerAssertion(Node assertion, ProofRule reason) { Debug("proof:cnf") << "CnfProof::registerAssertion " << assertion << " reason " << reason << std::endl; @@ -142,6 +143,16 @@ void CnfProof::registerAssertion(Node assertion, ProofRule reason) { d_assertionToProofRule.insert(assertion, reason); } +void CnfProof::registerExplanationLemma(ClauseId clauseId) { + d_clauseIdToOwnerTheory.insert(clauseId, getExplainerTheory()); +} + +theory::TheoryId CnfProof::getOwnerTheory(ClauseId clause) { + Assert(d_clauseIdToOwnerTheory.find(clause) != d_clauseIdToOwnerTheory.end()); + return d_clauseIdToOwnerTheory[clause]; +} + + void CnfProof::setCnfDependence(Node from, Node to) { Debug("proof:cnf") << "CnfProof::setCnfDependence " << "from " << from << std::endl @@ -160,10 +171,10 @@ void CnfProof::pushCurrentAssertion(Node assertion) { void CnfProof::popCurrentAssertion() { Assert (d_currentAssertionStack.size()); - + Debug("proof:cnf") << "CnfProof::popCurrentAssertion " << d_currentAssertionStack.back() << std::endl; - + d_currentAssertionStack.pop_back(); } @@ -172,6 +183,14 @@ Node CnfProof::getCurrentAssertion() { return d_currentAssertionStack.back(); } +void CnfProof::setExplainerTheory(theory::TheoryId theory) { + d_explainerTheory = theory; +} + +theory::TheoryId CnfProof::getExplainerTheory() { + return d_explainerTheory; +} + void CnfProof::pushCurrentDefinition(Node definition) { Debug("proof:cnf") << "CnfProof::pushCurrentDefinition " << definition << std::endl; @@ -181,10 +200,10 @@ void CnfProof::pushCurrentDefinition(Node definition) { void CnfProof::popCurrentDefinition() { Assert (d_currentDefinitionStack.size()); - + Debug("proof:cnf") << "CnfProof::popCurrentDefinition " << d_currentDefinitionStack.back() << std::endl; - + d_currentDefinitionStack.pop_back(); } @@ -204,8 +223,8 @@ Node CnfProof::getAtom(prop::SatVariable var) { void CnfProof::collectAtoms(const prop::SatClause* clause, NodeSet& atoms) { for (unsigned i = 0; i < clause->size(); ++i) { - SatLiteral lit = clause->operator[](i); - SatVariable var = lit.getSatVariable(); + prop::SatLiteral lit = clause->operator[](i); + prop::SatVariable var = lit.getSatVariable(); TNode atom = getAtom(var); if (atoms.find(atom) == atoms.end()) { Assert (atoms.find(atom) == atoms.end()); @@ -247,8 +266,8 @@ void CnfProof::collectAssertionsForClauses(const IdToSatClause& clauses, void LFSCCnfProof::printAtomMapping(const NodeSet& atoms, std::ostream& os, std::ostream& paren) { - NodeSet::const_iterator it = atoms.begin(); - NodeSet::const_iterator end = atoms.end(); + NodeSet::const_iterator it = atoms.begin(); + NodeSet::const_iterator end = atoms.end(); for (;it != end; ++it) { os << "(decl_atom "; @@ -257,7 +276,7 @@ void LFSCCnfProof::printAtomMapping(const NodeSet& atoms, //FIXME hideous LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); pe->printLetTerm(atom.toExpr(), os); - + os << " (\\ " << ProofManager::getVarName(var, d_name) << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; paren << ")))"; @@ -297,9 +316,9 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, // paren << "))"; // return; - + Assert( clause->size()>0 ); - + Node base_assertion = getDefinitionForClause(id); //get the assertion for the clause id @@ -359,19 +378,19 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, std::stringstream os_paren; //eliminate each one for (int j = base_assertion.getNumChildren()-2; j >= 0; j--) { + Trace("cnf-pf-debug") << "; base_assertion[" << j << "] is: " << base_assertion[j] + << ", and its kind is: " << base_assertion[j].getKind() << std::endl ; + Node child_base = base_assertion[j].getKind()==kind::NOT ? base_assertion[j][0] : base_assertion[j]; bool child_pol = base_assertion[j].getKind()!=kind::NOT; - - if( j==0 && base_assertion.getKind()==kind::IMPLIES ){ - child_pol = !child_pol; - } - + Trace("cnf-pf-debug") << "; child " << j << " " - << child_base << " " - << child_pol << " " - << childPol[child_base] << std::endl; - + << ", child base: " << child_base + << ", child pol: " << child_pol + << ", childPol[child_base] " + << childPol[child_base] << ", base pol: " << base_pol << std::endl; + std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); if( itcic!=childIndex.end() ){ @@ -379,7 +398,13 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, os_main << "(or_elim_1 _ _ "; prop::SatLiteral lit = (*clause)[itcic->second]; // Should be if in the original formula it was negated - if( childPol[child_base] && base_pol ){ + // if( childPol[child_base] && base_pol ){ + + // Adding the below to catch a specific case where the first child of an IMPLIES is negative, + // in which case we need not_not introduction. + if (base_assertion.getKind() == kind::IMPLIES && !child_pol && base_pol) { + os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; + } else if (childPol[child_base] && base_pol) { os_main << ProofManager::getLitName(lit, d_name) << " "; }else{ os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; @@ -393,6 +418,7 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, success = false; } } + if( success ){ if( base_assertion.getKind()==kind::IMPLIES ){ os_main << "(impl_elim _ _ "; @@ -422,7 +448,7 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, }else if ((base_assertion.getKind()==kind::AND && base_pol) || ((base_assertion.getKind()==kind::OR || base_assertion.getKind()==kind::IMPLIES) && !base_pol)) { - + std::stringstream os_main; Node iatom; @@ -433,7 +459,7 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, Assert( assertion.getNumChildren()==1 ); iatom = assertion[0]; } - + Trace("cnf-pf") << "; and/or case 2, iatom = " << iatom << std::endl; Node e_base = iatom.getKind()==kind::NOT ? iatom[0] : iatom; bool e_pol = iatom.getKind()!=kind::NOT; @@ -731,6 +757,4 @@ bool LFSCCnfProof::printProofTopLevel(Node e, std::ostream& out) { } } - - } /* CVC4 namespace */ diff --git a/src/proof/cnf_proof.h b/src/proof/cnf_proof.h index e5a80b428..b4df850f7 100644 --- a/src/proof/cnf_proof.h +++ b/src/proof/cnf_proof.h @@ -21,7 +21,7 @@ #ifndef __CVC4__CNF_PROOF_H #define __CVC4__CNF_PROOF_H -#include +#include #include #include @@ -43,6 +43,7 @@ typedef __gnu_cxx::hash_set ClauseIdSet; typedef context::CDHashMap ClauseIdToNode; typedef context::CDHashMap NodeToProofRule; +typedef context::CDHashMap ClauseIdToTheory; class CnfProof { protected: @@ -54,28 +55,33 @@ protected: /** Map from assertion to reason for adding assertion **/ NodeToProofRule d_assertionToProofRule; + /** Map from assertion to the theory that added this assertion **/ + ClauseIdToTheory d_clauseIdToOwnerTheory; + + /** The last theory to explain a lemma **/ + theory::TheoryId d_explainerTheory; + /** Top of stack is assertion currently being converted to CNF **/ std::vector d_currentAssertionStack; /** Top of stack is top-level fact currently being converted to CNF **/ std::vector d_currentDefinitionStack; - /** Map from ClauseId to the top-level fact that lead to adding this clause **/ ClauseIdToNode d_clauseToDefinition; /** Top-level facts that follow from assertions during convertAndAssert **/ NodeSet d_definitions; - + /** Map from top-level fact to facts/assertion that it follows from **/ NodeToNode d_cnfDeps; ClauseIdSet d_explanations; - + bool isDefinition(Node node); Node getDefinitionForClause(ClauseId clause); - + std::string d_name; public: CnfProof(CVC4::prop::CnfStream* cnfStream, @@ -103,10 +109,10 @@ public: /** Clause is one of the clauses defining top-level assertion node*/ void setClauseAssertion(ClauseId clause, Node node); - + void registerAssertion(Node assertion, ProofRule reason); void setCnfDependence(Node from, Node to); - + void pushCurrentAssertion(Node assertion); // the current assertion being converted void popCurrentAssertion(); Node getCurrentAssertion(); @@ -115,14 +121,18 @@ public: void popCurrentDefinition(); Node getCurrentDefinition(); - + void setExplainerTheory(theory::TheoryId theory); + theory::TheoryId getExplainerTheory(); + theory::TheoryId getOwnerTheory(ClauseId clause); + + void registerExplanationLemma(ClauseId clauseId); + // accessors for the leaf assertions that are being converted to CNF bool isAssertion(Node node); ProofRule getProofRule(Node assertion); ProofRule getProofRule(ClauseId clause); Node getAssertionForClause(ClauseId clause); - /** Virtual methods for printing things **/ virtual void printAtomMapping(const NodeSet& atoms, std::ostream& os, @@ -154,7 +164,7 @@ public: void printAtomMapping(const NodeSet& atoms, std::ostream& os, std::ostream& paren); - + void printClause(const prop::SatClause& clause, std::ostream& os, std::ostream& paren); diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index 93752d3cf..0cec6e149 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -1,19 +1,19 @@ /********************* */ /*! \file proof_manager.cpp - ** \verbatim - ** Original author: Liana Hadarean - ** Major contributors: Morgan Deters - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2014 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ +** \verbatim +** Original author: Liana Hadarean +** Major contributors: Morgan Deters +** Minor contributors (to current version): Andrew Reynolds +** This file is part of the CVC4 project. +** Copyright (c) 2009-2014 New York University and The University of Iowa +** See the file COPYING in the top-level source directory for licensing +** information.\endverbatim +** +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ #include "proof/proof_manager.h" @@ -105,6 +105,7 @@ UFProof* ProofManager::getUfProof() { TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_UF); return (UFProof*)pf; } + BitVectorProof* ProofManager::getBitVectorProof() { Assert (options::proof()); TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_BV); @@ -117,6 +118,17 @@ ArrayProof* ProofManager::getArrayProof() { return (ArrayProof*)pf; } +ArithProof* ProofManager::getArithProof() { + Assert (options::proof()); + TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_ARITH); + return (ArithProof*)pf; +} + +SkolemizationManager* ProofManager::getSkolemizationManager() { + Assert (options::proof()); + return &(currentPM()->d_skolemizationManager); +} + void ProofManager::initSatProof(Minisat::Solver* solver) { Assert (currentPM()->d_satProof == NULL); Assert(currentPM()->d_format == LFSC); @@ -210,14 +222,22 @@ std::string ProofManager::getLitName(TNode lit, return getLitName(currentPM()->d_cnfProof->getLiteral(lit), prefix); } -std::string ProofManager::sanitize(TNode var) { - Assert (var.isVar()); - std::string name = var.toString(); - std::replace(name.begin(), name.end(), ' ', '_'); +std::string ProofManager::sanitize(TNode node) { + Assert (node.isVar() || node.isConst()); + + std::string name = node.toString(); + if (node.isVar()) { + std::replace(name.begin(), name.end(), ' ', '_'); + } else if (node.isConst()) { + name.erase(std::remove(name.begin(), name.end(), '('), name.end()); + name.erase(std::remove(name.begin(), name.end(), ')'), name.end()); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + name = "const" + name; + } + return name; } - void ProofManager::traceDeps(TNode n) { Debug("cores") << "trace deps " << n << std::endl; if ((n.isConst() && n == NodeManager::currentNM()->mkConst(true)) || @@ -319,10 +339,51 @@ void LFSCProof::toStream(std::ostream& out) { d_satProof->collectClausesUsed(used_inputs, used_lemmas); + IdToSatClause::iterator it2; + Debug("pf::pm") << std::endl << "Used inputs: " << std::endl; + for (it2 = used_inputs.begin(); it2 != used_inputs.end(); ++it2) { + Debug("pf::pm") << "\t input = " << *(it2->second) << std::endl; + } + Debug("pf::pm") << std::endl; + + // Debug("pf::pm") << std::endl << "Used lemmas: " << std::endl; + // for (it2 = used_lemmas.begin(); it2 != used_lemmas.end(); ++it2) { + // Debug("pf::pm") << "\t lemma = " << *(it2->second) << std::endl; + // } + // Debug("pf::pm") << std::endl; + Debug("pf::pm") << std::endl << "Used lemmas: " << std::endl; + for (it2 = used_lemmas.begin(); it2 != used_lemmas.end(); ++it2) { + + std::vector clause_expr; + for(unsigned i = 0; i < it2->second->size(); ++i) { + prop::SatLiteral lit = (*(it2->second))[i]; + Expr atom = d_cnfProof->getAtom(lit.getSatVariable()).toExpr(); + if (atom.isConst()) { + Assert (atom == utils::mkTrue()); + continue; + } + Expr expr_lit = lit.isNegated() ? atom.notExpr(): atom; + clause_expr.push_back(expr_lit); + } + + Debug("pf::pm") << "\t lemma " << it2->first << " = " << *(it2->second) << std::endl; + Debug("pf::pm") << "\t"; + for (unsigned i = 0; i < clause_expr.size(); ++i) { + Debug("pf::pm") << clause_expr[i] << " "; + } + Debug("pf::pm") << std::endl; + } + Debug("pf::pm") << std::endl; + // collecting assertions that lead to the clauses being asserted NodeSet used_assertions; d_cnfProof->collectAssertionsForClauses(used_inputs, used_assertions); + NodeSet::iterator it3; + Debug("pf::pm") << std::endl << "Used assertions: " << std::endl; + for (it3 = used_assertions.begin(); it3 != used_assertions.end(); ++it3) + Debug("pf::pm") << "\t assertion = " << *it3 << std::endl; + NodeSet atoms; // collects the atoms in the clauses d_cnfProof->collectAtomsForClauses(used_inputs, atoms); @@ -334,21 +395,25 @@ void LFSCProof::toStream(std::ostream& out) { utils::collectAtoms(*it, atoms); } - if (Debug.isOn("proof:pm")) { - // std::cout << NodeManager::currentNM(); - Debug("proof:pm") << "LFSCProof::Used assertions: "<< std::endl; - for(NodeSet::const_iterator it = used_assertions.begin(); it != used_assertions.end(); ++it) { - Debug("proof:pm") << " " << *it << std::endl; - } + NodeSet::iterator atomIt; + Debug("pf::pm") << std::endl << "Dumping atoms from lemmas, inputs and assertions: " << std::endl << std::endl; + for (atomIt = atoms.begin(); atomIt != atoms.end(); ++atomIt) { + Debug("pf::pm") << "\tAtom: " << *atomIt << std::endl; - Debug("proof:pm") << "LFSCProof::Used atoms: "<< std::endl; - for(NodeSet::const_iterator it = atoms.begin(); it != atoms.end(); ++it) { - Debug("proof:pm") << " " << *it << std::endl; + if (Debug.isOn("proof:pm")) { + // std::cout << NodeManager::currentNM(); + Debug("proof:pm") << "LFSCProof::Used assertions: "<< std::endl; + for(NodeSet::const_iterator it = used_assertions.begin(); it != used_assertions.end(); ++it) { + Debug("proof:pm") << " " << *it << std::endl; + } + + Debug("proof:pm") << "LFSCProof::Used atoms: "<< std::endl; + for(NodeSet::const_iterator it = atoms.begin(); it != atoms.end(); ++it) { + Debug("proof:pm") << " " << *it << std::endl; + } } } - - smt::SmtScope scope(d_smtEngine); std::ostringstream paren; out << "(check\n"; @@ -357,32 +422,55 @@ void LFSCProof::toStream(std::ostream& out) { // declare the theory atoms NodeSet::const_iterator it = atoms.begin(); NodeSet::const_iterator end = atoms.end(); + + Debug("pf::pm") << "LFSCProof::toStream: registering terms:" << std::endl; for(; it != end; ++it) { + Debug("pf::pm") << "\tTerm: " << (*it).toExpr() << std::endl; d_theoryProof->registerTerm((*it).toExpr()); } + + Debug("pf::pm") << std::endl << "Term registration done!" << std::endl << std::endl; + + Debug("pf::pm") << std::endl << "LFSCProof::toStream: starting to print assertions" << std::endl; + // print out all the original assertions + d_theoryProof->registerTermsFromAssertions(); + d_theoryProof->printSortDeclarations(out, paren); + d_theoryProof->printTermDeclarations(out, paren); d_theoryProof->printAssertions(out, paren); + Debug("pf::pm") << std::endl << "LFSCProof::toStream: print assertions DONE" << std::endl; - out << "(: (holds cln)\n"; + out << "(: (holds cln)\n\n"; + + // Have the theory proofs print deferred declarations, e.g. for skolem variables. + out << " ;; Printing deferred declarations \n"; + d_theoryProof->printDeferredDeclarations(out, paren); // print trust that input assertions are their preprocessed form printPreprocessedAssertions(used_assertions, out, paren); // print mapping between theory atoms and internal SAT variables + out << ";; Printing mapping from preprocessed assertions into atoms \n"; d_cnfProof->printAtomMapping(atoms, out, paren); + Debug("pf::pm") << std::endl << "Printing cnf proof for clauses" << std::endl; + IdToSatClause::const_iterator cl_it = used_inputs.begin(); // print CNF conversion proof for each clause for (; cl_it != used_inputs.end(); ++cl_it) { d_cnfProof->printCnfProofForClause(cl_it->first, cl_it->second, out, paren); } + Debug("pf::pm") << std::endl << "Printing cnf proof for clauses DONE" << std::endl; + // FIXME: for now assume all theory lemmas are in CNF form so // distinguish between them and inputs // print theory lemmas for resolution proof - d_theoryProof->printTheoryLemmas(used_lemmas, out, paren); + Debug("pf::pm") << "Proof manager: printing theory lemmas" << std::endl; + d_theoryProof->printTheoryLemmas(used_lemmas, out, paren); + Debug("pf::pm") << "Proof manager: printing theory lemmas DONE!" << std::endl; if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && ProofManager::getBitVectorProof()) { // print actual resolution proof @@ -404,7 +492,7 @@ void LFSCProof::toStream(std::ostream& out) { void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, std::ostream& os, std::ostream& paren) { - os << " ;; Preprocessing \n"; + os << "\n ;; In the preprocessor we trust \n"; NodeSet::const_iterator it = assertions.begin(); NodeSet::const_iterator end = assertions.end(); @@ -420,11 +508,12 @@ void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, paren << "))"; } -} - + os << "\n"; +} //---from Morgan--- + bool ProofManager::hasOp(TNode n) const { return d_bops.find(n) != d_bops.end(); } @@ -436,16 +525,21 @@ Node ProofManager::lookupOp(TNode n) const { } Node ProofManager::mkOp(TNode n) { + Trace("mgd-pm-mkop") << "MkOp : " << n << " " << n.getKind() << std::endl; if(n.getKind() != kind::BUILTIN) { return n; } + Node& op = d_ops[n]; if(op.isNull()) { - Debug("mgd") << "making an op for " << n << "\n"; + Assert((n.getConst() == kind::SELECT) || (n.getConst() == kind::STORE)); + + Debug("mgd-pm-mkop") << "making an op for " << n << "\n"; + std::stringstream ss; ss << n; std::string s = ss.str(); - Debug("mgd") << " : " << s << std::endl; + Debug("mgd-pm-mkop") << " : " << s << std::endl; std::vector v; v.push_back(NodeManager::currentNM()->integerType()); if(n.getConst() == kind::SELECT) { @@ -457,44 +551,54 @@ Node ProofManager::mkOp(TNode n) { v.push_back(NodeManager::currentNM()->integerType()); } TypeNode type = NodeManager::currentNM()->mkFunctionType(v); + Debug("mgd-pm-mkop") << "typenode is: " << type << "\n"; op = NodeManager::currentNM()->mkSkolem(s, type, " ignore", NodeManager::SKOLEM_NO_NOTIFY); d_bops[op] = n; } + Debug("mgd-pm-mkop") << "returning the op: " << op << "\n"; return op; } //---end from Morgan--- +bool ProofManager::wasPrinted(const Type& type) const { + return d_printedTypes.find(type) != d_printedTypes.end(); +} + +void ProofManager::markPrinted(const Type& type) { + d_printedTypes.insert(type); +} + std::ostream& operator<<(std::ostream& out, CVC4::ProofRule k) { switch(k) { case RULE_GIVEN: - out << "RULE_GIVEN"; + out << "RULE_GIVEN"; break; case RULE_DERIVED: - out << "RULE_DERIVED"; + out << "RULE_DERIVED"; break; case RULE_RECONSTRUCT: - out << "RULE_RECONSTRUCT"; + out << "RULE_RECONSTRUCT"; break; case RULE_TRUST: - out << "RULE_TRUST"; + out << "RULE_TRUST"; break; case RULE_INVALID: - out << "RULE_INVALID"; + out << "RULE_INVALID"; break; case RULE_CONFLICT: - out << "RULE_CONFLICT"; + out << "RULE_CONFLICT"; break; case RULE_TSEITIN: - out << "RULE_TSEITIN"; + out << "RULE_TSEITIN"; break; case RULE_SPLIT: - out << "RULE_SPLIT"; + out << "RULE_SPLIT"; break; case RULE_ARRAYS_EXT: - out << "RULE_ARRAYS"; + out << "RULE_ARRAYS"; break; case RULE_ARRAYS_ROW: - out << "RULE_ARRAYS"; + out << "RULE_ARRAYS"; break; default: out << "ProofRule Unknown! [" << unsigned(k) << "]"; diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index 58a2f45f7..a39d97816 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -21,7 +21,9 @@ #include #include - +#include "proof/proof.h" +#include "proof/skolemization_manager.h" +#include "util/proof.h" #include "expr/node.h" #include "proof/clause_id.h" #include "proof/proof.h" @@ -48,11 +50,11 @@ namespace prop { class SmtEngine; const ClauseId ClauseIdEmpty(-1); -const ClauseId ClauseIdUndef(-2); +const ClauseId ClauseIdUndef(-2); const ClauseId ClauseIdError(-3); class Proof; -template class TSatProof; +template class TSatProof; typedef TSatProof< CVC4::Minisat::Solver> CoreSatProof; class CnfProof; @@ -60,10 +62,11 @@ class RewriterProof; class TheoryProofEngine; class TheoryProof; class UFProof; +class ArithProof; class ArrayProof; class BitVectorProof; -template class LFSCSatProof; +template class LFSCSatProof; typedef LFSCSatProof< CVC4::Minisat::Solver> LFSCCoreSatProof; class LFSCCnfProof; @@ -74,7 +77,7 @@ class LFSCRewriterProof; template class ProofProxy; typedef ProofProxy< CVC4::Minisat::Solver> CoreProofProxy; -typedef ProofProxy< CVC4::BVMinisat::Solver> BVProofProxy; +typedef ProofProxy< CVC4::BVMinisat::Solver> BVProofProxy; namespace prop { typedef uint64_t SatVariable; @@ -105,7 +108,7 @@ enum ProofRule { RULE_CONFLICT, /* re-construct as a conflict */ RULE_TSEITIN, /* Tseitin CNF transformation */ RULE_SPLIT, /* A splitting lemma of the form a v ~ a*/ - + RULE_ARRAYS_EXT, /* arrays, extensional */ RULE_ARRAYS_ROW, /* arrays, read-over-write */ };/* enum ProofRules */ @@ -120,6 +123,8 @@ class ProofManager { ExprSet d_inputCoreFormulas; ExprSet d_outputCoreFormulas; + SkolemizationManager d_skolemizationManager; + int d_nextId; Proof* d_fullProof; @@ -129,6 +134,8 @@ class ProofManager { // trace dependences back to unsat core void traceDeps(TNode n); + std::set d_printedTypes; + protected: LogicInfo d_logic; @@ -153,6 +160,9 @@ public: static UFProof* getUfProof(); static BitVectorProof* getBitVectorProof(); static ArrayProof* getArrayProof(); + static ArithProof* getArithProof(); + + static SkolemizationManager *getSkolemizationManager(); // iterators over data shared by proofs typedef ExprSet::const_iterator assertions_iterator; @@ -163,17 +173,17 @@ public: } assertions_iterator end_assertions() const { return d_inputFormulas.end(); } size_t num_assertions() const { return d_inputFormulas.size(); } - + //---from Morgan--- Node mkOp(TNode n); Node lookupOp(TNode n) const; bool hasOp(TNode n) const; - + std::map d_ops; std::map d_bops; //---end from Morgan--- - - + + // variable prefixes static std::string getInputClauseName(ClauseId id, const std::string& prefix = ""); static std::string getLemmaClauseName(ClauseId id, const std::string& prefix = ""); @@ -181,7 +191,7 @@ public: static std::string getLearntClauseName(ClauseId id, const std::string& prefix = ""); static std::string getPreprocessedAssertionName(Node node, const std::string& prefix = ""); static std::string getAssertionName(Node node, const std::string& prefix = ""); - + static std::string getVarName(prop::SatVariable var, const std::string& prefix = ""); static std::string getAtomName(prop::SatVariable var, const std::string& prefix = ""); static std::string getAtomName(TNode atom, const std::string& prefix = ""); @@ -190,14 +200,13 @@ public: // for SMT variable names that have spaces and other things static std::string sanitize(TNode var); - - /** Add proof assertion - unlinke addCoreAssertion this is post definition expansion **/ + /** Add proof assertion - unlike addCoreAssertion this is post definition expansion **/ void addAssertion(Expr formula); - + /** Public unsat core methods **/ void addCoreAssertion(Expr formula); - + void addDependence(TNode n, TNode dep); void addUnsatCore(Expr formula); @@ -212,6 +221,9 @@ public: const std::string getLogic() const { return d_logic.getLogicString(); } LogicInfo & getLogicInfo() { return d_logic; } + void markPrinted(const Type& type); + bool wasPrinted(const Type& type) const; + };/* class ProofManager */ class LFSCProof : public Proof { diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index ccb81b9ad..9160ebcfb 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -124,7 +124,7 @@ protected: VarSet d_assumptions; // assumption literals for bv solver IdHashSet d_assumptionConflicts; // assumption conflicts not actually added to SAT solver IdToConflicts d_assumptionConflictsDebug; - + // resolutions IdResMap d_resChains; ResStack d_resStack; @@ -241,13 +241,13 @@ public: ClauseId getTrueUnit() const; ClauseId getFalseUnit() const; - + void registerAssumption(const typename Solver::TVar var); ClauseId registerAssumptionConflict(const typename Solver::TLitVec& confl); - + ClauseId storeUnitConflict(typename Solver::TLit lit, ClauseKind kind); - + /** * Marks the deleted clauses as deleted. Note we may still use them in the final * resolution. @@ -297,12 +297,13 @@ public: virtual void printResolutionEmptyClause(std::ostream& out, std::ostream& paren) = 0; virtual void printAssumptionsResolution(ClauseId id, std::ostream& out, std::ostream& paren) = 0; - void collectClausesUsed(IdToSatClause& inputs, IdToSatClause& lemmas); void storeClauseGlue(ClauseId clause, int glue); + + private: __gnu_cxx::hash_map d_glueMap; struct Statistics { @@ -321,7 +322,6 @@ private: Statistics d_statistics; };/* class TSatProof */ - template class ProofProxy { private: diff --git a/src/proof/sat_proof_implementation.h b/src/proof/sat_proof_implementation.h index d786eef76..653732dd9 100644 --- a/src/proof/sat_proof_implementation.h +++ b/src/proof/sat_proof_implementation.h @@ -31,19 +31,19 @@ namespace CVC4 { -template +template void printLit (typename Solver::TLit l) { Debug("proof:sat") << (sign(l) ? "-" : "") << var(l) + 1; } -template +template void printClause (typename Solver::TClause& c) { for (int i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } } -template +template void printClause (std::vector& c) { for (unsigned i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; @@ -51,7 +51,7 @@ void printClause (std::vector& c) { } -template +template void printLitSet(const std::set& s) { typename std::set < typename Solver::TLit>::const_iterator it = s.begin(); for(; it != s.end(); ++it) { @@ -62,11 +62,11 @@ void printLitSet(const std::set& s) { } // purely debugging functions -template +template void printDebug (typename Solver::TLit l) { Debug("proof:sat") << (sign(l) ? "-" : "") << var(l) + 1 << std::endl; } -template +template void printDebug (typename Solver::TClause& c) { for (int i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; @@ -81,7 +81,7 @@ void printDebug (typename Solver::TClause& c) { * @param id the clause id * @param set the clause converted to a set of literals */ -template +template void TSatProof::createLitSet(ClauseId id, LitSet& set) { Assert(set.empty()); if(isUnit(id)) { @@ -95,11 +95,11 @@ void TSatProof::createLitSet(ClauseId id, LitSet& set) { if (d_assumptionConflictsDebug.find(id) != d_assumptionConflictsDebug.end()) { LitVector* clause = d_assumptionConflictsDebug[id]; for (unsigned i = 0; i < clause->size(); ++i) { - set.insert(clause->operator[](i)); + set.insert(clause->operator[](i)); } return; } - + typename Solver::TCRef ref = getClauseRef(id); typename Solver::TClause& c = getClause(ref); for (int i = 0; i < c.size(); i++) { @@ -115,7 +115,7 @@ void TSatProof::createLitSet(ClauseId id, LitSet& set) { * @param clause1 * @param clause2 */ -template +template bool resolve(const typename Solver::TLit v, std::set& clause1, std::set& clause2, bool s) { @@ -134,7 +134,7 @@ bool resolve(const typename Solver::TLit v, } clause1.erase(var); clause2.erase(~var); - typename std::set::iterator it = clause2.begin(); + typename std::set::iterator it = clause2.begin(); for (; it!= clause2.end(); ++it) { clause1.insert(*it); } @@ -150,7 +150,7 @@ bool resolve(const typename Solver::TLit v, } clause1.erase(~var); clause2.erase(var); - typename std::set::iterator it = clause2.begin(); + typename std::set::iterator it = clause2.begin(); for (; it!= clause2.end(); ++it) { clause1.insert(*it); } @@ -159,19 +159,19 @@ bool resolve(const typename Solver::TLit v, } /// ResChain -template +template ResChain::ResChain(ClauseId start) : d_start(start), d_steps(), d_redundantLits(NULL) {} -template +template void ResChain::addStep(typename Solver::TLit lit, ClauseId id, bool sign) { ResStep step(lit, id, sign); d_steps.push_back(step); } -template +template void ResChain::addRedundantLit(typename Solver::TLit lit) { if (d_redundantLits) { d_redundantLits->insert(lit); @@ -183,19 +183,19 @@ void ResChain::addRedundantLit(typename Solver::TLit lit) { /// ProxyProof -template +template ProofProxy::ProofProxy(TSatProof* proof): d_proof(proof) {} -template +template void ProofProxy::updateCRef(typename Solver::TCRef oldref, typename Solver::TCRef newref) { d_proof->updateCRef(oldref, newref); } /// SatProof -template +template TSatProof::TSatProof(Solver* solver, const std::string& name, bool checkRes) : d_solver(solver) , d_cnfProof(NULL) @@ -218,7 +218,7 @@ TSatProof::TSatProof(Solver* solver, const std::string& name, bool check , d_unitConflictId() , d_storedUnitConflict(false) , d_trueLit(ClauseIdUndef) - , d_falseLit(ClauseIdUndef) + , d_falseLit(ClauseIdUndef) , d_name(name) , d_seenLearnt() , d_seenInputs() @@ -228,10 +228,10 @@ TSatProof::TSatProof(Solver* solver, const std::string& name, bool check d_proxy = new ProofProxy(this); } -template +template TSatProof::~TSatProof() { delete d_proxy; - + // FIXME: double free if deleted clause also appears in d_seenLemmas? IdToSatClause::iterator it = d_deletedTheoryLemmas.begin(); IdToSatClause::iterator end = d_deletedTheoryLemmas.end(); @@ -257,8 +257,8 @@ TSatProof::~TSatProof() { delete seen_it->second; } } - -template + +template void TSatProof::setCnfProof(CnfProof* cnf_proof) { Assert (d_cnfProof == NULL); d_cnfProof = cnf_proof; @@ -271,7 +271,7 @@ void TSatProof::setCnfProof(CnfProof* cnf_proof) { * * @return */ -template +template bool TSatProof::checkResolution(ClauseId id) { if(d_checkRes) { bool validRes = true; @@ -300,9 +300,9 @@ bool TSatProof::checkResolution(ClauseId id) { if (id == d_emptyClauseId) { return clause1.empty(); } - + LitVector c; - getLitVec(id, c); + getLitVec(id, c); for (unsigned i = 0; i < c.size(); ++i) { int count = clause1.erase(c[i]); @@ -335,7 +335,7 @@ bool TSatProof::checkResolution(ClauseId id) { /// helper methods -template +template ClauseId TSatProof::getClauseId(typename Solver::TCRef ref) { if(d_clauseId.find(ref) == d_clauseId.end()) { Debug("proof:sat") << "Missing clause \n"; @@ -344,12 +344,12 @@ ClauseId TSatProof::getClauseId(typename Solver::TCRef ref) { return d_clauseId[ref]; } -template +template ClauseId TSatProof::getClauseId(typename Solver::TLit lit) { Assert(d_unitId.find(toInt(lit)) != d_unitId.end()); return d_unitId[toInt(lit)]; } -template +template typename Solver::TCRef TSatProof::getClauseRef(ClauseId id) { if (d_idClause.find(id) == d_idClause.end()) { Debug("proof:sat") << "proof:getClauseRef cannot find clause "<::getClauseRef(ClauseId id) { return d_idClause[id]; } -template +template typename Solver::TClause& TSatProof::getClause(typename Solver::TCRef ref) { Assert(ref != Solver::TCRef_Undef); Assert(ref >= 0 && ref < d_solver->ca.size()); return d_solver->ca[ref]; } -template +template void TSatProof::getLitVec(ClauseId id, LitVector& vec) { if (isUnit(id)) { typename Solver::TLit lit = getUnit(id); vec.push_back(lit); - return; + return; } if (isAssumptionConflict(id)) { vec = *(d_assumptionConflictsDebug[id]); @@ -386,44 +386,44 @@ void TSatProof::getLitVec(ClauseId id, LitVector& vec) { } -template +template typename Solver::TLit TSatProof::getUnit(ClauseId id) { Assert(d_idUnit.find(id) != d_idUnit.end()); return d_idUnit[id]; } -template +template bool TSatProof::isUnit(ClauseId id) { return d_idUnit.find(id) != d_idUnit.end(); } -template +template bool TSatProof::isUnit(typename Solver::TLit lit) { return d_unitId.find(toInt(lit)) != d_unitId.end(); } -template +template ClauseId TSatProof::getUnitId(typename Solver::TLit lit) { Assert(isUnit(lit)); return d_unitId[toInt(lit)]; } -template +template bool TSatProof::hasResolution(ClauseId id) { return d_resChains.find(id) != d_resChains.end(); } -template +template bool TSatProof::isInputClause(ClauseId id) { return (d_inputClauses.find(id) != d_inputClauses.end()); } -template +template bool TSatProof::isLemmaClause(ClauseId id) { return (d_lemmaClauses.find(id) != d_lemmaClauses.end()); } -template +template bool TSatProof::isAssumptionConflict(ClauseId id) { return d_assumptionConflicts.find(id) != d_assumptionConflicts.end(); } -template +template void TSatProof::print(ClauseId id) { if (d_deleted.find(id) != d_deleted.end()) { Debug("proof:sat") << "del"<::print(ClauseId id) { printClause(getClause(ref)); } } -template +template void TSatProof::printRes(ClauseId id) { Assert(hasResolution(id)); Debug("proof:sat") << "id "<< id <<": "; printRes(d_resChains[id]); } -template +template void TSatProof::printRes(ResChain* res) { ClauseId start_id = res->getStart(); @@ -468,14 +468,14 @@ void TSatProof::printRes(ResChain* res) { } /// registration methods -template +template ClauseId TSatProof::registerClause(typename Solver::TCRef clause, ClauseKind kind) { Assert(clause != Solver::TCRef_Undef); typename ClauseIdMap::iterator it = d_clauseId.find(clause); if (it == d_clauseId.end()) { ClauseId newId = ProofManager::currentPM()->nextId(); - d_clauseId.insert(std::make_pair(clause, newId)); + d_clauseId.insert(std::make_pair(clause, newId)); d_idClause.insert(std::make_pair(newId, clause)); if (kind == INPUT) { Assert(d_inputClauses.find(newId) == d_inputClauses.end()); @@ -484,6 +484,11 @@ template if (kind == THEORY_LEMMA) { Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); d_lemmaClauses.insert(newId); + Debug("pf::sat") << "TSatProof::registerClause registering a new lemma clause: " + << newId << " = " << *buildClause(newId) + << ". Explainer theory: " << d_cnfProof->getExplainerTheory() + << std::endl; + d_cnfProof->registerExplanationLemma(newId); } } @@ -497,7 +502,7 @@ template return id; } -template +template ClauseId TSatProof::registerUnitClause(typename Solver::TLit lit, ClauseKind kind) { Debug("cores") << "registerUnitClause " << kind << std::endl; @@ -513,49 +518,54 @@ ClauseId TSatProof::registerUnitClause(typename Solver::TLit lit, } if (kind == THEORY_LEMMA) { Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); + Debug("pf::sat") << "TSatProof::registerUnitClause: registering a new lemma (UNIT CLAUSE): " + << lit + << ". Explainer theory: " << d_cnfProof->getExplainerTheory() + << std::endl; d_lemmaClauses.insert(newId); + d_cnfProof->registerExplanationLemma(newId); } } ClauseId id = d_unitId[toInt(lit)]; Assert(kind != INPUT || d_inputClauses.count(id)); Assert(kind != THEORY_LEMMA || d_lemmaClauses.count(id)); - Debug("proof:sat:detailed") << "registerUnitClause id: " << id + Debug("proof:sat:detailed") << "registerUnitClause id: " << id <<" kind: " << kind << "\n"; // ProofManager::currentPM()->setRegisteredClauseId( d_unitId[toInt(lit)] ); return id; } -template +template void TSatProof::registerTrueLit(const typename Solver::TLit lit) { Assert (d_trueLit == ClauseIdUndef); d_trueLit = registerUnitClause(lit, INPUT); } -template +template void TSatProof::registerFalseLit(const typename Solver::TLit lit) { Assert (d_falseLit == ClauseIdUndef); d_falseLit = registerUnitClause(lit, INPUT); } -template +template ClauseId TSatProof::getTrueUnit() const { Assert (d_trueLit != ClauseIdUndef); return d_trueLit; } -template +template ClauseId TSatProof::getFalseUnit() const { Assert (d_falseLit != ClauseIdUndef); return d_falseLit; } -template +template void TSatProof::registerAssumption(const typename Solver::TVar var) { Assert (d_assumptions.find(var) == d_assumptions.end()); d_assumptions.insert(var); } -template +template ClauseId TSatProof::registerAssumptionConflict(const typename Solver::TLitVec& confl) { Debug("proof:sat:detailed") << "registerAssumptionConflict " << std::endl; // Uniqueness is checked in the bit-vector proof @@ -565,13 +575,13 @@ ClauseId TSatProof::registerAssumptionConflict(const typename Solver::TL } ClauseId new_id = ProofManager::currentPM()->nextId(); d_assumptionConflicts.insert(new_id); - LitVector* vec_confl = new LitVector(confl.size()); + LitVector* vec_confl = new LitVector(confl.size()); for (int i = 0; i < confl.size(); ++i) { vec_confl->operator[](i) = confl[i]; } if (Debug.isOn("proof:sat:detailed")) { printClause(*vec_confl); - Debug("proof:sat:detailed") << "\n"; + Debug("proof:sat:detailed") << "\n"; } d_assumptionConflictsDebug[new_id] = vec_confl; @@ -579,7 +589,7 @@ ClauseId TSatProof::registerAssumptionConflict(const typename Solver::TL } -template +template void TSatProof::removedDfs(typename Solver::TLit lit, LitSet* removedSet, LitVector& removeStack, LitSet& inClause, LitSet& seen) { // if we already added the literal return if (seen.count(lit)) { @@ -606,7 +616,7 @@ void TSatProof::removedDfs(typename Solver::TLit lit, LitSet* removedSet } } -template +template void TSatProof::removeRedundantFromRes(ResChain* res, ClauseId id) { LitSet* removed = res->getRedundant(); if (removed == NULL) { @@ -637,8 +647,8 @@ void TSatProof::removeRedundantFromRes(ResChain* res, ClauseId i } removed->clear(); } - -template + +template void TSatProof::registerResolution(ClauseId id, ResChain* res) { Assert(res != NULL); @@ -662,14 +672,14 @@ void TSatProof::registerResolution(ClauseId id, ResChain* res) { /// recording resolutions -template +template void TSatProof::startResChain(typename Solver::TCRef start) { ClauseId id = getClauseId(start); ResChain* res = new ResChain(id); d_resStack.push_back(res); } -template +template void TSatProof::startResChain(typename Solver::TLit start) { ClauseId id = getUnitId(start); ResChain* res = new ResChain(id); @@ -677,7 +687,7 @@ void TSatProof::startResChain(typename Solver::TLit start) { } -template +template void TSatProof::addResolutionStep(typename Solver::TLit lit, typename Solver::TCRef clause, bool sign) { ClauseId id = registerClause(clause, LEARNT); @@ -685,7 +695,7 @@ void TSatProof::addResolutionStep(typename Solver::TLit lit, res->addStep(lit, id, sign); } -template +template void TSatProof::endResChain(ClauseId id) { Debug("proof:sat:detailed") <<"endResChain " << id << "\n"; Assert(d_resStack.size() > 0); @@ -695,7 +705,7 @@ void TSatProof::endResChain(ClauseId id) { } -// template +// template // void TSatProof::endResChain(typename Solver::TCRef clause) { // Assert(d_resStack.size() > 0); // ClauseId id = registerClause(clause, LEARNT); @@ -704,7 +714,7 @@ void TSatProof::endResChain(ClauseId id) { // d_resStack.pop_back(); // } -template +template void TSatProof::endResChain(typename Solver::TLit lit) { Assert(d_resStack.size() > 0); ClauseId id = registerUnitClause(lit, LEARNT); @@ -716,14 +726,14 @@ void TSatProof::endResChain(typename Solver::TLit lit) { } -template +template void TSatProof::cancelResChain() { Assert(d_resStack.size() > 0); d_resStack.pop_back(); } -template +template void TSatProof::storeLitRedundant(typename Solver::TLit lit) { Assert(d_resStack.size() > 0); ResChain* res = d_resStack.back(); @@ -731,18 +741,18 @@ void TSatProof::storeLitRedundant(typename Solver::TLit lit) { } /// constructing resolutions -template +template void TSatProof::resolveOutUnit(typename Solver::TLit lit) { ClauseId id = resolveUnit(~lit); ResChain* res = d_resStack.back(); res->addStep(lit, id, !sign(lit)); } -template +template void TSatProof::storeUnitResolution(typename Solver::TLit lit) { Debug("cores") << "STORE UNIT RESOLUTION" << std::endl; resolveUnit(lit); } -template +template ClauseId TSatProof::resolveUnit(typename Solver::TLit lit) { // first check if we already have a resolution for lit if(isUnit(lit)) { @@ -772,12 +782,12 @@ ClauseId TSatProof::resolveUnit(typename Solver::TLit lit) { registerResolution(unit_id, res); return unit_id; } -template +template void TSatProof::toStream(std::ostream& out) { Debug("proof:sat") << "TSatProof::printProof\n"; Unimplemented("native proof printing not supported yet"); } -template +template ClauseId TSatProof::storeUnitConflict(typename Solver::TLit conflict_lit, ClauseKind kind) { Debug("cores") << "STORE UNIT CONFLICT" << std::endl; @@ -787,7 +797,7 @@ ClauseId TSatProof::storeUnitConflict(typename Solver::TLit conflict_lit Debug("proof:sat:detailed") <<"storeUnitConflict " << d_unitConflictId << "\n"; return d_unitConflictId; } -template +template void TSatProof::finalizeProof(typename Solver::TCRef conflict_ref) { Assert(d_resStack.size() == 0); Assert(conflict_ref != Solver::TCRef_Undef); @@ -829,7 +839,7 @@ void TSatProof::finalizeProof(typename Solver::TCRef conflict_ref) { } /// CRef manager -template +template void TSatProof::updateCRef(typename Solver::TCRef oldref, typename Solver::TCRef newref) { if (d_clauseId.find(oldref) == d_clauseId.end()) { @@ -841,7 +851,7 @@ void TSatProof::updateCRef(typename Solver::TCRef oldref, d_temp_clauseId[newref] = id; d_temp_idClause[id] = newref; } -template +template void TSatProof::finishUpdateCRef() { d_clauseId.swap(d_temp_clauseId); d_temp_clauseId.clear(); @@ -849,7 +859,7 @@ void TSatProof::finishUpdateCRef() { d_idClause.swap(d_temp_idClause); d_temp_idClause.clear(); } -template +template void TSatProof::markDeleted(typename Solver::TCRef clause) { if (d_clauseId.find(clause) != d_clauseId.end()) { ClauseId id = getClauseId(clause); @@ -867,18 +877,18 @@ void TSatProof::markDeleted(typename Solver::TCRef clause) { // template<> // void toSatClause< ::BVMinisat::Solver> (const BVMinisat::Solver::TClause& minisat_cl, // prop::SatClause& sat_cl) { - + // prop::BVMinisatSatSolver::toSatClause(minisat_cl, sat_cl); // } -template +template void TSatProof::constructProof(ClauseId conflict) { collectClauses(conflict); } -template +template std::string TSatProof::clauseName(ClauseId id) { std::ostringstream os; if (isInputClause(id)) { @@ -894,7 +904,7 @@ std::string TSatProof::clauseName(ClauseId id) { } } -template +template prop::SatClause* TSatProof::buildClause(ClauseId id) { if (isUnit(id)) { typename Solver::TLit lit = getUnit(id); @@ -916,7 +926,7 @@ prop::SatClause* TSatProof::buildClause(ClauseId id) { return clause; } -template +template void TSatProof::collectClauses(ClauseId id) { if (d_seenInputs.find(id) != d_seenInputs.end() || d_seenLemmas.find(id) != d_seenLemmas.end() || @@ -949,7 +959,7 @@ void TSatProof::collectClauses(ClauseId id) { } } -template +template void TSatProof::collectClausesUsed(IdToSatClause& inputs, IdToSatClause& lemmas) { inputs = d_seenInputs; @@ -960,13 +970,13 @@ void TSatProof::collectClausesUsed(IdToSatClause& inputs, ); } -template +template void TSatProof::storeClauseGlue(ClauseId clause, int glue) { Assert (d_glueMap.find(clause) == d_glueMap.end()); d_glueMap.insert(std::make_pair(clause, glue)); } -template +template TSatProof::Statistics::Statistics(const std::string& prefix) : d_numLearnedClauses("satproof::"+prefix+"::NumLearnedClauses", 0) , d_numLearnedInProof("satproof::"+prefix+"::NumLearnedInProof", 0) @@ -986,7 +996,7 @@ TSatProof::Statistics::Statistics(const std::string& prefix) smtStatisticsRegistry()->registerStat(&d_usedClauseGlue); } -template +template TSatProof::Statistics::~Statistics() { smtStatisticsRegistry()->unregisterStat(&d_numLearnedClauses); smtStatisticsRegistry()->unregisterStat(&d_numLearnedInProof); @@ -1000,7 +1010,7 @@ TSatProof::Statistics::~Statistics() { /// LFSCSatProof class -template +template void LFSCSatProof::printResolution(ClauseId id, std::ostream& out, std::ostream& paren) { out << "(satlem_simplify _ _ _ "; @@ -1030,7 +1040,7 @@ void LFSCSatProof::printResolution(ClauseId id, std::ostream& out, std:: } /// LFSCSatProof class -template +template void LFSCSatProof::printAssumptionsResolution(ClauseId id, std::ostream& out, std::ostream& paren) { Assert (this->isAssumptionConflict(id)); // print the resolution proving the assumption conflict @@ -1038,7 +1048,7 @@ void LFSCSatProof::printAssumptionsResolution(ClauseId id, std::ostream& // resolve out assumptions to prove empty clause out << "(satlem_simplify _ _ _ "; std::vector& confl = *(this->d_assumptionConflictsDebug[id]); - + Assert (confl.size()); for (unsigned i = 0; i < confl.size(); ++i) { @@ -1046,8 +1056,8 @@ void LFSCSatProof::printAssumptionsResolution(ClauseId id, std::ostream& out <<"("; out << (lit.isNegated() ? "Q" : "R") <<" _ _ "; } - - out << this->clauseName(id)<< " "; + + out << this->clauseName(id)<< " "; for (int i = confl.size() - 1; i >= 0; --i) { prop::SatLiteral lit = toSatLiteral(confl[i]); prop::SatVariable v = lit.getSatVariable(); @@ -1074,20 +1084,20 @@ void LFSCSatProof::printResolutions(std::ostream& out, std::ostream& par template void LFSCSatProof::printResolutionEmptyClause(std::ostream& out, std::ostream& paren) { - printResolution(this->d_emptyClauseId, out, paren); + printResolution(this->d_emptyClauseId, out, paren); } inline std::ostream& operator<<(std::ostream& out, CVC4::ClauseKind k) { switch(k) { case CVC4::INPUT: - out << "INPUT"; + out << "INPUT"; break; case CVC4::THEORY_LEMMA: - out << "THEORY_LEMMA"; + out << "THEORY_LEMMA"; break; case CVC4::LEARNT: - out << "LEARNT"; + out << "LEARNT"; break; default: out << "ClauseKind Unknown! [" << unsigned(k) << "]"; diff --git a/src/proof/skolemization_manager.cpp b/src/proof/skolemization_manager.cpp new file mode 100644 index 000000000..526ea8dbb --- /dev/null +++ b/src/proof/skolemization_manager.cpp @@ -0,0 +1,55 @@ +/********************* */ +/*! \file skolemization_manager.cpp + **/ + +#include "proof/skolemization_manager.h" + +namespace CVC4 { + +void SkolemizationManager::registerSkolem(Node disequality, Node skolem) { + Debug("pf::pm") << "SkolemizationManager: registerSkolem: disequality = " << disequality << ", skolem = " << skolem << std::endl; + + if (isSkolem(skolem)) { + Assert(d_skolemToDisequality[skolem] == disequality); + return; + } + + d_disequalityToSkolem[disequality] = skolem; + d_skolemToDisequality[skolem] = disequality; +} + +bool SkolemizationManager::hasSkolem(Node disequality) { + return (d_disequalityToSkolem.find(disequality) != d_disequalityToSkolem.end()); +} + +Node SkolemizationManager::getSkolem(Node disequality) { + Debug("pf::pm") << "SkolemizationManager: getSkolem( "; + Assert (d_disequalityToSkolem.find(disequality) != d_disequalityToSkolem.end()); + Debug("pf::pm") << disequality << " ) = " << d_disequalityToSkolem[disequality] << std::endl; + return d_disequalityToSkolem[disequality]; +} + +Node SkolemizationManager::getDisequality(Node skolem) { + Assert (d_skolemToDisequality.find(skolem) != d_skolemToDisequality.end()); + return d_skolemToDisequality[skolem]; +} + +bool SkolemizationManager::isSkolem(Node skolem) { + return (d_skolemToDisequality.find(skolem) != d_skolemToDisequality.end()); +} + +void SkolemizationManager::clear() { + Debug("pf::pm") << "SkolemizationManager: clear" << std::endl; + d_disequalityToSkolem.clear(); + d_skolemToDisequality.clear(); +} + +std::hash_map::const_iterator SkolemizationManager::begin() { + return d_disequalityToSkolem.begin(); +} + +std::hash_map::const_iterator SkolemizationManager::end() { + return d_disequalityToSkolem.end(); +} + +} /* CVC4 namespace */ diff --git a/src/proof/skolemization_manager.h b/src/proof/skolemization_manager.h new file mode 100644 index 000000000..649f0bf40 --- /dev/null +++ b/src/proof/skolemization_manager.h @@ -0,0 +1,42 @@ +/********************* */ +/*! \file skolemization_manager.h + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__SKOLEMIZATION_MANAGER_H +#define __CVC4__SKOLEMIZATION_MANAGER_H + +#include +#include +#include "proof/proof.h" +#include "util/proof.h" +#include "expr/node.h" +#include "theory/logic_info.h" +#include "theory/substitutions.h" + +namespace CVC4 { + +class SkolemizationManager { +public: + void registerSkolem(Node disequality, Node skolem); + bool hasSkolem(Node disequality); + Node getSkolem(Node disequality); + Node getDisequality(Node skolem); + bool isSkolem(Node skolem); + + void clear(); + + std::hash_map::const_iterator begin(); + std::hash_map::const_iterator end(); + +private: + std::hash_map d_disequalityToSkolem; + std::hash_map d_skolemToDisequality; +}; + +}/* CVC4 namespace */ + + + +#endif /* __CVC4__SKOLEMIZATION_MANAGER_H */ diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp index 6a77faab7..efb6e6606 100644 --- a/src/proof/theory_proof.cpp +++ b/src/proof/theory_proof.cpp @@ -19,6 +19,7 @@ #include "base/cvc4_assert.h" #include "context/context.h" #include "options/bv_options.h" +#include "proof/arith_proof.h" #include "proof/array_proof.h" #include "proof/bitvector_proof.h" #include "proof/clause_id.h" @@ -79,13 +80,16 @@ public: return theory::LemmaStatus(TNode::null(), 0); } void requirePhase(TNode n, bool b) throw() { + Debug("theory-proof-debug") << "ProofOutputChannel::requirePhase called" << std::endl; Trace("theory-proof-debug") << "requirePhase " << n << " " << b << std::endl; } bool flipDecision() throw() { + Debug("theory-proof-debug") << "ProofOutputChannel::flipDecision called" << std::endl; AlwaysAssert(false); return false; } void setIncomplete() throw() { + Debug("theory-proof-debug") << "ProofOutputChannel::setIncomplete called" << std::endl; AlwaysAssert(false); } };/* class ProofOutputChannel */ @@ -146,11 +150,18 @@ void TheoryProofEngine::registerTheory(theory::Theory* th) { ((theory::bv::TheoryBV*)th)->setProofLog( bvp ); return; } + if (id == theory::THEORY_ARRAY) { d_theoryProofTable[id] = new LFSCArrayProof((theory::arrays::TheoryArrays*)th, this); return; } - // TODO other theories + + if (id == theory::THEORY_ARITH) { + d_theoryProofTable[id] = new LFSCArithProof((theory::arith::TheoryArith*)th, this); + return; + } + + // TODO other theories } } } @@ -164,9 +175,12 @@ void TheoryProofEngine::registerTerm(Expr term) { if (d_registrationCache.count(term)) { return; } + Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering new term: " << term << std::endl; theory::TheoryId theory_id = theory::Theory::theoryOf(term); + Debug("pf::tp") << "Term's theory: " << theory_id << std::endl; + // don't need to register boolean terms if (theory_id == theory::THEORY_BUILTIN || term.getKind() == kind::ITE) { @@ -178,20 +192,33 @@ void TheoryProofEngine::registerTerm(Expr term) { } if (!supportedTheory(theory_id)) return; - + getTheoryProof(theory_id)->registerTerm(term); d_registrationCache.insert(term); } theory::TheoryId TheoryProofEngine::getTheoryForLemma(ClauseId id) { - // TODO: now CNF proof has a map from formula to proof rule - // that should be checked to figure out what theory is responsible for this ProofManager* pm = ProofManager::currentPM(); - if (pm->getLogic() == "QF_UF") return theory::THEORY_UF; - if (pm->getLogic() == "QF_BV") return theory::THEORY_BV; - if (pm->getLogic() == "ALL_SUPPORTED") return theory::THEORY_BV; - Unreachable(); + Debug("pf::tp") << "TheoryProofEngine::getTheoryForLemma( " << id << " )" + << " = " << pm->getCnfProof()->getOwnerTheory(id) << std::endl; + + if ((pm->getLogic() == "QF_UFLIA") || (pm->getLogic() == "QF_UFLRA")) { + Debug("pf::tp") << "TheoryProofEngine::getTheoryForLemma: special hack for Arithmetic-with-holes support. " + << "Returning THEORY_ARITH" << std::endl; + return theory::THEORY_ARITH; + } + + return pm->getCnfProof()->getOwnerTheory(id); + + // if (pm->getLogic() == "QF_UF") return theory::THEORY_UF; + // if (pm->getLogic() == "QF_BV") return theory::THEORY_BV; + // if (pm->getLogic() == "QF_AX") return theory::THEORY_ARRAY; + // if (pm->getLogic() == "ALL_SUPPORTED") return theory::THEORY_BV; + + // Debug("pf::tp") << "Unsupported logic (" << pm->getLogic() << ")" << std::endl; + + // Unreachable(); } void LFSCTheoryProofEngine::bind(Expr term, LetMap& map, Bindings& let_order) { @@ -245,6 +272,9 @@ void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) { void LFSCTheoryProofEngine::printTheoryTerm(Expr term, std::ostream& os, const LetMap& map) { theory::TheoryId theory_id = theory::Theory::theoryOf(term); + Debug("pf::tp") << std::endl << "LFSCTheoryProofEngine::printTheoryTerm: term = " << term + << ", theory_id = " << theory_id << std::endl; + // boolean terms and ITEs are special because they // are common to all theories if (theory_id == theory::THEORY_BUILTIN || @@ -254,39 +284,55 @@ void LFSCTheoryProofEngine::printTheoryTerm(Expr term, std::ostream& os, const L return; } // dispatch to proper theory - getTheoryProof(theory_id)->printTerm(term, os, map); + getTheoryProof(theory_id)->printOwnedTerm(term, os, map); } void LFSCTheoryProofEngine::printSort(Type type, std::ostream& os) { if (type.isSort()) { - getTheoryProof(theory::THEORY_UF)->printSort(type, os); + getTheoryProof(theory::THEORY_UF)->printOwnedSort(type, os); return; } if (type.isBitVector()) { - getTheoryProof(theory::THEORY_BV)->printSort(type, os); + getTheoryProof(theory::THEORY_BV)->printOwnedSort(type, os); return; } if (type.isArray()) { - getTheoryProof(theory::THEORY_ARRAY)->printSort(type, os); + getTheoryProof(theory::THEORY_ARRAY)->printOwnedSort(type, os); return; } + + if (type.isInteger() || type.isReal()) { + getTheoryProof(theory::THEORY_ARITH)->printOwnedSort(type, os); + return; + } + + if (type.isBoolean()) { + getTheoryProof(theory::THEORY_BOOL)->printOwnedSort(type, os); + return; + } + Unreachable(); } -void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) { - unsigned counter = 0; +void LFSCTheoryProofEngine::registerTermsFromAssertions() { ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); - // collect declarations first for(; it != end; ++it) { registerTerm(*it); } - printDeclarations(os, paren); +} + +void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions called" << std::endl << std::endl; + + unsigned counter = 0; + ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); + ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); - it = ProofManager::currentPM()->begin_assertions(); for (; it != end; ++it) { + Debug("pf::tp") << "printAssertions: assertion is: " << *it << std::endl; // FIXME: merge this with counter os << "(% A" << counter++ << " (th_holds "; printLetTerm(*it, os); @@ -295,13 +341,40 @@ void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& pare } //store map between assertion and counter // ProofManager::currentPM()->setAssertion( *it ); + Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions done" << std::endl << std::endl; } -void LFSCTheoryProofEngine::printDeclarations(std::ostream& os, std::ostream& paren) { +void LFSCTheoryProofEngine::printSortDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations called" << std::endl << std::endl; + TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); TheoryProofTable::const_iterator end = d_theoryProofTable.end(); for (; it != end; ++it) { - it->second->printDeclarations(os, paren); + it->second->printSortDeclarations(os, paren); + } + + Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations done" << std::endl << std::endl; +} + +void LFSCTheoryProofEngine::printTermDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations called" << std::endl << std::endl; + + TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); + TheoryProofTable::const_iterator end = d_theoryProofTable.end(); + for (; it != end; ++it) { + it->second->printTermDeclarations(os, paren); + } + + Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations done" << std::endl << std::endl; +} + +void LFSCTheoryProofEngine::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printDeferredDeclarations called" << std::endl; + + TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); + TheoryProofTable::const_iterator end = d_theoryProofTable.end(); + for (; it != end; ++it) { + it->second->printDeferredDeclarations(os, paren); } } @@ -313,6 +386,16 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, IdToSatClause::const_iterator it = lemmas.begin(); IdToSatClause::const_iterator end = lemmas.end(); + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: checking lemma owners..." << std::endl; + + for (; it != end; ++it) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: new lemma" << std::endl; + ClauseId id = it->first; + Debug("pf::tp") << "\tLemma = " << id + << ". Owner theory: " << pm->getCnfProof()->getOwnerTheory(id) << std::endl; + } + it = lemmas.begin(); + // BitVector theory is special case: must know all // conflicts needed ahead of time for resolution // proof lemmas @@ -353,13 +436,22 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, it = lemmas.begin(); + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemmas..." << std::endl; + for (; it != end; ++it) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing a new lemma!" << std::endl; + + // Debug("pf::tp") << "\tLemma = " << it->first << ", " << *(it->second) << std::endl; ClauseId id = it->first; + Debug("pf::tp") << "Owner theory:" << pm->getCnfProof()->getOwnerTheory(id) << std::endl; const prop::SatClause* clause = it->second; // printing clause as it appears in resolution proof os << "(satlem _ _ "; std::ostringstream clause_paren; + + Debug("pf::tp") << "CnfProof printing clause..." << std::endl; pm->getCnfProof()->printClause(*clause, os, clause_paren); + Debug("pf::tp") << "CnfProof printing clause - Done!" << std::endl; std::vector clause_expr; for(unsigned i = 0; i < clause->size(); ++i) { @@ -373,10 +465,14 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, clause_expr.push_back(expr_lit); } + Debug("pf::tp") << "Expression printing done!" << std::endl; + // query appropriate theory for proof of clause theory::TheoryId theory_id = getTheoryForLemma(id); + Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; Debug("theory-proof-debug") << ";; Get theory lemma from " << theory_id << "..." << std::endl; getTheoryProof(theory_id)->printTheoryLemmaProof(clause_expr, os, paren); + Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; // os << " (clausify_false trust)"; os << clause_paren.str(); os << "( \\ " << pm->getLemmaClauseName(id) <<"\n"; @@ -385,15 +481,19 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, } void LFSCTheoryProofEngine::printBoundTerm(Expr term, std::ostream& os, const LetMap& map) { + // Debug("pf::tp") << "LFSCTheoryProofEngine::printBoundTerm( " << term << " ) " << std::endl; + LetMap::const_iterator it = map.find(term); - Assert (it != map.end()); - unsigned id = it->second.id; - unsigned count = it->second.count; - if (count > LET_COUNT) { - os <<"let"<second.id; + unsigned count = it->second.count; + if (count > LET_COUNT) { + os <<"let"<& lemma, std::ostream& th = new theory::arrays::TheoryArrays(&fakeContext, &fakeContext, oc, v, ProofManager::currentPM()->getLogicInfo(), "replay::"); + } else if (d_theory->getId() == theory::THEORY_ARITH) { + Trace("theory-proof-debug") << "Arith proofs currently not supported. Use 'trust'" << std::endl; + os << " (clausify_false trust)"; + return; } else { InternalError(std::string("can't generate theory-proof for ") + ProofManager::currentPM()->getLogic()); } + + Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->ProduceProofs()" << std::endl; th->produceProofs(); + Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->ProduceProofs() DONE" << std::endl; + MyPreRegisterVisitor preRegVisitor(th); for( unsigned i=0; i::run(preRegVisitor, lit); th->assertFact(lit, false); } + + Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->check()" << std::endl; th->check(theory::Theory::EFFORT_FULL); + Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->check() DONE" << std::endl; + if(oc.d_conflict.isNull()) { - Trace("theory-proof-debug") << "; conflict is null" << std::endl; + Trace("pf::tp") << "; conflict is null" << std::endl; Assert(!oc.d_lemma.isNull()); - Trace("theory-proof-debug") << "; ++ but got lemma: " << oc.d_lemma << std::endl; - Trace("theory-proof-debug") << "; asserting " << oc.d_lemma[1].negate() << std::endl; - th->assertFact(oc.d_lemma[1].negate(), false); + Trace("pf::tp") << "; ++ but got lemma: " << oc.d_lemma << std::endl; + + // Original, as in Liana's branch + // Trace("pf::tp") << "; asserting " << oc.d_lemma[1].negate() << std::endl; + // th->assertFact(oc.d_lemma[1].negate(), false); + // th->check(theory::Theory::EFFORT_FULL); + + // Altered version, to handle OR lemmas + + if (oc.d_lemma.getKind() == kind::OR) { + Debug("pf::tp") << "OR lemma. Negating each child separately" << std::endl; + for (unsigned i = 0; i < oc.d_lemma.getNumChildren(); ++i) { + if (oc.d_lemma[i].getKind() == kind::NOT) { + Trace("pf::tp") << "; asserting fact: " << oc.d_lemma[i][0] << std::endl; + th->assertFact(oc.d_lemma[i][0], false); + } + else { + Trace("pf::tp") << "; asserting fact: " << oc.d_lemma[i].notNode() << std::endl; + th->assertFact(oc.d_lemma[i].notNode(), false); + } + } + } + else { + Unreachable(); + + Assert(oc.d_lemma.getKind() == kind::NOT); + Debug("pf::tp") << "NOT lemma" << std::endl; + Trace("pf::tp") << "; asserting fact: " << oc.d_lemma[0] << std::endl; + th->assertFact(oc.d_lemma[0], false); + } + + // Trace("pf::tp") << "; ++ but got lemma: " << oc.d_lemma << std::endl; + // Trace("pf::tp") << "; asserting " << oc.d_lemma[1].negate() << std::endl; + // th->assertFact(oc.d_lemma[1].negate(), false); + + // th->check(theory::Theory::EFFORT_FULL); } + Debug("pf::tp") << "Calling oc.d_proof->toStream(os)" << std::endl; oc.d_proof->toStream(os); + Debug("pf::tp") << "Calling oc.d_proof->toStream(os) -- DONE!" << std::endl; + + Debug("pf::tp") << "About to delete the theory solver used for proving the lemma... " << std::endl; delete th; + Debug("pf::tp") << "About to delete the theory solver used for proving the lemma: DONE! " << std::endl; } bool TheoryProofEngine::supportedTheory(theory::TheoryId id) { return (id == theory::THEORY_ARRAY || + id == theory::THEORY_ARITH || id == theory::THEORY_BV || id == theory::THEORY_UF || id == theory::THEORY_BOOL); @@ -560,7 +711,7 @@ void BooleanProof::registerTerm(Expr term) { } } -void LFSCBooleanProof::printTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { Assert (term.getType().isBoolean()); if (term.isVariable()) { os << "(p_app " << ProofManager::sanitize(term) <<")"; @@ -611,11 +762,16 @@ void LFSCBooleanProof::printTerm(Expr term, std::ostream& os, const LetMap& map) } -void LFSCBooleanProof::printSort(Type type, std::ostream& os) { +void LFSCBooleanProof::printOwnedSort(Type type, std::ostream& os) { Assert (type.isBoolean()); os << "Bool"; } -void LFSCBooleanProof::printDeclarations(std::ostream& os, std::ostream& paren) { + +void LFSCBooleanProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + +void LFSCBooleanProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { Expr term = *it; @@ -626,6 +782,10 @@ void LFSCBooleanProof::printDeclarations(std::ostream& os, std::ostream& paren) } } +void LFSCBooleanProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + void LFSCBooleanProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) { diff --git a/src/proof/theory_proof.h b/src/proof/theory_proof.h index d14704760..b04dd4c60 100644 --- a/src/proof/theory_proof.h +++ b/src/proof/theory_proof.h @@ -39,14 +39,14 @@ struct LetCount { static unsigned counter; static void resetCounter() { counter = 0; } static unsigned newId() { return ++counter; } - + unsigned count; unsigned id; LetCount() : count(0) , id(-1) {} - + void increment() { ++count; } LetCount(unsigned i) : count(1) @@ -65,7 +65,7 @@ struct LetCount { count = rhs.count; return *this; } -}; +}; struct LetOrderElement { Expr expr; @@ -84,7 +84,7 @@ struct LetOrderElement { typedef __gnu_cxx::hash_map < ClauseId, prop::SatClause* > IdToSatClause; typedef __gnu_cxx::hash_map LetMap; -typedef std::vector Bindings; +typedef std::vector Bindings; class TheoryProof; @@ -123,6 +123,14 @@ public: */ virtual void printSort(Type type, std::ostream& os) = 0; + /** + * Go over the assertions and register all terms with the theories. + * + * @param os + * @param paren closing parenthesis + */ + virtual void registerTermsFromAssertions() = 0; + /** * Print the theory assertions (arbitrary formulas over * theory atoms) @@ -131,6 +139,14 @@ public: * @param paren closing parenthesis */ virtual void printAssertions(std::ostream& os, std::ostream& paren) = 0; + /** + * Print variable declarations that need to appear within the proof, + * e.g. skolemized variables. + * + * @param os + * @param paren closing parenthesis + */ + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; /** * Print proofs of all the theory lemmas (must prove @@ -168,11 +184,14 @@ public: LFSCTheoryProofEngine() : TheoryProofEngine() {} - void printDeclarations(std::ostream& os, std::ostream& paren); + void registerTermsFromAssertions(); + void printSortDeclarations(std::ostream& os, std::ostream& paren); + void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printCoreTerm(Expr term, std::ostream& os, const LetMap& map); virtual void printLetTerm(Expr term, std::ostream& os); virtual void printBoundTerm(Expr term, std::ostream& os, const LetMap& map); virtual void printAssertions(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); virtual void printTheoryLemmas(const IdToSatClause& lemmas, std::ostream& os, std::ostream& paren); @@ -189,41 +208,74 @@ public: : d_theory(th) , d_proofEngine(proofEngine) {} - virtual ~TheoryProof() {}; - /** - * Print a term belonging to this theory. - * + virtual ~TheoryProof() {}; + /** + * Print a term belonging some theory, not neccessarily this one. + * * @param term expresion representing term * @param os output stream */ - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map) = 0; - /** - * Print the proof representation of the given type. - * - * @param type - * @param os + void printTerm(Expr term, std::ostream& os, const LetMap& map) { + d_proofEngine->printBoundTerm(term, os, map); + } + /** + * Print a term belonging to THIS theory. + * + * @param term expresion representing term + * @param os output stream + */ + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) = 0; + /** + * Print the proof representation of the given type that belongs to some theory. + * + * @param type + * @param os */ - virtual void printSort(Type type, std::ostream& os) = 0; - /** + void printSort(Type type, std::ostream& os) { + d_proofEngine->printSort(type, os); + } + /** + * Print the proof representation of the given type that belongs to THIS theory. + * + * @param type + * @param os + */ + virtual void printOwnedSort(Type type, std::ostream& os) = 0; + /** * Print a proof for the theory lemmas. Must prove * clause representing lemmas to be used in resolution proof. - * + * * @param os output stream */ virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); - /** - * Print the variable/sorts declarations for this theory. - * - * @param os - * @param paren + /** + * Print the sorts declarations for this theory. + * + * @param os + * @param paren */ - virtual void printDeclarations(std::ostream& os, std::ostream& paren) = 0; - /** + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren) = 0; + /** + * Print the term declarations for this theory. + * + * @param os + * @param paren + */ + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren) = 0; + /** + * Print any deferred variable/sorts declarations for this theory + * (those that need to appear inside the actual proof). + * + * @param os + * @param paren + */ + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; + /** * Register a term of this theory that appears in the proof. - * - * @param term + * + * @param term */ - virtual void registerTerm(Expr term) = 0; + virtual void registerTerm(Expr term) = 0; }; class BooleanProof : public TheoryProof { @@ -233,12 +285,14 @@ public: BooleanProof(TheoryProofEngine* proofEngine); virtual void registerTerm(Expr term); - - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map) = 0; - virtual void printSort(Type type, std::ostream& os) = 0; + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) = 0; + + virtual void printOwnedSort(Type type, std::ostream& os) = 0; virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren) = 0; - virtual void printDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; }; class LFSCBooleanProof : public BooleanProof { @@ -246,13 +300,14 @@ public: LFSCBooleanProof(TheoryProofEngine* proofEngine) : BooleanProof(proofEngine) {} - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map); - virtual void printSort(Type type, std::ostream& os); + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedSort(Type type, std::ostream& os); virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); - virtual void printDeclarations(std::ostream& os, std::ostream& paren); + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); }; - } /* CVC4 namespace */ #endif /* __CVC4__THEORY_PROOF_H */ diff --git a/src/proof/uf_proof.cpp b/src/proof/uf_proof.cpp index ec0d90ae7..6a6f8d906 100644 --- a/src/proof/uf_proof.cpp +++ b/src/proof/uf_proof.cpp @@ -21,10 +21,7 @@ #include "theory/uf/theory_uf.h" #include -using namespace CVC4; -using namespace CVC4::theory; -using namespace CVC4::theory::uf; - +namespace CVC4 { inline static Node eqNode(TNode n1, TNode n2) { return NodeManager::currentNM()->mkNode(n1.getType().isBoolean() ? kind::IFF : kind::EQUAL, n1, n2); @@ -32,14 +29,14 @@ inline static Node eqNode(TNode n1, TNode n2) { // congrence matching term helper inline static bool match(TNode n1, TNode n2) { - Debug("mgd") << "match " << n1 << " " << n2 << std::endl; + Debug("pf::uf") << "match " << n1 << " " << n2 << std::endl; if(ProofManager::currentPM()->hasOp(n1)) { n1 = ProofManager::currentPM()->lookupOp(n1); } if(ProofManager::currentPM()->hasOp(n2)) { n2 = ProofManager::currentPM()->lookupOp(n2); } - Debug("mgd") << "+ match " << n1 << " " << n2 << std::endl; + Debug("pf::uf") << "+ match " << n1 << " " << n2 << std::endl; if(n1 == n2) { return true; } @@ -77,6 +74,7 @@ void ProofUF::toStream(std::ostream& out) { } void ProofUF::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map) { + Debug("pf::uf") << "ProofUF::toStreamLFSC starting" << std::endl; Debug("lfsc-uf") << "Printing uf proof in LFSC : " << std::endl; pf->debug_print("lfsc-uf"); Debug("lfsc-uf") << std::endl; @@ -84,18 +82,18 @@ void ProofUF::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqPr } Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map) { - Debug("gk::proof") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; - pf->debug_print("gk::proof"); - Debug("gk::proof") << std::endl; + Debug("pf::uf") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; + pf->debug_print("pf::uf"); + Debug("pf::uf") << std::endl; if(tb == 0) { - Assert(pf->d_id == eq::MERGED_THROUGH_TRANS); + Assert(pf->d_id == theory::eq::MERGED_THROUGH_TRANS); Assert(!pf->d_node.isNull()); Assert(pf->d_children.size() >= 2); int neg = -1; theory::eq::EqProof subTrans; - subTrans.d_id = eq::MERGED_THROUGH_TRANS; + subTrans.d_id = theory::eq::MERGED_THROUGH_TRANS; subTrans.d_node = pf->d_node; size_t i = 0; @@ -108,38 +106,38 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } // Handle congruence closures over equalities. - else if (pf->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && pf->d_children[i]->d_node.isNull()) { - Debug("gk::proof") << "Handling congruence over equalities" << std::endl; + else if (pf->d_children[i]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && pf->d_children[i]->d_node.isNull()) { + Debug("pf::uf") << "Handling congruence over equalities" << std::endl; // Gather the sequence of consecutive congruence closures. std::vector congruenceClosures; unsigned count; - Debug("gk::proof") << "Collecting congruence sequence" << std::endl; + Debug("pf::uf") << "Collecting congruence sequence" << std::endl; for (count = 0; i + count < pf->d_children.size() && - pf->d_children[i + count]->d_id==eq::MERGED_THROUGH_CONGRUENCE && + pf->d_children[i + count]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && pf->d_children[i + count]->d_node.isNull(); ++count) { - Debug("gk::proof") << "Found a congruence: " << std::endl; - pf->d_children[i+count]->debug_print("gk::proof"); + Debug("pf::uf") << "Found a congruence: " << std::endl; + pf->d_children[i+count]->debug_print("pf::uf"); congruenceClosures.push_back(pf->d_children[i+count]); } - Debug("gk::proof") << "Total number of congruences found: " << congruenceClosures.size() << std::endl; + Debug("pf::uf") << "Total number of congruences found: " << congruenceClosures.size() << std::endl; // Determine if the "target" of the congruence sequence appears right before or right after the sequence. bool targetAppearsBefore = true; bool targetAppearsAfter = true; if ((i == 0) || (i == 1 && neg == 0)) { - Debug("gk::proof") << "Target does not appear before" << std::endl; + Debug("pf::uf") << "Target does not appear before" << std::endl; targetAppearsBefore = false; } if ((i + count >= pf->d_children.size()) || (!pf->d_children[i + count]->d_node.isNull() && pf->d_children[i + count]->d_node.getKind() == kind::NOT)) { - Debug("gk::proof") << "Target does not appear after" << std::endl; + Debug("pf::uf") << "Target does not appear after" << std::endl; targetAppearsAfter = false; } @@ -162,16 +160,16 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E // Start with the congruence closure closest to the target clause, and work our way back/forward. if (targetAppearsBefore) { for (unsigned j = 0; j < count; ++j) { - if (pf->d_children[i + j]->d_children[0]->d_id != eq::MERGED_THROUGH_REFLEXIVITY) + if (pf->d_children[i + j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + j]->d_children[0]); - if (pf->d_children[i + j]->d_children[1]->d_id != eq::MERGED_THROUGH_REFLEXIVITY) + if (pf->d_children[i + j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + j]->d_children[1]); } } else { for (unsigned j = 0; j < count; ++j) { - if (pf->d_children[i + count - 1 - j]->d_children[0]->d_id != eq::MERGED_THROUGH_REFLEXIVITY) + if (pf->d_children[i + count - 1 - j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) orderedEqualities.insert(orderedEqualities.begin(), pf->d_children[i + count - 1 - j]->d_children[0]); - if (pf->d_children[i + count - 1 - j]->d_children[1]->d_id != eq::MERGED_THROUGH_REFLEXIVITY) + if (pf->d_children[i + count - 1 - j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) orderedEqualities.insert(orderedEqualities.end(), pf->d_children[i + count - 1 - j]->d_children[1]); } } @@ -197,22 +195,22 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E Node n1; std::stringstream ss; //Assert(subTrans.d_children.size() == pf->d_children.size() - 1); - Debug("mgdx") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; + Debug("pf::uf") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; if(pf->d_children.size() > 2) { n1 = toStreamRecLFSC(ss, tp, &subTrans, 1, map); } else { n1 = toStreamRecLFSC(ss, tp, subTrans.d_children[0], 1, map); - Debug("mgdx") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; + Debug("pf::uf") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; } Node n2 = pf->d_children[neg]->d_node; Assert(n2.getKind() == kind::NOT); out << "(clausify_false (contra _ "; - Debug("mgdx") << "\nhave proven: " << n1 << std::endl; - Debug("mgdx") << "n2 is " << n2[0] << std::endl; + Debug("pf::uf") << "\nhave proven: " << n1 << std::endl; + Debug("pf::uf") << "n2 is " << n2[0] << std::endl; - if (n2[0].getNumChildren() > 0) { Debug("mgdx") << "\nn2[0]: " << n2[0][0] << std::endl; } - if (n1.getNumChildren() > 1) { Debug("mgdx") << "n1[1]: " << n1[1] << std::endl; } + if (n2[0].getNumChildren() > 0) { Debug("pf::uf") << "\nn2[0]: " << n2[0][0] << std::endl; } + if (n1.getNumChildren() > 1) { Debug("pf::uf") << "n1[1]: " << n1[1] << std::endl; } if(n2[0].getKind() == kind::APPLY_UF) { out << "(trans _ _ _ _ "; @@ -232,44 +230,44 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } switch(pf->d_id) { - case eq::MERGED_THROUGH_CONGRUENCE: { - Debug("mgd") << "\nok, looking at congruence:\n"; - pf->debug_print("mgd"); + case theory::eq::MERGED_THROUGH_CONGRUENCE: { + Debug("pf::uf") << "\nok, looking at congruence:\n"; + pf->debug_print("pf::uf"); std::stack stk; - for(const theory::eq::EqProof* pf2 = pf; pf2->d_id == eq::MERGED_THROUGH_CONGRUENCE; pf2 = pf2->d_children[0]) { + for(const theory::eq::EqProof* pf2 = pf; pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; pf2 = pf2->d_children[0]) { Assert(!pf2->d_node.isNull()); Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF || pf2->d_node.getKind() == kind::BUILTIN || pf2->d_node.getKind() == kind::APPLY_UF || pf2->d_node.getKind() == kind::SELECT || pf2->d_node.getKind() == kind::STORE); Assert(pf2->d_children.size() == 2); out << "(cong _ _ _ _ _ _ "; stk.push(pf2); } - Assert(stk.top()->d_children[0]->d_id != eq::MERGED_THROUGH_CONGRUENCE); + Assert(stk.top()->d_children[0]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE); NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), b2(kind::PARTIAL_APPLY_UF); const theory::eq::EqProof* pf2 = stk.top(); stk.pop(); - Assert(pf2->d_id == eq::MERGED_THROUGH_CONGRUENCE); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); Node n1 = toStreamRecLFSC(out, tp, pf2->d_children[0], tb + 1, map); out << " "; std::stringstream ss; Node n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); - Debug("mgd") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; - pf2->debug_print("mgd"); - Debug("mgd") << "looking at " << pf2->d_node << "\n"; - Debug("mgd") << " " << n1 << "\n"; - Debug("mgd") << " " << n2 << "\n"; + Debug("pf::uf") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; + pf2->debug_print("pf::uf"); + Debug("pf::uf") << "looking at " << pf2->d_node << "\n"; + Debug("pf::uf") << " " << n1 << "\n"; + Debug("pf::uf") << " " << n2 << "\n"; int side = 0; if(match(pf2->d_node, n1[0])) { //if(tb == 1) { - Debug("mgd") << "SIDE IS 0\n"; + Debug("pf::uf") << "SIDE IS 0\n"; //} side = 0; } else { //if(tb == 1) { - Debug("mgd") << "SIDE IS 1\n"; + Debug("pf::uf") << "SIDE IS 1\n"; //} if(!match(pf2->d_node, n1[1])) { - Debug("mgd") << "IN BAD CASE, our first subproof is\n"; - pf2->d_children[0]->debug_print("mgd"); + Debug("pf::uf") << "IN BAD CASE, our first subproof is\n"; + pf2->d_children[0]->debug_print("pf::uf"); } Assert(match(pf2->d_node, n1[1])); side = 1; @@ -294,11 +292,11 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } else { b2 << n1[1-side]; } - Debug("mgd") << "pf2->d_node " << pf2->d_node << std::endl; - Debug("mgd") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; - Debug("mgd") << "n1 " << n1 << std::endl; - Debug("mgd") << "n2 " << n2 << std::endl; - Debug("mgd") << "side " << side << std::endl; + Debug("pf::uf") << "pf2->d_node " << pf2->d_node << std::endl; + Debug("pf::uf") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; + Debug("pf::uf") << "n1 " << n1 << std::endl; + Debug("pf::uf") << "n2 " << n2 << std::endl; + Debug("pf::uf") << "side " << side << std::endl; if(pf2->d_node[b1.getNumChildren() - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { b1 << n2[side]; b2 << n2[1-side]; @@ -312,20 +310,20 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E out << ")"; while(!stk.empty()) { if(tb == 1) { - Debug("mgd") << "\nMORE TO DO\n"; + Debug("pf::uf") << "\nMORE TO DO\n"; } pf2 = stk.top(); stk.pop(); - Assert(pf2->d_id == eq::MERGED_THROUGH_CONGRUENCE); + Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); out << " "; ss.str(""); n2 = toStreamRecLFSC(ss, tp, pf2->d_children[1], tb + 1, map); - Debug("mgd") << "\nok, in cong[" << stk.size() << "]" << "\n"; - Debug("mgd") << "looking at " << pf2->d_node << "\n"; - Debug("mgd") << " " << n1 << "\n"; - Debug("mgd") << " " << n2 << "\n"; - Debug("mgd") << " " << b1 << "\n"; - Debug("mgd") << " " << b2 << "\n"; + Debug("pf::uf") << "\nok, in cong[" << stk.size() << "]" << "\n"; + Debug("pf::uf") << "looking at " << pf2->d_node << "\n"; + Debug("pf::uf") << " " << n1 << "\n"; + Debug("pf::uf") << " " << n2 << "\n"; + Debug("pf::uf") << " " << b1 << "\n"; + Debug("pf::uf") << " " << b2 << "\n"; if(pf2->d_node[b1.getNumChildren()] == n2[side]) { b1 << n2[side]; b2 << n2[1-side]; @@ -340,7 +338,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } n1 = b1; n2 = b2; - Debug("mgd") << "at end assert, got " << pf2->d_node << " and " << n1 << std::endl; + Debug("pf::uf") << "at end assert, got " << pf2->d_node << " and " << n1 << std::endl; if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { Assert(n1 == pf2->d_node); } @@ -353,7 +351,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } b1.append(n1.begin(), n1.end()); n1 = b1; - Debug("mgd") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; + Debug("pf::uf") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; if(pf2->d_node.getKind() == kind::APPLY_UF) { Assert(n1 == pf2->d_node); } @@ -370,12 +368,12 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } Node n = (side == 0 ? eqNode(n1, n2) : eqNode(n2, n1)); if(tb == 1) { - Debug("mgdx") << "\ncong proved: " << n << "\n"; + Debug("pf::uf") << "\ncong proved: " << n << "\n"; } return n; } - case eq::MERGED_THROUGH_REFLEXIVITY: + case theory::eq::MERGED_THROUGH_REFLEXIVITY: Assert(!pf->d_node.isNull()); Assert(pf->d_children.empty()); out << "(refl _ "; @@ -383,33 +381,44 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E out << ")"; return eqNode(pf->d_node, pf->d_node); - case eq::MERGED_THROUGH_EQUALITY: + case theory::eq::MERGED_THROUGH_EQUALITY: Assert(!pf->d_node.isNull()); Assert(pf->d_children.empty()); out << ProofManager::getLitName(pf->d_node.negate()); return pf->d_node; - case eq::MERGED_THROUGH_TRANS: { + case theory::eq::MERGED_THROUGH_TRANS: { Assert(!pf->d_node.isNull()); Assert(pf->d_children.size() >= 2); std::stringstream ss; - Debug("mgd") << "\ndoing trans proof[[\n"; - pf->debug_print("mgd"); - Debug("mgd") << "\n"; + Debug("pf::uf") << "\ndoing trans proof[[\n"; + pf->debug_print("pf::uf"); + Debug("pf::uf") << "\n"; Node n1 = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); - Debug("mgd") << "\ndoing trans proof, got n1 " << n1 << "\n"; + Debug("pf::uf") << "\ndoing trans proof, got n1 " << n1 << "\n"; if(tb == 1) { - Debug("mgdx") << "\ntrans proof[0], got n1 " << n1 << "\n"; + Debug("pf::uf") << "\ntrans proof[0], got n1 " << n1 << "\n"; } bool identicalEqualities = false; bool evenLengthSequence; Node nodeAfterEqualitySequence; + std::map childToStream; + for(size_t i = 1; i < pf->d_children.size(); ++i) { std::stringstream ss1(ss.str()), ss2; ss.str(""); - Node n2 = toStreamRecLFSC(ss2, tp, pf->d_children[i], tb + 1, map); + + // It is possible that we've already converted the i'th child to stream. If so, + // use previously stored result. Otherwise, convert and store. + Node n2; + if (childToStream.find(i) != childToStream.end()) + n2 = childToStream[i]; + else { + n2 = toStreamRecLFSC(ss2, tp, pf->d_children[i], tb + 1, map); + childToStream[i] = n2; + } // The following branch is dedicated to handling sequences of identical equalities, // i.e. trans[ a=b, a=b, a=b ]. @@ -425,13 +434,13 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E if (((n1[0] == n2[0]) && (n1[1] == n2[1])) || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) { // We are in a sequence of identical equalities - Debug("gk::proof") << "Detected identical equalities: " << std::endl << "\t" << n1 << std::endl; + Debug("pf::uf") << "Detected identical equalities: " << std::endl << "\t" << n1 << std::endl; if (!identicalEqualities) { // The sequence of identical equalities has started just now identicalEqualities = true; - Debug("gk::proof") << "The sequence is just beginning. Determining length..." << std::endl; + Debug("pf::uf") << "The sequence is just beginning. Determining length..." << std::endl; // Determine whether the length of this sequence is odd or even. evenLengthSequence = true; @@ -455,11 +464,11 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E if (evenLengthSequence) { // If the length is even, we need to apply transitivity for the "correct" hand of the equality. - Debug("gk::proof") << "Equality sequence of even length" << std::endl; - Debug("gk::proof") << "n1 is: " << n1 << std::endl; - Debug("gk::proof") << "n2 is: " << n2 << std::endl; - Debug("gk::proof") << "pf-d_node is: " << pf->d_node << std::endl; - Debug("gk::proof") << "Next node is: " << nodeAfterEqualitySequence << std::endl; + Debug("pf::uf") << "Equality sequence of even length" << std::endl; + Debug("pf::uf") << "n1 is: " << n1 << std::endl; + Debug("pf::uf") << "n2 is: " << n2 << std::endl; + Debug("pf::uf") << "pf-d_node is: " << pf->d_node << std::endl; + Debug("pf::uf") << "Next node is: " << nodeAfterEqualitySequence << std::endl; ss << "(trans _ _ _ _ "; @@ -472,7 +481,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E n1 = eqNode(n1[1], n1[1]); ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); } else { - Debug("gk::proof") << "Error: identical equalities over, but hands don't match what we're proving." + Debug("pf::uf") << "Error: identical equalities over, but hands don't match what we're proving." << std::endl; Assert(false); } @@ -495,7 +504,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E n1 = eqNode(n1[1], n1[1]); } else { - Debug("gk::proof") << "Error: even length sequence, but I don't know which hand to keep!" << std::endl; + Debug("pf::uf") << "Error: even length sequence, but I don't know which hand to keep!" << std::endl; Assert(false); } } @@ -503,11 +512,11 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E ss << ")"; } else { - Debug("gk::proof") << "Equality sequence length is odd!" << std::endl; + Debug("pf::uf") << "Equality sequence length is odd!" << std::endl; ss.str(ss1.str()); } - Debug("gk::proof") << "Have proven: " << n1 << std::endl; + Debug("pf::uf") << "Have proven: " << n1 << std::endl; } else { ss.str(ss1.str()); } @@ -522,21 +531,21 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E identicalEqualities = false; } - Debug("mgd") << "\ndoing trans proof, got n2 " << n2 << "\n"; + Debug("pf::uf") << "\ndoing trans proof, got n2 " << n2 << "\n"; if(tb == 1) { - Debug("mgdx") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; - Debug("mgdx") << (n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) << "\n"; + Debug("pf::uf") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; + Debug("pf::uf") << (n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) << "\n"; if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) { - Debug("mgdx") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; - Debug("mgdx") << n1[0].getId() << " " << n1[0] << "\n"; - Debug("mgdx") << n1[1].getId() << " " << n1[1] << "\n"; - Debug("mgdx") << n2[0].getId() << " " << n2[0] << "\n"; - Debug("mgdx") << n2[1].getId() << " " << n2[1] << "\n"; - Debug("mgdx") << (n1[0] == n2[0]) << "\n"; - Debug("mgdx") << (n1[1] == n2[1]) << "\n"; - Debug("mgdx") << (n1[0] == n2[1]) << "\n"; - Debug("mgdx") << (n1[1] == n2[0]) << "\n"; + Debug("pf::uf") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; + Debug("pf::uf") << n1[0].getId() << " " << n1[0] << "\n"; + Debug("pf::uf") << n1[1].getId() << " " << n1[1] << "\n"; + Debug("pf::uf") << n2[0].getId() << " " << n2[0] << "\n"; + Debug("pf::uf") << n2[1].getId() << " " << n2[1] << "\n"; + Debug("pf::uf") << (n1[0] == n2[0]) << "\n"; + Debug("pf::uf") << (n1[1] == n2[1]) << "\n"; + Debug("pf::uf") << (n1[0] == n2[1]) << "\n"; + Debug("pf::uf") << (n1[1] == n2[0]) << "\n"; } } ss << "(trans _ _ _ _ "; @@ -546,32 +555,32 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E // Both elements of the transitivity rule are equalities/iffs { if(n1[0] == n2[0]) { - if(tb == 1) { Debug("mgdx") << "case 1\n"; } + if(tb == 1) { Debug("pf::uf") << "case 1\n"; } n1 = eqNode(n1[1], n2[1]); ss << "(symm _ _ _ " << ss1.str() << ") " << ss2.str(); } else if(n1[1] == n2[1]) { - if(tb == 1) { Debug("mgdx") << "case 2\n"; } + if(tb == 1) { Debug("pf::uf") << "case 2\n"; } n1 = eqNode(n1[0], n2[0]); ss << ss1.str() << " (symm _ _ _ " << ss2.str() << ")"; } else if(n1[0] == n2[1]) { - if(tb == 1) { Debug("mgdx") << "case 3\n"; } + if(tb == 1) { Debug("pf::uf") << "case 3\n"; } n1 = eqNode(n2[0], n1[1]); ss << ss2.str() << " " << ss1.str(); - if(tb == 1) { Debug("mgdx") << "++ proved " << n1 << "\n"; } + if(tb == 1) { Debug("pf::uf") << "++ proved " << n1 << "\n"; } } else if(n1[1] == n2[0]) { - if(tb == 1) { Debug("mgdx") << "case 4\n"; } + if(tb == 1) { Debug("pf::uf") << "case 4\n"; } n1 = eqNode(n1[0], n2[1]); ss << ss1.str() << " " << ss2.str(); } else { Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; Warning() << "0 proves " << n1 << "\n"; Warning() << "1 proves " << n2 << "\n\n"; - pf->debug_print("mgdx",0); + pf->debug_print("pf::uf",0); //toStreamRec(Warning.getStream(), pf, 0); Warning() << "\n\n"; Unreachable(); } - Debug("mgd") << "++ trans proof[" << i << "], now have " << n1 << std::endl; + Debug("pf::uf") << "++ trans proof[" << i << "], now have " << n1 << std::endl; } else if(n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF) { // n1 is an equality/iff, but n2 is a predicate if(n1[0] == n2) { @@ -602,12 +611,15 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E ss << ")"; } out << ss.str(); - Debug("mgd") << "\n++ trans proof done, have proven " << n1 << std::endl; + Debug("pf::uf") << "\n++ trans proof done, have proven " << n1 << std::endl; return n1; } - case eq::MERGED_ARRAYS_ROW: { - Debug("mgd") << "row lemma: " << pf->d_node << std::endl; + case theory::eq::MERGED_ARRAYS_ROW: { + Debug("pf::uf") << "eq::MERGED_ARRAYS_ROW encountered in UF_PROOF" << std::endl; + Unreachable(); + + Debug("pf::uf") << "row lemma: " << pf->d_node << std::endl; Assert(pf->d_node.getKind() == kind::EQUAL); TNode t1, t2, t3, t4; Node ret; @@ -621,7 +633,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E t1 = pf->d_node[0][0]; t4 = pf->d_node[1][0][2]; ret = pf->d_node[1].eqNode(pf->d_node[0]); - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; + Debug("pf::uf") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; } else { Assert(pf->d_node[0].getKind() == kind::SELECT && pf->d_node[0][0].getKind() == kind::STORE && @@ -633,7 +645,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E t1 = pf->d_node[1][0]; t4 = pf->d_node[0][0][2]; ret = pf->d_node; - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; + Debug("pf::uf") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; } out << "(row _ _ "; tp->printTerm(t2.toExpr(), out, map); @@ -647,8 +659,11 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E return ret; } - case eq::MERGED_ARRAYS_ROW1: { - Debug("mgd") << "row1 lemma: " << pf->d_node << std::endl; + case theory::eq::MERGED_ARRAYS_ROW1: { + Debug("pf::uf") << "eq::MERGED_ARRAYS_ROW1 encountered in UF_PROOF" << std::endl; + Unreachable(); + + Debug("pf::uf") << "row1 lemma: " << pf->d_node << std::endl; Assert(pf->d_node.getKind() == kind::EQUAL); TNode t1, t2, t3; Node ret; @@ -660,7 +675,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E t2 = pf->d_node[1][0][1]; t3 = pf->d_node[0]; ret = pf->d_node[1].eqNode(pf->d_node[0]); - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; + Debug("pf::uf") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; } else { Assert(pf->d_node[0].getKind() == kind::SELECT && pf->d_node[0][0].getKind() == kind::STORE && @@ -670,7 +685,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E t2 = pf->d_node[0][0][1]; t3 = pf->d_node[1]; ret = pf->d_node; - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; + Debug("pf::uf") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; } out << "(row1 _ _ "; tp->printTerm(t1.toExpr(), out, map); @@ -685,7 +700,7 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E default: Assert(!pf->d_node.isNull()); Assert(pf->d_children.empty()); - Debug("mgd") << "theory proof: " << pf->d_node << " by rule " << int(pf->d_id) << std::endl; + Debug("pf::uf") << "theory proof: " << pf->d_node << " by rule " << int(pf->d_id) << std::endl; AlwaysAssert(false); return pf->d_node; } @@ -722,8 +737,10 @@ void UFProof::registerTerm(Expr term) { } } -void LFSCUFProof::printTerm(Expr term, std::ostream& os, const LetMap& map) { - Assert (Theory::theoryOf(term) == THEORY_UF); +void LFSCUFProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { + Debug("pf::uf") << std::endl << "(pf::uf) LFSCUfProof::printOwnedTerm: term = " << term << std::endl; + + Assert (theory::Theory::theoryOf(term) == theory::THEORY_UF); if (term.getKind() == kind::VARIABLE || term.getKind() == kind::SKOLEM) { @@ -742,7 +759,7 @@ void LFSCUFProof::printTerm(Expr term, std::ostream& os, const LetMap& map) { } os << func << " "; for (unsigned i = 0; i < term.getNumChildren(); ++i) { - printTerm(term[i], os, map); + d_proofEngine->printBoundTerm(term[i], os, map); os << ")"; } if(term.getType().isBoolean()) { @@ -750,7 +767,9 @@ void LFSCUFProof::printTerm(Expr term, std::ostream& os, const LetMap& map) { } } -void LFSCUFProof::printSort(Type type, std::ostream& os) { +void LFSCUFProof::printOwnedSort(Type type, std::ostream& os) { + Debug("pf::uf") << std::endl << "(pf::uf) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; + Assert (type.isSort()); os << type <<" "; } @@ -765,13 +784,17 @@ void LFSCUFProof::printTheoryLemmaProof(std::vector& lemma, std::ostream& UFProof::printTheoryLemmaProof( lemma, os, paren ); } -void LFSCUFProof::printDeclarations(std::ostream& os, std::ostream& paren) { - // declaring the sorts +void LFSCUFProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { for (TypeSet::const_iterator it = d_sorts.begin(); it != d_sorts.end(); ++it) { - os << "(% " << *it << " sort\n"; - paren << ")"; + if (!ProofManager::currentPM()->wasPrinted(*it)) { + os << "(% " << *it << " sort\n"; + paren << ")"; + ProofManager::currentPM()->markPrinted(*it); + } } +} +void LFSCUFProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { // declaring the terms for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { Expr term = *it; @@ -802,3 +825,9 @@ void LFSCUFProof::printDeclarations(std::ostream& os, std::ostream& paren) { paren << ")"; } } + +void LFSCUFProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + +} /* namespace CVC4 */ diff --git a/src/proof/uf_proof.h b/src/proof/uf_proof.h index 121db1fcd..5f6d4203a 100644 --- a/src/proof/uf_proof.h +++ b/src/proof/uf_proof.h @@ -37,7 +37,7 @@ public: static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map); }; - + namespace theory { namespace uf { class TheoryUF; @@ -51,7 +51,7 @@ class UFProof : public TheoryProof { protected: TypeSet d_sorts; // all the uninterpreted sorts in this theory ExprSet d_declarations; // all the variable/function declarations - + public: UFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* proofEngine); @@ -63,10 +63,12 @@ public: LFSCUFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* proofEngine) : UFProof(uf, proofEngine) {} - virtual void printTerm(Expr term, std::ostream& os, const LetMap& map); - virtual void printSort(Type type, std::ostream& os); + virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedSort(Type type, std::ostream& os); virtual void printTheoryLemmaProof(std::vector& lemma, std::ostream& os, std::ostream& paren); - virtual void printDeclarations(std::ostream& os, std::ostream& paren); + virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); + virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); + virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); }; diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index 9bc352f9b..7409c7222 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -674,7 +674,8 @@ void TseitinCnfStream::convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, - TNode from) { + TNode from, + theory::TheoryId ownerTheory) { Debug("cnf") << "convertAndAssert(" << node << ", removable = " << (removable ? "true" : "false") << ", negated = " << (negated ? "true" : "false") << ")" << endl; @@ -684,6 +685,7 @@ void TseitinCnfStream::convertAndAssert(TNode node, Node assertion = negated ? node.notNode() : (Node)node; Node from_assertion = negated? from.notNode() : (Node) from; + d_cnfProof->setExplainerTheory(ownerTheory); if (proof_id != RULE_INVALID) { d_cnfProof->pushCurrentAssertion(from.isNull() ? assertion : from_assertion); d_cnfProof->registerAssertion(from.isNull() ? assertion : from_assertion, proof_id); @@ -695,7 +697,10 @@ void TseitinCnfStream::convertAndAssert(TNode node, }); convertAndAssert(node, negated); - PROOF(if (d_cnfProof) d_cnfProof->popCurrentAssertion(); ); + PROOF + (if (d_cnfProof) { + d_cnfProof->popCurrentAssertion(); + }); } void TseitinCnfStream::convertAndAssert(TNode node, bool negated) { diff --git a/src/prop/cnf_stream.h b/src/prop/cnf_stream.h index a6b6781ca..32e2205a1 100644 --- a/src/prop/cnf_stream.h +++ b/src/prop/cnf_stream.h @@ -207,9 +207,14 @@ public: * @param node node to convert and assert * @param removable whether the sat solver can choose to remove the clauses * @param negated whether we are asserting the node negated + * @param ownerTheory indicates the theory that should invoked to prove the formula. */ - virtual void convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, TNode from = TNode::null()) = 0; - + virtual void convertAndAssert(TNode node, + bool removable, + bool negated, + ProofRule proof_id, + TNode from = TNode::null(), + theory::TheoryId ownerTheory = theory::THEORY_LAST) = 0; /** * Get the node that is represented by the given SatLiteral. * @param literal the literal from the sat solver @@ -278,7 +283,8 @@ public: * @param negated true if negated */ void convertAndAssert(TNode node, bool removable, - bool negated, ProofRule rule, TNode from = TNode::null()); + bool negated, ProofRule rule, TNode from = TNode::null(), + theory::TheoryId ownerTheory = theory::THEORY_LAST); /** * Constructs the stream to use the given sat solver. diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index b13448bb2..411b89514 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -230,7 +230,7 @@ CRef Solver::reason(Var x) { proxy->explainPropagation(MinisatSatSolver::toSatLiteral(l), explanation_cl); vec explanation; - MinisatSatSolver::toMinisatClause(explanation_cl, explanation); + MinisatSatSolver::toMinisatClause(explanation_cl, explanation); // Sort the literals by trail index level lemma_lt lt(*this); @@ -603,20 +603,20 @@ Lit Solver::pickBranchLit() /*_________________________________________________________________________________________________ | | analyze : (confl : Clause*) (out_learnt : vec&) (out_btlevel : int&) -> [void] -| +| | Description: | Analyze conflict and produce a reason clause. -| +| | Pre-conditions: | * 'out_learnt' is assumed to be cleared. | * Current decision level must be greater than root level. -| +| | Post-conditions: | * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. -| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the | rest of literals. There may be others from the same level though. | * returns the maximal level of the resolved clauses -| +| |________________________________________________________________________________________________@*/ int Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) { @@ -662,14 +662,14 @@ int Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) } } } - + // Select next clause to look at: while (!seen[var(trail[index--])]); p = trail[index+1]; confl = reason(var(p)); seen[var(p)] = 0; pathC--; - + if ( pathC > 0 && confl != CRef_Undef ) { PROOF( ProofManager::getSatProof()->addResolutionStep(p, confl, sign(p)); ) } @@ -695,13 +695,13 @@ int Solver::analyze(CRef confl, vec& out_learnt, int& out_btlevel) out_learnt[j++] = out_learnt[i]; } else { PROOF( ProofManager::getSatProof()->storeLitRedundant(out_learnt[i]); ) - // Literal is redundant, to be safe, mark the level as current assertion level + // Literal is redundant, to be safe, mark the level as current assertion level // TODO: maybe optimize max_resolution_level = std::max(max_resolution_level, user_level(var(out_learnt[i]))); } } } - + }else if (ccmin_mode == 1){ Unreachable(); for (i = j = 1; i < out_learnt.size(); i++){ @@ -787,7 +787,7 @@ bool Solver::litRedundant(Lit p, uint32_t abstract_levels) /*_________________________________________________________________________________________________ | | analyzeFinal : (p : Lit) -> [void] -| +| | Description: | Specialized analysis procedure to express the final conflict in terms of assumptions. | Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and @@ -866,7 +866,7 @@ CRef Solver::propagate(TheoryCheckType type) if (lemmas.size() > 0) { recheck = true; confl = updateLemmas(); - return confl; + return confl; } else { recheck = proxy->theoryNeedCheck(); return confl; @@ -922,7 +922,7 @@ void Solver::propagateTheory() { proxy->theoryPropagate(propagatedLiteralsClause); vec propagatedLiterals; - MinisatSatSolver::toMinisatClause(propagatedLiteralsClause, propagatedLiterals); + MinisatSatSolver::toMinisatClause(propagatedLiteralsClause, propagatedLiterals); int oldTrailSize = trail.size(); Debug("minisat") << "old trail size is " << oldTrailSize << ", propagating " << propagatedLiterals.size() << " lits..." << std::endl; @@ -964,11 +964,11 @@ void Solver::theoryCheck(CVC4::theory::Theory::Effort effort) /*_________________________________________________________________________________________________ | | propagateBool : [void] -> [Clause*] -| +| | Description: | Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, | otherwise CRef_Undef. -| +| | Post-conditions: | * the propagation queue is empty, even if there was a conflict. |________________________________________________________________________________________________@*/ @@ -1038,16 +1038,16 @@ CRef Solver::propagateBool() /*_________________________________________________________________________________________________ | | reduceDB : () -> [void] -| +| | Description: | Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked | clauses are clauses that are reason to some assignment. Binary clauses are never removed. |________________________________________________________________________________________________@*/ -struct reduceDB_lt { +struct reduceDB_lt { ClauseAllocator& ca; reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} - bool operator () (CRef x, CRef y) { - return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } + bool operator () (CRef x, CRef y) { + return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } }; void Solver::reduceDB() { @@ -1115,7 +1115,7 @@ void Solver::rebuildOrderHeap() /*_________________________________________________________________________________________________ | | simplify : [void] -> [bool] -| +| | Description: | Simplify the clause database according to the current top-level assigment. Currently, the only | thing done here is the removal of satisfied clauses, but more things can be put here. @@ -1147,11 +1147,11 @@ bool Solver::simplify() /*_________________________________________________________________________________________________ | | search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] -| +| | Description: -| Search for a model the specified number of conflicts. +| Search for a model the specified number of conflicts. | NOTE! Use negative value for 'nof_conflicts' indicate infinity. -| +| | Output: | 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If | all variables are decision variables, this means that the clause set is satisfiable. 'l_False' @@ -1220,9 +1220,9 @@ lbool Solver::search(int nof_conflicts) max_learnts *= learntsize_inc; if (verbosity >= 1) - printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", - (int)conflicts, - (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, + printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", + (int)conflicts, + (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100); } @@ -1418,7 +1418,7 @@ lbool Solver::solve_() //================================================================================================= // Writing CNF to DIMACS: -// +// // FIXME: this needs to be rewritten completely. static Var mapVar(Var x, vec& map, Var& max) @@ -1467,7 +1467,7 @@ void Solver::toDimacs(FILE* f, const vec& assumps) for (int i = 0; i < clauses_persistent.size(); i++) if (!satisfied(ca[clauses_persistent[i]])) cnt++; - + for (int i = 0; i < clauses_persistent.size(); i++) if (!satisfied(ca[clauses_persistent[i]])){ Clause& c = ca[clauses_persistent[i]]; @@ -1538,11 +1538,11 @@ void Solver::garbageCollect() { // Initialize the next region to a size corresponding to the estimated utilization degree. This // is not precise but should avoid some unnecessary reallocations for the new region: - ClauseAllocator to(ca.size() - ca.wasted()); + ClauseAllocator to(ca.size() - ca.wasted()); relocAll(to); if (verbosity >= 2) - printf("| Garbage collection: %12d bytes => %12d bytes |\n", + printf("| Garbage collection: %12d bytes => %12d bytes |\n", ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); to.moveTo(ca); } @@ -1699,7 +1699,7 @@ CRef Solver::updateLemmas() { int backtrack_index = trail.size(); PROOF(Assert (lemmas.size() == (int)lemmas_cnf_assertion.size());); - + // Attach all the clauses and enqueue all the propagations for (int i = 0; i < lemmas.size(); ++ i) { @@ -1740,7 +1740,7 @@ CRef Solver::updateLemmas() { ( Node cnf_assertion = lemmas_cnf_assertion[i].first; Node cnf_def = lemmas_cnf_assertion[i].second; - + ClauseId id = ProofManager::getSatProof()->registerUnitClause(lemma[0], THEORY_LEMMA); ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); ProofManager::getCnfProof()->setClauseDefinition(id, cnf_def); @@ -1785,20 +1785,20 @@ CRef Solver::updateLemmas() { void ClauseAllocator::reloc(CRef& cr, ClauseAllocator& to, CVC4::CoreProofProxy* proxy) { - + // FIXME what is this CRef_lazy if (cr == CRef_Lazy) return; - + CRef old = cr; // save the old reference Clause& c = operator[](cr); if (c.reloced()) { cr = c.relocation(); return; } - + cr = to.alloc(c.level(), c, c.removable()); c.relocate(cr); if (proxy) { - proxy->updateCRef(old, cr); + proxy->updateCRef(old, cr); } - // Copy extra data-fields: + // Copy extra data-fields: // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) to[cr].mark(c.mark()); if (to[cr].removable()) to[cr].activity() = c.activity(); diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index 583e9da18..ab9ee6588 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -132,12 +132,13 @@ void PropEngine::assertFormula(TNode node) { void PropEngine::assertLemma(TNode node, bool negated, bool removable, ProofRule rule, + theory::TheoryId ownerTheory, TNode from) { //Assert(d_inCheckSat, "Sat solver should be in solve()!"); Debug("prop::lemmas") << "assertLemma(" << node << ")" << endl; // Assert as (possibly) removable - d_cnfStream->convertAndAssert(node, removable, negated, rule, from); + d_cnfStream->convertAndAssert(node, removable, negated, rule, from, ownerTheory); } void PropEngine::requirePhase(TNode n, bool phase) { diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index a71d4832d..c1c433b05 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -134,7 +134,7 @@ public: * @param removable whether this lemma can be quietly removed based * on an activity heuristic (or not) */ - void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, TNode from = TNode::null()); + void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, theory::TheoryId ownerTheory, TNode from = TNode::null()); /** * If ever n is decided upon, it must be in the given phase. This diff --git a/src/prop/theory_proxy.cpp b/src/prop/theory_proxy.cpp index 63a09169f..268a90cf6 100644 --- a/src/prop/theory_proxy.cpp +++ b/src/prop/theory_proxy.cpp @@ -98,19 +98,30 @@ void TheoryProxy::theoryPropagate(std::vector& output) { void TheoryProxy::explainPropagation(SatLiteral l, SatClause& explanation) { TNode lNode = d_cnfStream->getNode(l); Debug("prop-explain") << "explainPropagation(" << lNode << ")" << std::endl; - Node theoryExplanation = d_theoryEngine->getExplanation(lNode); - PROOF(ProofManager::getCnfProof()->pushCurrentAssertion(theoryExplanation); ); - Debug("prop-explain") << "explainPropagation() => " << theoryExplanation << std::endl; - if (theoryExplanation.getKind() == kind::AND) { - Node::const_iterator it = theoryExplanation.begin(); - Node::const_iterator it_end = theoryExplanation.end(); + + NodeTheoryPair theoryExplanation = d_theoryEngine->getExplanationAndExplainer(lNode); + Node explanationNode = theoryExplanation.node; + theory::TheoryId explainerTheory = theoryExplanation.theory; + + PROOF({ + ProofManager::getCnfProof()->pushCurrentAssertion(explanationNode); + ProofManager::getCnfProof()->setExplainerTheory(explainerTheory); + + Debug("pf::sat") << "TheoryProxy::explainPropagation: setting explainer theory to: " + << explainerTheory << std::endl; + }); + + Debug("prop-explain") << "explainPropagation() => " << explanationNode << std::endl; + if (explanationNode.getKind() == kind::AND) { + Node::const_iterator it = explanationNode.begin(); + Node::const_iterator it_end = explanationNode.end(); explanation.push_back(l); for (; it != it_end; ++ it) { explanation.push_back(~d_cnfStream->getLiteral(*it)); } } else { explanation.push_back(l); - explanation.push_back(~d_cnfStream->getLiteral(theoryExplanation)); + explanation.push_back(~d_cnfStream->getLiteral(explanationNode)); } } @@ -164,7 +175,7 @@ void TheoryProxy::notifyRestart() { if(lemmaCount % 1 == 0) { Debug("shared") << "=) " << asNode << std::endl; } - d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID); + d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID, theory::THEORY_LAST); } else { Debug("shared") << "=(" << asNode << std::endl; } diff --git a/src/smt/smt_engine_check_proof.cpp b/src/smt/smt_engine_check_proof.cpp index 354a43cc8..1712744d7 100644 --- a/src/smt/smt_engine_check_proof.cpp +++ b/src/smt/smt_engine_check_proof.cpp @@ -63,17 +63,26 @@ void SmtEngine::checkProof() { Chat() << "checking proof..." << endl; - if( !(d_logic.isPure(theory::THEORY_BOOL) || - d_logic.isPure(theory::THEORY_BV) || - (d_logic.isPure(theory::THEORY_UF) && - ! d_logic.hasCardinalityConstraints())) || - d_logic.isQuantified()) { + if ( !(d_logic.isPure(theory::THEORY_BOOL) || + d_logic.isPure(theory::THEORY_BV) || + d_logic.isPure(theory::THEORY_ARRAY) || + (d_logic.isPure(theory::THEORY_UF) && + ! d_logic.hasCardinalityConstraints())) || + d_logic.isQuantified()) { // no checking for these yet Notice() << "Notice: no proof-checking for non-UF/Bool/BV proofs yet" << endl; return; } - char* pfFile = strdup("/tmp/cvc4_proof.XXXXXX"); + char const* tempDir = getenv("TMPDIR"); + if (!tempDir) { + tempDir = "/tmp"; + } + + stringstream pfPath; + pfPath << tempDir << "/cvc4_proof.XXXXXX"; + + char* pfFile = strdup(pfPath.str().c_str()); int fd = mkstemp(pfFile); // ensure this temp file is removed after diff --git a/src/theory/arrays/array_info.cpp b/src/theory/arrays/array_info.cpp index 55f013f8c..16412c05b 100644 --- a/src/theory/arrays/array_info.cpp +++ b/src/theory/arrays/array_info.cpp @@ -44,16 +44,16 @@ Info::~Info() { in_stores->deleteSelf(); } -ArrayInfo::ArrayInfo(context::Context* c, Backtracker* b) +ArrayInfo::ArrayInfo(context::Context* c, Backtracker* b, std::string statisticsPrefix) : ct(c), bck(b), info_map(), - d_mergeInfoTimer("theory::arrays::mergeInfoTimer"), - d_avgIndexListLength("theory::arrays::avgIndexListLength"), - d_avgStoresListLength("theory::arrays::avgStoresListLength"), - d_avgInStoresListLength("theory::arrays::avgInStoresListLength"), - d_listsCount("theory::arrays::listsCount",0), - d_callsMergeInfo("theory::arrays::callsMergeInfo",0), - d_maxList("theory::arrays::maxList",0), - d_tableSize("theory::arrays::infoTableSize", info_map) { + d_mergeInfoTimer(statisticsPrefix + "theory::arrays::mergeInfoTimer"), + d_avgIndexListLength(statisticsPrefix + "theory::arrays::avgIndexListLength"), + d_avgStoresListLength(statisticsPrefix + "theory::arrays::avgStoresListLength"), + d_avgInStoresListLength(statisticsPrefix + "theory::arrays::avgInStoresListLength"), + d_listsCount(statisticsPrefix + "theory::arrays::listsCount",0), + d_callsMergeInfo(statisticsPrefix + "theory::arrays::callsMergeInfo",0), + d_maxList(statisticsPrefix + "theory::arrays::maxList",0), + d_tableSize(statisticsPrefix + "theory::arrays::infoTableSize", info_map) { emptyList = new(true) CTNodeList(ct); emptyInfo = new Info(ct, bck); smtStatisticsRegistry()->registerStat(&d_mergeInfoTimer); @@ -208,7 +208,7 @@ void ArrayInfo::setNonLinear(const TNode a) { } else { (*it).second->isNonLinear = true; } - + } void ArrayInfo::setRIntro1Applied(const TNode a) { @@ -222,7 +222,7 @@ void ArrayInfo::setRIntro1Applied(const TNode a) { } else { (*it).second->rIntro1Applied = true; } - + } void ArrayInfo::setModelRep(const TNode a, const TNode b) { @@ -236,7 +236,7 @@ void ArrayInfo::setModelRep(const TNode a, const TNode b) { } else { (*it).second->modelRep = b; } - + } void ArrayInfo::setConstArr(const TNode a, const TNode constArr) { diff --git a/src/theory/arrays/array_info.h b/src/theory/arrays/array_info.h index 319864c34..04b9cffb1 100644 --- a/src/theory/arrays/array_info.h +++ b/src/theory/arrays/array_info.h @@ -155,7 +155,8 @@ public: currentStatisticsRegistry()->registerStat(&d_maxList); currentStatisticsRegistry()->registerStat(&d_tableSize); }*/ - ArrayInfo(context::Context* c, Backtracker* b); + + ArrayInfo(context::Context* c, Backtracker* b, std::string statisticsPrefix = ""); ~ArrayInfo(); diff --git a/src/theory/arrays/kinds b/src/theory/arrays/kinds index d5f313ca1..be16d684d 100644 --- a/src/theory/arrays/kinds +++ b/src/theory/arrays/kinds @@ -54,6 +54,11 @@ typerule STORE_ALL ::CVC4::theory::arrays::ArrayStoreTypeRule typerule ARR_TABLE_FUN ::CVC4::theory::arrays::ArrayTableFunTypeRule typerule ARRAY_LAMBDA ::CVC4::theory::arrays::ArrayLambdaTypeRule +operator PARTIAL_SELECT_0 0:2 "partial array select, for internal use only" +operator PARTIAL_SELECT_1 0:2 "partial array select, for internal use only" +typerule PARTIAL_SELECT_0 ::CVC4::theory::arrays::ArrayPartialSelectTypeRule +typerule PARTIAL_SELECT_1 ::CVC4::theory::arrays::ArrayPartialSelectTypeRule + # store operations that are ordered (by index) over a store-all are constant construle STORE ::CVC4::theory::arrays::ArrayStoreTypeRule diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 631785437..fcde18b66 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -21,13 +21,14 @@ #include "expr/kind.h" #include "options/arrays_options.h" #include "options/smt_options.h" +#include "proof/array_proof.h" +#include "proof/proof_manager.h" +#include "proof/theory_proof.h" #include "smt/command.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" #include "theory/rewriter.h" #include "theory/theory_model.h" -#include "proof/theory_proof.h" -#include "proof/proof_manager.h" #include "theory/valuation.h" using namespace std; @@ -78,7 +79,7 @@ TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, d_equalityEngine(d_notify, c, name + "theory::arrays::TheoryArrays", true), d_conflict(c, false), d_backtracker(c), - d_infoMap(c, &d_backtracker), + d_infoMap(c, &d_backtracker, name), d_mergeQueue(c), d_mergeInProgress(false), d_RowQueue(c), @@ -383,21 +384,27 @@ bool TheoryArrays::propagate(TNode literal) }/* TheoryArrays::propagate(TNode) */ -void TheoryArrays::explain(TNode literal, std::vector& assumptions) { +void TheoryArrays::explain(TNode literal, std::vector& assumptions, eq::EqProof *proof) { // Do the work bool polarity = literal.getKind() != kind::NOT; TNode atom = polarity ? literal : literal[0]; //eq::EqProof * eqp = new eq::EqProof; - eq::EqProof * eqp = NULL; + // eq::EqProof * eqp = NULL; if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) { - d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions, eqp); + d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions, proof); } else { - d_equalityEngine.explainPredicate(atom, polarity, assumptions); + d_equalityEngine.explainPredicate(atom, polarity, assumptions, proof); } - if( eqp ){ - Debug("array-pf") << " Proof is : " << std::endl; - eqp->debug_print("array-pf"); + if(proof){ + Debug("pf::array") << " Proof is : " << std::endl; + proof->debug_print("pf::array"); } + + Debug("pf::array") << "Array: explain( " << literal << " ):" << std::endl << "\t"; + for (unsigned i = 0; i < assumptions.size(); ++i) { + Debug("pf::array") << assumptions[i] << " "; + } + Debug("pf::array") << std::endl; } TNode TheoryArrays::weakEquivGetRep(TNode node) { @@ -597,7 +604,7 @@ void TheoryArrays::checkWeakEquiv(bool arraysMerged) { } } } - } + } } /** @@ -787,6 +794,7 @@ void TheoryArrays::preRegisterTermInternal(TNode node) else { d_equalityEngine.addTerm(node); } + break; } // Invariant: preregistered terms are exactly the terms in the equality engine @@ -807,12 +815,16 @@ void TheoryArrays::propagate(Effort e) } -Node TheoryArrays::explain(TNode literal) +Node TheoryArrays::explain(TNode literal) { + return explain(literal, NULL); +} + +Node TheoryArrays::explain(TNode literal, eq::EqProof *proof) { ++d_numExplain; Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::explain(" << literal << ")" << std::endl; std::vector assumptions; - explain(literal, assumptions); + explain(literal, assumptions, proof); return mkAnd(assumptions); } @@ -1230,7 +1242,11 @@ Node TheoryArrays::getSkolem(TNode ref, const string& name, const TypeNode& type makeEqual = false; } } + + Debug("pf::array") << "Pregistering a Skolem" << std::endl; preRegisterTermInternal(skolem); + Debug("pf::array") << "Pregistering a Skolem DONE" << std::endl; + if (makeEqual) { Node d = skolem.eqNode(ref); Debug("arrays-model-based") << "Asserting skolem equality " << d << endl; @@ -1239,6 +1255,8 @@ Node TheoryArrays::getSkolem(TNode ref, const string& name, const TypeNode& type d_skolemAssertions.push_back(d); d_skolemIndex = d_skolemIndex + 1; } + + Debug("pf::array") << "getSkolem DONE" << std::endl; return skolem; } @@ -1293,28 +1311,76 @@ void TheoryArrays::check(Effort e) { // Apply ArrDiseq Rule if diseq is between arrays if(fact[0][0].getType().isArray() && !d_conflict) { + if (d_conflict) { Debug("pf::array") << "Entering the skolemization branch" << std::endl; } + NodeManager* nm = NodeManager::currentNM(); TypeNode indexType = fact[0][0].getType()[0]; - TNode k = getSkolem(fact,"array_ext_index", indexType, "an extensional lemma index variable from the theory of arrays", false); + + TNode k; + // k is the skolem for this disequality. + if (!d_proofsEnabled) { + Debug("pf::array") << "Check: kind::NOT: array theory making a skolem" << std::endl; + + // If not in replay mode, generate a fresh skolem variable + k = getSkolem(fact, + "array_ext_index", + indexType, + "an extensional lemma index variable from the theory of arrays", + false); + + // Register this skolem for the proof replay phase + PROOF(ProofManager::getSkolemizationManager()->registerSkolem(fact, k)); + } else { + if (!ProofManager::getSkolemizationManager()->hasSkolem(fact)) { + // In the solution pass we didn't need this skolem. Therefore, we don't need it + // in this reply pass, either. + break; + } + + // Reuse the same skolem as in the solution pass + k = ProofManager::getSkolemizationManager()->getSkolem(fact); + Debug("pf::array") << "Skolem = " << k << std::endl; + } Node ak = nm->mkNode(kind::SELECT, fact[0][0], k); Node bk = nm->mkNode(kind::SELECT, fact[0][1], k); Node eq = ak.eqNode(bk); Node lemma = fact[0].orNode(eq.notNode()); + + // In solve mode we don't care if ak and bk are registered. If they aren't, they'll be registered + // when we output the lemma. However, in replay need the lemma to be propagated, and so we + // preregister manually. + if (d_proofsEnabled) { + if (!d_equalityEngine.hasTerm(ak)) { preRegisterTermInternal(ak); } + if (!d_equalityEngine.hasTerm(bk)) { preRegisterTermInternal(bk); } + } + if (options::arraysPropagate() > 0 && d_equalityEngine.hasTerm(ak) && d_equalityEngine.hasTerm(bk)) { // Propagate witness disequality - might produce a conflict d_permRef.push_back(lemma); - d_equalityEngine.assertEquality(eq, false, lemma, eq::MERGED_ARRAYS_EXT); + Debug("pf::array") << "Asserting to the equality engine:" << std::endl + << "\teq = " << eq << std::endl + << "\treason = " << fact << std::endl; + + d_equalityEngine.assertEquality(eq, false, fact, eq::MERGED_ARRAYS_EXT); ++d_numProp; } - Trace("arrays-lem")<<"Arrays::addExtLemma " << lemma <<"\n"; - d_out->lemma(lemma); - ++d_numExt; + + if (!d_proofsEnabled) { + // If this is the solution pass, generate the lemma. Otherwise, don't generate it - + // as this is the lemma that we're reproving... + Trace("arrays-lem")<<"Arrays::addExtLemma " << lemma <<"\n"; + d_out->lemma(lemma); + ++d_numExt; + } + } else { + Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem" << std::endl; + d_modelConstraints.push_back(fact); } } break; - default: - Unreachable(); + default: + Unreachable(); } } @@ -1389,8 +1455,10 @@ void TheoryArrays::check(Effort e) { weakEquivBuildCond(r2[0], r[1], conjunctions); lemma = mkAnd(conjunctions, true); // LSH FIXME: which kind of arrays lemma is this + Trace("arrays-lem") << "Arrays::addExtLemma " << lemma <<"\n"; d_out->lemma(lemma, RULE_INVALID, false, false, true); d_readTableContext->pop(); + Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl; return; } } @@ -1401,7 +1469,7 @@ void TheoryArrays::check(Effort e) { if(!options::arraysEagerLemmas() && fullEffort(e) && !d_conflict && !options::arraysWeakEquivalence()) { // generate the lemmas on the worklist - Trace("arrays-lem")<<"Arrays::discharging lemmas: "< 0 && !d_conflict) { if (dischargeLemmas()) { break; @@ -1865,6 +1933,9 @@ void TheoryArrays::checkRowLemmas(TNode a, TNode b) void TheoryArrays::propagate(RowLemmaType lem) { + Debug("pf::array") << "TheoryArrays: RowLemma Propagate called. options::arraysPropagate() = " + << options::arraysPropagate() << std::endl; + TNode a = lem.first; TNode b = lem.second; TNode i = lem.third; @@ -1919,6 +1990,8 @@ void TheoryArrays::propagate(RowLemmaType lem) void TheoryArrays::queueRowLemma(RowLemmaType lem) { + Debug("pf::array") << "Array solver: queue row lemma called" << std::endl; + if (d_conflict || d_RowAlreadyAdded.contains(lem)) { return; } @@ -1958,14 +2031,20 @@ void TheoryArrays::queueRowLemma(RowLemmaType lem) // Prefer equality between indexes so as not to introduce new read terms if (options::arraysEagerIndexSplitting() && !bothExist && !d_equalityEngine.areDisequal(i,j, false)) { - Node i_eq_j = d_valuation.ensureLiteral(i.eqNode(j)); + Node i_eq_j; + if (!d_proofsEnabled) { + i_eq_j = d_valuation.ensureLiteral(i.eqNode(j)); // TODO: think about this + } else { + i_eq_j = i.eqNode(j); + } + getOutputChannel().requirePhase(i_eq_j, true); d_decisionRequests.push(i_eq_j); } // TODO: maybe add triggers here - if (options::arraysEagerLemmas() || bothExist) { + if ((options::arraysEagerLemmas() || bothExist) && !d_proofsEnabled) { // Make sure that any terms introduced by rewriting are appropriately stored in the equality database Node aj2 = Rewriter::rewrite(aj); @@ -2137,14 +2216,20 @@ bool TheoryArrays::dischargeLemmas() } void TheoryArrays::conflict(TNode a, TNode b) { + Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl; + eq::EqProof* proof = d_proofsEnabled ? new eq::EqProof() : NULL; if (a.getKind() == kind::CONST_BOOLEAN) { - d_conflictNode = explain(a.iffNode(b)); + d_conflictNode = explain(a.iffNode(b), proof); } else { - d_conflictNode = explain(a.eqNode(b)); + d_conflictNode = explain(a.eqNode(b), proof); } + if (!d_inCheckModel) { - d_out->conflict(d_conflictNode); + if (proof) { proof->debug_print("pf::array"); } + ProofArray* proof_array = d_proofsEnabled ? new ProofArray( proof ) : NULL; + d_out->conflict(d_conflictNode, proof_array); } + d_conflict = true; } diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index eba6c000e..2f69c52f9 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -128,7 +128,7 @@ class TheoryArrays : public Theory { TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, - std::string instanceName = ""); + std::string name = ""); ~TheoryArrays(); void setMasterEqualityEngine(eq::EqualityEngine* eq); @@ -183,7 +183,7 @@ class TheoryArrays : public Theory { bool propagate(TNode literal); /** Explain why this literal is true by adding assumptions */ - void explain(TNode literal, std::vector& assumptions); + void explain(TNode literal, std::vector& assumptions, eq::EqProof *proof); /** For debugging only- checks invariants about when things are preregistered*/ context::CDHashSet d_isPreRegistered; @@ -195,6 +195,7 @@ class TheoryArrays : public Theory { void preRegisterTerm(TNode n); void propagate(Effort e); + Node explain(TNode n, eq::EqProof *proof); Node explain(TNode n); ///////////////////////////////////////////////////////////////////////////// diff --git a/src/theory/arrays/theory_arrays_type_rules.h b/src/theory/arrays/theory_arrays_type_rules.h index 70e1c1a5b..a5d0eac69 100644 --- a/src/theory/arrays/theory_arrays_type_rules.h +++ b/src/theory/arrays/theory_arrays_type_rules.h @@ -214,6 +214,15 @@ struct ArraysProperties { } };/* struct ArraysProperties */ + +struct ArrayPartialSelectTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::PARTIAL_SELECT_0 || n.getKind() == kind::PARTIAL_SELECT_1); + return nodeManager->integerType(); + } +};/* struct ArrayPartialSelectTypeRule */ + }/* CVC4::theory::arrays namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/bv/eager_bitblaster.cpp b/src/theory/bv/eager_bitblaster.cpp index 77e75091d..ed76dbb80 100644 --- a/src/theory/bv/eager_bitblaster.cpp +++ b/src/theory/bv/eager_bitblaster.cpp @@ -99,7 +99,7 @@ void EagerBitblaster::bbAtom(TNode node) { // asserting that the atom is true iff the definition holds Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); - AlwaysAssert (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER); + AlwaysAssert (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER); storeBBAtom(node, atom_bb); d_cnfStream->convertAndAssert(atom_definition, false, false, RULE_INVALID, TNode::null()); } @@ -108,7 +108,7 @@ void EagerBitblaster::storeBBAtom(TNode atom, Node atom_bb) { if( d_bvp ){ d_bvp->registerAtomBB(atom.toExpr(), atom_bb.toExpr()); } - d_bbAtoms.insert(atom); + d_bbAtoms.insert(atom); } void EagerBitblaster::storeBBTerm(TNode node, const Bits& bits) { @@ -140,13 +140,13 @@ void EagerBitblaster::bbTerm(TNode node, Bits& bits) { void EagerBitblaster::makeVariable(TNode var, Bits& bits) { Assert(bits.size() == 0); for (unsigned i = 0; i < utils::getSize(var); ++i) { - bits.push_back(utils::mkBitOf(var, i)); + bits.push_back(utils::mkBitOf(var, i)); } - d_variables.insert(var); + d_variables.insert(var); } Node EagerBitblaster::getBBAtom(TNode node) const { - return node; + return node; } diff --git a/src/theory/bv/lazy_bitblaster.cpp b/src/theory/bv/lazy_bitblaster.cpp index ca21e98c4..c8c2d62f3 100644 --- a/src/theory/bv/lazy_bitblaster.cpp +++ b/src/theory/bv/lazy_bitblaster.cpp @@ -9,9 +9,9 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief Bitblaster for the lazy bv solver. + ** \brief Bitblaster for the lazy bv solver. ** - ** Bitblaster for the lazy bv solver. + ** Bitblaster for the lazy bv solver. **/ #include "bitblaster_template.h" @@ -123,11 +123,11 @@ void TLazyBitblaster::bbAtom(TNode node) { } atom_bb = utils::mkAnd(atoms); } - Assert (!atom_bb.isNull()); + Assert (!atom_bb.isNull()); Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); storeBBAtom(node, atom_bb); d_cnfStream->convertAndAssert(atom_definition, false, false, RULE_INVALID, TNode::null()); - return; + return; } // the bitblasted definition of the atom @@ -138,7 +138,7 @@ void TLazyBitblaster::bbAtom(TNode node) { if (!options::proof()) { atom_bb = Rewriter::rewrite(atom_bb); } - + // asserting that the atom is true iff the definition holds Node atom_definition = utils::mkNode(kind::IFF, node, atom_bb); storeBBAtom(node, atom_bb); @@ -150,7 +150,7 @@ void TLazyBitblaster::storeBBAtom(TNode atom, Node atom_bb) { if( d_bvp != NULL ){ d_bvp->registerAtomBB(atom.toExpr(), atom_bb.toExpr()); } - d_bbAtoms.insert(atom); + d_bbAtoms.insert(atom); } void TLazyBitblaster::storeBBTerm(TNode node, const Bits& bits) { @@ -160,16 +160,16 @@ void TLazyBitblaster::storeBBTerm(TNode node, const Bits& bits) { bool TLazyBitblaster::hasBBAtom(TNode atom) const { - return d_bbAtoms.find(atom) != d_bbAtoms.end(); + return d_bbAtoms.find(atom) != d_bbAtoms.end(); } void TLazyBitblaster::makeVariable(TNode var, Bits& bits) { Assert(bits.size() == 0); for (unsigned i = 0; i < utils::getSize(var); ++i) { - bits.push_back(utils::mkBitOf(var, i)); + bits.push_back(utils::mkBitOf(var, i)); } - d_variables.insert(var); + d_variables.insert(var); } uint64_t TLazyBitblaster::computeAtomWeight(TNode node, NodeSet& seen) { @@ -182,7 +182,7 @@ uint64_t TLazyBitblaster::computeAtomWeight(TNode node, NodeSet& seen) { // cnf conversion ensures the atom represents itself Node TLazyBitblaster::getBBAtom(TNode node) const { - return node; + return node; } void TLazyBitblaster::bbTerm(TNode node, Bits& bits) { @@ -220,9 +220,9 @@ void TLazyBitblaster::explain(TNode atom, std::vector& explanation) { for (unsigned i = 0; i < literal_explanation.size(); ++i) { explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); } - return; + return; } - + std::vector literal_explanation; d_satSolver->explain(lit, literal_explanation); for (unsigned i = 0; i < literal_explanation.size(); ++i) { @@ -285,7 +285,7 @@ bool TLazyBitblaster::solve() { } } Debug("bitvector") << "TLazyBitblaster::solve() asserted atoms " << d_assertedAtoms->size() <<"\n"; - d_satSolverFullModel.set(true); + d_satSolverFullModel.set(true); return prop::SAT_VALUE_TRUE == d_satSolver->solve(); } @@ -357,11 +357,11 @@ bool TLazyBitblaster::MinisatNotify::notify(prop::SatLiteral lit) { d_lazyBB->d_explanations->insert(lit, literal_explanation); } else { // we propagated it at a lower level - return true; + return true; } } ++(d_lazyBB->d_statistics.d_numBitblastingPropagations); - TNode atom = d_cnf->getNode(lit); + TNode atom = d_cnf->getNode(lit); return d_bv->storePropagation(atom, SUB_BITBLAST); } @@ -398,13 +398,13 @@ EqualityStatus TLazyBitblaster::getEqualityStatus(TNode a, TNode b) { if (a_eq_b == utils::mkTrue()) return theory::EQUALITY_TRUE; if (!d_satSolverFullModel.get()) - return theory::EQUALITY_UNKNOWN; - + return theory::EQUALITY_UNKNOWN; + // Check if cache is valid (invalidated in check and pops) if (d_bv->d_invalidateModelCache.get()) { - invalidateModelCache(); + invalidateModelCache(); } - d_bv->d_invalidateModelCache.set(false); + d_bv->d_invalidateModelCache.set(false); Node a_value = getTermModel(a, true); Node b_value = getTermModel(b, true); @@ -414,10 +414,10 @@ EqualityStatus TLazyBitblaster::getEqualityStatus(TNode a, TNode b) { if (a_value == b_value) { Debug("bv-equality-status")<< "theory::EQUALITY_TRUE_IN_MODEL\n"; - return theory::EQUALITY_TRUE_IN_MODEL; + return theory::EQUALITY_TRUE_IN_MODEL; } Debug("bv-equality-status")<< "theory::EQUALITY_FALSE_IN_MODEL\n"; - return theory::EQUALITY_FALSE_IN_MODEL; + return theory::EQUALITY_FALSE_IN_MODEL; } @@ -426,9 +426,9 @@ bool TLazyBitblaster::isSharedTerm(TNode node) { } bool TLazyBitblaster::hasValue(TNode a) { - Assert (hasBBTerm(a)); + Assert (hasBBTerm(a)); Bits bits; - getBBTerm(a, bits); + getBBTerm(a, bits); for (int i = bits.size() -1; i >= 0; --i) { prop::SatValue bit_value; if (d_cnfStream->hasLiteral(bits[i])) { @@ -456,7 +456,7 @@ Node TLazyBitblaster::getModelFromSatSolver(TNode a, bool fullModel) { if (!hasBBTerm(a)) { return fullModel? utils::mkConst(utils::getSize(a), 0u) : Node(); } - + Bits bits; getBBTerm(a, bits); Integer value(0); @@ -486,13 +486,13 @@ void TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { // not actually a leaf of the bit-vector theory if (d_variables.find(var) == d_variables.end()) continue; - - Assert (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)); + + Assert (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)); // only shared terms could not have been bit-blasted Assert (hasBBTerm(var) || isSharedTerm(var)); - + Node const_value = getModelFromSatSolver(var, fullModel); - Assert (const_value.isNull() || const_value.isConst()); + Assert (const_value.isNull() || const_value.isConst()); if(const_value != Node()) { Debug("bitvector-model") << "TLazyBitblaster::collectModelInfo (assert (= " << var << " " diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp index 89cba3ae4..8cfd5159e 100644 --- a/src/theory/shared_terms_database.cpp +++ b/src/theory/shared_terms_database.cpp @@ -122,7 +122,7 @@ Theory::Set SharedTermsDatabase::getNotifiedTheories(TNode term) const { bool SharedTermsDatabase::propagateSharedEquality(TheoryId theory, TNode a, TNode b, bool value) { - Debug("shared-terms-database") << "SharedTermsDatabase::newEquality(" << theory << a << "," << b << ", " << (value ? "true" : "false") << ")" << endl; + Debug("shared-terms-database") << "SharedTermsDatabase::newEquality(" << theory << "," << a << "," << b << ", " << (value ? "true" : "false") << ")" << endl; if (d_inConflict) { return false; diff --git a/src/theory/theory.h b/src/theory/theory.h index c988c9120..b95b4f5a3 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -277,7 +277,7 @@ protected: * */ bool d_proofsEnabled; - + /** * Returns the next assertion in the assertFact() queue. * diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 8228861be..045af0864 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -198,6 +198,7 @@ void TheoryEngine::interrupt() throw(ModalException) { void TheoryEngine::preRegister(TNode preprocessed) { + Debug("theory") << "TheoryEngine::preRegister( " << preprocessed << ")" << std::endl; if(Dump.isOn("missed-t-propagations")) { d_possiblePropagations.push_back(preprocessed); } @@ -425,7 +426,7 @@ void TheoryEngine::check(Theory::Effort effort) { } Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas"); - Debug("theory") << ", need check = " << needCheck() << endl; + Debug("theory") << ", need check = " << (needCheck() ? "YES" : "NO") << endl; if(!d_inConflict && Theory::fullEffort(effort) && d_masterEqualityEngine != NULL && !d_lemmasAdded) { AlwaysAssert(d_masterEqualityEngine->consistent()); @@ -490,7 +491,7 @@ void TheoryEngine::combineTheories() { // We need to split on it Debug("combineTheories") << "TheoryEngine::combineTheories(): requesting a split " << endl; - lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory); + lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, carePair.theory); // This code is supposed to force preference to follow what the theory models already have // but it doesn't seem to make a big difference - need to explore more -Clark // if (true) { @@ -1263,8 +1264,7 @@ static Node mkExplanation(const std::vector& explanation) { return conjunction; } - -Node TheoryEngine::getExplanation(TNode node) { +NodeTheoryPair TheoryEngine::getExplanationAndExplainer(TNode node) { Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl; bool polarity = node.getKind() != kind::NOT; @@ -1274,12 +1274,16 @@ Node TheoryEngine::getExplanation(TNode node) { if (!d_logicInfo.isSharingEnabled()) { Node explanation = theoryOf(atom)->explain(node); Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return explanation; + return NodeTheoryPair(explanation, theoryOf(atom)->getId()); } // Initial thing to explain NodeTheoryPair toExplain(node, THEORY_SAT_SOLVER, d_propagationMapTimestamp); Assert(d_propagationMap.find(toExplain) != d_propagationMap.end()); + + NodeTheoryPair nodeExplainerPair = d_propagationMap[toExplain]; + TheoryId explainer = nodeExplainerPair.theory; + // Create the workplace for explanations std::vector explanationVector; explanationVector.push_back(d_propagationMap[toExplain]); @@ -1289,7 +1293,11 @@ Node TheoryEngine::getExplanation(TNode node) { Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return explanation; + return NodeTheoryPair(explanation, explainer); +} + +Node TheoryEngine::getExplanation(TNode node) { + return getExplanationAndExplainer(node).node; } struct AtomsCollect { @@ -1391,7 +1399,8 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable, bool preprocess, - theory::TheoryId atomsTo) { + theory::TheoryId atomsTo, + theory::TheoryId ownerTheory) { // For resource-limiting (also does a time check). // spendResource(); @@ -1417,12 +1426,13 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, d_channels->getLemmaOutputChannel()->notifyNewLemma(node.toExpr()); } + std::vector additionalLemmas; + IteSkolemMap iteSkolemMap; + // Run theory preprocessing, maybe Node ppNode = preprocess ? this->preprocess(node) : Node(node); // Remove the ITEs - std::vector additionalLemmas; - IteSkolemMap iteSkolemMap; additionalLemmas.push_back(ppNode); d_iteRemover.run(additionalLemmas, iteSkolemMap); additionalLemmas[0] = theory::Rewriter::rewrite(additionalLemmas[0]); @@ -1440,10 +1450,10 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, } // assert to prop engine - d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, node); + d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, ownerTheory, node); for (unsigned i = 1; i < additionalLemmas.size(); ++ i) { additionalLemmas[i] = theory::Rewriter::rewrite(additionalLemmas[i]); - d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, node); + d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, ownerTheory, node); } // WARNING: Below this point don't assume additionalLemmas[0] to be not negated. @@ -1487,11 +1497,11 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { Node fullConflict = mkExplanation(explanationVector); Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl; Assert(properConflict(fullConflict)); - lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST); + lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); } else { // When only one theory, the conflict should need no processing Assert(properConflict(conflict)); - lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST); + lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); } } diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 05ecc0e91..21d0905e5 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -59,7 +59,7 @@ struct NodeTheoryPair { Node node; theory::TheoryId theory; size_t timestamp; - NodeTheoryPair(TNode node, theory::TheoryId theory, size_t timestamp) + NodeTheoryPair(TNode node, theory::TheoryId theory, size_t timestamp = 0) : node(node), theory(theory), timestamp(timestamp) {} NodeTheoryPair() : theory(theory::THEORY_LAST) {} @@ -263,7 +263,7 @@ class TheoryEngine { { } - void safePoint(uint64_t ammount) throw(theory::Interrupted, UnsafeInterruptException, AssertionException) { + void safePoint(uint64_t ammount) throw(theory::Interrupted, UnsafeInterruptException, AssertionException) { spendResource(ammount); if (d_engine->d_interrupted) { throw theory::Interrupted(); @@ -294,14 +294,14 @@ class TheoryEngine { Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; ++ d_statistics.lemmas; d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, rule, false, removable, preprocess, sendAtoms ? d_theory: theory::THEORY_LAST); + return d_engine->lemma(lemma, rule, false, removable, preprocess, sendAtoms ? d_theory : theory::THEORY_LAST, d_theory); } theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; ++ d_statistics.lemmas; d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory); + return d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, d_theory); } void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { @@ -448,7 +448,8 @@ class TheoryEngine { bool negated, bool removable, bool preprocess, - theory::TheoryId atomsTo); + theory::TheoryId atomsTo, + theory::TheoryId ownerTheory); /** Enusre that the given atoms are send to the given theory */ void ensureLemmaAtoms(const std::vector& atoms, theory::TheoryId theory); @@ -718,6 +719,12 @@ public: */ Node getExplanation(TNode node); + /** + * Returns an explanation of the node propagated to the SAT solver and the theory + * that propagated it. + */ + NodeTheoryPair getExplanationAndExplainer(TNode node); + /** * collect model info */ @@ -783,8 +790,8 @@ public: */ void printSynthSolution( std::ostream& out ); - /** - * Get instantiations + /** + * Get instantiations */ void getInstantiations( std::map< Node, std::vector< Node > >& insts ); diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 0ac5096d2..53347c365 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -42,7 +42,6 @@ EqualityEngine::Statistics::~Statistics() { smtStatisticsRegistry()->unregisterStat(&constantTermsCount); } - /** * Data used in the BFS search through the equality graph. */ @@ -157,7 +156,7 @@ void EqualityEngine::setMasterEqualityEngine(EqualityEngine* master) { } void EqualityEngine::enqueue(const MergeCandidate& candidate, bool back) { - Debug("equality") << d_name << "::eq::enqueue(" << d_nodes[candidate.t1Id] << ", " << d_nodes[candidate.t2Id] << ", " << candidate.type << ")" << std::endl; + Debug("equality") << d_name << "::eq::enqueue(" << d_nodes[candidate.t1Id] << ", " << d_nodes[candidate.t2Id] << ", " << candidate.type << "). reason: " << candidate.reason << std::endl; if (back) { d_propagationQueue.push_back(candidate); } else { @@ -920,7 +919,7 @@ std::string EqualityEngine::edgesToString(EqualityEdgeId edgeId) const { } void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vector& equalities, EqProof * eqp) const { - Debug("equality") << d_name << "::eq::explainEquality(" << t1 << ", " << t2 << ", " << (polarity ? "true" : "false") << ")" << std::endl; + Debug("equality") << d_name << "::eq::explainEquality(" << t1 << ", " << t2 << ", " << (polarity ? "true" : "false") << ")" << ", proof = " << (eqp ? "ON" : "OFF") << std::endl; // The terms must be there already Assert(hasTerm(t1) && hasTerm(t2));; @@ -933,13 +932,83 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vec // Get the explanation getExplanation(t1Id, t2Id, equalities, eqp); } else { + if (eqp) { + eqp->d_id = eq::MERGED_THROUGH_TRANS; + eqp->d_node = d_nodes[t1Id].eqNode(d_nodes[t2Id]).notNode(); + } + // Get the reason for this disequality EqualityPair pair(t1Id, t2Id); Assert(d_disequalityReasonsMap.find(pair) != d_disequalityReasonsMap.end(), "Don't ask for stuff I didn't notify you about"); DisequalityReasonRef reasonRef = d_disequalityReasonsMap.find(pair)->second; + for (unsigned i = reasonRef.mergesStart; i < reasonRef.mergesEnd; ++ i) { EqualityPair toExplain = d_deducedDisequalityReasons[i]; - getExplanation(toExplain.first, toExplain.second, equalities, eqp); + } + + for (unsigned i = reasonRef.mergesStart; i < reasonRef.mergesEnd; ++ i) { + + EqualityPair toExplain = d_deducedDisequalityReasons[i]; + EqProof* eqpc = NULL; + + // If we're constructing a (transitivity) proof, we don't need to include an explanation for x=x. + if (eqp && toExplain.first != toExplain.second) { + eqpc = new EqProof; + } + + // Some edges have the form ((a == b) == false). Translate this into (not (a == b)). + // Assert(d_nodes[toExplain.first] != NodeManager::currentNM()->mkConst(false)); + + getExplanation(toExplain.first, toExplain.second, equalities, eqpc); + + // if (eqpc) { + // eqp->d_children.push_back(eqpc); + // } + + if (eqpc) { + Debug("pf::ee") << "Child proof is:" << std::endl; + eqpc->debug_print("pf::ee", 1); + + if (eqpc->d_id == eq::MERGED_THROUGH_TRANS) { + std::vector orderedChildren; + bool nullCongruenceFound = false; + for (unsigned i = 0; i < eqpc->d_children.size(); ++i) { + if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && eqpc->d_children[i]->d_node.isNull()) { + + // For now, assume there can only be one null congruence child + Assert(!nullCongruenceFound); + nullCongruenceFound = true; + + Debug("pf::ee") << "Have congruence with empty d_node. Splitting..." << std::endl; + orderedChildren.insert(orderedChildren.begin(), eqpc->d_children[i]->d_children[0]); + orderedChildren.push_back(eqpc->d_children[i]->d_children[1]); + } else { + orderedChildren.push_back(eqpc->d_children[i]); + } + } + + if (nullCongruenceFound) { + eqpc->d_children = orderedChildren; + Debug("pf::ee") << "Child proof's children have been reordered. It is now:" << std::endl; + eqpc->debug_print("pf::ee", 1); + } + } + + eqp->d_children.push_back(eqpc); + } + } + + if (eqp) { + if(eqp->d_children.size() == 1) { + // The transitivity proof has just one child. Simplify. + EqProof* temp = eqp->d_children[0]; + eqp->d_children.clear(); + *eqp = *temp; + delete temp; + } + + Debug("pf::ee") << "Disequality explanation final proof: " << std::endl; + eqp->debug_print("pf::ee", 1); } } } @@ -972,7 +1041,12 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st // If the nodes are the same, we're done if (t1Id == t2Id){ if( eqp ) { - eqp->d_node = ProofManager::currentPM()->mkOp(d_nodes[t1Id]); + if ((d_nodes[t1Id].getKind() == kind::BUILTIN) && (d_nodes[t1Id].getConst() == kind::SELECT)) { + std::vector no_children; + eqp->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_0, no_children); + } else { + eqp->d_node = ProofManager::currentPM()->mkOp(d_nodes[t1Id]); + } } return; } @@ -1019,7 +1093,7 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << d_name << "::eq::getExplanation(): path found: " << std::endl; - std::vector< EqProof * > eqp_trans; + std::vector eqp_trans; // Reconstruct the path do { @@ -1029,11 +1103,13 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st MergeReasonType reasonType = d_equalityEdges[currentEdge].getReasonType(); Debug("equality") << d_name << "::eq::getExplanation(): currentEdge = " << currentEdge << ", currentNode = " << currentNode << std::endl; + Debug("equality") << d_name << " targetNode = " << d_nodes[edgeNode] << std::endl; Debug("equality") << d_name << " in currentEdge = (" << d_nodes[currentNode] << "," << d_nodes[edge.getNodeId()] << ")" << std::endl; + Debug("equality") << d_name << " reason type = " << reasonType << std::endl; - EqProof * eqpc = NULL; - //make child proof if a proof is being constructed - if( eqp ){ + EqProof* eqpc = NULL; + // Make child proof if a proof is being constructed + if (eqp) { eqpc = new EqProof; eqpc->d_id = reasonType; } @@ -1044,26 +1120,60 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << d_name << "::eq::getExplanation(): due to congruence, going deeper" << std::endl; const FunctionApplication& f1 = d_applications[currentNode].original; const FunctionApplication& f2 = d_applications[edgeNode].original; + + Debug("pf::ee") << "We need to prove two equalities:" << std::endl; + Debug("pf::ee") << "\tLHS: " << d_nodes[f1.a] << " = " << d_nodes[f2.a] << std::endl; + Debug("pf::ee") << "\tRHS: " << d_nodes[f1.b] << " = " << d_nodes[f2.b] << std::endl; + Debug("equality") << push; + Debug("equality") << "Explaining left hand side equalities" << std::endl; EqProof * eqpc1 = eqpc ? new EqProof : NULL; getExplanation(f1.a, f2.a, equalities, eqpc1); + Debug("equality") << "Explaining right hand side equalities" << std::endl; EqProof * eqpc2 = eqpc ? new EqProof : NULL; getExplanation(f1.b, f2.b, equalities, eqpc2); if( eqpc ){ eqpc->d_children.push_back( eqpc1 ); eqpc->d_children.push_back( eqpc2 ); - Debug("equality-pf") << "Congruence : " << d_nodes[currentNode] << " " << d_nodes[edgeNode] << std::endl; + Debug("pf::ee") << "Congruence : " << d_nodes[currentNode] << " " << d_nodes[edgeNode] << std::endl; if( d_nodes[currentNode].getKind()==kind::EQUAL ){ //leave node null for now eqpc->d_node = Node::null(); }else{ - Debug("equality-pf") << d_nodes[f1.a] << " / " << d_nodes[f2.a] << ", " << d_nodes[f1.b] << " / " << d_nodes[f2.b] << std::endl; + Debug("pf::ee") << d_nodes[f1.a] << " / " << d_nodes[f2.a] << ", " << d_nodes[f1.b] << " / " << d_nodes[f2.b] << std::endl; if(d_nodes[f1.a].getKind() == kind::APPLY_UF || d_nodes[f1.a].getKind() == kind::SELECT || d_nodes[f1.a].getKind() == kind::STORE) { eqpc->d_node = d_nodes[f1.a]; } else { - eqpc->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_APPLY_UF, ProofManager::currentPM()->mkOp(d_nodes[f1.a]), d_nodes[f1.b]); + + Debug("pf::ee") << "f1.a is: " << d_nodes[f1.a] + << ". kind is: " << d_nodes[f1.a].getKind() << std::endl; + + if (d_nodes[f1.a].getKind() == kind::BUILTIN && d_nodes[f1.a].getConst() == kind::SELECT) { + Debug("pf::ee") << "f1.a getConst is: " << d_nodes[f1.a].getConst() << std::endl; + + eqpc->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_1, d_nodes[f1.b]); + // The first child is a PARTIAL_SELECT_0. Give it a child so that we know what kind of (read) it is, when we dump to LFSC. + Debug("pf::ee") << "eqpc->d_children[0]->d_node.getKind() == " << eqpc->d_children[0]->d_node.getKind() << std::endl; + Assert(eqpc->d_children[0]->d_node.getKind() == kind::PARTIAL_SELECT_0); + Assert(eqpc->d_children[0]->d_children.size() == 0); + + Debug("pf::ee") << "Dumping the equality proof before:" << std::endl; + eqpc->debug_print("pf::ee", 1); + + eqpc->d_children[0]->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_0, d_nodes[f1.b]); + + Debug("pf::ee") << "Dumping the equality proof after:" << std::endl; + eqpc->debug_print("pf::ee", 1); + + //eqpc->d_children[0]->d_node.append(d_nodes[f1.b]); + } else { + eqpc->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_APPLY_UF, + ProofManager::currentPM()->mkOp(d_nodes[f1.a]), + d_nodes[f1.b]); + } + Debug("pf::ee") << "New d_node is: " << eqpc->d_node << std::endl; } } } @@ -1114,16 +1224,162 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st break; } + case MERGED_ARRAYS_EXT: + + Debug("equality") << d_name << "::eq::getExplanation(): MERGED_ARRAYS_EXT" << std::endl; + + if (eqpc) { + Node a = d_nodes[d_equalityEdges[currentEdge].getNodeId()]; + Node b = d_nodes[currentNode]; + + // GK: todo: here we assume that a=b is an assertion. We should probably call explain() + // recursively, to explain this. + + if (a == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = b.notNode(); + } else if (b == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = a.notNode(); + } else { + eqpc->d_node = a.eqNode(b); + } + + EqProof* eqpc1 = new EqProof; + eqpc1->d_node = d_equalityEdges[currentEdge].getReason(); + eqpc->d_children.push_back( eqpc1 ); + + eqpc1->debug_print("pf::ee", 1); + eqpc->debug_print("pf::ee", 1); + } + + // We push the reason into "equalities" because that's what the theory of arrays expects. + equalities.push_back(d_equalityEdges[currentEdge].getReason()); + break; + + case MERGED_ARRAYS_ROW: { + Debug("equality") << d_name << "::eq::getExplanation(): MERGED_ARRAYS_ROW" << std::endl; + + // ROW rules mean that (i==k) OR ((a[i]:=t)[k] == a[k]) + // The equality here will be either (i == k) because ((a[i]:=t)[k] != a[k]), + // or ((a[i]:=t)[k] == a[k]) because (i != k). + + if (eqpc) { + if (d_nodes[currentNode].getNumChildren() == 2) { + // This is the case of ((a[i]:=t)[k] == a[k]) because (i != k). + + // The edge is ((a[i]:=t)[k], a[k]), or (a[k], (a[i]:=t)[k]). This flag should be + // false in the first case and true in the second case. + bool currentNodeIsUnchangedArray; + + Assert(d_nodes[currentNode].getNumChildren() == 2); + Assert(d_nodes[edgeNode].getNumChildren() == 2); + + if (d_nodes[currentNode][0].getKind() == kind::VARIABLE || + d_nodes[currentNode][0].getKind() == kind::SKOLEM) { + currentNodeIsUnchangedArray = true; + } else if (d_nodes[edgeNode][0].getKind() == kind::VARIABLE || + d_nodes[edgeNode][0].getKind() == kind::SKOLEM) { + currentNodeIsUnchangedArray = false; + } else { + Assert(d_nodes[currentNode][0].getKind() == kind::STORE); + Assert(d_nodes[edgeNode][0].getKind() == kind::STORE); + + if (d_nodes[currentNode][0][0] == d_nodes[edgeNode][0]) { + currentNodeIsUnchangedArray = false; + } else if (d_nodes[edgeNode][0][0] == d_nodes[currentNode][0]) { + currentNodeIsUnchangedArray = true; + } else { + Unreachable(); + } + } + + Node indexOne = + currentNodeIsUnchangedArray ? d_nodes[currentNode][1] : d_nodes[currentNode][0][1]; + Node indexTwo = + currentNodeIsUnchangedArray ? d_nodes[edgeNode][0][1] : d_nodes[edgeNode][1]; + + // Some assertions to ensure that the theory of arrays behaves as expected + Assert(d_nodes[currentNode][1] == d_nodes[edgeNode][1]); + if (currentNodeIsUnchangedArray) { + Assert(d_nodes[currentNode][0] == d_nodes[edgeNode][0][0]); + } else { + Assert(d_nodes[currentNode][0][0] == d_nodes[edgeNode][0]); + } + + Debug("pf::ee") << "Getting explanation for ROW guard: " + << indexOne << " != " << indexTwo << std::endl; + + EqProof* eqpcc = eqpc ? new EqProof : NULL; + explainEquality(indexOne, indexTwo, false, equalities, eqpcc); + + Debug("pf::ee") << "The two indices are: " << indexOne << ", " << indexTwo << std::endl; + Debug("pf::ee") << "And the explanation of their disequality is: " << std::endl; + eqpcc->debug_print("pf::ee", 1); + eqpc->d_children.push_back(eqpcc); + } else { + // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), + + Debug("pf::ee") << "In the new case of ROW!" << std::endl; + + Node indexOne = d_nodes[currentNode]; + Node indexTwo = d_nodes[edgeNode]; + + Debug("pf::ee") << "The two indices are: " << indexOne << ", " << indexTwo << std::endl; + Debug("pf::ee") << "The reason for the edge is: " << d_equalityEdges[currentEdge].getReason() + << std::endl; + + Assert(d_equalityEdges[currentEdge].getReason().getNumChildren() == 2); + Node reason = d_equalityEdges[currentEdge].getReason()[1]; + Debug("pf::ee") << "Getting explanation for ROW guard: " << reason << std::endl; + + EqProof* eqpcc = eqpc ? new EqProof : NULL; + explainEquality(reason[0], reason[1], false, equalities, eqpcc); + + if (eqpc) { + Debug("pf::ee") << "The guard's explanation is: " << std::endl; + eqpcc->debug_print("pf::ee", 1); + eqpc->d_children.push_back(eqpcc); + } + } + + Node a = d_nodes[d_equalityEdges[currentEdge].getNodeId()]; + Node b = d_nodes[currentNode]; + + if (a == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = b.notNode(); + } else if (b == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = a.notNode(); + } else { + eqpc->d_node = a.eqNode(b); + } + } + + // We push the reason into "equalities" because that's what the theory of arrays expects. + equalities.push_back(d_equalityEdges[currentEdge].getReason()); + break; + } + default: { // Construct the equality Debug("equality") << d_name << "::eq::getExplanation(): adding: " << d_equalityEdges[currentEdge].getReason() << std::endl; - if( eqpc ){ - if(reasonType == MERGED_THROUGH_EQUALITY) { + Debug("equality") << d_name << "::eq::getExplanation(): reason type = " << reasonType << std::endl; + + if (eqpc) { + if (reasonType == MERGED_THROUGH_EQUALITY) { eqpc->d_node = d_equalityEdges[currentEdge].getReason(); } else { - // theory-specific proof rule - eqpc->d_node = d_nodes[d_equalityEdges[currentEdge].getNodeId()].eqNode(d_nodes[currentNode]); - Debug("equality-pf") << "theory eq : " << eqpc->d_node << std::endl; + // The LFSC translator prefers (not (= a b)) over (= (a==b) false) + + Node a = d_nodes[d_equalityEdges[currentEdge].getNodeId()]; + Node b = d_nodes[currentNode]; + + if (a == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = b.notNode(); + } else if (b == NodeManager::currentNM()->mkConst(false)) { + eqpc->d_node = a.notNode(); + } else { + eqpc->d_node = a.eqNode(b); + } + Debug("pf::ee") << "theory eq : " << eqpc->d_node << std::endl; } eqpc->d_id = reasonType; } @@ -1137,7 +1393,7 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st currentIndex = bfsQueue[currentIndex].previousIndex; //---from Morgan--- - if(eqpc != NULL && eqpc->d_id == MERGED_THROUGH_REFLEXIVITY) { + if (eqpc != NULL && eqpc->d_id == MERGED_THROUGH_REFLEXIVITY) { if(eqpc->d_node.isNull()) { Assert(eqpc->d_children.size() == 1); EqProof *p = eqpc; @@ -1149,11 +1405,10 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st } //---end from Morgan--- - eqp_trans.push_back( eqpc ); - + eqp_trans.push_back(eqpc); } while (currentEdge != null_id); - if(eqp) { + if (eqp) { if(eqp_trans.size() == 1) { *eqp = *eqp_trans[0]; delete eqp_trans[0]; @@ -1162,6 +1417,8 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st eqp->d_children.insert( eqp->d_children.end(), eqp_trans.begin(), eqp_trans.end() ); eqp->d_node = NodeManager::currentNM()->mkNode(d_nodes[t1Id].getType().isBoolean() ? kind::IFF : kind::EQUAL, d_nodes[t1Id], d_nodes[t2Id]); } + + eqp->debug_print("pf::ee", 1); } // Done @@ -1508,7 +1765,7 @@ void EqualityEngine::debugPrintGraph() const { EqualityEdgeId edgeId = d_equalityGraph[nodeId]; while (edgeId != null_edge) { const EqualityEdge& edge = d_equalityEdges[edgeId]; - Debug("equality::graph") << " " << d_nodes[edge.getNodeId()] << ":" << edge.getReason(); + Debug("equality::graph") << " [" << edge.getNodeId() << "] " << d_nodes[edge.getNodeId()] << ":" << edge.getReason(); edgeId = edge.getNext(); } @@ -1517,17 +1774,19 @@ void EqualityEngine::debugPrintGraph() const { } bool EqualityEngine::areEqual(TNode t1, TNode t2) const { - Debug("equality") << d_name << "::eq::areEqual(" << t1 << "," << t2 << ")" << std::endl; + Debug("equality") << d_name << "::eq::areEqual(" << t1 << "," << t2 << ")"; Assert(hasTerm(t1)); Assert(hasTerm(t2)); - return getEqualityNode(t1).getFind() == getEqualityNode(t2).getFind(); + bool result = getEqualityNode(t1).getFind() == getEqualityNode(t2).getFind(); + Debug("equality") << (result ? "\t(YES)" : "\t(NO)") << std::endl; + return result; } bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const { - Debug("equality") << d_name << "::eq::areDisequal(" << t1 << "," << t2 << ")" << std::endl; + Debug("equality") << d_name << "::eq::areDisequal(" << t1 << "," << t2 << ")"; // Add the terms Assert(hasTerm(t1)); @@ -1539,6 +1798,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const // If we propagated this disequality we're true if (hasPropagatedDisequality(t1Id, t2Id)) { + Debug("equality") << "\t(YES)" << std::endl; return true; } @@ -1556,6 +1816,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t2Id, t2ClassId)); nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id); } + Debug("equality") << "\t(YES)" << std::endl; return true; } @@ -1571,6 +1832,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t2Id, original.b)); nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id); } + Debug("equality") << "\t(YES)" << std::endl; return true; } } @@ -1587,11 +1849,13 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t1Id, original.b)); nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id); } + Debug("equality") << "\t(YES)" << std::endl; return true; } } // Couldn't deduce dis-equalityReturn whether the terms are disequal + Debug("equality") << "\t(NO)" << std::endl; return false; } @@ -2103,7 +2367,7 @@ void EqProof::debug_print( const char * c, unsigned tb ) const{ d_children[i]->debug_print( c, tb+1 ); } } - Debug( c ) << ")"; + Debug( c ) << ")" << std::endl; } diff --git a/src/theory/uf/equality_engine_types.h b/src/theory/uf/equality_engine_types.h index fb1e73575..0c2c3e354 100644 --- a/src/theory/uf/equality_engine_types.h +++ b/src/theory/uf/equality_engine_types.h @@ -95,7 +95,16 @@ inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { case MERGED_THROUGH_TRANS: out << "transitivity"; break; - default: + case MERGED_ARRAYS_ROW: + out << "arrays ROW"; + break; + case MERGED_ARRAYS_ROW1: + out << "arrays ROW1"; + break; + case MERGED_ARRAYS_EXT: + out << "arrays EXT"; + break; +default: out << "[theory]"; break; } @@ -365,4 +374,3 @@ struct TriggerInfo { } // namespace eq } // namespace theory } // namespace CVC4 - diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index ffb537734..b8e88a1a3 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -105,6 +105,8 @@ void TheoryUF::check(Effort level) { TNode fact = assertion.assertion; Debug("uf") << "TheoryUF::check(): processing " << fact << std::endl; + Debug("uf") << "Term's theory: " << theory::Theory::theoryOf(fact.toExpr()) << std::endl; + if (d_thss != NULL) { bool isDecision = d_valuation.isSatLiteral(fact) && d_valuation.isDecision(fact); d_thss->assertNode(fact, isDecision); @@ -219,9 +221,15 @@ void TheoryUF::explain(TNode literal, std::vector& assumptions, eq::EqPro d_equalityEngine.explainPredicate(atom, polarity, assumptions, pf); } if( pf ){ - Debug("uf-pf") << std::endl; - pf->debug_print("uf-pf"); + Debug("pf::uf") << std::endl; + pf->debug_print("pf::uf"); + } + + Debug("pf::uf") << "UF: explain( " << literal << " ):" << std::endl << "\t"; + for (unsigned i = 0; i < assumptions.size(); ++i) { + Debug("pf::uf") << assumptions[i] << " "; } + Debug("pf::uf") << std::endl; } Node TheoryUF::explain(TNode literal) { @@ -270,6 +278,7 @@ void TheoryUF::presolve() { for(vector::const_iterator i = newClauses.begin(); i != newClauses.end(); ++i) { + Debug("uf") << "uf: generating a lemma: " << *i << std::endl; d_out->lemma(*i); } } @@ -521,7 +530,7 @@ void TheoryUF::conflict(TNode a, TNode b) { } else { d_conflictNode = explain(a.eqNode(b),pf); } - ProofUF * puf = d_proofsEnabled ? new ProofUF( pf ) : NULL; + ProofUF* puf = d_proofsEnabled ? new ProofUF( pf ) : NULL; d_out->conflict(d_conflictNode, puf); d_conflict = true; } diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index e29b923f9..863059525 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -136,7 +136,7 @@ private: * Explain a literal, with proof (if "pf" is non-NULL). */ Node explain(TNode literal, eq::EqProof* pf); - + /** Literals to propagate */ context::CDList d_literalsToPropagate;