diff --git a/src_colibri2/tests/solve/all/sat/div_abs.smt2 b/src_colibri2/tests/solve/all/sat/div_abs.smt2 new file mode 100644 index 0000000000000000000000000000000000000000..8d12bd08eab028c0908826f33cf79720c817b109 --- /dev/null +++ b/src_colibri2/tests/solve/all/sat/div_abs.smt2 @@ -0,0 +1,11 @@ +(set-logic ALL) +(set-info :smt-lib-version 2.6) +(declare-const x Real) +(declare-const y Real) + +(assert (< y 0.0)) + +(assert + (= (colibri_abs_real (/ x y)) + (/ (colibri_abs_real x) (colibri_abs_real y)))) +(check-sat) diff --git a/src_colibri2/tests/solve/all/sat/div_abs2.smt2 b/src_colibri2/tests/solve/all/sat/div_abs2.smt2 new file mode 100644 index 0000000000000000000000000000000000000000..e0871e29c8248bfe8dc6af411ef25607a5dc8cbb --- /dev/null +++ b/src_colibri2/tests/solve/all/sat/div_abs2.smt2 @@ -0,0 +1,9 @@ +(set-logic ALL) +(set-info :smt-lib-version 2.6) +(declare-const x Real) +(declare-const y Real) + +(assert + (not (= (colibri_abs_real (/ x y)) + (/ (colibri_abs_real x) (colibri_abs_real y))))) +(check-sat) diff --git a/src_colibri2/tests/solve/all/sat/dune b/src_colibri2/tests/solve/all/sat/dune new file mode 100644 index 0000000000000000000000000000000000000000..137bdf589269179a3ae42355c4024e65ca273535 --- /dev/null +++ b/src_colibri2/tests/solve/all/sat/dune @@ -0,0 +1,13 @@ +(include dune.inc) + +(rule + (alias runtest) + (deps + (glob_files *.cnf) + (glob_files *.smt2) + (glob_files *.psmt2)) + (action + (with-stdout-to + dune.inc + (run %{exe:../../../generate_tests/generate_dune_tests.exe} . sat))) + (mode promote)) diff --git a/src_colibri2/tests/solve/all/sat/dune.inc b/src_colibri2/tests/solve/all/sat/dune.inc new file mode 100644 index 0000000000000000000000000000000000000000..aca73444f0d4e5534f76b17b6ead8815ff6700e6 --- /dev/null +++ b/src_colibri2/tests/solve/all/sat/dune.inc @@ -0,0 +1,6 @@ +(rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status sat +--dont-print-result %{dep:div_abs.smt2}))) +(rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status sat --learning --dont-print-result %{dep:div_abs.smt2}))) +(rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status sat +--dont-print-result %{dep:div_abs2.smt2}))) +(rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status sat --learning --dont-print-result %{dep:div_abs2.smt2}))) diff --git a/src_colibri2/tests/solve/all/unsat/div_abs.smt2 b/src_colibri2/tests/solve/all/unsat/div_abs.smt2 new file mode 100644 index 0000000000000000000000000000000000000000..62465424103aee5dc8b9348d373fcaca57fc879e --- /dev/null +++ b/src_colibri2/tests/solve/all/unsat/div_abs.smt2 @@ -0,0 +1,11 @@ +(set-logic ALL) +(set-info :smt-lib-version 2.6) +(declare-const x Real) +(declare-const y Real) + +(assert (< y 0.0)) + +(assert + (not (= (colibri_abs_real (/ x y)) + (/ (colibri_abs_real x) (colibri_abs_real y))))) +(check-sat) diff --git a/src_colibri2/tests/solve/all/unsat/dune.inc b/src_colibri2/tests/solve/all/unsat/dune.inc index 0b55ae9be6c6f5798936b861fe65579fa5085103..0c02372cf204487ed05c2de668ff6475ca9d257b 100644 --- a/src_colibri2/tests/solve/all/unsat/dune.inc +++ b/src_colibri2/tests/solve/all/unsat/dune.inc @@ -2,6 +2,9 @@ --dont-print-result %{dep:bag-BagImpl-createqtvc.psmt2}))) (rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --learning --dont-print-result %{dep:bag-BagImpl-createqtvc.psmt2}))) (rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat +--dont-print-result %{dep:div_abs.smt2}))) +(rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --learning --dont-print-result %{dep:div_abs.smt2}))) +(rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --dont-print-result %{dep:fact-FactRecursive-fact_recqtvc.psmt2}))) (rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --learning --dont-print-result %{dep:fact-FactRecursive-fact_recqtvc.psmt2}))) (rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat @@ -10,3 +13,6 @@ (rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --dont-print-result %{dep:interval-Convexe-exists_memqtvc_2.psmt2}))) (rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --learning --dont-print-result %{dep:interval-Convexe-exists_memqtvc_2.psmt2}))) +(rule (alias runtest) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat +--dont-print-result %{dep:mul_abs.smt2}))) +(rule (alias runtest-learning) (action (run %{bin:colibri2} --size=15M --time=30s --max-steps 3500 --check-status unsat --learning --dont-print-result %{dep:mul_abs.smt2}))) diff --git a/src_colibri2/tests/solve/all/unsat/mul_abs.smt2 b/src_colibri2/tests/solve/all/unsat/mul_abs.smt2 new file mode 100644 index 0000000000000000000000000000000000000000..4799c11cbd5ca98d795b9feb11f45fef750d385a --- /dev/null +++ b/src_colibri2/tests/solve/all/unsat/mul_abs.smt2 @@ -0,0 +1,8 @@ +(set-logic ALL) + +(declare-fun a () Real) +(declare-fun b () Real) + +(assert (not (= (* (colibri_abs_real a) (colibri_abs_real b)) (colibri_abs_real (* a b))))) + +(check-sat) diff --git a/src_colibri2/theories/LRA/dom_product.ml b/src_colibri2/theories/LRA/dom_product.ml index 62d3bbffcf449cbb7d152b5cbc93c5e725b70cf5..c726fd0ddc59e524be6ae4e1266dd9218041c88a 100644 --- a/src_colibri2/theories/LRA/dom_product.ml +++ b/src_colibri2/theories/LRA/dom_product.ml @@ -192,7 +192,7 @@ and SolveSign : sig _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit val reshape : - Egraph.wt -> Node.t -> f:(Sign_product.t -> Sign_product.t) -> unit + Egraph.wt -> Node.t -> f:(Sign_product.t -> Sign_product.t option) -> unit end = Pivot.WithUnsolved (struct let name = "ARITH_SIGN" @@ -333,12 +333,21 @@ let converter d (f : Ground.t) = SolveAbs.attach_repr_change d ~node (factorize res a Q.minus_one b); SolveSign.attach_repr_change d ~node (factorize res a Q.minus_one b)) [ a; b ] + | { + app = { builtin = Expr.Abs | RealValue.Builtin.Abs_real }; + tyargs = []; + args; + _; + } -> + let a = IArray.extract1_exn args in + reg a; + SolveAbs.assume_equality d res (Product.of_list [ (a, Q.one) ]); + SolveSign.assume_equality d res Sign_product.one | _ -> () let reshape_non_zero d n = let q = Sign_product.of_one_node_non_zero n in - SolveSign.reshape d n ~f:(fun p -> - Option.value ~default:p (Sign_product.subst p n q)) + SolveSign.reshape d n ~f:(fun p -> Sign_product.subst p n q) let init env = SolveAbs.init env; @@ -352,16 +361,26 @@ let init env = let p1 = Product.of_list [ (cl, Q.one) ] in Debug.dprintf4 debug "[Polynome->Product] propagate %a is %a" Node.pp n Node.pp cl; - SolveAbs.assume_equality d n p1)); - SolveAbs.attach_eqs_change env (fun d n -> - SolveAbs.iter_eqs d n ~f:(fun p -> - if Node.M.is_num_elt 1 p.poly then - let cl, q = Node.M.choose p.poly in - if Q.equal q Q.one then ( - let p1 = Polynome.of_list Q.zero [ (cl, Q.one) ] in - Debug.dprintf4 debug "[Product->Polynome] propagate %a is %a" - Node.pp n Node.pp cl; - Dom_polynome.assume_equality d n p1))); + SolveAbs.assume_equality d n p1; + SolveSign.assume_equality d n (Sign_product.of_one_node cl))); + let product_polynome d n = + (* todo more efficient *) + SolveAbs.iter_eqs d n ~f:(fun p -> + match Product.is_one_node p with + | None -> () + | Some cl -> + SolveSign.iter_eqs d n ~f:(fun p -> + match Sign_product.is_one_node p with + | Some cl' when Egraph.is_equal d cl cl' -> + let p1 = Polynome.of_list Q.zero [ (cl, Q.one) ] in + Debug.dprintf4 debug + "[Product->Polynome] propagate %a is %a" Node.pp n Node.pp + cl; + Dom_polynome.assume_equality d n p1 + | _ -> ())) + in + SolveAbs.attach_eqs_change env product_polynome; + SolveSign.attach_eqs_change env product_polynome; DaemonWithFilter.attach_any_dom env Dom_interval.dom (fun d n -> match Dom_interval.zero_is d n with | Eq -> Some (fun d -> SolveSign.assume_equality d n Sign_product.zero) diff --git a/src_colibri2/theories/LRA/pivot.ml b/src_colibri2/theories/LRA/pivot.ml index c5199deb8cfdd61017c1fa738fbd4d0139a525b4..100d4f5ddf2e07f3072b044ec4bc47921022734e 100644 --- a/src_colibri2/theories/LRA/pivot.ml +++ b/src_colibri2/theories/LRA/pivot.ml @@ -71,7 +71,7 @@ end) : sig val attach_eqs_change : _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit - val reshape : Egraph.wt -> Node.t -> f:(P.t -> P.t) -> unit + val reshape : Egraph.wt -> Node.t -> f:(P.t -> P.t option) -> unit end = struct type t = { repr : P.t; @@ -125,36 +125,6 @@ end = struct add_used_product d cl' (P.nodes t.repr); P.S.iter (fun p -> add_used_product d cl' (P.nodes p)) t.eqs - let subst_doms d cl p = - let b = - match Node.HC.find used_in_poly d cl with - | exception Not_found -> Node.S.empty - | b -> b - in - Node.S.iter - (fun cl' -> - match Egraph.get_dom d dom cl' with - | None -> assert false (* absurd: can't be used and absent *) - | Some q -> - let fold (new_cl, acc, chg) (q : P.t) = - let new_cl = - Node.M.set_union new_cl - (Node.M.set_diff (P.nodes p) (P.nodes q)) - in - match P.subst q cl p with - | None -> (new_cl, P.S.add q acc, chg) - | Some q' -> (new_cl, P.S.add q' acc, (q, q') :: chg) - in - let new_cl, acc, chg = fold (Node.M.empty, P.S.empty, []) q.repr in - let repr = P.S.choose acc in - let new_cl, acc, chg = - P.S.fold_left fold (new_cl, acc, chg) q.eqs - in - let eqs = P.S.remove repr acc in - add_used_product d cl' new_cl; - set_poly d cl' { repr; eqs } chg) - b - let norm_product d p = P.normalize p ~f:(fun cl -> let cl = Egraph.find d cl in @@ -259,10 +229,47 @@ end = struct | Some p -> assert (Option.equal Node.equal (P.is_one_node p.repr) (Some cl)); subst_doms d cl eq; + let eq = norm_product d eq in assert ( P.equal eq (Base.Option.value_exn (Egraph.get_dom d dom cl)).repr); merge_one_new_eq d cl eq + and subst_doms d cl p = + let b = + match Node.HC.find used_in_poly d cl with + | exception Not_found -> Node.S.empty + | b -> b + in + let touched = Node.H.create 10 in + Node.S.iter + (fun cl' -> + match Egraph.get_dom d dom cl' with + | None -> assert false (* absurd: can't be used and absent *) + | Some q -> + let fold (new_cl, acc, chg) (q : P.t) = + let new_cl = + Node.M.set_union new_cl + (Node.M.set_diff (P.nodes p) (P.nodes q)) + in + match P.subst q cl p with + | None -> (new_cl, P.S.add q acc, chg) + | Some q' -> + Node.H.replace touched cl' (); + (new_cl, P.S.add q' acc, (q, q') :: chg) + in + let new_cl, acc, chg = + fold (Node.M.empty, P.S.empty, []) q.repr + in + let repr = P.S.choose acc in + let new_cl, acc, chg = + P.S.fold_left fold (new_cl, acc, chg) q.eqs + in + let eqs = P.S.remove repr acc in + add_used_product d cl' new_cl; + set_poly d cl' { repr; eqs } chg) + b; + Node.H.iter (recheck d) touched + and part d l = List.map (fun p -> P.info d p) l and solve d l = @@ -284,6 +291,19 @@ end = struct `Solved | () -> `Not_solved + and recheck d n () = + match Egraph.get_dom d dom n with + | None -> assert false (* absurd: can't be used and absent *) + | Some p -> ( + match + solve d + (Base.List.cartesian_product + (part d (p.repr :: P.S.elements p.eqs)) + (part d (p.repr :: P.S.elements p.eqs))) + with + | `Solved -> recheck d n () + | `Not_solved -> ()) + let key = dom type nonrec t = t @@ -311,24 +331,35 @@ end = struct let p = norm_product d p in Th.merge_one_new_eq d n p - let reshape d cl ~(f : P.t -> P.t) = + let reshape d cl ~(f : P.t -> P.t option) = match Node.HC.find used_in_poly d cl with | exception Not_found -> () | b -> + let touched = Node.H.create 10 in Node.S.iter (fun cl' -> match Egraph.get_dom d dom cl' with | None -> assert false (* absurd: can't be used and absent *) | Some q -> + let replace p = + match f p with + | None -> p + | Some p -> + Node.H.replace touched cl' (); + p + in let eqs = - P.S.fold (fun p acc -> P.S.add (f p) acc) q.eqs P.S.empty + P.S.fold + (fun p acc -> P.S.add (replace p) acc) + q.eqs P.S.empty in - let q' = { repr = f q.repr; eqs } in + let q' = { repr = replace q.repr; eqs } in Egraph.set_dom d dom cl' q'; let l = Th.part d (q'.repr :: P.S.elements q'.eqs) in let l = Base.List.cartesian_product l l in ignore (Th.solve d l)) - b + b; + Node.H.iter (Th.recheck d) touched module ChangeInfo = struct type runable = Node.S.t diff --git a/src_colibri2/theories/LRA/pivot.mli b/src_colibri2/theories/LRA/pivot.mli index ee9fc530676a5e195404b4799423fbfe9dae9978..1112944c7f41b68a5ebcfcd04bec5d092270119c 100644 --- a/src_colibri2/theories/LRA/pivot.mli +++ b/src_colibri2/theories/LRA/pivot.mli @@ -88,7 +88,7 @@ end) : sig val attach_eqs_change : _ Egraph.t -> ?node:Node.t -> (Egraph.wt -> Node.t -> unit) -> unit - val reshape : Egraph.wt -> Node.t -> f:(P.t -> P.t) -> unit + val reshape : Egraph.wt -> Node.t -> f:(P.t -> P.t option) -> unit (** Apply the given function to all the normalized form which contains this node. The resulting normalized form should contain the same or less nodes than before. *)