3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 19:27:06 +00:00

Merge pull request #1906 from NikolajBjorner/csp

integrate native support for recursive definitions
This commit is contained in:
Nikolaj Bjorner 2018-10-31 14:41:51 -05:00 committed by GitHub
commit eef2ac0ff5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 4838 additions and 459 deletions

View file

@ -34,7 +34,7 @@ endif()
################################################################################
set(Z3_VERSION_MAJOR 4)
set(Z3_VERSION_MINOR 8)
set(Z3_VERSION_PATCH 2)
set(Z3_VERSION_PATCH 3)
set(Z3_VERSION_TWEAK 0)
set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}")
set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified

View file

@ -1,5 +1,19 @@
RELEASE NOTES
Version 4.8.3
=============
- New features
- native handling of recursive function definitions
- PB rounding based option for conflict resolution when reasoning about PB constraints.
Version 4.8.2
=============
- Post-Release.
Version 4.8.1
=============
- Release. Bug-fix for 4.8.0
Version 4.8.0
=============

93
doc/design_recfuns.md Normal file
View file

@ -0,0 +1,93 @@
# Design for handling recursive functions
Main source of inspiration is [Sutter, Köksal & Kuncak 2011],
as implemented in Leon, but the main
differences is that we should unroll function definitions directly from the
inside of Z3, in a backtracking way. Termination and fairness are ensured by
iterative-deepening on the maximum number of unrollings in a given branch.
## Unfolding
The idea is that every function definition `f(x1…xn) := rhs[x1…xn]` is
compiled into:
- a list of cases `A_f_i[x1…xn] => f(x1…xn) = rhs_i[x1…xn]`.
When `A_f_i[t1…tn]` becomes true in the model, `f(t1…tn)` is said to be
*unfolded* and the clause `A_f_i[t1…tn] => f(t1…tn) = rhs_i[t1…tn]`
is added as an auxiliary clause.
- a list of constraints `Γ_f_i[x1…xn] <=> A_f_i[x1…xn]`
that states when `A_f_i[x1…xn]` should be true, depending on inputs `x1…xn`.
For every term `f(t1…tn)` present in congruence closure, we
immediately add all the `Γ_f_i[t1…tn] <=> A_f_i[t1…tn]` as auxiliary clauses
(maybe during internalization of `f(t1…tn)`?).
where each `A_f_i[x1…xn]` is a special new predicate representing the
given case of `f`, and `rhs_i` does not contain any `ite`.
We assume pattern matching has been compiled to `ite` beforehand.
For example, `fact(n) := if n<2 then 1 else n * fact(n-1)` is compiled into:
- `A_fact_0[n] => fact(n) = 1`
- `A_fact_1[n] => fact(n) = n * fact(n-1)`
- `A_fact_0[n] <=> n < 2`
- `A_fact_1[n] <=> ¬(n < 2)`
The 2 first clauses are only added when `A_fact_0[t]` is true
(respectively `A_fact_1[t]` is true).
The 2 other clauses are added as soon as `fact(t)` is internalized.
## Termination
To ensure termination, we define variables:
- `unfold_depth: int`
- `current_max_unfold_depth: int`
- `global_max_unfold_depth: int`
and a special literal `[max_depth=$n]` for each `n:int`.
Solving is done under the local assumption
`[max_depth=$current_max_unfold_depth]` (this should be handled in some outer
loop, e.g. in a custom tactic).
Whenever `A_f_i[t1…tn]` becomes true (for any `f`), we increment
`unfold_depth`. If `unfold_depth > current_max_unfold_depth`, then
the conflict clause `[max_depth=$current_max_unfold_depth] => Γ => false`
where `Γ` is the conjunction of all `A_f_i[t1…tn]` true in the trail.
For non-recursive functions, we don't have to increment `unfold_depth`. Some other functions that are known
If the solver answers "SAT", we have a model.
Otherwise, if `[max_depth=$current_max_unfold_depth]` is part of the
unsat-core, then we increase `current_max_unfold_depth`.
If `current_max_unfold_depth == global_max_unfold_depth` then
we report "UNKNOWN" (reached global depth limit), otherwise we can
try to `solve()` again with the new assumption (higher depth limit).
## Tactic
there should be a parametrized tactic `funrec(t, n)` where `t` is the tactic
used to solve (under assumption that depth is limited to `current_max_unfold_depth`)
and `n` is an integer that is assigned to `global_max_unfold_depth`.
This way, to try and find models for a problem with recursive functions + LIA,
one could use something like `(funrec (then simplify dl smt) 100)`.
## Expected benefits
This addition to Z3 would bring many benefits compared to current alternatives (Leon, quantifiers, …)
- should be very fast and lightweight
(compared to Leon or quantifiers).
In particular, every function call is very lightweight even compared to Leon (no need for full model building, followed by unsat core extraction)
- possibility of answering "SAT" for any `QF_*` fragment +
recursive functions
- makes `define-funs-rec` a first-class citizen of the language, usable to model user-defined theories or to analyze functional
programs directly
## Optimizations
- maybe `C_f_i` literals should never be decided on
(they can always be propagated).
Even stronger: they should not be part of conflicts?
(i.e. tune conflict resolution to always resolve
these literals away, disregarding their level)

View file

@ -1190,6 +1190,20 @@ void mk_model_example() {
std::cout << m.eval(a + b < 2)<< std::endl;
}
void recfun_example() {
std::cout << "recfun example\n";
context c;
expr x = c.int_const("x");
expr y = c.int_const("y");
expr b = c.bool_const("b");
sort I = c.int_sort();
sort B = c.bool_sort();
func_decl f = recfun("f", I, B, I);
expr_vector args(c);
args.push_back(x); args.push_back(b);
c.recdef(f, args, ite(b, x, f(x + 1, !b)));
prove(f(x,c.bool_val(false)) > x);
}
int main() {
@ -1239,6 +1253,7 @@ int main() {
consequence_example(); std::cout << "\n";
parse_example(); std::cout << "\n";
mk_model_example(); std::cout << "\n";
recfun_example(); std::cout << "\n";
std::cout << "done\n";
}
catch (exception & ex) {

View file

@ -9,7 +9,7 @@ from mk_util import *
# Z3 Project definition
def init_project_def():
set_version(4, 8, 2, 0)
set_version(4, 8, 3, 0)
add_lib('util', [], includes2install = ['z3_version.h'])
add_lib('polynomial', ['util'], 'math/polynomial')
add_lib('sat', ['util'])

View file

@ -38,6 +38,8 @@ Revision History:
#include "util/cancel_eh.h"
#include "util/scoped_timer.h"
#include "ast/pp_params.hpp"
#include "ast/expr_abstract.h"
extern bool is_numeral_sort(Z3_context c, Z3_sort ty);
@ -110,6 +112,54 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const* domain,
Z3_sort range) {
Z3_TRY;
LOG_Z3_mk_rec_func_decl(c, s, domain_size, domain, range);
RESET_ERROR_CODE();
//
recfun::promise_def def =
mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s),
domain_size,
to_sorts(domain),
to_sort(range));
func_decl* d = def.get_def()->get_decl();
mk_c(c)->save_ast_trail(d);
RETURN_Z3(of_func_decl(d));
Z3_CATCH_RETURN(nullptr);
}
void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body) {
Z3_TRY;
LOG_Z3_add_rec_def(c, f, n, args, body);
func_decl* d = to_func_decl(f);
ast_manager& m = mk_c(c)->m();
recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin();
expr_ref abs_body(m);
expr_ref_vector _args(m);
var_ref_vector _vars(m);
for (unsigned i = 0; i < n; ++i) {
_args.push_back(to_expr(args[i]));
_vars.push_back(m.mk_var(n - i - 1, m.get_sort(_args.back())));
if (m.get_sort(_args.back()) != d->get_domain(i)) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return;
}
}
expr_abstract(m, 0, n, _args.c_ptr(), to_expr(body), abs_body);
recfun::promise_def pd = p.get_promise_def(d);
if (!pd.get_def()) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return;
}
if (m.get_sort(abs_body) != d->get_range()) {
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return;
}
p.set_definition(pd, n, _vars.c_ptr(), abs_body);
Z3_CATCH;
}
Z3_ast Z3_API Z3_mk_app(Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const * args) {
Z3_TRY;
LOG_Z3_mk_app(c, d, num_args, args);

View file

@ -79,6 +79,7 @@ namespace api {
m_datalog_util(m()),
m_fpa_util(m()),
m_sutil(m()),
m_recfun(m()),
m_last_result(m()),
m_ast_trail(m()),
m_pmanager(m_limit) {

View file

@ -29,6 +29,7 @@ Revision History:
#include "ast/datatype_decl_plugin.h"
#include "ast/dl_decl_plugin.h"
#include "ast/fpa_decl_plugin.h"
#include "ast/recfun_decl_plugin.h"
#include "smt/smt_kernel.h"
#include "smt/params/smt_params.h"
#include "util/event_handler.h"
@ -62,6 +63,7 @@ namespace api {
datalog::dl_decl_util m_datalog_util;
fpa_util m_fpa_util;
seq_util m_sutil;
recfun::util m_recfun;
// Support for old solver API
smt_params m_fparams;
@ -128,6 +130,7 @@ namespace api {
fpa_util & fpautil() { return m_fpa_util; }
datatype_util& dtutil() { return m_dt_plugin->u(); }
seq_util& sutil() { return m_sutil; }
recfun::util& recfun() { return m_recfun; }
family_id get_basic_fid() const { return m_basic_fid; }
family_id get_array_fid() const { return m_array_fid; }
family_id get_arith_fid() const { return m_arith_fid; }

View file

@ -715,6 +715,4 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
};

View file

@ -1,5 +1,5 @@
/*++
Copyright (c) Microsoft Corporation, Arive Gurfinkel 2017
Copyright (c) Microsoft Corporation, Arie Gurfinkel 2017
Module Name:

View file

@ -311,6 +311,13 @@ namespace z3 {
func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range);
func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range);
func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range);
func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range);
func_decl recfun(char const * name, sort const & domain, sort const & range);
func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range);
void recdef(func_decl, expr_vector const& args, expr const& body);
expr constant(symbol const & name, sort const & s);
expr constant(char const * name, sort const & s);
expr bool_const(char const * name);
@ -2815,6 +2822,37 @@ namespace z3 {
return func_decl(*this, f);
}
inline func_decl context::recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
array<Z3_sort> args(arity);
for (unsigned i = 0; i < arity; i++) {
check_context(domain[i], range);
args[i] = domain[i];
}
Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, arity, args.ptr(), range);
check_error();
return func_decl(*this, f);
}
inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) {
return recfun(str_symbol(name), arity, domain, range);
}
inline func_decl context::recfun(char const * name, sort const& d1, sort const & range) {
return recfun(str_symbol(name), 1, &d1, range);
}
inline func_decl context::recfun(char const * name, sort const& d1, sort const& d2, sort const & range) {
sort dom[2] = { d1, d2 };
return recfun(str_symbol(name), 2, dom, range);
}
void context::recdef(func_decl f, expr_vector const& args, expr const& body) {
check_context(f, args); check_context(f, body);
array<Z3_ast> vars(args);
Z3_add_rec_def(f.ctx(), f, vars.size(), vars.ptr(), body);
}
inline expr context::constant(symbol const & name, sort const & s) {
Z3_ast r = Z3_mk_const(m_ctx, name, s);
check_error();
@ -2976,6 +3014,19 @@ namespace z3 {
return range.ctx().function(name.c_str(), domain, range);
}
inline func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) {
return range.ctx().recfun(name, arity, domain, range);
}
inline func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range) {
return range.ctx().recfun(name, arity, domain, range);
}
inline func_decl recfun(char const * name, sort const& d1, sort const & range) {
return range.ctx().recfun(name, d1, range);
}
inline func_decl recfun(char const * name, sort const& d1, sort const& d2, sort const & range) {
return range.ctx().recfun(name, d1, d2, range);
}
inline expr select(expr const & a, expr const & i) {
check_context(a, i);
Z3_ast r = Z3_mk_select(a.ctx(), a, i);

View file

@ -532,6 +532,34 @@ namespace Microsoft.Z3
return new FuncDecl(this, MkSymbol(name), domain, range);
}
/// <summary>
/// Creates a new recursive function declaration.
/// </summary>
public FuncDecl MkRecFuncDecl(string name, Sort[] domain, Sort range)
{
Debug.Assert(range != null);
Debug.Assert(domain.All(d => d != null));
CheckContextMatch<Sort>(domain);
CheckContextMatch(range);
return new FuncDecl(this, MkSymbol(name), domain, range, true);
}
/// <summary>
/// Bind a definition to a recursive function declaration.
/// The function must have previously been created using
/// MkRecFuncDecl. The body may contain recursive uses of the function or
/// other mutually recursive functions.
/// </summary>
public void AddRecDef(FuncDecl f, Expr[] args, Expr body)
{
CheckContextMatch(f);
CheckContextMatch<Expr>(args);
CheckContextMatch(body);
IntPtr[] argsNative = AST.ArrayToNative(args);
Native.Z3_add_rec_def(nCtx, f.NativeObject, (uint)args.Length, argsNative, body.NativeObject);
}
/// <summary>
/// Creates a new function declaration.
/// </summary>

View file

@ -302,6 +302,15 @@ namespace Microsoft.Z3
Debug.Assert(range != null);
}
internal FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range, bool is_rec)
: base(ctx, Native.Z3_mk_rec_func_decl(ctx.nCtx, name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject))
{
Debug.Assert(ctx != null);
Debug.Assert(name != null);
Debug.Assert(range != null);
}
#if DEBUG
internal override void CheckNativeObject(IntPtr obj)
{

View file

@ -1,4 +1,5 @@
############################################
# Copyright (c) 2012 Microsoft Corporation
#
@ -799,6 +800,49 @@ def Function(name, *sig):
def _to_func_decl_ref(a, ctx):
return FuncDeclRef(a, ctx)
def RecFunction(name, *sig):
"""Create a new Z3 recursive with the given sorts."""
sig = _get_args(sig)
if __debug__:
_z3_assert(len(sig) > 0, "At least two arguments expected")
arity = len(sig) - 1
rng = sig[arity]
if __debug__:
_z3_assert(is_sort(rng), "Z3 sort expected")
dom = (Sort * arity)()
for i in range(arity):
if __debug__:
_z3_assert(is_sort(sig[i]), "Z3 sort expected")
dom[i] = sig[i].ast
ctx = rng.ctx
return FuncDeclRef(Z3_mk_rec_func_decl(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx)
def RecAddDefinition(f, args, body):
"""Set the body of a recursive function.
Recursive definitions are only unfolded during search.
>>> ctx = Context()
>>> fac = RecFunction('fac', IntSort(ctx), IntSort(ctx))
>>> n = Int('n', ctx)
>>> RecAddDefinition(fac, n, If(n == 0, 1, n*fac(n-1)))
>>> simplify(fac(5))
fac(5)
>>> s = Solver(ctx=ctx)
>>> s.add(fac(n) < 3)
>>> s.check()
sat
>>> s.model().eval(fac(5))
120
"""
if is_app(args):
args = [args]
ctx = body.ctx
args = _get_args(args)
n = len(args)
_args = (Ast * n)()
for i in range(n):
_args[i] = args[i].ast
Z3_add_rec_def(ctx.ref(), f.ast, n, _args, body.ast)
#########################################
#
# Expressions
@ -6511,8 +6555,8 @@ class Solver(Z3PPObject):
>>> s.add(x > 0, x < 2)
>>> s.check()
sat
>>> s.model()
[x = 1]
>>> s.model().eval(x)
1
>>> s.add(x < 1)
>>> s.check()
unsat

View file

@ -2081,6 +2081,7 @@ extern "C" {
Z3_sort range);
/**
\brief Create a constant or function application.
@ -2140,6 +2141,48 @@ extern "C" {
def_API('Z3_mk_fresh_const', AST, (_in(CONTEXT), _in(STRING), _in(SORT)))
*/
Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, Z3_string prefix, Z3_sort ty);
/**
\brief Declare a recursive function
\param c logical context.
\param s name of the function.
\param domain_size number of arguments. It should be greater than 0.
\param domain array containing the sort of each argument. The array must contain domain_size elements.
\param range sort of the constant or the return sort of the function.
After declaring recursive function, it should be associated with a recursive definition #Z3_mk_rec_def.
The function #Z3_mk_app can be used to create a constant or function
application.
\sa Z3_mk_app
\sa Z3_mk_rec_def
def_API('Z3_mk_rec_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT)))
*/
Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s,
unsigned domain_size, Z3_sort const domain[],
Z3_sort range);
/**
\brief Define the body of a recursive function.
\param c logical context.
\param f function declaration.
\param n number of arguments to the function
\param args constants that are used as arguments to the recursive function in the definition.
\param body body of the recursive function
After declaring a recursive function or a collection of mutually recursive functions, use
this function to provide the definition for the recursive function.
\sa Z3_mk_rec_func_decl
def_API('Z3_add_rec_def', VOID, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST), _in(AST)))
*/
void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body);
/*@}*/
/** @name Propositional Logic and Equality */

View file

@ -14,6 +14,7 @@ z3_add_component(ast
ast_translation.cpp
ast_util.cpp
bv_decl_plugin.cpp
csp_decl_plugin.cpp
datatype_decl_plugin.cpp
decl_collector.cpp
dl_decl_plugin.cpp
@ -35,6 +36,7 @@ z3_add_component(ast
occurs.cpp
pb_decl_plugin.cpp
pp.cpp
recfun_decl_plugin.cpp
reg_decl_plugins.cpp
seq_decl_plugin.cpp
shared_occs.cpp

View file

@ -361,6 +361,9 @@ public:
app * mk_int(int i) {
return mk_numeral(rational(i), true);
}
app * mk_int(rational const& r) {
return mk_numeral(r, true);
}
app * mk_real(int i) {
return mk_numeral(rational(i), false);
}

View file

@ -16,8 +16,8 @@ Author:
Revision History:
--*/
#include<sstream>
#include<cstring>
#include <sstream>
#include <cstring>
#include "ast/ast.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
@ -1360,7 +1360,8 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs):
m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode),
m_trace_stream(src.m_trace_stream),
m_trace_stream_owner(false),
m_rec_fun(":rec-fun") {
m_rec_fun(":rec-fun"),
m_lambda_def(":lambda-def") {
SASSERT(!src.is_format_manager());
m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true);
init();
@ -1541,6 +1542,10 @@ void ast_manager::raise_exception(char const * msg) {
throw ast_exception(msg);
}
void ast_manager::raise_exception(std::string const& msg) {
throw ast_exception(msg.c_str());
}
std::ostream& ast_manager::display(std::ostream& out, parameter const& p) {
switch (p.get_kind()) {

View file

@ -707,6 +707,10 @@ public:
func_decl * get_decl() const { return m_decl; }
family_id get_family_id() const { return get_decl()->get_family_id(); }
decl_kind get_decl_kind() const { return get_decl()->get_decl_kind(); }
symbol const& get_name() const { return get_decl()->get_name(); }
unsigned get_num_parameters() const { return get_decl()->get_num_parameters(); }
parameter const& get_parameter(unsigned idx) const { return get_decl()->get_parameter(idx); }
parameter const* get_parameters() const { return get_decl()->get_parameters(); }
bool is_app_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; }
unsigned get_num_args() const { return m_num_args; }
expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; }
@ -1558,7 +1562,7 @@ public:
// Equivalent to throw ast_exception(msg)
Z3_NORETURN void raise_exception(char const * msg);
Z3_NORETURN void raise_exception(std::string const& msg) { raise_exception(msg.c_str()); }
Z3_NORETURN void raise_exception(std::string const& s);
std::ostream& display(std::ostream& out, parameter const& p);
@ -1632,7 +1636,7 @@ public:
bool are_distinct(expr * a, expr * b) const;
bool contains(ast * a) const { return m_ast_table.contains(a); }
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; }
func_decl* get_rec_fun_decl(quantifier* q) const;
@ -2497,6 +2501,7 @@ public:
void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); }
};
typedef ast_ref_fast_mark<1> ast_ref_fast_mark1;
typedef ast_ref_fast_mark<2> ast_ref_fast_mark2;
typedef ast_ref_fast_mark1 expr_ref_fast_mark1;

