3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

Merge branch 'opt' of https://git01.codeplex.com/z3 into opt

This commit is contained in:
unknown 2015-01-28 17:54:26 -08:00
commit f020b7c7b8
380 changed files with 66767 additions and 3722 deletions

View file

@ -24,6 +24,7 @@ Revision History:
#include"bv_decl_plugin.h"
#include"datatype_decl_plugin.h"
#include"array_decl_plugin.h"
#include"pb_decl_plugin.h"
#include"ast_translation.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
@ -1075,7 +1076,6 @@ extern "C" {
case OP_BSREM_I:
case OP_BUREM_I:
case OP_BSMOD_I:
return Z3_OP_UNINTERPRETED;
default:
UNREACHABLE();
@ -1084,9 +1084,10 @@ extern "C" {
}
if (mk_c(c)->get_dt_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
default:
UNREACHABLE();
return Z3_OP_UNINTERPRETED;
@ -1124,6 +1125,15 @@ extern "C" {
}
}
if (mk_c(c)->get_pb_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_PB_LE: return Z3_OP_PB_LE;
case OP_PB_GE: return Z3_OP_PB_GE;
case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
default: UNREACHABLE();
}
}
return Z3_OP_UNINTERPRETED;
Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED);
}

View file

@ -37,9 +37,7 @@ extern "C" {
catch (z3_exception & ex) {
// The error handler is only available for contexts
// Just throw a warning.
std::ostringstream buffer;
buffer << "Error setting " << param_id << ", " << ex.msg();
warning_msg(buffer.str().c_str());
warning_msg(ex.msg());
}
}
@ -64,9 +62,7 @@ extern "C" {
catch (z3_exception & ex) {
// The error handler is only available for contexts
// Just throw a warning.
std::ostringstream buffer;
buffer << "Error setting " << param_id << ": " << ex.msg();
warning_msg(buffer.str().c_str());
warning_msg(ex.msg());
return Z3_FALSE;
}
}
@ -92,9 +88,7 @@ extern "C" {
catch (z3_exception & ex) {
// The error handler is only available for contexts
// Just throw a warning.
std::ostringstream buffer;
buffer << "Error setting " << param_id << ": " << ex.msg();
warning_msg(buffer.str().c_str());
warning_msg(ex.msg());
}
}

View file

@ -109,6 +109,7 @@ namespace api {
m_basic_fid = m().get_basic_family_id();
m_arith_fid = m().mk_family_id("arith");
m_bv_fid = m().mk_family_id("bv");
m_pb_fid = m().mk_family_id("pb");
m_array_fid = m().mk_family_id("array");
m_dt_fid = m().mk_family_id("datatype");
m_datalog_fid = m().mk_family_id("datalog_relation");

View file

@ -75,6 +75,7 @@ namespace api {
family_id m_bv_fid;
family_id m_dt_fid;
family_id m_datalog_fid;
family_id m_pb_fid;
datatype_decl_plugin * m_dt_plugin;
std::string m_string_buffer; // temporary buffer used to cache strings sent to the "external" world.
@ -121,6 +122,7 @@ namespace api {
family_id get_bv_fid() const { return m_bv_fid; }
family_id get_dt_fid() const { return m_dt_fid; }
family_id get_datalog_fid() const { return m_datalog_fid; }
family_id get_pb_fid() const { return m_pb_fid; }
datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; }
Z3_error_code get_error_code() const { return m_error_code; }

View file

@ -125,7 +125,7 @@ namespace api {
return "unknown";
}
}
std::string to_string(unsigned num_queries, expr*const* queries) {
std::string to_string(unsigned num_queries, expr* const* queries) {
std::stringstream str;
m_context.display_smt2(num_queries, queries, str);
return str.str();
@ -466,13 +466,16 @@ extern "C" {
ast_manager& m = mk_c(c)->m();
Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m);
mk_c(c)->save_object(v);
expr_ref_vector rules(m);
expr_ref_vector rules(m), queries(m);
svector<symbol> names;
to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, names);
to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, queries, names);
for (unsigned i = 0; i < rules.size(); ++i) {
v->m_ast_vector.push_back(rules[i].get());
}
for (unsigned i = 0; i < queries.size(); ++i) {
v->m_ast_vector.push_back(m.mk_not(queries[i].get()));
}
RETURN_Z3(of_ast_vector(v));
Z3_CATCH_RETURN(0);
}

View file

@ -618,4 +618,25 @@ extern "C" {
Z3_CATCH_RETURN(0);
}
Z3_ast Z3_datatype_update_field(
__in Z3_context c, __in Z3_func_decl f, __in Z3_ast t, __in Z3_ast v) {
Z3_TRY;
LOG_Z3_datatype_update_field(c, f, t, v);
RESET_ERROR_CODE();
ast_manager & m = mk_c(c)->m();
func_decl* _f = to_func_decl(f);
expr* _t = to_expr(t);
expr* _v = to_expr(v);
expr* args[2] = { _t, _v };
sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) };
parameter param(_f);
func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_DT_UPDATE_FIELD, 1, &param, 2, domain);
app* r = m.mk_app(d, 2, args);
mk_c(c)->save_ast_trail(r);
check_sorts(c, r);
RETURN_Z3(of_ast(r));
Z3_CATCH_RETURN(0);
}
};

243
src/api/api_opt.cpp Normal file
View file

@ -0,0 +1,243 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
api_opt.cpp
Abstract:
API for optimization
Author:
Nikolaj Bjorner (nbjorner) 2013-12-3.
Revision History:
--*/
#include<iostream>
#include"z3.h"
#include"api_log_macros.h"
#include"api_stats.h"
#include"api_context.h"
#include"api_util.h"
#include"api_model.h"
#include"opt_context.h"
#include"cancel_eh.h"
#include"scoped_timer.h"
extern "C" {
struct Z3_optimize_ref : public api::object {
opt::context* m_opt;
Z3_optimize_ref():m_opt(0) {}
virtual ~Z3_optimize_ref() { dealloc(m_opt); }
};
inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast<Z3_optimize_ref *>(o); }
inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast<Z3_optimize>(o); }
inline opt::context* to_optimize_ptr(Z3_optimize o) { return to_optimize(o)->m_opt; }
Z3_optimize Z3_API Z3_mk_optimize(Z3_context c) {
Z3_TRY;
LOG_Z3_mk_optimize(c);
RESET_ERROR_CODE();
Z3_optimize_ref * o = alloc(Z3_optimize_ref);
o->m_opt = alloc(opt::context,mk_c(c)->m());
mk_c(c)->save_object(o);
RETURN_Z3(of_optimize(o));
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_inc_ref(c, o);
RESET_ERROR_CODE();
to_optimize(o)->inc_ref();
Z3_CATCH;
}
void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_dec_ref(c, o);
RESET_ERROR_CODE();
to_optimize(o)->dec_ref();
Z3_CATCH;
}
void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a) {
Z3_TRY;
LOG_Z3_optimize_assert(c, o, a);
RESET_ERROR_CODE();
CHECK_FORMULA(a,);
to_optimize_ptr(o)->add_hard_constraint(to_expr(a));
Z3_CATCH;
}
unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id) {
Z3_TRY;
LOG_Z3_optimize_assert_soft(c, o, a, weight, id);
RESET_ERROR_CODE();
CHECK_FORMULA(a,0);
rational w(weight);
return to_optimize_ptr(o)->add_soft_constraint(to_expr(a), w, to_symbol(id));
Z3_CATCH_RETURN(0);
}
unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t) {
Z3_TRY;
LOG_Z3_optimize_maximize(c, o, t);
RESET_ERROR_CODE();
CHECK_VALID_AST(t,0);
return to_optimize_ptr(o)->add_objective(to_app(t), true);
Z3_CATCH_RETURN(0);
}
unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t) {
Z3_TRY;
LOG_Z3_optimize_minimize(c, o, t);
RESET_ERROR_CODE();
CHECK_VALID_AST(t,0);
return to_optimize_ptr(o)->add_objective(to_app(t), false);
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d) {
Z3_TRY;
LOG_Z3_optimize_push(c, d);
RESET_ERROR_CODE();
to_optimize_ptr(d)->push();
Z3_CATCH;
}
void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d) {
Z3_TRY;
LOG_Z3_optimize_pop(c, d);
RESET_ERROR_CODE();
to_optimize_ptr(d)->pop(1);
Z3_CATCH;
}
Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_check(c, o);
RESET_ERROR_CODE();
lbool r = l_undef;
cancel_eh<opt::context> eh(*to_optimize_ptr(o));
unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout());
api::context::set_interruptable si(*(mk_c(c)), eh);
{
scoped_timer timer(timeout, &eh);
try {
r = to_optimize_ptr(o)->optimize();
}
catch (z3_exception& ex) {
mk_c(c)->handle_exception(ex);
r = l_undef;
}
// to_optimize_ref(d).cleanup();
}
return of_lbool(r);
Z3_CATCH_RETURN(Z3_L_UNDEF);
}
Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_get_model(c, o);
RESET_ERROR_CODE();
model_ref _m;
to_optimize_ptr(o)->get_model(_m);
Z3_model_ref * m_ref = alloc(Z3_model_ref);
if (_m) {
m_ref->m_model = _m;
}
else {
m_ref->m_model = alloc(model, mk_c(c)->m());
}
mk_c(c)->save_object(m_ref);
RETURN_Z3(of_model(m_ref));
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) {
Z3_TRY;
LOG_Z3_optimize_set_params(c, o, p);
RESET_ERROR_CODE();
param_descrs descrs;
to_optimize_ptr(o)->collect_param_descrs(descrs);
to_params(p)->m_params.validate(descrs);
params_ref pr = to_param_ref(p);
to_optimize_ptr(o)->updt_params(pr);
Z3_CATCH;
}
Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_get_param_descrs(c, o);
RESET_ERROR_CODE();
Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref);
mk_c(c)->save_object(d);
to_optimize_ptr(o)->collect_param_descrs(d->m_descrs);
Z3_param_descrs r = of_param_descrs(d);
RETURN_Z3(r);
Z3_CATCH_RETURN(0);
}
// get lower value or current approximation
Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx) {
Z3_TRY;
LOG_Z3_optimize_get_lower(c, o, idx);
RESET_ERROR_CODE();
expr_ref e = to_optimize_ptr(o)->get_lower(idx);
mk_c(c)->save_ast_trail(e);
RETURN_Z3(of_expr(e));
Z3_CATCH_RETURN(0);
}
// get upper or current approximation
Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx) {
Z3_TRY;
LOG_Z3_optimize_get_upper(c, o, idx);
RESET_ERROR_CODE();
expr_ref e = to_optimize_ptr(o)->get_upper(idx);
mk_c(c)->save_ast_trail(e);
RETURN_Z3(of_expr(e));
Z3_CATCH_RETURN(0);
}
Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) {
Z3_TRY;
LOG_Z3_optimize_to_string(c, o);
RESET_ERROR_CODE();
return mk_c(c)->mk_external_string(to_optimize_ptr(o)->to_string());
Z3_CATCH_RETURN("");
}
Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize d) {
Z3_TRY;
LOG_Z3_optimize_get_help(c, d);
RESET_ERROR_CODE();
std::ostringstream buffer;
param_descrs descrs;
to_optimize_ptr(d)->collect_param_descrs(descrs);
descrs.display(buffer);
return mk_c(c)->mk_external_string(buffer.str());
Z3_CATCH_RETURN("");
}
Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d) {
Z3_TRY;
LOG_Z3_optimize_get_statistics(c, d);
RESET_ERROR_CODE();
Z3_stats_ref * st = alloc(Z3_stats_ref);
to_optimize_ptr(d)->collect_statistics(st->m_stats);
mk_c(c)->save_object(st);
Z3_stats r = of_stats(st);
RETURN_Z3(r);
Z3_CATCH_RETURN(0);
}
};

61
src/api/api_pb.cpp Normal file
View file

@ -0,0 +1,61 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
api_pb.cpp
Abstract:
API for pb theory
Author:
Nikolaj Bjorner (nbjorner) 2013-11-13.
Revision History:
--*/
#include<iostream>
#include"z3.h"
#include"api_log_macros.h"
#include"api_context.h"
#include"api_util.h"
#include"pb_decl_plugin.h"
extern "C" {
Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args,
Z3_ast const args[], unsigned k) {
Z3_TRY;
LOG_Z3_mk_atmost(c, num_args, args, k);
RESET_ERROR_CODE();
parameter param(k);
pb_util util(mk_c(c)->m());
ast* a = util.mk_at_most_k(num_args, to_exprs(args), k);
mk_c(c)->save_ast_trail(a);
check_sorts(c, a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(0);
}
Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args,
Z3_ast const args[], int _coeffs[],
int k) {
Z3_TRY;
LOG_Z3_mk_pble(c, num_args, args, _coeffs, k);
RESET_ERROR_CODE();
pb_util util(mk_c(c)->m());
vector<rational> coeffs;
for (unsigned i = 0; i < num_args; ++i) {
coeffs.push_back(rational(_coeffs[i]));
}
ast* a = util.mk_le(num_args, coeffs.c_ptr(), to_exprs(args), rational(k));
mk_c(c)->save_ast_trail(a);
check_sorts(c, a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(0);
}
};

View file

@ -1314,6 +1314,26 @@ namespace z3 {
expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); }
expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); }
friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; }
std::string to_smt2(char const* status = "unknown") {
array<Z3_ast> es(assertions());
Z3_ast const* fmls = es.ptr();
Z3_ast fml = 0;
unsigned sz = es.size();
if (sz > 0) {
--sz;
fml = fmls[sz];
}
else {
fml = ctx().bool_val(true);
}
return std::string(Z3_benchmark_to_smtlib_string(
ctx(),
"", "", status, "",
sz,
fmls,
fml));
}
};
class goal : public object {
@ -1513,6 +1533,62 @@ namespace z3 {
}
};
class optimize : public object {
Z3_optimize m_opt;
public:
class handle {
unsigned m_h;
public:
handle(unsigned h): m_h(h) {}
unsigned h() const { return m_h; }
};
optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); }
~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); }
operator Z3_optimize() const { return m_opt; }
void add(expr const& e) {
assert(e.is_bool());
Z3_optimize_assert(ctx(), m_opt, e);
}
handle add(expr const& e, unsigned weight) {
assert(e.is_bool());
std::stringstream strm;
strm << weight;
return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, strm.str().c_str(), 0));
}
handle add(expr const& e, char const* weight) {
assert(e.is_bool());
return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, weight, 0));
}
handle maximize(expr const& e) {
return handle(Z3_optimize_maximize(ctx(), m_opt, e));
}
handle minimize(expr const& e) {
return handle(Z3_optimize_minimize(ctx(), m_opt, e));
}
void push() {
Z3_optimize_push(ctx(), m_opt);
}
void pop() {
Z3_optimize_pop(ctx(), m_opt);
}
check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt); check_error(); return to_check_result(r); }
model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); }
void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); }
expr lower(handle const& h) {
Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h());
check_error();
return expr(ctx(), r);
}
expr upper(handle const& h) {
Z3_ast r = Z3_optimize_get_upper(ctx(), m_opt, h.h());
check_error();
return expr(ctx(), r);
}
stats statistics() const { Z3_stats r = Z3_optimize_get_statistics(ctx(), m_opt); check_error(); return stats(ctx(), r); }
friend std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; }
std::string help() const { char const * r = Z3_optimize_get_help(ctx(), m_opt); check_error(); return r; }
};
inline tactic fail_if(probe const & p) {
Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p);
p.check_error();

View file

@ -449,6 +449,19 @@ namespace Microsoft.Z3
return MkDatatypeSorts(MkSymbols(names), c);
}
/// <summary>
/// Update a datatype field at expression t with value v.
/// The function performs a record update at t. The field
/// that is passed in as argument is updated with value v,
/// the remainig fields of t are unchanged.
/// </summary>
public Expr MkUpdateField(FuncDecl field, Expr t, Expr v)
{
return Expr.Create(this, Native.Z3_datatype_update_field(
nCtx, field.NativeObject,
t.NativeObject, v.NativeObject));
}
#endregion
#endregion
@ -2251,6 +2264,36 @@ namespace Microsoft.Z3
}
#endregion
#region Pseudo-Boolean constraints
/// <summary>
/// Create an at-most-k constraint.
/// </summary>
public BoolExpr MkAtMost(BoolExpr[] args, uint k)
{
Contract.Requires(args != null);
Contract.Requires(Contract.Result<BoolExpr[]>() != null);
CheckContextMatch(args);
return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length,
AST.ArrayToNative(args), k));
}
/// <summary>
/// Create a pseudo-Boolean less-or-equal constraint.
/// </summary>
public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k)
{
Contract.Requires(args != null);
Contract.Requires(coeffs != null);
Contract.Requires(args.Length == coeffs.Length);
Contract.Requires(Contract.Result<BoolExpr[]>() != null);
CheckContextMatch(args);
return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length,
AST.ArrayToNative(args),
coeffs, k));
}
#endregion
#region Numerals
#region General Numerals
@ -3438,6 +3481,18 @@ namespace Microsoft.Z3
}
#endregion
#region Optimization
/// <summary>
/// Create an Optimization context.
/// </summary>
public Optimize MkOptimize()
{
Contract.Ensures(Contract.Result<Optimize>() != null);
return new Optimize(this);
}
#endregion
#region Miscellaneous
/// <summary>
@ -3594,6 +3649,7 @@ namespace Microsoft.Z3
Contract.Invariant(m_Statistics_DRQ != null);
Contract.Invariant(m_Tactic_DRQ != null);
Contract.Invariant(m_Fixedpoint_DRQ != null);
Contract.Invariant(m_Optimize_DRQ != null);
}
readonly private AST.DecRefQueue m_AST_DRQ = new AST.DecRefQueue();
@ -3611,6 +3667,7 @@ namespace Microsoft.Z3
readonly private Statistics.DecRefQueue m_Statistics_DRQ = new Statistics.DecRefQueue();
readonly private Tactic.DecRefQueue m_Tactic_DRQ = new Tactic.DecRefQueue();
readonly private Fixedpoint.DecRefQueue m_Fixedpoint_DRQ = new Fixedpoint.DecRefQueue();
readonly private Optimize.DecRefQueue m_Optimize_DRQ = new Optimize.DecRefQueue();
internal AST.DecRefQueue AST_DRQ { get { Contract.Ensures(Contract.Result<AST.DecRefQueue>() != null); return m_AST_DRQ; } }
internal ASTMap.DecRefQueue ASTMap_DRQ { get { Contract.Ensures(Contract.Result<ASTMap.DecRefQueue>() != null); return m_ASTMap_DRQ; } }
@ -3627,6 +3684,7 @@ namespace Microsoft.Z3
internal Statistics.DecRefQueue Statistics_DRQ { get { Contract.Ensures(Contract.Result<Statistics.DecRefQueue>() != null); return m_Statistics_DRQ; } }
internal Tactic.DecRefQueue Tactic_DRQ { get { Contract.Ensures(Contract.Result<Tactic.DecRefQueue>() != null); return m_Tactic_DRQ; } }
internal Fixedpoint.DecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result<Fixedpoint.DecRefQueue>() != null); return m_Fixedpoint_DRQ; } }
internal Optimize.DecRefQueue Optimize_DRQ { get { Contract.Ensures(Contract.Result<Optimize.DecRefQueue>() != null); return m_Optimize_DRQ; } }
internal long refCount = 0;
@ -3670,6 +3728,7 @@ namespace Microsoft.Z3
Statistics_DRQ.Clear(this);
Tactic_DRQ.Clear(this);
Fixedpoint_DRQ.Clear(this);
Optimize_DRQ.Clear(this);
m_boolSort = null;
m_intSort = null;

View file

@ -0,0 +1,111 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
Deprecated.cs
Abstract:
Expose deprecated features for use from the managed API
those who use them for experiments.
Author:
Christoph Wintersteiger (cwinter) 2012-03-15
Notes:
--*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
namespace Microsoft.Z3
{
/// <summary>
/// The main interaction with Z3 happens via the Context.
/// </summary>
[ContractVerification(true)]
public class Deprecated
{
/// <summary>
/// Creates a backtracking point.
/// </summary>
/// <seealso cref="Pop"/>
public static void Push(Context ctx) {
Native.Z3_push(ctx.nCtx);
}
/// <summary>
/// Backtracks <paramref name="n"/> backtracking points.
/// </summary>
/// <remarks>Note that an exception is thrown if <paramref name="n"/> is not smaller than <c>NumScopes</c></remarks>
/// <seealso cref="Push"/>
public static void Pop(Context ctx, uint n = 1) {
Native.Z3_pop(ctx.nCtx, n);
}
/// <summary>
/// Assert a constraint (or multiple) into the solver.
/// </summary>
public static void Assert(Context ctx, params BoolExpr[] constraints)
{
Contract.Requires(constraints != null);
Contract.Requires(Contract.ForAll(constraints, c => c != null));
ctx.CheckContextMatch(constraints);
foreach (BoolExpr a in constraints)
{
Native.Z3_assert_cnstr(ctx.nCtx, a.NativeObject);
}
}
/// <summary>
/// Checks whether the assertions in the context are consistent or not.
/// </summary>
public static Status Check(Context ctx, List<BoolExpr> core, ref Model model, ref Expr proof, params Expr[] assumptions)
{
Z3_lbool r;
model = null;
proof = null;
if (assumptions == null || assumptions.Length == 0)
r = (Z3_lbool)Native.Z3_check(ctx.nCtx);
else {
IntPtr mdl = IntPtr.Zero, prf = IntPtr.Zero;
uint core_size = 0;
IntPtr[] native_core = new IntPtr[assumptions.Length];
r = (Z3_lbool)Native.Z3_check_assumptions(ctx.nCtx,
(uint)assumptions.Length, AST.ArrayToNative(assumptions),
ref mdl, ref prf, ref core_size, native_core);
for (uint i = 0; i < core_size; i++)
core.Add((BoolExpr)Expr.Create(ctx, native_core[i]));
if (mdl != IntPtr.Zero) {
model = new Model(ctx, mdl);
}
if (prf != IntPtr.Zero) {
proof = Expr.Create(ctx, prf);
}
}
switch (r)
{
case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE;
case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE;
default: return Status.UNKNOWN;
}
}
/// <summary>
/// Retrieves an assignment to atomic propositions for a satisfiable context.
/// </summary>
public static BoolExpr GetAssignment(Context ctx)
{
IntPtr x = Native.Z3_get_context_assignment(ctx.nCtx);
return (BoolExpr)Expr.Create(ctx, x);
}
}
}

View file

@ -304,6 +304,19 @@ namespace Microsoft.Z3
}
/// <summary>
/// Fixedpoint statistics.
/// </summary>
public Statistics Statistics
{
get
{
Contract.Ensures(Contract.Result<Statistics>() != null);
return new Statistics(Context, Native.Z3_fixedpoint_get_statistics(Context.nCtx, NativeObject));
}
}
/// <summary>
/// Parse an SMT-LIB2 file with fixedpoint rules.
/// Add the rules to the current fixedpoint context.
/// Return the set of queries in the file.

View file

@ -19,12 +19,12 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\..\cwinter\bugs\z3bugs\Debug\</OutputPath>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>C:\cwinter\bugs\z3bugs\Debug\Microsoft.Z3.XML</DocumentationFile>
<DocumentationFile>..\Debug\Microsoft.Z3.XML</DocumentationFile>
<CodeContractsEnableRuntimeChecking>False</CodeContractsEnableRuntimeChecking>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
<CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
@ -254,7 +254,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\..\..\cwinter\bugs\z3bugs\Debug\</OutputPath>
<OutputPath>..\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
@ -266,7 +266,7 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<DocumentationFile>C:\cwinter\bugs\z3bugs\Debug\Microsoft.Z3.XML</DocumentationFile>
<DocumentationFile>..\x86\Debug\Microsoft.Z3.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>

296
src/api/dotnet/Optimize.cs Normal file
View file

@ -0,0 +1,296 @@
/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
Optimize.cs
Abstract:
Z3 Managed API: Optimizes
Author:
Nikolaj Bjorner (nbjorner) 2013-12-03
Notes:
--*/
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace Microsoft.Z3
{
/// <summary>
/// Object for managing optimizization context
/// </summary>
[ContractVerification(true)]
public class Optimize : Z3Object
{
/// <summary>
/// A string that describes all available optimize solver parameters.
/// </summary>
public string Help
{
get
{
Contract.Ensures(Contract.Result<string>() != null);
return Native.Z3_optimize_get_help(Context.nCtx, NativeObject);
}
}
/// <summary>
/// Sets the optimize solver parameters.
/// </summary>
public Params Parameters
{
set
{
Contract.Requires(value != null);
Context.CheckContextMatch(value);
Native.Z3_optimize_set_params(Context.nCtx, NativeObject, value.NativeObject);
}
}
/// <summary>
/// Retrieves parameter descriptions for Optimize solver.
/// </summary>
public ParamDescrs ParameterDescriptions
{
get { return new ParamDescrs(Context, Native.Z3_optimize_get_param_descrs(Context.nCtx, NativeObject)); }
}
/// <summary>
/// Assert a constraint (or multiple) into the optimize solver.
/// </summary>
public void Assert(params BoolExpr[] constraints)
{
Contract.Requires(constraints != null);
Contract.Requires(Contract.ForAll(constraints, c => c != null));
Context.CheckContextMatch(constraints);
foreach (BoolExpr a in constraints)
{
Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject);
}
}
/// <summary>
/// Alias for Assert.
/// </summary>
public void Add(params BoolExpr[] constraints)
{
Assert(constraints);
}
/// <summary>
/// Handle to objectives returned by objective functions.
/// </summary>
public class Handle
{
Optimize opt;
uint handle;
internal Handle(Optimize opt, uint h)
{
this.opt = opt;
this.handle = h;
}
/// <summary>
/// Retrieve a lower bound for the objective handle.
/// </summary>
public ArithExpr Lower
{
get { return opt.GetLower(handle); }
}
/// <summary>
/// Retrieve an upper bound for the objective handle.
/// </summary>
public ArithExpr Upper
{
get { return opt.GetUpper(handle); }
}
/// <summary>
/// Retrieve the value of an objective.
/// </summary>
public ArithExpr Value
{
get { return Lower; }
}
}
/// <summary>
/// Assert soft constraint
/// </summary>
/// <remarks>
/// Return an objective which associates with the group of constraints.
/// </remarks>
public Handle AssertSoft(BoolExpr constraint, uint weight, string group)
{
Context.CheckContextMatch(constraint);
Symbol s = Context.MkSymbol(group);
return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject));
}
///
/// <summary>
/// Check satisfiability of asserted constraints.
/// Produce a model that (when the objectives are bounded and
/// don't use strict inequalities) meets the objectives.
/// </summary>
///
public Status Check() {
Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject);
switch (r)
{
case Z3_lbool.Z3_L_TRUE:
return Status.SATISFIABLE;
case Z3_lbool.Z3_L_FALSE:
return Status.UNSATISFIABLE;
default:
return Status.UNKNOWN;
}
}
/// <summary>
/// Creates a backtracking point.
/// </summary>
/// <seealso cref="Pop"/>
public void Push()
{
Native.Z3_optimize_push(Context.nCtx, NativeObject);
}
/// <summary>
/// Backtrack one backtracking point.
/// </summary>
/// <remarks>Note that an exception is thrown if Pop is called without a corresponding <c>Push</c></remarks>
/// <seealso cref="Push"/>
public void Pop()
{
Native.Z3_optimize_pop(Context.nCtx, NativeObject);
}
/// <summary>
/// The model of the last <c>Check</c>.
/// </summary>
/// <remarks>
/// The result is <c>null</c> if <c>Check</c> was not invoked before,
/// if its results was not <c>SATISFIABLE</c>, or if model production is not enabled.
/// </remarks>
public Model Model
{
get
{
IntPtr x = Native.Z3_optimize_get_model(Context.nCtx, NativeObject);
if (x == IntPtr.Zero)
return null;
else
return new Model(Context, x);
}
}
/// <summary>
/// Declare an arithmetical maximization objective.
/// Return a handle to the objective. The handle is used as
/// to retrieve the values of objectives after calling Check.
/// </summary>
public Handle MkMaximize(ArithExpr e)
{
return new Handle(this, Native.Z3_optimize_maximize(Context.nCtx, NativeObject, e.NativeObject));
}
/// <summary>
/// Declare an arithmetical minimization objective.
/// Similar to MkMaximize.
/// </summary>
public Handle MkMinimize(ArithExpr e)
{
return new Handle(this, Native.Z3_optimize_minimize(Context.nCtx, NativeObject, e.NativeObject));
}
/// <summary>
/// Retrieve a lower bound for the objective handle.
/// </summary>
private ArithExpr GetLower(uint index)
{
return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_lower(Context.nCtx, NativeObject, index));
}
/// <summary>
/// Retrieve an upper bound for the objective handle.
/// </summary>
private ArithExpr GetUpper(uint index)
{
return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index));
}
/// <summary>
/// Print the context to a string (SMT-LIB parseable benchmark).
/// </summary>
public override string ToString()
{
return Native.Z3_optimize_to_string(Context.nCtx, NativeObject);
}
/// <summary>
/// Optimize statistics.
/// </summary>
public Statistics Statistics
{
get
{
Contract.Ensures(Contract.Result<Statistics>() != null);
return new Statistics(Context, Native.Z3_optimize_get_statistics(Context.nCtx, NativeObject));
}
}
#region Internal
internal Optimize(Context ctx, IntPtr obj)
: base(ctx, obj)
{
Contract.Requires(ctx != null);
}
internal Optimize(Context ctx)
: base(ctx, Native.Z3_mk_optimize(ctx.nCtx))
{
Contract.Requires(ctx != null);
}
internal class DecRefQueue : IDecRefQueue
{
public override void IncRef(Context ctx, IntPtr obj)
{
Native.Z3_optimize_inc_ref(ctx.nCtx, obj);
}
public override void DecRef(Context ctx, IntPtr obj)
{
Native.Z3_optimize_dec_ref(ctx.nCtx, obj);
}
};
internal override void IncRef(IntPtr o)
{
Context.Optimize_DRQ.IncAndClear(Context, o);
base.IncRef(o);
}
internal override void DecRef(IntPtr o)
{
Context.Optimize_DRQ.Add(o);
base.DecRef(o);
}
#endregion
}
}

View file

@ -79,6 +79,7 @@ namespace Microsoft.Z3
Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, value.NativeObject);
}
/// <summary>
/// Adds a parameter setting.
/// </summary>
@ -118,6 +119,7 @@ namespace Microsoft.Z3
/// </summary>
public void Add(string name, string value)
{
Contract.Requires(name != null);
Contract.Requires(value != null);
Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject);

View file

@ -0,0 +1,9 @@
The default Z3 bindings for .NET are built for the .NET framework version 4.
Should the need arise, it is also possible to build them for .NET 3.5; the
instructions are as follows:
In the project properties of Microsoft.Z3.csproj:
- Under 'Application': Change Target framework to .NET Framework 3.5
- Under 'Build': Add FRAMEWORK_LT_4 to the condidional compilation symbols
- Remove the reference to System.Numerics
- Install the NuGet Package "Microsoft Code Contracts for Net3.5"

View file

@ -375,6 +375,23 @@ public class Context extends IDisposable
return mkDatatypeSorts(MkSymbols(names), c);
}
/**
* Update a datatype field at expression t with value v.
* The function performs a record update at t. The field
* that is passed in as argument is updated with value v,
* the remainig fields of t are unchanged.
**/
public Expr MkUpdateField(FuncDecl field, Expr t, Expr v)
throws Z3Exception
{
return Expr.create
(this,
Native.datatypeUpdateField
(nCtx(), field.getNativeObject(),
t.getNativeObject(), v.getNativeObject()));
}
/**
* Creates a new function declaration.
**/

View file

@ -319,6 +319,18 @@ public class Fixedpoint extends Z3Object
return res;
}
/**
* Fixedpoint statistics.
*
* @throws Z3Exception
**/
public Statistics getStatistics() throws Z3Exception
{
return new Statistics(getContext(), Native.fixedpointGetStatistics(
getContext().nCtx(), getNativeObject()));
}
Fixedpoint(Context ctx, long obj) throws Z3Exception
{
super(ctx, obj);

View file

@ -298,9 +298,8 @@ class AstRef(Z3PPObject):
return self.ast
def get_id(self):
"""Return unique identifier for object. It can be used for hash-tables and maps."""
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
"""Return unique identifier for object. It can be used for hash-tables and maps."""
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def ctx_ref(self):
"""Return a reference to the C context where this AST node is stored."""
@ -453,8 +452,7 @@ class SortRef(AstRef):
return Z3_sort_to_ast(self.ctx_ref(), self.ast)
def get_id(self):
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def kind(self):
"""Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts.
@ -555,6 +553,8 @@ def _to_sort_ref(s, ctx):
return ArraySortRef(s, ctx)
elif k == Z3_DATATYPE_SORT:
return DatatypeSortRef(s, ctx)
elif k == Z3_FINITE_DOMAIN_SORT:
return FiniteDomainSortRef(s, ctx)
return SortRef(s, ctx)
def _sort(ctx, a):
@ -595,7 +595,7 @@ class FuncDeclRef(AstRef):
return Z3_func_decl_to_ast(self.ctx_ref(), self.ast)
def get_id(self):
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def as_func_decl(self):
return self.ast
@ -743,7 +743,7 @@ class ExprRef(AstRef):
return self.ast
def get_id(self):
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def sort(self):
"""Return the sort of expression `self`.
@ -1540,7 +1540,7 @@ class PatternRef(ExprRef):
return Z3_pattern_to_ast(self.ctx_ref(), self.ast)
def get_id(self):
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def is_pattern(a):
"""Return `True` if `a` is a Z3 pattern (hint for quantifier instantiation.
@ -1605,7 +1605,7 @@ class QuantifierRef(BoolRef):
return self.ast
def get_id(self):
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
def sort(self):
"""Return the Boolean sort."""
@ -6033,22 +6033,20 @@ class Solver(Z3PPObject):
return Z3_solver_to_string(self.ctx.ref(), self.solver)
def to_smt2(self):
"""return SMTLIB2 formatted benchmark for solver's assertions"""
es = self.assertions()
sz = len(es)
sz1 = sz
if sz1 > 0:
sz1 -= 1
v = (Ast * sz1)()
for i in range(sz1):
v[i] = es[i].as_ast()
if sz > 0:
e = es[sz1].as_ast()
else:
e = BoolVal(True, self.ctx).as_ast()
return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e)
"""return SMTLIB2 formatted benchmark for solver's assertions"""
es = self.assertions()
sz = len(es)
sz1 = sz
if sz1 > 0:
sz1 -= 1
v = (Ast * sz1)()
for i in range(sz1):
v[i] = es[i].as_ast()
if sz > 0:
e = es[sz1].as_ast()
else:
e = BoolVal(True, self.ctx).as_ast()
return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e)
def SolverFor(logic, ctx=None):
"""Create a solver customized for the given logic.
@ -6166,7 +6164,7 @@ class Fixedpoint(Z3PPObject):
Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, head.as_ast(), name)
else:
body = _get_args(body)
f = self.abstract(Implies(And(body),head))
f = self.abstract(Implies(And(body, self.ctx),head))
Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name)
def rule(self, head, body = None, name = None):
@ -6183,7 +6181,7 @@ class Fixedpoint(Z3PPObject):
"""
query = _get_args(query)
sz = len(query)
if sz >= 1 and isinstance(query[0], FuncDecl):
if sz >= 1 and isinstance(query[0], FuncDeclRef):
_decls = (FuncDecl * sz)()
i = 0
for q in query:
@ -6194,7 +6192,7 @@ class Fixedpoint(Z3PPObject):
if sz == 1:
query = query[0]
else:
query = And(query)
query = And(query, self.ctx)
query = self.abstract(query, False)
r = Z3_fixedpoint_query(self.ctx.ref(), self.fixedpoint, query.as_ast())
return CheckSatResult(r)
@ -6213,7 +6211,7 @@ class Fixedpoint(Z3PPObject):
name = ""
name = to_symbol(name, self.ctx)
body = _get_args(body)
f = self.abstract(Implies(And(body),head))
f = self.abstract(Implies(And(body, self.ctx),head))
Z3_fixedpoint_update_rule(self.ctx.ref(), self.fixedpoint, f.as_ast(), name)
def get_answer(self):
@ -6310,6 +6308,166 @@ class Fixedpoint(Z3PPObject):
else:
return Exists(self.vars, fml)
#########################################
#
# Finite domain sorts
#
#########################################
class FiniteDomainSortRef(SortRef):
"""Finite domain sort."""
def size(self):
"""Return the size of the finite domain sort"""
r = (ctype.c_ulonglong * 1)()
if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast(), r):
return r[0]
else:
raise Z3Exception("Failed to retrieve finite domain sort size")
def FiniteDomainSort(name, sz, ctx=None):
"""Create a named finite domain sort of a given size sz"""
ctx = _get_ctx(ctx)
return FiniteDomainSortRef(Z3_mk_finite_domain_sort(ctx.ref(), name, sz), ctx)
#########################################
#
# Optimize
#
#########################################
class OptimizeObjective:
def __init__(self, opt, value, is_max):
self._opt = opt
self._value = value
self._is_max = is_max
def lower(self):
opt = self._opt
return _to_expr_ref(Z3_optimize_get_lower(opt.ctx.ref(), opt.optimize, self._value), opt.ctx)
def upper(self):
opt = self._opt
return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx)
def value(self):
if self._is_max:
return self.upper()
else:
return self.lower()
class Optimize(Z3PPObject):
"""Optimize API provides methods for solving using objective functions and weighted soft constraints"""
def __init__(self, ctx=None):
self.ctx = _get_ctx(ctx)
self.optimize = Z3_mk_optimize(self.ctx.ref())
Z3_optimize_inc_ref(self.ctx.ref(), self.optimize)
def __del__(self):
if self.optimize != None:
Z3_optimize_dec_ref(self.ctx.ref(), self.optimize)
def set(self, *args, **keys):
"""Set a configuration option. The method `help()` return a string containing all available options.
"""
p = args2params(args, keys, self.ctx)
Z3_optimize_set_params(self.ctx.ref(), self.optimize, p.params)
def help(self):
"""Display a string describing all available options."""
print(Z3_optimize_get_help(self.ctx.ref(), self.optimize))
def param_descrs(self):
"""Return the parameter description set."""
return ParamDescrsRef(Z3_optimize_get_param_descrs(self.ctx.ref(), self.optimize), self.ctx)
def assert_exprs(self, *args):
"""Assert constraints as background axioms for the optimize solver."""
args = _get_args(args)
for arg in args:
if isinstance(arg, Goal) or isinstance(arg, AstVector):
for f in arg:
Z3_optimize_assert(self.ctx.ref(), self.optimize, f.as_ast())
else:
Z3_optimize_assert(self.ctx.ref(), self.optimize, arg.as_ast())
def add(self, *args):
"""Assert constraints as background axioms for the optimize solver. Alias for assert_expr."""
self.assert_exprs(*args)
def add_soft(self, arg, weight = "1", id = None):
"""Add soft constraint with optional weight and optional identifier.
If no weight is supplied, then the penalty for violating the soft constraint
is 1.
Soft constraints are grouped by identifiers. Soft constraints that are
added without identifiers are grouped by default.
"""
if _is_int(weight):
weight = "%d" % weight
if not isinstance(weight, str):
raise Z3Exception("weight should be a string or an integer")
if id == None:
id = ""
id = to_symbol(id, self.ctx)
v = Z3_optimize_assert_soft(self.ctx.ref(), self.optimize, arg.as_ast(), weight, id)
return OptimizeObjective(self, v, False)
def maximize(self, arg):
"""Add objective function to maximize."""
return OptimizeObjective(self, Z3_optimize_maximize(self.ctx.ref(), self.optimize, arg.as_ast()), True)
def minimize(self, arg):
"""Add objective function to minimize."""
return OptimizeObjective(self, Z3_optimize_minimize(self.ctx.ref(), self.optimize, arg.as_ast()), False)
def push(self):
"""create a backtracking point for added rules, facts and assertions"""
Z3_optimize_push(self.ctx.ref(), self.optimize)
def pop(self):
"""restore to previously created backtracking point"""
Z3_optimize_pop(self.ctx.ref(), self.optimize)
def check(self):
"""Check satisfiability while optimizing objective functions."""
return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize))
def model(self):
"""Return a model for the last check()."""
try:
return ModelRef(Z3_optimize_get_model(self.ctx.ref(), self.optimize), self.ctx)
except Z3Exception:
raise Z3Exception("model is not available")
def lower(self, obj):
if not isinstance(obj, OptimizeObjective):
raise Z3Exception("Expecting objective handle returned by maximize/minimize")
return obj.lower()
def upper(self, obj):
if not isinstance(obj, OptimizeObjective):
raise Z3Exception("Expecting objective handle returned by maximize/minimize")
return obj.upper()
def __repr__(self):
"""Return a formatted string with all added rules and constraints."""
return self.sexpr()
def sexpr(self):
"""Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format.
"""
return Z3_optimize_to_string(self.ctx.ref(), self.optimize)
def statistics(self):
"""Return statistics for the last `query()`.
"""
return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx)
#########################################
#
# ApplyResult

