Open NikolajBjorner opened 2 years ago
Hello!
For the instance
(set-logic HORN)
(declare-fun P1 (Int Bool) Bool)
(declare-fun P2 (Int Bool) Bool)
(declare-fun P3 (Int Int) Bool)
(declare-fun P4 (Int) Bool)
(declare-fun P5 (Int) Bool)
(declare-fun P6 (Int Int) Bool)
(declare-fun P7 (Int Int Int Int) Bool)
(declare-fun P8 (Int Int) Bool)
(declare-fun P9 (Int) Bool)
(assert (forall ((v1 Int) (v2 Bool))
(=> (and (P4 v1) v2) (P1 v1 v2))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Bool))
(=> (and (P2 v3 v4) (= (+ 1 v1) v2) (>= v2 1))
(P4 v1))))
(assert (forall ((v1 Int) (v2 Bool) (v3 Int))
(=> (and (P8 3 v3) (= v2 (>= v1 0)))
(P2 v1 v2))))
(assert (forall ((v1 Int) (v2 Int))
(=> (and (P4 v1)) (P3 v1 v2))))
(assert (forall ((v1 Int)) (=> (P8 3 v1) (P4 v1))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int))
(=> (and (P5 v3)) (P8 v1 v2))))
(assert (forall ((v1 Int))
(=> (P6 0 v1) (P5 v1))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int))
(=> (and (P7 v4 0 v1 v2)
(P9 v4)
(= (+ 1 v3) v4)
(<= 1 v4))
(P6 v1 v2))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int))
(=> (and (= v2 1)) (P7 v1 v2 v3 v4))))
(assert (forall ((v1 Int) (v2 Int))
(=> (and (P9 v1) (<= v1 0)) (P8 v1 v2))))
(assert (forall ((v1 Int) (v2 Int)) (=> (and (>= v2 1)) (P9 v1))))
(assert (forall ((v1 Int)) (=> (= v1 3) (P9 v1))))
(check-sat)
(get-model)
(exit)
z3 returns
sat
(
(define-fun P8 ((x!0 Int) (x!1 Int)) Bool
true)
(define-fun P5 ((x!0 Int)) Bool
false)
(define-fun P4 ((x!0 Int)) Bool
(exists ((x!1 Int) (x!2 Int) (x!3 Bool))
(! (and (= x!3 (>= x!2 0)) (>= x!1 1) (= x!0 (+ (- 1) x!1))) :weight 0)))
(define-fun P1 ((x!0 Int) (x!1 Bool)) Bool
false)
(define-fun P7 ((x!0 Int) (x!1 Int) (x!2 Int) (x!3 Int)) Bool
(= x!1 1))
(define-fun P2 ((x!0 Int) (x!1 Bool)) Bool
(= x!1 (>= x!0 0)))
(define-fun P6 ((x!0 Int) (x!1 Int)) Bool
false)
(define-fun P3 ((x!0 Int) (x!1 Int)) Bool
(exists ((x!2 Int) (x!3 Int) (x!4 Bool))
(! (and (= x!4 (>= x!3 0)) (>= x!2 1) (= x!0 (+ (- 1) x!2))) :weight 0)))
(define-fun P9 ((x!0 Int)) Bool
true)
)
In this interpretation the first clause is equivalent to the implication true => false if v2 is true and v1 >= 0.
Hello, For the instance
(set-logic HORN)
(declare-fun P1 (Int Int Int) Bool)
(declare-fun P2 (Int) Bool)
(declare-fun P3 (Int Int Int) Bool)
(declare-fun P4 (Int Int Int Int Int Int Int) Bool)
(declare-fun P5 (Int Int Int) Bool)
(assert (forall ((v1 Int) (v2 Int) (v3 Int))
(=> (and (P5 v3 v2 v1))
(P3 v3 v2 v1))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int)
(v5 Int) (v6 Int) (v7 Int) (v8 Int))
(let ((a!1 (= (not (= 0 v7)) (and (not (= 0 v6))))))
(let ((a!2 (and a!1
(= (not (= 0 v6)) (>= v5 0))
(= v5 (+ v4))
(= v4 v3)
(not (not (= 0 v7)))
(P5 v3 v2 v1))))
(=> a!2 (P2 v8))))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int))
(let ((a!1 (and (not (not (= 0 v1)))
(P5 v3 v2 v1))))
(=> a!1
(P3 v3 v2 v1)))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int)
(v5 Int) (v6 Int) (v7 Int))
(=> (and (P3 v2 v1 v7))
(P4 v6 v5 v4 v3 v2 v1 v7))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int))
(=> (and (P3 v2 v1 v3))
(P1 v2 v1 v3))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int) (v5 Int))
(=> (and (= v5 v3)
(= v4 1)
(P1 v3 v2 v1))
(P5 v5 v3 v4))))
(assert (not (exists ((v1 Int))
(and (P2 v1)))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int) (v4 Int) (v5 Int))
(let ((a!1 (= (not (= 0 v5)) (and (not (= 0 v4))))))
(let ((a!2 (and a!1
(= (not (= 0 v4)) (>= v1 0))
(not (= 0 v5)))))
(=> a!2
(P1 v1 v3 v2))))))
(check-sat)
(get-model)
(exit)
z3 returns
sat
(
(define-fun P3 ((x!0 Int) (x!1 Int) (x!2 Int)) Bool
true)
(define-fun P4 ((x!0 Int)
(x!1 Int)
(x!2 Int)
(x!3 Int)
(x!4 Int)
(x!5 Int)
(x!6 Int)) Bool
true)
(define-fun P2 ((x!0 Int)) Bool
false)
(define-fun P1 ((x!0 Int) (x!1 Int) (x!2 Int)) Bool
(>= x!0 0))
(define-fun P5 ((x!0 Int) (x!1 Int) (x!2 Int)) Bool
(and (>= x!0 0) (= x!1 x!0) (= x!2 1)))
)
If v2 < 0 in the fifth clause, this interpretation is incorrect.
Hello, For instance
(set-logic HORN)
(declare-fun P (Int) Bool)
(declare-fun Q (Bool Int Int Bool Int Int) Bool)
(declare-fun R (Bool Int Int) Bool)
(assert (not (exists ((v1 Int)) (P v1))))
(assert (forall ((v1 Int) (v2 Int) (v3 Bool)
(v4 Int) (v5 Int) (v6 Int))
(=> (and (Q v3 v4 v2 true v5 v6)
(>= v2 1))
(P v1))))
(assert (forall ((v1 Bool) (v2 Int) (v3 Int) (v4 Bool) (v5 Int)
(v6 Int) (v7 Int) (v8 Bool) (v9 Int) (v10 Int))
(=> (and (Q v8 v9 v7 v1 v2 v10)
(= (+ 1 v3) v7)
(>= v7 1)
(= v5 v2)
(not v1)
(not v4))
(Q v1 v2 v3 v4 v5 v6))))
(assert (forall ((v1 Bool) (v2 Int) (v3 Int) (v4 Int)
(v5 Bool) (v6 Int) (v7 Int))
(=> (and (R v5 v6 v4)
(Q v5 v6 v4 v1 v2 v7)
(>= v4 1)
(= (+ 1 v3) v4)
(not v1))
(R v1 v2 v3))))
(assert (forall ((v1 Bool) (v2 Int) (v3 Int) (v5 Bool) (v6 Int) (v7 Int))
(=> (and (not v5))
(Q v1 v2 v3 v5 v6 v7))))
(assert (forall ((v1 Bool) (v2 Int) (v3 Int))
(=> (and (not v1))
(R v1 v2 v3))))
(check-sat)
(get-model)
(exit)
z3 returns
sat
(
(define-fun P ((x!0 Int)) Bool
(exists ((x!1 Int)) (! (>= x!1 1) :weight 0)))
(define-fun R ((x!0 Bool) (x!1 Int) (x!2 Int)) Bool
true)
(define-fun Q ((x!0 Bool) (x!1 Int) (x!2 Int) (x!3 Bool) (x!4 Int) (x!5 Int)) Bool
true)
)
This interpretation is incorrect for the first clause.
Hello!
For the instance
(set-logic HORN)
(set-option :fp.xform.inline_linear_branch true)
(set-option :fp.xform.inline_eager false)
(declare-fun P1 (Int Int) Bool)
(declare-fun P2 (Int Int Int) Bool)
(declare-fun P3 (Int) Bool)
(declare-fun P4 (Int Int) Bool)
(assert (forall ((v1 Int) (v2 Int))
(=> (and (P4 v1 v2))
(P3 v1))))
(assert (forall ((v1 Int) (v2 Int))
(=> (and (= v2 0)) (P4 v2 v1))))
(assert (forall ((v1 Int) (v2 Int) (v3 Int))
(=> (and (P1 v1 v3) (P4 v3 v2))
(P2 v1 v3 v2))))
(assert (forall ((v1 Int) (v2 Int))
(=> (and (P3 v2))
(P1 v1 v2))))
(check-sat)
(get-model)
(exit)
z3 returns
sat
(
(define-fun P3 ((x!0 Int)) Bool
(= x!0 0))
(define-fun P1 ((x!0 Int) (x!1 Int)) Bool
true)
(define-fun P4 ((x!0 Int) (x!1 Int)) Bool
true)
(define-fun P2 ((x!0 Int) (x!1 Int) (x!2 Int)) Bool
true)
)
If in the first clause v1 != 0, this interpretation is incorrect. With fp.xform.inline_eager in true or fp.xform.inline_linear_branch in false, z3 returns correct model.
In this case, P3 is in-lined by the clause for P4 (v2 = 0). But P4 is sent to SPACER and set to true. SPACER does not see P3 or the dependency of P3 on P4. Unclear if a "simple" patch is possible based on the implementation. The model transformer has to ensure that when deleting a rule P3(x) :- P4(x,y), then the interpretation for P3 should contain P4. It deletes the rule without ensuring this, it just in-lines P3(x) :- x = 0. And then ensures that P3(x) contains at least x = 0. At this point, it serves possibly better as a use case for developing a formal account for model reconstruction than pointing to something immediately actionable in the code (because model reconstruction there was an after-thought I don't think it is the best idea to keep patching it). In principle, model reconstruction takes a trail of deleted and added Horn rules and updates the interpretation for predicates in the head. In practice, all deleted rules have to be tracked correctly and rules that involve recursion would need to be accounted for. Shortcuts may be possible if predicates are guaranteed to be unreachable and therefore can be interpreted as "true".
This issue collects several separate filings. They are all or mostly related to rule in-lining and model transformation.
There are comments associated with closed issues that can give more information. The work required to close these issues is beyond basic bug fixing and will require planning. At this point it seems more productive to spend efforts developing the appropriate theory for fixing this.
The high level:
Preprocessing transformations add and delete Horn clauses. The (addition and) deletion can be tracked in a trail such that a model for the transformed Horn clauses is translated to a model of the original clauses. There are some different cases for when Horn clauses are deleted. The rules for fixing models depend on the cases and should be worked out clearly. The code for inlining also performs some shortcuts (topological sorting) and the rules for fixing models have to consider this.
It is worth writing a paper on and then changing/overhauling the code. It is probably less useful to poke at more bugs: models can be checked and if they fail, then disable the transformation rules that don't record model reconstruction. The overall soundness of SAT vs. UNSAT seems not be at stake.