View file

@ -32,5 +32,27 @@ struct mk_pp : public mk_ismt2_pp {
}
};
//<! print vector of ASTs
class mk_pp_vec {
ast_manager & m;
ast_ref_vector vec;
public:
mk_pp_vec(unsigned len, ast ** vec0, ast_manager & m) : m(m), vec(m) {
for (unsigned i=0; i<len; ++i) vec.push_back(vec0[i]);
}
void display(std::ostream & out) const {
bool first = true;
for (ast* e : vec) {
if (first) { first = false; } else { out << " "; }
out << mk_pp(e, m);
}
}
};
inline std::ostream& operator<<(std::ostream & out, mk_pp_vec const & pp) {
pp.display(out);
return out;
}
#endif

View file

@ -20,6 +20,7 @@ Revision History:
#include "ast/ast_pp_util.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_smt_pp.h"
#include "ast/recfun_decl_plugin.h"
void ast_pp_util::collect(expr* e) {
coll.visit(e);
@ -49,7 +50,14 @@ void ast_pp_util::display_decls(std::ostream& out) {
ast_smt2_pp(out, f, m_env) << "\n";
}
}
SASSERT(coll.get_num_preds() == 0);
vector<std::pair<func_decl*, expr*>> recfuns;
recfun::util u(m);
func_decl_ref_vector funs = u.get_rec_funs();
if (funs.empty()) return;
for (func_decl * f : funs) {
recfuns.push_back(std::make_pair(f, u.get_def(f).get_rhs()));
}
ast_smt2_pp_recdefs(out, recfuns, m_env);
}
void ast_pp_util::remove_decl(func_decl* f) {

View file

@ -31,7 +31,7 @@ class ast_pp_util {
decl_collector coll;
ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m, false) {}
ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m) {}
void collect(expr* e);

View file

@ -1163,6 +1163,33 @@ public:
unregister_var_names(f->get_arity());
}
// format set of mutually recursive definitions
void operator()(vector<std::pair<func_decl*, expr*>> const& funs, format_ref & r) {
format_ref_vector decls(m()), bodies(m());
format_ref r1(m()), r2(m());
for (auto const& p : funs) {
unsigned len;
func_decl* f = p.first;
expr* e = p.second;
format * fname = m_env.pp_fdecl_name(f, len);
register_var_names(f->get_arity());
format * args[3];
args[0] = fname;
args[1] = pp_var_args(f->get_arity(), f->get_domain());
args[2] = m_env.pp_sort(f->get_range());
decls.push_back(mk_seq1<format**, f2f>(m(), args, args+3, f2f(), ""));
process(e, r);
bodies.push_back(r);
unregister_var_names(f->get_arity());
}
r1 = mk_seq1<format*const*, f2f>(m(), decls.begin(), decls.end(), f2f(), "");
r2 = mk_seq1<format*const*, f2f>(m(), bodies.begin(), bodies.end(), f2f(), "");
format * args[2];
args[0] = r1;
args[1] = r2;
r = mk_seq1<format**, f2f>(m(), args, args+2, f2f(), "define-rec-funs");
}
};
@ -1275,6 +1302,16 @@ std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem,
return out;
}
std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p) {
ast_manager & m = env.get_manager();
format_ref r(fm(m));
smt2_printer pr(env, p);
pr(funs, r);
pp(out, r.get(), m, p);
return out;
}
mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix):
m_ast(t),
m_manager(m),

View file

@ -105,6 +105,8 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun");
std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun");
std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref());
std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref());
/**
\brief Internal wrapper (for debugging purposes only)

View file

@ -980,14 +980,6 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
}
}
for (unsigned i = 0; i < decls.get_num_preds(); ++i) {
func_decl* d = decls.get_pred_decls()[i];
if (!(*m_is_declared)(d)) {
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
p(d);
strm << "\n";
}
}
#endif
for (expr* a : m_assumptions) {

374
src/ast/csp_decl_plugin.cpp Normal file
View file

@ -0,0 +1,374 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
csp_decl_plugin.h
Abstract:
Declarations used for a job-shop scheduling domain.
Author:
Nikolaj Bjorner (nbjorner) 2018-8-9
Revision History:
--*/
#include "ast/csp_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
void csp_decl_plugin::set_manager(ast_manager* m, family_id fid) {
decl_plugin::set_manager(m, fid);
m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT);
m_alist_sort = m_manager->mk_sort(symbol("AList"), sort_info(m_family_id, ALIST_SORT));
m_job_sort = m_manager->mk_sort(symbol("Job"), sort_info(m_family_id, JOB_SORT));
m_resource_sort = m_manager->mk_sort(symbol("Resource"), sort_info(m_family_id, RESOURCE_SORT));
m_manager->inc_ref(m_int_sort);
m_manager->inc_ref(m_resource_sort);
m_manager->inc_ref(m_job_sort);
m_manager->inc_ref(m_alist_sort);
}
void csp_decl_plugin::finalize() {
m_manager->dec_ref(m_alist_sort);
m_manager->dec_ref(m_job_sort);
m_manager->dec_ref(m_resource_sort);
m_manager->dec_ref(m_int_sort);
}
sort * csp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
if (num_parameters != 0) {
m_manager->raise_exception("no parameters expected with job-shop sort");
}
switch (static_cast<js_sort_kind>(k)) {
case JOB_SORT: return m_job_sort;
case RESOURCE_SORT: return m_resource_sort;
case ALIST_SORT: return m_alist_sort;
default: UNREACHABLE(); return nullptr;
}
}
func_decl * csp_decl_plugin::mk_func_decl(
decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) {
symbol name;
sort* rng = nullptr;
switch (static_cast<js_op_kind>(k)) {
case OP_JS_JOB:
check_arity(arity);
check_index1(num_parameters, parameters);
name = symbol("job");
rng = m_job_sort;
break;
case OP_JS_RESOURCE:
check_arity(arity);
check_index1(num_parameters, parameters);
name = symbol("resource");
rng = m_resource_sort;
break;
case OP_JS_RESOURCE_MAKESPAN:
if (arity != 1 || domain[0] != m_resource_sort) m_manager->raise_exception("makespan expects a resource argument");
name = symbol("makespan");
rng = m_int_sort;
break;
case OP_JS_START:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("start expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job-start");
rng = m_int_sort;
break;
case OP_JS_END:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("resource expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job-end");
rng = m_int_sort;
break;
case OP_JS_JOB2RESOURCE:
if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("job2resource expects a job argument");
if (num_parameters > 0) m_manager->raise_exception("no parameters");
name = symbol("job2resource");
rng = m_resource_sort;
break;
case OP_JS_MODEL:
// has no parameters
// all arguments are of sort alist
name = symbol("js-model");
rng = m_manager->mk_bool_sort();
break;
case OP_JS_JOB_RESOURCE:
if (arity != 6) m_manager->raise_exception("add-job-resource expects 6 arguments");
if (domain[0] != m_job_sort) m_manager->raise_exception("first argument of add-job-resource expects should be a job");
if (domain[1] != m_resource_sort) m_manager->raise_exception("second argument of add-job-resource expects should be a resource");
if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-job-resource expects should be an integer");
if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-job-resource expects should be an integer");
if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-job-resource expects should be an integer");
if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-job-resource should be an a list of properties");
name = symbol("add-job-resource");
rng = m_alist_sort;
break;
case OP_JS_RESOURCE_AVAILABLE:
if (arity != 6) m_manager->raise_exception("add-resource-available expects 6 arguments");
if (domain[0] != m_resource_sort) m_manager->raise_exception("first argument of add-resource-available expects should be a resource");
if (domain[2] != m_int_sort) m_manager->raise_exception("2nd argument of add-resource-available expects should be an integer");
if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-resource-available expects should be an integer");
if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-resource-available expects should be an integer");
if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-resource-available expects should be an integer");
if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-resource-available should be an a list of properties");
name = symbol("add-resource-available");
rng = m_alist_sort;
break;
case OP_JS_JOB_PREEMPTABLE:
if (arity != 1 || domain[0] != m_job_sort)
m_manager->raise_exception("set-preemptable expects one argument, which is a job");
name = symbol("set-preemptable");
rng = m_alist_sort;
break;
case OP_JS_PROPERTIES:
if (arity != 0) m_manager->raise_exception("js-properties takes no arguments");
for (unsigned i = 0; i < num_parameters; ++i) {
if (!parameters[i].is_symbol()) m_manager->raise_exception("js-properties expects a list of keyword parameters");
}
name = symbol("js-properties");
rng = m_alist_sort;
break;
case OP_JS_JOB_GOAL:
if (arity != 1 || domain[0] != m_job_sort)
m_manager->raise_exception("add-job-goal expects one argument, which is a job");
if (num_parameters != 2 || !parameters[0].is_symbol() || !parameters[1].is_int())
m_manager->raise_exception("add-job-goal expects one symbol and one integer parameter");
name = symbol("add-job-goal");
rng = m_alist_sort;
break;
case OP_JS_OBJECTIVE:
if (arity != 0)
m_manager->raise_exception("add-optimization-objective expects no arguments");
if (num_parameters != 1 || !parameters[0].is_symbol())
m_manager->raise_exception("add-optimization-objective expects one symbol parameter");
name = symbol("add-optimization-objective");
rng = m_alist_sort;
break;
default:
UNREACHABLE();
return nullptr;
}
return m_manager->mk_func_decl(name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters));
}
void csp_decl_plugin::check_arity(unsigned arity) {
if (arity > 0)
m_manager->raise_exception("csp variables use parameters only and take no arguments");
}
void csp_decl_plugin::check_index1(unsigned num_parameters, parameter const* ps) {
if (num_parameters != 1 || !ps[0].is_int())
m_manager->raise_exception("csp variable expects a single integer parameter");
}
void csp_decl_plugin::check_index2(unsigned num_parameters, parameter const* ps) {
if (num_parameters != 2 || !ps[0].is_int() || !ps[1].is_int())
m_manager->raise_exception("csp variable expects two integer parameters");
}
bool csp_decl_plugin::is_value(app * e) const {
return is_app_of(e, m_family_id, OP_JS_JOB) || is_app_of(e, m_family_id, OP_JS_RESOURCE);
}
void csp_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
if (logic == symbol("CSP")) {
op_names.push_back(builtin_name("job", OP_JS_JOB));
op_names.push_back(builtin_name("resource", OP_JS_RESOURCE));
op_names.push_back(builtin_name("makespan", OP_JS_RESOURCE_MAKESPAN));
op_names.push_back(builtin_name("job-start", OP_JS_START));
op_names.push_back(builtin_name("job-end", OP_JS_END));
op_names.push_back(builtin_name("job2resource", OP_JS_JOB2RESOURCE));
op_names.push_back(builtin_name("js-model", OP_JS_MODEL));
op_names.push_back(builtin_name("add-job-resource", OP_JS_JOB_RESOURCE));
op_names.push_back(builtin_name("add-resource-available", OP_JS_RESOURCE_AVAILABLE));
op_names.push_back(builtin_name("set-preemptable", OP_JS_JOB_PREEMPTABLE));
op_names.push_back(builtin_name("js-properties", OP_JS_PROPERTIES));
op_names.push_back(builtin_name("add-job-goal", OP_JS_JOB_GOAL));
op_names.push_back(builtin_name("add-optimization-objective", OP_JS_OBJECTIVE));
}
}
void csp_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
if (logic == symbol("CSP")) {
sort_names.push_back(builtin_name("Job", JOB_SORT));
sort_names.push_back(builtin_name("Resource", RESOURCE_SORT));
}
}
expr * csp_decl_plugin::get_some_value(sort * s) {
parameter p(0);
if (is_sort_of(s, m_family_id, JOB_SORT))
return m_manager->mk_const(mk_func_decl(OP_JS_JOB, 1, &p, 0, nullptr, nullptr));
if (is_sort_of(s, m_family_id, RESOURCE_SORT))
return m_manager->mk_const(mk_func_decl(OP_JS_RESOURCE, 1, &p, 0, nullptr, nullptr));
UNREACHABLE();
return nullptr;
}
csp_util::csp_util(ast_manager& m): m(m) {
m_fid = m.mk_family_id("csp");
m_plugin = static_cast<csp_decl_plugin*>(m.get_plugin(m_fid));
}
sort* csp_util::mk_job_sort() {
return m_plugin->mk_job_sort();
}
sort* csp_util::mk_resource_sort() {
return m_plugin->mk_resource_sort();
}
app* csp_util::mk_job(unsigned j) {
parameter p(j);
return m.mk_const(m.mk_func_decl(m_fid, OP_JS_JOB, 1, &p, 0, (sort*const*)nullptr, nullptr));
}
unsigned csp_util::job2id(expr* j) {
if (is_app_of(j, m_fid, OP_JS_JOB)) {
return to_app(j)->get_decl()->get_parameter(0).get_int();
}
SASSERT(is_app_of(j, m_fid, OP_JS_START) ||
is_app_of(j, m_fid, OP_JS_END) ||
is_app_of(j, m_fid, OP_JS_JOB2RESOURCE));
return job2id(to_app(j)->get_arg(0));
}
app* csp_util::mk_resource(unsigned r) {
parameter p(r);
return m.mk_const(m.mk_func_decl(m_fid, OP_JS_RESOURCE, 1, &p, 0, (sort*const*)nullptr, nullptr));
}
unsigned csp_util::resource2id(expr* r) {
SASSERT(is_app_of(r, m_fid, OP_JS_RESOURCE));
return to_app(r)->get_decl()->get_parameter(0).get_int();
}
app* csp_util::mk_start(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_START, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_end(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_END, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_job2resource(unsigned j) {
app_ref job(mk_job(j), m);
sort* js = m.get_sort(job);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_JOB2RESOURCE, 0, nullptr, 1, &js, nullptr), job);
}
app* csp_util::mk_makespan(unsigned r) {
app_ref resource(mk_resource(r), m);
sort* rs = m.get_sort(resource);
return m.mk_app(m.mk_func_decl(m_fid, OP_JS_RESOURCE_MAKESPAN, 0, nullptr, 1, &rs, nullptr), resource);
}
bool csp_util::is_resource(expr* e, unsigned& r) {
return is_app_of(e, m_fid, OP_JS_RESOURCE) && (r = resource2id(e), true);
}
bool csp_util::is_makespan(expr * e, unsigned& r) {
return is_app_of(e, m_fid, OP_JS_RESOURCE_MAKESPAN) && is_resource(to_app(e)->get_arg(0), r);
}
bool csp_util::is_job(expr* e, unsigned& j) {
return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true);
}
bool csp_util::is_job2resource(expr* e, unsigned& j) {
return is_app_of(e, m_fid, OP_JS_JOB2RESOURCE) && (j = job2id(e), true);
}
bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false;
res = to_app(e)->get_arg(0);
arith_util a(m);
rational r;
if (!a.is_numeral(to_app(e)->get_arg(1), r) || !r.is_unsigned()) return false;
loadpct = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false;
cap_time = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false;
start = r.get_uint64();
if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false;
end = r.get_uint64();
if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false;
return true;
}
bool csp_util::is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_JOB_RESOURCE)) return false;
job = to_app(e)->get_arg(0);
res = to_app(e)->get_arg(1);
arith_util a(m);
rational r;
if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false;
loadpct = r.get_unsigned();
if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false;
capacity = r.get_uint64();
if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false;
end = r.get_uint64();
if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false;
return true;
}
bool csp_util::is_set_preemptable(expr* e, expr *& job) {
if (!is_app_of(e, m_fid, OP_JS_JOB_PREEMPTABLE)) return false;
job = to_app(e)->get_arg(0);
return true;
}
bool csp_util::is_js_properties(expr* e, svector<symbol>& properties) {
if (!is_app_of(e, m_fid, OP_JS_PROPERTIES))
return false;
unsigned sz = to_app(e)->get_decl()->get_num_parameters();
for (unsigned i = 0; i < sz; ++i) {
properties.push_back(to_app(e)->get_decl()->get_parameter(i).get_symbol());
}
return true;
}
bool csp_util::is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job) {
if (!is_app_of(e, m_fid, OP_JS_JOB_GOAL))
return false;
SASSERT(2 == to_app(e)->get_decl()->get_num_parameters());
SASSERT(1 == to_app(e)->get_num_args());
symbol g = to_app(e)->get_decl()->get_parameter(0).get_symbol();
level = to_app(e)->get_decl()->get_parameter(1).get_int();
if (g == ":earliest-end-time" || g == "earliest-end-time")
goal = JS_JOB_GOAL_EARLIEST_END_TIME;
else if (g == ":latest-start-time" || g == "latest-start-time")
goal = JS_JOB_GOAL_LATEST_START_TIME;
else
return false;
job = to_app(e)->get_arg(0);
return true;
}
bool csp_util::is_objective(expr* e, js_optimization_objective& objective) {
if (!is_app_of(e, m_fid, OP_JS_OBJECTIVE))
return false;
SASSERT(1 == to_app(e)->get_decl()->get_num_parameters());
symbol obj = to_app(e)->get_decl()->get_parameter(0).get_symbol();
if (obj == ":duration" || obj == "duration")
objective = JS_OBJECTIVE_DURATION;
else if (obj == ":priority" || obj == "priority")
objective = JS_OBJECTIVE_PRIORITY;
else
return false;
return true;
}