View file

@ -842,6 +842,8 @@ class Formatter:
return self.pp_seq(a.assertions(), 0, [])
elif isinstance(a, z3.Fixedpoint):
return a.sexpr()
elif isinstance(a, z3.Optimize):
return a.sexpr()
elif isinstance(a, z3.ApplyResult):
return self.pp_seq_seq(a, 0, [])
elif isinstance(a, z3.ModelRef):

View file

@ -78,6 +78,10 @@ class FixedpointObj(ctypes.c_void_p):
def __init__(self, fixedpoint): self._as_parameter_ = fixedpoint
def from_param(obj): return obj
class OptimizeObj(ctypes.c_void_p):
def __init__(self, optimize): self._as_parameter_ = optimize
def from_param(obj): return obj
class ModelObj(ctypes.c_void_p):
def __init__(self, model): self._as_parameter_ = model
def from_param(obj): return obj

View file

@ -47,6 +47,7 @@ DEFINE_TYPE(Z3_func_interp);
#define Z3_func_interp_opt Z3_func_interp
DEFINE_TYPE(Z3_func_entry);
DEFINE_TYPE(Z3_fixedpoint);
DEFINE_TYPE(Z3_optimize);
DEFINE_TYPE(Z3_rcf_num);
DEFINE_VOID(Z3_theory_data);
#endif
@ -85,6 +86,7 @@ DEFINE_VOID(Z3_theory_data);
- \c Z3_func_interp: interpretation of a function in a model.
- \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point.
- \c Z3_fixedpoint: context for the recursive predicate solver.
- \c Z3_optimize: context for solving optimization queries.
- \c Z3_ast_vector: vector of \c Z3_ast objects.
- \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects.
- \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers.
@ -875,6 +877,17 @@ typedef enum
- Z3_OP_DT_ACCESSOR: datatype accessor.
- Z3_OP_DT_UPDATE_FIELD: datatype field update.
- Z3_OP_PB_AT_MOST: Cardinality constraint.
E.g., x + y + z <= 2
- Z3_OP_PB_LE: Generalized Pseudo-Boolean cardinality constraint.
Example 2*x + 3*y <= 4
- Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint.
Example 2*x + 3*y + 2*z >= 4
- Z3_OP_UNINTERPRETED: kind used for uninterpreted symbols.
*/
typedef enum {
@ -1055,6 +1068,12 @@ typedef enum {
Z3_OP_DT_CONSTRUCTOR=0x800,
Z3_OP_DT_RECOGNISER,
Z3_OP_DT_ACCESSOR,
Z3_OP_DT_UPDATE_FIELD,
// Pseudo Booleans
Z3_OP_PB_AT_MOST=0x900,
Z3_OP_PB_LE,
Z3_OP_PB_GE,
Z3_OP_UNINTERPRETED
} Z3_decl_kind;
@ -1192,6 +1211,7 @@ typedef enum
def_Type('FUNC_INTERP', 'Z3_func_interp', 'FuncInterpObj')
def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj')
def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj')
def_Type('OPTIMIZE', 'Z3_optimize', 'OptimizeObj')
def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs')
def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj')
*/
@ -3734,6 +3754,28 @@ END_MLAPI_EXCLUDE
Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(
__in Z3_context c, __in Z3_sort t, unsigned idx_c, unsigned idx_a);
/**
\brief Update record field with a value.
This corresponds to the 'with' construct in OCaml.
It has the effect of updating a record field with a given value.
The remaining fields are left unchanged. It is the record
equivalent of an array store (see \sa Z3_mk_store).
If the datatype has more than one constructor, then the update function
behaves as identity if there is a miss-match between the accessor and
constructor. For example ((_ update-field car) nil 1) is nil,
while ((_ update-field car) (cons 2 nil) 1) is (cons 1 nil).
\pre Z3_get_sort_kind(Z3_get_sort(c, t)) == Z3_get_domain(c, field_access, 1) == Z3_DATATYPE_SORT
\pre Z3_get_sort(c, value) == Z3_get_range(c, field_access)
def_API('Z3_datatype_update_field', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_datatype_update_field(
__in Z3_context c, __in Z3_func_decl field_access,
__in Z3_ast t, __in Z3_ast value);
/**
\brief Return arity of relation.
@ -3759,6 +3801,29 @@ END_MLAPI_EXCLUDE
Z3_sort Z3_API Z3_get_relation_column(__in Z3_context c, __in Z3_sort s, unsigned col);
/**
\brief Pseudo-Boolean relations.
Encode p1 + p2 + ... + pn <= k
def_API('Z3_mk_atmost', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT)))
*/
Z3_ast Z3_API Z3_mk_atmost(__in Z3_context c, __in unsigned num_args,
__in_ecount(num_args) Z3_ast const args[], __in unsigned k);
/**
\brief Pseudo-Boolean relations.
Encode k1*p1 + k2*p2 + ... + kn*pn <= k
def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT)))
*/
Z3_ast Z3_API Z3_mk_pble(__in Z3_context c, __in unsigned num_args,
__in_ecount(num_args) Z3_ast const args[], __in_ecount(num_args) int coeffs[],
__in int k);
/**
\mlonly {3 {L Function Declarations}} \endmlonly
*/
@ -3988,6 +4053,12 @@ END_MLAPI_EXCLUDE
/**
\brief Return a unique identifier for \c t.
The identifier is unique up to structural equality. Thus, two ast nodes
created by the same context and having the same children and same function symbols
have the same identifiers. Ast nodes created in the same context, but having
different children or different functions have different identifiers.
Variables and quantifiers are also assigned different identifiers according to
their structure.
\mlonly \remark Implicitly used by [Pervasives.compare] for values of type [ast], [app], [sort], [func_decl], and [pattern]. \endmlonly
def_API('Z3_get_ast_id', UINT, (_in(CONTEXT), _in(AST)))
@ -3996,6 +4067,8 @@ END_MLAPI_EXCLUDE
/**
\brief Return a hash code for the given AST.
The hash code is structural. You can use Z3_get_ast_id interchangably with
this function.
\mlonly \remark Implicitly used by [Hashtbl.hash] for values of type [ast], [app], [sort], [func_decl], and [pattern]. \endmlonly
def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST)))
@ -5904,6 +5977,197 @@ END_MLAPI_EXCLUDE
#endif
#endif
#ifdef CorML4
/**
@name Optimize facilities
*/
/*@{*/
/**
\brief Create a new optimize context.
\conly \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects.
\conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc.
def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), ))
*/
Z3_optimize Z3_API Z3_mk_optimize(__in Z3_context c);
#ifdef Conly
/**
\brief Increment the reference counter of the given optimize context
def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
*/
void Z3_API Z3_optimize_inc_ref(__in Z3_context c,__in Z3_optimize d);
/**
\brief Decrement the reference counter of the given optimize context.
def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
*/
void Z3_API Z3_optimize_dec_ref(__in Z3_context c,__in Z3_optimize d);
#endif
/**
\brief Assert hard constraint to the optimization context.
def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
*/
void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a);
/**
\brief Assert soft constraint to the optimization context.
\param c - context
\param o - optimization context
\param a - formula
\param weight - a positive weight, penalty for violating soft constraint
\param id - optional identifier to group soft constraints
def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL)))
*/
unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id);
/**
\brief Add a maximization constraint.
\param c - context
\param o - optimization context
\param a - arithmetical term
def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
*/
unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t);
/**
\brief Add a minimization constraint.
\param c - context
\param o - optimization context
\param a - arithmetical term
def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
*/
unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t);
/**
\brief Create a backtracking point.
The optimize solver contains a set of rules, added facts and assertions.
The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop.
\sa Z3_optimize_pop
def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
*/
void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d);
/**
\brief Backtrack one level.
\sa Z3_optimize_push
\pre The number of calls to pop cannot exceed calls to push.
def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
*/
void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d);
/**
\brief Check consistency and produce optimal values.
\param c - context
\param o - optimization context
def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o);
/**
\brief Retrieve the model for the last #Z3_optimize_check
The error handler is invoked if a model is not available because
the commands above were not invoked for the given optimization
solver, or if the result was \c Z3_L_FALSE.
def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o);
/**
\brief Set parameters on optimization context.
\param c - context
\param o - optimization context
\param p - parameters
def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS)))
*/
void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p);
/**
\brief Return the parameter description set for the given optimize object.
\param c - context
\param o - optimization context
def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o);
/**
\brief Retrieve lower bound value or approximation for the i'th optimization objective.
\param c - context
\param o - optimization context
\param idx - index of optimization objective
def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT)))
*/
Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx);
/**
\brief Retrieve upper bound value or approximation for the i'th optimization objective.
\param c - context
\param o - optimization context
\param idx - index of optimization objective
def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT)))
*/
Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx);
/**
\brief Print the current context as a string.
\param c - context.
\param o - optimization context.
def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_string Z3_API Z3_optimize_to_string(
__in Z3_context c,
__in Z3_optimize o);
/**
\brief Return a string containing a description of parameters accepted by optimize.
def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_string Z3_API Z3_optimize_get_help(__in Z3_context c, __in Z3_optimize t);
/**
\brief Retrieve statistics information from the last call to #Z3_optimize_check
def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE)))
*/
Z3_stats Z3_API Z3_optimize_get_statistics(__in Z3_context c,__in Z3_optimize d);
#endif
#ifdef CorML4
/*@}*/

View file

@ -22,12 +22,14 @@ Notes:
#include"stream_buffer.h"
#include"symbol.h"
#include"trace.h"
#include<sstream>
void register_z3_replayer_cmds(z3_replayer & in);
void throw_invalid_reference() {
TRACE("z3_replayer", tout << "invalid argument reference\n";);
throw z3_replayer_exception("invalid argument reference");
throw z3_replayer_exception("invalid argument reference1");
}
struct z3_replayer::imp {
@ -44,7 +46,37 @@ struct z3_replayer::imp {
size_t_map<void *> m_heap;
svector<z3_replayer_cmd> m_cmds;
enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY };
enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY };
char const* kind2string(value_kind k) const {
switch (k) {
case INT64: return "int64";
case UINT64: return "uint64";
case DOUBLE: return "double";
case STRING: return "string";
case SYMBOL: return "symbol";
case OBJECT: return "object";
case UINT_ARRAY: return "uint_array";
case INT_ARRAY: return "int_array";
case SYMBOL_ARRAY: return "symbol_array";
case OBJECT_ARRAY: return "object_array";
default: UNREACHABLE(); return "unknown";
}
}
void check_arg(unsigned pos, value_kind k) const {
if (pos >= m_args.size()) {
TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";);
throw z3_replayer_exception("invalid argument reference2");
}
if (m_args[pos].m_kind != k) {
std::stringstream strm;
strm << "expecting " << kind2string(k) << " at position "
<< pos << " but got " << kind2string(m_args[pos].m_kind);
throw z3_replayer_exception(strm.str().c_str());
}
}
struct value {
value_kind m_kind;
@ -68,6 +100,7 @@ struct z3_replayer::imp {
vector<ptr_vector<void> > m_obj_arrays;
vector<svector<Z3_symbol> > m_sym_arrays;
vector<unsigned_vector> m_unsigned_arrays;
vector<svector<int> > m_int_arrays;
imp(z3_replayer & o, std::istream & in):
m_owner(o),
@ -295,6 +328,15 @@ struct z3_replayer::imp {
v.push_back(static_cast<unsigned>(m_args[i].m_uint));
}
}
if (k == INT64) {
aidx = m_int_arrays.size();
nk = INT_ARRAY;
m_int_arrays.push_back(svector<int>());
svector<int> & v = m_int_arrays.back();
for (unsigned i = asz - sz; i < asz; i++) {
v.push_back(static_cast<int>(m_args[i].m_int));
}
}
else if (k == SYMBOL) {
aidx = m_sym_arrays.size();
nk = SYMBOL_ARRAY;
@ -457,8 +499,7 @@ struct z3_replayer::imp {
next(); skip_blank(); read_ptr(); skip_blank(); read_uint64();
unsigned pos = static_cast<unsigned>(m_uint64);
TRACE("z3_replayer", tout << "[" << m_line << "] " << "* " << m_ptr << " " << pos << "\n";);
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
throw_invalid_reference();
check_arg(pos, OBJECT);
m_heap.insert(m_ptr, m_args[pos].m_obj);
break;
}
@ -467,8 +508,7 @@ struct z3_replayer::imp {
// @ obj_id array_pos idx
next(); skip_blank(); read_ptr(); skip_blank(); read_uint64();
unsigned pos = static_cast<unsigned>(m_uint64);
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY)
throw_invalid_reference();
check_arg(pos, OBJECT_ARRAY);
unsigned aidx = static_cast<unsigned>(m_args[pos].m_uint);
ptr_vector<void> & v = m_obj_arrays[aidx];
skip_blank(); read_uint64();
@ -493,70 +533,65 @@ struct z3_replayer::imp {
}
int get_int(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
throw_invalid_reference();
check_arg(pos, INT64);
return static_cast<int>(m_args[pos].m_int);
}
__int64 get_int64(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
throw_invalid_reference();
check_arg(pos, INT64);
return m_args[pos].m_int;
}
unsigned get_uint(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
throw_invalid_reference();
check_arg(pos, UINT64);
return static_cast<unsigned>(m_args[pos].m_uint);
}
__uint64 get_uint64(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
throw_invalid_reference();
check_arg(pos, UINT64);
return m_args[pos].m_uint;
}
double get_double(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != DOUBLE)
throw_invalid_reference();
check_arg(pos, DOUBLE);
return m_args[pos].m_double;
}
Z3_string get_str(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != STRING)
throw_invalid_reference();
check_arg(pos, STRING);
return m_args[pos].m_str;
}
Z3_symbol get_symbol(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL)
throw_invalid_reference();
check_arg(pos, SYMBOL);
return reinterpret_cast<Z3_symbol>(const_cast<char*>(m_args[pos].m_str));
}
void * get_obj(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
throw_invalid_reference();
check_arg(pos, OBJECT);
return m_args[pos].m_obj;
}
unsigned * get_uint_array(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != UINT_ARRAY)
throw_invalid_reference();
check_arg(pos, UINT_ARRAY);
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
return m_unsigned_arrays[idx].c_ptr();
}
int * get_int_array(unsigned pos) const {
check_arg(pos, INT_ARRAY);
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
return m_int_arrays[idx].c_ptr();
}
Z3_symbol * get_symbol_array(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL_ARRAY)
throw_invalid_reference();
check_arg(pos, SYMBOL_ARRAY);
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
return m_sym_arrays[idx].c_ptr();
}
void ** get_obj_array(unsigned pos) const {
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY)
throw_invalid_reference();
check_arg(pos, OBJECT_ARRAY);
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
ptr_vector<void> const & v = m_obj_arrays[idx];
TRACE("z3_replayer_bug", tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n";
@ -565,38 +600,32 @@ struct z3_replayer::imp {
}
int * get_int_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
throw_invalid_reference();
check_arg(pos, INT64);
return reinterpret_cast<int*>(&(m_args[pos].m_int));
}
__int64 * get_int64_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
throw_invalid_reference();
check_arg(pos, INT64);
return &(m_args[pos].m_int);
}
unsigned * get_uint_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
throw_invalid_reference();
check_arg(pos, UINT64);
return reinterpret_cast<unsigned*>(&(m_args[pos].m_uint));
}
__uint64 * get_uint64_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
throw_invalid_reference();
check_arg(pos, UINT64);
return &(m_args[pos].m_uint);
}
Z3_string * get_str_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != STRING)
throw_invalid_reference();
check_arg(pos, STRING);
return &(m_args[pos].m_str);
}
void ** get_obj_addr(unsigned pos) {
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
throw_invalid_reference();
check_arg(pos, OBJECT);
return &(m_args[pos].m_obj);
}
@ -615,6 +644,7 @@ struct z3_replayer::imp {
m_obj_arrays.reset();
m_sym_arrays.reset();
m_unsigned_arrays.reset();
m_int_arrays.reset();
}
@ -673,6 +703,10 @@ unsigned * z3_replayer::get_uint_array(unsigned pos) const {
return m_imp->get_uint_array(pos);
}
int * z3_replayer::get_int_array(unsigned pos) const {
return m_imp->get_int_array(pos);
}
Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const {
return m_imp->get_symbol_array(pos);
}

View file

@ -49,6 +49,7 @@ public:
void * get_obj(unsigned pos) const;
unsigned * get_uint_array(unsigned pos) const;
int * get_int_array(unsigned pos) const;
Z3_symbol * get_symbol_array(unsigned pos) const;
void ** get_obj_array(unsigned pos) const;

View file

@ -417,6 +417,7 @@ inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) {
app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) {
if (is_int && !val.is_int()) {
SASSERT(false);
m_manager->raise_exception("invalid rational value passed as an integer");
}
if (val.is_unsigned()) {

View file

@ -1013,6 +1013,17 @@ func_decl * basic_decl_plugin::mk_ite_decl(sort * s) {
return m_ite_decls[id];
}
sort* basic_decl_plugin::join(sort* s1, sort* s2) {
if (s1 == s2) return s1;
if (s1->get_family_id() == m_manager->m_arith_family_id &&
s2->get_family_id() == m_manager->m_arith_family_id) {
if (s1->get_decl_kind() == REAL_SORT) {
return s1;
}
}
return s2;
}
func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
switch (static_cast<basic_op_kind>(k)) {
@ -1025,10 +1036,10 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
case OP_IFF: return m_iff_decl;
case OP_IMPLIES: return m_implies_decl;
case OP_XOR: return m_xor_decl;
case OP_ITE: return arity == 3 ? mk_ite_decl(domain[1]) : 0;
case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : 0;
// eq and oeq must have at least two arguments, they can have more since they are chainable
case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, domain[0], m_eq_decls) : 0;
case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, domain[0], m_oeq_decls) : 0;
case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(domain[0],domain[1]), m_eq_decls) : 0;
case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(domain[0],domain[1]), m_oeq_decls) : 0;
case OP_DISTINCT: {
func_decl_info info(m_family_id, OP_DISTINCT);
info.set_pairwise();
@ -1061,10 +1072,12 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
case OP_IFF: return m_iff_decl;
case OP_IMPLIES: return m_implies_decl;
case OP_XOR: return m_xor_decl;
case OP_ITE: return num_args == 3 ? mk_ite_decl(m_manager->get_sort(args[1])): 0;
case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): 0;
// eq and oeq must have at least two arguments, they can have more since they are chainable
case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, m_manager->get_sort(args[0]), m_eq_decls) : 0;
case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, m_manager->get_sort(args[0]), m_oeq_decls) : 0;
case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(m_manager->get_sort(args[0]),
m_manager->get_sort(args[1])), m_eq_decls) : 0;
case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(m_manager->get_sort(args[0]),
m_manager->get_sort(args[1])), m_oeq_decls) : 0;
case OP_DISTINCT:
return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range);
default:
@ -2058,6 +2071,8 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
return r;
}
func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
sort * const * domain, sort * range) {
func_decl_info info(null_family_id, null_decl_kind);

View file

@ -1100,6 +1100,7 @@ protected:
virtual void set_manager(ast_manager * m, family_id id);
func_decl * mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector<func_decl> & cache);
func_decl * mk_ite_decl(sort * s);
sort* join(sort* s1, sort* s2);
public:
basic_decl_plugin();
@ -1378,7 +1379,7 @@ enum proof_gen_mode {
// -----------------------------------
class ast_manager {
protected:
friend class basic_decl_plugin;
protected:
struct config {
typedef ast_manager value_manager;
@ -2005,6 +2006,7 @@ public:
app * mk_false() { return m_false; }
app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); }
func_decl* mk_and_decl() {
sort* domain[2] = { m_bool_sort, m_bool_sort };
return mk_func_decl(m_basic_family_id, OP_AND, 0, 0, 2, domain);

View file

@ -77,6 +77,8 @@ bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const {
for (i = 0; i < num; i++) {
if (f->get_parameter(i).is_int())
continue;
if (f->get_parameter(i).is_rational())
continue;
if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))
continue;
break;
@ -105,9 +107,13 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) {
ptr_buffer<format> fs;
fs.push_back(fname);
for (unsigned i = 0; i < num; i++) {
SASSERT(f->get_parameter(i).is_int() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
SASSERT(f->get_parameter(i).is_int() ||
f->get_parameter(i).is_rational() ||
(f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
if (f->get_parameter(i).is_int())
fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int()));
else if (f->get_parameter(i).is_rational())
fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str()));
else
fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast())));
}
@ -1126,6 +1132,26 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) {
return out;
}
std::ostream& operator<<(std::ostream& out, expr_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager());
}
std::ostream& operator<<(std::ostream& out, app_ref const& e) {
return out << mk_ismt2_pp(e.get(), e.get_manager());
}
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i)
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
return out;
}
std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) {
for (unsigned i = 0; i < e.size(); ++i)
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
return out;
}
#ifdef Z3DEBUG
void pp(expr const * n, ast_manager & m) {
std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl;

View file

@ -110,4 +110,10 @@ struct mk_ismt2_pp {
std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p);
std::ostream& operator<<(std::ostream& out, expr_ref const& e);
std::ostream& operator<<(std::ostream& out, app_ref const& e);
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e);
std::ostream& operator<<(std::ostream& out, app_ref_vector const& e);
#endif

View file

@ -1058,7 +1058,8 @@ void ast_smt_pp::display_ast_smt2(std::ostream& strm, ast* a, unsigned indent, u
void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
ptr_vector<quantifier> ql;
decl_collector decls(m_manager);
ast_manager& m = m_manager;
decl_collector decls(m);
smt_renaming rn;
for (unsigned i = 0; i < m_assumptions.size(); ++i) {
@ -1069,7 +1070,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
}
decls.visit(n);
if (m_manager.is_proof(n)) {
if (m.is_proof(n)) {
strm << "(";
}
if (m_benchmark_name != symbol::null) {
@ -1078,7 +1079,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
if (m_source_info != symbol::null && m_source_info != symbol("")) {
strm << "; :source { " << m_source_info << " }\n";
}
if (m_manager.is_bool(n)) {
if (m.is_bool(n)) {
strm << "(set-info :status " << m_status << ")\n";
}
if (m_category != symbol::null && m_category != symbol("")) {
@ -1095,7 +1096,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
for (unsigned i = 0; i < decls.get_num_sorts(); ++i) {
sort* s = decls.get_sorts()[i];
if (!(*m_is_declared)(s)) {
smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
p.pp_sort_decl(sort_mark, s);
}
}
@ -1103,7 +1104,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
for (unsigned i = 0; i < decls.get_num_decls(); ++i) {
func_decl* d = decls.get_func_decls()[i];
if (!(*m_is_declared)(d)) {
smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
p(d);
strm << "\n";
}
@ -1112,34 +1113,36 @@ 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_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
p(d);
strm << "\n";
}
}
for (unsigned i = 0; i < m_assumptions.size(); ++i) {
strm << "(assert\n";
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
p(m_assumptions[i].get());
strm << ")\n";
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1);
strm << "(assert\n ";
p(m_assumptions[i].get());
strm << ")\n";
}
for (unsigned i = 0; i < m_assumptions_star.size(); ++i) {
strm << "(assert\n";
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
p(m_assumptions_star[i].get());
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1);
strm << "(assert\n ";
p(m_assumptions_star[i].get());
strm << ")\n";
}
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
if (m_manager.is_bool(n)) {
strm << "(assert\n";
p(n);
strm << ")\n";
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0);
if (m.is_bool(n)) {
if (!m.is_true(n)) {
strm << "(assert\n ";
p(n);
strm << ")\n";
}
strm << "(check-sat)\n";
}
else if (m_manager.is_proof(n)) {
else if (m.is_proof(n)) {
strm << "(proof\n";
p(n);
strm << "))\n";

76
src/ast/ast_trail.h Normal file
View file

@ -0,0 +1,76 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
ast_trail.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-06-02.
Revision History:
Extracted AST specific features from trail.h
nbjorner 2014-9-28
--*/
#ifndef _AST_TRAIL_H_
#define _AST_TRAIL_H_
#include"ast.h"
#include"trail.h"
template<typename S, typename T>
class ast2ast_trailmap {
ref_vector<S, ast_manager> m_domain;
ref_vector<T, ast_manager> m_range;
obj_map<S, T*> m_map;
public:
ast2ast_trailmap(ast_manager& m):
m_domain(m),
m_range(m),
m_map()
{}
bool find(S* s, T*& t) {
return m_map.find(s,t);
}
void insert(S* s, T* t) {
SASSERT(!m_map.contains(s));
m_domain.push_back(s);
m_range.push_back(t);
m_map.insert(s,t);
}
void pop() {
SASSERT(!m_domain.empty());
m_map.remove(m_domain.back());
m_domain.pop_back();
m_range.pop_back();
}
};
template<typename Ctx, typename S, typename T>
class ast2ast_trail : public trail<Ctx> {
ast2ast_trailmap<S,T>& m_map;
public:
ast2ast_trail(ast2ast_trailmap<S,T>& m, S* s, T* t) :
m_map(m) {
m.insert(s,t);
}
virtual void undo(Ctx& ctx) {
m_map.pop();
}
};
#endif /* _AST_TRAIL_H_ */

View file

@ -422,8 +422,55 @@ static sort * get_type(ast_manager & m, family_id datatype_fid, sort * source_da
}
}
func_decl * datatype_decl_plugin::mk_update_field(
unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
decl_kind k = OP_DT_UPDATE_FIELD;
ast_manager& m = *m_manager;
if (num_parameters != 1 || !parameters[0].is_ast()) {
m.raise_exception("invalid parameters for datatype field update");
return 0;
}
if (arity != 2) {
m.raise_exception("invalid number of arguments for datatype field update");
return 0;
}
func_decl* acc = 0;
if (is_func_decl(parameters[0].get_ast())) {
acc = to_func_decl(parameters[0].get_ast());
}
if (acc && !get_util().is_accessor(acc)) {
acc = 0;
}
if (!acc) {
m.raise_exception("datatype field update requires a datatype accessor as the second argument");
return 0;
}
sort* dom = acc->get_domain(0);
sort* rng = acc->get_range();
if (dom != domain[0]) {
m.raise_exception("first argument to field update should be a data-type");
return 0;
}
if (rng != domain[1]) {
std::ostringstream buffer;
buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m)
<< " instead of " << mk_ismt2_pp(domain[1], m);
m.raise_exception(buffer.str().c_str());
return 0;
}
range = domain[0];
func_decl_info info(m_family_id, k, num_parameters, parameters);
return m.mk_func_decl(symbol("update_field"), arity, domain, range, info);
}
func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
if (k == OP_DT_UPDATE_FIELD) {
return mk_update_field(num_parameters, parameters, arity, domain, range);
}
if (num_parameters < 2 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) {
m_manager->raise_exception("invalid parameters for datatype operator");
return 0;
@ -521,6 +568,9 @@ func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_paramet
return m_manager->mk_func_decl(a_name, arity, domain, a_type, info);
}
break;
case OP_DT_UPDATE_FIELD:
UNREACHABLE();
return 0;
default:
m_manager->raise_exception("invalid datatype operator kind");
return 0;
@ -672,6 +722,13 @@ bool datatype_decl_plugin::is_value(app * e) const {
return true;
}
void datatype_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
if (logic == symbol::null) {
op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
}
}
datatype_util::datatype_util(ast_manager & m):
m_manager(m),
m_family_id(m.mk_family_id("datatype")),
@ -919,9 +976,9 @@ void datatype_util::display_datatype(sort *s0, std::ostream& strm) {
todo.push_back(s0);
mark.mark(s0, true);
while (!todo.empty()) {
sort* s = todo.back();
sort* s = todo.back();
todo.pop_back();
strm << s->get_name() << " =\n";
strm << s->get_name() << " =\n";
ptr_vector<func_decl> const * cnstrs = get_datatype_constructors(s);
for (unsigned i = 0; i < cnstrs->size(); ++i) {
@ -931,14 +988,14 @@ void datatype_util::display_datatype(sort *s0, std::ostream& strm) {
ptr_vector<func_decl> const * accs = get_constructor_accessors(cns);
for (unsigned j = 0; j < accs->size(); ++j) {
func_decl* acc = (*accs)[j];
sort* s1 = acc->get_range();
sort* s1 = acc->get_range();
strm << "(" << acc->get_name() << ": " << s1->get_name() << ") ";
if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) {
mark.mark(s1, true);
todo.push_back(s1);
}
}
strm << "\n";
strm << "\n";
}
}

View file

@ -32,6 +32,7 @@ enum datatype_op_kind {
OP_DT_CONSTRUCTOR,
OP_DT_RECOGNISER,
OP_DT_ACCESSOR,
OP_DT_UPDATE_FIELD,
LAST_DT_OP
};
@ -149,8 +150,14 @@ public:
virtual bool is_unique_value(app * e) const { return is_value(e); }
virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
private:
bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
func_decl * mk_update_field(
unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
};
class datatype_util {
@ -181,9 +188,11 @@ public:
bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
unsigned get_datatype_num_constructors(sort * ty) { return get_datatype_constructors(ty)->size(); }
unsigned get_constructor_idx(func_decl * f) const { SASSERT(is_constructor(f)); return f->get_parameter(1).get_int(); }

View file

@ -44,6 +44,10 @@ public:
void erase(expr * k);
void reset();
void flush();
void set_store_proofs(bool f) {
if (m_store_proofs != f) flush();
m_store_proofs = f;
}
};
#endif

View file

@ -2155,15 +2155,15 @@ void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * arg
SASSERT(f->get_num_parameters() == 1);
SASSERT(f->get_parameter(0).is_int());
unsigned ebits = m_util.get_ebits(f->get_range());
unsigned sbits = m_util.get_sbits(f->get_range());
int width = f->get_parameter(0).get_int();
//unsigned ebits = m_util.get_ebits(f->get_range());
//unsigned sbits = m_util.get_sbits(f->get_range());
//int width = f->get_parameter(0).get_int();
expr * rm = args[0];
expr * x = args[1];
//expr * rm = args[0];
//expr * x = args[1];
expr * sgn, *s, *e;
split(x, sgn, s, e);
//expr * sgn, *s, *e;
//split(x, sgn, s, e);
NOT_IMPLEMENTED_YET();
}
@ -2173,15 +2173,15 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg
SASSERT(f->get_num_parameters() == 1);
SASSERT(f->get_parameter(0).is_int());
unsigned ebits = m_util.get_ebits(f->get_range());
unsigned sbits = m_util.get_sbits(f->get_range());
int width = f->get_parameter(0).get_int();
//unsigned ebits = m_util.get_ebits(f->get_range());
//unsigned sbits = m_util.get_sbits(f->get_range());
//int width = f->get_parameter(0).get_int();
expr * rm = args[0];
expr * x = args[1];
//expr * rm = args[0];
//expr * x = args[1];
expr * sgn, *s, *e;
split(x, sgn, s, e);
//expr * sgn, *s, *e;
//split(x, sgn, s, e);
NOT_IMPLEMENTED_YET();
}
@ -2189,15 +2189,15 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg
void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
SASSERT(num == 1);
unsigned ebits = m_util.get_ebits(f->get_range());
unsigned sbits = m_util.get_sbits(f->get_range());
int width = f->get_parameter(0).get_int();
//unsigned ebits = m_util.get_ebits(f->get_range());
//unsigned sbits = m_util.get_sbits(f->get_range());
//int width = f->get_parameter(0).get_int();
expr * rm = args[0];
expr * x = args[1];
//expr * rm = args[0];
//expr * x = args[1];
expr * sgn, *s, *e;
split(x, sgn, s, e);
//expr * sgn, *s, *e;
//split(x, sgn, s, e);
NOT_IMPLEMENTED_YET();
}

View file

