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:
commit
043c6c0ad1
259 changed files with 18907 additions and 3725 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
43
src/api/go/char.go
Normal 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))
|
||||
}
|
||||
|
|
@ -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 = ¶mPtrs[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))
|
||||
|
|
|
|||
|
|
@ -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
38
src/api/go/relations.go
Normal 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
77
src/api/go/set.go
Normal 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
131
src/api/go/spacer.go
Normal 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))
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
254
src/api/go/z3.go
254
src/api/go/z3.go
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
7
src/api/js/package-lock.json
generated
7
src/api/js/package-lock.json
generated
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
43
src/api/js/src/browser.test.ts
Normal file
43
src/api/js/src/browser.test.ts
Normal 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',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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']>>;
|
||||
|
|
|
|||
37
src/api/js/src/node.test.ts
Normal file
37
src/api/js/src/node.test.ts
Normal 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 });
|
||||
});
|
||||
});
|
||||
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, ¶m, 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:
|
|||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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()); \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ struct enum2bv_rewriter::imp {
|
|||
new_body_ref = mk_and(bounds);
|
||||
break;
|
||||
case lambda_k:
|
||||
case choice_k:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
396
src/ast/simplifiers/fold_unfold.cpp
Normal file
396
src/ast/simplifiers/fold_unfold.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
108
src/ast/simplifiers/fold_unfold.h
Normal file
108
src/ast/simplifiers/fold_unfold.h
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
2258
src/cmd_context/tptp_frontend.cpp
Normal file
2258
src/cmd_context/tptp_frontend.cpp
Normal file
File diff suppressed because it is too large
Load diff
4
src/cmd_context/tptp_frontend.h
Normal file
4
src/cmd_context/tptp_frontend.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
unsigned read_tptp(char const* file_name);
|
||||
unsigned read_tptp_string(char const* input);
|
||||
|
|
@ -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,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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; };
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue