Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion cvc5_pythonic_api/cvc5_pythonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
* FiniteDomainSort
* Fixedpoint API
* SMT2 file support
* recursive functions
* Not missing, but different
* Options
* as expected
Expand Down Expand Up @@ -150,6 +149,8 @@ def __init__(self):
self.vars = {}
# An increasing identifier used to make fresh identifiers
self.next_fresh_var = 0
# Function definitions to be added to a solver once it is created
self.defined_functions = []

def __del__(self):
self.tm = None
Expand Down Expand Up @@ -937,6 +938,50 @@ def FreshFunction(*sig):
return Function(name, *sig)


def RecFunction(name, *sig):
"""Create a new SMT uninterpreted function with the given sorts."""
return Function(name, sig)


def RecAddDefinition(func, args, body):
"""Define a new SMT recursive function with the given function declaration.
Replaces constants in `args` with bound variables.

>>> fact = Function('fact', IntSort(), IntSort())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Z3, a recursive function must be declared using RecFunction instead of Function. If you try to add a definition using RecAddDefinition to a function declared with Function, Z3 will trigger an error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had an implementation of RecFunction but it turned out exactly the same as Function. The reason for this distinction in z3 is because they have Z3_mk_func_decl and Z3_mk_rec_func_decl, here I believe we just need mkConst to declare a function, recursive or not.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add RecFunction to maintain compatibility, but do you think we should restrict the usage of RecAddDefinition like z3 does?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have discussed this in the cvc5 team and the conclusion is:

  • we should support the same functionality that z3 does, so to have a RecFunction method
  • we can also have extra functionality, so it's OK to support the case where we use Function

>>> n = Int('n')
>>> RecAddDefinition(fact, n, If(n == 0, 1, n * fact(n - 1)))
>>> solve(Not(fact(5) == 120))
unsat
"""
if is_app(args):
args = [args]
ctx = func.ctx
consts = [a.ast for a in args]
vars_ = [ctx.tm.mkVar(a.sort().ast, str(a)) for a in args]
subbed_body = body.ast.substitute(consts, vars_)
ctx.defined_functions.append(((func.ast, vars_, subbed_body), True))


def AddDefinition(name, args, body):
"""Define a new SMT function with the given function declaration.
Replaces constants in `args` with bound variables.

>>> x, y = Ints('x y')
>>> minus = AddDefinition(minus, [x, y], x - y)
>>> solve(Not(minus(10, 5) == 5))
unsat
"""
if is_app(args):
args = [args]
ctx = body.ctx
consts = [a.ast for a in args]
vars_ = [ctx.tm.mkVar(a.sort().ast, str(a)) for a in args]
subbed_body = body.ast.substitute(consts, vars_)
ctx.defined_functions.append(
((name, vars_, subbed_body.getSort(), subbed_body), False)
)


#########################################
#
# Expressions
Expand Down Expand Up @@ -6006,6 +6051,16 @@ def initFromLogic(self):
self.solver.setLogic(self.logic)
self.solver.setOption("produce-models", "true")

def add_func_definitions(self):
"""Add function definitions present in the current context"""
# FIXME: This is a temporary fix and should be removed once the base
# API have the proper solution in place.
for func in self.ctx.defined_functions:
if func[1]:
self.solver.defineFunRec(*func[0])
else:
self.solver.defineFun(*func[0])

def __del__(self):
if self.solver is not None:
self.solver = None
Expand Down Expand Up @@ -6190,6 +6245,7 @@ def check(self, *assumptions):
unsat
>>> s.resetAssertions()
"""
self.add_func_definitions()
assumptions = _get_args(assumptions)
r = CheckSatResult(self.solver.checkSatAssuming(*[a.ast for a in assumptions]))
self.last_result = r
Expand Down
Loading