@ -255,9 +255,9 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref
app * n = to_app(_n);
quantifier * q = 0;
func_decl * d = n->get_decl();
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m_manager) << "\nd:\n" << d->get_name() << "\n";);
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";);
if (m_macro_manager.m_decl2macro.find(d, q)) {
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m_manager) << "\n";);
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";);
app * head = 0;
expr * def = 0;
m_macro_manager.get_head_def(q, d, head, def);
@ -272,17 +272,17 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref
SASSERT(subst_args[nidx] == 0);
subst_args[nidx] = n->get_arg(i);
}
var_subst s(m_manager);
var_subst s(m);
s(def, num, subst_args.c_ptr(), r);
if (m_manager.proofs_enabled()) {
expr_ref instance(m_manager);
if (m.proofs_enabled()) {
expr_ref instance(m);
s(q->get_expr(), num, subst_args.c_ptr(), instance);
proof * qi_pr = m_manager.mk_quant_inst(m_manager.mk_or(m_manager.mk_not(q), instance), num, subst_args.c_ptr());
proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr());
proof * q_pr = 0;
m_macro_manager.m_decl2macro_pr.find(d, q_pr);
SASSERT(q_pr != 0);
proof * prs[2] = { qi_pr, q_pr };
p = m_manager.mk_unit_resolution(2, prs);
p = m.mk_unit_resolution(2, prs);
}
else {
p = 0;

View file

@ -116,7 +116,7 @@ void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) {
n = e.m_node;
unsigned delta = e.m_delta;
TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";);
TRACE("collect_info", tout << mk_pp(n, m_manager) << "\n";);
TRACE("collect_info", tout << mk_pp(n, m) << "\n";);
if (visit_children(n, delta)) {
m_todo.pop_back();
save_candidate(n, delta);
@ -170,9 +170,9 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
free_vars.insert(idx);
info * i = 0;
if (delta == 0)
i = alloc(info, m_manager, n, free_vars, 1);
i = alloc(info, m, n, free_vars, 1);
else
i = alloc(info, m_manager, m_manager.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
save(n, delta, i);
}
else {
@ -189,7 +189,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
}
if (c->get_num_args() == 0) {
save(n, delta, alloc(info, m_manager, n, uint_set(), 1));
save(n, delta, alloc(info, m, n, uint_set(), 1));
return;
}
@ -219,10 +219,10 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
app * new_node = 0;
if (changed)
new_node = m_manager.mk_app(decl, buffer.size(), buffer.c_ptr());
new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr());
else
new_node = to_app(n);
save(n, delta, alloc(info, m_manager, new_node, free_vars, size));
save(n, delta, alloc(info, m, new_node, free_vars, size));
// Remark: arithmetic patterns are only used if they are nested inside other terms.
// That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern
// if arithmetic is not in the forbidden list.
@ -235,7 +235,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
decl_kind k = c->get_decl_kind();
if (!free_vars.empty() &&
(fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m_manager) << "\n";);
TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";);
m_owner.add_candidate(new_node, free_vars, size);
}
return;
@ -338,7 +338,7 @@ bool pattern_inference::contains_subpattern::operator()(expr * n) {
uint_set const & s2 = e->get_data().m_value.m_free_vars;
SASSERT(s2.subset_of(s1));
if (s1 == s2) {
TRACE("pattern_inference", tout << mk_pp(n, m_owner.m_manager) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m_manager) << "\n";);
TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";);
return true;
}
}
@ -411,7 +411,7 @@ void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candid
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
info const & i = e->get_data().m_value;
if (i.m_free_vars.num_elems() == m_num_bindings) {
app * new_pattern = m_manager.mk_pattern(candidate);
app * new_pattern = m.mk_pattern(candidate);
result.push_back(new_pattern);
}
else {
@ -435,7 +435,7 @@ void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns,
for (unsigned j = 0; j < m_pre_patterns.size(); j++) {
pre_pattern * curr = m_pre_patterns[j];
if (curr->m_free_vars.num_elems() == m_num_bindings) {
app * new_pattern = m_manager.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
result.push_back(new_pattern);
if (result.size() >= max_num_patterns)
return;
@ -489,7 +489,7 @@ bool pattern_inference::is_forbidden(app * n) const {
// occur outside of the quantifier. That is, Z3 will never match this kind of
// pattern.
if (m_params.m_pi_avoid_skolems && decl->is_skolem()) {
CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m_manager) << "\n";);
CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";);
return true;
}
if (is_forbidden(decl))
@ -509,8 +509,8 @@ bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patte
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
info const & i = e->get_data().m_value;
if (i.m_free_vars.num_elems() == m_num_bindings) {
TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m_manager) << "\n";);
app * p = m_manager.mk_pattern(candidate);
TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";);
app * p = m.mk_pattern(candidate);
result.push_back(p);
found = true;
}
@ -531,11 +531,11 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
m_collect(n, num_bindings);
TRACE("pattern_inference",
tout << mk_pp(n, m_manager);
tout << mk_pp(n, m);
tout << "\ncandidates:\n";
unsigned num = m_candidates.size();
for (unsigned i = 0; i < num; i++) {
tout << mk_pp(m_candidates.get(i), m_manager) << "\n";
tout << mk_pp(m_candidates.get(i), m) << "\n";
});
if (!m_candidates.empty()) {
@ -543,7 +543,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
filter_looping_patterns(m_tmp1);
TRACE("pattern_inference",
tout << "candidates after removing looping-patterns:\n";
dump_app_vector(tout, m_tmp1, m_manager););
dump_app_vector(tout, m_tmp1, m););
SASSERT(!m_tmp1.empty());
if (!has_preferred_patterns(m_tmp1, result)) {
// continue if there are no preferred patterns
@ -552,7 +552,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
SASSERT(!m_tmp2.empty());
TRACE("pattern_inference",
tout << "candidates after removing bigger patterns:\n";
dump_app_vector(tout, m_tmp2, m_manager););
dump_app_vector(tout, m_tmp2, m););
m_tmp1.reset();
candidates2unary_patterns(m_tmp2, m_tmp1, result);
unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns;
@ -563,7 +563,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt);
TRACE("pattern_inference",
tout << "candidates after sorting:\n";
dump_app_vector(tout, m_tmp1, m_manager););
dump_app_vector(tout, m_tmp1, m););
candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result);
}
}
@ -577,7 +577,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
#include"database.h" // defines g_pattern_database
void pattern_inference::reduce1_quantifier(quantifier * q) {
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m_manager) << "\n";);
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
if (!q->is_forall()) {
simplifier::reduce1_quantifier(q);
return;
@ -587,27 +587,27 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
if (m_params.m_pi_use_database) {
m_database.initialize(g_pattern_database);
app_ref_vector new_patterns(m_manager);
app_ref_vector new_patterns(m);
unsigned new_weight;
if (m_database.match_quantifier(q, new_patterns, new_weight)) {
#ifdef Z3DEBUG
for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m_manager, new_patterns.get(i))); }
for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); }
#endif
quantifier_ref new_q(m_manager);
quantifier_ref new_q(m);
if (q->get_num_patterns() > 0) {
// just update the weight...
TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m_manager) << "\n";);
new_q = m_manager.update_quantifier_weight(q, new_weight);
TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";);
new_q = m.update_quantifier_weight(q, new_weight);
}
else {
quantifier_ref tmp(m_manager);
tmp = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
new_q = m_manager.update_quantifier_weight(tmp, new_weight);
TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m_manager) << "\n";);
quantifier_ref tmp(m);
tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
new_q = m.update_quantifier_weight(tmp, new_weight);
TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";);
}
proof * pr = 0;
if (m_manager.fine_grain_proofs())
pr = m_manager.mk_rewrite(q, new_q);
if (m.fine_grain_proofs())
pr = m.mk_rewrite(q, new_q);
cache_result(q, new_q, pr);
return;
}
@ -635,7 +635,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
new_no_patterns.push_back(new_pattern);
}
app_ref_buffer new_patterns(m_manager);
app_ref_buffer new_patterns(m);
if (m_params.m_pi_arith == AP_CONSERVATIVE)
m_forbidden.push_back(m_afid);
@ -677,26 +677,26 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).",
q->get_qid().str().c_str(), weight);
}
// verbose_stream() << mk_pp(q, m_manager) << "\n";
// verbose_stream() << mk_pp(q, m) << "\n";
}
}
}
quantifier_ref new_q(m_manager);
new_q = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
quantifier_ref new_q(m);
new_q = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
if (weight != q->get_weight())
new_q = m_manager.update_quantifier_weight(new_q, weight);
proof_ref pr(m_manager);
if (m_manager.fine_grain_proofs()) {
new_q = m.update_quantifier_weight(new_q, weight);
proof_ref pr(m);
if (m.fine_grain_proofs()) {
if (new_body_pr == 0)
new_body_pr = m_manager.mk_reflexivity(new_body);
pr = m_manager.mk_quant_intro(q, new_q, new_body_pr);
new_body_pr = m.mk_reflexivity(new_body);
pr = m.mk_quant_intro(q, new_q, new_body_pr);
}
if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) {
pull_quant pull(m_manager);
expr_ref new_expr(m_manager);
proof_ref new_pr(m_manager);
pull_quant pull(m);
expr_ref new_expr(m);
proof_ref new_pr(m);
pull(new_q, new_expr, new_pr);
quantifier * new_new_q = to_quantifier(new_expr);
if (new_new_q != new_q) {
@ -705,12 +705,12 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
if (m_params.m_pi_warnings) {
warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str());
}
new_q = m_manager.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
if (m_manager.fine_grain_proofs()) {
pr = m_manager.mk_transitivity(pr, new_pr);
pr = m_manager.mk_transitivity(pr, m_manager.mk_quant_intro(new_new_q, new_q, m_manager.mk_reflexivity(new_q->get_expr())));
new_q = m.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
if (m.fine_grain_proofs()) {
pr = m.mk_transitivity(pr, new_pr);
pr = m.mk_transitivity(pr, m.mk_quant_intro(new_new_q, new_q, m.mk_reflexivity(new_q->get_expr())));
}
TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m_manager) << "\n";);
TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";);
}
}
}
@ -719,7 +719,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
if (m_params.m_pi_warnings) {
warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str());
}
TRACE("pi_failed", tout << mk_pp(q, m_manager) << "\n";);
TRACE("pi_failed", tout << mk_pp(q, m) << "\n";);
}
if (new_patterns.empty() && new_body == q->get_expr()) {

View file

@ -38,7 +38,7 @@ Revision History:
every instance of f(g(X)) is also an instance of f(X).
*/
class smaller_pattern {
ast_manager & m_manager;
ast_manager & m;
ptr_vector<expr> m_bindings;
typedef std::pair<expr *, expr *> expr_pair;
@ -54,7 +54,7 @@ class smaller_pattern {
public:
smaller_pattern(ast_manager & m):
m_manager(m) {
m(m) {
}
bool operator()(unsigned num_bindings, expr * p1, expr * p2);
@ -135,7 +135,7 @@ class pattern_inference : public simplifier {
m_node(n, m), m_free_vars(vars), m_size(sz) {}
};
ast_manager & m_manager;
ast_manager & m;
pattern_inference & m_owner;
family_id m_afid;
unsigned m_num_bindings;
@ -150,7 +150,7 @@ class pattern_inference : public simplifier {
void save_candidate(expr * n, unsigned delta);
void reset();
public:
collect(ast_manager & m, pattern_inference & o):m_manager(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
void operator()(expr * n, unsigned num_bindings);
};

282
src/ast/pb_decl_plugin.cpp Normal file
View file

@ -0,0 +1,282 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_decl_plugin.cpp
Abstract:
Cardinality Constraints plugin
Author:
Nikolaj Bjorner (nbjorner) 2013-05-11
Revision History:
--*/
#include "pb_decl_plugin.h"
pb_decl_plugin::pb_decl_plugin():
m_at_most_sym("at-most"),
m_at_least_sym("at-least"),
m_pble_sym("pble"),
m_pbge_sym("pbge"),
m_pbeq_sym("pbeq")
{}
func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
SASSERT(m_manager);
ast_manager& m = *m_manager;
for (unsigned i = 0; i < arity; ++i) {
if (!m.is_bool(domain[i])) {
m.raise_exception("invalid non-Boolean sort applied to 'at-most'");
}
}
symbol sym;
switch(k) {
case OP_AT_LEAST_K: sym = m_at_least_sym; break;
case OP_AT_MOST_K: sym = m_at_most_sym; break;
case OP_PB_LE: sym = m_pble_sym; break;
case OP_PB_GE: sym = m_pbge_sym; break;
case OP_PB_EQ: sym = m_pbeq_sym; break;
default: break;
}
switch(k) {
case OP_AT_LEAST_K:
case OP_AT_MOST_K: {
if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) {
m.raise_exception("function expects one non-negative integer parameter");
}
func_decl_info info(m_family_id, k, 1, parameters);
return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info);
}
case OP_PB_GE:
case OP_PB_LE:
case OP_PB_EQ: {
if (num_parameters != 1 + arity) {
m.raise_exception("function expects arity+1 rational parameters");
}
vector<parameter> params;
for (unsigned i = 0; i < num_parameters; ++i) {
parameter const& p = parameters[i];
if (p.is_int()) {
params.push_back(p);
}
else if (p.is_rational()) {
// HACK: ast pretty printer does not work with rationals.
rational r = p.get_rational();
if (r.is_int32()) {
params.push_back(parameter(r.get_int32()));
}
else {
params.push_back(p);
}
}
else {
m.raise_exception("functions 'pble/pbge/pbeq' expect arity+1 integer parameters");
}
}
func_decl_info info(m_family_id, k, num_parameters, params.c_ptr());
return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info);
}
default:
UNREACHABLE();
return 0;
}
}
void pb_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
if (logic == symbol::null) {
op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K));
op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K));
op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE));
op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE));
op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ));
}
}
app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
vector<parameter> params;
params.push_back(parameter(k));
for (unsigned i = 0; i < num_args; ++i) {
params.push_back(parameter(coeffs[i]));
}
return m.mk_app(m_fid, OP_PB_LE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
}
app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
vector<parameter> params;
params.push_back(parameter(k));
for (unsigned i = 0; i < num_args; ++i) {
params.push_back(parameter(coeffs[i]));
}
return m.mk_app(m_fid, OP_PB_GE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
}
app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
vector<parameter> params;
params.push_back(parameter(k));
for (unsigned i = 0; i < num_args; ++i) {
params.push_back(parameter(coeffs[i]));
}
return m.mk_app(m_fid, OP_PB_EQ, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
}
// ax + by < k
// <=>
// -ax - by >= -k + 1
// <=>
// a(1-x) + b(1-y) >= -k + a + b + 1
app * pb_util::mk_lt(unsigned num_args, rational const * _coeffs, expr * const * _args, rational const& _k) {
vector<rational> coeffs;
rational k(_k);
expr_ref_vector args(m);
expr* f;
rational d(denominator(k));
for (unsigned i = 0; i < num_args; ++i) {
coeffs.push_back(_coeffs[i]);
d = lcm(d, denominator(coeffs[i]));
if (m.is_not(_args[i], f)) {
args.push_back(f);
}
else {
args.push_back(m.mk_not(_args[i]));
}
}
if (!d.is_one()) {
k *= d;
for (unsigned i = 0; i < num_args; ++i) {
coeffs[i] *= d;
}
}
k.neg();
k += rational::one();
for (unsigned i = 0; i < num_args; ++i) {
k += coeffs[i];
}
return mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k);
}
app * pb_util::mk_at_most_k(unsigned num_args, expr * const * args, unsigned k) {
parameter param(k);
return m.mk_app(m_fid, OP_AT_MOST_K, 1, &param, num_args, args, m.mk_bool_sort());
}
bool pb_util::is_at_most_k(func_decl *a) const {
return is_decl_of(a, m_fid, OP_AT_MOST_K);
}
bool pb_util::is_at_most_k(expr *a, rational& k) const {
if (is_at_most_k(a)) {
k = get_k(a);
return true;
}
else {
return false;
}
}
app * pb_util::mk_at_least_k(unsigned num_args, expr * const * args, unsigned k) {
parameter param(k);
return m.mk_app(m_fid, OP_AT_LEAST_K, 1, &param, num_args, args, m.mk_bool_sort());
}
bool pb_util::is_at_least_k(func_decl *a) const {
return is_decl_of(a, m_fid, OP_AT_LEAST_K);
}
bool pb_util::is_at_least_k(expr *a, rational& k) const {
if (is_at_least_k(a)) {
k = get_k(a);
return true;
}
else {
return false;
}
}
rational pb_util::get_k(func_decl *a) const {
parameter const& p = a->get_parameter(0);
if (is_at_most_k(a) || is_at_least_k(a)) {
return to_rational(p);
}
else {
SASSERT(is_le(a) || is_ge(a) || is_eq(a));
return to_rational(p);
}
}
bool pb_util::is_le(func_decl *a) const {
return is_decl_of(a, m_fid, OP_PB_LE);
}
bool pb_util::is_le(expr* a, rational& k) const {
if (is_le(a)) {
k = get_k(a);
return true;
}
else {
return false;
}
}
bool pb_util::is_ge(func_decl *a) const {
return is_decl_of(a, m_fid, OP_PB_GE);
}
bool pb_util::is_ge(expr* a, rational& k) const {
if (is_ge(a)) {
k = get_k(a);
return true;
}
else {
return false;
}
}
bool pb_util::is_eq(func_decl *a) const {
return is_decl_of(a, m_fid, OP_PB_EQ);
}
bool pb_util::is_eq(expr* a, rational& k) const {
if (is_eq(a)) {
k = get_k(a);
return true;
}
else {
return false;
}
}
rational pb_util::get_coeff(func_decl* a, unsigned index) const {
if (is_at_most_k(a) || is_at_least_k(a)) {
return rational::one();
}
SASSERT(is_le(a) || is_ge(a) || is_eq(a));
SASSERT(1 + index < a->get_num_parameters());
return to_rational(a->get_parameter(index + 1));
}
rational pb_util::to_rational(parameter const& p) const {
if (p.is_int()) {
return rational(p.get_int());
}
SASSERT(p.is_rational());
return p.get_rational();
}
bool pb_util::has_unit_coefficients(func_decl* f) const {
if (is_at_most_k(f) || is_at_least_k(f)) return true;
unsigned sz = f->get_arity();
for (unsigned i = 0; i < sz; ++i) {
if (!get_coeff(f, i).is_one()) return false;
}
return true;
}

123
src/ast/pb_decl_plugin.h Normal file
View file

@ -0,0 +1,123 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_decl_plugin.h
Abstract:
Pseudo-Boolean and Cardinality Constraints plugin
Author:
Nikolaj Bjorner (nbjorner) 2013-05-11
Notes:
(at-most-k x1 .... x_n) means x1 + ... + x_n <= k
hence:
(not (at-most-k x1 .... x_n)) means x1 + ... + x_n >= k + 1
--*/
#ifndef _PB_DECL_PLUGIN_H_
#define _PB_DECL_PLUGIN_H_
#include"ast.h"
enum pb_op_kind {
OP_AT_MOST_K, // at most K Booleans are true.
OP_AT_LEAST_K, // at least K Booleans are true.
OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k)
OP_PB_GE, // pseudo-Boolean >=
OP_PB_EQ, // equality
LAST_PB_OP
};
class pb_decl_plugin : public decl_plugin {
symbol m_at_most_sym;
symbol m_at_least_sym;
symbol m_pble_sym;
symbol m_pbge_sym;
symbol m_pbeq_sym;
func_decl * mk_at_most(unsigned arity, unsigned k);
func_decl * mk_at_least(unsigned arity, unsigned k);
func_decl * mk_le(unsigned arity, rational const* coeffs, int k);
func_decl * mk_ge(unsigned arity, rational const* coeffs, int k);
func_decl * mk_eq(unsigned arity, rational const* coeffs, int k);
public:
pb_decl_plugin();
virtual ~pb_decl_plugin() {}
virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
UNREACHABLE();
return 0;
}
virtual decl_plugin * mk_fresh() {
return alloc(pb_decl_plugin);
}
//
// Contract for func_decl:
// parameters[0] - integer (at most k elements)
// all sorts are Booleans
// parameters[1] .. parameters[arity] - coefficients
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range);
virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
};
class pb_util {
ast_manager & m;
family_id m_fid;
public:
pb_util(ast_manager& m):m(m), m_fid(m.mk_family_id("pb")) {}
ast_manager & get_manager() const { return m; }
family_id get_family_id() const { return m_fid; }
app * mk_at_most_k(unsigned num_args, expr * const * args, unsigned k);
app * mk_at_least_k(unsigned num_args, expr * const * args, unsigned k);
app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
app * mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
app * mk_lt(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
bool is_at_most_k(func_decl *a) const;
bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); }
bool is_at_most_k(expr *a, rational& k) const;
bool is_at_least_k(func_decl *a) const;
bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); }
bool is_at_least_k(expr *a, rational& k) const;
rational get_k(func_decl *a) const;
rational get_k(expr *a) const { return get_k(to_app(a)->get_decl()); }
bool is_le(func_decl *a) const;
bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); }
bool is_le(expr* a, rational& k) const;
bool is_ge(func_decl* a) const;
bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); }
bool is_ge(expr* a, rational& k) const;
rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); }
rational get_coeff(func_decl* a, unsigned index) const;
bool has_unit_coefficients(func_decl* f) const;
bool has_unit_coefficients(expr* f) const { return is_app(f) && has_unit_coefficients(to_app(f)->get_decl()); }
bool is_eq(func_decl* f) const;
bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); }
bool is_eq(expr* e, rational& k) const;
private:
rational to_rational(parameter const& p) const;
};
#endif /* _PB_DECL_PLUGIN_H_ */

View file

@ -25,6 +25,7 @@ Revision History:
#include"dl_decl_plugin.h"
#include"seq_decl_plugin.h"
#include"float_decl_plugin.h"
#include"pb_decl_plugin.h"
void reg_decl_plugins(ast_manager & m) {
if (!m.get_plugin(m.mk_family_id(symbol("arith")))) {
@ -48,4 +49,7 @@ void reg_decl_plugins(ast_manager & m) {
if (!m.get_plugin(m.mk_family_id(symbol("float")))) {
m.register_plugin(symbol("float"), alloc(float_decl_plugin));
}
if (!m.get_plugin(m.mk_family_id(symbol("pb")))) {
m.register_plugin(symbol("pb"), alloc(pb_decl_plugin));
}
}

View file

@ -18,12 +18,9 @@ Revision History:
--*/
#include "ast_counter.h"
#include "var_subst.h"
void counter::update(unsigned el, int delta) {
int & counter = get(el);
SASSERT(!m_stay_non_negative || counter>=0);
SASSERT(!m_stay_non_negative || static_cast<int>(counter)>=-delta);
counter += delta;
}
@ -92,16 +89,14 @@ int counter::get_max_counter_value() const {
void var_counter::count_vars(ast_manager & m, const app * pred, int coef) {
unsigned n = pred->get_num_args();
for (unsigned i = 0; i < n; i++) {
m_sorts.reset();
m_todo.reset();
m_mark.reset();
::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts);
for (unsigned j = 0; j < m_sorts.size(); ++j) {
if (m_sorts[j]) {
m_fv(pred->get_arg(i));
for (unsigned j = 0; j < m_fv.size(); ++j) {
if (m_fv[j]) {
update(j, coef);
}
}
}
m_fv.reset();
}

View file

@ -27,16 +27,16 @@ Revision History:
#include "ast.h"
#include "map.h"
#include "uint_set.h"
#include "var_subst.h"
class counter {
protected:
typedef u_map<int> map_impl;
map_impl m_data;
const bool m_stay_non_negative;
public:
typedef map_impl::iterator iterator;
counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {}
counter() {}
void reset() { m_data.reset(); }
iterator begin() const { return m_data.begin(); }
@ -69,14 +69,13 @@ public:
class var_counter : public counter {
protected:
ptr_vector<sort> m_sorts;
expr_fast_mark1 m_visited;
expr_free_vars m_fv;
ptr_vector<expr> m_todo;
ast_mark m_mark;
unsigned_vector m_scopes;
unsigned get_max_var(bool & has_var);
public:
var_counter(bool stay_non_negative = true): counter(stay_non_negative) {}
var_counter() {}
void count_vars(ast_manager & m, const app * t, int coef = 1);
unsigned get_max_var(expr* e);
unsigned get_next_var(expr* e);
@ -85,11 +84,10 @@ public:
class ast_counter {
typedef obj_map<ast, int> map_impl;
map_impl m_data;
bool m_stay_non_negative;
public:
typedef map_impl::iterator iterator;
ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {}
ast_counter() {}
iterator begin() const { return m_data.begin(); }
iterator end() const { return m_data.end(); }
@ -99,7 +97,6 @@ class ast_counter {
}
void update(ast * el, int delta){
get(el) += delta;
SASSERT(!m_stay_non_negative || get(el) >= 0);
}
void inc(ast * el) { update(el, 1); }

View file

@ -1038,6 +1038,11 @@ void bit_blaster_tpl<Cfg>::mk_ext_rotate_left_right(unsigned sz, expr * const *
mk_rotate_right(sz, a_bits, static_cast<unsigned>(k.get_uint64()), out_bits);
}
else {
//
// Review: a better tuned implementation is possible by using shifts by power of two.
// e.g., looping over the bits of b_bits, then rotate by a power of two depending
// on the bit-position. This would get rid of the mk_urem.
//
expr_ref_vector sz_bits(m());
expr_ref_vector masked_b_bits(m());
expr_ref_vector eqs(m());

View file

@ -60,6 +60,32 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
UNREACHABLE();
break;
}
case OP_DT_UPDATE_FIELD: {
SASSERT(num_args == 2);
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
return BR_FAILED;
app * a = to_app(args[0]);
func_decl * c_decl = a->get_decl();
if (c_decl != m_util.get_accessor_constructor(f)) {
result = a;
return BR_DONE;
}
ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
SASSERT(acc && acc->size() == a->get_num_args());
unsigned num = acc->size();
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < num; ++i) {
if (f == (*acc)[i]) {
new_args.push_back(args[1]);
}
else {
new_args.push_back(a->get_arg(i));
}
}
result = m().mk_app(c_decl, num, new_args.c_ptr());
return BR_DONE;
}
default:
UNREACHABLE();
}

View file

@ -29,43 +29,39 @@ void expr_safe_replace::insert(expr* src, expr* dst) {
}
void expr_safe_replace::operator()(expr* e, expr_ref& res) {
obj_map<expr,expr*> cache;
ptr_vector<expr> todo, args;
expr_ref_vector refs(m);
todo.push_back(e);
m_todo.push_back(e);
expr* a, *b, *d;
todo.push_back(e);
while (!todo.empty()) {
a = todo.back();
if (cache.contains(a)) {
todo.pop_back();
while (!m_todo.empty()) {
a = m_todo.back();
if (m_cache.contains(a)) {
m_todo.pop_back();
}
else if (m_subst.find(a, b)) {
cache.insert(a, b);
todo.pop_back();
m_cache.insert(a, b);
m_todo.pop_back();
}
else if (is_var(a)) {
cache.insert(a, a);
todo.pop_back();
m_cache.insert(a, a);
m_todo.pop_back();
}
else if (is_app(a)) {
app* c = to_app(a);
unsigned n = c->get_num_args();
args.reset();
m_args.reset();
for (unsigned i = 0; i < n; ++i) {
if (cache.find(c->get_arg(i), d)) {
args.push_back(d);
if (m_cache.find(c->get_arg(i), d)) {
m_args.push_back(d);
}
else {
todo.push_back(c->get_arg(i));
m_todo.push_back(c->get_arg(i));
}
}
if (args.size() == n) {
b = m.mk_app(c->get_decl(), args.size(), args.c_ptr());
refs.push_back(b);
cache.insert(a, b);
todo.pop_back();
if (m_args.size() == n) {
b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr());
m_refs.push_back(b);
m_cache.insert(a, b);
m_todo.pop_back();
}
}
else {
@ -93,12 +89,16 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) {
}
replace(q->get_expr(), new_body);
b = m.update_quantifier(q, pats.size(), pats.c_ptr(), nopats.size(), nopats.c_ptr(), new_body);
refs.push_back(b);
cache.insert(a, b);
todo.pop_back();
m_refs.push_back(b);
m_cache.insert(a, b);
m_todo.pop_back();
}
}
res = cache.find(e);
res = m_cache.find(e);
m_cache.reset();
m_todo.reset();
m_args.reset();
m_refs.reset();
}
void expr_safe_replace::reset() {

View file

@ -29,9 +29,12 @@ class expr_safe_replace {
expr_ref_vector m_src;
expr_ref_vector m_dst;
obj_map<expr, expr*> m_subst;
obj_map<expr,expr*> m_cache;
ptr_vector<expr> m_todo, m_args;
expr_ref_vector m_refs;
public:
expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m) {}
expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m), m_refs(m) {}
void insert(expr* src, expr* dst);
@ -42,6 +45,8 @@ public:
void apply_substitution(expr* s, expr* def, expr_ref& t);
void reset();
bool empty() const { return m_subst.empty(); }
};
#endif /* __EXPR_SAFE_REPLACE_H__ */

View file

@ -0,0 +1,275 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_rewriter.cpp
Abstract:
Basic rewriting rules for PB constraints.
Author:
Nikolaj Bjorner (nbjorner) 2013-14-12
Notes:
--*/
#include "pb_rewriter.h"
#include "pb_rewriter_def.h"
#include "ast_pp.h"
#include "ast_smt_pp.h"
class pb_ast_rewriter_util {
ast_manager& m;
expr_ref_vector m_refs;
public:
typedef std::pair<expr*, rational> arg_t;
typedef vector<arg_t> args_t;
typedef rational numeral;
pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {}
expr* negate(expr* e) {
if (m.is_true(e)) {
return m.mk_false();
}
if (m.is_false(e)) {
return m.mk_true();
}
if (m.is_not(e, e)) {
return e;
}
m_refs.push_back(m.mk_not(e));
return m_refs.back();
}
void display(std::ostream& out, expr* e) {
out << mk_pp(e, m);
}
bool is_negated(expr* e) const {
return m.is_not(e);
}
bool is_true(expr* e) const {
return m.is_true(e);
}
bool is_false(expr* e) const {
return m.is_false(e);
}
struct compare {
bool operator()(std::pair<expr*,rational> const& a,
std::pair<expr*,rational> const& b) {
return a.first->get_id() < b.first->get_id();
}
};
};
expr_ref pb_rewriter::translate_pb2lia(obj_map<expr,expr*>& vars, expr* fml) {
pb_util util(m());
arith_util a(m());
expr_ref result(m()), tmp(m());
expr_ref_vector es(m());
expr*const* args = to_app(fml)->get_args();
unsigned sz = to_app(fml)->get_num_args();
for (unsigned i = 0; i < sz; ++i) {
expr* e = args[i];
if (m().is_not(e, e)) {
es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e)));
}
else {
es.push_back(vars.find(e));
}
}
if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) {
if (es.empty()) {
tmp = a.mk_numeral(rational(0), true);
}
else {
tmp = a.mk_add(es.size(), es.c_ptr());
}
if (util.is_at_most_k(fml)) {
result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false));
}
else {
result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false));
}
}
else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) {
for (unsigned i = 0; i < sz; ++i) {
es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get());
}
if (es.empty()) {
tmp = a.mk_numeral(rational(0), true);
}
else {
tmp = a.mk_add(es.size(), es.c_ptr());
}
if (util.is_le(fml)) {
result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false));
}
else if (util.is_ge(fml)) {
result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false));
}
else {
result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false));
}
}
else {
result = fml;
}
return result;
}
expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) {
ast_manager& m = e1.get_manager();
arith_util a(m);
symbol name;
obj_map<expr,expr*> vars;
expr_ref_vector trail(m), fmls(m);
unsigned sz = to_app(e1)->get_num_args();
expr*const*args = to_app(e1)->get_args();
for (unsigned i = 0; i < sz; ++i) {
expr* e = args[i];
if (m.is_true(e)) {
if (!vars.contains(e)) {
trail.push_back(a.mk_numeral(rational(1), true));
vars.insert(e, trail.back());
}
continue;
}
if (m.is_false(e)) {
if (!vars.contains(e)) {
trail.push_back(a.mk_numeral(rational(0), true));
vars.insert(e, trail.back());
}
continue;
}
std::ostringstream strm;
strm << "x" << i;
name = symbol(strm.str().c_str());
trail.push_back(m.mk_const(name, a.mk_int()));
expr* x = trail.back();
m.is_not(e,e);
vars.insert(e, x);
fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x));
fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true)));
}
expr_ref tmp(m);
expr_ref fml1 = translate_pb2lia(vars, e1);
expr_ref fml2 = translate_pb2lia(vars, e2);
tmp = m.mk_not(m.mk_eq(fml1, fml2));
fmls.push_back(tmp);
tmp = m.mk_and(fmls.size(), fmls.c_ptr());
return tmp;
}
static unsigned s_lemma = 0;
void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) {
ast_manager& m = fml.get_manager();
app_ref tmp1(m), tmp2(m);
tmp1 = m.mk_app(f, sz, args);
tmp2 = to_app(fml);
expr_ref tmp = mk_validate_rewrite(tmp1, tmp2);
dump_pb_rewrite(tmp);
}
void pb_rewriter::dump_pb_rewrite(expr* fml) {
std::ostringstream strm;
strm << "pb_rewrite_" << (s_lemma++) << ".smt2";
std::ofstream out(strm.str().c_str());
ast_smt_pp pp(m());
pp.display_smt2(out, fml);
out.close();
}
br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
ast_manager& m = result.get_manager();
rational sum(0), maxsum(0);
for (unsigned i = 0; i < num_args; ++i) {
if (m.is_true(args[i])) {
sum += m_util.get_coeff(f, i);
maxsum += m_util.get_coeff(f, i);
}
else if (!m.is_false(args[i])) {
maxsum += m_util.get_coeff(f, i);
}
}
rational k = m_util.get_k(f);
vector<std::pair<expr*,rational> > vec;
for (unsigned i = 0; i < num_args; ++i) {
vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i)));
}
switch(f->get_decl_kind()) {
case OP_AT_MOST_K:
case OP_PB_LE:
for (unsigned i = 0; i < num_args; ++i) {
vec[i].second.neg();
}
k.neg();
break;
case OP_AT_LEAST_K:
case OP_PB_GE:
case OP_PB_EQ:
break;
default:
UNREACHABLE();
return BR_FAILED;
}
bool is_eq = f->get_decl_kind() == OP_PB_EQ;
pb_ast_rewriter_util pbu(m);
pb_rewriter_util<pb_ast_rewriter_util> util(pbu);
util.unique(vec, k, is_eq);
lbool is_sat = util.normalize(vec, k, is_eq);
util.prune(vec, k, is_eq);
switch (is_sat) {
case l_true:
result = m.mk_true();
break;
case l_false:
result = m.mk_false();
break;
default:
m_args.reset();
m_coeffs.reset();
for (unsigned i = 0; i < vec.size(); ++i) {
m_args.push_back(vec[i].first);
m_coeffs.push_back(vec[i].second);
}
if (is_eq) {
result = m_util.mk_eq(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k);
}
else {
result = m_util.mk_ge(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k);
}
break;
}
TRACE("pb",
expr_ref tmp(m);
tmp = m.mk_app(f, num_args, args);
tout << tmp << "\n";
tout << result << "\n";
);
TRACE("pb_validate",
validate_rewrite(f, num_args, args, result););
return BR_DONE;
}

View file

@ -0,0 +1,65 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_rewriter.h
Abstract:
Basic rewriting rules for PB constraints.
Author:
Nikolaj Bjorner (nbjorner) 2013-14-12
Notes:
--*/
#ifndef _PB_REWRITER_H_
#define _PB_REWRITER_H_
#include"pb_decl_plugin.h"
#include"rewriter_types.h"
#include"params.h"
#include"lbool.h"
template<typename PBU>
class pb_rewriter_util {
PBU& m_util;
void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
public:
pb_rewriter_util(PBU& u) : m_util(u) {}
void unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
void prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
};
/**
\brief Cheap rewrite rules for PB constraints
*/
class pb_rewriter {
pb_util m_util;
vector<rational> m_coeffs;
ptr_vector<expr> m_args;
void validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml);
public:
pb_rewriter(ast_manager & m, params_ref const & p = params_ref()):
m_util(m) {
}
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
void updt_params(params_ref const & p) {}
static void get_param_descrs(param_descrs & r) {}
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
expr_ref translate_pb2lia(obj_map<expr,expr*>& vars, expr* fml);
expr_ref mk_validate_rewrite(app_ref& e1, app_ref& e2);
void dump_pb_rewrite(expr* fml);
};
#endif

View file