162
src/ast/csp_decl_plugin.h Normal file
View file

@ -0,0 +1,162 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
csp_decl_plugin.h
Abstract:
Declarations used for a job-shop scheduling domain.
The job-shop domain comprises of constants job(j), resource(r)
It finds values to variables:
- start(j), end(j), job2resource(j)
It assumes a background of:
- resources : Job -> Resource -> Int * LoadPct - time to run job j on resource r assuming LoadPct
- runtime : Job -> Int - time to run job j if not associated with any resource
- capacity : Resource -> Int -> LoadPct - capacity of resource r at time t, given as sequence of time intervals
// assume each job has at least one resource associated with it.
// introduce a dummy resource if needed.
// Theory:
end(j) - start(j) = time-to-execute(j)
time-to-execute(j) := time-to-execute(j, resource(j)) otherwise
time-to-execute(j, r) := (T - start(j))
where capacity(j,r) = sum_{t = start(j)}^{T} load(loadpct(j,r), r, t)
capacity(j, r) := cap where (cap, loadpct) = resources j r
loadpct(j, r) := loadpct where (cap, loadpct) = resources j r
load(loadpct, r, t) := min(capacity r t, loadpct) / loadpct
capacity(r, t) >= sum_{j | job-on-resource(j, r, t) } min(capacity r t, loadpct(j, r))
// Macros:
job-on-resource(j, r) := r = resource(j);
job-on-resource(j, r, t) := (job-on-resource(j, r) & start(j) <= t <= end(j));
start_min(j, t) := start(j) >= t;
end_max(j, t) := end(j) <= t;
job_link(j1, j2, startstart, hard) := start(j1) = start(j2);
job_link(j1, j2, startstart, soft) := start(j1) <= start(j2);
job_link(j1, j2, endend, hard) := end(j1) = end(j2);
job_link(j1, j2, endend, soft) := end(j2) <= end(j1);
job_link(j1, j2, endstart, hard) := end(j1) = start(j2);
job_link(j1, j2, endstart, soft) := end(j2) <= start(j1);
job_link(j1, j2, startend, hard) := end(j2) = start(j1);
job_link(j1, j2, startend, soft) := end(j1) <= start(j2);
job_delay(j1, j2, t) := end(j1) + t <= end(j2);
job_on_same_resource(j1, j2) := resource(j1) = resource(j2);
job_not_on_same_resource(j1, j2) := resource(j1) != resource(j2);
job_time_intersect(j1, j2) := start(j1) <= end(j2) <= end(j1) || start(j2) <= end(j1) <= end(j2);
job-on-resource(j, r, t) => job-property(j) = null or job_property(j) in working_time_property(r, t);
Author:
Nikolaj Bjorner (nbjorner) 2018-8-9
Revision History:
--*/
#pragma once;
#include "ast/ast.h"
enum js_sort_kind {
JOB_SORT,
RESOURCE_SORT,
ALIST_SORT
};
enum js_op_kind {
OP_JS_JOB, // value of type job
OP_JS_RESOURCE, // value of type resource
OP_JS_RESOURCE_MAKESPAN, // makespan of resource: the minimal resource time required for assigned jobs.
OP_JS_START, // start time of a job
OP_JS_END, // end time of a job
OP_JS_JOB2RESOURCE, // resource associated with job
OP_JS_MODEL, // jobscheduler model
OP_JS_JOB_RESOURCE, // model declaration for job assignment to resource
OP_JS_JOB_PREEMPTABLE, // model declaration for whether job is pre-emptable
OP_JS_RESOURCE_AVAILABLE, // model declaration for availability intervals of resource
OP_JS_PROPERTIES, // model declaration of a set of properties. Each property is a keyword.
OP_JS_JOB_GOAL, // job goal objective :earliest-end-time or :latest-start-time
OP_JS_OBJECTIVE // duration or completion-time
};
enum js_job_goal {
JS_JOB_GOAL_EARLIEST_END_TIME,
JS_JOB_GOAL_LATEST_START_TIME
};
enum js_optimization_objective {
JS_OBJECTIVE_DURATION,
JS_OBJECTIVE_PRIORITY
};
class csp_decl_plugin : public decl_plugin {
public:
csp_decl_plugin() {}
~csp_decl_plugin() override {}
void finalize() override;
void set_manager(ast_manager* m, family_id fid) override;
decl_plugin * mk_fresh() override { return alloc(csp_decl_plugin); }
sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override;
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) override;
bool is_value(app * e) const override;
bool is_unique_value(app * e) const override { return is_value(e); }
void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override;
void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) override;
expr * get_some_value(sort * s) override;
sort * mk_job_sort() const { return m_job_sort; }
sort * mk_resource_sort() const { return m_resource_sort; }
sort * mk_alist_sort() const { return m_alist_sort; }
private:
sort* m_job_sort;
sort* m_resource_sort;
sort* m_alist_sort;
sort* m_int_sort;
void check_arity(unsigned arity);
void check_index1(unsigned n, parameter const* ps);
void check_index2(unsigned n, parameter const* ps);
};
class csp_util {
ast_manager& m;
family_id m_fid;
csp_decl_plugin* m_plugin;
public:
csp_util(ast_manager& m);
sort* mk_job_sort();
sort* mk_resource_sort();
app* mk_job(unsigned j);
app* mk_resource(unsigned r);
app* mk_start(unsigned j);
app* mk_end(unsigned j);
app* mk_job2resource(unsigned j);
app* mk_makespan(unsigned r);
bool is_job(expr* e, unsigned& j);
bool is_job2resource(expr* e, unsigned& j);
bool is_resource(expr* e, unsigned& r);
bool is_makespan(expr* e, unsigned& r);
bool is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properites);
bool is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector<symbol>& properites);
bool is_set_preemptable(expr* e, expr *& job);
bool is_model(expr* e) const { return is_app_of(e, m_fid, OP_JS_MODEL); }
bool is_js_properties(expr* e, svector<symbol>& properties);
bool is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job);
bool is_objective(expr* e, js_optimization_objective& objective);
private:
unsigned job2id(expr* j);
unsigned resource2id(expr* r);
};

View file

@ -50,18 +50,14 @@ void decl_collector::visit_func(func_decl * n) {
if (!m_visited.is_marked(n)) {
family_id fid = n->get_family_id();
if (fid == null_family_id) {
if (m_sep_preds && is_bool(n->get_range()))
m_preds.push_back(n);
else
m_decls.push_back(n);
m_decls.push_back(n);
}
m_visited.mark(n, true);
}
}
decl_collector::decl_collector(ast_manager & m, bool preds):
decl_collector::decl_collector(ast_manager & m):
m_manager(m),
m_sep_preds(preds),
m_dt_util(m) {
m_basic_fid = m_manager.get_basic_family_id();
m_dt_fid = m_dt_util.get_family_id();

View file

@ -26,10 +26,8 @@ Revision History:
class decl_collector {
ast_manager & m_manager;
bool m_sep_preds;
ptr_vector<sort> m_sorts;
ptr_vector<func_decl> m_decls;
ptr_vector<func_decl> m_preds;
ast_mark m_visited;
family_id m_basic_fid;
family_id m_dt_fid;
@ -46,8 +44,7 @@ class decl_collector {
public:
// if preds == true, then predicates are stored in a separate collection.
decl_collector(ast_manager & m, bool preds = true);
decl_collector(ast_manager & m);
ast_manager & m() { return m_manager; }
void visit_func(func_decl* n);
@ -59,11 +56,9 @@ public:
unsigned get_num_sorts() const { return m_sorts.size(); }
unsigned get_num_decls() const { return m_decls.size(); }
unsigned get_num_preds() const { return m_preds.size(); }
sort * const * get_sorts() const { return m_sorts.c_ptr(); }
func_decl * const * get_func_decls() const { return m_decls.c_ptr(); }
func_decl * const * get_pred_decls() const { return m_preds.c_ptr(); }
};
#endif

View file

@ -69,7 +69,11 @@ void check_pred::visit(expr* e) {
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(e);
expr* arg = q->get_expr();
if (m_visited.is_marked(arg)) {
if (!m_check_quantifiers) {
todo.pop_back();
m_visited.mark(e, true);
}
else if (m_visited.is_marked(arg)) {
todo.pop_back();
if (m_pred_holds.is_marked(arg)) {
m_pred_holds.mark(e, true);

View file

@ -53,8 +53,10 @@ class check_pred {
ast_mark m_pred_holds;
ast_mark m_visited;
expr_ref_vector m_refs;
bool m_check_quantifiers;
public:
check_pred(i_expr_pred& p, ast_manager& m) : m_pred(p), m_refs(m) {}
check_pred(i_expr_pred& p, ast_manager& m, bool check_quantifiers = true) :
m_pred(p), m_refs(m), m_check_quantifiers(check_quantifiers) {}
bool operator()(expr* e);

View file

@ -0,0 +1,428 @@
/*++
Copyright (c) 2017 Microsoft Corporation, Simon Cruanes
Module Name:
recfun_decl_plugin.cpp
Abstract:
Declaration and definition of (potentially recursive) functions
Author:
Simon Cruanes 2017-11
Revision History:
--*/
#include <functional>
#include <sstream>
#include "ast/expr_functors.h"
#include "ast/expr_substitution.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/ast_pp.h"
#include "util/scoped_ptr_vector.h"
#define TRACEFN(x) TRACE("recfun", tout << x << '\n';)
#define VALIDATE_PARAM(m, _pred_) if (!(_pred_)) m.raise_exception("invalid parameter to recfun " #_pred_);
namespace recfun {
case_def::case_def(
ast_manager &m,
family_id fid,
def * d,
std::string & name,
unsigned case_index,
sort_ref_vector const & arg_sorts,
expr_ref_vector const& guards,
expr* rhs)
: m_pred(m),
m_guards(guards),
m_rhs(expr_ref(rhs,m)),
m_def(d) {
parameter p(case_index);
func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p);
m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.c_ptr(), m.mk_bool_sort(), info);
}
def::def(ast_manager &m, family_id fid, symbol const & s,
unsigned arity, sort* const * domain, sort* range)
: m(m), m_name(s),
m_domain(m, arity, domain),
m_range(range, m), m_vars(m), m_cases(),
m_decl(m),
m_rhs(m),
m_fid(fid)
{
SASSERT(arity == get_arity());
func_decl_info info(fid, OP_FUN_DEFINED);
m_decl = m.mk_func_decl(s, arity, domain, range, info);
}
// does `e` contain any `ite` construct?
bool def::contains_ite(expr * e) {
struct ite_find_p : public i_expr_pred {
ast_manager & m;
ite_find_p(ast_manager & m) : m(m) {}
virtual bool operator()(expr * e) { return m.is_ite(e); }
};
// ignore ites under quantifiers.
// this is redundant as the code
// that unfolds ites uses quantifier-free portion.
ite_find_p p(m);
check_pred cp(p, m, false);
return cp(e);
}
/*
* compilation of functions to a list of cases.
*
* We use a backtracking algorithm in a relatively functional style,
* where the multiple states (corresponding to alternatives) are stored in
* a region, and deallocated at the end
*/
// immutable list of choices of `ite` terms (mapping each one's condition to true/false)
struct choice_lst {
app * ite;
bool sign;
choice_lst const * next; // or null for the last one
choice_lst(app * ite, bool sign, choice_lst const * next) : ite(ite), sign(sign), next(next) {}
};
struct ite_lst {
app * ite; // invariant: `is_ite(e)`
ite_lst const * next;
ite_lst(app * ite, ite_lst const * next) : ite(ite), next(next) {}
};
// immutable stack of expressions to unfold
struct unfold_lst {
expr * e;
unfold_lst const * next; // or null for last one
};
// main state for one branch of the search tree.
struct branch {
choice_lst const * path; // choices made so far
ite_lst const * to_split; // `ite` terms to make a choice on
unfold_lst const * to_unfold; // terms yet to unfold
branch(unfold_lst const * to_unfold):
path(nullptr), to_split(nullptr), to_unfold(to_unfold) {}
branch(choice_lst const * path, ite_lst const * to_split, unfold_lst const * to_unfold) :
path(path), to_split(to_split), to_unfold(to_unfold) {}
branch(branch const & from) :
path(from.path), to_split(from.to_split), to_unfold(from.to_unfold) {}
};
// state for computing cases from the RHS of a functions' definition
class case_state {
region m_reg;
vector<branch> m_branches;
public:
case_state() : m_reg(), m_branches() {}
bool empty() const { return m_branches.empty(); }
branch pop_branch() {
branch res = m_branches.back();
m_branches.pop_back();
return res;
}
void push_branch(branch const & b) { m_branches.push_back(b); }
unfold_lst const * cons_unfold(expr * e, unfold_lst const * next) {
return new (m_reg) unfold_lst{e, next};
}
unfold_lst const * cons_unfold(expr * e1, expr * e2, unfold_lst const * next) {
return cons_unfold(e1, cons_unfold(e2, next));
}
unfold_lst const * mk_unfold_lst(expr * e) {
return cons_unfold(e, nullptr);
}
ite_lst const * cons_ite(app * ite, ite_lst const * next) {
return new (m_reg) ite_lst{ite, next};
}
choice_lst const * cons_choice(app * ite, bool sign, choice_lst const * next) {
return new (m_reg) choice_lst{ite, sign, next};
}
};
//<! build a substitution and a list of conditions from a path
static void convert_path(ast_manager & m,
choice_lst const * choices,
expr_ref_vector & conditions /* out */,
expr_substitution & subst /* out */)
{
for (; choices != nullptr; choices = choices->next) {
app * ite = choices->ite;
expr* c = nullptr, *th = nullptr, *el = nullptr;
VERIFY(m.is_ite(ite, c, th, el));
// condition to add to the guard
conditions.push_back(choices->sign ? c : m.mk_not(c));
// binding to add to the substitution
subst.insert(ite, choices->sign ? th : el);
}
}
// substitute `subst` in `e`
static expr_ref replace_subst(th_rewriter & th_rw, ast_manager & m,
expr_substitution & subst, expr * e) {
th_rw.reset();
th_rw.set_substitution(&subst);
expr_ref res(m);
th_rw(e, res);
return res;
}
void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) {
case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs);
c.set_is_immediate(is_imm);
TRACEFN("add_case " << name << " " << mk_pp(rhs, m)
<< " :is_imm " << is_imm
<< " :guards " << conditions);
m_cases.push_back(c);
}
// Compute a set of cases, given the RHS
void def::compute_cases(is_immediate_pred & is_i, th_rewriter & th_rw,
unsigned n_vars, var *const * vars, expr* rhs)
{
VERIFY(m_cases.empty() && "cases cannot already be computed");
SASSERT(n_vars == m_domain.size());
TRACEFN("compute cases " << mk_pp(rhs, m));
unsigned case_idx = 0;
std::string name("case-");
name.append(m_name.bare_str());
m_vars.append(n_vars, vars);
m_rhs = rhs;
expr_ref_vector conditions(m);
// is the function a macro (unconditional body)?
if (n_vars == 0 || !contains_ite(rhs)) {
// constant function or trivial control flow, only one (dummy) case
add_case(name, 0, conditions, rhs);
return;
}
// analyze control flow of `rhs`, accumulating guards and
// rebuilding a `ite`-free RHS on the fly for each path in `rhs`.
// Each such `ite`-free term is converted into a case_def and added to definition.
case_state st;
st.push_branch(branch(st.mk_unfold_lst(rhs)));
while (! st.empty()) {
TRACEFN("main loop iter");
branch b = st.pop_branch();
// first: unfold expressions, stopping when we meet subterms that are `ite`
while (b.to_unfold != nullptr) {
ptr_vector<expr> stack;
stack.push_back(b.to_unfold->e);
b.to_unfold = b.to_unfold->next;
while (! stack.empty()) {
expr * e = stack.back();
stack.pop_back();
if (m.is_ite(e)) {
// need to do a case split on `e`, forking the search space
b.to_split = st.cons_ite(to_app(e), b.to_split);
}
else if (is_app(e)) {
// explore arguments
for (expr * arg : *to_app(e)) {
if (contains_ite(arg)) {
stack.push_back(arg);
}
}
}
}
}
if (b.to_split != nullptr) {
// split one `ite`, which will lead to distinct (sets of) cases
app * ite = b.to_split->ite;
expr* c = nullptr, *th = nullptr, *el = nullptr;
VERIFY(m.is_ite(ite, c, th, el));
/* explore both positive choice and negative choice.
* each contains a longer path, with `ite` mapping to `true` (resp. `false),
* and must unfold the `then` (resp. `else`) branch.
* We must also unfold the test itself, for it could contain
* tests.
*/
branch b_pos(st.cons_choice(ite, true, b.path),
b.to_split->next,
st.cons_unfold(c, th, b.to_unfold));
branch b_neg(st.cons_choice(ite, false, b.path),
b.to_split->next,
st.cons_unfold(c, el, b.to_unfold));
st.push_branch(b_neg);
st.push_branch(b_pos);
}
else {
// leaf of the search tree
conditions.reset();
expr_substitution subst(m);
convert_path(m, b.path, conditions, subst);
// substitute, to get rid of `ite` terms
expr_ref case_rhs = replace_subst(th_rw, m, subst, rhs);
for (unsigned i = 0; i < conditions.size(); ++i) {
conditions[i] = replace_subst(th_rw, m, subst, conditions.get(i));
}
// yield new case
bool is_imm = is_i(case_rhs);
add_case(name, case_idx++, conditions, case_rhs, is_imm);
}
}
TRACEFN("done analyzing " << get_name());
}
/*
* Main manager for defined functions
*/
util::util(ast_manager & m)
: m_manager(m), m_fid(m.get_family_id("recfun")), m_th_rw(m),
m_plugin(dynamic_cast<decl::plugin*>(m.get_plugin(m_fid))) {
}
util::~util() {
}
def * util::decl_fun(symbol const& name, unsigned n, sort *const * domain, sort * range) {
return alloc(def, m(), m_fid, name, n, domain, range);
}
void util::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) {
d.set_definition(n_vars, vars, rhs);
}
app_ref util::mk_depth_limit_pred(unsigned d) {
parameter p(d);
func_decl_info info(m_fid, OP_DEPTH_LIMIT, 1, &p);
func_decl* decl = m().mk_const_decl(symbol("recfun-depth-limit"), m().mk_bool_sort(), info);
return app_ref(m().mk_const(decl), m());
}
// used to know which `app` are from this theory
struct is_imm_pred : is_immediate_pred {
util & u;
is_imm_pred(util & u) : u(u) {}
bool operator()(expr * rhs) {
// find an `app` that is an application of a defined function
struct find : public i_expr_pred {
util & u;
find(util & u) : u(u) {}
bool operator()(expr * e) override {
//return is_app(e) ? u.owns_app(to_app(e)) : false;
if (! is_app(e)) return false;
app * a = to_app(e);
return u.is_defined(a);
}
};
find f(u);
check_pred cp(f, u.m());
bool contains_defined_fun = cp(rhs);
return ! contains_defined_fun;
}
};
// set definition
void promise_def::set_definition(unsigned n_vars, var * const * vars, expr * rhs) {
SASSERT(n_vars == d->get_arity());
is_imm_pred is_i(*u);
d->compute_cases(is_i, u->get_th_rewriter(), n_vars, vars, rhs);
}
namespace decl {
plugin::plugin() : decl_plugin(), m_defs(), m_case_defs() {}
plugin::~plugin() { finalize(); }
void plugin::finalize() {
for (auto& kv : m_defs) {
dealloc(kv.m_value);
}
m_defs.reset();
// m_case_defs does not own its data, no need to deallocate
m_case_defs.reset();
m_util = nullptr; // force deletion
}
util & plugin::u() const {
SASSERT(m_manager);
SASSERT(m_family_id != null_family_id);
if (!m_util.get()) {
m_util = alloc(util, *m_manager);
}
return *(m_util.get());
}
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range) {
def* d = u().decl_fun(name, n, params, range);
SASSERT(! m_defs.contains(d->get_decl()));
m_defs.insert(d->get_decl(), d);
return promise_def(&u(), d);
}
void plugin::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) {
u().set_definition(d, n_vars, vars, rhs);
for (case_def & c : d.get_def()->get_cases()) {
m_case_defs.insert(c.get_decl(), &c);
}
}
bool plugin::has_defs() const {
return !m_case_defs.empty();
}
def* plugin::mk_def(symbol const& name, unsigned n, sort ** params, sort * range,
unsigned n_vars, var ** vars, expr * rhs) {
promise_def d = mk_def(name, n, params, range);
SASSERT(! m_defs.contains(d.get_def()->get_decl()));
set_definition(d, n_vars, vars, rhs);
return d.get_def();
}
// generic declaration of symbols
func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range)
{
UNREACHABLE();
return nullptr;
}
}
}

