mirror of
https://github.com/Z3Prover/z3
synced 2025-04-15 13:28:47 +00:00
merge
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
commit
faf96ca910
50
examples/python/data/horn1.smt2
Normal file
50
examples/python/data/horn1.smt2
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
(declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool))
|
||||||
|
(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool))
|
||||||
|
(declare-var A Bool)
|
||||||
|
(declare-var B Bool)
|
||||||
|
(declare-var C Bool)
|
||||||
|
(declare-var D Bool)
|
||||||
|
(declare-var E Bool)
|
||||||
|
(declare-var F Bool)
|
||||||
|
(declare-var G Bool)
|
||||||
|
(declare-var H Bool)
|
||||||
|
(declare-var I Bool)
|
||||||
|
(declare-var J Bool)
|
||||||
|
(declare-var K Bool)
|
||||||
|
(declare-var L Bool)
|
||||||
|
(declare-var M Bool)
|
||||||
|
(declare-var N Bool)
|
||||||
|
(declare-var O Bool)
|
||||||
|
(declare-var P Bool)
|
||||||
|
(declare-var Q Bool)
|
||||||
|
(declare-var R Bool)
|
||||||
|
(declare-var S Bool)
|
||||||
|
(declare-var T Bool)
|
||||||
|
(declare-var U Bool)
|
||||||
|
(declare-var V Bool)
|
||||||
|
(declare-var W Bool)
|
||||||
|
(declare-var X Bool)
|
||||||
|
(rule (=> (not (or L K J I H G F E D C B A)) (Invariant L K J I H G F E D C B A)))
|
||||||
|
(rule (let ((a!1 (and (Invariant X W V U T S R Q P O N M)
|
||||||
|
(=> (not (and true)) (not F))
|
||||||
|
(=> (not (and true)) (not E))
|
||||||
|
(=> (not (and W)) (not D))
|
||||||
|
(=> (not (and W)) (not C))
|
||||||
|
(=> (not (and U)) (not B))
|
||||||
|
(=> (not (and U)) (not A))
|
||||||
|
(= L (xor F X))
|
||||||
|
(= K (xor E W))
|
||||||
|
(= J (xor D V))
|
||||||
|
(= I (xor C U))
|
||||||
|
(= H (xor B T))
|
||||||
|
(= G (xor A S))
|
||||||
|
(=> D (not E))
|
||||||
|
(=> C (not E))
|
||||||
|
(=> B (not C))
|
||||||
|
(=> A (not C))
|
||||||
|
((_ at-most 5) L K J I H G))))
|
||||||
|
(=> a!1 (Invariant L K J I H G F E D C B A))))
|
||||||
|
(rule (=> (and (Invariant L K J I H G F E D C B A) L (not K) J (not I) H G)
|
||||||
|
(Goal L K J I H G F E D C B A)))
|
||||||
|
|
||||||
|
(query Goal)
|
44
examples/python/data/horn2.smt2
Normal file
44
examples/python/data/horn2.smt2
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool))
|
||||||
|
(declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool))
|
||||||
|
(declare-var A Bool)
|
||||||
|
(declare-var B Bool)
|
||||||
|
(declare-var C Bool)
|
||||||
|
(declare-var D Bool)
|
||||||
|
(declare-var E Bool)
|
||||||
|
(declare-var F Bool)
|
||||||
|
(declare-var G Bool)
|
||||||
|
(declare-var H Bool)
|
||||||
|
(declare-var I Bool)
|
||||||
|
(declare-var J Bool)
|
||||||
|
(declare-var K Bool)
|
||||||
|
(declare-var L Bool)
|
||||||
|
(declare-var M Bool)
|
||||||
|
(declare-var N Bool)
|
||||||
|
(declare-var O Bool)
|
||||||
|
(declare-var P Bool)
|
||||||
|
(declare-var Q Bool)
|
||||||
|
(declare-var R Bool)
|
||||||
|
(declare-var S Bool)
|
||||||
|
(declare-var T Bool)
|
||||||
|
(rule (=> (not (or J I H G F E D C B A)) (Invariant J I H G F E D C B A)))
|
||||||
|
(rule (let ((a!1 (and (Invariant T S R Q P O N M L K)
|
||||||
|
(=> (not (and true)) (not E))
|
||||||
|
(=> (not (and T)) (not D))
|
||||||
|
(=> (not (and S)) (not C))
|
||||||
|
(=> (not (and R)) (not B))
|
||||||
|
(=> (not (and Q)) (not A))
|
||||||
|
(= J (xor E T))
|
||||||
|
(= I (xor D S))
|
||||||
|
(= H (xor C R))
|
||||||
|
(= G (xor B Q))
|
||||||
|
(= F (xor A P))
|
||||||
|
(=> D (not E))
|
||||||
|
(=> C (not D))
|
||||||
|
(=> B (not C))
|
||||||
|
(=> A (not B))
|
||||||
|
((_ at-most 3) J I H G F))))
|
||||||
|
(=> a!1 (Invariant J I H G F E D C B A))))
|
||||||
|
(rule (=> (and (Invariant J I H G F E D C B A) (not J) (not I) (not H) (not G) F)
|
||||||
|
(Goal J I H G F E D C B A)))
|
||||||
|
|
||||||
|
(query Goal)
|
438
examples/python/mini_ic3.py
Normal file
438
examples/python/mini_ic3.py
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
from z3 import *
|
||||||
|
import heapq
|
||||||
|
|
||||||
|
|
||||||
|
# Simplistic (and fragile) converter from
|
||||||
|
# a class of Horn clauses corresponding to
|
||||||
|
# a transition system into a transition system
|
||||||
|
# representation as <init, trans, goal>
|
||||||
|
# It assumes it is given three Horn clauses
|
||||||
|
# of the form:
|
||||||
|
# init(x) => Invariant(x)
|
||||||
|
# Invariant(x) and trans(x,x') => Invariant(x')
|
||||||
|
# Invariant(x) and goal(x) => Goal(x)
|
||||||
|
# where Invariant and Goal are uninterpreted predicates
|
||||||
|
|
||||||
|
class Horn2Transitions:
|
||||||
|
def __init__(self):
|
||||||
|
self.trans = True
|
||||||
|
self.init = True
|
||||||
|
self.goal = True
|
||||||
|
self.index = 0
|
||||||
|
|
||||||
|
def parse(self, file):
|
||||||
|
fp = Fixedpoint()
|
||||||
|
goals = fp.parse_file(file)
|
||||||
|
for r in fp.get_rules():
|
||||||
|
if not is_quantifier(r):
|
||||||
|
continue
|
||||||
|
b = r.body()
|
||||||
|
if not is_implies(b):
|
||||||
|
continue
|
||||||
|
f = b.arg(0)
|
||||||
|
g = b.arg(1)
|
||||||
|
if self.is_goal(f, g):
|
||||||
|
continue
|
||||||
|
if self.is_transition(f, g):
|
||||||
|
continue
|
||||||
|
if self.is_init(f, g):
|
||||||
|
continue
|
||||||
|
|
||||||
|
def is_pred(self, p, name):
|
||||||
|
return is_app(p) and p.decl().name() == name
|
||||||
|
|
||||||
|
def is_goal(self, body, head):
|
||||||
|
if not self.is_pred(head, "Goal"):
|
||||||
|
return False
|
||||||
|
pred, inv = self.is_body(body)
|
||||||
|
if pred is None:
|
||||||
|
return False
|
||||||
|
self.goal = self.subst_vars("x", inv, pred)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_body(self, body):
|
||||||
|
if not is_and(body):
|
||||||
|
return None, None
|
||||||
|
fmls = [f for f in body.children() if self.is_inv(f) is None]
|
||||||
|
inv = None
|
||||||
|
for f in body.children():
|
||||||
|
if self.is_inv(f) is not None:
|
||||||
|
inv = f;
|
||||||
|
break
|
||||||
|
return And(fmls), inv
|
||||||
|
|
||||||
|
def is_inv(self, f):
|
||||||
|
if self.is_pred(f, "Invariant"):
|
||||||
|
return f
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_transition(self, body, head):
|
||||||
|
pred, inv0 = self.is_body(body)
|
||||||
|
if pred is None:
|
||||||
|
return False
|
||||||
|
inv1 = self.is_inv(head)
|
||||||
|
if inv1 is None:
|
||||||
|
return False
|
||||||
|
pred = self.subst_vars("x", inv0, pred)
|
||||||
|
self.xs = self.vars
|
||||||
|
pred = self.subst_vars("xn", inv1, pred)
|
||||||
|
self.xns = self.vars
|
||||||
|
self.trans = pred
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_init(self, body, head):
|
||||||
|
for f in body.children():
|
||||||
|
if self.is_inv(f) is not None:
|
||||||
|
return False
|
||||||
|
inv = self.is_inv(head)
|
||||||
|
if inv is None:
|
||||||
|
return False
|
||||||
|
self.init = self.subst_vars("x", inv, body)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def subst_vars(self, prefix, inv, fml):
|
||||||
|
subst = self.mk_subst(prefix, inv)
|
||||||
|
self.vars = [ v for (k,v) in subst ]
|
||||||
|
return substitute(fml, subst)
|
||||||
|
|
||||||
|
def mk_subst(self, prefix, inv):
|
||||||
|
self.index = 0
|
||||||
|
return [(f, self.mk_bool(prefix)) for f in inv.children()]
|
||||||
|
|
||||||
|
def mk_bool(self, prefix):
|
||||||
|
self.index += 1
|
||||||
|
return Bool("%s%d" % (prefix, self.index))
|
||||||
|
|
||||||
|
# Produce a finite domain solver.
|
||||||
|
# The theory QF_FD covers bit-vector formulas
|
||||||
|
# and pseudo-Boolean constraints.
|
||||||
|
# By default cardinality and pseudo-Boolean
|
||||||
|
# constraints are converted to clauses. To override
|
||||||
|
# this default for cardinality constraints
|
||||||
|
# we set sat.cardinality.solver to True
|
||||||
|
|
||||||
|
def fd_solver():
|
||||||
|
s = SolverFor("QF_FD")
|
||||||
|
s.set("sat.cardinality.solver", True)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
# negate, avoid double negation
|
||||||
|
def negate(f):
|
||||||
|
if is_not(f):
|
||||||
|
return f.arg(0)
|
||||||
|
else:
|
||||||
|
return Not(f)
|
||||||
|
|
||||||
|
def cube2clause(cube):
|
||||||
|
return Or([negate(f) for f in cube])
|
||||||
|
|
||||||
|
class State:
|
||||||
|
def __init__(self, s):
|
||||||
|
self.R = set([])
|
||||||
|
self.solver = s
|
||||||
|
|
||||||
|
def add(self, clause):
|
||||||
|
if clause not in self.R:
|
||||||
|
self.R |= { clause }
|
||||||
|
self.solver.add(clause)
|
||||||
|
|
||||||
|
class Goal:
|
||||||
|
def __init__(self, cube, parent, level):
|
||||||
|
self.level = level
|
||||||
|
self.cube = cube
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def is_seq(f):
|
||||||
|
return isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector)
|
||||||
|
|
||||||
|
# Check if the initial state is bad
|
||||||
|
def check_disjoint(a, b):
|
||||||
|
s = fd_solver()
|
||||||
|
s.add(a)
|
||||||
|
s.add(b)
|
||||||
|
return unsat == s.check()
|
||||||
|
|
||||||
|
|
||||||
|
# Remove clauses that are subsumed
|
||||||
|
def prune(R):
|
||||||
|
removed = set([])
|
||||||
|
s = fd_solver()
|
||||||
|
for f1 in R:
|
||||||
|
s.push()
|
||||||
|
for f2 in R:
|
||||||
|
if f2 not in removed:
|
||||||
|
s.add(Not(f2) if f1.eq(f2) else f2)
|
||||||
|
if s.check() == unsat:
|
||||||
|
removed |= { f1 }
|
||||||
|
s.pop()
|
||||||
|
return R - removed
|
||||||
|
|
||||||
|
class MiniIC3:
|
||||||
|
def __init__(self, init, trans, goal, x0, xn):
|
||||||
|
self.x0 = x0
|
||||||
|
self.xn = xn
|
||||||
|
self.init = init
|
||||||
|
self.bad = goal
|
||||||
|
self.trans = trans
|
||||||
|
self.min_cube_solver = fd_solver()
|
||||||
|
self.min_cube_solver.add(Not(trans))
|
||||||
|
self.goals = []
|
||||||
|
s = State(fd_solver())
|
||||||
|
s.add(init)
|
||||||
|
s.solver.add(trans)
|
||||||
|
self.states = [s]
|
||||||
|
self.s_bad = fd_solver()
|
||||||
|
self.s_good = fd_solver()
|
||||||
|
self.s_bad.add(self.bad)
|
||||||
|
self.s_good.add(Not(self.bad))
|
||||||
|
|
||||||
|
def next(self, f):
|
||||||
|
if is_seq(f):
|
||||||
|
return [self.next(f1) for f1 in f]
|
||||||
|
return substitute(f, zip(self.x0, self.xn))
|
||||||
|
|
||||||
|
def prev(self, f):
|
||||||
|
if is_seq(f):
|
||||||
|
return [self.prev(f1) for f1 in f]
|
||||||
|
return substitute(f, zip(self.xn, self.x0))
|
||||||
|
|
||||||
|
def add_solver(self):
|
||||||
|
s = fd_solver()
|
||||||
|
s.add(self.trans)
|
||||||
|
self.states += [State(s)]
|
||||||
|
|
||||||
|
def R(self, i):
|
||||||
|
return And(self.states[i].R)
|
||||||
|
|
||||||
|
# Check if there are two states next to each other that have the same clauses.
|
||||||
|
def is_valid(self):
|
||||||
|
i = 1
|
||||||
|
while i + 1 < len(self.states):
|
||||||
|
if not (self.states[i].R - self.states[i+1].R):
|
||||||
|
return And(prune(self.states[i].R))
|
||||||
|
i += 1
|
||||||
|
return None
|
||||||
|
|
||||||
|
def value2literal(self, m, x):
|
||||||
|
value = m.eval(x)
|
||||||
|
if is_true(value):
|
||||||
|
return x
|
||||||
|
if is_false(value):
|
||||||
|
return Not(x)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def values2literals(self, m, xs):
|
||||||
|
p = [self.value2literal(m, x) for x in xs]
|
||||||
|
return [x for x in p if x is not None]
|
||||||
|
|
||||||
|
def project0(self, m):
|
||||||
|
return self.values2literals(m, self.x0)
|
||||||
|
|
||||||
|
def projectN(self, m):
|
||||||
|
return self.values2literals(m, self.xn)
|
||||||
|
|
||||||
|
# Determine if there is a cube for the current state
|
||||||
|
# that is potentially reachable.
|
||||||
|
def unfold(self):
|
||||||
|
core = []
|
||||||
|
self.s_bad.push()
|
||||||
|
R = self.R(len(self.states)-1)
|
||||||
|
self.s_bad.add(R)
|
||||||
|
is_sat = self.s_bad.check()
|
||||||
|
if is_sat == sat:
|
||||||
|
m = self.s_bad.model()
|
||||||
|
props = self.project0(m)
|
||||||
|
self.s_good.push()
|
||||||
|
self.s_good.add(R)
|
||||||
|
is_sat2 = self.s_good.check(props)
|
||||||
|
assert is_sat2 == unsat
|
||||||
|
core = self.s_good.unsat_core()
|
||||||
|
self.s_good.pop()
|
||||||
|
self.s_bad.pop()
|
||||||
|
return is_sat, core
|
||||||
|
|
||||||
|
# Block a cube by asserting the clause corresponding to its negation
|
||||||
|
def block_cube(self, i, cube):
|
||||||
|
self.assert_clause(i, cube2clause(cube))
|
||||||
|
|
||||||
|
# Add a clause to levels 0 until i
|
||||||
|
def assert_clause(self, i, clause):
|
||||||
|
for j in range(i + 1):
|
||||||
|
self.states[j].add(clause)
|
||||||
|
|
||||||
|
# minimize cube that is core of Dual solver.
|
||||||
|
# this assumes that props & cube => Trans
|
||||||
|
def minimize_cube(self, cube, lits):
|
||||||
|
is_sat = self.min_cube_solver.check(lits + [c for c in cube])
|
||||||
|
assert is_sat == unsat
|
||||||
|
core = self.min_cube_solver.unsat_core()
|
||||||
|
assert core
|
||||||
|
return [c for c in core if c in set(cube)]
|
||||||
|
|
||||||
|
# push a goal on a heap
|
||||||
|
def push_heap(self, goal):
|
||||||
|
heapq.heappush(self.goals, (goal.level, goal))
|
||||||
|
|
||||||
|
# A state s0 and level f0 such that
|
||||||
|
# not(s0) is f0-1 inductive
|
||||||
|
def ic3_blocked(self, s0, f0):
|
||||||
|
self.push_heap(Goal(self.next(s0), None, f0))
|
||||||
|
while self.goals:
|
||||||
|
f, g = heapq.heappop(self.goals)
|
||||||
|
sys.stdout.write("%d." % f)
|
||||||
|
sys.stdout.flush()
|
||||||
|
# Not(g.cube) is f-1 invariant
|
||||||
|
if f == 0:
|
||||||
|
print("")
|
||||||
|
return g
|
||||||
|
cube, f, is_sat = self.is_inductive(f, g.cube)
|
||||||
|
if is_sat == unsat:
|
||||||
|
self.block_cube(f, self.prev(cube))
|
||||||
|
if f < f0:
|
||||||
|
self.push_heap(Goal(g.cube, g.parent, f + 1))
|
||||||
|
elif is_sat == sat:
|
||||||
|
self.push_heap(Goal(cube, g, f - 1))
|
||||||
|
self.push_heap(g)
|
||||||
|
else:
|
||||||
|
return is_sat
|
||||||
|
print("")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Rudimentary generalization:
|
||||||
|
# If the cube is already unsat with respect to transition relation
|
||||||
|
# extract a core (not necessarily minimal)
|
||||||
|
# otherwise, just return the cube.
|
||||||
|
def generalize(self, cube, f):
|
||||||
|
s = self.states[f - 1].solver
|
||||||
|
if unsat == s.check(cube):
|
||||||
|
return s.unsat_core(), f
|
||||||
|
return cube, f
|
||||||
|
|
||||||
|
# Check if the negation of cube is inductive at level f
|
||||||
|
def is_inductive(self, f, cube):
|
||||||
|
s = self.states[f - 1].solver
|
||||||
|
s.push()
|
||||||
|
s.add(self.prev(Not(And(cube))))
|
||||||
|
is_sat = s.check(cube)
|
||||||
|
if is_sat == sat:
|
||||||
|
m = s.model()
|
||||||
|
s.pop()
|
||||||
|
if is_sat == sat:
|
||||||
|
cube = self.next(self.minimize_cube(self.project0(m), self.projectN(m)))
|
||||||
|
elif is_sat == unsat:
|
||||||
|
cube, f = self.generalize(cube, f)
|
||||||
|
return cube, f, is_sat
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not check_disjoint(self.init, self.bad):
|
||||||
|
return "goal is reached in initial state"
|
||||||
|
level = 0
|
||||||
|
while True:
|
||||||
|
inv = self.is_valid()
|
||||||
|
if inv is not None:
|
||||||
|
return inv
|
||||||
|
is_sat, cube = self.unfold()
|
||||||
|
if is_sat == unsat:
|
||||||
|
level += 1
|
||||||
|
print("Unfold %d" % level)
|
||||||
|
sys.stdout.flush()
|
||||||
|
self.add_solver()
|
||||||
|
elif is_sat == sat:
|
||||||
|
cex = self.ic3_blocked(cube, level)
|
||||||
|
if cex is not None:
|
||||||
|
return cex
|
||||||
|
else:
|
||||||
|
return is_sat
|
||||||
|
|
||||||
|
def test(file):
|
||||||
|
h2t = Horn2Transitions()
|
||||||
|
h2t.parse(file)
|
||||||
|
mp = MiniIC3(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.xns)
|
||||||
|
result = mp.run()
|
||||||
|
if isinstance(result, Goal):
|
||||||
|
g = result
|
||||||
|
print("Trace")
|
||||||
|
while g:
|
||||||
|
print(g.level, g.cube)
|
||||||
|
g = g.parent
|
||||||
|
return
|
||||||
|
if isinstance(result, ExprRef):
|
||||||
|
print("Invariant:\n%s " % result)
|
||||||
|
return
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
test("data/horn1.smt2")
|
||||||
|
test("data/horn2.smt2")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
# TBD: Quip variant of IC3
|
||||||
|
|
||||||
|
must = True
|
||||||
|
may = False
|
||||||
|
|
||||||
|
class QGoal:
|
||||||
|
def __init__(self, cube, parent, level, must):
|
||||||
|
self.level = level
|
||||||
|
self.cube = cube
|
||||||
|
self.parent = parent
|
||||||
|
self.must = must
|
||||||
|
|
||||||
|
class Quip(MiniIC3):
|
||||||
|
|
||||||
|
# prev & tras -> r', such that r' intersects with cube
|
||||||
|
def add_reachable(self, prev, cube):
|
||||||
|
s = fd_solver()
|
||||||
|
s.add(self.trans)
|
||||||
|
s.add(prev)
|
||||||
|
s.add(Or(cube))
|
||||||
|
is_sat = s.check()
|
||||||
|
assert is_sat == sat
|
||||||
|
m = s.model();
|
||||||
|
result = self.values2literals(m, cube)
|
||||||
|
assert result
|
||||||
|
self.reachable.add(result)
|
||||||
|
|
||||||
|
# A state s0 and level f0 such that
|
||||||
|
# not(s0) is f0-1 inductive
|
||||||
|
def quip_blocked(self, s0, f0):
|
||||||
|
self.push_heap(QGoal(self.next(s0), None, f0, must))
|
||||||
|
while self.goals:
|
||||||
|
f, g = heapq.heappop(self.goals)
|
||||||
|
sys.stdout.write("%d." % f)
|
||||||
|
sys.stdout.flush()
|
||||||
|
if f == 0:
|
||||||
|
if g.must:
|
||||||
|
print("")
|
||||||
|
return g
|
||||||
|
self.add_reachable(self.init, p.parent.cube)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TBD
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not check_disjoint(self.init, self.bad):
|
||||||
|
return "goal is reached in initial state"
|
||||||
|
level = 0
|
||||||
|
while True:
|
||||||
|
inv = self.is_valid()
|
||||||
|
if inv is not None:
|
||||||
|
return inv
|
||||||
|
is_sat, cube = self.unfold()
|
||||||
|
if is_sat == unsat:
|
||||||
|
level += 1
|
||||||
|
print("Unfold %d" % level)
|
||||||
|
sys.stdout.flush()
|
||||||
|
self.add_solver()
|
||||||
|
elif is_sat == sat:
|
||||||
|
cex = self.quipie_blocked(cube, level)
|
||||||
|
if cex is not None:
|
||||||
|
return cex
|
||||||
|
else:
|
||||||
|
return is_sat
|
||||||
|
|
||||||
|
"""
|
|
@ -10,7 +10,7 @@ from mk_util import *
|
||||||
# Z3 Project definition
|
# Z3 Project definition
|
||||||
def init_project_def():
|
def init_project_def():
|
||||||
set_version(4, 8, 0, 0)
|
set_version(4, 8, 0, 0)
|
||||||
add_lib('util', [])
|
add_lib('util', [], includes2install = ['z3_version.h'])
|
||||||
add_lib('polynomial', ['util'], 'math/polynomial')
|
add_lib('polynomial', ['util'], 'math/polynomial')
|
||||||
add_lib('sat', ['util'])
|
add_lib('sat', ['util'])
|
||||||
add_lib('nlsat', ['polynomial', 'sat'])
|
add_lib('nlsat', ['polynomial', 'sat'])
|
||||||
|
|
|
@ -2805,8 +2805,8 @@ def get_full_version_string(major, minor, build, revision):
|
||||||
# Update files with the version number
|
# Update files with the version number
|
||||||
def mk_version_dot_h(major, minor, build, revision):
|
def mk_version_dot_h(major, minor, build, revision):
|
||||||
c = get_component(UTIL_COMPONENT)
|
c = get_component(UTIL_COMPONENT)
|
||||||
version_template = os.path.join(c.src_dir, 'version.h.in')
|
version_template = os.path.join(c.src_dir, 'z3_version.h.in')
|
||||||
version_header_output = os.path.join(c.src_dir, 'version.h')
|
version_header_output = os.path.join(c.src_dir, 'z3_version.h')
|
||||||
# Note the substitution names are what is used by the CMake
|
# Note the substitution names are what is used by the CMake
|
||||||
# builds system. If you change these you should change them
|
# builds system. If you change these you should change them
|
||||||
# in the CMake build too
|
# in the CMake build too
|
||||||
|
|
|
@ -166,6 +166,8 @@ foreach (header ${libz3_public_headers})
|
||||||
set_property(TARGET libz3 APPEND PROPERTY
|
set_property(TARGET libz3 APPEND PROPERTY
|
||||||
PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/src/api/${header}")
|
PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/src/api/${header}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
set_property(TARGET libz3 APPEND PROPERTY
|
||||||
|
PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/util/z3_version.h")
|
||||||
|
|
||||||
install(TARGETS libz3
|
install(TARGETS libz3
|
||||||
EXPORT Z3_EXPORTED_TARGETS
|
EXPORT Z3_EXPORTED_TARGETS
|
||||||
|
|
|
@ -19,7 +19,7 @@ Revision History:
|
||||||
--*/
|
--*/
|
||||||
#include<typeinfo>
|
#include<typeinfo>
|
||||||
#include "api/api_context.h"
|
#include "api/api_context.h"
|
||||||
#include "util/version.h"
|
#include "util/z3_version.h"
|
||||||
#include "ast/ast_pp.h"
|
#include "ast/ast_pp.h"
|
||||||
#include "ast/ast_ll_pp.h"
|
#include "ast/ast_ll_pp.h"
|
||||||
#include "api/api_log_macros.h"
|
#include "api/api_log_macros.h"
|
||||||
|
|
|
@ -19,7 +19,7 @@ Revision History:
|
||||||
#include "api/z3.h"
|
#include "api/z3.h"
|
||||||
#include "api/api_log_macros.h"
|
#include "api/api_log_macros.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
#include "util/version.h"
|
#include "util/z3_version.h"
|
||||||
|
|
||||||
std::ostream * g_z3_log = nullptr;
|
std::ostream * g_z3_log = nullptr;
|
||||||
bool g_z3_log_enabled = false;
|
bool g_z3_log_enabled = false;
|
||||||
|
|
|
@ -179,6 +179,7 @@ extern "C" {
|
||||||
LOG_Z3_solver_from_file(c, s, file_name);
|
LOG_Z3_solver_from_file(c, s, file_name);
|
||||||
char const* ext = get_extension(file_name);
|
char const* ext = get_extension(file_name);
|
||||||
std::ifstream is(file_name);
|
std::ifstream is(file_name);
|
||||||
|
init_solver(c, s);
|
||||||
if (!is) {
|
if (!is) {
|
||||||
SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr);
|
SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -371,6 +372,21 @@ extern "C" {
|
||||||
Z3_CATCH_RETURN(0);
|
Z3_CATCH_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s) {
|
||||||
|
Z3_TRY;
|
||||||
|
LOG_Z3_solver_get_non_units(c, s);
|
||||||
|
RESET_ERROR_CODE();
|
||||||
|
init_solver(c, s);
|
||||||
|
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||||
|
mk_c(c)->save_object(v);
|
||||||
|
expr_ref_vector fmls = to_solver_ref(s)->get_non_units(mk_c(c)->m());
|
||||||
|
for (expr* f : fmls) {
|
||||||
|
v->m_ast_vector.push_back(f);
|
||||||
|
}
|
||||||
|
RETURN_Z3(of_ast_vector(v));
|
||||||
|
Z3_CATCH_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) {
|
static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) {
|
||||||
for (unsigned i = 0; i < num_assumptions; i++) {
|
for (unsigned i = 0; i < num_assumptions; i++) {
|
||||||
if (!is_expr(to_ast(assumptions[i]))) {
|
if (!is_expr(to_ast(assumptions[i]))) {
|
||||||
|
|
|
@ -2038,6 +2038,8 @@ namespace z3 {
|
||||||
stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); }
|
stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); }
|
||||||
expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
||||||
expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
||||||
|
expr_vector non_units() const { Z3_ast_vector r = Z3_solver_get_non_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
||||||
|
expr_vector units() const { Z3_ast_vector r = Z3_solver_get_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
|
||||||
expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); }
|
expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); }
|
||||||
friend std::ostream & operator<<(std::ostream & out, solver const & s);
|
friend std::ostream & operator<<(std::ostream & out, solver const & s);
|
||||||
|
|
||||||
|
|
|
@ -1815,9 +1815,10 @@ struct
|
||||||
| _ -> UNKNOWN
|
| _ -> UNKNOWN
|
||||||
|
|
||||||
let get_model x =
|
let get_model x =
|
||||||
let q = Z3native.solver_get_model (gc x) x in
|
try
|
||||||
try if Z3native.is_null_model q then None else Some q with | _ -> None
|
let q = Z3native.solver_get_model (gc x) x in
|
||||||
|
if Z3native.is_null_model q then None else Some q
|
||||||
|
with | _ -> None
|
||||||
|
|
||||||
let get_proof x =
|
let get_proof x =
|
||||||
let q = Z3native.solver_get_proof (gc x) x in
|
let q = Z3native.solver_get_proof (gc x) x in
|
||||||
|
@ -1953,8 +1954,10 @@ struct
|
||||||
| _ -> Solver.UNKNOWN
|
| _ -> Solver.UNKNOWN
|
||||||
|
|
||||||
let get_model (x:optimize) =
|
let get_model (x:optimize) =
|
||||||
let q = Z3native.optimize_get_model (gc x) x in
|
try
|
||||||
if Z3native.is_null_model q then None else Some q
|
let q = Z3native.optimize_get_model (gc x) x in
|
||||||
|
if Z3native.is_null_model q then None else Some q
|
||||||
|
with | _ -> None
|
||||||
|
|
||||||
let get_lower (x:handle) = Z3native.optimize_get_lower (gc x.opt) x.opt x.h
|
let get_lower (x:handle) = Z3native.optimize_get_lower (gc x.opt) x.opt x.h
|
||||||
let get_upper (x:handle) = Z3native.optimize_get_upper (gc x.opt) x.opt x.h
|
let get_upper (x:handle) = Z3native.optimize_get_upper (gc x.opt) x.opt x.h
|
||||||
|
|
|
@ -1258,6 +1258,11 @@ def Consts(names, sort):
|
||||||
names = names.split(" ")
|
names = names.split(" ")
|
||||||
return [Const(name, sort) for name in names]
|
return [Const(name, sort) for name in names]
|
||||||
|
|
||||||
|
def FreshConst(sort, prefix='c'):
|
||||||
|
"""Create a fresh constant of a specified sort"""
|
||||||
|
ctx = _get_ctx(sort.ctx)
|
||||||
|
return _to_expr_ref(Z3_mk_fresh_const(ctx.ref(), prefix, sort.ast), ctx)
|
||||||
|
|
||||||
def Var(idx, s):
|
def Var(idx, s):
|
||||||
"""Create a Z3 free variable. Free variables are used to create quantified formulas.
|
"""Create a Z3 free variable. Free variables are used to create quantified formulas.
|
||||||
|
|
||||||
|
@ -2176,6 +2181,8 @@ class ArithRef(ExprRef):
|
||||||
>>> (x * y).sort()
|
>>> (x * y).sort()
|
||||||
Real
|
Real
|
||||||
"""
|
"""
|
||||||
|
if isinstance(other, BoolRef):
|
||||||
|
return If(other, self, 0)
|
||||||
a, b = _coerce_exprs(self, other)
|
a, b = _coerce_exprs(self, other)
|
||||||
return ArithRef(_mk_bin(Z3_mk_mul, a, b), self.ctx)
|
return ArithRef(_mk_bin(Z3_mk_mul, a, b), self.ctx)
|
||||||
|
|
||||||
|
@ -4278,7 +4285,7 @@ def get_map_func(a):
|
||||||
_z3_assert(is_map(a), "Z3 array map expression expected.")
|
_z3_assert(is_map(a), "Z3 array map expression expected.")
|
||||||
return FuncDeclRef(Z3_to_func_decl(a.ctx_ref(), Z3_get_decl_ast_parameter(a.ctx_ref(), a.decl().ast, 0)), a.ctx)
|
return FuncDeclRef(Z3_to_func_decl(a.ctx_ref(), Z3_get_decl_ast_parameter(a.ctx_ref(), a.decl().ast, 0)), a.ctx)
|
||||||
|
|
||||||
def ArraySort(d, r):
|
def ArraySort(*sig):
|
||||||
"""Return the Z3 array sort with the given domain and range sorts.
|
"""Return the Z3 array sort with the given domain and range sorts.
|
||||||
|
|
||||||
>>> A = ArraySort(IntSort(), BoolSort())
|
>>> A = ArraySort(IntSort(), BoolSort())
|
||||||
|
@ -4292,12 +4299,23 @@ def ArraySort(d, r):
|
||||||
>>> AA
|
>>> AA
|
||||||
Array(Int, Array(Int, Bool))
|
Array(Int, Array(Int, Bool))
|
||||||
"""
|
"""
|
||||||
|
sig = _get_args(sig)
|
||||||
if __debug__:
|
if __debug__:
|
||||||
_z3_assert(is_sort(d), "Z3 sort expected")
|
_z3_assert(len(sig) > 1, "At least two arguments expected")
|
||||||
_z3_assert(is_sort(r), "Z3 sort expected")
|
arity = len(sig) - 1
|
||||||
_z3_assert(d.ctx == r.ctx, "Context mismatch")
|
r = sig[arity]
|
||||||
|
d = sig[0]
|
||||||
|
if __debug__:
|
||||||
|
for s in sig:
|
||||||
|
_z3_assert(is_sort(s), "Z3 sort expected")
|
||||||
|
_z3_assert(s.ctx == r.ctx, "Context mismatch")
|
||||||
ctx = d.ctx
|
ctx = d.ctx
|
||||||
return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx)
|
if len(sig) == 2:
|
||||||
|
return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx)
|
||||||
|
dom = (Sort * arity)()
|
||||||
|
for i in range(arity):
|
||||||
|
dom[i] = sig[i].ast
|
||||||
|
return ArraySortRef(Z3_mk_array_sort_n(ctx.ref(), arity, dom, r.ast), ctx)
|
||||||
|
|
||||||
def Array(name, dom, rng):
|
def Array(name, dom, rng):
|
||||||
"""Return an array constant named `name` with the given domain and range sorts.
|
"""Return an array constant named `name` with the given domain and range sorts.
|
||||||
|
@ -6603,7 +6621,12 @@ class Solver(Z3PPObject):
|
||||||
_handle_parse_error(e, self.ctx)
|
_handle_parse_error(e, self.ctx)
|
||||||
|
|
||||||
def cube(self, vars = None):
|
def cube(self, vars = None):
|
||||||
"""Get set of cubes"""
|
"""Get set of cubes
|
||||||
|
The method takes an optional set of variables that restrict which
|
||||||
|
variables may be used as a starting point for cubing.
|
||||||
|
If vars is not None, then the first case split is based on a variable in
|
||||||
|
this set.
|
||||||
|
"""
|
||||||
self.cube_vs = AstVector(None, self.ctx)
|
self.cube_vs = AstVector(None, self.ctx)
|
||||||
if vars is not None:
|
if vars is not None:
|
||||||
for v in vars:
|
for v in vars:
|
||||||
|
@ -6619,19 +6642,15 @@ class Solver(Z3PPObject):
|
||||||
return
|
return
|
||||||
|
|
||||||
def cube_vars(self):
|
def cube_vars(self):
|
||||||
|
"""Access the set of variables that were touched by the most recently generated cube.
|
||||||
|
This set of variables can be used as a starting point for additional cubes.
|
||||||
|
The idea is that variables that appear in clauses that are reduced by the most recent
|
||||||
|
cube are likely more useful to cube on."""
|
||||||
return self.cube_vs
|
return self.cube_vs
|
||||||
|
|
||||||
def proof(self):
|
def proof(self):
|
||||||
"""Return a proof for the last `check()`. Proof construction must be enabled."""
|
"""Return a proof for the last `check()`. Proof construction must be enabled."""
|
||||||
return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx)
|
return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx)
|
||||||
|
|
||||||
def from_file(self, filename):
|
|
||||||
"""Parse assertions from a file"""
|
|
||||||
Z3_solver_from_file(self.ctx.ref(), self.solver, filename)
|
|
||||||
|
|
||||||
def from_string(self, s):
|
|
||||||
"""Parse assertions from a string"""
|
|
||||||
Z3_solver_from_string(self.ctx.ref(), self.solver, s)
|
|
||||||
|
|
||||||
def assertions(self):
|
def assertions(self):
|
||||||
"""Return an AST vector containing all added constraints.
|
"""Return an AST vector containing all added constraints.
|
||||||
|
@ -6652,6 +6671,11 @@ class Solver(Z3PPObject):
|
||||||
"""
|
"""
|
||||||
return AstVector(Z3_solver_get_units(self.ctx.ref(), self.solver), self.ctx)
|
return AstVector(Z3_solver_get_units(self.ctx.ref(), self.solver), self.ctx)
|
||||||
|
|
||||||
|
def non_units(self):
|
||||||
|
"""Return an AST vector containing all atomic formulas in solver state that are not units.
|
||||||
|
"""
|
||||||
|
return AstVector(Z3_solver_get_non_units(self.ctx.ref(), self.solver), self.ctx)
|
||||||
|
|
||||||
def statistics(self):
|
def statistics(self):
|
||||||
"""Return statistics for the last `check()`.
|
"""Return statistics for the last `check()`.
|
||||||
|
|
||||||
|
@ -8040,7 +8064,7 @@ def substitute(t, *m):
|
||||||
"""
|
"""
|
||||||
if isinstance(m, tuple):
|
if isinstance(m, tuple):
|
||||||
m1 = _get_args(m)
|
m1 = _get_args(m)
|
||||||
if isinstance(m1, list):
|
if isinstance(m1, list) and all(isinstance(p, tuple) for p in m1):
|
||||||
m = m1
|
m = m1
|
||||||
if __debug__:
|
if __debug__:
|
||||||
_z3_assert(is_expr(t), "Z3 expression expected")
|
_z3_assert(is_expr(t), "Z3 expression expected")
|
||||||
|
|
|
@ -6121,6 +6121,14 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s);
|
Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Return the set of non units in the solver state.
|
||||||
|
|
||||||
|
def_API('Z3_solver_get_non_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER)))
|
||||||
|
*/
|
||||||
|
Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Check whether the assertions in a given solver are consistent or not.
|
\brief Check whether the assertions in a given solver are consistent or not.
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ Revision History:
|
||||||
#include "math/polynomial/algebraic_numbers.h"
|
#include "math/polynomial/algebraic_numbers.h"
|
||||||
#include "util/id_gen.h"
|
#include "util/id_gen.h"
|
||||||
#include "ast/ast_smt2_pp.h"
|
#include "ast/ast_smt2_pp.h"
|
||||||
|
#include "util/gparams.h"
|
||||||
|
|
||||||
struct arith_decl_plugin::algebraic_numbers_wrapper {
|
struct arith_decl_plugin::algebraic_numbers_wrapper {
|
||||||
unsynch_mpq_manager m_qmanager;
|
unsynch_mpq_manager m_qmanager;
|
||||||
|
@ -487,7 +488,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
||||||
if (arity != 1 || domain[0] != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) {
|
if (arity != 1 || domain[0] != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) {
|
||||||
m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer");
|
m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer");
|
||||||
}
|
}
|
||||||
return m_manager->mk_func_decl(symbol("divides"), 1, &m_int_decl, m_manager->mk_bool_sort(),
|
return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(),
|
||||||
func_decl_info(m_family_id, k, num_parameters, parameters));
|
func_decl_info(m_family_id, k, num_parameters, parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +513,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
||||||
if (num_args != 1 || m_manager->get_sort(args[0]) != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) {
|
if (num_args != 1 || m_manager->get_sort(args[0]) != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) {
|
||||||
m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer");
|
m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer");
|
||||||
}
|
}
|
||||||
return m_manager->mk_func_decl(symbol("divides"), 1, &m_int_decl, m_manager->mk_bool_sort(),
|
return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(),
|
||||||
func_decl_info(m_family_id, k, num_parameters, parameters));
|
func_decl_info(m_family_id, k, num_parameters, parameters));
|
||||||
}
|
}
|
||||||
if (m_manager->int_real_coercions() && use_coercion(k)) {
|
if (m_manager->int_real_coercions() && use_coercion(k)) {
|
||||||
|
@ -549,8 +550,9 @@ void arith_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol con
|
||||||
op_names.push_back(builtin_name("*",OP_MUL));
|
op_names.push_back(builtin_name("*",OP_MUL));
|
||||||
op_names.push_back(builtin_name("/",OP_DIV));
|
op_names.push_back(builtin_name("/",OP_DIV));
|
||||||
op_names.push_back(builtin_name("div",OP_IDIV));
|
op_names.push_back(builtin_name("div",OP_IDIV));
|
||||||
// clashes with user-defined functions
|
if (gparams::get_value("smtlib2_compliant") == "true") {
|
||||||
// op_names.push_back(builtin_name("divides",OP_IDIVIDES));
|
op_names.push_back(builtin_name("divisible",OP_IDIVIDES));
|
||||||
|
}
|
||||||
op_names.push_back(builtin_name("rem",OP_REM));
|
op_names.push_back(builtin_name("rem",OP_REM));
|
||||||
op_names.push_back(builtin_name("mod",OP_MOD));
|
op_names.push_back(builtin_name("mod",OP_MOD));
|
||||||
op_names.push_back(builtin_name("to_real",OP_TO_REAL));
|
op_names.push_back(builtin_name("to_real",OP_TO_REAL));
|
||||||
|
|
|
@ -1656,6 +1656,12 @@ bool ast_manager::are_distinct(expr* a, expr* b) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func_decl* ast_manager::get_rec_fun_decl(quantifier* q) const {
|
||||||
|
SASSERT(is_rec_fun_def(q));
|
||||||
|
return to_app(to_app(q->get_pattern(0))->get_arg(0))->get_decl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
|
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
|
||||||
SASSERT(m_plugins.get(id, 0) == 0);
|
SASSERT(m_plugins.get(id, 0) == 0);
|
||||||
m_plugins.setx(id, plugin, 0);
|
m_plugins.setx(id, plugin, 0);
|
||||||
|
|
|
@ -1632,6 +1632,7 @@ public:
|
||||||
|
|
||||||
bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; }
|
bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; }
|
||||||
bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
|
bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
|
||||||
|
func_decl* get_rec_fun_decl(quantifier* q) const;
|
||||||
|
|
||||||
symbol const& rec_fun_qid() const { return m_rec_fun; }
|
symbol const& rec_fun_qid() const { return m_rec_fun; }
|
||||||
|
|
||||||
|
|
|
@ -842,7 +842,9 @@ void seq_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
app* seq_decl_plugin::mk_string(symbol const& s) {
|
app* seq_decl_plugin::mk_string(symbol const& s) {
|
||||||
parameter param(s);
|
zstring canonStr(s.bare_str());
|
||||||
|
symbol canonSym(canonStr.encode().c_str());
|
||||||
|
parameter param(canonSym);
|
||||||
func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string,
|
func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string,
|
||||||
func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m));
|
func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m));
|
||||||
return m_manager->mk_const(f);
|
return m_manager->mk_const(f);
|
||||||
|
|
|
@ -17,7 +17,7 @@ Notes:
|
||||||
--*/
|
--*/
|
||||||
#include "util/gparams.h"
|
#include "util/gparams.h"
|
||||||
#include "util/env_params.h"
|
#include "util/env_params.h"
|
||||||
#include "util/version.h"
|
#include "util/z3_version.h"
|
||||||
#include "ast/ast_smt_pp.h"
|
#include "ast/ast_smt_pp.h"
|
||||||
#include "ast/ast_smt2_pp.h"
|
#include "ast/ast_smt2_pp.h"
|
||||||
#include "ast/ast_pp_dot.h"
|
#include "ast/ast_pp_dot.h"
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace datalog {
|
||||||
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
|
rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed):
|
||||||
m_context(o.m_context) {
|
m_context(o.m_context) {
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
for (auto & kv : o) {
|
for (auto & kv : o) {
|
||||||
func_decl * pred = kv.m_key;
|
func_decl * pred = kv.m_key;
|
||||||
item_set & orig_items = *kv.get_value();
|
item_set & orig_items = *kv.get_value();
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ namespace datalog {
|
||||||
app* a = to_app(e);
|
app* a = to_app(e);
|
||||||
d = a->get_decl();
|
d = a->get_decl();
|
||||||
if (m_context.is_predicate(d)) {
|
if (m_context.is_predicate(d)) {
|
||||||
// insert d and ensure the invariant
|
// insert d and ensure the invariant
|
||||||
// that every predicate is present as
|
// that every predicate is present as
|
||||||
// a key in m_data
|
// a key in m_data
|
||||||
s.insert(d);
|
s.insert(d);
|
||||||
ensure_key(d);
|
ensure_key(d);
|
||||||
|
@ -148,7 +148,7 @@ namespace datalog {
|
||||||
item_set& itms = *kv.get_value();
|
item_set& itms = *kv.get_value();
|
||||||
set_intersection(itms, allowed);
|
set_intersection(itms, allowed);
|
||||||
}
|
}
|
||||||
for (func_decl* f : to_remove)
|
for (func_decl* f : to_remove)
|
||||||
remove_m_data_entry(f);
|
remove_m_data_entry(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,18 +253,18 @@ namespace datalog {
|
||||||
//
|
//
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
|
|
||||||
rule_set::rule_set(context & ctx)
|
rule_set::rule_set(context & ctx)
|
||||||
: m_context(ctx),
|
: m_context(ctx),
|
||||||
m_rule_manager(ctx.get_rule_manager()),
|
m_rule_manager(ctx.get_rule_manager()),
|
||||||
m_rules(m_rule_manager),
|
m_rules(m_rule_manager),
|
||||||
m_deps(ctx),
|
m_deps(ctx),
|
||||||
m_stratifier(nullptr),
|
m_stratifier(nullptr),
|
||||||
m_refs(ctx.get_manager()) {
|
m_refs(ctx.get_manager()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rule_set::rule_set(const rule_set & other)
|
rule_set::rule_set(const rule_set & other)
|
||||||
: m_context(other.m_context),
|
: m_context(other.m_context),
|
||||||
m_rule_manager(other.m_rule_manager),
|
m_rule_manager(other.m_rule_manager),
|
||||||
m_rules(m_rule_manager),
|
m_rules(m_rule_manager),
|
||||||
m_deps(other.m_context),
|
m_deps(other.m_context),
|
||||||
m_stratifier(nullptr),
|
m_stratifier(nullptr),
|
||||||
|
@ -353,10 +353,27 @@ namespace datalog {
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
DEL_VECTOR(*rules);
|
DEL_VECTOR(*rules);
|
||||||
DEL_VECTOR(m_rules);
|
DEL_VECTOR(m_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rule_set::replace_rule(rule * r, rule * other) {
|
||||||
|
TRACE("dl", r->display(m_context, tout << "replace:"););
|
||||||
|
func_decl* d = r->get_decl();
|
||||||
|
rule_vector* rules = m_head2rules.find(d);
|
||||||
|
#define REPLACE_VECTOR(_v) \
|
||||||
|
for (unsigned i = (_v).size(); i > 0; ) { \
|
||||||
|
--i; \
|
||||||
|
if ((_v)[i] == r) { \
|
||||||
|
(_v)[i] = other; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
|
||||||
|
REPLACE_VECTOR(*rules);
|
||||||
|
REPLACE_VECTOR(m_rules);
|
||||||
|
}
|
||||||
|
|
||||||
void rule_set::ensure_closed() {
|
void rule_set::ensure_closed() {
|
||||||
if (!is_closed()) {
|
if (!is_closed()) {
|
||||||
|
@ -365,7 +382,7 @@ namespace datalog {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rule_set::close() {
|
bool rule_set::close() {
|
||||||
SASSERT(!is_closed()); //the rule_set is not already closed
|
SASSERT(!is_closed()); //the rule_set is not already closed
|
||||||
m_deps.populate(*this);
|
m_deps.populate(*this);
|
||||||
m_stratifier = alloc(rule_stratifier, m_deps);
|
m_stratifier = alloc(rule_stratifier, m_deps);
|
||||||
if (!stratified_negation()) {
|
if (!stratified_negation()) {
|
||||||
|
@ -426,7 +443,7 @@ namespace datalog {
|
||||||
inherit_predicates(src);
|
inherit_predicates(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
|
const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const {
|
||||||
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
|
decl2rules::obj_map_entry * e = m_head2rules.find_core(pred);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
return m_empty_rule_vector;
|
return m_empty_rule_vector;
|
||||||
|
@ -519,7 +536,7 @@ namespace datalog {
|
||||||
out << "\n";
|
out << "\n";
|
||||||
non_empty = false;
|
non_empty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (func_decl * first : *strat) {
|
for (func_decl * first : *strat) {
|
||||||
const func_decl_set & deps = m_deps.get_deps(first);
|
const func_decl_set & deps = m_deps.get_deps(first);
|
||||||
for (func_decl * dep : deps) {
|
for (func_decl * dep : deps) {
|
||||||
|
@ -545,8 +562,8 @@ namespace datalog {
|
||||||
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
|
unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const {
|
||||||
unsigned num;
|
unsigned num;
|
||||||
if (!m_pred_strat_nums.find(pred, num)) {
|
if (!m_pred_strat_nums.find(pred, num)) {
|
||||||
//the number of the predicate is not stored, therefore it did not appear
|
//the number of the predicate is not stored, therefore it did not appear
|
||||||
//in the algorithm and therefore it does not depend on anything and nothing
|
//in the algorithm and therefore it does not depend on anything and nothing
|
||||||
//depends on it. So it is safe to assign zero strate to it, although it is
|
//depends on it. So it is safe to assign zero strate to it, although it is
|
||||||
//not strictly true.
|
//not strictly true.
|
||||||
num = 0;
|
num = 0;
|
||||||
|
@ -641,7 +658,7 @@ namespace datalog {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We put components whose indegree is zero to m_strats and assign its
|
// We put components whose indegree is zero to m_strats and assign its
|
||||||
// m_components entry to zero.
|
// m_components entry to zero.
|
||||||
unsigned comp_cnt = m_components.size();
|
unsigned comp_cnt = m_components.size();
|
||||||
for (unsigned i = 0; i < comp_cnt; i++) {
|
for (unsigned i = 0; i < comp_cnt; i++) {
|
||||||
|
@ -680,7 +697,7 @@ namespace datalog {
|
||||||
strats_index++;
|
strats_index++;
|
||||||
}
|
}
|
||||||
//we have managed to topologicaly order all the components
|
//we have managed to topologicaly order all the components
|
||||||
SASSERT(std::find_if(m_components.begin(), m_components.end(),
|
SASSERT(std::find_if(m_components.begin(), m_components.end(),
|
||||||
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
|
std::bind1st(std::not_equal_to<item_set*>(), (item_set*)0)) == m_components.end());
|
||||||
|
|
||||||
//reverse the strats array, so that the only the later components would depend on earlier ones
|
//reverse the strats array, so that the only the later components would depend on earlier ones
|
||||||
|
@ -713,7 +730,7 @@ namespace datalog {
|
||||||
}
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace datalog {
|
||||||
\brief Number of predicates that depend on \c f.
|
\brief Number of predicates that depend on \c f.
|
||||||
*/
|
*/
|
||||||
unsigned out_degree(func_decl * f) const;
|
unsigned out_degree(func_decl * f) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief If the rependency graph is acyclic, put all elements into \c res
|
\brief If the rependency graph is acyclic, put all elements into \c res
|
||||||
ordered so that elements can depend only on elements that are before them.
|
ordered so that elements can depend only on elements that are before them.
|
||||||
|
@ -131,7 +131,7 @@ namespace datalog {
|
||||||
it must exist for the whole lifetime of the \c stratifier object.
|
it must exist for the whole lifetime of the \c stratifier object.
|
||||||
*/
|
*/
|
||||||
rule_stratifier(const rule_dependencies & deps)
|
rule_stratifier(const rule_dependencies & deps)
|
||||||
: m_deps(deps), m_next_preorder(0)
|
: m_deps(deps), m_next_preorder(0)
|
||||||
{
|
{
|
||||||
process();
|
process();
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ namespace datalog {
|
||||||
const comp_vector & get_strats() const { return m_strats; }
|
const comp_vector & get_strats() const { return m_strats; }
|
||||||
|
|
||||||
unsigned get_predicate_strat(func_decl * pred) const;
|
unsigned get_predicate_strat(func_decl * pred) const;
|
||||||
|
|
||||||
void display( std::ostream & out ) const;
|
void display( std::ostream & out ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -203,6 +203,10 @@ namespace datalog {
|
||||||
\brief Remove rule \c r from the rule set.
|
\brief Remove rule \c r from the rule set.
|
||||||
*/
|
*/
|
||||||
void del_rule(rule * r);
|
void del_rule(rule * r);
|
||||||
|
/**
|
||||||
|
\brief Replace a rule \c r with the rule \c other
|
||||||
|
*/
|
||||||
|
void replace_rule(rule * r, rule * other);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Add all rules from a different rule_set.
|
\brief Add all rules from a different rule_set.
|
||||||
|
@ -276,8 +280,7 @@ namespace datalog {
|
||||||
inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; }
|
inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DL_RULE_SET_H_ */
|
#endif /* DL_RULE_SET_H_ */
|
||||||
|
|
||||||
|
|
|
@ -177,5 +177,6 @@ def_module_params('fp',
|
||||||
('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'),
|
('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'),
|
||||||
('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'),
|
('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'),
|
||||||
('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'),
|
('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'),
|
||||||
|
('spacer.use_bg_invs', BOOL, False, 'Enable external background invariants'),
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|
|
@ -493,7 +493,8 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) :
|
||||||
m_pob(nullptr), m_ctp(nullptr),
|
m_pob(nullptr), m_ctp(nullptr),
|
||||||
m_lvl(lvl), m_init_lvl(m_lvl),
|
m_lvl(lvl), m_init_lvl(m_lvl),
|
||||||
m_bumped(0), m_weakness(WEAKNESS_MAX),
|
m_bumped(0), m_weakness(WEAKNESS_MAX),
|
||||||
m_external(false), m_blocked(false) {
|
m_external(false), m_blocked(false),
|
||||||
|
m_background(false) {
|
||||||
SASSERT(m_body);
|
SASSERT(m_body);
|
||||||
normalize(m_body, m_body);
|
normalize(m_body, m_body);
|
||||||
}
|
}
|
||||||
|
@ -505,7 +506,8 @@ lemma::lemma(pob_ref const &p) :
|
||||||
m_pob(p), m_ctp(nullptr),
|
m_pob(p), m_ctp(nullptr),
|
||||||
m_lvl(p->level()), m_init_lvl(m_lvl),
|
m_lvl(p->level()), m_init_lvl(m_lvl),
|
||||||
m_bumped(0), m_weakness(p->weakness()),
|
m_bumped(0), m_weakness(p->weakness()),
|
||||||
m_external(false), m_blocked(false) {
|
m_external(false), m_blocked(false),
|
||||||
|
m_background(false) {
|
||||||
SASSERT(m_pob);
|
SASSERT(m_pob);
|
||||||
m_pob->get_skolems(m_zks);
|
m_pob->get_skolems(m_zks);
|
||||||
add_binding(m_pob->get_binding());
|
add_binding(m_pob->get_binding());
|
||||||
|
@ -519,8 +521,8 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) :
|
||||||
m_pob(p), m_ctp(nullptr),
|
m_pob(p), m_ctp(nullptr),
|
||||||
m_lvl(p->level()), m_init_lvl(m_lvl),
|
m_lvl(p->level()), m_init_lvl(m_lvl),
|
||||||
m_bumped(0), m_weakness(p->weakness()),
|
m_bumped(0), m_weakness(p->weakness()),
|
||||||
m_external(false), m_blocked(false)
|
m_external(false), m_blocked(false),
|
||||||
{
|
m_background(false) {
|
||||||
if (m_pob) {
|
if (m_pob) {
|
||||||
m_pob->get_skolems(m_zks);
|
m_pob->get_skolems(m_zks);
|
||||||
add_binding(m_pob->get_binding());
|
add_binding(m_pob->get_binding());
|
||||||
|
@ -921,10 +923,10 @@ void pred_transformer::simplify_formulas()
|
||||||
{m_frames.simplify_formulas ();}
|
{m_frames.simplify_formulas ();}
|
||||||
|
|
||||||
|
|
||||||
expr_ref pred_transformer::get_formulas(unsigned level) const
|
expr_ref pred_transformer::get_formulas(unsigned level, bool bg) const
|
||||||
{
|
{
|
||||||
expr_ref_vector res(m);
|
expr_ref_vector res(m);
|
||||||
m_frames.get_frame_geq_lemmas (level, res);
|
m_frames.get_frame_geq_lemmas (level, res, bg);
|
||||||
return mk_and(res);
|
return mk_and(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,6 +937,7 @@ bool pred_transformer::propagate_to_next_level (unsigned src_level)
|
||||||
/// \brief adds a lemma to the solver and to child solvers
|
/// \brief adds a lemma to the solver and to child solvers
|
||||||
void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only)
|
void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only)
|
||||||
{
|
{
|
||||||
|
SASSERT(!lemma->is_background());
|
||||||
unsigned lvl = lemma->level();
|
unsigned lvl = lemma->level();
|
||||||
expr* l = lemma->get_expr();
|
expr* l = lemma->get_expr();
|
||||||
SASSERT(!lemma->is_ground() || is_clause(m, l));
|
SASSERT(!lemma->is_ground() || is_clause(m, l));
|
||||||
|
@ -975,8 +978,9 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only)
|
||||||
next_level(lvl), ground_only); }
|
next_level(lvl), ground_only); }
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pred_transformer::add_lemma (expr *e, unsigned lvl) {
|
bool pred_transformer::add_lemma (expr *e, unsigned lvl, bool bg) {
|
||||||
lemma_ref lem = alloc(lemma, m, e, lvl);
|
lemma_ref lem = alloc(lemma, m, e, lvl);
|
||||||
|
lem->set_background(bg);
|
||||||
return m_frames.add_lemma(lem.get());
|
return m_frames.add_lemma(lem.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1217,15 +1221,18 @@ expr_ref pred_transformer::get_origin_summary (model &mdl,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void pred_transformer::add_cover(unsigned level, expr* property)
|
void pred_transformer::add_cover(unsigned level, expr* property, bool bg)
|
||||||
{
|
{
|
||||||
|
SASSERT(!bg || is_infty_level(level));
|
||||||
// replace bound variables by local constants.
|
// replace bound variables by local constants.
|
||||||
expr_ref result(property, m), v(m), c(m);
|
expr_ref result(property, m), v(m), c(m);
|
||||||
expr_substitution sub(m);
|
expr_substitution sub(m);
|
||||||
|
proof_ref pr(m);
|
||||||
|
pr = m.mk_asserted(m.mk_true());
|
||||||
for (unsigned i = 0; i < sig_size(); ++i) {
|
for (unsigned i = 0; i < sig_size(); ++i) {
|
||||||
c = m.mk_const(pm.o2n(sig(i), 0));
|
c = m.mk_const(pm.o2n(sig(i), 0));
|
||||||
v = m.mk_var(i, sig(i)->get_range());
|
v = m.mk_var(i, sig(i)->get_range());
|
||||||
sub.insert(v, c);
|
sub.insert(v, c, pr);
|
||||||
}
|
}
|
||||||
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
|
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
|
||||||
rep->set_substitution(&sub);
|
rep->set_substitution(&sub);
|
||||||
|
@ -1236,13 +1243,38 @@ void pred_transformer::add_cover(unsigned level, expr* property)
|
||||||
expr_ref_vector lemmas(m);
|
expr_ref_vector lemmas(m);
|
||||||
flatten_and(result, lemmas);
|
flatten_and(result, lemmas);
|
||||||
for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) {
|
for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) {
|
||||||
add_lemma(lemmas.get(i), level);
|
add_lemma(lemmas.get(i), level, bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pred_transformer::propagate_to_infinity (unsigned level)
|
void pred_transformer::propagate_to_infinity (unsigned level)
|
||||||
{m_frames.propagate_to_infinity (level);}
|
{m_frames.propagate_to_infinity (level);}
|
||||||
|
|
||||||
|
// compute a conjunction of all background facts
|
||||||
|
void pred_transformer::get_pred_bg_invs(expr_ref_vector& out) {
|
||||||
|
expr_ref inv(m), tmp1(m), tmp2(m);
|
||||||
|
ptr_vector<func_decl> preds;
|
||||||
|
for (auto kv : m_pt_rules) {
|
||||||
|
expr* tag = kv.m_value->tag();
|
||||||
|
datalog::rule const &r = kv.m_value->rule();
|
||||||
|
find_predecessors (r, preds);
|
||||||
|
|
||||||
|
for (unsigned i = 0, preds_sz = preds.size(); i < preds_sz; i++) {
|
||||||
|
func_decl* pre = preds[i];
|
||||||
|
pred_transformer &pt = ctx.get_pred_transformer(pre);
|
||||||
|
const lemma_ref_vector &invs = pt.get_bg_invs();
|
||||||
|
CTRACE("spacer", !invs.empty(),
|
||||||
|
tout << "add-bg-invariant: " << mk_pp (pre, m) << "\n";);
|
||||||
|
for (auto inv : invs) {
|
||||||
|
// tag -> inv1 ... tag -> invn
|
||||||
|
tmp1 = m.mk_implies(tag, inv->get_expr());
|
||||||
|
pm.formula_n2o(tmp1, tmp2, i);
|
||||||
|
out.push_back(tmp2);
|
||||||
|
TRACE("spacer", tout << tmp2 << "\n";);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// \brief Returns true if the obligation is already blocked by current lemmas
|
/// \brief Returns true if the obligation is already blocked by current lemmas
|
||||||
|
@ -1344,6 +1376,14 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core,
|
||||||
|
|
||||||
expr_ref_vector post (m), reach_assumps (m);
|
expr_ref_vector post (m), reach_assumps (m);
|
||||||
post.push_back (n.post ());
|
post.push_back (n.post ());
|
||||||
|
flatten_and(post);
|
||||||
|
|
||||||
|
// if equality propagation is disabled in arithmetic, expand
|
||||||
|
// equality literals into two inequalities to increase the space
|
||||||
|
// for interpolation
|
||||||
|
if (!ctx.use_eq_prop()) {
|
||||||
|
expand_literals(m, post);
|
||||||
|
}
|
||||||
|
|
||||||
// populate reach_assumps
|
// populate reach_assumps
|
||||||
if (n.level () > 0 && !m_all_init) {
|
if (n.level () > 0 && !m_all_init) {
|
||||||
|
@ -1472,7 +1512,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
|
||||||
expr_ref lemma_expr(m);
|
expr_ref lemma_expr(m);
|
||||||
lemma_expr = lem->get_expr();
|
lemma_expr = lem->get_expr();
|
||||||
|
|
||||||
expr_ref_vector conj(m), aux(m);
|
expr_ref_vector cand(m), aux(m), conj(m);
|
||||||
expr_ref gnd_lemma(m);
|
expr_ref gnd_lemma(m);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1482,8 +1522,8 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
|
||||||
lemma_expr = gnd_lemma.get();
|
lemma_expr = gnd_lemma.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
conj.push_back(mk_not(m, lemma_expr));
|
cand.push_back(mk_not(m, lemma_expr));
|
||||||
flatten_and (conj);
|
flatten_and (cand);
|
||||||
|
|
||||||
prop_solver::scoped_level _sl(*m_solver, level);
|
prop_solver::scoped_level _sl(*m_solver, level);
|
||||||
prop_solver::scoped_subset_core _sc (*m_solver, true);
|
prop_solver::scoped_subset_core _sc (*m_solver, true);
|
||||||
|
@ -1494,9 +1534,12 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem,
|
||||||
if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;}
|
if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;}
|
||||||
m_solver->set_core(core);
|
m_solver->set_core(core);
|
||||||
m_solver->set_model(mdl_ref_ptr);
|
m_solver->set_model(mdl_ref_ptr);
|
||||||
expr * bg = m_extend_lit.get ();
|
|
||||||
lbool r = m_solver->check_assumptions (conj, aux, m_transition_clause,
|
conj.push_back(m_extend_lit);
|
||||||
1, &bg, 1);
|
if (ctx.use_bg_invs()) get_pred_bg_invs(conj);
|
||||||
|
|
||||||
|
lbool r = m_solver->check_assumptions (cand, aux, m_transition_clause,
|
||||||
|
conj.size(), conj.c_ptr(), 1);
|
||||||
if (r == l_false) {
|
if (r == l_false) {
|
||||||
solver_level = m_solver->uses_level ();
|
solver_level = m_solver->uses_level ();
|
||||||
lem->reset_ctp();
|
lem->reset_ctp();
|
||||||
|
@ -1527,6 +1570,7 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state,
|
||||||
m_solver->set_core(&core);
|
m_solver->set_core(&core);
|
||||||
m_solver->set_model (nullptr);
|
m_solver->set_model (nullptr);
|
||||||
expr_ref_vector aux (m);
|
expr_ref_vector aux (m);
|
||||||
|
if (ctx.use_bg_invs()) get_pred_bg_invs(conj);
|
||||||
conj.push_back (m_extend_lit);
|
conj.push_back (m_extend_lit);
|
||||||
lbool res = m_solver->check_assumptions (state, aux,
|
lbool res = m_solver->check_assumptions (state, aux,
|
||||||
m_transition_clause,
|
m_transition_clause,
|
||||||
|
@ -1941,14 +1985,27 @@ void pred_transformer::update_solver_with_rfs(prop_solver *solver,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pred_transformer::frames
|
/// pred_transformer::frames
|
||||||
|
|
||||||
|
|
||||||
bool pred_transformer::frames::add_lemma(lemma *new_lemma)
|
bool pred_transformer::frames::add_lemma(lemma *new_lemma)
|
||||||
{
|
{
|
||||||
TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " "
|
TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " "
|
||||||
<< m_pt.head()->get_name() << " "
|
<< m_pt.head()->get_name() << " "
|
||||||
<< mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";);
|
<< mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";);
|
||||||
|
|
||||||
|
if (new_lemma->is_background()) {
|
||||||
|
SASSERT (is_infty_level(new_lemma->level()));
|
||||||
|
|
||||||
|
for (auto &l : m_bg_invs) {
|
||||||
|
if (l->get_expr() == new_lemma->get_expr()) return false;
|
||||||
|
}
|
||||||
|
TRACE("spacer", tout << "add-external-lemma: "
|
||||||
|
<< pp_level(new_lemma->level()) << " "
|
||||||
|
<< m_pt.head()->get_name() << " "
|
||||||
|
<< mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";);
|
||||||
|
|
||||||
|
m_bg_invs.push_back(new_lemma);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned i = 0;
|
unsigned i = 0;
|
||||||
for (auto *old_lemma : m_lemmas) {
|
for (auto *old_lemma : m_lemmas) {
|
||||||
if (old_lemma->get_expr() == new_lemma->get_expr()) {
|
if (old_lemma->get_expr() == new_lemma->get_expr()) {
|
||||||
|
@ -2295,6 +2352,7 @@ void context::updt_params() {
|
||||||
m_use_restarts = m_params.spacer_restarts();
|
m_use_restarts = m_params.spacer_restarts();
|
||||||
m_restart_initial_threshold = m_params.spacer_restart_initial_threshold();
|
m_restart_initial_threshold = m_params.spacer_restart_initial_threshold();
|
||||||
m_pdr_bfs = m_params.spacer_gpdr_bfs();
|
m_pdr_bfs = m_params.spacer_gpdr_bfs();
|
||||||
|
m_use_bg_invs = m_params.spacer_use_bg_invs();
|
||||||
|
|
||||||
if (m_use_gpdr) {
|
if (m_use_gpdr) {
|
||||||
// set options to be compatible with GPDR
|
// set options to be compatible with GPDR
|
||||||
|
@ -2423,36 +2481,38 @@ expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p)
|
||||||
if (m_rels.find(p, pt)) {
|
if (m_rels.find(p, pt)) {
|
||||||
return pt->get_cover_delta(p_orig, level);
|
return pt->get_cover_delta(p_orig, level);
|
||||||
} else {
|
} else {
|
||||||
IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";);
|
IF_VERBOSE(10, verbose_stream() << "did not find predicate "
|
||||||
|
<< p->get_name() << "\n";);
|
||||||
return expr_ref(m.mk_true(), m);
|
return expr_ref(m.mk_true(), m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::add_cover(int level, func_decl* p, expr* property)
|
void context::add_cover(int level, func_decl* p, expr* property, bool bg)
|
||||||
{
|
{
|
||||||
|
scoped_proof _pf_(m);
|
||||||
|
|
||||||
pred_transformer* pt = nullptr;
|
pred_transformer* pt = nullptr;
|
||||||
if (!m_rels.find(p, pt)) {
|
if (!m_rels.find(p, pt)) {
|
||||||
pt = alloc(pred_transformer, *this, get_manager(), p);
|
pt = alloc(pred_transformer, *this, get_manager(), p);
|
||||||
m_rels.insert(p, pt);
|
m_rels.insert(p, pt);
|
||||||
IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";);
|
IF_VERBOSE(10, verbose_stream() << "did not find predicate "
|
||||||
|
<< p->get_name() << "\n";);
|
||||||
}
|
}
|
||||||
unsigned lvl = (level == -1)?infty_level():((unsigned)level);
|
unsigned lvl = (level == -1)?infty_level():((unsigned)level);
|
||||||
pt->add_cover(lvl, property);
|
pt->add_cover(lvl, property, bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::add_invariant (func_decl *p, expr *property)
|
void context::add_invariant (func_decl *p, expr *property)
|
||||||
{add_cover (infty_level(), p, property);}
|
{add_cover (infty_level(), p, property, true);}
|
||||||
|
|
||||||
expr_ref context::get_reachable(func_decl *p)
|
expr_ref context::get_reachable(func_decl *p) {
|
||||||
{
|
|
||||||
pred_transformer* pt = nullptr;
|
pred_transformer* pt = nullptr;
|
||||||
if (!m_rels.find(p, pt))
|
if (!m_rels.find(p, pt))
|
||||||
{ return expr_ref(m.mk_false(), m); }
|
{ return expr_ref(m.mk_false(), m); }
|
||||||
return pt->get_reachable();
|
return pt->get_reachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool context::validate()
|
bool context::validate() {
|
||||||
{
|
|
||||||
if (!m_validate_result) { return true; }
|
if (!m_validate_result) { return true; }
|
||||||
|
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
|
@ -2483,7 +2543,7 @@ bool context::validate()
|
||||||
model_ref model;
|
model_ref model;
|
||||||
vector<relation_info> rs;
|
vector<relation_info> rs;
|
||||||
model_converter_ref mc;
|
model_converter_ref mc;
|
||||||
get_level_property(m_inductive_lvl, refs, rs);
|
get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
|
||||||
inductive_property ex(m, mc, rs);
|
inductive_property ex(m, mc, rs);
|
||||||
ex.to_model(model);
|
ex.to_model(model);
|
||||||
var_subst vs(m, false);
|
var_subst vs(m, false);
|
||||||
|
@ -2624,13 +2684,13 @@ void context::init_lemma_generalizers()
|
||||||
}
|
}
|
||||||
|
|
||||||
void context::get_level_property(unsigned lvl, expr_ref_vector& res,
|
void context::get_level_property(unsigned lvl, expr_ref_vector& res,
|
||||||
vector<relation_info>& rs) const {
|
vector<relation_info>& rs, bool with_bg) const {
|
||||||
for (auto const& kv : m_rels) {
|
for (auto const& kv : m_rels) {
|
||||||
pred_transformer* r = kv.m_value;
|
pred_transformer* r = kv.m_value;
|
||||||
if (r->head() == m_query_pred) {
|
if (r->head() == m_query_pred) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
expr_ref conj = r->get_formulas(lvl);
|
expr_ref conj = r->get_formulas(lvl, with_bg);
|
||||||
m_pm.formula_n2o(0, false, conj);
|
m_pm.formula_n2o(0, false, conj);
|
||||||
res.push_back(conj);
|
res.push_back(conj);
|
||||||
ptr_vector<func_decl> sig(r->head()->get_arity(), r->sig());
|
ptr_vector<func_decl> sig(r->head()->get_arity(), r->sig());
|
||||||
|
@ -2662,7 +2722,7 @@ lbool context::solve(unsigned from_lvl)
|
||||||
IF_VERBOSE(1, {
|
IF_VERBOSE(1, {
|
||||||
expr_ref_vector refs(m);
|
expr_ref_vector refs(m);
|
||||||
vector<relation_info> rs;
|
vector<relation_info> rs;
|
||||||
get_level_property(m_inductive_lvl, refs, rs);
|
get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
|
||||||
model_converter_ref mc;
|
model_converter_ref mc;
|
||||||
inductive_property ex(m, mc, rs);
|
inductive_property ex(m, mc, rs);
|
||||||
verbose_stream() << ex.to_string();
|
verbose_stream() << ex.to_string();
|
||||||
|
@ -2844,7 +2904,7 @@ model_ref context::get_model()
|
||||||
model_ref model;
|
model_ref model;
|
||||||
expr_ref_vector refs(m);
|
expr_ref_vector refs(m);
|
||||||
vector<relation_info> rs;
|
vector<relation_info> rs;
|
||||||
get_level_property(m_inductive_lvl, refs, rs);
|
get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
|
||||||
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
|
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
|
||||||
ex.to_model (model);
|
ex.to_model (model);
|
||||||
return model;
|
return model;
|
||||||
|
@ -2877,7 +2937,7 @@ expr_ref context::mk_unsat_answer() const
|
||||||
{
|
{
|
||||||
expr_ref_vector refs(m);
|
expr_ref_vector refs(m);
|
||||||
vector<relation_info> rs;
|
vector<relation_info> rs;
|
||||||
get_level_property(m_inductive_lvl, refs, rs);
|
get_level_property(m_inductive_lvl, refs, rs, use_bg_invs());
|
||||||
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
|
inductive_property ex(m, const_cast<model_converter_ref&>(m_mc), rs);
|
||||||
return ex.to_expr();
|
return ex.to_expr();
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,8 +128,9 @@ class lemma {
|
||||||
unsigned m_init_lvl; // level at which lemma was created
|
unsigned m_init_lvl; // level at which lemma was created
|
||||||
unsigned m_bumped:16;
|
unsigned m_bumped:16;
|
||||||
unsigned m_weakness:16;
|
unsigned m_weakness:16;
|
||||||
unsigned m_external:1;
|
unsigned m_external:1; // external lemma from another solver
|
||||||
unsigned m_blocked:1;
|
unsigned m_blocked:1; // blocked by CTP
|
||||||
|
unsigned m_background:1; // background assumed fact
|
||||||
|
|
||||||
void mk_expr_core();
|
void mk_expr_core();
|
||||||
void mk_cube_core();
|
void mk_cube_core();
|
||||||
|
@ -163,6 +164,9 @@ public:
|
||||||
void set_external(bool ext){m_external = ext;}
|
void set_external(bool ext){m_external = ext;}
|
||||||
bool external() { return m_external;}
|
bool external() { return m_external;}
|
||||||
|
|
||||||
|
void set_background(bool v) {m_background = v;}
|
||||||
|
bool is_background() {return m_background;}
|
||||||
|
|
||||||
bool is_blocked() {return m_blocked;}
|
bool is_blocked() {return m_blocked;}
|
||||||
void set_blocked(bool v) {m_blocked=v;}
|
void set_blocked(bool v) {m_blocked=v;}
|
||||||
|
|
||||||
|
@ -222,6 +226,7 @@ class pred_transformer {
|
||||||
pred_transformer &m_pt; // parent pred_transformer
|
pred_transformer &m_pt; // parent pred_transformer
|
||||||
lemma_ref_vector m_pinned_lemmas; // all created lemmas
|
lemma_ref_vector m_pinned_lemmas; // all created lemmas
|
||||||
lemma_ref_vector m_lemmas; // active lemmas
|
lemma_ref_vector m_lemmas; // active lemmas
|
||||||
|
lemma_ref_vector m_bg_invs; // background (assumed) invariants
|
||||||
unsigned m_size; // num of frames
|
unsigned m_size; // num of frames
|
||||||
|
|
||||||
bool m_sorted; // true if m_lemmas is sorted by m_lt
|
bool m_sorted; // true if m_lemmas is sorted by m_lt
|
||||||
|
@ -230,7 +235,8 @@ class pred_transformer {
|
||||||
void sort ();
|
void sort ();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {}
|
frames (pred_transformer &pt) : m_pt (pt),
|
||||||
|
m_size(0), m_sorted (true) {}
|
||||||
~frames() {}
|
~frames() {}
|
||||||
void simplify_formulas ();
|
void simplify_formulas ();
|
||||||
|
|
||||||
|
@ -245,17 +251,25 @@ class pred_transformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const {
|
void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out,
|
||||||
|
bool with_bg = false) const {
|
||||||
for (auto &lemma : m_lemmas) {
|
for (auto &lemma : m_lemmas) {
|
||||||
if (lemma->level() >= level) {
|
if (lemma->level() >= level) {
|
||||||
out.push_back(lemma->get_expr());
|
out.push_back(lemma->get_expr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (with_bg) {
|
||||||
|
for (auto &lemma : m_bg_invs)
|
||||||
|
out.push_back(lemma->get_expr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned size () const {return m_size;}
|
const lemma_ref_vector& get_bg_invs() const {return m_bg_invs;}
|
||||||
unsigned lemma_size () const {return m_lemmas.size ();}
|
unsigned size() const {return m_size;}
|
||||||
void add_frame () {m_size++;}
|
unsigned lemma_size() const {return m_lemmas.size ();}
|
||||||
|
unsigned bg_invs_size() const {return m_bg_invs.size();}
|
||||||
|
|
||||||
|
void add_frame() {m_size++;}
|
||||||
void inherit_frames (frames &other) {
|
void inherit_frames (frames &other) {
|
||||||
for (auto &other_lemma : other.m_lemmas) {
|
for (auto &other_lemma : other.m_lemmas) {
|
||||||
lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(),
|
lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(),
|
||||||
|
@ -265,6 +279,7 @@ class pred_transformer {
|
||||||
add_lemma(new_lemma.get());
|
add_lemma(new_lemma.get());
|
||||||
}
|
}
|
||||||
m_sorted = false;
|
m_sorted = false;
|
||||||
|
m_bg_invs.append(other.m_bg_invs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add_lemma (lemma *new_lemma);
|
bool add_lemma (lemma *new_lemma);
|
||||||
|
@ -418,6 +433,11 @@ class pred_transformer {
|
||||||
|
|
||||||
app_ref mk_fresh_rf_tag ();
|
app_ref mk_fresh_rf_tag ();
|
||||||
|
|
||||||
|
// get tagged formulae of all of the background invariants for all of the
|
||||||
|
// predecessors of the current transformer
|
||||||
|
void get_pred_bg_invs(expr_ref_vector &out);
|
||||||
|
const lemma_ref_vector &get_bg_invs() const {return m_frames.get_bg_invs();}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
pred_transformer(context& ctx, manager& pm, func_decl* head);
|
pred_transformer(context& ctx, manager& pm, func_decl* head);
|
||||||
~pred_transformer() {}
|
~pred_transformer() {}
|
||||||
|
@ -448,7 +468,7 @@ public:
|
||||||
}
|
}
|
||||||
unsigned get_num_levels() const {return m_frames.size ();}
|
unsigned get_num_levels() const {return m_frames.size ();}
|
||||||
expr_ref get_cover_delta(func_decl* p_orig, int level);
|
expr_ref get_cover_delta(func_decl* p_orig, int level);
|
||||||
void add_cover(unsigned level, expr* property);
|
void add_cover(unsigned level, expr* property, bool bg = false);
|
||||||
expr_ref get_reachable();
|
expr_ref get_reachable();
|
||||||
|
|
||||||
std::ostream& display(std::ostream& strm) const;
|
std::ostream& display(std::ostream& strm) const;
|
||||||
|
@ -484,7 +504,7 @@ public:
|
||||||
bool propagate_to_next_level(unsigned level);
|
bool propagate_to_next_level(unsigned level);
|
||||||
void propagate_to_infinity(unsigned level);
|
void propagate_to_infinity(unsigned level);
|
||||||
/// \brief Add a lemma to the current context and all users
|
/// \brief Add a lemma to the current context and all users
|
||||||
bool add_lemma(expr * lemma, unsigned lvl);
|
bool add_lemma(expr * e, unsigned lvl, bool bg);
|
||||||
bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);}
|
bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);}
|
||||||
expr* get_reach_case_var (unsigned idx) const;
|
expr* get_reach_case_var (unsigned idx) const;
|
||||||
bool has_rfs () const { return !m_reach_facts.empty () ;}
|
bool has_rfs () const { return !m_reach_facts.empty () ;}
|
||||||
|
@ -527,7 +547,7 @@ public:
|
||||||
bool check_inductive(unsigned level, expr_ref_vector& state,
|
bool check_inductive(unsigned level, expr_ref_vector& state,
|
||||||
unsigned& assumes_level, unsigned weakness = UINT_MAX);
|
unsigned& assumes_level, unsigned weakness = UINT_MAX);
|
||||||
|
|
||||||
expr_ref get_formulas(unsigned level) const;
|
expr_ref get_formulas(unsigned level, bool bg = false) const;
|
||||||
|
|
||||||
void simplify_formulas();
|
void simplify_formulas();
|
||||||
|
|
||||||
|
@ -958,6 +978,7 @@ class context {
|
||||||
bool m_simplify_formulas_pre;
|
bool m_simplify_formulas_pre;
|
||||||
bool m_simplify_formulas_post;
|
bool m_simplify_formulas_post;
|
||||||
bool m_pdr_bfs;
|
bool m_pdr_bfs;
|
||||||
|
bool m_use_bg_invs;
|
||||||
unsigned m_push_pob_max_depth;
|
unsigned m_push_pob_max_depth;
|
||||||
unsigned m_max_level;
|
unsigned m_max_level;
|
||||||
unsigned m_restart_initial_threshold;
|
unsigned m_restart_initial_threshold;
|
||||||
|
@ -992,7 +1013,8 @@ class context {
|
||||||
|
|
||||||
// Generate inductive property
|
// Generate inductive property
|
||||||
void get_level_property(unsigned lvl, expr_ref_vector& res,
|
void get_level_property(unsigned lvl, expr_ref_vector& res,
|
||||||
vector<relation_info> & rs) const;
|
vector<relation_info> & rs,
|
||||||
|
bool with_bg = false) const;
|
||||||
|
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
|
@ -1027,18 +1049,20 @@ public:
|
||||||
|
|
||||||
|
|
||||||
const fp_params &get_params() const { return m_params; }
|
const fp_params &get_params() const { return m_params; }
|
||||||
bool use_native_mbp () {return m_use_native_mbp;}
|
bool use_eq_prop() const {return m_use_eq_prop;}
|
||||||
bool use_ground_pob () {return m_ground_pob;}
|
bool use_native_mbp() const {return m_use_native_mbp;}
|
||||||
bool use_instantiate () {return m_instantiate;}
|
bool use_ground_pob() const {return m_ground_pob;}
|
||||||
bool weak_abs() {return m_weak_abs;}
|
bool use_instantiate() const {return m_instantiate;}
|
||||||
bool use_qlemmas () {return m_use_qlemmas;}
|
bool weak_abs() const {return m_weak_abs;}
|
||||||
bool use_euf_gen() {return m_use_euf_gen;}
|
bool use_qlemmas() const {return m_use_qlemmas;}
|
||||||
bool simplify_pob() {return m_simplify_pob;}
|
bool use_euf_gen() const {return m_use_euf_gen;}
|
||||||
bool use_ctp() {return m_use_ctp;}
|
bool simplify_pob() const {return m_simplify_pob;}
|
||||||
bool use_inc_clause() {return m_use_inc_clause;}
|
bool use_ctp() const {return m_use_ctp;}
|
||||||
unsigned blast_term_ite_inflation() {return m_blast_term_ite_inflation;}
|
bool use_inc_clause() const {return m_use_inc_clause;}
|
||||||
bool elim_aux() {return m_elim_aux;}
|
unsigned blast_term_ite_inflation() const {return m_blast_term_ite_inflation;}
|
||||||
bool reach_dnf() {return m_reach_dnf;}
|
bool elim_aux() const {return m_elim_aux;}
|
||||||
|
bool reach_dnf() const {return m_reach_dnf;}
|
||||||
|
bool use_bg_invs() const {return m_use_bg_invs;}
|
||||||
|
|
||||||
ast_manager& get_ast_manager() const {return m;}
|
ast_manager& get_ast_manager() const {return m;}
|
||||||
manager& get_manager() {return m_pm;}
|
manager& get_manager() {return m_pm;}
|
||||||
|
@ -1081,7 +1105,7 @@ public:
|
||||||
unsigned get_num_levels(func_decl* p);
|
unsigned get_num_levels(func_decl* p);
|
||||||
|
|
||||||
expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p);
|
expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p);
|
||||||
void add_cover(int level, func_decl* pred, expr* property);
|
void add_cover(int level, func_decl* pred, expr* property, bool bg = false);
|
||||||
expr_ref get_reachable (func_decl* p);
|
expr_ref get_reachable (func_decl* p);
|
||||||
void add_invariant (func_decl *pred, expr* property);
|
void add_invariant (func_decl *pred, expr* property);
|
||||||
model_ref get_model();
|
model_ref get_model();
|
||||||
|
|
|
@ -94,6 +94,7 @@ void prop_solver::add_level()
|
||||||
|
|
||||||
void prop_solver::ensure_level(unsigned lvl)
|
void prop_solver::ensure_level(unsigned lvl)
|
||||||
{
|
{
|
||||||
|
if (is_infty_level(lvl)) return;
|
||||||
while (lvl >= level_cnt()) {
|
while (lvl >= level_cnt()) {
|
||||||
add_level();
|
add_level();
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ namespace spacer {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_infty_level(unsigned lvl) {
|
inline bool is_infty_level(unsigned lvl) {
|
||||||
return lvl == infty_level ();
|
// XXX: level is 16 bits in class pob
|
||||||
|
return lvl >= 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned next_level(unsigned lvl) {
|
inline unsigned next_level(unsigned lvl) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ z3_add_component(transforms
|
||||||
dl_mk_array_eq_rewrite.cpp
|
dl_mk_array_eq_rewrite.cpp
|
||||||
dl_mk_array_instantiation.cpp
|
dl_mk_array_instantiation.cpp
|
||||||
dl_mk_elim_term_ite.cpp
|
dl_mk_elim_term_ite.cpp
|
||||||
|
dl_mk_synchronize.cpp
|
||||||
COMPONENT_DEPENDENCIES
|
COMPONENT_DEPENDENCIES
|
||||||
dataflow
|
dataflow
|
||||||
hilbert
|
hilbert
|
||||||
|
|
376
src/muz/transforms/dl_mk_synchronize.cpp
Normal file
376
src/muz/transforms/dl_mk_synchronize.cpp
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2017-2018 Saint-Petersburg State University
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
dl_mk_synchronize.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Rule transformer that attempts to merge recursive iterations
|
||||||
|
relaxing the shape of the inductive invariant.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Dmitry Mordvinov (dvvrd) 2017-05-24
|
||||||
|
Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "muz/transforms/dl_mk_synchronize.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace datalog {
|
||||||
|
|
||||||
|
typedef mk_synchronize::item_set_vector item_set_vector;
|
||||||
|
|
||||||
|
mk_synchronize::mk_synchronize(context& ctx, unsigned priority):
|
||||||
|
rule_transformer::plugin(priority, false),
|
||||||
|
m_ctx(ctx),
|
||||||
|
m(ctx.get_manager()),
|
||||||
|
rm(ctx.get_rule_manager())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool mk_synchronize::is_recursive(rule &r, func_decl &decl) const {
|
||||||
|
func_decl *hdecl = r.get_head()->get_decl();
|
||||||
|
// AG: shouldn't decl appear in the body?
|
||||||
|
if (hdecl == &decl) return true;
|
||||||
|
auto & strata = m_stratifier->get_strats();
|
||||||
|
unsigned num_of_stratum = m_stratifier->get_predicate_strat(hdecl);
|
||||||
|
return strata[num_of_stratum]->contains(&decl);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mk_synchronize::has_recursive_premise(app * app) const {
|
||||||
|
func_decl* app_decl = app->get_decl();
|
||||||
|
if (m_deps->get_deps(app_decl).contains(app_decl)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
rule_stratifier::comp_vector const & strata = m_stratifier->get_strats();
|
||||||
|
unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decl);
|
||||||
|
return strata[num_of_stratum]->size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
item_set_vector mk_synchronize::add_merged_decls(ptr_vector<app> & apps) {
|
||||||
|
unsigned sz = apps.size();
|
||||||
|
item_set_vector merged_decls;
|
||||||
|
merged_decls.resize(sz);
|
||||||
|
auto & strata = m_stratifier->get_strats();
|
||||||
|
for (unsigned j = 0; j < sz; ++j) {
|
||||||
|
unsigned nos;
|
||||||
|
nos = m_stratifier->get_predicate_strat(apps[j]->get_decl());
|
||||||
|
merged_decls[j] = strata[nos];
|
||||||
|
}
|
||||||
|
return merged_decls;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::add_new_rel_symbols(unsigned idx,
|
||||||
|
item_set_vector const & decls,
|
||||||
|
ptr_vector<func_decl> & decls_buf,
|
||||||
|
bool & was_added) {
|
||||||
|
if (idx >= decls.size()) {
|
||||||
|
string_buffer<> buffer;
|
||||||
|
ptr_vector<sort> domain;
|
||||||
|
for (auto &d : decls_buf) {
|
||||||
|
buffer << d->get_name() << "!!";
|
||||||
|
domain.append(d->get_arity(), d->get_domain());
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol new_name = symbol(buffer.c_str());
|
||||||
|
|
||||||
|
if (!m_cache.contains(new_name)) {
|
||||||
|
was_added = true;
|
||||||
|
func_decl* orig = decls_buf[0];
|
||||||
|
func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name,
|
||||||
|
symbol::null, domain.size(), domain.c_ptr(), orig);
|
||||||
|
m_cache.insert(new_name, product_pred);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- compute Cartesian product of decls, and create a new
|
||||||
|
// -- predicate for each element of the product
|
||||||
|
for (auto &p : *decls[idx]) {
|
||||||
|
decls_buf[idx] = p;
|
||||||
|
add_new_rel_symbols(idx + 1, decls, decls_buf, was_added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::replace_applications(rule & r, rule_set & rules,
|
||||||
|
ptr_vector<app> & apps) {
|
||||||
|
app_ref replacing = product_application(apps);
|
||||||
|
|
||||||
|
ptr_vector<app> new_tail;
|
||||||
|
svector<bool> new_tail_neg;
|
||||||
|
unsigned n = r.get_tail_size() - apps.size() + 1;
|
||||||
|
unsigned tail_idx = 0;
|
||||||
|
new_tail.resize(n);
|
||||||
|
new_tail_neg.resize(n);
|
||||||
|
new_tail[0] = replacing;
|
||||||
|
new_tail_neg[0] = false;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) {
|
||||||
|
app* tail = r.get_tail(i);
|
||||||
|
if (!apps.contains(tail)) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = tail;
|
||||||
|
new_tail_neg[tail_idx] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = r.get_tail(i);
|
||||||
|
new_tail_neg[tail_idx] = true;
|
||||||
|
}
|
||||||
|
for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = r.get_tail(i);
|
||||||
|
new_tail_neg[tail_idx] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_ref new_rule(rm);
|
||||||
|
new_rule = rm.mk(r.get_head(), tail_idx + 1,
|
||||||
|
new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol::null, false);
|
||||||
|
rules.replace_rule(&r, new_rule.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r,
|
||||||
|
unsigned & var_idx) {
|
||||||
|
// AG: shift all variables in a rule so that lowest var index is var_idx?
|
||||||
|
// AG: update var_idx in the process?
|
||||||
|
ptr_vector<sort> sorts;
|
||||||
|
r->get_vars(m, sorts);
|
||||||
|
expr_ref_vector revsub(m);
|
||||||
|
revsub.resize(sorts.size());
|
||||||
|
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||||
|
if (sorts[i]) {
|
||||||
|
revsub[i] = m.mk_var(var_idx++, sorts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_ref new_rule(rm);
|
||||||
|
new_rule = rm.mk(r);
|
||||||
|
rm.substitute(new_rule, revsub.size(), revsub.c_ptr());
|
||||||
|
return new_rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<rule_ref_vector> mk_synchronize::rename_bound_vars(item_set_vector const & heads,
|
||||||
|
rule_set & rules) {
|
||||||
|
// AG: is every item_set in heads corresponds to rules that are merged?
|
||||||
|
// AG: why are bound variables renamed in the first place?
|
||||||
|
// AG: the data structure seems too complex
|
||||||
|
vector<rule_ref_vector> result;
|
||||||
|
unsigned var_idx = 0;
|
||||||
|
for (auto item : heads) {
|
||||||
|
rule_ref_vector dst_vector(rm);
|
||||||
|
for (auto *head : *item) {
|
||||||
|
for (auto *r : rules.get_predicate_rules(head)) {
|
||||||
|
rule_ref new_rule = rename_bound_vars_in_rule(r, var_idx);
|
||||||
|
dst_vector.push_back(new_rule.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_back(dst_vector);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::add_rec_tail(vector< ptr_vector<app> > & recursive_calls,
|
||||||
|
app_ref_vector & new_tail,
|
||||||
|
svector<bool> & new_tail_neg,
|
||||||
|
unsigned & tail_idx) {
|
||||||
|
unsigned max_sz = 0;
|
||||||
|
for (auto &rc : recursive_calls)
|
||||||
|
max_sz= std::max(rc.size(), max_sz);
|
||||||
|
|
||||||
|
unsigned n = recursive_calls.size();
|
||||||
|
ptr_vector<app> merged_recursive_calls;
|
||||||
|
|
||||||
|
for (unsigned j = 0; j < max_sz; ++j) {
|
||||||
|
merged_recursive_calls.reset();
|
||||||
|
merged_recursive_calls.resize(n);
|
||||||
|
for (unsigned i = 0; i < n; ++i) {
|
||||||
|
unsigned sz = recursive_calls[i].size();
|
||||||
|
merged_recursive_calls[i] =
|
||||||
|
j < sz ? recursive_calls[i][j] : recursive_calls[i][sz - 1];
|
||||||
|
}
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = product_application(merged_recursive_calls);
|
||||||
|
new_tail_neg[tail_idx] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::add_non_rec_tail(rule & r, app_ref_vector & new_tail,
|
||||||
|
svector<bool> & new_tail_neg,
|
||||||
|
unsigned & tail_idx) {
|
||||||
|
for (unsigned i = 0, sz = r.get_positive_tail_size(); i < sz; ++i) {
|
||||||
|
app* tail = r.get_tail(i);
|
||||||
|
if (!is_recursive(r, *tail)) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = tail;
|
||||||
|
new_tail_neg[tail_idx] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = r.get_positive_tail_size(),
|
||||||
|
sz = r.get_uninterpreted_tail_size() ; i < sz; ++i) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = r.get_tail(i);
|
||||||
|
new_tail_neg[tail_idx] = true;
|
||||||
|
}
|
||||||
|
for (unsigned i = r.get_uninterpreted_tail_size(),
|
||||||
|
sz = r.get_tail_size(); i < sz; ++i) {
|
||||||
|
++tail_idx;
|
||||||
|
new_tail[tail_idx] = r.get_tail(i);
|
||||||
|
new_tail_neg[tail_idx] = r.is_neg_tail(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app_ref mk_synchronize::product_application(ptr_vector<app> const &apps) {
|
||||||
|
unsigned args_num = 0;
|
||||||
|
string_buffer<> buffer;
|
||||||
|
|
||||||
|
// AG: factor out into mk_name
|
||||||
|
for (auto *app : apps) {
|
||||||
|
buffer << app->get_decl()->get_name() << "!!";
|
||||||
|
args_num += app->get_num_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol name = symbol(buffer.c_str());
|
||||||
|
SASSERT(m_cache.contains(name));
|
||||||
|
func_decl * pred = m_cache[name];
|
||||||
|
|
||||||
|
ptr_vector<expr> args;
|
||||||
|
args.resize(args_num);
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (auto *a : apps) {
|
||||||
|
for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i, ++idx)
|
||||||
|
args[idx] = a->get_arg(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return app_ref(m.mk_app(pred, args_num, args.c_ptr()), m);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_ref mk_synchronize::product_rule(rule_ref_vector const & rules) {
|
||||||
|
unsigned n = rules.size();
|
||||||
|
|
||||||
|
string_buffer<> buffer;
|
||||||
|
bool first_rule = true;
|
||||||
|
for (auto it = rules.begin(); it != rules.end(); ++it, first_rule = false) {
|
||||||
|
if (!first_rule) {
|
||||||
|
buffer << "+";
|
||||||
|
}
|
||||||
|
buffer << (*it)->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_vector<app> heads;
|
||||||
|
heads.resize(n);
|
||||||
|
for (unsigned i = 0; i < n; ++i) {
|
||||||
|
heads[i] = rules[i]->get_head();
|
||||||
|
}
|
||||||
|
app_ref product_head = product_application(heads);
|
||||||
|
unsigned product_tail_length = 0;
|
||||||
|
bool has_recursion = false;
|
||||||
|
vector< ptr_vector<app> > recursive_calls;
|
||||||
|
recursive_calls.resize(n);
|
||||||
|
for (unsigned i = 0; i < n; ++i) {
|
||||||
|
rule& rule = *rules[i];
|
||||||
|
product_tail_length += rule.get_tail_size();
|
||||||
|
for (unsigned j = 0; j < rule.get_positive_tail_size(); ++j) {
|
||||||
|
app* tail = rule.get_tail(j);
|
||||||
|
if (is_recursive(rule, *tail)) {
|
||||||
|
has_recursion = true;
|
||||||
|
recursive_calls[i].push_back(tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (recursive_calls[i].empty()) {
|
||||||
|
recursive_calls[i].push_back(rule.get_head());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app_ref_vector new_tail(m);
|
||||||
|
svector<bool> new_tail_neg;
|
||||||
|
new_tail.resize(product_tail_length);
|
||||||
|
new_tail_neg.resize(product_tail_length);
|
||||||
|
unsigned tail_idx = -1;
|
||||||
|
if (has_recursion) {
|
||||||
|
add_rec_tail(recursive_calls, new_tail, new_tail_neg, tail_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rule_vector::const_iterator it = rules.begin(); it != rules.end(); ++it) {
|
||||||
|
rule& rule = **it;
|
||||||
|
add_non_rec_tail(rule, new_tail, new_tail_neg, tail_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_ref new_rule(rm);
|
||||||
|
new_rule = rm.mk(product_head, tail_idx + 1,
|
||||||
|
new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol(buffer.c_str()), false);
|
||||||
|
rm.fix_unbound_vars(new_rule, false);
|
||||||
|
return new_rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf,
|
||||||
|
vector<rule_ref_vector> const & merged_rules,
|
||||||
|
rule_set & all_rules) {
|
||||||
|
if (idx >= merged_rules.size()) {
|
||||||
|
rule_ref product = product_rule(buf);
|
||||||
|
all_rules.add_rule(product.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto *r : merged_rules[idx]) {
|
||||||
|
buf[idx] = r;
|
||||||
|
merge_rules(idx + 1, buf, merged_rules, all_rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mk_synchronize::merge_applications(rule & r, rule_set & rules) {
|
||||||
|
ptr_vector<app> non_recursive_preds;
|
||||||
|
obj_hashtable<app> apps;
|
||||||
|
for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) {
|
||||||
|
app* t = r.get_tail(i);
|
||||||
|
if (!is_recursive(r, *t) && has_recursive_premise(t)) {
|
||||||
|
apps.insert(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (apps.size() < 2) return;
|
||||||
|
for (auto *a : apps) non_recursive_preds.push_back(a);
|
||||||
|
|
||||||
|
item_set_vector merged_decls = add_merged_decls(non_recursive_preds);
|
||||||
|
|
||||||
|
unsigned n = non_recursive_preds.size();
|
||||||
|
ptr_vector<func_decl> decls_buf;
|
||||||
|
decls_buf.resize(n);
|
||||||
|
bool was_added = false;
|
||||||
|
add_new_rel_symbols(0, merged_decls, decls_buf, was_added);
|
||||||
|
if (was_added){
|
||||||
|
rule_ref_vector rules_buf(rm);
|
||||||
|
rules_buf.resize(n);
|
||||||
|
vector<rule_ref_vector> renamed_rules = rename_bound_vars(merged_decls, rules);
|
||||||
|
merge_rules(0, rules_buf, renamed_rules, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_applications(r, rules, non_recursive_preds);
|
||||||
|
m_deps->populate(rules);
|
||||||
|
m_stratifier = alloc(rule_stratifier, *m_deps);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule_set * mk_synchronize::operator()(rule_set const & source) {
|
||||||
|
rule_set* rules = alloc(rule_set, m_ctx);
|
||||||
|
rules->inherit_predicates(source);
|
||||||
|
|
||||||
|
for (auto *r : source) { rules->add_rule(r); }
|
||||||
|
|
||||||
|
m_deps = alloc(rule_dependencies, m_ctx);
|
||||||
|
m_deps->populate(*rules);
|
||||||
|
m_stratifier = alloc(rule_stratifier, *m_deps);
|
||||||
|
|
||||||
|
unsigned current_rule = 0;
|
||||||
|
while (current_rule < rules->get_num_rules()) {
|
||||||
|
rule *r = rules->get_rule(current_rule);
|
||||||
|
merge_applications(*r, *rules);
|
||||||
|
++current_rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
134
src/muz/transforms/dl_mk_synchronize.h
Normal file
134
src/muz/transforms/dl_mk_synchronize.h
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2017-2018 Saint-Petersburg State University
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
dl_mk_synchronize.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Rule transformer that attempts to merge recursive iterations
|
||||||
|
relaxing the shape of the inductive invariant.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
Q(z) :- A(x), B(y), phi1(x,y,z).
|
||||||
|
A(x) :- A(x'), phi2(x,x').
|
||||||
|
A(x) :- phi3(x).
|
||||||
|
B(y) :- C(y'), phi4(y,y').
|
||||||
|
C(y) :- B(y'), phi5(y,y').
|
||||||
|
B(y) :- phi6(y).
|
||||||
|
|
||||||
|
Transformed clauses:
|
||||||
|
|
||||||
|
Q(z) :- AB(x,y), phi1(x,y,z).
|
||||||
|
AB(x,y) :- AC(x',y'), phi2(x,x'), phi4(y,y').
|
||||||
|
AC(x,y) :- AB(x',y'), phi2(x,x'), phi5(y,y').
|
||||||
|
AB(x,y) :- AC(x, y'), phi3(x), phi4(y,y').
|
||||||
|
AC(x,y) :- AB(x, y'), phi3(x), phi5(y,y').
|
||||||
|
AB(x,y) :- AB(x',y), phi2(x,x'), phi6(y).
|
||||||
|
AB(x,y) :- phi3(x), phi6(y).
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Dmitry Mordvinov (dvvrd) 2017-05-24
|
||||||
|
Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#ifndef DL_MK_SYNCHRONIZE_H_
|
||||||
|
#define DL_MK_SYNCHRONIZE_H_
|
||||||
|
|
||||||
|
#include"muz/base/dl_context.h"
|
||||||
|
#include"muz/base/dl_rule_set.h"
|
||||||
|
#include"util/uint_set.h"
|
||||||
|
#include"muz/base/dl_rule_transformer.h"
|
||||||
|
#include"muz/transforms/dl_mk_rule_inliner.h"
|
||||||
|
|
||||||
|
namespace datalog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Implements a synchronous product transformation.
|
||||||
|
*/
|
||||||
|
class mk_synchronize : public rule_transformer::plugin {
|
||||||
|
public:
|
||||||
|
typedef ptr_vector<rule_stratifier::item_set> item_set_vector;
|
||||||
|
private:
|
||||||
|
context& m_ctx;
|
||||||
|
ast_manager& m;
|
||||||
|
rule_manager& rm;
|
||||||
|
|
||||||
|
scoped_ptr<rule_dependencies> m_deps;
|
||||||
|
scoped_ptr<rule_stratifier> m_stratifier;
|
||||||
|
|
||||||
|
// symbol table that maps new predicate names to corresponding
|
||||||
|
// func_decl
|
||||||
|
map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> m_cache;
|
||||||
|
|
||||||
|
/// returns true if decl is recursive in the given rule
|
||||||
|
/// requires that decl appears in the tail of r
|
||||||
|
bool is_recursive(rule &r, func_decl &decl) const;
|
||||||
|
bool is_recursive(rule &r, expr &e) const {
|
||||||
|
SASSERT(is_app(&e));
|
||||||
|
return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns true if the top-level predicate of app is recursive
|
||||||
|
bool has_recursive_premise(app * app) const;
|
||||||
|
|
||||||
|
item_set_vector add_merged_decls(ptr_vector<app> & apps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Compute Cartesian product of decls and create a new
|
||||||
|
predicate for each element. For example, if decls is
|
||||||
|
|
||||||
|
( (a, b), (c, d) ) )
|
||||||
|
|
||||||
|
create new predicates: a!!c, a!!d, b!!c, and b!!d
|
||||||
|
*/
|
||||||
|
void add_new_rel_symbols(unsigned idx, item_set_vector const & decls,
|
||||||
|
ptr_vector<func_decl> & buf, bool & was_added);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert vector of predicate apps into a single app. For example,
|
||||||
|
(Foo a) (Bar b) becomes (Foo!!Bar a b)
|
||||||
|
*/
|
||||||
|
app_ref product_application(ptr_vector<app> const & apps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Replace a given rule by a rule in which conjunction of
|
||||||
|
predicates is replaced by a single product predicate
|
||||||
|
*/
|
||||||
|
void replace_applications(rule & r, rule_set & rules,
|
||||||
|
ptr_vector<app> & apps);
|
||||||
|
|
||||||
|
rule_ref rename_bound_vars_in_rule(rule * r, unsigned & var_idx);
|
||||||
|
vector<rule_ref_vector> rename_bound_vars(item_set_vector const & heads,
|
||||||
|
rule_set & rules);
|
||||||
|
|
||||||
|
void add_rec_tail(vector< ptr_vector<app> > & recursive_calls,
|
||||||
|
app_ref_vector & new_tail,
|
||||||
|
svector<bool> & new_tail_neg, unsigned & tail_idx);
|
||||||
|
void add_non_rec_tail(rule & r, app_ref_vector & new_tail,
|
||||||
|
svector<bool> & new_tail_neg,
|
||||||
|
unsigned & tail_idx);
|
||||||
|
|
||||||
|
rule_ref product_rule(rule_ref_vector const & rules);
|
||||||
|
|
||||||
|
void merge_rules(unsigned idx, rule_ref_vector & buf,
|
||||||
|
vector<rule_ref_vector> const & merged_rules, rule_set & all_rules);
|
||||||
|
void merge_applications(rule & r, rule_set & rules);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\brief Create synchronous product transformer.
|
||||||
|
*/
|
||||||
|
mk_synchronize(context & ctx, unsigned priority = 22500);
|
||||||
|
|
||||||
|
rule_set * operator()(rule_set const & source);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DL_MK_SYNCHRONIZE_H_ */
|
|
@ -351,7 +351,7 @@ namespace opt {
|
||||||
void context::get_model_core(model_ref& mdl) {
|
void context::get_model_core(model_ref& mdl) {
|
||||||
mdl = m_model;
|
mdl = m_model;
|
||||||
fix_model(mdl);
|
fix_model(mdl);
|
||||||
mdl->set_model_completion(true);
|
if (mdl) mdl->set_model_completion(true);
|
||||||
TRACE("opt", tout << *mdl;);
|
TRACE("opt", tout << *mdl;);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1084,11 +1084,7 @@ namespace opt {
|
||||||
}
|
}
|
||||||
term = m_arith.mk_add(args.size(), args.c_ptr());
|
term = m_arith.mk_add(args.size(), args.c_ptr());
|
||||||
}
|
}
|
||||||
else if (m_arith.is_arith_expr(term) && !is_mul_const(term)) {
|
else if (m.is_ite(term) || !is_mul_const(term)) {
|
||||||
TRACE("opt", tout << "Purifying " << term << "\n";);
|
|
||||||
term = purify(fm, term);
|
|
||||||
}
|
|
||||||
else if (m.is_ite(term)) {
|
|
||||||
TRACE("opt", tout << "Purifying " << term << "\n";);
|
TRACE("opt", tout << "Purifying " << term << "\n";);
|
||||||
term = purify(fm, term);
|
term = purify(fm, term);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1266,9 +1266,9 @@ namespace qe {
|
||||||
in->reset();
|
in->reset();
|
||||||
in->inc_depth();
|
in->inc_depth();
|
||||||
result.push_back(in.get());
|
result.push_back(in.get());
|
||||||
if (in->models_enabled()) {
|
if (in->models_enabled()) {
|
||||||
model_converter_ref mc;
|
model_converter_ref mc;
|
||||||
mc = model2model_converter(m_model.get());
|
mc = model2model_converter(m_model_save.get());
|
||||||
mc = concat(m_pred_abs.fmc(), mc.get());
|
mc = concat(m_pred_abs.fmc(), mc.get());
|
||||||
in->add(mc.get());
|
in->add(mc.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1551,10 +1551,10 @@ namespace sat {
|
||||||
if (k == 1 && lit == null_literal) {
|
if (k == 1 && lit == null_literal) {
|
||||||
literal_vector _lits(lits);
|
literal_vector _lits(lits);
|
||||||
s().mk_clause(_lits.size(), _lits.c_ptr(), learned);
|
s().mk_clause(_lits.size(), _lits.c_ptr(), learned);
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) {
|
if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) {
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
void * mem = m_allocator.allocate(card::get_obj_size(lits.size()));
|
void * mem = m_allocator.allocate(card::get_obj_size(lits.size()));
|
||||||
card* c = new (mem) card(next_id(), lit, lits, k);
|
card* c = new (mem) card(next_id(), lit, lits, k);
|
||||||
|
@ -1615,7 +1615,7 @@ namespace sat {
|
||||||
bool units = true;
|
bool units = true;
|
||||||
for (wliteral wl : wlits) units &= wl.first == 1;
|
for (wliteral wl : wlits) units &= wl.first == 1;
|
||||||
if (k == 0 && lit == null_literal) {
|
if (k == 0 && lit == null_literal) {
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (units || k == 1) {
|
if (units || k == 1) {
|
||||||
literal_vector lits;
|
literal_vector lits;
|
||||||
|
@ -3405,7 +3405,7 @@ namespace sat {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (wliteral l : p1) {
|
for (wliteral l : p1) {
|
||||||
SASSERT(m_weights[l.second.index()] == 0);
|
SASSERT(m_weights.size() <= l.second.index() || m_weights[l.second.index()] == 0);
|
||||||
m_weights.setx(l.second.index(), l.first, 0);
|
m_weights.setx(l.second.index(), l.first, 0);
|
||||||
mark_visited(l.second);
|
mark_visited(l.second);
|
||||||
}
|
}
|
||||||
|
@ -3837,8 +3837,8 @@ namespace sat {
|
||||||
reset_active_var_set();
|
reset_active_var_set();
|
||||||
m_wlits.reset();
|
m_wlits.reset();
|
||||||
uint64_t sum = 0;
|
uint64_t sum = 0;
|
||||||
if (m_bound == 1) return 0;
|
if (m_bound == 1) return nullptr;
|
||||||
if (m_overflow) return 0;
|
if (m_overflow) return nullptr;
|
||||||
|
|
||||||
for (bool_var v : m_active_vars) {
|
for (bool_var v : m_active_vars) {
|
||||||
int coeff = get_int_coeff(v);
|
int coeff = get_int_coeff(v);
|
||||||
|
@ -3850,7 +3850,7 @@ namespace sat {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_overflow || sum >= UINT_MAX/2) {
|
if (m_overflow || sum >= UINT_MAX/2) {
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return add_pb_ge(null_literal, m_wlits, m_bound, true);
|
return add_pb_ge(null_literal, m_wlits, m_bound, true);
|
||||||
|
@ -3905,7 +3905,7 @@ namespace sat {
|
||||||
++k;
|
++k;
|
||||||
}
|
}
|
||||||
if (k == 1) {
|
if (k == 1) {
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
while (!m_wlits.empty()) {
|
while (!m_wlits.empty()) {
|
||||||
wliteral wl = m_wlits.back();
|
wliteral wl = m_wlits.back();
|
||||||
|
@ -3928,7 +3928,7 @@ namespace sat {
|
||||||
++num_max_level;
|
++num_max_level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_overflow) return 0;
|
if (m_overflow) return nullptr;
|
||||||
|
|
||||||
if (slack >= k) {
|
if (slack >= k) {
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -3937,7 +3937,7 @@ namespace sat {
|
||||||
std::cout << "not asserting\n";
|
std::cout << "not asserting\n";
|
||||||
display(std::cout, m_A, true);
|
display(std::cout, m_A, true);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// produce asserting cardinality constraint
|
// produce asserting cardinality constraint
|
||||||
|
|
|
@ -16,6 +16,8 @@ Author:
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
|
@ -50,17 +50,39 @@ def_module_params('sat',
|
||||||
('unit_walk', BOOL, False, 'use unit-walk search instead of CDCL'),
|
('unit_walk', BOOL, False, 'use unit-walk search instead of CDCL'),
|
||||||
('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'),
|
('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'),
|
||||||
('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'),
|
('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'),
|
||||||
|
# - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth.
|
||||||
|
# So if the value is 10, at most 1024 cubes will be generated of length 10.
|
||||||
|
# - freevars: cutoff based on a variable fraction of lookahead.cube.freevars.
|
||||||
|
# Cut if the number of current unassigned variables drops below a fraction of number of initial variables.
|
||||||
|
# - psat: Let psat_heur := (Sum_{clause C} (psat.clause_base ^ {-|C|+1})) / |freevars|^psat.var_exp
|
||||||
|
# Cut if the value of psat_heur exceeds psat.trigger
|
||||||
|
# - adaptive_freevars: Cut if the number of current unassigned variables drops below a fraction of free variables
|
||||||
|
# at the time of the last conflict. The fraction is increased every time the a cutoff is created.
|
||||||
|
# - adative_psat: Cut based on psat_heur in an adaptive way.
|
||||||
('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'),
|
('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'),
|
||||||
('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'),
|
('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'),
|
||||||
('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free fariable fraction. Used when lookahead.cube.cutoff is freevars'),
|
('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free variable fraction. Used when lookahead.cube.cutoff is freevars'),
|
||||||
('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'),
|
('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'),
|
||||||
('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'),
|
('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'),
|
||||||
('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'),
|
('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'),
|
||||||
('lookahead_search', BOOL, False, 'use lookahead solver'),
|
|
||||||
('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'),
|
('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'),
|
||||||
('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'),
|
('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'),
|
||||||
('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'),
|
('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'),
|
||||||
('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'),
|
('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'),
|
||||||
('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'),
|
('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'),
|
||||||
('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu')))
|
('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu'))
|
||||||
|
# reward function used to determine which literal to cube on.
|
||||||
|
# - ternary: reward function useful for random 3-SAT instances. Used by Heule and Knuth in March.
|
||||||
|
# - heule_schur: reward function based on "Schur Number 5", Heule, AAAI 2018
|
||||||
|
# The score of a literal lit is:
|
||||||
|
# Sum_{C in Clauses | lit in C} 2 ^ (- |C|+1)
|
||||||
|
# * Sum_{lit' in C | lit' != lit} lit_occs(~lit')
|
||||||
|
# / | C |
|
||||||
|
# where lit_occs(lit) is the number of clauses containing lit.
|
||||||
|
# - heuleu: The score of a literal lit is: Sum_{C in Clauses | lit in C} 2 ^ (-|C| + 1)
|
||||||
|
# - unit: heule_schur + also counts number of unit clauses.
|
||||||
|
# - march_cu: default reward function used in a version of March
|
||||||
|
# Each reward function also comes with its own variant of "mix_diff", which
|
||||||
|
# is the function for combining reward metrics for the positive and negative variant of a literal.
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ public:
|
||||||
return m_num_scopes;
|
return m_num_scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assert_expr_core2(expr * t, expr * a) override {
|
void assert_expr_core2(expr * t, expr * a) override {
|
||||||
if (a) {
|
if (a) {
|
||||||
m_asmsf.push_back(a);
|
m_asmsf.push_back(a);
|
||||||
assert_expr_core(m.mk_implies(a, t));
|
assert_expr_core(m.mk_implies(a, t));
|
||||||
|
@ -311,7 +311,10 @@ public:
|
||||||
expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override {
|
expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override {
|
||||||
if (!is_internalized()) {
|
if (!is_internalized()) {
|
||||||
lbool r = internalize_formulas();
|
lbool r = internalize_formulas();
|
||||||
if (r != l_true) return expr_ref_vector(m);
|
if (r != l_true) {
|
||||||
|
IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n");
|
||||||
|
return expr_ref_vector(m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
convert_internalized();
|
convert_internalized();
|
||||||
obj_hashtable<expr> _vs;
|
obj_hashtable<expr> _vs;
|
||||||
|
@ -329,6 +332,7 @@ public:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (result == l_true) {
|
if (result == l_true) {
|
||||||
|
IF_VERBOSE(1, verbose_stream() << "formulas are SAT\n");
|
||||||
return expr_ref_vector(m);
|
return expr_ref_vector(m);
|
||||||
}
|
}
|
||||||
expr_ref_vector fmls(m);
|
expr_ref_vector fmls(m);
|
||||||
|
@ -345,6 +349,7 @@ public:
|
||||||
vs.push_back(x);
|
vs.push_back(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (fmls.empty()) { IF_VERBOSE(0, verbose_stream() << "no literals were produced in cube\n"); }
|
||||||
return fmls;
|
return fmls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,6 +478,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void convert_internalized() {
|
void convert_internalized() {
|
||||||
|
m_solver.pop_to_base_level();
|
||||||
if (!is_internalized() && m_fmls_head > 0) {
|
if (!is_internalized() && m_fmls_head > 0) {
|
||||||
internalize_formulas();
|
internalize_formulas();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ Revision History:
|
||||||
#include "shell/smtlib_frontend.h"
|
#include "shell/smtlib_frontend.h"
|
||||||
#include "shell/z3_log_frontend.h"
|
#include "shell/z3_log_frontend.h"
|
||||||
#include "util/warning.h"
|
#include "util/warning.h"
|
||||||
#include "util/version.h"
|
#include "util/z3_version.h"
|
||||||
#include "shell/dimacs_frontend.h"
|
#include "shell/dimacs_frontend.h"
|
||||||
#include "shell/datalog_frontend.h"
|
#include "shell/datalog_frontend.h"
|
||||||
#include "shell/opt_frontend.h"
|
#include "shell/opt_frontend.h"
|
||||||
|
|
|
@ -13,6 +13,7 @@ z3_add_component(smt
|
||||||
old_interval.cpp
|
old_interval.cpp
|
||||||
qi_queue.cpp
|
qi_queue.cpp
|
||||||
smt_almost_cg_table.cpp
|
smt_almost_cg_table.cpp
|
||||||
|
smt_arith_value.cpp
|
||||||
smt_case_split_queue.cpp
|
smt_case_split_queue.cpp
|
||||||
smt_cg_table.cpp
|
smt_cg_table.cpp
|
||||||
smt_checker.cpp
|
smt_checker.cpp
|
||||||
|
|
97
src/smt/smt_arith_value.cpp
Normal file
97
src/smt/smt_arith_value.cpp
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2018 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
smt_arith_value.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Utility to extract arithmetic values from context.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2018-12-08.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
|
||||||
|
#include "smt/smt_arith_value.h"
|
||||||
|
#include "smt/theory_lra.h"
|
||||||
|
#include "smt/theory_arith.h"
|
||||||
|
|
||||||
|
namespace smt {
|
||||||
|
|
||||||
|
arith_value::arith_value(context& ctx):
|
||||||
|
m_ctx(ctx), m(ctx.get_manager()), a(m) {}
|
||||||
|
|
||||||
|
bool arith_value::get_lo(expr* e, rational& lo, bool& is_strict) {
|
||||||
|
if (!m_ctx.e_internalized(e)) return false;
|
||||||
|
family_id afid = a.get_family_id();
|
||||||
|
is_strict = false;
|
||||||
|
enode* next = m_ctx.get_enode(e), *n = next;
|
||||||
|
bool found = false;
|
||||||
|
bool is_strict1;
|
||||||
|
rational lo1;
|
||||||
|
theory* th = m_ctx.get_theory(afid);
|
||||||
|
theory_mi_arith* tha = dynamic_cast<theory_mi_arith*>(th);
|
||||||
|
theory_i_arith* thi = dynamic_cast<theory_i_arith*>(th);
|
||||||
|
theory_lra* thr = dynamic_cast<theory_lra*>(th);
|
||||||
|
do {
|
||||||
|
if ((tha && tha->get_lower(next, lo1, is_strict1)) ||
|
||||||
|
(thi && thi->get_lower(next, lo1, is_strict1)) ||
|
||||||
|
(thr && thr->get_lower(next, lo1, is_strict1))) {
|
||||||
|
if (!found || lo1 > lo || (lo == lo1 && is_strict1)) lo = lo1, is_strict = is_strict1;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
next = next->get_next();
|
||||||
|
}
|
||||||
|
while (n != next);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arith_value::get_up(expr* e, rational& up, bool& is_strict) {
|
||||||
|
if (!m_ctx.e_internalized(e)) return false;
|
||||||
|
family_id afid = a.get_family_id();
|
||||||
|
is_strict = false;
|
||||||
|
enode* next = m_ctx.get_enode(e), *n = next;
|
||||||
|
bool found = false, is_strict1;
|
||||||
|
rational up1;
|
||||||
|
theory* th = m_ctx.get_theory(afid);
|
||||||
|
theory_mi_arith* tha = dynamic_cast<theory_mi_arith*>(th);
|
||||||
|
theory_i_arith* thi = dynamic_cast<theory_i_arith*>(th);
|
||||||
|
theory_lra* thr = dynamic_cast<theory_lra*>(th);
|
||||||
|
do {
|
||||||
|
if ((tha && tha->get_upper(next, up1, is_strict1)) ||
|
||||||
|
(thi && thi->get_upper(next, up1, is_strict1)) ||
|
||||||
|
(thr && thr->get_upper(next, up1, is_strict1))) {
|
||||||
|
if (!found || up1 < up || (up1 == up && is_strict1)) up = up1, is_strict = is_strict1;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
next = next->get_next();
|
||||||
|
}
|
||||||
|
while (n != next);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool arith_value::get_value(expr* e, rational& val) {
|
||||||
|
if (!m_ctx.e_internalized(e)) return false;
|
||||||
|
expr_ref _val(m);
|
||||||
|
enode* next = m_ctx.get_enode(e), *n = next;
|
||||||
|
family_id afid = a.get_family_id();
|
||||||
|
theory* th = m_ctx.get_theory(afid);
|
||||||
|
theory_mi_arith* tha = dynamic_cast<theory_mi_arith*>(th);
|
||||||
|
theory_i_arith* thi = dynamic_cast<theory_i_arith*>(th);
|
||||||
|
theory_lra* thr = dynamic_cast<theory_lra*>(th);
|
||||||
|
do {
|
||||||
|
e = next->get_owner();
|
||||||
|
if (tha && tha->get_value(next, _val) && a.is_numeral(_val, val)) return true;
|
||||||
|
if (thi && thi->get_value(next, _val) && a.is_numeral(_val, val)) return true;
|
||||||
|
if (thr && thr->get_value(next, val)) return true;
|
||||||
|
next = next->get_next();
|
||||||
|
}
|
||||||
|
while (next != n);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
37
src/smt/smt_arith_value.h
Normal file
37
src/smt/smt_arith_value.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2018 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
smt_arith_value.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Utility to extract arithmetic values from context.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2018-12-08.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ast/arith_decl_plugin.h"
|
||||||
|
#include "smt/smt_context.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace smt {
|
||||||
|
class arith_value {
|
||||||
|
context& m_ctx;
|
||||||
|
ast_manager& m;
|
||||||
|
arith_util a;
|
||||||
|
public:
|
||||||
|
arith_value(context& ctx);
|
||||||
|
bool get_lo(expr* e, rational& lo, bool& strict);
|
||||||
|
bool get_up(expr* e, rational& up, bool& strict);
|
||||||
|
bool get_value(expr* e, rational& value);
|
||||||
|
};
|
||||||
|
};
|
|
@ -426,6 +426,7 @@ namespace smt {
|
||||||
std::stringstream strm;
|
std::stringstream strm;
|
||||||
strm << "lemma_" << (++m_lemma_id) << ".smt2";
|
strm << "lemma_" << (++m_lemma_id) << ".smt2";
|
||||||
std::ofstream out(strm.str());
|
std::ofstream out(strm.str());
|
||||||
|
TRACE("lemma", tout << strm.str() << "\n";);
|
||||||
display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic);
|
display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic);
|
||||||
out.close();
|
out.close();
|
||||||
return m_lemma_id;
|
return m_lemma_id;
|
||||||
|
@ -466,6 +467,7 @@ namespace smt {
|
||||||
std::stringstream strm;
|
std::stringstream strm;
|
||||||
strm << "lemma_" << (++m_lemma_id) << ".smt2";
|
strm << "lemma_" << (++m_lemma_id) << ".smt2";
|
||||||
std::ofstream out(strm.str());
|
std::ofstream out(strm.str());
|
||||||
|
TRACE("lemma", tout << strm.str() << "\n";);
|
||||||
display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic);
|
display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic);
|
||||||
out.close();
|
out.close();
|
||||||
return m_lemma_id;
|
return m_lemma_id;
|
||||||
|
|
|
@ -312,6 +312,13 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_ref farkas_util::get() {
|
expr_ref farkas_util::get() {
|
||||||
|
TRACE("arith",
|
||||||
|
for (unsigned i = 0; i < m_coeffs.size(); ++i) {
|
||||||
|
tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") ";
|
||||||
|
}
|
||||||
|
tout << "\n";
|
||||||
|
);
|
||||||
|
|
||||||
m_normalize_factor = rational::one();
|
m_normalize_factor = rational::one();
|
||||||
expr_ref res(m);
|
expr_ref res(m);
|
||||||
if (m_coeffs.empty()) {
|
if (m_coeffs.empty()) {
|
||||||
|
@ -330,13 +337,12 @@ namespace smt {
|
||||||
partition_ineqs();
|
partition_ineqs();
|
||||||
expr_ref_vector lits(m);
|
expr_ref_vector lits(m);
|
||||||
unsigned lo = 0;
|
unsigned lo = 0;
|
||||||
for (unsigned i = 0; i < m_his.size(); ++i) {
|
for (unsigned hi : m_his) {
|
||||||
unsigned hi = m_his[i];
|
|
||||||
lits.push_back(extract_consequence(lo, hi));
|
lits.push_back(extract_consequence(lo, hi));
|
||||||
lo = hi;
|
lo = hi;
|
||||||
}
|
}
|
||||||
bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res);
|
bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res);
|
||||||
IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } });
|
IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << res << "\n"; } });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = extract_consequence(0, m_coeffs.size());
|
res = extract_consequence(0, m_coeffs.size());
|
||||||
|
|
|
@ -216,13 +216,17 @@ namespace smt {
|
||||||
SASSERT(m_manager.is_bool(n));
|
SASSERT(m_manager.is_bool(n));
|
||||||
if (is_gate(m_manager, n)) {
|
if (is_gate(m_manager, n)) {
|
||||||
switch(to_app(n)->get_decl_kind()) {
|
switch(to_app(n)->get_decl_kind()) {
|
||||||
case OP_AND:
|
case OP_AND: {
|
||||||
UNREACHABLE();
|
for (expr * arg : *to_app(n)) {
|
||||||
|
internalize(arg, true);
|
||||||
|
literal lit = get_literal(arg);
|
||||||
|
mk_root_clause(1, &lit, pr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OP_OR: {
|
case OP_OR: {
|
||||||
literal_buffer lits;
|
literal_buffer lits;
|
||||||
unsigned num = to_app(n)->get_num_args();
|
for (expr * arg : *to_app(n)) {
|
||||||
for (unsigned i = 0; i < num; i++) {
|
|
||||||
expr * arg = to_app(n)->get_arg(i);
|
|
||||||
internalize(arg, true);
|
internalize(arg, true);
|
||||||
lits.push_back(get_literal(arg));
|
lits.push_back(get_literal(arg));
|
||||||
}
|
}
|
||||||
|
@ -294,8 +298,7 @@ namespace smt {
|
||||||
sort * s = m_manager.get_sort(n->get_arg(0));
|
sort * s = m_manager.get_sort(n->get_arg(0));
|
||||||
sort_ref u(m_manager.mk_fresh_sort("distinct-elems"), m_manager);
|
sort_ref u(m_manager.mk_fresh_sort("distinct-elems"), m_manager);
|
||||||
func_decl_ref f(m_manager.mk_fresh_func_decl("distinct-aux-f", "", 1, &s, u), m_manager);
|
func_decl_ref f(m_manager.mk_fresh_func_decl("distinct-aux-f", "", 1, &s, u), m_manager);
|
||||||
for (unsigned i = 0; i < num_args; i++) {
|
for (expr * arg : *n) {
|
||||||
expr * arg = n->get_arg(i);
|
|
||||||
app_ref fapp(m_manager.mk_app(f, arg), m_manager);
|
app_ref fapp(m_manager.mk_app(f, arg), m_manager);
|
||||||
app_ref val(m_manager.mk_fresh_const("unique-value", u), m_manager);
|
app_ref val(m_manager.mk_fresh_const("unique-value", u), m_manager);
|
||||||
enode * e = mk_enode(val, false, false, true);
|
enode * e = mk_enode(val, false, false, true);
|
||||||
|
@ -826,9 +829,7 @@ namespace smt {
|
||||||
void context::internalize_uninterpreted(app * n) {
|
void context::internalize_uninterpreted(app * n) {
|
||||||
SASSERT(!e_internalized(n));
|
SASSERT(!e_internalized(n));
|
||||||
// process args
|
// process args
|
||||||
unsigned num = n->get_num_args();
|
for (expr * arg : *n) {
|
||||||
for (unsigned i = 0; i < num; i++) {
|
|
||||||
expr * arg = n->get_arg(i);
|
|
||||||
internalize(arg, false);
|
internalize(arg, false);
|
||||||
SASSERT(e_internalized(arg));
|
SASSERT(e_internalized(arg));
|
||||||
}
|
}
|
||||||
|
@ -1542,10 +1543,9 @@ namespace smt {
|
||||||
void context::add_and_rel_watches(app * n) {
|
void context::add_and_rel_watches(app * n) {
|
||||||
if (relevancy()) {
|
if (relevancy()) {
|
||||||
relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n);
|
relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n);
|
||||||
unsigned num = n->get_num_args();
|
for (expr * arg : *n) {
|
||||||
for (unsigned i = 0; i < num; i++) {
|
|
||||||
// if one child is assigned to false, the and-parent must be notified
|
// if one child is assigned to false, the and-parent must be notified
|
||||||
literal l = get_literal(n->get_arg(i));
|
literal l = get_literal(arg);
|
||||||
add_rel_watch(~l, eh);
|
add_rel_watch(~l, eh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1554,10 +1554,9 @@ namespace smt {
|
||||||
void context::add_or_rel_watches(app * n) {
|
void context::add_or_rel_watches(app * n) {
|
||||||
if (relevancy()) {
|
if (relevancy()) {
|
||||||
relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n);
|
relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n);
|
||||||
unsigned num = n->get_num_args();
|
for (expr * arg : *n) {
|
||||||
for (unsigned i = 0; i < num; i++) {
|
|
||||||
// if one child is assigned to true, the or-parent must be notified
|
// if one child is assigned to true, the or-parent must be notified
|
||||||
literal l = get_literal(n->get_arg(i));
|
literal l = get_literal(arg);
|
||||||
add_rel_watch(l, eh);
|
add_rel_watch(l, eh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1588,9 +1587,8 @@ namespace smt {
|
||||||
TRACE("mk_and_cnstr", tout << "l: "; display_literal(tout, l); tout << "\n";);
|
TRACE("mk_and_cnstr", tout << "l: "; display_literal(tout, l); tout << "\n";);
|
||||||
literal_buffer buffer;
|
literal_buffer buffer;
|
||||||
buffer.push_back(l);
|
buffer.push_back(l);
|
||||||
unsigned num_args = n->get_num_args();
|
for (expr * arg : *n) {
|
||||||
for (unsigned i = 0; i < num_args; i++) {
|
literal l_arg = get_literal(arg);
|
||||||
literal l_arg = get_literal(n->get_arg(i));
|
|
||||||
TRACE("mk_and_cnstr", tout << "l_arg: "; display_literal(tout, l_arg); tout << "\n";);
|
TRACE("mk_and_cnstr", tout << "l_arg: "; display_literal(tout, l_arg); tout << "\n";);
|
||||||
mk_gate_clause(~l, l_arg);
|
mk_gate_clause(~l, l_arg);
|
||||||
buffer.push_back(~l_arg);
|
buffer.push_back(~l_arg);
|
||||||
|
@ -1602,9 +1600,8 @@ namespace smt {
|
||||||
literal l = get_literal(n);
|
literal l = get_literal(n);
|
||||||
literal_buffer buffer;
|
literal_buffer buffer;
|
||||||
buffer.push_back(~l);
|
buffer.push_back(~l);
|
||||||
unsigned num_args = n->get_num_args();
|
for (expr * arg : *n) {
|
||||||
for (unsigned i = 0; i < num_args; i++) {
|
literal l_arg = get_literal(arg);
|
||||||
literal l_arg = get_literal(n->get_arg(i));
|
|
||||||
mk_gate_clause(l, ~l_arg);
|
mk_gate_clause(l, ~l_arg);
|
||||||
buffer.push_back(l_arg);
|
buffer.push_back(l_arg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ namespace smt {
|
||||||
m_model_finder(mf),
|
m_model_finder(mf),
|
||||||
m_max_cexs(1),
|
m_max_cexs(1),
|
||||||
m_iteration_idx(0),
|
m_iteration_idx(0),
|
||||||
|
m_has_rec_fun(false),
|
||||||
m_curr_model(nullptr),
|
m_curr_model(nullptr),
|
||||||
m_pinned_exprs(m) {
|
m_pinned_exprs(m) {
|
||||||
}
|
}
|
||||||
|
@ -351,9 +352,7 @@ namespace smt {
|
||||||
bool model_checker::check_rec_fun(quantifier* q, bool strict_rec_fun) {
|
bool model_checker::check_rec_fun(quantifier* q, bool strict_rec_fun) {
|
||||||
TRACE("model_checker", tout << mk_pp(q, m) << "\n";);
|
TRACE("model_checker", tout << mk_pp(q, m) << "\n";);
|
||||||
SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body.
|
SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body.
|
||||||
expr* fn = to_app(q->get_pattern(0))->get_arg(0);
|
func_decl* f = m.get_rec_fun_decl(q);
|
||||||
SASSERT(is_app(fn));
|
|
||||||
func_decl* f = to_app(fn)->get_decl();
|
|
||||||
|
|
||||||
expr_ref_vector args(m);
|
expr_ref_vector args(m);
|
||||||
unsigned num_decls = q->get_num_decls();
|
unsigned num_decls = q->get_num_decls();
|
||||||
|
@ -433,7 +432,7 @@ namespace smt {
|
||||||
TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";);
|
TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";);
|
||||||
m_max_cexs += m_params.m_mbqi_max_cexs;
|
m_max_cexs += m_params.m_mbqi_max_cexs;
|
||||||
|
|
||||||
if (num_failures == 0 && !m_context->validate_model()) {
|
if (num_failures == 0 && (!m_context->validate_model() || has_rec_under_quantifiers())) {
|
||||||
num_failures = 1;
|
num_failures = 1;
|
||||||
// this time force expanding recursive function definitions
|
// this time force expanding recursive function definitions
|
||||||
// that are not forced true in the current model.
|
// that are not forced true in the current model.
|
||||||
|
@ -450,6 +449,43 @@ namespace smt {
|
||||||
return num_failures == 0;
|
return num_failures == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct has_rec_fun_proc {
|
||||||
|
obj_hashtable<func_decl>& m_rec_funs;
|
||||||
|
bool m_has_rec_fun;
|
||||||
|
|
||||||
|
bool has_rec_fun() const { return m_has_rec_fun; }
|
||||||
|
|
||||||
|
has_rec_fun_proc(obj_hashtable<func_decl>& rec_funs):
|
||||||
|
m_rec_funs(rec_funs),
|
||||||
|
m_has_rec_fun(false) {}
|
||||||
|
|
||||||
|
void operator()(app* fn) {
|
||||||
|
m_has_rec_fun |= m_rec_funs.contains(fn->get_decl());
|
||||||
|
}
|
||||||
|
void operator()(expr*) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool model_checker::has_rec_under_quantifiers() {
|
||||||
|
if (!m_has_rec_fun) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
obj_hashtable<func_decl> rec_funs;
|
||||||
|
for (quantifier * q : *m_qm) {
|
||||||
|
if (m.is_rec_fun_def(q)) {
|
||||||
|
rec_funs.insert(m.get_rec_fun_decl(q));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr_fast_mark1 visited;
|
||||||
|
has_rec_fun_proc proc(rec_funs);
|
||||||
|
for (quantifier * q : *m_qm) {
|
||||||
|
if (!m.is_rec_fun_def(q)) {
|
||||||
|
quick_for_each_expr(proc, visited, q);
|
||||||
|
if (proc.has_rec_fun()) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// (repeated from defined_names.cpp)
|
// (repeated from defined_names.cpp)
|
||||||
// NB. The pattern for lambdas is incomplete.
|
// NB. The pattern for lambdas is incomplete.
|
||||||
|
@ -479,6 +515,7 @@ namespace smt {
|
||||||
}
|
}
|
||||||
found_relevant = true;
|
found_relevant = true;
|
||||||
if (m.is_rec_fun_def(q)) {
|
if (m.is_rec_fun_def(q)) {
|
||||||
|
m_has_rec_fun = true;
|
||||||
if (!check_rec_fun(q, strict_rec_fun)) {
|
if (!check_rec_fun(q, strict_rec_fun)) {
|
||||||
TRACE("model_checker", tout << "checking recursive function failed\n";);
|
TRACE("model_checker", tout << "checking recursive function failed\n";);
|
||||||
num_failures++;
|
num_failures++;
|
||||||
|
|
|
@ -51,8 +51,10 @@ namespace smt {
|
||||||
scoped_ptr<context> m_aux_context; // Auxiliary context used for model checking quantifiers.
|
scoped_ptr<context> m_aux_context; // Auxiliary context used for model checking quantifiers.
|
||||||
unsigned m_max_cexs;
|
unsigned m_max_cexs;
|
||||||
unsigned m_iteration_idx;
|
unsigned m_iteration_idx;
|
||||||
|
bool m_has_rec_fun;
|
||||||
proto_model * m_curr_model;
|
proto_model * m_curr_model;
|
||||||
obj_map<expr, expr *> m_value2expr;
|
obj_map<expr, expr *> m_value2expr;
|
||||||
|
|
||||||
friend class instantiation_set;
|
friend class instantiation_set;
|
||||||
|
|
||||||
void init_aux_context();
|
void init_aux_context();
|
||||||
|
@ -64,6 +66,7 @@ namespace smt {
|
||||||
bool add_blocking_clause(model * cex, expr_ref_vector & sks);
|
bool add_blocking_clause(model * cex, expr_ref_vector & sks);
|
||||||
bool check(quantifier * q);
|
bool check(quantifier * q);
|
||||||
bool check_rec_fun(quantifier* q, bool strict_rec_fun);
|
bool check_rec_fun(quantifier* q, bool strict_rec_fun);
|
||||||
|
bool has_rec_under_quantifiers();
|
||||||
void check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures);
|
void check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures);
|
||||||
|
|
||||||
struct instance {
|
struct instance {
|
||||||
|
|
|
@ -1071,6 +1071,8 @@ namespace smt {
|
||||||
|
|
||||||
bool get_lower(enode* n, expr_ref& r);
|
bool get_lower(enode* n, expr_ref& r);
|
||||||
bool get_upper(enode* n, expr_ref& r);
|
bool get_upper(enode* n, expr_ref& r);
|
||||||
|
bool get_lower(enode* n, rational& r, bool &is_strict);
|
||||||
|
bool get_upper(enode* n, rational& r, bool &is_strict);
|
||||||
bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r);
|
bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -484,7 +484,6 @@ namespace smt {
|
||||||
void theory_arith<Ext>::mk_idiv_mod_axioms(expr * dividend, expr * divisor) {
|
void theory_arith<Ext>::mk_idiv_mod_axioms(expr * dividend, expr * divisor) {
|
||||||
if (!m_util.is_zero(divisor)) {
|
if (!m_util.is_zero(divisor)) {
|
||||||
ast_manager & m = get_manager();
|
ast_manager & m = get_manager();
|
||||||
bool is_numeral = m_util.is_numeral(divisor);
|
|
||||||
// if divisor is zero, then idiv and mod are uninterpreted functions.
|
// if divisor is zero, then idiv and mod are uninterpreted functions.
|
||||||
expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m);
|
expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m);
|
||||||
expr_ref eqz(m), eq(m), lower(m), upper(m);
|
expr_ref eqz(m), eq(m), lower(m), upper(m);
|
||||||
|
@ -3303,6 +3302,21 @@ namespace smt {
|
||||||
return b && to_expr(b->get_value(), is_int(v), r);
|
return b && to_expr(b->get_value(), is_int(v), r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Ext>
|
||||||
|
bool theory_arith<Ext>::get_lower(enode * n, rational& r, bool& is_strict) {
|
||||||
|
theory_var v = n->get_th_var(get_id());
|
||||||
|
bound* b = (v == null_theory_var) ? nullptr : lower(v);
|
||||||
|
return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_pos(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ext>
|
||||||
|
bool theory_arith<Ext>::get_upper(enode * n, rational& r, bool& is_strict) {
|
||||||
|
theory_var v = n->get_th_var(get_id());
|
||||||
|
bound* b = (v == null_theory_var) ? nullptr : upper(v);
|
||||||
|
return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_neg(), true);
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
//
|
//
|
||||||
// Backtracking
|
// Backtracking
|
||||||
|
|
|
@ -396,7 +396,9 @@ namespace smt {
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) {
|
if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) {
|
||||||
theory_var v = it->m_var;
|
theory_var v = it->m_var;
|
||||||
expr * bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(rational::zero(), is_int(v)));
|
expr* e = get_enode(v)->get_owner();
|
||||||
|
bool _is_int = m_util.is_int(e);
|
||||||
|
expr * bound = m_util.mk_ge(e, m_util.mk_numeral(rational::zero(), _is_int));
|
||||||
context & ctx = get_context();
|
context & ctx = get_context();
|
||||||
ctx.internalize(bound, true);
|
ctx.internalize(bound, true);
|
||||||
ctx.mark_as_relevant(bound);
|
ctx.mark_as_relevant(bound);
|
||||||
|
|
|
@ -55,7 +55,7 @@ std::ostream& operator<<(std::ostream& out, bound_kind const& k) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class bound {
|
class bound {
|
||||||
smt::bool_var m_bv;
|
smt::bool_var m_bv;
|
||||||
smt::theory_var m_var;
|
smt::theory_var m_var;
|
||||||
bool m_is_int;
|
bool m_is_int;
|
||||||
rational m_value;
|
rational m_value;
|
||||||
|
@ -129,6 +129,7 @@ class theory_lra::imp {
|
||||||
|
|
||||||
struct scope {
|
struct scope {
|
||||||
unsigned m_bounds_lim;
|
unsigned m_bounds_lim;
|
||||||
|
unsigned m_idiv_lim;
|
||||||
unsigned m_asserted_qhead;
|
unsigned m_asserted_qhead;
|
||||||
unsigned m_asserted_atoms_lim;
|
unsigned m_asserted_atoms_lim;
|
||||||
unsigned m_underspecified_lim;
|
unsigned m_underspecified_lim;
|
||||||
|
@ -154,6 +155,7 @@ class theory_lra::imp {
|
||||||
ast_manager& m;
|
ast_manager& m;
|
||||||
theory_arith_params& m_arith_params;
|
theory_arith_params& m_arith_params;
|
||||||
arith_util a;
|
arith_util a;
|
||||||
|
bool m_has_int;
|
||||||
arith_eq_adapter m_arith_eq_adapter;
|
arith_eq_adapter m_arith_eq_adapter;
|
||||||
vector<rational> m_columns;
|
vector<rational> m_columns;
|
||||||
|
|
||||||
|
@ -163,13 +165,13 @@ class theory_lra::imp {
|
||||||
expr_ref_vector m_terms;
|
expr_ref_vector m_terms;
|
||||||
vector<rational> m_coeffs;
|
vector<rational> m_coeffs;
|
||||||
svector<theory_var> m_vars;
|
svector<theory_var> m_vars;
|
||||||
rational m_coeff;
|
rational m_offset;
|
||||||
ptr_vector<expr> m_terms_to_internalize;
|
ptr_vector<expr> m_terms_to_internalize;
|
||||||
internalize_state(ast_manager& m): m_terms(m) {}
|
internalize_state(ast_manager& m): m_terms(m) {}
|
||||||
void reset() {
|
void reset() {
|
||||||
m_terms.reset();
|
m_terms.reset();
|
||||||
m_coeffs.reset();
|
m_coeffs.reset();
|
||||||
m_coeff.reset();
|
m_offset.reset();
|
||||||
m_vars.reset();
|
m_vars.reset();
|
||||||
m_terms_to_internalize.reset();
|
m_terms_to_internalize.reset();
|
||||||
}
|
}
|
||||||
|
@ -195,7 +197,7 @@ class theory_lra::imp {
|
||||||
expr_ref_vector& terms() { return m_st.m_terms; }
|
expr_ref_vector& terms() { return m_st.m_terms; }
|
||||||
vector<rational>& coeffs() { return m_st.m_coeffs; }
|
vector<rational>& coeffs() { return m_st.m_coeffs; }
|
||||||
svector<theory_var>& vars() { return m_st.m_vars; }
|
svector<theory_var>& vars() { return m_st.m_vars; }
|
||||||
rational& coeff() { return m_st.m_coeff; }
|
rational& offset() { return m_st.m_offset; }
|
||||||
ptr_vector<expr>& terms_to_internalize() { return m_st.m_terms_to_internalize; }
|
ptr_vector<expr>& terms_to_internalize() { return m_st.m_terms_to_internalize; }
|
||||||
void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); }
|
void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); }
|
||||||
void set_back(unsigned i) {
|
void set_back(unsigned i) {
|
||||||
|
@ -214,6 +216,10 @@ class theory_lra::imp {
|
||||||
svector<theory_var> m_term_index2theory_var; // reverse map from lp_solver variables to theory variables
|
svector<theory_var> m_term_index2theory_var; // reverse map from lp_solver variables to theory variables
|
||||||
var_coeffs m_left_side; // constraint left side
|
var_coeffs m_left_side; // constraint left side
|
||||||
mutable std::unordered_map<lp::var_index, rational> m_variable_values; // current model
|
mutable std::unordered_map<lp::var_index, rational> m_variable_values; // current model
|
||||||
|
lp::var_index m_one_var;
|
||||||
|
lp::var_index m_zero_var;
|
||||||
|
lp::var_index m_rone_var;
|
||||||
|
lp::var_index m_rzero_var;
|
||||||
|
|
||||||
enum constraint_source {
|
enum constraint_source {
|
||||||
inequality_source,
|
inequality_source,
|
||||||
|
@ -229,6 +235,7 @@ class theory_lra::imp {
|
||||||
svector<delayed_atom> m_asserted_atoms;
|
svector<delayed_atom> m_asserted_atoms;
|
||||||
expr* m_not_handled;
|
expr* m_not_handled;
|
||||||
ptr_vector<app> m_underspecified;
|
ptr_vector<app> m_underspecified;
|
||||||
|
ptr_vector<expr> m_idiv_terms;
|
||||||
unsigned_vector m_var_trail;
|
unsigned_vector m_var_trail;
|
||||||
vector<ptr_vector<lp_api::bound> > m_use_list; // bounds where variables are used.
|
vector<ptr_vector<lp_api::bound> > m_use_list; // bounds where variables are used.
|
||||||
|
|
||||||
|
@ -328,6 +335,31 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lp::var_index add_const(int c, lp::var_index& var, bool is_int) {
|
||||||
|
if (var != UINT_MAX) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
app_ref cnst(a.mk_numeral(rational(c), is_int), m);
|
||||||
|
TRACE("arith", tout << "add " << cnst << "\n";);
|
||||||
|
enode* e = mk_enode(cnst);
|
||||||
|
theory_var v = mk_var(cnst);
|
||||||
|
var = m_solver->add_var(v, true);
|
||||||
|
m_theory_var2var_index.setx(v, var, UINT_MAX);
|
||||||
|
m_var_index2theory_var.setx(var, v, UINT_MAX);
|
||||||
|
m_var_trail.push_back(v);
|
||||||
|
add_def_constraint(m_solver->add_var_bound(var, lp::GE, rational(c)));
|
||||||
|
add_def_constraint(m_solver->add_var_bound(var, lp::LE, rational(c)));
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
lp::var_index get_one(bool is_int) {
|
||||||
|
return add_const(1, is_int ? m_one_var : m_rone_var, is_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
lp::var_index get_zero(bool is_int) {
|
||||||
|
return add_const(0, is_int ? m_zero_var : m_rzero_var, is_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void found_not_handled(expr* n) {
|
void found_not_handled(expr* n) {
|
||||||
m_not_handled = n;
|
m_not_handled = n;
|
||||||
|
@ -372,7 +404,7 @@ class theory_lra::imp {
|
||||||
expr_ref_vector & terms = st.terms();
|
expr_ref_vector & terms = st.terms();
|
||||||
svector<theory_var>& vars = st.vars();
|
svector<theory_var>& vars = st.vars();
|
||||||
vector<rational>& coeffs = st.coeffs();
|
vector<rational>& coeffs = st.coeffs();
|
||||||
rational& coeff = st.coeff();
|
rational& offset = st.offset();
|
||||||
rational r;
|
rational r;
|
||||||
expr* n1, *n2;
|
expr* n1, *n2;
|
||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
|
@ -412,7 +444,7 @@ class theory_lra::imp {
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
else if (a.is_numeral(n, r)) {
|
else if (a.is_numeral(n, r)) {
|
||||||
coeff += coeffs[index]*r;
|
offset += coeffs[index]*r;
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
else if (a.is_uminus(n, n1)) {
|
else if (a.is_uminus(n, n1)) {
|
||||||
|
@ -424,7 +456,6 @@ class theory_lra::imp {
|
||||||
app* t = to_app(n);
|
app* t = to_app(n);
|
||||||
internalize_args(t);
|
internalize_args(t);
|
||||||
mk_enode(t);
|
mk_enode(t);
|
||||||
|
|
||||||
theory_var v = mk_var(n);
|
theory_var v = mk_var(n);
|
||||||
coeffs[vars.size()] = coeffs[index];
|
coeffs[vars.size()] = coeffs[index];
|
||||||
vars.push_back(v);
|
vars.push_back(v);
|
||||||
|
@ -442,6 +473,7 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
else if (a.is_idiv(n, n1, n2)) {
|
else if (a.is_idiv(n, n1, n2)) {
|
||||||
if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n);
|
if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n);
|
||||||
|
m_idiv_terms.push_back(n);
|
||||||
app * mod = a.mk_mod(n1, n2);
|
app * mod = a.mk_mod(n1, n2);
|
||||||
ctx().internalize(mod, false);
|
ctx().internalize(mod, false);
|
||||||
if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod);
|
if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod);
|
||||||
|
@ -451,6 +483,7 @@ class theory_lra::imp {
|
||||||
if (!is_num) {
|
if (!is_num) {
|
||||||
found_not_handled(n);
|
found_not_handled(n);
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
else {
|
else {
|
||||||
app_ref div(a.mk_idiv(n1, n2), m);
|
app_ref div(a.mk_idiv(n1, n2), m);
|
||||||
mk_enode(div);
|
mk_enode(div);
|
||||||
|
@ -461,7 +494,8 @@ class theory_lra::imp {
|
||||||
// abs(r) > v >= 0
|
// abs(r) > v >= 0
|
||||||
assert_idiv_mod_axioms(u, v, w, r);
|
assert_idiv_mod_axioms(u, v, w, r);
|
||||||
}
|
}
|
||||||
if (!ctx().relevancy() && !is_num) mk_idiv_mod_axioms(n1, n2);
|
#endif
|
||||||
|
if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2);
|
||||||
}
|
}
|
||||||
else if (a.is_rem(n, n1, n2)) {
|
else if (a.is_rem(n, n1, n2)) {
|
||||||
if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n);
|
if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n);
|
||||||
|
@ -544,6 +578,7 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
enode * mk_enode(app * n) {
|
enode * mk_enode(app * n) {
|
||||||
|
TRACE("arith", tout << expr_ref(n, m) << "\n";);
|
||||||
if (ctx().e_internalized(n)) {
|
if (ctx().e_internalized(n)) {
|
||||||
return get_enode(n);
|
return get_enode(n);
|
||||||
}
|
}
|
||||||
|
@ -624,6 +659,7 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
if (result == UINT_MAX) {
|
if (result == UINT_MAX) {
|
||||||
result = m_solver->add_var(v, is_int(v));
|
result = m_solver->add_var(v, is_int(v));
|
||||||
|
m_has_int |= is_int(v);
|
||||||
m_theory_var2var_index.setx(v, result, UINT_MAX);
|
m_theory_var2var_index.setx(v, result, UINT_MAX);
|
||||||
m_var_index2theory_var.setx(result, v, UINT_MAX);
|
m_var_index2theory_var.setx(result, v, UINT_MAX);
|
||||||
m_var_trail.push_back(v);
|
m_var_trail.push_back(v);
|
||||||
|
@ -677,7 +713,7 @@ class theory_lra::imp {
|
||||||
m_constraint_sources.setx(index, inequality_source, null_source);
|
m_constraint_sources.setx(index, inequality_source, null_source);
|
||||||
m_inequalities.setx(index, lit, null_literal);
|
m_inequalities.setx(index, lit, null_literal);
|
||||||
++m_stats.m_add_rows;
|
++m_stats.m_add_rows;
|
||||||
TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";);
|
TRACE("arith", m_solver->print_constraint(index, tout) << "\n";);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_def_constraint(lp::constraint_index index) {
|
void add_def_constraint(lp::constraint_index index) {
|
||||||
|
@ -692,9 +728,7 @@ class theory_lra::imp {
|
||||||
++m_stats.m_add_rows;
|
++m_stats.m_add_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
void internalize_eq(theory_var v1, theory_var v2) {
|
void internalize_eq(theory_var v1, theory_var v2) {
|
||||||
enode* n1 = get_enode(v1);
|
|
||||||
enode* n2 = get_enode(v2);
|
|
||||||
app_ref term(m.mk_fresh_const("eq", a.mk_real()), m);
|
app_ref term(m.mk_fresh_const("eq", a.mk_real()), m);
|
||||||
scoped_internalize_state st(*this);
|
scoped_internalize_state st(*this);
|
||||||
st.vars().push_back(v1);
|
st.vars().push_back(v1);
|
||||||
|
@ -707,8 +741,8 @@ class theory_lra::imp {
|
||||||
add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero()));
|
add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero()));
|
||||||
TRACE("arith",
|
TRACE("arith",
|
||||||
{
|
{
|
||||||
expr* o1 = n1->get_owner();
|
expr* o1 = get_enode(v1)->get_owner();
|
||||||
expr* o2 = n2->get_owner();
|
expr* o2 = get_enode(v2)->get_owner();
|
||||||
tout << "v" << v1 << " = " << "v" << v2 << ": "
|
tout << "v" << v1 << " = " << "v" << v2 << ": "
|
||||||
<< mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n";
|
<< mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n";
|
||||||
});
|
});
|
||||||
|
@ -733,10 +767,19 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_unit_var(scoped_internalize_state& st) {
|
bool is_unit_var(scoped_internalize_state& st) {
|
||||||
return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one();
|
return st.offset().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_one(scoped_internalize_state& st) {
|
||||||
|
return st.offset().is_one() && st.vars().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zero(scoped_internalize_state& st) {
|
||||||
|
return st.offset().is_zero() && st.vars().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
theory_var internalize_def(app* term, scoped_internalize_state& st) {
|
theory_var internalize_def(app* term, scoped_internalize_state& st) {
|
||||||
|
TRACE("arith", tout << expr_ref(term, m) << "\n";);
|
||||||
if (ctx().e_internalized(term)) {
|
if (ctx().e_internalized(term)) {
|
||||||
IF_VERBOSE(0, verbose_stream() << "repeated term\n";);
|
IF_VERBOSE(0, verbose_stream() << "repeated term\n";);
|
||||||
return mk_var(term, false);
|
return mk_var(term, false);
|
||||||
|
@ -766,13 +809,24 @@ class theory_lra::imp {
|
||||||
if (is_unit_var(st)) {
|
if (is_unit_var(st)) {
|
||||||
return st.vars()[0];
|
return st.vars()[0];
|
||||||
}
|
}
|
||||||
|
else if (is_one(st)) {
|
||||||
|
return get_one(a.is_int(term));
|
||||||
|
}
|
||||||
|
else if (is_zero(st)) {
|
||||||
|
return get_zero(a.is_int(term));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
init_left_side(st);
|
init_left_side(st);
|
||||||
theory_var v = mk_var(term);
|
theory_var v = mk_var(term);
|
||||||
lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX);
|
lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX);
|
||||||
TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";);
|
TRACE("arith", tout << mk_pp(term, m) << " v" << v << "\n";);
|
||||||
if (vi == UINT_MAX) {
|
if (vi == UINT_MAX) {
|
||||||
vi = m_solver->add_term(m_left_side, st.coeff());
|
rational const& offset = st.offset();
|
||||||
|
if (!offset.is_zero()) {
|
||||||
|
m_left_side.push_back(std::make_pair(offset, get_one(a.is_int(term))));
|
||||||
|
}
|
||||||
|
SASSERT(!m_left_side.empty());
|
||||||
|
vi = m_solver->add_term(m_left_side);
|
||||||
m_theory_var2var_index.setx(v, vi, UINT_MAX);
|
m_theory_var2var_index.setx(v, vi, UINT_MAX);
|
||||||
if (m_solver->is_term(vi)) {
|
if (m_solver->is_term(vi)) {
|
||||||
m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX);
|
m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX);
|
||||||
|
@ -782,7 +836,7 @@ class theory_lra::imp {
|
||||||
}
|
}
|
||||||
m_var_trail.push_back(v);
|
m_var_trail.push_back(v);
|
||||||
TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n";
|
TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n";
|
||||||
m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";);
|
m_solver->print_term(m_solver->get_term(vi), tout) << "\n";);
|
||||||
}
|
}
|
||||||
rational val;
|
rational val;
|
||||||
if (a.is_numeral(term, val)) {
|
if (a.is_numeral(term, val)) {
|
||||||
|
@ -798,9 +852,14 @@ public:
|
||||||
th(th), m(m),
|
th(th), m(m),
|
||||||
m_arith_params(ap),
|
m_arith_params(ap),
|
||||||
a(m),
|
a(m),
|
||||||
|
m_has_int(false),
|
||||||
m_arith_eq_adapter(th, ap, a),
|
m_arith_eq_adapter(th, ap, a),
|
||||||
m_internalize_head(0),
|
m_internalize_head(0),
|
||||||
m_not_handled(0),
|
m_one_var(UINT_MAX),
|
||||||
|
m_zero_var(UINT_MAX),
|
||||||
|
m_rone_var(UINT_MAX),
|
||||||
|
m_rzero_var(UINT_MAX),
|
||||||
|
m_not_handled(nullptr),
|
||||||
m_asserted_qhead(0),
|
m_asserted_qhead(0),
|
||||||
m_assume_eq_head(0),
|
m_assume_eq_head(0),
|
||||||
m_num_conflicts(0),
|
m_num_conflicts(0),
|
||||||
|
@ -871,16 +930,18 @@ public:
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_arith(enode* n) {
|
||||||
|
return n && n->get_th_var(get_id()) != null_theory_var;
|
||||||
|
}
|
||||||
|
|
||||||
void internalize_eq_eh(app * atom, bool_var) {
|
void internalize_eq_eh(app * atom, bool_var) {
|
||||||
expr* lhs = 0, *rhs = 0;
|
TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";);
|
||||||
|
expr* lhs = nullptr, *rhs = nullptr;
|
||||||
VERIFY(m.is_eq(atom, lhs, rhs));
|
VERIFY(m.is_eq(atom, lhs, rhs));
|
||||||
enode * n1 = get_enode(lhs);
|
enode * n1 = get_enode(lhs);
|
||||||
enode * n2 = get_enode(rhs);
|
enode * n2 = get_enode(rhs);
|
||||||
if (n1->get_th_var(get_id()) != null_theory_var &&
|
if (is_arith(n1) && is_arith(n2) && n1 != n2) {
|
||||||
n2->get_th_var(get_id()) != null_theory_var &&
|
|
||||||
n1 != n2) {
|
|
||||||
TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";);
|
|
||||||
m_arith_eq_adapter.mk_axioms(n1, n2);
|
m_arith_eq_adapter.mk_axioms(n1, n2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,6 +971,7 @@ public:
|
||||||
scope& s = m_scopes.back();
|
scope& s = m_scopes.back();
|
||||||
s.m_bounds_lim = m_bounds_trail.size();
|
s.m_bounds_lim = m_bounds_trail.size();
|
||||||
s.m_asserted_qhead = m_asserted_qhead;
|
s.m_asserted_qhead = m_asserted_qhead;
|
||||||
|
s.m_idiv_lim = m_idiv_terms.size();
|
||||||
s.m_asserted_atoms_lim = m_asserted_atoms.size();
|
s.m_asserted_atoms_lim = m_asserted_atoms.size();
|
||||||
s.m_not_handled = m_not_handled;
|
s.m_not_handled = m_not_handled;
|
||||||
s.m_underspecified_lim = m_underspecified.size();
|
s.m_underspecified_lim = m_underspecified.size();
|
||||||
|
@ -935,6 +997,7 @@ public:
|
||||||
}
|
}
|
||||||
m_theory_var2var_index[m_var_trail[i]] = UINT_MAX;
|
m_theory_var2var_index[m_var_trail[i]] = UINT_MAX;
|
||||||
}
|
}
|
||||||
|
m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim);
|
||||||
m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim);
|
m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim);
|
||||||
m_asserted_qhead = m_scopes[old_size].m_asserted_qhead;
|
m_asserted_qhead = m_scopes[old_size].m_asserted_qhead;
|
||||||
m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim);
|
m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim);
|
||||||
|
@ -1030,37 +1093,74 @@ public:
|
||||||
add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero()));
|
add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero()));
|
||||||
add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::GE, rational::zero()));
|
add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::GE, rational::zero()));
|
||||||
add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::LT, abs(r)));
|
add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::LT, abs(r)));
|
||||||
|
TRACE("arith", m_solver->print_constraints(tout << term << "\n"););
|
||||||
}
|
}
|
||||||
|
|
||||||
void mk_idiv_mod_axioms(expr * p, expr * q) {
|
void mk_idiv_mod_axioms(expr * p, expr * q) {
|
||||||
if (a.is_zero(q)) {
|
if (a.is_zero(q)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
TRACE("arith", tout << expr_ref(p, m) << " " << expr_ref(q, m) << "\n";);
|
||||||
// if q is zero, then idiv and mod are uninterpreted functions.
|
// if q is zero, then idiv and mod are uninterpreted functions.
|
||||||
expr_ref div(a.mk_idiv(p, q), m);
|
expr_ref div(a.mk_idiv(p, q), m);
|
||||||
expr_ref mod(a.mk_mod(p, q), m);
|
expr_ref mod(a.mk_mod(p, q), m);
|
||||||
expr_ref zero(a.mk_int(0), m);
|
expr_ref zero(a.mk_int(0), m);
|
||||||
literal q_ge_0 = mk_literal(a.mk_ge(q, zero));
|
|
||||||
literal q_le_0 = mk_literal(a.mk_le(q, zero));
|
|
||||||
// literal eqz = th.mk_eq(q, zero, false);
|
|
||||||
literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false);
|
literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false);
|
||||||
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
|
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
|
||||||
// q >= 0 or p = (p mod q) + q * (p div q)
|
literal div_ge_0 = mk_literal(a.mk_ge(div, zero));
|
||||||
// q <= 0 or p = (p mod q) + q * (p div q)
|
literal div_le_0 = mk_literal(a.mk_le(div, zero));
|
||||||
// q >= 0 or (p mod q) >= 0
|
literal p_ge_0 = mk_literal(a.mk_ge(p, zero));
|
||||||
// q <= 0 or (p mod q) >= 0
|
literal p_le_0 = mk_literal(a.mk_le(p, zero));
|
||||||
// q <= 0 or (p mod q) < q
|
|
||||||
// q >= 0 or (p mod q) < -q
|
rational k(0);
|
||||||
// enable_trace("mk_bool_var");
|
expr_ref upper(m);
|
||||||
mk_axiom(q_ge_0, eq);
|
|
||||||
mk_axiom(q_le_0, eq);
|
if (a.is_numeral(q, k)) {
|
||||||
mk_axiom(q_ge_0, mod_ge_0);
|
if (k.is_pos()) {
|
||||||
mk_axiom(q_le_0, mod_ge_0);
|
upper = a.mk_numeral(k - 1, true);
|
||||||
mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero)));
|
}
|
||||||
mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero)));
|
else if (k.is_neg()) {
|
||||||
rational k;
|
upper = a.mk_numeral(-k - 1, true);
|
||||||
if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) &&
|
}
|
||||||
k.is_pos() && k < rational(8)) {
|
}
|
||||||
|
else {
|
||||||
|
k = rational::zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!k.is_zero()) {
|
||||||
|
mk_axiom(eq);
|
||||||
|
mk_axiom(mod_ge_0);
|
||||||
|
mk_axiom(mk_literal(a.mk_le(mod, upper)));
|
||||||
|
if (k.is_pos()) {
|
||||||
|
mk_axiom(~p_ge_0, div_ge_0);
|
||||||
|
mk_axiom(~p_le_0, div_le_0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mk_axiom(~p_ge_0, div_le_0);
|
||||||
|
mk_axiom(~p_le_0, div_ge_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// q >= 0 or p = (p mod q) + q * (p div q)
|
||||||
|
// q <= 0 or p = (p mod q) + q * (p div q)
|
||||||
|
// q >= 0 or (p mod q) >= 0
|
||||||
|
// q <= 0 or (p mod q) >= 0
|
||||||
|
// q <= 0 or (p mod q) < q
|
||||||
|
// q >= 0 or (p mod q) < -q
|
||||||
|
literal q_ge_0 = mk_literal(a.mk_ge(q, zero));
|
||||||
|
literal q_le_0 = mk_literal(a.mk_le(q, zero));
|
||||||
|
mk_axiom(q_ge_0, eq);
|
||||||
|
mk_axiom(q_le_0, eq);
|
||||||
|
mk_axiom(q_ge_0, mod_ge_0);
|
||||||
|
mk_axiom(q_le_0, mod_ge_0);
|
||||||
|
mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero)));
|
||||||
|
mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero)));
|
||||||
|
mk_axiom(q_le_0, ~p_ge_0, div_ge_0);
|
||||||
|
mk_axiom(q_le_0, ~p_le_0, div_le_0);
|
||||||
|
mk_axiom(q_ge_0, ~p_ge_0, div_le_0);
|
||||||
|
mk_axiom(q_ge_0, ~p_le_0, div_ge_0);
|
||||||
|
}
|
||||||
|
if (m_arith_params.m_arith_enum_const_mod && k.is_pos() && k < rational(8)) {
|
||||||
unsigned _k = k.get_unsigned();
|
unsigned _k = k.get_unsigned();
|
||||||
literal_buffer lits;
|
literal_buffer lits;
|
||||||
for (unsigned j = 0; j < _k; ++j) {
|
for (unsigned j = 0; j < _k; ++j) {
|
||||||
|
@ -1152,7 +1252,6 @@ public:
|
||||||
m_todo_terms.pop_back();
|
m_todo_terms.pop_back();
|
||||||
if (m_solver->is_term(vi)) {
|
if (m_solver->is_term(vi)) {
|
||||||
const lp::lar_term& term = m_solver->get_term(vi);
|
const lp::lar_term& term = m_solver->get_term(vi);
|
||||||
result += term.m_v * coeff;
|
|
||||||
for (const auto & i: term) {
|
for (const auto & i: term) {
|
||||||
m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff()));
|
m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff()));
|
||||||
}
|
}
|
||||||
|
@ -1189,7 +1288,6 @@ public:
|
||||||
m_todo_terms.pop_back();
|
m_todo_terms.pop_back();
|
||||||
if (m_solver->is_term(wi)) {
|
if (m_solver->is_term(wi)) {
|
||||||
const lp::lar_term& term = m_solver->get_term(wi);
|
const lp::lar_term& term = m_solver->get_term(wi);
|
||||||
result += term.m_v * coeff;
|
|
||||||
for (const auto & i : term) {
|
for (const auto & i : term) {
|
||||||
if (m_variable_values.count(i.var()) > 0) {
|
if (m_variable_values.count(i.var()) > 0) {
|
||||||
result += m_variable_values[i.var()] * coeff * i.coeff();
|
result += m_variable_values[i.var()] * coeff * i.coeff();
|
||||||
|
@ -1208,10 +1306,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_variable_values() {
|
void init_variable_values() {
|
||||||
|
reset_variable_values();
|
||||||
if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) {
|
if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) {
|
||||||
reset_variable_values();
|
TRACE("arith", tout << "update variable values\n";);
|
||||||
m_solver->get_model(m_variable_values);
|
m_solver->get_model(m_variable_values);
|
||||||
TRACE("arith", display(tout););
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,6 +1412,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
final_check_status final_check_eh() {
|
final_check_status final_check_eh() {
|
||||||
|
IF_VERBOSE(2, verbose_stream() << "final-check\n");
|
||||||
m_use_nra_model = false;
|
m_use_nra_model = false;
|
||||||
lbool is_sat = l_true;
|
lbool is_sat = l_true;
|
||||||
if (m_solver->get_status() != lp::lp_status::OPTIMAL) {
|
if (m_solver->get_status() != lp::lp_status::OPTIMAL) {
|
||||||
|
@ -1328,7 +1427,7 @@ public:
|
||||||
}
|
}
|
||||||
if (assume_eqs()) {
|
if (assume_eqs()) {
|
||||||
return FC_CONTINUE;
|
return FC_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (check_lia()) {
|
switch (check_lia()) {
|
||||||
case l_true:
|
case l_true:
|
||||||
|
@ -1340,7 +1439,7 @@ public:
|
||||||
st = FC_CONTINUE;
|
st = FC_CONTINUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (check_nra()) {
|
switch (check_nra()) {
|
||||||
case l_true:
|
case l_true:
|
||||||
break;
|
break;
|
||||||
|
@ -1378,6 +1477,18 @@ public:
|
||||||
u_map<rational> coeffs;
|
u_map<rational> coeffs;
|
||||||
term2coeffs(term, coeffs, rational::one(), offset);
|
term2coeffs(term, coeffs, rational::one(), offset);
|
||||||
offset.neg();
|
offset.neg();
|
||||||
|
TRACE("arith",
|
||||||
|
m_solver->print_term(term, tout << "term: ") << "\n";
|
||||||
|
for (auto const& kv : coeffs) {
|
||||||
|
tout << "v" << kv.m_key << " * " << kv.m_value << "\n";
|
||||||
|
}
|
||||||
|
tout << offset << "\n";
|
||||||
|
rational g(0);
|
||||||
|
for (auto const& kv : coeffs) {
|
||||||
|
g = gcd(g, kv.m_value);
|
||||||
|
}
|
||||||
|
tout << "gcd: " << g << "\n";
|
||||||
|
);
|
||||||
if (is_int) {
|
if (is_int) {
|
||||||
// 3x + 6y >= 5 -> x + 3y >= 5/3, then x + 3y >= 2
|
// 3x + 6y >= 5 -> x + 3y >= 5/3, then x + 3y >= 2
|
||||||
// 3x + 6y <= 5 -> x + 3y <= 1
|
// 3x + 6y <= 5 -> x + 3y <= 1
|
||||||
|
@ -1385,10 +1496,12 @@ public:
|
||||||
rational g = gcd_reduce(coeffs);
|
rational g = gcd_reduce(coeffs);
|
||||||
if (!g.is_one()) {
|
if (!g.is_one()) {
|
||||||
if (lower_bound) {
|
if (lower_bound) {
|
||||||
offset = div(offset + g - rational::one(), g);
|
TRACE("arith", tout << "lower: " << offset << " / " << g << " = " << offset / g << " >= " << ceil(offset / g) << "\n";);
|
||||||
|
offset = ceil(offset / g);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
offset = div(offset, g);
|
TRACE("arith", tout << "upper: " << offset << " / " << g << " = " << offset / g << " <= " << floor(offset / g) << "\n";);
|
||||||
|
offset = floor(offset / g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1408,27 +1521,247 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("arith", tout << t << ": " << atom << "\n";
|
TRACE("arith", tout << t << ": " << atom << "\n";
|
||||||
m_solver->print_term(term, tout << "bound atom: "); tout << (lower_bound?" >= ":" <= ") << k << "\n";);
|
m_solver->print_term(term, tout << "bound atom: ") << (lower_bound?" >= ":" <= ") << k << "\n";);
|
||||||
ctx().internalize(atom, true);
|
ctx().internalize(atom, true);
|
||||||
ctx().mark_as_relevant(atom.get());
|
ctx().mark_as_relevant(atom.get());
|
||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool make_sure_all_vars_have_bounds() {
|
||||||
|
if (!m_has_int) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
unsigned nv = std::min(th.get_num_vars(), m_theory_var2var_index.size());
|
||||||
|
bool all_bounded = true;
|
||||||
|
for (unsigned v = 0; v < nv; ++v) {
|
||||||
|
lp::var_index vi = m_theory_var2var_index[v];
|
||||||
|
if (vi == UINT_MAX)
|
||||||
|
continue;
|
||||||
|
if (!m_solver->is_term(vi) && !var_has_bound(vi, true) && !var_has_bound(vi, false)) {
|
||||||
|
lp::lar_term term;
|
||||||
|
term.add_monomial(rational::one(), vi);
|
||||||
|
app_ref b = mk_bound(term, rational::zero(), true);
|
||||||
|
TRACE("arith", tout << "added bound " << b << "\n";);
|
||||||
|
IF_VERBOSE(2, verbose_stream() << "bound: " << b << "\n");
|
||||||
|
all_bounded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all_bounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* n = (div p q)
|
||||||
|
*
|
||||||
|
* (div p q) * q + (mod p q) = p, (mod p q) >= 0
|
||||||
|
*
|
||||||
|
* 0 < q => (p/q <= v(p)/v(q) => n <= floor(v(p)/v(q)))
|
||||||
|
* 0 < q => (v(p)/v(q) <= p/q => v(p)/v(q) - 1 < n)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool check_idiv_bounds() {
|
||||||
|
if (m_idiv_terms.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool all_divs_valid = true;
|
||||||
|
init_variable_values();
|
||||||
|
for (expr* n : m_idiv_terms) {
|
||||||
|
expr* p = nullptr, *q = nullptr;
|
||||||
|
VERIFY(a.is_idiv(n, p, q));
|
||||||
|
theory_var v = mk_var(n);
|
||||||
|
theory_var v1 = mk_var(p);
|
||||||
|
theory_var v2 = mk_var(q);
|
||||||
|
rational r1 = get_value(v1);
|
||||||
|
rational r2;
|
||||||
|
|
||||||
|
if (!r1.is_int() || r1.is_neg()) {
|
||||||
|
// TBD
|
||||||
|
// r1 = 223/4, r2 = 2, r = 219/8
|
||||||
|
// take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0
|
||||||
|
// then
|
||||||
|
// p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2))
|
||||||
|
// p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.is_numeral(q, r2) && r2.is_pos()) {
|
||||||
|
if (get_value(v) == div(r1, r2)) continue;
|
||||||
|
|
||||||
|
rational div_r = div(r1, r2);
|
||||||
|
// p <= q * div(r1, q) + q - 1 => div(p, q) <= div(r1, r2)
|
||||||
|
// p >= q * div(r1, q) => div(r1, q) <= div(p, q)
|
||||||
|
rational mul(1);
|
||||||
|
rational hi = r2 * div_r + r2 - 1;
|
||||||
|
rational lo = r2 * div_r;
|
||||||
|
|
||||||
|
// used to normalize inequalities so they
|
||||||
|
// don't appear as 8*x >= 15, but x >= 2
|
||||||
|
expr *n1 = nullptr, *n2 = nullptr;
|
||||||
|
if (a.is_mul(p, n1, n2) && is_numeral(n1, mul) && mul.is_pos()) {
|
||||||
|
p = n2;
|
||||||
|
hi = floor(hi/mul);
|
||||||
|
lo = ceil(lo/mul);
|
||||||
|
}
|
||||||
|
literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true)));
|
||||||
|
literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true)));
|
||||||
|
literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true)));
|
||||||
|
literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true)));
|
||||||
|
mk_axiom(~p_le_r1, n_le_div);
|
||||||
|
mk_axiom(~p_ge_r1, n_ge_div);
|
||||||
|
|
||||||
|
all_divs_valid = false;
|
||||||
|
|
||||||
|
TRACE("arith",
|
||||||
|
tout << r1 << " div " << r2 << "\n";
|
||||||
|
literal_vector lits;
|
||||||
|
lits.push_back(~p_le_r1);
|
||||||
|
lits.push_back(n_le_div);
|
||||||
|
ctx().display_literals_verbose(tout, lits) << "\n\n";
|
||||||
|
lits[0] = ~p_ge_r1;
|
||||||
|
lits[1] = n_ge_div;
|
||||||
|
ctx().display_literals_verbose(tout, lits) << "\n";);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// TBD similar for non-linear division.
|
||||||
|
// better to deal with in nla_solver:
|
||||||
|
|
||||||
|
all_divs_valid = false;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// p/q <= r1/r2 => n <= div(r1, r2)
|
||||||
|
// <=>
|
||||||
|
// p*r2 <= q*r1 => n <= div(r1, r2)
|
||||||
|
//
|
||||||
|
// p/q >= r1/r2 => n >= div(r1, r2)
|
||||||
|
// <=>
|
||||||
|
// p*r2 >= r1*q => n >= div(r1, r2)
|
||||||
|
//
|
||||||
|
expr_ref zero(a.mk_int(0), m);
|
||||||
|
expr_ref divc(a.mk_numeral(div(r1, r2), true), m);
|
||||||
|
expr_ref pqr(a.mk_sub(a.mk_mul(a.mk_numeral(r2, true), p), a.mk_mul(a.mk_numeral(r1, true), q)), m);
|
||||||
|
literal pq_lhs = ~mk_literal(a.mk_le(pqr, zero));
|
||||||
|
literal pq_rhs = ~mk_literal(a.mk_ge(pqr, zero));
|
||||||
|
literal n_le_div = mk_literal(a.mk_le(n, divc));
|
||||||
|
literal n_ge_div = mk_literal(a.mk_ge(n, divc));
|
||||||
|
mk_axiom(pq_lhs, n_le_div);
|
||||||
|
mk_axiom(pq_rhs, n_ge_div);
|
||||||
|
TRACE("arith",
|
||||||
|
literal_vector lits;
|
||||||
|
lits.push_back(pq_lhs);
|
||||||
|
lits.push_back(n_le_div);
|
||||||
|
ctx().display_literals_verbose(tout, lits) << "\n";
|
||||||
|
lits[0] = pq_rhs;
|
||||||
|
lits[1] = n_ge_div;
|
||||||
|
ctx().display_literals_verbose(tout, lits) << "\n";);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_divs_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref var2expr(lp::var_index v) {
|
||||||
|
std::ostringstream name;
|
||||||
|
name << "v" << m_solver->local2external(v);
|
||||||
|
return expr_ref(m.mk_const(symbol(name.str().c_str()), a.mk_int()), m);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref multerm(rational const& r, expr* e) {
|
||||||
|
if (r.is_one()) return expr_ref(e, m);
|
||||||
|
return expr_ref(a.mk_mul(a.mk_numeral(r, true), e), m);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref term2expr(lp::lar_term const& term) {
|
||||||
|
expr_ref t(m);
|
||||||
|
expr_ref_vector ts(m);
|
||||||
|
for (auto const& p : term) {
|
||||||
|
lp::var_index wi = p.var();
|
||||||
|
if (m_solver->is_term(wi)) {
|
||||||
|
ts.push_back(multerm(p.coeff(), term2expr(m_solver->get_term(wi))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ts.push_back(multerm(p.coeff(), var2expr(wi)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ts.size() == 1) {
|
||||||
|
t = ts.back();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
t = a.mk_add(ts.size(), ts.c_ptr());
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref constraint2fml(lp::constraint_index ci) {
|
||||||
|
lp::lar_base_constraint const& c = *m_solver->constraints()[ci];
|
||||||
|
expr_ref fml(m);
|
||||||
|
expr_ref_vector ts(m);
|
||||||
|
rational rhs = c.m_right_side;
|
||||||
|
for (auto cv : c.get_left_side_coefficients()) {
|
||||||
|
ts.push_back(multerm(cv.first, var2expr(cv.second)));
|
||||||
|
}
|
||||||
|
switch (c.m_kind) {
|
||||||
|
case lp::LE: fml = a.mk_le(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break;
|
||||||
|
case lp::LT: fml = a.mk_lt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break;
|
||||||
|
case lp::GE: fml = a.mk_ge(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break;
|
||||||
|
case lp::GT: fml = a.mk_gt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break;
|
||||||
|
case lp::EQ: fml = m.mk_eq(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break;
|
||||||
|
}
|
||||||
|
return fml;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_cut_lemma(std::ostream& out, lp::lar_term const& term, lp::mpq const& k, lp::explanation const& ex, bool upper) {
|
||||||
|
m_solver->print_term(term, out << "bound: ");
|
||||||
|
out << (upper?" <= ":" >= ") << k << "\n";
|
||||||
|
for (auto const& p : term) {
|
||||||
|
lp::var_index wi = p.var();
|
||||||
|
out << p.coeff() << " * ";
|
||||||
|
if (m_solver->is_term(wi)) {
|
||||||
|
m_solver->print_term(m_solver->get_term(wi), out) << "\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out << "v" << m_solver->local2external(wi) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto const& ev : ex.m_explanation) {
|
||||||
|
m_solver->print_constraint(ev.second, out << ev.first << ": ");
|
||||||
|
}
|
||||||
|
expr_ref_vector fmls(m);
|
||||||
|
for (auto const& ev : ex.m_explanation) {
|
||||||
|
fmls.push_back(constraint2fml(ev.second));
|
||||||
|
}
|
||||||
|
expr_ref t(term2expr(term), m);
|
||||||
|
if (upper) {
|
||||||
|
fmls.push_back(m.mk_not(a.mk_ge(t, a.mk_numeral(k, true))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fmls.push_back(m.mk_not(a.mk_le(t, a.mk_numeral(k, true))));
|
||||||
|
}
|
||||||
|
ast_pp_util visitor(m);
|
||||||
|
visitor.collect(fmls);
|
||||||
|
visitor.display_decls(out);
|
||||||
|
visitor.display_asserts(out, fmls, true);
|
||||||
|
out << "(check-sat)\n";
|
||||||
|
}
|
||||||
|
|
||||||
lbool check_lia() {
|
lbool check_lia() {
|
||||||
if (m.canceled()) {
|
if (m.canceled()) {
|
||||||
TRACE("arith", tout << "canceled\n";);
|
TRACE("arith", tout << "canceled\n";);
|
||||||
return l_undef;
|
return l_undef;
|
||||||
}
|
}
|
||||||
lp::lar_term term;
|
if (!check_idiv_bounds()) {
|
||||||
lp::mpq k;
|
TRACE("arith", tout << "idiv bounds check\n";);
|
||||||
lp::explanation ex; // TBD, this should be streamlined accross different explanations
|
return l_false;
|
||||||
bool upper;
|
}
|
||||||
switch(m_lia->check(term, k, ex, upper)) {
|
m_explanation.reset();
|
||||||
|
switch (m_lia->check()) {
|
||||||
case lp::lia_move::sat:
|
case lp::lia_move::sat:
|
||||||
return l_true;
|
return l_true;
|
||||||
case lp::lia_move::branch: {
|
case lp::lia_move::branch: {
|
||||||
TRACE("arith", tout << "branch\n";);
|
TRACE("arith", tout << "branch\n";);
|
||||||
app_ref b = mk_bound(term, k, !upper);
|
app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper());
|
||||||
|
IF_VERBOSE(2, verbose_stream() << "branch " << b << "\n";);
|
||||||
// branch on term >= k + 1
|
// branch on term >= k + 1
|
||||||
// branch on term <= k
|
// branch on term <= k
|
||||||
// TBD: ctx().force_phase(ctx().get_literal(b));
|
// TBD: ctx().force_phase(ctx().get_literal(b));
|
||||||
|
@ -1440,11 +1773,13 @@ public:
|
||||||
TRACE("arith", tout << "cut\n";);
|
TRACE("arith", tout << "cut\n";);
|
||||||
++m_stats.m_gomory_cuts;
|
++m_stats.m_gomory_cuts;
|
||||||
// m_explanation implies term <= k
|
// m_explanation implies term <= k
|
||||||
app_ref b = mk_bound(term, k, !upper);
|
app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper());
|
||||||
|
IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n");
|
||||||
|
TRACE("arith", dump_cut_lemma(tout, m_lia->get_term(), m_lia->get_offset(), m_lia->get_explanation(), m_lia->is_upper()););
|
||||||
m_eqs.reset();
|
m_eqs.reset();
|
||||||
m_core.reset();
|
m_core.reset();
|
||||||
m_params.reset();
|
m_params.reset();
|
||||||
for (auto const& ev : ex.m_explanation) {
|
for (auto const& ev : m_lia->get_explanation().m_explanation) {
|
||||||
if (!ev.first.is_zero()) {
|
if (!ev.first.is_zero()) {
|
||||||
set_evidence(ev.second);
|
set_evidence(ev.second);
|
||||||
}
|
}
|
||||||
|
@ -1457,8 +1792,9 @@ public:
|
||||||
return l_false;
|
return l_false;
|
||||||
}
|
}
|
||||||
case lp::lia_move::conflict:
|
case lp::lia_move::conflict:
|
||||||
|
TRACE("arith", tout << "conflict\n";);
|
||||||
// ex contains unsat core
|
// ex contains unsat core
|
||||||
m_explanation = ex.m_explanation;
|
m_explanation = m_lia->get_explanation().m_explanation;
|
||||||
set_conflict1();
|
set_conflict1();
|
||||||
return l_false;
|
return l_false;
|
||||||
case lp::lia_move::undef:
|
case lp::lia_move::undef:
|
||||||
|
@ -2062,18 +2398,18 @@ public:
|
||||||
SASSERT(!bounds.empty());
|
SASSERT(!bounds.empty());
|
||||||
if (bounds.size() == 1) return;
|
if (bounds.size() == 1) return;
|
||||||
if (m_unassigned_bounds[v] == 0) return;
|
if (m_unassigned_bounds[v] == 0) return;
|
||||||
|
bool v_is_int = is_int(v);
|
||||||
literal lit1(bv, !is_true);
|
literal lit1(bv, !is_true);
|
||||||
literal lit2 = null_literal;
|
literal lit2 = null_literal;
|
||||||
bool find_glb = (is_true == (k == lp_api::lower_t));
|
bool find_glb = (is_true == (k == lp_api::lower_t));
|
||||||
|
TRACE("arith", tout << "find_glb: " << find_glb << " is_true: " << is_true << " k: " << k << " is_lower: " << (k == lp_api::lower_t) << "\n";);
|
||||||
if (find_glb) {
|
if (find_glb) {
|
||||||
rational glb;
|
rational glb;
|
||||||
lp_api::bound* lb = 0;
|
lp_api::bound* lb = nullptr;
|
||||||
for (unsigned i = 0; i < bounds.size(); ++i) {
|
for (lp_api::bound* b2 : bounds) {
|
||||||
lp_api::bound* b2 = bounds[i];
|
|
||||||
if (b2 == &b) continue;
|
if (b2 == &b) continue;
|
||||||
rational const& val2 = b2->get_value();
|
rational const& val2 = b2->get_value();
|
||||||
if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) {
|
if (((is_true || v_is_int) ? val2 < val : val2 <= val) && (!lb || glb < val2)) {
|
||||||
lb = b2;
|
lb = b2;
|
||||||
glb = val2;
|
glb = val2;
|
||||||
}
|
}
|
||||||
|
@ -2084,12 +2420,11 @@ public:
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rational lub;
|
rational lub;
|
||||||
lp_api::bound* ub = 0;
|
lp_api::bound* ub = nullptr;
|
||||||
for (unsigned i = 0; i < bounds.size(); ++i) {
|
for (lp_api::bound* b2 : bounds) {
|
||||||
lp_api::bound* b2 = bounds[i];
|
|
||||||
if (b2 == &b) continue;
|
if (b2 == &b) continue;
|
||||||
rational const& val2 = b2->get_value();
|
rational const& val2 = b2->get_value();
|
||||||
if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) {
|
if (((is_true || v_is_int) ? val < val2 : val <= val2) && (!ub || val2 < lub)) {
|
||||||
ub = b2;
|
ub = b2;
|
||||||
lub = val2;
|
lub = val2;
|
||||||
}
|
}
|
||||||
|
@ -2107,7 +2442,7 @@ public:
|
||||||
m_params.reset();
|
m_params.reset();
|
||||||
m_core.reset();
|
m_core.reset();
|
||||||
m_eqs.reset();
|
m_eqs.reset();
|
||||||
m_core.push_back(lit2);
|
m_core.push_back(lit1);
|
||||||
m_params.push_back(parameter(symbol("farkas")));
|
m_params.push_back(parameter(symbol("farkas")));
|
||||||
m_params.push_back(parameter(rational(1)));
|
m_params.push_back(parameter(rational(1)));
|
||||||
m_params.push_back(parameter(rational(1)));
|
m_params.push_back(parameter(rational(1)));
|
||||||
|
@ -2383,6 +2718,18 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool var_has_bound(lp::var_index vi, bool is_lower) {
|
||||||
|
bool is_strict = false;
|
||||||
|
rational b;
|
||||||
|
lp::constraint_index ci;
|
||||||
|
if (is_lower) {
|
||||||
|
return m_solver->has_lower_bound(vi, ci, b, is_strict);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return m_solver->has_upper_bound(vi, ci, b, is_strict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); }
|
bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); }
|
||||||
|
|
||||||
bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); }
|
bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); }
|
||||||
|
@ -2606,7 +2953,7 @@ public:
|
||||||
m_todo_terms.push_back(std::make_pair(vi, rational::one()));
|
m_todo_terms.push_back(std::make_pair(vi, rational::one()));
|
||||||
|
|
||||||
TRACE("arith", tout << "v" << v << " := w" << vi << "\n";
|
TRACE("arith", tout << "v" << v << " := w" << vi << "\n";
|
||||||
m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";);
|
m_solver->print_term(m_solver->get_term(vi), tout) << "\n";);
|
||||||
|
|
||||||
m_nra->am().set(r, 0);
|
m_nra->am().set(r, 0);
|
||||||
while (!m_todo_terms.empty()) {
|
while (!m_todo_terms.empty()) {
|
||||||
|
@ -2614,13 +2961,13 @@ public:
|
||||||
vi = m_todo_terms.back().first;
|
vi = m_todo_terms.back().first;
|
||||||
m_todo_terms.pop_back();
|
m_todo_terms.pop_back();
|
||||||
lp::lar_term const& term = m_solver->get_term(vi);
|
lp::lar_term const& term = m_solver->get_term(vi);
|
||||||
TRACE("arith", m_solver->print_term(term, tout); tout << "\n";);
|
TRACE("arith", m_solver->print_term(term, tout) << "\n";);
|
||||||
scoped_anum r1(m_nra->am());
|
scoped_anum r1(m_nra->am());
|
||||||
rational c1 = term.m_v * wcoeff;
|
rational c1(0);
|
||||||
m_nra->am().set(r1, c1.to_mpq());
|
m_nra->am().set(r1, c1.to_mpq());
|
||||||
m_nra->am().add(r, r1, r);
|
m_nra->am().add(r, r1, r);
|
||||||
for (auto const & arg : term) {
|
for (auto const & arg : term) {
|
||||||
lp::var_index wi = m_solver->local2external(arg.var());
|
lp::var_index wi = arg.var();
|
||||||
c1 = arg.coeff() * wcoeff;
|
c1 = arg.coeff() * wcoeff;
|
||||||
if (m_solver->is_term(wi)) {
|
if (m_solver->is_term(wi)) {
|
||||||
m_todo_terms.push_back(std::make_pair(wi, c1));
|
m_todo_terms.push_back(std::make_pair(wi, c1));
|
||||||
|
@ -2658,13 +3005,23 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_value(enode* n, expr_ref& r) {
|
bool get_value(enode* n, rational& val) {
|
||||||
theory_var v = n->get_th_var(get_id());
|
theory_var v = n->get_th_var(get_id());
|
||||||
if (!can_get_bound(v)) return false;
|
if (!can_get_bound(v)) return false;
|
||||||
lp::var_index vi = m_theory_var2var_index[v];
|
lp::var_index vi = m_theory_var2var_index[v];
|
||||||
rational val;
|
|
||||||
if (m_solver->has_value(vi, val)) {
|
if (m_solver->has_value(vi, val)) {
|
||||||
|
TRACE("arith", tout << expr_ref(n->get_owner(), m) << " := " << val << "\n";);
|
||||||
if (is_int(n) && !val.is_int()) return false;
|
if (is_int(n) && !val.is_int()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_value(enode* n, expr_ref& r) {
|
||||||
|
rational val;
|
||||||
|
if (get_value(n, val)) {
|
||||||
r = a.mk_numeral(val, is_int(n));
|
r = a.mk_numeral(val, is_int(n));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2673,7 +3030,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_lower(enode* n, expr_ref& r) {
|
bool get_lower(enode* n, rational& val, bool& is_strict) {
|
||||||
theory_var v = n->get_th_var(get_id());
|
theory_var v = n->get_th_var(get_id());
|
||||||
if (!can_get_bound(v)) {
|
if (!can_get_bound(v)) {
|
||||||
TRACE("arith", tout << "cannot get lower for " << v << "\n";);
|
TRACE("arith", tout << "cannot get lower for " << v << "\n";);
|
||||||
|
@ -2681,29 +3038,36 @@ public:
|
||||||
}
|
}
|
||||||
lp::var_index vi = m_theory_var2var_index[v];
|
lp::var_index vi = m_theory_var2var_index[v];
|
||||||
lp::constraint_index ci;
|
lp::constraint_index ci;
|
||||||
rational val;
|
return m_solver->has_lower_bound(vi, ci, val, is_strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_lower(enode* n, expr_ref& r) {
|
||||||
bool is_strict;
|
bool is_strict;
|
||||||
if (m_solver->has_lower_bound(vi, ci, val, is_strict)) {
|
rational val;
|
||||||
|
if (get_lower(n, val, is_strict) && !is_strict) {
|
||||||
r = a.mk_numeral(val, is_int(n));
|
r = a.mk_numeral(val, is_int(n));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
TRACE("arith", m_solver->print_constraints(tout << "does not have lower bound " << vi << "\n"););
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_upper(enode* n, expr_ref& r) {
|
bool get_upper(enode* n, rational& val, bool& is_strict) {
|
||||||
theory_var v = n->get_th_var(get_id());
|
theory_var v = n->get_th_var(get_id());
|
||||||
if (!can_get_bound(v))
|
if (!can_get_bound(v))
|
||||||
return false;
|
return false;
|
||||||
lp::var_index vi = m_theory_var2var_index[v];
|
lp::var_index vi = m_theory_var2var_index[v];
|
||||||
lp::constraint_index ci;
|
lp::constraint_index ci;
|
||||||
rational val;
|
return m_solver->has_upper_bound(vi, ci, val, is_strict);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_upper(enode* n, expr_ref& r) {
|
||||||
bool is_strict;
|
bool is_strict;
|
||||||
if (m_solver->has_upper_bound(vi, ci, val, is_strict)) {
|
rational val;
|
||||||
|
if (get_upper(n, val, is_strict) && !is_strict) {
|
||||||
r = a.mk_numeral(val, is_int(n));
|
r = a.mk_numeral(val, is_int(n));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
TRACE("arith", m_solver->print_constraints(tout << "does not have upper bound " << vi << "\n"););
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2875,7 +3239,6 @@ public:
|
||||||
coeffs.find(w, c0);
|
coeffs.find(w, c0);
|
||||||
coeffs.insert(w, c0 + ti.coeff() * coeff);
|
coeffs.insert(w, c0 + ti.coeff() * coeff);
|
||||||
}
|
}
|
||||||
offset += coeff * term.m_v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app_ref coeffs2app(u_map<rational> const& coeffs, rational const& offset, bool is_int) {
|
app_ref coeffs2app(u_map<rational> const& coeffs, rational const& offset, bool is_int) {
|
||||||
|
@ -2915,15 +3278,17 @@ public:
|
||||||
|
|
||||||
rational gcd_reduce(u_map<rational>& coeffs) {
|
rational gcd_reduce(u_map<rational>& coeffs) {
|
||||||
rational g(0);
|
rational g(0);
|
||||||
for (auto const& kv : coeffs) {
|
for (auto const& kv : coeffs) {
|
||||||
g = gcd(g, kv.m_value);
|
g = gcd(g, kv.m_value);
|
||||||
}
|
}
|
||||||
if (!g.is_one() && !g.is_zero()) {
|
if (g.is_zero())
|
||||||
for (auto& kv : coeffs) {
|
return rational::one();
|
||||||
kv.m_value /= g;
|
if (!g.is_one()) {
|
||||||
}
|
for (auto& kv : coeffs) {
|
||||||
}
|
kv.m_value /= g;
|
||||||
return g;
|
}
|
||||||
|
}
|
||||||
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
app_ref mk_obj(theory_var v) {
|
app_ref mk_obj(theory_var v) {
|
||||||
|
@ -2951,7 +3316,7 @@ public:
|
||||||
}
|
}
|
||||||
if (!ctx().b_internalized(b)) {
|
if (!ctx().b_internalized(b)) {
|
||||||
fm.hide(b->get_decl());
|
fm.hide(b->get_decl());
|
||||||
bool_var bv = ctx().mk_bool_var(b);
|
bool_var bv = ctx().mk_bool_var(b);
|
||||||
ctx().set_var_theory(bv, get_id());
|
ctx().set_var_theory(bv, get_id());
|
||||||
// ctx().set_enode_flag(bv, true);
|
// ctx().set_enode_flag(bv, true);
|
||||||
lp_api::bound_kind bkind = lp_api::bound_kind::lower_t;
|
lp_api::bound_kind bkind = lp_api::bound_kind::lower_t;
|
||||||
|
@ -3132,6 +3497,9 @@ void theory_lra::init_model(model_generator & m) {
|
||||||
model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) {
|
model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) {
|
||||||
return m_imp->mk_value(n, mg);
|
return m_imp->mk_value(n, mg);
|
||||||
}
|
}
|
||||||
|
bool theory_lra::get_value(enode* n, rational& r) {
|
||||||
|
return m_imp->get_value(n, r);
|
||||||
|
}
|
||||||
bool theory_lra::get_value(enode* n, expr_ref& r) {
|
bool theory_lra::get_value(enode* n, expr_ref& r) {
|
||||||
return m_imp->get_value(n, r);
|
return m_imp->get_value(n, r);
|
||||||
}
|
}
|
||||||
|
@ -3141,6 +3509,12 @@ bool theory_lra::get_lower(enode* n, expr_ref& r) {
|
||||||
bool theory_lra::get_upper(enode* n, expr_ref& r) {
|
bool theory_lra::get_upper(enode* n, expr_ref& r) {
|
||||||
return m_imp->get_upper(n, r);
|
return m_imp->get_upper(n, r);
|
||||||
}
|
}
|
||||||
|
bool theory_lra::get_lower(enode* n, rational& r, bool& is_strict) {
|
||||||
|
return m_imp->get_lower(n, r, is_strict);
|
||||||
|
}
|
||||||
|
bool theory_lra::get_upper(enode* n, rational& r, bool& is_strict) {
|
||||||
|
return m_imp->get_upper(n, r, is_strict);
|
||||||
|
}
|
||||||
|
|
||||||
bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const {
|
bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const {
|
||||||
return m_imp->validate_eq_in_model(v1, v2, is_true);
|
return m_imp->validate_eq_in_model(v1, v2, is_true);
|
||||||
|
|
|
@ -78,8 +78,11 @@ namespace smt {
|
||||||
model_value_proc * mk_value(enode * n, model_generator & mg) override;
|
model_value_proc * mk_value(enode * n, model_generator & mg) override;
|
||||||
|
|
||||||
bool get_value(enode* n, expr_ref& r) override;
|
bool get_value(enode* n, expr_ref& r) override;
|
||||||
|
bool get_value(enode* n, rational& r);
|
||||||
bool get_lower(enode* n, expr_ref& r);
|
bool get_lower(enode* n, expr_ref& r);
|
||||||
bool get_upper(enode* n, expr_ref& r);
|
bool get_upper(enode* n, expr_ref& r);
|
||||||
|
bool get_lower(enode* n, rational& r, bool& is_strict);
|
||||||
|
bool get_upper(enode* n, rational& r, bool& is_strict);
|
||||||
|
|
||||||
bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override;
|
bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override;
|
||||||
|
|
||||||
|
|
|
@ -4588,10 +4588,10 @@ bool theory_seq::lower_bound2(expr* _e, rational& lo) {
|
||||||
theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, m_autil.get_family_id(), e);
|
theory_mi_arith* tha = get_th_arith<theory_mi_arith>(ctx, m_autil.get_family_id(), e);
|
||||||
if (!tha) {
|
if (!tha) {
|
||||||
theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, m_autil.get_family_id(), e);
|
theory_i_arith* thi = get_th_arith<theory_i_arith>(ctx, m_autil.get_family_id(), e);
|
||||||
if (!thi || !thi->get_lower(ctx.get_enode(e), _lo)) return false;
|
if (!thi || !thi->get_lower(ctx.get_enode(e), _lo) || !m_autil.is_numeral(_lo, lo)) return false;
|
||||||
}
|
}
|
||||||
enode *ee = ctx.get_enode(e);
|
enode *ee = ctx.get_enode(e);
|
||||||
if (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo)) {
|
if (tha && (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo))) {
|
||||||
enode *next = ee->get_next();
|
enode *next = ee->get_next();
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
while (next != ee) {
|
while (next != ee) {
|
||||||
|
|
|
@ -217,13 +217,12 @@ struct mus::imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_set mss_set;
|
expr_set mss_set;
|
||||||
for (unsigned i = 0; i < mss.size(); ++i) {
|
for (expr* e : mss) {
|
||||||
mss_set.insert(mss[i]);
|
mss_set.insert(e);
|
||||||
}
|
}
|
||||||
expr_set::iterator it = min_core.begin(), end = min_core.end();
|
for (expr * e : min_core) {
|
||||||
for (; it != end; ++it) {
|
if (mss_set.contains(e) && min_lit != e) {
|
||||||
if (mss_set.contains(*it) && min_lit != *it) {
|
unknown.push_back(e);
|
||||||
unknown.push_back(*it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core_literal = min_lit;
|
core_literal = min_lit;
|
||||||
|
|
|
@ -178,10 +178,19 @@ lbool solver::preferred_sat(expr_ref_vector const& asms, vector<expr_ref_vector>
|
||||||
return check_sat(0, nullptr);
|
return check_sat(0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool solver::is_literal(ast_manager& m, expr* e) {
|
|
||||||
return is_uninterp_const(e) || (m.is_not(e, e) && is_uninterp_const(e));
|
static bool is_m_atom(ast_manager& m, expr* f) {
|
||||||
|
if (!is_app(f)) return true;
|
||||||
|
app* _f = to_app(f);
|
||||||
|
family_id bfid = m.get_basic_family_id();
|
||||||
|
if (_f->get_family_id() != bfid) return true;
|
||||||
|
if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) return false;
|
||||||
|
return m.is_eq(f) || m.is_distinct(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool solver::is_literal(ast_manager& m, expr* e) {
|
||||||
|
return is_m_atom(m, e) || (m.is_not(e, e) && is_m_atom(m, e));
|
||||||
|
}
|
||||||
|
|
||||||
void solver::assert_expr(expr* f) {
|
void solver::assert_expr(expr* f) {
|
||||||
expr_ref fml(f, get_manager());
|
expr_ref fml(f, get_manager());
|
||||||
|
@ -256,3 +265,40 @@ expr_ref_vector solver::get_units(ast_manager& m) {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
expr_ref_vector solver::get_non_units(ast_manager& m) {
|
||||||
|
expr_ref_vector result(m), fmls(m);
|
||||||
|
get_assertions(fmls);
|
||||||
|
family_id bfid = m.get_basic_family_id();
|
||||||
|
expr_mark marked;
|
||||||
|
unsigned sz0 = fmls.size();
|
||||||
|
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||||
|
expr* f = fmls.get(i);
|
||||||
|
if (marked.is_marked(f)) continue;
|
||||||
|
marked.mark(f);
|
||||||
|
if (!is_app(f)) {
|
||||||
|
if (i >= sz0) result.push_back(f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
app* _f = to_app(f);
|
||||||
|
if (_f->get_family_id() == bfid) {
|
||||||
|
// basic objects are true/false/and/or/not/=/distinct
|
||||||
|
// and proof objects (that are not Boolean).
|
||||||
|
if (i < sz0 && m.is_not(f) && is_m_atom(m, _f->get_arg(0))) {
|
||||||
|
marked.mark(_f->get_arg(0));
|
||||||
|
}
|
||||||
|
else if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) {
|
||||||
|
fmls.append(_f->get_num_args(), _f->get_args());
|
||||||
|
}
|
||||||
|
else if (i >= sz0 && is_m_atom(m, f)) {
|
||||||
|
result.push_back(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
if (i >= sz0) result.push_back(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -236,6 +236,8 @@ public:
|
||||||
*/
|
*/
|
||||||
expr_ref_vector get_units(ast_manager& m);
|
expr_ref_vector get_units(ast_manager& m);
|
||||||
|
|
||||||
|
expr_ref_vector get_non_units(ast_manager& m);
|
||||||
|
|
||||||
class scoped_push {
|
class scoped_push {
|
||||||
solver& s;
|
solver& s;
|
||||||
bool m_nopop;
|
bool m_nopop;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
namespace lp {
|
namespace lp {
|
||||||
|
#include "util/lp/lp_utils.h"
|
||||||
struct gomory_test {
|
struct gomory_test {
|
||||||
gomory_test(
|
gomory_test(
|
||||||
std::function<std::string (unsigned)> name_function_p,
|
std::function<std::string (unsigned)> name_function_p,
|
||||||
|
@ -88,7 +89,7 @@ struct gomory_test {
|
||||||
lp_assert(is_int(x_j));
|
lp_assert(is_int(x_j));
|
||||||
lp_assert(!a.is_int());
|
lp_assert(!a.is_int());
|
||||||
lp_assert(f_0 > zero_of_type<mpq>() && f_0 < one_of_type<mpq>());
|
lp_assert(f_0 > zero_of_type<mpq>() && f_0 < one_of_type<mpq>());
|
||||||
mpq f_j = int_solver::fractional_part(a);
|
mpq f_j = fractional_part(a);
|
||||||
TRACE("gomory_cut_detail",
|
TRACE("gomory_cut_detail",
|
||||||
tout << a << " x_j = " << x_j << ", k = " << k << "\n";
|
tout << a << " x_j = " << x_j << ", k = " << k << "\n";
|
||||||
tout << "f_j: " << f_j << "\n";
|
tout << "f_j: " << f_j << "\n";
|
||||||
|
@ -184,7 +185,6 @@ struct gomory_test {
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_term(lar_term & t, std::ostream & out) {
|
void print_term(lar_term & t, std::ostream & out) {
|
||||||
lp_assert(is_zero(t.m_v));
|
|
||||||
vector<std::pair<mpq, unsigned>> row;
|
vector<std::pair<mpq, unsigned>> row;
|
||||||
for (auto p : t.m_coeffs)
|
for (auto p : t.m_coeffs)
|
||||||
row.push_back(std::make_pair(p.second, p.first));
|
row.push_back(std::make_pair(p.second, p.first));
|
||||||
|
@ -206,7 +206,7 @@ struct gomory_test {
|
||||||
unsigned x_j;
|
unsigned x_j;
|
||||||
mpq a;
|
mpq a;
|
||||||
bool some_int_columns = false;
|
bool some_int_columns = false;
|
||||||
mpq f_0 = int_solver::fractional_part(get_value(inf_col));
|
mpq f_0 = fractional_part(get_value(inf_col));
|
||||||
mpq one_min_f_0 = 1 - f_0;
|
mpq one_min_f_0 = 1 - f_0;
|
||||||
for ( auto pp : row) {
|
for ( auto pp : row) {
|
||||||
a = pp.first;
|
a = pp.first;
|
||||||
|
|
|
@ -2667,13 +2667,20 @@ void test_term() {
|
||||||
lar_solver solver;
|
lar_solver solver;
|
||||||
unsigned _x = 0;
|
unsigned _x = 0;
|
||||||
unsigned _y = 1;
|
unsigned _y = 1;
|
||||||
|
unsigned _one = 2;
|
||||||
var_index x = solver.add_var(_x, false);
|
var_index x = solver.add_var(_x, false);
|
||||||
var_index y = solver.add_var(_y, false);
|
var_index y = solver.add_var(_y, false);
|
||||||
|
var_index one = solver.add_var(_one, false);
|
||||||
|
|
||||||
|
vector<std::pair<mpq, var_index>> term_one;
|
||||||
|
term_one.push_back(std::make_pair((int)1, one));
|
||||||
|
solver.add_constraint(term_one, lconstraint_kind::EQ, mpq(0));
|
||||||
|
|
||||||
vector<std::pair<mpq, var_index>> term_ls;
|
vector<std::pair<mpq, var_index>> term_ls;
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
term_ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)1, y));
|
term_ls.push_back(std::pair<mpq, var_index>((int)1, y));
|
||||||
var_index z = solver.add_term(term_ls, mpq(3));
|
term_ls.push_back(std::make_pair((int)3, one));
|
||||||
|
var_index z = solver.add_term(term_ls);
|
||||||
|
|
||||||
vector<std::pair<mpq, var_index>> ls;
|
vector<std::pair<mpq, var_index>> ls;
|
||||||
ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
||||||
|
@ -2743,10 +2750,10 @@ void test_bound_propagation_one_small_sample1() {
|
||||||
vector<std::pair<mpq, var_index>> coeffs;
|
vector<std::pair<mpq, var_index>> coeffs;
|
||||||
coeffs.push_back(std::pair<mpq, var_index>(1, a));
|
coeffs.push_back(std::pair<mpq, var_index>(1, a));
|
||||||
coeffs.push_back(std::pair<mpq, var_index>(-1, c));
|
coeffs.push_back(std::pair<mpq, var_index>(-1, c));
|
||||||
ls.add_term(coeffs, zero_of_type<mpq>());
|
ls.add_term(coeffs);
|
||||||
coeffs.pop_back();
|
coeffs.pop_back();
|
||||||
coeffs.push_back(std::pair<mpq, var_index>(-1, b));
|
coeffs.push_back(std::pair<mpq, var_index>(-1, b));
|
||||||
ls.add_term(coeffs, zero_of_type<mpq>());
|
ls.add_term(coeffs);
|
||||||
coeffs.clear();
|
coeffs.clear();
|
||||||
coeffs.push_back(std::pair<mpq, var_index>(1, a));
|
coeffs.push_back(std::pair<mpq, var_index>(1, a));
|
||||||
coeffs.push_back(std::pair<mpq, var_index>(-1, b));
|
coeffs.push_back(std::pair<mpq, var_index>(-1, b));
|
||||||
|
@ -3485,12 +3492,12 @@ void test_maximize_term() {
|
||||||
vector<std::pair<mpq, var_index>> term_ls;
|
vector<std::pair<mpq, var_index>> term_ls;
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
term_ls.push_back(std::pair<mpq, var_index>((int)1, x));
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)-1, y));
|
term_ls.push_back(std::pair<mpq, var_index>((int)-1, y));
|
||||||
unsigned term_x_min_y = solver.add_term(term_ls, mpq(0));
|
unsigned term_x_min_y = solver.add_term(term_ls);
|
||||||
term_ls.clear();
|
term_ls.clear();
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)2, x));
|
term_ls.push_back(std::pair<mpq, var_index>((int)2, x));
|
||||||
term_ls.push_back(std::pair<mpq, var_index>((int)2, y));
|
term_ls.push_back(std::pair<mpq, var_index>((int)2, y));
|
||||||
|
|
||||||
unsigned term_2x_pl_2y = solver.add_term(term_ls, mpq(0));
|
unsigned term_2x_pl_2y = solver.add_term(term_ls);
|
||||||
solver.add_var_bound(term_x_min_y, LE, zero_of_type<mpq>());
|
solver.add_var_bound(term_x_min_y, LE, zero_of_type<mpq>());
|
||||||
solver.add_var_bound(term_2x_pl_2y, LE, mpq((int)5));
|
solver.add_var_bound(term_2x_pl_2y, LE, mpq((int)5));
|
||||||
solver.find_feasible_solution();
|
solver.find_feasible_solution();
|
||||||
|
@ -3502,8 +3509,7 @@ void test_maximize_term() {
|
||||||
std::cout<< "v[" << p.first << "] = " << p.second << std::endl;
|
std::cout<< "v[" << p.first << "] = " << p.second << std::endl;
|
||||||
}
|
}
|
||||||
std::cout << "calling int_solver\n";
|
std::cout << "calling int_solver\n";
|
||||||
lar_term t; mpq k; explanation ex; bool upper;
|
lia_move lm = i_solver.check();
|
||||||
lia_move lm = i_solver.check(t, k, ex, upper);
|
|
||||||
VERIFY(lm == lia_move::sat);
|
VERIFY(lm == lia_move::sat);
|
||||||
impq term_max;
|
impq term_max;
|
||||||
lp_status st = solver.maximize_term(term_2x_pl_2y, term_max);
|
lp_status st = solver.maximize_term(term_2x_pl_2y, term_max);
|
||||||
|
|
|
@ -18,6 +18,7 @@ Copyright (c) 2015 Microsoft Corporation
|
||||||
#include "ast/rewriter/th_rewriter.h"
|
#include "ast/rewriter/th_rewriter.h"
|
||||||
#include "tactic/fd_solver/fd_solver.h"
|
#include "tactic/fd_solver/fd_solver.h"
|
||||||
#include "solver/solver.h"
|
#include "solver/solver.h"
|
||||||
|
#include "ast/arith_decl_plugin.h"
|
||||||
|
|
||||||
static void test1() {
|
static void test1() {
|
||||||
ast_manager m;
|
ast_manager m;
|
||||||
|
@ -194,9 +195,20 @@ static void test3() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test4() {
|
||||||
|
ast_manager m;
|
||||||
|
reg_decl_plugins(m);
|
||||||
|
arith_util arith(m);
|
||||||
|
expr_ref a(m.mk_const(symbol("a"), arith.mk_int()), m);
|
||||||
|
expr_ref b(m.mk_const(symbol("b"), arith.mk_int()), m);
|
||||||
|
expr_ref eq(m.mk_eq(a,b), m);
|
||||||
|
std::cout << "is_atom: " << is_atom(m, eq) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
void tst_pb2bv() {
|
void tst_pb2bv() {
|
||||||
test1();
|
test1();
|
||||||
test2();
|
test2();
|
||||||
test3();
|
test3();
|
||||||
|
test4();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.h")
|
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h")
|
||||||
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/version.h\""
|
message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h\""
|
||||||
${z3_polluted_tree_msg}
|
${z3_polluted_tree_msg}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"")
|
set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"")
|
||||||
configure_file(version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
|
configure_file(z3_version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/z3_version.h)
|
||||||
|
|
||||||
|
|
||||||
z3_add_component(util
|
z3_add_component(util
|
||||||
SOURCES
|
SOURCES
|
||||||
|
|
|
@ -6,6 +6,7 @@ z3_add_component(lp
|
||||||
core_solver_pretty_printer.cpp
|
core_solver_pretty_printer.cpp
|
||||||
dense_matrix.cpp
|
dense_matrix.cpp
|
||||||
eta_matrix.cpp
|
eta_matrix.cpp
|
||||||
|
gomory.cpp
|
||||||
indexed_vector.cpp
|
indexed_vector.cpp
|
||||||
int_solver.cpp
|
int_solver.cpp
|
||||||
lar_solver.cpp
|
lar_solver.cpp
|
||||||
|
|
|
@ -17,10 +17,6 @@ const impq & bound_propagator::get_upper_bound(unsigned j) const {
|
||||||
}
|
}
|
||||||
void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) {
|
void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) {
|
||||||
j = m_lar_solver.adjust_column_index_to_term_index(j);
|
j = m_lar_solver.adjust_column_index_to_term_index(j);
|
||||||
if (m_lar_solver.is_term(j)) {
|
|
||||||
// lp treats terms as not having a free coefficient, restoring it below for the outside consumption
|
|
||||||
v += m_lar_solver.get_term(j).m_v;
|
|
||||||
}
|
|
||||||
|
|
||||||
lconstraint_kind kind = is_low? GE : LE;
|
lconstraint_kind kind = is_low? GE : LE;
|
||||||
if (strict)
|
if (strict)
|
||||||
|
|
|
@ -69,16 +69,6 @@ public:
|
||||||
m_column_index(static_cast<unsigned>(-1))
|
m_column_index(static_cast<unsigned>(-1))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
column_info(unsigned column_index) :
|
|
||||||
m_lower_bound_is_set(false),
|
|
||||||
m_lower_bound_is_strict(false),
|
|
||||||
m_upper_bound_is_set (false),
|
|
||||||
m_upper_bound_is_strict (false),
|
|
||||||
m_is_fixed(false),
|
|
||||||
m_cost(numeric_traits<T>::zero()),
|
|
||||||
m_column_index(column_index) {
|
|
||||||
}
|
|
||||||
|
|
||||||
column_info(const column_info & ci) {
|
column_info(const column_info & ci) {
|
||||||
m_name = ci.m_name;
|
m_name = ci.m_name;
|
||||||
m_lower_bound_is_set = ci.m_lower_bound_is_set;
|
m_lower_bound_is_set = ci.m_lower_bound_is_set;
|
||||||
|
|
|
@ -33,29 +33,6 @@ public:
|
||||||
print_linear_combination_of_column_indices(coeff, out);
|
print_linear_combination_of_column_indices(coeff, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
|
|
||||||
bool first = true;
|
|
||||||
for (const auto & it : coeffs) {
|
|
||||||
auto val = it.first;
|
|
||||||
if (first) {
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
if (numeric_traits<T>::is_pos(val)) {
|
|
||||||
out << " + ";
|
|
||||||
} else {
|
|
||||||
out << " - ";
|
|
||||||
val = -val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val == -numeric_traits<T>::one())
|
|
||||||
out << " - ";
|
|
||||||
else if (val != numeric_traits<T>::one())
|
|
||||||
out << T_to_string(val);
|
|
||||||
|
|
||||||
out << "v" << it.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
341
src/util/lp/gomory.cpp
Normal file
341
src/util/lp/gomory.cpp
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2017 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
<name>
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
<abstract>
|
||||||
|
|
||||||
|
Author:
|
||||||
|
Nikolaj Bjorner (nbjorner)
|
||||||
|
Lev Nachmanson (levnach)
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "util/lp/gomory.h"
|
||||||
|
#include "util/lp/int_solver.h"
|
||||||
|
#include "util/lp/lar_solver.h"
|
||||||
|
#include "util/lp/lp_utils.h"
|
||||||
|
namespace lp {
|
||||||
|
|
||||||
|
class gomory::imp {
|
||||||
|
lar_term & m_t; // the term to return in the cut
|
||||||
|
mpq & m_k; // the right side of the cut
|
||||||
|
explanation& m_ex; // the conflict explanation
|
||||||
|
unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value
|
||||||
|
const row_strip<mpq>& m_row;
|
||||||
|
const int_solver& m_int_solver;
|
||||||
|
mpq m_lcm_den;
|
||||||
|
mpq m_f;
|
||||||
|
mpq m_one_minus_f;
|
||||||
|
mpq m_fj;
|
||||||
|
mpq m_one_minus_fj;
|
||||||
|
|
||||||
|
const impq & get_value(unsigned j) const { return m_int_solver.get_value(j); }
|
||||||
|
bool is_real(unsigned j) const { return m_int_solver.is_real(j); }
|
||||||
|
bool at_lower(unsigned j) const { return m_int_solver.at_lower(j); }
|
||||||
|
bool at_upper(unsigned j) const { return m_int_solver.at_upper(j); }
|
||||||
|
const impq & lower_bound(unsigned j) const { return m_int_solver.lower_bound(j); }
|
||||||
|
const impq & upper_bound(unsigned j) const { return m_int_solver.upper_bound(j); }
|
||||||
|
constraint_index column_lower_bound_constraint(unsigned j) const { return m_int_solver.column_lower_bound_constraint(j); }
|
||||||
|
constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); }
|
||||||
|
bool column_is_fixed(unsigned j) const { return m_int_solver.m_lar_solver->column_is_fixed(j); }
|
||||||
|
|
||||||
|
void int_case_in_gomory_cut(unsigned j) {
|
||||||
|
lp_assert(is_int(j) && m_fj.is_pos());
|
||||||
|
TRACE("gomory_cut_detail",
|
||||||
|
tout << " k = " << m_k;
|
||||||
|
tout << ", fj: " << m_fj << ", ";
|
||||||
|
tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl;
|
||||||
|
);
|
||||||
|
mpq new_a;
|
||||||
|
if (at_lower(j)) {
|
||||||
|
new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f);
|
||||||
|
lp_assert(new_a.is_pos());
|
||||||
|
m_k.addmul(new_a, lower_bound(j).x);
|
||||||
|
m_ex.push_justification(column_lower_bound_constraint(j));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp_assert(at_upper(j));
|
||||||
|
// the upper terms are inverted: therefore we have the minus
|
||||||
|
new_a = - (m_fj <= m_f ? m_fj / m_f : ((1 - m_fj) / m_one_minus_f));
|
||||||
|
lp_assert(new_a.is_neg());
|
||||||
|
m_k.addmul(new_a, upper_bound(j).x);
|
||||||
|
m_ex.push_justification(column_upper_bound_constraint(j));
|
||||||
|
}
|
||||||
|
m_t.add_monomial(new_a, j);
|
||||||
|
m_lcm_den = lcm(m_lcm_den, denominator(new_a));
|
||||||
|
TRACE("gomory_cut_detail", tout << "v" << j << " new_a = " << new_a << ", k = " << m_k << ", m_lcm_den = " << m_lcm_den << "\n";);
|
||||||
|
}
|
||||||
|
|
||||||
|
void real_case_in_gomory_cut(const mpq & a, unsigned j) {
|
||||||
|
TRACE("gomory_cut_detail_real", tout << "real\n";);
|
||||||
|
mpq new_a;
|
||||||
|
if (at_lower(j)) {
|
||||||
|
if (a.is_pos()) {
|
||||||
|
new_a = a / m_one_minus_f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_a = - a / m_f;
|
||||||
|
}
|
||||||
|
m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than
|
||||||
|
// k += lower_bound(j).x * new_a;
|
||||||
|
m_ex.push_justification(column_lower_bound_constraint(j));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lp_assert(at_upper(j));
|
||||||
|
if (a.is_pos()) {
|
||||||
|
new_a = - a / m_f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_a = a / m_one_minus_f;
|
||||||
|
}
|
||||||
|
m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a;
|
||||||
|
m_ex.push_justification(column_upper_bound_constraint(j));
|
||||||
|
}
|
||||||
|
TRACE("gomory_cut_detail_real", tout << a << "*v" << j << " k: " << m_k << "\n";);
|
||||||
|
m_t.add_monomial(new_a, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
lia_move report_conflict_from_gomory_cut() {
|
||||||
|
lp_assert(m_k.is_pos());
|
||||||
|
// conflict 0 >= k where k is positive
|
||||||
|
m_k.neg(); // returning 0 <= -k
|
||||||
|
return lia_move::conflict;
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjust_term_and_k_for_some_ints_case_gomory() {
|
||||||
|
lp_assert(!m_t.is_empty());
|
||||||
|
// k = 1 + sum of m_t at bounds
|
||||||
|
auto pol = m_t.coeffs_as_vector();
|
||||||
|
m_t.clear();
|
||||||
|
if (pol.size() == 1) {
|
||||||
|
TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;);
|
||||||
|
unsigned v = pol[0].second;
|
||||||
|
lp_assert(is_int(v));
|
||||||
|
const mpq& a = pol[0].first;
|
||||||
|
m_k /= a;
|
||||||
|
if (a.is_pos()) { // we have av >= k
|
||||||
|
if (!m_k.is_int())
|
||||||
|
m_k = ceil(m_k);
|
||||||
|
// switch size
|
||||||
|
m_t.add_monomial(- mpq(1), v);
|
||||||
|
m_k.neg();
|
||||||
|
} else {
|
||||||
|
if (!m_k.is_int())
|
||||||
|
m_k = floor(m_k);
|
||||||
|
m_t.add_monomial(mpq(1), v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_lcm_den = lcm(m_lcm_den, denominator(m_k));
|
||||||
|
lp_assert(m_lcm_den.is_pos());
|
||||||
|
TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;);
|
||||||
|
if (!m_lcm_den.is_one()) {
|
||||||
|
// normalize coefficients of integer parameters to be integers.
|
||||||
|
for (auto & pi: pol) {
|
||||||
|
pi.first *= m_lcm_den;
|
||||||
|
SASSERT(!is_int(pi.second) || pi.first.is_int());
|
||||||
|
}
|
||||||
|
m_k *= m_lcm_den;
|
||||||
|
}
|
||||||
|
// negate everything to return -pol <= -m_k
|
||||||
|
for (const auto & pi: pol) {
|
||||||
|
TRACE("gomory_cut", tout << pi.first << "* " << "v" << pi.second << "\n";);
|
||||||
|
m_t.add_monomial(-pi.first, pi.second);
|
||||||
|
}
|
||||||
|
m_k.neg();
|
||||||
|
}
|
||||||
|
TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;);
|
||||||
|
lp_assert(m_k.is_int());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string var_name(unsigned j) const {
|
||||||
|
return std::string("x") + std::to_string(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& dump_coeff_val(std::ostream & out, const mpq & a) const {
|
||||||
|
if (a.is_int()) {
|
||||||
|
out << a;
|
||||||
|
}
|
||||||
|
else if ( a >= zero_of_type<mpq>())
|
||||||
|
out << "(/ " << numerator(a) << " " << denominator(a) << ")";
|
||||||
|
else {
|
||||||
|
out << "(- ( / " << numerator(-a) << " " << denominator(-a) << "))";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void dump_coeff(std::ostream & out, const T& c) const {
|
||||||
|
out << "( * ";
|
||||||
|
dump_coeff_val(out, c.coeff());
|
||||||
|
out << " " << var_name(c.var()) << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& dump_row_coefficients(std::ostream & out) const {
|
||||||
|
mpq lc(1);
|
||||||
|
for (const auto& p : m_row) {
|
||||||
|
lc = lcm(lc, denominator(p.coeff()));
|
||||||
|
}
|
||||||
|
for (const auto& p : m_row) {
|
||||||
|
dump_coeff_val(out << " (* ", p.coeff()*lc) << " " << var_name(p.var()) << ")";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_the_row(std::ostream& out) const {
|
||||||
|
out << "; the row, excluding fixed vars\n";
|
||||||
|
out << "(assert ( = ( +";
|
||||||
|
dump_row_coefficients(out) << ") 0))\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_declaration(std::ostream& out, unsigned v) const {
|
||||||
|
out << "(declare-const " << var_name(v) << (is_int(v) ? " Int" : " Real") << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_declarations(std::ostream& out) const {
|
||||||
|
// for a column j the var name is vj
|
||||||
|
for (const auto & p : m_row) {
|
||||||
|
dump_declaration(out, p.var());
|
||||||
|
}
|
||||||
|
for (const auto& p : m_t) {
|
||||||
|
unsigned v = p.var();
|
||||||
|
if (m_int_solver.m_lar_solver->is_term(v)) {
|
||||||
|
dump_declaration(out, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_lower_bound_expl(std::ostream & out, unsigned j) const {
|
||||||
|
out << "(assert (>= " << var_name(j) << " " << lower_bound(j).x << "))\n";
|
||||||
|
}
|
||||||
|
void dump_upper_bound_expl(std::ostream & out, unsigned j) const {
|
||||||
|
out << "(assert (<= " << var_name(j) << " " << upper_bound(j).x << "))\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_explanations(std::ostream& out) const {
|
||||||
|
for (const auto & p : m_row) {
|
||||||
|
unsigned j = p.var();
|
||||||
|
if (j == m_inf_col || (!is_real(j) && p.coeff().is_int())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (at_lower(j)) {
|
||||||
|
dump_lower_bound_expl(out, j);
|
||||||
|
} else {
|
||||||
|
lp_assert(at_upper(j));
|
||||||
|
dump_upper_bound_expl(out, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& dump_term_coefficients(std::ostream & out) const {
|
||||||
|
for (const auto& p : m_t) {
|
||||||
|
dump_coeff(out, p);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& dump_term_sum(std::ostream & out) const {
|
||||||
|
return dump_term_coefficients(out << "(+ ") << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& dump_term_le_k(std::ostream & out) const {
|
||||||
|
return dump_term_sum(out << "(<= ") << " " << m_k << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_the_cut_assert(std::ostream & out) const {
|
||||||
|
dump_term_le_k(out << "(assert (not ") << "))\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const {
|
||||||
|
dump_declarations(out);
|
||||||
|
dump_the_row(out);
|
||||||
|
dump_explanations(out);
|
||||||
|
dump_the_cut_assert(out);
|
||||||
|
out << "(check-sat)\n";
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
lia_move create_cut() {
|
||||||
|
TRACE("gomory_cut",
|
||||||
|
tout << "applying cut at:\n"; print_linear_combination_of_column_indices_only(m_row, tout); tout << std::endl;
|
||||||
|
for (auto & p : m_row) {
|
||||||
|
m_int_solver.m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout);
|
||||||
|
}
|
||||||
|
tout << "inf_col = " << m_inf_col << std::endl;
|
||||||
|
);
|
||||||
|
|
||||||
|
// gomory will be t <= k and the current solution has a property t > k
|
||||||
|
m_k = 1;
|
||||||
|
m_t.clear();
|
||||||
|
mpq m_lcm_den(1);
|
||||||
|
bool some_int_columns = false;
|
||||||
|
mpq m_f = fractional_part(get_value(m_inf_col));
|
||||||
|
TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", ";
|
||||||
|
tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f;);
|
||||||
|
lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int());
|
||||||
|
|
||||||
|
mpq one_min_m_f = 1 - m_f;
|
||||||
|
for (const auto & p : m_row) {
|
||||||
|
unsigned j = p.var();
|
||||||
|
if (j == m_inf_col) {
|
||||||
|
lp_assert(p.coeff() == one_of_type<mpq>());
|
||||||
|
TRACE("gomory_cut_detail", tout << "seeing basic var";);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T)
|
||||||
|
if (is_real(j)) {
|
||||||
|
real_case_in_gomory_cut(- p.coeff(), j);
|
||||||
|
} else {
|
||||||
|
if (p.coeff().is_int()) {
|
||||||
|
// m_fj will be zero and no monomial will be added
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
some_int_columns = true;
|
||||||
|
m_fj = fractional_part(-p.coeff());
|
||||||
|
m_one_minus_fj = 1 - m_fj;
|
||||||
|
int_case_in_gomory_cut(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_t.is_empty())
|
||||||
|
return report_conflict_from_gomory_cut();
|
||||||
|
if (some_int_columns)
|
||||||
|
adjust_term_and_k_for_some_ints_case_gomory();
|
||||||
|
lp_assert(m_int_solver.current_solution_is_inf_on_cut());
|
||||||
|
TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout););
|
||||||
|
m_int_solver.m_lar_solver->subs_term_columns(m_t);
|
||||||
|
TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t, tout << "gomory cut:"); tout << " <= " << m_k << std::endl;);
|
||||||
|
return lia_move::cut;
|
||||||
|
}
|
||||||
|
|
||||||
|
imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& int_slv ) :
|
||||||
|
m_t(t),
|
||||||
|
m_k(k),
|
||||||
|
m_ex(ex),
|
||||||
|
m_inf_col(basic_inf_int_j),
|
||||||
|
m_row(row),
|
||||||
|
m_int_solver(int_slv),
|
||||||
|
m_lcm_den(1),
|
||||||
|
m_f(fractional_part(get_value(basic_inf_int_j).x)),
|
||||||
|
m_one_minus_f(1 - m_f) {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
lia_move gomory::create_cut() {
|
||||||
|
return m_imp->create_cut();
|
||||||
|
}
|
||||||
|
|
||||||
|
gomory::gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& s) {
|
||||||
|
m_imp = alloc(imp, t, k, ex, basic_inf_int_j, row, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
gomory::~gomory() { dealloc(m_imp); }
|
||||||
|
|
||||||
|
}
|
36
src/util/lp/gomory.h
Normal file
36
src/util/lp/gomory.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2017 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
<name>
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
<abstract>
|
||||||
|
|
||||||
|
Author:
|
||||||
|
Nikolaj Bjorner (nbjorner)
|
||||||
|
Lev Nachmanson (levnach)
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#pragma once
|
||||||
|
#include "util/lp/lar_term.h"
|
||||||
|
#include "util/lp/lia_move.h"
|
||||||
|
#include "util/lp/explanation.h"
|
||||||
|
#include "util/lp/static_matrix.h"
|
||||||
|
|
||||||
|
namespace lp {
|
||||||
|
class int_solver;
|
||||||
|
class gomory {
|
||||||
|
class imp;
|
||||||
|
imp *m_imp;
|
||||||
|
public :
|
||||||
|
gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip<mpq>& row, const int_solver& s);
|
||||||
|
lia_move create_cut();
|
||||||
|
~gomory();
|
||||||
|
};
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
#include "util/lp/lp_utils.h"
|
#include "util/lp/lp_utils.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "util/lp/monomial.h"
|
#include "util/lp/monomial.h"
|
||||||
|
#include "util/lp/gomory.h"
|
||||||
namespace lp {
|
namespace lp {
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,10 +102,7 @@ bool int_solver::is_gomory_cut_target(const row_strip<mpq>& row) {
|
||||||
unsigned j;
|
unsigned j;
|
||||||
for (const auto & p : row) {
|
for (const auto & p : row) {
|
||||||
j = p.var();
|
j = p.var();
|
||||||
if (is_base(j)) continue;
|
if (!is_base(j) && (!at_bound(j) || !is_zero(get_value(j).y))) {
|
||||||
if (!at_bound(j))
|
|
||||||
return false;
|
|
||||||
if (!is_zero(get_value(j).y)) {
|
|
||||||
TRACE("gomory_cut", tout << "row is not gomory cut target:\n";
|
TRACE("gomory_cut", tout << "row is not gomory cut target:\n";
|
||||||
display_column(tout, j);
|
display_column(tout, j);
|
||||||
tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";);
|
tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";);
|
||||||
|
@ -115,36 +113,6 @@ bool int_solver::is_gomory_cut_target(const row_strip<mpq>& row) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) {
|
|
||||||
TRACE("gomory_cut_detail_real", tout << "real\n";);
|
|
||||||
mpq new_a;
|
|
||||||
if (at_low(x_j)) {
|
|
||||||
if (a.is_pos()) {
|
|
||||||
new_a = a / one_minus_f_0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_a = a / f_0;
|
|
||||||
new_a.neg();
|
|
||||||
}
|
|
||||||
m_k->addmul(new_a, lower_bound(x_j).x); // is it a faster operation than
|
|
||||||
// k += lower_bound(x_j).x * new_a;
|
|
||||||
m_ex->push_justification(column_lower_bound_constraint(x_j), new_a);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lp_assert(at_upper(x_j));
|
|
||||||
if (a.is_pos()) {
|
|
||||||
new_a = a / f_0;
|
|
||||||
new_a.neg(); // the upper terms are inverted.
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_a = a / one_minus_f_0;
|
|
||||||
}
|
|
||||||
m_k->addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a;
|
|
||||||
m_ex->push_justification(column_upper_bound_constraint(x_j), new_a);
|
|
||||||
}
|
|
||||||
TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << *m_k << "\n";);
|
|
||||||
m_t->add_monomial(new_a, x_j);
|
|
||||||
}
|
|
||||||
|
|
||||||
constraint_index int_solver::column_upper_bound_constraint(unsigned j) const {
|
constraint_index int_solver::column_upper_bound_constraint(unsigned j) const {
|
||||||
return m_lar_solver->get_column_upper_bound_witness(j);
|
return m_lar_solver->get_column_upper_bound_witness(j);
|
||||||
|
@ -155,221 +123,31 @@ constraint_index int_solver::column_lower_bound_constraint(unsigned j) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j,
|
|
||||||
mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) {
|
|
||||||
lp_assert(is_int(x_j));
|
|
||||||
lp_assert(!a.is_int());
|
|
||||||
mpq f_j = fractional_part(a);
|
|
||||||
TRACE("gomory_cut_detail",
|
|
||||||
tout << a << " x_j" << x_j << " k = " << *m_k << "\n";
|
|
||||||
tout << "f_j: " << f_j << "\n";
|
|
||||||
tout << "f_0: " << f_0 << "\n";
|
|
||||||
tout << "1 - f_0: " << 1 - f_0 << "\n";
|
|
||||||
tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl;
|
|
||||||
);
|
|
||||||
lp_assert (!f_j.is_zero());
|
|
||||||
mpq new_a;
|
|
||||||
if (at_low(x_j)) {
|
|
||||||
if (f_j <= one_minus_f_0) {
|
|
||||||
new_a = f_j / one_minus_f_0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_a = (1 - f_j) / f_0;
|
|
||||||
}
|
|
||||||
m_k->addmul(new_a, lower_bound(x_j).x);
|
|
||||||
m_ex->push_justification(column_lower_bound_constraint(x_j), new_a);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lp_assert(at_upper(x_j));
|
|
||||||
if (f_j <= f_0) {
|
|
||||||
new_a = f_j / f_0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_a = (mpq(1) - f_j) / one_minus_f_0;
|
|
||||||
}
|
|
||||||
new_a.neg(); // the upper terms are inverted
|
|
||||||
m_k->addmul(new_a, upper_bound(x_j).x);
|
|
||||||
m_ex->push_justification(column_upper_bound_constraint(x_j), new_a);
|
|
||||||
}
|
|
||||||
TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << *m_k << "\n";);
|
|
||||||
m_t->add_monomial(new_a, x_j);
|
|
||||||
lcm_den = lcm(lcm_den, denominator(new_a));
|
|
||||||
}
|
|
||||||
|
|
||||||
lia_move int_solver::report_conflict_from_gomory_cut() {
|
|
||||||
TRACE("empty_pol",);
|
|
||||||
lp_assert(m_k->is_pos());
|
|
||||||
// conflict 0 >= k where k is positive
|
|
||||||
m_k->neg(); // returning 0 <= -k
|
|
||||||
return lia_move::conflict;
|
|
||||||
}
|
|
||||||
|
|
||||||
void int_solver::gomory_cut_adjust_t_and_k(vector<std::pair<mpq, unsigned>> & pol,
|
|
||||||
lar_term & t,
|
|
||||||
mpq &k,
|
|
||||||
bool some_ints,
|
|
||||||
mpq & lcm_den) {
|
|
||||||
if (!some_ints)
|
|
||||||
return;
|
|
||||||
|
|
||||||
t.clear();
|
|
||||||
if (pol.size() == 1) {
|
|
||||||
unsigned v = pol[0].second;
|
|
||||||
lp_assert(is_int(v));
|
|
||||||
bool k_is_int = k.is_int();
|
|
||||||
const mpq& a = pol[0].first;
|
|
||||||
k /= a;
|
|
||||||
if (a.is_pos()) { // we have av >= k
|
|
||||||
if (!k_is_int)
|
|
||||||
k = ceil(k);
|
|
||||||
// switch size
|
|
||||||
t.add_monomial(- mpq(1), v);
|
|
||||||
k.neg();
|
|
||||||
} else {
|
|
||||||
if (!k_is_int)
|
|
||||||
k = floor(k);
|
|
||||||
t.add_monomial(mpq(1), v);
|
|
||||||
}
|
|
||||||
} else if (some_ints) {
|
|
||||||
lcm_den = lcm(lcm_den, denominator(k));
|
|
||||||
lp_assert(lcm_den.is_pos());
|
|
||||||
if (!lcm_den.is_one()) {
|
|
||||||
// normalize coefficients of integer parameters to be integers.
|
|
||||||
for (auto & pi: pol) {
|
|
||||||
pi.first *= lcm_den;
|
|
||||||
SASSERT(!is_int(pi.second) || pi.first.is_int());
|
|
||||||
}
|
|
||||||
k *= lcm_den;
|
|
||||||
}
|
|
||||||
// negate everything to return -pol <= -k
|
|
||||||
for (const auto & pi: pol)
|
|
||||||
t.add_monomial(-pi.first, pi.second);
|
|
||||||
k.neg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool int_solver::current_solution_is_inf_on_cut() const {
|
bool int_solver::current_solution_is_inf_on_cut() const {
|
||||||
const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x;
|
const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x;
|
||||||
impq v = m_t->apply(x);
|
impq v = m_t.apply(x);
|
||||||
mpq sign = *m_upper ? one_of_type<mpq>() : -one_of_type<mpq>();
|
mpq sign = m_upper ? one_of_type<mpq>() : -one_of_type<mpq>();
|
||||||
CTRACE("current_solution_is_inf_on_cut", v * sign <= (*m_k) * sign,
|
CTRACE("current_solution_is_inf_on_cut", v * sign <= m_k * sign,
|
||||||
tout << "m_upper = " << *m_upper << std::endl;
|
tout << "m_upper = " << m_upper << std::endl;
|
||||||
tout << "v = " << v << ", k = " << (*m_k) << std::endl;
|
tout << "v = " << v << ", k = " << m_k << std::endl;
|
||||||
);
|
);
|
||||||
return v * sign > (*m_k) * sign;
|
return v * sign > m_k * sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
void int_solver::adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) {
|
|
||||||
lp_assert(!m_t->is_empty());
|
|
||||||
auto pol = m_t->coeffs_as_vector();
|
|
||||||
m_t->clear();
|
|
||||||
if (pol.size() == 1) {
|
|
||||||
TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;);
|
|
||||||
unsigned v = pol[0].second;
|
|
||||||
lp_assert(is_int(v));
|
|
||||||
const mpq& a = pol[0].first;
|
|
||||||
(*m_k) /= a;
|
|
||||||
if (a.is_pos()) { // we have av >= k
|
|
||||||
if (!(*m_k).is_int())
|
|
||||||
(*m_k) = ceil((*m_k));
|
|
||||||
// switch size
|
|
||||||
m_t->add_monomial(- mpq(1), v);
|
|
||||||
(*m_k).neg();
|
|
||||||
} else {
|
|
||||||
if (!(*m_k).is_int())
|
|
||||||
(*m_k) = floor((*m_k));
|
|
||||||
m_t->add_monomial(mpq(1), v);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;);
|
|
||||||
lcm_den = lcm(lcm_den, denominator((*m_k)));
|
|
||||||
lp_assert(lcm_den.is_pos());
|
|
||||||
if (!lcm_den.is_one()) {
|
|
||||||
// normalize coefficients of integer parameters to be integers.
|
|
||||||
for (auto & pi: pol) {
|
|
||||||
pi.first *= lcm_den;
|
|
||||||
SASSERT(!is_int(pi.second) || pi.first.is_int());
|
|
||||||
}
|
|
||||||
(*m_k) *= lcm_den;
|
|
||||||
}
|
|
||||||
// negate everything to return -pol <= -(*m_k)
|
|
||||||
for (const auto & pi: pol)
|
|
||||||
m_t->add_monomial(-pi.first, pi.second);
|
|
||||||
(*m_k).neg();
|
|
||||||
}
|
|
||||||
TRACE("gomory_cut_detail", tout << "k = " << (*m_k) << std::endl;);
|
|
||||||
lp_assert((*m_k).is_int());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip<mpq> & row) {
|
lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip<mpq> & row) {
|
||||||
|
|
||||||
lp_assert(column_is_int_inf(inf_col));
|
lp_assert(column_is_int_inf(inf_col));
|
||||||
|
|
||||||
TRACE("gomory_cut",
|
gomory gc(m_t, m_k, m_ex, inf_col, row, *this);
|
||||||
tout << "applying cut at:\n"; m_lar_solver->print_row(row, tout); tout << std::endl;
|
return gc.create_cut();
|
||||||
for (auto & p : row) {
|
|
||||||
m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout);
|
|
||||||
}
|
|
||||||
tout << "inf_col = " << inf_col << std::endl;
|
|
||||||
);
|
|
||||||
|
|
||||||
// gomory will be t <= k and the current solution has a property t > k
|
|
||||||
*m_k = 1;
|
|
||||||
mpq lcm_den(1);
|
|
||||||
unsigned x_j;
|
|
||||||
mpq a;
|
|
||||||
bool some_int_columns = false;
|
|
||||||
mpq f_0 = int_solver::fractional_part(get_value(inf_col));
|
|
||||||
mpq one_min_f_0 = 1 - f_0;
|
|
||||||
for (const auto & p : row) {
|
|
||||||
x_j = p.var();
|
|
||||||
if (x_j == inf_col)
|
|
||||||
continue;
|
|
||||||
// make the format compatible with the format used in: Integrating Simplex with DPLL(T)
|
|
||||||
a = p.coeff();
|
|
||||||
a.neg();
|
|
||||||
if (is_real(x_j))
|
|
||||||
real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0);
|
|
||||||
else if (!a.is_int()) { // f_j will be zero and no monomial will be added
|
|
||||||
some_int_columns = true;
|
|
||||||
int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_t->is_empty())
|
|
||||||
return report_conflict_from_gomory_cut();
|
|
||||||
if (some_int_columns)
|
|
||||||
adjust_term_and_k_for_some_ints_case_gomory(lcm_den);
|
|
||||||
|
|
||||||
lp_assert(current_solution_is_inf_on_cut());
|
|
||||||
m_lar_solver->subs_term_columns(*m_t);
|
|
||||||
TRACE("gomory_cut", tout<<"precut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;);
|
|
||||||
return lia_move::cut;
|
|
||||||
}
|
|
||||||
|
|
||||||
int int_solver::find_free_var_in_gomory_row(const row_strip<mpq>& row) {
|
|
||||||
unsigned j;
|
|
||||||
for (const auto & p : row) {
|
|
||||||
j = p.var();
|
|
||||||
if (!is_base(j) && is_free(j))
|
|
||||||
return static_cast<int>(j);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lia_move int_solver::proceed_with_gomory_cut(unsigned j) {
|
lia_move int_solver::proceed_with_gomory_cut(unsigned j) {
|
||||||
const row_strip<mpq>& row = m_lar_solver->get_row(row_of_basic_column(j));
|
const row_strip<mpq>& row = m_lar_solver->get_row(row_of_basic_column(j));
|
||||||
|
|
||||||
if (-1 != find_free_var_in_gomory_row(row))
|
|
||||||
return lia_move::undef;
|
|
||||||
|
|
||||||
if (!is_gomory_cut_target(row))
|
if (!is_gomory_cut_target(row))
|
||||||
return lia_move::undef;
|
return create_branch_on_column(j);
|
||||||
|
|
||||||
*m_upper = true;
|
m_upper = true;
|
||||||
return mk_gomory_cut(j, row);
|
return mk_gomory_cut(j, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,17 +172,19 @@ typedef monomial mono;
|
||||||
|
|
||||||
|
|
||||||
// this will allow to enable and disable tracking of the pivot rows
|
// this will allow to enable and disable tracking of the pivot rows
|
||||||
struct pivoted_rows_tracking_control {
|
struct check_return_helper {
|
||||||
lar_solver * m_lar_solver;
|
lar_solver * m_lar_solver;
|
||||||
bool m_track_pivoted_rows;
|
const lia_move & m_r;
|
||||||
pivoted_rows_tracking_control(lar_solver* ls) :
|
bool m_track_pivoted_rows;
|
||||||
|
check_return_helper(lar_solver* ls, const lia_move& r) :
|
||||||
m_lar_solver(ls),
|
m_lar_solver(ls),
|
||||||
|
m_r(r),
|
||||||
m_track_pivoted_rows(ls->get_track_pivoted_rows())
|
m_track_pivoted_rows(ls->get_track_pivoted_rows())
|
||||||
{
|
{
|
||||||
TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;);
|
TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;);
|
||||||
m_lar_solver->set_track_pivoted_rows(false);
|
m_lar_solver->set_track_pivoted_rows(false);
|
||||||
}
|
}
|
||||||
~pivoted_rows_tracking_control() {
|
~check_return_helper() {
|
||||||
TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;);
|
TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;);
|
||||||
m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows);
|
m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows);
|
||||||
}
|
}
|
||||||
|
@ -589,21 +369,21 @@ lia_move int_solver::make_hnf_cut() {
|
||||||
#else
|
#else
|
||||||
vector<mpq> x0;
|
vector<mpq> x0;
|
||||||
#endif
|
#endif
|
||||||
lia_move r = m_hnf_cutter.create_cut(*m_t, *m_k, *m_ex, *m_upper, x0);
|
lia_move r = m_hnf_cutter.create_cut(m_t, m_k, m_ex, m_upper, x0);
|
||||||
|
|
||||||
if (r == lia_move::cut) {
|
if (r == lia_move::cut) {
|
||||||
TRACE("hnf_cut",
|
TRACE("hnf_cut",
|
||||||
m_lar_solver->print_term(*m_t, tout << "cut:");
|
m_lar_solver->print_term(m_t, tout << "cut:");
|
||||||
tout << " <= " << *m_k << std::endl;
|
tout << " <= " << m_k << std::endl;
|
||||||
for (unsigned i : m_hnf_cutter.constraints_for_explanation()) {
|
for (unsigned i : m_hnf_cutter.constraints_for_explanation()) {
|
||||||
m_lar_solver->print_constraint(i, tout);
|
m_lar_solver->print_constraint(i, tout);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
lp_assert(current_solution_is_inf_on_cut());
|
lp_assert(current_solution_is_inf_on_cut());
|
||||||
settings().st().m_hnf_cuts++;
|
settings().st().m_hnf_cuts++;
|
||||||
m_ex->clear();
|
m_ex.clear();
|
||||||
for (unsigned i : m_hnf_cutter.constraints_for_explanation()) {
|
for (unsigned i : m_hnf_cutter.constraints_for_explanation()) {
|
||||||
m_ex->push_justification(i);
|
m_ex.push_justification(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
@ -619,14 +399,17 @@ lia_move int_solver::hnf_cut() {
|
||||||
return lia_move::undef;
|
return lia_move::undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex, bool & upper) {
|
lia_move int_solver::check() {
|
||||||
if (!has_inf_int()) return lia_move::sat;
|
if (!has_inf_int()) return lia_move::sat;
|
||||||
|
|
||||||
m_t = &t; m_k = &k; m_ex = &ex; m_upper = &upper;
|
m_t.clear();
|
||||||
|
m_k.reset();
|
||||||
|
m_ex.clear();
|
||||||
|
m_upper = false;
|
||||||
lia_move r = run_gcd_test();
|
lia_move r = run_gcd_test();
|
||||||
if (r != lia_move::undef) return r;
|
if (r != lia_move::undef) return r;
|
||||||
|
|
||||||
pivoted_rows_tracking_control pc(m_lar_solver);
|
check_return_helper pc(m_lar_solver, r);
|
||||||
|
|
||||||
if(settings().m_int_pivot_fixed_vars_from_basis)
|
if(settings().m_int_pivot_fixed_vars_from_basis)
|
||||||
m_lar_solver->pivot_fixed_vars_from_basis();
|
m_lar_solver->pivot_fixed_vars_from_basis();
|
||||||
|
@ -862,8 +645,8 @@ bool int_solver::gcd_test_for_row(static_matrix<mpq, numeric_pair<mpq>> & A, uns
|
||||||
void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) {
|
void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) {
|
||||||
constraint_index lc, uc;
|
constraint_index lc, uc;
|
||||||
m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc);
|
m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc);
|
||||||
m_ex->m_explanation.push_back(std::make_pair(mpq(1), lc));
|
m_ex.m_explanation.push_back(std::make_pair(mpq(1), lc));
|
||||||
m_ex->m_explanation.push_back(std::make_pair(mpq(1), uc));
|
m_ex.m_explanation.push_back(std::make_pair(mpq(1), uc));
|
||||||
}
|
}
|
||||||
void int_solver::fill_explanation_from_fixed_columns(const row_strip<mpq> & row) {
|
void int_solver::fill_explanation_from_fixed_columns(const row_strip<mpq> & row) {
|
||||||
for (const auto & c : row) {
|
for (const auto & c : row) {
|
||||||
|
@ -1126,7 +909,7 @@ bool int_solver::at_bound(unsigned j) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool int_solver::at_low(unsigned j) const {
|
bool int_solver::at_lower(unsigned j) const {
|
||||||
auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver;
|
auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver;
|
||||||
switch (mpq_solver.m_column_types[j] ) {
|
switch (mpq_solver.m_column_types[j] ) {
|
||||||
case column_type::fixed:
|
case column_type::fixed:
|
||||||
|
@ -1258,20 +1041,20 @@ const impq& int_solver::lower_bound(unsigned j) const {
|
||||||
|
|
||||||
lia_move int_solver::create_branch_on_column(int j) {
|
lia_move int_solver::create_branch_on_column(int j) {
|
||||||
TRACE("check_main_int", tout << "branching" << std::endl;);
|
TRACE("check_main_int", tout << "branching" << std::endl;);
|
||||||
lp_assert(m_t->is_empty());
|
lp_assert(m_t.is_empty());
|
||||||
lp_assert(j != -1);
|
lp_assert(j != -1);
|
||||||
m_t->add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j));
|
m_t.add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j));
|
||||||
if (is_free(j)) {
|
if (is_free(j)) {
|
||||||
*m_upper = true;
|
m_upper = true;
|
||||||
*m_k = mpq(0);
|
m_k = mpq(0);
|
||||||
} else {
|
} else {
|
||||||
*m_upper = left_branch_is_more_narrow_than_right(j);
|
m_upper = left_branch_is_more_narrow_than_right(j);
|
||||||
*m_k = *m_upper? floor(get_value(j)) : ceil(get_value(j));
|
m_k = m_upper? floor(get_value(j)) : ceil(get_value(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("arith_int", tout << "branching v" << j << " = " << get_value(j) << "\n";
|
TRACE("arith_int", tout << "branching v" << j << " = " << get_value(j) << "\n";
|
||||||
display_column(tout, j);
|
display_column(tout, j);
|
||||||
tout << "k = " << *m_k << std::endl;
|
tout << "k = " << m_k << std::endl;
|
||||||
);
|
);
|
||||||
return lia_move::branch;
|
return lia_move::branch;
|
||||||
|
|
||||||
|
|
|
@ -39,20 +39,31 @@ public:
|
||||||
// fields
|
// fields
|
||||||
lar_solver *m_lar_solver;
|
lar_solver *m_lar_solver;
|
||||||
unsigned m_number_of_calls;
|
unsigned m_number_of_calls;
|
||||||
lar_term *m_t; // the term to return in the cut
|
lar_term m_t; // the term to return in the cut
|
||||||
mpq *m_k; // the right side of the cut
|
mpq m_k; // the right side of the cut
|
||||||
explanation *m_ex; // the conflict explanation
|
explanation m_ex; // the conflict explanation
|
||||||
bool *m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise
|
bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise
|
||||||
hnf_cutter m_hnf_cutter;
|
hnf_cutter m_hnf_cutter;
|
||||||
// methods
|
// methods
|
||||||
int_solver(lar_solver* lp);
|
int_solver(lar_solver* lp);
|
||||||
|
|
||||||
// main function to check that the solution provided by lar_solver is valid for integral values,
|
// main function to check that the solution provided by lar_solver is valid for integral values,
|
||||||
// or provide a way of how it can be adjusted.
|
// or provide a way of how it can be adjusted.
|
||||||
lia_move check(lar_term& t, mpq& k, explanation& ex, bool & upper);
|
lia_move check();
|
||||||
|
lar_term const& get_term() const { return m_t; }
|
||||||
|
mpq const& get_offset() const { return m_k; }
|
||||||
|
explanation const& get_explanation() const { return m_ex; }
|
||||||
|
bool is_upper() const { return m_upper; }
|
||||||
|
|
||||||
bool move_non_basic_column_to_bounds(unsigned j);
|
bool move_non_basic_column_to_bounds(unsigned j);
|
||||||
lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex);
|
|
||||||
bool is_base(unsigned j) const;
|
bool is_base(unsigned j) const;
|
||||||
|
bool is_real(unsigned j) const;
|
||||||
|
const impq & lower_bound(unsigned j) const;
|
||||||
|
const impq & upper_bound(unsigned j) const;
|
||||||
|
bool is_int(unsigned j) const;
|
||||||
|
const impq & get_value(unsigned j) const;
|
||||||
|
bool at_lower(unsigned j) const;
|
||||||
|
bool at_upper(unsigned j) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -79,10 +90,7 @@ private:
|
||||||
void add_to_explanation_from_fixed_or_boxed_column(unsigned j);
|
void add_to_explanation_from_fixed_or_boxed_column(unsigned j);
|
||||||
lia_move patch_nbasic_columns();
|
lia_move patch_nbasic_columns();
|
||||||
bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m);
|
bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m);
|
||||||
const impq & lower_bound(unsigned j) const;
|
private:
|
||||||
const impq & upper_bound(unsigned j) const;
|
|
||||||
bool is_int(unsigned j) const;
|
|
||||||
bool is_real(unsigned j) const;
|
|
||||||
bool is_boxed(unsigned j) const;
|
bool is_boxed(unsigned j) const;
|
||||||
bool is_fixed(unsigned j) const;
|
bool is_fixed(unsigned j) const;
|
||||||
bool is_free(unsigned j) const;
|
bool is_free(unsigned j) const;
|
||||||
|
@ -91,7 +99,6 @@ private:
|
||||||
void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val);
|
void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val);
|
||||||
bool non_basic_columns_are_at_bounds() const;
|
bool non_basic_columns_are_at_bounds() const;
|
||||||
bool is_feasible() const;
|
bool is_feasible() const;
|
||||||
const impq & get_value(unsigned j) const;
|
|
||||||
bool column_is_int_inf(unsigned j) const;
|
bool column_is_int_inf(unsigned j) const;
|
||||||
void trace_inf_rows() const;
|
void trace_inf_rows() const;
|
||||||
lia_move branch_or_sat();
|
lia_move branch_or_sat();
|
||||||
|
@ -104,39 +111,22 @@ private:
|
||||||
bool move_non_basic_columns_to_bounds();
|
bool move_non_basic_columns_to_bounds();
|
||||||
void branch_infeasible_int_var(unsigned);
|
void branch_infeasible_int_var(unsigned);
|
||||||
lia_move mk_gomory_cut(unsigned inf_col, const row_strip<mpq>& row);
|
lia_move mk_gomory_cut(unsigned inf_col, const row_strip<mpq>& row);
|
||||||
lia_move report_conflict_from_gomory_cut();
|
|
||||||
void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den);
|
|
||||||
lia_move proceed_with_gomory_cut(unsigned j);
|
lia_move proceed_with_gomory_cut(unsigned j);
|
||||||
int find_free_var_in_gomory_row(const row_strip<mpq>& );
|
|
||||||
bool is_gomory_cut_target(const row_strip<mpq>&);
|
bool is_gomory_cut_target(const row_strip<mpq>&);
|
||||||
bool at_bound(unsigned j) const;
|
bool at_bound(unsigned j) const;
|
||||||
bool at_low(unsigned j) const;
|
|
||||||
bool at_upper(unsigned j) const;
|
|
||||||
bool has_low(unsigned j) const;
|
bool has_low(unsigned j) const;
|
||||||
bool has_upper(unsigned j) const;
|
bool has_upper(unsigned j) const;
|
||||||
unsigned row_of_basic_column(unsigned j) const;
|
unsigned row_of_basic_column(unsigned j) const;
|
||||||
inline static bool is_rational(const impq & n) {
|
|
||||||
return is_zero(n.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void display_column(std::ostream & out, unsigned j) const;
|
void display_column(std::ostream & out, unsigned j) const;
|
||||||
inline static
|
|
||||||
mpq fractional_part(const impq & n) {
|
|
||||||
lp_assert(is_rational(n));
|
|
||||||
return n.x - floor(n.x);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0);
|
|
||||||
void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0);
|
|
||||||
constraint_index column_upper_bound_constraint(unsigned j) const;
|
constraint_index column_upper_bound_constraint(unsigned j) const;
|
||||||
constraint_index column_lower_bound_constraint(unsigned j) const;
|
constraint_index column_lower_bound_constraint(unsigned j) const;
|
||||||
void display_row_info(std::ostream & out, unsigned row_index) const;
|
|
||||||
void gomory_cut_adjust_t_and_k(vector<std::pair<mpq, unsigned>> & pol, lar_term & t, mpq &k, bool num_ints, mpq &lcm_den);
|
|
||||||
bool current_solution_is_inf_on_cut() const;
|
bool current_solution_is_inf_on_cut() const;
|
||||||
public:
|
|
||||||
bool shift_var(unsigned j, unsigned range);
|
bool shift_var(unsigned j, unsigned range);
|
||||||
private:
|
private:
|
||||||
|
void display_row_info(std::ostream & out, unsigned row_index) const;
|
||||||
unsigned random();
|
unsigned random();
|
||||||
bool has_inf_int() const;
|
bool has_inf_int() const;
|
||||||
lia_move create_branch_on_column(int j);
|
lia_move create_branch_on_column(int j);
|
||||||
|
@ -161,5 +151,5 @@ public:
|
||||||
bool hnf_has_var_with_non_integral_value() const;
|
bool hnf_has_var_with_non_integral_value() const;
|
||||||
bool hnf_cutter_is_full() const;
|
bool hnf_cutter_is_full() const;
|
||||||
void patch_nbasic_column(unsigned j, bool patch_only_int_vals);
|
void patch_nbasic_column(unsigned j, bool patch_only_int_vals);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ struct lar_term_constraint: public lar_base_constraint {
|
||||||
}
|
}
|
||||||
unsigned size() const override { return m_term->size();}
|
unsigned size() const override { return m_term->size();}
|
||||||
lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { }
|
lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { }
|
||||||
mpq get_free_coeff_of_left_side() const override { return m_term->m_v;}
|
// mpq get_free_coeff_of_left_side() const override { return m_term->m_v;}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ void clear() {lp_assert(false); // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lar_solver::lar_solver() : m_status(lp_status::OPTIMAL),
|
lar_solver::lar_solver() : m_status(lp_status::UNKNOWN),
|
||||||
m_infeasible_column_index(-1),
|
m_infeasible_column_index(-1),
|
||||||
m_terms_start_index(1000000),
|
m_terms_start_index(1000000),
|
||||||
m_mpq_lar_core_solver(m_settings, *this),
|
m_mpq_lar_core_solver(m_settings, *this),
|
||||||
|
@ -71,7 +71,7 @@ bool lar_solver::sizes_are_correct() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const {
|
std::ostream& lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const {
|
||||||
out << "implied bound\n";
|
out << "implied bound\n";
|
||||||
unsigned v = be.m_j;
|
unsigned v = be.m_j;
|
||||||
if (is_term(v)) {
|
if (is_term(v)) {
|
||||||
|
@ -83,6 +83,7 @@ void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out
|
||||||
}
|
}
|
||||||
out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl;
|
out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl;
|
||||||
out << "end of implied bound" << std::endl;
|
out << "end of implied bound" << std::endl;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const {
|
bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const {
|
||||||
|
@ -136,7 +137,7 @@ bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be,
|
||||||
kind = static_cast<lconstraint_kind>(-kind);
|
kind = static_cast<lconstraint_kind>(-kind);
|
||||||
}
|
}
|
||||||
rs_of_evidence /= ratio;
|
rs_of_evidence /= ratio;
|
||||||
rs_of_evidence += t->m_v * ratio;
|
// rs_of_evidence += t->m_v * ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
return kind == be.kind() && rs_of_evidence == be.m_bound;
|
return kind == be.kind() && rs_of_evidence == be.m_bound;
|
||||||
|
@ -601,7 +602,7 @@ void lar_solver::register_monoid_in_map(std::unordered_map<var_index, mpq> & coe
|
||||||
|
|
||||||
|
|
||||||
void lar_solver::substitute_terms_in_linear_expression(const vector<std::pair<mpq, var_index>>& left_side_with_terms,
|
void lar_solver::substitute_terms_in_linear_expression(const vector<std::pair<mpq, var_index>>& left_side_with_terms,
|
||||||
vector<std::pair<mpq, var_index>> &left_side, mpq & free_coeff) const {
|
vector<std::pair<mpq, var_index>> &left_side) const {
|
||||||
std::unordered_map<var_index, mpq> coeffs;
|
std::unordered_map<var_index, mpq> coeffs;
|
||||||
for (auto & t : left_side_with_terms) {
|
for (auto & t : left_side_with_terms) {
|
||||||
unsigned j = t.second;
|
unsigned j = t.second;
|
||||||
|
@ -612,7 +613,6 @@ void lar_solver::substitute_terms_in_linear_expression(const vector<std::pair<mp
|
||||||
for (auto & p : term.coeffs()){
|
for (auto & p : term.coeffs()){
|
||||||
register_monoid_in_map(coeffs, t.first * p.second , p.first);
|
register_monoid_in_map(coeffs, t.first * p.second , p.first);
|
||||||
}
|
}
|
||||||
free_coeff += t.first * term.m_v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +781,7 @@ void lar_solver::solve_with_core_solver() {
|
||||||
update_x_and_inf_costs_for_columns_with_changed_bounds();
|
update_x_and_inf_costs_for_columns_with_changed_bounds();
|
||||||
m_mpq_lar_core_solver.solve();
|
m_mpq_lar_core_solver.solve();
|
||||||
set_status(m_mpq_lar_core_solver.m_r_solver.get_status());
|
set_status(m_mpq_lar_core_solver.m_r_solver.get_status());
|
||||||
lp_assert(m_status != lp_status::OPTIMAL || all_constraints_hold());
|
lp_assert((m_settings.random_next() % 100) != 0 || m_status != lp_status::OPTIMAL || all_constraints_hold());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -909,13 +909,8 @@ bool lar_solver::try_to_set_fixed(column_info<mpq> & ci) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
column_type lar_solver::get_column_type(const column_info<mpq> & ci) {
|
column_type lar_solver::get_column_type(unsigned j) const{
|
||||||
auto ret = ci.get_column_type_no_flipping();
|
return m_mpq_lar_core_solver.m_column_types[j];
|
||||||
if (ret == column_type::boxed) { // changing boxed to fixed because of the no span
|
|
||||||
if (ci.get_lower_bound() == ci.get_upper_bound())
|
|
||||||
ret = column_type::fixed;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string lar_solver::get_column_name(unsigned j) const {
|
std::string lar_solver::get_column_name(unsigned j) const {
|
||||||
|
@ -954,9 +949,9 @@ bool lar_solver::constraint_holds(const lar_base_constraint & constr, std::unord
|
||||||
mpq left_side_val = get_left_side_val(constr, var_map);
|
mpq left_side_val = get_left_side_val(constr, var_map);
|
||||||
switch (constr.m_kind) {
|
switch (constr.m_kind) {
|
||||||
case LE: return left_side_val <= constr.m_right_side;
|
case LE: return left_side_val <= constr.m_right_side;
|
||||||
case LT: return left_side_val < constr.m_right_side;
|
case LT: return left_side_val < constr.m_right_side;
|
||||||
case GE: return left_side_val >= constr.m_right_side;
|
case GE: return left_side_val >= constr.m_right_side;
|
||||||
case GT: return left_side_val > constr.m_right_side;
|
case GT: return left_side_val > constr.m_right_side;
|
||||||
case EQ: return left_side_val == constr.m_right_side;
|
case EQ: return left_side_val == constr.m_right_side;
|
||||||
default:
|
default:
|
||||||
lp_unreachable();
|
lp_unreachable();
|
||||||
|
@ -975,8 +970,10 @@ bool lar_solver::the_relations_are_of_same_type(const vector<std::pair<mpq, unsi
|
||||||
flip_kind(m_constraints[con_ind]->m_kind);
|
flip_kind(m_constraints[con_ind]->m_kind);
|
||||||
if (kind == GT || kind == LT)
|
if (kind == GT || kind == LT)
|
||||||
strict = true;
|
strict = true;
|
||||||
if (kind == GE || kind == GT) n_of_G++;
|
if (kind == GE || kind == GT)
|
||||||
else if (kind == LE || kind == LT) n_of_L++;
|
n_of_G++;
|
||||||
|
else if (kind == LE || kind == LT)
|
||||||
|
n_of_L++;
|
||||||
}
|
}
|
||||||
the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ);
|
the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ);
|
||||||
if (strict)
|
if (strict)
|
||||||
|
@ -1116,7 +1113,7 @@ bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value
|
||||||
bool lar_solver::has_value(var_index var, mpq& value) const {
|
bool lar_solver::has_value(var_index var, mpq& value) const {
|
||||||
if (is_term(var)) {
|
if (is_term(var)) {
|
||||||
lar_term const& t = get_term(var);
|
lar_term const& t = get_term(var);
|
||||||
value = t.m_v;
|
value = 0;
|
||||||
for (auto const& cv : t) {
|
for (auto const& cv : t) {
|
||||||
impq const& r = get_column_value(cv.var());
|
impq const& r = get_column_value(cv.var());
|
||||||
if (!numeric_traits<mpq>::is_zero(r.y)) return false;
|
if (!numeric_traits<mpq>::is_zero(r.y)) return false;
|
||||||
|
@ -1177,6 +1174,7 @@ void lar_solver::get_model(std::unordered_map<var_index, mpq> & variable_values)
|
||||||
std::unordered_set<impq> set_of_different_pairs;
|
std::unordered_set<impq> set_of_different_pairs;
|
||||||
std::unordered_set<mpq> set_of_different_singles;
|
std::unordered_set<mpq> set_of_different_singles;
|
||||||
delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta);
|
delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta);
|
||||||
|
TRACE("get_model", tout << "delta=" << delta << "size = " << m_mpq_lar_core_solver.m_r_x.size() << std::endl;);
|
||||||
for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) {
|
for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) {
|
||||||
const numeric_pair<mpq> & rp = m_mpq_lar_core_solver.m_r_x[i];
|
const numeric_pair<mpq> & rp = m_mpq_lar_core_solver.m_r_x[i];
|
||||||
set_of_different_pairs.insert(rp);
|
set_of_different_pairs.insert(rp);
|
||||||
|
@ -1208,42 +1206,40 @@ std::string lar_solver::get_variable_name(var_index vi) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ********** print region start
|
// ********** print region start
|
||||||
void lar_solver::print_constraint(constraint_index ci, std::ostream & out) const {
|
std::ostream& lar_solver::print_constraint(constraint_index ci, std::ostream & out) const {
|
||||||
if (ci >= m_constraints.size()) {
|
if (ci >= m_constraints.size()) {
|
||||||
out << "constraint " << T_to_string(ci) << " is not found";
|
out << "constraint " << T_to_string(ci) << " is not found";
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
return;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_constraint(m_constraints[ci], out);
|
return print_constraint(m_constraints[ci], out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_constraints(std::ostream& out) const {
|
std::ostream& lar_solver::print_constraints(std::ostream& out) const {
|
||||||
out << "number of constraints = " << m_constraints.size() << std::endl;
|
out << "number of constraints = " << m_constraints.size() << std::endl;
|
||||||
for (auto c : m_constraints) {
|
for (auto c : m_constraints) {
|
||||||
print_constraint(c, out);
|
print_constraint(c, out);
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_terms(std::ostream& out) const {
|
std::ostream& lar_solver::print_terms(std::ostream& out) const {
|
||||||
for (auto it : m_terms) {
|
for (auto it : m_terms) {
|
||||||
print_term(*it, out);
|
print_term(*it, out) << "\n";
|
||||||
out << "\n";
|
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const {
|
std::ostream& lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const {
|
||||||
print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out);
|
print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out);
|
||||||
mpq free_coeff = c->get_free_coeff_of_left_side();
|
mpq free_coeff = c->get_free_coeff_of_left_side();
|
||||||
if (!is_zero(free_coeff))
|
if (!is_zero(free_coeff))
|
||||||
out << " + " << free_coeff;
|
out << " + " << free_coeff;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_term(lar_term const& term, std::ostream & out) const {
|
std::ostream& lar_solver::print_term(lar_term const& term, std::ostream & out) const {
|
||||||
if (!numeric_traits<mpq>::is_zero(term.m_v)) {
|
|
||||||
out << term.m_v << " + ";
|
|
||||||
}
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (const auto p : term) {
|
for (const auto p : term) {
|
||||||
mpq val = p.coeff();
|
mpq val = p.coeff();
|
||||||
|
@ -1263,14 +1259,12 @@ void lar_solver::print_term(lar_term const& term, std::ostream & out) const {
|
||||||
out << T_to_string(val);
|
out << T_to_string(val);
|
||||||
out << this->get_column_name(p.var());
|
out << this->get_column_name(p.var());
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const {
|
std::ostream& lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const {
|
||||||
if (!numeric_traits<mpq>::is_zero(term.m_v)) {
|
print_linear_combination_of_column_indices_only(term, out);
|
||||||
out << term.m_v << " + ";
|
return out;
|
||||||
}
|
|
||||||
print_linear_combination_of_column_indices_only(term.coeffs_as_vector(), out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) const {
|
mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) const {
|
||||||
|
@ -1284,9 +1278,10 @@ mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::u
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const {
|
std::ostream& lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const {
|
||||||
print_left_side_of_constraint(c, out);
|
print_left_side_of_constraint(c, out);
|
||||||
out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl;
|
out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector<unsigned>& column_list) {
|
void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector<unsigned>& column_list) {
|
||||||
|
@ -1492,7 +1487,7 @@ bool lar_solver::term_is_int(const lar_term * t) const {
|
||||||
for (auto const & p : t->m_coeffs)
|
for (auto const & p : t->m_coeffs)
|
||||||
if (! (column_is_int(p.first) && p.second.is_int()))
|
if (! (column_is_int(p.first) && p.second.is_int()))
|
||||||
return false;
|
return false;
|
||||||
return t->m_v.is_int();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lar_solver::var_is_int(var_index v) const {
|
bool lar_solver::var_is_int(var_index v) const {
|
||||||
|
@ -1593,17 +1588,13 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var_index lar_solver::add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs,
|
var_index lar_solver::add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs) {
|
||||||
const mpq &m_v) {
|
push_and_register_term(new lar_term(coeffs));
|
||||||
push_and_register_term(new lar_term(coeffs, m_v));
|
|
||||||
return m_terms_start_index + m_terms.size() - 1;
|
return m_terms_start_index + m_terms.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Z3DEBUG_CHECK_UNIQUE_TERMS
|
#if Z3DEBUG_CHECK_UNIQUE_TERMS
|
||||||
bool lar_solver::term_coeffs_are_ok(const vector<std::pair<mpq, var_index>> & coeffs, const mpq& v) {
|
bool lar_solver::term_coeffs_are_ok(const vector<std::pair<mpq, var_index>> & coeffs) {
|
||||||
if (coeffs.empty()) {
|
|
||||||
return is_zero(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto & p : coeffs) {
|
for (const auto & p : coeffs) {
|
||||||
if (column_is_real(p.second))
|
if (column_is_real(p.second))
|
||||||
|
@ -1638,12 +1629,11 @@ void lar_solver::push_and_register_term(lar_term* t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// terms
|
// terms
|
||||||
var_index lar_solver::add_term(const vector<std::pair<mpq, var_index>> & coeffs,
|
var_index lar_solver::add_term(const vector<std::pair<mpq, var_index>> & coeffs) {
|
||||||
const mpq &m_v) {
|
|
||||||
if (strategy_is_undecided())
|
if (strategy_is_undecided())
|
||||||
return add_term_undecided(coeffs, m_v);
|
return add_term_undecided(coeffs);
|
||||||
|
|
||||||
push_and_register_term(new lar_term(coeffs, m_v));
|
push_and_register_term(new lar_term(coeffs));
|
||||||
unsigned adjusted_term_index = m_terms.size() - 1;
|
unsigned adjusted_term_index = m_terms.size() - 1;
|
||||||
var_index ret = m_terms_start_index + adjusted_term_index;
|
var_index ret = m_terms_start_index + adjusted_term_index;
|
||||||
if (use_tableau() && !coeffs.empty()) {
|
if (use_tableau() && !coeffs.empty()) {
|
||||||
|
@ -1656,7 +1646,7 @@ var_index lar_solver::add_term(const vector<std::pair<mpq, var_index>> & coeffs,
|
||||||
}
|
}
|
||||||
|
|
||||||
void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) {
|
void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) {
|
||||||
TRACE("dump_terms", print_term(*term, tout); tout << std::endl;);
|
TRACE("dump_terms", print_term(*term, tout) << std::endl;);
|
||||||
register_new_ext_var_index(term_ext_index, term_is_int(term));
|
register_new_ext_var_index(term_ext_index, term_is_int(term));
|
||||||
// j will be a new variable
|
// j will be a new variable
|
||||||
unsigned j = A_r().column_count();
|
unsigned j = A_r().column_count();
|
||||||
|
@ -1738,9 +1728,8 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k
|
||||||
// lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int());
|
// lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int());
|
||||||
unsigned term_j;
|
unsigned term_j;
|
||||||
if (m_var_register.external_is_used(j, term_j)) {
|
if (m_var_register.external_is_used(j, term_j)) {
|
||||||
mpq rs = right_side - m_terms[adjusted_term_index]->m_v;
|
|
||||||
m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side));
|
m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side));
|
||||||
update_column_type_and_bound(term_j, kind, rs, ci);
|
update_column_type_and_bound(term_j, kind, right_side, ci);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side);
|
add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side);
|
||||||
|
@ -1749,11 +1738,10 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k
|
||||||
|
|
||||||
constraint_index lar_solver::add_constraint(const vector<std::pair<mpq, var_index>>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) {
|
constraint_index lar_solver::add_constraint(const vector<std::pair<mpq, var_index>>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) {
|
||||||
vector<std::pair<mpq, var_index>> left_side;
|
vector<std::pair<mpq, var_index>> left_side;
|
||||||
mpq rs = -right_side_parm;
|
substitute_terms_in_linear_expression(left_side_with_terms, left_side);
|
||||||
substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs);
|
unsigned term_index = add_term(left_side);
|
||||||
unsigned term_index = add_term(left_side, zero_of_type<mpq>());
|
|
||||||
constraint_index ci = m_constraints.size();
|
constraint_index ci = m_constraints.size();
|
||||||
add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci);
|
add_var_bound_on_constraint_for_term(term_index, kind_par, right_side_parm, ci);
|
||||||
return ci;
|
return ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1762,7 +1750,7 @@ void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned ter
|
||||||
|
|
||||||
add_row_from_term_no_constraint(term, term_j);
|
add_row_from_term_no_constraint(term, term_j);
|
||||||
unsigned j = A_r().column_count() - 1;
|
unsigned j = A_r().column_count() - 1;
|
||||||
update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size());
|
update_column_type_and_bound(j, kind, right_side, m_constraints.size());
|
||||||
m_constraints.push_back(new lar_term_constraint(term, kind, right_side));
|
m_constraints.push_back(new lar_term_constraint(term, kind, right_side));
|
||||||
lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size());
|
lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size());
|
||||||
}
|
}
|
||||||
|
@ -1964,7 +1952,6 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin
|
||||||
|
|
||||||
if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) {
|
if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) {
|
||||||
m_status = lp_status::INFEASIBLE;
|
m_status = lp_status::INFEASIBLE;
|
||||||
lp_assert(false);
|
|
||||||
m_infeasible_column_index = j;
|
m_infeasible_column_index = j;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2261,6 +2248,7 @@ void lar_solver::set_cut_strategy(unsigned cut_frequency) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace lp
|
} // namespace lp
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -164,13 +164,11 @@ public:
|
||||||
|
|
||||||
|
|
||||||
// terms
|
// terms
|
||||||
var_index add_term(const vector<std::pair<mpq, var_index>> & coeffs,
|
var_index add_term(const vector<std::pair<mpq, var_index>> & coeffs);
|
||||||
const mpq &m_v);
|
|
||||||
|
|
||||||
var_index add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs,
|
var_index add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs);
|
||||||
const mpq &m_v);
|
|
||||||
|
|
||||||
bool term_coeffs_are_ok(const vector<std::pair<mpq, var_index>> & coeffs, const mpq& v);
|
bool term_coeffs_are_ok(const vector<std::pair<mpq, var_index>> & coeffs);
|
||||||
void push_and_register_term(lar_term* t);
|
void push_and_register_term(lar_term* t);
|
||||||
|
|
||||||
void add_row_for_term(const lar_term * term, unsigned term_ext_index);
|
void add_row_for_term(const lar_term * term, unsigned term_ext_index);
|
||||||
|
@ -208,7 +206,10 @@ public:
|
||||||
void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci);
|
void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci);
|
||||||
|
|
||||||
void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci);
|
void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci);
|
||||||
|
|
||||||
//end of init region
|
//end of init region
|
||||||
|
|
||||||
|
|
||||||
lp_settings & settings();
|
lp_settings & settings();
|
||||||
|
|
||||||
lp_settings const & settings() const;
|
lp_settings const & settings() const;
|
||||||
|
@ -227,9 +228,7 @@ public:
|
||||||
bool use_lu() const;
|
bool use_lu() const;
|
||||||
|
|
||||||
bool sizes_are_correct() const;
|
bool sizes_are_correct() const;
|
||||||
|
|
||||||
void print_implied_bound(const implied_bound& be, std::ostream & out) const;
|
|
||||||
|
|
||||||
bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
|
bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
|
||||||
|
|
||||||
void analyze_new_bounds_on_row(
|
void analyze_new_bounds_on_row(
|
||||||
|
@ -238,8 +237,7 @@ public:
|
||||||
|
|
||||||
void analyze_new_bounds_on_row_tableau(
|
void analyze_new_bounds_on_row_tableau(
|
||||||
unsigned row_index,
|
unsigned row_index,
|
||||||
bound_propagator & bp
|
bound_propagator & bp);
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
void substitute_basis_var_in_terms_for_row(unsigned i);
|
void substitute_basis_var_in_terms_for_row(unsigned i);
|
||||||
|
@ -331,7 +329,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
void substitute_terms_in_linear_expression( const vector<std::pair<mpq, var_index>>& left_side_with_terms,
|
void substitute_terms_in_linear_expression( const vector<std::pair<mpq, var_index>>& left_side_with_terms,
|
||||||
vector<std::pair<mpq, var_index>> &left_side, mpq & free_coeff) const;
|
vector<std::pair<mpq, var_index>> &left_side) const;
|
||||||
|
|
||||||
|
|
||||||
void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j);
|
void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j);
|
||||||
|
@ -397,7 +395,7 @@ public:
|
||||||
|
|
||||||
bool try_to_set_fixed(column_info<mpq> & ci);
|
bool try_to_set_fixed(column_info<mpq> & ci);
|
||||||
|
|
||||||
column_type get_column_type(const column_info<mpq> & ci);
|
column_type get_column_type(unsigned j) const;
|
||||||
|
|
||||||
std::string get_column_name(unsigned j) const;
|
std::string get_column_name(unsigned j) const;
|
||||||
|
|
||||||
|
@ -436,30 +434,33 @@ public:
|
||||||
int inf_sign) const;
|
int inf_sign) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void get_model(std::unordered_map<var_index, mpq> & variable_values) const;
|
void get_model(std::unordered_map<var_index, mpq> & variable_values) const;
|
||||||
|
|
||||||
void get_model_do_not_care_about_diff_vars(std::unordered_map<var_index, mpq> & variable_values) const;
|
void get_model_do_not_care_about_diff_vars(std::unordered_map<var_index, mpq> & variable_values) const;
|
||||||
|
|
||||||
std::string get_variable_name(var_index vi) const;
|
std::string get_variable_name(var_index vi) const;
|
||||||
|
|
||||||
// ********** print region start
|
// print utilities
|
||||||
void print_constraint(constraint_index ci, std::ostream & out) const;
|
|
||||||
|
|
||||||
void print_constraints(std::ostream& out) const ;
|
std::ostream& print_constraint(constraint_index ci, std::ostream & out) const;
|
||||||
|
|
||||||
void print_terms(std::ostream& out) const;
|
std::ostream& print_constraints(std::ostream& out) const ;
|
||||||
|
|
||||||
void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const;
|
std::ostream& print_terms(std::ostream& out) const;
|
||||||
|
|
||||||
void print_term(lar_term const& term, std::ostream & out) const;
|
std::ostream& print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const;
|
||||||
|
|
||||||
void print_term_as_indices(lar_term const& term, std::ostream & out) const;
|
std::ostream& print_term(lar_term const& term, std::ostream & out) const;
|
||||||
|
|
||||||
|
std::ostream& print_term_as_indices(lar_term const& term, std::ostream & out) const;
|
||||||
|
|
||||||
|
std::ostream& print_constraint(const lar_base_constraint * c, std::ostream & out) const;
|
||||||
|
|
||||||
|
std::ostream& print_implied_bound(const implied_bound& be, std::ostream & out) const;
|
||||||
|
|
||||||
|
|
||||||
mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) const;
|
mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) const;
|
||||||
|
|
||||||
void print_constraint(const lar_base_constraint * c, std::ostream & out) const;
|
|
||||||
|
|
||||||
void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector<unsigned>& column_list);
|
void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector<unsigned>& column_list);
|
||||||
|
|
||||||
void random_update(unsigned sz, var_index const * vars);
|
void random_update(unsigned sz, var_index const * vars);
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
#include "util/lp/indexed_vector.h"
|
#include "util/lp/indexed_vector.h"
|
||||||
namespace lp {
|
namespace lp {
|
||||||
struct lar_term {
|
struct lar_term {
|
||||||
// the term evaluates to sum of m_coeffs + m_v
|
// the term evaluates to sum of m_coeffs
|
||||||
std::unordered_map<unsigned, mpq> m_coeffs;
|
std::unordered_map<unsigned, mpq> m_coeffs;
|
||||||
mpq m_v;
|
// mpq m_v;
|
||||||
lar_term() {}
|
lar_term() {}
|
||||||
void add_monomial(const mpq& c, unsigned j) {
|
void add_monomial(const mpq& c, unsigned j) {
|
||||||
auto it = m_coeffs.find(j);
|
auto it = m_coeffs.find(j);
|
||||||
|
@ -37,7 +37,7 @@ struct lar_term {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_empty() const {
|
bool is_empty() const {
|
||||||
return m_coeffs.size() == 0 && is_zero(m_v);
|
return m_coeffs.size() == 0; // && is_zero(m_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
|
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
|
||||||
|
@ -46,8 +46,7 @@ struct lar_term {
|
||||||
return m_coeffs;
|
return m_coeffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs,
|
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs) {
|
||||||
const mpq & v) : m_v(v) {
|
|
||||||
for (const auto & p : coeffs) {
|
for (const auto & p : coeffs) {
|
||||||
add_monomial(p.first, p.second);
|
add_monomial(p.first, p.second);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +86,7 @@ struct lar_term {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T apply(const vector<T>& x) const {
|
T apply(const vector<T>& x) const {
|
||||||
T ret = T(m_v);
|
T ret(0);
|
||||||
for (const auto & t : m_coeffs) {
|
for (const auto & t : m_coeffs) {
|
||||||
ret += t.second * x[t.first];
|
ret += t.second * x[t.first];
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,6 @@ struct lar_term {
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
m_coeffs.clear();
|
m_coeffs.clear();
|
||||||
m_v = zero_of_type<mpq>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ival {
|
struct ival {
|
||||||
|
|
|
@ -577,7 +577,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_column_info(unsigned j, std::ostream & out) const {
|
void print_column_info(unsigned j, std::ostream & out) const {
|
||||||
out << "j = " << j << ", name = "<< column_name(j);
|
out << "j = " << j << ",\tname = "<< column_name(j) << "\t";
|
||||||
switch (m_column_types[j]) {
|
switch (m_column_types[j]) {
|
||||||
case column_type::fixed:
|
case column_type::fixed:
|
||||||
case column_type::boxed:
|
case column_type::boxed:
|
||||||
|
@ -596,11 +596,11 @@ public:
|
||||||
lp_assert(false);
|
lp_assert(false);
|
||||||
}
|
}
|
||||||
// out << "basis heading = " << m_basis_heading[j] << std::endl;
|
// out << "basis heading = " << m_basis_heading[j] << std::endl;
|
||||||
out << " x = " << m_x[j];
|
out << "\tx = " << m_x[j];
|
||||||
if (m_basis_heading[j] >= 0)
|
if (m_basis_heading[j] >= 0)
|
||||||
out << " base\n";
|
out << " base\n";
|
||||||
else
|
else
|
||||||
out << " nbas\n";
|
out << " \n";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; }
|
bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; }
|
||||||
|
|
|
@ -1238,6 +1238,7 @@ template <typename T, typename X> void lp_primal_core_solver<T, X>::print_column
|
||||||
break;
|
break;
|
||||||
case column_type::free_column:
|
case column_type::free_column:
|
||||||
out << "( _" << this->m_x[j] << "_)" << std::endl;
|
out << "( _" << this->m_x[j] << "_)" << std::endl;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
lp_unreachable();
|
lp_unreachable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -357,7 +357,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Z3DEBUG
|
#ifdef Z3DEBUG
|
||||||
static unsigned ddd; // used for debugging
|
static unsigned ddd; // used for debugging
|
||||||
#endif
|
#endif
|
||||||
}; // end of lp_settings class
|
}; // end of lp_settings class
|
||||||
|
|
||||||
|
@ -433,7 +433,15 @@ inline void ensure_increasing(vector<unsigned> & v) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static bool is_rational(const impq & n) { return is_zero(n.y); }
|
||||||
|
|
||||||
|
inline static mpq fractional_part(const impq & n) {
|
||||||
|
lp_assert(is_rational(n));
|
||||||
|
return n.x - floor(n.x);
|
||||||
|
}
|
||||||
|
inline static mpq fractional_part(const mpq & n) {
|
||||||
|
return n - floor(n);
|
||||||
|
}
|
||||||
|
|
||||||
#if Z3DEBUG
|
#if Z3DEBUG
|
||||||
bool D();
|
bool D();
|
||||||
|
|
|
@ -24,7 +24,7 @@ Revision History:
|
||||||
namespace lp {
|
namespace lp {
|
||||||
template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) {
|
template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) {
|
||||||
auto it = m_map_from_var_index_to_column_info.find(column);
|
auto it = m_map_from_var_index_to_column_info.find(column);
|
||||||
return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info<T>(static_cast<unsigned>(-1))) : it->second;
|
return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info<T>()) : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename X>
|
template <typename T, typename X>
|
||||||
|
|
|
@ -50,11 +50,34 @@ bool contains(const std::unordered_map<A, B> & map, const A& key) {
|
||||||
|
|
||||||
namespace lp {
|
namespace lp {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
inline void throw_exception(std::string && str) {
|
void print_linear_combination_of_column_indices_only(const T & coeffs, std::ostream & out) {
|
||||||
throw default_exception(std::move(str));
|
bool first = true;
|
||||||
|
for (const auto & it : coeffs) {
|
||||||
|
auto val = it.coeff();
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
if (val.is_pos()) {
|
||||||
|
out << " + ";
|
||||||
|
} else {
|
||||||
|
out << " - ";
|
||||||
|
val = -val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (val == 1)
|
||||||
|
out << " ";
|
||||||
|
else
|
||||||
|
out << T_to_string(val);
|
||||||
|
|
||||||
|
out << "x" << it.var();
|
||||||
}
|
}
|
||||||
typedef z3_exception exception;
|
}
|
||||||
|
|
||||||
|
inline void throw_exception(std::string && str) {
|
||||||
|
throw default_exception(std::move(str));
|
||||||
|
}
|
||||||
|
typedef z3_exception exception;
|
||||||
|
|
||||||
#define lp_assert(_x_) { SASSERT(_x_); }
|
#define lp_assert(_x_) { SASSERT(_x_); }
|
||||||
inline void lp_unreachable() { lp_assert(false); }
|
inline void lp_unreachable() { lp_assert(false); }
|
||||||
|
|
|
@ -152,7 +152,7 @@ class mpz_manager {
|
||||||
|
|
||||||
// make sure that n is a big number and has capacity equal to at least c.
|
// make sure that n is a big number and has capacity equal to at least c.
|
||||||
void allocate_if_needed(mpz & n, unsigned c) {
|
void allocate_if_needed(mpz & n, unsigned c) {
|
||||||
c = std::max(c, m_init_cell_capacity);
|
if (m_init_cell_capacity > c) c = m_init_cell_capacity;
|
||||||
if (n.m_ptr == nullptr || capacity(n) < c) {
|
if (n.m_ptr == nullptr || capacity(n) < c) {
|
||||||
deallocate(n);
|
deallocate(n);
|
||||||
n.m_val = 1;
|
n.m_val = 1;
|
||||||
|
|
Loading…
Reference in a new issue