@ -0,0 +1,298 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_rewriter_def.h
Abstract:
Basic rewriting rules for PB constraints.
Author:
Nikolaj Bjorner (nbjorner) 2013-14-12
Notes:
--*/
#ifndef _PB_REWRITER_DEF_H_
#define _PB_REWRITER_DEF_H_
#include"pb_rewriter.h"
template<typename PBU>
void pb_rewriter_util<PBU>::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
for (unsigned i = 0; i < args.size(); ++i) {
out << args[i].second << " * ";
m_util.display(out, args[i].first);
out << " ";
if (i+1 < args.size()) out << "+ ";
}
out << (is_eq?" = ":" >= ") << k << "\n";
}
template<typename PBU>
void pb_rewriter_util<PBU>::unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
TRACE("pb_verbose", display(tout << "pre-unique:", args, k, is_eq););
for (unsigned i = 0; i < args.size(); ++i) {
if (m_util.is_negated(args[i].first)) {
args[i].first = m_util.negate(args[i].first);
k -= args[i].second;
args[i].second = -args[i].second;
}
}
// remove constants
for (unsigned i = 0; i < args.size(); ++i) {
if (m_util.is_true(args[i].first)) {
k -= args[i].second;
std::swap(args[i], args[args.size()-1]);
args.pop_back();
--i;
}
else if (m_util.is_false(args[i].first)) {
std::swap(args[i], args[args.size()-1]);
args.pop_back();
--i;
}
}
// sort and coalesce arguments:
typename PBU::compare cmp;
std::sort(args.begin(), args.end(), cmp);
// coallesce
unsigned i, j;
for (i = 0, j = 1; j < args.size(); ++j) {
if (args[i].first == args[j].first) {
args[i].second += args[j].second;
}
else {
++i;
args[i] = args[j];
}
}
args.resize(i+1);
// remove 0s.
for (i = 0, j = 0; j < args.size(); ++j) {
if (!args[j].second.is_zero()) {
if (i != j) {
args[i] = args[j];
}
++i;
}
}
args.resize(i);
TRACE("pb_verbose", display(tout << "post-unique:", args, k, is_eq););
}
template<typename PBU>
lbool pb_rewriter_util<PBU>::normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
TRACE("pb_verbose", display(tout << "pre-normalize:", args, k, is_eq););
DEBUG_CODE(
bool found = false;
for (unsigned i = 0; !found && i < args.size(); ++i) {
found = args[i].second.is_zero();
}
if (found) display(verbose_stream(), args, k, is_eq);
SASSERT(!found););
//
// Ensure all coefficients are positive:
// c*l + y >= k
// <=>
// c*(1-~l) + y >= k
// <=>
// c - c*~l + y >= k
// <=>
// -c*~l + y >= k - c
//
typename PBU::numeral sum(0);
for (unsigned i = 0; i < args.size(); ++i) {
typename PBU::numeral c = args[i].second;
if (c.is_neg()) {
args[i].second = -c;
args[i].first = m_util.negate(args[i].first);
k -= c;
}
sum += args[i].second;
}
// detect tautologies:
if (!is_eq && k <= PBU::numeral::zero()) {
args.reset();
k = PBU::numeral::zero();
return l_true;
}
if (is_eq && k.is_zero() && args.empty()) {
return l_true;
}
// detect infeasible constraints:
if (sum < k) {
args.reset();
k = PBU::numeral::one();
return l_false;
}
if (is_eq && k == sum) {
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = PBU::numeral::one();
}
typename PBU::numeral num(args.size());
k = num;
return l_undef;
}
bool all_int = true;
for (unsigned i = 0; all_int && i < args.size(); ++i) {
all_int = args[i].second.is_int();
}
if (!all_int) {
// normalize to integers.
typename PBU::numeral d(denominator(k));
for (unsigned i = 0; i < args.size(); ++i) {
d = lcm(d, denominator(args[i].second));
}
SASSERT(!d.is_one());
k *= d;
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second *= d;
}
}
if (is_eq) {
TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq););
return l_undef;
}
// Ensure the largest coefficient is not larger than k:
sum = PBU::numeral::zero();
for (unsigned i = 0; i < args.size(); ++i) {
typename PBU::numeral c = args[i].second;
if (c > k) {
args[i].second = k;
}
sum += args[i].second;
}
SASSERT(!args.empty());
// normalize tight inequalities to unit coefficients.
if (sum == k) {
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = PBU::numeral::one();
}
typename PBU::numeral num(args.size());
k = num;
}
// apply cutting plane reduction:
typename PBU::numeral g(0);
for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) {
typename PBU::numeral c = args[i].second;
if (c != k) {
if (g.is_zero()) {
g = c;
}
else {
g = gcd(g, c);
}
}
}
if (g.is_zero()) {
// all coefficients are equal to k.
for (unsigned i = 0; i < args.size(); ++i) {
SASSERT(args[i].second == k);
args[i].second = PBU::numeral::one();
}
k = PBU::numeral::one();
}
else if (g > PBU::numeral::one()) {
//
// Example 5x + 5y + 2z + 2u >= 5
// becomes 3x + 3y + z + u >= 3
//
typename PBU::numeral k_new = div(k, g);
if (!(k % g).is_zero()) { // k_new is the ceiling of k / g.
k_new++;
}
for (unsigned i = 0; i < args.size(); ++i) {
SASSERT(args[i].second.is_pos());
typename PBU::numeral c = args[i].second;
if (c == k) {
c = k_new;
}
else {
c = div(c, g);
}
args[i].second = c;
SASSERT(args[i].second.is_pos());
}
k = k_new;
}
//
// normalize coefficients that fall within a range
// k/n <= ... < k/(n-1) for some n = 1,2,...
//
// e.g, k/n <= min <= max < k/(n-1)
// k/min <= n, n-1 < k/max
// . floor(k/max) = ceil(k/min) - 1
// . floor(k/max) < k/max
//
// example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2
// replace all coefficients by 1, and k by 2.
//
if (!k.is_one()) {
typename PBU::numeral min = args[0].second, max = args[0].second;
for (unsigned i = 1; i < args.size(); ++i) {
if (args[i].second < min) min = args[i].second;
if (args[i].second > max) max = args[i].second;
}
SASSERT(min.is_pos());
typename PBU::numeral n0 = k/max;
typename PBU::numeral n1 = floor(n0);
typename PBU::numeral n2 = ceil(k/min) - PBU::numeral::one();
if (n1 == n2 && !n0.is_int()) {
IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k, is_eq););
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = PBU::numeral::one();
}
k = n1 + PBU::numeral::one();
}
}
TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq););
return l_undef;
}
template<typename PBU>
void pb_rewriter_util<PBU>::prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
if (is_eq) {
return;
}
typename PBU::numeral nlt(0);
unsigned occ = 0;
for (unsigned i = 0; nlt < k && i < args.size(); ++i) {
if (args[i].second < k) {
nlt += args[i].second;
++occ;
}
}
if (0 < occ && nlt < k) {
for (unsigned i = 0; i < args.size(); ++i) {
if (args[i].second < k) {
args[i] = args.back();
args.pop_back();
--i;
}
}
unique(args, k, is_eq);
normalize(args, k, is_eq);
}
}
#endif

View file

@ -34,6 +34,8 @@ void poly_rewriter<Config>::updt_params(params_ref const & _p) {
m_hoist_mul = p.hoist_mul();
m_hoist_cmul = p.hoist_cmul();
m_som_blowup = p.som_blowup();
if (!m_flat) m_som = false;
if (m_som) m_hoist_mul = false;
}
template<typename Config>

View file

@ -25,6 +25,7 @@ Notes:
#include"array_rewriter.h"
#include"float_rewriter.h"
#include"dl_rewriter.h"
#include"pb_rewriter.h"
#include"rewriter_def.h"
#include"expr_substitution.h"
#include"ast_smt2_pp.h"
@ -41,6 +42,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
datatype_rewriter m_dt_rw;
float_rewriter m_f_rw;
dl_rewriter m_dl_rw;
pb_rewriter m_pb_rw;
arith_util m_a_util;
bv_util m_bv_util;
unsigned long long m_max_memory; // in bytes
@ -196,6 +198,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
return m_f_rw.mk_app_core(f, num, args, result);
if (fid == m_dl_rw.get_fid())
return m_dl_rw.mk_app_core(f, num, args, result);
if (fid == m_pb_rw.get_fid())
return m_pb_rw.mk_app_core(f, num, args, result);
return BR_FAILED;
}
@ -645,6 +649,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
m_dt_rw(m),
m_f_rw(m, p),
m_dl_rw(m),
m_pb_rw(m),
m_a_util(m),
m_bv_util(m),
m_used_dependencies(m),

View file

@ -164,7 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref
tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";);
}
static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigned offset, expr* e, ptr_vector<sort>& sorts) {
static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector<expr>& todo, unsigned offset, expr* e, ptr_vector<sort>& sorts) {
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
@ -176,7 +176,7 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigne
switch(e->get_kind()) {
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(e);
ast_mark mark1;
expr_sparse_mark mark1;
ptr_vector<expr> todo1;
get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts);
break;
@ -210,11 +210,33 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigne
void get_free_vars(expr* e, ptr_vector<sort>& sorts) {
ast_mark mark;
expr_sparse_mark mark;
ptr_vector<expr> todo;
get_free_vars_offset(mark, todo, 0, e, sorts);
}
void get_free_vars(ast_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts) {
void get_free_vars(expr_sparse_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts) {
get_free_vars_offset(mark, todo, 0, e, sorts);
}
void expr_free_vars::reset() {
m_mark.reset();
m_sorts.reset();
SASSERT(m_todo.empty());
}
void expr_free_vars::set_default_sort(sort *s) {
for (unsigned i = 0; i < m_sorts.size(); ++i) {
if (!m_sorts[i]) m_sorts[i] = s;
}
}
void expr_free_vars::operator()(expr* e) {
reset();
get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts);
}
void expr_free_vars::accumulate(expr* e) {
SASSERT(m_todo.empty());
get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts);
}

View file

@ -81,9 +81,23 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref
Return the sorts of the free variables.
*/
void get_free_vars(expr* e, ptr_vector<sort>& sorts);
void get_free_vars(ast_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts);
class expr_free_vars {
expr_sparse_mark m_mark;
ptr_vector<sort> m_sorts;
ptr_vector<expr> m_todo;
public:
void reset();
void operator()(expr* e);
void accumulate(expr* e);
bool empty() const { return m_sorts.empty(); }
unsigned size() const { return m_sorts.size(); }
sort* operator[](unsigned idx) const { return m_sorts[idx]; }
bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; }
void set_default_sort(sort* s);
void reverse() { m_sorts.reverse(); }
sort*const* c_ptr() const { return m_sorts.c_ptr(); }
};
#endif

View file

@ -20,21 +20,32 @@ Notes:
#define _BASE_SIMPLIFIER_H_
#include"expr_map.h"
#include"ast_pp.h"
/**
\brief Implements basic functionality used by expression simplifiers.
*/
class base_simplifier {
protected:
ast_manager & m_manager;
ast_manager & m;
expr_map m_cache;
ptr_vector<expr> m_todo;
void cache_result(expr * n, expr * r, proof * p) { m_cache.insert(n, r, p); }
void cache_result(expr * n, expr * r, proof * p) {
m_cache.insert(n, r, p);
CTRACE("simplifier", !is_rewrite_proof(n, r, p),
tout << mk_pp(n, m) << "\n";
tout << mk_pp(r, m) << "\n";
tout << mk_pp(p, m) << "\n";);
SASSERT(is_rewrite_proof(n, r, p));
}
void reset_cache() { m_cache.reset(); }
void flush_cache() { m_cache.flush(); }
void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); }
void reinitialize() { m_cache.set_store_proofs(m.fine_grain_proofs()); }
void visit(expr * n, bool & visited) {
if (!is_cached(n)) {
m_todo.push_back(n);
@ -44,11 +55,22 @@ protected:
public:
base_simplifier(ast_manager & m):
m_manager(m),
m(m),
m_cache(m, m.fine_grain_proofs()) {
}
bool is_cached(expr * n) const { return m_cache.contains(n); }
ast_manager & get_manager() { return m_manager; }
ast_manager & get_manager() { return m; }
bool is_rewrite_proof(expr* n, expr* r, proof* p) {
if (p &&
!m.is_undef_proof(p) &&
!(m.has_fact(p) &&
(m.is_eq(m.get_fact(p)) || m.is_oeq(m.get_fact(p)) || m.is_iff(m.get_fact(p))) &&
to_app(m.get_fact(p))->get_arg(0) == n &&
to_app(m.get_fact(p))->get_arg(1) == r)) return false;
return (!m.fine_grain_proofs() || p || (n == r));
}
};
#endif /* _BASE_SIMPLIFIER_H_ */

View file

@ -100,11 +100,11 @@ bool bv_elim_star::visit_quantifier(quantifier* q) {
}
void bv_elim_star::reduce1_quantifier(quantifier* q) {
quantifier_ref r(m_manager);
proof_ref pr(m_manager);
quantifier_ref r(m);
proof_ref pr(m);
m_bv_elim.elim(q, r);
if (m_manager.fine_grain_proofs()) {
pr = m_manager.mk_rewrite(q, r.get());
if (m.fine_grain_proofs()) {
pr = m.mk_rewrite(q, r.get());
}
else {
pr = 0;

View file

@ -81,6 +81,8 @@ bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr *
}
UNREACHABLE();
}
case OP_DT_UPDATE_FIELD:
return false;
default:
UNREACHABLE();
}

View file

@ -203,20 +203,20 @@ void elim_bounds_star::reduce1_quantifier(quantifier * q) {
cache_result(q, q, 0);
return;
}
quantifier_ref new_q(m_manager);
quantifier_ref new_q(m);
expr * new_body = 0;
proof * new_pr;
get_cached(q->get_expr(), new_body, new_pr);
new_q = m_manager.update_quantifier(q, new_body);
expr_ref r(m_manager);
new_q = m.update_quantifier(q, new_body);
expr_ref r(m);
m_elim(new_q, r);
if (q == r.get()) {
cache_result(q, q, 0);
return;
}
proof_ref pr(m_manager);
if (m_manager.fine_grain_proofs())
pr = m_manager.mk_rewrite(q, r); // TODO: improve justification
proof_ref pr(m);
if (m.fine_grain_proofs())
pr = m.mk_rewrite(q, r); // TODO: improve justification
cache_result(q, r, pr);
}

View file

@ -187,7 +187,7 @@ pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s):
bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) {
if (is_app(n) && is_target(to_app(n))) {
app_ref tmp(m_manager);
app_ref tmp(m);
m_proc(to_app(n), tmp, p);
r = tmp;
return true;
@ -199,10 +199,10 @@ bool pull_cheap_ite_tree_star::is_target(app * n) const {
bool r =
n->get_num_args() == 2 &&
n->get_family_id() != null_family_id &&
m_manager.is_bool(n) &&
(m_manager.is_value(n->get_arg(0)) || m_manager.is_value(n->get_arg(1))) &&
(m_manager.is_term_ite(n->get_arg(0)) || m_manager.is_term_ite(n->get_arg(1)));
TRACE("pull_ite_target", tout << mk_pp(n, m_manager) << "\nresult: " << r << "\n";);
m.is_bool(n) &&
(m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) &&
(m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1)));
TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";);
return r;
}

View file