View file

@ -0,0 +1,249 @@
/*++
Copyright (c) 2017 Microsoft Corporation, Simon Cruanes
Module Name:
recfun_decl_plugin.h
Abstract:
Declaration and definition of (potentially recursive) functions
Author:
Simon Cruanes 2017-11
Revision History:
--*/
#pragma once
#include "ast/ast.h"
#include "ast/rewriter/th_rewriter.h"
#include "util/obj_hashtable.h"
namespace recfun {
class case_def; //<! one possible control path of a function
class case_pred; //<! a predicate guarding a given control flow path of a function
class util; //<! util for other modules
class def; //!< definition of a (recursive) function
class promise_def; //!< definition to be complete
enum op_kind {
OP_FUN_DEFINED, // defined function with one or more cases, possibly recursive
OP_FUN_CASE_PRED, // predicate guarding a given control flow path
OP_DEPTH_LIMIT, // predicate enforcing some depth limit
};
/*! A predicate `p(t1...tn)`, that, if true, means `f(t1...tn)` is following
a given path of its control flow and can be unrolled.
For example, `fact n := if n<2 then 1 else n * fact(n-1)` would have two cases,
and therefore two case predicates `C_fact_0` and `C_fact_1`, where
`C_fact_0(t)=true` means `t<2` (first path) and `C_fact_1(t)=true` means `not (t<2)` (second path).
*/
typedef var_ref_vector vars;
class case_def {
friend class def;
func_decl_ref m_pred; //<! predicate used for this case
expr_ref_vector m_guards; //<! conjunction that is equivalent to this case
expr_ref m_rhs; //<! if guard is true, `f(t1…tn) = rhs` holds
def * m_def; //<! definition this is a part of
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
case_def(ast_manager & m,
family_id fid,
def * d,
std::string & name,
unsigned case_index,
sort_ref_vector const & arg_sorts,
expr_ref_vector const& guards,
expr* rhs);
void add_guard(expr_ref && e) { m_guards.push_back(e); }
public:
func_decl* get_decl() const { return m_pred; }
app_ref apply_case_predicate(ptr_vector<expr> const & args) const {
ast_manager& m = m_pred.get_manager();
return app_ref(m.mk_app(m_pred, args.size(), args.c_ptr()), m);
}
def * get_def() const { return m_def; }
expr_ref_vector const & get_guards() const { return m_guards; }
expr * get_guards_c_ptr() const { return *m_guards.c_ptr(); }
expr * get_guard(unsigned i) const { return m_guards[i]; }
expr * get_rhs() const { return m_rhs; }
unsigned num_guards() const { return m_guards.size(); }
bool is_immediate() const { return m_immediate; };
void set_is_immediate(bool b) { m_immediate = b; }
};
// closure for computing whether a `rhs` expression is immediate
struct is_immediate_pred {
virtual bool operator()(expr * rhs) = 0;
};
class def {
friend class util;
friend class promise_def;
typedef vector<case_def> cases;
ast_manager & m;
symbol m_name; //<! name of function
sort_ref_vector m_domain; //<! type of arguments
sort_ref m_range; //<! return type
vars m_vars; //<! variables of the function
cases m_cases; //!< possible cases
func_decl_ref m_decl; //!< generic declaration
expr_ref m_rhs; //!< definition
family_id m_fid;
def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range);
// compute cases for a function, given its RHS (possibly containing `ite`).
void compute_cases(is_immediate_pred &, th_rewriter & th_rw,
unsigned n_vars, var *const * vars, expr* rhs);
void add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false);
bool contains_ite(expr* e); // expression contains a test?
public:
symbol const & get_name() const { return m_name; }
vars const & get_vars() const { return m_vars; }
cases & get_cases() { return m_cases; }
unsigned get_arity() const { return m_domain.size(); }
sort_ref_vector const & get_domain() const { return m_domain; }
sort_ref const & get_range() const { return m_range; }
func_decl * get_decl() const { return m_decl.get(); }
expr * get_rhs() const { return m_rhs; }
bool is_fun_macro() const { return m_cases.size() == 1; }
bool is_fun_defined() const { return !is_fun_macro(); }
};
// definition to be complete (missing RHS)
class promise_def {
friend class util;
util * u;
def * d;
void set_definition(unsigned n_vars, var * const * vars, expr * rhs); // call only once
public:
promise_def(util * u, def * d) : u(u), d(d) {}
promise_def(promise_def const & from) : u(from.u), d(from.d) {}
def * get_def() const { return d; }
};
namespace decl {
class plugin : public decl_plugin {
typedef obj_map<func_decl, def*> def_map;
typedef obj_map<func_decl, case_def*> case_def_map;
mutable scoped_ptr<util> m_util;
def_map m_defs; // function->def
case_def_map m_case_defs; // case_pred->def
ast_manager & m() { return *m_manager; }
public:
plugin();
~plugin() override;
void finalize() override;
util & u() const; // build or return util
bool is_fully_interp(sort * s) const override { return false; } // might depend on unin sorts
decl_plugin * mk_fresh() override { return alloc(plugin); }
sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override {
UNREACHABLE(); return nullptr;
}
func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) override;
promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range);
void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs);
def* mk_def(symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs);
bool has_def(func_decl* f) const { return m_defs.contains(f); }
bool has_defs() const;
def const& get_def(func_decl* f) const { return *(m_defs[f]); }
promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); }
def& get_def(func_decl* f) { return *(m_defs[f]); }
bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); }
case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); }
func_decl_ref_vector get_rec_funs() {
func_decl_ref_vector result(m());
for (auto& kv : m_defs) result.push_back(kv.m_key);
return result;
}
};
}
// Varus utils for recursive functions
class util {
friend class decl::plugin;
ast_manager & m_manager;
family_id m_fid;
th_rewriter m_th_rw;
decl::plugin * m_plugin;
bool compute_is_immediate(expr * rhs);
void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs);
public:
util(ast_manager &m);
~util();
ast_manager & m() { return m_manager; }
th_rewriter & get_th_rewriter() { return m_th_rw; }
decl::plugin& get_plugin() { return *m_plugin; }
bool is_case_pred(expr * e) const { return is_app_of(e, m_fid, OP_FUN_CASE_PRED); }
bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); }
bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); }
bool is_depth_limit(expr * e) const { return is_app_of(e, m_fid, OP_DEPTH_LIMIT); }
bool owns_app(app * e) const { return e->get_family_id() == m_fid; }
//<! don't use native theory if recursive function declarations are not populated with defs
bool has_defs() const { return m_plugin->has_defs(); }
//<! add a function declaration
def * decl_fun(symbol const & s, unsigned n_args, sort *const * args, sort * range);
def& get_def(func_decl* f) {
SASSERT(m_plugin->has_def(f));
return m_plugin->get_def(f);
}
case_def& get_case_def(expr* e) {
SASSERT(is_case_pred(e));
return m_plugin->get_case_def(to_app(e)->get_decl());
}
app* mk_fun_defined(def const & d, unsigned n_args, expr * const * args) {
return m().mk_app(d.get_decl(), n_args, args);
}
app* mk_fun_defined(def const & d, ptr_vector<expr> const & args) {
return mk_fun_defined(d, args.size(), args.c_ptr());
}
func_decl_ref_vector get_rec_funs() {
return m_plugin->get_rec_funs();
}
app_ref mk_depth_limit_pred(unsigned d);
};
}

View file

@ -22,6 +22,7 @@ Revision History:
#include "ast/array_decl_plugin.h"
#include "ast/bv_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/dl_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/pb_decl_plugin.h"
@ -40,6 +41,9 @@ void reg_decl_plugins(ast_manager & m) {
if (!m.get_plugin(m.mk_family_id(symbol("datatype")))) {
m.register_plugin(symbol("datatype"), alloc(datatype_decl_plugin));
}
if (!m.get_plugin(m.mk_family_id(symbol("recfun")))) {
m.register_plugin(symbol("recfun"), alloc(recfun::decl::plugin));
}
if (!m.get_plugin(m.mk_family_id(symbol("datalog_relation")))) {
m.register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin));
}

View file

@ -279,8 +279,9 @@ protected:
return false;
}
bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) {
return m_cfg.get_macro(f, def, q, def_pr);
bool get_macro(func_decl * f, expr * & def, proof * & def_pr) {
quantifier* q = nullptr;
return m_cfg.get_macro(f, def, q, def_pr);
}
void push_frame(expr * t, bool mcache, unsigned max_depth) {

View file

@ -212,6 +212,7 @@ bool rewriter_tpl<Config>::constant_fold(app * t, frame & fr) {
return false;
}
template<typename Config>
template<bool ProofGen>
void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
@ -338,15 +339,12 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
// TODO: add rewrite rules support
expr * def = nullptr;
proof * def_pr = nullptr;
quantifier * def_q = nullptr;
// When get_macro succeeds, then
// we know that:
// forall X. f(X) = def[X]
// and def_pr is a proof for this quantifier.
//
// Remark: def_q is only used for proof generation.
// It is the quantifier forall X. f(X) = def[X]
if (get_macro(f, def, def_q, def_pr)) {
if (get_macro(f, def, def_pr)) {
SASSERT(!f->is_associative() || !flat_assoc(f));
SASSERT(new_num_args == t->get_num_args());
SASSERT(m().get_sort(def) == m().get_sort(t));

View file

@ -30,6 +30,7 @@ Notes:
#include "ast/seq_decl_plugin.h"
#include "ast/pb_decl_plugin.h"
#include "ast/fpa_decl_plugin.h"
#include "ast/csp_decl_plugin.h"
#include "ast/ast_pp.h"
#include "ast/rewriter/var_subst.h"
#include "ast/pp.h"
@ -474,7 +475,6 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
m_manager(m),
m_own_manager(m == nullptr),
m_manager_initialized(false),
m_rec_fun_declared(false),
m_pmanager(nullptr),
m_sexpr_manager(nullptr),
m_regular("stdout", std::cout),
@ -679,6 +679,8 @@ bool cmd_context::logic_has_datatype() const {
return !has_logic() || smt_logics::logic_has_datatype(m_logic);
}
bool cmd_context::logic_has_recfun() const { return true; }
void cmd_context::init_manager_core(bool new_manager) {
SASSERT(m_manager != 0);
SASSERT(m_pmanager != 0);
@ -691,10 +693,12 @@ void cmd_context::init_manager_core(bool new_manager) {
register_plugin(symbol("bv"), alloc(bv_decl_plugin), logic_has_bv());
register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array());
register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype());
register_plugin(symbol("recfun"), alloc(recfun::decl::plugin), logic_has_recfun());
register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq());
register_plugin(symbol("pb"), alloc(pb_decl_plugin), logic_has_pb());
register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa());
register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic());
register_plugin(symbol("csp"), alloc(csp_decl_plugin), smt_logics::logic_is_csp(m_logic));
}
else {
// the manager was created by an external module
@ -706,9 +710,11 @@ void cmd_context::init_manager_core(bool new_manager) {
load_plugin(symbol("bv"), logic_has_bv(), fids);
load_plugin(symbol("array"), logic_has_array(), fids);
load_plugin(symbol("datatype"), logic_has_datatype(), fids);
load_plugin(symbol("recfun"), logic_has_recfun(), fids);
load_plugin(symbol("seq"), logic_has_seq(), fids);
load_plugin(symbol("fpa"), logic_has_fpa(), fids);
load_plugin(symbol("pb"), logic_has_pb(), fids);
load_plugin(symbol("csp"), smt_logics::logic_is_csp(m_logic), fids);
for (family_id fid : fids) {
decl_plugin * p = m_manager->get_plugin(fid);
if (p) {
@ -790,9 +796,11 @@ void cmd_context::insert(symbol const & s, func_decl * f) {
if (contains_macro(s, f)) {
throw cmd_exception("invalid declaration, named expression already defined with this name ", s);
}
#if 0
if (m_builtin_decls.contains(s)) {
throw cmd_exception("invalid declaration, builtin symbol ", s);
}
#endif
dictionary<func_decls>::entry * e = m_func_decls.insert_if_not_there2(s, func_decls());
func_decls & fs = e->get_data().m_value;
if (!fs.insert(m(), f)) {
@ -827,9 +835,11 @@ void cmd_context::insert(symbol const & s, psort_decl * p) {
void cmd_context::insert(symbol const & s, unsigned arity, sort *const* domain, expr * t) {
expr_ref _t(t, m());
#if 0
if (m_builtin_decls.contains(s)) {
throw cmd_exception("invalid macro/named expression, builtin symbol ", s);
}
#endif
if (contains_macro(s, arity, domain)) {
throw cmd_exception("named expression already defined");
}
@ -888,7 +898,20 @@ void cmd_context::model_del(func_decl* f) {
m_mc0->hide(f);
}
void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e) {
recfun::decl::plugin& cmd_context::get_recfun_plugin() {
recfun::util u(get_ast_manager());
return u.get_plugin();
}
recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) {
SASSERT(logic_has_recfun());
return get_recfun_plugin().mk_def(name, arity, domain, range);
}
// insert a recursive function as a regular quantified axiom
void cmd_context::insert_rec_fun_as_axiom(func_decl *f, expr_ref_vector const& binding, svector<symbol> const &ids, expr* e) {
expr_ref eq(m());
app_ref lhs(m());
lhs = m().mk_app(f, binding.size(), binding.c_ptr());
@ -907,19 +930,42 @@ void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, s
eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, m().rec_fun_qid(), symbol::null, 2, pats);
}
//
// disable warning given the current way they are used
// (Z3 will here silently assume and not check the definitions to be well founded,
// and please use HSF for everything else).
//
if (false && !ids.empty() && !m_rec_fun_declared) {
warning_msg("recursive function definitions are assumed well-founded");
m_rec_fun_declared = true;
}
assert_expr(eq);
}
void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* rhs) {
if (gparams::get_value("smt.recfun.native") != "true") {
// just use an axiom
insert_rec_fun_as_axiom(f, binding, ids, rhs);
return;
}
TRACE("recfun", tout<< "define recfun " << f->get_name() << " = " << mk_pp(rhs, m()) << "\n";);
recfun::decl::plugin& p = get_recfun_plugin();
var_ref_vector vars(m());
for (expr* b : binding) {
SASSERT(is_var(b));
vars.push_back(to_var(b));
}
recfun::promise_def d = p.get_promise_def(f);
p.set_definition(d, vars.size(), vars.c_ptr(), rhs);
}
func_decl * cmd_context::find_func_decl(symbol const & s) const {
if (contains_macro(s)) {
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
}
func_decls fs;
if (m_func_decls.find(s, fs)) {
if (fs.more_than_one())
throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate (<symbol> (<sort>*) <sort>) ", s);
return fs.first();
}
builtin_decl d;
if (m_builtin_decls.find(s, d)) {
try {
@ -933,15 +979,6 @@ func_decl * cmd_context::find_func_decl(symbol const & s) const {
}
throw cmd_exception("invalid function declaration reference, must provide signature for builtin symbol ", s);
}
if (contains_macro(s)) {
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
}
func_decls fs;
if (m_func_decls.find(s, fs)) {
if (fs.more_than_one())
throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate (<symbol> (<sort>*) <sort>) ", s);
return fs.first();
}
throw cmd_exception("invalid function declaration reference, unknown function ", s);
return nullptr;
}
@ -966,6 +1003,18 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family
func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices,
unsigned arity, sort * const * domain, sort * range) const {
if (domain && contains_macro(s, arity, domain))
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
func_decl * f = nullptr;
func_decls fs;
if (num_indices == 0 && m_func_decls.find(s, fs)) {
f = fs.find(arity, domain, range);
}
if (f) {
return f;
}
builtin_decl d;
if (domain && m_builtin_decls.find(s, d)) {
family_id fid = d.m_fid;
@ -990,21 +1039,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices,
throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s);
return f;
}
if (domain && contains_macro(s, arity, domain))
throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s);
if (num_indices > 0)
throw cmd_exception("invalid indexed function declaration reference, unknown builtin function ", s);
func_decl * f = nullptr;
func_decls fs;
if (m_func_decls.find(s, fs)) {
f = fs.find(arity, domain, range);
}
if (f == nullptr)
throw cmd_exception("invalid function declaration reference, unknown function ", s);
return f;
throw cmd_exception("invalid function declaration reference, unknown function ", s);
}
psort_decl * cmd_context::find_psort_decl(symbol const & s) const {
@ -1041,29 +1076,6 @@ void cmd_context::mk_const(symbol const & s, expr_ref & result) const {
void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range,
expr_ref & result) const {
builtin_decl d;
if (m_builtin_decls.find(s, d)) {
family_id fid = d.m_fid;
decl_kind k = d.m_decl;
// Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use.
if (d.m_decl != 0 && num_args > 0) {
builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id());
fid = d2.m_fid;
k = d2.m_decl;
}
if (num_indices == 0) {
result = m().mk_app(fid, k, 0, nullptr, num_args, args, range);
}
else {
result = m().mk_app(fid, k, num_indices, indices, num_args, args, range);
}
if (result.get() == nullptr)
throw cmd_exception("invalid builtin application ", s);
CHECK_SORT(result.get());
return;
}
if (num_indices > 0)
throw cmd_exception("invalid use of indexed identifier, unknown builtin function ", s);
expr* _t;
if (macros_find(s, num_args, args, _t)) {
TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n";
@ -1079,6 +1091,31 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg
func_decls fs;
if (!m_func_decls.find(s, fs)) {
builtin_decl d;
if (m_builtin_decls.find(s, d)) {
family_id fid = d.m_fid;
decl_kind k = d.m_decl;
// Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use.
if (d.m_decl != 0 && num_args > 0) {
builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id());
fid = d2.m_fid;
k = d2.m_decl;
}
if (num_indices == 0) {
result = m().mk_app(fid, k, 0, nullptr, num_args, args, range);
}
else {
result = m().mk_app(fid, k, num_indices, indices, num_args, args, range);
}
if (result.get() == nullptr)
throw cmd_exception("invalid builtin application ", s);
CHECK_SORT(result.get());
return;
}
if (num_indices > 0)
throw cmd_exception("invalid use of indexed identifier, unknown builtin function ", s);
if (num_args == 0) {
throw cmd_exception("unknown constant ", s);
}
@ -1088,14 +1125,17 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg
if (num_args == 0 && range == nullptr) {
if (fs.more_than_one())
throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as <symbol> <sort>) to disumbiguate ", s);
throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as <symbol> <sort>) to disambiguate ", s);
func_decl * f = fs.first();
if (f == nullptr) {
throw cmd_exception("unknown constant ", s);
}
if (f->get_arity() != 0)
throw cmd_exception("invalid function application, missing arguments ", s);
result = m().mk_const(f);
if (f->get_arity() != 0) {
result = array_util(m()).mk_as_array(f);
}
else {
result = m().mk_const(f);
}
}
else {
func_decl * f = fs.find(m(), num_args, args, range);
@ -2000,7 +2040,7 @@ void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr
if (logic != symbol::null)
out << "(set-logic " << logic << ")" << std::endl;
// collect uninterpreted function declarations
decl_collector decls(m(), false);
decl_collector decls(m());
for (unsigned i = 0; i < num; i++) {
decls.visit(assertions[i]);
}
@ -2031,8 +2071,8 @@ void cmd_context::slow_progress_sample() {
svector<symbol> labels;
m_solver->get_labels(labels);
regular_stream() << "(labels";
for (unsigned i = 0; i < labels.size(); i++) {
regular_stream() << " " << labels[i];
for (symbol const& s : labels) {
regular_stream() << " " << s;
}
regular_stream() << "))" << std::endl;
}

View file

@ -32,6 +32,7 @@ Notes:
#include "ast/ast.h"
#include "ast/ast_printer.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/recfun_decl_plugin.h"
#include "tactic/generic_model_converter.h"
#include "solver/solver.h"
#include "solver/progress_callback.h"
@ -199,7 +200,6 @@ protected:
ast_manager * m_manager;
bool m_own_manager;
bool m_manager_initialized;
bool m_rec_fun_declared;
pdecl_manager * m_pmanager;
sexpr_manager * m_sexpr_manager;
check_logic m_check_logic;
@ -291,6 +291,7 @@ protected:
bool logic_has_array() const;
bool logic_has_datatype() const;
bool logic_has_fpa() const;
bool logic_has_recfun() const;
void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; }
void print_unsupported_info(symbol const& s, int line, int pos) { if (s != symbol::null) diagnostic_stream() << "; " << s << " line: " << line << " position: " << pos << std::endl;}
@ -306,6 +307,7 @@ protected:
void erase_macro(symbol const& s);
bool macros_find(symbol const& s, unsigned n, expr*const* args, expr*& t) const;
recfun::decl::plugin& get_recfun_plugin();
public:
cmd_context(bool main_ctx = true, ast_manager * m = nullptr, symbol const & l = symbol::null);
@ -384,12 +386,14 @@ public:
void insert(probe_info * p) { tactic_manager::insert(p); }
void insert_user_tactic(symbol const & s, sexpr * d);
void insert_aux_pdecl(pdecl * p);
void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e);
void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t);
void model_del(func_decl* f);
void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e);
void insert_rec_fun_as_axiom(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e);
func_decl * find_func_decl(symbol const & s) const;
func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices,
unsigned arity, sort * const * domain, sort * range) const;
recfun::promise_def decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range);
psort_decl * find_psort_decl(symbol const & s) const;
cmd * find_cmd(symbol const & s) const;
sexpr * find_user_tactic(symbol const & s) const;

