3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-22 08:30:28 +00:00

Merge branch 'master' into c3

This commit is contained in:
CEisenhofer 2026-06-03 17:33:26 +02:00
commit 043c6c0ad1
259 changed files with 18907 additions and 3725 deletions

View file

@ -129,7 +129,6 @@ extern "C" {
Z3_TRY;
LOG_Z3_mk_rec_func_decl(c, s, domain_size, domain, range);
RESET_ERROR_CODE();
//
recfun::promise_def def =
mk_c(c)->recfun().get_plugin().mk_def(
to_symbol(s), domain_size, to_sorts(domain), to_sort(range), false);
@ -1088,407 +1087,425 @@ extern "C" {
Z3_CATCH_RETURN("");
}
// Helper functions to reduce instruction cache pressure in Z3_get_decl_kind.
// Each theory gets its own function to avoid loading the entire switch table.
static Z3_decl_kind get_decl_kind_basic(decl_kind k) {
switch(k) {
case OP_TRUE: return Z3_OP_TRUE;
case OP_FALSE: return Z3_OP_FALSE;
case OP_EQ: return Z3_OP_EQ;
case OP_DISTINCT: return Z3_OP_DISTINCT;
case OP_ITE: return Z3_OP_ITE;
case OP_AND: return Z3_OP_AND;
case OP_OR: return Z3_OP_OR;
case OP_XOR: return Z3_OP_XOR;
case OP_NOT: return Z3_OP_NOT;
case OP_IMPLIES: return Z3_OP_IMPLIES;
case OP_OEQ: return Z3_OP_OEQ;
case PR_UNDEF: return Z3_OP_PR_UNDEF;
case PR_TRUE: return Z3_OP_PR_TRUE;
case PR_ASSERTED: return Z3_OP_PR_ASSERTED;
case PR_GOAL: return Z3_OP_PR_GOAL;
case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS;
case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY;
case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY;
case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY;
case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR;
case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY;
case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO;
case PR_BIND: return Z3_OP_PR_BIND;
case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY;
case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM;
case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM;
case PR_REWRITE: return Z3_OP_PR_REWRITE;
case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR;
case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT;
case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT;
case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS;
case PR_DER: return Z3_OP_PR_DER;
case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST;
case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS;
case PR_LEMMA: return Z3_OP_PR_LEMMA;
case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION;
case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE;
case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE;
case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY;
case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM;
case PR_ASSUMPTION_ADD: return Z3_OP_PR_ASSUMPTION_ADD;
case PR_LEMMA_ADD: return Z3_OP_PR_LEMMA_ADD;
case PR_REDUNDANT_DEL: return Z3_OP_PR_REDUNDANT_DEL;
case PR_CLAUSE_TRAIL: return Z3_OP_PR_CLAUSE_TRAIL;
case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO;
case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF;
case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ;
case PR_NNF_POS: return Z3_OP_PR_NNF_POS;
case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG;
case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE;
case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ;
case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA;
case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_arith(decl_kind k) {
switch(k) {
case OP_NUM: return Z3_OP_ANUM;
case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM;
case OP_LE: return Z3_OP_LE;
case OP_GE: return Z3_OP_GE;
case OP_LT: return Z3_OP_LT;
case OP_GT: return Z3_OP_GT;
case OP_ADD: return Z3_OP_ADD;
case OP_SUB: return Z3_OP_SUB;
case OP_UMINUS: return Z3_OP_UMINUS;
case OP_MUL: return Z3_OP_MUL;
case OP_DIV: return Z3_OP_DIV;
case OP_IDIV: return Z3_OP_IDIV;
case OP_REM: return Z3_OP_REM;
case OP_MOD: return Z3_OP_MOD;
case OP_POWER: return Z3_OP_POWER;
case OP_ABS: return Z3_OP_ABS;
case OP_TO_REAL: return Z3_OP_TO_REAL;
case OP_TO_INT: return Z3_OP_TO_INT;
case OP_IS_INT: return Z3_OP_IS_INT;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_array(decl_kind k) {
switch(k) {
case OP_STORE: return Z3_OP_STORE;
case OP_SELECT: return Z3_OP_SELECT;
case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY;
case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT;
case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP;
case OP_SET_UNION: return Z3_OP_SET_UNION;
case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT;
case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE;
case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT;
case OP_SET_SUBSET: return Z3_OP_SET_SUBSET;
case OP_AS_ARRAY: return Z3_OP_AS_ARRAY;
case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_special_relations(decl_kind k) {
switch(k) {
case OP_SPECIAL_RELATION_LO : return Z3_OP_SPECIAL_RELATION_LO;
case OP_SPECIAL_RELATION_PO : return Z3_OP_SPECIAL_RELATION_PO;
case OP_SPECIAL_RELATION_PLO: return Z3_OP_SPECIAL_RELATION_PLO;
case OP_SPECIAL_RELATION_TO : return Z3_OP_SPECIAL_RELATION_TO;
case OP_SPECIAL_RELATION_TC : return Z3_OP_SPECIAL_RELATION_TC;
default: UNREACHABLE(); return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_bv(decl_kind k) {
switch(k) {
case OP_BV_NUM: return Z3_OP_BNUM;
case OP_BIT1: return Z3_OP_BIT1;
case OP_BIT0: return Z3_OP_BIT0;
case OP_BNEG: return Z3_OP_BNEG;
case OP_BADD: return Z3_OP_BADD;
case OP_BSUB: return Z3_OP_BSUB;
case OP_BMUL: return Z3_OP_BMUL;
case OP_BSDIV: return Z3_OP_BSDIV;
case OP_BUDIV: return Z3_OP_BUDIV;
case OP_BSREM: return Z3_OP_BSREM;
case OP_BUREM: return Z3_OP_BUREM;
case OP_BSMOD: return Z3_OP_BSMOD;
case OP_BSDIV0: return Z3_OP_BSDIV0;
case OP_BUDIV0: return Z3_OP_BUDIV0;
case OP_BSREM0: return Z3_OP_BSREM0;
case OP_BUREM0: return Z3_OP_BUREM0;
case OP_BSMOD0: return Z3_OP_BSMOD0;
case OP_ULEQ: return Z3_OP_ULEQ;
case OP_SLEQ: return Z3_OP_SLEQ;
case OP_UGEQ: return Z3_OP_UGEQ;
case OP_SGEQ: return Z3_OP_SGEQ;
case OP_ULT: return Z3_OP_ULT;
case OP_SLT: return Z3_OP_SLT;
case OP_UGT: return Z3_OP_UGT;
case OP_SGT: return Z3_OP_SGT;
case OP_BAND: return Z3_OP_BAND;
case OP_BOR: return Z3_OP_BOR;
case OP_BNOT: return Z3_OP_BNOT;
case OP_BXOR: return Z3_OP_BXOR;
case OP_BNAND: return Z3_OP_BNAND;
case OP_BNOR: return Z3_OP_BNOR;
case OP_BXNOR: return Z3_OP_BXNOR;
case OP_CONCAT: return Z3_OP_CONCAT;
case OP_SIGN_EXT: return Z3_OP_SIGN_EXT;
case OP_ZERO_EXT: return Z3_OP_ZERO_EXT;
case OP_EXTRACT: return Z3_OP_EXTRACT;
case OP_REPEAT: return Z3_OP_REPEAT;
case OP_BREDOR: return Z3_OP_BREDOR;
case OP_BREDAND: return Z3_OP_BREDAND;
case OP_BCOMP: return Z3_OP_BCOMP;
case OP_BSHL: return Z3_OP_BSHL;
case OP_BLSHR: return Z3_OP_BLSHR;
case OP_BASHR: return Z3_OP_BASHR;
case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT;
case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT;
case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT;
case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT;
case OP_INT2BV: return Z3_OP_INT2BV;
case OP_UBV2INT: return Z3_OP_BV2INT;
case OP_SBV2INT: return Z3_OP_SBV2INT;
case OP_CARRY: return Z3_OP_CARRY;
case OP_XOR3: return Z3_OP_XOR3;
case OP_BIT2BOOL: return Z3_OP_BIT2BOOL;
case OP_BSMUL_NO_OVFL: return Z3_OP_BSMUL_NO_OVFL;
case OP_BUMUL_NO_OVFL: return Z3_OP_BUMUL_NO_OVFL;
case OP_BSMUL_NO_UDFL: return Z3_OP_BSMUL_NO_UDFL;
case OP_BSDIV_I: return Z3_OP_BSDIV_I;
case OP_BUDIV_I: return Z3_OP_BUDIV_I;
case OP_BSREM_I: return Z3_OP_BSREM_I;
case OP_BUREM_I: return Z3_OP_BUREM_I;
case OP_BSMOD_I: return Z3_OP_BSMOD_I;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_dt(decl_kind k) {
switch(k) {
case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
case OP_DT_IS: return Z3_OP_DT_IS;
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_datalog(decl_kind k) {
switch(k) {
case datalog::OP_RA_STORE: return Z3_OP_RA_STORE;
case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY;
case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY;
case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN;
case datalog::OP_RA_UNION: return Z3_OP_RA_UNION;
case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN;
case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT;
case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER;
case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER;
case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME;
case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT;
case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT;
case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE;
case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT;
case datalog::OP_DL_LT: return Z3_OP_FD_LT;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_seq(decl_kind k) {
switch (k) {
case OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT;
case OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY;
case OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT;
case OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX;
case OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX;
case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS;
case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT;
case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE;
case OP_SEQ_REPLACE_RE: return Z3_OP_SEQ_REPLACE_RE;
case OP_SEQ_REPLACE_RE_ALL: return Z3_OP_SEQ_REPLACE_RE_ALL;
case OP_SEQ_REPLACE_ALL: return Z3_OP_SEQ_REPLACE_ALL;
case OP_SEQ_AT: return Z3_OP_SEQ_AT;
case OP_SEQ_NTH: return Z3_OP_SEQ_NTH;
case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH;
case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX;
case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE;
case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE;
case OP_SEQ_MAP: return Z3_OP_SEQ_MAP;
case OP_SEQ_MAPI: return Z3_OP_SEQ_MAPI;
case OP_SEQ_FOLDL: return Z3_OP_SEQ_FOLDL;
case OP_SEQ_FOLDLI: return Z3_OP_SEQ_FOLDLI;
case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE;
case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT;
case _OP_STRING_LENGTH: return Z3_OP_SEQ_LENGTH;
case _OP_STRING_STRCTN: return Z3_OP_SEQ_CONTAINS;
case _OP_STRING_PREFIX: return Z3_OP_SEQ_PREFIX;
case _OP_STRING_SUFFIX: return Z3_OP_SEQ_SUFFIX;
case _OP_STRING_IN_REGEXP: return Z3_OP_SEQ_IN_RE;
case _OP_STRING_TO_REGEXP: return Z3_OP_SEQ_TO_RE;
case _OP_STRING_CHARAT: return Z3_OP_SEQ_AT;
case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT;
case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX;
case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET;
case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET;
case OP_STRING_STOI: return Z3_OP_STR_TO_INT;
case OP_STRING_ITOS: return Z3_OP_INT_TO_STR;
case OP_STRING_TO_CODE: return Z3_OP_STR_TO_CODE;
case OP_STRING_FROM_CODE: return Z3_OP_STR_FROM_CODE;
case OP_STRING_UBVTOS: return Z3_OP_UBV_TO_STR;
case OP_STRING_SBVTOS: return Z3_OP_SBV_TO_STR;
case OP_STRING_LT: return Z3_OP_STRING_LT;
case OP_STRING_LE: return Z3_OP_STRING_LE;
case OP_RE_PLUS: return Z3_OP_RE_PLUS;
case OP_RE_STAR: return Z3_OP_RE_STAR;
case OP_RE_OPTION: return Z3_OP_RE_OPTION;
case OP_RE_RANGE: return Z3_OP_RE_RANGE;
case OP_RE_CONCAT: return Z3_OP_RE_CONCAT;
case OP_RE_UNION: return Z3_OP_RE_UNION;
case OP_RE_DIFF: return Z3_OP_RE_DIFF;
case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT;
case OP_RE_LOOP: return Z3_OP_RE_LOOP;
case OP_RE_POWER: return Z3_OP_RE_POWER;
case OP_RE_COMPLEMENT: return Z3_OP_RE_COMPLEMENT;
case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET;
case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_CHAR_SET;
case OP_RE_OF_PRED: return Z3_OP_RE_OF_PRED;
case OP_RE_REVERSE: return Z3_OP_RE_REVERSE;
case OP_RE_DERIVATIVE: return Z3_OP_RE_DERIVATIVE;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_char(decl_kind k) {
switch (k) {
case OP_CHAR_CONST: return Z3_OP_CHAR_CONST;
case OP_CHAR_LE: return Z3_OP_CHAR_LE;
case OP_CHAR_TO_INT: return Z3_OP_CHAR_TO_INT;
case OP_CHAR_TO_BV: return Z3_OP_CHAR_TO_BV;
case OP_CHAR_FROM_BV: return Z3_OP_CHAR_FROM_BV;
case OP_CHAR_IS_DIGIT: return Z3_OP_CHAR_IS_DIGIT;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_fpa(decl_kind k) {
switch (k) {
case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN;
case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY;
case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE;
case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE;
case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO;
case OP_FPA_NUM: return Z3_OP_FPA_NUM;
case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF;
case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF;
case OP_FPA_NAN: return Z3_OP_FPA_NAN;
case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO;
case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO;
case OP_FPA_ADD: return Z3_OP_FPA_ADD;
case OP_FPA_SUB: return Z3_OP_FPA_SUB;
case OP_FPA_NEG: return Z3_OP_FPA_NEG;
case OP_FPA_MUL: return Z3_OP_FPA_MUL;
case OP_FPA_DIV: return Z3_OP_FPA_DIV;
case OP_FPA_REM: return Z3_OP_FPA_REM;
case OP_FPA_ABS: return Z3_OP_FPA_ABS;
case OP_FPA_MIN: return Z3_OP_FPA_MIN;
case OP_FPA_MAX: return Z3_OP_FPA_MAX;
case OP_FPA_FMA: return Z3_OP_FPA_FMA;
case OP_FPA_SQRT: return Z3_OP_FPA_SQRT;
case OP_FPA_EQ: return Z3_OP_FPA_EQ;
case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL;
case OP_FPA_LT: return Z3_OP_FPA_LT;
case OP_FPA_GT: return Z3_OP_FPA_GT;
case OP_FPA_LE: return Z3_OP_FPA_LE;
case OP_FPA_GE: return Z3_OP_FPA_GE;
case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN;
case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF;
case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO;
case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL;
case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL;
case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE;
case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE;
case OP_FPA_FP: return Z3_OP_FPA_FP;
case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP;
case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED;
case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV;
case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV;
case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL;
case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV;
case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP;
case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_label(decl_kind k) {
switch(k) {
case OP_LABEL: return Z3_OP_LABEL;
case OP_LABEL_LIT: return Z3_OP_LABEL_LIT;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_pb(decl_kind k) {
switch(k) {
case OP_PB_LE: return Z3_OP_PB_LE;
case OP_PB_GE: return Z3_OP_PB_GE;
case OP_PB_EQ: return Z3_OP_PB_EQ;
case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST;
default: return Z3_OP_INTERNAL;
}
}
static Z3_decl_kind get_decl_kind_finite_set(decl_kind k) {
switch(k) {
case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
default: return Z3_OP_INTERNAL;
}
}
Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d) {
Z3_TRY;
LOG_Z3_get_decl_kind(c, d);
RESET_ERROR_CODE();
func_decl* _d = to_func_decl(d);
if (d == nullptr || null_family_id == _d->get_family_id()) {
if (d == nullptr || null_family_id == _d->get_family_id())
return Z3_OP_UNINTERPRETED;
}
if (mk_c(c)->get_basic_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_TRUE: return Z3_OP_TRUE;
case OP_FALSE: return Z3_OP_FALSE;
case OP_EQ: return Z3_OP_EQ;
case OP_DISTINCT: return Z3_OP_DISTINCT;
case OP_ITE: return Z3_OP_ITE;
case OP_AND: return Z3_OP_AND;
case OP_OR: return Z3_OP_OR;
case OP_XOR: return Z3_OP_XOR;
case OP_NOT: return Z3_OP_NOT;
case OP_IMPLIES: return Z3_OP_IMPLIES;
case OP_OEQ: return Z3_OP_OEQ;
case PR_UNDEF: return Z3_OP_PR_UNDEF;
case PR_TRUE: return Z3_OP_PR_TRUE;
case PR_ASSERTED: return Z3_OP_PR_ASSERTED;
case PR_GOAL: return Z3_OP_PR_GOAL;
case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS;
case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY;
case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY;
case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY;
case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR;
case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY;
case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO;
case PR_BIND: return Z3_OP_PR_BIND;
case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY;
case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM;
case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM;
case PR_REWRITE: return Z3_OP_PR_REWRITE;
case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR;
case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT;
case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT;
case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS;
case PR_DER: return Z3_OP_PR_DER;
case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST;
case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS;
case PR_LEMMA: return Z3_OP_PR_LEMMA;
case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION;
case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE;
case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE;
case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY;
case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM;
case PR_ASSUMPTION_ADD: return Z3_OP_PR_ASSUMPTION_ADD;
case PR_LEMMA_ADD: return Z3_OP_PR_LEMMA_ADD;
case PR_REDUNDANT_DEL: return Z3_OP_PR_REDUNDANT_DEL;
case PR_CLAUSE_TRAIL: return Z3_OP_PR_CLAUSE_TRAIL;
case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO;
case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF;
case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ;
case PR_NNF_POS: return Z3_OP_PR_NNF_POS;
case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG;
case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE;
case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ;
case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA;
case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_arith_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_NUM: return Z3_OP_ANUM;
case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM;
case OP_LE: return Z3_OP_LE;
case OP_GE: return Z3_OP_GE;
case OP_LT: return Z3_OP_LT;
case OP_GT: return Z3_OP_GT;
case OP_ADD: return Z3_OP_ADD;
case OP_SUB: return Z3_OP_SUB;
case OP_UMINUS: return Z3_OP_UMINUS;
case OP_MUL: return Z3_OP_MUL;
case OP_DIV: return Z3_OP_DIV;
case OP_IDIV: return Z3_OP_IDIV;
case OP_REM: return Z3_OP_REM;
case OP_MOD: return Z3_OP_MOD;
case OP_POWER: return Z3_OP_POWER;
case OP_ABS: return Z3_OP_ABS;
case OP_TO_REAL: return Z3_OP_TO_REAL;
case OP_TO_INT: return Z3_OP_TO_INT;
case OP_IS_INT: return Z3_OP_IS_INT;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_array_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_STORE: return Z3_OP_STORE;
case OP_SELECT: return Z3_OP_SELECT;
case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY;
case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT;
case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP;
case OP_SET_UNION: return Z3_OP_SET_UNION;
case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT;
case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE;
case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT;
case OP_SET_SUBSET: return Z3_OP_SET_SUBSET;
case OP_AS_ARRAY: return Z3_OP_AS_ARRAY;
case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT;
default:
return Z3_OP_INTERNAL;
}
}
family_id fid = _d->get_family_id();
decl_kind k = _d->get_decl_kind();
if (mk_c(c)->get_special_relations_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_SPECIAL_RELATION_LO : return Z3_OP_SPECIAL_RELATION_LO;
case OP_SPECIAL_RELATION_PO : return Z3_OP_SPECIAL_RELATION_PO;
case OP_SPECIAL_RELATION_PLO: return Z3_OP_SPECIAL_RELATION_PLO;
case OP_SPECIAL_RELATION_TO : return Z3_OP_SPECIAL_RELATION_TO;
case OP_SPECIAL_RELATION_TC : return Z3_OP_SPECIAL_RELATION_TC;
default: UNREACHABLE();
}
}
if (mk_c(c)->get_bv_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_BV_NUM: return Z3_OP_BNUM;
case OP_BIT1: return Z3_OP_BIT1;
case OP_BIT0: return Z3_OP_BIT0;
case OP_BNEG: return Z3_OP_BNEG;
case OP_BADD: return Z3_OP_BADD;
case OP_BSUB: return Z3_OP_BSUB;
case OP_BMUL: return Z3_OP_BMUL;
case OP_BSDIV: return Z3_OP_BSDIV;
case OP_BUDIV: return Z3_OP_BUDIV;
case OP_BSREM: return Z3_OP_BSREM;
case OP_BUREM: return Z3_OP_BUREM;
case OP_BSMOD: return Z3_OP_BSMOD;
case OP_BSDIV0: return Z3_OP_BSDIV0;
case OP_BUDIV0: return Z3_OP_BUDIV0;
case OP_BSREM0: return Z3_OP_BSREM0;
case OP_BUREM0: return Z3_OP_BUREM0;
case OP_BSMOD0: return Z3_OP_BSMOD0;
case OP_ULEQ: return Z3_OP_ULEQ;
case OP_SLEQ: return Z3_OP_SLEQ;
case OP_UGEQ: return Z3_OP_UGEQ;
case OP_SGEQ: return Z3_OP_SGEQ;
case OP_ULT: return Z3_OP_ULT;
case OP_SLT: return Z3_OP_SLT;
case OP_UGT: return Z3_OP_UGT;
case OP_SGT: return Z3_OP_SGT;
case OP_BAND: return Z3_OP_BAND;
case OP_BOR: return Z3_OP_BOR;
case OP_BNOT: return Z3_OP_BNOT;
case OP_BXOR: return Z3_OP_BXOR;
case OP_BNAND: return Z3_OP_BNAND;
case OP_BNOR: return Z3_OP_BNOR;
case OP_BXNOR: return Z3_OP_BXNOR;
case OP_CONCAT: return Z3_OP_CONCAT;
case OP_SIGN_EXT: return Z3_OP_SIGN_EXT;
case OP_ZERO_EXT: return Z3_OP_ZERO_EXT;
case OP_EXTRACT: return Z3_OP_EXTRACT;
case OP_REPEAT: return Z3_OP_REPEAT;
case OP_BREDOR: return Z3_OP_BREDOR;
case OP_BREDAND: return Z3_OP_BREDAND;
case OP_BCOMP: return Z3_OP_BCOMP;
case OP_BSHL: return Z3_OP_BSHL;
case OP_BLSHR: return Z3_OP_BLSHR;
case OP_BASHR: return Z3_OP_BASHR;
case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT;
case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT;
case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT;
case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT;
case OP_INT2BV: return Z3_OP_INT2BV;
case OP_UBV2INT: return Z3_OP_BV2INT;
case OP_SBV2INT: return Z3_OP_SBV2INT;
case OP_CARRY: return Z3_OP_CARRY;
case OP_XOR3: return Z3_OP_XOR3;
case OP_BIT2BOOL: return Z3_OP_BIT2BOOL;
case OP_BSMUL_NO_OVFL: return Z3_OP_BSMUL_NO_OVFL;
case OP_BUMUL_NO_OVFL: return Z3_OP_BUMUL_NO_OVFL;
case OP_BSMUL_NO_UDFL: return Z3_OP_BSMUL_NO_UDFL;
case OP_BSDIV_I: return Z3_OP_BSDIV_I;
case OP_BUDIV_I: return Z3_OP_BUDIV_I;
case OP_BSREM_I: return Z3_OP_BSREM_I;
case OP_BUREM_I: return Z3_OP_BUREM_I;
case OP_BSMOD_I: return Z3_OP_BSMOD_I;
default:
return Z3_OP_INTERNAL;
}
}
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_IS: return Z3_OP_DT_IS;
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_datalog_fid() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case datalog::OP_RA_STORE: return Z3_OP_RA_STORE;
case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY;
case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY;
case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN;
case datalog::OP_RA_UNION: return Z3_OP_RA_UNION;
case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN;
case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT;
case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER;
case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER;
case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME;
case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT;
case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT;
case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE;
case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT;
case datalog::OP_DL_LT: return Z3_OP_FD_LT;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_seq_fid() == _d->get_family_id()) {
switch (_d->get_decl_kind()) {
case OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT;
case OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY;
case OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT;
case OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX;
case OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX;
case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS;
case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT;
case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE;
case OP_SEQ_REPLACE_RE: return Z3_OP_SEQ_REPLACE_RE;
case OP_SEQ_REPLACE_RE_ALL: return Z3_OP_SEQ_REPLACE_RE_ALL;
case OP_SEQ_REPLACE_ALL: return Z3_OP_SEQ_REPLACE_ALL;
case OP_SEQ_AT: return Z3_OP_SEQ_AT;
case OP_SEQ_NTH: return Z3_OP_SEQ_NTH;
case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH;
case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX;
case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE;
case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE;
case OP_SEQ_MAP: return Z3_OP_SEQ_MAP;
case OP_SEQ_MAPI: return Z3_OP_SEQ_MAPI;
case OP_SEQ_FOLDL: return Z3_OP_SEQ_FOLDL;
case OP_SEQ_FOLDLI: return Z3_OP_SEQ_FOLDLI;
case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE;
case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT;
case _OP_STRING_LENGTH: return Z3_OP_SEQ_LENGTH;
case _OP_STRING_STRCTN: return Z3_OP_SEQ_CONTAINS;
case _OP_STRING_PREFIX: return Z3_OP_SEQ_PREFIX;
case _OP_STRING_SUFFIX: return Z3_OP_SEQ_SUFFIX;
case _OP_STRING_IN_REGEXP: return Z3_OP_SEQ_IN_RE;
case _OP_STRING_TO_REGEXP: return Z3_OP_SEQ_TO_RE;
case _OP_STRING_CHARAT: return Z3_OP_SEQ_AT;
case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT;
case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX;
case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET;
case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET;
case OP_STRING_STOI: return Z3_OP_STR_TO_INT;
case OP_STRING_ITOS: return Z3_OP_INT_TO_STR;
case OP_STRING_TO_CODE: return Z3_OP_STR_TO_CODE;
case OP_STRING_FROM_CODE: return Z3_OP_STR_FROM_CODE;
case OP_STRING_UBVTOS: return Z3_OP_UBV_TO_STR;
case OP_STRING_SBVTOS: return Z3_OP_SBV_TO_STR;
case OP_STRING_LT: return Z3_OP_STRING_LT;
case OP_STRING_LE: return Z3_OP_STRING_LE;
case OP_RE_PLUS: return Z3_OP_RE_PLUS;
case OP_RE_STAR: return Z3_OP_RE_STAR;
case OP_RE_OPTION: return Z3_OP_RE_OPTION;
case OP_RE_RANGE: return Z3_OP_RE_RANGE;
case OP_RE_CONCAT: return Z3_OP_RE_CONCAT;
case OP_RE_UNION: return Z3_OP_RE_UNION;
case OP_RE_DIFF: return Z3_OP_RE_DIFF;
case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT;
case OP_RE_LOOP: return Z3_OP_RE_LOOP;
case OP_RE_POWER: return Z3_OP_RE_POWER;
case OP_RE_COMPLEMENT: return Z3_OP_RE_COMPLEMENT;
case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET;
case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_CHAR_SET;
case OP_RE_OF_PRED: return Z3_OP_RE_OF_PRED;
case OP_RE_REVERSE: return Z3_OP_RE_REVERSE;
case OP_RE_DERIVATIVE: return Z3_OP_RE_DERIVATIVE;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_char_fid() == _d->get_family_id()) {
switch (_d->get_decl_kind()) {
case OP_CHAR_CONST: return Z3_OP_CHAR_CONST;
case OP_CHAR_LE: return Z3_OP_CHAR_LE;
case OP_CHAR_TO_INT: return Z3_OP_CHAR_TO_INT;
case OP_CHAR_TO_BV: return Z3_OP_CHAR_TO_BV;
case OP_CHAR_FROM_BV: return Z3_OP_CHAR_FROM_BV;
case OP_CHAR_IS_DIGIT: return Z3_OP_CHAR_IS_DIGIT;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->get_fpa_fid() == _d->get_family_id()) {
switch (_d->get_decl_kind()) {
case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN;
case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY;
case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE;
case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE;
case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO;
case OP_FPA_NUM: return Z3_OP_FPA_NUM;
case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF;
case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF;
case OP_FPA_NAN: return Z3_OP_FPA_NAN;
case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO;
case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO;
case OP_FPA_ADD: return Z3_OP_FPA_ADD;
case OP_FPA_SUB: return Z3_OP_FPA_SUB;
case OP_FPA_NEG: return Z3_OP_FPA_NEG;
case OP_FPA_MUL: return Z3_OP_FPA_MUL;
case OP_FPA_DIV: return Z3_OP_FPA_DIV;
case OP_FPA_REM: return Z3_OP_FPA_REM;
case OP_FPA_ABS: return Z3_OP_FPA_ABS;
case OP_FPA_MIN: return Z3_OP_FPA_MIN;
case OP_FPA_MAX: return Z3_OP_FPA_MAX;
case OP_FPA_FMA: return Z3_OP_FPA_FMA;
case OP_FPA_SQRT: return Z3_OP_FPA_SQRT;
case OP_FPA_EQ: return Z3_OP_FPA_EQ;
case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL;
case OP_FPA_LT: return Z3_OP_FPA_LT;
case OP_FPA_GT: return Z3_OP_FPA_GT;
case OP_FPA_LE: return Z3_OP_FPA_LE;
case OP_FPA_GE: return Z3_OP_FPA_GE;
case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN;
case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF;
case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO;
case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL;
case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL;
case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE;
case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE;
case OP_FPA_FP: return Z3_OP_FPA_FP;
case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP;
case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED;
case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV;
case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV;
case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL;
case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV;
case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP;
case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM;
return Z3_OP_UNINTERPRETED;
default:
return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->m().get_label_family_id() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_LABEL: return Z3_OP_LABEL;
case OP_LABEL_LIT: return Z3_OP_LABEL_LIT;
default:
return Z3_OP_INTERNAL;
}
}
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_PB_EQ: return Z3_OP_PB_EQ;
case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST;
default: return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->fsutil().get_family_id() == _d->get_family_id()) {
switch(_d->get_decl_kind()) {
case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
default: return Z3_OP_INTERNAL;
}
}
if (mk_c(c)->recfun().get_family_id() == _d->get_family_id())
if (mk_c(c)->get_basic_fid() == fid)
return get_decl_kind_basic(k);
if (mk_c(c)->get_arith_fid() == fid)
return get_decl_kind_arith(k);
if (mk_c(c)->get_bv_fid() == fid)
return get_decl_kind_bv(k);
if (mk_c(c)->get_array_fid() == fid)
return get_decl_kind_array(k);
if (mk_c(c)->get_dt_fid() == fid)
return get_decl_kind_dt(k);
if (mk_c(c)->get_seq_fid() == fid)
return get_decl_kind_seq(k);
if (mk_c(c)->get_fpa_fid() == fid)
return get_decl_kind_fpa(k);
if (mk_c(c)->get_datalog_fid() == fid)
return get_decl_kind_datalog(k);
if (mk_c(c)->get_pb_fid() == fid)
return get_decl_kind_pb(k);
if (mk_c(c)->get_special_relations_fid() == fid)
return get_decl_kind_special_relations(k);
if (mk_c(c)->get_char_fid() == fid)
return get_decl_kind_char(k);
if (mk_c(c)->m().get_label_family_id() == fid)
return get_decl_kind_label(k);
if (mk_c(c)->fsutil().get_family_id() == fid)
return get_decl_kind_finite_set(k);
if (mk_c(c)->recfun().get_family_id() == fid)
return Z3_OP_RECURSIVE;
return Z3_OP_UNINTERPRETED;
@ -1505,11 +1522,7 @@ extern "C" {
return 0;
}
var* va = to_var(_a);
if (va) {
return va->get_idx();
}
SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
return 0;
return va->get_idx();
Z3_CATCH_RETURN(0);
}

View file

@ -147,8 +147,13 @@ endforeach()
set(Z3_DOTNET_NUPKG_VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}")
if(TARGET_ARCHITECTURE STREQUAL "i686")
set(Z3_DOTNET_PLATFORM "x86")
set(Z3_DOTNET_WIN_RID "win-x86")
elseif(TARGET_ARCHITECTURE STREQUAL "arm64")
set(Z3_DOTNET_PLATFORM "AnyCPU")
set(Z3_DOTNET_WIN_RID "win-arm64")
else()
set(Z3_DOTNET_PLATFORM "AnyCPU")
set(Z3_DOTNET_WIN_RID "win-x64")
endif()
# TODO conditional for signing. we can then enable the ``Release_delaysign`` configuration

View file

@ -562,6 +562,63 @@ namespace Microsoft.Z3
}
}
/// <summary>
/// Create a type variable sort for use as a parameter in polymorphic datatypes.
/// </summary>
/// <param name="name">name of the type variable</param>
public Sort MkTypeVariable(Symbol name)
{
Debug.Assert(name != null);
CheckContextMatch(name);
return new Sort(this, Native.Z3_mk_type_variable(nCtx, name.NativeObject));
}
/// <summary>
/// Create a type variable sort for use as a parameter in polymorphic datatypes.
/// </summary>
/// <param name="name">name of the type variable</param>
public Sort MkTypeVariable(string name)
{
using var symbol = MkSymbol(name);
return MkTypeVariable(symbol);
}
/// <summary>
/// Create a polymorphic datatype sort with explicit type parameters.
/// Type parameters should be sorts created with <see cref="MkTypeVariable(string)"/>.
/// </summary>
/// <param name="name">name of the datatype sort</param>
/// <param name="typeParams">array of type variable sorts</param>
/// <param name="constructors">array of constructors</param>
public DatatypeSort MkPolymorphicDatatypeSort(Symbol name, Sort[] typeParams, Constructor[] constructors)
{
Debug.Assert(name != null);
Debug.Assert(typeParams != null);
Debug.Assert(constructors != null);
Debug.Assert(constructors.All(c => c != null));
CheckContextMatch(name);
CheckContextMatch<Sort>(typeParams);
CheckContextMatch<Constructor>(constructors);
return new DatatypeSort(this,
Native.Z3_mk_polymorphic_datatype(nCtx, name.NativeObject,
(uint)typeParams.Length, AST.ArrayToNative(typeParams),
(uint)constructors.Length, Z3Object.ArrayToNative(constructors)));
}
/// <summary>
/// Create a polymorphic datatype sort with explicit type parameters.
/// Type parameters should be sorts created with <see cref="MkTypeVariable(string)"/>.
/// </summary>
/// <param name="name">name of the datatype sort</param>
/// <param name="typeParams">array of type variable sorts</param>
/// <param name="constructors">array of constructors</param>
public DatatypeSort MkPolymorphicDatatypeSort(string name, Sort[] typeParams, Constructor[] constructors)
{
using var symbol = MkSymbol(name);
return MkPolymorphicDatatypeSort(symbol, typeParams, constructors);
}
/// <summary>
/// Update a datatype field at expression t with value v.
/// The function performs a record update at t. The field

View file

@ -84,10 +84,10 @@ ${Z3_DOTNET_COMPILE_ITEMS}
<!-- TODO we may want to pack x64 and x86 native assemblies into a single nupkg -->
<!-- Native binaries x64 -->
<!-- Native binaries x64/arm64 -->
<ItemGroup Condition="'$(Platform)' != 'x86'">
<Content Include="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$(_DN_CMAKE_CONFIG)/libz3.dll" Condition="Exists('${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$(_DN_CMAKE_CONFIG)/libz3.dll')">
<PackagePath>runtimes\win-x64\native</PackagePath>
<Content Include="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.dll" Condition="Exists('${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.dll')">
<PackagePath>runtimes\${Z3_DOTNET_WIN_RID}\native</PackagePath>
</Content>
<Content Include="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.so" Condition="Exists('${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.so')">
<PackagePath>runtimes\linux-x64\native</PackagePath>
@ -99,7 +99,7 @@ ${Z3_DOTNET_COMPILE_ITEMS}
<!-- Native binaries for x86; currently only Windows is supported. -->
<ItemGroup Condition="'$(Platform)' == 'x86'">
<Content Include="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$(_DN_CMAKE_CONFIG)/libz3.dll" Condition="Exists('${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$(_DN_CMAKE_CONFIG)/libz3.dll')">
<Content Include="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.dll" Condition="Exists('${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libz3.dll')">
<PackagePath>runtimes\win-x86\native</PackagePath>
</Content>
</ItemGroup>

View file

@ -9,7 +9,8 @@
<!-- Probe the package root path -->
<Z3_PACKAGE_PATH Condition="('$(Z3_PACKAGE_PATH)' == '')">$(MSBuildThisFileDirectory)..\</Z3_PACKAGE_PATH>
<Z3_NATIVE_LIB_PATH Condition="'$(IsWindows)' == 'true' and '$(Platform)' != 'x86'">$(Z3_PACKAGE_PATH)runtimes\win-x64\native\libz3.dll</Z3_NATIVE_LIB_PATH>
<Z3_NATIVE_LIB_PATH Condition="'$(IsWindows)' == 'true' and '$(Platform)' == 'arm64'">$(Z3_PACKAGE_PATH)runtimes\win-arm64\native\libz3.dll</Z3_NATIVE_LIB_PATH>
<Z3_NATIVE_LIB_PATH Condition="'$(IsWindows)' == 'true' and '$(Platform)' != 'x86' and '$(Platform)' != 'arm64'">$(Z3_PACKAGE_PATH)runtimes\win-x64\native\libz3.dll</Z3_NATIVE_LIB_PATH>
<Z3_NATIVE_LIB_PATH Condition="'$(IsWindows)' == 'true' and '$(Platform)' == 'x86'">$(Z3_PACKAGE_PATH)runtimes\win-x86\native\libz3.dll</Z3_NATIVE_LIB_PATH>
<Z3_NATIVE_LIB_PATH Condition="'$(IsLinux)' == 'true'">$(Z3_PACKAGE_PATH)runtimes\linux-x64\native\libz3.so</Z3_NATIVE_LIB_PATH>
</PropertyGroup>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition="!$(TargetFramework.Contains('netstandard')) and !$(TargetFramework.Contains('netcoreapp'))">
<ItemGroup Condition="!$(TargetFramework.Contains('netstandard')) and !$(TargetFramework.Contains('netcoreapp')) and !$(TargetFramework.Contains('.'))">
<None Include="$(Z3_NATIVE_LIB_PATH)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

43
src/api/go/char.go Normal file
View file

@ -0,0 +1,43 @@
package z3
/*
#include "z3.h"
*/
import "C"
// Char operations
// MkCharSort creates the character sort (Unicode characters).
func (c *Context) MkCharSort() *Sort {
return newSort(c, C.Z3_mk_char_sort(c.ptr))
}
// MkChar creates a character literal from a Unicode code point.
func (c *Context) MkChar(ch uint) *Expr {
return newExpr(c, C.Z3_mk_char(c.ptr, C.uint(ch)))
}
// MkCharLe creates a character less-than-or-equal predicate (ch1 ≤ ch2).
func (c *Context) MkCharLe(ch1, ch2 *Expr) *Expr {
return newExpr(c, C.Z3_mk_char_le(c.ptr, ch1.ptr, ch2.ptr))
}
// MkCharToInt converts a character to its integer (Unicode code point) value.
func (c *Context) MkCharToInt(ch *Expr) *Expr {
return newExpr(c, C.Z3_mk_char_to_int(c.ptr, ch.ptr))
}
// MkCharToBV converts a character to a bit-vector.
func (c *Context) MkCharToBV(ch *Expr) *Expr {
return newExpr(c, C.Z3_mk_char_to_bv(c.ptr, ch.ptr))
}
// MkCharFromBV converts a bit-vector to a character.
func (c *Context) MkCharFromBV(bv *Expr) *Expr {
return newExpr(c, C.Z3_mk_char_from_bv(c.ptr, bv.ptr))
}
// MkCharIsDigit creates a predicate that is true if the character is a decimal digit.
func (c *Context) MkCharIsDigit(ch *Expr) *Expr {
return newExpr(c, C.Z3_mk_char_is_digit(c.ptr, ch.ptr))
}

View file

@ -127,6 +127,41 @@ func (c *Context) MkDatatypeSort(name string, constructors []*Constructor) *Sort
return newSort(c, C.Z3_mk_datatype(c.ptr, sym.ptr, C.uint(numCons), &cons[0]))
}
// MkPolymorphicDatatypeSort creates a polymorphic datatype sort with explicit type parameters.
// typeParams should be sorts created with MkTypeVariable.
// Self-recursive field sorts should be passed as nil; use the fieldSortRefs parameter in
// MkConstructor to indicate the recursive reference by index.
func (c *Context) MkPolymorphicDatatypeSort(name string, typeParams []*Sort, constructors []*Constructor) *Sort {
sym := c.MkStringSymbol(name)
numParams := len(typeParams)
numCons := len(constructors)
var paramPtr *C.Z3_sort
if numParams > 0 {
paramPtrs := make([]C.Z3_sort, numParams)
for i, p := range typeParams {
paramPtrs[i] = p.ptr
}
paramPtr = &paramPtrs[0]
}
var consPtr *C.Z3_constructor
if numCons > 0 {
consPtrs := make([]C.Z3_constructor, numCons)
for i, cons := range constructors {
consPtrs[i] = cons.ptr
}
consPtr = &consPtrs[0]
}
return newSort(c, C.Z3_mk_polymorphic_datatype(
c.ptr, sym.ptr,
C.uint(numParams), paramPtr,
C.uint(numCons), consPtr,
))
}
// MkDatatypeSorts creates multiple mutually recursive datatype sorts.
func (c *Context) MkDatatypeSorts(names []string, constructorLists [][]*Constructor) []*Sort {
numTypes := uint(len(names))

View file

@ -218,6 +218,59 @@ func (f *Fixedpoint) FromFile(filename string) {
C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
}
// QueryFromLvl poses a query against the asserted rules at the given level.
// This is a Spacer-specific function.
func (f *Fixedpoint) QueryFromLvl(query *Expr, lvl uint) Status {
result := C.Z3_fixedpoint_query_from_lvl(f.ctx.ptr, f.ptr, query.ptr, C.uint(lvl))
switch result {
case C.Z3_L_TRUE:
return Satisfiable
case C.Z3_L_FALSE:
return Unsatisfiable
default:
return Unknown
}
}
// GetGroundSatAnswer retrieves a bottom-up sequence of ground facts.
// The previous call to Query or QueryFromLvl must have returned Satisfiable.
// This is a Spacer-specific function.
func (f *Fixedpoint) GetGroundSatAnswer() *Expr {
ptr := C.Z3_fixedpoint_get_ground_sat_answer(f.ctx.ptr, f.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// GetRulesAlongTrace returns the list of rules along the counterexample trace.
// This is a Spacer-specific function.
func (f *Fixedpoint) GetRulesAlongTrace() *ASTVector {
return newASTVector(f.ctx, C.Z3_fixedpoint_get_rules_along_trace(f.ctx.ptr, f.ptr))
}
// GetRuleNamesAlongTrace returns the list of rule names along the counterexample trace.
// This is a Spacer-specific function.
func (f *Fixedpoint) GetRuleNamesAlongTrace() *Symbol {
return newSymbol(f.ctx, C.Z3_fixedpoint_get_rule_names_along_trace(f.ctx.ptr, f.ptr))
}
// AddInvariant adds an assumed invariant for the predicate pred.
// This is a Spacer-specific function.
func (f *Fixedpoint) AddInvariant(pred *FuncDecl, property *Expr) {
C.Z3_fixedpoint_add_invariant(f.ctx.ptr, f.ptr, pred.ptr, property.ptr)
}
// GetReachable retrieves the reachable states of a predicate.
// This is a Spacer-specific function.
func (f *Fixedpoint) GetReachable(pred *FuncDecl) *Expr {
ptr := C.Z3_fixedpoint_get_reachable(f.ctx.ptr, f.ptr, pred.ptr)
if ptr == nil {
return nil
}
return newExpr(f.ctx, ptr)
}
// Statistics represents statistics for Z3 solvers
type Statistics struct {
ctx *Context

38
src/api/go/relations.go Normal file
View file

@ -0,0 +1,38 @@
package z3
/*
#include "z3.h"
*/
import "C"
// Special relation constructors
// MkLinearOrder creates a linear (total) order relation over the given sort.
// The id parameter distinguishes multiple linear orders over the same sort.
func (c *Context) MkLinearOrder(s *Sort, id uint) *FuncDecl {
return newFuncDecl(c, C.Z3_mk_linear_order(c.ptr, s.ptr, C.uint(id)))
}
// MkPartialOrder creates a partial order relation over the given sort.
// The id parameter distinguishes multiple partial orders over the same sort.
func (c *Context) MkPartialOrder(s *Sort, id uint) *FuncDecl {
return newFuncDecl(c, C.Z3_mk_partial_order(c.ptr, s.ptr, C.uint(id)))
}
// MkPiecewiseLinearOrder creates a piecewise linear order relation over the given sort.
// The id parameter distinguishes multiple piecewise linear orders over the same sort.
func (c *Context) MkPiecewiseLinearOrder(s *Sort, id uint) *FuncDecl {
return newFuncDecl(c, C.Z3_mk_piecewise_linear_order(c.ptr, s.ptr, C.uint(id)))
}
// MkTreeOrder creates a tree order relation over the given sort.
// The id parameter distinguishes multiple tree orders over the same sort.
func (c *Context) MkTreeOrder(s *Sort, id uint) *FuncDecl {
return newFuncDecl(c, C.Z3_mk_tree_order(c.ptr, s.ptr, C.uint(id)))
}
// MkTransitiveClosure creates the transitive closure of a binary relation.
// The resulting relation is recursive.
func (c *Context) MkTransitiveClosure(f *FuncDecl) *FuncDecl {
return newFuncDecl(c, C.Z3_mk_transitive_closure(c.ptr, f.ptr))
}

77
src/api/go/set.go Normal file
View file

@ -0,0 +1,77 @@
package z3
/*
#include "z3.h"
*/
import "C"
// Regular (array-encoded) Set operations
// MkSetSort creates a set sort with the given element sort.
func (c *Context) MkSetSort(elemSort *Sort) *Sort {
return newSort(c, C.Z3_mk_set_sort(c.ptr, elemSort.ptr))
}
// MkEmptySet creates an empty set of the given element sort.
func (c *Context) MkEmptySet(elemSort *Sort) *Expr {
return newExpr(c, C.Z3_mk_empty_set(c.ptr, elemSort.ptr))
}
// MkFullSet creates the full set (universe) of the given element sort.
func (c *Context) MkFullSet(elemSort *Sort) *Expr {
return newExpr(c, C.Z3_mk_full_set(c.ptr, elemSort.ptr))
}
// MkSetAdd adds an element to a set.
func (c *Context) MkSetAdd(set, elem *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_add(c.ptr, set.ptr, elem.ptr))
}
// MkSetDel removes an element from a set.
func (c *Context) MkSetDel(set, elem *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_del(c.ptr, set.ptr, elem.ptr))
}
// MkSetUnion creates the union of two or more sets.
func (c *Context) MkSetUnion(sets ...*Expr) *Expr {
if len(sets) == 0 {
return nil
}
cSets := make([]C.Z3_ast, len(sets))
for i, s := range sets {
cSets[i] = s.ptr
}
return newExpr(c, C.Z3_mk_set_union(c.ptr, C.uint(len(sets)), &cSets[0]))
}
// MkSetIntersect creates the intersection of two or more sets.
func (c *Context) MkSetIntersect(sets ...*Expr) *Expr {
if len(sets) == 0 {
return nil
}
cSets := make([]C.Z3_ast, len(sets))
for i, s := range sets {
cSets[i] = s.ptr
}
return newExpr(c, C.Z3_mk_set_intersect(c.ptr, C.uint(len(sets)), &cSets[0]))
}
// MkSetDifference creates the set difference (set1 \ set2).
func (c *Context) MkSetDifference(set1, set2 *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_difference(c.ptr, set1.ptr, set2.ptr))
}
// MkSetComplement creates the complement of a set.
func (c *Context) MkSetComplement(set *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_complement(c.ptr, set.ptr))
}
// MkSetMember creates a membership predicate: elem ∈ set.
func (c *Context) MkSetMember(elem, set *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_member(c.ptr, elem.ptr, set.ptr))
}
// MkSetSubset creates a subset predicate: set1 ⊆ set2.
func (c *Context) MkSetSubset(set1, set2 *Expr) *Expr {
return newExpr(c, C.Z3_mk_set_subset(c.ptr, set1.ptr, set2.ptr))
}

131
src/api/go/spacer.go Normal file
View file

@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation 2025
// Z3 Go API: Spacer quantifier elimination and model projection functions
package z3
/*
#include "z3.h"
#include <stdlib.h>
*/
import "C"
import "runtime"
// ASTMap represents a mapping from Z3 ASTs to Z3 ASTs.
type ASTMap struct {
ctx *Context
ptr C.Z3_ast_map
}
// newASTMap creates a new ASTMap and manages its reference count.
func newASTMap(ctx *Context, ptr C.Z3_ast_map) *ASTMap {
m := &ASTMap{ctx: ctx, ptr: ptr}
C.Z3_ast_map_inc_ref(ctx.ptr, ptr)
runtime.SetFinalizer(m, func(am *ASTMap) {
C.Z3_ast_map_dec_ref(am.ctx.ptr, am.ptr)
})
return m
}
// MkASTMap creates a new empty AST map.
func (c *Context) MkASTMap() *ASTMap {
return newASTMap(c, C.Z3_mk_ast_map(c.ptr))
}
// Contains returns true if the map contains the key k.
func (m *ASTMap) Contains(k *Expr) bool {
return bool(C.Z3_ast_map_contains(m.ctx.ptr, m.ptr, k.ptr))
}
// Find returns the value associated with key k.
func (m *ASTMap) Find(k *Expr) *Expr {
return newExpr(m.ctx, C.Z3_ast_map_find(m.ctx.ptr, m.ptr, k.ptr))
}
// Insert associates key k with value v in the map.
func (m *ASTMap) Insert(k, v *Expr) {
C.Z3_ast_map_insert(m.ctx.ptr, m.ptr, k.ptr, v.ptr)
}
// Erase removes the entry with key k from the map.
func (m *ASTMap) Erase(k *Expr) {
C.Z3_ast_map_erase(m.ctx.ptr, m.ptr, k.ptr)
}
// Reset removes all entries from the map.
func (m *ASTMap) Reset() {
C.Z3_ast_map_reset(m.ctx.ptr, m.ptr)
}
// Size returns the number of entries in the map.
func (m *ASTMap) Size() uint {
return uint(C.Z3_ast_map_size(m.ctx.ptr, m.ptr))
}
// Keys returns all keys in the map as an ASTVector.
func (m *ASTMap) Keys() *ASTVector {
return newASTVector(m.ctx, C.Z3_ast_map_keys(m.ctx.ptr, m.ptr))
}
// String returns the string representation of the map.
func (m *ASTMap) String() string {
return C.GoString(C.Z3_ast_map_to_string(m.ctx.ptr, m.ptr))
}
// ModelExtrapolate extrapolates a model of a formula.
// Given a model m and formula fml, returns an expression that is implied by fml
// and is consistent with the model. This is a Spacer-specific function.
func (c *Context) ModelExtrapolate(m *Model, fml *Expr) *Expr {
return newExpr(c, C.Z3_model_extrapolate(c.ptr, m.ptr, fml.ptr))
}
// QeLite performs best-effort quantifier elimination.
// vars is a vector of variables to eliminate, body is the formula.
func (c *Context) QeLite(vars *ASTVector, body *Expr) *Expr {
return newExpr(c, C.Z3_qe_lite(c.ptr, vars.ptr, body.ptr))
}
// QeModelProject projects variables given a model.
// bound is a slice of application expressions representing the variables to project.
func (c *Context) QeModelProject(m *Model, bound []*Expr, body *Expr) *Expr {
n := len(bound)
cBound := make([]C.Z3_app, n)
for i, b := range bound {
cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
}
var boundPtr *C.Z3_app
if n > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_qe_model_project(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr))
}
// QeModelProjectSkolem projects variables given a model, storing the skolem witnesses in map_.
// bound is a slice of application expressions representing the variables to project.
func (c *Context) QeModelProjectSkolem(m *Model, bound []*Expr, body *Expr, map_ *ASTMap) *Expr {
n := len(bound)
cBound := make([]C.Z3_app, n)
for i, b := range bound {
cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
}
var boundPtr *C.Z3_app
if n > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_qe_model_project_skolem(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr, map_.ptr))
}
// QeModelProjectWithWitness projects variables given a model and extracts witnesses.
// The map_ is populated with bindings of projected variables to witness terms.
// bound is a slice of application expressions representing the variables to project.
func (c *Context) QeModelProjectWithWitness(m *Model, bound []*Expr, body *Expr, map_ *ASTMap) *Expr {
n := len(bound)
cBound := make([]C.Z3_app, n)
for i, b := range bound {
cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
}
var boundPtr *C.Z3_app
if n > 0 {
boundPtr = &cBound[0]
}
return newExpr(c, C.Z3_qe_model_project_with_witness(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr, map_.ptr))
}

View file

@ -228,6 +228,17 @@ func (g *Goal) String() string {
return C.GoString(C.Z3_goal_to_string(g.ctx.ptr, g.ptr))
}
// IsInconsistent returns true if the goal contains the formula false.
func (g *Goal) IsInconsistent() bool {
return bool(C.Z3_goal_inconsistent(g.ctx.ptr, g.ptr))
}
// ToDimacsString converts the goal to a string in DIMACS format.
// If includeNames is true, formula names are included as comments.
func (g *Goal) ToDimacsString(includeNames bool) string {
return C.GoString(C.Z3_goal_to_dimacs_string(g.ctx.ptr, g.ptr, C.bool(includeNames)))
}
// ApplyResult represents the result of applying a tactic to a goal.
type ApplyResult struct {
ctx *Context

View file

@ -240,6 +240,45 @@ func newExpr(ctx *Context, ptr C.Z3_ast) *Expr {
return expr
}
// intsToCs converts a []int slice to []C.int, returning the slice and
// a pointer to its first element (nil if empty).
func intsToCs(ints []int) ([]C.int, *C.int) {
if len(ints) == 0 {
return nil, nil
}
cInts := make([]C.int, len(ints))
for i, v := range ints {
cInts[i] = C.int(v)
}
return cInts, &cInts[0]
}
// exprsToASTs converts a []*Expr slice to []C.Z3_ast, returning the slice and
// a pointer to its first element (nil if empty).
func exprsToASTs(exprs []*Expr) ([]C.Z3_ast, *C.Z3_ast) {
if len(exprs) == 0 {
return nil, nil
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return cExprs, &cExprs[0]
}
// sortsToCSorts converts a []*Sort slice to []C.Z3_sort, returning the slice and
// a pointer to its first element (nil if empty).
func sortsToCSorts(sorts []*Sort) ([]C.Z3_sort, *C.Z3_sort) {
if len(sorts) == 0 {
return nil, nil
}
cSorts := make([]C.Z3_sort, len(sorts))
for i, s := range sorts {
cSorts[i] = s.ptr
}
return cSorts, &cSorts[0]
}
// String returns the string representation of the expression.
func (e *Expr) String() string {
return C.GoString(C.Z3_ast_to_string(e.ctx.ptr, e.ptr))
@ -291,6 +330,21 @@ func newASTVector(ctx *Context, ptr C.Z3_ast_vector) *ASTVector {
return v
}
// Size returns the number of ASTs in the vector.
func (v *ASTVector) Size() uint {
return uint(C.Z3_ast_vector_size(v.ctx.ptr, v.ptr))
}
// Get returns the i-th AST in the vector.
func (v *ASTVector) Get(i uint) *Expr {
return newExpr(v.ctx, C.Z3_ast_vector_get(v.ctx.ptr, v.ptr, C.uint(i)))
}
// String returns the string representation of the AST vector.
func (v *ASTVector) String() string {
return C.GoString(C.Z3_ast_vector_to_string(v.ctx.ptr, v.ptr))
}
// ParamDescrs represents parameter descriptions for Z3 objects.
type ParamDescrs struct {
ctx *Context
@ -353,11 +407,8 @@ func (c *Context) MkAnd(exprs ...*Expr) *Expr {
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), &cExprs[0]))
_, cExprsPtr := exprsToASTs(exprs)
return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), cExprsPtr))
}
// MkOr creates a disjunction.
@ -368,11 +419,8 @@ func (c *Context) MkOr(exprs ...*Expr) *Expr {
if len(exprs) == 1 {
return exprs[0]
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
}
return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), &cExprs[0]))
_, cExprsPtr := exprsToASTs(exprs)
return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), cExprsPtr))
}
// MkNot creates a negation.
@ -407,11 +455,52 @@ func (c *Context) MkDistinct(exprs ...*Expr) *Expr {
if len(exprs) <= 1 {
return c.MkTrue()
}
cExprs := make([]C.Z3_ast, len(exprs))
for i, e := range exprs {
cExprs[i] = e.ptr
_, cExprsPtr := exprsToASTs(exprs)
return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), cExprsPtr))
}
// Pseudo-Boolean / cardinality constraints
// MkAtMost encodes p1 + p2 + ... + pn <= k.
func (c *Context) MkAtMost(args []*Expr, k uint) *Expr {
_, cArgsPtr := exprsToASTs(args)
return newExpr(c, C.Z3_mk_atmost(c.ptr, C.uint(len(args)), cArgsPtr, C.uint(k)))
}
// MkAtLeast encodes p1 + p2 + ... + pn >= k.
func (c *Context) MkAtLeast(args []*Expr, k uint) *Expr {
_, cArgsPtr := exprsToASTs(args)
return newExpr(c, C.Z3_mk_atleast(c.ptr, C.uint(len(args)), cArgsPtr, C.uint(k)))
}
// MkPBLe encodes k1*p1 + k2*p2 + ... + kn*pn <= k.
func (c *Context) MkPBLe(args []*Expr, coeffs []int, k int) *Expr {
if len(args) != len(coeffs) {
panic("MkPBLe: args and coeffs must have the same length")
}
return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), &cExprs[0]))
_, cArgsPtr := exprsToASTs(args)
_, cCoeffsPtr := intsToCs(coeffs)
return newExpr(c, C.Z3_mk_pble(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
}
// MkPBGe encodes k1*p1 + k2*p2 + ... + kn*pn >= k.
func (c *Context) MkPBGe(args []*Expr, coeffs []int, k int) *Expr {
if len(args) != len(coeffs) {
panic("MkPBGe: args and coeffs must have the same length")
}
_, cArgsPtr := exprsToASTs(args)
_, cCoeffsPtr := intsToCs(coeffs)
return newExpr(c, C.Z3_mk_pbge(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
}
// MkPBEq encodes k1*p1 + k2*p2 + ... + kn*pn = k.
func (c *Context) MkPBEq(args []*Expr, coeffs []int, k int) *Expr {
if len(args) != len(coeffs) {
panic("MkPBEq: args and coeffs must have the same length")
}
_, cArgsPtr := exprsToASTs(args)
_, cCoeffsPtr := intsToCs(coeffs)
return newExpr(c, C.Z3_mk_pbeq(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
}
// FuncDecl represents a function declaration.
@ -460,54 +549,26 @@ func (f *FuncDecl) GetRange() *Sort {
// MkFuncDecl creates a function declaration.
func (c *Context) MkFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
cDomain := make([]C.Z3_sort, len(domain))
for i, s := range domain {
cDomain[i] = s.ptr
}
var domainPtr *C.Z3_sort
if len(domain) > 0 {
domainPtr = &cDomain[0]
}
_, domainPtr := sortsToCSorts(domain)
return newFuncDecl(c, C.Z3_mk_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// MkRecFuncDecl creates a recursive function declaration.
// After creating, use AddRecDef to provide the function body.
func (c *Context) MkRecFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
cDomain := make([]C.Z3_sort, len(domain))
for i, s := range domain {
cDomain[i] = s.ptr
}
var domainPtr *C.Z3_sort
if len(domain) > 0 {
domainPtr = &cDomain[0]
}
_, domainPtr := sortsToCSorts(domain)
return newFuncDecl(c, C.Z3_mk_rec_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// AddRecDef adds the definition (body) for a recursive function created with MkRecFuncDecl.
func (c *Context) AddRecDef(f *FuncDecl, args []*Expr, body *Expr) {
cArgs := make([]C.Z3_ast, len(args))
for i, a := range args {
cArgs[i] = a.ptr
}
var argsPtr *C.Z3_ast
if len(args) > 0 {
argsPtr = &cArgs[0]
}
_, argsPtr := exprsToASTs(args)
C.Z3_add_rec_def(c.ptr, f.ptr, C.uint(len(args)), argsPtr, body.ptr)
}
// MkApp creates a function application.
func (c *Context) MkApp(decl *FuncDecl, args ...*Expr) *Expr {
cArgs := make([]C.Z3_ast, len(args))
for i, a := range args {
cArgs[i] = a.ptr
}
var argsPtr *C.Z3_ast
if len(args) > 0 {
argsPtr = &cArgs[0]
}
_, argsPtr := exprsToASTs(args)
return newExpr(c, C.Z3_mk_app(c.ptr, decl.ptr, C.uint(len(args)), argsPtr))
}
@ -546,6 +607,66 @@ func (e *Expr) Simplify() *Expr {
return newExpr(e.ctx, C.Z3_simplify(e.ctx.ptr, e.ptr))
}
// GetDecl returns the function declaration of an application expression.
func (e *Expr) GetDecl() *FuncDecl {
return newFuncDecl(e.ctx, C.Z3_get_app_decl(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr)))
}
// NumArgs returns the number of arguments of an application expression.
func (e *Expr) NumArgs() uint {
return uint(C.Z3_get_app_num_args(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr)))
}
// Arg returns the i-th argument of an application expression.
func (e *Expr) Arg(i uint) *Expr {
return newExpr(e.ctx, C.Z3_get_app_arg(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr), C.uint(i)))
}
// Substitute replaces every occurrence of from[i] in the expression with to[i].
// The from and to slices must have the same length.
func (e *Expr) Substitute(from, to []*Expr) *Expr {
n := len(from)
cFrom := make([]C.Z3_ast, n)
cTo := make([]C.Z3_ast, n)
for i := range from {
cFrom[i] = from[i].ptr
cTo[i] = to[i].ptr
}
var fromPtr, toPtr *C.Z3_ast
if n > 0 {
fromPtr = &cFrom[0]
toPtr = &cTo[0]
}
return newExpr(e.ctx, C.Z3_substitute(e.ctx.ptr, e.ptr, C.uint(n), fromPtr, toPtr))
}
// SubstituteVars replaces free variables in the expression with the expressions in to.
// Variable with de-Bruijn index i is replaced with to[i].
func (e *Expr) SubstituteVars(to []*Expr) *Expr {
_, toPtr := exprsToASTs(to)
return newExpr(e.ctx, C.Z3_substitute_vars(e.ctx.ptr, e.ptr, C.uint(len(to)), toPtr))
}
// SubstituteFuns replaces every occurrence of from[i] applied to arguments
// with to[i] in the expression.
// The from and to slices must have the same length.
func (e *Expr) SubstituteFuns(from []*FuncDecl, to []*Expr) *Expr {
n := len(from)
cFrom := make([]C.Z3_func_decl, n)
cTo := make([]C.Z3_ast, n)
for i := range from {
cFrom[i] = from[i].ptr
cTo[i] = to[i].ptr
}
var fromPtr *C.Z3_func_decl
var toPtr *C.Z3_ast
if n > 0 {
fromPtr = &cFrom[0]
toPtr = &cTo[0]
}
return newExpr(e.ctx, C.Z3_substitute_funs(e.ctx.ptr, e.ptr, C.uint(n), fromPtr, toPtr))
}
// MkTypeVariable creates a type variable sort for use in polymorphic functions and datatypes
func (c *Context) MkTypeVariable(name *Symbol) *Sort {
return newSort(c, C.Z3_mk_type_variable(c.ptr, name.ptr))
@ -639,12 +760,7 @@ func (q *Quantifier) String() string {
// MkQuantifier creates a quantifier with patterns
func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names []*Symbol, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.bool
if isForall {
forallInt = true
} else {
forallInt = false
}
forallInt := C.bool(isForall)
numBound := len(sorts)
if numBound != len(names) {
@ -687,12 +803,7 @@ func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names [
// MkQuantifierConst creates a quantifier using constant bound variables
func (c *Context) MkQuantifierConst(isForall bool, weight int, bound []*Expr, body *Expr, patterns []*Pattern) *Quantifier {
var forallInt C.bool
if isForall {
forallInt = true
} else {
forallInt = false
}
forallInt := C.bool(isForall)
numBound := len(bound)
var cBound []C.Z3_app
@ -815,6 +926,33 @@ func (c *Context) MkLambdaConst(bound []*Expr, body *Expr) *Lambda {
return newLambda(c, ptr)
}
// SetGlobalParam sets a global Z3 parameter.
func SetGlobalParam(id, value string) {
cID := C.CString(id)
cValue := C.CString(value)
defer C.free(unsafe.Pointer(cID))
defer C.free(unsafe.Pointer(cValue))
C.Z3_global_param_set(cID, cValue)
}
// GetGlobalParam retrieves the value of a global Z3 parameter.
// Returns the value and true if the parameter exists, or empty string and false otherwise.
func GetGlobalParam(id string) (string, bool) {
cID := C.CString(id)
defer C.free(unsafe.Pointer(cID))
var cValue C.Z3_string
ok := C.Z3_global_param_get(cID, &cValue)
if ok == C.bool(false) {
return "", false
}
return C.GoString(cValue), true
}
// ResetAllGlobalParams resets all global Z3 parameters to their default values.
func ResetAllGlobalParams() {
C.Z3_global_param_reset_all()
}
// astVectorToExprs converts a Z3_ast_vector to a slice of Expr.
// This function properly manages the reference count of the vector by
// incrementing it on entry and decrementing it on exit.

View file

@ -4616,6 +4616,38 @@ public class Context implements AutoCloseable {
);
}
/**
* Creates a piecewise linear order.
* @param index The index of the order.
* @param sort The sort of the order.
*/
public final <R extends Sort> FuncDecl<BoolSort> mkPiecewiseLinearOrder(R sort, int index) {
return (FuncDecl<BoolSort>) FuncDecl.create(
this,
Native.mkPiecewiseLinearOrder(
nCtx(),
sort.getNativeObject(),
index
)
);
}
/**
* Creates a tree order.
* @param index The index of the order.
* @param sort The sort of the order.
*/
public final <R extends Sort> FuncDecl<BoolSort> mkTreeOrder(R sort, int index) {
return (FuncDecl<BoolSort>) FuncDecl.create(
this,
Native.mkTreeOrder(
nCtx(),
sort.getNativeObject(),
index
)
);
}
/**
* Return the nonzero subresultants of p and q with respect to the "variable" x.
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.

View file

@ -16,6 +16,17 @@ const {
This package has different initialization for browser and node. Your bundler and node should choose good version automatically, but you can import the one you need manually - `const { init } = require('z3-solver/node');` or `const { init } = require('z3-solver/browser');`.
The `init` function also accepts an optional Emscripten module overrides object. This is useful in runtimes such as Deno where you may want to provide a wasm load path explicitly instead of relying on filesystem reads. In Deno 2.1+, `import.meta.resolve(...)` returns a string synchronously, so it can be used directly in `locateFile`. For example:
```typescript
import { init } from 'npm:z3-solver';
const api = await init({
locateFile: (file, _prefix): string =>
import.meta.resolve(`npm:z3-solver/build/${file}`), // _prefix is unused here
});
```
### Limitations
The package requires threads, which means you'll need to be running in an environment which supports `SharedArrayBuffer`. In browsers, in addition to ensuring the browser has implemented `SharedArrayBuffer`, you'll need to serve your page with [special headers](https://web.dev/coop-coep/). There's a [neat trick](https://github.com/gzuidhof/coi-serviceworker) for doing that client-side on e.g. Github Pages, though you shouldn't use that trick in more complex applications.

View file

@ -5842,10 +5842,11 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},

View file

@ -444,8 +444,8 @@ ${Object.entries(primitiveTypes)
.map(e => `type ${e[0]} = ${e[1]};`)
.join('\n')}
export async function init(initModule: any) {
let Mod = await initModule();
export async function init(initModule: any, moduleOverrides: Record<string, unknown> = {}) {
let Mod = await initModule(moduleOverrides);
// this works for both signed and unsigned, because JS will wrap for you when constructing the Uint32Array
function intArrayToByteArr(ints: number[]) {
@ -461,13 +461,13 @@ export async function init(initModule: any) {
}
let outAddress = Mod._malloc(${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS});
let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, 4));
let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4}));
let getOutUint = (i: ${getValidOutArrayIndexes(4)}) => outUintArray[i];
let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, 4));
let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4}));
let getOutInt = (i: ${getValidOutArrayIndexes(4)}) => outIntArray[i];
let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, 2));
let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 8}));
let getOutUint64 = (i: ${getValidOutArrayIndexes(8)}) => outUint64Array[i];
let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, 2));
let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 8}));
let getOutInt64 = (i: ${getValidOutArrayIndexes(8)}) => outInt64Array[i];
return {

View file

@ -0,0 +1,43 @@
const mockInitWrapper = jest.fn();
const mockCreateApi = jest.fn();
jest.mock('./low-level', () => ({
init: mockInitWrapper,
Z3Core: undefined,
Z3LowLevel: undefined,
}));
jest.mock('./high-level', () => ({
createApi: mockCreateApi,
}));
import { init } from './browser';
describe('browser init', () => {
beforeEach(() => {
delete (global as any).initZ3;
mockInitWrapper.mockReset();
mockCreateApi.mockReset();
});
it('passes module overrides to the browser initializer', async () => {
const initZ3 = jest.fn();
const locateFile = jest.fn((file: string) => `https://example.test/${file}`);
const lowLevel = { Z3: { low: true }, em: { module: true } };
const highLevel = { Context: jest.fn() };
(global as any).initZ3 = initZ3;
mockInitWrapper.mockResolvedValue(lowLevel);
mockCreateApi.mockReturnValue(highLevel);
const api = await init({ locateFile });
expect(mockInitWrapper).toHaveBeenCalledWith(initZ3, { locateFile });
expect(mockCreateApi).toHaveBeenCalledWith(lowLevel.Z3, lowLevel.em);
expect(api).toEqual({ ...lowLevel, ...highLevel });
});
it('throws when initZ3 is unavailable', async () => {
await expect(init()).rejects.toThrow(
'initZ3 was not imported correctly. Please consult documentation on how to load Z3 in browser',
);
});
});

View file

@ -1,16 +1,16 @@
import { createApi, Z3HighLevel } from './high-level';
import { init as initWrapper, Z3LowLevel } from './low-level';
import { init as initWrapper, Z3LowLevel, Z3ModuleOverrides } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
export async function init(): Promise<Z3LowLevel & Z3HighLevel> {
export async function init(moduleOverrides: Z3ModuleOverrides = {}): Promise<Z3LowLevel & Z3HighLevel> {
const initZ3 = (global as any).initZ3;
if (initZ3 === undefined) {
throw new Error('initZ3 was not imported correctly. Please consult documentation on how to load Z3 in browser');
}
const lowLevel = await initWrapper(initZ3);
const lowLevel = await initWrapper(initZ3, moduleOverrides);
const highLevel = createApi(lowLevel.Z3, lowLevel.em);
return { ...lowLevel, ...highLevel };
}

View file

@ -1172,9 +1172,16 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
createDatatypes(...datatypes: DatatypeImpl[]): DatatypeSortImpl[] {
return createDatatypes(...datatypes);
},
createPolymorphicDatatype(typeParams: Sort<Name>[], datatype: DatatypeImpl): DatatypeSortImpl {
return createPolymorphicDatatype(typeParams, datatype);
},
},
);
function TypeVariable(name: string): Sort<Name> {
return new SortImpl(check(Z3.mk_type_variable(contextPtr, Z3.mk_string_symbol(contextPtr, name))));
}
////////////////
// Operations //
////////////////
@ -1950,10 +1957,46 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
return new FuncDeclImpl(check(Z3.mk_partial_order(contextPtr, sort.ptr, index)));
}
function mkLinearOrder(sort: Sort<Name>, index: number): FuncDecl<Name> {
return new FuncDeclImpl(check(Z3.mk_linear_order(contextPtr, sort.ptr, index)));
}
function mkPiecewiseLinearOrder(sort: Sort<Name>, index: number): FuncDecl<Name> {
return new FuncDeclImpl(check(Z3.mk_piecewise_linear_order(contextPtr, sort.ptr, index)));
}
function mkTreeOrder(sort: Sort<Name>, index: number): FuncDecl<Name> {
return new FuncDeclImpl(check(Z3.mk_tree_order(contextPtr, sort.ptr, index)));
}
function mkTransitiveClosure(f: FuncDecl<Name>): FuncDecl<Name> {
return new FuncDeclImpl(check(Z3.mk_transitive_closure(contextPtr, f.ptr)));
}
function mkChar(ch: number): Expr<Name> {
return new ExprImpl(check(Z3.mk_char(contextPtr, ch)));
}
function mkCharLe(ch1: Expr<Name>, ch2: Expr<Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_char_le(contextPtr, ch1.ast, ch2.ast)));
}
function mkCharToInt(ch: Expr<Name>): Arith<Name> {
return new ArithImpl(check(Z3.mk_char_to_int(contextPtr, ch.ast)));
}
function mkCharToBV(ch: Expr<Name>): Expr<Name> {
return new ExprImpl(check(Z3.mk_char_to_bv(contextPtr, ch.ast)));
}
function mkCharFromBV(bv: Expr<Name>): Expr<Name> {
return new ExprImpl(check(Z3.mk_char_from_bv(contextPtr, bv.ast)));
}
function mkCharIsDigit(ch: Expr<Name>): Bool<Name> {
return new BoolImpl(check(Z3.mk_char_is_digit(contextPtr, ch.ast)));
}
async function polynomialSubresultants(
p: Arith<Name>,
q: Arith<Name>,
@ -4689,6 +4732,10 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
const datatypes = createDatatypes(this);
return datatypes[0];
}
createPolymorphic(typeParams: Sort<Name>[]): DatatypeSort<Name> {
return createPolymorphicDatatype(typeParams, this);
}
}
class DatatypeSortImpl extends SortImpl implements DatatypeSort<Name> {
@ -4845,6 +4892,84 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
}
}
function createPolymorphicDatatype(typeParams: Sort<Name>[], datatype: DatatypeImpl): DatatypeSortImpl {
if (!(datatype instanceof DatatypeImpl)) {
throw new Error('Datatype instance expected');
}
const constructors: Z3_constructor[] = [];
try {
for (const [constructorName, fields] of datatype.constructors) {
const fieldNames: string[] = [];
const fieldSorts: Z3_sort[] = [];
const fieldRefs: number[] = [];
for (const [fieldName, fieldSort] of fields) {
fieldNames.push(fieldName);
if (fieldSort instanceof DatatypeImpl) {
// Self-recursive reference
if (fieldSort !== datatype) {
throw new Error(
`Referenced datatype "${fieldSort.name}" is not the polymorphic datatype being created; mutual recursion is not supported in createPolymorphicDatatype`,
);
}
fieldSorts.push(null as any);
fieldRefs.push(0);
} else {
fieldSorts.push((fieldSort as Sort<Name>).ptr);
fieldRefs.push(0);
}
}
const constructor = Z3.mk_constructor(
contextPtr,
Z3.mk_string_symbol(contextPtr, constructorName),
Z3.mk_string_symbol(contextPtr, `is_${constructorName}`),
fieldNames.map(name => Z3.mk_string_symbol(contextPtr, name)),
fieldSorts,
fieldRefs,
);
constructors.push(constructor);
}
const nameSymbol = Z3.mk_string_symbol(contextPtr, datatype.name);
const paramPtrs = typeParams.map(p => p.ptr);
const resultSort = Z3.mk_polymorphic_datatype(contextPtr, nameSymbol, paramPtrs, constructors);
const sortImpl = new DatatypeSortImpl(resultSort);
// Attach constructor, recognizer, and accessor functions dynamically
const numConstructors = sortImpl.numConstructors();
for (let j = 0; j < numConstructors; j++) {
const constructor = sortImpl.constructorDecl(j);
const recognizer = sortImpl.recognizer(j);
const constructorName = constructor.name().toString();
if (constructor.arity() === 0) {
(sortImpl as any)[constructorName] = constructor.call();
} else {
(sortImpl as any)[constructorName] = constructor;
}
(sortImpl as any)[`is_${constructorName}`] = recognizer;
for (let k = 0; k < constructor.arity(); k++) {
const accessor = sortImpl.accessor(j, k);
const accessorName = accessor.name().toString();
(sortImpl as any)[accessorName] = accessor;
}
}
return sortImpl;
} finally {
for (const constructor of constructors) {
Z3.del_constructor(contextPtr, constructor);
}
}
}
class QuantifierImpl<
QVarSorts extends NonEmptySortArray<Name>,
QSort extends BoolSort<Name> | SMTArraySort<Name, QVarSorts>,
@ -5292,6 +5417,7 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
Set,
FiniteSet,
Datatype,
TypeVariable,
////////////////
// Operations //
@ -5400,7 +5526,16 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
Full,
mkPartialOrder,
mkLinearOrder,
mkPiecewiseLinearOrder,
mkTreeOrder,
mkTransitiveClosure,
mkChar,
mkCharLe,
mkCharToInt,
mkCharToBV,
mkCharFromBV,
mkCharIsDigit,
polynomialSubresultants,
};
cleanup.register(ctx, () => Z3.del_context(contextPtr));

View file

@ -479,6 +479,12 @@ export interface Context<Name extends string = 'main'> {
/** @category Expressions */
readonly Datatype: DatatypeCreation<Name>;
/**
* Create a type variable sort for use as a parameter in polymorphic datatypes.
* @category Sorts
*/
TypeVariable(name: string): Sort<Name>;
////////////////
// Operations //
////////////////
@ -927,6 +933,30 @@ export interface Context<Name extends string = 'main'> {
*/
mkPartialOrder(sort: Sort<Name>, index: number): FuncDecl<Name>;
/**
* Create a linear (total) order relation over a sort.
* @param sort The sort of the relation
* @param index The index of the relation
* @category Operations
*/
mkLinearOrder(sort: Sort<Name>, index: number): FuncDecl<Name>;
/**
* Create a piecewise linear order relation over a sort.
* @param sort The sort of the relation
* @param index The index of the relation
* @category Operations
*/
mkPiecewiseLinearOrder(sort: Sort<Name>, index: number): FuncDecl<Name>;
/**
* Create a tree order relation over a sort.
* @param sort The sort of the relation
* @param index The index of the relation
* @category Operations
*/
mkTreeOrder(sort: Sort<Name>, index: number): FuncDecl<Name>;
/**
* Create the transitive closure of a binary relation.
* The resulting relation is recursive.
@ -935,6 +965,49 @@ export interface Context<Name extends string = 'main'> {
*/
mkTransitiveClosure(f: FuncDecl<Name>): FuncDecl<Name>;
/**
* Create a character literal from a Unicode code point.
* @param ch The Unicode code point
* @category Characters
*/
mkChar(ch: number): Expr<Name>;
/**
* Create a character less-than-or-equal predicate (ch1 ch2).
* @param ch1 First character
* @param ch2 Second character
* @category Characters
*/
mkCharLe(ch1: Expr<Name>, ch2: Expr<Name>): Bool<Name>;
/**
* Convert a character to its integer (Unicode code point) value.
* @param ch The character expression
* @category Characters
*/
mkCharToInt(ch: Expr<Name>): Arith<Name>;
/**
* Convert a character to a bit-vector.
* @param ch The character expression
* @category Characters
*/
mkCharToBV(ch: Expr<Name>): Expr<Name>;
/**
* Convert a bit-vector to a character.
* @param bv The bit-vector expression
* @category Characters
*/
mkCharFromBV(bv: Expr<Name>): Expr<Name>;
/**
* Create a predicate that is true if the character is a decimal digit.
* @param ch The character expression
* @category Characters
*/
mkCharIsDigit(ch: Expr<Name>): Bool<Name>;
/**
* Return the nonzero subresultants of p and q with respect to the "variable" x.
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
@ -3136,6 +3209,15 @@ export interface Datatype<Name extends string = 'main'> {
* For mutually recursive datatypes, use Context.createDatatypes instead.
*/
create(): DatatypeSort<Name>;
/**
* Create a polymorphic datatype sort with explicit type parameters.
* Type parameters should be sorts created with Context.TypeVariable.
* Self-recursive fields may reference this Datatype object directly.
*
* @param typeParams Array of type variable sorts
*/
createPolymorphic(typeParams: AnySort<Name>[]): DatatypeSort<Name>;
}
/**
@ -3154,6 +3236,17 @@ export interface DatatypeCreation<Name extends string> {
* @returns Array of created DatatypeSort instances
*/
createDatatypes(...datatypes: Datatype<Name>[]): DatatypeSort<Name>[];
/**
* Create a single polymorphic datatype sort with explicit type parameters.
* Type parameters should be sorts created with Context.TypeVariable.
* Self-recursive fields in constructors may reference the Datatype object directly.
*
* @param typeParams Array of type variable sorts
* @param datatype Datatype declaration with constructors
* @returns Created DatatypeSort instance
*/
createPolymorphicDatatype(typeParams: AnySort<Name>[], datatype: Datatype<Name>): DatatypeSort<Name>;
}
/**

View file

@ -1,4 +1,8 @@
export * from './types.__GENERATED__';
export * from './wrapper.__GENERATED__';
export type Z3ModuleOverrides = {
locateFile?: (path: string, prefix: string) => string;
[key: string]: unknown;
};
export type Z3Core = Awaited<ReturnType<(typeof import('./wrapper.__GENERATED__'))['init']>>['Z3'];
export type Z3LowLevel = Awaited<ReturnType<(typeof import('./wrapper.__GENERATED__'))['init']>>;

View file

@ -0,0 +1,37 @@
const mockInitModule = jest.fn();
const mockInitWrapper = jest.fn();
const mockCreateApi = jest.fn();
jest.mock('./z3-built', () => mockInitModule, { virtual: true });
jest.mock('./low-level', () => ({
init: mockInitWrapper,
Z3Core: undefined,
Z3LowLevel: undefined,
}));
jest.mock('./high-level', () => ({
createApi: mockCreateApi,
}));
import { init } from './node';
describe('node init', () => {
beforeEach(() => {
mockInitModule.mockReset();
mockInitWrapper.mockReset();
mockCreateApi.mockReset();
});
it('passes module overrides to the low-level initializer', async () => {
const locateFile = jest.fn((file: string) => `npm:z3-solver/build/${file}`);
const lowLevel = { Z3: { low: true }, em: { module: true } };
const highLevel = { Context: jest.fn() };
mockInitWrapper.mockResolvedValue(lowLevel);
mockCreateApi.mockReturnValue(highLevel);
const api = await init({ locateFile });
expect(mockInitWrapper).toHaveBeenCalledWith(mockInitModule, { locateFile });
expect(mockCreateApi).toHaveBeenCalledWith(lowLevel.Z3, lowLevel.em);
expect(api).toEqual({ ...lowLevel, ...highLevel });
});
});

View file

@ -2,7 +2,7 @@
import initModule = require('./z3-built');
import { createApi, Z3HighLevel } from './high-level';
import { init as initWrapper, Z3LowLevel } from './low-level';
import { init as initWrapper, Z3LowLevel, Z3ModuleOverrides } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
@ -29,10 +29,17 @@ export * from './low-level/types.__GENERATED__';
*
* console.log(`x=${model.get(x)}, y=${model.get(y)}`);
* // x=0, y=12
*
* // Deno users can provide an Emscripten locateFile hook to load the wasm
* // through npm's asset resolution instead of filesystem reads.
* // const api = await init({
* // locateFile: (file, _prefix): string =>
* // import.meta.resolve(`npm:z3-solver/build/${file}`), // _prefix is unused here
* // });
* ```
* @category Global */
export async function init(): Promise<Z3HighLevel & Z3LowLevel> {
const lowLevel = await initWrapper(initModule);
export async function init(moduleOverrides: Z3ModuleOverrides = {}): Promise<Z3HighLevel & Z3LowLevel> {
const lowLevel = await initWrapper(initModule, moduleOverrides);
const highLevel = createApi(lowLevel.Z3, lowLevel.em);
return { ...lowLevel, ...highLevel };
}

View file

@ -320,6 +320,20 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
m.method("finite_set_map", &finite_set_map);
m.method("finite_set_filter", &finite_set_filter);
m.method("finite_set_range", &finite_set_range);
m.method("empty_set", &empty_set);
m.method("full_set", &full_set);
m.method("set_add", &set_add);
m.method("set_del", &set_del);
m.method("set_union", &set_union);
m.method("set_intersect", &set_intersect);
m.method("set_difference", &set_difference);
m.method("set_complement", &set_complement);
m.method("set_member", &set_member);
m.method("set_subset", &set_subset);
m.method("linear_order", &linear_order);
m.method("partial_order", &partial_order);
m.method("piecewise_linear_order", &piecewise_linear_order);
m.method("tree_order", &tree_order);
// -------------------------------------------------------------------------
@ -629,7 +643,13 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(context, string_sort)
.MM(context, seq_sort)
.MM(context, re_sort)
.MM(context, char_sort)
.MM(context, finite_set_sort)
.method("set_sort", [](context &c, sort s) {
Z3_sort r = Z3_mk_set_sort(c, s);
c.check_error();
return sort(c, r);
})
.method("array_sort", static_cast<sort (context::*)(sort, sort)>(&context::array_sort))
.method("array_sort", static_cast<sort (context::*)(sort_vector const&, sort)>(&context::array_sort))
.method("fpa_sort", static_cast<sort (context::*)(unsigned, unsigned)>(&context::fpa_sort))

View file

@ -475,6 +475,7 @@ sig
val substitute : expr -> expr list -> expr list -> expr
val substitute_one : expr -> expr -> expr -> expr
val substitute_vars : expr -> expr list -> expr
val substitute_funs : expr -> FuncDecl.func_decl list -> expr list -> expr
val translate : expr -> context -> expr
val to_string : expr -> string
val is_numeral : expr -> bool
@ -537,6 +538,13 @@ end = struct
let substitute_vars x to_ =
Z3native.substitute_vars (gc x) x (List.length to_) to_
let substitute_funs x from to_ =
let len = List.length from in
if List.length to_ <> len then
raise (Error "Argument sizes do not match")
else
Z3native.substitute_funs (gc x) x len from to_
let translate (x:expr) to_ctx =
if gc x = to_ctx then
x
@ -587,6 +595,12 @@ struct
let mk_eq = Z3native.mk_eq
let mk_distinct ctx args = Z3native.mk_distinct ctx (List.length args) args
let mk_atmost ctx args k = Z3native.mk_atmost ctx (List.length args) args k
let mk_atleast ctx args k = Z3native.mk_atleast ctx (List.length args) args k
let mk_pble ctx args coeffs k = Z3native.mk_pble ctx (List.length args) args coeffs k
let mk_pbge ctx args coeffs k = Z3native.mk_pbge ctx (List.length args) args coeffs k
let mk_pbeq ctx args coeffs k = Z3native.mk_pbeq ctx (List.length args) args coeffs k
let get_bool_value x = lbool_of_int (Z3native.get_bool_value (gc x) x)
let is_bool x =
@ -1283,6 +1297,9 @@ struct
let mk_seq_contains = Z3native.mk_seq_contains
let mk_seq_extract = Z3native.mk_seq_extract
let mk_seq_replace = Z3native.mk_seq_replace
let mk_seq_replace_all = Z3native.mk_seq_replace_all
let mk_seq_replace_re = Z3native.mk_seq_replace_re
let mk_seq_replace_re_all = Z3native.mk_seq_replace_re_all
let mk_seq_at = Z3native.mk_seq_at
let mk_seq_length = Z3native.mk_seq_length
let mk_seq_nth = Z3native.mk_seq_nth
@ -1308,6 +1325,7 @@ struct
let mk_re_loop = Z3native.mk_re_loop
let mk_re_intersect ctx args = Z3native.mk_re_intersect ctx (List.length args) args
let mk_re_complement = Z3native.mk_re_complement
let mk_re_diff = Z3native.mk_re_diff
let mk_re_empty = Z3native.mk_re_empty
let mk_re_full = Z3native.mk_re_full
let mk_char = Z3native.mk_char
@ -1336,6 +1354,15 @@ struct
let mk_range = Z3native.mk_finite_set_range
end
module SpecialRelation =
struct
let mk_linear_order = Z3native.mk_linear_order
let mk_partial_order = Z3native.mk_partial_order
let mk_piecewise_linear_order = Z3native.mk_piecewise_linear_order
let mk_tree_order = Z3native.mk_tree_order
let mk_transitive_closure = Z3native.mk_transitive_closure
end
module FloatingPoint =
struct
module RoundingMode =

View file

@ -531,6 +531,10 @@ sig
For every [i] smaller than [num_exprs], the variable with de-Bruijn index [i] is replaced with term [to[i]]. *)
val substitute_vars : Expr.expr -> Expr.expr list -> expr
(** Substitute every application of [from[i]] with [to[i]] in the expression.
The [from] and [to] lists must have the same length. *)
val substitute_funs : Expr.expr -> FuncDecl.func_decl list -> Expr.expr list -> expr
(** Translates (copies) the term to another context.
@return A copy of the term which is associated with the other context *)
val translate : Expr.expr -> context -> expr
@ -632,6 +636,21 @@ sig
(** Creates a [distinct] term. *)
val mk_distinct : context -> Expr.expr list -> Expr.expr
(** Encodes p1 + p2 + ... + pn <= k. *)
val mk_atmost : context -> Expr.expr list -> int -> Expr.expr
(** Encodes p1 + p2 + ... + pn >= k. *)
val mk_atleast : context -> Expr.expr list -> int -> Expr.expr
(** Encodes k1*p1 + k2*p2 + ... + kn*pn <= k. *)
val mk_pble : context -> Expr.expr list -> int list -> int -> Expr.expr
(** Encodes k1*p1 + k2*p2 + ... + kn*pn >= k. *)
val mk_pbge : context -> Expr.expr list -> int list -> int -> Expr.expr
(** Encodes k1*p1 + k2*p2 + ... + kn*pn = k. *)
val mk_pbeq : context -> Expr.expr list -> int list -> int -> Expr.expr
(** Indicates whether the expression is the true or false expression
or something else (L_UNDEF). *)
val get_bool_value : Expr.expr -> Z3enums.lbool
@ -1968,9 +1987,22 @@ sig
(** extract sub-sequence starting at index given by second argument and of length provided by third argument *)
val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** replace first occurrence of second argument by third *)
(** [mk_seq_replace ctx seq target replacement] replaces the first occurrence
of [target] within [seq] with [replacement]. *)
val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** [mk_seq_replace_all ctx seq target replacement] replaces all occurrences
of [target] within [seq] with [replacement]. *)
val mk_seq_replace_all : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** [mk_seq_replace_re ctx seq re replacement] replaces the first occurrence
matching the regular expression [re] within [seq] with [replacement]. *)
val mk_seq_replace_re : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** [mk_seq_replace_re_all ctx seq re replacement] replaces all occurrences
matching the regular expression [re] within [seq] with [replacement]. *)
val mk_seq_replace_re_all : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
(** a unit sequence at index provided by second argument *)
val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr
@ -2048,6 +2080,9 @@ sig
(** the regular expression complement *)
val mk_re_complement : context -> Expr.expr -> Expr.expr
(** the regular expression difference *)
val mk_re_diff : context -> Expr.expr -> Expr.expr -> Expr.expr
(** the regular expression that accepts no sequences *)
val mk_re_empty : context -> Sort.sort -> Expr.expr
@ -2121,6 +2156,31 @@ sig
end
(** Special relation constructors *)
module SpecialRelation :
sig
(** Create a linear (total) order relation over the given sort.
The [id] parameter distinguishes multiple linear orders over the same sort. *)
val mk_linear_order : context -> Sort.sort -> int -> FuncDecl.func_decl
(** Create a partial order relation over the given sort.
The [id] parameter distinguishes multiple partial orders over the same sort. *)
val mk_partial_order : context -> Sort.sort -> int -> FuncDecl.func_decl
(** Create a piecewise linear order relation over the given sort.
The [id] parameter distinguishes multiple piecewise linear orders over the same sort. *)
val mk_piecewise_linear_order : context -> Sort.sort -> int -> FuncDecl.func_decl
(** Create a tree order relation over the given sort.
The [id] parameter distinguishes multiple tree orders over the same sort. *)
val mk_tree_order : context -> Sort.sort -> int -> FuncDecl.func_decl
(** Create the transitive closure of a binary relation.
The resulting relation is recursive. *)
val mk_transitive_closure : context -> FuncDecl.func_decl -> FuncDecl.func_decl
end
(** Floating-Point Arithmetic *)
module FloatingPoint :
sig

View file

@ -313,6 +313,7 @@ class bdist_wheel(_bdist_wheel):
("linux", "x86_64"): "linux_x86_64",
("linux", "aarch64"): "linux_aarch64",
('linux', "riscv64"): "linux_riscv64",
("linux", "loongarch64"): "linux_loongarch64",
# windows arm64 is not supported by pypi yet
("win", "x64"): "win_amd64",
("win", "x86"): "win32",

View file

@ -5612,6 +5612,20 @@ class Datatype:
"""
return CreateDatatypes([self])[0]
def create_polymorphic(self, type_params):
"""Create a polymorphic Z3 datatype with explicit type variables.
`type_params` is a list of type variables created with `DeclareTypeVar`.
Constructor field sorts may reference these type variables.
Self-recursive fields may reference this datatype directly.
>>> A = DeclareTypeVar('A')
>>> Pair = Datatype('Pair')
>>> Pair.declare('pair', ('fst', A), ('snd', A))
>>> Pair = Pair.create_polymorphic([A])
"""
return CreatePolymorphicDatatype(self, type_params)
class ScopedConstructor:
"""Auxiliary object used to create Z3 datatypes."""
@ -5733,6 +5747,76 @@ def CreateDatatypes(*ds):
return tuple(result)
def CreatePolymorphicDatatype(d, type_params):
"""Create a single polymorphic Z3 datatype with explicit type parameters.
`d` is a `Datatype` helper object whose constructors have been declared.
`type_params` is a list of type variables created with `DeclareTypeVar`.
Constructor field sorts may reference these type variables, and self-recursive
fields may reference `d` directly.
>>> A = DeclareTypeVar('A')
>>> Pair = Datatype('Pair')
>>> Pair.declare('pair', ('fst', A), ('snd', A))
>>> Pair = CreatePolymorphicDatatype(Pair, [A])
"""
if z3_debug():
_z3_assert(isinstance(d, Datatype), "Datatype expected")
_z3_assert(d.constructors != [], "Non-empty Datatype expected")
ctx = d.ctx
name = to_symbol(d.name, ctx)
num_params = len(type_params)
params_arr = (Sort * num_params)()
for i, p in enumerate(type_params):
if z3_debug():
_z3_assert(is_sort(p), "Z3 sort expected for type parameter")
params_arr[i] = p.ast
num_cs = len(d.constructors)
cs = (Constructor * num_cs)()
to_delete = []
for j in range(num_cs):
c = d.constructors[j]
cname = to_symbol(c[0], ctx)
rname = to_symbol(c[1], ctx)
fs = c[2]
num_fs = len(fs)
fnames = (Symbol * num_fs)()
sorts = (Sort * num_fs)()
refs = (ctypes.c_uint * num_fs)()
for k in range(num_fs):
fname = fs[k][0]
ftype = fs[k][1]
fnames[k] = to_symbol(fname, ctx)
if isinstance(ftype, Datatype):
if z3_debug():
_z3_assert(ftype is d, "Only self-recursive references are supported in polymorphic datatypes. Use CreateDatatypes for mutually recursive datatypes.")
sorts[k] = None
refs[k] = 0
else:
if z3_debug():
_z3_assert(is_sort(ftype), "Z3 sort expected")
sorts[k] = ftype.ast
refs[k] = 0
cs[j] = Z3_mk_constructor(ctx.ref(), cname, rname, num_fs, fnames, sorts, refs)
to_delete.append(ScopedConstructor(cs[j], ctx))
out = Z3_mk_polymorphic_datatype(ctx.ref(), name, num_params, params_arr, num_cs, cs)
dref = DatatypeSortRef(out, ctx)
num_cs_actual = dref.num_constructors()
for j in range(num_cs_actual):
cref = dref.constructor(j)
cref_name = cref.name()
cref_arity = cref.arity()
if cref_arity == 0:
cref = cref()
setattr(dref, cref_name, cref)
rref = dref.recognizer(j)
setattr(dref, "is_" + cref_name, rref)
for k in range(cref_arity):
aref = dref.accessor(j, k)
setattr(dref, aref.name(), aref)
return dref
class DatatypeSortRef(SortRef):
"""Datatype sorts."""

View file

@ -36,7 +36,8 @@ array_decl_plugin::array_decl_plugin():
m_set_complement_sym("complement"),
m_set_subset_sym("subset"),
m_array_ext_sym("array-ext"),
m_as_array_sym("as-array") {
m_as_array_sym("as-array"),
m_choice_sym("choice") {
}
#define ARRAY_SORT_STR "Array"
@ -433,6 +434,20 @@ func_decl * array_decl_plugin::mk_as_array(func_decl * f) {
return m_manager->mk_const_decl(m_as_array_sym, s, info);
}
func_decl* array_decl_plugin::mk_choice(unsigned arity, sort* const* domain) {
if (arity != 1) {
m_manager->raise_exception("choice takes one argument");
return nullptr;
}
sort* s = domain[0];
if (!is_array_sort(s) || get_array_arity(s) != 1 || !m_manager->is_bool(get_array_range(s))) {
m_manager->raise_exception("choice expects an argument with sort (Array T Bool)");
return nullptr;
}
return m_manager->mk_func_decl(m_choice_sym, arity, domain, get_array_domain(s, 0),
func_decl_info(m_family_id, OP_CHOICE));
}
func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
@ -501,6 +516,8 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
func_decl * f = to_func_decl(parameters[0].get_ast());
return mk_as_array(f);
}
case OP_CHOICE:
return mk_choice(arity, domain);
default: return nullptr;
}
}
@ -529,6 +546,7 @@ void array_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol con
op_names.push_back(builtin_name("complement",OP_SET_COMPLEMENT));
op_names.push_back(builtin_name("subset",OP_SET_SUBSET));
op_names.push_back(builtin_name("as-array", OP_AS_ARRAY));
op_names.push_back(builtin_name("choice", OP_CHOICE));
op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT));
#if 0
@ -655,4 +673,3 @@ func_decl* array_util::mk_array_ext(sort *domain, unsigned i) {
parameter p(i);
return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains);
}

View file

@ -63,6 +63,7 @@ enum array_op_kind {
OP_SET_COMPLEMENT,
OP_SET_SUBSET,
OP_AS_ARRAY, // used for model construction
OP_CHOICE,
LAST_ARRAY_OP
};
@ -79,6 +80,7 @@ class array_decl_plugin : public decl_plugin {
symbol m_set_subset_sym;
symbol m_array_ext_sym;
symbol m_as_array_sym;
symbol m_choice_sym;
bool check_set_arguments(unsigned arity, sort * const * domain);
@ -106,6 +108,8 @@ class array_decl_plugin : public decl_plugin {
func_decl * mk_as_array(func_decl * f);
func_decl * mk_choice(unsigned arity, sort* const* domain);
bool is_array_sort(sort* s) const;
public:
array_decl_plugin();
@ -164,6 +168,7 @@ public:
bool is_difference(expr* n) const { return is_app_of(n, m_fid, OP_SET_DIFFERENCE); }
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
bool is_choice(expr* n) const { return is_app_of(n, m_fid, OP_CHOICE); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); }
bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); }
@ -172,6 +177,7 @@ public:
bool is_union(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_UNION); }
bool is_intersect(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_INTERSECT); }
bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); }
bool is_choice(func_decl* f) const { return is_decl_of(f, m_fid, OP_CHOICE); }
bool is_default(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_DEFAULT); }
bool is_default(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_DEFAULT); }
bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_SET_SUBSET); }
@ -308,6 +314,10 @@ public:
return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, &param, 0, nullptr, nullptr);
}
app* mk_choice(expr* p) const {
return m_manager.mk_app(m_fid, OP_CHOICE, p);
}
sort* get_array_range_rec(sort* s) {
while (is_array(s)) {
s = get_array_range(s);
@ -317,5 +327,3 @@ public:
};

View file

@ -242,21 +242,14 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa
m_injective(false),
m_idempotent(false),
m_skolem(false),
m_lambda(false),
m_polymorphic(false) {
}
bool func_decl_info::operator==(func_decl_info const & info) const {
return decl_info::operator==(info) &&
m_left_assoc == info.m_left_assoc &&
m_right_assoc == info.m_right_assoc &&
m_flat_associative == info.m_flat_associative &&
m_commutative == info.m_commutative &&
m_chainable == info.m_chainable &&
m_pairwise == info.m_pairwise &&
m_injective == info.m_injective &&
m_skolem == info.m_skolem &&
m_lambda == info.m_lambda;
return decl_info::operator==(info) && m_left_assoc == info.m_left_assoc && m_right_assoc == info.m_right_assoc &&
m_flat_associative == info.m_flat_associative && m_commutative == info.m_commutative &&
m_chainable == info.m_chainable && m_pairwise == info.m_pairwise && m_injective == info.m_injective &&
m_skolem == info.m_skolem;
}
std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
@ -270,7 +263,6 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
if (info.is_injective()) out << " :injective ";
if (info.is_idempotent()) out << " :idempotent ";
if (info.is_skolem()) out << " :skolem ";
if (info.is_lambda()) out << " :lambda ";
if (info.is_polymorphic()) out << " :polymorphic ";
return out;
}
@ -1625,19 +1617,6 @@ bool ast_manager::are_distinct(expr* a, expr* b) const {
return false;
}
void ast_manager::add_lambda_def(func_decl* f, quantifier* q) {
TRACE(model, tout << "add lambda def " << mk_pp(q, *this) << "\n");
m_lambda_defs.insert(f, q);
f->get_info()->set_lambda(true);
inc_ref(q);
}
quantifier* ast_manager::is_lambda_def(func_decl* f) {
if (f->get_info() && f->get_info()->is_lambda())
return m_lambda_defs[f];
return nullptr;
}
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
SASSERT(m_plugins.get(id, 0) == 0);
@ -1670,7 +1649,7 @@ bool ast_manager::slow_not_contains(ast const * n) {
}
#endif
#if 1
#if 0
static unsigned s_count = 0;
static void track_id(ast_manager& m, ast* n, unsigned id) {
@ -1832,10 +1811,6 @@ void ast_manager::delete_node(ast * n) {
m_poly_roots.erase(f);
if (f->m_info != nullptr) {
func_decl_info * info = f->get_info();
if (info->is_lambda()) {
push_dec_ref(m_lambda_defs[f]);
m_lambda_defs.remove(f);
}
info->del_eh(*this);
dealloc(info);
}

View file

@ -404,7 +404,6 @@ struct func_decl_info : public decl_info {
bool m_injective:1;
bool m_idempotent:1;
bool m_skolem:1;
bool m_lambda:1;
bool m_polymorphic:1;
func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr);
@ -419,7 +418,6 @@ struct func_decl_info : public decl_info {
bool is_injective() const { return m_injective; }
bool is_idempotent() const { return m_idempotent; }
bool is_skolem() const { return m_skolem; }
bool is_lambda() const { return m_lambda; }
bool is_polymorphic() const { return m_polymorphic; }
void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; }
@ -432,7 +430,6 @@ struct func_decl_info : public decl_info {
void set_injective(bool flag = true) { m_injective = flag; }
void set_idempotent(bool flag = true) { m_idempotent = flag; }
void set_skolem(bool flag = true) { m_skolem = flag; }
void set_lambda(bool flag = true) { m_lambda = flag; }
void set_polymorphic(bool flag = true) { m_polymorphic = flag; }
bool operator==(func_decl_info const & info) const;
@ -661,7 +658,6 @@ public:
bool is_pairwise() const { return get_info() != nullptr && get_info()->is_pairwise(); }
bool is_injective() const { return get_info() != nullptr && get_info()->is_injective(); }
bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); }
bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); }
bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); }
bool is_polymorphic() const { return get_info() != nullptr && get_info()->is_polymorphic(); }
unsigned get_arity() const { return m_arity; }
@ -857,7 +853,8 @@ public:
enum quantifier_kind {
forall_k,
exists_k,
lambda_k
lambda_k,
choice_k
};
class quantifier : public expr {
@ -1512,7 +1509,6 @@ protected:
proof_gen_mode m_proof_mode;
bool m_int_real_coercions; // If true, use hack that automatically introduces to_int/to_real when needed.
ast_table m_ast_table;
obj_map<func_decl, quantifier*> m_lambda_defs;
id_gen m_expr_id_gen;
id_gen m_decl_id_gen;
sort * m_bool_sort;
@ -1642,15 +1638,7 @@ public:
bool are_distinct(expr * a, expr * b) const;
bool contains(ast * a) const { return m_ast_table.contains(a); }
bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
void add_lambda_def(func_decl* f, quantifier* q);
quantifier* is_lambda_def(func_decl* f);
quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
obj_map<func_decl, quantifier*> const& lambda_defs() const { return m_lambda_defs; }
symbol const& lambda_def_qid() const { return m_lambda_def; }
unsigned get_num_asts() const { return m_ast_table.size(); }
void debug_ref_count() { m_debug_ref_count = true; }

View file

@ -18,12 +18,12 @@ Revision History:
--*/
#include "ast/ast.h"
#define check_symbol(S1,S2) if (S1 != S2) return lt(S1,S2)
#define check_value(V1,V2) if (V1 != V2) return V1 < V2
#define check_bool(B1,B2) if (B1 != B2) return !B1 && B2
#define check_ptr(P1,P2) if (!P1 && P2) return true; if (P1 && !P2) return false
#define check_ast(T1,T2) if (T1 != T2) { n1 = T1; n2 = T2; goto start; }
#define check_zstring(S1, S2) if (S1 != S2) return S1 < S2
#define check_symbol(S1,S2) if ((S1) != (S2)) return lt((S1),(S2))
#define check_value(V1,V2) if ((V1) != (V2)) return (V1) < (V2)
#define check_bool(B1,B2) if ((B1) != (B2)) return !(B1) && (B2)
#define check_ptr(P1,P2) if (!(P1) && (P2)) return true; if ((P1) && !(P2)) return false
#define check_ast(T1,T2) if ((T1) != (T2)) { n1 = (T1); n2 = (T2); goto start; }
#define check_zstring(S1, S2) if ((S1) != (S2)) return (S1) < (S2)
#define check_parameter(p1, p2) { \
check_value(p1.get_kind(), p2.get_kind()); \

View file

@ -77,7 +77,7 @@ private:
void pp_atomic_step(const expr * e) {
unsigned id = get_id(e);
m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << std::endl;
m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << '\n';
}
void pp_step(const proof * p) {
@ -91,7 +91,7 @@ private:
m_first ? (m_first=false,"color=\"red\"") : num_parents==0 ? "color=\"yellow\"": "";
m_out << "node_" << id <<
" [shape=box,style=\"filled\",label=\"" << label_of_expr(p_res) << "\""
<< color << "]" << std::endl;
<< color << "]" << '\n';
// now print edges to parents (except last one, which is the result)
std::string label = p->get_decl()->get_name().str();
for (unsigned i = 0 ; i < num_parents; ++i) {
@ -99,7 +99,7 @@ private:
// explore parent, also print a link to it
push_term(to_app(parent));
m_out << "node_" << id << " -> " << "node_" << get_id((expr*)parent)
<< "[label=\"" << label << "\"];" << std::endl;;
<< "[label=\"" << label << "\"];" << '\n';
}
} else {
pp_atomic_step(p);
@ -120,11 +120,11 @@ private:
// main printer
std::ostream & ast_pp_dot::pp(std::ostream & out) const {
out << "digraph proof { " << std::endl;
out << "digraph proof { " << '\n';
ast_pp_dot_st pp_st(this, out);
pp_st.push_term(m_pr);
pp_st.pp_loop();
out << std::endl << " } " << std::endl << std::flush;
out << '\n' << " } " << '\n' << std::flush;
return out;
}

View file

@ -733,7 +733,8 @@ public:
m_AUFLIRA("AUFLIRA"),
// It's much easier to read those testcases with that.
m_no_lets(no_lets),
m_simplify_implies(simplify_implies)
m_simplify_implies(simplify_implies),
m_top(nullptr)
{
m_basic_fid = m.get_basic_family_id();
m_label_fid = m.mk_family_id("label");

View file

@ -181,20 +181,12 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) {
new_fi.set_injective(fi->is_injective());
new_fi.set_skolem(fi->is_skolem());
new_fi.set_idempotent(fi->is_idempotent());
new_fi.set_lambda(fi->is_lambda());
new_f = m_to_manager.mk_func_decl(f->get_name(),
f->get_arity(),
new_domain,
new_range,
new_fi);
if (new_fi.is_lambda()) {
quantifier* q = from().is_lambda_def(f);
ast_translation tr(from(), to());
quantifier* new_q = tr(q);
to().add_lambda_def(new_f, new_q);
}
}
TRACE(ast_translation,
tout << f->get_name() << " "; if (fi) tout << *fi; tout << "\n";

View file

@ -168,7 +168,7 @@ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) {
}
app* mk_and(ast_manager & m, unsigned num_args, app * const * args) {
return to_app(mk_and(m, num_args, (expr* const*) args));
return to_app(mk_and(m, num_args, reinterpret_cast<expr* const*>(args)));
}
expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) {

View file

@ -783,6 +783,9 @@ void bv_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const
op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT));
op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT));
op_names.push_back(builtin_name("bit2bool", OP_BIT2BOOL));
op_names.push_back(builtin_name("ubv_to_int", OP_UBV2INT));
op_names.push_back(builtin_name("sbv_to_int", OP_SBV2INT));
op_names.push_back(builtin_name("int_to_bv", OP_INT2BV));
if (logic == symbol::null || logic == symbol("ALL") || logic == "QF_FD" || logic == "HORN") {
op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL));
@ -804,11 +807,10 @@ void bv_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const
op_names.push_back(builtin_name("ext_rotate_left",OP_EXT_ROTATE_LEFT));
op_names.push_back(builtin_name("ext_rotate_right",OP_EXT_ROTATE_RIGHT));
op_names.push_back(builtin_name("int2bv",OP_INT2BV));
op_names.push_back(builtin_name("int_to_bv",OP_INT2BV));
op_names.push_back(builtin_name("bv2int",OP_UBV2INT));
op_names.push_back(builtin_name("bv2nat",OP_UBV2INT));
op_names.push_back(builtin_name("ubv_to_int",OP_UBV2INT));
op_names.push_back(builtin_name("sbv_to_int",OP_SBV2INT));
op_names.push_back(builtin_name("mkbv",OP_MKBV));
}
}

View file

@ -133,7 +133,7 @@ namespace euf {
// Instructions
//
// ------------------------------------
typedef enum {
typedef enum : uint8_t {
INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN, INITAC,
BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN,
YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
@ -239,6 +239,7 @@ namespace euf {
unsigned short m_num_args;
unsigned m_ireg;
unsigned m_oreg;
unsigned m_curr_generation;
};
struct get_cgr : public instruction {
@ -1926,28 +1927,38 @@ namespace euf {
m_max_generation = std::max(m_max_generation, n->generation());
}
void get_f_app(func_decl* lbl, unsigned num_expected_args, enode* curr, enode*& matching_cgr, enode*& min_gen_match) {
if (curr->get_decl() == lbl && curr->num_args() == num_expected_args) {
if (curr->is_cgr() && !matching_cgr)
matching_cgr = curr;
if (!min_gen_match || min_gen_match->generation() > curr->generation())
min_gen_match = curr;
}
}
// We have to provide the number of expected arguments because we have flat-assoc applications such as +.
// Flat-assoc applications may have arbitrary number of arguments.
enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * first) {
enode *matching_cgr = nullptr, *min_gen_match = nullptr;
for (enode* curr : euf::enode_class(first)) {
if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
update_max_generation(curr, first);
return curr;
}
get_f_app(lbl, num_expected_args, curr, matching_cgr, min_gen_match);
curr = curr->get_next();
}
return nullptr;
if (matching_cgr)
update_max_generation(min_gen_match, first);
return matching_cgr;
}
enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) {
curr = curr->get_next();
enode *matching_cgr = nullptr, *min_gen_match = nullptr;
while (curr != first) {
if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
update_max_generation(curr, first);
return curr;
}
get_f_app(lbl, num_expected_args, curr, matching_cgr, min_gen_match);
curr = curr->get_next();
}
return nullptr;
if (matching_cgr)
update_max_generation(min_gen_match, first);
return matching_cgr;
}
/**
@ -2563,6 +2574,7 @@ namespace euf {
m_backtrack_stack[m_top].m_instr = m_pc; \
m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation; \
m_backtrack_stack[m_top].m_curr = m_app; \
const_cast<bind*>(static_cast<bind const*>(m_pc))->m_curr_generation = m_max_generation; \
m_top++;
BIND_COMMON();
@ -2829,7 +2841,8 @@ namespace euf {
goto backtrack; \
} \
bp.m_curr = m_app; \
TRACE(mam_int, tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_expr(), m);); \
m_max_generation = m_b->m_curr_generation; \
TRACE(mam_int, tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_expr(), m);); \
m_oreg = m_b->m_oreg
BBIND_COMMON();
@ -4059,4 +4072,4 @@ void euf::mam::ground_subterms(expr* e, ptr_vector<app>& ground) {
euf::mam* euf::mam::mk(euf::mam_solver& ctx, euf::on_binding_callback& em) {
return alloc(mam_impl, ctx, em, true);
}
}

View file

@ -64,7 +64,7 @@ namespace euf {
}
void ho_matcher::search() {
IF_VERBOSE(1, display(verbose_stream()));
IF_VERBOSE(10, display(verbose_stream()));
while (m.inc()) {
// Q, B -> Q', B'. Push work on the backtrack stack and new work items
@ -77,7 +77,7 @@ namespace euf {
break;
}
IF_VERBOSE(1, display(verbose_stream() << "ho_matcher: done\n"));
IF_VERBOSE(10, display(verbose_stream() << "ho_matcher: done\n"));
}
void ho_matcher::backtrack() {
@ -92,7 +92,7 @@ namespace euf {
while (!m_backtrack.empty()) {
auto& wi = *m_backtrack.back();
bool st = consume_work(wi);
IF_VERBOSE(3, display(verbose_stream() << "ho_matcher::consume_work: " << wi.pat << " =?= " << wi.t << " -> " << (st?"true":"false") << "\n"););
TRACE(ho_matching, display(tout << "ho_matcher::consume_work: " << mk_bounded_pp(wi.pat, m) << " =?= " << mk_bounded_pp(wi.t, m) << " -> " << (st?"true":"false") << "\n"););
if (st) {
if (m_goals.empty())
m_on_match(m_subst);
@ -110,7 +110,11 @@ namespace euf {
}
lbool ho_matcher::are_equal(unsigned o1, expr* p, unsigned o2, expr* t) const {
SASSERT(p->get_sort() == t->get_sort());
if (p->get_sort() != t->get_sort()) {
TRACE(ho_matching, tout << "sort mismatch: " << mk_pp(p, m) << " : " << mk_pp(p->get_sort(), m)
<< " vs " << mk_pp(t, m) << " : " << mk_pp(t->get_sort(), m) << "\n";);
return l_false;
}
if (o1 == o2 && p == t)
return l_true;
@ -239,25 +243,19 @@ namespace euf {
return r;
}
// We assume that m_rewriter should produce
// something amounting to weak-head normal form WHNF
expr_ref ho_matcher::whnf_star(expr *e, unsigned offset) const {
expr_ref r(e, m);
while (true) {
auto q = whnf(r, offset);
if (q == r)
return r;
r = q;
}
}
void ho_matcher::reduce(match_goal& wi) {
while (true) {
expr_ref r = whnf(wi.pat, wi.pat_offset());
if (r == wi.pat)
break;
IF_VERBOSE(3, verbose_stream() << "ho_matcher::reduce: " << wi.pat << " -> " << r << "\n";);
wi.pat = r;
}
while (true) {
expr_ref r = whnf(wi.t, wi.term_offset());
if (r == wi.t)
break;
IF_VERBOSE(3, verbose_stream() << "ho_matcher::reduce: " << wi.t << " -> " << r << "\n";);
wi.t = r;
}
wi.pat = whnf_star(wi.pat, wi.pat_offset());
wi.t = whnf_star(wi.t, wi.term_offset());
}
bool ho_matcher::consume_work(match_goal &wi) {
@ -288,7 +286,6 @@ namespace euf {
break;
}
// v >= offset
// v - offset |-> t
if (is_meta_var(p, wi.pat_offset()) && is_closed(t, 0, wi.term_offset())) {
@ -299,7 +296,6 @@ namespace euf {
return true;
}
// N = \ x. T => ((shift1 N) x) = T
if (is_lambda(t) && !is_lambda(p)) {
auto q = to_quantifier(t);
@ -318,6 +314,43 @@ namespace euf {
return true;
}
// \x . N = T => N = ((shift1 T) x)
if (is_lambda(p) && !is_lambda(t)) {
auto q = to_quantifier(p);
auto p_body = q->get_expr();
auto nd = q->get_num_decls();
var_shifter vs(m);
expr_ref r(m);
vs(t, nd, r);
expr_ref_vector args(m);
args.push_back(r);
for (unsigned i = 0; i < nd; ++i)
args.push_back(m.mk_var(nd - 1 - i, q->get_decl_sort(i)));
r = m_array.mk_select(args);
m_goals.push(wi.level, wi.term_offset() + nd, p_body, r);
wi.set_done();
return true;
}
//
// lambda x . p == lambda x . t
//
if (is_quantifier(p) && is_quantifier(t)) {
auto qp = to_quantifier(p);
auto qt = to_quantifier(t);
unsigned pd = qp->get_num_decls();
unsigned td = qt->get_num_decls();
if (qp->get_kind() != qt->get_kind())
return false;
if (pd != td)
return false;
for (unsigned i = 0; i < pd; ++i)
if (qp->get_decl_sort(i) != qt->get_decl_sort(i))
return false;
m_goals.push(wi.level, wi.term_offset() + td, qp->get_expr(), qt->get_expr());
return true;
}
// Flex head unitary
// H(pat) = t
@ -457,25 +490,7 @@ namespace euf {
m_goals.push(wi.level, wi.term_offset(), tp->get_arg(i), ta->get_arg(i));
return true;
}
//
// lambda x . p == lambda x . t
//
if (is_quantifier(p) && is_quantifier(t)) {
auto qp = to_quantifier(p);
auto qt = to_quantifier(t);
unsigned pd = qp->get_num_decls();
unsigned td = qt->get_num_decls();
if (qp->get_kind() != qt->get_kind())
return false;
if (pd != td)
return false;
for (unsigned i = 0; i < pd; ++i)
if (qp->get_decl_sort(i) != qt->get_decl_sort(i))
return false;
m_goals.push(wi.level, wi.term_offset() + td, qp->get_expr(), qt->get_expr());
return true;
}
return false;
}
@ -488,8 +503,7 @@ namespace euf {
uint_set vars;
while (m_array.is_select(p)) {
auto a = to_app(p);
for (unsigned i = 1; i < a->get_num_args(); ++i) {
auto arg = a->get_arg(i);
for (auto arg : *a) {
if (!is_bound_var(arg, offset))
return false;
auto idx = to_var(arg)->get_idx();
@ -549,15 +563,12 @@ namespace euf {
}
expr_ref_vector pat2bound(m);
for (auto a : pats) {
unsigned sz = a->get_num_args();
for (unsigned i = 1; i < sz; ++i) {
auto arg = a->get_arg(i);
for (auto arg : *a) {
SASSERT(is_bound_var(arg, offset));
auto idx = to_var(arg)->get_idx();
pat2bound.reserve(idx + 1);
pat2bound[idx] = m.mk_var(--num_bound, arg->get_sort());
}
p1 = a->get_arg(0);
}
}
var_subst sub(m, false);
expr_ref lam = sub(t, pat2bound);
@ -575,7 +586,7 @@ namespace euf {
//
// keep track of number of internal scopes and offset to non-capture variables.
// a variable is captured if it's index is in the interval [scopes, offset[.
// a variable is captured if its index is in the interval [scopes, offset[.
//
bool ho_matcher::is_closed(expr* v, unsigned scopes, unsigned offset) const {
if (is_ground(v))
@ -630,49 +641,59 @@ namespace euf {
void ho_matcher::add_binding(var* v, unsigned offset, expr* t) {
SASSERT(v->get_idx() >= offset);
m_subst.set(v->get_idx() - offset, t);
IF_VERBOSE(1, verbose_stream() << "ho_matcher::add_binding: v" << v->get_idx() - offset << " -> " << mk_pp(t, m) << "\n";);
SASSERT(v->get_sort() == t->get_sort());
TRACE(ho_matching, tout << "ho_matcher::add_binding: v" << v->get_idx() - offset << " -> " << mk_pp(t, m) << "\n";);
m_trail.push(undo_set(m_subst, v->get_idx() - offset));
}
std::pair<quantifier*, app*> ho_matcher::compile_ho_pattern(quantifier* q, app* p) {
app* p1 = nullptr;
if (m_pat2hopat.find(p, p)) {
q = m_q2hoq[q];
return { q, p };
quantifier *q1 = nullptr;
if (m_pat2hopat.find(p, p1) && m_q2hoq.find(q, q1)) {
return { q1, p1 };
}
auto is_ho = any_of(subterms::all(expr_ref(p, m)), [&](expr* t) { return m_unitary.is_flex(0, t); });
auto is_ho = any_of(subterms::all(expr_ref(p, m)), [&](expr* t) {
return m_unitary.is_flex(0, t) ||
// m.is_lambda_def(t) ||
is_lambda(t);
});
if (!is_ho)
return { q, p };
ptr_vector<expr> todo;
vector<std::pair<expr*, unsigned>> todo;
ptr_buffer<var> bound;
expr_ref_vector cache(m);
unsigned nb = q->get_num_decls();
todo.push_back(p);
bool contains_pat2abs = m_pat2abs.contains(p);
SASSERT(m.is_pattern(p));
todo.push_back({p, 0});
while (!todo.empty()) {
auto t = todo.back();
auto [t, lvl] = todo.back();
if (is_var(t)) {
cache.setx(t->get_id(), t);
todo.pop_back();
continue;
}
if (m_unitary.is_flex(0, t)) {
m_pat2abs.insert_if_not_there(p, svector<std::pair<unsigned, expr*>>()).push_back({ nb, t });
if ((m_unitary.is_flex(0, t) && lvl > 1) || // m.is_lambda_def(t) ||
is_lambda(t)) {
if (!contains_pat2abs)
m_pat2abs.insert_if_not_there(p, svector<std::pair<unsigned, expr*>>()).push_back({ nb, t });
auto v = m.mk_var(nb++, t->get_sort());
bound.push_back(v);
cache.setx(t->get_id(), v);
todo.pop_back();
continue;
}
}
if (is_app(t)) {
auto a = to_app(t);
unsigned sz = a->get_num_args();
ptr_buffer<expr> args;
for (auto arg : *a) {
cache.reserve(arg->get_id() + 1);
expr* arg1 = cache.get(arg->get_id());
if (!arg1)
todo.push_back(arg);
todo.push_back({arg, lvl + 1});
else
args.push_back(arg1);
}
@ -682,11 +703,15 @@ namespace euf {
cache.setx(t->get_id(), m.mk_app(a->get_decl(), args.size(), args.data()));
}
if (is_quantifier(t)) {
m_pat2abs.remove(p);
if (!contains_pat2abs)
m_pat2abs.remove(p);
return { q, p };
}
}
p1 = to_app(cache.get(p->get_id()));
if (p1 == p)
return {q, p};
expr_free_vars free_vars;
free_vars(p1);
app_ref_vector new_ground(m);
@ -713,6 +738,8 @@ namespace euf {
auto body = q->get_expr();
if (!new_patterns.empty()) {
ptr_vector<app> pats;
CTRACE(ho_matching, !m.is_pattern(p1),
tout << mk_pp(p, m) << "\n" << mk_pp(p1, m) << "\n";);
VERIFY(m.is_pattern(p1, pats));
for (auto p : new_patterns) // patterns for variables that are not free in new pattern
pats.push_back(p);
@ -721,23 +748,40 @@ namespace euf {
p1 = m.mk_pattern(pats.size(), pats.data());
}
quantifier* q1 = m.mk_forall(sorts.size(), sorts.data(), names.data(), body);
q1 = m.mk_forall(sorts.size(), sorts.data(), names.data(), body);
m_pat2hopat.insert(p, p1);
m_hopat2pat.insert(p1, p);
m_q2hoq.insert(q, q1);
m_hoq2q.insert(q1, q);
m_hopat2free_vars.insert(p1, std::move(free_vars));
m_ho_patterns.push_back(p1);
m_ho_qs.push_back(q1);
trail().push(push_back_vector(m_ho_patterns));
trail().push(push_back_vector(m_ho_qs));
trail().push(insert_map(m_pat2hopat, p));
trail().push(insert_map(m_hopat2pat, p1));
trail().push(insert_map(m_pat2abs, p));
trail().push(insert_map(m_q2hoq, q));
trail().push(insert_map(m_hoq2q, q1));
trail().push(insert_map(m_hopat2free_vars, p1));
if (!m_pat2hopat.contains(p)) {
m_pat2hopat.insert(p, p1);
trail().push(insert_map(m_pat2hopat, p));
}
if (!m_hopat2pat.contains(p1)) {
m_hopat2pat.insert(p1, p);
trail().push(insert_map(m_hopat2pat, p1));
}
if (!m_q2hoq.contains(q)) {
m_q2hoq.insert(q, q1);
trail().push(insert_map(m_q2hoq, q));
}
if (!m_hoq2q.contains(q1)) {
m_hoq2q.insert(q1, q);
trail().push(insert_map(m_hoq2q, q1));
}
if (!m_hopat2free_vars.contains(p1)) {
m_hopat2free_vars.insert(p1, std::move(free_vars));
trail().push(insert_map(m_hopat2free_vars, p1));
}
if (!contains_pat2abs)
trail().push(insert_map(m_pat2abs, p));
TRACE(ho_matching, tout << mk_pp(q, m) << "\n"
<< mk_pp(p, m) << "\n->\n"
<< mk_pp(q1, m) << "\n"
<< mk_pp(p1, m) << "\n");
return { q1, p1 };
}
@ -745,28 +789,46 @@ namespace euf {
return m_hopat2pat.contains(p);
}
void ho_matcher::register_ho_pattern(app* alias_p, app* full_p) {
if (alias_p == full_p) return;
auto orig_p = m_hopat2pat[full_p];
m_hopat2pat.insert(alias_p, orig_p);
m_hopat2free_vars.insert(alias_p, m_hopat2free_vars[full_p]);
m_ho_patterns.push_back(alias_p);
trail().push(push_back_vector(m_ho_patterns));
trail().push(insert_map(m_hopat2pat, alias_p));
trail().push(insert_map(m_hopat2free_vars, alias_p));
}
void ho_matcher::refine_ho_match(app* p, expr_ref_vector& s) {
auto fo_pat = m_hopat2pat[p];
IF_VERBOSE(10, verbose_stream() << "refine_ho_match: p=" << mk_pp(p, m) << "\n fo_pat=" << mk_pp(fo_pat, m) << "\n";
verbose_stream() << " m_pat2abs has fo_pat: " << m_pat2abs.contains(fo_pat) << "\n";
auto& abs = m_pat2abs[fo_pat];
verbose_stream() << " m_pat2abs size: " << abs.size() << "\n";
for (auto [v, pat] : abs) verbose_stream() << " v=" << v << " pat=" << mk_pp(pat, m) << "\n";);
m_trail.push_scope();
m_subst.resize(0);
m_subst.resize(s.size());
m_goals.reset();
// MAM bindings are reversed: s[i] = binding for var idx = s.size()-1-i
// m_subst is indexed by var index directly
for (unsigned i = 0; i < s.size(); ++i) {
auto idx = s.size() - i - 1;
if (!m_hopat2free_vars[p].contains(idx))
s[i] = m.mk_var(idx, s[i]->get_sort());
else if (s.get(i))
m_subst.set(i, s.get(i));
m_subst.set(idx, s.get(i));
}
IF_VERBOSE(1, verbose_stream() << "refine " << mk_pp(p, m) << "\n" << s << "\n");
TRACE(ho_matching, tout << "refine " << mk_pp(p, m) << "\n" << s << "\n");
unsigned num_bound = 0, level = 0;
for (auto [v, pat] : m_pat2abs[fo_pat]) {
var_subst sub(m, true);
auto pat_refined = sub(pat, s);
IF_VERBOSE(1, verbose_stream() << mk_pp(pat, m) << " -> " << pat_refined << "\n");
m_goals.push(level, num_bound, pat_refined, s.get(s.size() - v - 1));
TRACE(ho_matching, tout << mk_pp(pat, m) << " -> " << pat_refined << "\n");
m_goals.push(level, num_bound, pat_refined, m_subst.get(v));
}
search();

View file

@ -25,6 +25,7 @@ Author:
#include "ast/for_each_expr.h"
#include "ast/reg_decl_plugins.h"
#include "ast/ast_pp.h"
#include "ast/ast_ll_pp.h"
#include "ast/rewriter/array_rewriter.h"
#include "ast/rewriter/var_subst.h"
@ -88,13 +89,15 @@ namespace euf {
}
match_goal(unsigned level, unsigned offset, expr_ref const& pat, expr_ref const& t) noexcept :
base_offset(offset), pat(pat), t(t), level(level) {}
base_offset(offset), pat(pat), t(t), level(level) {
SASSERT(pat->get_sort() == t->get_sort());
}
unsigned term_offset() const { return base_offset + delta_offset; }
unsigned pat_offset() const { return base_offset + delta_offset; }
std::ostream& display(std::ostream& out) const {
return out << "[" << level << ":" << base_offset + delta_offset << "] " << pat << " ~ " << t << "\n";
return out << "[" << level << ":" << base_offset + delta_offset << "] " << mk_bounded_pp(pat, pat.m()) << " ~ " << mk_bounded_pp(t, t.m()) << "\n";
}
};
@ -329,6 +332,8 @@ namespace euf {
bool consume_work(match_goal& wi);
expr_ref whnf(expr* e, unsigned offset) const;
expr_ref whnf_star(expr *e, unsigned offset) const;
bool is_bound_var(expr* v, unsigned offset) const { return is_var(v) && to_var(v)->get_idx() < offset; }
@ -389,11 +394,23 @@ namespace euf {
bool is_ho_pattern(app* p);
// Register an alias pattern (e.g., after stripping ground elements)
// that maps to the same original pattern as full_p
void register_ho_pattern(app* alias_p, app* full_p);
void refine_ho_match(app* p, expr_ref_vector& s);
bool is_free(app* p, unsigned i) const { return m_hopat2free_vars[p].contains(i); }
quantifier* hoq2q(quantifier* q) const { return m_hoq2q[q]; }
svector<std::pair<unsigned, expr*>> const* get_flex_subterms(app* p) const {
auto orig_p = m_hopat2pat.find_core(p);
if (!orig_p) return nullptr;
auto abs = m_pat2abs.find_core(orig_p->get_data().get_value());
return abs ? &abs->get_data().get_value() : nullptr;
}
};
}

View file

@ -2976,13 +2976,12 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar
prev_bit = bit;
}
expr_ref one_div_exp2(m);
one_div_exp2 = m_arith_util.mk_div(one, exp2);
exp2 = m.mk_ite(exp_is_neg, one_div_exp2, exp2);
dbg_decouple("fpa2bv_to_real_exp2", exp2);
expr_ref res(m), two_exp2(m), minus_res(m), sgn_is_1(m);
expr_ref two_exp2(m), one_div_two_exp2(m);
two_exp2 = m_arith_util.mk_power(two, exp2);
one_div_two_exp2 = m_arith_util.mk_div(one, two_exp2);
two_exp2 = m.mk_ite(exp_is_neg, one_div_two_exp2, two_exp2);
dbg_decouple("fpa2bv_to_real_exp2", two_exp2);
expr_ref res(m), minus_res(m), sgn_is_1(m);
res = m_arith_util.mk_mul(rsig, two_exp2);
minus_res = m_arith_util.mk_uminus(res);
sgn_is_1 = m.mk_eq(sgn, bv1);
@ -2990,7 +2989,7 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar
dbg_decouple("fpa2bv_to_real_sig_times_exp2", res);
TRACE(fpa2bv_to_real, tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl;
tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;);
tout << "two_exp2 = " << mk_ismt2_pp(two_exp2, m) << std::endl;);
expr_ref unspec(m);
mk_to_real_unspecified(f, num, args, unspec);

View file

@ -121,9 +121,6 @@ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffe
sort * range = e->get_sort();
func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.data(), range);
app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.data());
if (is_lambda(e)) {
m.add_lambda_def(new_skolem_decl, to_quantifier(e));
}
return n;
}
@ -193,43 +190,7 @@ void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var
else if (m.is_term_ite(e)) {
bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs);
bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs);
}
else if (is_lambda(e)) {
// n(y) = \x . M[x,y]
// =>
// n(y)[x] = M, forall x y
//
// NB. The pattern is incomplete.
// consider store(a, i, v) == \lambda j . if i = j then v else a[j]
// the instantiation rules for store(a, i, v) are:
// store(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] }
// The first pattern is not included.
// TBD use a model-based scheme for extracting instantiations instead of
// using multi-patterns.
//
quantifier* q = to_quantifier(e);
expr_ref_vector args(m);
expr_ref n2(m), n3(m);
var_shifter vs(m);
vs(n, q->get_num_decls(), n2);
args.push_back(n2);
var_sorts.append(q->get_num_decls(), q->get_decl_sorts());
var_names.append(q->get_num_decls(), q->get_decl_names());
for (unsigned i = 0; i < q->get_num_decls(); ++i) {
args.push_back(m.mk_var(q->get_num_decls() - i - 1, q->get_decl_sort(i)));
}
array_util autil(m);
func_decl * f = nullptr;
if (autil.is_as_array(n2, f)) {
n3 = m.mk_app(f, args.size()-1, args.data() + 1);
}
else {
n3 = autil.mk_select(args.size(), args.data());
}
bound_vars(var_sorts, var_names, MK_EQ(q->get_expr(), n3), to_app(n3), defs, m.lambda_def_qid());
}
}
else {
bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs);
}

View file

@ -100,7 +100,7 @@ class name_quantifier_labels : public name_exprs_core {
public:
pred(ast_manager & m):m(m) {}
bool operator()(expr * t) override {
return is_quantifier(t) || m.is_label(t);
return (is_quantifier(t) && !is_lambda(t)) || m.is_label(t);
}
};
@ -127,7 +127,7 @@ class name_nested_formulas : public name_exprs_core {
TRACE(name_exprs, tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m) << "\n";);
if (is_app(t))
return to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root;
return m.is_label(t) || is_quantifier(t);
return m.is_label(t) || (is_quantifier(t) && !is_lambda(t));
}
};

View file

@ -188,7 +188,7 @@ struct pull_quant::imp {
var_names.data(),
nested_q->get_expr(),
std::min(q->get_weight(), nested_q->get_weight()),
m.is_lambda_def(q) ? symbol("pulled-lambda") : q->get_qid());
q->get_qid());
}
void pull_quant1(quantifier * q, expr * new_expr, expr_ref & result) {

View file

@ -254,6 +254,27 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) {
}
return;
}
case AST_QUANTIFIER: {
quantifier * q = to_quantifier(n);
unsigned num_decls = q->get_num_decls();
info * body_info = nullptr;
m_cache.find(entry(q->get_expr(), delta + num_decls), body_info);
if (body_info == nullptr) {
save(n, delta, nullptr);
return;
}
// The lambda/quantifier itself is a valid sub-term in a pattern.
// Propagate the free variables from the body (they already refer
// to the outer quantifier's bindings) and keep the node as-is.
expr * new_body = body_info->m_node.get();
quantifier_ref new_q(m);
if (new_body != q->get_expr())
new_q = m.update_quantifier(q, new_body);
else
new_q = q;
save(n, delta, alloc(info, m, new_q, body_info->m_free_vars, body_info->m_size + 1));
return;
}
default:
save(n, delta, nullptr);
return;
@ -363,6 +384,8 @@ bool pattern_inference_cfg::contains_subpattern::operator()(expr * n) {
break;
case AST_VAR:
break;
case AST_QUANTIFIER:
break;
default:
UNREACHABLE();
}
@ -525,7 +548,7 @@ void pattern_inference_cfg::reset_pre_patterns() {
bool pattern_inference_cfg::is_forbidden(app * n) const {
func_decl const * decl = n->get_decl();
func_decl * decl = n->get_decl();
if (is_ground(n))
return false;
// Remark: skolem constants should not be used in patterns, since they do not

View file

@ -444,7 +444,8 @@ namespace recfun {
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
SASSERT(!m_defs.contains(d->get_decl()));
if (m_defs.contains(d->get_decl()))
throw default_exception(std::string("recursive function ") + name.str() + " already defined");
m_defs.insert(d->get_decl(), d);
return promise_def(&u(), d);
}

View file

@ -750,7 +750,10 @@ bool array_rewriter::add_store(expr_ref_vector& args, unsigned num_idxs, expr* e
}
if (is_var(e1) && is_ground(e2)) {
unsigned idx = to_var(e1)->get_idx();
args[num_idxs - idx - 1] = e2;
unsigned nidx = num_idxs - idx - 1;
if (args.get(nidx) && args.get(nidx) != e2)
return false;
args[nidx] = e2;
}
else {
return false;
@ -858,19 +861,45 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
return false;
};
auto domain_is_larger_than = [&](sort* s, unsigned num_stores) {
unsigned sz = get_array_arity(s);
rational dsz(1);
for (unsigned i = 0; i < sz; ++i) {
sort* d = get_array_domain(s, i);
if (d->is_infinite())
return true;
if (d->is_very_big())
return false;
dsz *= rational(d->get_num_elements().size(), rational::ui64());
if (dsz > rational(num_stores, rational::ui64()))
return true;
}
return false;
};
expr* lhs1 = lhs;
expr* rhs1 = rhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
domain_is_larger_than(lhs->get_sort(), num_lhs + num_rhs)) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
fmls.push_back(m().mk_eq(v, w));
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
if (m_expand_store_eq) {
expr* lhs1 = lhs;
expr* rhs1 = rhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);

View file

@ -225,6 +225,7 @@ struct enum2bv_rewriter::imp {
new_body_ref = mk_and(bounds);
break;
case lambda_k:
case choice_k:
UNREACHABLE();
break;
}

View file

@ -561,9 +561,13 @@ void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
expr * const * np = it + 1;
expr * const * nnp = np + num_pats;
unsigned j = 0;
for (unsigned i = 0; i < num_pats; ++i)
for (unsigned i = 0; i < num_pats; ++i) {
if (m_manager.is_pattern(np[i]))
new_pats[j++] = np[i];
else {
IF_VERBOSE(10, verbose_stream() << "[rewriter] dropping pattern (is_pattern check failed) for qid=" << q->get_qid() << " pattern[" << i << "]: " << mk_ismt2_pp(np[i], m_manager, 3) << "\n";);
}
}
new_pats.shrink(j);
num_pats = j;
j = 0;
@ -664,7 +668,7 @@ template<typename Config>
void rewriter_tpl<Config>::display_bindings(std::ostream& out) {
for (unsigned i = 0; i < m_bindings.size(); ++i) {
if (m_bindings[i])
out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << ";\n";
out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << " : " << mk_pp(m_bindings[i]->get_sort(), m()) << ";\n";
}
}

View file

@ -17,6 +17,7 @@ z3_add_component(simplifiers
euf_completion.cpp
extract_eqs.cpp
factor_simplifier.cpp
fold_unfold.cpp
linear_equation.cpp
max_bv_sharing.cpp
model_reconstruction_trail.cpp

View file

@ -381,7 +381,7 @@ bool bound_propagator::relevant_bound(var x, double new_k) const {
if (b == nullptr)
return true; // variable did not have a bound
double interval_size;
double interval_size = 0.0;
bool bounded = get_interval_size(x, interval_size);
if (!is_int(x)) {
@ -939,4 +939,3 @@ void bound_propagator::display(std::ostream & out) const {
}

View file

@ -88,22 +88,6 @@ void dependent_expr_state::freeze_recfun() {
m_num_recfun = sz;
}
/**
* Freeze all functions used in lambda defined declarations
*/
void dependent_expr_state::freeze_lambda() {
auto& m = m_frozen_trail.get_manager();
unsigned sz = m.lambda_defs().size();
if (m_num_lambdas >= sz)
return;
ast_mark visited;
for (auto const& [f, body] : m.lambda_defs())
freeze_terms(body, false, visited);
m_trail.push(value_trail(m_num_lambdas));
m_num_lambdas = sz;
}
/**
* The current qhead is to be updated to qtail.
@ -122,8 +106,7 @@ void dependent_expr_state::freeze_suffix() {
if (m_suffix_frozen)
return;
m_suffix_frozen = true;
freeze_recfun();
freeze_lambda();
freeze_recfun();
auto& m = m_frozen_trail.get_manager();
ast_mark visited;
ptr_vector<expr> es;

View file

@ -51,7 +51,6 @@ class dependent_expr_state {
func_decl_ref_vector m_frozen_trail;
void freeze_prefix();
void freeze_recfun();
void freeze_lambda();
void freeze_terms(expr* term, bool only_as_array, ast_mark& visited);
void freeze(func_decl* f);
struct thaw : public trail {

View file

@ -121,7 +121,7 @@ eliminate:
elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) :
dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m), m_args(m) {
std::function<bool(expr*)> is_var = [&](expr* e) {
return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).is_root() && get_node(e).num_parents() <= 1;
return is_uninterp_const(e) && !m_fmls.frozen(e) && !m_disabled.is_marked(e) && get_node(e).is_root() && get_node(e).num_parents() <= 1;
};
m_inverter.set_is_var(is_var);
}
@ -247,10 +247,12 @@ elim_unconstrained::node& elim_unconstrained::get_node(expr* t) {
m_heap.increased(arg->get_id());
}
}
else if (is_quantifier(t)) {
node& ch = get_node(to_quantifier(t)->get_expr());
else if (is_quantifier(t)) {
auto body = to_quantifier(t)->get_expr();
node& ch = get_node(body);
SASSERT(ch.is_root());
ch.add_parent(*n);
disable(body);
}
}
return *n;
@ -411,10 +413,9 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector<
case generic_model_converter::instruction::HIDE:
break;
case generic_model_converter::instruction::ADD:
// new_def = entry.m_def;
// (*rp)(new_def);
new_def = m.mk_const(entry.m_f);
sub->insert(new_def, new_def, nullptr, nullptr);
new_def = entry.m_def;
(*rp)(new_def);
sub->insert(m.mk_const(entry.m_f), new_def, nullptr, nullptr);
break;
}
}
@ -436,6 +437,7 @@ void elim_unconstrained::reduce() {
assert_normalized(old_fmls);
update_model_trail(*mc, old_fmls);
mc->reset();
m_disabled.reset();
}
}
@ -443,3 +445,21 @@ void elim_unconstrained::updt_params(params_ref const& p) {
smt_params_helper sp(p);
m_config.m_enabled = sp.elim_unconstrained();
}
void elim_unconstrained::disable(expr* e) {
if (m_disabled.is_marked(e))
return;
ptr_buffer<expr> todo;
todo.push_back(e);
while (!todo.empty()) {
e = todo.back();
todo.pop_back();
if (m_disabled.is_marked(e))
continue;
m_disabled.mark(e);
if (is_app(e))
for (auto arg : *to_app(e))
todo.push_back(arg);
}
}

View file

@ -92,6 +92,7 @@ class elim_unconstrained : public dependent_expr_simplifier {
stats m_stats;
config m_config;
bool m_created_compound = false;
expr_mark m_disabled;
bool is_var_lt(int v1, int v2) const;
node& get_node(unsigned n) const { return *m_nodes[n]; }
@ -108,6 +109,7 @@ class elim_unconstrained : public dependent_expr_simplifier {
expr* reconstruct_term(node& n);
void assert_normalized(vector<dependent_expr>& old_fmls);
void update_model_trail(generic_model_converter& mc, vector<dependent_expr> const& old_fmls);
void disable(expr *e);
public:

View file

@ -0,0 +1,396 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
fold_unfold.h
Abstract:
fold-unfold simplifier
Author:
Nikolaj Bjorner (nbjorner) 2025-11-5.
- remove alias x = y
- remove alias with const x = k
- fold-unfold simplification x = f(y), y = g(z), f(g(z)) = u -> x |-> u
- assign levels to E-nodes:
- dfs over roots.
- visit children, assign level
-
- remove alias with linear x = f(y) -> x |-> f(y) if level y < level x
--*/
#include "ast/ast_pp.h"
#include "ast/simplifiers/fold_unfold.h"
#include "ast/rewriter/expr_replacer.h"
#include "util/union_find.h"
#include "params/smt_params_helper.hpp"
namespace euf {
fold_unfold::fold_unfold(ast_manager& m, dependent_expr_state& fmls)
: dependent_expr_simplifier(m, fmls),
m_rewriter(m),
m_egraph(m) {
register_extract_eqs(m, m_extract_plugins);
m_rewriter.set_flat_and_or(false);
// flat sum/prod := false
}
void fold_unfold::reduce() {
if (!m_config.m_enabled)
return;
m_fmls.freeze_suffix();
for (extract_eq* ex : m_extract_plugins)
ex->pre_process(m_fmls);
reduce_alias(true);
reduce_linear();
reduce_alias(false);
}
void fold_unfold::reduce_alias(bool fuf) {
m_subst = nullptr;
dep_eq_vector eqs;
get_eqs(eqs);
extract_subst(fuf, eqs);
vector<dependent_expr> old_fmls;
apply_subst(old_fmls);
}
void fold_unfold::get_eqs(dep_eq_vector& eqs) {
for (extract_eq* ex : m_extract_plugins)
for (unsigned i : indices())
ex->get_eqs(m_fmls[i], eqs);
}
void fold_unfold::extract_subst(bool fuf, dep_eq_vector const& eqs) {
m_find.reset();
for (auto const& [orig, v, t, d] : eqs) {
auto a = mk_enode(v);
auto b = mk_enode(t);
// verbose_stream() << mk_bounded_pp(v, m) << " == " << mk_bounded_pp(t, m) << "\n";
proof_ref pr(m);
auto j = to_ptr(push_pr_dep(pr, d));
m_egraph.merge(a, b, j);
}
// choose uninterpreted or value representative
auto find_rep = [&](enode *a, ptr_buffer<enode>& vars) {
enode *rep = nullptr;
for (auto b : euf::enode_class(a)) {
expr *t = b->get_expr();
if (is_uninterp_const(t))
vars.push_back(b);
if (m.is_value(t))
rep = b;
}
if (!rep) {
for (auto v : vars)
if (!rep || v->get_id() < rep->get_id())
rep = v;
}
return rep;
};
for (auto a : m_egraph.nodes()) {
if (!a->is_root())
continue;
ptr_buffer<enode> vars;
enode *rep = find_rep(a, vars);
if (!rep)
continue;
for (auto w : vars) {
if (w != rep)
m_find.setx(w->get_id(), rep, nullptr);
}
}
if (fuf) {
// find new equalities by performing fold-unfold
vector<std::tuple<enode *, expr_ref, proof_ref, expr_dependency *>> new_eqs;
for (auto n : m_egraph.nodes()) {
if (!n->is_root())
continue;
auto ne = n->get_expr();
unsigned depth = 3;
vector<std::pair<expr_ref, expr_dependency *>> es;
unfold(depth, n, nullptr, es);
// verbose_stream() << "unfolds " << es.size() << "\n";
for (auto [e, d] : es) {
expr_ref r(m);
proof_ref pr(m);
fold(e, r, pr);
if (ne == r)
continue;
new_eqs.push_back({n, r, pr, d});
}
}
for (auto const &[a, t, pr, d] : new_eqs) {
auto b = mk_enode(t);
auto j = to_ptr(push_pr_dep(pr, d));
m_egraph.merge(a, b, j);
}
}
for (auto a : m_egraph.nodes()) {
if (!a->is_root())
continue;
ptr_buffer<enode> vars;
enode *rep = find_rep(a, vars);
if (!rep)
continue;
for (auto v : vars) {
if (v == rep)
continue;
m_find.setx(v->get_id(), rep, nullptr);
// verbose_stream() << "insert " << mk_pp(v->get_expr(), m) << " " << mk_pp(rep->get_expr(), m) << "\n";
insert_subst(v->get_expr(), rep->get_expr(), explain_eq(v, rep));
m_stats.m_num_elim_vars++;
}
}
}
expr_dependency *fold_unfold::explain_eq(enode *a, enode *b) {
if (a == b)
return nullptr;
ptr_vector<size_t> just;
m_egraph.begin_explain();
m_egraph.explain_eq(just, nullptr, a, b);
m_egraph.end_explain();
expr_dependency *d = nullptr;
for (size_t *j : just)
d = m.mk_join(d, m_pr_dep[from_ptr(j)].second);
return d;
}
unsigned fold_unfold::push_pr_dep(proof *pr, expr_dependency *d) {
unsigned sz = m_pr_dep.size();
SASSERT(!m.proofs_enabled() || pr);
m_pr_dep.push_back({proof_ref(pr, m), d});
m_trail.push(push_back_vector(m_pr_dep));
return sz;
}
enode *fold_unfold::mk_enode(expr *e) {
m_todo.push_back(e);
enode *n;
while (!m_todo.empty()) {
e = m_todo.back();
if (m_egraph.find(e)) {
m_todo.pop_back();
continue;
}
if (!is_app(e)) {
m_egraph.mk(e, m_generation, 0, nullptr);
m_todo.pop_back();
continue;
}
m_args.reset();
unsigned sz = m_todo.size();
for (expr *arg : *to_app(e)) {
n = m_egraph.find(arg);
if (n)
m_args.push_back(n);
else
m_todo.push_back(arg);
}
if (sz == m_todo.size()) {
n = m_egraph.mk(e, m_generation, m_args.size(), m_args.data());
if (m_egraph.get_plugin(e->get_sort()->get_family_id()))
m_egraph.add_th_var(n, m_th_var++, e->get_sort()->get_family_id());
if (!m.is_eq(e)) {
for (auto ch : m_args)
for (auto idv : euf::enode_th_vars(*ch))
m_egraph.register_shared(n, idv.get_id());
}
m_todo.pop_back();
}
}
return m_egraph.find(e);
}
void fold_unfold::fold(expr *e, expr_ref &result, proof_ref &pr) {
m_rewriter(e, result, pr);
}
void fold_unfold::unfold(unsigned n, enode *e, expr_dependency* d, vector<std::pair<expr_ref, expr_dependency*>>& es) {
if (n == 0) {
es.push_back({expr_ref(e->get_expr(), m), d});
return;
}
if (es.size() > 10)
return;
unsigned count = 0;
for (auto sib : euf::enode_class(e)) {
auto sib_e = sib->get_expr();
if (!is_app(sib_e))
continue;
if (is_uninterp_const(sib_e)) {
auto f = m_find.get(sib->get_id(), nullptr);
if (f && f != sib)
continue;
}
++count;
expr_ref_vector args(m);
expr_dependency *d1 = m.mk_join(d, explain_eq(sib, e));
unfold_arg(n, 0, sib, args, d1, es);
if (count > 2)
break;
}
// verbose_stream() << "count " << count << "\n";
}
void fold_unfold::unfold_arg(unsigned n, unsigned i, enode* e, expr_ref_vector& args, expr_dependency* d,
vector<std::pair<expr_ref, expr_dependency*>>& es) {
if (i == e->num_args()) {
es.push_back({expr_ref(m.mk_app(e->get_decl(), args), m), d});
return;
}
vector<std::pair<expr_ref, expr_dependency *>> es_arg;
unfold(n - 1, e->get_arg(i), d, es_arg);
for (auto [arg, dep] : es_arg) {
args.push_back(arg);
unfold_arg(n, i + 1, e, args, dep, es);
args.pop_back();
if (es.size() > 10)
return;
}
}
void fold_unfold::insert_subst(expr * v, expr * t, expr_dependency* d) {
if (!m_subst)
m_subst = alloc(expr_substitution, m, true, false);
m_subst->insert(v, t, d);
}
void fold_unfold::apply_subst(vector<dependent_expr> &old_fmls) {
if (!m.inc())
return;
if (!m_subst)
return;
scoped_ptr<expr_replacer> rp = mk_default_expr_replacer(m, false);
rp->set_substitution(m_subst.get());
for (unsigned i : indices()) {
auto [f, p, d] = m_fmls[i]();
auto [new_f, new_dep] = rp->replace_with_dep(f);
proof_ref new_pr(m);
expr_ref tmp(m);
m_rewriter(new_f, tmp, new_pr);
if (tmp == f)
continue;
new_dep = m.mk_join(d, new_dep);
old_fmls.push_back(m_fmls[i]);
m_fmls.update(i, dependent_expr(m, tmp, mp(p, new_pr), new_dep));
}
m_fmls.model_trail().push(m_subst.detach(), old_fmls, false);
}
void fold_unfold::set_levels() {
m_node2level.reset();
m_level2node.reset();
m_level_count = 0;
for (auto n : m_egraph.nodes())
if (n->is_root())
set_level(n);
for (auto n : m_egraph.nodes())
if (n->is_root())
n->unmark1();
}
void fold_unfold::set_level(enode* n) {
SASSERT(n->is_root());
if (m_node2level.get(n->get_id(), UINT_MAX) != UINT_MAX)
return;
if (!n->is_marked1()) {
n->mark1();
for (auto b : enode_class(n)) {
for (auto arg : enode_args(b))
set_level(arg->get_root());
}
}
if (m_node2level.get(n->get_id(), UINT_MAX) != UINT_MAX)
return;
for (auto a : enode_class(n)) {
m_node2level.setx(a->get_id(), m_level_count, UINT_MAX);
m_level2node.setx(m_level_count, a, nullptr);
}
++m_level_count;
}
void fold_unfold::reduce_linear() {
set_levels();
m_subst = alloc(expr_substitution, m, true, false);
scoped_ptr<expr_replacer> rp = mk_default_expr_replacer(m, false);
rp->set_substitution(m_subst.get());
for (auto n : m_level2node) {
SASSERT(n);
SASSERT(n->is_root());
// if a is uninterpreted and is not eliminated,
// n is equal to a linear term with lower level argument
// back-substitute the linear term using existing subst.
// update subst with a -> linear term
enode *var = nullptr;
enode *term = nullptr;
for (auto a : enode_class(n)) {
if (m_find.get(a->get_id(), nullptr) != nullptr) // already substituted
continue;
if (is_uninterp_const(a->get_expr()))
var = a;
else if (is_linear_term(a))
term = a;
}
if (var && term) {
m_find.setx(var->get_id(), term, nullptr); // record that var was replaced
auto dep = explain_eq(var, term);
auto [new_term, new_dep] = rp->replace_with_dep(term->get_expr());
expr_ref r(m);
proof_ref pr(m);
m_rewriter(new_term, r, pr);
m_subst->insert(var->get_expr(), r, m.mk_join(dep, new_dep));
}
}
vector<dependent_expr> old_fmls;
apply_subst(old_fmls);
}
bool fold_unfold::is_linear_term(enode *n) {
unsigned num_vars = 0;
unsigned level = m_node2level[n->get_root_id()];
for (auto arg : enode_args(n))
if (!m.is_value(arg->get_expr())) {
if (m_node2level[arg->get_root_id()] >= level)
return false;
++num_vars;
}
return num_vars <= 1;
}
void fold_unfold::updt_params(params_ref const &p) {
m_config.m_enabled = true;
params_ref p1;
p1.set_bool("eliminate_mod", false);
for (auto ex : m_extract_plugins) {
ex->updt_params(p);
ex->updt_params(p1);
}
}
void fold_unfold::collect_param_descrs(param_descrs &r) {}
void fold_unfold::collect_statistics(statistics &st) const {
st.update("fold-unfold-steps", m_stats.m_num_steps);
st.update("fold-unfold-elim-vars", m_stats.m_num_elim_vars);
}
}

View file

@ -0,0 +1,108 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
fold_unfold.h
Abstract:
fold-unfold simplifier
Author:
Nikolaj Bjorner (nbjorner) 2025-11-5.
--*/
#pragma once
#include "util/scoped_ptr_vector.h"
#include "ast/expr_substitution.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/simplifiers/extract_eqs.h"
#include "ast/euf/euf_egraph.h"
namespace euf {
class fold_unfold : public dependent_expr_simplifier {
friend class solve_context_eqs;
struct stats {
unsigned m_num_steps = 0;
unsigned m_num_elim_vars = 0;
void reset() {
m_num_steps = 0;
m_num_elim_vars = 0;
}
};
struct config {
bool m_enabled = true;
};
stats m_stats;
config m_config;
th_rewriter m_rewriter;
egraph m_egraph;
scoped_ptr_vector<extract_eq> m_extract_plugins;
unsigned_vector m_var2id; // app->get_id() |-> small numeral
scoped_ptr<expr_substitution> m_subst; // current substitution
vector<std::pair<proof_ref, expr_dependency *>> m_pr_dep;
void get_eqs(dep_eq_vector &eqs);
void extract_subst(bool fuf, dep_eq_vector const &eqs);
void insert_subst(expr *v, expr *t, expr_dependency* d);
void apply_subst(vector<dependent_expr> &old_fmls);
void reduce_alias(bool fuf);
void reduce_linear();
size_t *to_ptr(size_t i) const {
return reinterpret_cast<size_t *>(i);
}
unsigned from_ptr(size_t *s) const {
return (unsigned)reinterpret_cast<size_t>(s);
}
unsigned push_pr_dep(proof *pr, expr_dependency *d);
expr_dependency *explain_eq(enode *a, enode *b);
ptr_vector<expr> m_todo;
enode_vector m_args, m_find;
unsigned_vector m_node2level;
enode_vector m_level2node;
unsigned m_level_count = 0;
void set_levels();
void set_level(enode *n);
bool is_linear_term(enode *n);
unsigned m_generation = 0;
unsigned m_th_var = 0;
enode *mk_enode(expr *e);
void fold(expr *e, expr_ref &result, proof_ref &pr);
void unfold(unsigned n, enode *e, expr_dependency* d, vector<std::pair<expr_ref, expr_dependency *>> &es);
void unfold_arg(unsigned n, unsigned i, enode *e, expr_ref_vector &args, expr_dependency *d,
vector<std::pair<expr_ref, expr_dependency *>> &es);
public:
fold_unfold(ast_manager &m, dependent_expr_state &fmls);
char const *name() const override {
return "fold-unfold";
}
void reduce() override;
void updt_params(params_ref const &p) override;
void collect_param_descrs(param_descrs &r) override;
void collect_statistics(statistics &st) const override;
void reset_statistics() override {
m_stats.reset();
}
};
} // namespace euf

View file

@ -121,7 +121,10 @@ namespace euf {
continue;
if (!m_config.m_enable_non_ground && has_quantifiers(t))
continue;
continue;
if (!m_config.m_enable_non_linear && !is_linear(t))
continue;
bool is_safe = true;
unsigned todo_sz = todo.size();
@ -241,10 +244,12 @@ namespace euf {
unsigned count = 0;
vector<dependent_expr> old_fmls;
dep_eq_vector eqs;
auto _reset_unsafe = on_scope_exit([&]() { m_unsafe_vars.reset(); });
do {
old_fmls.reset();
m_subst_ids.reset();
eqs.reset();
filter_unsafe_vars();
get_eqs(eqs);
extract_dep_graph(eqs);
extract_subst();
@ -262,6 +267,7 @@ namespace euf {
old_fmls.reset();
m_subst_ids.reset();
eqs.reset();
filter_unsafe_vars();
solve_context_eqs context_solve(*this);
context_solve.collect_nested_equalities(eqs);
extract_dep_graph(eqs);
@ -313,6 +319,15 @@ namespace euf {
return num <= m_config.m_max_occs;
}
bool solve_eqs::is_linear(expr* t) const {
unsigned num_values = 0;
if (!is_app(t))
return false;
for (auto arg : *to_app(t))
num_values += m.is_value(arg) ? 0 : 1;
return num_values <= 1;
}
void solve_eqs::save_subst(vector<dependent_expr> const& old_fmls) {
if (!m_subst->empty())
m_fmls.model_trail().push(m_subst.detach(), old_fmls, false);
@ -322,7 +337,7 @@ namespace euf {
m_unsafe_vars.reset();
recfun::util rec(m);
for (func_decl* f : rec.get_rec_funs())
for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m), &m_todo, &m_visited))
for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m)))
m_unsafe_vars.mark(term);
}
@ -342,6 +357,7 @@ namespace euf {
smt_params_helper sp(p);
m_config.m_enabled = sp.solve_eqs();
m_config.m_enable_non_ground = sp.solve_eqs_non_ground();
m_config.m_enable_non_linear = !sp.solve_eqs_linear();
}
void solve_eqs::collect_param_descrs(param_descrs& r) {

View file

@ -43,6 +43,7 @@ namespace euf {
unsigned m_max_occs = UINT_MAX;
bool m_enabled = true;
bool m_enable_non_ground = true;
bool m_enable_non_linear = true;
};
stats m_stats;
@ -74,6 +75,7 @@ namespace euf {
void collect_num_occs(expr * t, expr_fast_mark1 & visited);
void collect_num_occs();
bool check_occs(expr* t) const;
bool is_linear(expr *t) const;
public:

View file

@ -12,6 +12,7 @@ z3_add_component(cmd_context
simplifier_cmds.cpp
tactic_cmds.cpp
tactic_manager.cpp
tptp_frontend.cpp
COMPONENT_DEPENDENCIES
rewriter
solver

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
#pragma once
unsigned read_tptp(char const* file_name);
unsigned read_tptp_string(char const* input);

View file

@ -238,18 +238,13 @@ namespace lp {
r.c() -= b.c();
return r;
}
#if Z3DEBUG
friend bool operator==(const term_o& a, const term_o& b) {
friend bool eq(const term_o& a, const term_o& b) {
term_o t = a.clone();
t += mpq(-1) * b;
return t.c() == mpq(0) && t.size() == 0;
}
friend bool operator!=(const term_o& a, const term_o& b) {
return ! (a == b);
}
#endif
term_o& operator+=(const term_o& t) {
for (const auto& p : t) {
add_monomial(p.coeff(), p.j());
@ -1541,7 +1536,7 @@ namespace lp {
term_o t1 = open_ml(t0);
t1.add_monomial(mpq(1), j);
term_o rs = fix_vars(t1);
if (ls != rs) {
if (!eq(ls, rs)) {
TRACE(dio, tout << "ls:"; print_term_o(ls, tout) << "\n";
tout << "rs:"; print_term_o(rs, tout) << "\n";);
return false;
@ -2351,7 +2346,7 @@ namespace lp {
return false;
}
bool ret = ls == fix_vars(open_ml(m_l_matrix.m_rows[ei]));
bool ret = eq(ls, fix_vars(open_ml(m_l_matrix.m_rows[ei])));
if (!ret) {
CTRACE(dio, !ret,
{

View file

@ -36,7 +36,12 @@ namespace lp {
struct term_comparer {
bool operator()(const lar_term& a, const lar_term& b) const {
return a == b;
if (a.size() != b.size()) return false;
for (const auto& p : a) {
auto const* e = b.coeffs().find_core(p.j());
if (!e || e->get_data().m_value != p.coeff()) return false;
}
return true;
}
};

View file

@ -129,8 +129,8 @@ public:
add_monomial(a, v1);
add_monomial(b, v2);
}
bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms
bool operator!=(const lar_term & a) const { return ! (*this == a);}
bool operator==(const lar_term & a) const = delete; // take care not to create identical terms
bool operator!=(const lar_term & a) const = delete;
// some terms get used in add constraint
// it is the same as the offset in the m_constraints

View file

@ -312,6 +312,12 @@ namespace nla {
}
dep.mul<dep_intervals::with_deps>(product, vi, product);
}
if (do_propagate_down && c().params().arith_nl_monomial_sandwich() &&
propagate_shared_factor(m))
return true;
if (c().params().arith_nl_monomial_binomial_sign() &&
propagate_binomial_sign(m))
return true;
return do_propagate_up && propagate_value(product, m.var());
}
@ -501,11 +507,196 @@ namespace nla {
}
lpvar monomial_bounds::non_fixed_var(monic const& m) {
for (lpvar v : m)
for (lpvar v : m)
if (!c().var_is_fixed(v))
return v;
return null_lpvar;
}
/**
* Dual-row shared-factor sandwich. For a binary monomial m = u*v, find LP
* term columns whose term has shape a_m * m + a_v * v (exactly two
* variables, both factors of m). The term column's bound is a sound
* interval for (a_m * m + a_v * v). Substituting m = u*v yields
* v * (a_m * u + a_v); dividing by the interval on v (sign-determined)
* gives an interval on (a_m * u + a_v), and an affine shift gives an
* interval on u. The derived interval is fed to the existing
* propagate_value path so the lemma channel and integer rounding are
* shared with the rest of the propagation pipeline.
*/
bool monomial_bounds::propagate_shared_factor(monic const& m) {
if (m.size() != 2)
return false;
lpvar f0 = m.vars()[0], f1 = m.vars()[1];
if (f0 == f1)
return false;
unsigned const fanout_limit = c().params().arith_nl_monomial_sandwich_max_fanout();
auto try_pair = [&](lpvar u, lpvar v) -> bool {
// Skip if u participates in too many monomials: tightening such a
// factor cascades through ord-binom / monotonicity on every monic
// that contains it.
if (fanout_limit > 0) {
unsigned fanout = 0;
for (auto const& m1 : c().emons().get_use_list(u)) {
(void)m1;
if (++fanout > fanout_limit)
return false;
}
}
scoped_dep_interval vi(dep);
var2interval(v, vi);
if (!dep.separated_from_zero(vi))
return false;
auto& lra = c().lra;
unsigned const ROW_CAP = 16;
unsigned scanned = 0;
for (auto const& cell : lra.A_r().m_columns[m.var()]) {
if (++scanned > ROW_CAP)
break;
unsigned basic = lra.get_base_column_in_row(cell.var());
if (basic == m.var() || basic == v || basic == u)
continue;
if (!lra.column_has_term(basic))
continue;
auto const& term = lra.get_term(basic);
if (term.size() != 2 ||
!term.contains(m.var()) || !term.contains(v))
continue;
rational const& a_m = term.get_coeff(m.var());
rational const& a_v = term.get_coeff(v);
if (a_m.is_zero())
continue;
// Term value = a_m*m + a_v*v; bound on basic bounds the term.
// Substituting m = u*v: term = v * (a_m*u + a_v).
scoped_dep_interval bi(dep);
var2interval(basic, bi);
scoped_dep_interval inner(dep);
dep.div<dep_intervals::with_deps>(bi, vi, inner);
scoped_dep_interval shift(dep);
dep.set_value(shift, -a_v);
scoped_dep_interval scaled(dep);
dep.add<dep_intervals::with_deps>(inner, shift, scaled);
scoped_dep_interval u_int(dep);
dep.mul<dep_intervals::with_deps>(rational::one() / a_m, scaled, u_int);
TRACE(nla_solver, tout << "sandwich shared-factor basic=" << basic
<< " m=" << m.var() << " v=" << v << " u=" << u
<< " a_m=" << a_m << " a_v=" << a_v << "\n";);
if (propagate_value(u_int, u))
return true; // one lemma per call to keep the channel quiet
}
return false;
};
return try_pair(f1, f0) || try_pair(f0, f1);
}
/**
* Sign-pinned binomial bound. For a binary monomial m = u*v in m_to_refine,
* use the current LP value mv = val(m.var()) as a one-sided anchor on the
* monomial value variable, and derive a deterministic interval for u via
* sign-aware division by v.
*
* Direction is chosen by the disagreement: if val(m.var()) > val(u)*val(v)
* the LP placed the monomial above the factor product, so we condition on
* "m.var() >= mv"; otherwise on "m.var() <= mv". The resulting clause is
* structurally analogous to a propagate_value lemma plus one extra
* snapshot literal on m.var(): under the asserted bounds on v, the clause
* reduces to a 2-disjunct (snapshot literal | factor bound).
*
* Targets the case ord-binom currently handles: factors have determined
* signs, m.var() may have no LP bound at all. The clause is sound modulo
* the monomial definition (the same condition propagate_down,
* propagate_shared_factor and ord-binom rely on).
*/
bool monomial_bounds::propagate_binomial_sign(monic const& m) {
if (m.size() != 2)
return false;
lpvar f0 = m.vars()[0], f1 = m.vars()[1];
if (f0 == f1)
return false;
rational const mv = c().val(m.var());
rational const fp = c().val(f0) * c().val(f1);
if (mv == fp)
return false;
bool const below = mv > fp; // LP placed m.var() too high
llc const anchor_cmp = below ? llc::LT : llc::GT;
auto try_anchor = [&](lpvar u, lpvar v) -> bool {
// Throttle once per (m.var(), u, v, direction) tuple. Without it
// each new val(m.var()) snapshot would re-emit and the search
// would cascade across model changes the same way ord-binom does.
if (c().throttle().insert_new(
nla_throttle::MONOMIAL_BINOMIAL_SIGN,
m.var(), u, v, below))
return false;
scoped_dep_interval vi(dep);
var2interval(v, vi);
if (!dep.separated_from_zero(vi))
return false;
// Synthesize a one-sided interval for m.var() at mv. No deps;
// the snapshot literal goes into the lemma body directly.
scoped_dep_interval mi_anchor(dep);
if (below) {
dep.set_lower(mi_anchor, mv);
dep.set_lower_is_inf(mi_anchor, false);
dep.set_lower_is_open(mi_anchor, false);
dep.set_upper_is_inf(mi_anchor, true);
} else {
dep.set_upper(mi_anchor, mv);
dep.set_upper_is_inf(mi_anchor, false);
dep.set_upper_is_open(mi_anchor, false);
dep.set_lower_is_inf(mi_anchor, true);
}
scoped_dep_interval u_int(dep);
dep.div<dep_intervals::with_deps>(mi_anchor, vi, u_int);
bool emitted = false;
if (should_propagate_lower(u_int, u, 1)) {
auto const& lower = dep.lower(u_int);
if (!is_too_big(lower)) {
auto cmp = dep.lower_is_open(u_int) ? llc::GT : llc::GE;
lp::explanation ex;
dep.get_lower_dep(u_int, ex);
lemma_builder lemma(c(), "binomial sign anchor");
lemma &= ex;
lemma |= ineq(m.var(), anchor_cmp, mv);
lemma |= ineq(u, cmp, lower);
emitted = true;
}
}
if (should_propagate_upper(u_int, u, 1)) {
auto const& upper = dep.upper(u_int);
if (!is_too_big(upper)) {
auto cmp = dep.upper_is_open(u_int) ? llc::LT : llc::LE;
lp::explanation ex;
dep.get_upper_dep(u_int, ex);
lemma_builder lemma(c(), "binomial sign anchor");
lemma &= ex;
lemma |= ineq(m.var(), anchor_cmp, mv);
lemma |= ineq(u, cmp, upper);
emitted = true;
}
}
return emitted;
};
return try_anchor(f1, f0) || try_anchor(f0, f1);
}
}

View file

@ -33,6 +33,8 @@ namespace nla {
u_dependency* explain_fixed(monic const& m, rational const& k);
lp::explanation get_explanation(u_dependency* dep);
bool propagate_down(monic const& m, dep_interval& mi, lpvar v, unsigned power, dep_interval& product);
bool propagate_shared_factor(monic const& m);
bool propagate_binomial_sign(monic const& m);
void analyze_monomial(monic const& m, unsigned& num_free, lpvar& free_v, unsigned& power) const;
bool is_free(lpvar v) const;
bool is_zero(lpvar v) const;

View file

@ -215,9 +215,9 @@ public:
void deregister_monic_from_tables(const monic & m, unsigned i);
void add_monic(lpvar v, unsigned sz, lpvar const* vs);
void add_idivision(lpvar q, lpvar x, lpvar y) { m_divisions.add_idivision(q, x, y); }
void add_rdivision(lpvar q, lpvar x, lpvar y) { m_divisions.add_rdivision(q, x, y); }
void add_bounded_division(lpvar q, lpvar x, lpvar y) { m_divisions.add_bounded_division(q, x, y); }
void add_idivision(lpvar q, lpvar x, lpvar y, lpvar r) { m_divisions.add_idivision(q, x, y, r); }
void add_rdivision(lpvar q, lpvar x, lpvar y, lpvar r) { m_divisions.add_rdivision(q, x, y, r); }
void add_bounded_division(lpvar q, lpvar x, lpvar y, lpvar r) { m_divisions.add_bounded_division(q, x, y, r); }
void set_add_mul_def_hook(std::function<lpvar(unsigned, lpvar const*)> const& f) { m_add_mul_def_hook = f; }
lpvar add_mul_def(unsigned sz, lpvar const* vs) { SASSERT(m_add_mul_def_hook); lpvar v = m_add_mul_def_hook(sz, vs); add_monic(v, sz, vs); return v; }

View file

@ -18,26 +18,26 @@ Description:
namespace nla {
void divisions::add_idivision(lpvar q, lpvar x, lpvar y) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar)
void divisions::add_idivision(lpvar q, lpvar x, lpvar y, lpvar r) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar || r == null_lpvar)
return;
m_idivisions.push_back({q, x, y});
m_idivisions.push_back({q, x, y, r});
m_core.trail().push(push_back_vector(m_idivisions));
}
void divisions::add_rdivision(lpvar q, lpvar x, lpvar y) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar)
void divisions::add_rdivision(lpvar q, lpvar x, lpvar y, lpvar r) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar || r == null_lpvar)
return;
m_rdivisions.push_back({ q, x, y });
m_rdivisions.push_back({ q, x, y, r });
m_core.trail().push(push_back_vector(m_rdivisions));
}
void divisions::add_bounded_division(lpvar q, lpvar x, lpvar y) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar)
void divisions::add_bounded_division(lpvar q, lpvar x, lpvar y, lpvar r) {
if (x == null_lpvar || y == null_lpvar || q == null_lpvar || r == null_lpvar)
return;
if (m_core.lra.column_has_term(x) || m_core.lra.column_has_term(y) || m_core.lra.column_has_term(q))
return;
m_bounded_divisions.push_back({ q, x, y });
m_bounded_divisions.push_back({ q, x, y, r });
m_core.trail().push(push_back_vector(m_bounded_divisions));
}
@ -111,7 +111,7 @@ namespace nla {
return false;
};
for (auto const & [r, x, y] : m_idivisions) {
for (auto const & [r, x, y, md] : m_idivisions) {
if (!c.is_relevant(r))
continue;
auto xval = c.val(x);
@ -120,7 +120,7 @@ namespace nla {
// idiv semantics
if (!xval.is_int() || !yval.is_int() || yval == 0 || rval == div(xval, yval))
continue;
for (auto const& [q2, x2, y2] : m_idivisions) {
for (auto const& [q2, x2, y2, md2] : m_idivisions) {
if (q2 == r)
continue;
if (!c.is_relevant(q2))
@ -133,7 +133,7 @@ namespace nla {
}
}
for (auto const& [r, x, y] : m_rdivisions) {
for (auto const& [r, x, y, md] : m_rdivisions) {
if (!c.is_relevant(r))
continue;
auto xval = c.val(x);
@ -142,7 +142,7 @@ namespace nla {
// / semantics
if (yval == 0 || rval == xval / yval)
continue;
for (auto const& [q2, x2, y2] : m_rdivisions) {
for (auto const& [q2, x2, y2, md2] : m_rdivisions) {
if (q2 == r)
continue;
if (!c.is_relevant(q2))
@ -154,7 +154,8 @@ namespace nla {
return;
}
}
check_mod_mult();
}
// if p is bounded, q a value, r = eval(p):
@ -163,11 +164,11 @@ namespace nla {
void divisions::check_bounded_divisions() {
core& c = m_core;
unsigned offset = c.random(), sz = m_bounded_divisions.size();
unsigned offset = c.random(), sz = m_bounded_divisions.size();
for (unsigned j = 0; j < sz; ++j) {
unsigned i = (offset + j) % sz;
auto [q, x, y] = m_bounded_divisions[i];
auto [q, x, y, r] = m_bounded_divisions[i];
if (!c.is_relevant(q))
continue;
auto xv = c.val(x);
@ -188,9 +189,9 @@ namespace nla {
rational lo = yv * div_v;
if (xv > hi) {
lemma_builder lemma(c, "y = yv & x <= yv * div(xv, yv) + yv - 1 => div(p, y) <= div(xv, yv)");
lemma |= ineq(y, llc::NE, yv);
lemma |= ineq(x, llc::GT, hi);
lemma |= ineq(q, llc::LE, div_v);
lemma |= ineq(y, llc::NE, yv);
lemma |= ineq(x, llc::GT, hi);
lemma |= ineq(q, llc::LE, div_v);
return;
}
if (xv < lo) {
@ -201,5 +202,45 @@ namespace nla {
return;
}
}
}
}
// mod(factor, p) = 0 => mod(factor * k, p) = 0
// For each division (q, x, y, r) where x is a monic m = f1 * f2 * ... * fk,
// if some factor fi has mod(fi, p) = 0 (fixed), then mod(x, p) = 0.
void divisions::check_mod_mult() {
core& c = m_core;
unsigned offset = c.random(), sz = m_bounded_divisions.size();
for (unsigned j = 0; j < sz; ++j) {
unsigned i = (offset + j) % sz;
auto [q, x, y, r] = m_bounded_divisions[i];
if (!c.is_relevant(q))
continue;
if (c.var_is_fixed_to_zero(r))
continue;
if (c.val(r).is_zero())
continue;
if (!c.is_monic_var(x))
continue;
auto yv = c.val(y);
if (yv <= 0 || !yv.is_int())
continue;
auto const& m = c.emons()[x];
for (lpvar f : m.vars()) {
for (auto const& [q2, x2, y2, r2] : m_bounded_divisions) {
if (x2 != f)
continue;
if (c.val(y2) != yv)
continue;
if (!c.var_is_fixed_to_zero(r2))
continue;
// mod(factor, p) = 0 => mod(product, p) = 0
lemma_builder lemma(c, "mod(factor, p) = 0 => mod(factor * k, p) = 0");
lemma |= ineq(r2, llc::NE, 0);
lemma |= ineq(r, llc::EQ, 0);
return;
}
}
}
}
}

View file

@ -22,16 +22,17 @@ namespace nla {
class divisions {
core& m_core;
vector<std::tuple<lpvar, lpvar, lpvar>> m_idivisions;
vector<std::tuple<lpvar, lpvar, lpvar>> m_rdivisions;
vector<std::tuple<lpvar, lpvar, lpvar>> m_bounded_divisions;
vector<std::tuple<lpvar, lpvar, lpvar, lpvar>> m_idivisions;
vector<std::tuple<lpvar, lpvar, lpvar, lpvar>> m_rdivisions;
vector<std::tuple<lpvar, lpvar, lpvar, lpvar>> m_bounded_divisions;
public:
divisions(core& c):m_core(c) {}
void add_idivision(lpvar q, lpvar x, lpvar y);
void add_rdivision(lpvar q, lpvar x, lpvar y);
void add_bounded_division(lpvar q, lpvar x, lpvar y);
void add_idivision(lpvar q, lpvar x, lpvar y, lpvar r);
void add_rdivision(lpvar q, lpvar x, lpvar y, lpvar r);
void add_bounded_division(lpvar q, lpvar x, lpvar y, lpvar r);
void check();
void check_bounded_divisions();
void check_mod_mult();
};
}

View file

@ -10,6 +10,8 @@ Author:
Nikolaj Bjorner (nbjorner)
--*/
#include <algorithm>
#include <climits>
#include "util/uint_set.h"
#include "params/smt_params_helper.hpp"
#include "math/lp/nla_core.h"
@ -77,56 +79,75 @@ namespace nla {
if (!configure())
return;
bool productive = false;
try {
if (propagate_gcd_test())
return;
productive = true;
}
catch (...) {
}
m_solver.saturate();
TRACE(grobner, m_solver.display(tout));
if (!productive) {
m_solver.saturate();
TRACE(grobner, m_solver.display(tout));
if (m_delay_base > 0)
--m_delay_base;
try {
if (m_delay_base > 0)
--m_delay_base;
if (is_conflicting())
return;
try {
productive = is_conflicting()
|| propagate_quotients()
|| propagate_gcd_test()
|| propagate_eqs()
|| propagate_factorization()
|| propagate_linear_equations();
}
catch (...) {
if (propagate_quotients())
return;
if (propagate_gcd_test())
return;
if (propagate_eqs())
return;
if (propagate_factorization())
return;
if (propagate_linear_equations())
return;
}
catch (...) {
}
}
// DEBUG_CODE(for (auto e : m_solver.equations()) check_missing_propagation(*e););
if (c().params().arith_nl_grobner_adaptive())
update_growth_boost(productive);
if (productive)
return;
// for (auto e : m_solver.equations()) check_missing_propagation(*e);
++m_delay_base;
if (m_quota > 0)
--m_quota;
--m_quota;
IF_VERBOSE(5, verbose_stream() << "grobner miss, quota " << m_quota << "\n");
IF_VERBOSE(5, diagnose_pdd_miss(verbose_stream()));
}
void grobner::update_growth_boost(bool productive) {
// Bumping is conservative: requires two consecutive productive runs
// before any boost; misses decay toward unit by 1/4 per call.
unsigned const unit = m_config.m_adaptive_unit;
unsigned const cap = m_config.m_adaptive_max;
if (productive) {
++m_hit_streak;
if (m_hit_streak >= m_config.m_adaptive_bump_after) {
unsigned next = m_growth_boost + (m_growth_boost >> 1);
m_growth_boost = std::min(next, cap);
m_hit_streak = 0;
}
}
else {
m_hit_streak = 0;
if (m_growth_boost > unit) {
unsigned excess = m_growth_boost - unit;
m_growth_boost -= (excess + 3) / 4;
if (m_growth_boost < unit)
m_growth_boost = unit;
}
}
IF_VERBOSE(5, verbose_stream() << "grobner adaptive boost " << m_growth_boost
<< "/" << unit << (productive ? " (hit)" : " (miss)") << "\n");
}
bool grobner::is_conflicting() {
for (auto eq : m_solver.equations()) {
if (is_conflicting(*eq)) {
@ -210,8 +231,6 @@ namespace nla {
if (vars.empty() || !q.is_linear())
return false;
// IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n");
auto [t, offset] = linear_to_term(q);
vector<ineq> ineqs;
@ -226,7 +245,6 @@ namespace nla {
add_dependencies(lemma, eq);
for (auto const& i : ineqs)
lemma |= i;
//lemma.display(verbose_stream());
return true;
}
@ -368,6 +386,70 @@ namespace nla {
nl_vars.insert(j);
}
// mod_residue: derive v's residue mod M from polynomial divisibility.
//
// Common case. Given polynomial
// p = M*v1 + v - M*v2*v3 = 0,
// every monomial except v is M-divisible, so v ≡ 0 (mod M).
// Combined with 0 ≤ v < M, this forces v = 0.
// Emit: dependencies => (v < 0) (v ≥ M) (v = 0).
//
// General case. For a linear monomial c_v*v in p with c0 the constant
// term, require c_i/c_v integer for every non-v monomial and c0/c_v
// integer (call it K). Let M = gcd(|c_i/c_v|) over non-v monomials.
// Then p/c_v gives v + M*Q + K = 0 with Q integer, so v ≡ -K (mod M).
// With target = (-K) mod M ∈ [0, M-1], emit
// dependencies => (v < 0) (v ≥ M) (v = target).
for (auto const& mv : p) {
if (mv.vars.size() != 1)
continue;
lpvar vv = mv.vars[0];
if (!c().var_is_int(vv))
continue;
rational c_v = mv.coeff;
SASSERT(c_v != 0);
rational M(0); // 0 sentinel: "no non-v non-constant monomial seen yet".
rational c0(0);
bool ok = true;
for (auto const& mi : p) {
if (mi.vars.size() == 1 && mi.vars[0] == vv)
continue; // skip the mv monomial itself
if (mi.vars.empty()) {
c0 = mi.coeff;
continue;
}
rational quot = mi.coeff / c_v;
if (!quot.is_int()) { ok = false; break; }
rational a = abs(quot);
SASSERT(a != 0);
M = M == 0 ? a : gcd(M, a);
if (M == 1) { ok = false; break; } // trivial modulus, abort
}
if (!ok || M == 0)
continue;
rational K = c0 / c_v;
if (!K.is_int())
continue;
rational target = mod(-K, M); // Euclidean: result in [0, M-1].
SASSERT(target >= 0 && target < M);
// Skip if the lemma is already satisfied by the current model:
// any of (v < 0), (v ≥ M), (v = target) trivially holding means
// emission would be redundant. Without this guard, the lemma
// re-emits every Grobner round on the same polynomial.
rational v_val = c().val(vv);
if (v_val < 0 || v_val >= M || v_val == target)
continue;
lemma_builder lemma(c(), "grobner-mod-residue");
add_dependencies(lemma, eq);
lemma |= ineq(vv, llc::LT, rational::zero());
lemma |= ineq(vv, llc::GE, M);
lemma |= ineq(vv, llc::EQ, target);
TRACE(grobner, lemma.display(tout << "mod_residue v=" << vv
<< " M=" << M << " c_v=" << c_v << " c0=" << c0
<< " target=" << target << "\n"));
return true;
}
bool found_lemma = false;
for (auto v : nl_vars) {
auto& m = p.manager();
@ -559,25 +641,27 @@ namespace nla {
}
TRACE(grobner, m_solver.display(tout));
#if 0
IF_VERBOSE(2, m_pdd_grobner.display(verbose_stream()));
dd::pdd_eval eval(m_pdd_manager);
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_pdd_grobner.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (p.is_linear() && !eval(p).is_zero()) {
IF_VERBOSE(0, verbose_stream() << "violated linear constraint " << p << "\n");
}
}
#endif
struct dd::solver::config cfg;
cfg.m_max_steps = m_solver.equations().size();
cfg.m_max_simplified = c().params().arith_nl_grobner_max_simplified();
cfg.m_eqs_growth = c().params().arith_nl_grobner_eqs_growth();
cfg.m_expr_size_growth = c().params().arith_nl_grobner_expr_size_growth();
cfg.m_expr_degree_growth = c().params().arith_nl_grobner_expr_degree_growth();
if (c().params().arith_nl_grobner_adaptive() && m_growth_boost != m_config.m_adaptive_unit) {
// Wider intermediate to prevent overflow when a user param is
// close to UINT_MAX; clamp before assigning back to the unsigned
// config fields.
uint64_t const unit = m_config.m_adaptive_unit;
uint64_t const boost = m_growth_boost;
auto scale = [unit, boost](unsigned x) -> unsigned {
uint64_t y = (static_cast<uint64_t>(x) * boost) / unit;
return y > UINT_MAX ? UINT_MAX : static_cast<unsigned>(y);
};
cfg.m_eqs_growth = scale(cfg.m_eqs_growth);
cfg.m_expr_size_growth = scale(cfg.m_expr_size_growth);
cfg.m_expr_degree_growth = scale(cfg.m_expr_degree_growth);
cfg.m_max_simplified = scale(cfg.m_max_simplified);
}
cfg.m_number_of_conflicts_to_report = c().params().arith_nl_grobner_cnfl_to_report();
m_solver.set(cfg);
m_solver.adjust_cfg();
@ -588,14 +672,12 @@ namespace nla {
std::ostream& grobner::diagnose_pdd_miss(std::ostream& out) {
// m_pdd_grobner.display(out);
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_solver.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (!v.is_zero()) {
if (v != 0) {
out << p << " := " << v << "\n";
}
}
@ -701,7 +783,15 @@ namespace nla {
lp::lpvar j = c().lra.add_term(coeffs, UINT_MAX);
c().lra.update_column_type_and_bound(j, lp::lconstraint_kind::EQ, offset, e.dep());
c().m_check_feasible = true;
c().m_check_feasible = true;
TRACE(nla_solver,
// Print the term as installed (post subst_known_terms), not the
// pre-add_term coeffs vector. add_term normalizes/substitutes
// term-column references, so coeffs and the resulting row can
// diverge if any var is itself a term-column.
tout << "grobner-linear-eq: ";
c().lra.print_term(c().lra.get_term(j), tout);
tout << " = " << offset << "\n";);
return true;
}

View file

@ -24,7 +24,13 @@ namespace nla {
bool m_propagate_quotients = false;
bool m_gcd_test = false;
bool m_expand_terms = false;
// Adaptive growth (gated by arith.nl.grobner_adaptive). m_growth_boost
// is in fixed-point units of 1/m_adaptive_unit (m_adaptive_unit == 1.0x).
unsigned m_adaptive_unit = 16;
unsigned m_adaptive_max = 4 * 16;
unsigned m_adaptive_bump_after = 2;
};
config m_config;
dd::pdd_manager m_pdd_manager;
dd::solver m_solver;
lp::lar_solver& lra;
@ -32,8 +38,9 @@ namespace nla {
unsigned m_quota = 0;
unsigned m_delay_base = 0;
unsigned m_delay = 0;
unsigned m_growth_boost = m_config.m_adaptive_unit;
unsigned m_hit_streak = 0;
bool m_add_all_eqs = false;
config m_config;
std::unordered_map<unsigned_vector, lpvar, hash_svector> m_mon2var;
lp::lp_settings& lp_settings();
@ -70,6 +77,9 @@ namespace nla {
bool equation_is_true(dd::solver::equation const& eq);
// adaptive growth (gated by arith.nl.grobner_adaptive)
void update_growth_boost(bool productive);
// setup
bool configure();
void set_level2var();

View file

@ -81,9 +81,11 @@ void order::order_lemma_on_binomial(const monic& ac) {
*/
void order::order_lemma_on_binomial_sign(const monic& xy, lpvar x, lpvar y, int sign) {
if (!c().params().arith_nl_order_binomial_sign())
return;
if (!c().var_is_int(x) && val(x).is_big())
return;
SASSERT(!_().mon_has_zero(xy.vars()));
int sy = rat_sign(val(y));

View file

@ -20,16 +20,16 @@ namespace nla {
m_core->add_monic(v, sz, vs);
}
void solver::add_idivision(lpvar q, lpvar x, lpvar y) {
m_core->add_idivision(q, x, y);
void solver::add_idivision(lpvar q, lpvar x, lpvar y, lpvar r) {
m_core->add_idivision(q, x, y, r);
}
void solver::add_rdivision(lpvar q, lpvar x, lpvar y) {
m_core->add_rdivision(q, x, y);
void solver::add_rdivision(lpvar q, lpvar x, lpvar y, lpvar r) {
m_core->add_rdivision(q, x, y, r);
}
void solver::add_bounded_division(lpvar q, lpvar x, lpvar y) {
m_core->add_bounded_division(q, x, y);
void solver::add_bounded_division(lpvar q, lpvar x, lpvar y, lpvar r) {
m_core->add_bounded_division(q, x, y, r);
}
void solver::set_relevant(std::function<bool(lpvar)>& is_relevant) {

View file

@ -28,9 +28,9 @@ namespace nla {
~solver();
const auto& monics_with_changed_bounds() const { return m_core->monics_with_changed_bounds(); }
void add_monic(lpvar v, unsigned sz, lpvar const* vs);
void add_idivision(lpvar q, lpvar x, lpvar y);
void add_rdivision(lpvar q, lpvar x, lpvar y);
void add_bounded_division(lpvar q, lpvar x, lpvar y);
void add_idivision(lpvar q, lpvar x, lpvar y, lpvar r);
void add_rdivision(lpvar q, lpvar x, lpvar y, lpvar r);
void add_bounded_division(lpvar q, lpvar x, lpvar y, lpvar r);
void check_bounded_divisions();
void set_relevant(std::function<bool(lpvar)>& is_relevant);
void updt_params(params_ref const& p);

View file

@ -18,6 +18,12 @@ class tangent_imp {
rational m_correct_v;
// "below" means that the incorrect value is less than the correct one, that is m_v < m_correct_v
bool m_below;
// pl is in the strict interior of the bound box (model-driven points
// get_initial_points + push_point); McCormick at the box corners
// requires non-strict inequality because the tangent meets the surface
// along the box's edges (xy = pl.y*x + pl.x*y - pl.x*pl.y at x = pl.x
// or y = pl.y).
bool m_pl_strict_interior = true;
rational m_v; // the monomial value
lpvar m_j; // the monic variable
const monic& m_m;
@ -89,7 +95,10 @@ private:
t.add_monomial(- m_y.rat_sign()*pl.x, m_jy);
t.add_monomial(- m_x.rat_sign()*pl.y, m_jx);
t.add_var(m_j);
lemma |= ineq(t, m_below? llc::GT : llc::LT, - pl.x*pl.y);
llc cmp = m_below
? (m_pl_strict_interior ? llc::GT : llc::GE)
: (m_pl_strict_interior ? llc::LT : llc::LE);
lemma |= ineq(t, cmp, - pl.x*pl.y);
explain(lemma);
}
@ -164,14 +173,61 @@ private:
return a.x * m_xy.y + a.y * m_xy.x - a.x * a.y;
}
// McCormick at box corners: choose m_a, m_b at the corners of
// [x_lo, x_hi] x [y_lo, y_hi] that bound xy from the side dictated by
// m_below. Returns false if either factor has an unbounded side, the
// box is degenerate, or the current LP value of a factor coincides with
// a chosen corner — generate_plane's negate_relation requires
// val(j) != corner_coord (SASSERT in debug; trivially-true literal in
// release). The caller falls back to the model-driven point selection in
// these cases.
bool set_box_corners() {
if (!c().has_lower_bound(m_jx) || !c().has_upper_bound(m_jx))
return false;
if (!c().has_lower_bound(m_jy) || !c().has_upper_bound(m_jy))
return false;
rational const& x_lo = c().get_lower_bound(m_jx);
rational const& x_hi = c().get_upper_bound(m_jx);
rational const& y_lo = c().get_lower_bound(m_jy);
rational const& y_hi = c().get_upper_bound(m_jy);
if (x_lo == x_hi || y_lo == y_hi)
return false;
// negate_relation requires the model value to be strictly separated
// from the corner coordinate it's compared to. If LP currently sits
// exactly at a box edge, fall back.
rational const& vx = c().val(m_jx);
rational const& vy = c().val(m_jy);
if (vx == x_lo || vx == x_hi || vy == y_lo || vy == y_hi)
return false;
if (m_below) {
// Under-approximation: tangents at (x_lo, y_lo) and (x_hi, y_hi)
// bound xy from below across the box.
m_a = point(x_lo, y_lo);
m_b = point(x_hi, y_hi);
} else {
// Over-approximation: anti-diagonal corners.
m_a = point(x_lo, y_hi);
m_b = point(x_hi, y_lo);
}
m_pl_strict_interior = false;
return true;
}
void get_points() {
if (c().params().arith_nl_tangents_box_corners() && set_box_corners()) {
// Box corners are extremes; pushing further moves out of the box
// and would invalidate the McCormick property.
TRACE(nla_solver, tout << "xy = " << m_xy << ", box-corner points: ";
print_tangent_domain(tout) << std::endl;);
return;
}
get_initial_points();
TRACE(nla_solver, tout << "xy = " << m_xy << ", correct val = " << m_correct_v;
print_tangent_domain(tout << "\ntang points:") << std::endl;);
push_point(m_a);
push_point(m_a);
push_point(m_b);
TRACE(nla_solver,
tout << "pushed a = " << m_a << std::endl
tout << "pushed a = " << m_a << std::endl
<< "pushed b = " << m_b << std::endl
<< "tang_plane(a) = " << tang_plane(m_a) << " , val = " << m_a << ", "
<< "tang_plane(b) = " << tang_plane(m_b) << " , val = " << m_b << std::endl;);

View file

@ -18,9 +18,10 @@ class nla_throttle {
public:
enum throttle_kind {
ORDER_LEMMA, // order lemma (9 params)
BINOMIAL_SIGN_LEMMA, // binomial sign (6 params)
BINOMIAL_SIGN_LEMMA, // binomial sign (6 params)
MONOTONE_LEMMA, // monotonicity (2 params)
TANGENT_LEMMA // tangent lemma (5 params: monic_var, x_var, y_var, below, plane_type)
TANGENT_LEMMA, // tangent lemma (5 params: monic_var, x_var, y_var, below, plane_type)
MONOMIAL_BINOMIAL_SIGN // monomial binomial sign anchor (4 params: monic_var, u, v, below)
};
private:

View file

@ -41,9 +41,8 @@ namespace nla {
ineq(const lp::lar_term& term, lp::lconstraint_kind cmp, const rational& rs) : m_cmp(cmp), m_term(term), m_rs(rs) {}
ineq(lpvar v, lp::lconstraint_kind cmp, int i): m_cmp(cmp), m_term(v), m_rs(rational(i)) {}
ineq(lpvar v, lp::lconstraint_kind cmp, rational const& r): m_cmp(cmp), m_term(v), m_rs(r) {}
bool operator==(const ineq& a) const {
return m_cmp == a.m_cmp && m_term == a.m_term && m_rs == a.m_rs;
}
bool operator==(const ineq& a) const = delete;
bool operator!=(const ineq& a) const = delete;
const lp::lar_term& term() const { return m_term; };
lp::lconstraint_kind cmp() const { return m_cmp; };
const rational& rs() const { return m_rs; };

View file

@ -64,8 +64,10 @@ struct solver::imp {
m_lp2nl.reset();
}
// Create polynomial definition for variable v used in setup_assignment_solver.
// Side-effects: updates m_vars2mon when v is a monic variable.
// Create polynomial definition for variable v used in setup_solver_poly.
// The definition recursively expands monic and term variables into
// polynomials in leaf variables, scaled by an integer denominator
// tracked in `denominators` to keep the coefficients integral.
void mk_definition(unsigned v, polynomial_ref_vector &definitions, vector<rational>& denominators) {
auto &pm = m_nlsat->pm();
polynomial::polynomial_ref p(pm);
@ -100,44 +102,6 @@ struct solver::imp {
denominators.push_back(den);
}
// Create polynomial definition for variable v used in setup_assignment_solver.
// Side-effects: updates m_vars2mon when v is a monic variable.
void mk_definition_assignment(unsigned v, polynomial_ref_vector &definitions) {
auto &pm = m_nlsat->pm();
polynomial::polynomial_ref p(pm);
if (m_nla_core.emons().is_monic_var(v)) {
auto const &m = m_nla_core.emons()[v];
auto vars = m.vars();
std::sort(vars.begin(), vars.end());
m_vars2mon.insert(vars, v);
for (auto v2 : vars) {
auto pv = definitions.get(v2);
if (!p)
p = pv;
else
p = pm.mul(p, pv);
}
}
else if (lra.column_has_term(v)) {
rational den(1);
for (auto const& [w, coeff] : lra.get_term(v))
den = lcm(den, denominator(coeff));
for (auto const& [w, coeff] : lra.get_term(v)) {
auto pw = definitions.get(w);
polynomial::polynomial_ref term(pm);
term = pm.mul(den * coeff, pw);
if (!p)
p = term;
else
p = pm.add(p, term);
}
}
else {
p = pm.mk_polynomial(lp2nl(v));
}
definitions.push_back(p);
}
void setup_solver_poly() {
m_coi.init();
auto &pm = m_nlsat->pm();
@ -260,20 +224,9 @@ struct solver::imp {
out.close();
}
lbool r = l_undef;
statistics& st = m_nla_core.lp_settings().stats().m_st;
try {
r = m_nlsat->check();
}
catch (z3_exception&) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
m_nlsat->collect_statistics(st);
throw;
}
}
lbool r = m_nlsat->check();
m_nlsat->collect_statistics(st);
TRACE(nra, tout << "nra result " << r << "\n");
CTRACE(nra, false,
@ -329,6 +282,24 @@ struct solver::imp {
m_coi.init();
auto &pm = m_nlsat->pm();
polynomial_ref_vector definitions(pm);
vector<rational> denominators;
// Create an NLSAT polyvar for each LRA variable (identity mapping),
// seed the assignment from the current LRA model, populate
// m_vars2mon, and build the inlined polynomial definition of v.
//
// The definition expands monic and term variables into polynomials
// over leaf variables. Each definition is scaled by denominators[v]
// so that all coefficients stay integral; the scaling cancels on
// both sides of every constraint we build below (just like in
// setup_solver_poly).
//
// This "de-linearized" representation is what the linear-cell
// construction in NLSAT needs: a cell built around a constraint
// polynomial that mentions several multiplications at once can
// yield a lemma constraining all of them simultaneously, which is
// strictly stronger than the per-multiplication lemmas we would
// get from asserting `v_mon - v1*...*vk = 0` separately.
for (unsigned v = 0; v < lra.number_of_vars(); ++v) {
auto j = m_nlsat->mk_var(lra.var_is_int(v));
VERIFY(j == v);
@ -336,29 +307,47 @@ struct solver::imp {
scoped_anum a(am());
am().set(a, m_nla_core.val(v).to_mpq());
m_values->push_back(a);
mk_definition_assignment(v, definitions);
if (m_nla_core.emons().is_monic_var(v)) {
auto const &m = m_nla_core.emons()[v];
auto vars = m.vars();
std::sort(vars.begin(), vars.end());
m_vars2mon.insert(vars, v);
}
mk_definition(v, definitions, denominators);
}
// Substitute each variable in the LRA constraint by its definition
// and rescale to keep integer coefficients. Symbolically:
//
// v == definitions[v] / denominators[v]
//
// sum(coeff_v * v) k rhs
// == sum((coeff_v / denominators[v]) * definitions[v]) k rhs
//
// We pick den := lcm of all denominators(coeff_v / denominators[v])
// together with denominator(rhs), so that den * coeff_v / denominators[v]
// and den * rhs are all integers. The relation kind k is preserved
// because den > 0.
for (auto ci : m_coi.constraints()) {
auto &c = lra.constraints()[ci];
auto &pm = m_nlsat->pm();
auto k = c.kind();
auto rhs = c.rhs();
auto lhs = c.coeffs();
rational den = denominator(rhs);
for (auto [coeff, v] : lhs)
den = lcm(den, denominator(coeff));
den = lcm(den, denominator(coeff / denominators[v]));
polynomial::polynomial_ref p(pm);
p = pm.mk_const(-den * rhs);
for (auto [coeff, v] : lhs) {
polynomial_ref poly(pm);
poly = pm.mul(den * coeff, definitions.get(v));
poly = definitions.get(v);
poly = poly * constant(den * coeff / denominators[v]);
p = p + poly;
}
auto lit = add_constraint(p, ci, k);
m_literal2constraint.setx(lit.index(), ci, lp::null_ci);
}
definitions.reset();
}
void process_polynomial_check_assignment(polynomial::polynomial const* p, rational& bound, const u_map<lp::lpvar>& nl2lp, lp::lar_term& t) {
@ -406,25 +395,15 @@ struct solver::imp {
setup_assignment_solver();
lbool r = l_undef;
statistics &st = m_nla_core.lp_settings().stats().m_st;
nlsat::literal_vector clause;
try {
nlsat::assignment rvalues(m_nlsat->am());
for (auto [j, x] : m_lp2nl) {
scoped_anum a(am());
am().set(a, m_nla_core.val(j).to_mpq());
rvalues.set(x, a);
}
r = m_nlsat->check(rvalues, clause);
}
catch (z3_exception &) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
m_nlsat->collect_statistics(st);
throw;
}
nlsat::literal_vector clause;
nlsat::assignment rvalues(m_nlsat->am());
for (auto [j, x] : m_lp2nl) {
scoped_anum a(am());
am().set(a, m_nla_core.val(j).to_mpq());
rvalues.set(x, a);
}
r = m_nlsat->check(rvalues, clause);
m_nlsat->collect_statistics(st);
switch (r) {
case l_true:
@ -449,7 +428,6 @@ struct solver::imp {
lbool add_lemma(nlsat::literal_vector const &clause) {
u_map<lp::lpvar> nl2lp = reverse_lp2nl();
polynomial::manager &pm = m_nlsat->pm();
lbool result = l_false;
{
nla::lemma_builder lemma(m_nla_core, __FUNCTION__);
@ -657,20 +635,8 @@ struct solver::imp {
add_ub(lra.get_upper_bound(v), w, lra.get_column_upper_bound_witness(v));
}
lbool r = l_undef;
statistics& st = m_nla_core.lp_settings().stats().m_st;
try {
r = m_nlsat->check();
}
catch (z3_exception&) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
m_nlsat->collect_statistics(st);
throw;
}
}
lbool r = m_nlsat->check();
statistics &st = m_nla_core.lp_settings().stats().m_st;
m_nlsat->collect_statistics(st);
switch (r) {
@ -719,18 +685,8 @@ struct solver::imp {
add_ub(lra.get_upper_bound(v), w);
}
lbool r = l_undef;
try {
r = m_nlsat->check();
}
catch (z3_exception&) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
throw;
}
}
lbool r = m_nlsat->check();
if (r == l_true)
return r;
@ -959,19 +915,68 @@ solver::~solver() {
lbool solver::check() {
return m_imp->check();
try {
return m_imp->check();
}
catch (z3_exception &) {
statistics &st = m_imp->m_nla_core.lp_settings().stats().m_st;
m_imp->m_nlsat->collect_statistics(st);
if (m_imp->m_limit.is_canceled()) {
return l_undef;
}
else {
throw;
}
}
}
lbool solver::check(vector<dd::pdd> const& eqs) {
return m_imp->check(eqs);
try {
return m_imp->check(eqs);
}
catch (z3_exception &) {
statistics &st = m_imp->m_nla_core.lp_settings().stats().m_st;
m_imp->m_nlsat->collect_statistics(st);
if (m_imp->m_limit.is_canceled()) {
return l_undef;
}
else {
throw;
}
}
}
lbool solver::check(dd::solver::equation_vector const& eqs) {
return m_imp->check(eqs);
try {
return m_imp->check(eqs);
}
catch (z3_exception &) {
statistics &st = m_imp->m_nla_core.lp_settings().stats().m_st;
m_imp->m_nlsat->collect_statistics(st);
if (m_imp->m_limit.is_canceled()) {
return l_undef;
}
else {
throw;
}
}
}
lbool solver::check_assignment() {
return m_imp->check_assignment();
try {
return m_imp->check_assignment();
}
catch (z3_exception &) {
statistics &st = m_imp->m_nla_core.lp_settings().stats().m_st;
m_imp->m_nlsat->collect_statistics(st);
IF_VERBOSE(0, verbose_stream() << "check-assignment\n");
if (m_imp->m_limit.is_canceled()) {
return l_undef;
}
else {
throw;
}
}
}
bool solver::need_check() {

View file

@ -2028,6 +2028,20 @@ namespace algebraic_numbers {
}
IF_VERBOSE(9, verbose_stream() << "sturm 1\n");
// Check whether a can be separated from b's interval and vice versa
// this recognizes the case where the intervals overlap,
// but the anums do not lie in the intersection of the intervals.
scoped_mpq l_a(qm()), u_a(qm()), l_b(qm()), u_b(qm());
to_mpq(qm(), la, l_a);
to_mpq(qm(), ua, u_a);
to_mpq(qm(), lb, l_b);
to_mpq(qm(), ub, u_b);
if (compare(cell_a, l_b) == sign_neg) return sign_neg;
if (compare(cell_a, u_b) == sign_pos) return sign_pos;
if (compare(cell_b, l_a) == sign_neg) return sign_pos;
if (compare(cell_b, u_a) == sign_pos) return sign_neg;
//
// EXPENSIVE CASE
// Let seq be the Sturm-Tarski sequence for

View file

@ -381,7 +381,7 @@ namespace algebraic_numbers {
class anum {
class anum {
enum anum_kind { BASIC = 0, ROOT };
void* m_cell;
public:
@ -389,6 +389,17 @@ namespace algebraic_numbers {
anum(basic_cell* cell) :m_cell(TAG(void*, cell, BASIC)) { }
anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) { }
// Move nulls the source so std::sort's inner shifts stay alias-free
// if the comparator throws between moves (avoids a later double-free).
anum(anum const &) = default;
anum & operator=(anum const &) = default;
anum(anum && other) noexcept : m_cell(other.m_cell) { other.m_cell = nullptr; }
anum & operator=(anum && other) noexcept {
m_cell = other.m_cell;
other.m_cell = nullptr;
return *this;
}
bool is_basic() const { return GET_TAG(m_cell) == BASIC; }
basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); }
algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); }

View file

@ -130,19 +130,3 @@ void model_core::unregister_decl(func_decl * d) {
}
}
void model_core::add_lambda_defs() {
unsigned sz = get_num_decls();
for (unsigned i = sz; i-- > 0; ) {
func_decl* f = get_decl(i);
quantifier* q = m.is_lambda_def(f);
if (!q)
continue;
if (f->get_arity() > 0) {
func_interp* fi = alloc(func_interp, m, f->get_arity());
fi->set_else(q);
register_decl(f, fi);
}
else
register_decl(f, q);
}
}

View file

@ -58,6 +58,8 @@ public:
return eval(f, r) && m.is_false(r);
}
void add_lambda_defs();
unsigned get_num_constants() const { return m_const_decls.size(); }
unsigned get_num_functions() const { return m_func_decls.size(); }
func_decl * get_constant(unsigned i) const { return m_const_decls[i]; }
@ -72,8 +74,6 @@ public:
void unregister_decl(func_decl * d);
func_interp* update_func_interp(func_decl* f, func_interp* fi);
void add_lambda_defs();
virtual expr * get_some_value(sort * s) = 0;
virtual expr * get_fresh_value(sort * s) = 0;
virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) = 0;

View file

@ -777,7 +777,7 @@ namespace datalog {
datatype_util dt;
bv_util bv;
array_util ar;
DL_ENGINE m_engine_type;
DL_ENGINE m_engine_type = DATALOG_ENGINE;
bool is_large_bv(expr *e) {
sort *s = e->get_sort();
@ -961,7 +961,6 @@ namespace datalog {
if (get_engine() == DATALOG_ENGINE) {
m_rel = dynamic_cast<rel_context_base*>(m_engine.get());
}
}
}

View file

@ -192,7 +192,7 @@ namespace datalog {
model_converter_ref m_mc;
proof_converter_ref m_pc;
rel_context_base* m_rel;
rel_context_base* m_rel = nullptr;
scoped_ptr<engine_base> m_engine;
bool m_closed;
@ -201,7 +201,7 @@ namespace datalog {
execution_result m_last_status;
expr_ref m_last_answer;
expr_ref m_last_ground_answer;
DL_ENGINE m_engine_type;
DL_ENGINE m_engine_type = LAST_ENGINE;

View file

@ -86,6 +86,7 @@ namespace datalog {
case forall_k: m_univ = true; break;
case exists_k: m_exist = true; break;
case lambda_k: m_lambda = true; break;
case choice_k: break;
}
}
void operator()(app * n) { }

View file

@ -9,6 +9,7 @@
#include <algorithm>
#include <cstdint>
#include <numeric>
#include <set>
#include <unordered_map>
#include <utility>
@ -85,15 +86,20 @@ namespace nlsat {
unsigned ps_idx; // index in m_level_ps
root_function(anum_manager& am, poly* p, unsigned idx, anum const& v, unsigned ps_idx)
: val(am), ire{ p, idx }, ps_idx(ps_idx) { am.set(val, v); }
root_function(root_function&& other) noexcept : val(other.val.m()), ire(other.ire), ps_idx(other.ps_idx) { val = other.val; }
root_function(root_function&& other) noexcept : val(std::move(other.val)), ire(other.ire), ps_idx(other.ps_idx) { }
root_function(root_function const&) = delete;
root_function& operator=(root_function const&) = delete;
root_function& operator=(root_function&& other) noexcept {
val = other.val;
val.swap(other.val);
ire = other.ire;
ps_idx = other.ps_idx;
return *this;
}
friend void swap(root_function& a, root_function& b) noexcept {
a.val.swap(b.val);
std::swap(a.ire, b.ire);
std::swap(a.ps_idx, b.ps_idx);
}
};
// Root functions (Theta) and the chosen relation (≼) on a given level.
@ -950,12 +956,48 @@ namespace nlsat {
return m_pm.id(a.ire.p) < m_pm.id(b.ire.p);
}
// Apply a permutation to a range of root_functions using swap cycles,
// avoiding the bulk anum allocations that std::sort's move operations cause.
void apply_permutation(std_vector<root_function>& rfs, unsigned offset, std_vector<unsigned> const& perm) {
std_vector<bool> done(perm.size(), false);
for (unsigned i = 0; i < perm.size(); ++i) {
if (done[i] || perm[i] == i)
continue;
unsigned j = i;
while (!done[j]) {
done[j] = true;
unsigned k = perm[j];
if (!done[k])
swap(rfs[offset + j], rfs[offset + k]);
j = k;
}
}
}
void sort_root_function_partitions(std_vector<root_function>::iterator mid) {
auto& rfs = m_rel.m_rfunc;
std::sort(rfs.begin(), mid,
[&](root_function const& a, root_function const& b) { return root_function_lt(a, b, true); });
std::sort(mid, rfs.end(),
[&](root_function const& a, root_function const& b) { return root_function_lt(a, b, false); });
unsigned mid_pos = static_cast<unsigned>(mid - rfs.begin());
// Sort lower partition [0, mid_pos) by index permutation
if (mid_pos > 1) {
std_vector<unsigned> perm(mid_pos);
std::iota(perm.begin(), perm.end(), 0u);
std::sort(perm.begin(), perm.end(), [&](unsigned a, unsigned b) {
return root_function_lt(rfs[a], rfs[b], true);
});
apply_permutation(rfs, 0, perm);
}
// Sort upper partition [mid_pos, size) by index permutation
unsigned upper_sz = static_cast<unsigned>(rfs.size()) - mid_pos;
if (upper_sz > 1) {
std_vector<unsigned> perm(upper_sz);
std::iota(perm.begin(), perm.end(), 0u);
std::sort(perm.begin(), perm.end(), [&](unsigned a, unsigned b) {
return root_function_lt(rfs[mid_pos + a], rfs[mid_pos + b], false);
});
apply_permutation(rfs, mid_pos, perm);
}
}
// Populate Θ (root functions) around the sample, partitioned at `mid`, and sort each partition.
@ -964,6 +1006,9 @@ namespace nlsat {
init_poly_has_roots();
std_vector<root_function> lhalf, uhalf;
// Pre-reserve to reduce reallocation during emplace_back
lhalf.reserve(m_level_ps.size());
uhalf.reserve(m_level_ps.size());
if (!collect_partitioned_root_functions_around_sample(v, lhalf, uhalf))
return false;
@ -1039,6 +1084,8 @@ namespace nlsat {
void add_linear_approximations(anum const& v) {
polynomial_ref p_lower(m_pm), p_upper(m_pm);
auto& r = m_rel.m_rfunc;
// Reserve space to avoid reallocation during emplace
r.reserve(r.size() + 2);
if (m_I[m_level].is_section()) {
if (!m_am.is_rational(v)) {
NOT_IMPLEMENTED_YET();

View file

@ -133,8 +133,7 @@ class nlsat_tactic : public tactic {
return ok;
}
void operator()(goal_ref const & g,
goal_ref_buffer & result) {
void operator()(goal_ref const & g, goal_ref_buffer & result) {
tactic_report report("nlsat", *g);
if (g->is_decided()) {

View file

@ -4,4 +4,9 @@ def_module_params('smt_parallel',
params=(
('inprocessing', BOOL, False, 'integrate in-processing as a heuristic simplification'),
('sls', BOOL, False, 'add sls-tactic as a separate worker thread outside the search tree parallelism'),
))
('num_global_bb_fl_threads', UINT, 0, 'run failed-literal backbone worker threads; default is 0 (off), supported values are 1 (negative mode only) or 2 (negative and positive mode)'),
('num_global_bb_batch_threads', UINT, 0, 'run Janota-style chunking backbone worker threads; default is 0 (off), supported values are 1 (negative mode only) or 2 (negative and positive mode)'),
('local_backbones', BOOL, False, 'enable local backbones experiment within the search tree parallelism'),
('core_minimize', BOOL, True, 'minimize unsat cores used for parallel cube backtracking'),
('ablate_backtracking', BOOL, False, 'ablation: pass entire cube as core instead of unsat core during backtracking'),
))

View file

@ -27,6 +27,7 @@ void smt_params::updt_local_params(params_ref const & _p) {
m_random_seed = p.random_seed();
m_relevancy_lvl = p.relevancy();
m_ematching = p.ematching();
m_ho_matching = p.ho_matching();
m_induction = p.induction();
m_clause_proof = p.clause_proof();
m_phase_selection = static_cast<phase_selection>(p.phase_selection());

View file

@ -109,6 +109,7 @@ struct smt_params : public preprocessor_params,
bool m_display_features = false;
bool m_new_core2th_eq = true;
bool m_ematching = true;
bool m_ho_matching = false;
bool m_induction = false;
bool m_clause_proof = false;
symbol m_proof_log;

View file

@ -10,6 +10,7 @@ def_module_params(module_name='smt',
('quasi_macros', BOOL, False, 'try to find universally quantified formulas that are quasi-macros'),
('restricted_quasi_macros', BOOL, False, 'try to find universally quantified formulas that are restricted quasi-macros'),
('ematching', BOOL, True, 'E-Matching based quantifier instantiation'),
('ho_matching', BOOL, False, 'higher-order matching for quantifier instantiation'),
('phase_selection', UINT, 3, 'phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences, 7 - theory'),
('phase_caching_on', UINT, 400, 'number of conflicts while phase caching is on'),
('phase_caching_off', UINT, 100, 'number of conflicts while phase caching is off'),
@ -21,6 +22,7 @@ def_module_params(module_name='smt',
('elim_unconstrained', BOOL, True, 'pre-processing: eliminate unconstrained subterms'),
('solve_eqs', BOOL, True, 'pre-processing: solve equalities'),
('solve_eqs.non_ground', BOOL, True, 'pre-processing: solve equalities. Allow eliminating variables by non-ground solutions which can break behavior for model evaluation.'),
('solve_eqs.linear', BOOL, False, 'allow only linear substitutions where a variable is replaced by a term having at most one non-constant argument'),
('propagate_values', BOOL, True, 'pre-processing: propagate values'),
('bound_simplifier', BOOL, True, 'apply bounds simplification during pre-processing'),
('pull_nested_quantifiers', BOOL, False, 'pre-processing: pull nested quantifiers'),
@ -66,8 +68,10 @@ def_module_params(module_name='smt',
('arith.nl.expensive_patching', BOOL, False, 'use the expensive of monomials'),
('arith.nl.rounds', UINT, 1024, 'threshold for number of (nested) final checks for non linear arithmetic, relevant only if smt.arith.solver=2'),
('arith.nl.order', BOOL, True, 'run order lemmas'),
('arith.nl.order.binomial_sign', BOOL, True, 'run order_lemma_on_binomial_sign; disabling it keeps the structural order-lemma splitting'),
('arith.nl.expp', BOOL, False, 'expensive patching'),
('arith.nl.tangents', BOOL, True, 'run tangent lemmas'),
('arith.nl.tangents.box_corners', BOOL, False, 'choose tangent-plane points at the bound-box corners instead of the model-centered val(x) +/- delta; produces the McCormick under/over envelope and is deterministic and snapshot-independent'),
('arith.nl.horner', BOOL, True, 'run horner\'s heuristic'),
('arith.nl.horner_subs_fixed', UINT, 2, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.horner_frequency', UINT, 4, 'horner\'s call frequency'),
@ -83,9 +87,13 @@ def_module_params(module_name='smt',
('arith.nl.grobner_propagate_quotients', BOOL, True, 'detect conflicts x*y + z = 0 where x doesn\'t divide z'),
('arith.nl.grobner_gcd_test', BOOL, True, 'detect gcd conflicts for polynomial powers x^k - y = 0'),
('arith.nl.grobner_exp_delay', BOOL, True, 'use exponential delay between grobner basis attempts'),
('arith.nl.grobner_adaptive', BOOL, False, 'scale grobner growth knobs (eqs/size/degree/max_simplified) up on productive runs and down on misses'),
('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'),
('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.grobner_expand_terms', BOOL, True, 'expand terms before computing grobner basis'),
('arith.nl.monomial_sandwich', BOOL, False, 'derive bound on a monomial factor by pairing two LP rows that share the other factor'),
('arith.nl.monomial_sandwich.max_fanout', UINT, 0, 'skip monomial sandwich when the conclusion factor appears in more than this many monomials (0 = no limit)'),
('arith.nl.monomial_binomial_sign', BOOL, False, 'derive bound on a binomial-monomial factor anchored on the current LP value of the monomial; replaces order_lemma_on_binomial_sign with a deterministic factor bound conditioned on a one-sided snapshot of the monomial value'),
('arith.nl.reduce_pseudo_linear', BOOL, True, 'create incremental linearization axioms for pseudo-linear monomials'),
('arith.nl.delay', UINT, 10, 'number of calls to final check before invoking bounded nlsat check'),
('arith.nl.propagate_linear_monomials', BOOL, True, 'propagate linear monomials'),

View file

@ -21,6 +21,7 @@ Revision History:
#include "ast/bv_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/ast_pp.h"
#include "ast/well_sorted.h"
#include "ast/rewriter/rewriter.h"
@ -79,6 +80,7 @@ namespace smt2 {
symbol m_forall;
symbol m_exists;
symbol m_lambda;
symbol m_choice;
symbol m_as;
symbol m_not;
symbol m_root_obj;
@ -156,12 +158,14 @@ namespace smt2 {
unsigned m_expr_spos;
unsigned m_param_spos;
bool m_as_sort;
app_frame(symbol const & f, unsigned expr_spos, unsigned param_spos, bool as_sort):
bool m_expr_head;
app_frame(symbol const & f, unsigned expr_spos, unsigned param_spos, bool as_sort, bool expr_head = false):
expr_frame(EF_APP),
m_f(f),
m_expr_spos(expr_spos),
m_param_spos(param_spos),
m_as_sort(as_sort) {}
m_as_sort(as_sort),
m_expr_head(expr_head) {}
};
struct quant_frame : public expr_frame {
@ -420,6 +424,11 @@ namespace smt2 {
bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; }
bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; }
bool curr_id_is_lambda() const { SASSERT(curr_is_identifier()); return curr_id() == m_lambda; }
bool curr_id_is_choice() const {
SASSERT(curr_is_identifier());
return curr_id() == m_choice;
}
bool curr_id_is_bang() const { SASSERT(curr_is_identifier()); return curr_id() == m_bang; }
bool curr_id_is_let() const { SASSERT(curr_is_identifier()); return curr_id() == m_let; }
bool curr_id_is_root_obj() const { SASSERT(curr_is_identifier()); return curr_id() == m_root_obj; }
@ -1354,10 +1363,11 @@ namespace smt2 {
void push_quant_frame(quantifier_kind k) {
SASSERT(curr_is_identifier());
SASSERT(curr_id_is_forall() || curr_id_is_exists() || curr_id_is_lambda());
SASSERT(curr_id_is_forall() || curr_id_is_exists() || curr_id_is_lambda() || curr_id_is_choice());
SASSERT((k == forall_k) == curr_id_is_forall());
SASSERT((k == exists_k) == curr_id_is_exists());
SASSERT((k == lambda_k) == curr_id_is_lambda());
SASSERT((k == choice_k) == curr_id_is_choice());
next();
void * mem = m_stack.allocate(sizeof(quant_frame));
new (mem) quant_frame(k, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(),
@ -1888,23 +1898,7 @@ namespace smt2 {
sexpr_stack().pop_back();
}
void push_app_frame() {
SASSERT(curr_is_lparen() || curr_is_identifier());
unsigned param_spos = m_param_stack.size();
unsigned expr_spos = expr_stack().size();
bool has_as, is_lambda;
auto f = parse_qualified_identifier(has_as, is_lambda);
void * mem = m_stack.allocate(sizeof(app_frame));
new (mem) app_frame(f, expr_spos, param_spos, has_as);
m_num_expr_frames++;
if (is_lambda)
push_quant_frame(lambda_k);
}
void push_expr_frame(expr_frame * curr) {
SASSERT(curr_is_lparen());
next();
void push_expr_frame_core(expr_frame * curr) {
TRACE(push_expr_frame, tout << "push_expr_frame(), curr(): " << m_curr << "\n";);
if (curr_is_identifier()) {
TRACE(push_expr_frame, tout << "push_expr_frame(), curr_id(): " << curr_id() << "\n";);
@ -1920,6 +1914,9 @@ namespace smt2 {
else if (curr_id_is_lambda()) {
push_quant_frame(lambda_k);
}
else if (curr_id_is_choice()) {
push_quant_frame(choice_k);
}
else if (curr_id_is_bang()) {
push_bang_frame(curr);
}
@ -1944,6 +1941,49 @@ namespace smt2 {
}
}
void push_app_frame() {
SASSERT(curr_is_lparen() || curr_is_identifier());
unsigned param_spos = m_param_stack.size();
unsigned expr_spos = expr_stack().size();
bool has_as = false, is_lambda = false;
symbol f = symbol::null;
bool expr_head = false;
if (curr_is_lparen()) {
next();
if (curr_is_identifier() && curr_id_is_lambda()) {
is_lambda = true;
f = symbol("select");
}
else if (curr_is_identifier() && (curr_id_is_underscore() || curr_id_is_as())) {
f = parse_qualified_identifier_core(has_as);
}
else {
expr_head = true;
}
}
else {
f = parse_qualified_identifier(has_as, is_lambda);
}
void * mem = m_stack.allocate(sizeof(app_frame));
auto* frame = new (mem) app_frame(f, expr_spos, param_spos, has_as, expr_head);
m_num_expr_frames++;
if (is_lambda) {
push_quant_frame(lambda_k);
}
else if (expr_head) {
push_expr_frame_core(frame);
}
}
void push_expr_frame(expr_frame * curr) {
SASSERT(curr_is_lparen());
next();
push_expr_frame_core(curr);
}
void pop_app_frame(app_frame * fr) {
SASSERT(expr_stack().size() >= fr->m_expr_spos);
SASSERT(m_param_stack.size() >= fr->m_param_spos);
@ -1952,15 +1992,15 @@ namespace smt2 {
unsigned num_args = expr_stack().size() - fr->m_expr_spos;
unsigned num_indices = m_param_stack.size() - fr->m_param_spos;
expr_ref t_ref(m());
local l;
if (m_env.find(fr->m_f, l)) {
push_local(l);
t_ref = expr_stack().back();
for (unsigned i = 0; i < num_args; ++i) {
if (fr->m_expr_head) {
if (num_args < 2)
throw parser_exception("invalid function application, arguments missing");
t_ref = expr_stack().get(fr->m_expr_spos);
for (unsigned i = 1; i < num_args; ++i) {
expr* arg = expr_stack().get(fr->m_expr_spos + i);
expr* args[2] = { t_ref.get(), arg };
m_ctx.mk_app(symbol("select"),
2,
m_ctx.mk_app(symbol("select"),
2,
args,
0,
nullptr,
@ -1969,13 +2009,31 @@ namespace smt2 {
}
}
else {
m_ctx.mk_app(fr->m_f,
num_args,
expr_stack().data() + fr->m_expr_spos,
num_indices,
m_param_stack.data() + fr->m_param_spos,
fr->m_as_sort ? sort_stack().back() : nullptr,
t_ref);
local l;
if (m_env.find(fr->m_f, l)) {
push_local(l);
t_ref = expr_stack().back();
for (unsigned i = 0; i < num_args; ++i) {
expr* arg = expr_stack().get(fr->m_expr_spos + i);
expr* args[2] = { t_ref.get(), arg };
m_ctx.mk_app(symbol("select"),
2,
args,
0,
nullptr,
nullptr,
t_ref);
}
}
else {
m_ctx.mk_app(fr->m_f,
num_args,
expr_stack().data() + fr->m_expr_spos,
num_indices,
m_param_stack.data() + fr->m_param_spos,
fr->m_as_sort ? sort_stack().back() : nullptr,
t_ref);
}
}
expr_stack().shrink(fr->m_expr_spos);
m_param_stack.shrink(fr->m_param_spos);
@ -2061,7 +2119,7 @@ namespace smt2 {
fr->m_qid = symbol((unsigned)m_scanner.get_line());
if (fr->m_kind != lambda_k && !m().is_bool(expr_stack().back()))
throw parser_exception("quantifier body must be a Boolean expression");
quantifier* new_q = m().mk_quantifier(fr->m_kind,
quantifier* new_q = m().mk_quantifier(fr->m_kind == choice_k ? lambda_k : fr->m_kind,
num_decls,
sort_stack().data() + fr->m_sort_spos,
symbol_stack().data() + fr->m_sym_spos,
@ -2082,8 +2140,11 @@ namespace smt2 {
m_env.end_scope();
SASSERT(num_decls <= m_num_bindings);
m_num_bindings -= num_decls;
expr_stack().push_back(new_q);
if (fr->m_kind == choice_k) {
expr_stack().push_back(array_util(m()).mk_choice(new_q));
}
else
expr_stack().push_back(new_q);
m_stack.deallocate(fr);
m_num_expr_frames--;
}
@ -3088,6 +3149,7 @@ namespace smt2 {
m_forall("forall"),
m_exists("exists"),
m_lambda("lambda"),
m_choice("choice"),
m_as("as"),
m_not("not"),
m_root_obj("root-obj"),
@ -3296,5 +3358,3 @@ sexpr_ref parse_sexpr(cmd_context& ctx, std::istream& is, params_ref const& ps,
return p.parse_sexpr_ref();
}

View file

@ -163,6 +163,24 @@ struct mbp_dt_tg::impl {
if (is_app(term) &&
m_dt_util.is_accessor(to_app(term)->get_decl()) &&
has_var(to_app(term)->get_arg(0))) {
// Only apply rm_accessor if the model confirms the argument
// has the constructor that this accessor belongs to.
// Otherwise we introduce a contradictory is-cons literal.
func_decl *cons =
m_dt_util.get_accessor_constructor(to_app(term)->get_decl());
func_decl *rec = m_dt_util.get_constructor_recognizer(cons);
expr_ref is_rec(m.mk_app(rec, to_app(term)->get_arg(0)), m);
if (!m_mdl.is_true(is_rec)) {
// Ground the argument so the accessor term becomes
// constructively ground. This preserves any enclosing
// literal (e.g., (not (is-nil (tl nil)))) as a guard in
// the output, preventing an over-approximation.
expr_ref is(m.mk_not(is_rec), m);
m_tg.add_lit(is);
mark_seen(term);
progress = true;
continue;
}
mark_seen(term);
progress = true;
rm_accessor(term);

View file

@ -192,7 +192,7 @@ namespace sat {
return false;
}
binary b(~y, x, nullptr);
if (!binaries.find(b, b)) {
if (!binaries.find(b, b) || !b.use_list) {
return false;
}
for (auto p : *b.use_list) {

View file

@ -27,6 +27,7 @@ Notes:
#include "solver/tactic2solver.h"
#include "solver/parallel_params.hpp"
#include "solver/parallel_tactical.h"
#include "solver/parallel_tactical2.h"
#include "tactic/tactical.h"
#include "tactic/aig/aig_tactic.h"
#include "tactic/core/propagate_values_tactic.h"
@ -1183,5 +1184,9 @@ void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* sof
tactic * mk_psat_tactic(ast_manager& m, params_ref const& p) {
parallel_params pp(p);
return pp.enable() ? mk_parallel_tactic(mk_inc_sat_solver(m, p, false), p) : mk_sat_tactic(m);
if (pp.enable())
return mk_parallel_tactic(mk_inc_sat_solver(m, p, false), p);
if (pp.enable2())
return mk_parallel_tactic2(mk_inc_sat_solver(m, p, false), p);
return mk_sat_tactic(m);
}

View file

@ -68,6 +68,8 @@ namespace array {
return assert_extensionality(r.n->get_expr(), r.select->get_expr());
case axiom_record::kind_t::is_congruence:
return assert_congruent_axiom(r.n->get_expr(), r.select->get_expr());
case axiom_record::kind_t::is_choice:
return assert_choice_axiom(r.n->get_app());
default:
UNREACHABLE();
break;
@ -469,6 +471,27 @@ namespace array {
return ctx.propagate(e_internalize(alpha), e_internalize(beta), array_axiom());
}
bool solver::assert_choice_axiom(app* choice_term) {
++m_stats.m_num_choice_axiom;
SASSERT(a.is_choice(choice_term));
expr* pred = choice_term->get_arg(0);
sort* pred_sort = pred->get_sort();
SASSERT(a.is_array(pred_sort));
SASSERT(get_array_arity(pred_sort) == 1);
SASSERT(m.is_bool(get_array_range(pred_sort)));
sort* x_sort = get_array_domain(pred_sort, 0);
expr_ref x(m.mk_var(0, x_sort), m);
expr* args1[2] = { pred, x };
expr_ref px(a.mk_select(2, args1), m);
expr* args2[2] = { pred, choice_term };
expr_ref pc(a.mk_select(2, args2), m);
expr_ref body(m.mk_implies(px, pc), m);
symbol x_name("x");
expr_ref q(m.mk_forall(1, &x_sort, &x_name, body), m);
rewrite(q);
return add_unit(mk_literal(q));
}
/**
\brief assert n1 = n2 => forall vars . (n1 vars) = (n2 vars)
*/
@ -691,4 +714,3 @@ namespace array {
}
}

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