@ -34,7 +34,7 @@ push_app_ite::~push_app_ite() {
int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) {
for (unsigned i = 0; i < num_args; i++)
if (m_manager.is_ite(args[i]))
if (m.is_ite(args[i]))
return i;
return -1;
}
@ -53,10 +53,10 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg
expr ** args_prime = const_cast<expr**>(args);
expr * old = args_prime[ite_arg_idx];
args_prime[ite_arg_idx] = t;
expr_ref t_new(m_manager);
expr_ref t_new(m);
apply(decl, num_args, args_prime, t_new);
args_prime[ite_arg_idx] = e;
expr_ref e_new(m_manager);
expr_ref e_new(m);
apply(decl, num_args, args_prime, e_new);
args_prime[ite_arg_idx] = old;
expr * new_args[3] = { c, t_new, e_new };
@ -67,11 +67,11 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg
\brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
*/
bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
if (m_manager.is_ite(decl))
if (m.is_ite(decl))
return false;
bool found_ite = false;
for (unsigned i = 0; i < num_args; i++) {
if (m_manager.is_ite(args[i]) && !m_manager.is_bool(args[i])) {
if (m.is_ite(args[i]) && !m.is_bool(args[i])) {
if (found_ite) {
if (m_conservative)
return false;
@ -83,7 +83,7 @@ bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const *
}
CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n";
tout << decl->get_name();
for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m_manager);
for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m);
tout << "\n";);
return found_ite;
}
@ -94,19 +94,19 @@ void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) {
reduce_core(s);
get_cached(s, result, result_proof);
r = result;
switch (m_manager.proof_mode()) {
switch (m.proof_mode()) {
case PGM_DISABLED:
p = m_manager.mk_undef_proof();
p = m.mk_undef_proof();
break;
case PGM_COARSE:
if (result == s)
p = m_manager.mk_reflexivity(s);
p = m.mk_reflexivity(s);
else
p = m_manager.mk_rewrite_star(s, result, 0, 0);
p = m.mk_rewrite_star(s, result, 0, 0);
break;
case PGM_FINE:
if (result == s)
p = m_manager.mk_reflexivity(s);
p = m.mk_reflexivity(s);
else
p = result_proof;
break;
@ -171,24 +171,24 @@ void push_app_ite::reduce1_app(app * n) {
m_args.reset();
func_decl * decl = n->get_decl();
proof_ref p1(m_manager);
proof_ref p1(m);
get_args(n, m_args, p1);
expr_ref r(m_manager);
expr_ref r(m);
if (is_target(decl, m_args.size(), m_args.c_ptr()))
apply(decl, m_args.size(), m_args.c_ptr(), r);
else
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
if (!m_manager.fine_grain_proofs())
if (!m.fine_grain_proofs())
cache_result(n, r, 0);
else {
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
proof * p;
if (n == r)
p = 0;
else if (r != s)
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
else
p = p1;
cache_result(n, r, p);
@ -200,8 +200,8 @@ void push_app_ite::reduce1_quantifier(quantifier * q) {
proof * new_body_pr;
get_cached(q->get_expr(), new_body, new_body_pr);
quantifier * new_q = m_manager.update_quantifier(q, new_body);
proof * p = q == new_q ? 0 : m_manager.mk_quant_intro(q, new_q, new_body_pr);
quantifier * new_q = m.update_quantifier(q, new_body);
proof * p = q == new_q ? 0 : m.mk_quant_intro(q, new_q, new_body_pr);
cache_result(q, new_q, p);
}

View file

@ -61,16 +61,18 @@ void simplifier::enable_ac_support(bool flag) {
*/
void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
m_need_reset = true;
reinitialize();
expr * s_orig = s;
expr * old_s;
expr * result;
proof * result_proof;
switch (m_manager.proof_mode()) {
switch (m.proof_mode()) {
case PGM_DISABLED: // proof generation is disabled.
reduce_core(s);
// after executing reduce_core, the result of the simplification is in the cache
get_cached(s, result, result_proof);
r = result;
p = m_manager.mk_undef_proof();
p = m.mk_undef_proof();
break;
case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r.
m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst.
@ -78,10 +80,10 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
get_cached(s, result, result_proof);
r = result;
if (result == s)
p = m_manager.mk_reflexivity(s);
p = m.mk_reflexivity(s);
else {
remove_duplicates(m_subst_proofs);
p = m_manager.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
p = m.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
}
break;
case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described.
@ -90,17 +92,20 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
// keep simplyfing until no further simplifications are possible.
while (s != old_s) {
TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";);
TRACE("simplifier_loop", tout << mk_ll_pp(s, m_manager) << "\n";);
TRACE("simplifier_loop", tout << mk_ll_pp(s, m) << "\n";);
reduce_core(s);
get_cached(s, result, result_proof);
if (result_proof != 0)
SASSERT(is_rewrite_proof(s, result, result_proof));
if (result_proof != 0) {
m_proofs.push_back(result_proof);
}
old_s = s;
s = result;
}
SASSERT(s != 0);
r = s;
p = m_proofs.empty() ? m_manager.mk_reflexivity(s) : m_manager.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
p = m_proofs.empty() ? m.mk_reflexivity(s) : m.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
SASSERT(is_rewrite_proof(s_orig, r, p));
break;
default:
UNREACHABLE();
@ -259,9 +264,9 @@ void simplifier::reduce1(expr * n) {
specific simplifications via plugins.
*/
void simplifier::reduce1_app(app * n) {
expr_ref r(m_manager);
proof_ref p(m_manager);
TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m_manager) << "\n";);
expr_ref r(m);
proof_ref p(m);
TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m) << "\n";);
if (get_subst(n, r, p)) {
TRACE("reduce", tout << "applying substitution...\n";);
cache_result(n, r, p);
@ -279,7 +284,7 @@ void simplifier::reduce1_app(app * n) {
void simplifier::reduce1_app_core(app * n) {
m_args.reset();
func_decl * decl = n->get_decl();
proof_ref p1(m_manager);
proof_ref p1(m);
// Stores the new arguments of n in m_args.
// Let n be of the form
// (decl arg_0 ... arg_{n-1})
@ -296,23 +301,23 @@ void simplifier::reduce1_app_core(app * n) {
// If none of the arguments have been simplified, and n is not a theory symbol,
// Then no simplification is possible, and we can cache the result of the simplification of n as n.
if (has_new_args || decl->get_family_id() != null_family_id) {
expr_ref r(m_manager);
TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m_manager););
expr_ref r(m);
TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m););
// the method mk_app invokes get_subst and plugins to simplify
// (decl arg_0' ... arg_{n-1}')
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
if (!m_manager.fine_grain_proofs()) {
if (!m.fine_grain_proofs()) {
cache_result(n, r, 0);
}
else {
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
proof * p;
if (n == r)
p = 0;
else if (r != s)
// we use a "theory rewrite generic proof" to justify the step
// s = (decl arg_0' ... arg_{n-1}') --> r
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
else
p = p1;
cache_result(n, r, p);
@ -354,11 +359,11 @@ bool is_ac_vector(app * n) {
}
void simplifier::reduce1_ac_app_core(app * n) {
app_ref n_c(m_manager);
proof_ref p1(m_manager);
app_ref n_c(m);
proof_ref p1(m);
mk_ac_congruent_term(n, n_c, p1);
TRACE("ac", tout << "expr:\n" << mk_pp(n, m_manager) << "\ncongruent term:\n" << mk_pp(n_c, m_manager) << "\n";);
expr_ref r(m_manager);
TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";);
expr_ref r(m);
func_decl * decl = n->get_decl();
family_id fid = decl->get_family_id();
plugin * p = get_plugin(fid);
@ -376,7 +381,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
// done...
}
else {
r = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
r = m.mk_app(decl, m_args.size(), m_args.c_ptr());
}
}
else {
@ -385,7 +390,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
get_ac_args(n_c, m_args, m_mults);
TRACE("ac", tout << "AC args:\n";
for (unsigned i = 0; i < m_args.size(); i++) {
tout << mk_pp(m_args[i], m_manager) << " * " << m_mults[i] << "\n";
tout << mk_pp(m_args[i], m) << " * " << m_mults[i] << "\n";
});
if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) {
// done...
@ -393,12 +398,12 @@ void simplifier::reduce1_ac_app_core(app * n) {
else {
ptr_buffer<expr> new_args;
expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args);
r = m_manager.mk_app(decl, new_args.size(), new_args.c_ptr());
r = m.mk_app(decl, new_args.size(), new_args.c_ptr());
}
}
TRACE("ac", tout << "AC result:\n" << mk_pp(r, m_manager) << "\n";);
TRACE("ac", tout << "AC result:\n" << mk_pp(r, m) << "\n";);
if (!m_manager.fine_grain_proofs()) {
if (!m.fine_grain_proofs()) {
cache_result(n, r, 0);
}
else {
@ -406,7 +411,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
if (n == r.get())
p = 0;
else if (r.get() != n_c.get())
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(n_c, r));
p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r));
else
p = p1;
cache_result(n, r, p);
@ -416,8 +421,8 @@ void simplifier::reduce1_ac_app_core(app * n) {
static unsigned g_rewrite_lemma_id = 0;
void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) {
expr_ref arg(m_manager);
arg = m_manager.mk_app(decl, num_args, args);
expr_ref arg(m);
arg = m.mk_app(decl, num_args, args);
if (arg.get() != result) {
char buffer[128];
#ifdef _WINDOWS
@ -425,11 +430,11 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr *
#else
sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id);
#endif
ast_smt_pp pp(m_manager);
ast_smt_pp pp(m);
pp.set_benchmark_name("rewrite_lemma");
pp.set_status("unsat");
expr_ref n(m_manager);
n = m_manager.mk_not(m_manager.mk_eq(arg.get(), result));
expr_ref n(m);
n = m.mk_not(m.mk_eq(arg.get(), result));
std::ofstream out(buffer);
pp.display(out, n);
out.close();
@ -445,14 +450,14 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr *
*/
void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) {
m_need_reset = true;
if (m_manager.is_eq(decl)) {
sort * s = m_manager.get_sort(args[0]);
if (m.is_eq(decl)) {
sort * s = m.get_sort(args[0]);
plugin * p = get_plugin(s->get_family_id());
if (p != 0 && p->reduce_eq(args[0], args[1], result))
return;
}
else if (m_manager.is_distinct(decl)) {
sort * s = m_manager.get_sort(args[0]);
else if (m.is_distinct(decl)) {
sort * s = m.get_sort(args[0]);
plugin * p = get_plugin(s->get_family_id());
if (p != 0 && p->reduce_distinct(num_args, args, result))
return;
@ -464,7 +469,7 @@ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args
//dump_rewrite_lemma(decl, num_args, args, result.get());
return;
}
result = m_manager.mk_app(decl, num_args, args);
result = m.mk_app(decl, num_args, args);
}
/**
@ -484,7 +489,7 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
get_cached(arg, new_arg, arg_proof);
CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0),
tout << mk_ll_pp(arg, m_manager) << "\n---->\n" << mk_ll_pp(new_arg, m_manager) << "\n";
tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n";
tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n";
tout << arg << " " << new_arg << "\n";);
@ -500,11 +505,11 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
args.push_back(new_arg);
}
if (has_new_args) {
r = m_manager.mk_app(n->get_decl(), args.size(), args.c_ptr());
r = m.mk_app(n->get_decl(), args.size(), args.c_ptr());
if (m_use_oeq)
p = m_manager.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
p = m.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
else
p = m_manager.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
p = m.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
}
else {
r = n;
@ -523,8 +528,8 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
bool simplifier::get_args(app * n, ptr_vector<expr> & result, proof_ref & p) {
bool has_new_args = false;
unsigned num = n->get_num_args();
if (m_manager.fine_grain_proofs()) {
app_ref r(m_manager);
if (m.fine_grain_proofs()) {
app_ref r(m);
mk_congruent_term(n, r, p);
result.append(r->get_num_args(), r->get_args());
SASSERT(n->get_num_args() == result.size());
@ -582,7 +587,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_arg = true;
if (m_manager.fine_grain_proofs()) {
if (m.fine_grain_proofs()) {
proof * pr = 0;
m_ac_pr_cache.find(to_app(arg), pr);
if (pr != 0)
@ -601,7 +606,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_arg = true;
if (m_manager.fine_grain_proofs() && pr != 0)
if (m.fine_grain_proofs() && pr != 0)
new_arg_prs.push_back(pr);
}
}
@ -610,14 +615,14 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
todo.pop_back();
if (!has_new_arg) {
m_ac_cache.insert(curr, curr);
if (m_manager.fine_grain_proofs())
if (m.fine_grain_proofs())
m_ac_pr_cache.insert(curr, 0);
}
else {
app * new_curr = m_manager.mk_app(f, new_args.size(), new_args.c_ptr());
app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr());
m_ac_cache.insert(curr, new_curr);
if (m_manager.fine_grain_proofs()) {
proof * p = m_manager.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
if (m.fine_grain_proofs()) {
proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
m_ac_pr_cache.insert(curr, p);
}
}
@ -628,7 +633,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
app * new_n = 0;
m_ac_cache.find(n, new_n);
r = new_n;
if (m_manager.fine_grain_proofs()) {
if (m.fine_grain_proofs()) {
proof * new_pr = 0;
m_ac_pr_cache.find(n, new_pr);
p = new_pr;
@ -719,7 +724,7 @@ void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational>
SASSERT(!sorted_exprs.empty());
SASSERT(sorted_exprs[sorted_exprs.size()-1] == n);
TRACE("ac", tout << mk_ll_pp(n, m_manager, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
for (unsigned i = 0; i < sorted_exprs.size(); i++) {
tout << "#" << sorted_exprs[i]->get_id() << " ";
}
@ -754,10 +759,10 @@ void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational>
void simplifier::reduce1_quantifier(quantifier * q) {
expr * new_body;
proof * new_body_pr;
SASSERT(is_well_sorted(m_manager, q));
SASSERT(is_well_sorted(m, q));
get_cached(q->get_expr(), new_body, new_body_pr);
quantifier_ref q1(m_manager);
quantifier_ref q1(m);
proof * p1 = 0;
if (is_quantifier(new_body) &&
@ -774,7 +779,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts());
names.append(nested_q->get_num_decls(), nested_q->get_decl_names());
q1 = m_manager.mk_quantifier(q->is_forall(),
q1 = m.mk_quantifier(q->is_forall(),
sorts.size(),
sorts.c_ptr(),
names.c_ptr(),
@ -783,13 +788,13 @@ void simplifier::reduce1_quantifier(quantifier * q) {
q->get_qid(),
q->get_skid(),
0, 0, 0, 0);
SASSERT(is_well_sorted(m_manager, q1));
SASSERT(is_well_sorted(m, q1));
if (m_manager.fine_grain_proofs()) {
quantifier * q0 = m_manager.update_quantifier(q, new_body);
proof * p0 = q == q0 ? 0 : m_manager.mk_quant_intro(q, q0, new_body_pr);
p1 = m_manager.mk_pull_quant(q0, q1);
p1 = m_manager.mk_transitivity(p0, p1);
if (m.fine_grain_proofs()) {
quantifier * q0 = m.update_quantifier(q, new_body);
proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr);
p1 = m.mk_pull_quant(q0, q1);
p1 = m.mk_transitivity(p0, p1);
}
}
else {
@ -802,7 +807,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
unsigned num = q->get_num_patterns();
for (unsigned i = 0; i < num; i++) {
get_cached(q->get_pattern(i), new_pattern, new_pattern_pr);
if (m_manager.is_pattern(new_pattern)) {
if (m.is_pattern(new_pattern)) {
new_patterns.push_back(new_pattern);
}
}
@ -815,7 +820,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
remove_duplicates(new_patterns);
remove_duplicates(new_no_patterns);
q1 = m_manager.mk_quantifier(q->is_forall(),
q1 = m.mk_quantifier(q->is_forall(),
q->get_num_decls(),
q->get_decl_sorts(),
q->get_decl_names(),
@ -827,26 +832,26 @@ void simplifier::reduce1_quantifier(quantifier * q) {
new_patterns.c_ptr(),
new_no_patterns.size(),
new_no_patterns.c_ptr());
SASSERT(is_well_sorted(m_manager, q1));
SASSERT(is_well_sorted(m, q1));
TRACE("simplifier", tout << mk_pp(q, m_manager) << "\n" << mk_pp(q1, m_manager) << "\n";);
if (m_manager.fine_grain_proofs()) {
TRACE("simplifier", tout << mk_pp(q, m) << "\n" << mk_pp(q1, m) << "\n";);
if (m.fine_grain_proofs()) {
if (q != q1 && !new_body_pr) {
new_body_pr = m_manager.mk_rewrite(q->get_expr(), new_body);
new_body_pr = m.mk_rewrite(q->get_expr(), new_body);
}
p1 = q == q1 ? 0 : m_manager.mk_quant_intro(q, q1, new_body_pr);
p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr);
}
}
expr_ref r(m_manager);
elim_unused_vars(m_manager, q1, r);
expr_ref r(m);
elim_unused_vars(m, q1, r);
proof * pr = 0;
if (m_manager.fine_grain_proofs()) {
if (m.fine_grain_proofs()) {
proof * p2 = 0;
if (q1.get() != r.get())
p2 = m_manager.mk_elim_unused_vars(q1, r);
pr = m_manager.mk_transitivity(p1, p2);
p2 = m.mk_elim_unused_vars(q1, r);
pr = m.mk_transitivity(p1, p2);
}
cache_result(q, r, pr);
@ -892,7 +897,7 @@ bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
m_subst_map->get(n, _r, _p);
r = _r;
p = _p;
if (m_manager.coarse_grain_proofs())
if (m.coarse_grain_proofs())
m_subst_proofs.push_back(p);
return true;
}

View file

@ -210,7 +210,7 @@ public:
plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); }
ast_manager & get_manager() { return m_manager; }
ast_manager & get_manager() { return m; }
void borrow_plugins(simplifier const & s);
void release_plugins();

View file

@ -793,8 +793,10 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
}
else {
TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";);
if (!st(n->m_expr))
if (!st(n->m_expr)) {
clear_stack();
return false;
}
if (!backtrack())
break;
}
@ -806,12 +808,16 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
else if (!backtrack())
break;
}
clear_stack();
return true;
}
void substitution_tree::clear_stack() {
while (!m_bstack.empty()) {
m_subst->pop_scope();
m_bstack.pop_back();
}
m_subst->pop_scope();
return true;
}
template<substitution_tree::st_visit_mode Mode>

View file

@ -123,6 +123,8 @@ class substitution_tree {
template<st_visit_mode Mode>
void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset);
void clear_stack();
public:
substitution_tree(ast_manager & m);
~substitution_tree();

View file

@ -25,6 +25,7 @@ Notes:
#include"datatype_decl_plugin.h"
#include"seq_decl_plugin.h"
#include"float_decl_plugin.h"
#include"pb_decl_plugin.h"
#include"ast_pp.h"
#include"var_subst.h"
#include"pp.h"
@ -314,8 +315,9 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
m_numeral_as_real(false),
m_ignore_check(false),
m_exit_on_error(false),
m_manager(m),
m_manager(m),
m_own_manager(m == 0),
m_manager_initialized(false),
m_pmanager(0),
m_sexpr_manager(0),
m_regular("stdout", std::cout),
@ -326,8 +328,6 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
install_core_tactic_cmds(*this);
install_interpolant_cmds(*this);
SASSERT(m != 0 || !has_manager());
if (m)
init_external_manager();
if (m_main_ctx) {
set_verbose_stream(diagnostic_stream());
}
@ -337,10 +337,10 @@ cmd_context::~cmd_context() {
if (m_main_ctx) {
set_verbose_stream(std::cerr);
}
reset(true);
finalize_cmds();
finalize_tactic_cmds();
finalize_probes();
reset(true);
m_solver = 0;
m_check_sat_result = 0;
}
@ -358,6 +358,18 @@ void cmd_context::set_cancel(bool f) {
m().set_cancel(f);
}
opt_wrapper* cmd_context::get_opt() {
return m_opt.get();
}
void cmd_context::set_opt(opt_wrapper* opt) {
m_opt = opt;
for (unsigned i = 0; i < m_scopes.size(); ++i) {
m_opt->push();
}
m_opt->set_logic(m_logic);
}
void cmd_context::global_params_updated() {
m_params.updt_params();
if (m_params.m_smtlib2_compliant)
@ -471,6 +483,16 @@ void cmd_context::register_plugin(symbol const & name, decl_plugin * p, bool ins
}
}
void cmd_context::load_plugin(symbol const & name, bool install, svector<family_id>& fids) {
family_id id = m_manager->get_family_id(name);
decl_plugin* p = m_manager->get_plugin(id);
if (install && p && fids.contains(id)) {
register_builtin_sorts(p);
register_builtin_ops(p);
}
fids.erase(id);
}
bool cmd_context::logic_has_arith_core(symbol const & s) const {
return
s == "QF_LRA" ||
@ -551,6 +573,7 @@ bool cmd_context::logic_has_floats() const {
return !has_logic() || m_logic == "QF_FPA" || m_logic == "QF_FPABV";
}
bool cmd_context::logic_has_array_core(symbol const & s) const {
return
s == "QF_AX" ||
@ -587,17 +610,27 @@ void cmd_context::init_manager_core(bool new_manager) {
register_builtin_sorts(basic);
register_builtin_ops(basic);
// the manager was created by the command context.
register_plugin(symbol("arith"), alloc(arith_decl_plugin), logic_has_arith());
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("arith"), alloc(arith_decl_plugin), logic_has_arith());
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("seq"), alloc(seq_decl_plugin), logic_has_seq());
register_plugin(symbol("float"), alloc(float_decl_plugin), logic_has_floats());
register_plugin(symbol("pb"), alloc(pb_decl_plugin), !has_logic());
}
else {
// the manager was created by an external module, we must register all plugins available in the manager.
// the manager was created by an external module
// we register all plugins available in the manager.
// unless the logic specifies otherwise.
svector<family_id> fids;
m_manager->get_range(fids);
load_plugin(symbol("arith"), logic_has_arith(), fids);
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("seq"), logic_has_seq(), fids);
load_plugin(symbol("float"), logic_has_floats(), fids);
svector<family_id>::iterator it = fids.begin();
svector<family_id>::iterator end = fids.end();
for (; it != end; ++it) {
@ -620,12 +653,22 @@ void cmd_context::init_manager_core(bool new_manager) {
}
void cmd_context::init_manager() {
SASSERT(m_manager == 0);
SASSERT(m_pmanager == 0);
m_check_sat_result = 0;
m_manager = m_params.mk_ast_manager();
m_pmanager = alloc(pdecl_manager, *m_manager);
init_manager_core(true);
if (m_manager_initialized) {
// no-op
}
else if (m_manager) {
m_manager_initialized = true;
SASSERT(!m_own_manager);
init_external_manager();
}
else {
m_manager_initialized = true;
SASSERT(m_pmanager == 0);
m_check_sat_result = 0;
m_manager = m_params.mk_ast_manager();
m_pmanager = alloc(pdecl_manager, *m_manager);
init_manager_core(true);
}
}
void cmd_context::init_external_manager() {
@ -708,7 +751,7 @@ void cmd_context::insert(symbol const & s, func_decl * f) {
if (!m_global_decls) {
m_func_decls_stack.push_back(sf_pair(s, f));
}
TRACE("cmd_context", tout << "new sort decl\n" << mk_pp(f, m()) << "\n";);
TRACE("cmd_context", tout << "new function decl\n" << mk_pp(f, m()) << "\n";);
}
void cmd_context::insert(symbol const & s, psort_decl * p) {
@ -1141,7 +1184,6 @@ void cmd_context::insert_aux_pdecl(pdecl * p) {
}
void cmd_context::reset(bool finalize) {
m_check_sat_result = 0;
m_logic = symbol::null;
m_check_sat_result = 0;
m_numeral_as_real = false;
@ -1157,6 +1199,7 @@ void cmd_context::reset(bool finalize) {
restore_assertions(0);
if (m_solver)
m_solver = 0;
m_opt = 0;
m_pp_env = 0;
m_dt_eh = 0;
if (m_manager) {
@ -1165,12 +1208,15 @@ void cmd_context::reset(bool finalize) {
if (m_own_manager) {
dealloc(m_manager);
m_manager = 0;
m_manager_initialized = false;
}
else {
// doesn't own manager... so it cannot be deleted
// reinit cmd_context if this is not a finalization step
if (!finalize)
init_external_manager();
else
m_manager_initialized = false;
}
}
if (m_sexpr_manager) {
@ -1211,8 +1257,7 @@ void cmd_context::assert_expr(symbol const & name, expr * t) {
void cmd_context::push() {
m_check_sat_result = 0;
if (!has_manager())
init_manager();
init_manager();
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_func_decls_stack_lim = m_func_decls_stack.size();
@ -1222,6 +1267,8 @@ void cmd_context::push() {
s.m_assertions_lim = m_assertions.size();
if (m_solver)
m_solver->push();
if (m_opt)
m_opt->push();
}
void cmd_context::push(unsigned n) {
@ -1317,6 +1364,8 @@ void cmd_context::pop(unsigned n) {
if (m_solver) {
m_solver->pop(n);
}
if (m_opt)
m_opt->pop(n);
unsigned new_lvl = lvl - n;
scope & s = m_scopes[new_lvl];
restore_func_decls(s.m_func_decls_stack_lim);
@ -1332,17 +1381,49 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions
return;
IF_VERBOSE(100, verbose_stream() << "(started \"check-sat\")" << std::endl;);
TRACE("before_check_sat", dump_assertions(tout););
if (!has_manager())
init_manager();
if (m_solver) {
init_manager();
unsigned timeout = m_params.m_timeout;
scoped_watch sw(*this);
lbool r;
if (m_opt && !m_opt->empty()) {
bool was_pareto = false;
m_check_sat_result = get_opt();
cancel_eh<opt_wrapper> eh(*get_opt());
scoped_ctrl_c ctrlc(eh);
scoped_timer timer(timeout, &eh);
ptr_vector<expr> cnstr(m_assertions);
cnstr.append(num_assumptions, assumptions);
get_opt()->set_hard_constraints(cnstr);
try {
r = get_opt()->optimize();
while (r == l_true && get_opt()->is_pareto()) {
was_pareto = true;
get_opt()->display_assignment(regular_stream());
regular_stream() << "\n";
r = get_opt()->optimize();
}
}
catch (z3_error & ex) {
throw ex;
}
catch (z3_exception & ex) {
throw cmd_exception(ex.msg());
}
if (was_pareto && r == l_false) {
r = l_true;
}
get_opt()->set_status(r);
if (r != l_false && !was_pareto) {
get_opt()->display_assignment(regular_stream());
}
}
else if (m_solver) {
m_check_sat_result = m_solver.get(); // solver itself stores the result.
m_solver->set_progress_callback(this);
unsigned timeout = m_params.m_timeout;
scoped_watch sw(*this);
cancel_eh<solver> eh(*m_solver);
scoped_ctrl_c ctrlc(eh);
scoped_timer timer(timeout, &eh);
lbool r;
try {
r = m_solver->check_sat(num_assumptions, assumptions);
}
@ -1353,15 +1434,17 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions
throw cmd_exception(ex.msg());
}
m_solver->set_status(r);
display_sat_result(r);
validate_check_sat_result(r);
if (r == l_true)
validate_model();
}
else {
// There is no solver installed in the command context.
regular_stream() << "unknown" << std::endl;
return;
}
display_sat_result(r);
validate_check_sat_result(r);
if (r == l_true)
validate_model();
}
void cmd_context::display_sat_result(lbool r) {
@ -1538,6 +1621,9 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) {
else if (m_solver) {
m_solver->collect_statistics(st);
}
else if (m_opt) {
m_opt->collect_statistics(st);
}
st.display_smt2(regular_stream());
}

View file

@ -111,6 +111,21 @@ struct builtin_decl {
builtin_decl(family_id fid, decl_kind k, builtin_decl * n = 0):m_fid(fid), m_decl(k), m_next(n) {}
};
class opt_wrapper : public check_sat_result {
public:
virtual bool empty() = 0;
virtual void push() = 0;
virtual void pop(unsigned n) = 0;
virtual void set_cancel(bool f) = 0;
virtual void reset_cancel() = 0;
virtual void cancel() = 0;
virtual lbool optimize() = 0;
virtual void set_hard_constraints(ptr_vector<expr> & hard) = 0;
virtual void display_assignment(std::ostream& out) = 0;
virtual bool is_pareto() = 0;
virtual void set_logic(symbol const& s) = 0;
};
class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context {
public:
enum status {
@ -149,6 +164,7 @@ protected:
ast_manager * m_manager;
bool m_own_manager;
bool m_manager_initialized;
pdecl_manager * m_pmanager;
sexpr_manager * m_sexpr_manager;
check_logic m_check_logic;
@ -187,8 +203,9 @@ protected:
svector<scope> m_scopes;
scoped_ptr<solver_factory> m_solver_factory;
scoped_ptr<solver_factory> m_interpolating_solver_factory;
ref<solver> m_solver;
ref<solver> m_solver;
ref<check_sat_result> m_check_sat_result;
ref<opt_wrapper> m_opt;
stopwatch m_watch;
@ -212,7 +229,7 @@ protected:
void register_builtin_sorts(decl_plugin * p);
void register_builtin_ops(decl_plugin * p);
void register_plugin(symbol const & name, decl_plugin * p, bool install_names);
void load_plugin(symbol const & name, bool install_names, svector<family_id>& fids);
void init_manager_core(bool new_manager);
void init_manager();
void init_external_manager();
@ -256,6 +273,8 @@ public:
context_params & params() { return m_params; }
solver_factory &get_solver_factory() { return *m_solver_factory; }
solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; }
opt_wrapper* get_opt();
void set_opt(opt_wrapper* o);
void global_params_updated(); // this method should be invoked when global (and module) params are updated.
bool set_logic(symbol const & s);
bool has_logic() const { return m_logic != symbol::null; }
@ -293,7 +312,7 @@ public:
std::string reason_unknown() const;
bool has_manager() const { return m_manager != 0; }
ast_manager & m() const { if (!m_manager) const_cast<cmd_context*>(this)->init_manager(); return *m_manager; }
ast_manager & m() const { const_cast<cmd_context*>(this)->init_manager(); return *m_manager; }
virtual ast_manager & get_ast_manager() { return m(); }
pdecl_manager & pm() const { if (!m_pmanager) const_cast<cmd_context*>(this)->init_manager(); return *m_pmanager; }
sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast<cmd_context*>(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; }
@ -304,7 +323,8 @@ public:
check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); }
check_sat_state cs_state() const;
void validate_model();
void register_plugin(symbol const & name, decl_plugin * p, bool install_names);
bool is_func_decl(symbol const & s) const;
bool is_sort_decl(symbol const& s) const { return m_psort_decls.contains(s); }
void insert(cmd * c);

View file

@ -83,13 +83,13 @@ void context_params::set(char const * param, char const * value) {
set_bool(m_smtlib2_compliant, param, value);
}
else {
param_descrs d;
collect_param_descrs(d);
std::stringstream strm;
strm << "unknown parameter '" << p << "'\n";
strm << "Legal parameters are:\n";
d.display(strm, 2, false, false);
throw default_exception(strm.str());
param_descrs d;
collect_param_descrs(d);
std::stringstream strm;
strm << "unknown parameter '" << p << "'\n";
strm << "Legal parameters are:\n";
d.display(strm, 2, false, false);
throw default_exception(strm.str());
}
}

4
src/duality/duality.h Executable file → Normal file
View file

@ -778,6 +778,10 @@ protected:
struct Bad {
};
// thrown on more serious internal error
struct ReallyBad {
};
/** Pop a scope (see Push). Note, you cannot pop axioms. */
void Pop(int num_scopes);

View file

@ -2643,6 +2643,10 @@ namespace Duality {
GetGroundLitsUnderQuants(memo,f.body(),res,1);
return;
}
if(f.is_var()){
// std::cout << "foo!\n";
return;
}
if(under && f.is_ground())
res.push_back(f);
}
@ -3065,10 +3069,14 @@ namespace Duality {
node->Annotation.SetEmpty();
hash_set<ast> *core = new hash_set<ast>;
core->insert(node->Outgoing->dual);
expr prev_annot = ctx.bool_val(false);
expr prev_impl = ctx.bool_val(false);
int repeated_case_count = 0;
while(1){
by_case_counter++;
is.push();
expr annot = !GetAnnotation(node);
Transformer old_annot = node->Annotation;
is.add(annot);
if(is.check() == unsat){
is.pop(1);
@ -3076,56 +3084,70 @@ namespace Duality {
}
is.pop(1);
Push();
ConstrainEdgeLocalized(node->Outgoing,is.get_implicant());
expr the_impl = is.get_implicant();
if(eq(the_impl,prev_impl)){
// std::cout << "got old implicant\n";
repeated_case_count++;
}
prev_impl = the_impl;
ConstrainEdgeLocalized(node->Outgoing,the_impl);
ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this?
check_result foo = Check(root);
if(foo != unsat){
slvr().print("should_be_unsat.smt2");
throw "should be unsat";
}
std::vector<expr> assumps, axioms_to_add;
slvr().get_proof().get_assumptions(assumps);
for(unsigned i = 0; i < assumps.size(); i++){
(*core).insert(assumps[i]);
if(axioms_needed.find(assumps[i]) != axioms_needed.end()){
axioms_to_add.push_back(assumps[i]);
axioms_needed.erase(assumps[i]);
}
}
// AddToProofCore(*core);
Transformer old_annot = node->Annotation;
SolveSingleNode(root,node);
{
expr itp = GetAnnotation(node);
dualModel = is.get_model(); // TODO: what does this mean?
std::vector<expr> case_lits;
itp = StrengthenFormulaByCaseSplitting(itp, case_lits);
SetAnnotation(node,itp);
node->Annotation.Formula = node->Annotation.Formula.simplify();
}
for(unsigned i = 0; i < axioms_to_add.size(); i++)
is.add(axioms_to_add[i]);
#define TEST_BAD
#ifdef TEST_BAD
{
static int bad_count = 0, num_bads = 1;
if(bad_count >= num_bads){
bad_count = 0;
num_bads = num_bads * 2;
check_result foo = Check(root);
if(foo != unsat){
Pop(1);
is.pop(1);
delete core;
timer_stop("InterpolateByCases");
throw Bad();
throw ReallyBad();
// slvr().print("should_be_unsat.smt2");
// throw "should be unsat";
}
bad_count++;
}
#endif
std::vector<expr> assumps, axioms_to_add;
slvr().get_proof().get_assumptions(assumps);
for(unsigned i = 0; i < assumps.size(); i++){
(*core).insert(assumps[i]);
if(axioms_needed.find(assumps[i]) != axioms_needed.end()){
axioms_to_add.push_back(assumps[i]);
axioms_needed.erase(assumps[i]);
}
}
// AddToProofCore(*core);
SolveSingleNode(root,node);
if(node->Annotation.IsEmpty()){
{
expr itp = GetAnnotation(node);
dualModel = is.get_model(); // TODO: what does this mean?
std::vector<expr> case_lits;
itp = StrengthenFormulaByCaseSplitting(itp, case_lits);
SetAnnotation(node,itp);
node->Annotation.Formula = node->Annotation.Formula.simplify();
}
for(unsigned i = 0; i < axioms_to_add.size(); i++)
is.add(axioms_to_add[i]);
#define TEST_BAD
#ifdef TEST_BAD
{
static int bad_count = 0, num_bads = 1;
if(bad_count >= num_bads){
bad_count = 0;
num_bads = num_bads * 2;
Pop(1);
is.pop(1);
delete core;
timer_stop("InterpolateByCases");
throw Bad();
}
bad_count++;
}
#endif
}
if(node->Annotation.IsEmpty() || eq(node->Annotation.Formula,prev_annot) || (repeated_case_count > 0 && !axioms_added) || (repeated_case_count >= 10)){
// looks_bad:
if(!axioms_added){
// add the axioms in the off chance they are useful
const std::vector<expr> &theory = ls->get_axioms();
@ -3134,6 +3156,7 @@ namespace Duality {
axioms_added = true;
}
else {
//#define KILL_ON_BAD_INTERPOLANT
#ifdef KILL_ON_BAD_INTERPOLANT
std::cout << "bad in InterpolateByCase -- core:\n";
#if 0
@ -3175,10 +3198,11 @@ namespace Duality {
is.pop(1);
delete core;
timer_stop("InterpolateByCases");
throw Bad();
throw ReallyBad();
}
}
Pop(1);
prev_annot = node->Annotation.Formula;
node->Annotation.UnionWith(old_annot);
}
if(proof_core)

12
src/duality/duality_solver.cpp Executable file → Normal file
View file

@ -2279,6 +2279,18 @@ namespace Duality {
// bad interpolants can get us here
throw DoRestart();
}
catch(const RPFP::ReallyBad &){
// this could be caused by incompleteness
for(unsigned i = 0; i < expansions.size(); i++){
Node *node = expansions[i];
node->map->Annotation.SetFull();
std::vector<Node *> &chs = node->map->Outgoing->Children;
for(unsigned j = 0; j < chs.size(); j++)
chs[j]->Annotation.SetFull();
reporter->Message("incompleteness: cleared annotation and child annotations");
}
throw DoRestart();
}
catch(char const *msg){
// bad interpolants can get us here
reporter->Message(std::string("interpolation failure:") + msg);

1
src/duality/duality_wrapper.h Executable file → Normal file
View file

@ -1489,4 +1489,3 @@ namespace std {
}
#endif

View file

@ -161,7 +161,7 @@ class iz3base : public iz3mgr, public scopes {
stl_ext::hash_map<ast,ast> simplify_memo;
stl_ext::hash_map<ast,int> frame_map; // map assertions to frames
int frames; // number of frames
// int frames; // number of frames
protected:
void add_frame_range(int frame, ast t);

View file

@ -56,7 +56,7 @@ namespace hash_space {
class hash<std::string> {
public:
size_t operator()(const std::string &s) const {
return string_hash(s.c_str(), s.size(), 0);
return string_hash(s.c_str(), static_cast<unsigned>(s.size()), 0);
}
};
@ -111,7 +111,7 @@ namespace hash_space {
4294967291ul
};
inline unsigned long next_prime(unsigned long n) {
inline size_t next_prime(size_t n) {
const unsigned long* to = primes + (int)num_primes;
for(const unsigned long* p = primes; p < to; p++)
if(*p >= n) return *p;
@ -378,7 +378,7 @@ namespace hash_space {
void resize(size_t new_size) {
const size_t old_n = buckets.size();
if (new_size <= old_n) return;
const size_t n = next_prime(new_size);
const size_t n = next_prime(static_cast<unsigned>(new_size));
if (n <= old_n) return;
Table tmp(n, (Entry*)(0));
for (size_t i = 0; i < old_n; ++i) {

View file

@ -778,6 +778,8 @@ int iz3mgr::occurs_in(ast var, ast e){
bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){
if(occurs_in(v,y))
return false;
if(op(x) == Plus){
int n = num_args(x);
for(int i = 0; i < n; i++){
@ -801,8 +803,8 @@ iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set<ast> &cont_eq_memo, bool truth, as
return ast();
cont_eq_memo.insert(e);
if(!truth && op(e) == Equal){
if(arg(e,0) == v) return(arg(e,1));
if(arg(e,1) == v) return(arg(e,0));
if(arg(e,0) == v && !occurs_in(v,arg(e,1))) return(arg(e,1));
if(arg(e,1) == v && !occurs_in(v,arg(e,0))) return(arg(e,0));
ast res;
if(solve_arith(v,arg(e,0),arg(e,1),res)) return res;
if(solve_arith(v,arg(e,1),arg(e,0),res)) return res;

View file

@ -278,7 +278,8 @@ class iz3mgr {
}
symb sym(ast t){
return to_app(t.raw())->get_decl();
raw_ast *_ast = t.raw();
return is_app(_ast) ? to_app(_ast)->get_decl() : 0;
}
std::string string_of_symbol(symb s){

View file

@ -1027,7 +1027,7 @@ class iz3proof_itp_impl : public iz3proof_itp {
linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1)));
}
else {
ast pf = extract_rewrites(make(concat,mk_true(),rest),p1);
ast pf = extract_rewrites(make(concat,mk_true(),last),p1);
ast new_normal = fix_normal(term1,term2,pf);
normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves);
}
@ -2747,7 +2747,8 @@ class iz3proof_itp_impl : public iz3proof_itp {
ast orig_e = e;
pf = make_refl(e); // proof that e = e
prover::range erng = pv->ast_scope(e);
// prover::range erng =
pv->ast_scope(e);
#if 0
if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){
return e; // this term occurs in range, so it's O.K.

View file

@ -1712,11 +1712,17 @@ public:
std::cout << "foo!\n";
// no idea why this shows up
if(dk == PR_MODUS_PONENS_OEQ)
if(dk == PR_MODUS_PONENS_OEQ){
if(conc(prem(proof,0)) == con){
res = translate_main(prem(proof,0),expect_clause);
return res;
}
if(expect_clause && op(con) == Or){ // skolemization does this
Iproof::node clause = translate_main(prem(proof,0),true);
res = RewriteClause(clause,prem(proof,1));
return res;
}
}
#if 0
if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){
@ -1800,7 +1806,9 @@ public:
}
break;
}
case PR_MONOTONICITY: {
case PR_QUANT_INTRO:
case PR_MONOTONICITY:
{
std::vector<ast> eqs; eqs.resize(args.size());
for(unsigned i = 0; i < args.size(); i++)
eqs[i] = conc(prem(proof,i));

View file

@ -0,0 +1,200 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
network_flow.h
Abstract:
Implements Network Simplex algorithm for min cost flow problem
Author:
Anh-Dung Phan (t-anphan) 2013-10-24
Notes:
This will be used to solve the dual of min cost flow problem
i.e. optimization of difference constraint.
We need a function to reduce DL constraints to min cost flow problem
and another function to convert from min cost flow solution to DL solution.
It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex.
A naive approach is to run an algorithm on max flow in order to get a spanning tree.
--*/
#ifndef _NETWORK_FLOW_H_
#define _NETWORK_FLOW_H_
#include"inf_rational.h"
#include"diff_logic.h"
#include"spanning_tree.h"
namespace smt {
enum min_flow_result {
// Min cost flow problem is infeasible.
// Diff logic optimization could be unbounded or infeasible.
INFEASIBLE,
// Min cost flow and diff logic optimization are both optimal.
OPTIMAL,
// Min cost flow problem is unbounded.
// Diff logic optimization has to be infeasible.
UNBOUNDED,
};
enum pivot_rule {
// First eligible edge pivot rule
// Edges are traversed in a wraparound fashion
FIRST_ELIGIBLE,
// Best eligible edge pivot rule
// The best edge is selected in every iteration
BEST_ELIGIBLE,
// Candidate list pivot rule
// Major iterations: candidate list is built from eligible edges (in a wraparound way)
// Minor iterations: the best edge is selected from the list
CANDIDATE_LIST
};
// Solve minimum cost flow problem using Network Simplex algorithm
template<typename Ext>
class network_flow : private Ext {
private:
enum edge_state {
LOWER = 1,
BASIS = 0,
};
typedef dl_var node;
typedef dl_edge<Ext> edge;
typedef dl_graph<Ext> graph;
typedef typename Ext::numeral numeral;
typedef typename Ext::fin_numeral fin_numeral;
class pivot_rule_impl {
protected:
graph & m_graph;
svector<edge_state> & m_states;
vector<numeral> & m_potentials;
edge_id & m_enter_id;
bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; }
public:
pivot_rule_impl(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id)
: m_graph(g),
m_potentials(potentials),
m_states(states),
m_enter_id(enter_id) {
}
virtual ~pivot_rule_impl() {}
virtual bool choose_entering_edge() = 0;
virtual pivot_rule rule() const = 0;
};
class first_eligible_pivot : public pivot_rule_impl {
edge_id m_next_edge;
public:
first_eligible_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id),
m_next_edge(0) {
}
virtual bool choose_entering_edge();
virtual pivot_rule rule() const { return FIRST_ELIGIBLE; }
};
class best_eligible_pivot : public pivot_rule_impl {
public:
best_eligible_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id) {
}
virtual pivot_rule rule() const { return BEST_ELIGIBLE; }
virtual bool choose_entering_edge();
};
class candidate_list_pivot : public pivot_rule_impl {
private:
edge_id m_next_edge;
svector<edge_id> m_candidates;
unsigned m_num_candidates;
unsigned m_minor_step;
unsigned m_current_length;
static const unsigned NUM_CANDIDATES = 10;
static const unsigned MINOR_STEP_LIMIT = 5;
public:
candidate_list_pivot(graph & g, vector<numeral> & potentials,
svector<edge_state> & states, edge_id & enter_id) :
pivot_rule_impl(g, potentials, states, enter_id),
m_next_edge(0),
m_minor_step(0),
m_current_length(0),
m_num_candidates(NUM_CANDIDATES),
m_candidates(m_num_candidates) {
}
virtual pivot_rule rule() const { return CANDIDATE_LIST; }
virtual bool choose_entering_edge();
};
graph m_graph;
scoped_ptr<spanning_tree_base> m_tree;
scoped_ptr<pivot_rule_impl> m_pivot;
vector<fin_numeral> m_balances; // nodes + 1 |-> [b -1b] Denote supply/demand b_i on node i
vector<numeral> m_potentials; // nodes + 1 |-> initial: +/- 1
// Duals of flows which are convenient to compute dual solutions
// become solutions to Dual simplex.
vector<numeral> m_flows; // edges + nodes |-> assignemnt Basic feasible flows
svector<edge_state> m_states;
unsigned m_step;
edge_id m_enter_id;
edge_id m_leave_id;
optional<numeral> m_delta;
// Initialize the network with a feasible spanning tree
void initialize();
void update_potentials();
void update_flows();
bool choose_entering_edge(pivot_rule pr);
// Send as much flow as possible around the cycle, the first basic edge with flow 0 will leave
// Return false if the problem is unbounded
bool choose_leaving_edge();
void update_spanning_tree();
numeral get_cost() const;
bool edge_in_tree(edge_id id) const;
bool is_infeasible();
bool check_well_formed();
bool check_optimal();
void display_primal(std::ofstream & os);
void display_dual(std::ofstream & os);
void display_spanning_tree(std::ofstream & os);
void display_system(std::ofstream & os);
public:
network_flow(graph & g, vector<fin_numeral> const & balances);
// Minimize cost flows
// Return true if found an optimal solution, and return false if unbounded
min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE);
// Compute the optimal solution
numeral get_optimal_solution(vector<numeral> & result, bool is_dual);
};
}
#endif

View file

@ -0,0 +1,526 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
network_flow_def.h
Abstract:
Implements Network Simplex algorithm for min cost flow problem
Author:
Anh-Dung Phan (t-anphan) 2013-10-24
Notes:
--*/
#ifndef _NETWORK_FLOW_DEF_H_
#define _NETWORK_FLOW_DEF_H_
#include"network_flow.h"
#include"uint_set.h"
#include"spanning_tree_def.h"
namespace smt {
template<typename Ext>
bool network_flow<Ext>::first_eligible_pivot::choose_entering_edge() {
numeral cost = numeral::zero();
int num_edges = m_graph.get_num_edges();
for (int i = m_next_edge; i < m_next_edge + num_edges; ++i) {
edge_id id = i % num_edges;
if (edge_in_tree(id)) {
continue;
}
node src = m_graph.get_source(id);
node tgt = m_graph.get_target(id);
cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
if (cost.is_pos()) {
m_next_edge = m_enter_id = id;
return true;
}
}
return false;
};
template<typename Ext>
bool network_flow<Ext>::best_eligible_pivot::choose_entering_edge() {
unsigned num_edges = m_graph.get_num_edges();
numeral cost = numeral::zero();
for (unsigned i = 0; i < num_edges; ++i) {
node src = m_graph.get_source(i);
node tgt = m_graph.get_target(i);
if (!edge_in_tree(i)) {
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(i);
if (new_cost > cost) {
cost = new_cost;
m_enter_id = i;
}
}
}
return cost.is_pos();
};
template<typename Ext>
bool network_flow<Ext>::candidate_list_pivot::choose_entering_edge() {
numeral cost = numeral::zero();
if (m_current_length == 0 || m_minor_step == MINOR_STEP_LIMIT) {
// Build the candidate list
unsigned num_edges = m_graph.get_num_edges();
m_current_length = 0;
for (unsigned i = m_next_edge; i < m_next_edge + num_edges; ++i) {
edge_id id = (i >= num_edges) ? i - num_edges : i;
node src = m_graph.get_source(id);
node tgt = m_graph.get_target(id);
if (!edge_in_tree(id)) {
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
if (new_cost.is_pos()) {
m_candidates[m_current_length] = id;
++m_current_length;
if (new_cost > cost) {
cost = new_cost;
m_enter_id = id;
}
}
if (m_current_length >= m_num_candidates) break;
}
}
m_next_edge = m_enter_id;
m_minor_step = 1;
return cost.is_pos();
}
++m_minor_step;
for (unsigned i = 0; i < m_current_length; ++i) {
edge_id id = m_candidates[i];
node src = m_graph.get_source(id);
node tgt = m_graph.get_target(id);
if (!edge_in_tree(id)) {
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
if (new_cost > cost) {
cost = new_cost;
m_enter_id = id;
}
// Remove stale candidates
if (!new_cost.is_pos()) {
--m_current_length;
m_candidates[i] = m_candidates[m_current_length];
--i;
}
}
}
return cost.is_pos();
};
template<typename Ext>
network_flow<Ext>::network_flow(graph & g, vector<fin_numeral> const & balances) :
m_balances(balances) {
// Network flow graph has the edges in the reversed order compared to constraint graph
// We only take enabled edges from the original graph
for (unsigned i = 0; i < g.get_num_nodes(); ++i) {
m_graph.init_var(i);
}
vector<edge> const & es = g.get_all_edges();
for (unsigned i = 0; i < es.size(); ++i) {
edge const & e = es[i];
if (e.is_enabled()) {
m_graph.add_edge(e.get_target(), e.get_source(), e.get_weight(), explanation());
}
}
TRACE("network_flow", {
tout << "Difference logic optimization:" << std::endl;
display_dual(tout);
tout << "Minimum cost flow:" << std::endl;
display_primal(tout);
};);
m_step = 0;
m_tree = alloc(basic_spanning_tree<Ext>, m_graph);
}
template<typename Ext>
void network_flow<Ext>::initialize() {
TRACE("network_flow", tout << "initialize...\n";);
// Create an artificial root node to construct initial spanning tree
unsigned num_nodes = m_graph.get_num_nodes();
unsigned num_edges = m_graph.get_num_edges();
node root = num_nodes;
m_graph.init_var(root);
m_potentials.resize(num_nodes + 1);
m_potentials[root] = numeral::zero();
m_balances.resize(num_nodes + 1);
fin_numeral sum_supply = fin_numeral::zero();
for (unsigned i = 0; i < num_nodes; ++i) {
sum_supply += m_balances[i];
}
m_balances[root] = -sum_supply;
m_flows.resize(num_nodes + num_edges);
m_flows.fill(numeral::zero());
m_states.resize(num_nodes + num_edges);
m_states.fill(LOWER);
// Create artificial edges from/to root node to/from other nodes and initialize the spanning tree
svector<edge_id> tree;
for (unsigned i = 0; i < num_nodes; ++i) {
bool is_forward = !m_balances[i].is_neg();
m_states[num_edges + i] = BASIS;
node src = is_forward ? i : root;
node tgt = is_forward ? root : i;
m_flows[num_edges + i] = is_forward ? m_balances[i] : -m_balances[i];
m_potentials[i] = is_forward ? numeral::one() : -numeral::one();
tree.push_back(m_graph.add_edge(src, tgt, numeral::one(), explanation()));
}
m_tree->initialize(tree);
TRACE("network_flow",
tout << pp_vector("Potentials", m_potentials);
tout << pp_vector("Flows", m_flows);
tout << "Cost: " << get_cost() << "\n";
tout << "Spanning tree:\n";
display_spanning_tree(tout);
display_primal(tout););
SASSERT(check_well_formed());
}
template<typename Ext>
void network_flow<Ext>::update_potentials() {
node src = m_graph.get_source(m_enter_id);
node tgt = m_graph.get_target(m_enter_id);
numeral cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(m_enter_id);
numeral change;
node start;
if (m_tree->in_subtree_t2(tgt)) {
change = cost;
start = tgt;
}
else {
change = -cost;
start = src;
}
SASSERT(m_tree->in_subtree_t2(start));
TRACE("network_flow", tout << "update_potentials of T_" << start << " with change = " << change << "...\n";);
svector<node> descendants;
m_tree->get_descendants(start, descendants);
SASSERT(descendants.size() >= 1);
for (unsigned i = 0; i < descendants.size(); ++i) {
node u = descendants[i];
m_potentials[u] += change;
}
TRACE("network_flow", tout << pp_vector("Potentials", m_potentials););
}
template<typename Ext>
void network_flow<Ext>::update_flows() {
m_flows[m_enter_id] += *m_delta;
node src = m_graph.get_source(m_enter_id);
node tgt = m_graph.get_target(m_enter_id);
svector<edge_id> path;
svector<bool> against;
m_tree->get_path(src, tgt, path, against);
SASSERT(path.size() >= 1);
for (unsigned i = 0; i < path.size(); ++i) {
edge_id e_id = path[i];
m_flows[e_id] += against[i] ? - *m_delta : *m_delta;
}
TRACE("network_flow", tout << pp_vector("Flows", m_flows););
}
template<typename Ext>
bool network_flow<Ext>::choose_leaving_edge() {
node src = m_graph.get_source(m_enter_id);
node tgt = m_graph.get_target(m_enter_id);
m_delta.set_invalid();
edge_id leave_id = null_edge_id;
svector<edge_id> path;
svector<bool> against;
m_tree->get_path(src, tgt, path, against);
SASSERT(path.size() >= 1);
for (unsigned i = 0; i < path.size(); ++i) {
edge_id e_id = path[i];
if (against[i] && (!m_delta || m_flows[e_id] < *m_delta)) {
m_delta = m_flows[e_id];
leave_id = e_id;
}
}
m_leave_id = leave_id;
return m_delta;
}
template<typename Ext>
void network_flow<Ext>::update_spanning_tree() {
m_tree->update(m_enter_id, m_leave_id);
}
template<typename Ext>
bool network_flow<Ext>::choose_entering_edge(pivot_rule pr) {
if (!m_pivot || pr != m_pivot->rule()) {
switch (pr) {
case FIRST_ELIGIBLE:
m_pivot = alloc(first_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id);
break;
case BEST_ELIGIBLE:
m_pivot = alloc(best_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id);
break;
case CANDIDATE_LIST:
m_pivot = alloc(candidate_list_pivot, m_graph, m_potentials, m_states, m_enter_id);
break;
default:
UNREACHABLE();
}
}
return m_pivot->choose_entering_edge();
}
// Minimize cost flows
template<typename Ext>
min_flow_result network_flow<Ext>::min_cost(pivot_rule pr) {
initialize();
while (choose_entering_edge(pr)) {
bool bounded = choose_leaving_edge();
if (!bounded) return UNBOUNDED;
vector<edge>const& es = m_graph.get_all_edges();
TRACE("network_flow",
{
edge const& e_in = es[m_enter_id];
edge const& e_out = es[m_leave_id];
node src_in = e_in.get_source();
node tgt_in = e_in.get_target();
node src_out = e_out.get_source();
node tgt_out = e_out.get_target();
numeral c1 = m_potentials[src_in] - m_potentials[tgt_in] - m_graph.get_weight(m_enter_id);
numeral c2 = m_potentials[src_out] - m_potentials[tgt_out] - m_graph.get_weight(m_leave_id);
tout << "new base: y_" << src_in << "_" << tgt_in << " cost: " << c1 << " delta: " << *m_delta << "\n";
tout << "old base: y_" << src_out << "_" << tgt_out << " cost: " << c2 << "\n";
}
);
update_flows();
if (m_enter_id != m_leave_id) {
SASSERT(edge_in_tree(m_leave_id));
SASSERT(!edge_in_tree(m_enter_id));
m_states[m_enter_id] = BASIS;
m_states[m_leave_id] = LOWER;
update_spanning_tree();
update_potentials();
TRACE("network_flow",
tout << "Spanning tree:\n";
display_spanning_tree(tout);
tout << "Cost: " << get_cost() << "\n";
display_primal(tout);
);
SASSERT(check_well_formed());
}
}
TRACE("network_flow",
tout << "Spanning tree:\n";
display_spanning_tree(tout);
tout << "Cost: " << get_cost() << "\n";
display_primal(tout);
);
if (is_infeasible()) return INFEASIBLE;
TRACE("network_flow", tout << "Found optimal solution.\n";);
SASSERT(check_optimal());
return OPTIMAL;
}
template<typename Ext>
bool network_flow<Ext>::is_infeasible() {
// Flows of artificial arcs should be zero
unsigned num_nodes = m_graph.get_num_nodes();
unsigned num_edges = m_graph.get_num_edges();
SASSERT(m_flows.size() == num_edges);
for (unsigned i = 1; i < num_nodes; ++i) {
if (m_flows[num_edges - i].is_pos()) return true;
}
return false;
}
// Get the optimal solution
template<typename Ext>
typename network_flow<Ext>::numeral network_flow<Ext>::get_optimal_solution(vector<numeral> & result, bool is_dual) {
numeral objective_value = get_cost();
result.reset();
if (is_dual) {
result.append(m_potentials);
}
else {
result.append(m_flows);
}
return objective_value;
}
template<typename Ext>
typename network_flow<Ext>::numeral network_flow<Ext>::get_cost() const {
numeral objective_value = numeral::zero();
unsigned num_edges = m_graph.get_num_edges();
for (unsigned i = 0; i < num_edges; ++i) {
if (edge_in_tree(i)) {
objective_value += m_flows[i].get_rational() * m_graph.get_weight(i);
}
}
return objective_value;
}
template<typename Ext>
bool network_flow<Ext>::edge_in_tree(edge_id id) const {
return m_states[id] == BASIS;
}
template<typename Ext>
bool network_flow<Ext>::check_well_formed() {
SASSERT(m_tree->check_well_formed());
SASSERT(!m_delta || !(*m_delta).is_neg());
// m_flows are zero on non-basic edges
for (unsigned i = 0; i < m_flows.size(); ++i) {
SASSERT(!m_flows[i].is_neg());
SASSERT(edge_in_tree(i) || m_flows[i].is_zero());
}
unsigned num_edges = m_graph.get_num_edges();
for (unsigned i = 0; i < num_edges; ++i) {
if (edge_in_tree(i)) {
dl_var src = m_graph.get_source(i);
dl_var tgt = m_graph.get_target(i);
numeral weight = m_graph.get_weight(i);
SASSERT(m_potentials[src] - m_potentials[tgt] == weight);
}
}
return true;
}
template<typename Ext>
bool network_flow<Ext>::check_optimal() {
numeral total_cost = get_cost();
unsigned num_edges = m_graph.get_num_edges();
for (unsigned i = 0; i < num_edges; ++i) {
dl_var src = m_graph.get_source(i);
dl_var tgt = m_graph.get_target(i);
numeral weight = m_graph.get_weight(i);
SASSERT(m_potentials[src] - m_potentials[tgt] <= weight);
}
// m_flows are zero on non-basic edges
for (unsigned i = 0; i < m_flows.size(); ++i) {
SASSERT(edge_in_tree(i) || m_flows[i].is_zero());
}
numeral total_balance = numeral::zero();
for (unsigned i = 0; i < m_potentials.size(); ++i) {
total_balance += m_balances[i] * m_potentials[i];
}
TRACE("network_flow", tout << "Total balance: " << total_balance << ", total cost: " << total_cost << std::endl;);
return total_cost == total_balance;
}
// display methods
template<typename Ext>
void network_flow<Ext>::display_primal(std::ofstream & os) {
vector<edge> const & es = m_graph.get_all_edges();
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
edge const & e = es[i];
os << "(declare-fun y_" << e.get_source() << "_" << e.get_target() << " () Real)" << std::endl;
};
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
edge const & e = es[i];
os << "(assert (>= y_" << e.get_source() << "_" << e.get_target() << " 0))" << std::endl;
};
for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) {
bool initialized = false;
for (unsigned j = 0; j < m_graph.get_num_edges(); ++j) {
edge const & e = es[j];
if (e.get_target() == i || e.get_source() == i) {
if (!initialized) {
os << "(assert (= (+";
}
initialized = true;
if (e.get_target() == i) {
os << " y_" << e.get_source() << "_" << e.get_target();
}
else {
os << " (- y_" << e.get_source() << "_" << e.get_target() << ")";
}
}
}
if(initialized) {
os << " " << m_balances[i] << ") 0))" << std::endl;
}
}
os << "(minimize (+";
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
edge const & e = es[i];
os << " (* " << e.get_weight() << " y_" << e.get_source() << "_" << e.get_target() << ")";
};
os << "))" << std::endl;
os << "(optimize)" << std::endl;
if (!m_flows.empty()) {
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
edge const & e = es[i];
os << "; y_" << e.get_source() << "_" << e.get_target() << " = " << m_flows[i] << "\n";
}
}
}
template<typename Ext>
void network_flow<Ext>::display_dual(std::ofstream & os) {
for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) {
os << "(declare-fun v" << i << " () Real)" << std::endl;
}
vector<edge> const & es = m_graph.get_all_edges();
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
edge const & e = es[i];
os << "(assert (<= (- v" << e.get_source() << " v" << e.get_target() << ") " << e.get_weight() << "))" << std::endl;
};
os << "(assert (= v0 0))" << std::endl;
os << "(maximize (+";
for (unsigned i = 0; i < m_balances.size(); ++i) {
os << " (* " << m_balances[i] << " v" << i << ")";
};
os << "))" << std::endl;
os << "(optimize)" << std::endl;
}
template<typename Ext>
void network_flow<Ext>::display_spanning_tree(std::ofstream & os) {
++m_step;;
std::string prefix = "T";
prefix.append(std::to_string(m_step));
prefix.append("_");
unsigned root = m_graph.get_num_nodes() - 1;
for (unsigned i = 0; i < root; ++i) {
os << prefix << i << "[shape=circle,label=\"" << prefix << i << " [";
os << m_potentials[i] << "/" << m_balances[i] << "]\"];\n";
}
os << prefix << root << "[shape=doublecircle,label=\"" << prefix << root << " [";
os << m_potentials[root] << "/" << m_balances[root] << "]\"];\n";
unsigned num_edges = m_graph.get_num_edges();
for (unsigned i = 0; i < num_edges; ++i) {
os << prefix << m_graph.get_source(i) << " -> " << prefix << m_graph.get_target(i);
if (edge_in_tree(i)) {
os << "[color=red,penwidth=3.0,label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n";
}
else {
os << "[label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n";
}
}
os << std::endl;
}
}
#endif

View file

@ -0,0 +1,26 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
simplex.h
Abstract:
Multi-precision simplex tableau.
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#include"simplex.h"
#include"sparse_matrix_def.h"
#include"simplex_def.h"
namespace simplex {
template class simplex<mpz_ext>;
template class simplex<mpq_ext>;
};

202
src/math/simplex/simplex.h Normal file
View file

@ -0,0 +1,202 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
simplex.h
Abstract:
Multi-precision simplex tableau.
- It uses code from theory_arith where applicable.
- It is detached from the theory class and ASTs.
- It uses non-shared mpz/mpq's avoiding global locks and operations on rationals.
- It follows the same sparse tableau layout (no LU yet).
- It does not include features for non-linear arithmetic.
- Branch/bound/cuts is external.
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#ifndef _SIMPLEX_H_
#define _SIMPLEX_H_
#include "sparse_matrix.h"
#include "mpq_inf.h"
#include "heap.h"
#include "lbool.h"
#include "uint_set.h"
namespace simplex {
template<typename Ext>
class simplex {
typedef unsigned var_t;
typedef typename Ext::eps_numeral eps_numeral;
typedef typename Ext::numeral numeral;
typedef typename Ext::manager manager;
typedef typename Ext::eps_manager eps_manager;
typedef typename Ext::scoped_numeral scoped_numeral;
typedef _scoped_numeral<eps_manager> scoped_eps_numeral;
typedef _scoped_numeral_vector<eps_manager> scoped_eps_numeral_vector;
typedef sparse_matrix<Ext> matrix;
struct var_lt {
bool operator()(var_t v1, var_t v2) const { return v1 < v2; }
};
typedef heap<var_lt> var_heap;
struct stats {
unsigned m_num_pivots;
unsigned m_num_infeasible;
unsigned m_num_checks;
stats() { reset(); }
void reset() {
memset(this, 0, sizeof(*this));
}
};
enum pivot_strategy_t {
S_BLAND,
S_GREATEST_ERROR,
S_LEAST_ERROR,
S_DEFAULT
};
struct var_info {
unsigned m_base2row:29;
unsigned m_is_base:1;
unsigned m_lower_valid:1;
unsigned m_upper_valid:1;
eps_numeral m_value;
eps_numeral m_lower;
eps_numeral m_upper;
numeral m_base_coeff;
var_info():
m_base2row(0),
m_is_base(false),
m_lower_valid(false),
m_upper_valid(false)
{}
};
static const var_t null_var;
mutable manager m;
mutable eps_manager em;
mutable matrix M;
unsigned m_max_iterations;
volatile bool m_cancel;
var_heap m_to_patch;
vector<var_info> m_vars;
svector<var_t> m_row2base;
bool m_bland;
unsigned m_blands_rule_threshold;
random_gen m_random;
uint_set m_left_basis;
unsigned m_infeasible_var;
unsigned_vector m_base_vars;
stats m_stats;
public:
simplex():
M(m),
m_max_iterations(UINT_MAX),
m_cancel(false),
m_to_patch(1024),
m_bland(false),
m_blands_rule_threshold(1000) {}
typedef typename matrix::row row;
typedef typename matrix::row_iterator row_iterator;
typedef typename matrix::col_iterator col_iterator;
void ensure_var(var_t v);
row add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs);
row get_infeasible_row();
var_t get_base_var(row const& r) const { return m_row2base[r.id()]; }
numeral const& get_base_coeff(row const& r) const { return m_vars[m_row2base[r.id()]].m_base_coeff; }
void del_row(var_t base_var);
void set_lower(var_t var, eps_numeral const& b);
void set_upper(var_t var, eps_numeral const& b);
void get_lower(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_lower; }
void get_upper(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_upper; }
bool above_lower(var_t var, eps_numeral const& b) const;
bool below_upper(var_t var, eps_numeral const& b) const;
bool below_lower(var_t v) const;
bool above_upper(var_t v) const;
bool lower_valid(var_t var) const { return m_vars[var].m_lower_valid; }
bool upper_valid(var_t var) const { return m_vars[var].m_upper_valid; }
void unset_lower(var_t var);
void unset_upper(var_t var);
void set_value(var_t var, eps_numeral const& b);
void set_cancel(bool f) { m_cancel = f; }
void set_max_iterations(unsigned m) { m_max_iterations = m; }
void reset();
lbool make_feasible();
lbool minimize(var_t var);
eps_numeral const& get_value(var_t v);
void display(std::ostream& out) const;
void display_row(std::ostream& out, row const& r, bool values = true);
unsigned get_num_vars() const { return m_vars.size(); }
row_iterator row_begin(row const& r) { return M.row_begin(r); }
row_iterator row_end(row const& r) { return M.row_end(r); }
void collect_statistics(::statistics & st) const;
private:
void del_row(row const& r);
var_t select_var_to_fix();
pivot_strategy_t pivot_strategy();
var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); }
var_t select_error_var(bool least);
void check_blands_rule(var_t v, unsigned& num_repeated);
bool make_var_feasible(var_t x_i);
void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value);
void update_value(var_t v, eps_numeral const& delta);
void update_value_core(var_t v, eps_numeral const& delta);
void pivot(var_t x_i, var_t x_j, numeral const& a_ij);
void move_to_bound(var_t x, bool to_lower);
var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
var_t select_pivot_blands(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
int get_num_non_free_dep_vars(var_t x_j, int best_so_far);
var_t pick_var_to_leave(var_t x_j, bool is_pos,
scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc);
void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j);
bool at_lower(var_t v) const;
bool at_upper(var_t v) const;
bool above_lower(var_t v) const;
bool below_upper(var_t v) const;
bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); }
bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; }
bool is_non_free(var_t v) const { return !is_free(v); }
bool is_base(var_t x) const { return m_vars[x].m_is_base; }
void add_patch(var_t v);
bool well_formed() const;
bool well_formed_row(row const& r) const;
bool is_feasible() const;
};
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,275 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sparse_matrix.h
Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#ifndef _SPARSE_MATRIX_H_
#define _SPARSE_MATRIX_H_
#include "mpq_inf.h"
#include "statistics.h"
namespace simplex {
template<typename Ext>
class sparse_matrix {
public:
typedef typename Ext::numeral numeral;
typedef typename Ext::scoped_numeral scoped_numeral;
typedef typename Ext::manager manager;
typedef unsigned var_t;
struct row_entry {
numeral m_coeff;
var_t m_var;
row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {}
};
private:
struct stats {
unsigned m_add_rows;
stats() { reset(); }
void reset() {
memset(this, 0, sizeof(*this));
}
};
static const int dead_id = -1;
/**
\brief A row_entry is: m_var*m_coeff
m_col_idx points to the place in the
column where the variable occurs.
*/
struct _row_entry : public row_entry {
union {
int m_col_idx;
int m_next_free_row_entry_idx;
};
_row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {}
_row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {}
bool is_dead() const { return row_entry::m_var == dead_id; }
};
/**
\brief A column entry points to the row and the row_entry within the row
that has a non-zero coefficient on the variable associated
with the column entry.
*/
struct col_entry {
int m_row_id;
union {
int m_row_idx;
int m_next_free_col_entry_idx;
};
col_entry(int r, int i): m_row_id(r), m_row_idx(i) {}
col_entry(): m_row_id(0), m_row_idx(0) {}
bool is_dead() const { return m_row_id == dead_id; }
};
struct column;
/**
\brief A row contains a base variable and set of
row_entries. The base variable must occur in the set of
row_entries with coefficient 1.
*/
struct _row {
vector<_row_entry> m_entries;
unsigned m_size; // the real size, m_entries contains dead row_entries.
int m_first_free_idx; // first available position.
_row();
unsigned size() const { return m_size; }
unsigned num_entries() const { return m_entries.size(); }
void reset(manager& m);
_row_entry & add_row_entry(unsigned & pos_idx);
void del_row_entry(unsigned idx);
void compress(manager& m, vector<column> & cols);
void compress_if_needed(manager& m, vector<column> & cols);
void save_var_pos(svector<int> & result_map, unsigned_vector& idxs) const;
//bool is_coeff_of(var_t v, numeral const & expected) const;
int get_idx_of(var_t v) const;
};
/**
\brief A column stores in which rows a variable occurs.
The column may have free/dead entries. The field m_first_free_idx
is a reference to the first free/dead entry.
*/
struct column {
svector<col_entry> m_entries;
unsigned m_size;
int m_first_free_idx;
mutable unsigned m_refs;
column():m_size(0), m_first_free_idx(-1), m_refs(0) {}
unsigned size() const { return m_size; }
unsigned num_entries() const { return m_entries.size(); }
void reset();
void compress(vector<_row> & rows);
void compress_if_needed(vector<_row> & rows);
//void compress_singleton(vector<_row> & rows, unsigned singleton_pos);
col_entry const * get_first_col_entry() const;
col_entry & add_col_entry(int & pos_idx);
void del_col_entry(unsigned idx);
};
manager& m;
vector<_row> m_rows;
svector<unsigned> m_dead_rows; // rows to recycle
vector<column> m_columns; // per var
svector<int> m_var_pos; // temporary map from variables to positions in row
unsigned_vector m_var_pos_idx; // indices in m_var_pos
stats m_stats;
bool well_formed_row(unsigned row_id) const;
bool well_formed_column(unsigned column_id) const;
void del_row_entry(_row& r, unsigned pos);
public:
sparse_matrix(manager& m): m(m) {}
~sparse_matrix();
void reset();
class row {
unsigned m_id;
public:
explicit row(unsigned r):m_id(r) {}
row():m_id(UINT_MAX) {}
bool operator!=(row const& other) const {
return m_id != other.m_id;
}
unsigned id() const { return m_id; }
};
void ensure_var(var_t v);
row mk_row();
void add_var(row r, numeral const& n, var_t var);
void add(row r, numeral const& n, row src);
void mul(row r, numeral const& n);
void neg(row r);
void del(row r);
void gcd_normalize(row const& r, scoped_numeral& g);
class row_iterator {
friend class sparse_matrix;
unsigned m_curr;
_row & m_row;
void move_to_used() {
while (m_curr < m_row.num_entries() &&
m_row.m_entries[m_curr].is_dead()) {
++m_curr;
}
}
row_iterator(_row & r, bool begin):
m_curr(0), m_row(r) {
if (begin) {
move_to_used();
}
else {
m_curr = m_row.num_entries();
}
}
public:
row_entry & operator*() const { return m_row.m_entries[m_curr]; }
row_entry * operator->() const { return &(operator*()); }
row_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; }
bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; }
bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; }
};
row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); }
row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); }
unsigned column_size(var_t v) const { return m_columns[v].size(); }
class col_iterator {
friend class sparse_matrix;
unsigned m_curr;
column const& m_col;
vector<_row> const& m_rows;
void move_to_used() {
while (m_curr < m_col.num_entries() && m_col.m_entries[m_curr].is_dead()) {
++m_curr;
}
}
col_iterator(column const& c, vector<_row> const& r, bool begin):
m_curr(0), m_col(c), m_rows(r) {
++m_col.m_refs;
if (begin) {
move_to_used();
}
else {
m_curr = m_col.num_entries();
}
}
public:
~col_iterator() {
--m_col.m_refs;
}
row get_row() {
return row(m_col.m_entries[m_curr].m_row_id);
}
row_entry const& get_row_entry() {
col_entry const& c = m_col.m_entries[m_curr];
int row_id = c.m_row_id;
return m_rows[row_id].m_entries[c.m_row_idx];
}
col_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; }
bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; }
bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; }
};
col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); }
col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); }
void display(std::ostream& out);
void display_row(std::ostream& out, row const& r);
bool well_formed() const;
void collect_statistics(::statistics & st) const;
};
struct mpz_ext {
typedef mpz numeral;
typedef scoped_mpz scoped_numeral;
typedef unsynch_mpz_manager manager;
typedef mpq_inf eps_numeral;
typedef unsynch_mpq_inf_manager eps_manager;
};
struct mpq_ext {
typedef mpq numeral;
typedef scoped_mpq scoped_numeral;
typedef unsynch_mpq_manager manager;
typedef mpq_inf eps_numeral;
typedef unsynch_mpq_inf_manager eps_manager;
};
};
#endif