View file

@ -91,6 +91,11 @@ UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "<term>", "display shared subterms of
ctx.regular_stream() << ")" << std::endl;
});
UNARY_CMD(assert_not_cmd, "assert-not", "<term>", "assert negation", CPK_EXPR, expr *, {
expr_ref ne(ctx.m().mk_not(arg), ctx.m());
ctx.assert_expr(ne);
});
UNARY_CMD(num_shared_cmd, "dbg-num-shared", "<term>", "return the number of shared subterms", CPK_EXPR, expr *, {
shared_occs s(ctx.m());
s(arg);
@ -537,6 +542,7 @@ void install_dbg_cmds(cmd_context & ctx) {
ctx.insert(alloc(shift_vars_cmd));
ctx.insert(alloc(pp_shared_cmd));
ctx.insert(alloc(num_shared_cmd));
ctx.insert(alloc(assert_not_cmd));
ctx.insert(alloc(size_cmd));
ctx.insert(alloc(subst_cmd));
ctx.insert(alloc(bool_rewriter_cmd));

View file

@ -21,6 +21,7 @@ Revision History:
#include "model/model_smt2_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/func_decl_dependencies.h"
#include "ast/recfun_decl_plugin.h"
#include "ast/pp.h"
using namespace format_ns;
@ -60,11 +61,9 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod
ctx.display(buffer, s, 13);
buffer << ":\n";
pp_indent(buffer, TAB_SZ);
ptr_vector<expr>::const_iterator it = u.begin();
ptr_vector<expr>::const_iterator end = u.end();
for (; it != end; ++it) {
SASSERT(is_app(*it));
app * c = to_app(*it);
for (expr* e : u) {
SASSERT(is_app(e));
app * c = to_app(e);
pp_symbol(buffer, c->get_decl()->get_name());
buffer << " ";
}
@ -87,9 +86,8 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod
out << "\n";
pp_indent(out, indent);
out << ";; definitions for universe elements:\n";
it = u.begin();
for (; it != end; ++it) {
app * c = to_app(*it);
for (expr * e : u) {
app * c = to_app(e);
pp_indent(out, indent);
out << "(declare-fun ";
unsigned len = pp_symbol(out, c->get_decl()->get_name());
@ -101,9 +99,8 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod
out << ";; cardinality constraint:\n";
f_conds.reset();
format * var = mk_string(m, "x");
it = u.begin();
for (; it != end; ++it) {
app * c = to_app(*it);
for (expr* e : u) {
app * c = to_app(e);
symbol csym = c->get_decl()->get_name();
std::string cname;
if (is_smt2_quoted_symbol(csym))
@ -170,10 +167,7 @@ void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer<func_decl
func_decl_set deps;
bool all_visited = true;
collect_func_decls(m, curr_i->get_else(), deps);
func_decl_set::iterator it2 = deps.begin();
func_decl_set::iterator end2 = deps.end();
for (; it2 != end2; ++it2) {
func_decl * d = *it2;
for (func_decl * d : deps) {
if (d->get_arity() > 0 && md.has_interpretation(d) && !visited.contains(d)) {
todo.push_back(d);
visited.insert(d);
@ -189,8 +183,10 @@ void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer<func_decl
}
}
static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) {
ast_manager & m = ctx.get_ast_manager();
recfun::util recfun_util(m);
sbuffer<symbol> var_names;
ptr_buffer<format> f_var_names;
ptr_buffer<format> f_arg_decls;
@ -200,6 +196,9 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co
sort_fun_decls(m, md, func_decls);
for (unsigned i = 0; i < func_decls.size(); i++) {
func_decl * f = func_decls[i];
if (recfun_util.is_defined(f)) {
continue;
}
func_interp * f_i = md.get_func_interp(f);
SASSERT(f->get_arity() == f_i->get_arity());
format_ref body(fm(m));

View file

@ -114,6 +114,7 @@ namespace smt2 {
symbol m_define_fun_rec;
symbol m_define_funs_rec;
symbol m_match;
symbol m_case;
symbol m_underscore;
typedef std::pair<symbol, expr*> named_expr;
@ -382,7 +383,9 @@ namespace smt2 {
next();
return;
}
throw parser_exception(msg);
std::ostringstream str;
str << msg << " got " << curr_id();
throw parser_exception(str.str());
}
symbol const & curr_id() const { return m_scanner.get_id(); }
@ -406,6 +409,7 @@ namespace smt2 {
bool curr_id_is_underscore() const { SASSERT(curr_is_identifier()); return curr_id() == m_underscore; }
bool curr_id_is_as() const { SASSERT(curr_is_identifier()); return curr_id() == m_as; }
bool curr_id_is_match() const { SASSERT(curr_is_identifier()); return curr_id() == m_match; }
bool curr_id_is_case() const { return curr_id() == m_case; }
bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; }
bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; }
bool curr_id_is_lambda() const { SASSERT(curr_is_identifier()); return curr_id() == m_lambda; }
@ -625,8 +629,6 @@ namespace smt2 {
args.push_back(u);
next();
}
if (args.empty())
throw parser_exception("invalid indexed sort, index expected");
sort * r = d->instantiate(pm(), args.size(), args.c_ptr());
if (r == nullptr)
throw parser_exception("invalid sort application");
@ -1321,7 +1323,13 @@ namespace smt2 {
/**
* SMT-LIB 2.6 pattern matches are of the form
* (match t ((p1 t1) ... (pm+1 tm+1)))
*
* (match t ((p1 t1) ... (pm+1 tm+1)))
*
* precursor form is
*
* (match t (case p1 t1) (case p2 t2) ... )
*
*/
void push_match_frame() {
SASSERT(curr_is_identifier());
@ -1338,21 +1346,42 @@ namespace smt2 {
sort* srt = m().get_sort(t);
check_lparen_next("pattern bindings should be enclosed in a parenthesis");
while (!curr_is_rparen()) {
m_env.begin_scope();
unsigned num_bindings = m_num_bindings;
check_lparen_next("invalid pattern binding, '(' expected");
parse_match_pattern(srt);
patterns.push_back(expr_stack().back());
expr_stack().pop_back();
parse_expr();
cases.push_back(expr_stack().back());
expr_stack().pop_back();
m_num_bindings = num_bindings;
m_env.end_scope();
check_rparen_next("invalid pattern binding, ')' expected");
if (curr_id_is_case()) {
while (curr_id_is_case()) {
next();
m_env.begin_scope();
unsigned num_bindings = m_num_bindings;
parse_match_pattern(srt);
patterns.push_back(expr_stack().back());
expr_stack().pop_back();
parse_expr();
cases.push_back(expr_stack().back());
expr_stack().pop_back();
m_num_bindings = num_bindings;
m_env.end_scope();
check_rparen_next("invalid pattern binding, ')' expected");
if (curr_is_lparen()) {
next();
}
}
}
else {
while (!curr_is_rparen()) {
m_env.begin_scope();
unsigned num_bindings = m_num_bindings;
parse_match_pattern(srt);
patterns.push_back(expr_stack().back());
expr_stack().pop_back();
check_lparen_next("invalid pattern binding, '(' expected");
parse_expr();
cases.push_back(expr_stack().back());
expr_stack().pop_back();
m_num_bindings = num_bindings;
m_env.end_scope();
check_rparen_next("invalid pattern binding, ')' expected");
}
next();
}
next();
m_num_expr_frames = num_frames + 1;
expr_stack().push_back(compile_patterns(t, patterns, cases));
}
@ -1410,6 +1439,12 @@ namespace smt2 {
// compute match condition and substitution
// t is shifted by size of subst.
expr_ref bind_match(expr* t, expr* pattern, expr_ref_vector& subst) {
if (m().get_sort(t) != m().get_sort(pattern)) {
std::ostringstream str;
str << "sorts of pattern " << expr_ref(pattern, m()) << " and term "
<< expr_ref(t, m()) << " are not aligned";
throw parser_exception(str.str());
}
expr_ref tsh(m());
if (is_var(pattern)) {
shifter()(t, 1, tsh);
@ -1520,11 +1555,22 @@ namespace smt2 {
check_identifier("invalid indexed identifier, symbol expected");
symbol r = curr_id();
next();
unsigned num_indices = 0;
while (!curr_is_rparen()) {
if (curr_is_int()) {
unsigned u = curr_unsigned();
m_param_stack.push_back(parameter(u));
if (!curr_numeral().is_unsigned()) {
m_param_stack.push_back(parameter(curr_numeral()));
}
else {
m_param_stack.push_back(parameter(curr_unsigned()));
}
next();
}
else if (curr_is_float()) {
m_param_stack.push_back(parameter(curr_numeral()));
next();
}
else if (curr_is_keyword()) {
m_param_stack.push_back(parameter(curr_id()));
next();
}
else if (curr_is_identifier() || curr_is_lparen()) {
@ -1533,10 +1579,7 @@ namespace smt2 {
else {
throw parser_exception("invalid indexed identifier, integer, identifier or '(' expected");
}
num_indices++;
}
if (num_indices == 0)
throw parser_exception("invalid indexed identifier, index expected");
next();
return r;
}
@ -1857,13 +1900,31 @@ namespace smt2 {
unsigned num_args = expr_stack().size() - fr->m_expr_spos;
unsigned num_indices = m_param_stack.size() - fr->m_param_spos;
expr_ref t_ref(m());
m_ctx.mk_app(fr->m_f,
num_args,
expr_stack().c_ptr() + fr->m_expr_spos,
num_indices,
m_param_stack.c_ptr() + fr->m_param_spos,
fr->m_as_sort ? sort_stack().back() : nullptr,
t_ref);
local l;
if (m_env.find(fr->m_f, l)) {
push_local(l);
t_ref = expr_stack().back();
for (unsigned i = 0; i < num_args; ++i) {
expr* arg = expr_stack().get(fr->m_expr_spos + i);
expr* args[2] = { t_ref.get(), arg };
m_ctx.mk_app(symbol("select"),
2,
args,
0,
nullptr,
nullptr,
t_ref);
}
}
else {
m_ctx.mk_app(fr->m_f,
num_args,
expr_stack().c_ptr() + fr->m_expr_spos,
num_indices,
m_param_stack.c_ptr() + fr->m_param_spos,
fr->m_as_sort ? sort_stack().back() : nullptr,
t_ref);
}
expr_stack().shrink(fr->m_expr_spos);
m_param_stack.shrink(fr->m_param_spos);
if (fr->m_as_sort)
@ -2216,10 +2277,14 @@ namespace smt2 {
parse_expr();
if (m().get_sort(expr_stack().back()) != sort_stack().back())
throw parser_exception("invalid function/constant definition, sort mismatch");
if (is_fun)
m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back());
else
m_ctx.model_add(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back());
sort* const* sorts = sort_stack().c_ptr() + sort_spos;
expr* t = expr_stack().back();
if (is_fun) {
m_ctx.insert(id, num_vars, sorts, t);
}
else {
m_ctx.model_add(id, num_vars, sorts, t);
}
check_rparen("invalid function/constant definition, ')' expected");
// restore stacks & env
symbol_stack().shrink(sym_spos);
@ -2311,7 +2376,7 @@ namespace smt2 {
next();
}
void parse_rec_fun_decl(func_decl_ref& f, expr_ref_vector& bindings, svector<symbol>& ids) {
recfun::promise_def parse_rec_fun_decl(func_decl_ref& f, expr_ref_vector& bindings, svector<symbol>& ids) {
SASSERT(m_num_bindings == 0);
check_identifier("invalid function/constant definition, symbol expected");
symbol id = curr_id();
@ -2322,7 +2387,8 @@ namespace smt2 {
unsigned num_vars = parse_sorted_vars();
SASSERT(num_vars == m_num_bindings);
parse_sort("Invalid recursive function definition");
f = m().mk_func_decl(id, num_vars, sort_stack().c_ptr() + sort_spos, sort_stack().back());
recfun::promise_def pdef = m_ctx.decl_rec_fun(id, num_vars, sort_stack().c_ptr() + sort_spos, sort_stack().back());
f = pdef.get_def()->get_decl();
bindings.append(num_vars, expr_stack().c_ptr() + expr_spos);
ids.append(num_vars, symbol_stack().c_ptr() + sym_spos);
symbol_stack().shrink(sym_spos);
@ -2330,6 +2396,7 @@ namespace smt2 {
expr_stack().shrink(expr_spos);
m_env.end_scope();
m_num_bindings = 0;
return pdef;
}
void parse_rec_fun_bodies(func_decl_ref_vector const& decls, vector<expr_ref_vector> const& bindings, vector<svector<symbol> >const & ids) {
@ -3005,6 +3072,7 @@ namespace smt2 {
m_define_fun_rec("define-fun-rec"),
m_define_funs_rec("define-funs-rec"),
m_match("match"),
m_case("case"),
m_underscore("_"),
m_num_open_paren(0),
m_current_file(filename) {

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,7 @@ Revision History:
#include "sat/sat_solver.h"
#include "sat/sat_lookahead.h"
#include "sat/sat_unit_walk.h"
#include "sat/sat_big.h"
#include "util/scoped_ptr_vector.h"
#include "util/sorting_network.h"
@ -41,8 +42,11 @@ namespace sat {
unsigned m_num_bin_subsumes;
unsigned m_num_clause_subsumes;
unsigned m_num_pb_subsumes;
unsigned m_num_big_strengthenings;
unsigned m_num_cut;
unsigned m_num_gc;
unsigned m_num_overflow;
unsigned m_num_lemmas;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
@ -57,6 +61,7 @@ namespace sat {
class card;
class pb;
class xr;
class pb_base;
class constraint {
protected:
@ -104,6 +109,7 @@ namespace sat {
card const& to_card() const;
pb const& to_pb() const;
xr const& to_xr() const;
pb_base const& to_pb_base() const;
bool is_card() const { return m_tag == card_t; }
bool is_pb() const { return m_tag == pb_t; }
bool is_xr() const { return m_tag == xr_t; }
@ -118,7 +124,7 @@ namespace sat {
};
friend std::ostream& operator<<(std::ostream& out, constraint const& c);
// base class for pb and cardinality constraints
class pb_base : public constraint {
protected:
@ -204,12 +210,18 @@ namespace sat {
protected:
struct ineq {
literal_vector m_lits;
svector<uint64_t> m_coeffs;
svector<wliteral> m_wlits;
uint64_t m_k;
ineq(): m_k(0) {}
void reset(uint64_t k) { m_lits.reset(); m_coeffs.reset(); m_k = k; }
void push(literal l, uint64_t c) { m_lits.push_back(l); m_coeffs.push_back(c); }
unsigned size() const { return m_wlits.size(); }
literal lit(unsigned i) const { return m_wlits[i].second; }
unsigned coeff(unsigned i) const { return m_wlits[i].first; }
void reset(uint64_t k) { m_wlits.reset(); m_k = k; }
void push(literal l, unsigned c) { m_wlits.push_back(wliteral(c,l)); }
unsigned bv_coeff(bool_var v) const;
void divide(unsigned c);
void weaken(unsigned i);
bool contains(literal l) const { for (auto wl : m_wlits) if (wl.second == l) return true; return false; }
};
solver* m_solver;
@ -273,7 +285,8 @@ namespace sat {
// simplification routines
svector<bool> m_visited;
svector<unsigned> m_visited;
unsigned m_visited_ts;
vector<svector<constraint*>> m_cnstr_use_list;
use_list m_clause_use_list;
bool m_simplify_change;
@ -292,9 +305,11 @@ namespace sat {
void binary_subsumption(card& c1, literal lit);
void clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses);
void card_subsumption(card& c1, literal lit);
void mark_visited(literal l) { m_visited[l.index()] = true; }
void unmark_visited(literal l) { m_visited[l.index()] = false; }
bool is_marked(literal l) const { return m_visited[l.index()] != 0; }
void init_visited();
void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; }
void mark_visited(bool_var v) { mark_visited(literal(v, false)); }
bool is_visited(bool_var v) const { return is_visited(literal(v, false)); }
bool is_visited(literal l) const { return m_visited[l.index()] == m_visited_ts; }
unsigned get_num_unblocked_bin(literal l);
literal get_min_occurrence_literal(card const& c);
void init_use_lists();
@ -302,6 +317,9 @@ namespace sat {
unsigned set_non_external();
unsigned elim_pure();
bool elim_pure(literal lit);
void unit_strengthen();
void unit_strengthen(big& big, constraint& cs);
void unit_strengthen(big& big, pb_base& p);
void subsumption(constraint& c1);
void subsumption(card& c1);
void gc_half(char const* _method);
@ -396,10 +414,23 @@ namespace sat {
lbool eval(model const& m, pb const& p) const;
double get_reward(pb const& p, literal_occs_fun& occs) const;
// RoundingPb conflict resolution
lbool resolve_conflict_rs();
void round_to_one(ineq& ineq, bool_var v);
void round_to_one(bool_var v);
void divide(unsigned c);
void resolve_on(literal lit);
void resolve_with(ineq const& ineq);
void reset_marks(unsigned idx);
void mark_variables(ineq const& ineq);
void bail_resolve_conflict(unsigned idx);
// access solver
inline lbool value(bool_var v) const { return value(literal(v, false)); }
inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); }
inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; }
inline bool is_false(literal lit) const { return l_false == value(lit); }
inline unsigned lvl(literal lit) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(lit); }
inline unsigned lvl(bool_var v) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(v); }
@ -426,9 +457,11 @@ namespace sat {
mutable bool m_overflow;
void reset_active_var_set();
void normalize_active_coeffs();
bool test_and_set_active(bool_var v);
void inc_coeff(literal l, unsigned offset);
int64_t get_coeff(bool_var v) const;
uint64_t get_coeff(literal lit) const;
wliteral get_wliteral(bool_var v);
unsigned get_abs_coeff(bool_var v) const;
int get_int_coeff(bool_var v) const;
unsigned get_bound() const;
@ -436,6 +469,7 @@ namespace sat {
literal get_asserting_literal(literal conseq);
void process_antecedent(literal l, unsigned offset);
void process_antecedent(literal l) { process_antecedent(l, 1); }
void process_card(card& c, unsigned offset);
void cut();
bool create_asserting_lemma();
@ -446,6 +480,7 @@ namespace sat {
bool validate_conflict(pb const& p) const;
bool validate_assign(literal_vector const& lits, literal lit);
bool validate_lemma();
bool validate_ineq(ineq const& ineq) const;
bool validate_unit_propagation(card const& c, literal alit) const;
bool validate_unit_propagation(pb const& p, literal alit) const;
bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const;
@ -464,12 +499,17 @@ namespace sat {
ineq m_A, m_B, m_C;
void active2pb(ineq& p);
constraint* active2lemma();
constraint* active2constraint();
constraint* active2card();
void active2wlits();
void active2wlits(svector<wliteral>& wlits);
void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p);
void constraint2pb(constraint& cnstr, literal lit, unsigned offset, ineq& p);
bool validate_resolvent();
unsigned get_coeff(ineq const& pb, literal lit);
void display(std::ostream& out, ineq& p, bool values = false) const;
void display(std::ostream& out, ineq const& p, bool values = false) const;
void display(std::ostream& out, card const& c, bool values) const;
void display(std::ostream& out, pb const& p, bool values) const;
void display(std::ostream& out, xr const& c, bool values) const;

View file

@ -22,7 +22,8 @@ Revision History:
namespace sat {
big::big(random_gen& rand):
m_rand(rand) {
m_rand(rand),
m_include_cardinality(false) {
}
void big::init(solver& s, bool learned) {
@ -42,22 +43,22 @@ namespace sat {
m_roots[v.index()] = false;
edges.push_back(v);
}
#if 0
if (w.is_ext_constraint() &&
if (m_include_cardinality &&
w.is_ext_constraint() &&
s.m_ext &&
learned &&
learned && // cannot (yet) observe if ext constraints are learned
!seen_idx.contains(w.get_ext_constraint_idx()) &&
s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), r)) {
seen_idx.insert(w.get_ext_constraint_idx(), true);
for (unsigned i = 0; i < r.size(); ++i) {
literal u = r[i];
for (unsigned j = i + 1; j < r.size(); ++j) {
// add ~r[i] -> r[j]
literal v = r[j];
literal u = ~r[j];
for (unsigned i = 0; i < std::min(4u, r.size()); ++i) {
shuffle<literal>(r.size(), r.c_ptr(), m_rand);
literal u = r[0];
for (unsigned j = 1; j < r.size(); ++j) {
literal v = ~r[j];
// add u -> v
m_roots[v.index()] = false;
m_dag[u.index()].push_back(v);
// add ~r[j] -> r[i]
// add ~v -> ~u
v.neg();
u.neg();
m_roots[u.index()] = false;
@ -65,7 +66,6 @@ namespace sat {
}
}
}
#endif
}
}
done_adding_edges();
@ -268,6 +268,16 @@ namespace sat {
return out << v;
}
literal big::get_root(literal l) {
literal r = l;
do {
l = r;
r = m_root[l.index()];
}
while (r != l);
return r;
}
void big::display(std::ostream& out) const {
unsigned idx = 0;
for (auto& next : m_dag) {

View file

@ -34,6 +34,7 @@ namespace sat {
svector<int> m_left, m_right;
literal_vector m_root, m_parent;
bool m_learned;
bool m_include_cardinality;
svector<std::pair<literal, literal>> m_del_bin;
@ -54,6 +55,9 @@ namespace sat {
// static svector<std::pair<literal, literal>> s_del_bin;
big(random_gen& rand);
void set_include_cardinality(bool f) { m_include_cardinality = f; }
/**
\brief initialize a BIG from a solver.
*/
@ -77,7 +81,7 @@ namespace sat {
int get_left(literal l) const { return m_left[l.index()]; }
int get_right(literal l) const { return m_right[l.index()]; }
literal get_parent(literal l) const { return m_parent[l.index()]; }
literal get_root(literal l) const { return m_root[l.index()]; }
literal get_root(literal l);
bool reaches(literal u, literal v) const { return m_left[u.index()] < m_left[v.index()] && m_right[v.index()] < m_right[u.index()]; }
bool connected(literal u, literal v) const { return reaches(u, v) || reaches(~v, ~u); }
void display(std::ostream& out) const;

View file

@ -199,6 +199,22 @@ namespace sat {
else
throw sat_param_exception("invalid PB solver: solver, totalizer, circuit, sorting, segmented");
s = p.pb_resolve();
if (s == "cardinality")
m_pb_resolve = PB_CARDINALITY;
else if (s == "rounding")
m_pb_resolve = PB_ROUNDING;
else
throw sat_param_exception("invalid PB resolve: 'cardinality' or 'rounding' expected");
s = p.pb_lemma_format();
if (s == "cardinality")
m_pb_lemma_format = PB_LEMMA_CARDINALITY;
else if (s == "pb")
m_pb_lemma_format = PB_LEMMA_PB;
else
throw sat_param_exception("invalid PB lemma format: 'cardinality' or 'pb' expected");
m_card_solver = p.cardinality_solver();
sat_simplifier_params sp(_p);

View file

@ -60,6 +60,16 @@ namespace sat {
PB_SEGMENTED
};
enum pb_resolve {
PB_CARDINALITY,
PB_ROUNDING
};
enum pb_lemma_format {
PB_LEMMA_CARDINALITY,
PB_LEMMA_PB
};
enum reward_t {
ternary_reward,
unit_literal_reward,
@ -151,6 +161,8 @@ namespace sat {
pb_solver m_pb_solver;
bool m_card_solver;
pb_resolve m_pb_resolve;
pb_lemma_format m_pb_lemma_format;
// branching heuristic settings.
branching_heuristic m_branching_heuristic;

View file

@ -43,8 +43,10 @@ def_module_params('sat',
('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'),
('cardinality.solver', BOOL, True, 'use cardinality solver'),
('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), solver (use native solver)'),
('xor.solver', BOOL, False, 'use xor solver'),
('xor.solver', BOOL, False, 'use xor solver'),
('cardinality.encoding', SYMBOL, 'grouped', 'encoding used for at-most-k constraints: grouped, bimander, ordered, unate, circuit'),
('pb.resolve', SYMBOL, 'cardinality', 'resolution strategy for boolean algebra solver: cardinality, rounding'),
('pb.lemma_format', SYMBOL, 'cardinality', 'generate either cardinality or pb lemmas'),
('local_search', BOOL, False, 'use local search instead of CDCL'),
('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'),
('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'),

View file

@ -60,7 +60,6 @@ namespace sat {
void ensure_big(bool learned) { m_big.ensure_big(m_solver, learned); }
int get_left(literal l) const { return m_big.get_left(l); }
int get_right(literal l) const { return m_big.get_right(l); }
literal get_root(literal l) const { return m_big.get_root(l); }
bool connected(literal u, literal v) const { return m_big.connected(u, v); }
};
};

View file

@ -343,12 +343,6 @@ namespace sat {
}
void solver::mk_bin_clause(literal l1, literal l2, bool learned) {
#if 0
if ((l1.var() == 2039 || l2.var() == 2039) &&
(l1.var() == 27042 || l2.var() == 27042)) {
IF_VERBOSE(1, verbose_stream() << "mk_bin: " << l1 << " " << l2 << " " << learned << "\n");
}
#endif
if (find_binary_watch(get_wlist(~l1), ~l2)) {
assign(l1, justification());
return;

View file

@ -82,6 +82,7 @@ struct goal2sat::imp {
m_is_lemma(false) {
updt_params(p);
m_true = sat::null_bool_var;
mk_true();
}
void updt_params(params_ref const & p) {

View file

@ -56,9 +56,11 @@ z3_add_component(smt
theory_dl.cpp
theory_dummy.cpp
theory_fpa.cpp
theory_jobscheduler.cpp
theory_lra.cpp
theory_opt.cpp
theory_pb.cpp
theory_recfun.cpp
theory_seq.cpp
theory_str.cpp
theory_utvpi.cpp

View file

@ -95,5 +95,7 @@ def_module_params(module_name='smt',
('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'),
('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body'),
('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none'),
('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy')
('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy'),
('recfun.native', BOOL, True, 'use native rec-fun solver'),
('recfun.depth', UINT, 2, 'initial depth for maxrec expansion')
))

View file

@ -37,6 +37,7 @@ Revision History:
#include "model/model_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/ast_translation.h"
#include "ast/recfun_decl_plugin.h"
namespace smt {
@ -1384,7 +1385,10 @@ namespace smt {
SASSERT(m_manager.is_eq(n));
expr * lhs = n->get_arg(0);
expr * rhs = n->get_arg(1);
if (val == l_true) {
if (m_manager.is_bool(lhs)) {
// no-op
}
else if (val == l_true) {
add_eq(get_enode(lhs), get_enode(rhs), eq_justification(l));
}
else {
@ -3207,7 +3211,7 @@ namespace smt {
}
else {
set_conflict(b_justification(tmp_clause.first), null_literal);
}
}
VERIFY(!resolve_conflict());
return l_false;
next_clause:
@ -3266,6 +3270,18 @@ namespace smt {
m_assumptions.reset();
}
bool context::should_research(lbool r) {
if (r != l_false || m_unsat_core.empty()) {
return false;
}
for (theory* th : m_theory_set) {
if (th->should_research(m_unsat_core)) {
return true;
}
}
return false;
}
lbool context::mk_unsat_core(lbool r) {
if (r != l_false) return r;
SASSERT(inconsistent());
@ -3353,7 +3369,7 @@ namespace smt {
add_theory_assumptions(theory_assumptions);
if (!theory_assumptions.empty()) {
TRACE("search", tout << "Adding theory assumptions to context" << std::endl;);
return check(theory_assumptions.size(), theory_assumptions.c_ptr(), reset_cancel, true);
return check(0, nullptr, reset_cancel);
}
internalize_assertions();
@ -3407,19 +3423,24 @@ namespace smt {
}
}
lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel, bool already_did_theory_assumptions) {
lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel) {
if (!check_preamble(reset_cancel)) return l_undef;
SASSERT(at_base_level());
setup_context(false);
expr_ref_vector asms(m_manager, num_assumptions, assumptions);
if (!already_did_theory_assumptions) add_theory_assumptions(asms);
// introducing proxies: if (!validate_assumptions(asms)) return l_undef;
TRACE("unsat_core_bug", tout << asms << "\n";);
internalize_assertions();
init_assumptions(asms);
TRACE("before_search", display(tout););
lbool r = search();
r = mk_unsat_core(r);
lbool r;
do {
pop_to_base_lvl();
expr_ref_vector asms(m_manager, num_assumptions, assumptions);
add_theory_assumptions(asms);
// introducing proxies: if (!validate_assumptions(asms)) return l_undef;
TRACE("unsat_core_bug", tout << asms << "\n";);
internalize_assertions();
init_assumptions(asms);
TRACE("before_search", display(tout););
r = search();
r = mk_unsat_core(r);
}
while (should_research(r));
r = check_finalize(r);
return r;
}
@ -3428,15 +3449,20 @@ namespace smt {
if (!check_preamble(true)) return l_undef;
TRACE("before_search", display(tout););
setup_context(false);
expr_ref_vector asms(cube);
add_theory_assumptions(asms);
// introducing proxies: if (!validate_assumptions(asms)) return l_undef;
for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef;
internalize_assertions();
init_assumptions(asms);
for (auto const& clause : clauses) init_clause(clause);
lbool r = search();
r = mk_unsat_core(r);
lbool r;
do {
pop_to_base_lvl();
expr_ref_vector asms(cube);
add_theory_assumptions(asms);
// introducing proxies: if (!validate_assumptions(asms)) return l_undef;
for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef;
internalize_assertions();
init_assumptions(asms);
for (auto const& clause : clauses) init_clause(clause);
r = search();
r = mk_unsat_core(r);
}
while (should_research(r));
r = check_finalize(r);
return r;
}
@ -3770,7 +3796,7 @@ namespace smt {
}
m_stats.m_num_final_checks++;
TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";);
TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";);
final_check_status ok = m_qmanager->final_check_eh(false);
if (ok != FC_DONE)
@ -4428,6 +4454,26 @@ namespace smt {
m_model->register_decl(f, fi);
}
}
recfun::util u(m);
recfun::decl::plugin& p = u.get_plugin();
func_decl_ref_vector recfuns = u.get_rec_funs();
for (func_decl* f : recfuns) {
auto& def = u.get_def(f);
expr* rhs = def.get_rhs();
if (!rhs) continue;
func_interp* fi = alloc(func_interp, m, f->get_arity());
// reverse argument order so that variable 0 starts at the beginning.
expr_ref_vector subst(m);
unsigned idx = 0;
for (unsigned i = 0; i < f->get_arity(); ++i) {
subst.push_back(m.mk_var(i, f->get_domain(i)));
}
var_subst sub(m, true);
expr_ref bodyr = sub(rhs, subst.size(), subst.c_ptr());
fi->set_else(bodyr);
m_model->register_decl(f, fi);
}
}
};

View file

@ -859,6 +859,10 @@ namespace smt {
void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = nullptr);
void mk_th_axiom(theory_id tid, literal_vector const& ls, unsigned num_params = 0, parameter * params = nullptr) {
mk_th_axiom(tid, ls.size(), ls.c_ptr(), num_params, params);
}
/*
* Provide a hint to the core solver that the specified literals form a "theory case split".
* The core solver will enforce the condition that exactly one of these literals can be
@ -1010,9 +1014,9 @@ namespace smt {
void restore_theory_vars(enode * r2, enode * r1);
void push_eq(enode * lhs, enode * rhs, eq_justification const & js) {
SASSERT(lhs != rhs);
SASSERT(lhs->get_root() != rhs->get_root());
m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js));
if (lhs->get_root() != rhs->get_root()) {
m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js));
}
}
void push_new_congruence(enode * n1, enode * n2, bool used_commutativity) {
@ -1133,6 +1137,8 @@ namespace smt {
void add_theory_assumptions(expr_ref_vector & theory_assumptions);
lbool mk_unsat_core(lbool result);
bool should_research(lbool result);
void validate_unsat_core();
@ -1243,6 +1249,11 @@ namespace smt {
public:
bool can_propagate() const;
// Retrieve arithmetic values.
bool get_arith_lo(expr* e, rational& lo, bool& strict);
bool get_arith_up(expr* e, rational& up, bool& strict);
bool get_arith_value(expr* e, rational& value);
// -----------------------------------
//
// Model checking... (must be improved)
@ -1515,7 +1526,7 @@ namespace smt {
void pop(unsigned num_scopes);
lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true, bool already_did_theory_assumptions = false);
lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true);
lbool check(expr_ref_vector const& cube, vector<expr_ref_vector> const& clauses);
@ -1609,6 +1620,34 @@ namespace smt {
void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep) { m_asserted_formulas.insert_macro(f, m, pr, dep); }
};
struct pp_lit {
context & ctx;
literal lit;
pp_lit(context & ctx, literal lit) : ctx(ctx), lit(lit) {}
};
inline std::ostream & operator<<(std::ostream & out, pp_lit const & pp) {
return pp.ctx.display_detailed_literal(out, pp.lit);
}
struct pp_lits {
context & ctx;
literal const *lits;
unsigned len;
pp_lits(context & ctx, unsigned len, literal const *lits) : ctx(ctx), lits(lits), len(len) {}
pp_lits(context & ctx, literal_vector const& ls) : ctx(ctx), lits(ls.c_ptr()), len(ls.size()) {}
};
inline std::ostream & operator<<(std::ostream & out, pp_lits const & pp) {
out << "{";
bool first = true;
for (unsigned i = 0; i < pp.len; ++i) {
if (first) { first = false; } else { out << " or\n"; }
pp.ctx.display_detailed_literal(out, pp.lits[i]);
}
return out << "}";
}
struct enode_eq_pp {
context const& ctx;
enode_pair const& p;

View file

@ -303,7 +303,7 @@ namespace smt {
for (bool_var v = 0; v < num; v++) {
if (has_enode(v)) {
enode * n = bool_var2enode(v);
if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false) {
if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false && !m_manager.is_iff(n->get_owner())) {
TRACE("check_th_diseq_propagation", tout << "checking: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m_manager) << "\n";);
enode * lhs = n->get_arg(0)->get_root();
enode * rhs = n->get_arg(1)->get_root();

View file

@ -402,7 +402,10 @@ namespace smt {
enode * n = m_context->get_enode(t);
unsigned num_args = n->get_num_args();
func_decl * f = n->get_decl();
if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) {
if (num_args == 0 && include_func_interp(f)) {
m_model->register_decl(f, get_value(n));
}
else if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) {
ptr_buffer<expr> args;
expr * result = get_value(n);
SASSERT(result);

View file

@ -28,6 +28,7 @@ Revision History:
#include "smt/theory_array_full.h"
#include "smt/theory_bv.h"
#include "smt/theory_datatype.h"
#include "smt/theory_recfun.h"
#include "smt/theory_dummy.h"
#include "smt/theory_dl.h"
#include "smt/theory_seq_empty.h"
@ -35,6 +36,7 @@ Revision History:
#include "smt/theory_pb.h"
#include "smt/theory_fpa.h"
#include "smt/theory_str.h"
#include "smt/theory_jobscheduler.h"
namespace smt {
@ -119,6 +121,8 @@ namespace smt {
setup_UFLRA();
else if (m_logic == "LRA")
setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else if (m_logic == "QF_FP")
setup_QF_FP();
else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP")
@ -196,6 +200,8 @@ namespace smt {
setup_QF_DT();
else if (m_logic == "LRA")
setup_LRA();
else if (m_logic == "CSP")
setup_CSP();
else
setup_unknown(st);
}
@ -217,6 +223,7 @@ namespace smt {
void setup::setup_QF_DT() {
setup_QF_UF();
setup_datatypes();
setup_recfuns();
}
void setup::setup_QF_BVRE() {
@ -873,6 +880,12 @@ namespace smt {
m_context.register_plugin(alloc(theory_datatype, m_manager, m_params));
}
void setup::setup_recfuns() {
TRACE("recfun", tout << "registering theory recfun...\n";);
theory_recfun * th = alloc(theory_recfun, m_manager);
m_context.register_plugin(th);
}
void setup::setup_dl() {
m_context.register_plugin(mk_theory_dl(m_manager));
}
@ -916,6 +929,11 @@ namespace smt {
m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params));
}
void setup::setup_CSP() {
setup_unknown();
m_context.register_plugin(alloc(smt::theory_jobscheduler, m_manager));
}
void setup::setup_unknown() {
static_features st(m_manager);
ptr_vector<expr> fmls;
@ -926,6 +944,7 @@ namespace smt {
setup_arrays();
setup_bv();
setup_datatypes();
setup_recfuns();
setup_dl();
setup_seq_str(st);
setup_card();
@ -945,6 +964,7 @@ namespace smt {
setup_seq_str(st);
setup_card();
setup_fpa();
setup_recfuns();
return;
}

View file

@ -81,6 +81,7 @@ namespace smt {
void setup_QF_FPBV();
void setup_QF_S();
void setup_LRA();
void setup_CSP();
void setup_AUFLIA(bool simple_array = true);
void setup_AUFLIA(static_features const & st);
void setup_AUFLIRA(bool simple_array = true);
@ -93,6 +94,7 @@ namespace smt {
void setup_unknown(static_features & st);
void setup_arrays();
void setup_datatypes();
void setup_recfuns();
void setup_bv();
void setup_arith();
void setup_dl();

View file

@ -194,6 +194,15 @@ namespace smt {
return l_false;
}
/**
\brief This method is called from the smt_context when an unsat core is generated.
The theory may tell the solver to perform iterative deepening by invalidating
this unsat core and increasing some resource constraints.
*/
virtual bool should_research(expr_ref_vector & unsat_core) {
return false;
}
/**
\brief This method is invoked before the search starts.
*/
@ -294,6 +303,8 @@ namespace smt {
SASSERT(m_context);
return *m_context;
}
context & ctx() const { return get_context(); }
ast_manager & get_manager() const {
SASSERT(m_manager);

View file

@ -3302,7 +3302,6 @@ namespace smt {
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());

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,244 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
theory_jobscheduling.h
Abstract:
Propagation solver for jobscheduling problems.
It relies on an external module to tighten bounds of
job variables.
Author:
Nikolaj Bjorner (nbjorner) 2018-09-08.
Revision History:
--*/
#pragma once;
#include "util/uint_set.h"
#include "ast/csp_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "smt/smt_theory.h"
namespace smt {
typedef uint64_t time_t;
class theory_jobscheduler : public theory {
public:
typedef svector<symbol> properties;
protected:
struct job_resource {
unsigned m_resource_id; // id of resource
time_t m_capacity; // amount of resource to use
unsigned m_loadpct; // assuming loadpct
time_t m_end; // must run before
properties m_properties;
job_resource(unsigned r, time_t cap, unsigned loadpct, time_t end, properties const& ps):
m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_end(end), m_properties(ps) {}
};
struct job_time {
unsigned m_job;
time_t m_time;
job_time(unsigned j, time_t time): m_job(j), m_time(time) {}
struct compare {
bool operator()(job_time const& jt1, job_time const& jt2) const {
return jt1.m_time < jt2.m_time;
}
};
};
struct job_info {
bool m_is_preemptable; // can job be pre-empted
vector<job_resource> m_resources; // resources allowed to run job.
u_map<unsigned> m_resource2index; // resource to index into vector
enode* m_job;
enode* m_start;
enode* m_end;
enode* m_job2resource;
bool m_is_bound;
job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr), m_is_bound(false) {}
};
struct res_available {
unsigned m_loadpct;
time_t m_start;
time_t m_end;
properties m_properties;
res_available(unsigned load_pct, time_t start, time_t end, properties const& ps):
m_loadpct(load_pct),
m_start(start),
m_end(end),
m_properties(ps)
{}
struct compare {
bool operator()(res_available const& ra1, res_available const& ra2) const {
return ra1.m_start < ra2.m_start;
}
};
};
struct res_info {
unsigned_vector m_jobs; // jobs allocated to run on resource
vector<res_available> m_available; // time intervals where resource is available
time_t m_end; // can't run after
enode* m_resource;
enode* m_makespan;
res_info(): m_end(std::numeric_limits<time_t>::max()), m_resource(nullptr), m_makespan(nullptr) {}
};
ast_manager& m;
csp_util u;
arith_util a;
vector<job_info> m_jobs;
vector<res_info> m_resources;
unsigned_vector m_bound_jobs;
unsigned m_bound_qhead;
struct scope {
unsigned m_bound_jobs_lim;
unsigned m_bound_qhead;
};
svector<scope> m_scopes;
protected:
theory_var mk_var(enode * n) override;
bool internalize_atom(app * atom, bool gate_ctx) override;
bool internalize_term(app * term) override;
void assign_eh(bool_var v, bool is_true) override {}
void new_eq_eh(theory_var v1, theory_var v2) override;
void new_diseq_eh(theory_var v1, theory_var v2) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
final_check_status final_check_eh() override;
bool can_propagate() override;
void propagate() override;
public:
theory_jobscheduler(ast_manager& m);
~theory_jobscheduler() override {}
void display(std::ostream & out) const override;
void collect_statistics(::statistics & st) const override;
bool include_func_interp(func_decl* f) override;
void init_model(model_generator & m) override;
model_value_proc * mk_value(enode * n, model_generator & mg) override;
bool get_value(enode * n, expr_ref & r) override;
theory * mk_fresh(context * new_ctx) override;
public:
// set up job/resource global constraints
void set_preemptable(unsigned j, bool is_preemptable);
void add_job_resource(unsigned j, unsigned r, unsigned loadpct, time_t cap, time_t end, properties const& ps);
void add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end, properties const& ps);
void add_done();
// assignments
time_t est(unsigned j); // earliest start time of job j
time_t lst(unsigned j); // last start time
time_t ect(unsigned j); // earliest completion time
time_t lct(unsigned j); // last completion time
time_t start(unsigned j); // start time of job j
time_t end(unsigned j); // end time of job j
time_t get_lo(expr* e);
time_t get_up(expr* e);
time_t get_value(expr* e);
unsigned resource(unsigned j); // resource of job j
// derived bounds
time_t ect(unsigned j, unsigned r, time_t start);
bool lst(unsigned j, unsigned r, time_t& t);
bool resource_available(job_resource const& jr, res_available const& ra) const;
bool first_available(job_resource const& jr, res_info const& ri, unsigned& idx) const;
bool last_available(job_resource const& jr, res_info const& ri, unsigned& idx) const;
time_t solve_for_start(unsigned load_pct, unsigned job_load_pct, time_t end, time_t cap);
time_t solve_for_end(unsigned load_pct, unsigned job_load_pct, time_t start, time_t cap);
time_t solve_for_capacity(unsigned load_pct, unsigned job_load_pct, time_t start, time_t end);
// validate assignment
void validate_assignment();
bool resource_available(unsigned r, time_t t, unsigned& idx); // load available on resource r at time t.
time_t capacity_used(unsigned j, unsigned r, time_t start, time_t end); // capacity used between start and end
job_resource const& get_job_resource(unsigned j, unsigned r) const;
// propagation
void propagate_end_time(unsigned j, unsigned r);
void propagate_job2resource(unsigned j, unsigned r);
// final check constraints
bool constrain_end_time_interval(unsigned j, unsigned r);
bool constrain_resource_energy(unsigned r);
bool split_job2resource(unsigned j);
void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq);
void assert_last_start_time(unsigned j, unsigned r, literal eq);
void assert_first_start_time(unsigned j, unsigned r, literal eq);
void assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq);
void assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq);
void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job);
class job_overlap {
time_t m_start;
vector<job_time> & m_starts, &m_ends;
unsigned s_idx, e_idx; // index into starts/ends
uint_set m_jobs;
public:
job_overlap(vector<job_time>& starts, vector<job_time>& ends);
bool next();
uint_set const& jobs() const { return m_jobs; }
};
// term builders
literal mk_ge(expr* e, time_t t);
expr* mk_ge_expr(expr* e, time_t t);
literal mk_ge(enode* e, time_t t);
literal mk_le(expr* e, time_t t);
expr* mk_le_expr(expr* e, time_t t);
literal mk_le(enode* e, time_t t);
literal mk_le(enode* l, enode* r);
literal mk_literal(expr* e);
literal mk_eq_lit(enode * l, enode * r) { return mk_eq_lit(l->get_owner(), r->get_owner()); }
literal mk_eq_lit(expr * l, expr * r);
void internalize_cmd(expr* cmd);
void unrecognized_argument(expr* arg) { invalid_argument("unrecognized argument ", arg); }
void invalid_argument(char const* msg, expr* arg);
std::ostream& display(std::ostream & out, res_info const& r) const;
std::ostream& display(std::ostream & out, res_available const& r) const;
std::ostream& display(std::ostream & out, job_info const& r) const;
std::ostream& display(std::ostream & out, job_resource const& r) const;
};
};

451
src/smt/theory_recfun.cpp Normal file
View file

@ -0,0 +1,451 @@
/*++
Copyright (c) 2018 Microsoft Corporation, Simon Cuares
Module Name:
theory_recfun.cpp
Abstract:
Theory responsible for unrolling recursive functions
Author:
Simon Cuares December 2017
Revision History:
--*/
#include "util/stats.h"
#include "ast/ast_util.h"
#include "ast/for_each_expr.h"
#include "smt/theory_recfun.h"
#include "smt/params/smt_params_helper.hpp"
#define TRACEFN(x) TRACE("recfun", tout << x << '\n';)
namespace smt {
theory_recfun::theory_recfun(ast_manager & m)
: theory(m.mk_family_id("recfun")),
m(m),
m_plugin(*reinterpret_cast<recfun::decl::plugin*>(m.get_plugin(get_family_id()))),
m_util(m_plugin.u()),
m_preds(m),
m_max_depth(0),
m_q_case_expand(),
m_q_body_expand()
{
}
theory_recfun::~theory_recfun() {
reset_queues();
}
char const * theory_recfun::get_name() const { return "recfun"; }
theory* theory_recfun::mk_fresh(context* new_ctx) {
return alloc(theory_recfun, new_ctx->get_manager());
}
void theory_recfun::init(context* ctx) {
theory::init(ctx);
smt_params_helper p(ctx->get_params());
m_max_depth = p.recfun_depth();
if (m_max_depth < 2) m_max_depth = 2;
}
void theory_recfun::init_search_eh() {
}
bool theory_recfun::internalize_atom(app * atom, bool gate_ctx) {
TRACEFN(mk_pp(atom, m));
if (!u().has_defs()) {
return false;
}
for (expr * arg : *atom) {
ctx().internalize(arg, false);
}
if (!ctx().e_internalized(atom)) {
ctx().mk_enode(atom, false, true, false);
}
if (!ctx().b_internalized(atom)) {
bool_var v = ctx().mk_bool_var(atom);
ctx().set_var_theory(v, get_id());
}
return true;
}
bool theory_recfun::internalize_term(app * term) {
if (!u().has_defs()) {
return false;
}
for (expr* e : *term) {
ctx().internalize(e, false);
}
// the internalization of the arguments may have triggered the internalization of term.
if (!ctx().e_internalized(term)) {
ctx().mk_enode(term, false, false, true);
}
return true;
}
void theory_recfun::reset_queues() {
for (auto* e : m_q_case_expand) {
dealloc(e);
}
m_q_case_expand.reset();
for (auto* e : m_q_body_expand) {
dealloc(e);
}
m_q_body_expand.reset();
m_q_clauses.clear();
}
void theory_recfun::reset_eh() {
reset_queues();
m_stats.reset();
theory::reset_eh();
}
/*
* when `n` becomes relevant, if it's `f(t1...tn)` with `f` defined,
* then case-expand `n`. If it's a macro we can also immediately
* body-expand it.
*/
void theory_recfun::relevant_eh(app * n) {
SASSERT(ctx().relevancy());
if (u().is_defined(n) && u().has_defs()) {
TRACEFN("relevant_eh: (defined) " << mk_pp(n, m));
push_case_expand(alloc(case_expansion, u(), n));
}
}
void theory_recfun::push_scope_eh() {
theory::push_scope_eh();
m_preds_lim.push_back(m_preds.size());
}
void theory_recfun::pop_scope_eh(unsigned num_scopes) {
theory::pop_scope_eh(num_scopes);
reset_queues();
// restore depth book-keeping
unsigned new_lim = m_preds_lim.size()-num_scopes;
#if 0
// depth tracking of recursive unfolding is
// turned off when enabling this code:
unsigned start = m_preds_lim[new_lim];
for (unsigned i = start; i < m_preds.size(); ++i) {
m_pred_depth.remove(m_preds.get(i));
}
m_preds.resize(start);
#endif
m_preds_lim.shrink(new_lim);
}
void theory_recfun::restart_eh() {
TRACEFN("restart");
reset_queues();
theory::restart_eh();
}
bool theory_recfun::can_propagate() {
return ! (m_q_case_expand.empty() &&
m_q_body_expand.empty() &&
m_q_clauses.empty());
}
void theory_recfun::propagate() {
for (literal_vector & c : m_q_clauses) {
TRACEFN("add axiom " << pp_lits(ctx(), c));
ctx().mk_th_axiom(get_id(), c);
}
m_q_clauses.clear();
for (unsigned i = 0; i < m_q_case_expand.size(); ++i) {
case_expansion* e = m_q_case_expand[i];
if (e->m_def->is_fun_macro()) {
// body expand immediately
assert_macro_axiom(*e);
}
else {
// case expand
SASSERT(e->m_def->is_fun_defined());
assert_case_axioms(*e);
}
dealloc(e);
m_q_case_expand[i] = nullptr;
}
m_stats.m_case_expansions += m_q_case_expand.size();
m_q_case_expand.reset();
for (unsigned i = 0; i < m_q_body_expand.size(); ++i) {
assert_body_axiom(*m_q_body_expand[i]);
dealloc(m_q_body_expand[i]);
m_q_body_expand[i] = nullptr;
}
m_stats.m_body_expansions += m_q_body_expand.size();
m_q_body_expand.reset();
}
/**
* make clause `depth_limit => ~guard`
* the guard appears at a depth below the current cutoff.
*/
void theory_recfun::assert_max_depth_limit(expr* guard) {
literal_vector c;
app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth);
c.push_back(~mk_literal(dlimit));
c.push_back(~mk_literal(guard));
TRACEFN("max-depth limit: add clause " << pp_lits(ctx(), c));
m_q_clauses.push_back(std::move(c));
}
/**
* retrieve depth associated with predicate or expression.
*/
unsigned theory_recfun::get_depth(expr* e) {
SASSERT(u().is_defined(e) || u().is_case_pred(e));
unsigned d = 0;
m_pred_depth.find(e, d);
TRACEFN("depth " << d << " " << mk_pp(e, m));
return d;
}
/**
* Update depth of subterms of e with respect to d.
*/
void theory_recfun::set_depth_rec(unsigned d, expr* e) {
struct insert_c {
theory_recfun& th;
unsigned m_depth;
insert_c(theory_recfun& th, unsigned d): th(th), m_depth(d) {}
void operator()(app* e) { th.set_depth(m_depth, e); }
void operator()(quantifier*) {}
void operator()(var*) {}
};
insert_c proc(*this, d);
for_each_expr(proc, e);
}
void theory_recfun::set_depth(unsigned depth, expr* e) {
if ((u().is_defined(e) || u().is_case_pred(e)) && !m_pred_depth.contains(e)) {
m_pred_depth.insert(e, depth);
m_preds.push_back(e);
TRACEFN("depth " << depth << " : " << mk_pp(e, m));
}
}
/**
* if `is_true` and `v = C_f_i(t1...tn)`,
* then body-expand i-th case of `f(t1...tn)`
*/
void theory_recfun::assign_eh(bool_var v, bool is_true) {
expr* e = ctx().bool_var2expr(v);
if (is_true && u().is_case_pred(e)) {
TRACEFN("assign_case_pred_true " << mk_pp(e, m));
// body-expand
push_body_expand(alloc(body_expansion, u(), to_app(e)));
}
}
// replace `vars` by `args` in `e`
expr_ref theory_recfun::apply_args(
unsigned depth,
recfun::vars const & vars,
ptr_vector<expr> const & args,
expr * e) {
SASSERT(is_standard_order(vars));
var_subst subst(m, true);
expr_ref new_body(m);
new_body = subst(e, args.size(), args.c_ptr());
ctx().get_rewriter()(new_body); // simplify
set_depth_rec(depth + 1, new_body);
return new_body;
}
literal theory_recfun::mk_literal(expr* e) {
ctx().internalize(e, false);
literal lit = ctx().get_literal(e);
ctx().mark_as_relevant(lit);
return lit;
}
literal theory_recfun::mk_eq_lit(expr* l, expr* r) {
literal lit;
if (m.is_true(r) || m.is_false(r)) {
std::swap(l, r);
}
if (m.is_true(l)) {
lit = mk_literal(r);
}
else if (m.is_false(l)) {
lit = ~mk_literal(r);
}
else {
lit = mk_eq(l, r, false);
}
ctx().mark_as_relevant(lit);
return lit;
}
/**
* For functions f(args) that are given as macros f(vs) = rhs
*
* 1. substitute `e.args` for `vs` into the macro rhs
* 2. add unit clause `f(args) = rhs`
*/
void theory_recfun::assert_macro_axiom(case_expansion & e) {
m_stats.m_macro_expansions++;
TRACEFN("case expansion " << pp_case_expansion(e, m) << "\n");
SASSERT(e.m_def->is_fun_macro());
auto & vars = e.m_def->get_vars();
expr_ref lhs(e.m_lhs, m);
unsigned depth = get_depth(e.m_lhs);
expr_ref rhs(apply_args(depth, vars, e.m_args, e.m_def->get_rhs()), m);
literal lit = mk_eq_lit(lhs, rhs);
ctx().mk_th_axiom(get_id(), 1, &lit);
TRACEFN("macro expansion yields " << mk_pp(rhs, m) << "\n" <<
"literal " << pp_lit(ctx(), lit));
}
/**
* Add case axioms for every case expansion path.
*
* assert `p(args) <=> And(guards)` (with CNF on the fly)
*
* also body-expand paths that do not depend on any defined fun
*/
void theory_recfun::assert_case_axioms(case_expansion & e) {
TRACEFN("assert_case_axioms "<< pp_case_expansion(e,m)
<< " with " << e.m_def->get_cases().size() << " cases");
SASSERT(e.m_def->is_fun_defined());
// add case-axioms for all case-paths
auto & vars = e.m_def->get_vars();
literal_vector preds;
for (recfun::case_def const & c : e.m_def->get_cases()) {
// applied predicate to `args`
app_ref pred_applied = c.apply_case_predicate(e.m_args);
// cut off cases below max-depth
unsigned depth = get_depth(e.m_lhs);
set_depth(depth, pred_applied);
SASSERT(u().owns_app(pred_applied));
literal concl = mk_literal(pred_applied);
preds.push_back(concl);
if (depth >= m_max_depth) {
assert_max_depth_limit(pred_applied);
continue;
}
literal_vector guards;
guards.push_back(concl);
for (auto & g : c.get_guards()) {
expr_ref ga = apply_args(depth, vars, e.m_args, g);
literal guard = mk_literal(ga);
guards.push_back(~guard);
literal c[2] = {~concl, guard};
ctx().mk_th_axiom(get_id(), 2, c);
}
ctx().mk_th_axiom(get_id(), guards);
if (c.is_immediate()) {
body_expansion be(pred_applied, c, e.m_args);
assert_body_axiom(be);
}
}
// the disjunction of branches is asserted
// to close the available cases.
ctx().mk_th_axiom(get_id(), preds);
}
/**
* For a guarded definition guards => f(vars) = rhs
* and occurrence f(args)
*
* substitute `args` for `vars` in guards, and rhs
* add axiom guards[args/vars] => f(args) = rhs[args/vars]
*
*/
void theory_recfun::assert_body_axiom(body_expansion & e) {
recfun::def & d = *e.m_cdef->get_def();
auto & vars = d.get_vars();
auto & args = e.m_args;
SASSERT(is_standard_order(vars));
unsigned depth = get_depth(e.m_pred);
expr_ref lhs(u().mk_fun_defined(d, args), m);
expr_ref rhs = apply_args(depth, vars, args, e.m_cdef->get_rhs());
literal_vector clause;
for (auto & g : e.m_cdef->get_guards()) {
expr_ref guard = apply_args(depth, vars, args, g);
clause.push_back(~mk_literal(guard));
if (clause.back() == true_literal) {
return;
}
if (clause.back() == false_literal) {
clause.pop_back();
}
}
clause.push_back(mk_eq_lit(lhs, rhs));
ctx().mk_th_axiom(get_id(), clause);
TRACEFN("body " << pp_body_expansion(e,m));
TRACEFN(pp_lits(ctx(), clause));
}
final_check_status theory_recfun::final_check_eh() {
TRACEFN("final\n");
if (can_propagate()) {
propagate();
return FC_CONTINUE;
}
return FC_DONE;
}
void theory_recfun::add_theory_assumptions(expr_ref_vector & assumptions) {
if (u().has_defs()) {
app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth);
TRACEFN("add_theory_assumption " << mk_pp(dlimit.get(), m));
assumptions.push_back(dlimit);
}
}
// if `dlimit` occurs in unsat core, return 'true'
bool theory_recfun::should_research(expr_ref_vector & unsat_core) {
for (auto & e : unsat_core) {
if (u().is_depth_limit(e)) {
m_max_depth = (3 * m_max_depth) / 2;
IF_VERBOSE(1, verbose_stream() << "(smt.recfun :increase-depth " << m_max_depth << ")\n");
return true;
}
}
return false;
}
void theory_recfun::display(std::ostream & out) const {
out << "recfun{}";
}
void theory_recfun::collect_statistics(::statistics & st) const {
st.update("recfun macro expansion", m_stats.m_macro_expansions);
st.update("recfun case expansion", m_stats.m_case_expansions);
st.update("recfun body expansion", m_stats.m_body_expansions);
}
std::ostream& operator<<(std::ostream & out, theory_recfun::pp_case_expansion const & e) {
return out << "case_exp(" << mk_pp(e.e.m_lhs, e.m) << ")";
}
std::ostream& operator<<(std::ostream & out, theory_recfun::pp_body_expansion const & e) {
out << "body_exp(" << e.e.m_cdef->get_decl()->get_name();
for (auto* t : e.e.m_args) {
out << " " << mk_pp(t,e.m);
}
return out << ")";
}
}

163
src/smt/theory_recfun.h Normal file
View file

@ -0,0 +1,163 @@
/*++
Copyright (c) 2018 Microsoft Corporation
Module Name:
theory_recfun.h
Abstract:
Theory responsible for unrolling recursive functions
Author:
Simon Cuares December 2017
Revision History:
--*/
#ifndef THEORY_RECFUN_H_
#define THEORY_RECFUN_H_
#include "smt/smt_theory.h"
#include "smt/smt_context.h"
#include "ast/ast_pp.h"
#include "ast/recfun_decl_plugin.h"
namespace smt {
class theory_recfun : public theory {
struct stats {
unsigned m_case_expansions, m_body_expansions, m_macro_expansions;
void reset() { memset(this, 0, sizeof(stats)); }
stats() { reset(); }
};
// one case-expansion of `f(t1...tn)`
struct case_expansion {
app * m_lhs; // the term to expand
recfun::def * m_def;
ptr_vector<expr> m_args;
case_expansion(recfun::util& u, app * n) :
m_lhs(n), m_def(nullptr), m_args() {
SASSERT(u.is_defined(n));
func_decl * d = n->get_decl();
m_def = &u.get_def(d);
m_args.append(n->get_num_args(), n->get_args());
}
case_expansion(case_expansion const & from)
: m_lhs(from.m_lhs),
m_def(from.m_def),
m_args(from.m_args) {}
case_expansion(case_expansion && from)
: m_lhs(from.m_lhs),
m_def(from.m_def),
m_args(std::move(from.m_args)) {}
};
struct pp_case_expansion {
case_expansion & e;
ast_manager & m;
pp_case_expansion(case_expansion & e, ast_manager & m) : e(e), m(m) {}
};
friend std::ostream& operator<<(std::ostream&, pp_case_expansion const &);
// one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)`
struct body_expansion {
app* m_pred;
recfun::case_def const * m_cdef;
ptr_vector<expr> m_args;
body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(0), m_args() {
m_cdef = &u.get_case_def(n);
m_args.append(n->get_num_args(), n->get_args());
}
body_expansion(app* pred, recfun::case_def const & d, ptr_vector<expr> & args) :
m_pred(pred), m_cdef(&d), m_args(args) {}
body_expansion(body_expansion const & from):
m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {}
body_expansion(body_expansion && from) :
m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {}
};
struct pp_body_expansion {
body_expansion & e;
ast_manager & m;
pp_body_expansion(body_expansion & e, ast_manager & m) : e(e), m(m) {}
};
friend std::ostream& operator<<(std::ostream&, pp_body_expansion const &);
ast_manager& m;
recfun::decl::plugin& m_plugin;
recfun::util& m_util;
stats m_stats;
// book-keeping for depth of predicates
obj_map<expr, unsigned> m_pred_depth;
expr_ref_vector m_preds;
unsigned_vector m_preds_lim;
unsigned m_max_depth; // for fairness and termination
ptr_vector<case_expansion> m_q_case_expand;
ptr_vector<body_expansion> m_q_body_expand;
vector<literal_vector> m_q_clauses;
recfun::util & u() const { return m_util; }
bool is_defined(app * f) const { return u().is_defined(f); }
bool is_case_pred(app * f) const { return u().is_case_pred(f); }
bool is_defined(enode * e) const { return is_defined(e->get_owner()); }
bool is_case_pred(enode * e) const { return is_case_pred(e->get_owner()); }
void reset_queues();
expr_ref apply_args(unsigned depth, recfun::vars const & vars, ptr_vector<expr> const & args, expr * e); //!< substitute variables by args
void assert_macro_axiom(case_expansion & e);
void assert_case_axioms(case_expansion & e);
void assert_body_axiom(body_expansion & e);
literal mk_literal(expr* e);
void assert_max_depth_limit(expr* guard);
unsigned get_depth(expr* e);
void set_depth(unsigned d, expr* e);
void set_depth_rec(unsigned d, expr* e);
literal mk_eq_lit(expr* l, expr* r);
bool is_standard_order(recfun::vars const& vars) const {
return vars.size() == 0 || vars[vars.size()-1]->get_idx() == 0;
}
protected:
void push_case_expand(case_expansion* e) { m_q_case_expand.push_back(e); }
void push_body_expand(body_expansion* e) { m_q_body_expand.push_back(e); }
bool internalize_atom(app * atom, bool gate_ctx) override;
bool internalize_term(app * term) override;
void reset_eh() override;
void relevant_eh(app * n) override;
char const * get_name() const override;
final_check_status final_check_eh() override;
void assign_eh(bool_var v, bool is_true) override;
void push_scope_eh() override;
void pop_scope_eh(unsigned num_scopes) override;
void restart_eh() override;
bool can_propagate() override;
void propagate() override;
bool should_research(expr_ref_vector &) override;
void new_eq_eh(theory_var v1, theory_var v2) override {}
void new_diseq_eh(theory_var v1, theory_var v2) override {}
void add_theory_assumptions(expr_ref_vector & assumptions) override;
void init(context* ctx) override;
public:
theory_recfun(ast_manager & m);
~theory_recfun() override;
theory * mk_fresh(context * new_ctx) override;
void init_search_eh() override;
void display(std::ostream & out) const override;
void collect_statistics(::statistics & st) const override;
};
}
#endif

View file

@ -22,7 +22,7 @@ Revision History:
bool smt_logics::supported_logic(symbol const & s) {
return logic_has_uf(s) || logic_is_all(s) || logic_has_fd(s) ||
return logic_has_uf(s) || logic_is_allcsp(s) || logic_has_fd(s) ||
logic_has_arith(s) || logic_has_bv(s) ||
logic_has_array(s) || logic_has_seq(s) || logic_has_str(s) ||
logic_has_horn(s) || logic_has_fpa(s);
@ -83,7 +83,7 @@ bool smt_logics::logic_has_arith(symbol const & s) {
s == "QF_FPBV" ||
s == "QF_BVFP" ||
s == "QF_S" ||
s == "ALL" ||
logic_is_allcsp(s) ||
s == "QF_FD" ||
s == "HORN" ||
s == "QF_FPLRA";
@ -102,7 +102,7 @@ bool smt_logics::logic_has_bv(symbol const & s) {
s == "QF_BVRE" ||
s == "QF_FPBV" ||
s == "QF_BVFP" ||
s == "ALL" ||
logic_is_allcsp(s) ||
s == "QF_FD" ||
s == "HORN";
}
@ -123,22 +123,22 @@ bool smt_logics::logic_has_array(symbol const & s) {
s == "AUFNIRA" ||
s == "AUFBV" ||
s == "ABV" ||
s == "ALL" ||
logic_is_allcsp(s) ||
s == "QF_ABV" ||
s == "QF_AUFBV" ||
s == "HORN";
}
bool smt_logics::logic_has_seq(symbol const & s) {
return s == "QF_BVRE" || s == "QF_S" || s == "ALL";
return s == "QF_BVRE" || s == "QF_S" || logic_is_allcsp(s);
}
bool smt_logics::logic_has_str(symbol const & s) {
return s == "QF_S" || s == "ALL";
return s == "QF_S" || logic_is_allcsp(s);
}
bool smt_logics::logic_has_fpa(symbol const & s) {
return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA" || s == "ALL";
return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA" || logic_is_allcsp(s);
}
bool smt_logics::logic_has_uf(symbol const & s) {
@ -150,9 +150,10 @@ bool smt_logics::logic_has_horn(symbol const& s) {
}
bool smt_logics::logic_has_pb(symbol const& s) {
return s == "QF_FD" || s == "ALL" || logic_has_horn(s);
return s == "QF_FD" || logic_is_allcsp(s) || logic_has_horn(s);
}
bool smt_logics::logic_has_datatype(symbol const& s) {
return s == "QF_FD" || s == "ALL" || s == "QF_DT";
return s == "QF_FD" || logic_is_allcsp(s) || s == "QF_DT";
}

View file

@ -25,6 +25,8 @@ public:
static bool supported_logic(symbol const & s);
static bool logic_has_reals_only(symbol const& l);
static bool logic_is_all(symbol const& s) { return s == "ALL"; }
static bool logic_is_csp(symbol const& s) { return s == "CSP"; }
static bool logic_is_allcsp(symbol const& s) { return logic_is_all(s) || logic_is_csp(s); }
static bool logic_has_uf(symbol const& s);
static bool logic_has_arith(symbol const & s);
static bool logic_has_bv(symbol const & s);

View file

@ -31,6 +31,7 @@ public:
void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc<T>()); m_vector.reset(); }
void push_back(T * ptr) { m_vector.push_back(ptr); }
void pop_back() { SASSERT(!empty()); set(size()-1, nullptr); m_vector.pop_back(); }
T * back() const { return m_vector.back(); }
T * operator[](unsigned idx) const { return m_vector[idx]; }
void set(unsigned idx, T * ptr) {
if (m_vector[idx] == ptr)
@ -51,6 +52,13 @@ public:
push_back(nullptr);
}
}
//!< swap last element with given pointer
void swap_back(scoped_ptr<T> & ptr) {
SASSERT(!empty());
T * tmp = ptr.detach();
ptr = m_vector.back();
m_vector[m_vector.size()-1] = tmp;
}
};
#endif

View file

@ -143,6 +143,17 @@ public:
};
template<typename Ctx, typename M, typename Mgr, typename D>
class insert_ref_map : public trail<Ctx> {
Mgr& m;
M& m_map;
D m_obj;
public:
insert_ref_map(Mgr& m, M& t, D o) : m(m), m_map(t), m_obj(o) {}
virtual ~insert_ref_map() {}
virtual void undo(Ctx & ctx) { m_map.remove(m_obj); m.dec_ref(m_obj); }
};
template<typename Ctx, typename V>
class push_back_vector : public trail<Ctx> {

View file

@ -269,10 +269,10 @@ public:
void remove(unsigned v) {
if (contains(v)) {
m_in_set[v] = false;
unsigned i = 0;
for (i = 0; i < m_set.size() && m_set[i] != v; ++i)
unsigned i = m_set.size();
for (; i > 0 && m_set[--i] != v; )
;
SASSERT(i < m_set.size());
SASSERT(m_set[i] == v);
m_set[i] = m_set.back();
m_set.pop_back();
}