View file

@ -0,0 +1,594 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sparse_matrix_def.h
Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
mainly hoisted from theory_arith.h and theory_arith_aux.h
--*/
#ifndef _SPARSE_MATRIX_DEF_H_
#define _SPARSE_MATRIX_DEF_H_
#include "sparse_matrix.h"
#include "uint_set.h"
namespace simplex {
// -----------------------------------
//
// Rows
//
// -----------------------------------
template<typename Ext>
sparse_matrix<Ext>::_row::_row():
m_size(0),
m_first_free_idx(-1) {
}
template<typename Ext>
void sparse_matrix<Ext>::_row::reset(manager& m) {
for (unsigned i = 0; i < m_entries.size(); ++i) {
m.reset(m_entries[i].m_coeff);
}
m_entries.reset();
m_size = 0;
m_first_free_idx = -1;
}
/**
\brief Add a new row_entry. The result is a reference to the new row_entry.
The position of the new row_entry in the
row is stored in pos_idx.
*/
template<typename Ext>
typename sparse_matrix<Ext>::_row_entry &
sparse_matrix<Ext>::_row::add_row_entry(unsigned & pos_idx) {
m_size++;
if (m_first_free_idx == -1) {
pos_idx = m_entries.size();
m_entries.push_back(_row_entry());
return m_entries.back();
}
else {
SASSERT(m_first_free_idx >= 0);
pos_idx = static_cast<unsigned>(m_first_free_idx);
_row_entry & result = m_entries[pos_idx];
SASSERT(result.is_dead());
m_first_free_idx = result.m_next_free_row_entry_idx;
return result;
}
}
/**
\brief Delete row_entry at position idx.
*/
template<typename Ext>
void sparse_matrix<Ext>::_row::del_row_entry(unsigned idx) {
_row_entry & t = m_entries[idx];
SASSERT(!t.is_dead());
t.m_next_free_row_entry_idx = m_first_free_idx;
t.m_var = dead_id;
m_size--;
m_first_free_idx = idx;
SASSERT(t.is_dead());
}
/**
\brief Remove holes (i.e., dead entries) from the row.
*/
template<typename Ext>
void sparse_matrix<Ext>::_row::compress(manager& m, vector<column> & cols) {
unsigned i = 0;
unsigned j = 0;
unsigned sz = m_entries.size();
for (; i < sz; i++) {
_row_entry & t1 = m_entries[i];
if (!t1.is_dead()) {
if (i != j) {
_row_entry & t2 = m_entries[j];
t2.m_coeff.swap(t1.m_coeff);
t2.m_var = t1.m_var;
t2.m_col_idx = t1.m_col_idx;
SASSERT(!t2.is_dead());
column & col = cols[t2.m_var];
col.m_entries[t2.m_col_idx].m_row_idx = j;
}
j++;
}
}
SASSERT(j == m_size);
//
// alternative: update the free-list to point to the
// tail and avoid shrinking.
// if m.does not allocate memory (for wrapper around
// double), also bypass this step.
//
for (unsigned i = m_size; i < m_entries.size(); ++i) {
m.reset(m_entries[i].m_coeff);
}
m_entries.shrink(m_size);
m_first_free_idx = -1;
}
template<typename Ext>
void sparse_matrix<Ext>::_row::compress_if_needed(manager& m, vector<column> & cols) {
if (size() *2 < num_entries()) {
compress(m, cols);
}
}
/**
\brief Fill the map var -> pos/idx
*/
template<typename Ext>
inline void sparse_matrix<Ext>::_row::save_var_pos(svector<int> & result_map, unsigned_vector& idxs) const {
typename vector<_row_entry>::const_iterator it = m_entries.begin();
typename vector<_row_entry>::const_iterator end = m_entries.end();
unsigned idx = 0;
for (; it != end; ++it, ++idx) {
if (!it->is_dead()) {
result_map[it->m_var] = idx;
idxs.push_back(it->m_var);
}
}
}
template<typename Ext>
int sparse_matrix<Ext>::_row::get_idx_of(var_t v) const {
typename vector<_row_entry>::const_iterator it = m_entries.begin();
typename vector<_row_entry>::const_iterator end = m_entries.end();
for (unsigned idx = 0; it != end; ++it, ++idx) {
if (!it->is_dead() && it->m_var == v)
return idx;
}
return -1;
}
// -----------------------------------
//
// Columns
//
// -----------------------------------
template<typename Ext>
void sparse_matrix<Ext>::column::reset() {
m_entries.reset();
m_size = 0;
m_first_free_idx = -1;
}
/**
\brief Remove holes (i.e., dead entries) from the column.
*/
template<typename Ext>
void sparse_matrix<Ext>::column::compress(vector<_row> & rows) {
unsigned i = 0;
unsigned j = 0;
unsigned sz = m_entries.size();
for (; i < sz; i++) {
col_entry & e1 = m_entries[i];
if (!e1.is_dead()) {
if (i != j) {
m_entries[j] = e1;
_row & r = rows[e1.m_row_id];
r.m_entries[e1.m_row_idx].m_col_idx = j;
}
j++;
}
}
SASSERT(j == m_size);
m_entries.shrink(m_size);
m_first_free_idx = -1;
}
/**
\brief Invoke compress if the column contains too many holes (i.e., dead entries).
*/
template<typename Ext>
inline void sparse_matrix<Ext>::column::compress_if_needed(vector<_row> & rows) {
if (size() * 2 < num_entries() && m_refs == 0) {
compress(rows);
}
}
#if 0
/**
\brief Special version of compress, that is used when the column contain
only one entry located at position singleton_pos.
*/
template<typename Ext>
void sparse_matrix<Ext>::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) {
SASSERT(m_size == 1);
if (singleton_pos != 0) {
col_entry & s = m_entries[singleton_pos];
m_entries[0] = s;
row & r = rows[s.m_row_id];
r[s.m_row_idx].m_col_idx = 0;
}
m_first_free_idx = -1;
m_entries.shrink(1);
}
#endif
template<typename Ext>
const typename sparse_matrix<Ext>::col_entry *
sparse_matrix<Ext>::column::get_first_col_entry() const {
typename svector<col_entry>::const_iterator it = m_entries.begin();
typename svector<col_entry>::const_iterator end = m_entries.end();
for (; it != end; ++it) {
if (!it->is_dead()) {
return it;
}
}
return 0;
}
template<typename Ext>
typename sparse_matrix<Ext>::col_entry &
sparse_matrix<Ext>::column::add_col_entry(int & pos_idx) {
m_size++;
if (m_first_free_idx == -1) {
pos_idx = m_entries.size();
m_entries.push_back(col_entry());
return m_entries.back();
}
else {
pos_idx = m_first_free_idx;
col_entry & result = m_entries[pos_idx];
SASSERT(result.is_dead());
m_first_free_idx = result.m_next_free_col_entry_idx;
return result;
}
}
template<typename Ext>
void sparse_matrix<Ext>::column::del_col_entry(unsigned idx) {
col_entry & c = m_entries[idx];
SASSERT(!c.is_dead());
c.m_row_id = dead_id;
c.m_next_free_col_entry_idx = m_first_free_idx;
m_first_free_idx = idx;
m_size--;
}
// -----------------------------------
//
// Matrix
//
// -----------------------------------
template<typename Ext>
sparse_matrix<Ext>::~sparse_matrix() {
for (unsigned i = 0; i < m_rows.size(); ++i) {
_row& r = m_rows[i];
for (unsigned j = 0; j < r.m_entries.size(); ++j) {
m.reset(r.m_entries[j].m_coeff);
}
}
}
template<typename Ext>
void sparse_matrix<Ext>::reset() {
m_rows.reset();
m_dead_rows.reset();
m_columns.reset();
m_var_pos.reset();
m_var_pos_idx.reset();
}
template<typename Ext>
void sparse_matrix<Ext>::ensure_var(var_t v) {
while (m_columns.size() <= v) {
m_columns.push_back(column());
m_var_pos.push_back(-1);
}
}
template<typename Ext>
typename sparse_matrix<Ext>::row
sparse_matrix<Ext>::mk_row() {
if (m_dead_rows.empty()) {
row r(m_rows.size());
m_rows.push_back(_row());
return r;
}
else {
row r(m_dead_rows.back());
m_dead_rows.pop_back();
return r;
}
}
template<typename Ext>
void sparse_matrix<Ext>::add_var(row dst, numeral const& n, var_t v) {
_row& r = m_rows[dst.id()];
column& c = m_columns[v];
unsigned r_idx;
int c_idx;
_row_entry & r_entry = r.add_row_entry(r_idx);
col_entry& c_entry = c.add_col_entry(c_idx);
r_entry.m_var = v;
m.set(r_entry.m_coeff, n);
r_entry.m_col_idx = c_idx;
c_entry.m_row_id = dst.id();
c_entry.m_row_idx = r_idx;
}
/**
\brief Set row1 <- row1 + row2 * n
*/
template<typename Ext>
void sparse_matrix<Ext>::add(row row1, numeral const& n, row row2) {
m_stats.m_add_rows++;
_row & r1 = m_rows[row1.id()];
r1.save_var_pos(m_var_pos, m_var_pos_idx);
//
// loop over variables in row2,
// add terms in row2 to row1.
//
#define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \
row_iterator it = row_begin(row2); \
row_iterator end = row_end(row2); \
for (; it != end; ++it) { \
var_t v = it->m_var; \
int pos = m_var_pos[v]; \
if (pos == -1) { \
/* variable v is not in row1 */ \
unsigned row_idx; \
_row_entry & r_entry = r1.add_row_entry(row_idx); \
r_entry.m_var = v; \
m.set(r_entry.m_coeff, it->m_coeff); \
_SET_COEFF_; \
column & c = m_columns[v]; \
int col_idx; \
col_entry & c_entry = c.add_col_entry(col_idx); \
r_entry.m_col_idx = col_idx; \
c_entry.m_row_id = row1.id(); \
c_entry.m_row_idx = row_idx; \
} \
else { \
/* variable v is in row1 */ \
_row_entry & r_entry = r1.m_entries[pos]; \
SASSERT(r_entry.m_var == v); \
_ADD_COEFF_; \
if (m.is_zero(r_entry.m_coeff)) { \
del_row_entry(r1, pos); \
} \
} \
} \
((void) 0)
if (m.is_one(n)) {
ADD_ROW({},
m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
}
else if (m.is_minus_one(n)) {
ADD_ROW(m.neg(r_entry.m_coeff),
m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
}
else {
scoped_numeral tmp(m);
ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff),
m.mul(it->m_coeff, n, tmp);
m.add(r_entry.m_coeff, tmp, r_entry.m_coeff));
}
// reset m_var_pos:
for (unsigned i = 0; i < m_var_pos_idx.size(); ++i) {
m_var_pos[m_var_pos_idx[i]] = -1;
}
m_var_pos_idx.reset();
r1.compress_if_needed(m, m_columns);
}
template<typename Ext>
void sparse_matrix<Ext>::del_row_entry(_row& r, unsigned pos) {
_row_entry & r_entry = r.m_entries[pos];
var_t v = r_entry.m_var;
int col_idx = r_entry.m_col_idx;
r.del_row_entry(pos);
column & c = m_columns[v];
c.del_col_entry(col_idx);
c.compress_if_needed(m_rows);
}
/**
\brief Set row <- -row
*/
template<typename Ext>
void sparse_matrix<Ext>::neg(row r) {
row_iterator it = row_begin(r);
row_iterator end = row_end(r);
for (; it != end; ++it) {
m.neg(it->m_coeff);
}
}
/**
\brief Set row <- n*row
*/
template<typename Ext>
void sparse_matrix<Ext>::mul(row r, numeral const& n) {
SASSERT(!m.is_zero(n));
if (m.is_one(n)) {
// no op
}
else if (m.is_minus_one(n)) {
neg(r);
}
else {
row_iterator it = row_begin(r);
row_iterator end = row_end(r);
for (; it != end; ++it) {
m.mul(it->m_coeff, n, it->m_coeff);
}
}
}
/**
\brief Delete row.
*/
template<typename Ext>
void sparse_matrix<Ext>::del(row r) {
_row& rw = m_rows[r.id()];
for (unsigned i = 0; i < rw.m_entries.size(); ++i) {
_row_entry& e = rw.m_entries[i];
if (!e.is_dead()) {
del_row_entry(rw, i);
}
}
SASSERT(rw.m_size == 0);
m_dead_rows.push_back(r.id());
}
/**
\brief normalize coefficients by dividing with they coefficients.
return the gcd.
*/
template<typename Ext>
void sparse_matrix<Ext>::gcd_normalize(row const& r, scoped_numeral& g) {
g.reset();
row_iterator it = row_begin(r), end = row_end(r);
for (; it != end && !m.is_one(g); ++it) {
if (m.is_zero(g)) g = it->m_coeff;
else m.gcd(g, it->m_coeff, g);
}
if (m.is_zero(g)) {
g = numeral(1);
}
if (!m.is_one(g)) {
row_iterator it2 = row_begin(r);
for (; it2 != end; ++it2) {
m.div(it2->m_coeff, g, it2->m_coeff);
}
}
}
/**
\brief well_formed check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed() const {
for (unsigned i = 0; i < m_rows.size(); ++i) {
well_formed_row(i);
}
for (unsigned i = 0; i < m_columns.size(); ++i) {
well_formed_column(i);
}
return true;
}
/**
\brief well_formed row check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed_row(unsigned row_id) const {
uint_set vars, dead;
_row const& r = m_rows[row_id];
for (unsigned i = 0; i < r.num_entries(); ++i) {
_row_entry const& e = r.m_entries[i];
if (e.is_dead()) {
dead.insert(i);
continue;
}
SASSERT(!vars.contains(e.m_var));
SASSERT(!m.is_zero(e.m_coeff));
SASSERT(e.m_var != dead_id);
col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx];
SASSERT(c.m_row_id == row_id);
SASSERT(c.m_row_idx == i);
vars.insert(e.m_var);
}
int idx = r.m_first_free_idx;
while (idx != -1) {
SASSERT(dead.contains(idx));
dead.remove(idx);
idx = r.m_entries[idx].m_next_free_row_entry_idx;
}
SASSERT(dead.empty());
return true;
}
/**
\brief well_formed column check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed_column(var_t v) const {
uint_set rows, dead;
column const& col = m_columns[v];
for (unsigned i = 0; i < col.num_entries(); ++i) {
col_entry const& c = col.m_entries[i];
if (c.is_dead()) {
dead.insert(i);
continue;
}
SASSERT(!rows.contains(c.m_row_id));
_row const& row = m_rows[c.m_row_id];
_row_entry const& r = row.m_entries[c.m_row_idx];
SASSERT(r.m_var == v);
SASSERT(r.m_col_idx == i);
rows.insert(c.m_row_id);
}
int idx = col.m_first_free_idx;
while (idx != -1) {
SASSERT(dead.contains(idx));
dead.remove(idx);
idx = col.m_entries[idx].m_next_free_col_entry_idx;
}
SASSERT(dead.empty());
return true;
}
/**
\brief statistics
*/
template<typename Ext>
void sparse_matrix<Ext>::collect_statistics(::statistics & st) const {
st.update("simplex add rows", m_stats.m_add_rows);
}
/**
\brief display method
*/
template<typename Ext>
void sparse_matrix<Ext>::display(std::ostream& out) {
for (unsigned i = 0; i < m_rows.size(); ++i) {
if (m_rows[i].size() == 0) continue;
display_row(out, row(i));
}
}
template<typename Ext>
void sparse_matrix<Ext>::display_row(std::ostream& out, row const& r) {
row_iterator it = row_begin(r), end = row_end(r);
for (; it != end; ++it) {
m.display(out, it->m_coeff);
out << "*v" << it->m_var << " ";
}
out << "\n";
}
};
#endif

View file

@ -159,11 +159,17 @@ void func_interp::insert_entry(expr * const * args, expr * r) {
void func_interp::insert_new_entry(expr * const * args, expr * r) {
reset_interp_cache();
CTRACE("func_interp_bug", get_entry(args) != 0,
tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
tout << "Args:";
for (unsigned i = 0; i < m_arity; i++) {
tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n";
}
tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";
tout << "Args:";
for (unsigned i = 0; i < m_arity; i++) {
tout << mk_ismt2_pp(args[i], m_manager) << "\n";
}
tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";);
);
SASSERT(get_entry(args) == 0);
func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r);
if (!new_entry->args_are_values())

View file

@ -23,6 +23,7 @@ Revision History:
#include"bool_rewriter.h"
#include"arith_rewriter.h"
#include"bv_rewriter.h"
#include"pb_rewriter.h"
#include"datatype_rewriter.h"
#include"array_rewriter.h"
#include"float_rewriter.h"
@ -36,6 +37,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
bv_rewriter m_bv_rw;
array_rewriter m_ar_rw;
datatype_rewriter m_dt_rw;
pb_rewriter m_pb_rw;
float_rewriter m_f_rw;
unsigned long long m_max_memory;
unsigned m_max_steps;
@ -52,6 +54,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
// See comment above. We want to allow customers to set :sort-store
m_ar_rw(m, p),
m_dt_rw(m),
m_pb_rw(m),
m_f_rw(m) {
m_b_rw.set_flat(false);
m_a_rw.set_flat(false);
@ -153,6 +156,8 @@ struct evaluator_cfg : public default_rewriter_cfg {
return m_ar_rw.mk_app_core(f, num, args, result);
if (fid == m_dt_rw.get_fid())
return m_dt_rw.mk_app_core(f, num, args, result);
if (fid == m_pb_rw.get_fid())
return m_pb_rw.mk_app_core(f, num, args, result);
if (fid == m_f_rw.get_fid())
return m_f_rw.mk_app_core(f, num, args, result);
return BR_FAILED;

View file

@ -0,0 +1,917 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
model_implicant.cpp
Abstract:
Facility to extract prime implicant from model.
Author:
Krystof Hoder (t-khoder) 2011-8-19.
Revision History:
Notes:
--*/
#include <sstream>
#include "array_decl_plugin.h"
#include "ast_pp.h"
#include "bool_rewriter.h"
#include "for_each_expr.h"
#include "model.h"
#include "ref_vector.h"
#include "rewriter.h"
#include "rewriter_def.h"
#include "util.h"
#include "model_implicant.h"
#include "arith_decl_plugin.h"
#include "expr_replacer.h"
#include "model_smt2_pp.h"
#include "poly_rewriter.h"
#include "poly_rewriter_def.h"
#include "arith_rewriter.h"
#include "scoped_proof.h"
/////////////////////////
// model_implicant
//
void model_implicant::assign_value(expr* e, expr* val) {
rational r;
if (m.is_true(val)) {
set_true(e);
}
else if (m.is_false(val)) {
set_false(e);
}
else if (m_arith.is_numeral(val, r)) {
set_number(e, r);
}
else if (m.is_value(val)) {
set_value(e, val);
}
else {
IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";);
TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";);
set_x(e);
}
}
void model_implicant::setup_model(model_ref& model) {
m_numbers.reset();
m_values.reset();
m_model = model;
rational r;
unsigned sz = model->get_num_constants();
for (unsigned i = 0; i < sz; i++) {
func_decl * d = model->get_constant(i);
expr* val = model->get_const_interp(d);
expr* e = m.mk_const(d);
m_refs.push_back(e);
assign_value(e, val);
}
}
void model_implicant::reset() {
m1.reset();
m2.reset();
m_values.reset();
m_visited.reset();
m_numbers.reset();
m_refs.reset();
m_model = 0;
}
expr_ref_vector model_implicant::minimize_model(ptr_vector<expr> const & formulas, model_ref& mdl) {
setup_model(mdl);
TRACE("pdr_verbose",
tout << "formulas:\n";
for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n";
);
expr_ref_vector model = prune_by_cone_of_influence(formulas);
TRACE("pdr_verbose",
tout << "pruned model:\n";
for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";);
reset();
DEBUG_CODE(
setup_model(mdl);
VERIFY(check_model(formulas));
reset(););
return model;
}
expr_ref_vector model_implicant::minimize_literals(ptr_vector<expr> const& formulas, model_ref& mdl) {
TRACE("pdr",
tout << "formulas:\n";
for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n";
);
expr_ref_vector result(m);
expr_ref tmp(m);
ptr_vector<expr> tocollect;
setup_model(mdl);
collect(formulas, tocollect);
for (unsigned i = 0; i < tocollect.size(); ++i) {
expr* e = tocollect[i];
expr* e1, *e2;
SASSERT(m.is_bool(e));
SASSERT(is_true(e) || is_false(e));
if (is_true(e)) {
result.push_back(e);
}
// hack to break disequalities for arithmetic variables.
else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) {
if (get_number(e1) < get_number(e2)) {
result.push_back(m_arith.mk_lt(e1,e2));
}
else {
result.push_back(m_arith.mk_lt(e2,e1));
}
}
else {
result.push_back(m.mk_not(e));
}
}
reset();
TRACE("pdr",
tout << "minimized model:\n";
for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n";
);
return result;
}
void model_implicant::process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect) {
SASSERT(m.is_bool(e));
SASSERT(is_true(e) || is_false(e));
unsigned v = is_true(e);
unsigned sz = e->get_num_args();
expr* const* args = e->get_args();
if (e->get_family_id() == m.get_basic_family_id()) {
switch(e->get_decl_kind()) {
case OP_TRUE:
break;
case OP_FALSE:
break;
case OP_EQ:
case OP_IFF:
if (args[0] == args[1]) {
SASSERT(v);
// no-op
}
else if (m.is_bool(args[0])) {
todo.append(sz, args);
}
else {
tocollect.push_back(e);
}
break;
case OP_DISTINCT:
tocollect.push_back(e);
break;
case OP_ITE:
if (args[1] == args[2]) {
tocollect.push_back(args[1]);
}
else if (is_true(args[1]) && is_true(args[2])) {
todo.append(2, args+1);
}
else if (is_false(args[1]) && is_false(args[2])) {
todo.append(2, args+1);
}
else if (is_true(args[0])) {
todo.append(2, args);
}
else {
SASSERT(is_false(args[0]));
todo.push_back(args[0]);
todo.push_back(args[2]);
}
break;
case OP_AND:
if (v) {
todo.append(sz, args);
}
else {
unsigned i = 0;
for (; !is_false(args[i]) && i < sz; ++i);
if (i == sz) {
fatal_error(1);
}
VERIFY(i < sz);
todo.push_back(args[i]);
}
break;
case OP_OR:
if (v) {
unsigned i = 0;
for (; !is_true(args[i]) && i < sz; ++i);
if (i == sz) {
fatal_error(1);
}
VERIFY(i < sz);
todo.push_back(args[i]);
}
else {
todo.append(sz, args);
}
break;
case OP_XOR:
case OP_NOT:
todo.append(sz, args);
break;
case OP_IMPLIES:
if (v) {
if (is_true(args[1])) {
todo.push_back(args[1]);
}
else if (is_false(args[0])) {
todo.push_back(args[0]);
}
else {
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
UNREACHABLE();
}
}
else {
todo.append(sz, args);
}
break;
default:
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
UNREACHABLE();
}
}
else {
tocollect.push_back(e);
}
}
void model_implicant::collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect) {
ptr_vector<expr> todo;
todo.append(formulas);
m_visited.reset();
VERIFY(check_model(formulas));
while (!todo.empty()) {
app* e = to_app(todo.back());
todo.pop_back();
if (!m_visited.is_marked(e)) {
process_formula(e, todo, tocollect);
m_visited.mark(e, true);
}
}
m_visited.reset();
}
expr_ref_vector model_implicant::prune_by_cone_of_influence(ptr_vector<expr> const & formulas) {
ptr_vector<expr> tocollect;
collect(formulas, tocollect);
m1.reset();
m2.reset();
for (unsigned i = 0; i < tocollect.size(); ++i) {
TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";);
for_each_expr(*this, m_visited, tocollect[i]);
}
unsigned sz = m_model->get_num_constants();
expr_ref e(m), eq(m), val(m);
expr_ref_vector model(m);
for (unsigned i = 0; i < sz; i++) {
e = m.mk_const(m_model->get_constant(i));
if (m_visited.is_marked(e)) {
val = eval(m_model, e);
eq = m.mk_eq(e, val);
model.push_back(eq);
}
}
m_visited.reset();
TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";);
return model;
}
void model_implicant::eval_arith(app* e) {
rational r, r2;
#define ARG1 e->get_arg(0)
#define ARG2 e->get_arg(1)
unsigned arity = e->get_num_args();
for (unsigned i = 0; i < arity; ++i) {
expr* arg = e->get_arg(i);
if (is_x(arg)) {
set_x(e);
return;
}
SASSERT(!is_unknown(arg));
}
switch(e->get_decl_kind()) {
case OP_NUM:
VERIFY(m_arith.is_numeral(e, r));
set_number(e, r);
break;
case OP_IRRATIONAL_ALGEBRAIC_NUM:
set_x(e);
break;
case OP_LE:
set_bool(e, get_number(ARG1) <= get_number(ARG2));
break;
case OP_GE:
set_bool(e, get_number(ARG1) >= get_number(ARG2));
break;
case OP_LT:
set_bool(e, get_number(ARG1) < get_number(ARG2));
break;
case OP_GT:
set_bool(e, get_number(ARG1) > get_number(ARG2));
break;
case OP_ADD:
r = rational::zero();
for (unsigned i = 0; i < arity; ++i) {
r += get_number(e->get_arg(i));
}
set_number(e, r);
break;
case OP_SUB:
r = get_number(e->get_arg(0));
for (unsigned i = 1; i < arity; ++i) {
r -= get_number(e->get_arg(i));
}
set_number(e, r);
break;
case OP_UMINUS:
SASSERT(arity == 1);
set_number(e, get_number(e->get_arg(0)));
break;
case OP_MUL:
r = rational::one();
for (unsigned i = 0; i < arity; ++i) {
r *= get_number(e->get_arg(i));
}
set_number(e, r);
break;
case OP_DIV:
SASSERT(arity == 2);
r = get_number(ARG2);
if (r.is_zero()) {
set_x(e);
}
else {
set_number(e, get_number(ARG1) / r);
}
break;
case OP_IDIV:
SASSERT(arity == 2);
r = get_number(ARG2);
if (r.is_zero()) {
set_x(e);
}
else {
set_number(e, div(get_number(ARG1), r));
}
break;
case OP_REM:
// rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
SASSERT(arity == 2);
r = get_number(ARG2);
if (r.is_zero()) {
set_x(e);
}
else {
r2 = mod(get_number(ARG1), r);
if (r.is_neg()) r2.neg();
set_number(e, r2);
}
break;
case OP_MOD:
SASSERT(arity == 2);
r = get_number(ARG2);
if (r.is_zero()) {
set_x(e);
}
else {
set_number(e, mod(get_number(ARG1), r));
}
break;
case OP_TO_REAL:
SASSERT(arity == 1);
set_number(e, get_number(ARG1));
break;
case OP_TO_INT:
SASSERT(arity == 1);
set_number(e, floor(get_number(ARG1)));
break;
case OP_IS_INT:
SASSERT(arity == 1);
set_bool(e, get_number(ARG1).is_int());
break;
case OP_POWER:
set_x(e);
break;
default:
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
UNREACHABLE();
break;
}
}
void model_implicant::inherit_value(expr* e, expr* v) {
expr* w;
SASSERT(!is_unknown(v));
SASSERT(m.get_sort(e) == m.get_sort(v));
if (is_x(v)) {
set_x(e);
}
else if (m.is_bool(e)) {
SASSERT(m.is_bool(v));
if (is_true(v)) set_true(e);
else if (is_false(v)) set_false(e);
else {
TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
set_x(e);
}
}
else if (m_arith.is_int_real(e)) {
set_number(e, get_number(v));
}
else if (m.is_value(v)) {
set_value(e, v);
}
else if (m_values.find(v, w)) {
set_value(e, w);
}
else {
TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
set_x(e);
}
}
void model_implicant::eval_exprs(expr_ref_vector& es) {
model_ref mr(m_model);
for (unsigned j = 0; j < es.size(); ++j) {
if (m_array.is_as_array(es[j].get())) {
es[j] = eval(mr, es[j].get());
}
}
}
bool model_implicant::extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) {
SASSERT(m_array.is_array(a));
TRACE("pdr", tout << mk_pp(a, m) << "\n";);
while (m_array.is_store(a)) {
expr_ref_vector store(m);
store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
eval_exprs(store);
stores.push_back(store);
a = to_app(a)->get_arg(0);
}
if (m_array.is_const(a)) {
else_case = to_app(a)->get_arg(0);
return true;
}
while (m_array.is_as_array(a)) {
func_decl* f = m_array.get_as_array_func_decl(to_app(a));
func_interp* g = m_model->get_func_interp(f);
unsigned sz = g->num_entries();
unsigned arity = f->get_arity();
for (unsigned i = 0; i < sz; ++i) {
expr_ref_vector store(m);
func_entry const* fe = g->get_entry(i);
store.append(arity, fe->get_args());
store.push_back(fe->get_result());
for (unsigned j = 0; j < store.size(); ++j) {
if (!is_ground(store[j].get())) {
TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";);
return false;
}
}
eval_exprs(store);
stores.push_back(store);
}
else_case = g->get_else();
if (!else_case) {
TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";);
return false;
}
if (!is_ground(else_case)) {
TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";);
return false;
}
if (m_array.is_as_array(else_case)) {
model_ref mr(m_model);
else_case = eval(mr, else_case);
}
TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";);
return true;
}
TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";);
return false;
}
/**
best effort evaluator of extensional array equality.
*/
void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) {
TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";);
expr_ref v1(m), v2(m);
m_model->eval(arg1, v1);
m_model->eval(arg2, v2);
if (v1 == v2) {
set_true(e);
return;
}
sort* s = m.get_sort(arg1);
sort* r = get_array_range(s);
// give up evaluating finite domain/range arrays
if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) {
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
set_x(e);
return;
}
vector<expr_ref_vector> store;
expr_ref else1(m), else2(m);
if (!extract_array_func_interp(v1, store, else1) ||
!extract_array_func_interp(v2, store, else2)) {
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
set_x(e);
return;
}
if (else1 != else2) {
if (m.is_value(else1) && m.is_value(else2)) {
TRACE("pdr", tout
<< "defaults are different: " << mk_pp(e, m) << " "
<< mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";);
set_false(e);
}
else if (m_array.is_array(else1)) {
eval_array_eq(e, else1, else2);
}
else {
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
set_x(e);
}
return;
}
expr_ref s1(m), s2(m), w1(m), w2(m);
expr_ref_vector args1(m), args2(m);
args1.push_back(v1);
args2.push_back(v2);
for (unsigned i = 0; i < store.size(); ++i) {
args1.resize(1);
args2.resize(1);
args1.append(store[i].size()-1, store[i].c_ptr());
args2.append(store[i].size()-1, store[i].c_ptr());
s1 = m_array.mk_select(args1.size(), args1.c_ptr());
s2 = m_array.mk_select(args2.size(), args2.c_ptr());
m_model->eval(s1, w1);
m_model->eval(s2, w2);
if (w1 == w2) {
continue;
}
if (m.is_value(w1) && m.is_value(w2)) {
TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n";
tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n";
tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";);
set_false(e);
}
else if (m_array.is_array(w1)) {
eval_array_eq(e, w1, w2);
if (is_true(e)) {
continue;
}
}
else {
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
set_x(e);
}
return;
}
set_true(e);
}
void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) {
if (arg1 == arg2) {
set_true(e);
}
else if (m_array.is_array(arg1)) {
eval_array_eq(e, arg1, arg2);
}
else if (is_x(arg1) || is_x(arg2)) {
expr_ref eq(m), vl(m);
eq = m.mk_eq(arg1, arg2);
m_model->eval(eq, vl);
if (m.is_true(vl)) {
set_bool(e, true);
}
else if (m.is_false(vl)) {
set_bool(e, false);
}
else {
TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";);
set_x(e);
}
}
else if (m.is_bool(arg1)) {
bool val = is_true(arg1) == is_true(arg2);
SASSERT(val == (is_false(arg1) == is_false(arg2)));
if (val) {
set_true(e);
}
else {
set_false(e);
}
}
else if (m_arith.is_int_real(arg1)) {
set_bool(e, get_number(arg1) == get_number(arg2));
}
else {
expr* e1 = get_value(arg1);
expr* e2 = get_value(arg2);
if (m.is_value(e1) && m.is_value(e2)) {
set_bool(e, e1 == e2);
}
else if (e1 == e2) {
set_bool(e, true);
}
else {
TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";);
set_x(e);
}
}
}
void model_implicant::eval_basic(app* e) {
expr* arg1, *arg2;
expr *argCond, *argThen, *argElse, *arg;
bool has_x = false;
unsigned arity = e->get_num_args();
switch(e->get_decl_kind()) {
case OP_AND:
for (unsigned j = 0; j < arity; ++j) {
expr * arg = e->get_arg(j);
if (is_false(arg)) {
set_false(e);
return;
}
else if (is_x(arg)) {
has_x = true;
}
else {
SASSERT(is_true(arg));
}
}
if (has_x) {
set_x(e);
}
else {
set_true(e);
}
break;
case OP_OR:
for (unsigned j = 0; j < arity; ++j) {
expr * arg = e->get_arg(j);
if (is_true(arg)) {
set_true(e);
return;
}
else if (is_x(arg)) {
has_x = true;
}
else {
SASSERT(is_false(arg));
}
}
if (has_x) {
set_x(e);
}
else {
set_false(e);
}
break;
case OP_NOT:
VERIFY(m.is_not(e, arg));
if (is_true(arg)) {
set_false(e);
}
else if (is_false(arg)) {
set_true(e);
}
else {
SASSERT(is_x(arg));
set_x(e);
}
break;
case OP_IMPLIES:
VERIFY(m.is_implies(e, arg1, arg2));
if (is_false(arg1) || is_true(arg2)) {
set_true(e);
}
else if (arg1 == arg2) {
set_true(e);
}
else if (is_true(arg1) && is_false(arg2)) {
set_false(e);
}
else {
SASSERT(is_x(arg1) || is_x(arg2));
set_x(e);
}
break;
case OP_IFF:
VERIFY(m.is_iff(e, arg1, arg2));
eval_eq(e, arg1, arg2);
break;
case OP_ITE:
VERIFY(m.is_ite(e, argCond, argThen, argElse));
if (is_true(argCond)) {
inherit_value(e, argThen);
}
else if (is_false(argCond)) {
inherit_value(e, argElse);
}
else if (argThen == argElse) {
inherit_value(e, argThen);
}
else if (m.is_bool(e)) {
SASSERT(is_x(argCond));
if (is_x(argThen) || is_x(argElse)) {
set_x(e);
}
else if (is_true(argThen) == is_true(argElse)) {
inherit_value(e, argThen);
}
else {
set_x(e);
}
}
else {
set_x(e);
}
break;
case OP_TRUE:
set_true(e);
break;
case OP_FALSE:
set_false(e);
break;
case OP_EQ:
VERIFY(m.is_eq(e, arg1, arg2));
eval_eq(e, arg1, arg2);
break;
case OP_DISTINCT: {
vector<rational> values;
for (unsigned i = 0; i < arity; ++i) {
expr* arg = e->get_arg(i);
if (is_x(arg)) {
set_x(e);
return;
}
values.push_back(get_number(arg));
}
std::sort(values.begin(), values.end());
for (unsigned i = 0; i + 1 < values.size(); ++i) {
if (values[i] == values[i+1]) {
set_false(e);
return;
}
}
set_true(e);
break;
}
default:
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
UNREACHABLE();
}
}
bool model_implicant::check_model(ptr_vector<expr> const& formulas) {
ptr_vector<expr> todo(formulas);
while (!todo.empty()) {
expr * curr_e = todo.back();
if (!is_app(curr_e)) {
todo.pop_back();
continue;
}
app * curr = to_app(curr_e);
if (!is_unknown(curr)) {
todo.pop_back();
continue;
}
unsigned arity = curr->get_num_args();
for (unsigned i = 0; i < arity; ++i) {
if (is_unknown(curr->get_arg(i))) {
todo.push_back(curr->get_arg(i));
}
}
if (todo.back() != curr) {
continue;
}
todo.pop_back();
if (curr->get_family_id() == m_arith.get_family_id()) {
eval_arith(curr);
}
else if (curr->get_family_id() == m.get_basic_family_id()) {
eval_basic(curr);
}
else {
expr_ref vl(m);
m_model->eval(curr, vl);
assign_value(curr, vl);
}
IF_VERBOSE(35,verbose_stream() << "assigned "<<mk_pp(curr_e,m)
<<(is_true(curr_e) ? "true" : is_false(curr_e) ? "false" : "unknown") << "\n";);
SASSERT(!is_unknown(curr));
}
bool has_x = false;
for (unsigned i = 0; i < formulas.size(); ++i) {
expr * form = formulas[i];
SASSERT(!is_unknown(form));
TRACE("pdr_verbose",
tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" <<mk_pp(form, m)<< "\n";);
if (is_false(form)) {
IF_VERBOSE(0, verbose_stream() << "formula false in model: " << mk_pp(form, m) << "\n";);
UNREACHABLE();
}
if (is_x(form)) {
IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";);
TRACE("pdr", model_smt2_pp(tout, m, *m_model, 0););
has_x = true;
}
}
return !has_x;
}
expr_ref model_implicant::eval(model_ref& model, func_decl* d) {
SASSERT(d->get_arity() == 0);
expr_ref result(m);
if (m_array.is_array(d->get_range())) {
expr_ref e(m);
e = m.mk_const(d);
result = eval(model, e);
}
else {
result = model->get_const_interp(d);
}
return result;
}
expr_ref model_implicant::eval(model_ref& model, expr* e) {
expr_ref result(m);
m_model = model;
VERIFY(m_model->eval(e, result, true));
if (m_array.is_array(e)) {
vector<expr_ref_vector> stores;
expr_ref_vector args(m);
expr_ref else_case(m);
if (extract_array_func_interp(result, stores, else_case)) {
result = m_array.mk_const_array(m.get_sort(e), else_case);
while (!stores.empty() && stores.back().back() == else_case) {
stores.pop_back();
}
for (unsigned i = stores.size(); i > 0; ) {
--i;
args.resize(1);
args[0] = result;
args.append(stores[i]);
result = m_array.mk_store(args.size(), args.c_ptr());
}
return result;
}
}
return result;
}

118
src/model/model_implicant.h Normal file
View file

@ -0,0 +1,118 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
model_implicant.h
Abstract:
Facility to extract prime implicant from model.
Author:
Krystof Hoder (t-khoder) 2011-8-19.
Revision History:
--*/
#ifndef _MODEL_IMPLICANT_H_
#define _MODEL_IMPLICANT_H_
#include "ast.h"
#include "ast_pp.h"
#include "obj_hashtable.h"
#include "ref_vector.h"
#include "trace.h"
#include "vector.h"
#include "arith_decl_plugin.h"
#include "array_decl_plugin.h"
class model;
class model_core;
class model_implicant {
ast_manager& m;
arith_util m_arith;
array_util m_array;
obj_map<expr,rational> m_numbers;
expr_ref_vector m_refs;
obj_map<expr, expr*> m_values;
model_ref m_model;
//00 -- non-visited
//01 -- X
//10 -- false
//11 -- true
expr_mark m1;
expr_mark m2;
expr_mark m_visited;
void reset();
void setup_model(model_ref& model);
void assign_value(expr* e, expr* v);
void collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect);
void process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect);
expr_ref_vector prune_by_cone_of_influence(ptr_vector<expr> const & formulas);
void eval_arith(app* e);
void eval_basic(app* e);
void eval_eq(app* e, expr* arg1, expr* arg2);
void eval_array_eq(app* e, expr* arg1, expr* arg2);
void inherit_value(expr* e, expr* v);
inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); }
inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); }
inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); }
inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); }
inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); }
inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); }
inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); }
inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } }
inline rational const& get_number(expr* x) const { return m_numbers.find(x); }
inline void set_number(expr* x, rational const& v) {
set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v);
}
inline expr* get_value(expr* x) { return m_values.find(x); }
inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); }
bool check_model(ptr_vector<expr> const & formulas);
bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case);
void eval_exprs(expr_ref_vector& es);
public:
model_implicant(ast_manager& m) :
m(m), m_arith(m), m_array(m), m_refs(m) {}
/**
\brief extract equalities from model that suffice to satisfy formula.
\pre model satisfies formulas
*/
expr_ref_vector minimize_model(ptr_vector<expr> const & formulas, model_ref& mdl);
/**
\brief extract literals from model that satisfy formulas.
\pre model satisfies formulas
*/
expr_ref_vector minimize_literals(ptr_vector<expr> const & formulas, model_ref& mdl);
/**
for_each_expr visitor.
*/
void operator()(expr* e) {}
expr_ref eval(model_ref& mdl, expr* e);
expr_ref eval(model_ref& mdl, func_decl* d);
};
#endif

View file

@ -0,0 +1,154 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
bind_variables.cpp
Abstract:
Utility to find constants that are declared as variables.
Author:
Nikolaj Bjorner (nbjorner) 9-24-2014
Notes:
--*/
#include "bind_variables.h"
bind_variables::bind_variables(ast_manager & m):
m(m),
m_vars(m),
m_pinned(m)
{}
bind_variables::~bind_variables() {
}
expr_ref bind_variables::operator()(expr* fml, bool is_forall) {
if (m_vars.empty()) {
return expr_ref(fml, m);
}
SASSERT(m_pinned.empty());
expr_ref result = abstract(fml, m_cache, 0);
if (!m_names.empty()) {
m_bound.reverse();
m_names.reverse();
result = m.mk_quantifier(is_forall, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result);
}
m_pinned.reset();
m_cache.reset();
m_names.reset();
m_bound.reset();
for (var2bound::iterator it = m_var2bound.begin(); it != m_var2bound.end(); ++it) {
it->m_value = 0;
}
return result;
}
expr_ref bind_variables::abstract(expr* term, cache_t& cache, unsigned scope) {
unsigned sz = m_todo.size();
m_todo.push_back(term);
m_args.reset();
expr* b, *arg;
while (m_todo.size() > sz) {
expr* e = m_todo.back();
if (cache.contains(e)) {
m_todo.pop_back();
continue;
}
switch(e->get_kind()) {
case AST_VAR: {
SASSERT(to_var(e)->get_idx() < scope);
// mixing bound variables and free is possible for the caller,
// but not proper use.
// So we assert here even though we don't check for it.
cache.insert(e, e);
m_todo.pop_back();
break;
}
case AST_APP: {
app* a = to_app(e);
var2bound::obj_map_entry* w = m_var2bound.find_core(a);
if (w) {
var* v = w->get_data().m_value;
if (!v) {
// allocate a bound index.
v = m.mk_var(m_names.size(), m.get_sort(a));
m_names.push_back(a->get_decl()->get_name());
m_bound.push_back(m.get_sort(a));
w->get_data().m_value = v;
m_pinned.push_back(v);
}
if (scope == 0) {
cache.insert(e, v);
}
else {
var* v1 = m.mk_var(scope + v->get_idx(), m.get_sort(v));
m_pinned.push_back(v1);
cache.insert(e, v1);
}
m_todo.pop_back();
break;
}
bool all_visited = true;
bool some_diff = false;
m_args.reset();
for (unsigned i = 0; i < a->get_num_args(); ++i) {
arg = a->get_arg(i);
if (!cache.find(arg, b)) {
m_todo.push_back(arg);
all_visited = false;
}
else if (all_visited) {
m_args.push_back(b);
if (b != arg) {
some_diff = true;
}
}
}
if (all_visited) {
if (some_diff) {
b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr());
m_pinned.push_back(b);
}
else {
b = a;
}
cache.insert(e, b);
m_todo.pop_back();
}
break;
}
case AST_QUANTIFIER: {
quantifier* q = to_quantifier(e);
expr_ref_buffer patterns(m);
expr_ref result1(m);
unsigned new_scope = scope + q->get_num_decls();
cache_t new_cache;
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
patterns.push_back(abstract(q->get_pattern(i), new_cache, new_scope));
}
result1 = abstract(q->get_expr(), new_cache, new_scope);
b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get());
m_pinned.push_back(b);
cache.insert(e, b);
m_todo.pop_back();
break;
}
default:
UNREACHABLE();
}
}
return expr_ref(cache.find(term), m);
}
void bind_variables::add_var(app* v) {
m_vars.push_back(v);
m_var2bound.insert(v, 0);
}

View file

@ -0,0 +1,51 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
bind_variables.h
Abstract:
Utility to find constants that are declard as varaibles.
Author:
Nikolaj Bjorner (nbjorner) 9-24-2014
Notes:
--*/
#ifndef _BIND_VARIABLES_H_
#define _BIND_VARIABLES_H_
#include"ast.h"
class bind_variables {
typedef obj_map<app, var*> var2bound;
typedef obj_map<expr, expr*> cache_t;
ast_manager& m;
app_ref_vector m_vars;
obj_map<expr, expr*> m_cache;
var2bound m_var2bound;
expr_ref_vector m_pinned;
ptr_vector<sort> m_bound;
svector<symbol> m_names;
ptr_vector<expr> m_todo;
ptr_vector<expr> m_args;
expr_ref abstract(expr* fml, cache_t& cache, unsigned scope);
public:
bind_variables(ast_manager & m);
~bind_variables();
expr_ref operator()(expr* fml, bool is_forall);
void add_var(app* v);
};
#endif /* _BIND_VARIABLES_H_ */

View file

@ -210,14 +210,12 @@ namespace datalog {
m_rewriter(m),
m_var_subst(m),
m_rule_manager(*this),
m_elim_unused_vars(m),
m_abstractor(m),
m_contains_p(*this),
m_check_pred(m_contains_p, m),
m_rule_properties(m, m_rule_manager, *this, m_contains_p),
m_transf(*this),
m_trail(*this),
m_pinned(m),
m_vars(m),
m_bind_variables(m),
m_rule_set(*this),
m_transformed_rule_set(*this),
m_rule_fmls_head(0),
@ -233,6 +231,7 @@ namespace datalog {
m_engine_type(LAST_ENGINE),
m_cancel(false) {
re.set_context(this);
updt_params(pa);
}
context::~context() {
@ -272,35 +271,39 @@ namespace datalog {
}
bool context::generate_proof_trace() const { return m_params->generate_proof_trace(); }
bool context::output_profile() const { return m_params->output_profile(); }
bool context::output_tuples() const { return m_params->output_tuples(); }
bool context::use_map_names() const { return m_params->use_map_names(); }
bool context::fix_unbound_vars() const { return m_params->fix_unbound_vars(); }
symbol context::default_table() const { return m_params->default_table(); }
symbol context::default_relation() const { return m_params->default_relation(); } // external_relation_plugin::get_name());
symbol context::default_table_checker() const { return m_params->default_table_checker(); }
bool context::default_table_checked() const { return m_params->default_table_checked(); }
bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->dbg_fpr_nonempty_relation_signature(); }
unsigned context::dl_profile_milliseconds_threshold() const { return m_params->profile_timeout_milliseconds(); }
bool context::all_or_nothing_deltas() const { return m_params->all_or_nothing_deltas(); }
bool context::compile_with_widening() const { return m_params->compile_with_widening(); }
bool context::unbound_compressor() const { return m_params->unbound_compressor(); }
bool context::similarity_compressor() const { return m_params->similarity_compressor(); }
unsigned context::similarity_compressor_threshold() const { return m_params->similarity_compressor_threshold(); }
bool context::generate_proof_trace() const { return m_generate_proof_trace; }
bool context::output_profile() const { return m_params->datalog_output_profile(); }
bool context::output_tuples() const { return m_params->datalog_print_tuples(); }
bool context::use_map_names() const { return m_params->datalog_use_map_names(); }
bool context::fix_unbound_vars() const { return m_params->xform_fix_unbound_vars(); }
symbol context::default_table() const { return m_params->datalog_default_table(); }
symbol context::default_relation() const { return m_default_relation; }
void context::set_default_relation(symbol const& s) { m_default_relation = s; }
symbol context::print_aig() const { return m_params->print_aig(); }
symbol context::check_relation() const { return m_params->datalog_check_relation(); }
symbol context::default_table_checker() const { return m_params->datalog_default_table_checker(); }
bool context::default_table_checked() const { return m_params->datalog_default_table_checked(); }
bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->datalog_dbg_fpr_nonempty_relation_signature(); }
unsigned context::dl_profile_milliseconds_threshold() const { return m_params->datalog_profile_timeout_milliseconds(); }
bool context::all_or_nothing_deltas() const { return m_params->datalog_all_or_nothing_deltas(); }
bool context::compile_with_widening() const { return m_params->datalog_compile_with_widening(); }
bool context::unbound_compressor() const { return m_unbound_compressor; }
void context::set_unbound_compressor(bool f) { m_unbound_compressor = f; }
bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); }
unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); }
unsigned context::soft_timeout() const { return m_fparams.m_soft_timeout; }
unsigned context::initial_restart_timeout() const { return m_params->initial_restart_timeout(); }
bool context::generate_explanations() const { return m_params->generate_explanations(); }
bool context::explanations_on_relation_level() const { return m_params->explanations_on_relation_level(); }
bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); }
bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); }
bool context::bit_blast() const { return m_params->bit_blast(); }
bool context::karr() const { return m_params->karr(); }
bool context::scale() const { return m_params->scale(); }
bool context::magic() const { return m_params->magic(); }
bool context::quantify_arrays() const { return m_params->quantify_arrays(); }
bool context::instantiate_quantifiers() const { return m_params->instantiate_quantifiers(); }
unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); }
bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); }
bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); }
bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); }
symbol context::tab_selection() const { return m_params->tab_selection(); }
bool context::xform_slice() const { return m_params->xform_slice(); }
bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); }
bool context::karr() const { return m_params->xform_karr(); }
bool context::scale() const { return m_params->xform_scale(); }
bool context::magic() const { return m_params->xform_magic(); }
bool context::quantify_arrays() const { return m_params->xform_quantify_arrays(); }
bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); }
void context::register_finite_sort(sort * s, sort_kind k) {
@ -321,48 +324,11 @@ namespace datalog {
}
void context::register_variable(func_decl* var) {
m_vars.push_back(m.mk_const(var));
m_bind_variables.add_var(m.mk_const(var));
}
expr_ref context::bind_variables(expr* fml, bool is_forall) {
expr_ref result(m);
app_ref_vector const & vars = m_vars;
rule_manager& rm = get_rule_manager();
if (vars.empty()) {
result = fml;
}
else {
m_names.reset();
m_abstractor(0, vars.size(), reinterpret_cast<expr*const*>(vars.c_ptr()), fml, result);
rm.collect_vars(result);
ptr_vector<sort>& sorts = rm.get_var_sorts();
if (sorts.empty()) {
result = fml;
}
else {
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i]) {
if (i < vars.size()) {
sorts[i] = vars[i]->get_decl()->get_range();
}
else {
sorts[i] = m.mk_bool_sort();
}
}
if (i < vars.size()) {
m_names.push_back(vars[i]->get_decl()->get_name());
}
else {
m_names.push_back(symbol(i));
}
}
quantifier_ref q(m);
sorts.reverse();
q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result);
m_elim_unused_vars(q, result);
}
}
return result;
expr_ref context::bind_vars(expr* fml, bool is_forall) {
return m_bind_variables(fml, is_forall);
}
void context::register_predicate(func_decl * decl, bool named) {
@ -490,12 +456,7 @@ namespace datalog {
rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]);
++m_rule_fmls_head;
}
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
rule_ref r(m_rule_manager);
for (; it != end; ++it) {
r = *it;
check_rule(r);
}
check_rules(m_rule_set);
}
//
@ -581,147 +542,57 @@ namespace datalog {
m_engine->add_cover(level, pred, property);
}
void context::check_uninterpreted_free(rule_ref& r) {
func_decl* f = 0;
if (r->has_uninterpreted_non_predicates(m, f)) {
std::stringstream stm;
stm << "Uninterpreted '"
<< f->get_name()
<< "' in ";
r->display(*this, stm);
throw default_exception(stm.str());
}
}
void context::check_quantifier_free(rule_ref& r) {
if (r->has_quantifiers()) {
std::stringstream stm;
stm << "cannot process quantifiers in rule ";
r->display(*this, stm);
throw default_exception(stm.str());
}
}
void context::check_existential_tail(rule_ref& r) {
unsigned ut_size = r->get_uninterpreted_tail_size();
unsigned t_size = r->get_tail_size();
TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";);
for (unsigned i = ut_size; i < t_size; ++i) {
app* t = r->get_tail(i);
TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";);
if (m_check_pred(t)) {
std::ostringstream out;
out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate";
throw default_exception(out.str());
}
}
}
void context::check_positive_predicates(rule_ref& r) {
ast_mark visited;
ptr_vector<expr> todo, tocheck;
unsigned ut_size = r->get_uninterpreted_tail_size();
unsigned t_size = r->get_tail_size();
for (unsigned i = 0; i < ut_size; ++i) {
if (r->is_neg_tail(i)) {
tocheck.push_back(r->get_tail(i));
}
}
ast_manager& m = get_manager();
contains_pred contains_p(*this);
check_pred check_pred(contains_p, get_manager());
for (unsigned i = ut_size; i < t_size; ++i) {
todo.push_back(r->get_tail(i));
}
while (!todo.empty()) {
expr* e = todo.back(), *e1, *e2;
todo.pop_back();
if (visited.is_marked(e)) {
continue;
}
visited.mark(e, true);
if (is_predicate(e)) {
}
else if (m.is_and(e) || m.is_or(e)) {
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
}
else if (m.is_implies(e, e1, e2)) {
tocheck.push_back(e1);
todo.push_back(e2);
}
else if (is_quantifier(e)) {
todo.push_back(to_quantifier(e)->get_expr());
}
else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
m.is_true(e1)) {
todo.push_back(e2);
}
else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
m.is_true(e2)) {
todo.push_back(e1);
}
else {
tocheck.push_back(e);
}
}
for (unsigned i = 0; i < tocheck.size(); ++i) {
expr* e = tocheck[i];
if (check_pred(e)) {
std::ostringstream out;
out << "recursive predicate " << mk_ismt2_pp(e, get_manager()) << " occurs nested in body";
r->display(*this, out << "\n");
throw default_exception(out.str());
}
}
}
void context::check_rule(rule_ref& r) {
void context::check_rules(rule_set& r) {
m_rule_properties.set_generate_proof(generate_proof_trace());
switch(get_engine()) {
case DATALOG_ENGINE:
check_quantifier_free(r);
check_uninterpreted_free(r);
check_existential_tail(r);
case DATALOG_ENGINE:
m_rule_properties.collect(r);
m_rule_properties.check_quantifier_free();
m_rule_properties.check_uninterpreted_free();
m_rule_properties.check_nested_free();
break;
case PDR_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
check_uninterpreted_free(r);
m_rule_properties.collect(r);
m_rule_properties.check_existential_tail();
m_rule_properties.check_for_negated_predicates();
m_rule_properties.check_uninterpreted_free();
break;
case QPDR_ENGINE:
check_positive_predicates(r);
check_uninterpreted_free(r);
m_rule_properties.collect(r);
m_rule_properties.check_for_negated_predicates();
m_rule_properties.check_uninterpreted_free();
break;
case BMC_ENGINE:
check_positive_predicates(r);
m_rule_properties.collect(r);
m_rule_properties.check_for_negated_predicates();
break;
case QBMC_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
m_rule_properties.collect(r);
m_rule_properties.check_existential_tail();
m_rule_properties.check_for_negated_predicates();
break;
case TAB_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
m_rule_properties.collect(r);
m_rule_properties.check_existential_tail();
m_rule_properties.check_for_negated_predicates();
break;
case DUALITY_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
m_rule_properties.collect(r);
m_rule_properties.check_existential_tail();
m_rule_properties.check_for_negated_predicates();
break;
case CLP_ENGINE:
check_existential_tail(r);
check_positive_predicates(r);
m_rule_properties.collect(r);
m_rule_properties.check_existential_tail();
m_rule_properties.check_for_negated_predicates();
break;
case DDNF_ENGINE:
break;
case LAST_ENGINE:
default:
UNREACHABLE();
break;
}
if (generate_proof_trace() && !r->get_proof()) {
m_rule_manager.mk_rule_asserted_proof(*r.get());
}
}
void context::add_rule(rule_ref& r) {
@ -847,6 +718,9 @@ namespace datalog {
void context::updt_params(params_ref const& p) {
m_params_ref.copy(p);
if (m_engine.get()) m_engine->updt_params();
m_generate_proof_trace = m_params->generate_proof_trace();
m_unbound_compressor = m_params->datalog_unbound_compressor();
m_default_relation = m_params->datalog_default_relation();
}
expr_ref context::get_background_assertion() {
@ -908,6 +782,9 @@ namespace datalog {
};
void context::configure_engine() {
if (m_engine_type != LAST_ENGINE) {
return;
}
symbol e = m_params->engine();
if (e == symbol("datalog")) {
@ -934,6 +811,9 @@ namespace datalog {
else if (e == symbol("duality")) {
m_engine_type = DUALITY_ENGINE;
}
else if (e == symbol("ddnf")) {
m_engine_type = DDNF_ENGINE;
}
if (m_engine_type == LAST_ENGINE) {
expr_fast_mark1 mark;
@ -963,12 +843,7 @@ namespace datalog {
// TODO: what?
if(get_engine() != DUALITY_ENGINE) {
new_query();
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
rule_ref r(m_rule_manager);
for (; it != end; ++it) {
r = *it;
check_rule(r);
}
check_rules(m_rule_set);
}
#endif
m_mc = mk_skip_model_converter();
@ -982,6 +857,7 @@ namespace datalog {
case QBMC_ENGINE:
case TAB_ENGINE:
case CLP_ENGINE:
case DDNF_ENGINE:
flush_add_rules();
break;
case DUALITY_ENGINE:
@ -1009,6 +885,7 @@ namespace datalog {
void context::ensure_engine() {
if (!m_engine.get()) {
m_engine = m_register_engine.mk_engine(get_engine());
m_engine->updt_params();
// break abstraction.
if (get_engine() == DATALOG_ENGINE) {
@ -1109,7 +986,7 @@ namespace datalog {
void context::get_raw_rule_formulas(expr_ref_vector& rules, svector<symbol>& names, vector<unsigned> &bounds){
for (unsigned i = 0; i < m_rule_fmls.size(); ++i) {
expr_ref r = bind_variables(m_rule_fmls[i].get(), true);
expr_ref r = bind_vars(m_rule_fmls[i].get(), true);
rules.push_back(r.get());
// rules.push_back(m_rule_fmls[i].get());
names.push_back(m_rule_names[i]);
@ -1117,15 +994,15 @@ namespace datalog {
}
}
void context::get_rules_as_formulas(expr_ref_vector& rules, svector<symbol>& names) {
void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector<symbol>& names) {
expr_ref fml(m);
datalog::rule_manager& rm = get_rule_manager();
rule_manager& rm = get_rule_manager();
// ensure that rules are all using bound variables.
for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) {
ptr_vector<sort> sorts;
get_free_vars(m_rule_fmls[i].get(), sorts);
if (!sorts.empty()) {
m_free_vars(m_rule_fmls[i].get());
if (!m_free_vars.empty()) {
rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]);
m_rule_fmls[i] = m_rule_fmls.back();
m_rule_names[i] = m_rule_names.back();
@ -1137,9 +1014,30 @@ namespace datalog {
}
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
for (; it != end; ++it) {
(*it)->to_formula(fml);
rules.push_back(fml);
names.push_back((*it)->name());
rule* r = *it;
rm.to_formula(*r, fml);
func_decl* h = r->get_decl();
if (m_rule_set.is_output_predicate(h)) {
expr* body = fml;
expr* e2;
if (is_quantifier(body)) {
quantifier* q = to_quantifier(body);
expr* e = q->get_expr();
VERIFY(m.is_implies(e, body, e2));
fml = m.mk_quantifier(false, q->get_num_decls(),
q->get_decl_sorts(), q->get_decl_names(),
body);
}
else {
VERIFY(m.is_implies(body, body, e2));
fml = body;
}
queries.push_back(fml);
}
else {
rules.push_back(fml);
names.push_back(r->name());
}
}
for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) {
rules.push_back(m_rule_fmls[i].get());
@ -1147,10 +1045,7 @@ namespace datalog {
}
}
void context::display_smt2(
unsigned num_queries,
expr* const* queries,
std::ostream& out) {
void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) {
ast_manager& m = get_manager();
free_func_visitor visitor(m);
expr_mark visited;
@ -1158,21 +1053,22 @@ namespace datalog {
unsigned num_axioms = m_background.size();
expr* const* axioms = m_background.c_ptr();
expr_ref fml(m);
expr_ref_vector rules(m);
expr_ref_vector rules(m), queries(m);
svector<symbol> names;
bool use_fixedpoint_extensions = m_params->print_with_fixedpoint_extensions();
bool use_fixedpoint_extensions = m_params->print_fixedpoint_extensions();
bool print_low_level = m_params->print_low_level_smt2();
bool do_declare_vars = m_params->print_with_variable_declarations();
#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env);
get_rules_as_formulas(rules, names);
get_rules_as_formulas(rules, queries, names);
queries.append(num_queries, qs);
smt2_pp_environment_dbg env(m);
mk_fresh_name fresh_names;
collect_free_funcs(num_axioms, axioms, visited, visitor, fresh_names);
collect_free_funcs(rules.size(), rules.c_ptr(), visited, visitor, fresh_names);
collect_free_funcs(num_queries, queries, visited, visitor, fresh_names);
collect_free_funcs(queries.size(), queries.c_ptr(), visited, visitor, fresh_names);
func_decl_set funcs;
func_decl_set::iterator it = visitor.funcs().begin();
func_decl_set::iterator end = visitor.funcs().end();
@ -1258,22 +1154,22 @@ namespace datalog {
out << ")\n";
}
if (use_fixedpoint_extensions) {
for (unsigned i = 0; i < num_queries; ++i) {
for (unsigned i = 0; i < queries.size(); ++i) {
out << "(query ";
PP(queries[i]);
PP(queries[i].get());
out << ")\n";
}
}
else {
for (unsigned i = 0; i < num_queries; ++i) {
if (num_queries > 1) out << "(push)\n";
for (unsigned i = 0; i < queries.size(); ++i) {
if (queries.size() > 1) out << "(push)\n";
out << "(assert ";
expr_ref q(m);
q = m.mk_not(queries[i]);
q = m.mk_not(queries[i].get());
PP(q);
out << ")\n";
out << "(check-sat)\n";
if (num_queries > 1) out << "(pop)\n";
if (queries.size() > 1) out << "(pop)\n";
}
}
}

View file

@ -39,9 +39,10 @@ Revision History:
#include"model2expr.h"
#include"smt_params.h"
#include"dl_rule_transformer.h"
#include"expr_abstract.h"
#include"expr_functors.h"
#include"dl_engine_base.h"
#include"bind_variables.h"
#include"rule_properties.h"
struct fixedpoint_params;
@ -142,19 +143,6 @@ namespace datalog {
SK_UINT64,
SK_SYMBOL
};
private:
class sort_domain;
class symbol_sort_domain;
class uint64_sort_domain;
class restore_rules;
class contains_pred;
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
typedef obj_map<const sort, sort_domain*> sort_domain_map;
class contains_pred : public i_expr_pred {
context const& ctx;
public:
@ -167,30 +155,43 @@ namespace datalog {
};
private:
class sort_domain;
class symbol_sort_domain;
class uint64_sort_domain;
class restore_rules;
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
typedef obj_map<const sort, sort_domain*> sort_domain_map;
ast_manager & m;
register_engine_base& m_register_engine;
smt_params & m_fparams;
params_ref m_params_ref;
fixedpoint_params* m_params;
bool m_generate_proof_trace; // cached configuration parameter
bool m_unbound_compressor; // cached configuration parameter
symbol m_default_relation; // cached configuration parameter
dl_decl_util m_decl_util;
th_rewriter m_rewriter;
var_subst m_var_subst;
rule_manager m_rule_manager;
unused_vars_eliminator m_elim_unused_vars;
expr_abstractor m_abstractor;
contains_pred m_contains_p;
check_pred m_check_pred;
rule_properties m_rule_properties;
rule_transformer m_transf;
trail_stack<context> m_trail;
ast_ref_vector m_pinned;
app_ref_vector m_vars;
svector<symbol> m_names;
bind_variables m_bind_variables;
sort_domain_map m_sorts;
func_decl_set m_preds;
sym2decl m_preds_by_name;
pred2syms m_argument_var_names;
rule_set m_rule_set;
rule_set m_transformed_rule_set;
expr_free_vars m_free_vars;
unsigned m_rule_fmls_head;
expr_ref_vector m_rule_fmls;
svector<symbol> m_rule_names;
@ -249,27 +250,32 @@ namespace datalog {
bool fix_unbound_vars() const;
symbol default_table() const;
symbol default_relation() const;
symbol default_table_checker() const;
void set_default_relation(symbol const& s);
symbol default_table_checker() const;
symbol check_relation() const;
bool default_table_checked() const;
bool dbg_fpr_nonempty_relation_signature() const;
unsigned dl_profile_milliseconds_threshold() const;
bool all_or_nothing_deltas() const;
bool compile_with_widening() const;
bool unbound_compressor() const;
void set_unbound_compressor(bool f);
bool similarity_compressor() const;
symbol print_aig() const;
symbol tab_selection() const;
unsigned similarity_compressor_threshold() const;
unsigned soft_timeout() const;
unsigned initial_restart_timeout() const;
bool generate_explanations() const;
bool explanations_on_relation_level() const;
bool magic_sets_for_queries() const;
bool eager_emptiness_checking() const;
bool bit_blast() const;
bool karr() const;
bool scale() const;
bool magic() const;
bool quantify_arrays() const;
bool instantiate_quantifiers() const;
bool xform_bit_blast() const;
bool xform_slice() const;
void register_finite_sort(sort * s, sort_kind k);
@ -286,7 +292,7 @@ namespace datalog {
universal (if is_forall is true) or existential
quantifier.
*/
expr_ref bind_variables(expr* fml, bool is_forall);
expr_ref bind_vars(expr* fml, bool is_forall);
/**
Register datalog relation.
@ -366,7 +372,7 @@ namespace datalog {
rule_set & get_rules() { flush_add_rules(); return m_rule_set; }
void get_rules_as_formulas(expr_ref_vector& fmls, svector<symbol>& names);
void get_rules_as_formulas(expr_ref_vector& fmls, expr_ref_vector& qs, svector<symbol>& names);
void get_raw_rule_formulas(expr_ref_vector& fmls, svector<symbol>& names, vector<unsigned> &bounds);
void add_fact(app * head);
@ -420,7 +426,7 @@ namespace datalog {
/**
\brief Check if rule is well-formed according to engine.
*/
void check_rule(rule_ref& r);
void check_rules(rule_set& r);
/**
\brief Return true if facts to \c pred can be added using the \c add_table_fact() function.
@ -467,7 +473,7 @@ namespace datalog {
void display(std::ostream & out) const;
void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out);
void display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out);
void display_profile(std::ostream& out) const;
@ -566,11 +572,6 @@ namespace datalog {
void ensure_engine();
void check_quantifier_free(rule_ref& r);
void check_uninterpreted_free(rule_ref& r);
void check_existential_tail(rule_ref& r);
void check_positive_predicates(rule_ref& r);
// auxilary functions for SMT2 pretty-printer.
void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out);

View file

@ -30,8 +30,9 @@ namespace datalog {
QBMC_ENGINE,
TAB_ENGINE,
CLP_ENGINE,
LAST_ENGINE,
DUALITY_ENGINE
DUALITY_ENGINE,
DDNF_ENGINE,
LAST_ENGINE
};
class engine_base {

View file

@ -56,7 +56,8 @@ namespace datalog {
m_hnf(m),
m_qe(m),
m_cfg(m),
m_rwr(m, false, m_cfg) {}
m_rwr(m, false, m_cfg),
m_ufproc(m) {}
void rule_manager::inc_ref(rule * r) {
if (r) {
@ -111,16 +112,14 @@ namespace datalog {
}
void rule_manager::reset_collect_vars() {
m_vars.reset();
m_var_idx.reset();
m_todo.reset();
m_mark.reset();
m_free_vars.reset();
}
var_idx_set& rule_manager::finalize_collect_vars() {
unsigned sz = m_vars.size();
for (unsigned i=0; i<sz; ++i) {
if (m_vars[i]) m_var_idx.insert(i);
unsigned sz = m_free_vars.size();
for (unsigned i = 0; i < sz; ++i) {
if (m_free_vars[i]) m_var_idx.insert(i);
}
return m_var_idx;
}
@ -157,7 +156,7 @@ namespace datalog {
}
void rule_manager::accumulate_vars(expr* e) {
::get_free_vars(m_mark, m_todo, e, m_vars);
m_free_vars.accumulate(e);
}
@ -188,8 +187,6 @@ namespace datalog {
}
void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) {
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(fml, sorts); );
expr_ref_vector fmls(m);
proof_ref_vector prs(m);
m_hnf.reset();
@ -200,8 +197,6 @@ namespace datalog {
m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false);
}
for (unsigned i = 0; i < fmls.size(); ++i) {
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(fmls[i].get(), sorts); );
mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name);
}
}
@ -228,7 +223,7 @@ namespace datalog {
expr_ref fml1(m);
if (p) {
r->to_formula(fml1);
to_formula(*r, fml1);
if (fml1 == fml) {
// no-op.
}
@ -246,7 +241,7 @@ namespace datalog {
if (p) {
expr_ref fml2(m);
r->to_formula(fml2);
to_formula(*r, fml2);
if (fml1 != fml2) {
p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2));
}
@ -257,17 +252,16 @@ namespace datalog {
unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) {
expr* e1, *e2;
unsigned index = m_counter.get_next_var(fml);
if (::is_forall(fml)) {
index += to_quantifier(fml)->get_num_decls();
fml = to_quantifier(fml)->get_expr();
}
unsigned index = m_counter.get_next_var(fml);
if (m.is_implies(fml, e1, e2)) {
expr_ref_vector es(m);
m_args.reset();
head = ensure_app(e2);
qe::flatten_and(e1, es);
for (unsigned i = 0; i < es.size(); ++i) {
body.push_back(ensure_app(es[i].get()));
qe::flatten_and(e1, m_args);
for (unsigned i = 0; i < m_args.size(); ++i) {
body.push_back(ensure_app(m_args[i].get()));
}
}
else {
@ -299,7 +293,8 @@ namespace datalog {
quantifier_hoister qh(m);
qh.pull_quantifier(false, q, 0, &names);
// retrieve free variables.
get_free_vars(q, vars);
m_free_vars(q);
vars.append(m_free_vars.size(), m_free_vars.c_ptr());
if (vars.contains(static_cast<sort*>(0))) {
var_subst sub(m, false);
expr_ref_vector args(m);
@ -316,7 +311,8 @@ namespace datalog {
}
sub(q, args.size(), args.c_ptr(), q);
vars.reset();
get_free_vars(q, vars);
m_free_vars(q);
vars.append(m_free_vars.size(), m_free_vars.c_ptr());
}
SASSERT(!vars.contains(static_cast<sort*>(0)) && "Unused variables have been eliminated");
@ -373,7 +369,7 @@ namespace datalog {
}
void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) {
result = m_ctx.bind_variables(fml, is_forall);
result = m_ctx.bind_vars(fml, is_forall);
}
void rule_manager::flatten_body(app_ref_vector& body) {
@ -498,11 +494,6 @@ namespace datalog {
app * * uninterp_tail = r->m_tail; //grows upwards
app * * interp_tail = r->m_tail+n; //grows downwards
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(head, sorts);
for (unsigned i = 0; i < n; ++i) {
::get_free_vars(tail[i], sorts);
});
bool has_neg = false;
@ -556,11 +547,6 @@ namespace datalog {
if (normalize) {
r->norm_vars(*this);
}
DEBUG_CODE(ptr_vector<sort> sorts;
::get_free_vars(head, sorts);
for (unsigned i = 0; i < n; ++i) {
::get_free_vars(tail[i], sorts);
});
return r;
}
@ -587,6 +573,55 @@ namespace datalog {
return r;
}
void rule_manager::to_formula(rule const& r, expr_ref& fml) {
ast_manager & m = fml.get_manager();
expr_ref_vector body(m);
for (unsigned i = 0; i < r.get_tail_size(); i++) {
body.push_back(r.get_tail(i));
if (r.is_neg_tail(i)) {
body[body.size()-1] = m.mk_not(body.back());
}
}
fml = r.get_head();
switch (body.size()) {
case 0: break;
case 1: fml = m.mk_implies(body[0].get(), fml); break;
default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break;
}
m_free_vars(fml);
if (m_free_vars.empty()) {
return;
}
svector<symbol> names;
used_symbols<> us;
m_free_vars.set_default_sort(m.mk_bool_sort());
us(fml);
m_free_vars.reverse();
for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) {
for (char c = 'A'; i < m_free_vars.size() && c <= 'Z'; ++c) {
func_decl_ref f(m);
std::stringstream _name;
_name << c;
if (j > 0) _name << j;
symbol name(_name.str().c_str());
if (!us.contains(name)) {
names.push_back(name);
++i;
}
}
}
fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml);
}
std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) {
expr_ref fml(m);
to_formula(r, fml);
return out << mk_ismt2_pp(fml, m);
}
void rule_manager::reduce_unbound_vars(rule_ref& r) {
unsigned ut_len = r->get_uninterpreted_tail_size();
unsigned t_len = r->get_tail_size();
@ -647,9 +682,7 @@ namespace datalog {
svector<bool> tail_neg;
app_ref head(r->get_head(), m);
collect_rule_vars(r);
vctr.count_vars(m, head);
ptr_vector<sort>& free_rule_vars = m_vars;
for (unsigned i = 0; i < ut_len; i++) {
app * t = r->get_tail(i);
@ -658,18 +691,16 @@ namespace datalog {
tail_neg.push_back(r->is_neg_tail(i));
}
ptr_vector<sort> interp_vars;
var_idx_set unbound_vars;
expr_ref_vector tails_with_unbound(m);
for (unsigned i = ut_len; i < t_len; i++) {
app * t = r->get_tail(i);
interp_vars.reset();
::get_free_vars(t, interp_vars);
m_free_vars(t);
bool has_unbound = false;
unsigned iv_size = interp_vars.size();
unsigned iv_size = m_free_vars.size();
for (unsigned i=0; i<iv_size; i++) {
if (!interp_vars[i]) { continue; }
if (!m_free_vars[i]) { continue; }
if (vctr.get(i)==0) {
has_unbound = true;
unbound_vars.insert(i);
@ -693,16 +724,15 @@ namespace datalog {
bool_rewriter(m).mk_and(tails_with_unbound.size(), tails_with_unbound.c_ptr(), unbound_tail);
unsigned q_var_cnt = unbound_vars.num_elems();
unsigned max_var = m_counter.get_max_rule_var(*r);
collect_rule_vars(r);
expr_ref_vector subst(m);
ptr_vector<sort> qsorts;
qsorts.resize(q_var_cnt);
unsigned q_idx = 0;
for (unsigned v = 0; v <= max_var; ++v) {
sort * v_sort = free_rule_vars[v];
for (unsigned v = 0; v < m_free_vars.size(); ++v) {
sort * v_sort = m_free_vars[v];
if (!v_sort) {
//this variable index is not used
continue;
@ -780,7 +810,7 @@ namespace datalog {
!new_rule.get_proof() &&
old_rule.get_proof()) {
expr_ref fml(m);
new_rule.to_formula(fml);
to_formula(new_rule, fml);
scoped_proof _sc(m);
proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml);
new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p));
@ -791,7 +821,7 @@ namespace datalog {
if (m_ctx.generate_proof_trace()) {
scoped_proof _scp(m);
expr_ref fml(m);
r.to_formula(fml);
to_formula(r, fml);
r.set_proof(m, m.mk_asserted(fml));
}
}
@ -881,83 +911,40 @@ namespace datalog {
return false;
}
struct uninterpreted_function_finder_proc {
ast_manager& m;
datatype_util m_dt;
bool m_found;
func_decl* m_func;
uninterpreted_function_finder_proc(ast_manager& m):
m(m), m_dt(m), m_found(false), m_func(0) {}
void operator()(var * n) { }
void operator()(quantifier * n) { }
void operator()(app * n) {
if (is_uninterp(n)) {
m_found = true;
m_func = n->get_decl();
}
else if (m_dt.is_accessor(n)) {
sort* s = m.get_sort(n->get_arg(0));
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() > 1) {
m_found = true;
m_func = n->get_decl();
}
}
}
bool found(func_decl*& f) const { f = m_func; return m_found; }
};
//
// non-predicates may appear only in the interpreted tail, it is therefore
// sufficient only to check the tail.
//
bool rule::has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const {
unsigned sz = get_tail_size();
uninterpreted_function_finder_proc proc(m);
expr_mark visited;
for (unsigned i = get_uninterpreted_tail_size(); i < sz && !proc.found(f); ++i) {
for_each_expr(proc, visited, get_tail(i));
bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const {
unsigned sz = r.get_tail_size();
m_ufproc.reset();
m_visited.reset();
for (unsigned i = r.get_uninterpreted_tail_size(); i < sz && !m_ufproc.found(f); ++i) {
for_each_expr_core<uninterpreted_function_finder_proc,expr_sparse_mark, true, false>(m_ufproc, m_visited, r.get_tail(i));
}
return proc.found(f);
return m_ufproc.found(f);
}
struct quantifier_finder_proc {
bool m_exist;
bool m_univ;
quantifier_finder_proc() : m_exist(false), m_univ(false) {}
void operator()(var * n) { }
void operator()(quantifier * n) {
if (n->is_forall()) {
m_univ = true;
}
else {
SASSERT(n->is_exists());
m_exist = true;
}
}
void operator()(app * n) { }
};
//
// Quantifiers may appear only in the interpreted tail, it is therefore
// sufficient only to check the interpreted tail.
//
void rule::has_quantifiers(bool& existential, bool& universal) const {
unsigned sz = get_tail_size();
quantifier_finder_proc proc;
expr_mark visited;
for (unsigned i = get_uninterpreted_tail_size(); i < sz; ++i) {
for_each_expr(proc, visited, get_tail(i));
void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const {
unsigned sz = r.get_tail_size();
m_qproc.reset();
m_visited.reset();
for (unsigned i = r.get_uninterpreted_tail_size(); i < sz; ++i) {
for_each_expr_core<quantifier_finder_proc,expr_sparse_mark, true, false>(m_qproc, m_visited, r.get_tail(i));
}
existential = proc.m_exist;
universal = proc.m_univ;
existential = m_qproc.m_exist;
universal = m_qproc.m_univ;
}
bool rule::has_quantifiers() const {
bool rule_manager::has_quantifiers(rule const& r) const {
bool exist, univ;
has_quantifiers(exist, univ);
has_quantifiers(r, exist, univ);
return exist || univ;
}
@ -1066,57 +1053,6 @@ namespace datalog {
}
}
void rule::to_formula(expr_ref& fml) const {
ast_manager & m = fml.get_manager();
expr_ref_vector body(m);
for (unsigned i = 0; i < m_tail_size; i++) {
body.push_back(get_tail(i));
if (is_neg_tail(i)) {
body[body.size()-1] = m.mk_not(body.back());
}
}
switch(body.size()) {
case 0: fml = m_head; break;
case 1: fml = m.mk_implies(body[0].get(), m_head); break;
default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), m_head); break;
}
ptr_vector<sort> sorts;
get_free_vars(fml, sorts);
if (sorts.empty()) {
return;
}
svector<symbol> names;
used_symbols<> us;
for (unsigned i = 0; i < sorts.size(); ++i) {
if (!sorts[i]) {
sorts[i] = m.mk_bool_sort();
}
}
us(fml);
sorts.reverse();
for (unsigned j = 0, i = 0; i < sorts.size(); ++j) {
for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) {
func_decl_ref f(m);
std::stringstream _name;
_name << c;
if (j > 0) _name << j;
symbol name(_name.str().c_str());
if (!us.contains(name)) {
names.push_back(name);
++i;
}
}
}
fml = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), fml);
}
std::ostream& rule::display_smt2(ast_manager& m, std::ostream & out) const {
expr_ref fml(m);
to_formula(fml);
return out << mk_ismt2_pp(fml, m);
}
bool rule_eq_proc::operator()(const rule * r1, const rule * r2) const {
if (r1->get_head()!=r2->get_head()) { return false; }

View file

@ -30,6 +30,8 @@ Revision History:
#include"rewriter.h"
#include"hnf.h"
#include"qe_lite.h"
#include"var_subst.h"
#include"datatype_decl_plugin.h"
namespace datalog {
@ -42,6 +44,57 @@ namespace datalog {
typedef obj_ref<rule, rule_manager> rule_ref;
typedef ref_vector<rule, rule_manager> rule_ref_vector;
typedef ptr_vector<rule> rule_vector;
struct quantifier_finder_proc {
bool m_exist;
bool m_univ;
quantifier_finder_proc() : m_exist(false), m_univ(false) {}
void operator()(var * n) { }
void operator()(quantifier * n) {
if (n->is_forall()) {
m_univ = true;
}
else {
SASSERT(n->is_exists());
m_exist = true;
}
}
void operator()(app * n) { }
void reset() { m_exist = m_univ = false; }
};
struct uninterpreted_function_finder_proc {
ast_manager& m;
datatype_util m_dt;
bool m_found;
func_decl* m_func;
uninterpreted_function_finder_proc(ast_manager& m):
m(m), m_dt(m), m_found(false), m_func(0) {}
void reset() { m_found = false; m_func = 0; }
void operator()(var * n) { }
void operator()(quantifier * n) { }
void operator()(app * n) {
if (is_uninterp(n)) {
m_found = true;
m_func = n->get_decl();
}
else if (m_dt.is_accessor(n)) {
sort* s = m.get_sort(n->get_arg(0));
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() > 1) {
m_found = true;
m_func = n->get_decl();
}
}
}
bool found(func_decl*& f) const { f = m_func; return m_found; }
};
/**
\brief Manager for the \c rule class
@ -64,10 +117,8 @@ namespace datalog {
context& m_ctx;
rule_counter m_counter;
used_vars m_used;
ptr_vector<sort> m_vars;
var_idx_set m_var_idx;
ptr_vector<expr> m_todo;
ast_mark m_mark;
expr_free_vars m_free_vars;
app_ref_vector m_body;
app_ref m_head;
expr_ref_vector m_args;
@ -76,6 +127,9 @@ namespace datalog {
qe_lite m_qe;
remove_label_cfg m_cfg;
rewriter_tpl<remove_label_cfg> m_rwr;
mutable uninterpreted_function_finder_proc m_ufproc;
mutable quantifier_finder_proc m_qproc;
mutable expr_sparse_mark m_visited;
// only the context can create a rule_manager
@ -143,7 +197,7 @@ namespace datalog {
void accumulate_vars(expr* pred);
ptr_vector<sort>& get_var_sorts() { return m_vars; }
// ptr_vector<sort>& get_var_sorts() { return m_vars; }
var_idx_set& get_var_idx() { return m_var_idx; }
@ -213,11 +267,18 @@ namespace datalog {
*/
bool is_fact(app * head) const;
static bool is_forall(ast_manager& m, expr* e, quantifier*& q);
rule_counter& get_counter() { return m_counter; }
void to_formula(rule const& r, expr_ref& result);
std::ostream& display_smt2(rule const& r, std::ostream & out);
bool has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const;
void has_quantifiers(rule const& r, bool& existential, bool& universal) const;
bool has_quantifiers(rule const& r) const;
};
class rule : public accounted_object {
@ -293,9 +354,6 @@ namespace datalog {
*/
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
bool has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const;
void has_quantifiers(bool& existential, bool& universal) const;
bool has_quantifiers() const;
bool has_negation() const;
/**
@ -306,12 +364,8 @@ namespace datalog {
void get_vars(ast_manager& m, ptr_vector<sort>& sorts) const;
void to_formula(expr_ref& result) const;
void display(context & ctx, std::ostream & out) const;
std::ostream& display_smt2(ast_manager& m, std::ostream & out) const;
symbol const& name() const { return m_name; }
unsigned hash() const;

View file

@ -39,10 +39,10 @@ namespace datalog {
Each master object is also present as a key of the map, even if its master set
is empty.
*/
deps_type m_data;
context & m_context;
deps_type m_data;
context & m_context;
ptr_vector<expr> m_todo;
ast_mark m_visited;
expr_sparse_mark m_visited;
//we need to take care with removing to avoid memory leaks

View file

@ -56,9 +56,9 @@ namespace datalog {
bool contains_var(expr * trm, unsigned var_idx) {
ptr_vector<sort> vars;
::get_free_vars(trm, vars);
return var_idx < vars.size() && vars[var_idx] != 0;
expr_free_vars fv;
fv(trm);
return fv.contains(var_idx);
}
unsigned count_variable_arguments(app * pred)
@ -300,14 +300,15 @@ namespace datalog {
}
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
void resolve_rule(rule_manager& rm,
replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) {
if (!pc) return;
ast_manager& m = s1.get_manager();
expr_ref fml1(m), fml2(m), fml3(m);
r1.to_formula(fml1);
r2.to_formula(fml2);
res.to_formula(fml3);
rm.to_formula(r1, fml1);
rm.to_formula(r2, fml2);
rm.to_formula(res, fml3);
vector<expr_ref_vector> substs;
svector<std::pair<unsigned, unsigned> > positions;
substs.push_back(s1);
@ -337,7 +338,7 @@ namespace datalog {
pc->insert(pr);
}
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) {
if (!r1.get_proof()) {
return;
@ -345,7 +346,7 @@ namespace datalog {
SASSERT(r2.get_proof());
ast_manager& m = s1.get_manager();
expr_ref fml(m);
res.to_formula(fml);
rm.to_formula(res, fml);
vector<expr_ref_vector> substs;
svector<std::pair<unsigned, unsigned> > positions;
substs.push_back(s1);

View file

@ -41,12 +41,13 @@ namespace datalog {
class pentagon_relation;
class relation_fact;
class relation_signature;
class rule_manager;
class verbose_action {
unsigned m_lvl;
class stopwatch* m_sw;
public:
verbose_action(char const* msg, unsigned lvl = 1);
verbose_action(char const* msg, unsigned lvl = 11);
~verbose_action();
};
@ -345,17 +346,19 @@ namespace datalog {
class rule_counter : public var_counter {
public:
rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {}
rule_counter(){}
void count_rule_vars(ast_manager & m, const rule * r, int coef = 1);
unsigned get_max_rule_var(const rule& r);
};
void del_rule(horn_subsume_model_converter* mc, rule& r);
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
void resolve_rule(rule_manager& rm,
replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res);
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
void resolve_rule(rule_manager& rm,
rule const& r1, rule const& r2, unsigned idx,
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res);
model_converter* mk_skip_model_converter();

View file

@ -2,83 +2,147 @@ def_module_params('fixedpoint',
description='fixedpoint parameters',
export=True,
params=(('timeout', UINT, UINT_MAX, 'set timeout'),
('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, pdr, bmc'),
('default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'),
('default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'),
('generate_explanations', BOOL, False, '(DATALOG) produce explanations for produced facts when using the datalog engine'),
('use_map_names', BOOL, True, "(DATALOG) use names from map files when displaying tuples"),
('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'),
('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'),
('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"),
('magic', BOOL, False, "perform symbolic magic set transformation"),
('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"),
('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"),
('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"),
('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"),
('all_or_nothing_deltas', BOOL, False, "(DATALOG) compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not"),
('compile_with_widening', BOOL, False, "(DATALOG) widening will be used to compile recursive rules"),
('eager_emptiness_checking', BOOL, True, "(DATALOG) emptiness of affected relations will be checked after each instruction, so that we may ommit unnecessary instructions"),
('default_table_checked', BOOL, False, "if true, the detault table will be default_table inside a wrapper that checks that its results are the same as of default_table_checker table"),
('default_table_checker', SYMBOL, 'null', "see default_table_checked"),
('initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), zero means no restarts"),
('output_profile', BOOL, False, "determines whether profile informations should be output when outputting Datalog rules or instructions"),
('inline_linear', BOOL, True, "try linear inlining method"),
('inline_eager', BOOL, True, "try eager inlining of rules"),
('inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"),
('fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
('output_tuples', BOOL, True, "determines whether tuples for output predicates should be output"),
('print_with_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, when printing rules"),
('print_low_level_smt2', BOOL, False, "use (faster) low-level SMT2 printer (the printer is scalable but the result may not be as readable)"),
('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules (instead of attempting to use original names)"),
('bfs_model_search', BOOL, True, "PDR: use BFS strategy for expanding model search"),
('use_farkas', BOOL, True, "PDR: use lemma generator based on Farkas (for linear real arithmetic)"),
('generate_proof_trace', BOOL, False, "PDR: trace for 'sat' answer as proof object"),
('flexible_trace', BOOL, False, "PDR: allow PDR generate long counter-examples "
"by extending candidate trace within search area"),
('unfold_rules', UINT, 0, "PDR: unfold rules statically using iterative squarring"),
('use_model_generalizer', BOOL, False, "PDR: use model for backwards propagation (instead of symbolic simulation)"),
('validate_result', BOOL, False, "PDR validate result (by proof checking or model checking)"),
('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"),
('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"),
('slice', BOOL, True, "PDR: simplify clause set using slicing"),
('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"),
('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"),
('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"),
('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"),
('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"),
('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"),
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
"checking for reachability (not only during cube weakening)"),
('max_num_contexts', UINT, 500, "PDR: maximal number of contexts to create"),
('try_minimize_core', BOOL, False, "PDR: try to reduce core size (before inductive minimization)"),
('profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold will not be printed when printed the instruction/rule list"),
('dbg_fpr_nonempty_relation_signature', BOOL, False,
"if true, finite_product_relation will attempt to avoid creating inner relation with empty signature "
"by putting in half of the table columns, if it would have been empty otherwise"),
('print_answer', BOOL, False, 'print answer instance(s) to query'),
('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'),
('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'),
('print_statistics', BOOL, False, 'print statistics'),
('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'),
('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'),
('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'),
('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'),
('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'),
('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'),
('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'),
('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'),
('profile', BOOL, False, 'DUALITY: profile run time'),
('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'),
('batch_expand', BOOL, False, 'DUALITY: use batch expansion'),
('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'),
('enable_restarts', BOOL, False, 'DUALITY: enable restarts'),
('engine', SYMBOL, 'auto-config',
'Select: auto-config, datalog, duality, pdr, bmc'),
('datalog.default_table', SYMBOL, 'sparse',
'default table implementation: sparse, hashtable, bitvector, interval'),
('datalog.default_relation', SYMBOL, 'pentagon',
'default relation implementation: external_relation, pentagon'),
('datalog.generate_explanations', BOOL, False,
'produce explanations for produced facts when using the datalog engine'),
('datalog.use_map_names', BOOL, True,
"use names from map files when displaying tuples"),
('datalog.magic_sets_for_queries', BOOL, False,
"magic set transformation will be used for queries"),
('datalog.explanations_on_relation_level', BOOL, False,
'if true, explanations are generated as history of each relation, ' +
'rather than per fact (generate_explanations must be set to true for ' +
'this option to have any effect)'),
('datalog.unbound_compressor', BOOL, True,
"auxiliary relations will be introduced to avoid unbound variables " +
"in rule heads"),
('datalog.similarity_compressor', BOOL, True,
"rules that differ only in values of constants will be merged into " +
"a single rule"),
('datalog.similarity_compressor_threshold', UINT, 11,
"if similarity_compressor is on, this value determines how many " +
"similar rules there must be in order for them to be merged"),
('datalog.all_or_nothing_deltas', BOOL, False,
"compile rules so that it is enough for the delta relation in " +
"union and widening operations to determine only whether the " +
"updated relation was modified or not"),
('datalog.compile_with_widening', BOOL, False,
"widening will be used to compile recursive rules"),
('datalog.default_table_checked', BOOL, False, "if true, the detault " +
'table will be default_table inside a wrapper that checks that its results ' +
'are the same as of default_table_checker table'),
('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"),
('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " +
"operations on the default relation will be verified using SMT solving"),
('datalog.initial_restart_timeout', UINT, 0,
"length of saturation run before the first restart (in ms), " +
"zero means no restarts"),
('datalog.output_profile', BOOL, False,
"determines whether profile information should be " +
"output when outputting Datalog rules or instructions"),
('datalog.print.tuples', BOOL, True,
"determines whether tuples for output predicates should be output"),
('datalog.profile_timeout_milliseconds', UINT, 0,
"instructions and rules that took less than the threshold " +
"will not be printed when printed the instruction/rule list"),
('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False,
"if true, finite_product_relation will attempt to avoid creating " +
"inner relation with empty signature by putting in half of the " +
"table columns, if it would have been empty otherwise"),
('duality.full_expand', BOOL, False, 'Fully expand derivation trees'),
('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'),
('duality.feasible_edges', BOOL, True,
'Don\'t expand definitley infeasible edges'),
('duality.use_underapprox', BOOL, False, 'Use underapproximations'),
('duality.stratified_inlining', BOOL, False, 'Use stratified inlining'),
('duality.recursion_bound', UINT, UINT_MAX,
'Recursion bound for stratified inlining'),
('duality.profile', BOOL, False, 'profile run time'),
('duality.mbqi', BOOL, True, 'use model-based quantifier instantion'),
('duality.batch_expand', BOOL, False, 'use batch expansion'),
('duality.conjecture_file', STRING, '', 'save conjectures to file'),
('pdr.bfs_model_search', BOOL, True,
"use BFS strategy for expanding model search"),
('pdr.farkas', BOOL, True,
"use lemma generator based on Farkas (for linear real arithmetic)"),
('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"),
('pdr.flexible_trace', BOOL, False,
"allow PDR generate long counter-examples " +
"by extending candidate trace within search area"),
('pdr.use_model_generalizer', BOOL, False,
"use model for backwards propagation (instead of symbolic simulation)"),
('pdr.validate_result', BOOL, False,
"validate result (by proof checking or model checking)"),
('pdr.simplify_formulas_pre', BOOL, False,
"simplify derived formulas before inductive propagation"),
('pdr.simplify_formulas_post', BOOL, False,
"simplify derived formulas after inductive propagation"),
('pdr.use_multicore_generalizer', BOOL, False,
"extract multiple cores for blocking states"),
('pdr.use_inductive_generalizer', BOOL, True,
"generalize lemmas using induction strengthening"),
('pdr.use_arith_inductive_generalizer', BOOL, False,
"generalize lemmas using arithmetic heuristics for induction strengthening"),
('pdr.use_convex_closure_generalizer', BOOL, False,
"generalize using convex closures of lemmas"),
('pdr.use_convex_interior_generalizer', BOOL, False,
"generalize using convex interiors of lemmas"),
('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " +
"cache (2) for model search"),
('pdr.inductive_reachability_check', BOOL, False,
"assume negation of the cube on the previous level when " +
"checking for reachability (not only during cube weakening)"),
('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"),
('pdr.try_minimize_core', BOOL, False,
"try to reduce core size (before inductive minimization)"),
('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'),
('print.fixedpoint_extensions', BOOL, True,
"use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " +
"when printing rules"),
('print.low_level_smt2', BOOL, False,
"use (faster) low-level SMT2 printer (the printer is scalable " +
"but the result may not be as readable)"),
('print.with_variable_declarations', BOOL, True,
"use variable declarations when displaying rules " +
"(instead of attempting to use original names)"),
('print.answer', BOOL, False, 'print answer instance(s) to query'),
('print.certificate', BOOL, False,
'print certificate for reachability or non-reachability'),
('print.boogie_certificate', BOOL, False,
'print certificate for reachability or non-reachability using a ' +
'format understood by Boogie'),
('print.statistics', BOOL, False, 'print statistics'),
('print.aig', SYMBOL, '',
'Dump clauses in AIG text format (AAG) to the given file name'),
('tab.selection', SYMBOL, 'weight',
'selection method for tabular strategy: weight (default), first, var-use'),
('xform.bit_blast', BOOL, False,
'bit-blast bit-vectors'),
('xform.magic', BOOL, False,
"perform symbolic magic set transformation"),
('xform.scale', BOOL, False,
"add scaling variable to linear real arithemtic clauses"),
('xform.inline_linear', BOOL, True, "try linear inlining method"),
('xform.inline_eager', BOOL, True, "try eager inlining of rules"),
('xform.inline_linear_branch', BOOL, False,
"try linear inlining method with potential expansion"),
('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
('xform.unfold_rules', UINT, 0,
"unfold rules statically using iterative squarring"),
('xform.slice', BOOL, True, "simplify clause set using slicing"),
('xform.karr', BOOL, False,
"Add linear invariants to clauses using Karr's method"),
('xform.quantify_arrays', BOOL, False,
"create quantified Horn clauses from clauses with arrays"),
('xform.instantiate_quantifiers', BOOL, False,
"instantiate quantified Horn clauses using E-matching heuristic"),
('xform.coalesce_rules', BOOL, False, "coalesce rules"),
('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'),
))

View file

@ -58,6 +58,19 @@ Notes:
#include"for_each_expr.h"
class hnf::imp {
class contains_predicate_proc {
imp const& m;
public:
struct found {};
contains_predicate_proc(imp const& m): m(m) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app* n) {
if (m.is_predicate(n)) throw found();
}
};
ast_manager& m;
bool m_produce_proofs;
volatile bool m_cancel;
@ -73,6 +86,9 @@ class hnf::imp {
func_decl_ref_vector m_fresh_predicates;
expr_ref_vector m_body;
proof_ref_vector m_defs;
contains_predicate_proc m_proc;
expr_free_vars m_free_vars;
ast_fast_mark1 m_mark1;
public:
@ -87,13 +103,41 @@ public:
m_qh(m),
m_fresh_predicates(m),
m_body(m),
m_defs(m) {
m_defs(m),
m_proc(*this) {
}
bool is_horn(expr* n) {
expr* n1, *n2;
while (is_forall(n)) n = to_quantifier(n)->get_expr();
if (m.is_implies(n, n1, n2) && is_predicate(n2)) {
app* a1 = to_app(n1);
if (m.is_and(a1)) {
for (unsigned i = 0; i < a1->get_num_args(); ++i) {
if (!is_predicate(a1->get_arg(i)) &&
contains_predicate(a1->get_arg(i))) {
return false;
}
}
}
else if (!is_predicate(a1) && contains_predicate(a1)) {
return false;
}
return true;
}
return false;
}
void operator()(expr * n,
proof* p,
expr_ref_vector& result,
proof_ref_vector& ps) {
if (is_horn(n)) {
result.push_back(n);
ps.push_back(p);
return;
}
expr_ref fml(m);
proof_ref pr(m);
m_todo.reset();
@ -166,24 +210,13 @@ private:
return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id;
}
class contains_predicate_proc {
imp const& m;
public:
struct found {};
contains_predicate_proc(imp const& m): m(m) {}
void operator()(var * n) {}
void operator()(quantifier * n) {}
void operator()(app* n) {
if (m.is_predicate(n)) throw found();
}
};
bool contains_predicate(expr* fml) const {
contains_predicate_proc proc(*this);
bool contains_predicate(expr* fml) {
try {
quick_for_each_expr(proc, fml);
quick_for_each_expr(m_proc, m_mark1, fml);
m_mark1.reset();
}
catch (contains_predicate_proc::found) {
m_mark1.reset();
return true;
}
return false;
@ -348,13 +381,13 @@ private:
}
app_ref mk_fresh_head(expr* e) {
ptr_vector<sort> sorts0, sorts1;
get_free_vars(e, sorts0);
ptr_vector<sort> sorts1;
m_free_vars(e);
expr_ref_vector args(m);
for (unsigned i = 0; i < sorts0.size(); ++i) {
if (sorts0[i]) {
args.push_back(m.mk_var(i, sorts0[i]));
sorts1.push_back(sorts0[i]);
for (unsigned i = 0; i < m_free_vars.size(); ++i) {
if (m_free_vars[i]) {
args.push_back(m.mk_var(i, m_free_vars[i]));
sorts1.push_back(m_free_vars[i]);
}
}
func_decl_ref f(m);

Some files were not shown because too many files have changed in this diff Show more