diff --git a/Makefile.build b/Makefile.build index 1a9e651e0..f046a1953 100644 --- a/Makefile.build +++ b/Makefile.build @@ -89,7 +89,7 @@ srcsubdirs = mt io terms utils solvers solvers/floyd_warshall \ solvers/funs solvers/bv solvers/egraph solvers/cdcl solvers/simplex solvers/quant \ parser_utils model api frontend frontend/common \ frontend/smt1 frontend/yices frontend/smt2 context exists_forall \ - mcsat mcsat/eq mcsat/weq mcsat/uf mcsat/bool mcsat/ite mcsat/nra mcsat/bv \ + mcsat mcsat/eq mcsat/weq mcsat/uf mcsat/bool mcsat/ite mcsat/nra mcsat/ff mcsat/bv \ mcsat/bv/explain mcsat/utils testdir = tests/unit diff --git a/src/Makefile b/src/Makefile index 267a96287..c509aaa2b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -349,12 +349,16 @@ mcsat_src_c := \ mcsat/model.c \ mcsat/trail.c \ mcsat/conflict.c \ + mcsat/unit_info.c \ mcsat/gc.c \ mcsat/eq/equality_graph.c \ mcsat/eq/merge_queue.c \ mcsat/weq/weak_eq_graph.c \ mcsat/utils/int_mset.c \ mcsat/utils/int_lset.c \ + mcsat/utils/lp_data.c \ + mcsat/utils/lp_utils.c \ + mcsat/utils/lp_constraint_db.c \ mcsat/utils/value_hash_map.c \ mcsat/utils/value_vector.c \ mcsat/utils/scope_holder.c \ @@ -368,9 +372,13 @@ mcsat_src_c := \ mcsat/nra/nra_plugin.c \ mcsat/nra/nra_plugin_internal.c \ mcsat/nra/nra_plugin_explain.c \ - mcsat/nra/libpoly_utils.c \ - mcsat/nra/poly_constraint.c \ + mcsat/nra/nra_libpoly.c \ mcsat/nra/feasible_set_db.c \ + mcsat/ff/ff_plugin.c \ + mcsat/ff/ff_plugin_internal.c \ + mcsat/ff/ff_plugin_explain.c \ + mcsat/ff/ff_libpoly.c \ + mcsat/ff/ff_feasible_set_db.c \ mcsat/ite/ite_plugin.c \ mcsat/bv/bv_plugin.c \ mcsat/bv/bv_bdd_manager.c \ diff --git a/src/api/context_config.c b/src/api/context_config.c index 17c352e81..dcf88892b 100644 --- a/src/api/context_config.c +++ b/src/api/context_config.c @@ -140,6 +140,7 @@ static const int32_t logic2arch[NUM_SMT_LOGICS] = { -1, // AX -1, // BV (supported by EF) + -1, // FFA -1, // IDL (supported by EF) -1, // LIA (supported by EF) -1, // LRA (supported by EF) @@ -180,6 +181,7 @@ static const int32_t logic2arch[NUM_SMT_LOGICS] = { CTX_ARCH_EGFUN, // QF_AX CTX_ARCH_BV, // QF_BV + CTX_ARCH_MCSAT, // QF_FFA CTX_ARCH_SPLX, // QF_IDL CTX_ARCH_SPLX, // QF_LIA CTX_ARCH_SPLX, // QF_LRA @@ -236,6 +238,7 @@ static const bool fragment2iflag[NUM_ARITH_FRAGMENTS+1] = { true, // NIA false, // NRA true, // NIRA + false, // FFA false, // no arithmetic }; diff --git a/src/api/smt_logic_codes.c b/src/api/smt_logic_codes.c index 0ded412d7..fb2f744cf 100644 --- a/src/api/smt_logic_codes.c +++ b/src/api/smt_logic_codes.c @@ -54,6 +54,7 @@ static const char * const smt_logic_names[NUM_SMT_LOGIC_NAMES] = { "AX", "BV", "BVLRA", + "FFA", "IDL", "LIA", "LIRA", @@ -82,6 +83,7 @@ static const char * const smt_logic_names[NUM_SMT_LOGIC_NAMES] = { "QF_AX", "QF_BV", "QF_BVLRA", + "QF_FFA", "QF_IDL", "QF_LIA", "QF_LIRA", @@ -142,6 +144,7 @@ static const smt_logic_t smt_code[NUM_SMT_LOGIC_NAMES] = { AUFNRA, AX, BV, + FFA, BVLRA, IDL, LIA, @@ -171,6 +174,7 @@ static const smt_logic_t smt_code[NUM_SMT_LOGIC_NAMES] = { QF_AX, QF_BV, QF_BVLRA, + QF_FFA, QF_IDL, QF_LIA, QF_LIRA, @@ -237,6 +241,7 @@ smt_logic_t smt_logic_code(const char *logic_name) { */ static const char * const fragment_names[NUM_ARITH_FRAGMENTS] = { "IDL", + "FFA", "LIA", "LIRA", "LRA", @@ -248,6 +253,7 @@ static const char * const fragment_names[NUM_ARITH_FRAGMENTS] = { static const arith_fragment_t fragment_code[NUM_ARITH_FRAGMENTS] = { ARITH_IDL, + ARITH_FFA, ARITH_LIA, ARITH_LIRA, ARITH_LRA, @@ -290,6 +296,7 @@ static const uint8_t has_arrays[NUM_SMT_LOGICS] = { true, // AX false, // BV + false, // FFA false, // IDL false, // LIA false, // LRA @@ -330,6 +337,7 @@ static const uint8_t has_arrays[NUM_SMT_LOGICS] = { true, // QF_AX false, // QF_BV + false, // QF_FFA false, // QF_IDL false, // QF_LIA false, // QF_LRA @@ -376,6 +384,7 @@ static const uint8_t has_bv[NUM_SMT_LOGICS] = { false, // AX true, // BV + false, // FFA false, // IDL false, // LIA false, // LRA @@ -416,6 +425,7 @@ static const uint8_t has_bv[NUM_SMT_LOGICS] = { false, // QF_AX true, // QF_BV + false, // QF_FFA false, // QF_IDL false, // QF_LIA false, // QF_LRA @@ -462,6 +472,7 @@ static const uint8_t has_quantifiers[NUM_SMT_LOGICS] = { true, // AX true, // BV + true, // FFA true, // IDL true, // LIA true, // LRA @@ -502,6 +513,7 @@ static const uint8_t has_quantifiers[NUM_SMT_LOGICS] = { false, // QF_AX false, // QF_BV + false, // QF_FFA false, // QF_IDL false, // QF_LIA false, // QF_LRA @@ -548,6 +560,7 @@ static const uint8_t has_uf[NUM_SMT_LOGICS] = { false, // AX false, // BV + false, // FFA false, // IDL false, // LIA false, // LRA @@ -588,6 +601,7 @@ static const uint8_t has_uf[NUM_SMT_LOGICS] = { false, // QF_AX false, // QF_BV + false, // QF_FFA false, // QF_IDL false, // QF_LIA false, // QF_LRA @@ -634,6 +648,7 @@ static const uint8_t arith_frag[NUM_SMT_LOGICS] = { ARITH_NONE, // AX ARITH_NONE, // BV + ARITH_FFA, // FFA ARITH_IDL, // IDL ARITH_LIA, // LIA ARITH_LRA, // LRA @@ -674,6 +689,7 @@ static const uint8_t arith_frag[NUM_SMT_LOGICS] = { ARITH_NONE, // QF_AX ARITH_NONE, // QF_BV + ARITH_FFA, // QF_FFA ARITH_IDL, // QF_IDL ARITH_LIA, // QF_LIA ARITH_LRA, // QF_LRA @@ -761,6 +777,7 @@ static const smt_logic_t logic2qf[NUM_SMT_LOGICS] = { */ QF_AX, QF_BV, + QF_FFA, QF_IDL, QF_LIA, QF_LRA, @@ -804,6 +821,7 @@ static const smt_logic_t logic2qf[NUM_SMT_LOGICS] = { */ QF_AX, QF_BV, + QF_FFA, QF_IDL, QF_LIA, QF_LRA, @@ -863,6 +881,7 @@ static const bool is_official[NUM_SMT_LOGICS] = { false, // AX true, // BV + false, // FFA false, // IDL true, // LIA true, // LRA @@ -903,6 +922,7 @@ static const bool is_official[NUM_SMT_LOGICS] = { true, // QF_AX true, // QF_BV + false, // QF_FFA true, // QF_IDL true, // QF_LIA true, // QF_LRA diff --git a/src/api/smt_logic_codes.h b/src/api/smt_logic_codes.h index d57b59282..fd51579e3 100644 --- a/src/api/smt_logic_codes.h +++ b/src/api/smt_logic_codes.h @@ -67,6 +67,7 @@ typedef enum smt_logic { */ AX, // arrays BV, // bitvectors + FFA, // finite fields IDL, // integer difference logic LIA, // linear integer arithmetic LRA, // linear real arithmetic @@ -118,6 +119,7 @@ typedef enum smt_logic { */ QF_AX, // arrays QF_BV, // bitvectors + QF_FFA, // finite fields QF_IDL, // integer difference logic QF_LIA, // linear integer arithmetic QF_LRA, // linear real arithmetic @@ -195,6 +197,7 @@ typedef enum arith_fragment { ARITH_NIA, // non-linear integer arithmetic ARITH_NRA, // non-linear real arithmetic ARITH_NIRA, // non-linear mixed arithmetic + ARITH_FFA, // finite field arithmetic ARITH_NONE, // no arithmetic } arith_fragment_t; diff --git a/src/api/yices_api.c b/src/api/yices_api.c index 36fb65534..9ca060b83 100644 --- a/src/api/yices_api.c +++ b/src/api/yices_api.c @@ -1577,6 +1577,17 @@ static inline void type_vector_push(type_vector_t *v, type_t tau) { * Otherwise they return false and set the error code and diagnostic data. */ +// Check whether n (as mpz) is positive +static bool check_positive_mpz(mpz_t n) { + if (mpz_sgn(n) != 1) { + error_report_t *error = get_yices_error(); + error->code = POS_INT_REQUIRED; + error->badval = mpz_fits_slong_p(n) ? mpz_get_si(n) : -1; + return false; + } + return true; +} + // Check whether n is positive static bool check_positive(uint32_t n) { if (n == 0) { @@ -1912,6 +1923,21 @@ static bool check_integer_term(term_manager_t *mngr, term_t t) { } #endif +// Check whether t is a finitefield term, t must be valid. +static bool check_arith_ff_term(term_manager_t *mngr, term_t t) { + term_table_t *tbl; + + tbl = term_manager_get_terms(mngr); + + if (! is_finitefield_term(tbl, t)) { + error_report_t *error = get_yices_error(); + error->code = ARITHTERM_REQUIRED; + error->term1 = t; + return false; + } + return true; +} + // Check whether t is a real term, t must be valid. static bool check_real_term(term_manager_t *mngr, term_t t) { term_table_t *tbl; @@ -2740,6 +2766,7 @@ static bool check_elim_vars(term_manager_t *mngr, uint32_t n, const term_t *var) case BOOL_TYPE: case INT_TYPE: case REAL_TYPE: + case FF_TYPE: case BITVECTOR_TYPE: case SCALAR_TYPE: break; @@ -2840,6 +2867,16 @@ type_t _o_yices_bv_type(uint32_t size) { return bv_type(__yices_globals.types, size); } +type_t _o_yices_ff_type(mpz_t order) { + if (! check_positive_mpz(order)) { + return NULL_TYPE; + } + if (! mpz_probab_prime_p(order, 18)) { + return NULL_TYPE; + } + return ff_type(__yices_globals.types, order); +} + EXPORTED type_t yices_new_uninterpreted_type(void) { MT_PROTECT(type_t, __yices_globals.lock, _o_yices_new_uninterpreted_type()); } @@ -7192,6 +7229,19 @@ int32_t _o_yices_rational_const_value(term_t t, mpq_t q) { return 0; } +EXPORTED int32_t yices_finitefield_const_value(term_t t, mpz_t z) { + MT_PROTECT(int32_t, __yices_globals.lock, _o_yices_finitefield_const_value(t, z)); +} + +int32_t _o_yices_finitefield_const_value(term_t t, mpz_t z) { + if (! check_good_term(__yices_globals.manager, t) || + ! check_constructor(__yices_globals.terms, t, YICES_ARITH_FF_CONSTANT)) { + return -1; + } + arith_ff_const_value(__yices_globals.terms, t, z); + return 0; +} + /* * Components of a sum t @@ -7207,6 +7257,7 @@ EXPORTED int32_t yices_sum_component(term_t t, int32_t i, mpq_t coeff, term_t *t int32_t _o_yices_sum_component(term_t t, int32_t i, mpq_t coeff, term_t *term) { if (! check_good_term(__yices_globals.manager, t) || ! check_constructor(__yices_globals.terms, t, YICES_ARITH_SUM) || + ! check_constructor(__yices_globals.terms, t, YICES_ARITH_FF_SUM) || ! check_child_idx(__yices_globals.terms, t, i)) { return -1; } @@ -7283,6 +7334,14 @@ term_t arith_buffer_get_lt0_atom(rba_buffer_t *b) { return mk_arith_lt0(__yices_globals.manager, b); } +term_t arith_ff_buffer_get_term(rba_buffer_t *b, rational_t *mod) { + return mk_arith_ff_term(__yices_globals.manager, b, mod); +} + +term_t arith_ff_buffer_get_eq0_atom(rba_buffer_t *b, rational_t *mod) { + return mk_arith_ff_eq0(__yices_globals.manager, b, mod); +} + term_t bvlogic_buffer_get_term(bvlogic_buffer_t *b) { return mk_bvlogic_term(__yices_globals.manager, b); } @@ -7309,6 +7368,10 @@ term_t yices_bvconst64_term(uint32_t n, uint64_t c) { return bv64_constant(__yices_globals.terms, n, c); } +term_t yices_ffconst_term(rational_t *q, rational_t *mod) { + return arith_ff_constant(__yices_globals.terms, q, mod); +} + term_t yices_rational_term(rational_t *q) { return arith_constant(__yices_globals.terms, q); } @@ -7351,6 +7414,21 @@ bool yices_check_arith_term(term_t t) { return check_good_term(__yices_globals.manager, t) && check_arith_term(__yices_globals.manager, t); } +/* + * Check whether t is a valid finite field arithmetic term + * - if not set the internal error report: + * + * If t is not a valid term: + * code = INVALID_TERM + * term1 = t + * index = -1 + * If t is not an arithmetic term; + * code = ARITHTERM_REQUIRED + * term1 = t + */ +bool yices_check_arith_ff_term(term_t t) { + return check_good_term(__yices_globals.manager, t) && check_arith_ff_term(__yices_globals.manager, t); +} /* * Check for degree overflow in the product (b * t) @@ -7369,7 +7447,7 @@ bool yices_check_mul_term(rba_buffer_t *b, term_t t) { tbl = __yices_globals.terms; - assert(good_term(tbl, t) && is_arithmetic_term(tbl, t)); + assert(good_term(tbl, t) && (is_arithmetic_term(tbl, t) || is_finitefield_term(tbl, t))); d1 = rba_buffer_degree(b); d2 = term_degree(tbl, t); diff --git a/src/api/yices_api_lock_free.h b/src/api/yices_api_lock_free.h index f9ba671a1..0c0bcd644 100644 --- a/src/api/yices_api_lock_free.h +++ b/src/api/yices_api_lock_free.h @@ -51,6 +51,8 @@ extern type_t _o_yices_real_type(void); extern type_t _o_yices_bv_type(uint32_t size); +extern type_t _o_yices_ff_type(mpz_t order); + extern type_t _o_yices_new_uninterpreted_type(void); extern type_t _o_yices_new_scalar_type(uint32_t card); @@ -501,6 +503,8 @@ extern int32_t _o_yices_scalar_const_value(term_t t, int32_t *val); extern int32_t _o_yices_rational_const_value(term_t t, mpq_t q); +extern int32_t _o_yices_finitefield_const_value(term_t t, mpz_t z); + extern int32_t _o_yices_sum_component(term_t t, int32_t i, mpq_t coeff, term_t *term); extern int32_t _o_yices_bvsum_component(term_t t, int32_t i, int32_t val[], term_t *term); diff --git a/src/api/yices_error.c b/src/api/yices_error.c index 5279d527a..a507c9c29 100644 --- a/src/api/yices_error.c +++ b/src/api/yices_error.c @@ -46,6 +46,7 @@ static const char* const term_kind2string[NUM_TERM_KINDS] = { "uninterpreted or Boolean constant", // CONSTANT_TERM "rational constant", // ARITH_CONSTANT + "finite field constant", // ARITH_FF_CONSTANT "bitvector constant", // BV64_CONSTANT "bitvector constant", // BV_CONSTANT "variable", // VARIABLE @@ -57,6 +58,7 @@ static const char* const term_kind2string[NUM_TERM_KINDS] = { "ceil", // ARITH_CEIL "absolute value", // ARITH_ABS "algebraic root", // ARITH_ROOT_ATOM + "finite field arithmetic equality", // ARITH_FF_EQ_ATOM "if-then-else", // ITE_TERM "if-then-else", // ITE_SPECIAL "uninterpreted function or predicate", // APP_TERM @@ -73,6 +75,7 @@ static const char* const term_kind2string[NUM_TERM_KINDS] = { "integer division", // ARITH_IDIV "mod", // ARITH_MOD "divides", // ARITH_DIVIDES_ATOM + "finite field arithmetic equality", // ARITH_FF_BINEQ_ATOM "bit-vector", // BV_ARRAY "bv-div", // BV_DIV "bv-rem", // BV_REM @@ -89,6 +92,7 @@ static const char* const term_kind2string[NUM_TERM_KINDS] = { "bit extrction", // BIT_TERM "product", // POWER_PRODUCT "polynomial", // ARITH_POLY + "finite field polynomial", // ARITH_FF_POLY "bit-vector polynomial", // BV64_POLY "bit-vector polynomial", // BV_POLY }; @@ -282,6 +286,14 @@ int32_t print_error(FILE *f) { code = fprintf(f, "Invalid term-exploration query\n"); break; + case INVALID_FFSIZE: + code = fprintf(f, "invalid finite field size (line %"PRIu32", column %"PRIu32")", error->line, error->column); + break; + + case INCOMPATIBLE_FFSIZES: + code = fprintf(f, "incompatible finite field types/orders (line %"PRIu32", column %"PRIu32")", error->line, error->column); + break; + /* * Parser errors */ @@ -349,6 +361,10 @@ int32_t print_error(FILE *f) { code = fprintf(f, "invalid number in 'mk-bv' (line %"PRIu32", column %"PRIu32")\n", error->line, error->column); break; + case INVALID_FFCONSTANT: + code = fprintf(f, "invalid number in 'mk-ff' (line %"PRIu32", column %"PRIu32")\n", error->line, error->column); + break; + case TYPE_MISMATCH_IN_DEF: code = fprintf(f, "type mismatch in 'define' (line %"PRIu32", column %"PRIu32")\n", error->line, error->column); break; @@ -799,6 +815,14 @@ char *error_string(void) { nchar = snprintf(buffer, BUFFER_SIZE, "Invalid term-exploration query"); break; + case INVALID_FFSIZE: + nchar = snprintf(buffer, BUFFER_SIZE, "invalid finite field size (line %"PRIu32", column %"PRIu32")", error->line, error->column); + break; + + case INCOMPATIBLE_FFSIZES: + nchar = snprintf(buffer, BUFFER_SIZE, "incompatible finite field types/orders (line %"PRIu32", column %"PRIu32")", error->line, error->column); + break; + /* * Parser errors */ @@ -866,6 +890,10 @@ char *error_string(void) { nchar = snprintf(buffer, BUFFER_SIZE, "invalid number in 'mk-bv' (line %"PRIu32", column %"PRIu32")", error->line, error->column); break; + case INVALID_FFCONSTANT: + nchar = snprintf(buffer, BUFFER_SIZE, "invalid number in 'mk-ff' (line %"PRIu32", column %"PRIu32")", error->line, error->column); + break; + case TYPE_MISMATCH_IN_DEF: nchar = snprintf(buffer, BUFFER_SIZE, "type mismatch in 'define' (line %"PRIu32", column %"PRIu32")", error->line, error->column); break; diff --git a/src/api/yices_extensions.h b/src/api/yices_extensions.h index a0e690e73..3e2cb9ffd 100644 --- a/src/api/yices_extensions.h +++ b/src/api/yices_extensions.h @@ -234,6 +234,15 @@ extern term_t arith_buffer_get_gt0_atom(rba_buffer_t *b); */ extern term_t arith_buffer_get_lt0_atom(rba_buffer_t *b); +/* + * similar to arith_buffer_get_term, but for finite fields + */ +extern term_t arith_ff_buffer_get_term(rba_buffer_t *b, rational_t *mod); + +/* + * similar to arith_buffer_get_eq0_term, but for finite fields + */ +extern term_t arith_ff_buffer_get_eq0_atom(rba_buffer_t *b, rational_t *mod); /* * Convert b to a term then reset b. @@ -293,6 +302,10 @@ extern term_t yices_bvconst_term(uint32_t n, uint32_t *v); */ extern term_t yices_bvconst64_term(uint32_t n, uint64_t c); +/* + * Convert a finite field constant to a term + */ +extern term_t yices_ffconst_term(rational_t *q, rational_t *mod); /* * Convert rational q to a term @@ -334,6 +347,20 @@ extern bool yices_check_boolean_term(term_t t); */ extern bool yices_check_arith_term(term_t t); +/* + * Check whether t is a valid finite field arithmetic term + * - if not set the internal error report: + * + * If t is not a valid term: + * code = INVALID_TERM + * term1 = t + * index = -1 + * If t is not an arithmetic term; + * code = ARITHTERM_REQUIRED + * term1 = t + */ +extern bool yices_check_arith_ff_term(term_t t); + /* * Check for degree overflow in the product (b * t) diff --git a/src/api/yval.c b/src/api/yval.c index 7bb76fdc6..e85704ed6 100644 --- a/src/api/yval.c +++ b/src/api/yval.c @@ -103,6 +103,7 @@ static const yval_tag_t val_kind2tag[NUM_VALUE_KIND] = { YVAL_UNKNOWN, // UNKNOWN_VALUE YVAL_BOOL, // BOOLEAN_VALUE YVAL_RATIONAL, // RATIONAL_VALUE + YVAL_FINITEFIELD,// FINITEFIELD_VALUE YVAL_ALGEBRAIC, // ALGEBRAIC_VALUE YVAL_BV, // BITVECTOR_VALUE YVAL_TUPLE, // TUPPLE_VALUE diff --git a/src/context/context_simplifier.c b/src/context/context_simplifier.c index 74e3c70f1..836587e8b 100644 --- a/src/context/context_simplifier.c +++ b/src/context/context_simplifier.c @@ -2323,6 +2323,13 @@ void flatten_assertion(context_t *ctx, term_t f) { intern_tbl_map_root(intern, r, bool2code(tt)); flatten_bit_select(ctx, r, tt); break; + + case ARITH_FF_POLY: + case ARITH_FF_CONSTANT: + case ARITH_FF_EQ_ATOM: + case ARITH_FF_BINEQ_ATOM: + exception = CONTEXT_UNSUPPORTED_THEORY; + goto abort; } } diff --git a/src/context/context_types.h b/src/context/context_types.h index 523a8e1fd..aa325c49b 100644 --- a/src/context/context_types.h +++ b/src/context/context_types.h @@ -787,6 +787,7 @@ enum { MCSAT_EXCEPTION_UNSUPPORTED_THEORY = -22, // new code: added 2021/06/29 HIGH_ORDER_FUN_NOT_SUPPORTED = -23, + CONTEXT_UNSUPPORTED_THEORY = -24, }; diff --git a/src/context/shared_terms.c b/src/context/shared_terms.c index 3575ba9a9..b69b35545 100644 --- a/src/context/shared_terms.c +++ b/src/context/shared_terms.c @@ -247,6 +247,14 @@ static void sharing_map_visit_subterms(sharing_map_t *map, int32_t i) { sharing_map_visit_bvpoly(map, bvpoly_for_idx(map->terms, i), i); break; + case ARITH_FF_CONSTANT: + case ARITH_FF_POLY: + case ARITH_FF_EQ_ATOM: + case ARITH_FF_BINEQ_ATOM: + // FF arithmetic is not supported in the non-mcsat solver yet + assert(false); + break; + case UNUSED_TERM: case RESERVED_TERM: assert(false); diff --git a/src/context/symmetry_breaking.c b/src/context/symmetry_breaking.c index f9f90d133..7c35eea0f 100644 --- a/src/context/symmetry_breaking.c +++ b/src/context/symmetry_breaking.c @@ -1235,8 +1235,8 @@ static void collect_constants(sym_breaker_t *breaker, term_t t, term_t *c, uint3 case CONSTANT_TERM: case UNINTERPRETED_TERM: if (constant_is_in_set(r, c, n, &k)) { - assert(0 <= k && k < n && c[k] == r); - ivector_push(v, k); + assert(0 <= k && k < n && c[k] == r); + ivector_push(v, k); } break; @@ -1314,6 +1314,13 @@ static void collect_constants(sym_breaker_t *breaker, term_t t, term_t *c, uint3 case BV_POLY: push_bvpoly_vars(queue, cache, bvpoly_term_desc(terms, r)); break; + + case ARITH_FF_POLY: + case ARITH_FF_CONSTANT: + case ARITH_FF_EQ_ATOM: + case ARITH_FF_BINEQ_ATOM: + longjmp(breaker->ctx->env, CONTEXT_UNSUPPORTED_THEORY); + break; } } while (! int_queue_is_empty(queue)); diff --git a/src/frontend/smt1/smt_lexer.c b/src/frontend/smt1/smt_lexer.c index cd977d751..b55beb9a1 100644 --- a/src/frontend/smt1/smt_lexer.c +++ b/src/frontend/smt1/smt_lexer.c @@ -255,6 +255,9 @@ static void init_smttoken2string(void) { * More logics: added June 10 2014 * * QF_UFLIRA Ints Reals + * + * More logics: added Nov 17 2023 + * QF_FFA Finite Fields */ static uint8_t smt_token_active[NUM_SMT_TOKENS]; @@ -435,6 +438,7 @@ static void activate_arith_fragment(arith_fragment_t code) { smt_token_active[SMT_TK_REAL] = true; break; + case ARITH_FFA: /* not defined in smt1 */ case ARITH_NONE: break; } diff --git a/src/frontend/smt2/smt2_commands.c b/src/frontend/smt2/smt2_commands.c index f201568f9..b5879a41c 100644 --- a/src/frontend/smt2/smt2_commands.c +++ b/src/frontend/smt2/smt2_commands.c @@ -1206,6 +1206,9 @@ static const char * const exception_string[NUM_SMT2_EXCEPTIONS] = { "number can't be converted to a bitvector constant", // TSTACK_INVALID_BVCONSTANT "error in bitvector arithmetic operation", //TSTACK_BVARITH_ERROR "error in bitvector operation", // TSTACK_BVLOGIC_ERROR + "Invalid finite field constant", // TSTACK_INVALID_FFCONSTANT + "invalid finite field order", // TSTACK_INVALID_FFCONSTANT + "incompatible finite field sizes", // TSTACK_INCOMPATIBLE_FFSIZES "incompatible sort in definition", // TSTACK_TYPE_ERROR_IN_DEFTERM "invalid term", // TSTACK_STRINGS_ARE_NOT_TERMS "variables and values not matching", // TSTACK_VARIABLES_VALUES_NOT_MATCHING @@ -1249,6 +1252,7 @@ static const char * const opcode_string[NUM_SMT2_OPCODES] = { "sort-variable declaration", // DECLARE_TYPE_VAR "let", // LET "BitVec", // MK_BV_TYPE + "FiniteField", // MK_FF_TYPE NULL, // MK_SCALAR_TYPE NULL, // MK_TUPLE_TYPE "function type", // MK_FUN_TYPE @@ -1336,9 +1340,13 @@ static const char * const opcode_string[NUM_SMT2_OPCODES] = { "divides", // MK_DIVIDES (not in SMT2 --> divisible) "is_int", // MK_IS_INT + "ff.constant", // MK_FF_CONST + "ff.add", // MK_FF_ADD + "ff.mul", // MK_FF_MUL + "build term", // BUILD_TERM "build_type", // BUILD_TYPE - // + "exit", // SMT2_EXIT "end of file", // SMT2_SILENT_EXIT "get-assertions", // SMT2_GET_ASSERTIONS @@ -1358,7 +1366,7 @@ static const char * const opcode_string[NUM_SMT2_OPCODES] = { "assert", // SMT2_ASSERT, "check-sat", // SMT2_CHECK_SAT, "check-sat-assuming", // SMT2_CHECK_SAT_ASSUMING, - "check-sat-assuming-model" // SMT2_CHECK_SAT_ASSUMING_MODEL + "check-sat-assuming-model", // SMT2_CHECK_SAT_ASSUMING_MODEL "declare-sort", // SMT2_DECLARE_SORT "define-sort", // SMT2_DEFINE_SORT "declare-fun", // SMT2_DECLARE_FUN @@ -1451,7 +1459,6 @@ void smt2_tstack_error(tstack_t *tstack, int32_t exception) { } break; - case TSTACK_RATIONAL_FORMAT: case TSTACK_FLOAT_FORMAT: case TSTACK_BVBIN_FORMAT: @@ -1477,7 +1484,10 @@ void smt2_tstack_error(tstack_t *tstack, int32_t exception) { case TSTACK_DIVIDE_BY_ZERO: case TSTACK_NON_CONSTANT_DIVISOR: case TSTACK_INCOMPATIBLE_BVSIZES: + case TSTACK_INCOMPATIBLE_FFSIZES: + case TSTACK_INVALID_FFSIZE: case TSTACK_INVALID_BVCONSTANT: + case TSTACK_INVALID_FFCONSTANT: case TSTACK_BVARITH_ERROR: case TSTACK_BVLOGIC_ERROR: case TSTACK_TYPE_ERROR_IN_DEFTERM: @@ -1549,12 +1559,18 @@ static void show_status(smt_status_t status) { static void report_status(smt2_globals_t *g, smt_status_t status) { switch (status) { case STATUS_UNKNOWN: - case STATUS_SAT: case STATUS_UNSAT: case YICES_STATUS_INTERRUPTED: show_status(status); break; + case STATUS_SAT: + show_status(status); + if (g->dump_models) { + smt2_get_model(); + } + break; + case STATUS_ERROR: print_yices_error(true); break; @@ -2906,6 +2922,7 @@ static bool needs_egraph(int_hset_t *seen, term_t t) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: case BV64_CONSTANT: case BV_CONSTANT: result = false; @@ -2924,6 +2941,10 @@ static bool needs_egraph(int_hset_t *seen, term_t t) { result = needs_egraph(seen, arith_root_atom_desc(terms, t)->p); break; + case ARITH_FF_EQ_ATOM: + result = needs_egraph(seen, arith_ff_eq_arg(terms, t)); + break; + case ITE_TERM: case ITE_SPECIAL: case EQ_TERM: @@ -2935,6 +2956,7 @@ static bool needs_egraph(int_hset_t *seen, term_t t) { case ARITH_IDIV: case ARITH_MOD: case ARITH_DIVIDES_ATOM: + case ARITH_FF_BINEQ_ATOM: case BV_ARRAY: case BV_DIV: case BV_REM: @@ -2971,6 +2993,10 @@ static bool needs_egraph(int_hset_t *seen, term_t t) { result = poly_needs_egraph(seen, poly_term_desc(terms, t)); break; + case ARITH_FF_POLY: + result = poly_needs_egraph(seen, finitefield_poly_term_desc(terms, t)); + break; + case BV64_POLY: result = bvpoly64_needs_egraph(seen, bvpoly64_term_desc(terms, t)); break; @@ -3065,9 +3091,9 @@ static void check_delayed_assertions(smt2_globals_t *g, bool report) { */ code = export_delayed_assertions(g->ctx, g->assertions.size, g->assertions.data, g->dimacs_file); if (code < 0) { - print_yices_error(true); - done = true; - return; + print_yices_error(true); + done = true; + return; } } else { @@ -3076,42 +3102,42 @@ static void check_delayed_assertions(smt2_globals_t *g, bool report) { */ code = yices_assert_formulas(g->ctx, g->assertions.size, g->assertions.data); if (code < 0) { - // error during assertion processing - print_yices_error(true); - done = true; - return; + // error during assertion processing + print_yices_error(true); + done = true; + return; } if (g->delegate != NULL && g->logic_code == QF_BV) { - /* - * Special case: QF_BV with delegate - */ - if (g->dimacs_file == NULL) { - status = check_with_delegate(g->ctx, g->delegate, g->verbosity); - } else { - code = process_then_export_to_dimacs(g->ctx, g->dimacs_file, &status); - if (code < 0) { - perror(g->dimacs_file); - exit(YICES_EXIT_SYSTEM_ERROR); - } - if (status == STATUS_UNKNOWN) { - // don't print anything - return; - } - } + /* + * Special case: QF_BV with delegate + */ + if (g->dimacs_file == NULL) { + status = check_with_delegate(g->ctx, g->delegate, g->verbosity); + } else { + code = process_then_export_to_dimacs(g->ctx, g->dimacs_file, &status); + if (code < 0) { + perror(g->dimacs_file); + exit(YICES_EXIT_SYSTEM_ERROR); + } + if (status == STATUS_UNKNOWN) { + // don't print anything + return; + } + } } else { - /* - * Regular check - */ - init_search_parameters(g); - if (g->random_seed != 0) { - g->parameters.random_seed = g->random_seed; - } - status = check_sat_with_timeout(g, &g->parameters); + /* + * Regular check + */ + init_search_parameters(g); + if (g->random_seed != 0) { + g->parameters.random_seed = g->random_seed; + } + status = check_sat_with_timeout(g, &g->parameters); } if (report) - report_status(g, status); + report_status(g, status); } } } @@ -3715,9 +3741,9 @@ static model_t *get_model(smt2_globals_t *g) { } else if (g->trivially_unsat) { print_error("the context is unsatisfiable"); } else { - // g->model should be not be NULL - assert(g->trivially_sat); - freport_bug(__smt2_globals.err, "get-model"); + // g->model should be not NULL + assert(g->trivially_sat); + freport_bug(__smt2_globals.err, "get-model"); } } else { @@ -4309,11 +4335,11 @@ static void show_unsat_model_interpolant(smt2_globals_t *g) { case STATUS_UNSAT: unsat_model_interpolant = context_get_unsat_model_interpolant(g->ctx); if (unsat_model_interpolant == NULL_TERM) { - print_error("Call (check-sat-assuming-model) first"); + print_error("Call (check-sat-assuming-model) first"); } else { - init_pretty_printer(&printer, g); - pp_term_full(&printer.pp, __yices_globals.terms, unsat_model_interpolant); - delete_smt2_pp(&printer, true); + init_pretty_printer(&printer, g); + pp_term_full(&printer.pp, __yices_globals.terms, unsat_model_interpolant); + delete_smt2_pp(&printer, true); } break; @@ -4580,6 +4606,7 @@ static void init_smt2_globals(smt2_globals_t *g) { g->verbosity = 0; init_ctx_params(&g->ctx_parameters); init_params_to_defaults(&g->parameters); + g->dump_models = false; g->nthreads = 0; g->timeout = 0; g->to = NULL; @@ -5414,6 +5441,10 @@ void smt2_get_option(const char *name) { print_uint32_value(g->verbosity); break; + case SMT2_KW_DUMP_MODELS: + print_boolean_value(g->dump_models); + break; + case SMT2_KW_PRODUCE_UNSAT_ASSUMPTIONS: print_boolean_value(g->produce_unsat_assumptions); break; @@ -6220,6 +6251,10 @@ void smt2_set_option(const char *name, aval_t value) { set_verbosity(g, name, value); break; + case SMT2_KW_DUMP_MODELS: + set_boolean_option(g, name, value, &g->dump_models); + break; + case SMT2_KW_PRODUCE_UNSAT_ASSUMPTIONS: // optional: if true, get-unsat-assumptions can be used if (option_can_be_set(name)) { @@ -6683,15 +6718,15 @@ void smt2_check_sat(void) { } else if (__smt2_globals.produce_unsat_cores) { delayed_assertions_unsat_core(&__smt2_globals); } else { - // show_delayed_assertions(&__smt2_globals); + // show_delayed_assertions(&__smt2_globals); #ifndef THREAD_SAFE check_delayed_assertions(&__smt2_globals, /*report=*/true); #else if (__smt2_globals.nthreads == 0) { check_delayed_assertions(&__smt2_globals, /*report=*/true); } else { - check_delayed_assertions_mt(&__smt2_globals); - } + check_delayed_assertions_mt(&__smt2_globals); + } #endif } } else { diff --git a/src/frontend/smt2/smt2_commands.h b/src/frontend/smt2/smt2_commands.h index c7d45ef72..3f5570859 100644 --- a/src/frontend/smt2/smt2_commands.h +++ b/src/frontend/smt2/smt2_commands.h @@ -390,6 +390,7 @@ typedef struct smt2_globals_s { bool produce_unsat_model_interpolants; // default = false bool produce_models; // default = false bool produce_assignments; // default = false + bool dump_models; // default = false uint32_t random_seed; // default = 0 uint32_t verbosity; // default = 0 diff --git a/src/frontend/smt2/smt2_keywords.txt b/src/frontend/smt2/smt2_keywords.txt index a74850ca1..20ad58844 100644 --- a/src/frontend/smt2/smt2_keywords.txt +++ b/src/frontend/smt2/smt2_keywords.txt @@ -37,3 +37,4 @@ struct keyword_s; :category, SMT2_KW_CATEGORY :difficulty, SMT2_KW_DIFFICULTY :notes, SMT2_KW_NOTES +:dump-models, SMT2_KW_DUMP_MODELS \ No newline at end of file diff --git a/src/frontend/smt2/smt2_lexer.c b/src/frontend/smt2/smt2_lexer.c index 4c5201766..3efb06a6c 100644 --- a/src/frontend/smt2/smt2_lexer.c +++ b/src/frontend/smt2/smt2_lexer.c @@ -115,6 +115,7 @@ static const char * const smt2_keyword_string[NUM_SMT2_KEYWORDS] = { ":regular-output-channel", // SMT2_KW_REGULAR_OUTPUT ":reproducible-resource-limit", // SMT2_KW_REPRODUCIBLE_RESOURCE_LIMIT ":verbosity", // SMT2_KW_VERBOSITY + ":dump-models", // SMT2_KW_DUMP_MODELS ":all-statistics", // SMT2_KW_ALL_STATISTICS ":assertion-stack-levesl", // SMT2_KM_ASSERTIONS_STACK_LEVELS @@ -208,6 +209,9 @@ static const char * const smt2_symbol_string[NUM_SMT2_SYMBOLS] = { "bvsle", // SMT2_SYM_BVSLE "bvsgt", // SMT2_SYM_BVSGT "bvsge", // SMT2_SYM_BVSGE + "FiniteField", // SMT2_SYM_FINITEFIELD + "ff.add", // SMT2_SYM_FFADD + "ff.mul", // SMT2_SYM_FFMUL // errors "", // SMT2_SYM_INVALID_BV_CONSTANT, @@ -395,6 +399,12 @@ static void smt2_activate_bv(void) { active_symbol[SMT2_SYM_BVSGE] = true; } +static void smt2_activate_ff(void) { + active_symbol[SMT2_SYM_FINITEFIELD] = true; + active_symbol[SMT2_SYM_FF_CONSTANT] = true; + active_symbol[SMT2_SYM_FFADD] = true; + active_symbol[SMT2_SYM_FFMUL] = true; +} /* * Select the built-in symbols for a given logic @@ -427,6 +437,10 @@ void smt2_lexer_activate_logic(smt_logic_t logic) { smt2_activate_mixed_arith(); break; + case ARITH_FFA: + smt2_activate_ff(); + break; + case ARITH_NONE: break; } @@ -1145,8 +1159,35 @@ static smt2_symbol_t string_to_bv_constant(const char *s, uint32_t n) { return sym; } - /* + * Check whether s is of the form 'ff' + * - n = length of s + * - return code: + * SMT2_SYM_FF_CONSTANT if s is of the form 'ff' + * SMT2_SYM_UNKNOWN otherwise (not a ffconstant) + */ +static smt2_symbol_t string_to_ff_constant(const char *s, uint32_t n) { + uint32_t i; + smt2_symbol_t sym; + + sym = SMT2_SYM_UNKNOWN; + if (n >= 3 && s[0] == 'f' && s[1] == 'f') { + // could be a negative number + i = (n >= 4 && s[2] == '-') ? 3 : 2; + while (i + } + } + sym = SMT2_SYM_FF_CONSTANT; + } + + done: + return sym; +} + + +/** * Convert a string s to a symbol code * - n = length of s * - return SMT2_SYM_UNKNOWN if s is not a predefined symbol, @@ -1158,6 +1199,13 @@ static smt2_symbol_t string_to_bv_constant(const char *s, uint32_t n) { * returned id is SMT2_SYM_BV_CONSTANT * - if the string starts with 'bv' but the rest is not , then * the returned if is SMT2_SYM_INVALID_BV_CONSTANT + * + * Special treatment for finite field constants, when the finite field + * theory is active: + * - if the string is of the form 'ff' then the + * returned id is SMT2_SYM_FF_CONSTANT + * - if the string starts with 'ff' but the rest is not , then + * the returned if is SMT2_SYM_INVALID_FF_CONSTANT */ smt2_symbol_t smt2_string_to_symbol(const char *s, uint32_t n) { const keyword_t *kw; @@ -1166,9 +1214,12 @@ smt2_symbol_t smt2_string_to_symbol(const char *s, uint32_t n) { sym = SMT2_SYM_UNKNOWN; kw = in_smt2_sym(s, n); if (kw == NULL) { - if (active_symbol[SMT2_SYM_BV_CONSTANT]) { + if (active_symbol[SMT2_SYM_BV_CONSTANT] && sym == SMT2_SYM_UNKNOWN) { sym = string_to_bv_constant(s, n); } + if (active_symbol[SMT2_SYM_FF_CONSTANT] && sym == SMT2_SYM_UNKNOWN) { + sym = string_to_ff_constant(s, n); + } } else if (active_symbol[kw->tk]) { sym = kw->tk; } diff --git a/src/frontend/smt2/smt2_lexer.h b/src/frontend/smt2/smt2_lexer.h index 38ff7cdf9..865fd48fc 100644 --- a/src/frontend/smt2/smt2_lexer.h +++ b/src/frontend/smt2/smt2_lexer.h @@ -135,6 +135,7 @@ enum smt2_keyword { SMT2_KW_REGULAR_OUTPUT, SMT2_KW_REPRODUCIBLE_RESOURCE_LIMIT, SMT2_KW_VERBOSITY, + SMT2_KW_DUMP_MODELS, // Predefined keywords for (get-info ...) SMT2_KW_ALL_STATISTICS, @@ -254,6 +255,14 @@ enum smt2_symbol { SMT2_SYM_BVSGT, SMT2_SYM_BVSGE, + // Special symbols used in the FF theory: (as ff (_ FiniteField )) + SMT2_SYM_FF_CONSTANT, + + // finite field symbols + SMT2_SYM_FINITEFIELD, + SMT2_SYM_FFADD, + SMT2_SYM_FFMUL, + // Errors SMT2_SYM_INVALID_BV_CONSTANT, diff --git a/src/frontend/smt2/smt2_parser.c b/src/frontend/smt2/smt2_parser.c index 21482ad3a..c327d85ee 100644 --- a/src/frontend/smt2/smt2_parser.c +++ b/src/frontend/smt2/smt2_parser.c @@ -30,7 +30,7 @@ #include "frontend/smt2/smt2_term_stack.h" /* - * Short cuts to save typing + * Short-cuts to save typing */ static inline char *tkval(lexer_t *lex) { return current_token_value(lex); @@ -460,9 +460,9 @@ static int32_t smt2_parse(parser_t *parser, state_t start) { case not_next_goto_c16c: if (! token_is_not(tkval(lex), tklen(lex))) { - // syntax error. - smt2_syntax_error(lex, -3); // NOT expected - goto cleanup; + // syntax error. + smt2_syntax_error(lex, -3); // NOT expected + goto cleanup; } state = c16c; goto loop; diff --git a/src/frontend/smt2/smt2_printer.c b/src/frontend/smt2/smt2_printer.c index e5ecc1e26..aa552b70e 100644 --- a/src/frontend/smt2/smt2_printer.c +++ b/src/frontend/smt2/smt2_printer.c @@ -124,6 +124,10 @@ static void smt2_pp_rational(smt2_pp_t *printer, rational_t *q) { q_clear(&den); } +static void smt2_pp_finitefield(smt2_pp_t *printer, value_ff_t *v) { + pp_finitefield(&printer->pp, v); +} + static void smt2_pp_algebraic(smt2_pp_t *printer, void *a) { pp_algebraic(&printer->pp, a); } @@ -166,6 +170,9 @@ void smt2_pp_object(smt2_pp_t *printer, value_table_t *table, value_t c) { case ALGEBRAIC_VALUE: smt2_pp_algebraic(printer, table->desc[c].ptr); break; + case FINITEFIELD_VALUE: + smt2_pp_finitefield(printer, table->desc[c].ptr); + break; case BITVECTOR_VALUE: smt2_pp_bitvector(printer, table->desc[c].ptr); break; diff --git a/src/frontend/smt2/smt2_symbols.txt b/src/frontend/smt2/smt2_symbols.txt index 0db0678df..e45118892 100644 --- a/src/frontend/smt2/smt2_symbols.txt +++ b/src/frontend/smt2/smt2_symbols.txt @@ -75,3 +75,6 @@ bvslt, SMT2_SYM_BVSLT bvsle, SMT2_SYM_BVSLE bvsgt, SMT2_SYM_BVSGT bvsge, SMT2_SYM_BVSGE +FiniteField, SMT2_SYM_FINITEFIELD +ff.add, SMT2_SYM_FFADD +ff.mul, SMT2_SYM_FFMUL diff --git a/src/frontend/smt2/smt2_term_stack.c b/src/frontend/smt2/smt2_term_stack.c index a386bf394..0cc1651a4 100644 --- a/src/frontend/smt2/smt2_term_stack.c +++ b/src/frontend/smt2/smt2_term_stack.c @@ -1257,6 +1257,7 @@ static void check_constant_term(tstack_t *stack, stack_elem_t *e) { switch (kind) { case YICES_BOOL_CONSTANT: case YICES_ARITH_CONSTANT: + case YICES_ARITH_FF_CONSTANT: case YICES_BV_CONSTANT: break; default: @@ -1659,6 +1660,7 @@ typedef enum smt2_key { // special codes SMT2_KEY_IDX_BV, // for bv construct SMT2_KEY_ERROR_BV, // for an invalid bv ( not a numeral) + SMT2_KEY_IDX_FF, // for ff construct SMT2_KEY_UNKNOWN, // not a built-in symbol } smt2_key_t; @@ -1746,6 +1748,10 @@ static const uint8_t smt2_key[NUM_SMT2_SYMBOLS] = { SMT2_KEY_TERM_OP, // SMT2_SYM_BVSLE SMT2_KEY_TERM_OP, // SMT2_SYM_BVSGT SMT2_KEY_TERM_OP, // SMT2_SYM_BVSGE + SMT2_KEY_IDX_FF, // SMT2_SYM_FF_CONSTANT + SMT2_KEY_IDX_TYPE, // SMT2_SYM_FINITEFIELD + SMT2_KEY_TERM_OP, // SMT2_SYM_FFADD + SMT2_KEY_TERM_OP, // SMT2_SYM_FFMUL SMT2_KEY_ERROR_BV, // SMT2_SYM_INVALID_BV_CONSTANT SMT2_KEY_UNKNOWN, // SMT2_SYM_UNKNOWN }; @@ -1821,6 +1827,10 @@ static const int32_t smt2_val[NUM_SMT2_SYMBOLS] = { MK_BV_SLE, // SMT2_SYM_BVSLE MK_BV_SGT, // SMT2_SYM_BVSGT MK_BV_SGE, // SMT2_SYM_BVSGE + MK_FF_CONST, // SMT2_SYM_FF_CONSTANT + MK_FF_TYPE, // SMT2_SYM_FINITEFIELD + MK_FF_ADD, // SMT2_SYM_FFADD + MK_FF_MUL, // SMT2_SYM_FFMUL NO_OP, // SMT2_SYM_INVALID_BV_CONSTANT (ignored) NO_OP, // SMT2_SYM_UNKNOWN (ignored) }; @@ -1978,6 +1988,13 @@ void tstack_push_term_name(tstack_t *stack, char *s, uint32_t n, loc_t *loc) { tstack_push_term(stack, smt2_val[symbol], loc); break; + case SMT2_KEY_IDX_FF: + // generates (_ ffN -1) + tstack_push_op(stack, MK_FF_CONST, loc); + tstack_push_rational(stack, s + 2, loc); + tstack_eval(stack); + break; + case SMT2_KEY_TERM_OP: push_exception(stack, loc, s, SMT2_SYMBOL_NOT_TERM); break; @@ -2105,6 +2122,13 @@ void tstack_push_idx_term(tstack_t *stack, char *s, uint32_t n, loc_t *loc) { tstack_push_rational(stack, s + 2, loc); // skip the 'bv' prefix break; + case SMT2_KEY_IDX_FF: + // s is ff and it is to be interpreted as (mk-ff ...) + assert(n > 2); + tstack_push_op(stack, MK_FF_CONST, loc); + tstack_push_rational(stack, s + 2, loc); // skip the 'ff' prefix + break; + case SMT2_KEY_ERROR_BV: // s is bv0: invalid bv push_exception(stack, loc, s, SMT2_INVALID_IDX_BV); @@ -2152,6 +2176,13 @@ void tstack_push_qual_idx_term_name(tstack_t *stack, char *s, uint32_t n, loc_t tstack_push_rational(stack, s + 2, loc); // skip the 'bv' prefix break; + case SMT2_KEY_IDX_FF: + // s is ff and is to be interpreted as (mk-ff ...) + assert(n > 2); + tstack_push_opcode(stack, MK_FF_CONST, loc); + tstack_push_rational(stack, s + 2, loc); // skip the 'ff' prefix + break; + case SMT2_KEY_ERROR_BV: // s is bv0: invalid bv push_exception(stack, loc, s, SMT2_INVALID_IDX_BV); @@ -2403,6 +2434,9 @@ static bool stack_elem_has_type(tstack_t *stack, stack_elem_t *e, type_t tau) { case TAG_RATIONAL: return is_real_type(tau) || (is_integer_type(tau) && q_is_integer(&e->val.rational)); + case TAG_FINITEFIELD: + return is_ff_type(__yices_globals.types, tau) && q_eq(&e->val.ff.mod, ff_type_size(__yices_globals.types, tau)); + case TAG_TERM: case TAG_SPECIAL_TERM: return yices_check_term_type(e->val.term, tau); @@ -2411,6 +2445,10 @@ static bool stack_elem_has_type(tstack_t *stack, stack_elem_t *e, type_t tau) { return is_real_type(tau) || (is_integer_type(tau) && yices_arith_buffer_is_int(e->val.arith_buffer)); + case TAG_ARITH_FF_BUFFER: + return is_ff_type(__yices_globals.types, tau) + && q_eq(&e->val.mod_arith_buffer.mod, ff_type_size(__yices_globals.types, tau)); + case TAG_BVARITH64_BUFFER: n = bvarith64_buffer_bitsize(e->val.bvarith64_buffer); break; @@ -2438,19 +2476,36 @@ static bool stack_elem_has_type(tstack_t *stack, stack_elem_t *e, type_t tau) { } /* - * Check whether the element on top of stack has a type compatible with tau + * Check whether the element has a type compatible with tau * - if not, raise exception TYPE_ERROR_IN_QUAL */ -static void check_topelem_type(tstack_t *stack, type_t tau) { - stack_elem_t *e; - - assert(stack->top > 0); - e = stack->elem + (stack->top - 1); +static void check_elem_type(tstack_t *stack, stack_elem_t *e, type_t tau) { if (!stack_elem_has_type(stack, e, tau)) { raise_exception(stack, e, SMT2_TYPE_ERROR_IN_QUAL); } } +static inline void check_topelem_type(tstack_t *stack, type_t tau) { + assert(stack->top > 0); + check_elem_type(stack, stack->elem + (stack->top - 1), tau); +} + +/* + * Finalizes a stack element once the type is known. + * Used for (as ) expressions. + */ +static void stack_elem_finalize_type(tstack_t *stack, stack_elem_t *e, type_t tau) { + switch (e->tag) { + case TAG_FINITEFIELD: + if (q_is_minus_one(&e->val.ff.mod) && is_ff_type(__yices_globals.types, tau)) { + q_set(&e->val.ff.mod, ff_type_size(__yices_globals.types, tau)); + } + break; + + default: + break; + } +} /* @@ -2483,8 +2538,6 @@ static void shift_stack_elems(stack_elem_t *f, uint32_t n) { - - /* * [sorted-term ] * @@ -2501,9 +2554,13 @@ static void eval_smt2_sorted_term(tstack_t *stack, stack_elem_t *f, uint32_t n) term_t t; type_t tau; - t = get_term(stack, f); tau = f[1].val.type; + // finalize the type before making it a term + stack_elem_finalize_type(stack, f, tau); + + t = get_term(stack, f); + tstack_pop_frame(stack); set_term_result(stack, t); @@ -2617,6 +2674,7 @@ void init_smt2_tstack(tstack_t *stack) { init_tstack(stack, NUM_SMT2_OPCODES); tstack_set_avtbl(stack, __smt2_globals.avtbl); + // overwrites the default OPs (from term_stack2.c) for SMT2, in case they are different tstack_add_op(stack, MK_BV_CONST, false, eval_smt2_mk_bv_const, check_smt2_mk_bv_const); tstack_add_op(stack, MK_BV_ROTATE_LEFT, false, eval_smt2_mk_bv_rotate_left, check_smt2_mk_bv_rotate_left); tstack_add_op(stack, MK_BV_ROTATE_RIGHT, false, eval_smt2_mk_bv_rotate_right, check_smt2_mk_bv_rotate_right); diff --git a/src/frontend/smt2/smt2_type_printer.c b/src/frontend/smt2/smt2_type_printer.c index 2fef21e20..9cf72b399 100644 --- a/src/frontend/smt2/smt2_type_printer.c +++ b/src/frontend/smt2/smt2_type_printer.c @@ -84,6 +84,16 @@ static void smt2_pp_type_recur(smt2_pp_t *printer, type_table_t *tbl, type_t tau } break; + case FF_TYPE: + if (name != NULL && level <= 0) { + smt2_pp_symbol(printer, name); + } else { + pp_open_block(&printer->pp, PP_OPEN_SMT2_FF_TYPE); + pp_rational(&printer->pp, ff_type_size(tbl, tau)); + pp_close_block(&printer->pp, true); + } + break; + case UNINTERPRETED_TYPE: if (name != NULL) { smt2_pp_symbol(printer, name); diff --git a/src/frontend/yices/yices_parser.c b/src/frontend/yices/yices_parser.c index f3b6980d5..f9117a08e 100644 --- a/src/frontend/yices/yices_parser.c +++ b/src/frontend/yices/yices_parser.c @@ -123,32 +123,42 @@ static error_code_t const tstack_error2yices_error[NUM_TSTACK_ERRORS] = { NO_ERROR, // TSTACK_OP_NOT_IMPLEMENTED UNDEFINED_TERM_NAME, // TSTACK_UNDEF_TERM UNDEFINED_TYPE_NAME, // TSTACK_UNDEF_TYPE + NO_ERROR, // TSTACK_UNDEF_MACRO // TODO find proper code INVALID_RATIONAL_FORMAT, // TSTACK_RATIONAL_FORMAT INVALID_FLOAT_FORMAT, // TSTACK_FLOAT_FORMAT INVALID_BVBIN_FORMAT, // TSTACK_BVBIN_FORMAT INVALID_BVHEX_FORMAT, // TSTACK_BVHEX_FORMAT REDEFINED_TYPE_NAME, // TSTACK_TYPENAME_REDEF REDEFINED_TERM_NAME, // TSTACK_TERMNAME_REDEF + NO_ERROR, // TSTACK_MACRO_REDEF // TODO find proper code DUPLICATE_NAME_IN_SCALAR, // TSTACK_DUPLICATE_SCALAR_NAME DUPLICATE_VAR_NAME, // TSTACK_DUPLICATE_VAR_NAME + NO_ERROR, // TSTACK_DUPLICATE_TYPE_VAR_NAME // TODO find proper code NO_ERROR, // TSTACK_INVALID_OP WRONG_NUMBER_OF_ARGUMENTS, // TSTACK_INVALID_FRAME INTEGER_OVERFLOW, // TSTACK_INTEGER_OVERFLOW NONNEG_INT_REQUIRED, // TSTACK_NEGATIVE_EXPONENT INTEGER_REQUIRED, // TSTACK_NOT_AN_INTEGER + NO_ERROR, // TSTACK_NOT_A_STRING // TODO find proper code SYMBOL_REQUIRED, // TSTACK_NOT_A_SYMBOL RATIONAL_REQUIRED, // TSTACK_NOT_A_RATIONAL TYPE_REQUIRED, // TSTACK_NOT_A_TYPE ARITH_ERROR, // TSTACK_ARITH_ERROR DIVISION_BY_ZERO, // TSTACK_DIVIDE_BY_ZERO NON_CONSTANT_DIVISOR, // TSTACK_NON_CONSTANT_DIVISOR - NEGATIVE_BVSIZE, // TSTACK_NEGATIVE_BVSIZE + NEGATIVE_BVSIZE, // TSTACK_NONPOSITIVE_BVSIZE INCOMPATIBLE_BVSIZES, // TSTACK_INCOMPATIBLE_BVSIZES INVALID_BVCONSTANT, // TSTACK_INVALID_BVCONSTANT BVARITH_ERROR, // TSTACK_BVARITH_ERROR BVARITH_ERROR, // TSTACK_BVLOGIC_ERROR + INVALID_FFCONSTANT, // TSTACK_INVALID_FFCONSTANT + INVALID_FFSIZE, // TSTACK_INVALID_FFSIZE + INCOMPATIBLE_FFSIZES, // TSTACK_INCOMPATIBLE_FFSIZES TYPE_MISMATCH_IN_DEF, // TSTACK_TYPE_ERROR_IN_DEFTERM NO_ERROR, // TSTACK_STRINGS_ARE_NOT_TERMS + NO_ERROR, // TSTACK_VARIABLES_VALUES_NOT_MATCHING, // TODO find proper code + NO_ERROR, // TSTACK_NOT_A_CONSTANT, // TODO find proper code + NO_ERROR, // TSTACK_NOT_A_VARIABLE, // TODO find proper code NO_ERROR, // TSTACK_YICES_ERROR }; diff --git a/src/frontend/yices_smt2.c b/src/frontend/yices_smt2.c index e7d9232f8..0bab70b95 100644 --- a/src/frontend/yices_smt2.c +++ b/src/frontend/yices_smt2.c @@ -97,6 +97,7 @@ static tstack_t stack; static bool incremental; static bool interactive; static bool smt2_model_format; +static bool dump_models; static bool bvdecimal; static bool show_stats; static int32_t verbosity; @@ -155,6 +156,7 @@ typedef enum optid { incremental_opt, // enable incremental mode interactive_opt, // enable interactive mode yicesformat_opt, // use the Yices model format for models + dump_model_opt, // print model on sat result bvdecimal_opt, // use (_ bv n) for bit-vector constants timeout_opt, // give a timeout delegate_opt, // use an external sat solver @@ -204,6 +206,7 @@ static option_desc_t options[NUM_OPTIONS] = { { "incremental", '\0', FLAG_OPTION, incremental_opt }, { "interactive", '\0', FLAG_OPTION, interactive_opt }, { "yices-model-format", '\0', FLAG_OPTION, yicesformat_opt }, + { "dump-models", '\0', FLAG_OPTION, dump_model_opt}, { "bvconst-in-decimal", '\0', FLAG_OPTION, bvdecimal_opt }, { "delegate", '\0', MANDATORY_STRING, delegate_opt }, { "dimacs", '\0', MANDATORY_STRING, dimacs_opt }, @@ -268,6 +271,7 @@ static void print_help(const char *progname) { " --incremental Enable support for push/pop\n" " --interactive Run in interactive mode (ignored if a filename is given)\n" " --yices-model-format Display models in the Yices model format (default = false)\n" + " --dump-models Display models on sat result (default = false)\n" " --bvconst-in-decimal Display bit-vector constants as decimal numbers (default = false)\n" " --delegate= Use an external SAT solver (can be cadical, cryptominisat, kissat, or y2sat)\n" " --dimacs= Bitblast and export to a file (in DIMACS format)\n" @@ -375,6 +379,7 @@ static void parse_command_line(int argc, char *argv[]) { incremental = false; interactive = false; smt2_model_format = true; + dump_models = false; bvdecimal = false; show_stats = false; verbosity = 0; @@ -471,15 +476,15 @@ static void parse_command_line(int argc, char *argv[]) { break; case nthreads_opt: - v = elem.i_value; - if (v < 0) { - fprintf(stderr, "%s: the number of threads must be non-negative\n", parser.command_name); - print_usage(parser.command_name); - code = YICES_EXIT_USAGE; - goto exit; - } - nthreads = v; - break; + v = elem.i_value; + if (v < 0) { + fprintf(stderr, "%s: the number of threads must be non-negative\n", parser.command_name); + print_usage(parser.command_name); + code = YICES_EXIT_USAGE; + goto exit; + } + nthreads = v; + break; case incremental_opt: incremental = true; @@ -527,6 +532,10 @@ static void parse_command_line(int argc, char *argv[]) { smt2_model_format = false; break; + case dump_model_opt: + dump_models = true; + break; + case bvdecimal_opt: bvdecimal = true; break; @@ -721,7 +730,17 @@ static void parse_command_line(int argc, char *argv[]) { exit(code); } -static void setup_mcsat(void) { +static void setup_options(void) { + aval_t aval_true; + + aval_true = attr_vtbl_symbol(__smt2_globals.avtbl, "true"); + + if (dump_models) { + smt2_set_option(":dump-models", aval_true); + } +} + +static void setup_options_mcsat(void) { aval_t aval_true; if (mcsat) { @@ -773,7 +792,7 @@ static void setup_mcsat(void) { } } -static void setup_ef(void) { +static void setup_options_ef(void) { aval_t aval_true, aval_false; aval_true = attr_vtbl_symbol(__smt2_globals.avtbl, "true"); @@ -1100,8 +1119,9 @@ int main(int argc, char *argv[]) { } } - setup_mcsat(); - setup_ef(); + setup_options(); + setup_options_mcsat(); + setup_options_ef(); while (smt2_active()) { if (prompt) { @@ -1113,9 +1133,9 @@ int main(int argc, char *argv[]) { if (code < 0) { // syntax error if (interactive) { - flush_lexer(&lexer); + flush_lexer(&lexer); } else { - break; // exit + break; // exit } } } diff --git a/src/include/yices.h b/src/include/yices.h index 25ce01df8..c1bce6896 100644 --- a/src/include/yices.h +++ b/src/include/yices.h @@ -2483,6 +2483,7 @@ __YICES_DLLSPEC__ extern int32_t yices_bv_const_value(term_t t, int32_t val[]); __YICES_DLLSPEC__ extern int32_t yices_scalar_const_value(term_t t, int32_t *val); #ifdef __GMP_H__ __YICES_DLLSPEC__ extern int32_t yices_rational_const_value(term_t t, mpq_t q); +__YICES_DLLSPEC__ extern int32_t yices_finitefield_const_value(term_t t, mpz_t z); #endif diff --git a/src/include/yices_limits.h b/src/include/yices_limits.h index 1463fb506..288192a9c 100644 --- a/src/include/yices_limits.h +++ b/src/include/yices_limits.h @@ -60,5 +60,4 @@ */ #define YICES_MAX_BVSIZE (UINT32_MAX/16) - #endif diff --git a/src/include/yices_types.h b/src/include/yices_types.h index 278fc4980..566da5203 100644 --- a/src/include/yices_types.h +++ b/src/include/yices_types.h @@ -135,6 +135,7 @@ typedef enum term_constructor { // atomic terms YICES_BOOL_CONSTANT, // boolean constant YICES_ARITH_CONSTANT, // rational constant + YICES_ARITH_FF_CONSTANT, // finite field rational constant YICES_BV_CONSTANT, // bitvector constant YICES_SCALAR_CONSTANT, // constant of uninterpreted/scalar YICES_VARIABLE, // variable in quantifiers @@ -184,6 +185,7 @@ typedef enum term_constructor { // sums YICES_BV_SUM, // sum of pairs a * t where a is a bitvector constant (and t is a bitvector term) YICES_ARITH_SUM, // sum of pairs a * t where a is a rational (and t is an arithmetic term) + YICES_ARITH_FF_SUM, // sum of pairs a * t where a is an finite field constant (and t is an finite field arithmetic term) // products YICES_POWER_PRODUCT // power products: (t1^d1 * ... * t_n^d_n) @@ -239,6 +241,7 @@ typedef enum yval_tag { YVAL_BOOL, YVAL_RATIONAL, YVAL_ALGEBRAIC, + YVAL_FINITEFIELD, // TODO establish me in API YVAL_BV, YVAL_SCALAR, YVAL_TUPLE, @@ -344,6 +347,8 @@ typedef enum error_code { BAD_TYPE_DECREF, // added 2013/10/03 INVALID_TYPE_OP, // added 2014/12/03 INVALID_TERM_OP, // added 2014/12/04 + INVALID_FFSIZE, // added 2024/05/20 + INCOMPATIBLE_FFSIZES, // added 2024/05/20 /* * Parser errors @@ -367,6 +372,7 @@ typedef enum error_code { TYPE_MISMATCH_IN_DEF, ARITH_ERROR, BVARITH_ERROR, + INVALID_FFCONSTANT, /* diff --git a/src/io/concrete_value_printer.c b/src/io/concrete_value_printer.c index 2b5421ebc..295452e5a 100644 --- a/src/io/concrete_value_printer.c +++ b/src/io/concrete_value_printer.c @@ -48,6 +48,13 @@ static inline void vtbl_print_rational(FILE *f, rational_t *v) { q_print(f, v); } +static inline void vtbl_print_finitefield(FILE *f, value_ff_t *v) { + fputs("#f", f); + q_print(f, &v->value); + fputs("m", f); + q_print(f, &v->mod); +} + static inline void vtbl_print_algebraic(FILE *f, void *v) { #ifdef HAVE_MCSAT lp_algebraic_number_print(v, f); @@ -168,6 +175,9 @@ void vtbl_print_object(FILE *f, value_table_t *table, value_t c) { case RATIONAL_VALUE: vtbl_print_rational(f, &table->desc[c].rational); break; + case FINITEFIELD_VALUE: + vtbl_print_finitefield(f, table->desc[c].ptr); + break; case ALGEBRAIC_VALUE: vtbl_print_algebraic(f, table->desc[c].ptr); break; @@ -455,6 +465,9 @@ void vtbl_pp_object(yices_pp_t *printer, value_table_t *table, value_t c) { case ALGEBRAIC_VALUE: pp_algebraic(printer, table->desc[c].ptr); break; + case FINITEFIELD_VALUE: + pp_finitefield(printer, table->desc[c].ptr); + break; case BITVECTOR_VALUE: vtbl_pp_bitvector(printer, table->desc[c].ptr); break; diff --git a/src/io/term_printer.c b/src/io/term_printer.c index beae8291b..685892ed6 100644 --- a/src/io/term_printer.c +++ b/src/io/term_printer.c @@ -570,6 +570,7 @@ static const char * const tag2string[NUM_TERM_KINDS] = { "reserved", "constant", "arith-const", + "arith-ff-const", "bv64-const", "bv-const", "variable", @@ -581,6 +582,7 @@ static const char * const tag2string[NUM_TERM_KINDS] = { "ceil", "abs", "arith-root-atom", + "arith-ff-eq", "ite", "s-ite", "app", // function application @@ -597,6 +599,7 @@ static const char * const tag2string[NUM_TERM_KINDS] = { "div", "mod", "divides", + "arith-ff-bineq", "bool-to-bv", "bvdiv", "bvrem", @@ -613,6 +616,7 @@ static const char * const tag2string[NUM_TERM_KINDS] = { "bit", "pprod", "arith-poly", + "arith-ff-poly", "bv64-poly", "bv-poly", }; @@ -1814,6 +1818,7 @@ static const pp_open_type_t term_kind2block[NUM_TERM_KINDS] = { 0, // CONSTANT_TERM 0, // ARITH_CONSTANT + 0, // ARITH_FF_CONSTANT 0, // BV64_CONSTANT 0, // BV_CONSTANT @@ -1828,6 +1833,8 @@ static const pp_open_type_t term_kind2block[NUM_TERM_KINDS] = { PP_OPEN_ABS, // ARITH_ABS PP_OPEN_ROOT_ATOM, // ARITH_ROOT_ATOM + PP_OPEN_EQ, // ARITH_FF_EQ_ATOM + PP_OPEN_ITE, // ITE_TERM PP_OPEN_ITE, // ITE_SPECIAL PP_OPEN_PAR, // APP_TERM @@ -1844,6 +1851,9 @@ static const pp_open_type_t term_kind2block[NUM_TERM_KINDS] = { PP_OPEN_IDIV, // ARITH_IDIV PP_OPEN_IMOD, // ARITH_MOD PP_OPEN_DIVIDES, // ARITH_DIVIDES_ATOM + + PP_OPEN_EQ, // ARITH_FF_BINEQ_ATOM + PP_OPEN_BV_ARRAY, // BV_ARRAY PP_OPEN_BV_DIV, // BV_DIV PP_OPEN_BV_REM, // BV_REM @@ -1862,6 +1872,7 @@ static const pp_open_type_t term_kind2block[NUM_TERM_KINDS] = { PP_OPEN_PROD, // POWER_PRODUCT PP_OPEN_SUM, // ARITH_POLY + PP_OPEN_SUM, // ARITH_FF_POLY PP_OPEN_SUM, // BV64_POLY PP_OPEN_SUM, // BV_POLY }; @@ -2333,6 +2344,16 @@ static void pp_bvconst64_term(yices_pp_t *printer, bvconst64_term_t *d) { pp_bv64(printer, d->value, d->bitsize); } +static void pp_finitefield_term(yices_pp_t *printer, const rational_t *v, const rational_t *mod) { + value_ff_t val; + q_init(&val.mod); + q_init(&val.value); + q_set(&val.mod, mod); + q_set(&val.value, v); + pp_finitefield(printer, &val); + q_clear(&val.mod); + q_clear(&val.value); +} /* @@ -2504,6 +2525,12 @@ static void pp_term_idx(yices_pp_t *printer, term_table_t *tbl, int32_t i, int32 pp_rational(printer, rational_for_idx(tbl, i)); break; + case ARITH_FF_CONSTANT: + assert(polarity); + type_t tau = type_for_idx(tbl, i); + pp_finitefield_term(printer, rational_for_idx(tbl, i), ff_type_size(tbl->types, tau)); + break; + case BV64_CONSTANT: assert(polarity); pp_bvconst64_term(printer, bvconst64_for_idx(tbl, i)); @@ -2515,6 +2542,7 @@ static void pp_term_idx(yices_pp_t *printer, term_table_t *tbl, int32_t i, int32 break; case ARITH_EQ_ATOM: + case ARITH_FF_EQ_ATOM: op = polarity ? PP_OPEN_EQ : PP_OPEN_NEQ; pp_open_block(printer, op); pp_term_recur(printer, tbl, integer_value_for_idx(tbl, i), level - 1, true); @@ -2564,6 +2592,7 @@ static void pp_term_idx(yices_pp_t *printer, term_table_t *tbl, int32_t i, int32 case EQ_TERM: case ARITH_BINEQ_ATOM: + case ARITH_FF_BINEQ_ATOM: case BV_EQ_ATOM: op = polarity ? PP_OPEN_EQ : PP_OPEN_NEQ; pp_binary_atom(printer, tbl, op, composite_for_idx(tbl, i), level - 1); @@ -2626,6 +2655,7 @@ static void pp_term_idx(yices_pp_t *printer, term_table_t *tbl, int32_t i, int32 break; case ARITH_POLY: + case ARITH_FF_POLY: assert(polarity); pp_poly(printer, tbl, polynomial_for_idx(tbl, i), level - 1); break; diff --git a/src/io/type_printer.c b/src/io/type_printer.c index 06679825b..4e390378a 100644 --- a/src/io/type_printer.c +++ b/src/io/type_printer.c @@ -103,6 +103,11 @@ static void print_type_recur(FILE *f, type_table_t *tbl, type_t tau, int32_t lev case BITVECTOR_TYPE: fprintf(f, "(bitvector %"PRIu32")", bv_type_size(tbl, tau)); break; + case FF_TYPE: + fprintf(f, "(finitefield "); + q_print(f, ff_type_size(tbl, tau)); + fprintf(f, ")"); + break; case SCALAR_TYPE: fprintf(f, "(enum!%"PRId32" %"PRIu32")", tau, scalar_type_cardinal(tbl, tau)); break; @@ -333,6 +338,11 @@ void print_type_table(FILE *f, type_table_t *tbl) { case BITVECTOR_TYPE: fprintf(f, "(bitvector %"PRIu32")\n", bv_type_size(tbl, i)); break; + case FF_TYPE: + fprintf(f, "(finitefield"); + q_print(f, ff_type_size(tbl, i)); + fprintf(f, ")\n"); + break; case SCALAR_TYPE: fprintf(f, "(enum, card = %"PRIu32")\n", scalar_type_cardinal(tbl, i)); break; @@ -447,6 +457,12 @@ static void pp_type_recur(yices_pp_t *printer, type_table_t *tbl, type_t tau, in pp_close_block(printer, true); break; + case FF_TYPE: + pp_open_block(printer, PP_OPEN_FF_TYPE); + pp_rational(printer, ff_type_size(tbl, tau)); + pp_close_block(printer, true); + break; + case SCALAR_TYPE: case UNINTERPRETED_TYPE: if (name != NULL) { diff --git a/src/io/yices_pp.c b/src/io/yices_pp.c index 0ffb3bc41..a386e63b2 100644 --- a/src/io/yices_pp.c +++ b/src/io/yices_pp.c @@ -161,13 +161,14 @@ static const pp_standard_block_t standard_block[NUM_STANDARD_BLOCKS] = { /* * Table of non-standard blocks */ -#define NUM_NONSTANDARD_BLOCKS 15 +#define NUM_NONSTANDARD_BLOCKS 18 static const pp_nonstandard_block_t nonstandard_block[NUM_NONSTANDARD_BLOCKS] = { { PP_OPEN, "", PP_HMT_LAYOUT, 0, 1, 1 }, { PP_OPEN_PAR, "", PP_HMT_LAYOUT, PP_TOKEN_PAR_MASK, 1, 1 }, { PP_OPEN_VPAR, "", PP_V_LAYOUT, PP_TOKEN_PAR_MASK, 1, 1 }, { PP_OPEN_BV_TYPE, "bitvector", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0 }, + { PP_OPEN_FF_TYPE, "finitefield", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0 }, { PP_OPEN_CONST_DEF, "constant", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0 }, { PP_OPEN_UNINT_DEF, "unint", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0 }, { PP_OPEN_VAR_DEF, "var", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0 }, @@ -177,6 +178,8 @@ static const pp_nonstandard_block_t nonstandard_block[NUM_NONSTANDARD_BLOCKS] = { PP_OPEN_FUNCTION, "function ", PP_V_LAYOUT, PP_TOKEN_PAR_MASK, 1, 1 }, { PP_OPEN_SMT2_BV_DEC, "_ bv", PP_H_LAYOUT, PP_TOKEN_PAR_MASK, 0, 0 }, { PP_OPEN_SMT2_BV_TYPE, "_ BitVec", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0}, + { PP_OPEN_SMT2_FF_DEC, "_ ff", PP_H_LAYOUT, PP_TOKEN_PAR_MASK, 0, 0 }, + { PP_OPEN_SMT2_FF_TYPE, "_ FiniteField", PP_H_LAYOUT, PP_TOKEN_DEF_MASK, 0, 0}, { PP_OPEN_SMT2_MODEL, "model", PP_T_LAYOUT, PP_TOKEN_DEF_MASK, 2, 2 }, { PP_OPEN_SMT2_DEF, "define-fun", PP_HMT_LAYOUT, PP_TOKEN_DEF_MASK, 2, 2 }, }; @@ -284,11 +287,20 @@ static void build_mpq(string_buffer_t *b, mpq_t q) { string_buffer_close(b); } -static void build_rational(string_buffer_t *b, rational_t *q) { +static void build_rational(string_buffer_t *b, const rational_t *q) { string_buffer_append_rational(b, q); string_buffer_close(b); } +static void build_finitefield(string_buffer_t *b, mpz_t value, mpz_t mod) { + string_buffer_append_char(b, '#'); + string_buffer_append_char(b, 'f'); + string_buffer_append_mpz(b, value); + string_buffer_append_char(b, 'm'); + string_buffer_append_mpz(b, mod); + string_buffer_close(b); +} + // prefix = "0b" static void build_bv(string_buffer_t *b, uint32_t *bv, uint32_t n) { assert(0 < n); @@ -463,6 +475,10 @@ static const char *get_string(yices_pp_t *printer, pp_atomic_token_t *tk) { build_rational(buffer, &atm->data.rat); s = buffer->data; break; + case PP_FINITEFIELD_ATOM: + build_finitefield(buffer, atm->data.ff.value, atm->data.ff.mod); + s = buffer->data; + break; case PP_BV64_ATOM: build_bv64(buffer, atm->data.bv64.bv, atm->data.bv64.nbits); s = buffer->data; @@ -548,6 +564,11 @@ static void free_atomic_token(yices_pp_t *printer, pp_atomic_token_t *tk) { q_clear(&atm->data.rat); break; + case PP_FINITEFIELD_ATOM: + mpz_clear(atm->data.ff.value); + mpz_clear(atm->data.ff.mod); + break; + case PP_STRING_ATOM: if (atm->data.string.cloned) { safe_free((void*) atm->data.string.string); @@ -1004,7 +1025,7 @@ void pp_mpq(yices_pp_t *printer, mpq_t q) { pp_push_token(&printer->pp, tk); } -void pp_rational(yices_pp_t *printer, rational_t *q) { +void pp_rational(yices_pp_t *printer, const rational_t *q) { pp_atom_t *atom; void *tk; string_buffer_t *buffer; @@ -1024,7 +1045,32 @@ void pp_rational(yices_pp_t *printer, rational_t *q) { pp_push_token(&printer->pp, tk); } -void pp_algebraic(yices_pp_t *printer, void *a) { +void pp_finitefield(yices_pp_t *printer, const value_ff_t *v) { + pp_atom_t *atom; + void *tk; + string_buffer_t *buffer; + uint32_t n; + + buffer = &printer->buffer; + assert(string_buffer_length(buffer) == 0); + build_rational(buffer, &v->value); + build_rational(buffer, &v->mod); + n = string_buffer_length(buffer); + string_buffer_reset(buffer); + + atom = new_atom(printer); + // finite field constants are printed as #f...m... so + // the length is n+3 + tk = init_atomic_token(&atom->tk, n+3, PP_FINITEFIELD_ATOM); + mpz_init(atom->data.ff.value); + mpz_init(atom->data.ff.mod); + q_get_mpz(&v->value, atom->data.ff.value); + q_get_mpz(&v->mod, atom->data.ff.mod); + + pp_push_token(&printer->pp, tk); +} + +void pp_algebraic(yices_pp_t *printer, const void *a) { #ifdef HAVE_MCSAT pp_atom_t *atom; void *tk; @@ -1367,6 +1413,9 @@ void pp_object(yices_pp_t *printer, value_table_t *table, value_t c) { case ALGEBRAIC_VALUE: pp_algebraic(printer, table->desc[c].ptr); break; + case FINITEFIELD_VALUE: + pp_finitefield(printer, table->desc[c].ptr); + break; case BITVECTOR_VALUE: pp_bitvector(printer, table->desc[c].ptr); break; diff --git a/src/io/yices_pp.h b/src/io/yices_pp.h index ec84dfda0..9033ce59d 100644 --- a/src/io/yices_pp.h +++ b/src/io/yices_pp.h @@ -56,6 +56,7 @@ typedef enum pp_atom_type { PP_UINT32_ATOM, // unsigned integer PP_DOUBLE_ATOM, // double number PP_RATIONAL_ATOM, // rational + PP_FINITEFIELD_ATOM, // finite field PP_BV64_ATOM, // bitvector constant stored in a 64bit unsigned integer PP_BV_ATOM, // bitvector constant stored in an array of words PP_BV_ZERO_ATOM, // bitvector constant 0b00...00 @@ -90,6 +91,11 @@ typedef struct pp_id_s { bool cloned; } pp_id_t; +typedef struct pp_ff_s { + mpz_t value; + mpz_t mod; +} pp_ff_t; + typedef struct pp_bv64_s { uint64_t bv; uint32_t nbits; @@ -139,6 +145,7 @@ typedef struct pp_atom_s { uint32_t u32; double dbl; rational_t rat; + pp_ff_t ff; pp_bv64_t bv64; pp_bv_t bv; pp_qstr_t qstr; @@ -170,6 +177,7 @@ typedef enum { PP_OPEN_VPAR, // empty label, open parenthesis, V layout PP_OPEN_BV_TYPE, + PP_OPEN_FF_TYPE, PP_OPEN_FUN_TYPE, PP_OPEN_TUPLE_TYPE, @@ -240,6 +248,8 @@ typedef enum { // more for the SMT2 model syntax PP_OPEN_SMT2_BV_DEC, // (_ bv... ..) PP_OPEN_SMT2_BV_TYPE, // (_ BitVec ...) + PP_OPEN_SMT2_FF_DEC, // (_ ff... ..) + PP_OPEN_SMT2_FF_TYPE, // (_ FiniteField ...) PP_OPEN_SMT2_MODEL, // (model ...) PP_OPEN_SMT2_DEF, // (define-fun ...) PP_OPEN_SMT2_STORE, // (store ) @@ -390,9 +400,10 @@ extern void pp_int32(yices_pp_t *printer, int32_t x); extern void pp_uint32(yices_pp_t *printer, uint32_t x); extern void pp_mpz(yices_pp_t *printer, mpz_t z); extern void pp_mpq(yices_pp_t *printer, mpq_t q); -extern void pp_rational(yices_pp_t *printer, rational_t *q); +extern void pp_rational(yices_pp_t *printer, const rational_t *q); extern void pp_bv64(yices_pp_t *printer, uint64_t bv, uint32_t n); -extern void pp_algebraic(yices_pp_t *printer, void *a); +extern void pp_finitefield(yices_pp_t *printer, const value_ff_t *v); +extern void pp_algebraic(yices_pp_t *printer, const void *a); /* * String and bv atoms without cloning diff --git a/src/mcsat/bv/bv_bdd_manager.c b/src/mcsat/bv/bv_bdd_manager.c index 2555db71f..061f34784 100644 --- a/src/mcsat/bv/bv_bdd_manager.c +++ b/src/mcsat/bv/bv_bdd_manager.c @@ -935,7 +935,7 @@ void bv_bdd_manager_compute_bdd(bv_bdd_manager_t* bddm, term_t t) { BDD** bv_bdd_manager_get_term_bdds(bv_bdd_manager_t* bddm, term_t t, uint32_t bitsize) { uint32_t i; - term_t t_recompute; + term_t t_recompute = NULL_TERM; assert(bddm->visited.nelems == 0); assert(bddm->bdd_recompute.size == 0); diff --git a/src/mcsat/bv/bv_plugin.c b/src/mcsat/bv/bv_plugin.c index 2b234a48d..239ab1bf5 100644 --- a/src/mcsat/bv/bv_plugin.c +++ b/src/mcsat/bv/bv_plugin.c @@ -80,11 +80,8 @@ typedef struct { /** Exception handler */ jmp_buf* exception; - /** Map from constraint variables to the constraint_unit_info_t enum */ - int_hmap_t constraint_unit_info; - - /** Map from constraint variables to the variables they are unit in */ - int_hmap_t constraint_unit_var; + /** The unit info */ + constraint_unit_info_t unit_info; /** Last variable that was decided, but yet unprocessed */ variable_t last_decided_and_unprocessed; @@ -149,8 +146,9 @@ void bv_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) { // Construct the watch-list data structures watch_list_manager_construct(&bv->wlm, bv->ctx->var_db); - init_int_hmap(&bv->constraint_unit_info, 0); - init_int_hmap(&bv->constraint_unit_var, 0); + + // Construct the unit info data structures + constraint_unit_info_init(&bv->unit_info); init_int_hmap(&bv->variable_propagation_type, 0); @@ -209,8 +207,7 @@ void bv_plugin_destruct(plugin_t* plugin) { } watch_list_manager_destruct(&bv->wlm); - delete_int_hmap(&bv->constraint_unit_info); - delete_int_hmap(&bv->constraint_unit_var); + constraint_unit_info_destruct(&bv->unit_info); delete_int_hmap(&bv->variable_propagation_type); scope_holder_destruct(&bv->scope); bv_feasible_set_db_delete(bv->feasible); @@ -233,68 +230,6 @@ bool bv_plugin_has_assignment(const bv_plugin_t* bv, variable_t x) { return trail_has_value(bv->ctx->trail, x) && trail_get_index(bv->ctx->trail, x) < bv->trail_i; } -/** - * Setting status of constraint: if value is CONSTRAINT_UNIT, then unit_var is the variable in which constraint is unit; - * otherwise unit_var is variable_null - */ - -static -void bv_plugin_set_unit_info(bv_plugin_t* bv, variable_t constraint, variable_t unit_var, constraint_unit_info_t value) { - int_hmap_pair_t* find = NULL; - int_hmap_pair_t* unit_find = NULL; - - // Add unit tag - find = int_hmap_find(&bv->constraint_unit_info, constraint); - if (find == NULL) { - // First time, just set - int_hmap_add(&bv->constraint_unit_info, constraint, value); - } else { - assert(find->val != value); - find->val = value; - } - - // Add unit variable - unit_find = int_hmap_find(&bv->constraint_unit_var, constraint); - if (value == CONSTRAINT_UNIT) { - if (unit_find == NULL) { - int_hmap_add(&bv->constraint_unit_var, constraint, unit_var); - } else { - unit_find->val = unit_var; - } - } else { - assert(unit_var == variable_null); - if (unit_find != NULL) { - unit_find->val = variable_null; - } - } -} - -/** - * Getting status of constraint: if return value is CONSTRAINT_UNIT, - * then bv_plugin_get_unit_var returns the variable in which constraint is unit - * (otherwise it returns variable_null) - */ - -static -constraint_unit_info_t bv_plugin_get_unit_info(bv_plugin_t* bv, variable_t constraint) { - int_hmap_pair_t* find = int_hmap_find(&bv->constraint_unit_info, constraint); - if (find == NULL) { - return CONSTRAINT_UNKNOWN; - } else { - return find->val; - } -} - -static -variable_t bv_plugin_get_unit_var(bv_plugin_t* bv, variable_t constraint) { - int_hmap_pair_t* find = int_hmap_find(&bv->constraint_unit_var, constraint); - if (find == NULL) { - return variable_null; - } else { - return find->val; - } -} - /** * Comparing variables; used for the creation of a watch list, which is initially sorted with this function. * the two initial watched variables are the two smallest variables according to this function. @@ -411,7 +346,7 @@ void bv_plugin_get_notified_term_subvariables(bv_plugin_t* bv, term_t constraint // Get the current term term_t current = generic_heap_get_min(&bv->visit_heap); assert(is_pos_term(current)); - uint32_t current_bump = int_hmap_find(&bv->visited_cache, current)->val; + int32_t current_bump = int_hmap_find(&bv->visited_cache, current)->val; if (ctx_trace_enabled(bv->ctx, "mcsat::new_term")) { ctx_trace_printf(bv->ctx, "bv_plugin_get_variables: "); @@ -821,7 +756,7 @@ void bv_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) // Check the current status of the constraint variable_t top_var = t_var_list->data[0]; - constraint_unit_info_t unit_status = CONSTRAINT_UNKNOWN; + constraint_unit_state_t unit_status = CONSTRAINT_UNKNOWN; if (bv_plugin_has_assignment(bv, top_var)) { // All variables assigned, unit_status = CONSTRAINT_FULLY_ASSIGNED; @@ -831,7 +766,7 @@ void bv_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) } // Set the status of the constraint - bv_plugin_set_unit_info(bv, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status); + constraint_unit_info_set(&bv->unit_info, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status); // Process the constraint if it needs to be switch (unit_status) { @@ -955,10 +890,10 @@ void bv_plugin_propagate_var(bv_plugin_t* bv, variable_t x, trail_token_t* prop) // - if fully assigned, we propagate it based on value (or check that it is correct) // - otherwise cstr is unit in vars[0] and we need to update the feasibility if (!bv_plugin_has_assignment(bv, cstr_vars[0])) { - bv_plugin_set_unit_info(bv, cstr, cstr_vars[0], CONSTRAINT_UNIT); + constraint_unit_info_set(&bv->unit_info, cstr, cstr_vars[0], CONSTRAINT_UNIT); bv_plugin_process_unit_constraint(bv, prop, cstr, cstr_vars[0]); } else { - bv_plugin_set_unit_info(bv, cstr, variable_null, CONSTRAINT_FULLY_ASSIGNED); + constraint_unit_info_set(&bv->unit_info, cstr, variable_null, CONSTRAINT_FULLY_ASSIGNED); bv_plugin_process_fully_assigned_constraint(bv, prop, cstr); } @@ -1034,18 +969,18 @@ void bv_plugin_pop(plugin_t* plugin) { remove_iterator_construct(&it, &bv->wlm, x); while (!remove_iterator_done(&it)) { variable_t cstr = remove_iterator_get_constraint(&it); - constraint_unit_info_t unit_info = bv_plugin_get_unit_info(bv, cstr); + constraint_unit_state_t unit_info = constraint_unit_info_get(&bv->unit_info, cstr); switch (unit_info) { case CONSTRAINT_UNKNOWN: // Nothing to do break; case CONSTRAINT_UNIT: // If it was unit it becomes not unit - bv_plugin_set_unit_info(bv, cstr, variable_null, CONSTRAINT_UNKNOWN); + constraint_unit_info_set(&bv->unit_info, cstr, variable_null, CONSTRAINT_UNKNOWN); break; case CONSTRAINT_FULLY_ASSIGNED: // It is unit now - bv_plugin_set_unit_info(bv, cstr, x, CONSTRAINT_UNIT); + constraint_unit_info_set(&bv->unit_info, cstr, x, CONSTRAINT_UNIT); break; } remove_iterator_next_and_keep(&it); @@ -1070,7 +1005,7 @@ void bv_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide, boo bv_plugin_t* bv = (bv_plugin_t*) plugin; assert(!trail_has_value(bv->ctx->trail, x)); - const mcsat_value_t* v = bv_feasible_set_db_pick_value(bv->feasible, x);; + const mcsat_value_t* v = bv_feasible_set_db_pick_value(bv->feasible, x); if (ctx_trace_enabled(bv->ctx, "mcsat::bv::decide")) { ctx_trace_printf(bv->ctx, "bv_plugin_decide: "); @@ -1301,12 +1236,12 @@ void bv_plugin_new_lemma_notify(plugin_t* plugin, ivector_t* lemma, trail_token_ term_t atom = unsigned_term(literal); variable_t atom_var = variable_db_get_variable_if_exists(bv->ctx->var_db, atom); assert(atom_var != variable_null); - if (bv_plugin_get_unit_info(bv, atom_var) != CONSTRAINT_UNIT) { + if (constraint_unit_info_get(&bv->unit_info, atom_var) != CONSTRAINT_UNIT) { // Not unit is_unit = false; } else { // Unit, check if same variable - variable_t atom_unit_var = bv_plugin_get_unit_var(bv, atom_var); + variable_t atom_unit_var = constraint_unit_info_get_unit_var(&bv->unit_info, atom_var); if (unit_var == variable_null) { unit_var = atom_unit_var; } else if (unit_var != atom_unit_var) { @@ -1362,13 +1297,12 @@ void bv_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) { // Feasible sets: everything asserted is in the trail, variables are // also marked by the watch manager... nothing to do - // Unit information (constraint_unit_info, constraint_unit_var) - gc_info_sweep_int_hmap_keys(gc_vars, &bv->constraint_unit_info); - gc_info_sweep_int_hmap_keys(gc_vars, &bv->constraint_unit_var); - // Propagation type gc_info_sweep_int_hmap_keys(gc_vars, &bv->variable_propagation_type); + // Unit information + constraint_unit_info_gc_sweep(&bv->unit_info, gc_vars); + // Watch list manager watch_list_manager_gc_sweep_lists(&bv->wlm, gc_vars); } diff --git a/src/mcsat/ff/ff_feasible_set_db.c b/src/mcsat/ff/ff_feasible_set_db.c new file mode 100644 index 000000000..253fa7c53 --- /dev/null +++ b/src/mcsat/ff/ff_feasible_set_db.c @@ -0,0 +1,579 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include + +#include +#include +#include +#include +#include + +#include "mcsat/ff/ff_feasible_set_db.h" +#include "utils/int_vectors.h" +#include "utils/ptr_hash_map.h" +#include "mcsat/tracing.h" +#include "mcsat/variable_db.h" +#include "mcsat/utils/scope_holder.h" +#include "ff_plugin_internal.h" +#include "utils/int_array_sort2.h" + + +/** + * Element in the list. Each element contains a pointer to the previous + * version, the reason for the update (reason) and its feasible set, and + * the new feasible set. + */ +typedef struct { + /** Next element */ + uint32_t prev; + /** Reasons for the update, if one then constraint, otherwise disjunction */ + variable_t* reasons; + /** Size of the reasons */ + uint32_t reasons_size; + /** The new total feasible set (i.e. feasible set of all asserted constraints) */ + lp_feasibility_set_int_t* feasible_set; + /** The feasible set of the reason (feasible = feasible intersect this) */ + lp_feasibility_set_int_t* reason_feasible_set; + +} feasibility_list_element_t; + +struct ff_feasible_set_db_struct { + /** Elements of the lists */ + feasibility_list_element_t* memory; + + /** The currently occupied memory size */ + uint32_t memory_size; + + /** The capacity of the memory */ + uint32_t memory_capacity; + + /** Map from variables to the first element (current feasible set) */ + int_hmap_t var_to_feasible_set_map; + + /** Variables that were updated, so we can backtrack */ + ivector_t updates; + + /** Size of the updates array, so that we can backtrack */ + uint32_t updates_size; + + /** All variables that were fixed */ + ivector_t fixed_variables; + + /** Size of the fixed variables array, for backtracking */ + uint32_t fixed_variable_size; + + /** Index into the fixed variables */ + uint32_t fixed_variables_i; + + /** Scope for push/pop */ + scope_holder_t scope; + + /** the plugin */ + ff_plugin_t* plugin; +}; + +static +uint32_t feasible_set_db_get_index(const ff_feasible_set_db_t* db, variable_t x) { + int_hmap_pair_t* find = int_hmap_find(&db->var_to_feasible_set_map, x); + if (find == NULL) { + return 0; + } else { + return find->val; + } +} + +static +void ff_feasible_set_element_delete(feasibility_list_element_t *element) { + // Deallocate allocated data + lp_feasibility_set_int_t* s1 = element->feasible_set; + lp_feasibility_set_int_t* s2 = element->reason_feasible_set; + lp_feasibility_set_int_delete(s1); + if (s1 != s2) { + lp_feasibility_set_int_delete(s2); + } + safe_free(element->reasons); +} + +#define INITIAL_DB_SIZE 100 + +/** Create a new database */ +ff_feasible_set_db_t* ff_feasible_set_db_new(ff_plugin_t* plugin) { + ff_feasible_set_db_t* db = safe_malloc(sizeof(ff_feasible_set_db_t)); + + db->memory_size = 1; + db->memory_capacity = INITIAL_DB_SIZE; + db->memory = safe_malloc(sizeof(feasibility_list_element_t) * db->memory_capacity); + + init_int_hmap(&db->var_to_feasible_set_map, 0); + init_ivector(&db->updates, 0); + init_ivector(&db->fixed_variables, 0); + + db->updates_size = 0; + db->fixed_variable_size = 0; + db->fixed_variables_i = 0; + + scope_holder_construct(&db->scope); + + db->plugin = plugin; + + return db; +} + +/** Delete the database */ +void ff_feasible_set_db_delete(ff_feasible_set_db_t* db) { + // Delete the feasible sets + // Start from 1, 0 is special. + for (uint32_t i = 1; i < db->memory_size; ++ i) { + ff_feasible_set_element_delete(db->memory + i); + } + // Delete the other stuff + delete_int_hmap(&db->var_to_feasible_set_map); + delete_ivector(&db->updates); + delete_ivector(&db->fixed_variables); + scope_holder_destruct(&db->scope); + + // Free the memory + safe_free(db->memory); + safe_free(db); +} + +lp_feasibility_set_int_t* ff_feasible_set_db_get(ff_feasible_set_db_t* db, variable_t x) { + uint32_t index = feasible_set_db_get_index(db, x); + if (index == 0) { + return NULL; + } else { + return db->memory[index].feasible_set; + } +} + +/** Print the feasible sets of given variable */ +void ff_feasible_set_db_print_var(ff_feasible_set_db_t* db, variable_t var, FILE* out) { + fprintf(out, "Feasible sets of "); + variable_db_print_variable(db->plugin->ctx->var_db, var, out); + fprintf(out, " :\n"); + uint32_t index = feasible_set_db_get_index(db, var); + while (index != 0) { + feasibility_list_element_t* current = db->memory + index; + fprintf(out, "\t"); + lp_feasibility_set_int_print(current->feasible_set, out); + fprintf(out, "\n\t\t"); + lp_feasibility_set_int_print(current->reason_feasible_set, out); + fprintf(out, "\n"); + if (current->reasons_size > 1) { + fprintf(out, "\t\tDue to lemma\n"); + } else { + fprintf(out, "\t\tDue to "); + term_t reason_term = variable_db_get_term(db->plugin->ctx->var_db, current->reasons[0]); + term_print_to_file(out, db->plugin->ctx->terms, reason_term); + if (term_type_kind(db->plugin->ctx->terms, reason_term) == BOOL_TYPE) { + // Otherwise it's a term evaluation, always true + fprintf(out, " assigned to %s\n", trail_get_boolean_value(db->plugin->ctx->trail, current->reasons[0]) ? "true" : "false"); + } + } + index = current->prev; + } +} + +/** Print the feasible set database */ +void ff_feasible_set_db_print(ff_feasible_set_db_t* db, FILE* out) { + int_hmap_pair_t* it; + for (it = int_hmap_first_record(&db->var_to_feasible_set_map); it != NULL; it = int_hmap_next_record(&db->var_to_feasible_set_map, it)) { + + variable_t var = it->key; + fprintf(out, "Feasible sets of "); + variable_db_print_variable(db->plugin->ctx->var_db, var, out); + fprintf(out, " :\n"); + if (trail_has_value(db->plugin->ctx->trail, var)) { + fprintf(out, "\tassigned to: "); + const mcsat_value_t* var_value = trail_get_value(db->plugin->ctx->trail, var); + mcsat_value_print(var_value, out); + fprintf(out, "\n"); + } + + uint32_t index = it->val; + while (index != 0) { + feasibility_list_element_t* current = db->memory + index; + fprintf(out, "\t"); + lp_feasibility_set_int_print(db->memory[index].feasible_set, out); + fprintf(out, "\n\t\t"); + lp_feasibility_set_int_print(db->memory[index].reason_feasible_set, out); + fprintf(out, "\n"); + index = current->prev; + } + } +} + +static inline +void ff_feasible_set_db_ensure_memory(ff_feasible_set_db_t* db) { + if (db->memory_size >= db->memory_capacity) { + db->memory_capacity = db->memory_capacity + db->memory_capacity / 2; + db->memory = safe_realloc(db->memory, db->memory_capacity * sizeof(feasibility_list_element_t)); + } + assert(db->memory_size < db->memory_capacity); +} + +bool ff_feasible_set_db_update(ff_feasible_set_db_t* db, variable_t x, lp_feasibility_set_int_t* new_set, const variable_t* reasons, size_t reasons_count) { + if (ctx_trace_enabled(db->plugin->ctx, "ff::feasible_set_db")) { + fprintf(ctx_trace_out(db->plugin->ctx), "ff_feasible_set_db_update\n"); + ff_feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); + } + + assert(db->updates_size == db->updates.size); + + bool feasible = true; + + // The one we're adding + lp_feasibility_set_int_t* intersect = NULL; + + // Intersect, if no difference, we're done + const lp_feasibility_set_int_t* old_set = ff_feasible_set_db_get(db, x); + + if (old_set != NULL) { + if (ctx_trace_enabled(db->plugin->ctx, "ff::feasible_set_db")) { + ctx_trace_printf(db->plugin->ctx, "ff_feasible_set_db_update()\n"); + ctx_trace_printf(db->plugin->ctx, "old_set = "); + lp_feasibility_set_int_print(old_set, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\nnew_set = "); + lp_feasibility_set_int_print(new_set, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\n"); + } + + assert(!lp_feasibility_set_int_is_empty(old_set)); + lp_feasibility_set_int_status_t status; + intersect = lp_feasibility_set_int_intersect_with_status(old_set, new_set, &status); + switch (status) { + case LP_FEASIBILITY_SET_INT_S1: + // old set stays + lp_feasibility_set_int_delete(new_set); + lp_feasibility_set_int_delete(intersect); + return true; + case LP_FEASIBILITY_SET_INT_S2: + case LP_FEASIBILITY_SET_INT_NEW: + // we have a new set + break; + case LP_FEASIBILITY_SET_INT_EMPTY: + feasible = false; + break; + } + } else { + feasible = !lp_feasibility_set_int_is_empty(new_set); + intersect = new_set; + } + + // Get the previous + uint32_t prev = feasible_set_db_get_index(db, x); + + // Allocate a new one + uint32_t new_index = db->memory_size; + // Allocate new element + db->memory_size ++; + ff_feasible_set_db_ensure_memory(db); + + // Set up the element + feasibility_list_element_t* new_element = db->memory + new_index; + new_element->feasible_set = intersect; + new_element->reason_feasible_set = new_set; + new_element->prev = prev; + // Reasons + new_element->reasons_size = reasons_count; + new_element->reasons = safe_malloc(sizeof(variable_t) * reasons_count); + for (uint32_t i = 0; i < reasons_count; ++ i) { + new_element->reasons[i] = reasons[i]; + } + // Add to map + int_hmap_pair_t* find = int_hmap_find(&db->var_to_feasible_set_map, x); + if (find == NULL) { + int_hmap_add(&db->var_to_feasible_set_map, x, new_index); + } else { + find->val = new_index; + } + // Add to updates list + ivector_push(&db->updates, x); + db->updates_size ++; + assert(db->updates_size == db->updates.size); + + // If fixed, put into the fixed array + if (lp_feasibility_set_int_is_point(intersect)) { + ivector_push(&db->fixed_variables, x); + db->fixed_variable_size ++; + } + + // Return whether we're feasible + return feasible; +} + +void ff_feasible_set_db_push(ff_feasible_set_db_t *db) { + scope_holder_push(&db->scope, + &db->updates_size, + &db->fixed_variable_size, + &db->fixed_variables_i, + NULL + ); +} + +void ff_feasible_set_db_pop(ff_feasible_set_db_t* db) { + if (ctx_trace_enabled(db->plugin->ctx, "ff::ff_feasible_set_db")) { + fprintf(ctx_trace_out(db->plugin->ctx), "ff_feasible_set_db_pop"); + ff_feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); + } + + scope_holder_pop(&db->scope, + &db->updates_size, + &db->fixed_variable_size, + &db->fixed_variables_i, + NULL + ); + + // Undo fixed variables + ivector_shrink(&db->fixed_variables, db->fixed_variable_size); + + // Undo updates + while (db->updates.size > db->updates_size) { + // The variable that was updated + variable_t x = ivector_last(&db->updates); + ivector_pop(&db->updates); + // Remove the element + db->memory_size --; + feasibility_list_element_t* element = db->memory + db->memory_size; + uint32_t prev = element->prev; + ff_feasible_set_element_delete(element); + // Redirect map to the previous one + int_hmap_pair_t* find = int_hmap_find(&db->var_to_feasible_set_map, x); + assert(find != NULL); + assert(find->val == db->memory_size); + find->val = prev; + } + + if (ctx_trace_enabled(db->plugin->ctx, "ff::ff_feasible_set_db")) { + ff_feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); + } +} + +static +void ff_feasible_set_quickxplain(const ff_feasible_set_db_t* db, const lp_feasibility_set_int_t* current, ivector_t* reasons, uint32_t begin, uint32_t end, ivector_t* out) { + + if (lp_feasibility_set_int_is_empty(current)) { + // Core already unsat, done + return; + } + + assert(begin < end); + if (begin + 1 == end) { + // Only one left, we keep it, since the core is still sat + ivector_push(out, reasons->data[begin]); + return; + } + + // Split: how many in first half? + uint32_t n = (end - begin) / 2; + + // Assert first half and minimize the second + lp_feasibility_set_int_t* feasible_A = lp_feasibility_set_int_new_copy(current); + for (uint32_t i = begin; i < begin + n; ++ i) { + const lp_feasibility_set_int_t* feasible_i = db->memory[reasons->data[i]].reason_feasible_set; + lp_feasibility_set_int_status_t intersect_status; + lp_feasibility_set_int_t* intersect = lp_feasibility_set_int_intersect_with_status(feasible_A, feasible_i, &intersect_status); + lp_feasibility_set_int_swap(intersect, feasible_A); + lp_feasibility_set_int_delete(intersect); + } + uint32_t old_out_size = out->size; + ff_feasible_set_quickxplain(db, feasible_A, reasons, begin + n, end, out); + lp_feasibility_set_int_delete(feasible_A); + + // Now, assert the minimized second half, and minimize the first half + lp_feasibility_set_int_t* feasible_B = lp_feasibility_set_int_new_copy(current); + for (uint32_t i = old_out_size; i < out->size; ++ i) { + const lp_feasibility_set_int_t* feasible_i = db->memory[out->data[i]].reason_feasible_set; + lp_feasibility_set_int_status_t intersect_status; + lp_feasibility_set_int_t* intersect = lp_feasibility_set_int_intersect_with_status(feasible_B, feasible_i, &intersect_status); + lp_feasibility_set_int_swap(intersect, feasible_B); + lp_feasibility_set_int_delete(intersect); + } + ff_feasible_set_quickxplain(db, feasible_B, reasons, begin, begin + n, out); + lp_feasibility_set_int_delete(feasible_B); +} + +static +void get_max_degree_max_level(const ff_plugin_t *nra, const feasibility_list_element_t* memory, uint32_t *max_deg, uint32_t *max_lvl) { + uint32_t deg = 0, lvl = 0; + + for (uint32_t i = 0; i < memory->reasons_size; ++ i) { + variable_t i_var = memory->reasons[i]; + if (trail_has_value(nra->ctx->trail, i_var)) { + uint32_t i_lvl = trail_get_level(nra->ctx->trail, i_var); + if (i_lvl > lvl) { + lvl = i_lvl; + } + } + const poly_constraint_t* i_constraint = poly_constraint_db_get(nra->constraint_db, i_var); + const lp_polynomial_t* i_poly = poly_constraint_get_polynomial(i_constraint); + uint32_t i_deg = lp_polynomial_degree(i_poly); + if (i_deg > deg) { + deg = i_deg; + } + } + + *max_deg = deg; + *max_lvl = lvl; +} + +/** Compare variables first by degree, then by level */ +static +bool compare_reasons(void *ff_plugin, int32_t r1, int32_t r2) { + const ff_plugin_t* ff = (ff_plugin_t*) ff_plugin; + const ff_feasible_set_db_t* db = ff->feasible_set_db; + + // Get max degree and max level of the reasons of both constraints + uint32_t r1_degree = 0, r2_degree = 0; + uint32_t r1_level = 0, r2_level = 0; + get_max_degree_max_level(ff, db->memory + r1, &r1_degree, &r1_level); + get_max_degree_max_level(ff, db->memory + r2, &r2_degree, &r2_level); + + // Prefer smaller degrees + if (r1_degree != r2_degree) { + return r1_degree < r2_degree; + } + + // Otherwise take the one of lower level + return r1_level < r2_level; +} + +static +void print_conflict_reasons(FILE* out, const ff_feasible_set_db_t* db, ff_plugin_t* ff, ivector_t* reason_indices) { + poly_constraint_db_t* poly_db = ff->constraint_db; + + for (uint32_t i = 0; i < reason_indices->size; ++ i) { + fprintf(out, "[%d]: ", i); + uint32_t r_i = reason_indices->data[i]; + uint32_t r_i_size = db->memory[r_i].reasons_size; + for (uint32_t j = 0; j < r_i_size; ++ j) { + if (j) fprintf(out, ", "); + variable_t r_i_var = db->memory[r_i].reasons[j]; + const poly_constraint_t* r_i_constraint = poly_constraint_db_get(poly_db, r_i_var); + poly_constraint_print(r_i_constraint, out); + } + fprintf(out, "\n"); + } +} + +static +void ff_feasible_set_filter_reason_indices(const ff_feasible_set_db_t* db, ivector_t* reasons_indices) { + ff_plugin_t *ff = db->plugin; + // The set we're trying to make empty + lp_feasibility_set_int_t* S = lp_feasibility_set_int_new_full(ff->lp_data->lp_ctx->K); + + // Sort variables by degree and trail level decreasing + int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) ff, compare_reasons); + + if (ctx_trace_enabled(db->plugin->ctx, "nra::conflict")) { + ctx_trace_printf(db->plugin->ctx, "filtering: before\n"); + print_conflict_reasons(ctx_trace_out(db->plugin->ctx), db, ff, reasons_indices); + } + + // Minimize the core + ivector_t out; + init_ivector(&out, 0); + ff_feasible_set_quickxplain(db, S, reasons_indices, 0, reasons_indices->size, &out); + ivector_swap(reasons_indices, &out); + delete_ivector(&out); + + // Sort again for consistency + int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) ff, compare_reasons); + + if (ctx_trace_enabled(db->plugin->ctx, "nra::conflict")) { + ctx_trace_printf(db->plugin->ctx, "filtering: after\n"); + print_conflict_reasons(ctx_trace_out(db->plugin->ctx), db, ff, reasons_indices); + } + + // Remove temps + lp_feasibility_set_int_delete(S); +} + +static +void ff_feasible_set_get_conflict_reason_indices(const ff_feasible_set_db_t* db, variable_t x, ivector_t* reasons_indices) { + // Go back from the top reason for x and gather the indices + uint32_t reason_index = feasible_set_db_get_index(db, x); + assert(reason_index); + while (reason_index) { + assert(reason_index); + ivector_push(reasons_indices, reason_index); + reason_index = db->memory[reason_index].prev; + } +} + +void ff_feasible_set_db_get_conflict_reasons(const ff_feasible_set_db_t* db, variable_t x, ivector_t* reasons_out, ivector_t* lemma_reasons) { + + if (ctx_trace_enabled(db->plugin->ctx, "ff::feasible_set_db")) { + ctx_trace_printf(db->plugin->ctx, "get_reasons of: "); + variable_db_print_variable(db->plugin->ctx->var_db, x, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\n"); + } + + ivector_t reasons_indices; + init_ivector(&reasons_indices, 0); + + // Get the indices of the set refinements + ff_feasible_set_get_conflict_reason_indices(db, x, &reasons_indices); + + ff_feasible_set_filter_reason_indices(db, &reasons_indices); + + // Return the conjunctive reasons + for (uint32_t i = 0; i < reasons_indices.size; ++ i) { + uint32_t set_index = reasons_indices.data[i]; + feasibility_list_element_t *element = db->memory + set_index; + if (element->reasons_size == 1) { + variable_t reason = element->reasons[0]; + assert(variable_db_is_boolean(db->plugin->ctx->var_db, reason)); + ivector_push(reasons_out, reason); + } else { + for (uint32_t j = 0; j < element->reasons_size; ++j) { + variable_t reason = element->reasons[j]; + assert(variable_db_is_boolean(db->plugin->ctx->var_db, reason)); + ivector_push(lemma_reasons, reason); + } + } + } + + delete_ivector(&reasons_indices); +} + +void ff_feasible_set_db_gc_mark(ff_feasible_set_db_t* db, gc_info_t* gc_vars) { + assert(db->plugin->ctx->trail->decision_level == db->plugin->ctx->trail->decision_level_base); + + if (gc_vars->level == 0) { + // We keep all the reasons (start from 1, 0 is not used) + uint32_t element_i, reason_i; + for (element_i = 1; element_i < db->memory_size; ++ element_i) { + feasibility_list_element_t* element = db->memory + element_i; + for (reason_i = 0; reason_i < element->reasons_size; ++ reason_i) { + gc_info_mark(gc_vars, element->reasons[reason_i]); + } + } + } +} + +variable_t ff_feasible_set_db_get_fixed(ff_feasible_set_db_t* db) { + for (; db->fixed_variables_i < db->fixed_variables.size; ++ db->fixed_variables_i) { + variable_t var = db->fixed_variables.data[db->fixed_variables_i]; + if (!trail_has_value(db->plugin->ctx->trail, var)) { + return var; + } + } + return variable_null; +} diff --git a/src/mcsat/ff/ff_feasible_set_db.h b/src/mcsat/ff/ff_feasible_set_db.h new file mode 100644 index 000000000..3f81d327f --- /dev/null +++ b/src/mcsat/ff/ff_feasible_set_db.h @@ -0,0 +1,69 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef FF_FEASIBLE_SET_DB_H +#define FF_FEASIBLE_SET_DB_H + +#include +#include + +#include "mcsat/variable_db.h" + +typedef struct ff_plugin_s ff_plugin_t; + +/** Contains the map from variables to feasible sets that can be backtracked */ +typedef struct ff_feasible_set_db_struct ff_feasible_set_db_t; + +/** Create a new database */ +ff_feasible_set_db_t* ff_feasible_set_db_new(ff_plugin_t* plugin); + +/** Delete the database */ +void ff_feasible_set_db_delete(ff_feasible_set_db_t* db); + +/** Get the feasible set of a variable */ +lp_feasibility_set_int_t* ff_feasible_set_db_get(ff_feasible_set_db_t* db, variable_t x); + +/** + * Update the feasible set of the variable with a new set. + * + * If more than one reason, it's considered a disjunctive top-level assertion (clause) + */ +bool ff_feasible_set_db_update(ff_feasible_set_db_t* db, variable_t x, lp_feasibility_set_int_t* new_set, const variable_t* reasons, size_t reasons_count); + +/** Get the reason for a conflict on x. Feasible set of x should be empty. */ +void ff_feasible_set_db_get_conflict_reasons(const ff_feasible_set_db_t* db, variable_t x, ivector_t* reasons_out, ivector_t* lemma_reasons); + +/** Push the context */ +void ff_feasible_set_db_push(ff_feasible_set_db_t* db); + +/** Pop the context */ +void ff_feasible_set_db_pop(ff_feasible_set_db_t* db); + +/** Print the feasible set database */ +void ff_feasible_set_db_print(ff_feasible_set_db_t* db, FILE* out); + +/** Print the feasible sets of given variable */ +void ff_feasible_set_db_print_var(ff_feasible_set_db_t* db, variable_t var, FILE* out); + +/** Return any fixed variables */ +variable_t ff_feasible_set_db_get_fixed(ff_feasible_set_db_t* db); + +/** Marks all the top level reasons */ +void ff_feasible_set_db_gc_mark(ff_feasible_set_db_t* db, gc_info_t* gc_vars); + +#endif /* FF_FEASIBLE_SET_DB_H */ diff --git a/src/mcsat/ff/ff_libpoly.c b/src/mcsat/ff/ff_libpoly.c new file mode 100644 index 000000000..6afa79095 --- /dev/null +++ b/src/mcsat/ff/ff_libpoly.c @@ -0,0 +1,131 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "ff_libpoly.h" + +#include "mcsat/ff/ff_plugin_internal.h" +#include "mcsat/tracing.h" + +#include +#include +#include +#include + +lp_polynomial_t* lp_polynomial_from_term_ff(ff_plugin_t* ff, term_t t, lp_integer_t* c) { + if (ctx_trace_enabled(ff->ctx, "ff::terms")) { + ctx_trace_printf(ff->ctx, "lp_polynomial_from_term: t = "); + ctx_trace_term(ff->ctx, t); + } + + assert(ff->lp_data); + lp_polynomial_t* result = lp_polynomial_from_term(ff->lp_data, t, ff->ctx->terms, c); + + if (ctx_trace_enabled(ff->ctx, "ff::terms")) { + ctx_trace_printf(ff->ctx, "lp_polynomial_from_term: result = "); + lp_polynomial_print(result, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + return result; +} + +term_t lp_polynomial_to_yices_term_ff(ff_plugin_t *ff, const lp_polynomial_t *lp_p) { + if (ctx_trace_enabled(ff->ctx, "ff::terms")) { + ctx_trace_printf(ff->ctx, "lp_polynomial_to_yices_term("); + lp_polynomial_print(lp_p, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, ")\n"); + } + + assert(ff->lp_data); + term_t result = lp_polynomial_to_yices_arith_ff_term(ff->lp_data, lp_p, ff->ctx->terms, &ff->buffer); + + if (ctx_trace_enabled(ff->ctx, "ff::terms")) { + ctx_trace_printf(ff->ctx, "lp_polynomial_to_yices_term("); + lp_polynomial_print(lp_p, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, ") => [%d] ", result); + ctx_trace_term(ff->ctx, result); + } + + return result; +} + +void ff_poly_constraint_add(ff_plugin_t *ff, variable_t constraint_var) { + if (poly_constraint_db_has(ff->constraint_db, constraint_var)) { + // Already added + return; + } + + poly_constraint_t* cstr = ff_poly_constraint_create(ff, constraint_var); + + (*ff->stats.constraint) ++; + + if (ctx_trace_enabled(ff->ctx, "mcsat::new_term")) { + ctx_trace_printf(ff->ctx, "poly_constraint_add: "); + poly_constraint_print(cstr, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + poly_constraint_db_add_constraint(ff->constraint_db, constraint_var, cstr); +} + +poly_constraint_t* ff_poly_constraint_create(ff_plugin_t *ff, variable_t constraint_var) { + term_t constraint_var_term; + + // Constraint components + lp_polynomial_t* cstr_polynomial; + lp_sign_condition_t sgn_condition; + + // Context + variable_db_t* var_db = ff->ctx->var_db; + term_table_t* terms = ff->ctx->terms; + + // Get the term of the variable + constraint_var_term = variable_db_get_term(var_db, constraint_var); + + // Depending on the kind, make the constraints + switch (term_kind(terms, constraint_var_term)) { + case ARITH_FF_EQ_ATOM: { + // p == 0 + term_t t1 = finitefield_atom_arg(terms, constraint_var_term); + cstr_polynomial = lp_polynomial_from_term_ff(ff, t1, NULL); + sgn_condition = LP_SGN_EQ_0; + break; + } + case EQ_TERM: + case ARITH_FF_BINEQ_ATOM: { + // LHS = RHS + term_t t1 = composite_term_arg(terms, constraint_var_term, 0); + term_t t2 = composite_term_arg(terms, constraint_var_term, 1); + // Get the polynomials + cstr_polynomial = lp_polynomial_from_term_ff(ff, t1, NULL); + lp_polynomial_t* tmp = lp_polynomial_from_term_ff(ff, t2, NULL); + lp_polynomial_sub(cstr_polynomial, cstr_polynomial, tmp); + // p1 = p2 + sgn_condition = LP_SGN_EQ_0; + // Remove temps + lp_polynomial_delete(tmp); + break; + } + default: + assert(0); + return NULL; + } + + // Result constraint + return poly_constraint_new_regular(cstr_polynomial, sgn_condition); +} diff --git a/src/mcsat/ff/ff_libpoly.h b/src/mcsat/ff/ff_libpoly.h new file mode 100644 index 000000000..d0f7b47ce --- /dev/null +++ b/src/mcsat/ff/ff_libpoly.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef FF_LIBPOLY_H_ +#define FF_LIBPOLY_H_ + +#include "mcsat/mcsat_types.h" +#include "mcsat/utils/lp_constraint_db.h" + +#include + +typedef struct ff_plugin_s ff_plugin_t; + +/** + * Create a libpoly polynomial from a yices term. Returns the polynomial + * lp_p and a positive integer constant c, such that lp_p = p * c. If c is + * NULL it is ignored. + */ +lp_polynomial_t* lp_polynomial_from_term_ff(ff_plugin_t* ff, term_t t, lp_integer_t* c); + +/** + * Get yices term from polynomial (FF plugin wrapper). + */ +term_t lp_polynomial_to_yices_term_ff(ff_plugin_t *ff, const lp_polynomial_t *lp_p); + +/** Add a new constraint */ +void ff_poly_constraint_add(ff_plugin_t *ff, variable_t constraint_var); + +/** Create a new constraint */ +poly_constraint_t* ff_poly_constraint_create(ff_plugin_t *ff, variable_t constraint_var); + +#endif /* FF_LIBPOLY_H_ */ diff --git a/src/mcsat/ff/ff_plugin.c b/src/mcsat/ff/ff_plugin.c new file mode 100644 index 000000000..373a90104 --- /dev/null +++ b/src/mcsat/ff/ff_plugin.c @@ -0,0 +1,1122 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +/* + * Anything that includes "yices.h" requires these macros. + * Otherwise the code doesn't build on Windows or Cygwin. + */ +#if defined(CYGWIN) || defined(MINGW) +#ifndef __YICES_DLLSPEC__ +#define __YICES_DLLSPEC__ __declspec(dllexport) +#endif +#endif + + +#include "context/context_types.h" + +#include "mcsat/ff/ff_plugin.h" +#include "mcsat/ff/ff_feasible_set_db.h" +#include "mcsat/ff/ff_plugin_internal.h" +#include "mcsat/ff/ff_plugin_explain.h" +#include "mcsat/ff/ff_libpoly.h" +#include "mcsat/tracing.h" + +#include "utils/int_array_sort2.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static +void ff_plugin_stats_init(ff_plugin_t* ff) { + // Add statistics + ff->stats.propagations = statistics_new_int(ff->ctx->stats, "mcsat::ff::propagations"); + ff->stats.conflicts = statistics_new_int(ff->ctx->stats, "mcsat::ff::conflicts"); + ff->stats.conflicts_assumption = statistics_new_int(ff->ctx->stats, "mcsat::ff::conflicts_assumption"); + ff->stats.constraints_attached = statistics_new_int(ff->ctx->stats, "mcsat::ff::constraints_attached"); + ff->stats.evaluations = statistics_new_int(ff->ctx->stats, "mcsat::ff::evaluations"); + ff->stats.constraint = statistics_new_int(ff->ctx->stats, "mcsat::ff::constraints"); + ff->stats.variable_hints = statistics_new_int(ff->ctx->stats, "mcsat::ff::variable_hints"); +} + +static +void ff_plugin_heuristics_init(ff_plugin_t* ff) { + // Initialize heuristic +} + +static +void ff_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + ff->ctx = ctx; + ff->trail_i = 0; + ff->conflict_variable = variable_null; + ff->last_decided_and_unprocessed = variable_null; + + watch_list_manager_construct(&ff->wlm, ctx->var_db); + + init_ivector(&ff->processed_variables, 0); + ff->processed_variables_size = 0; + + scope_holder_construct(&ff->scope); + + constraint_unit_info_init(&ff->unit_info); + + // Atoms + ctx->request_term_notification_by_kind(ctx, ARITH_FF_EQ_ATOM, false); + ctx->request_term_notification_by_kind(ctx, ARITH_FF_BINEQ_ATOM, false); + + // Terms + ctx->request_term_notification_by_kind(ctx, ARITH_FF_CONSTANT, false); + ctx->request_term_notification_by_kind(ctx, ARITH_FF_POLY, false); + ctx->request_term_notification_by_kind(ctx, POWER_PRODUCT, false); + + // Types + ctx->request_term_notification_by_type(ctx, FF_TYPE); + ctx->request_decision_calls(ctx, FF_TYPE); + + // Constraint db and libpoly are set once the field size is known + ff->constraint_db = NULL; + ff->lp_data = NULL; + ff->feasible_set_db = NULL; + + init_rba_buffer(&ff->buffer, ctx->terms->pprods); + + ff_plugin_stats_init(ff); + ff_plugin_heuristics_init(ff); +} + +static +void ff_plugin_destruct(plugin_t* plugin) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + constraint_unit_info_destruct(&ff->unit_info); + + if (ff->lp_data) { + lp_data_destruct(ff->lp_data); + assert(ff->constraint_db); + poly_constraint_db_delete(ff->constraint_db); + assert(ff->feasible_set_db); + ff_feasible_set_db_delete(ff->feasible_set_db); + } + + delete_ivector(&ff->processed_variables); + watch_list_manager_destruct(&ff->wlm); + scope_holder_destruct(&ff->scope); + delete_rba_buffer(&ff->buffer); +} + +static inline +bool ff_plugin_has_assignment(const ff_plugin_t* ff, variable_t x) { + return trail_has_value(ff->ctx->trail, x) && trail_get_index(ff->ctx->trail, x) < ff->trail_i; +} + +static inline +bool ff_plugin_trail_variable_compare(void *data, variable_t t1, variable_t t2) { + return trail_variable_compare((const mcsat_trail_t *)data, t1, t2); +} + +static inline +void ff_plugin_report_conflict(ff_plugin_t* ff, trail_token_t* prop, variable_t variable) { + prop->conflict(prop); + ff->conflict_variable = variable; + (*ff->stats.conflicts) ++; +} + +static +const mcsat_value_t* ff_plugin_constraint_evaluate(ff_plugin_t* ff, variable_t cstr_var, uint32_t* cstr_level) { + + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + assert(!trail_has_value(ff->ctx->trail, cstr_var)); + + // Check if it is a valid constraints + const poly_constraint_t* cstr = poly_constraint_db_get(ff->constraint_db, cstr_var); + if (!poly_constraint_is_valid(cstr)) { + return NULL; + } + assert(!poly_constraint_is_root_constraint(cstr)); + + // Constraint var list + variable_list_ref_t var_list_ref = watch_list_manager_get_list_of(&ff->wlm, cstr_var); + const variable_t* var_list = watch_list_manager_get_list(&ff->wlm, var_list_ref); + + // Get the timestamp and level + uint32_t cstr_timestamp = 0; + *cstr_level = ff->ctx->trail->decision_level_base; + const mcsat_trail_t* trail = ff->ctx->trail; + const variable_t* var_i = var_list; + while (*var_i != variable_null) { + if (ff_plugin_has_assignment(ff, *var_i)) { + uint32_t timestamp_i = trail_get_value_timestamp(trail, *var_i); + assert(timestamp_i > 0); + if (cstr_timestamp < timestamp_i) { + cstr_timestamp = timestamp_i; + } + uint32_t level_i = trail_get_level(trail, *var_i); + if (level_i > *cstr_level) { + *cstr_level = level_i; + } + } else { + // Doesn't evaluate + return NULL; + } + var_i ++; + } + + bool cstr_value = false; + +#if 0 + // Check the cache + int_hmap_pair_t* find_value = int_hmap_find(&ff->evaluation_value_cache, cstr_var); + int_hmap_pair_t* find_timestamp = NULL; + if (find_value != NULL) { + find_timestamp = int_hmap_find(&ff->evaluation_timestamp_cache, cstr_var); + assert(find_timestamp != NULL); + if (find_timestamp->val == cstr_timestamp) { + // Can use the cached value; + cstr_value = find_value->val; + return cstr_value ? &mcsat_value_true : &mcsat_value_false; + } + } +#endif + + // NOTE: with/without caching can change search. Some poly constraints + // do not evaluate (see ok below, but we can evaluate them in the cache) + + // Compute the evaluation + bool ok = poly_constraint_evaluate(cstr, ff->lp_data, &cstr_value); + (void) ok; + assert(ok); + (*ff->stats.evaluations) ++; + +#if 0 + // Set the cache + if (find_value != NULL) { + find_value->val = cstr_value; + find_timestamp->val = cstr_timestamp; + } else { + int_hmap_add(&ff->evaluation_value_cache, cstr_var, cstr_value); + int_hmap_add(&ff->evaluation_timestamp_cache, cstr_var, cstr_timestamp); + } +#endif + + return cstr_value ? &mcsat_value_true : &mcsat_value_false; +} + +static +void ff_plugin_process_fully_assigned_constraint(ff_plugin_t* ff, trail_token_t* prop, variable_t cstr_var) { + + uint32_t cstr_level = 0; + const mcsat_value_t* cstr_value = NULL; + + assert(!trail_has_value(ff->ctx->trail, cstr_var)); + + if (ctx_trace_enabled(ff->ctx, "ff::evaluate")) { + trail_print(ff->ctx->trail, ctx_trace_out(ff->ctx)); + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, cstr_var)); + } + + // Compute the evaluation timestamp + cstr_value = ff_plugin_constraint_evaluate(ff, cstr_var, &cstr_level); + + // Propagate + if (cstr_value) { + bool ok = prop->add_at_level(prop, cstr_var, cstr_value, cstr_level); + (void)ok; + assert(ok); + } + + if (ctx_trace_enabled(ff->ctx, "ff::evaluate")) { + trail_print(ff->ctx->trail, ctx_trace_out(ff->ctx)); + } +} + +static +void ff_plugin_set_lp_data(ff_plugin_t *ff, term_t t) { + type_t tau = term_type(ff->ctx->terms, t); + assert(is_ff_type(ff->ctx->types, tau)); + + mpz_t order; + mpz_init(order); + const rational_t *order_q = ff_type_size(ff->ctx->types, tau); + q_get_mpz(order_q, order); + + if (ff->lp_data) { + assert(ff->constraint_db); + if (!lp_data_is_order(ff->lp_data, order)) { + // currently only one finite field type is supported in MCSat + // an error is reported when two different finite field types are presented + longjmp(*ff->ctx->exception, MCSAT_EXCEPTION_UNSUPPORTED_THEORY); + } + } else { + ff->lp_data = lp_data_new(order, ff->ctx); + ff->constraint_db = poly_constraint_db_new(ff->lp_data); + ff->feasible_set_db = ff_feasible_set_db_new(ff); + } + mpz_clear(order); + + assert(ff->lp_data); + assert(ff->constraint_db); + assert(ff->feasible_set_db); +} + +static +void ff_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + term_table_t* terms = ff->ctx->terms; + + if (ctx_trace_enabled(ff->ctx, "mcsat::new_term")) { + ctx_trace_printf(ff->ctx, "ff_plugin_new_term_notify: "); + ctx_trace_term(ff->ctx, t); + } + + assert(!is_neg_term(t)); + + term_kind_t t_kind = term_kind(terms, t); + + if (t_kind == POWER_PRODUCT && !is_finitefield_term(terms, t)) { + return; + } + + // The variable + variable_t t_var = variable_db_get_variable(ff->ctx->var_db, t); + +// uninterpreted terms are just variables +// if (t_kind == UNINTERPRETED_TERM && term_type_kind(terms, t) != FF_TYPE) { +// assert(0); +// return; +// } + + int_mset_t t_variables; + int_mset_construct(&t_variables, variable_null); + ff_plugin_get_constraint_variables(ff, t, &t_variables); + + bool is_constraint = t_variables.element_list.size != 1 || t_variables.element_list.data[0] != t_var; + + if (is_constraint) { + + // Get the list of variables + ivector_t* t_variables_list = int_mset_get_list(&t_variables); + + assert(t_variables_list->size > 0); + + // Register all the variables to libpoly (these are mcsat_variables) + for (uint32_t i = 0; i < t_variables_list->size; ++ i) { + term_t tt = variable_db_get_term(ff->ctx->var_db, t_variables_list->data[i]); + ff_plugin_set_lp_data(ff, tt); + if (!lp_data_variable_has_term(ff->lp_data, tt)) { + lp_data_add_lp_variable(ff->lp_data, terms, tt); + } + } + + // Bump variables + for (uint32_t i = 0; i < t_variables_list->size; ++ i) { + variable_t x = t_variables_list->data[i]; + uint32_t deg = int_mset_contains(&t_variables, x); + ff->ctx->bump_variable_n(ff->ctx, x, deg); + } + + // Sort variables by trail index + int_array_sort2(t_variables_list->data, t_variables_list->size, (void*) ff->ctx->trail, ff_plugin_trail_variable_compare); + + if (ctx_trace_enabled(ff->ctx, "mcsat::new_term")) { + ctx_trace_printf(ff->ctx, "ff_plugin_new_term_notify: vars: \n"); + for (uint32_t i = 0; i < t_variables_list->size; ++ i) { + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, t_variables_list->data[i])); + } + } + + variable_list_ref_t var_list = watch_list_manager_new_list(&ff->wlm, t_variables_list->data, t_variables_list->size, t_var); + + // Add first variable to watch list + watch_list_manager_add_to_watch(&ff->wlm, var_list, t_variables_list->data[0]); + + // Add second variable to watch list + if (t_variables_list->size > 1) { + watch_list_manager_add_to_watch(&ff->wlm, var_list, t_variables_list->data[1]); + } + + // Check the current status of the constraint + variable_t top_var = t_variables_list->data[0]; + constraint_unit_state_t unit_status = CONSTRAINT_UNKNOWN; + if (ff_plugin_has_assignment(ff, top_var)) { + // All variables assigned, + unit_status = CONSTRAINT_FULLY_ASSIGNED; + } else { + if (t_variables_list->size == 1) { + // Single variable, unassigned => unit + unit_status = CONSTRAINT_UNIT; + } else if (ff_plugin_has_assignment(ff, t_variables_list->data[1])) { + // Second one is assigned and processed, so we're unit + unit_status = CONSTRAINT_UNIT; + } + } + + // Set the status of the constraint + constraint_unit_info_set(&ff->unit_info, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status); + + // Add the constraint to the database + ff_poly_constraint_add(ff, t_var); + + // Propagate if fully assigned + if (unit_status == CONSTRAINT_FULLY_ASSIGNED) { + ff_plugin_process_fully_assigned_constraint(ff, prop, t_var); + } + + // Stats + (*ff->stats.constraints_attached) ++; + } else { + if (t_kind == ARITH_FF_CONSTANT) { + lp_integer_t int_value; + lp_integer_construct(&int_value); + q_get_mpz(finitefield_term_desc(terms, t), &int_value); + lp_value_t lp_value; + lp_value_construct(&lp_value, LP_VALUE_INTEGER, &int_value); + mcsat_value_t mcsat_value; + mcsat_value_construct_lp_value(&mcsat_value, &lp_value); + prop->add_at_level(prop, t_var, &mcsat_value, ff->ctx->trail->decision_level_base); + mcsat_value_destruct(&mcsat_value); + lp_value_destruct(&lp_value); + lp_integer_destruct(&int_value); + } else { + // create variable for t if not existent + variable_db_get_variable(ff->ctx->var_db, t); + ff_plugin_set_lp_data(ff, t); + // register lp_variable for t if not existent + if (!lp_data_variable_has_term(ff->lp_data, t)) { + lp_data_add_lp_variable(ff->lp_data, terms, t); + } + } + } + + // Remove the variables vector + int_mset_destruct(&t_variables); +} + +static +lp_feasibility_set_int_t* ff_plugin_get_feasible_set(ff_plugin_t *ff, variable_t cstr_var, variable_t x, bool is_negated) { + const poly_constraint_t* constraint = poly_constraint_db_get(ff->constraint_db, cstr_var); + lp_assignment_t *m = ff->lp_data->lp_assignment; + + assert(!poly_constraint_is_root_constraint(constraint)); + assert(constraint->sgn_condition == LP_SGN_EQ_0); + assert(lp_polynomial_is_univariate_m(constraint->polynomial, m)); + assert(lp_data_get_ring(ff->lp_data) == lp_polynomial_get_context(constraint->polynomial)->K); +#ifndef NDEBUG + { + lp_variable_t lp_x = lp_data_get_lp_variable_from_term(ff->lp_data, variable_db_get_term(ff->ctx->var_db, x)); + lp_variable_list_t list; + lp_variable_list_construct(&list); + lp_polynomial_get_variables(constraint->polynomial, &list); + assert(lp_variable_list_contains(&list, lp_x)); + lp_variable_list_destruct(&list); + } +#endif + + // TODO caching + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: solutions of "); + lp_polynomial_print(constraint->polynomial, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n\t wrt "); + lp_assignment_print(m, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + return lp_polynomial_constraint_get_feasible_set_Zp(constraint->polynomial, constraint->sgn_condition, is_negated, m); +} + +static +void ff_plugin_process_unit_constraint(ff_plugin_t* ff, trail_token_t* prop, variable_t constraint_var) { + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: processing unit constraint :\n"); + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, constraint_var)); + } + + // Process if constraint is assigned, or an evaluation constraint + bool is_eval_constraint = !variable_db_is_boolean(ff->ctx->var_db, constraint_var); + if (is_eval_constraint || trail_has_value(ff->ctx->trail, constraint_var)) { + // Get the constraint value + bool constraint_value = is_eval_constraint || trail_get_value(ff->ctx->trail, constraint_var)->b; + + // Variable of the constraint + variable_t x = constraint_unit_info_get_unit_var(&ff->unit_info, constraint_var); + assert(x != variable_null); + + bool is_negated = !constraint_value; + lp_feasibility_set_int_t *constraint_feasible = ff_plugin_get_feasible_set(ff, constraint_var, x, is_negated); + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: constraint_feasible = "); + lp_feasibility_set_int_print(constraint_feasible, ctx_trace_out(ff->ctx)); + } + + bool consistent = ff_feasible_set_db_update(ff->feasible_set_db, x, constraint_feasible, &constraint_var, 1); + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: new feasible = "); + lp_feasibility_set_int_print(ff_feasible_set_db_get(ff->feasible_set_db, x), ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + if (!consistent) { + // conflict + ff_plugin_report_conflict(ff, prop, x); + } else { + const lp_feasibility_set_int_t *feasible = ff_feasible_set_db_get(ff->feasible_set_db, x); + if (lp_feasibility_set_int_is_point(feasible)) { + // If the value is implied at zero level, propagate it + // TODO why not always propagate it? + // because generating an explanation is not doable in case there is a a_2*x^2 +a_1*x + a_0 = 0 + // you need to find a term s that evaluates to the propagated value under the current assignment, but works in general + // and you need to find an explanaiton for this propagation + // TODO this can be done in ff -> propagation like single polynomial propagation + if (!trail_has_value(ff->ctx->trail, x)) { + if (trail_is_at_base_level(ff->ctx->trail)) { + mcsat_value_t value; + lp_value_t x_value; + lp_value_construct_zero(&x_value); + assert(x_value.type == LP_VALUE_INTEGER); + lp_feasibility_set_int_pick_value(feasible, &x_value.value.z); + mcsat_value_construct_lp_value(&value, &x_value); + prop->add_at_level(prop, x, &value, ff->ctx->trail->decision_level_base); + mcsat_value_destruct(&value); + lp_value_destruct(&x_value); + } else { + (*ff->stats.variable_hints) ++; + ff->ctx->hint_next_decision(ff->ctx, x); + } + } + } + } + } +} + +static +void ff_plugin_process_variable_assignment(ff_plugin_t* ff, trail_token_t* prop, variable_t var) { + remove_iterator_t it; + variable_list_ref_t var_list_ref; + variable_t* var_list; + variable_t* var_list_it; + + // The trail + const mcsat_trail_t* trail = ff->ctx->trail; + + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + assert(trail_is_consistent(trail)); + + // Mark the variable as processed + ivector_push(&ff->processed_variables, var); + ff->processed_variables_size ++; + + // Check if we have processed our last decision + if (var == ff->last_decided_and_unprocessed) { + ff->last_decided_and_unprocessed = variable_null; + } + + term_t t = variable_db_get_term(ff->ctx->var_db, var); + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: processing var assignment of :"); + ctx_trace_term(ff->ctx, t); + ctx_trace_printf(ff->ctx, "\n"); + } + + // If it's constant, just skip it + if (!lp_data_variable_has_term(ff->lp_data, t)) { + return; + } + + // Add to the lp model and context + assert(trail_get_value(trail, var)->type == VALUE_LIBPOLY); + lp_data_add_to_model_and_context(ff->lp_data, lp_data_get_lp_variable_from_term(ff->lp_data, t), &trail_get_value(trail, var)->lp_value); + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: var order: "); + lp_data_variable_order_print(ff->lp_data, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + if (ctx_trace_enabled(ff->ctx, "ff::wlm")) { + watch_list_manager_print(&ff->wlm, ctx_trace_out(ff->ctx)); + } + + remove_iterator_construct(&it, &ff->wlm, var); + + // Go through all constraints where this variable appears + while (!remove_iterator_done(&it)) { + + // Get the variables of the constraint + var_list_ref = remove_iterator_get_list_ref(&it); + var_list = watch_list_manager_get_list(&ff->wlm, var_list_ref); + + // Constraint variable + variable_t constraint_var = watch_list_manager_get_constraint(&ff->wlm, var_list_ref); + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "ff: processing constraint :"); + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, constraint_var)); + + ctx_trace_printf(ff->ctx, "ff: var_list :"); + var_list_it = var_list; + while (*var_list_it != variable_null) { + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, *var_list_it)); + var_list_it ++; + } + } + + // Put the variable to [1] so that [0] is the unit one + if (var_list[0] == var && var_list[1] != variable_null) { + var_list[0] = var_list[1]; + var_list[1] = var; + } + + // Find a new watch (start from [2], increase in for loop again!) + var_list_it = var_list + 1; + if (*var_list_it != variable_null) { + for (++ var_list_it; *var_list_it != variable_null; ++ var_list_it) { + if (!ff_plugin_has_assignment(ff, *var_list_it)) { + // Swap with var_list[1] + var_list[1] = *var_list_it; + *var_list_it = var; + // Add to new watch + watch_list_manager_add_to_watch(&ff->wlm, var_list_ref, var_list[1]); + // Don't watch this one + remove_iterator_next_and_remove(&it); + break; + } + } + } + + if (*var_list_it == variable_null) { + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "no watch found\n"); + } + if (!ff_plugin_has_assignment(ff, *var_list)) { + // We're unit + constraint_unit_info_set(&ff->unit_info, constraint_var, *var_list, CONSTRAINT_UNIT); + // Process the constraint + if (trail_is_consistent(trail)) { + ff_plugin_process_unit_constraint(ff, prop, constraint_var); + } + } else { + // Fully assigned + constraint_unit_info_set(&ff->unit_info, constraint_var, variable_null, CONSTRAINT_FULLY_ASSIGNED); + // Evaluate the constraint and propagate (if not assigned already) + if (trail_is_consistent(trail) && !trail_has_value(trail, constraint_var)) { + ff_plugin_process_fully_assigned_constraint(ff, prop, constraint_var); + } + } + // Keep the watch + remove_iterator_next_and_keep(&it); + } + } + + // Done, destruct the iterator + remove_iterator_destruct(&it); +} + +#ifndef NDEBUG +static +bool ff_plugin_check_assignment(ff_plugin_t* ff) { + if (!ctx_trace_enabled(ff->ctx, "ff::check_assignment")) { + return true; + } + + const mcsat_trail_t* trail = ff->ctx->trail; + const variable_db_t* var_db = ff->ctx->var_db; + const lp_data_t* lp_data = ff->lp_data; + + if (lp_data == NULL) { + // there's nothing to check + return true; + } + + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + + // Go through the trail and check if all assigned are in lp_assignment + uint32_t i; + for (i = 0; i < trail->elements.size; ++ i) { + variable_t x = trail->elements.data[i]; + if (x == ff->last_decided_and_unprocessed) { + continue; + } + const mcsat_value_t* value = trail_get_value(trail, x); + if (value->type == VALUE_LIBPOLY && ff_plugin_has_assignment(ff, x)) { + term_t t = variable_db_get_term(var_db, x); + lp_variable_t x_lp = lp_data_get_lp_variable_from_term(lp_data, t); + const lp_value_t* value_lp = lp_assignment_get_value(lp_data->lp_assignment, x_lp); + if (lp_value_cmp(&value->lp_value, value_lp) != 0) { + return false; + } + } + } + + // Go through lp_assignment and check if they are assigned in trail + const lp_variable_list_t* order = lp_variable_order_get_list(lp_data->lp_var_order); + for (i = 0; i < order->list_size; ++ i) { + lp_variable_t x_lp = order->list[i]; + term_t x_term = lp_data_get_term_from_lp_variable(lp_data, x_lp); + variable_t x = variable_db_get_variable_if_exists(var_db, x_term); + assert(x != variable_null); + const mcsat_value_t* value = trail_get_value(trail, x); + const lp_value_t* value_lp = lp_assignment_get_value(lp_data->lp_assignment, x_lp); + if (lp_value_cmp(&value->lp_value, value_lp) != 0) { + return false; + } + } + + return true; +} +#endif + +/** + * Propagates the trail with BCP. When done, either the trail is fully + * propagated, or the trail is in an inconsistent state. + */ +static +void ff_plugin_propagate(plugin_t* plugin, trail_token_t* prop) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + variable_t var; + + assert(ff_plugin_check_assignment(ff)); + + // Context + const mcsat_trail_t* trail = ff->ctx->trail; + variable_db_t* var_db = ff->ctx->var_db; + + if (ctx_trace_enabled(ff->ctx, "ff::propagate")) { + ctx_trace_printf(ff->ctx, "trail:\n"); + trail_print(trail, ff->ctx->tracer->file); + } + + // Propagate + while(trail_is_consistent(trail) && ff->trail_i < trail_size(trail)) { + var = trail_at(trail, ff->trail_i); + ff->trail_i ++; + if (variable_db_is_finitefield(var_db, var) /* TODO && same order */) { + ff_plugin_process_variable_assignment(ff, prop, var); + } + if (constraint_unit_info_has(&ff->unit_info, var)) { + constraint_unit_state_t info = constraint_unit_info_get(&ff->unit_info, var); + switch (info) { + case CONSTRAINT_UNIT: + // Process any unit constraints + ff_plugin_process_unit_constraint(ff, prop, var); + break; + case CONSTRAINT_UNKNOWN: + // Can't infer bounds for finite fields + case CONSTRAINT_FULLY_ASSIGNED: + // Do nothing + break; + } + } + } + + assert(ff_plugin_check_assignment(ff)); + + /* two jobs: + * - propagate information, like x = y, propagate f(x) = f(y). + * In my case propagate a truth value of a constraint if I know the constraint is fully assigned + * (can create new terms, keep track why I created new term (lazy explanation, mcsat might ask later about the explanation)) + * - base minimum: find a conflict (no assignment for one variable) + * report like in uf_plugin.c:390 + * then mcsat calls get_conflict and I fill the conflict vector + * + * + * propagate stuff like x == \alpha, because it forces a model value. + * maybe keep x != \alpha internally. + */ +} + +/* + * Check sat mod assignment (given a partial model at is-sat call). + * The decide_assignment is just recording an external assignment. Register a forced value from outside. + * decide is deciding on a theory variable by the solver. Pick a value and return it to the solver. + */ + +static +void ff_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide_token, bool must) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + assert(variable_db_is_finitefield(ff->ctx->var_db, x)); + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + + const lp_feasibility_set_int_t* feasible = ff_feasible_set_db_get(ff->feasible_set_db, x); + + if (ctx_trace_enabled(ff->ctx, "ff::decide")) { + ctx_trace_printf(ff->ctx, "decide on "); + variable_db_print_variable(ff->ctx->var_db, x, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "[%d] at level %d\n", x, ff->ctx->trail->decision_level); + if (feasible) { + ctx_trace_printf(ff->ctx, "feasible :"); + ff_feasible_set_db_print_var(ff->feasible_set_db, x, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } else { + ctx_trace_printf(ff->ctx, "feasible : ALL\n"); + } + } + + // the new value + bool using_cached = false; + const mcsat_value_t *x_new = NULL; + mcsat_value_t x_new_local; + + // see if there is a fitting cached value + if (trail_has_cached_value(ff->ctx->trail, x)) { + x_new = trail_get_cached_value(ff->ctx->trail, x); + assert(x_new->type == VALUE_LIBPOLY); + assert(x_new->lp_value.type == LP_VALUE_INTEGER); + if (feasible == NULL || lp_feasibility_set_int_contains(feasible, &x_new->lp_value.value.z)) { + using_cached = true; + } + } + + if (!using_cached) { + // create a 0 integer value + lp_value_t x_new_lp; + lp_value_construct_zero(&x_new_lp); + + // perform a db lookup + if (feasible) { + assert(!lp_feasibility_set_int_is_empty(feasible)); + assert(x_new_lp.type == LP_VALUE_INTEGER); + lp_feasibility_set_int_pick_value(feasible, &x_new_lp.value.z); + } // otherwise, all values are valid, including 0 + + // make an mcsat value + mcsat_value_construct_lp_value(&x_new_local, &x_new_lp); + x_new = &x_new_local; + lp_value_destruct(&x_new_lp); + } + + if (ctx_trace_enabled(ff->ctx, "ff::decide")) { + ctx_trace_printf(ff->ctx, "decided on "); + variable_db_print_variable(ff->ctx->var_db, x, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "[%d]: ", x); + mcsat_value_print(x_new, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + decide_token->add(decide_token, x, x_new); + + // Remember that we've decided this guy + ff->last_decided_and_unprocessed = x; + + if (x_new == &x_new_local) { + mcsat_value_destruct(&x_new_local); + } +} + +static +void ff_plugin_get_conflict(plugin_t* plugin, ivector_t* conflict) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + // we don't yet support assumptions, thus no assumption conflicts yet + assert(ff->conflict_variable != variable_null); + variable_t x = ff->conflict_variable; + + if (ctx_trace_enabled(ff->ctx, "ff::conflict")) { + ctx_trace_printf(ff->ctx, "ff_plugin_get_conflict(): "); + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, x)); + } + + ivector_t core, lemma_reasons; + init_ivector(&core, 0); + init_ivector(&lemma_reasons, 0); + ff_feasible_set_db_get_conflict_reasons(ff->feasible_set_db, x, &core, &lemma_reasons); + + if (ctx_trace_enabled(ff->ctx, "ff::conflict")) { + ctx_trace_printf(ff->ctx, "ff_plugin_get_conflict(): core:\n"); + for (size_t i = 0; i < core.size; ++ i) { + ctx_trace_printf(ff->ctx, "[%zu] (", i); + if (trail_has_value(ff->ctx->trail, core.data[i])) { + mcsat_value_print(trail_get_value(ff->ctx->trail, core.data[i]), ctx_trace_out(ff->ctx)); + } + ctx_trace_printf(ff->ctx, "): "); + ctx_trace_term(ff->ctx, variable_db_get_term(ff->ctx->var_db, core.data[i])); + } + } + + // not yet used + assert(lemma_reasons.size == 0); + assert(conflict->size == 0); + ff_plugin_explain_conflict(ff, &core, &lemma_reasons, conflict); + + if (ctx_trace_enabled(ff->ctx, "ff::conflict")) { + ctx_trace_printf(ff->ctx, "ff_plugin_get_conflict(): conflict:\n"); + for (size_t i = 0; i < conflict->size; ++ i) { + ctx_trace_printf(ff->ctx, "[%zu]: ", i); + ctx_trace_term(ff->ctx, conflict->data[i]); + } + } + + // Remove temps + delete_ivector(&core); + delete_ivector(&lemma_reasons); +} + +static +term_t ff_plugin_explain_propagation(plugin_t* plugin, variable_t var, ivector_t* reasons) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + // We only propagate evaluations, and we explain them using the literal itself + // The only other propagations are at 0-level, and those we explain with the value and no reasons + term_t atom = variable_db_get_term(ff->ctx->var_db, var); + if (ctx_trace_enabled(ff->ctx, "ff::conflict")) { + ctx_trace_printf(ff->ctx, "ff_plugin_explain_propagation():\n"); + ctx_trace_term(ff->ctx, atom); + } + const mcsat_value_t* value = trail_get_value(ff->ctx->trail, var); + if (ctx_trace_enabled(ff->ctx, "ff::conflict")) { + ctx_trace_printf(ff->ctx, "assigned to:"); + mcsat_value_print(value, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + + if (value->type == VALUE_BOOLEAN) { + if (value->b) { + // atom => atom = true + ivector_push(reasons, atom); + return bool2term(true); + } else { + // neg atom => atom = false + ivector_push(reasons, opposite_term(atom)); + return bool2term(false); + } + } else { + // we just return true => var = value + // this is only allowed at base level when explaining under assumptions + // there is currently no was to assert this properly + // assert(trail_is_at_base_level(ff->ctx->trail)); + return mcsat_value_to_term(value, ff->ctx->tm); + } +} + +static +bool ff_plugin_explain_evaluation(plugin_t* plugin, term_t t, int_mset_t* vars, mcsat_value_t* value) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + bool result = true; + + // Get all the variables and make sure they are all assigned. + ff_plugin_get_constraint_variables(ff, t, vars); + + // Check if the variables are assigned + ivector_t* var_list = int_mset_get_list(vars); + for (size_t i = 0; i < var_list->size; ++ i) { + if (!trail_has_value(ff->ctx->trail, var_list->data[i])) { + result = false; + } + } + + // All variables assigned + return result; +} + + +/* + * It is dealing with trail addition and rollback. + * Push and Pop trail frames + */ + +static +void ff_plugin_push(plugin_t* plugin) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + if (ff->lp_data == NULL) { + return; + } + + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + + scope_holder_push(&ff->scope, + &ff->trail_i, + &ff->processed_variables_size, + NULL); + + lp_data_variable_order_push(ff->lp_data); + ff_feasible_set_db_push(ff->feasible_set_db); +} + +static +void ff_plugin_pop(plugin_t* plugin) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + if (ff->lp_data == NULL) { + return; + } + + assert(ff->lp_data && ff->constraint_db && ff->feasible_set_db); + + // Pop the scoped variables + scope_holder_pop(&ff->scope, + &ff->trail_i, + &ff->processed_variables_size, + NULL); + + // Pop the variable order and the lp model + lp_data_variable_order_pop(ff->lp_data); + + // Undo the processed variables + while (ff->processed_variables.size > ff->processed_variables_size) { + // The variable to undo + variable_t x = ivector_last(&ff->processed_variables); + ivector_pop(&ff->processed_variables); + assert(variable_db_is_finitefield(ff->ctx->var_db, x)); + // Go through the watch and mark the constraints + remove_iterator_t it; + remove_iterator_construct(&it, &ff->wlm, x); + while (!remove_iterator_done(&it)) { + variable_t constraint_var = remove_iterator_get_constraint(&it); + constraint_unit_info_demote(&ff->unit_info, constraint_var, x); + remove_iterator_next_and_keep(&it); + } + remove_iterator_destruct(&it); + } + + ff_feasible_set_db_pop(ff->feasible_set_db); + + assert(ff_plugin_check_assignment(ff)); +} + +static +void ff_plugin_gc_mark(plugin_t* plugin, gc_info_t* gc_vars) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + if (ff->lp_data) { + assert(ff->feasible_set_db); + ff_feasible_set_db_gc_mark(ff->feasible_set_db, gc_vars); + } + + watch_list_manager_gc_mark(&ff->wlm, gc_vars); +} + +static +void ff_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + constraint_unit_info_gc_sweep(&ff->unit_info, gc_vars); + watch_list_manager_gc_sweep_lists(&ff->wlm, gc_vars); + + if (ff->lp_data) { + lp_data_gc_sweep(ff->lp_data, gc_vars); + assert(ff->constraint_db); + poly_constraint_db_gc_sweep(ff->constraint_db, ff->ctx, gc_vars); + } +} + +static +void ff_plugin_event_notify(plugin_t* plugin, plugin_notify_kind_t kind) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + (void)ff; + + switch (kind) { + case MCSAT_SOLVER_START: + // Re-initialize the heuristics + break; + case MCSAT_SOLVER_RESTART: + // Check if clause compaction needed + break; + case MCSAT_SOLVER_CONFLICT: + // Decay the scores each conflict + break; + case MCSAT_SOLVER_POP: + // Not much to do + break; + default: + assert(false); + } +} + +static +void ff_plugin_set_exception_handler(plugin_t* plugin, jmp_buf* handler) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + ff->exception = handler; +} + +// assumptions are not yet supported in the ff plugin +#if 0 +static +void ff_plugin_decide_assignment(plugin_t* plugin, variable_t x, const mcsat_value_t* value, trail_token_t* decide) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + (void)ff; + assert(false); +} + +static +void ff_plugin_learn(plugin_t* plugin, trail_token_t* prop) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + /* + * heavy propagation at the base level (when no decisions are made yet). + * General lemmas that are assignment independent are thus never undone. + * Should be called at every restart. Currently, it's only called once at the beginning of the search. + */ + (void)ff; + assert(false); +} + +static +bool ff_plugin_simplify_conflict_literal(plugin_t* plugin, term_t lit, ivector_t* output) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + (void)ff; + assert(false); + return false; +} + +static +void ff_plugin_new_lemma_notify(plugin_t* plugin, ivector_t* lemma, trail_token_t* prop) { + ff_plugin_t* ff = (ff_plugin_t*) plugin; + + (void)ff; + // nothing to be done here +} + +#endif + +plugin_t* ff_plugin_allocator(void) { + ff_plugin_t* plugin = safe_malloc(sizeof(ff_plugin_t)); + plugin_construct((plugin_t*) plugin); + plugin->plugin_interface.construct = ff_plugin_construct; + plugin->plugin_interface.destruct = ff_plugin_destruct; + plugin->plugin_interface.new_term_notify = ff_plugin_new_term_notify; +// plugin->plugin_interface.new_lemma_notify = ff_plugin_new_lemma_notify; + plugin->plugin_interface.event_notify = ff_plugin_event_notify; + plugin->plugin_interface.propagate = ff_plugin_propagate; + plugin->plugin_interface.decide = ff_plugin_decide; +// plugin->plugin_interface.decide_assignment = ff_plugin_decide_assignment; +// plugin->plugin_interface.learn = ff_plugin_learn; + plugin->plugin_interface.get_conflict = ff_plugin_get_conflict; + plugin->plugin_interface.explain_propagation = ff_plugin_explain_propagation; + plugin->plugin_interface.explain_evaluation = ff_plugin_explain_evaluation; +// plugin->plugin_interface.simplify_conflict_literal = ff_plugin_simplify_conflict_literal; + plugin->plugin_interface.push = ff_plugin_push; + plugin->plugin_interface.pop = ff_plugin_pop; + plugin->plugin_interface.gc_mark = ff_plugin_gc_mark; + plugin->plugin_interface.gc_sweep = ff_plugin_gc_sweep; + plugin->plugin_interface.set_exception_handler = ff_plugin_set_exception_handler; + + return (plugin_t*) plugin; +} diff --git a/src/mcsat/ff/ff_plugin.h b/src/mcsat/ff/ff_plugin.h new file mode 100644 index 000000000..b5ec97776 --- /dev/null +++ b/src/mcsat/ff/ff_plugin.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef FF_PLUGIN_H_ +#define FF_PLUGIN_H_ + +#include "mcsat/plugin.h" + +/** Allocate a new ff plugin and setup the plugin-interface method */ +plugin_t* ff_plugin_allocator(void); + +#endif /* FF_PLUGIN_H_ */ diff --git a/src/mcsat/ff/ff_plugin_explain.c b/src/mcsat/ff/ff_plugin_explain.c new file mode 100644 index 000000000..0d5d116e7 --- /dev/null +++ b/src/mcsat/ff/ff_plugin_explain.c @@ -0,0 +1,888 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "mcsat/ff/ff_plugin_explain.h" +#include "mcsat/ff/ff_plugin_internal.h" + +#include "mcsat/tracing.h" + +static +void explain_single(const lp_data_t *lp_data, const lp_polynomial_t *A, lp_polynomial_hash_set_t *e_ne); + +/** finalizes and empties eq and ne (to avoid polynomial copying) */ +static +void explain_multi(const lp_data_t *lp_data, + lp_polynomial_hash_set_t *eq, lp_polynomial_hash_set_t *ne, + lp_polynomial_hash_set_t *e_eq, lp_polynomial_hash_set_t *e_ne); + + +static inline +bool polynomial_is_zero_m(const lp_polynomial_t *A, const lp_assignment_t *m) { + assert(lp_polynomial_is_assigned(A, m)); + + lp_integer_t val; + lp_integer_construct(&val); + lp_polynomial_evaluate_integer(A, m, &val); + bool ret = lp_integer_is_zero(lp_polynomial_get_context(A)->K, &val) != 0; + lp_integer_destruct(&val); + return ret; +} + +#ifndef NDEBUG +static +bool check_hash_set_all(const lp_polynomial_hash_set_t *v, const lp_assignment_t *m, bool negated) { + assert(v->closed); + for (int i = 0; i < v->size; ++i) { + bool is_zero = polynomial_is_zero_m(v->data[i], m); + if (!is_zero == !negated) { + return false; + } + } + return true; +} + +static +bool check_assignment_cube(const lp_polynomial_hash_set_t *eq, const lp_polynomial_hash_set_t *ne, const lp_assignment_t *m) { + if (eq && !check_hash_set_all(eq, m, false)) { + return false; + } + if (ne && !check_hash_set_all(ne, m, true)) { + return false; + } + return true; +} + +static +bool heap_contains_check_top_variable(const lp_polynomial_heap_t *heap, lp_variable_t top) { + for (size_t i = 0; i < lp_polynomial_heap_size(heap); ++i) { + if (lp_polynomial_top_variable(lp_polynomial_heap_at(heap, i)) != top) { + return false; + } + } + return true; +} +#endif + +/** Gets the degree of A assuming top variable top */ +static inline +size_t polynomial_degree_safe(const lp_polynomial_t *A, lp_variable_t top) { + return lp_polynomial_top_variable(A) == top ? lp_polynomial_degree(A) : 0; +} + +/** Gets (a new instance of) the lc(p). Assumes that top(p) <= var. */ +static +lp_polynomial_t* lc(const lp_polynomial_t *p, lp_variable_t var) { + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(p); + size_t d = polynomial_degree_safe(p, var); + if (d == 0) { + return lp_polynomial_new_copy(p); + } + lp_polynomial_t *lc = lp_polynomial_new(ctx); + lp_polynomial_get_coefficient(lc, p, d); + return lc; +} + +static inline +lp_polynomial_t* red(const lp_polynomial_t *p, lp_variable_t var) { + assert(lp_polynomial_top_variable(p) == var); + (void)var; + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(p); + lp_polynomial_t *red = lp_polynomial_new(ctx); + lp_polynomial_reductum(red, p); + return red; +} + +static inline +lp_polynomial_t* pquo(const lp_polynomial_t *A, const lp_polynomial_t *B, lp_variable_t var) { + assert(lp_polynomial_top_variable(A) == var); + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(A); + lp_polynomial_t *quo = lp_polynomial_new(ctx); + lp_polynomial_t *rem = lp_polynomial_new(ctx); + lp_polynomial_pdivrem(quo, rem, A, B); + lp_polynomial_delete(rem); + return quo; +} + +static inline +bool polynomial_is_assigned_and_zero(const lp_polynomial_t *p, const lp_assignment_t *m) { + return lp_polynomial_is_assigned(p, m) && polynomial_is_zero_m(p, m); +} + +static inline +bool polynomial_is_assigned_and_non_zero(const lp_polynomial_t *p, const lp_assignment_t *m) { + return lp_polynomial_is_assigned(p, m) && !polynomial_is_zero_m(p, m); +} + +static inline +bool polynomial_lc_is_assigned_and_non_zero(const lp_polynomial_t *p, lp_variable_t var, const lp_assignment_t *m) { + lp_polynomial_t *p_lc = lc(p, var); + bool rslt = polynomial_is_assigned_and_non_zero(p_lc, m); + lp_polynomial_delete(p_lc); + return rslt; +} + +static +void exclude_coefficient(const lp_polynomial_t *A, const lp_assignment_t *m, lp_variable_t var, lp_polynomial_hash_set_t *result) { + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(A); + + if (lp_polynomial_is_constant(A)) + return; + + lp_polynomial_t *tmp; + lp_monomial_t mon; + lp_monomial_construct(ctx, &mon); + lp_integer_t num; + lp_integer_construct(&num); + + if (lp_polynomial_top_variable(A) != var) { + assert(lp_polynomial_is_assigned(A, m)); + tmp = lp_polynomial_new_copy(A); + lp_polynomial_evaluate_integer(A, m, &num); + lp_integer_neg(ctx->K, &num, &num); + lp_monomial_set_coefficient(ctx, &mon, &num); + lp_polynomial_add_monomial(tmp, &mon); + lp_polynomial_hash_set_insert(result, tmp); + } else { + const size_t degree = lp_polynomial_degree(A); + tmp = lp_polynomial_new(ctx); + for (size_t k = 0; k <= degree; ++k) { + lp_polynomial_get_coefficient(tmp, A, k); + if (lp_polynomial_is_constant(tmp)) + continue; + lp_polynomial_evaluate_integer(tmp, m, &num); + lp_integer_neg(ctx->K, &num, &num); + lp_monomial_set_coefficient(ctx, &mon, &num); + lp_polynomial_add_monomial(tmp, &mon); + lp_polynomial_hash_set_insert(result, tmp); + } + } + lp_polynomial_delete(tmp); + lp_integer_destruct(&num); + lp_monomial_destruct(&mon); +} + +static +void explain_single(const lp_data_t *lp_data, const lp_polynomial_t *A, lp_polynomial_hash_set_t *e_ne) { + const lp_assignment_t *m = lp_data->lp_assignment; + + // TODO do special case if polynomial is degree one?!? + + assert(lp_polynomial_is_univariate_m(A, m)); + + // maybe make internal copy to avoid repeated checks for order at get_coefficient + // lp_polynomial_construct_copy + // lp_polynomial_ensure_order + // or maybe do this in polynomial_coefficient_traverse and use it here + + if (ctx_trace_enabled(lp_data->plugin_ctx, "ff::explain")) { + ctx_trace_printf(lp_data->plugin_ctx, "ff_explain_single: "); + lp_polynomial_print(A, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, ", "); + lp_assignment_print(m, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, "\n"); + } + + lp_variable_t top = lp_polynomial_top_variable(A); + assert(!lp_assignment_is_set(m, top)); + assert(lp_polynomial_is_univariate_m(A, m)); + exclude_coefficient(A, m, top, e_ne); + lp_polynomial_hash_set_close(e_ne); + + if (ctx_trace_enabled(lp_data->plugin_ctx, "ff::explain")) { + ctx_trace_printf(lp_data->plugin_ctx, "explain_single () => "); + lp_polynomial_hash_set_print(e_ne, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, "\n"); + } +} + +static +lp_polynomial_t** srs(const lp_polynomial_t *f, const lp_polynomial_t *g, size_t *count) { + assert(lp_polynomial_context_equal(lp_polynomial_get_context(f), lp_polynomial_get_context(g))); + assert(lp_polynomial_top_variable(f) == lp_polynomial_top_variable(g)); + + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(f); + const lp_variable_t var = lp_polynomial_top_variable(f); + // calculate size of subres + size_t + deg_f = lp_polynomial_degree(f), + deg_g = lp_polynomial_degree(g); + + size_t cnt = (deg_f < deg_g ? deg_f : deg_g) + 1; + + // prepare memory + lp_polynomial_t + **subres = safe_malloc(sizeof(lp_polynomial_t*) * (cnt+1)), + **srs = safe_malloc(sizeof(lp_polynomial_t*) * (cnt+1)); + + for (size_t i = 0; i < cnt; ++i) { + subres[i] = lp_polynomial_new(ctx); + } + + // generate subresultants + lp_polynomial_subres(subres, f, g); + + // remove defective + *count = 0; + for (int i = 0; i < cnt; ++i) { + if (polynomial_degree_safe(subres[i], var) == i) { + srs[*count] = subres[i]; + (*count) ++; + } else { + lp_polynomial_delete(subres[i]); + } + } + free(subres); + + // return sub-chain + return srs; +} + +/** Checks the lc of p wrt. to var, checks if the variable is neq_zero and adds it to the correct side-condition set (M or N) */ +static inline +bool track_lc_branch_condition(const lp_polynomial_t *p, const lp_assignment_t *m, lp_variable_t var, int neq_zero, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N) { + lp_polynomial_t *p_lc = lc(p, var); + assert(lp_polynomial_is_assigned(p_lc, m)); + int is_zero = polynomial_is_zero_m(p_lc, m); + if (!lp_polynomial_is_constant(p_lc)) { + lp_polynomial_hash_set_insert_move(is_zero ? M : N, p_lc); + } + lp_polynomial_delete(p_lc); + // is_zero xor neq_zero + return !is_zero != !neq_zero; +} + +typedef enum { + /* no processing possible */ + NOT_APPLICABLE = 0, + /* excluding polynomial was found */ + FOUND, + /* degree reduction performed, but no excluding polynomial was found */ + PROCESSED +} explain_result_t; + +static +explain_result_t explain_p(const lp_polynomial_t *p2, + lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + explain_result_t rslt = NOT_APPLICABLE; + if (track_lc_branch_condition(p2, m, var, 0, M, N)) { + lp_polynomial_t *red_p2 = red(p2, var); + if (polynomial_is_assigned_and_non_zero(red_p2, m)) { + lp_polynomial_hash_set_insert(N, red_p2); + rslt = FOUND; + } else { + if (lp_polynomial_top_variable(red_p2) == var) { lp_polynomial_heap_push(F, red_p2); } + rslt = PROCESSED; + } + lp_polynomial_delete(red_p2); + } + return rslt; +} + +/** tries to reduce F wrt. to p2 by finding a gcd with one element from F */ +static +explain_result_t explain_pP(const lp_polynomial_t *p2, + lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + lp_polynomial_t *p1 = lp_polynomial_heap_pop(F); + + size_t r; + lp_polynomial_t **h = srs(p1, p2, &r); + + explain_result_t ret = NOT_APPLICABLE; + size_t i = 0; + if (lp_polynomial_top_variable(h[0]) != var) { + assert(r >= 1); + i++; + // since h[0] has no var, it is in F for all potential branches. + // If it is excluding, all branches are excluded,... + assert(lp_polynomial_is_assigned(h[0], m)); + if (polynomial_is_assigned_and_non_zero(h[0], m)) { + lp_polynomial_hash_set_insert(N, h[0]); + ret = FOUND; + goto cleanup; + } + // ... otherwise there is no need to add it to F (because it has no var). + } + // all h[i]'s top variable is var, thus none can be excluding + for (; i < r; i++) { + if (track_lc_branch_condition(h[i], m, var, 1, M, N)) { + assert(lp_polynomial_top_variable(h[i]) == var); + lp_polynomial_heap_push(F, h[i]); + ret = PROCESSED; + goto cleanup; + } + } + + // all lc are zero for m, should not happen + assert(ret == NOT_APPLICABLE); + lp_polynomial_heap_push(F, p1); + + cleanup: + lp_polynomial_delete(p1); + for (int j = 0; j < r; ++j) { + lp_polynomial_delete(h[j]); + } + free(h); + + return ret; +} + +/** tries to reduce G wrt. to p2 by finding a gcd with one element from G */ +static +explain_result_t explain_pQ(const lp_polynomial_t *p2, + lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + assert(!lp_polynomial_heap_is_empty(G)); + + const lp_polynomial_t *q = lp_polynomial_heap_peek(G); + + if (!(lp_polynomial_degree(q) >= lp_polynomial_degree(p2) || polynomial_lc_is_assigned_and_non_zero(q, var, m))) { + return NOT_APPLICABLE; + } + + size_t r; + lp_polynomial_t **h = srs(q, p2, &r); + + explain_result_t ret = NOT_APPLICABLE; + for (size_t i = 0; i < r; i++) { + if (track_lc_branch_condition(h[i], m, var, 1, M, N)) { + if (lp_polynomial_top_variable(h[i]) != var) { + assert(i == 0); + lp_polynomial_delete(lp_polynomial_heap_pop(G)); + } + lp_polynomial_t *rem = pquo(p2, h[i], var); + // did we find an excluding polynomial? + if (polynomial_is_assigned_and_non_zero(rem, m)) { + if (!lp_polynomial_is_constant(rem)) { lp_polynomial_hash_set_insert(N, rem); } + ret = FOUND; + } else { + assert(lp_polynomial_top_variable(rem) == var); + lp_polynomial_heap_push(F, rem); + ret = PROCESSED; + } + lp_polynomial_delete(rem); + goto cleanup; + } + } + + // all lc are zero for m + assert(ret == NOT_APPLICABLE); + + cleanup: + for (int j = 0; j < r; ++j) { + lp_polynomial_delete(h[j]); + } + free(h); + + return ret; +} + +static inline +explain_result_t exclude_p(const lp_polynomial_t *p2, + lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + // this is a set of negative condition, thus adding to the positive side-conditions to make it end up with the negative literals + exclude_coefficient(p2, m, var, M); + return FOUND; +} + +/** tries to reduce elements of G */ +static +explain_result_t explain_Q(lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + for (int i = 0; i < lp_polynomial_heap_size(G); ++i) { + const lp_polynomial_t *g = lp_polynomial_heap_at(G, i); + assert (lp_polynomial_top_variable(g) == var); + lp_polynomial_t *lc_g = lc(g, var); + if (polynomial_is_zero_m(lc_g, m)) { + lp_polynomial_hash_set_insert(M, lc_g); + lp_polynomial_heap_remove(G, g); + + explain_result_t ret; + lp_polynomial_t *red_g = red(g, var); + if (polynomial_is_assigned_and_zero(red_g, m)) { + // can only be the case when g = lc*var^d + rem (d > 0 and rem has no var) + if (!lp_polynomial_is_constant(red_g)) { lp_polynomial_hash_set_insert(M, red_g); } + ret = FOUND; + } else { + if (lp_polynomial_top_variable(red_g) == var) { lp_polynomial_heap_push(G, red_g); } + ret = PROCESSED; + } + lp_polynomial_delete(red_g); + lp_polynomial_delete(lc_g); + return ret; + } + // no need to add lc_g to N here + lp_polynomial_delete(lc_g); + } + + return NOT_APPLICABLE; +} + +static +explain_result_t exclude_q(lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + // very unlikely case (make sure that the core is minimized) + lp_polynomial_t *prod = lp_polynomial_heap_pop(G); + while (!lp_polynomial_heap_is_empty(G) && + lp_polynomial_top_variable(lp_polynomial_heap_peek(G)) == var) { + lp_polynomial_mul(prod, prod, lp_polynomial_heap_peek(G)); + lp_polynomial_delete(lp_polynomial_heap_pop(G)); + } + // when the result is constant, then it must be zero + assert(!lp_polynomial_is_constant(prod) || lp_polynomial_is_zero(prod)); + + // this is a set of negative condition, thus adding to the positive side-conditions to make it end up with the negative literals + exclude_coefficient(prod, m, var, M); + lp_polynomial_delete(prod); + return FOUND; +} + +static +explain_result_t exclude_m(const lp_polynomial_context_t *ctx, const lp_assignment_t *m, lp_polynomial_hash_set_t *M) { + fprintf(stderr, "excluding "); + lp_assignment_print(m, stderr); + fprintf(stderr, "\n"); + assert(lp_assignment_is_integer(m)); + lp_integer_t one; + lp_integer_construct_from_int(ctx->K, &one, 1); + for (int i = 0; i < m->size; ++ i) { + if (m->values[i].type != LP_VALUE_NONE) { + assert(m->values[i].type == LP_VALUE_INTEGER); + lp_polynomial_t + *x = lp_polynomial_alloc(), + *c = lp_polynomial_alloc(); + lp_polynomial_construct_simple(x, ctx, &one, i, 1); + lp_polynomial_construct_simple(c, ctx, &m->values[i].value.z, 0, 0); + lp_polynomial_sub(x, x, c); + lp_polynomial_hash_set_insert_move(M, x); + lp_polynomial_delete(c); + } + } + lp_integer_destruct(&one); + return FOUND; +} + +static inline +lp_variable_t top_variable(const lp_polynomial_t *p) { + return (p == NULL) ? lp_variable_null : lp_polynomial_top_variable(p); +} + +/* + * based on algorithm RegSer from Wang's book + * var variable to eliminate + * F list of positive polynomials (in var) + * G list of negative polynomials (in var) + * M list of positive side conditions (and negative conditions) + * N list of negative side conditions (and positive conditions) + * m current assignment + * + * Remark: side conditions are returned inverted to clausify implication + */ +static +void split_reg_ser(const lp_polynomial_context_t *ctx, + lp_polynomial_heap_t *F, lp_polynomial_heap_t *G, + lp_polynomial_hash_set_t *M, lp_polynomial_hash_set_t *N, + const lp_assignment_t *m, lp_variable_t var) { + + assert(var != lp_variable_null); + assert(!lp_polynomial_heap_is_empty(F) || !lp_polynomial_heap_is_empty(G)); + + explain_result_t rslt = NOT_APPLICABLE; + do { + assert(heap_contains_check_top_variable(F, var)); + assert(heap_contains_check_top_variable(G, var)); + + if (top_variable(lp_polynomial_heap_peek(F)) == var) { + lp_polynomial_t *p2 = lp_polynomial_heap_pop(F); + assert(lp_polynomial_top_variable(p2) == var); + + // ensure that lc(p2, var) != 0 for later operations + rslt = explain_p(p2, F, G, M, N, m, var); + if (rslt != NOT_APPLICABLE) { + lp_polynomial_delete(p2); + continue; + } + + assert(polynomial_lc_is_assigned_and_non_zero(p2, var, m)); + + if (top_variable(lp_polynomial_heap_peek(F)) == var) { + rslt = explain_pP(p2, F, G, M, N, m, var); + } else if (top_variable(lp_polynomial_heap_peek(G)) == var) { + rslt = explain_pQ(p2, F, G, M, N, m, var); + } else { + // p2 is the only polynomial with var + assert(top_variable(lp_polynomial_heap_peek(G)) != var); + rslt = exclude_p(p2, F, G, M, N, m, var); + assert(rslt == FOUND); + } + lp_polynomial_delete(p2); + } else { + assert(!lp_polynomial_heap_is_empty(G) && lp_polynomial_top_variable(lp_polynomial_heap_peek(G)) == var); + rslt = explain_Q(F, G, M, N, m, var); + if (rslt != NOT_APPLICABLE) + continue; + rslt = exclude_q(F, G, M, N, m, var); + assert(rslt == FOUND); + } + } while (rslt == PROCESSED); + + if (rslt == NOT_APPLICABLE) { + // no progress made, exclude current assignment as last resort + lp_polynomial_hash_set_clear(M); + lp_polynomial_hash_set_clear(N); + exclude_m(ctx, m, M); + } +} + +static +int compare_polynomial_inverse_degree(const lp_polynomial_t *p1, const lp_polynomial_t *p2) { + assert(lp_polynomial_context_equal(lp_polynomial_get_context(p1), lp_polynomial_get_context(p2))); + int cmp = lp_variable_order_cmp(lp_polynomial_get_context(p1)->var_order, + lp_polynomial_top_variable(p1), + lp_polynomial_top_variable(p2)); + if (cmp) { return cmp; } + return -((int)lp_polynomial_degree(p1) - (int)lp_polynomial_degree(p2)); +} + +static +void lp_polynomial_move_push_hash_set(lp_polynomial_heap_t *heap, lp_polynomial_hash_set_t *hset) { + lp_polynomial_hash_set_close(hset); + for (size_t i = 0; i < hset->size; ++i) { + lp_polynomial_heap_push_move(heap, hset->data[i]); + } +} + +static +void explain_multi(const lp_data_t *lp_data, + lp_polynomial_hash_set_t *eq, lp_polynomial_hash_set_t *ne, + lp_polynomial_hash_set_t *e_eq, lp_polynomial_hash_set_t *e_ne) { + + const lp_assignment_t *m = lp_data->lp_assignment; + + assert(eq->closed && ne->closed); + assert(eq->size > 0 || ne->size > 0); + assert(!e_eq->closed && !e_ne->closed); + + if (ctx_trace_enabled(lp_data->plugin_ctx, "ff::explain")) { + ctx_trace_printf(lp_data->plugin_ctx, "ff_explain_multi \n "); + lp_polynomial_hash_set_print(eq, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, ", \n "); + lp_polynomial_hash_set_print(ne, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, "\n"); + } + + const lp_polynomial_context_t *ctx = lp_data->lp_ctx; + + lp_polynomial_heap_t + *F = lp_polynomial_heap_new(compare_polynomial_inverse_degree), + *G = lp_polynomial_heap_new(compare_polynomial_inverse_degree); + + // moves all polynomials from the hashset to the heap + lp_polynomial_move_push_hash_set(F, eq); + lp_polynomial_move_push_hash_set(G, ne); + + lp_variable_t var = lp_variable_order_max(ctx->var_order, + !lp_polynomial_heap_is_empty(F) ? lp_polynomial_top_variable(lp_polynomial_heap_peek(F)) : lp_variable_null, + !lp_polynomial_heap_is_empty(G) ? lp_polynomial_top_variable(lp_polynomial_heap_peek(G)) : lp_variable_null + ); + assert(!lp_assignment_is_set(m, var)); + assert(heap_contains_check_top_variable(F, var)); + assert(heap_contains_check_top_variable(G, var)); + + split_reg_ser(ctx, F, G, e_ne, e_eq, m, var); + + lp_polynomial_hash_set_close(e_eq); + lp_polynomial_hash_set_close(e_ne); + + if (ctx_trace_enabled(lp_data->plugin_ctx, "ff::explain")) { + ctx_trace_printf(lp_data->plugin_ctx, "explain_multi () => \n "); + lp_polynomial_hash_set_print(e_eq, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, ", \n "); + lp_polynomial_hash_set_print(e_ne, ctx_trace_out(lp_data->plugin_ctx)); + ctx_trace_printf(lp_data->plugin_ctx, "\n"); + } + + lp_polynomial_heap_delete(F); + lp_polynomial_heap_delete(G); +} + +static inline +term_t lp_polynomial_to_term(ff_plugin_t* ff, const lp_polynomial_t* p) { + return lp_polynomial_to_yices_arith_ff_term(ff->lp_data, p, ff->ctx->tm->terms, &ff->buffer); +} + +#ifdef EXCLUDE_IRREDUCIBLE_FACTORS +static +lp_polynomial_t* irreducible_factors(const lp_polynomial_hash_set_t *polys, const lp_assignment_t *m) { + assert(polys->closed); + + lp_polynomial_hash_set_t *factors = NULL; + + for (int i = 0; i < polys->size; ++i) { + lp_polynomial_hash_set_t *new_factors = lp_polynomial_hash_set_new(); + lp_polynomial_t *poly = polys->data[i]; + lp_upolynomial_t *upoly = lp_polynomial_to_univariate_m(poly, m); + if (lp_upolynomial_degree(upoly) == 0) { + lp_upolynomial_delete(upoly); + continue; + } + lp_upolynomial_factors_t *upoly_factors = lp_upolynomial_factor(upoly); + lp_upolynomial_delete(upoly); + for (int j = 0; j < lp_upolynomial_factors_size(upoly_factors); ++j) { + size_t multiplicity; + lp_upolynomial_t *f = lp_upolynomial_factors_get_factor(upoly_factors, j, &multiplicity); + if (lp_upolynomial_degree(f) == 1) { + continue; + } + lp_polynomial_t *fp = lp_upolynomial_to_polynomial(f, lp_polynomial_get_context(poly), lp_polynomial_top_variable(poly)); + lp_polynomial_hash_set_insert_move(new_factors, fp); + lp_polynomial_delete(fp); + } + lp_upolynomial_factors_destruct(upoly_factors, true); + if (factors == NULL) { + factors = new_factors; + } else { + lp_polynomial_hash_set_intersect(factors, new_factors); + lp_polynomial_hash_set_delete(new_factors); + } + } + + if (!factors) { + return NULL; + } + + lp_polynomial_t *result; + lp_polynomial_hash_set_close(factors); + if (factors->size == 0) { + result = NULL; + } else { + result = lp_polynomial_alloc(); + lp_polynomial_construct_copy(result, factors->data[0]); + for (size_t i = 1; i < factors->size; ++i) { + lp_polynomial_mul(result, result, factors->data[i]); + } + } + + lp_polynomial_hash_set_delete(factors); + return result; +} +#endif + +/** performs basic polynomial clean-up operations: + * - reduced the exponents wrt. the field order + * - normalizes the constant lc to 1 + * - in case the polynomial is one monomial, set all exponents to 1 + */ +static +void clean_poly(lp_polynomial_t *poly) { + const lp_polynomial_context_t *ctx = lp_polynomial_get_context(poly); + + // reduce exponents + lp_polynomial_reduce_degree_Zp(poly, poly); + + // set constant of lc to 1 + lp_integer_t lcc; + lp_integer_construct(&lcc); + lp_polynomial_lc_constant(poly, &lcc); + lp_polynomial_t *c = lp_polynomial_alloc(); + lp_polynomial_construct_simple(c, lp_polynomial_get_context(poly), &lcc, 0, 0); + lp_polynomial_div(poly, poly, c); + lp_polynomial_delete(c); + lp_integer_destruct(&lcc); + + // in case polynomial is only monomial, reduce exponents to 1 + if (lp_polynomial_is_monomial(poly)) { + lp_monomial_t mono; + lp_monomial_construct(ctx, &mono); + lp_polynomial_to_monomial(poly, &mono); + for (int i = 0; i < mono.n; ++i) { + (mono.p + i)->d = 1; + } + lp_polynomial_t *p = lp_polynomial_new(ctx); + lp_polynomial_add_monomial(p, &mono); + lp_polynomial_swap(p, poly); + lp_polynomial_delete(p); + } +} + +void ff_plugin_explain_conflict(ff_plugin_t* ff, const ivector_t* core, const ivector_t* lemma_reasons, ivector_t* conflict) { + const mcsat_trail_t* trail = ff->ctx->trail; + variable_db_t* var_db = ff->ctx->var_db; + + if (ctx_trace_enabled(ff->ctx, "ff::explain")) { + ctx_trace_printf(ff->ctx, "ff_plugin_explain_conflict()\n"); + uint32_t i; + int_mset_t variables; + int_mset_construct(&variables, variable_null); + for (i = 0; i < core->size; ++ i) { + term_t core_i_t = variable_db_get_term(var_db, core->data[i]); + ff_plugin_get_constraint_variables(ff, core_i_t, &variables); + ctx_trace_printf(ff->ctx, "core[%u] = ", i); + ctx_trace_term(ff->ctx, core_i_t); + } + trail_print(ff->ctx->trail, ctx_trace_out(ff->ctx)); + ivector_t* variables_list = int_mset_get_list(&variables); + for (i = 0; i < variables_list->size; ++ i) { + variable_t var = variables_list->data[i]; + if (trail_has_value(ff->ctx->trail, var)) { + const mcsat_value_t* v = trail_get_value(ff->ctx->trail, var); + variable_db_print_variable(var_db, var, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, " -> "); + mcsat_value_print(v, ctx_trace_out(ff->ctx)); + ctx_trace_printf(ff->ctx, "\n"); + } + } + int_mset_destruct(&variables); + } + + lp_polynomial_hash_set_t pos; + lp_polynomial_hash_set_t neg; + + lp_polynomial_hash_set_construct(&pos); + lp_polynomial_hash_set_construct(&neg); + +#ifndef NDEBUG + // get the conflict variable as lp_variable_t + variable_t conflict_var = ff->conflict_variable; + assert(conflict_var != variable_null); + term_t conflict_term = variable_db_get_term(var_db, conflict_var); + lp_variable_t conflict_lp_var = lp_data_get_lp_variable_from_term(ff->lp_data, conflict_term); +#endif + + for (uint32_t core_i = 0; core_i < core->size; ++ core_i) { + variable_t constraint_var = core->data[core_i]; + assert(trail_has_value(trail, constraint_var)); + const poly_constraint_t* constraint = poly_constraint_db_get(ff->constraint_db, constraint_var); + + // get poly, condition, and negation status + const lp_polynomial_t* p = poly_constraint_get_polynomial(constraint); + lp_sign_condition_t sgn_condition = poly_constraint_get_sign_condition(constraint); + bool negated = !trail_get_boolean_value(trail, constraint_var); + + assert(lp_polynomial_top_variable(p) == conflict_lp_var); + assert(sgn_condition == LP_SGN_EQ_0 || sgn_condition == LP_SGN_NE_0); + bool is_pos = (!negated && (sgn_condition == LP_SGN_EQ_0)) || (negated && (sgn_condition == LP_SGN_NE_0)); + lp_polynomial_hash_set_insert(is_pos ? &pos : &neg, p); + } + + // not used yet + assert(lemma_reasons->size == 0); + + lp_polynomial_hash_set_close(&pos); +#ifdef EXCLUDE_IRREDUCIBLE_FACTORS + lp_polynomial_t *irr = irreducible_factors(&pos, m); + if (irr) { + lp_polynomial_hash_set_insert_move(&neg, irr); + } +#endif + lp_polynomial_hash_set_close(&neg); + + lp_polynomial_hash_set_t e_eq; + lp_polynomial_hash_set_t e_ne; + + lp_polynomial_hash_set_construct(&e_eq); + lp_polynomial_hash_set_construct(&e_ne); + + size_t cnt_pos = lp_polynomial_hash_set_size(&pos); + size_t cnt_neg = lp_polynomial_hash_set_size(&neg); + assert(cnt_pos + cnt_neg > 0); + + if (cnt_pos + cnt_neg > 1) { + explain_multi(ff->lp_data, &pos, &neg, &e_eq, &e_ne); + } else if (cnt_pos == 1) { + explain_single(ff->lp_data, pos.data[0], &e_ne); + } else if (cnt_neg == 1) { + explain_single(ff->lp_data, neg.data[0], &e_ne); + } + + lp_polynomial_hash_set_close(&e_eq); + lp_polynomial_hash_set_close(&e_ne); + +#ifndef NDEBUG + const lp_assignment_t *m = ff->lp_data->lp_assignment; +#endif + + for (size_t i = 0; i < e_ne.size; ++i) { + clean_poly(e_ne.data[i]); + } + + for (size_t i = 0; i < e_eq.size; ++i) { + clean_poly(e_eq.data[i]); + } + + // assert that the current assignment is excluded (all ne must be = 0 and eq must be != 0) + assert(check_assignment_cube(&e_ne, &e_eq, m)); + + term_manager_t *tm = ff->ctx->tm; + + // Add the explanation to the conflict + for (size_t i = 0; i < e_eq.size; ++i) { + assert(lp_polynomial_is_assigned(e_eq.data[i], m)); + term_t t = lp_polynomial_to_term(ff, e_eq.data[i]); + term_t atom = mk_arith_ff_term_neq0(tm, t); + ivector_push(conflict, atom); + assert(good_term(tm->terms, atom)); + } + + for (size_t i = 0; i < e_ne.size; ++i) { + assert(lp_polynomial_is_assigned(e_ne.data[i], m)); + term_t t = lp_polynomial_to_term(ff, e_ne.data[i]); + term_t atom = mk_arith_ff_term_eq0(tm, t); + ivector_push(conflict, atom); + assert(good_term(tm->terms, atom)); + } + + // Add the core to the conflict + for (uint32_t core_i = 0; core_i < core->size; ++ core_i) { + variable_t constraint_var = core->data[core_i]; + term_t constraint_term = variable_db_get_term(var_db, constraint_var); + assert(trail_has_value(trail, constraint_var)); + bool constraint_value = trail_get_boolean_value(trail, constraint_var); + if (!constraint_value) { + constraint_term = opposite_term(constraint_term); + } + ivector_push(conflict, constraint_term); + } + + // clean-up + lp_polynomial_hash_set_destruct(&pos); + lp_polynomial_hash_set_destruct(&neg); + lp_polynomial_hash_set_destruct(&e_eq); + lp_polynomial_hash_set_destruct(&e_ne); +} diff --git a/src/mcsat/ff/ff_plugin_explain.h b/src/mcsat/ff/ff_plugin_explain.h new file mode 100644 index 000000000..e79806cc7 --- /dev/null +++ b/src/mcsat/ff/ff_plugin_explain.h @@ -0,0 +1,28 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef FF_PLUGIN_EXPLAIN_H +#define FF_PLUGIN_EXPLAIN_H + +#include "utils/int_vectors.h" + +typedef struct ff_plugin_s ff_plugin_t; + +void ff_plugin_explain_conflict(ff_plugin_t* ff, const ivector_t* core, const ivector_t* lemma_reasons, ivector_t* conflict); + +#endif /* FF_PLUGIN_EXPLAIN_H */ diff --git a/src/mcsat/ff/ff_plugin_internal.c b/src/mcsat/ff/ff_plugin_internal.c new file mode 100644 index 000000000..86052da8d --- /dev/null +++ b/src/mcsat/ff/ff_plugin_internal.c @@ -0,0 +1,107 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "mcsat/ff/ff_plugin_internal.h" + +#include "mcsat/tracing.h" + +void ff_plugin_get_constraint_variables(ff_plugin_t* ff, term_t constraint, int_mset_t* vars_out) { + + term_table_t* terms = ff->ctx->terms; + + term_t atom = unsigned_term(constraint); + term_kind_t atom_kind = term_kind(ff->ctx->terms, atom); + + switch (atom_kind) { + case ARITH_FF_EQ_ATOM: + ff_plugin_get_term_variables(ff, arith_ff_eq_arg(terms, atom), vars_out); + break; + case EQ_TERM: + case ARITH_FF_BINEQ_ATOM: + ff_plugin_get_term_variables(ff, composite_term_arg(terms, atom, 0), vars_out); + ff_plugin_get_term_variables(ff, composite_term_arg(terms, atom, 1), vars_out); + break; + default: + // We're fine, just a variable, arithmetic term to eval, or a foreign term + ff_plugin_get_term_variables(ff, constraint, vars_out); + int_mset_add(vars_out, variable_db_get_variable(ff->ctx->var_db, constraint)); + break; + } +} + +void ff_plugin_get_term_variables(ff_plugin_t* ff, term_t t, int_mset_t* vars_out) { + + // The term table + term_table_t* terms = ff->ctx->terms; + + // Variable database + variable_db_t* var_db = ff->ctx->var_db; + + + if (ctx_trace_enabled(ff->ctx, "mcsat::new_term")) { + ctx_trace_printf(ff->ctx, "ff_plugin_get_variables: "); + ctx_trace_term(ff->ctx, t); + } + + term_kind_t kind = term_kind(terms, t); + switch (kind) { + case ARITH_FF_CONSTANT: + break; + case ARITH_FF_POLY: { + // The polynomial + polynomial_t* polynomial = finitefield_poly_term_desc(terms, t); + // Go through the polynomial and get the variables + uint32_t i, j, deg; + variable_t var; + for (i = 0; i < polynomial->nterms; ++i) { + term_t product = polynomial->mono[i].var; + if (product == const_idx) { + // Just the constant + continue; + } else if (term_kind(terms, product) == POWER_PRODUCT) { + pprod_t* pprod = pprod_for_term(terms, product); + for (j = 0; j < pprod->len; ++j) { + var = variable_db_get_variable(var_db, pprod->prod[j].var); + for (deg = 0; deg < pprod->prod[j].exp; ++ deg) { + int_mset_add(vars_out, var); + } + } + } else { + // Variable, or foreign term + var = variable_db_get_variable(var_db, product); + int_mset_add(vars_out, var); + } + } + break; + } + case POWER_PRODUCT: { + pprod_t* pprod = pprod_term_desc(terms, t); + uint32_t i, deg; + for (i = 0; i < pprod->len; ++ i) { + variable_t var = variable_db_get_variable(var_db, pprod->prod[i].var); + for (deg = 0; deg < pprod->prod[i].exp; ++ deg) { + int_mset_add(vars_out, var); + } + } + break; + } + default: + // A variable or a foreign term + int_mset_add(vars_out, variable_db_get_variable(var_db, t)); + } +} diff --git a/src/mcsat/ff/ff_plugin_internal.h b/src/mcsat/ff/ff_plugin_internal.h new file mode 100644 index 000000000..9ac35b895 --- /dev/null +++ b/src/mcsat/ff/ff_plugin_internal.h @@ -0,0 +1,127 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef FF_PLUGIN_INTERNAL_H +#define FF_PLUGIN_INTERNAL_H + +#include + +#include "mcsat/ff/ff_plugin_internal.h" +#include "mcsat/ff/ff_feasible_set_db.h" + +#include "mcsat/plugin.h" +#include "mcsat/unit_info.h" +#include "mcsat/watch_list_manager.h" +#include "mcsat/utils/scope_holder.h" +#include "mcsat/utils/lp_data.h" +#include "mcsat/utils/lp_utils.h" +#include "mcsat/utils/lp_constraint_db.h" +#include "mcsat/utils/int_mset.h" + +#include "terms/term_manager.h" + +typedef struct ff_plugin_s ff_plugin_t; + +struct ff_plugin_s { + + /** The plugin interface */ + plugin_t plugin_interface; + + /** The plugin context */ + plugin_context_t* ctx; + + /** The watch list manager */ + watch_list_manager_t wlm; + + /** The unit info */ + constraint_unit_info_t unit_info; + + /** Data related to libpoly */ + lp_data_t *lp_data; + + /** Last variable that was decided, but yet unprocessed */ + variable_t last_decided_and_unprocessed; + + /** Next index of the trail to process */ + uint32_t trail_i; + + /** The conflict variable (one with empty feasible set) */ + variable_t conflict_variable; + +#if 0 + /** The conflict variable (assumption not in feasible set) */ + variable_t conflict_variable_assumption; + + /** The value that got the assumptions variable in trouble */ + lp_value_t conflict_variable_value; + + /** Bound variable term */ + term_t global_bound_term; + +#endif + + /** Variables processed in propagation */ + ivector_t processed_variables; + + /** Size of processed (for backtracking) */ + uint32_t processed_variables_size; + + /** Scope holder for the int variables */ + scope_holder_t scope; + + struct { + statistic_int_t* propagations; + statistic_int_t* conflicts; + statistic_int_t* conflicts_assumption; + statistic_int_t* constraints_attached; + statistic_int_t* evaluations; + statistic_int_t* constraint; + statistic_int_t* variable_hints; + } stats; + + /** Database of polynomial constraints */ + poly_constraint_db_t* constraint_db; + + /** Map from variables to their feasible sets */ + ff_feasible_set_db_t* feasible_set_db; + +#if 0 + /** Buffer for evaluation */ + int_hmap_t evaluation_value_cache; + int_hmap_t evaluation_timestamp_cache; + + /** Buffer for feasible set computation (for true/false */ + int_hmap_t feasible_set_cache_top_var[2]; // Top var when cached + int_hmap_t feasible_set_cache_timestamp[2]; // Top timestamp of other variables when cached + ptr_hmap_t feasible_set_cache[2]; // The cache +#endif + + /** Arithmetic buffer for computation */ + rba_buffer_t buffer; + + /** Exception handler */ + jmp_buf* exception; + +}; + +void ff_plugin_get_constraint_variables(ff_plugin_t* ff, term_t constraint, int_mset_t* vars_out); + +void ff_plugin_get_term_variables(ff_plugin_t* ff, term_t t, int_mset_t* vars_out); + + +#endif /* FF_PLUGIN_INTERNAL_H */ diff --git a/src/mcsat/mcsat_types.h b/src/mcsat/mcsat_types.h index 0af353cf3..f912bce82 100644 --- a/src/mcsat/mcsat_types.h +++ b/src/mcsat/mcsat_types.h @@ -25,6 +25,7 @@ typedef struct mcsat_solver_s mcsat_solver_t; typedef struct mcsat_model_s mcsat_model_t; typedef struct mcsat_trail_s mcsat_trail_t; +typedef struct mcsat_value_s mcsat_value_t; typedef struct trail_token_s trail_token_t; typedef struct plugin_s plugin_t; typedef struct plugin_context_s plugin_context_t; diff --git a/src/mcsat/nra/feasible_set_db.c b/src/mcsat/nra/feasible_set_db.c index f792880c0..c966e9ce4 100644 --- a/src/mcsat/nra/feasible_set_db.c +++ b/src/mcsat/nra/feasible_set_db.c @@ -18,13 +18,14 @@ #include "mcsat/nra/feasible_set_db.h" #include "mcsat/utils/scope_holder.h" +#include "mcsat/utils/lp_constraint_db.h" #include "mcsat/tracing.h" #include "mcsat/nra/nra_plugin_internal.h" -#include "mcsat/nra/poly_constraint.h" #include "utils/int_array_sort2.h" #include +#include #include /** @@ -78,12 +79,12 @@ struct feasible_set_db_struct { /** Scope for push/pop */ scope_holder_t scope; - /** BV context */ - plugin_context_t* ctx; + /** the plugin */ + nra_plugin_t* plugin; }; static -uint32_t feasible_set_db_get_index(feasible_set_db_t* db, variable_t x) { +uint32_t feasible_set_db_get_index(const feasible_set_db_t* db, variable_t x) { int_hmap_pair_t* find = int_hmap_find(&db->var_to_feasible_set_map, x); if (find == NULL) { return 0; @@ -92,9 +93,21 @@ uint32_t feasible_set_db_get_index(feasible_set_db_t* db, variable_t x) { } } +static +void feasibility_list_element_delete(feasibility_list_element_t *element) { + // Deallocate allocated data + lp_feasibility_set_t* s1 = element->feasible_set; + lp_feasibility_set_t* s2 = element->reason_feasible_set; + lp_feasibility_set_delete(s1); + if (s1 != s2) { + lp_feasibility_set_delete(s2); + } + safe_free(element->reasons); +} + void feasible_set_db_print_var(feasible_set_db_t* db, variable_t var, FILE* out) { fprintf(out, "Feasible sets of "); - variable_db_print_variable(db->ctx->var_db, var, out); + variable_db_print_variable(db->plugin->ctx->var_db, var, out); fprintf(out, " :\n"); uint32_t index = feasible_set_db_get_index(db, var); while (index != 0) { @@ -108,11 +121,11 @@ void feasible_set_db_print_var(feasible_set_db_t* db, variable_t var, FILE* out) fprintf(out, "\t\tDue to lemma\n"); } else { fprintf(out, "\t\tDue to "); - term_t reason_term = variable_db_get_term(db->ctx->var_db, current->reasons[0]); - term_print_to_file(out, db->ctx->terms, reason_term); - if (term_type_kind(db->ctx->terms, reason_term) == BOOL_TYPE) { + term_t reason_term = variable_db_get_term(db->plugin->ctx->var_db, current->reasons[0]); + term_print_to_file(out, db->plugin->ctx->terms, reason_term); + if (term_type_kind(db->plugin->ctx->terms, reason_term) == BOOL_TYPE) { // Otherwise it's a term evaluation, always true - fprintf(out, " assigned to %s\n", trail_get_boolean_value(db->ctx->trail, current->reasons[0]) ? "true" : "false"); + fprintf(out, " assigned to %s\n", trail_get_boolean_value(db->plugin->ctx->trail, current->reasons[0]) ? "true" : "false"); } } index = current->prev; @@ -125,11 +138,11 @@ void feasible_set_db_print(feasible_set_db_t* db, FILE* out) { variable_t var = it->key; fprintf(out, "Feasible sets of "); - variable_db_print_variable(db->ctx->var_db, var, out); + variable_db_print_variable(db->plugin->ctx->var_db, var, out); fprintf(out, " :\n"); - if (trail_has_value(db->ctx->trail, var)) { + if (trail_has_value(db->plugin->ctx->trail, var)) { fprintf(out, "\tassigned to: "); - const mcsat_value_t* var_value = trail_get_value(db->ctx->trail, var); + const mcsat_value_t* var_value = trail_get_value(db->plugin->ctx->trail, var); mcsat_value_print(var_value, out); fprintf(out, "\n"); } @@ -149,12 +162,12 @@ void feasible_set_db_print(feasible_set_db_t* db, FILE* out) { #define INITIAL_DB_SIZE 100 -feasible_set_db_t* feasible_set_db_new(plugin_context_t* ctx) { +feasible_set_db_t* feasible_set_db_new(nra_plugin_t* nra) { feasible_set_db_t* db = safe_malloc(sizeof(feasible_set_db_t)); db->memory_size = 1; // 0 is special null ref db->memory_capacity = INITIAL_DB_SIZE; - db->memory = safe_malloc(sizeof(feasibility_list_element_t)*db->memory_capacity); + db->memory = safe_malloc(sizeof(feasibility_list_element_t) * db->memory_capacity); init_int_hmap(&db->var_to_feasible_set_map, 0); init_ivector(&db->updates, 0); @@ -167,23 +180,16 @@ feasible_set_db_t* feasible_set_db_new(plugin_context_t* ctx) { scope_holder_construct(&db->scope); - db->ctx = ctx; + db->plugin = nra; return db; } void feasible_set_db_delete(feasible_set_db_t* db) { // Delete the feasible sets - uint32_t i; // Start from 1, 0 is special. - for (i = 1; i < db->memory_size; ++ i) { - safe_free(db->memory[i].reasons); - lp_feasibility_set_t* s1 = db->memory[i].feasible_set; - lp_feasibility_set_t* s2 = db->memory[i].reason_feasible_set; - lp_feasibility_set_delete(s1); - if (s1 != s2) { - lp_feasibility_set_delete(s2); - } + for (uint32_t i = 1; i < db->memory_size; ++ i) { + feasibility_list_element_delete(db->memory + i); } // Delete the other stuff delete_int_hmap(&db->var_to_feasible_set_map); @@ -204,33 +210,42 @@ lp_feasibility_set_t* feasible_set_db_get(feasible_set_db_t* db, variable_t x) { } } +static inline +void ff_feasible_set_db_ensure_memory(feasible_set_db_t* db) { + if (db->memory_size >= db->memory_capacity) { + db->memory_capacity = db->memory_capacity + db->memory_capacity / 2; + db->memory = safe_realloc(db->memory, db->memory_capacity * sizeof(feasibility_list_element_t)); + } + assert(db->memory_size < db->memory_capacity); +} + /** Update the feasible set of the variable with a new set */ -bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_set_t* new_set, variable_t* cstr_list, uint32_t cstr_count) { +bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_set_t* new_set, const variable_t* cstr_list, uint32_t cstr_count) { assert(db->updates_size == db->updates.size); bool feasible = true; - if (ctx_trace_enabled(db->ctx, "nra::feasible_set_db")) { - fprintf(ctx_trace_out(db->ctx), "feasible_set_db_update\n"); - feasible_set_db_print(db, ctx_trace_out(db->ctx)); + if (ctx_trace_enabled(db->plugin->ctx, "nra::feasible_set_db")) { + fprintf(ctx_trace_out(db->plugin->ctx), "feasible_set_db_update\n"); + feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); } // The one we're adding - lp_feasibility_set_t* intersect = 0; + lp_feasibility_set_t* intersect = NULL; // Intersect, if no difference, we're done const lp_feasibility_set_t* old_set = feasible_set_db_get(db, x); - if (old_set != 0) { + if (old_set != NULL) { - if (ctx_trace_enabled(db->ctx, "nra::feasible_set_db")) { - ctx_trace_printf(db->ctx, "feasible_set_db_update()\n"); - ctx_trace_printf(db->ctx, "old_set = "); - lp_feasibility_set_print(old_set, ctx_trace_out(db->ctx)); - ctx_trace_printf(db->ctx, "\nnew_set = "); - lp_feasibility_set_print(new_set, ctx_trace_out(db->ctx)); - ctx_trace_printf(db->ctx, "\n"); + if (ctx_trace_enabled(db->plugin->ctx, "nra::feasible_set_db")) { + ctx_trace_printf(db->plugin->ctx, "feasible_set_db_update()\n"); + ctx_trace_printf(db->plugin->ctx, "old_set = "); + lp_feasibility_set_print(old_set, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\nnew_set = "); + lp_feasibility_set_print(new_set, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\n"); } assert(!lp_feasibility_set_is_empty(old_set)); @@ -242,7 +257,6 @@ bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_ lp_feasibility_set_delete(intersect); lp_feasibility_set_delete(new_set); return true; - break; case LP_FEASIBILITY_SET_INTERSECT_S2: case LP_FEASIBILITY_SET_NEW: // We have a proper new set @@ -264,12 +278,10 @@ bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_ // Allocate a new one uint32_t new_index = db->memory_size; // Allocate new element - if (db->memory_size == db->memory_capacity) { - db->memory_capacity = db->memory_capacity + db->memory_capacity/2; - db->memory = safe_realloc(db->memory, db->memory_capacity*sizeof(feasibility_list_element_t)); - } db->memory_size ++; - // Setup the element + ff_feasible_set_db_ensure_memory(db); + + // Set up the element feasibility_list_element_t* new_element = db->memory + new_index; new_element->feasible_set = intersect; new_element->reason_feasible_set = new_set; @@ -277,8 +289,7 @@ bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_ // Reasons new_element->reasons_size = cstr_count; new_element->reasons = safe_malloc(sizeof(variable_t)*cstr_count); - uint32_t i; - for (i = 0; i < cstr_count; ++ i) { + for (uint32_t i = 0; i < cstr_count; ++ i) { new_element->reasons[i] = cstr_list[i]; } // Add to map @@ -305,18 +316,18 @@ bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_ void feasible_set_db_push(feasible_set_db_t* db) { scope_holder_push(&db->scope, - &db->updates_size, - &db->fixed_variable_size, - &db->fixed_variables_i, - NULL + &db->updates_size, + &db->fixed_variable_size, + &db->fixed_variables_i, + NULL ); } void feasible_set_db_pop(feasible_set_db_t* db) { - if (ctx_trace_enabled(db->ctx, "nra::feasible_set_db")) { - fprintf(ctx_trace_out(db->ctx), "feasible_set_db_pop"); - feasible_set_db_print(db, ctx_trace_out(db->ctx)); + if (ctx_trace_enabled(db->plugin->ctx, "nra::feasible_set_db")) { + fprintf(ctx_trace_out(db->plugin->ctx), "feasible_set_db_pop"); + feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); } scope_holder_pop(&db->scope, @@ -338,14 +349,7 @@ void feasible_set_db_pop(feasible_set_db_t* db) { db->memory_size --; feasibility_list_element_t* element = db->memory + db->memory_size; uint32_t prev = element->prev; - // Deallocate aloocated data - lp_feasibility_set_t* s1 = element->feasible_set; - lp_feasibility_set_t* s2 = element->reason_feasible_set; - lp_feasibility_set_delete(s1); - if (s1 != s2) { - lp_feasibility_set_delete(s2); - } - safe_free(element->reasons); + feasibility_list_element_delete(element); // Redirect map to the previous one int_hmap_pair_t* find = int_hmap_find(&db->var_to_feasible_set_map, x); assert(find != NULL); @@ -353,13 +357,13 @@ void feasible_set_db_pop(feasible_set_db_t* db) { find->val = prev; } - if (ctx_trace_enabled(db->ctx, "nra::feasible_set_db")) { - feasible_set_db_print(db, ctx_trace_out(db->ctx)); + if (ctx_trace_enabled(db->plugin->ctx, "nra::feasible_set_db")) { + feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); } } static -void feasible_set_get_conflict_reason_indices(feasible_set_db_t* db, variable_t x, ivector_t* reasons_indices) { +void feasible_set_get_conflict_reason_indices(const feasible_set_db_t* db, variable_t x, ivector_t* reasons_indices) { // Go back from the top reason for x and gather the indices uint32_t reason_index = feasible_set_db_get_index(db, x); assert(reason_index); @@ -373,8 +377,6 @@ void feasible_set_get_conflict_reason_indices(feasible_set_db_t* db, variable_t static void feasible_set_quickxplain(const feasible_set_db_t* db, const lp_feasibility_set_t* current, const mcsat_value_t* value, ivector_t* reasons, uint32_t begin, uint32_t end, ivector_t* out) { - uint32_t i; - if (lp_feasibility_set_is_empty(current)) { // Core already unsat, done return; @@ -398,7 +400,7 @@ void feasible_set_quickxplain(const feasible_set_db_t* db, const lp_feasibility_ // Assert first half and minimize the second lp_feasibility_set_t* feasible_A = lp_feasibility_set_new_copy(current); - for (i = begin; i < begin + n; ++ i) { + for (uint32_t i = begin; i < begin + n; ++ i) { const lp_feasibility_set_t* feasible_i = db->memory[reasons->data[i]].reason_feasible_set; lp_feasibility_set_intersect_status_t intersect_status; lp_feasibility_set_t* intersect = lp_feasibility_set_intersect_with_status(feasible_A, feasible_i, &intersect_status); @@ -411,7 +413,7 @@ void feasible_set_quickxplain(const feasible_set_db_t* db, const lp_feasibility_ // Now, assert the minimized second half, and minimize the first half lp_feasibility_set_t* feasible_B = lp_feasibility_set_new_copy(current); - for (i = old_out_size; i < out->size; ++ i) { + for (uint32_t i = old_out_size; i < out->size; ++ i) { const lp_feasibility_set_t* feasible_i = db->memory[out->data[i]].reason_feasible_set; lp_feasibility_set_intersect_status_t intersect_status; lp_feasibility_set_t* intersect = lp_feasibility_set_intersect_with_status(feasible_B, feasible_i, &intersect_status); @@ -422,54 +424,43 @@ void feasible_set_quickxplain(const feasible_set_db_t* db, const lp_feasibility_ lp_feasibility_set_delete(feasible_B); } -/** Compare variables first by degree, then by level, prefer non root constraints */ static -bool compare_reasons(void *nra_plugin, int32_t r1, int32_t r2) { - - uint32_t i; - - const nra_plugin_t* nra = (nra_plugin_t*) nra_plugin; - feasible_set_db_t* db = nra->feasible_set_db; - poly_constraint_db_t* poly_db = nra->constraint_db; - const mcsat_trail_t* trail = nra->ctx->trail; - - // Get max degree and max level of the reasons of first constraint - uint32_t r1_degree = 0; - uint32_t r1_level = 0; - for (i = 0; i < db->memory[r1].reasons_size; ++ i) { - variable_t r1_i_var = db->memory[r1].reasons[i]; - if (trail_has_value(trail, r1_i_var)) { - uint32_t r1_i_level = trail_get_level(trail, r1_i_var); - if (r1_i_level > r1_level) { - r1_level = r1_i_level; +void get_max_degree_max_level(const nra_plugin_t *nra, const feasibility_list_element_t* memory, uint32_t *max_deg, uint32_t *max_lvl) { + uint32_t deg = 0, lvl = 0; + + for (uint32_t i = 0; i < memory->reasons_size; ++ i) { + variable_t i_var = memory->reasons[i]; + if (trail_has_value(nra->ctx->trail, i_var)) { + uint32_t i_lvl = trail_get_level(nra->ctx->trail, i_var); + if (i_lvl > lvl) { + lvl = i_lvl; } } - const poly_constraint_t* r1_i_constraint = poly_constraint_db_get(poly_db, r1_i_var); - const lp_polynomial_t* r1_i_poly = poly_constraint_get_polynomial(r1_i_constraint); - uint32_t r1_i_degree = lp_polynomial_degree(r1_i_poly); - if (r1_i_degree > r1_degree) { - r1_degree = r1_i_degree; + const poly_constraint_t* i_constraint = poly_constraint_db_get(nra->constraint_db, i_var); + const lp_polynomial_t* i_poly = poly_constraint_get_polynomial(i_constraint); + uint32_t i_deg = lp_polynomial_degree(i_poly); + if (i_deg > deg) { + deg = i_deg; } } - // Get max degree and max level of the reasons of second constraint - uint32_t r2_degree = 0; - uint32_t r2_level = 0; - for (i = 0; i < db->memory[r2].reasons_size; ++ i) { - variable_t r2_i_var = db->memory[r2].reasons[i]; - if (trail_has_value(trail, r2_i_var)) { - uint32_t r2_i_level = trail_get_level(trail, r2_i_var); - if (r2_i_level > r2_level) { - r2_level = r2_i_level; - } - } - const poly_constraint_t* r2_i_constraint = poly_constraint_db_get(poly_db, r2_i_var); - const lp_polynomial_t* r2_i_poly = poly_constraint_get_polynomial(r2_i_constraint); - uint32_t r2_i_degree = lp_polynomial_degree(r2_i_poly); - if (r2_i_degree > r2_degree) { - r2_degree = r2_i_degree; - } - } + *max_deg = deg; + *max_lvl = lvl; +} + +/** Compare variables first by degree, then by level, prefer non root constraints */ +static +bool compare_reasons(void *nra_plugin, int32_t r1, int32_t r2) { + // TODO actually prefer non root constraints (as hinted by the function description) + + const nra_plugin_t* nra = (nra_plugin_t*) nra_plugin; + const feasible_set_db_t* db = nra->feasible_set_db; + + // Get max degree and max level of the reasons of both constraints + uint32_t r1_degree = 0, r2_degree = 0; + uint32_t r1_level = 0, r2_level = 0; + get_max_degree_max_level(nra, db->memory + r1, &r1_degree, &r1_level); + get_max_degree_max_level(nra, db->memory + r2, &r2_degree, &r2_level); // Prefer smaller degrees if (r1_degree != r2_degree) { @@ -480,7 +471,8 @@ bool compare_reasons(void *nra_plugin, int32_t r1, int32_t r2) { return r1_level < r2_level; } -void print_conflict_reasons(FILE* out, feasible_set_db_t* db, nra_plugin_t* nra, ivector_t* reason_indices) { +static +void print_conflict_reasons(FILE* out, const feasible_set_db_t* db, nra_plugin_t* nra, ivector_t* reason_indices) { uint32_t i, j; poly_constraint_db_t* poly_db = nra->constraint_db; @@ -499,17 +491,17 @@ void print_conflict_reasons(FILE* out, feasible_set_db_t* db, nra_plugin_t* nra, } static -void feasible_set_filter_reason_indices(feasible_set_db_t* db, nra_plugin_t* nra, const mcsat_value_t* x_value, ivector_t* reasons_indices) { +void feasible_set_filter_reason_indices(const feasible_set_db_t* db, const mcsat_value_t* x_value, ivector_t* reasons_indices) { // The set we're trying to make empty lp_feasibility_set_t* S = lp_feasibility_set_new_full(); - // Sort variables by degree and trail level descreasing - int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) nra, compare_reasons); + // Sort variables by degree and trail level decreasing + int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) db->plugin, compare_reasons); - if (ctx_trace_enabled(db->ctx, "nra::conflict")) { - ctx_trace_printf(db->ctx, "filtering: before\n"); - print_conflict_reasons(ctx_trace_out(db->ctx), db, nra, reasons_indices); - } + if (ctx_trace_enabled(db->plugin->ctx, "nra::conflict")) { + ctx_trace_printf(db->plugin->ctx, "filtering: before\n"); + print_conflict_reasons(ctx_trace_out(db->plugin->ctx), db, db->plugin, reasons_indices); + } // Minimize the core ivector_t out; @@ -518,12 +510,12 @@ void feasible_set_filter_reason_indices(feasible_set_db_t* db, nra_plugin_t* nra ivector_swap(reasons_indices, &out); delete_ivector(&out); - // Sort again for consisency - int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) nra, compare_reasons); + // Sort again for consistency + int_array_sort2(reasons_indices->data, reasons_indices->size, (void*) db->plugin, compare_reasons); - if (ctx_trace_enabled(db->ctx, "nra::conflict")) { - ctx_trace_printf(db->ctx, "filtering: after\n"); - print_conflict_reasons(ctx_trace_out(db->ctx), db, nra, reasons_indices); + if (ctx_trace_enabled(db->plugin->ctx, "nra::conflict")) { + ctx_trace_printf(db->plugin->ctx, "filtering: after\n"); + print_conflict_reasons(ctx_trace_out(db->plugin->ctx), db, db->plugin, reasons_indices); } // Remove temps @@ -548,10 +540,10 @@ bool feasible_set_check_if_conflict(feasible_set_db_t* db, ivector_t* set_indice const lp_feasibility_set_t* reason_feasible = db->memory[set_index].reason_feasible_set; lp_feasibility_set_t* intersect = lp_feasibility_set_intersect(S, reason_feasible); - if (ctx_trace_enabled(db->ctx, "nra::get_conflict")) { - ctx_trace_printf(db->ctx, "S = "); lp_feasibility_set_print(S, ctx_trace_out(db->ctx)); ctx_trace_printf(db->ctx, "\n"); - ctx_trace_printf(db->ctx, "reason_feasible = "); lp_feasibility_set_print(reason_feasible, ctx_trace_out(db->ctx)); ctx_trace_printf(db->ctx, "\n"); - ctx_trace_printf(db->ctx, "intersect = "); lp_feasibility_set_print(intersect, ctx_trace_out(db->ctx)); ctx_trace_printf(db->ctx, "\n"); + if (ctx_trace_enabled(db->plugin->ctx, "nra::get_conflict")) { + ctx_trace_printf(db->plugin->ctx, "S = "); lp_feasibility_set_print(S, ctx_trace_out(db->plugin->ctx)); ctx_trace_printf(db->plugin->ctx, "\n"); + ctx_trace_printf(db->plugin->ctx, "reason_feasible = "); lp_feasibility_set_print(reason_feasible, ctx_trace_out(db->plugin->ctx)); ctx_trace_printf(db->plugin->ctx, "\n"); + ctx_trace_printf(db->plugin->ctx, "intersect = "); lp_feasibility_set_print(intersect, ctx_trace_out(db->plugin->ctx)); ctx_trace_printf(db->plugin->ctx, "\n"); } lp_feasibility_set_swap(intersect, S); @@ -567,12 +559,12 @@ bool feasible_set_check_if_conflict(feasible_set_db_t* db, ivector_t* set_indice return conflict; } -void feasible_set_db_get_conflict_reasons(feasible_set_db_t* db, nra_plugin_t* nra, variable_t x, const mcsat_value_t* x_value, ivector_t* reasons_out, ivector_t* lemma_reasons) { +void feasible_set_db_get_conflict_reasons(const feasible_set_db_t* db, variable_t x, const mcsat_value_t* x_value, ivector_t* reasons_out, ivector_t* lemma_reasons) { - if (ctx_trace_enabled(db->ctx, "nra::get_conflict")) { - ctx_trace_printf(db->ctx, "get_reasons of: "); - variable_db_print_variable(db->ctx->var_db, x, ctx_trace_out(db->ctx)); - ctx_trace_printf(db->ctx, "\n"); + if (ctx_trace_enabled(db->plugin->ctx, "nra::get_conflict")) { + ctx_trace_printf(db->plugin->ctx, "get_reasons of: "); + variable_db_print_variable(db->plugin->ctx->var_db, x, ctx_trace_out(db->plugin->ctx)); + ctx_trace_printf(db->plugin->ctx, "\n"); } ivector_t reasons_indices; @@ -582,22 +574,20 @@ void feasible_set_db_get_conflict_reasons(feasible_set_db_t* db, nra_plugin_t* n feasible_set_get_conflict_reason_indices(db, x, &reasons_indices); // Do a first pass filter from the back - feasible_set_filter_reason_indices(db, nra, x_value, &reasons_indices); + feasible_set_filter_reason_indices(db, x_value, &reasons_indices); // Return the conjunctive reasons - uint32_t i; - for (i = 0; i < reasons_indices.size; ++ i) { + for (uint32_t i = 0; i < reasons_indices.size; ++ i) { uint32_t set_index = reasons_indices.data[i]; feasibility_list_element_t* element = db->memory + set_index; if (element->reasons_size == 1) { variable_t reason = element->reasons[0]; - assert(variable_db_is_boolean(db->ctx->var_db, reason)); + assert(variable_db_is_boolean(db->plugin->ctx->var_db, reason)); ivector_push(reasons_out, reason); } else { - uint32_t j; - for (j = 0; j < element->reasons_size; ++j) { + for (uint32_t j = 0; j < element->reasons_size; ++j) { variable_t reason = element->reasons[j]; - assert(variable_db_is_boolean(db->ctx->var_db, reason)); + assert(variable_db_is_boolean(db->plugin->ctx->var_db, reason)); ivector_push(lemma_reasons, reason); } } @@ -610,24 +600,24 @@ variable_t feasible_set_db_get_cheap_unassigned(feasible_set_db_t* db, lp_value_ variable_t best_var = variable_null; size_t best_var_degree = 0; - if (ctx_trace_enabled(db->ctx, "nra::decide")) { - feasible_set_db_print(db, ctx_trace_out(db->ctx)); + if (ctx_trace_enabled(db->plugin->ctx, "nra::decide")) { + feasible_set_db_print(db, ctx_trace_out(db->plugin->ctx)); } int_hmap_pair_t* it = int_hmap_first_record(&db->var_to_feasible_set_map); for (; it != NULL; it = int_hmap_next_record(&db->var_to_feasible_set_map, it)) { variable_t current_var = it->key; - if (!trail_has_value(db->ctx->trail, current_var)) { + if (!trail_has_value(db->plugin->ctx->trail, current_var)) { lp_feasibility_set_t* current_var_set = feasible_set_db_get(db, current_var); if (current_var_set == NULL) { - if (best_var == variable_null || db->ctx->cmp_variables(db->ctx, current_var, best_var)) { + if (best_var == variable_null || db->plugin->ctx->cmp_variables(db->plugin->ctx, current_var, best_var)) { best_var = current_var; best_var_degree = 0; } } else { lp_feasibility_set_pick_value(current_var_set, value); if (lp_value_is_rational(value)) { - if (best_var == variable_null || db->ctx->cmp_variables(db->ctx, current_var, best_var)) { + if (best_var == variable_null || db->plugin->ctx->cmp_variables(db->plugin->ctx, current_var, best_var)) { best_var = current_var; best_var_degree = 0; } @@ -657,7 +647,7 @@ variable_t feasible_set_db_get_cheap_unassigned(feasible_set_db_t* db, lp_value_ void feasible_set_db_gc_mark(feasible_set_db_t* db, gc_info_t* gc_vars) { - assert(db->ctx->trail->decision_level == db->ctx->trail->decision_level_base); + assert(db->plugin->ctx->trail->decision_level == db->plugin->ctx->trail->decision_level_base); if (gc_vars->level == 0) { // We keep all the reasons (start from 1, 0 is not used) @@ -674,7 +664,7 @@ void feasible_set_db_gc_mark(feasible_set_db_t* db, gc_info_t* gc_vars) { variable_t feasible_set_db_get_fixed(feasible_set_db_t* db) { for (; db->fixed_variables_i < db->fixed_variables.size; ++ db->fixed_variables_i) { variable_t var = db->fixed_variables.data[db->fixed_variables_i]; - if (!trail_has_value(db->ctx->trail, var)) { + if (!trail_has_value(db->plugin->ctx->trail, var)) { return var; } } diff --git a/src/mcsat/nra/feasible_set_db.h b/src/mcsat/nra/feasible_set_db.h index ae95e7826..ecfcc8e10 100644 --- a/src/mcsat/nra/feasible_set_db.h +++ b/src/mcsat/nra/feasible_set_db.h @@ -31,7 +31,7 @@ typedef struct nra_plugin_s nra_plugin_t; typedef struct feasible_set_db_struct feasible_set_db_t; /** Create a new database */ -feasible_set_db_t* feasible_set_db_new(plugin_context_t* ctx); +feasible_set_db_t* feasible_set_db_new(nra_plugin_t * ctx); /** Delete the database */ void feasible_set_db_delete(feasible_set_db_t* db); @@ -42,7 +42,7 @@ void feasible_set_db_delete(feasible_set_db_t* db); * * If more than one reason, it's considered a disjunctive top-level assertion (clause); */ -bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_set_t* new_set, variable_t* reasons, uint32_t reasons_count); +bool feasible_set_db_update(feasible_set_db_t* db, variable_t x, lp_feasibility_set_t* new_set, const variable_t* reasons, uint32_t reasons_count); /** Get the feasible set of a variable */ lp_feasibility_set_t* feasible_set_db_get(feasible_set_db_t* db, variable_t x); @@ -54,7 +54,7 @@ void feasible_set_db_push(feasible_set_db_t* db); void feasible_set_db_pop(feasible_set_db_t* db); /** Get the reason for a conflict on x. Feasible set of x should be empty. */ -void feasible_set_db_get_conflict_reasons(feasible_set_db_t* db, nra_plugin_t* nra, variable_t x, const mcsat_value_t* x_value, ivector_t* reasons_out, ivector_t* lemma_reasons_out); +void feasible_set_db_get_conflict_reasons(const feasible_set_db_t* db, variable_t x, const mcsat_value_t* x_value, ivector_t* reasons_out, ivector_t* lemma_reasons_out); /** Return any fixed variables */ variable_t feasible_set_db_get_fixed(feasible_set_db_t* db); diff --git a/src/mcsat/nra/libpoly_utils.c b/src/mcsat/nra/libpoly_utils.c deleted file mode 100644 index 0f2db8fab..000000000 --- a/src/mcsat/nra/libpoly_utils.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * This file is part of the Yices SMT Solver. - * Copyright (C) 2017 SRI International. - * - * Yices is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Yices is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Yices. If not, see . - */ - -#include "mcsat/nra/libpoly_utils.h" -#include "mcsat/nra/nra_plugin_internal.h" -#include "mcsat/tracing.h" - -#include "terms/balanced_arith_buffers.h" -#include "terms/rba_buffer_terms.h" -#include "terms/term_manager.h" - -#include -#include - -#include - -lp_polynomial_t* lp_polynomial_from_power_product_nra(nra_plugin_t* nra, pprod_t* pp, lp_integer_t* c) { - - // Context - lp_polynomial_context_t* lp_ctx = nra->lp_data.lp_ctx; - - // The monomials - lp_monomial_t lp_monomial; - lp_monomial_construct(lp_ctx, &lp_monomial); - - // Set monomial coefficient to 1 - lp_integer_t one; - lp_integer_construct_from_int(lp_Z, &one, 1); - lp_monomial_set_coefficient(lp_ctx, &lp_monomial, &one); - lp_integer_destruct(&one); - - // Get the product terms - uint32_t i = 0; - for (i = 0; i < pp->len; ++ i) { - variable_t var = variable_db_get_variable(nra->ctx->var_db, pp->prod[i].var); - lp_variable_t lp_var = nra_plugin_get_lp_variable(nra, var); - lp_monomial_push(&lp_monomial, lp_var, pp->prod[i].exp); - } - - lp_polynomial_t* result = lp_polynomial_new(nra->lp_data.lp_ctx); - lp_polynomial_add_monomial(result, &lp_monomial); - - if (c) { - lp_integer_assign_int(lp_Z, c, 1); - } - - lp_monomial_destruct(&lp_monomial); - - return result; -} - -lp_polynomial_t* lp_polynomial_from_power_product(pprod_t* pp, int_hmap_t* term_to_lp_map, const lp_polynomial_context_t* lp_ctx, lp_integer_t* c) { - - // The monomials - lp_monomial_t lp_monomial; - lp_monomial_construct(lp_ctx, &lp_monomial); - - // Set monomial coefficient to 1 - lp_integer_t one; - lp_integer_construct_from_int(lp_Z, &one, 1); - lp_monomial_set_coefficient(lp_ctx, &lp_monomial, &one); - lp_integer_destruct(&one); - - // Get the product terms - uint32_t i = 0; - for (i = 0; i < pp->len; ++ i) { - int_hmap_pair_t* var_find = int_hmap_find(term_to_lp_map, pp->prod[i].var); - assert(var_find != NULL); - lp_variable_t lp_var = var_find->val; - lp_monomial_push(&lp_monomial, lp_var, pp->prod[i].exp); - } - - lp_polynomial_t* result = lp_polynomial_new(lp_ctx); - lp_polynomial_add_monomial(result, &lp_monomial); - - if (c) { - lp_integer_assign_int(lp_Z, c, 1); - } - - lp_monomial_destruct(&lp_monomial); - - return result; -} - -lp_polynomial_t* lp_polynomial_from_polynomial_nra(nra_plugin_t* nra, polynomial_t* p, lp_integer_t* c) { - - uint32_t i, j; - variable_t var; - lp_variable_t lp_var; - - lp_polynomial_t* result = lp_polynomial_new(nra->lp_data.lp_ctx); - - // - // we have - // q_1 + q_2*p_2 + ... + q_n p_n - // - // with q rationals, and p power products - // - // we get the lcm of the denominators first, and multiply it out - // - - // Integers to represent rationals - lp_integer_t a, b; - lp_integer_construct(&a); - lp_integer_construct(&b); - - // Compute the lcm - lp_integer_t lcm; - lp_integer_construct_from_int(lp_Z, &lcm, 1); - for (i = 0; i < p->nterms; ++ i) { - lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); - lp_integer_lcm_Z(&lcm, &lcm, &b); - } - - // Assign to c - if (c) { - lp_integer_assign(lp_Z, c, &lcm); - } - - // Context - term_table_t* terms = nra->ctx->terms; - variable_db_t* var_db = nra->ctx->var_db; - lp_polynomial_context_t* lp_ctx = nra->lp_data.lp_ctx; - - // The monomials - lp_monomial_t lp_monomial; - lp_monomial_construct(lp_ctx, &lp_monomial); - - // Add up all the monomials - for (i = 0; i < p->nterms; ++ i) { - - term_t product = p->mono[i].var; - lp_monomial_clear(lp_ctx, &lp_monomial); - - // The constant (a/b)*lcm - lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); - lp_integer_div_exact(lp_Z, &b, &lcm, &b); - lp_integer_mul(lp_Z, &a, &a, &b); - lp_monomial_set_coefficient(lp_ctx, &lp_monomial, &a); - - if (product == const_idx) { - // Constant polynomial, nothing to do - } else if (term_kind(terms, product) == POWER_PRODUCT) { - // Add all the variables - pprod_t* pprod = pprod_for_term(terms, product); - for (j = 0; j < pprod->len; ++j) { - var = variable_db_get_variable(var_db, pprod->prod[j].var); - lp_var = nra_plugin_get_lp_variable(nra, var); - lp_monomial_push(&lp_monomial, lp_var, pprod->prod[j].exp); - } - } else { - // Variable, or foreign term - var = variable_db_get_variable(var_db, product); - lp_var = nra_plugin_get_lp_variable(nra, var); - lp_monomial_push(&lp_monomial, lp_var, 1); - } - - // Add the monomial to the polynomial - lp_polynomial_add_monomial(result, &lp_monomial); - } - - // Remove temps - lp_monomial_destruct(&lp_monomial); - lp_integer_destruct(&a); - lp_integer_destruct(&b); - lp_integer_destruct(&lcm); - - return result; -} - -lp_polynomial_t* lp_polynomial_from_polynomial(polynomial_t* p, term_table_t* terms, int_hmap_t* term_to_lp_map, const lp_polynomial_context_t* lp_ctx, lp_integer_t* c) { - - uint32_t i, j; - lp_variable_t lp_var; - - lp_polynomial_t* result = lp_polynomial_new(lp_ctx); - - // - // we have - // q_1 + q_2*p_2 + ... + q_n p_n - // - // with q rationals, and p power products - // - // we get the lcm of the denominators first, and multiply it out - // - - // Integers to represent rationals - lp_integer_t a, b; - lp_integer_construct(&a); - lp_integer_construct(&b); - - // Compute the lcm - lp_integer_t lcm; - lp_integer_construct_from_int(lp_Z, &lcm, 1); - for (i = 0; i < p->nterms; ++ i) { - lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); - lp_integer_lcm_Z(&lcm, &lcm, &b); - } - - // Assign to c - if (c) { - lp_integer_assign(lp_Z, c, &lcm); - } - - // The monomials - lp_monomial_t lp_monomial; - lp_monomial_construct(lp_ctx, &lp_monomial); - - // Add up all the monomials - for (i = 0; i < p->nterms; ++ i) { - - term_t product = p->mono[i].var; - lp_monomial_clear(lp_ctx, &lp_monomial); - - // The constant (a/b)*lcm - lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); - lp_integer_div_exact(lp_Z, &b, &lcm, &b); - lp_integer_mul(lp_Z, &a, &a, &b); - lp_monomial_set_coefficient(lp_ctx, &lp_monomial, &a); - - if (product == const_idx) { - // Constant polynomial, nothing to do - } else if (term_kind(terms, product) == POWER_PRODUCT) { - // Add all the variables - pprod_t* pprod = pprod_for_term(terms, product); - for (j = 0; j < pprod->len; ++j) { - int_hmap_pair_t* var_find = int_hmap_find(term_to_lp_map, pprod->prod[j].var); - assert(var_find != NULL); - lp_var = var_find->val; - lp_monomial_push(&lp_monomial, lp_var, pprod->prod[j].exp); - } - } else { - // Variable, or foreign term - int_hmap_pair_t* var_find = int_hmap_find(term_to_lp_map, product); - assert(var_find != NULL); - lp_var = var_find->val; - lp_monomial_push(&lp_monomial, lp_var, 1); - } - - // Add the monomial to the polynomial - lp_polynomial_add_monomial(result, &lp_monomial); - } - - // Remove temps - lp_monomial_destruct(&lp_monomial); - lp_integer_destruct(&a); - lp_integer_destruct(&b); - lp_integer_destruct(&lcm); - - return result; -} -lp_polynomial_t* lp_polynomial_from_term_nra(nra_plugin_t* nra, term_t t, lp_integer_t* c) { - term_kind_t kind; - lp_polynomial_t* result = NULL; - term_table_t* terms = nra->ctx->terms; - - if (ctx_trace_enabled(nra->ctx, "nra::terms")) { - ctx_trace_printf(nra->ctx, "lp_polynomial_from_term: t = "); - ctx_trace_term(nra->ctx, t); - } - - kind = term_kind(terms, t); - switch (kind) { - case ARITH_POLY: - result = lp_polynomial_from_polynomial_nra(nra, poly_term_desc(terms, t), c); - break; - case ARITH_CONSTANT: { - // Get the constant numerator and denominator - lp_integer_t lp_p; - lp_integer_construct_from_int(lp_Z, &lp_p, 0); - lp_integer_assign_yices_rational(&lp_p, c, rational_term_desc(terms, t)); - // polynomial a*x^0 - result = lp_polynomial_alloc(); - lp_polynomial_construct_simple(result, nra->lp_data.lp_ctx, &lp_p, 0, 0); - // Remove temp - lp_integer_destruct(&lp_p); - break; - } - case POWER_PRODUCT: - result = lp_polynomial_from_power_product_nra(nra, pprod_term_desc(terms, t), c); - break; - default: { - // Constant 1 - lp_integer_t one; - lp_integer_construct_from_int(lp_Z, &one, 1); - // The variable - variable_t t_var = variable_db_get_variable_if_exists(nra->ctx->var_db, t); - lp_variable_t lp_var = nra_plugin_get_lp_variable(nra, t_var); - // Polynomial 1*x^1 - result = lp_polynomial_alloc(); - lp_polynomial_construct_simple(result, nra->lp_data.lp_ctx, &one, lp_var, 1); - // Put 1 if requested - if (c != NULL) { - lp_integer_assign(lp_Z, c, &one); - } - // Remove temp - lp_integer_destruct(&one); - } - } - - if (ctx_trace_enabled(nra->ctx, "nra::terms")) { - ctx_trace_printf(nra->ctx, "lp_polynomial_from_term: result = "); - lp_polynomial_print(result, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, "\n"); - } - - return result; -} - -lp_polynomial_t* lp_polynomial_from_term(term_t t, term_table_t* terms, int_hmap_t* term_to_lp_map, const lp_polynomial_context_t* lp_ctx, lp_integer_t* c) { - term_kind_t kind; - lp_polynomial_t* result = NULL; - - kind = term_kind(terms, t); - switch (kind) { - case ARITH_POLY: - result = lp_polynomial_from_polynomial(poly_term_desc(terms, t), terms, term_to_lp_map, lp_ctx, c); - break; - case ARITH_CONSTANT: { - // Get the constant numerator and denominator - lp_integer_t lp_p; - lp_integer_construct_from_int(lp_Z, &lp_p, 0); - lp_integer_assign_yices_rational(&lp_p, c, rational_term_desc(terms, t)); - // polynomial a*x^0 - result = lp_polynomial_alloc(); - lp_polynomial_construct_simple(result, lp_ctx, &lp_p, 0, 0); - // Remove temp - lp_integer_destruct(&lp_p); - break; - } - case POWER_PRODUCT: - result = lp_polynomial_from_power_product(pprod_term_desc(terms, t), term_to_lp_map, lp_ctx, c); - break; - default: { - // Constant 1 - lp_integer_t one; - lp_integer_construct_from_int(lp_Z, &one, 1); - // The variable - int_hmap_pair_t* t_find = int_hmap_find(term_to_lp_map, t); - assert(t_find != NULL); - lp_variable_t lp_var = t_find->val; - // Polynomial 1*x^1 - result = lp_polynomial_alloc(); - lp_polynomial_construct_simple(result, lp_ctx, &one, lp_var, 1); - // Put 1 if requested - if (c != NULL) { - lp_integer_assign(lp_Z, c, &one); - } - // Remove temp - lp_integer_destruct(&one); - } - } - - return result; -} - -void lp_integer_construct_from_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q) { - if (lp_p != NULL) { - rational_t q_num; - q_init(&q_num); - q_get_num(&q_num, q); - mpq_t q_num_mpq; - mpq_init(q_num_mpq); - q_get_mpq(&q_num, q_num_mpq); - lp_integer_construct_from_rational(lp_Z, lp_p, q_num_mpq); - mpq_clear(q_num_mpq); - q_clear(&q_num); - } - if (lp_q != NULL) { - rational_t q_den; - q_init(&q_den); - q_get_den(&q_den, q); - mpq_t q_den_mpq; - mpq_init(q_den_mpq); - q_get_mpq(&q_den, q_den_mpq); - lp_integer_construct_from_rational(lp_Z, lp_q, q_den_mpq); - mpq_clear(q_den_mpq); - q_clear(&q_den); - } -} - -void lp_integer_assign_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q) { - lp_integer_destruct(lp_p); - lp_integer_destruct(lp_q); - lp_integer_construct_from_yices_rational(lp_p, lp_q, q); -} - -void rational_construct_from_lp_integer(rational_t* q, const lp_integer_t* lp_z) { - q_init(q); - q_set_mpz(q, lp_z); -} - -typedef struct { - nra_plugin_t* nra; - rba_buffer_t* b; - term_table_t* terms; -} lp_polynomial_to_yices_term_nra_data; - -void lp_polynomial_to_yices_traverse_f_nra(const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* void_data) { - - lp_polynomial_to_yices_term_nra_data* data = (lp_polynomial_to_yices_term_nra_data*) void_data; - - if (ctx_trace_enabled(data->nra->ctx, "nra::terms")) { - ctx_trace_printf(data->nra->ctx, "lp_polynomial_to_yices_term("); - lp_monomial_print(ctx, m, ctx_trace_out(data->nra->ctx)); - ctx_trace_printf(data->nra->ctx, ")\n"); - } - - // Constant - rational_t a; - q_init(&a); - q_set_mpz(&a, &m->a); - - if (m->n == 0) { - // Just constant - rba_buffer_add_const(data->b, &a); - } else { - // Actual monomial - pp_buffer_t pp; - init_pp_buffer(&pp, 0); - uint32_t i = 0; - for (i = 0; i < m->n; ++ i) { - lp_variable_t lp_x = m->p[i].x; - variable_t x = nra_plugin_get_variable_from_lp_variable(data->nra, lp_x); - term_t x_term = variable_db_get_term(data->nra->ctx->var_db, x); - pp_buffer_mul_varexp(&pp, x_term, m->p[i].d); - } - pprod_t* pprod = pprod_from_buffer(data->terms->pprods, &pp); - term_t pp_term = pp_is_var(pprod) ? var_of_pp(pprod) : pprod_term(data->terms, pprod); - rba_buffer_add_const_times_term(data->b, data->terms, &a, pp_term); - delete_pp_buffer(&pp); - } - - q_clear(&a); -} - -term_t lp_polynomial_to_yices_term_nra( const lp_polynomial_t* lp_p, nra_plugin_t* nra) { - - term_table_t* terms = nra->ctx->terms; - - if (ctx_trace_enabled(nra->ctx, "nra::terms")) { - ctx_trace_printf(nra->ctx, "lp_polynomial_to_yices_term("); - lp_polynomial_print(lp_p, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, ")\n"); - } - - // Buffer for building - lp_polynomial_to_yices_term_nra_data data; - data.nra = nra; - data.b = &nra->buffer; - data.terms = terms; - reset_rba_buffer(data.b); - - // Traverse and build - lp_polynomial_traverse(lp_p, lp_polynomial_to_yices_traverse_f_nra, &data); - - // Make the term - term_t result = mk_direct_arith_term(terms, data.b); - - if (ctx_trace_enabled(nra->ctx, "nra::terms")) { - ctx_trace_printf(nra->ctx, "lp_polynomial_to_yices_term("); - lp_polynomial_print(lp_p, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, ") => [%d] ", result); - ctx_trace_term(nra->ctx, result); - } - - return result; -} - -typedef struct { - int_hmap_t* lp_to_term_map; - rba_buffer_t* b; - term_table_t* terms; -} lp_polynomial_to_yices_term_data; - -void lp_polynomial_to_yices_traverse_f(const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* void_data) { - - lp_polynomial_to_yices_term_data* data = (lp_polynomial_to_yices_term_data*) void_data; - - // Constant - rational_t a; - q_init(&a); - q_set_mpz(&a, &m->a); - - if (m->n == 0) { - // Just constant - rba_buffer_add_const(data->b, &a); - } else { - // Actual monomial - pp_buffer_t pp; - init_pp_buffer(&pp, 0); - uint32_t i = 0; - for (i = 0; i < m->n; ++ i) { - lp_variable_t lp_x = m->p[i].x; - int_hmap_pair_t* lp_x_find = int_hmap_find(data->lp_to_term_map, lp_x); - assert(lp_x_find != NULL); - term_t x_term = lp_x_find->val; - pp_buffer_mul_varexp(&pp, x_term, m->p[i].d); - } - pprod_t* pprod = pprod_from_buffer(data->terms->pprods, &pp); - term_t pp_term = pp_is_var(pprod) ? var_of_pp(pprod) : pprod_term(data->terms, pprod); - rba_buffer_add_const_times_term(data->b, data->terms, &a, pp_term); - delete_pp_buffer(&pp); - } - - q_clear(&a); -} - -term_t lp_polynomial_to_yices_term(const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b, int_hmap_t* lp_to_term_map) { - - // Buffer for building - lp_polynomial_to_yices_term_data data; - data.lp_to_term_map = lp_to_term_map; - data.b = b; - data.terms = terms; - reset_rba_buffer(data.b); - - // Traverse and build - lp_polynomial_traverse(lp_p, lp_polynomial_to_yices_traverse_f, &data); - - // Make the term - term_t result = mk_direct_arith_term(terms, data.b); - - return result; -} - - -const mcsat_value_t* ensure_lp_value(const mcsat_value_t* value, mcsat_value_t* alternative) { - lp_value_t lp_value; - lp_rational_t rat_value; - switch (value->type) { - case VALUE_LIBPOLY: - return value; - case VALUE_RATIONAL: - lp_rational_construct(&rat_value); - q_get_mpq((rational_t*)&value->q, &rat_value); - lp_value_construct(&lp_value, LP_VALUE_RATIONAL, &rat_value); - mcsat_value_construct_lp_value(alternative, &lp_value); - lp_value_destruct(&lp_value); - lp_rational_destruct(&rat_value); - return alternative; - default: - assert(false); - } - return NULL; -} - -lp_variable_t lp_variable_from_term(term_t t, term_table_t* terms, lp_variable_db_t* lp_var_db) { - - // Name of the term - char buffer[100]; - char* var_name = term_name(terms, t); - if (var_name == NULL) { - var_name = buffer; - sprintf(var_name, "#%d", t); - } - - // Make the variable - lp_variable_t lp_var = lp_variable_db_new_variable(lp_var_db, var_name); - - return lp_var; -} - diff --git a/src/mcsat/nra/libpoly_utils.h b/src/mcsat/nra/libpoly_utils.h deleted file mode 100644 index 9b9151311..000000000 --- a/src/mcsat/nra/libpoly_utils.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the Yices SMT Solver. - * Copyright (C) 2017 SRI International. - * - * Yices is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Yices is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Yices. If not, see . - */ - -#pragma once - -#include - -#include "mcsat/nra/nra_plugin_internal.h" -#include "terms/polynomials.h" - -/** - * Create a lipoly polynomial from a yices polynomial. Returns the polynomial - * lp_p and a positive integer constant c, such that lp_p = p * c. If c is NULL - * it is ignored. - */ -lp_polynomial_t* lp_polynomial_from_polynomial_nra(nra_plugin_t* nra, polynomial_t* p, lp_integer_t* c); - -/** - * Create a libpoly polynomial from a yices power product. Returns lp_p = pp * c. - */ -lp_polynomial_t* lp_polynomial_from_power_product_nra(nra_plugin_t* nra, pprod_t * pp, lp_integer_t* c); - -/** - * Create a libpoly polynomial from a yices power product. Returns lp_p = pp * c. - * - * @param term_to_lp_map a map from variables (terms) to variables (libpoly). - */ -lp_polynomial_t* lp_polynomial_from_power_product(pprod_t* pp, int_hmap_t* term_to_lp_map, const lp_polynomial_context_t* lp_ctx, lp_integer_t* c); - -/** - * Create a libpoly polynomial from a yices term. Returns the polynomial - * lp_p and a positive integer constant c, such that lp_p = p * c. If c is - * NULL it is ignored. - */ -lp_polynomial_t* lp_polynomial_from_term_nra(nra_plugin_t* nra, term_t p, lp_integer_t* c); - -/** - * Create a libpoly polynomial from a yices term. Returns the polynomial - * lp_p and a positive integer constant c, such that lp_p = p * c. If c is - * NULL it is ignored. - * - * @param term_to_lp_map a map from variables (terms) to variables (libpoly). - */ -lp_polynomial_t* lp_polynomial_from_term(term_t t, term_table_t* terms, int_hmap_t* term_to_lp_map, const lp_polynomial_context_t* lp_ctx, lp_integer_t* c); - -/** - * Construct an p/q from a rational constant. If any of p or q are - */ -void lp_integer_construct_from_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q); - -/** - * Assign p/q from a yices rational constant. - */ -void lp_integer_assign_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q); - -/** - * Construct a yices rational from lp_integer. - */ -void rational_construct_from_lp_integer(rational_t* q, const lp_integer_t* lp_z); - -/** - * Get yices term from polynomial (NRA plugin version). - */ -term_t lp_polynomial_to_yices_term_nra(const lp_polynomial_t* lp_p, nra_plugin_t* nra); - -/** - * Get yices term from polynomial (direct version). - */ -term_t lp_polynomial_to_yices_term(const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b, int_hmap_t* lp_to_term_map); - -/** - * Ensure value is an lp_value. If not the passed alternative will be constructed to an equivalent lp_value. - */ -const mcsat_value_t* ensure_lp_value(const mcsat_value_t* value, mcsat_value_t* alternative); - -/** - * Create a lp_variable from term. - */ -lp_variable_t lp_variable_from_term(term_t t, term_table_t* terms, lp_variable_db_t* lp_var_db); diff --git a/src/mcsat/nra/nra_libpoly.c b/src/mcsat/nra/nra_libpoly.c new file mode 100644 index 000000000..bec5999b0 --- /dev/null +++ b/src/mcsat/nra/nra_libpoly.c @@ -0,0 +1,279 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "nra_libpoly.h" + +#include "mcsat/nra/nra_plugin_internal.h" +#include "mcsat/utils/lp_utils.h" +#include "mcsat/tracing.h" + +#include +#include +#include +#include +#include + +lp_polynomial_t* lp_polynomial_from_term_nra(nra_plugin_t* nra, term_t t, lp_integer_t* c) { + if (ctx_trace_enabled(nra->ctx, "nra::terms")) { + ctx_trace_printf(nra->ctx, "lp_polynomial_from_term: t = "); + ctx_trace_term(nra->ctx, t); + } + + lp_polynomial_t* result = lp_polynomial_from_term(&nra->lp_data, t, nra->ctx->terms, c); + + if (ctx_trace_enabled(nra->ctx, "nra::terms")) { + ctx_trace_printf(nra->ctx, "lp_polynomial_from_term: result = "); + lp_polynomial_print(result, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + } + + return result; +} + +term_t lp_polynomial_to_yices_term_nra(nra_plugin_t *nra, const lp_polynomial_t *lp_p) { + if (ctx_trace_enabled(nra->ctx, "nra::terms")) { + ctx_trace_printf(nra->ctx, "lp_polynomial_to_yices_term("); + lp_polynomial_print(lp_p, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, ")\n"); + } + + term_t result = lp_polynomial_to_yices_arith_term(&nra->lp_data, lp_p, nra->ctx->terms, &nra->buffer); + + if (ctx_trace_enabled(nra->ctx, "nra::terms")) { + ctx_trace_printf(nra->ctx, "lp_polynomial_to_yices_term("); + lp_polynomial_print(lp_p, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, ") => [%d] ", result); + ctx_trace_term(nra->ctx, result); + } + + return result; +} + +void nra_poly_constraint_add(nra_plugin_t *nra, variable_t constraint_var) { + if (poly_constraint_db_has(nra->constraint_db, constraint_var)) { + // Already added + return; + } + + poly_constraint_t* cstr = nra_poly_constraint_create(nra, constraint_var); + + if (poly_constraint_is_root_constraint(cstr)) { + (*nra->stats.constraint_root) ++; + } else { + (*nra->stats.constraint_regular) ++; + } + + if (ctx_trace_enabled(nra->ctx, "mcsat::new_term")) { + ctx_trace_printf(nra->ctx, "poly_constraint_add: "); + poly_constraint_print(cstr, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + } + + poly_constraint_db_add_constraint(nra->constraint_db, constraint_var, cstr); +} + +poly_constraint_t* nra_poly_constraint_create(nra_plugin_t *nra, variable_t constraint_var) { + // assert(poly_constraint_db_check(db)); + term_t constraint_var_term; + + // Constraint components + lp_polynomial_t* cstr_polynomial; + lp_variable_t cstr_root_variable = lp_variable_null; + uint32_t cstr_root_index; + lp_sign_condition_t sgn_condition; + + // Context + variable_db_t* var_db = nra->ctx->var_db; + term_table_t* terms = nra->ctx->terms; + + // Get the term of the variable + constraint_var_term = variable_db_get_term(var_db, constraint_var); + + // Depending on the kind, make the constraints + switch (term_kind(terms, constraint_var_term)) { + case ARITH_EQ_ATOM: { + // p == 0 + term_t t1 = arith_atom_arg(terms, constraint_var_term); + cstr_polynomial = lp_polynomial_from_term_nra(nra, t1, NULL); + sgn_condition = LP_SGN_EQ_0; + break; + } + case ARITH_GE_ATOM: { + // p >= 0 + term_t t1 = arith_atom_arg(terms, constraint_var_term); + cstr_polynomial = lp_polynomial_from_term_nra(nra, t1, NULL); + sgn_condition = LP_SGN_GE_0; + break; + } + case EQ_TERM: + case ARITH_BINEQ_ATOM: { + // LHS = RHS + term_t t1 = composite_term_arg(terms, constraint_var_term, 0); + term_t t2 = composite_term_arg(terms, constraint_var_term, 1); + // Get the polynomials + lp_integer_t t1_c, t2_c; + lp_integer_construct(&t1_c); + lp_integer_construct(&t2_c); + lp_polynomial_t* t1_p = lp_polynomial_from_term_nra(nra, t1, &t1_c); + lp_polynomial_t* t2_p = lp_polynomial_from_term_nra(nra, t2, &t2_c); + // t1_p/t1_c = t2_p/t2_c + // t1_p*t2_c - t2_p*t1_c + lp_integer_neg(lp_Z, &t1_c, &t1_c); + lp_polynomial_mul_integer(t1_p, t1_p, &t2_c); + lp_polynomial_mul_integer(t2_p, t2_p, &t1_c); + // Add them + lp_polynomial_add(t1_p, t1_p, t2_p); + // p1 = p2 + sgn_condition = LP_SGN_EQ_0; + cstr_polynomial = t1_p; + // Remove temps + lp_polynomial_delete(t2_p); + lp_integer_destruct(&t1_c); + lp_integer_destruct(&t2_c); + break; + } + case ARITH_ROOT_ATOM: { + root_atom_t* r = arith_root_atom_desc(terms, constraint_var_term); + cstr_polynomial = lp_polynomial_from_term_nra(nra, r->p, NULL); + assert(variable_db_has_variable(nra->ctx->var_db, r->x)); + cstr_root_variable = lp_data_get_lp_variable_from_term(&nra->lp_data, r->x); + cstr_root_index = r->k; + switch (r->r) { + case ROOT_ATOM_LT: + sgn_condition = LP_SGN_LT_0; + break; + case ROOT_ATOM_LEQ: + sgn_condition = LP_SGN_LE_0; + break; + case ROOT_ATOM_EQ: + sgn_condition = LP_SGN_EQ_0; + break; + case ROOT_ATOM_NEQ: + sgn_condition = LP_SGN_NE_0; + break; + case ROOT_ATOM_GEQ: + sgn_condition = LP_SGN_GE_0; + break; + case ROOT_ATOM_GT: + sgn_condition = LP_SGN_GT_0; + break; + default: + sgn_condition = LP_SGN_EQ_0; + assert(false); + break; + } + break; + } + default: { + // terms like (x+y), we create regular constraint (x+y) = x + y + lp_integer_t t1_c, t2_c; + lp_integer_construct_from_int(lp_Z, &t1_c, 1); + lp_integer_construct(&t2_c); + lp_polynomial_t* t1_p = lp_polynomial_alloc(); + term_t t = variable_db_get_term(nra->ctx->var_db, constraint_var); + lp_variable_t constraint_lp_var = lp_data_get_lp_variable_from_term(&nra->lp_data, t); + lp_polynomial_construct_simple(t1_p, nra->lp_data.lp_ctx, &t1_c, constraint_lp_var, 1); + lp_polynomial_t* t2_p = lp_polynomial_from_term_nra(nra, constraint_var_term, &t2_c); + // t1_p/t1_c = t2_p/t2_c + // t1_p*t2_c - t2_p*t1_c + lp_integer_neg(lp_Z, &t1_c, &t1_c); + lp_polynomial_mul_integer(t2_p, t2_p, &t1_c); + lp_polynomial_mul_integer(t1_p, t1_p, &t2_c); + // Add them + lp_polynomial_add(t1_p, t1_p, t2_p); + // p1 = p2 + sgn_condition = LP_SGN_EQ_0; + cstr_polynomial = t1_p; + // Remove temps + lp_polynomial_delete(t2_p); + lp_integer_destruct(&t1_c); + lp_integer_destruct(&t2_c); + + break; + } + } + + if (cstr_root_variable == lp_variable_null) { + return poly_constraint_new_regular(cstr_polynomial, sgn_condition); + } else { + return poly_constraint_new_root(cstr_polynomial, sgn_condition, cstr_root_variable, cstr_root_index); + } +} + +const mcsat_value_t* nra_poly_constraint_db_approximate(nra_plugin_t* nra, variable_t constraint_var) { + poly_constraint_db_t* db = nra->constraint_db; + const mcsat_value_t* result = NULL; + + // Get the constraints + const poly_constraint_t* cstr = poly_constraint_db_get(db, constraint_var); + if (poly_constraint_is_root_constraint(cstr)) { + // TODO: check if possible + return NULL; + } + + // Reset the interval assignment + lp_interval_assignment_t* m = nra->lp_data.lp_interval_assignment; + lp_interval_assignment_reset(m); + + // Set up the assignment x -> I(x) + assert(watch_list_manager_has_constraint(&nra->wlm, constraint_var)); + variable_list_ref_t var_list_ref = watch_list_manager_get_list_of(&nra->wlm, constraint_var); + variable_t* vars = watch_list_manager_get_list(&nra->wlm, var_list_ref); + for (; *vars != variable_null; vars++) { + variable_t x = *vars; + term_t t = variable_db_get_term(nra->ctx->var_db, x); + lp_variable_t x_lp = lp_data_get_lp_variable_from_term(&nra->lp_data, t); + lp_interval_t x_interval; + lp_interval_construct_full(&x_interval); + feasible_set_db_approximate_value(nra->feasible_set_db, x, &x_interval); + if (ctx_trace_enabled(nra->ctx, "mcsat::nra::learn")) { + ctx_trace_printf(nra->ctx, " "); + ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, x)); + ctx_trace_printf(nra->ctx, " "); + lp_interval_print(&x_interval, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + } + lp_interval_assignment_set_interval(m, x_lp, &x_interval); + lp_interval_destruct(&x_interval); + } + + // Evaluate the polynomial + lp_interval_t value; + lp_interval_construct_full(&value); + lp_polynomial_interval_value(cstr->polynomial, m, &value); + if (ctx_trace_enabled(nra->ctx, "mcsat::nra::learn")) { + poly_constraint_print(cstr, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, " -> "); + lp_interval_print(&value, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + } + + lp_sign_condition_t pos = cstr->sgn_condition; + lp_sign_condition_t neg = lp_sign_condition_negate(cstr->sgn_condition); + + if (lp_sign_condition_consistent_interval(pos, &value)) { + result = &mcsat_value_true; + } else if (lp_sign_condition_consistent_interval(neg, &value)) { + result = &mcsat_value_false; + } + + // Remove temps + lp_interval_destruct(&value); + + return result; +} diff --git a/src/mcsat/nra/nra_libpoly.h b/src/mcsat/nra/nra_libpoly.h new file mode 100644 index 000000000..b38506026 --- /dev/null +++ b/src/mcsat/nra/nra_libpoly.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef NRA_LIBPOLY_H_ +#define NRA_LIBPOLY_H_ + +#include "mcsat/mcsat_types.h" +#include "mcsat/utils/lp_constraint_db.h" + +#include + +typedef struct nra_plugin_s nra_plugin_t; + +/** + * Create a libpoly polynomial from a yices term. Returns the polynomial + * lp_p and a positive integer constant c, such that lp_p = p * c. If c is + * NULL it is ignored. + */ +lp_polynomial_t* lp_polynomial_from_term_nra(nra_plugin_t* nra, term_t p, lp_integer_t* c); + +/** + * Get yices term from polynomial (NRA plugin wrapper). + */ +term_t lp_polynomial_to_yices_term_nra(nra_plugin_t *nra, const lp_polynomial_t *lp_p); + +/** Compute an approximation of the constraint value with interval computation */ +const mcsat_value_t* nra_poly_constraint_db_approximate(nra_plugin_t* nra, variable_t constraint_var); + +/** Add a new constraint */ +void nra_poly_constraint_add(nra_plugin_t *nra, variable_t constraint_var); + +/** Create a new constraint */ +poly_constraint_t* nra_poly_constraint_create(nra_plugin_t *nra, variable_t constraint_var); + +#endif /* NRA_LIBPOLY_H_ */ diff --git a/src/mcsat/nra/nra_plugin.c b/src/mcsat/nra/nra_plugin.c index b809206e5..8e0e9cb79 100644 --- a/src/mcsat/nra/nra_plugin.c +++ b/src/mcsat/nra/nra_plugin.c @@ -36,17 +36,18 @@ #include "mcsat/nra/nra_plugin.h" #include "mcsat/nra/nra_plugin_internal.h" -#include "mcsat/nra/libpoly_utils.h" #include "mcsat/tracing.h" #include "mcsat/utils/scope_holder.h" #include "mcsat/utils/int_mset.h" +#include "mcsat/utils/lp_data.h" +#include "mcsat/utils/lp_utils.h" #include "mcsat/watch_list_manager.h" -#include "mcsat/nra/poly_constraint.h" +#include "mcsat/nra/nra_libpoly.h" #include "mcsat/nra/nra_plugin_explain.h" #include "terms/terms.h" -#include "utils/int_array_sort2.h" #include "terms/term_explorer.h" +#include "utils/int_array_sort2.h" #include "utils/refcount_strings.h" #include "api/yices_api_lock_free.h" @@ -86,17 +87,13 @@ void nra_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) { nra->conflict_variable = variable_null; watch_list_manager_construct(&nra->wlm, ctx->var_db); - init_int_hmap(&nra->constraint_unit_info, 0); - init_int_hmap(&nra->constraint_unit_var, 0); + constraint_unit_info_init(&nra->unit_info); init_ivector(&nra->processed_variables, 0); nra->processed_variables_size = 0; scope_holder_construct(&nra->scope); - init_int_hmap(&nra->lp_data.mcsat_to_lp_var_map, 0); - init_int_hmap(&nra->lp_data.lp_to_mcsat_var_map, 0); - init_int_hmap(&nra->evaluation_value_cache, 0); init_int_hmap(&nra->evaluation_timestamp_cache, 0); @@ -108,30 +105,13 @@ void nra_plugin_construct(plugin_t* plugin, plugin_context_t* ctx) { init_ptr_hmap(&nra->feasible_set_cache[1], 0); // Constraint db - nra->constraint_db = poly_constraint_db_new(nra); + nra->constraint_db = poly_constraint_db_new(&nra->lp_data); // Feasible sets - nra->feasible_set_db = feasible_set_db_new(ctx); - - // lipoly init - nra->lp_data.lp_var_db = lp_variable_db_new(); - nra->lp_data.lp_var_order = lp_variable_order_new(); - nra->lp_data.lp_var_order_size = 0; - nra->lp_data.lp_ctx = lp_polynomial_context_new(lp_Z, nra->lp_data.lp_var_db, nra->lp_data.lp_var_order); - nra->lp_data.lp_assignment = lp_assignment_new(nra->lp_data.lp_var_db); - nra->lp_data.lp_interval_assignment = lp_interval_assignment_new(nra->lp_data.lp_var_db); + nra->feasible_set_db = feasible_set_db_new(nra); - // Tracing in libpoly - if (false) { -// lp_trace_enable("coefficient"); -// lp_trace_enable("coefficient::sgn"); - lp_trace_enable("coefficient::interval"); - } - - // Trace pscs - if (false) { - lp_trace_enable("polynomial::expensive"); - } + // libpoly init + lp_data_init(&nra->lp_data, NULL, nra->ctx); // Atoms ctx->request_term_notification_by_kind(ctx, ARITH_EQ_ATOM, false); @@ -175,14 +155,11 @@ void nra_plugin_destruct(plugin_t* plugin) { nra_plugin_t* nra = (nra_plugin_t*) plugin; watch_list_manager_destruct(&nra->wlm); - delete_int_hmap(&nra->constraint_unit_info); - delete_int_hmap(&nra->constraint_unit_var); + constraint_unit_info_destruct(&nra->unit_info); + delete_ivector(&nra->processed_variables); scope_holder_destruct(&nra->scope); - delete_int_hmap(&nra->lp_data.mcsat_to_lp_var_map); - delete_int_hmap(&nra->lp_data.lp_to_mcsat_var_map); - delete_int_hmap(&nra->evaluation_value_cache); delete_int_hmap(&nra->evaluation_timestamp_cache); @@ -207,53 +184,14 @@ void nra_plugin_destruct(plugin_t* plugin) { feasible_set_db_delete(nra->feasible_set_db); - lp_polynomial_context_detach(nra->lp_data.lp_ctx); - lp_variable_order_detach(nra->lp_data.lp_var_order); - lp_variable_db_detach(nra->lp_data.lp_var_db); - lp_assignment_delete(nra->lp_data.lp_assignment); - lp_interval_assignment_delete(nra->lp_data.lp_interval_assignment); + lp_data_destruct(&nra->lp_data); delete_rba_buffer(&nra->buffer); } -static +static inline bool nra_plugin_trail_variable_compare(void *data, variable_t t1, variable_t t2) { - const mcsat_trail_t* trail; - bool t1_has_value, t2_has_value; - uint32_t t1_index, t2_index; - - trail = data; - - // We compare variables based on the trail level, unassigned to the front, - // then assigned ones by decreasing level - - // Literals with no value - t1_has_value = trail_has_value(trail, t1); - t2_has_value = trail_has_value(trail, t2); - if (!t1_has_value && !t2_has_value) { - // Both have no value, just order by variable - return t1 < t2; - } - - // At least one has a value - if (!t1_has_value) { - // t1 < t2, goes to front - return true; - } - if (!t2_has_value) { - // t2 < t1, goes to front - return false; - } - - // Both literals have a value, sort by decreasing level - t1_index = trail_get_index(trail, t1); - t2_index = trail_get_index(trail, t2); - if (t1_index != t2_index) { - // t1 > t2 goes to front - return t1_index > t2_index; - } else { - return t1 < t2; - } + return trail_variable_compare((const mcsat_trail_t *)data, t1, t2); } static @@ -292,7 +230,7 @@ lp_feasibility_set_t* nra_plugin_get_feasible_set(nra_plugin_t* nra, variable_t } // Cached top variable - int_hmap_t* cache_top_var = &nra->feasible_set_cache_top_var[is_negated];\ + int_hmap_t* cache_top_var = &nra->feasible_set_cache_top_var[is_negated]; int_hmap_pair_t* find_top_var = int_hmap_get(cache_top_var, cstr_var); // Cached timestamp int_hmap_t* cache_timestamp = &nra->feasible_set_cache_timestamp[is_negated]; @@ -380,7 +318,7 @@ const mcsat_value_t* nra_plugin_constraint_evaluate(nra_plugin_t* nra, variable_ // do not evaluate (see ok below, but we can evaluate them in the cache) // Compute the evaluation - bool ok = poly_constraint_evaluate(cstr, nra, &cstr_value); + bool ok = poly_constraint_evaluate(cstr, &nra->lp_data, &cstr_value); (void) ok; assert(ok); (*nra->stats.evaluations) ++; @@ -417,9 +355,6 @@ void nra_plugin_process_fully_assigned_constraint(nra_plugin_t* nra, trail_token if (cstr_value) { bool ok = prop->add_at_level(prop, cstr_var, cstr_value, cstr_level); (void)ok; -// if (cstr_level < nra->ctx->trail->decision_level) { -// fprintf(stderr, "HERE"); -// } assert(ok); } @@ -572,8 +507,9 @@ void nra_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) // Register all the variables to libpoly (these are mcsat_variables) for (i = 0; i < t_variables_list->size; ++ i) { - if (!nra_plugin_variable_has_lp_variable(nra, t_variables_list->data[i])) { - nra_plugin_add_lp_variable(nra, t_variables_list->data[i]); + term_t tt = variable_db_get_term(nra->ctx->var_db, t_variables_list->data[i]); + if (!lp_data_variable_has_term(&nra->lp_data, tt)) { + lp_data_add_lp_variable(&nra->lp_data, terms, tt); } } @@ -607,7 +543,7 @@ void nra_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) // Check the current status of the constraint variable_t top_var = t_variables_list->data[0]; - constraint_unit_info_t unit_status = CONSTRAINT_UNKNOWN; + constraint_unit_state_t unit_status = CONSTRAINT_UNKNOWN; if (nra_plugin_has_assignment(nra, top_var)) { // All variables assigned, unit_status = CONSTRAINT_FULLY_ASSIGNED; @@ -622,10 +558,10 @@ void nra_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) } // Set the status of the constraint - nra_plugin_set_unit_info(nra, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status); + constraint_unit_info_set(&nra->unit_info, t_var, unit_status == CONSTRAINT_UNIT ? top_var : variable_null, unit_status); // Add the constraint to the database - poly_constraint_db_add(nra->constraint_db, t_var); + nra_poly_constraint_add(nra, t_var); // Propagate if fully assigned if (unit_status == CONSTRAINT_FULLY_ASSIGNED) { @@ -669,14 +605,15 @@ void nra_plugin_new_term_notify(plugin_t* plugin, term_t t, trail_token_t* prop) break; } default: { - - if (!nra_plugin_term_has_lp_variable(nra, t)) { - nra_plugin_add_lp_variable_from_term(nra, t); + // create variable for t if not existent + variable_db_get_variable(nra->ctx->var_db, t); + // register lp_variable for t if not existent + if (!lp_data_variable_has_term(&nra->lp_data, t)) { + lp_data_add_lp_variable(&nra->lp_data, terms, t); } if (nra->ctx->options->nra_bound) { if (nra->global_bound_term == NULL_TERM) { - term_table_t *terms = nra->ctx->terms; type_t reals = int_type(terms->types); nra->global_bound_term = new_uninterpreted_term(terms, reals); set_term_name(terms, nra->global_bound_term, clone_string("__mcsat_B")); @@ -774,22 +711,24 @@ void nra_plugin_infer_bounds_from_constraint(nra_plugin_t* nra, trail_token_t* p const lp_interval_t* x_interval = lp_interval_assignment_get_interval(m, x_lp); assert(x_interval != NULL); if (!lp_interval_is_full(x_interval)) { - variable_t x = nra_plugin_get_variable_from_lp_variable(nra, x_lp); + term_t x_term = lp_data_get_term_from_lp_variable(&nra->lp_data, x_lp); + variable_t x = variable_db_get_variable(nra->ctx->var_db, x_term); + assert(x != variable_null); lp_feasibility_set_t* x_feasible = lp_feasibility_set_new_from_interval(x_interval); bool consistent = feasible_set_db_update(nra->feasible_set_db, x, x_feasible, &constraint_var, 1); if (!consistent) { nra_plugin_report_conflict(nra, prop, constraint_var); } else if (variable_db_is_int(nra->ctx->var_db, x)) { - // BD: if x is an integer, we must check that there are integers in the interval. - lp_value_t v; - lp_value_construct_none(&v); - lp_feasibility_set_pick_value(feasible_set_db_get(nra->feasible_set_db, x), &v); - if (! lp_value_is_integer(&v)) { - nra->conflict_variable_int = x; - nra_plugin_report_conflict(nra, prop, x); - } - lp_value_destruct(&v); - } + // BD: if x is an integer, we must check that there are integers in the interval. + lp_value_t v; + lp_value_construct_none(&v); + lp_feasibility_set_pick_value(feasible_set_db_get(nra->feasible_set_db, x), &v); + if (!lp_value_is_integer(&v)) { + nra->conflict_variable_int = x; + nra_plugin_report_conflict(nra, prop, x); + } + lp_value_destruct(&v); + } } } @@ -801,8 +740,6 @@ void nra_plugin_infer_bounds_from_constraint(nra_plugin_t* nra, trail_token_t* p static void nra_plugin_process_unit_constraint(nra_plugin_t* nra, trail_token_t* prop, variable_t constraint_var) { - bool feasible; - if (ctx_trace_enabled(nra->ctx, "nra::propagate")) { ctx_trace_printf(nra->ctx, "nra: processing unit constraint :\n"); ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, constraint_var)); @@ -821,10 +758,11 @@ void nra_plugin_process_unit_constraint(nra_plugin_t* nra, trail_token_t* prop, } // Variable of the constraint - int_hmap_pair_t* x_find = int_hmap_find(&nra->constraint_unit_var, constraint_var); - variable_t x = x_find->val; + variable_t x = constraint_unit_info_get_unit_var(&nra->unit_info, constraint_var); + assert(x != variable_null); - lp_feasibility_set_t* constraint_feasible = nra_plugin_get_feasible_set(nra, constraint_var, x, !constraint_value); + bool is_negated = !constraint_value; + lp_feasibility_set_t* constraint_feasible = nra_plugin_get_feasible_set(nra, constraint_var, x, is_negated); if (ctx_trace_enabled(nra->ctx, "nra::propagate")) { ctx_trace_printf(nra->ctx, "nra: constraint_feasible = "); @@ -833,7 +771,7 @@ void nra_plugin_process_unit_constraint(nra_plugin_t* nra, trail_token_t* prop, } // Update the infeasible intervals - feasible = feasible_set_db_update(nra->feasible_set_db, x, constraint_feasible, &constraint_var, 1); + bool still_feasible = feasible_set_db_update(nra->feasible_set_db, x, constraint_feasible, &constraint_var, 1); if (ctx_trace_enabled(nra->ctx, "nra::propagate")) { ctx_trace_printf(nra->ctx, "nra: new feasible = "); @@ -842,7 +780,7 @@ void nra_plugin_process_unit_constraint(nra_plugin_t* nra, trail_token_t* prop, } // If the intervals are empty, we have a conflict - if (!feasible) { + if (!still_feasible) { nra_plugin_report_conflict(nra, prop, x); } else { const lp_feasibility_set_t *feasible_set = feasible_set_db_get(nra->feasible_set_db, x); @@ -938,26 +876,24 @@ void nra_plugin_process_variable_assignment(nra_plugin_t* nra, trail_token_t* pr nra->last_decided_and_unprocessed = variable_null; } + term_t t = variable_db_get_term(nra->ctx->var_db, var); if (ctx_trace_enabled(nra->ctx, "nra::propagate")) { ctx_trace_printf(nra->ctx, "nra: processing var assignment of :\n"); - ctx_trace_term(nra->ctx, variable_db_get_term(nra->ctx->var_db, var)); + ctx_trace_term(nra->ctx, t); } // If it's constant, just skip it - if (!nra_plugin_variable_has_lp_variable(nra, var)) { + if (!lp_data_variable_has_term(&nra->lp_data, t)) { return; } // Add to the lp model and context - lp_variable_t lp_var = nra_plugin_get_lp_variable(nra, var); assert(trail_get_value(trail, var)->type == VALUE_LIBPOLY); - lp_assignment_set_value(nra->lp_data.lp_assignment, lp_var, &trail_get_value(trail, var)->lp_value); - lp_variable_order_push(nra->lp_data.lp_var_order, lp_var); - nra->lp_data.lp_var_order_size ++; + lp_data_add_to_model_and_context(&nra->lp_data, lp_data_get_lp_variable_from_term(&nra->lp_data, t), &trail_get_value(trail, var)->lp_value); if (ctx_trace_enabled(nra->ctx, "nra::propagate")) { ctx_trace_printf(nra->ctx, "nra: var order :"); - lp_variable_order_print(nra->lp_data.lp_var_order, nra->lp_data.lp_var_db, ctx_trace_out(nra->ctx)); + lp_data_variable_order_print(&nra->lp_data, ctx_trace_out(nra->ctx)); ctx_trace_printf(nra->ctx, "\n"); } @@ -1019,14 +955,14 @@ void nra_plugin_process_variable_assignment(nra_plugin_t* nra, trail_token_t* pr } if (!nra_plugin_has_assignment(nra, *var_list)) { // We're unit - nra_plugin_set_unit_info(nra, constraint_var, *var_list, CONSTRAINT_UNIT); + constraint_unit_info_set(&nra->unit_info, constraint_var, *var_list, CONSTRAINT_UNIT); // Process the constraint if (trail_is_consistent(trail)) { nra_plugin_process_unit_constraint(nra, prop, constraint_var); } } else { // Fully assigned - nra_plugin_set_unit_info(nra, constraint_var, variable_null, CONSTRAINT_FULLY_ASSIGNED); + constraint_unit_info_set(&nra->unit_info, constraint_var, variable_null, CONSTRAINT_FULLY_ASSIGNED); // Evaluate the constraint and propagate (if not assigned already) if (trail_is_consistent(trail) && !trail_has_value(trail, constraint_var)) { nra_plugin_process_fully_assigned_constraint(nra, prop, constraint_var); @@ -1041,11 +977,16 @@ void nra_plugin_process_variable_assignment(nra_plugin_t* nra, trail_token_t* pr remove_iterator_destruct(&it); } - +#ifndef NDEBUG static -void nra_plugin_check_assignment(nra_plugin_t* nra) { +bool nra_plugin_check_assignment(nra_plugin_t* nra) { + if (!ctx_trace_enabled(nra->ctx, "nra::check_assignment")) { + return true; + } const mcsat_trail_t* trail = nra->ctx->trail; + const variable_db_t* var_db = nra->ctx->var_db; + const lp_data_t* lp_data = &nra->lp_data; // Go through the trail and check if all assigned are in lp_assignment uint32_t i; @@ -1056,26 +997,34 @@ void nra_plugin_check_assignment(nra_plugin_t* nra) { } const mcsat_value_t* value = trail_get_value(trail, x); if (value->type == VALUE_LIBPOLY && nra_plugin_has_assignment(nra, x)) { - lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x); - const lp_value_t* value_lp = lp_assignment_get_value(nra->lp_data.lp_assignment, x_lp); - int cmp = lp_value_cmp(&value->lp_value, value_lp); - (void)cmp; - assert(cmp == 0); + term_t t = variable_db_get_term(var_db, x); + lp_variable_t x_lp = lp_data_get_lp_variable_from_term(lp_data, t); + const lp_value_t* value_lp = lp_assignment_get_value(lp_data->lp_assignment, x_lp); + if (lp_value_cmp(&value->lp_value, value_lp) != 0) { + assert(false); + return false; + } } } // Go through lp_assignment and check if they are assigned in trail - const lp_variable_list_t* order = lp_variable_order_get_list(nra->lp_data.lp_var_order); + const lp_variable_list_t* order = lp_variable_order_get_list(lp_data->lp_var_order); for (i = 0; i < order->list_size; ++ i) { lp_variable_t x_lp = order->list[i]; - variable_t x = nra_plugin_get_variable_from_lp_variable(nra, x_lp); + term_t x_term = lp_data_get_term_from_lp_variable(lp_data, x_lp); + variable_t x = variable_db_get_variable_if_exists(var_db, x_term); + assert(x != variable_null); const mcsat_value_t* value = trail_get_value(trail, x); - const lp_value_t* value_lp = lp_assignment_get_value(nra->lp_data.lp_assignment, x_lp); - int cmp = lp_value_cmp(&value->lp_value, value_lp); - (void)cmp; - assert(cmp == 0); + const lp_value_t* value_lp = lp_assignment_get_value(lp_data->lp_assignment, x_lp); + if (lp_value_cmp(&value->lp_value, value_lp) != 0) { + assert(false); + return false; + } } + + return true; } +#endif /** @@ -1088,9 +1037,7 @@ void nra_plugin_propagate(plugin_t* plugin, trail_token_t* prop) { variable_t var; - if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) { - nra_plugin_check_assignment(nra); - } + assert(nra_plugin_check_assignment(nra)); // Context const mcsat_trail_t* trail = nra->ctx->trail; @@ -1110,8 +1057,8 @@ void nra_plugin_propagate(plugin_t* plugin, trail_token_t* prop) { // Real variables, detect if the constraint is unit nra_plugin_process_variable_assignment(nra, prop, var); } - if (nra_plugin_has_unit_info(nra, var)) { - constraint_unit_info_t info = nra_plugin_get_unit_info(nra, var); + if (constraint_unit_info_has(&nra->unit_info, var)) { + constraint_unit_state_t info = constraint_unit_info_get(&nra->unit_info, var); switch (info) { case CONSTRAINT_UNIT: // Process any unit constraints @@ -1132,9 +1079,7 @@ void nra_plugin_propagate(plugin_t* plugin, trail_token_t* prop) { nra_plugin_report_int_conflict(nra, prop, nra->conflict_variable_int); } - if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) { - nra_plugin_check_assignment(nra); - } + assert(nra_plugin_check_assignment(nra)); } static @@ -1144,7 +1089,7 @@ void nra_plugin_decide(plugin_t* plugin, variable_t x, trail_token_t* decide_tok assert(variable_db_is_real(nra->ctx->var_db, x) || variable_db_is_int(nra->ctx->var_db, x)); // Get the feasibility set - lp_feasibility_set_t* feasible = feasible_set_db_get(nra->feasible_set_db, x); + const lp_feasibility_set_t* feasible = feasible_set_db_get(nra->feasible_set_db, x); if (ctx_trace_enabled(nra->ctx, "nra::decide")) { ctx_trace_printf(nra->ctx, "decide on "); @@ -1280,7 +1225,8 @@ void nra_plugin_check_conflict(nra_plugin_t* nra, ivector_t* core) { if (x == nra->last_decided_and_unprocessed) { continue; } - lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x); + term_t t = variable_db_get_term(nra->ctx->var_db, x); + lp_variable_t x_lp = lp_data_get_lp_variable_from_term(&nra->lp_data, t); // Ignore unassigned too if (!trail_has_value(trail, x)) { assert(free_var == lp_variable_null); @@ -1359,7 +1305,7 @@ void nra_plugin_get_real_conflict(nra_plugin_t* nra, const int_mset_t* pos, cons ivector_t core, lemma_reasons; init_ivector(&core, 0); init_ivector(&lemma_reasons, 0); - feasible_set_db_get_conflict_reasons(nra->feasible_set_db, nra, x, NULL, &core, &lemma_reasons); + feasible_set_db_get_conflict_reasons(nra->feasible_set_db, x, NULL, &core, &lemma_reasons); if (ctx_trace_enabled(nra->ctx, "nra::conflict")) { ctx_trace_printf(nra->ctx, "nra_plugin_get_conflict(): core:\n"); @@ -1404,7 +1350,7 @@ bool nra_plugin_speculate_constraint(nra_plugin_t* nra, int_mset_t* pos, int_mse term_t constraint_atom = unsigned_term(constraint); bool negated = constraint != constraint_atom; variable_t constraint_var = variable_db_get_variable(nra->ctx->var_db, constraint_atom); - poly_constraint_db_add(nra->constraint_db, constraint_var); + nra_poly_constraint_add(nra, constraint_var); // Check if the constraint is in Boolean conflict if (trail_has_value(nra->ctx->trail, constraint_var)) { @@ -1456,6 +1402,15 @@ bool nra_plugin_speculate_constraint(nra_plugin_t* nra, int_mset_t* pos, int_mse return feasible; } +/** + * Construct a yices rational from lp_integer. + */ +static inline +void rational_construct_from_lp_integer(rational_t* q, const lp_integer_t* lp_z) { + q_init(q); + q_set_mpz(q, lp_z); +} + static void nra_plugin_get_int_conflict(nra_plugin_t* nra, int_mset_t* pos, int_mset_t* neg, variable_t x, ivector_t* conflict) { @@ -1565,7 +1520,7 @@ void nra_plugin_get_int_conflict(nra_plugin_t* nra, int_mset_t* pos, int_mset_t* feasible_set_db_pop(nra->feasible_set_db); // Remove resolved literals - uint32_t i = 0, to_keep; + uint32_t i, to_keep; for (i = 0, to_keep = 0; i < conflict->size; ++ i) { if (!int_mset_contains(&to_resolve, conflict->data[i])) { conflict->data[to_keep ++] = conflict->data[i]; @@ -1575,10 +1530,9 @@ void nra_plugin_get_int_conflict(nra_plugin_t* nra, int_mset_t* pos, int_mset_t* if (ctx_trace_enabled(nra->ctx, "nia")) { ctx_trace_printf(nra->ctx, "int_conflict: final conflict:\n"); - uint32_t i; - for (i = 0; i < conflict->size; ++ i) { + for (uint32_t j = 0; j < conflict->size; ++ j) { ctx_trace_printf(nra->ctx, " "); - ctx_trace_term(nra->ctx, conflict->data[i]); + ctx_trace_term(nra->ctx, conflict->data[j]); } } @@ -1603,7 +1557,7 @@ void nra_plugin_get_assumption_conflict(nra_plugin_t* nra, variable_t x, ivector ivector_t core, lemma_reasons; init_ivector(&core, 0); init_ivector(&lemma_reasons, 0); - feasible_set_db_get_conflict_reasons(nra->feasible_set_db, nra, x, x_value, &core, &lemma_reasons); + feasible_set_db_get_conflict_reasons(nra->feasible_set_db, x, x_value, &core, &lemma_reasons); if (ctx_trace_enabled(nra->ctx, "nra::conflict")) { ctx_trace_printf(nra->ctx, "nra_plugin_get_assumption_conflict(): core:\n"); @@ -1626,17 +1580,17 @@ void nra_plugin_get_assumption_conflict(nra_plugin_t* nra, variable_t x, ivector assert(core.size == 0); // We don't know the actual lemma terms, just the variables // We do know that if we evaluate with the conflict variable the terms should eval to false - // 1. Setup the model with the conflict variable + // 1. Set up the model with the conflict variable variable_t var = nra->conflict_variable_assumption; - lp_variable_t lp_var = nra_plugin_get_lp_variable(nra, var); - lp_assignment_set_value(nra->lp_data.lp_assignment, lp_var, &nra->conflict_variable_value); - lp_variable_order_push(nra->lp_data.lp_var_order, lp_var); + lp_data_variable_order_push(&nra->lp_data); + term_t t = variable_db_get_term(nra->ctx->var_db, var); + lp_data_add_to_model_and_context(&nra->lp_data, lp_data_get_lp_variable_from_term(&nra->lp_data, t), &nra->conflict_variable_value); for (i = 0; i < lemma_reasons.size; ++ i) { // 2. Evaluate the constraint and figure out how it evaluates to false variable_t constraint_var = lemma_reasons.data[i]; const poly_constraint_t* constraint = poly_constraint_db_get(nra->constraint_db, constraint_var); bool constraint_value = false; - bool ok = poly_constraint_evaluate(constraint, nra, &constraint_value); + bool ok = poly_constraint_evaluate(constraint, &nra->lp_data, &constraint_value); (void) ok; assert(ok); term_t constraint_term = variable_db_get_term(nra->ctx->var_db, constraint_var); @@ -1647,8 +1601,7 @@ void nra_plugin_get_assumption_conflict(nra_plugin_t* nra, variable_t x, ivector } } // 3. Pop the model - lp_variable_order_pop(nra->lp_data.lp_var_order); - lp_assignment_set_value(nra->lp_data.lp_assignment, lp_var, 0); + lp_data_variable_order_pop(&nra->lp_data); } else { assert(core.size == 1); @@ -1691,11 +1644,12 @@ void nra_plugin_get_assumption_conflict(nra_plugin_t* nra, variable_t x, ivector } else { // Case 3: single constraint from interval inference // Get the reason of the inference - lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x); + term_t t = variable_db_get_term(nra->ctx->var_db, x); + lp_variable_t x_lp = lp_data_get_lp_variable_from_term(&nra->lp_data, t); lp_polynomial_t* p_reason_lp = lp_polynomial_constraint_explain_infer_bounds(constraint_p, constraint_sgn_condition, !constraint_value, x_lp); assert(p_reason_lp != NULL); - term_t p_reason = lp_polynomial_to_yices_term_nra(p_reason_lp, nra); + term_t p_reason = lp_polynomial_to_yices_term_nra(nra, p_reason_lp); // Get the sign of the polynomial assert(trail_has_value(nra->ctx->trail, x)); @@ -1817,8 +1771,7 @@ bool nra_plugin_explain_evaluation(plugin_t* plugin, term_t t, int_mset_t* vars, // Check if the variables are assigned ivector_t* var_list = int_mset_get_list(vars); - size_t i = 0; - for (i = 0; i < var_list->size; ++ i) { + for (size_t i = 0; i < var_list->size; ++ i) { if (!trail_has_value(nra->ctx->trail, var_list->data[i])) { result = false; } @@ -1834,9 +1787,9 @@ void nra_plugin_push(plugin_t* plugin) { scope_holder_push(&nra->scope, &nra->trail_i, &nra->processed_variables_size, - &nra->lp_data.lp_var_order_size, NULL); + lp_data_variable_order_push(&nra->lp_data); feasible_set_db_push(nra->feasible_set_db); } @@ -1854,7 +1807,6 @@ void nra_plugin_pop(plugin_t* plugin) { scope_holder_pop(&nra->scope, &nra->trail_i, &nra->processed_variables_size, - &nra->lp_data.lp_var_order_size, NULL); // Undo the processed variables @@ -1868,39 +1820,16 @@ void nra_plugin_pop(plugin_t* plugin) { remove_iterator_construct(&it, &nra->wlm, x); while (!remove_iterator_done(&it)) { variable_t constraint_var = remove_iterator_get_constraint(&it); - constraint_unit_info_t unit_info = nra_plugin_get_unit_info(nra, constraint_var); - switch (unit_info) { - case CONSTRAINT_UNKNOWN: - // Nothing to do - break; - case CONSTRAINT_UNIT: - // If it was unit it becomes not unit - nra_plugin_set_unit_info(nra, constraint_var, variable_null, CONSTRAINT_UNKNOWN); - break; - case CONSTRAINT_FULLY_ASSIGNED: - // It is unit now - nra_plugin_set_unit_info(nra, constraint_var, x, CONSTRAINT_UNIT); - break; - } + constraint_unit_info_demote(&nra->unit_info, constraint_var, x); remove_iterator_next_and_keep(&it); } remove_iterator_destruct(&it); } // Pop the variable order and the lp model - lp_variable_order_t* order = nra->lp_data.lp_var_order; - lp_assignment_t* assignment = nra->lp_data.lp_assignment; - while (lp_variable_order_size(order) > nra->lp_data.lp_var_order_size) { - lp_variable_t lp_var = lp_variable_order_top(order); - lp_variable_order_pop(order); - lp_assignment_set_value(assignment, lp_var, 0); - variable_t var = nra_plugin_get_variable_from_lp_variable(nra, lp_var); - (void)var; - } + lp_data_variable_order_pop(&nra->lp_data); - if (ctx_trace_enabled(nra->ctx, "nra::check_assignment")) { - nra_plugin_check_assignment(nra); - } + assert(nra_plugin_check_assignment(nra)); // Pop the feasibility feasible_set_db_pop(nra->feasible_set_db); @@ -1933,13 +1862,10 @@ void nra_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) { // the watchlists and the unit information. // The constraint database - poly_constraint_db_gc_sweep(nra->constraint_db, gc_vars); + poly_constraint_db_gc_sweep(nra->constraint_db, nra->ctx, gc_vars); - // The lp_data mappings: - // - lpdata.lp_to_mcsat_var_map (values) - // - lpdata.mcsat_to_lp_var_map (keys) - gc_info_sweep_int_hmap_values(gc_vars, &nra->lp_data.lp_to_mcsat_var_map); - gc_info_sweep_int_hmap_keys(gc_vars, &nra->lp_data.mcsat_to_lp_var_map); + // The lp_data mappings + lp_data_gc_sweep(&nra->lp_data, gc_vars); // Evaluation cache gc_info_sweep_int_hmap_keys(gc_vars, &nra->evaluation_value_cache); @@ -1956,8 +1882,7 @@ void nra_plugin_gc_sweep(plugin_t* plugin, const gc_info_t* gc_vars) { gc_info_sweep_ptr_hmap_keys(gc_vars, &nra->feasible_set_cache[0], (ptr_hmap_ptr_delete) &lp_feasibility_set_delete); // Unit information (constraint_unit_info, constraint_unit_var) - gc_info_sweep_int_hmap_keys(gc_vars, &nra->constraint_unit_info); - gc_info_sweep_int_hmap_keys(gc_vars, &nra->constraint_unit_var); + constraint_unit_info_gc_sweep(&nra->unit_info, gc_vars); // Watch list manager watch_list_manager_gc_sweep_lists(&nra->wlm, gc_vars); @@ -2005,12 +1930,12 @@ void nra_plugin_new_lemma_notify(plugin_t* plugin, ivector_t* lemma, trail_token term_t atom = unsigned_term(literal); variable_t atom_var = variable_db_get_variable_if_exists(nra->ctx->var_db, atom); assert(atom_var != variable_null); - if (nra_plugin_get_unit_info(nra, atom_var) != CONSTRAINT_UNIT) { + if (constraint_unit_info_get(&nra->unit_info, atom_var) != CONSTRAINT_UNIT) { // Not unit unit = false; } else { // Unit, check if same variable - variable_t atom_unit_var = nra_plugin_get_unit_var(nra, atom_var); + variable_t atom_unit_var = constraint_unit_info_get_unit_var(&nra->unit_info, atom_var); if (unit_var == variable_null) { unit_var = atom_unit_var; } else if (unit_var != atom_unit_var) { @@ -2126,7 +2051,7 @@ void nra_plugin_set_exception_handler(plugin_t* plugin, jmp_buf* handler) { static void nra_plugin_decide_assignment(plugin_t* plugin, variable_t x, const mcsat_value_t* value, trail_token_t* decide) { nra_plugin_t* nra = (nra_plugin_t*) plugin; - // If we get a rational, conver to lp_value_t + // If we get a rational, convert to lp_value_t mcsat_value_t tmp; const mcsat_value_t* lp_value = ensure_lp_value(value, &tmp); // Get the feasibility set @@ -2185,7 +2110,7 @@ void nra_plugin_learn(plugin_t* plugin, trail_token_t* prop) { // Approximate the value const mcsat_value_t* constraint_value = NULL; - constraint_value = poly_constraint_db_approximate(nra->constraint_db, constraint_var, nra); + constraint_value = nra_poly_constraint_db_approximate(nra, constraint_var); if (ctx_trace_enabled(nra->ctx, "mcsat::nra::learn")) { ctx_trace_printf(nra->ctx, "nra_plugin_learn(): value = "); if (constraint_value != NULL) { @@ -2223,7 +2148,7 @@ void nra_plugin_learn(plugin_t* plugin, trail_token_t* prop) { bool nra_plugin_simplify_conflict_literal(plugin_t* plugin, term_t lit, ivector_t* output) { nra_plugin_t* nra = (nra_plugin_t*) plugin; - uint32_t start = output->size;; + uint32_t start = output->size; // We only simplify root constraints term_t lit_pos = unsigned_term(lit); diff --git a/src/mcsat/nra/nra_plugin_explain.c b/src/mcsat/nra/nra_plugin_explain.c index d9d1b21da..1142bc611 100644 --- a/src/mcsat/nra/nra_plugin_explain.c +++ b/src/mcsat/nra/nra_plugin_explain.c @@ -18,8 +18,8 @@ #include "nra_plugin_explain.h" #include "nra_plugin_internal.h" -#include "poly_constraint.h" -#include "libpoly_utils.h" +#include "nra_libpoly.h" +#include "mcsat/utils/lp_utils.h" #include "utils/int_hash_map.h" #include "utils/pointer_vectors.h" @@ -38,7 +38,6 @@ #include #include #include -#include #include #include @@ -92,18 +91,15 @@ struct lp_projection_map_struct { /** Map from indices to the projection sets where it is the top variable */ int_hmap_t var_to_index_map; + /** lp_data */ + const lp_data_t* lp_data; + /** List of all variables added */ lp_variable_list_t all_vars; /** List of all yet unprojected variables */ lp_variable_list_t unprojected_vars; - /** The polynomial context */ - const lp_polynomial_context_t* ctx; - - /** The assignment */ - const lp_assignment_t* m; - /** Whether to use root constraints for cell description */ bool use_root_constraints_for_cells; @@ -113,35 +109,29 @@ struct lp_projection_map_struct { /** Plugin context (if available) */ plugin_context_t* plugin_ctx; - /** NRA (if available) */ - nra_plugin_t* nra; - - /** Tmp buffer (if available) */ - rba_buffer_t buffer; - - /** Map from lp variables to terms (if available) */ - int_hmap_t* lp_to_term_map; + /** Tmp buffer */ + rba_buffer_t* buffer; + bool external_buffer; /// Projection options /** Whether to use model-based GCD */ bool use_mgcd; - /** WHether to use the default NLSAT projection */ + /** Whether to use the default NLSAT projection */ bool use_nlsat; - }; typedef struct lp_projection_map_struct lp_projection_map_t; #define LP_PROJECTION_MAP_DEFAULT_SIZE 10 +static void lp_projection_map_construct(lp_projection_map_t* map, - const lp_polynomial_context_t* lp_ctx, - const lp_assignment_t* lp_asignment, term_manager_t* tm, - nra_plugin_t* nra, /** Can be NULL */ - int_hmap_t* lp_to_term_map, /** Can be NULL */ + lp_data_t* lp_data, + rba_buffer_t* buffer, /** Can be NULL */ + plugin_context_t* ctx, /** Can be NULL */ bool use_mgcd, bool use_nlsat ) @@ -149,20 +139,20 @@ void lp_projection_map_construct(lp_projection_map_t* map, map->data_size = 0; map->data_capacity = LP_PROJECTION_MAP_DEFAULT_SIZE; map->data = safe_malloc(sizeof(lp_polynomial_hash_set_t)*map->data_capacity); - map->ctx = lp_ctx; - map->m = lp_asignment; + map->lp_data = lp_data; map->use_root_constraints_for_cells = true; map->tm = tm; - map->nra = nra; - map->lp_to_term_map = lp_to_term_map; - map->plugin_ctx = (nra == NULL ? NULL : nra->ctx); + map->plugin_ctx = ctx; map->use_mgcd = use_mgcd; map->use_nlsat = use_nlsat; - if (nra == NULL) { - init_rba_buffer(&map->buffer, tm->pprods); + map->external_buffer = (buffer != NULL); + if (map->external_buffer) { + map->buffer = buffer; + } else { + map->buffer = safe_malloc(sizeof(rba_buffer_t)); + init_rba_buffer(map->buffer, tm->pprods); } - lp_polynomial_hash_set_construct(&map->all_polynomials); init_int_hmap(&map->var_to_index_map, 0); lp_variable_list_construct(&map->all_vars); @@ -171,9 +161,7 @@ void lp_projection_map_construct(lp_projection_map_t* map, void lp_projection_map_construct_from_nra(lp_projection_map_t* map, nra_plugin_t* nra) { lp_projection_map_construct(map, - nra->lp_data.lp_ctx, nra->lp_data.lp_assignment, - nra->ctx->tm, nra, - NULL, + nra->ctx->tm, &nra->lp_data, &nra->buffer, nra->ctx, nra->ctx->options->nra_mgcd, nra->ctx->options->nra_nlsat); } @@ -187,30 +175,15 @@ void lp_projection_map_destruct(lp_projection_map_t* map) { delete_int_hmap(&map->var_to_index_map); lp_variable_list_destruct(&map->all_vars); lp_variable_list_destruct(&map->unprojected_vars); - if (map->nra == NULL) { - delete_rba_buffer(&map->buffer); + if (!map->external_buffer) { + delete_rba_buffer(map->buffer); + free(map->buffer); } } static inline term_t lp_projection_map_polynomial_to_term(lp_projection_map_t* map, const lp_polynomial_t* p) { - if (map->nra) { - return lp_polynomial_to_yices_term_nra(p, map->nra); - } else { - return lp_polynomial_to_yices_term(p, map->tm->terms, &map->buffer, map->lp_to_term_map); - } -} - -static inline -term_t lp_projection_map_var_to_term(lp_projection_map_t* map, lp_variable_t x_lp) { - if (map->nra) { - variable_t x_var = nra_plugin_get_variable_from_lp_variable(map->nra, x_lp); - term_t x_term = variable_db_get_term(map->nra->ctx->var_db, x_var); - return x_term; - } else { - assert(false); - return NULL_TERM; - } + return lp_polynomial_to_yices_arith_term(map->lp_data, p, map->tm->terms, map->buffer); } lp_polynomial_hash_set_t* lp_projection_map_get_set_of(lp_projection_map_t* map, lp_variable_t var) { @@ -269,7 +242,7 @@ void lp_projection_map_add(lp_projection_map_t* map, const lp_polynomial_t* p) { // Reduce the polynomials and add all the vanishing coefficients lp_variable_t x = lp_polynomial_top_variable(p); - lp_polynomial_t* p_r = lp_polynomial_new(map->ctx); + lp_polynomial_t* p_r = lp_data_new_polynomial(map->lp_data); lp_projection_map_reduce(map, x, p, p_r); // Don't add constants or things already there @@ -298,10 +271,10 @@ void lp_projection_map_add(lp_projection_map_t* map, const lp_polynomial_t* p) { lp_polynomial_t* p_r_zero = NULL; // If x is assigned, check if any of the factors evaluates to 0 - if (lp_assignment_get_value(map->m, x)->type != LP_VALUE_NONE) { + if (lp_assignment_get_value(map->lp_data->lp_assignment, x)->type != LP_VALUE_NONE) { for (i = 0; i < p_r_factors_size; ++ i) { // Get the sign of the polynomials - int sgn = lp_polynomial_sgn(p_r_factors[i], map->m); + int sgn = lp_polynomial_sgn(p_r_factors[i], map->lp_data->lp_assignment); if (sgn == 0) { if (p_r_zero == NULL) { p_r_zero = p_r_factors[i]; @@ -341,24 +314,15 @@ void lp_projection_map_add(lp_projection_map_t* map, const lp_polynomial_t* p) { lp_polynomial_delete(p_r); } -static -const lp_variable_order_t* lp_projection_map_variable_cmp_order = 0; - -int lp_projection_map_variable_cmp(const void* x, const void* y) { - lp_variable_t x_var = *(lp_variable_t*)x; - lp_variable_t y_var = *(lp_variable_t*)y; - return lp_variable_order_cmp(lp_projection_map_variable_cmp_order, x_var, y_var); -} - void lp_projection_map_order_vars(lp_projection_map_t* map) { - lp_variable_list_order(&map->all_vars, map->ctx->var_order); - lp_variable_list_order(&map->unprojected_vars, map->ctx->var_order); + lp_variable_list_order(&map->all_vars, map->lp_data->lp_var_order); + lp_variable_list_order(&map->unprojected_vars, map->lp_data->lp_var_order); } lp_variable_t lp_projection_map_pop_top_unprojected_var(lp_projection_map_t* map) { if (lp_variable_list_size(&map->unprojected_vars) > 0) { // Sort all unprojected variable based on order - lp_variable_list_order(&map->unprojected_vars, map->ctx->var_order); + lp_variable_list_order(&map->unprojected_vars, map->lp_data->lp_var_order); lp_variable_t top = lp_variable_list_top(&map->unprojected_vars); lp_variable_list_pop(&map->unprojected_vars); return top; @@ -372,7 +336,7 @@ int lp_projection_map_print(const lp_projection_map_t* map, FILE* out) { size_t i = 0; for (i = 0; i < map->all_vars.list_size; ++ i) { lp_variable_t x = map->all_vars.list[i]; - ret += fprintf(out, "%s : ", lp_variable_db_get_name(map->ctx->var_db, x)); + ret += fprintf(out, "%s : ", lp_variable_db_get_name(map->lp_data->lp_var_db, x)); int_hmap_pair_t* find = int_hmap_find((int_hmap_t*) &map->var_to_index_map, x); assert(find != NULL); const lp_polynomial_hash_set_t* x_set = map->data + find->val; @@ -415,10 +379,9 @@ void lp_projection_map_describe_cell_part(lp_projection_map_t* map, lp_variable_ case ROOT_ATOM_LEQ: root_atom = mk_arith_term_leq0(tm, p_term); break; - case ROOT_ATOM_EQ: { + case ROOT_ATOM_EQ: root_atom = mk_arith_term_eq0(tm, p_term); break; - } case ROOT_ATOM_NEQ: root_atom = mk_arith_term_neq0(tm, p_term); break; @@ -434,15 +397,15 @@ void lp_projection_map_describe_cell_part(lp_projection_map_t* map, lp_variable_ } else { // Regular root atom if (map->use_root_constraints_for_cells) { - term_t x_term = lp_projection_map_var_to_term(map, x); + term_t x_term = lp_data_get_term_from_lp_variable(map->lp_data, x); term_t p_term = lp_projection_map_polynomial_to_term(map, p); root_atom = mk_arith_root_atom(tm, root_index, x_term, p_term, r); } else { // Add all the derivatives according to the sign in the current model, disregard the root type lp_polynomial_t* current = lp_polynomial_new_copy(p); - lp_polynomial_t* current_d = lp_polynomial_new(map->ctx); + lp_polynomial_t* current_d = lp_data_new_polynomial(map->lp_data); while (!lp_polynomial_is_constant(current)) { - int current_sgn = lp_polynomial_sgn(current, map->m); + int current_sgn = lp_polynomial_sgn(current, map->lp_data->lp_assignment); term_t current_term = lp_projection_map_polynomial_to_term(map, current); term_t current_literal; if (current_sgn < 0) { @@ -497,9 +460,12 @@ int polynomial_cmp(const void* p1_void, const void* p2_void) { /** * Simplify 0-polynomials with the GCD. */ -void gcd_simplify_zero(const lp_polynomial_context_t* ctx, lp_polynomial_t** polys, size_t* size, const lp_assignment_t* m) { +static +void gcd_simplify_zero(const lp_data_t *lp_data, lp_polynomial_t** polys, size_t* size) { + const lp_assignment_t* m = lp_data->lp_assignment; + // Temp for GCD computation - lp_polynomial_t* gcd = lp_polynomial_new(ctx); + lp_polynomial_t* gcd = lp_data_new_polynomial(lp_data); uint32_t i, j, to_keep = 0; for (i = 0; i < *size; ++ i) { @@ -561,7 +527,7 @@ void lp_projection_map_construct_cell(lp_projection_map_t* map, lp_variable_t x, // Simplify the polynomials that evaluate to zero based on gcd: // * If two polynomials evaluate to 0, and their gcd also evaluates to 0, // we can remove both of them and replace them by the gcd. - gcd_simplify_zero(map->ctx, x_set->data, &x_set->size, map->m); + gcd_simplify_zero(map->lp_data, x_set->data, &x_set->size); // Sort the polynomials by degree qsort(x_set->data, x_set->size, sizeof(lp_polynomial_t*), polynomial_cmp); @@ -603,7 +569,7 @@ void lp_projection_map_construct_cell(lp_projection_map_t* map, lp_variable_t x, assert(p_deg > 0); lp_value_t* p_roots = safe_malloc(sizeof(lp_value_t)*p_deg); size_t p_roots_size = 0; - lp_polynomial_roots_isolate(p, map->m, p_roots, &p_roots_size); + lp_polynomial_roots_isolate(p, map->lp_data->lp_assignment, p_roots, &p_roots_size); if (ctx_trace_enabled(ctx, "nra::explain::projection")) { ctx_trace_printf(ctx, "roots = "); @@ -618,7 +584,7 @@ void lp_projection_map_construct_cell(lp_projection_map_t* map, lp_variable_t x, } // Binary search for the current value x_v - const lp_value_t* x_v = lp_assignment_get_value(map->m, x); + const lp_value_t* x_v = lp_assignment_get_value(map->lp_data->lp_assignment, x); if (ctx_trace_enabled(ctx, "nra::explain::projection")) { ctx_trace_printf(ctx, "x_v = "); lp_value_print(x_v, ctx_trace_out(ctx)); @@ -760,7 +726,7 @@ void lp_projection_map_add_psc(lp_projection_map_t* map, lp_polynomial_t*** poly size_t q_deg = lp_polynomial_degree(q); uint32_t psc_size = p_deg > q_deg ? q_deg + 1 : p_deg + 1; - polynomial_buffer_ensure_size(polynomial_buffer, polynomial_buffer_size, psc_size, map->ctx); + polynomial_buffer_ensure_size(polynomial_buffer, polynomial_buffer_size, psc_size, map->lp_data->lp_ctx); // Get the psc lp_polynomial_psc(*polynomial_buffer, p, q); @@ -770,7 +736,7 @@ void lp_projection_map_add_psc(lp_projection_map_t* map, lp_polynomial_t*** poly // Add it lp_projection_map_add(map, (*polynomial_buffer)[psc_i]); // If it doesn't vanish we're done - if (lp_polynomial_sgn((*polynomial_buffer)[psc_i], map->m)) { + if (lp_polynomial_sgn((*polynomial_buffer)[psc_i], map->lp_data->lp_assignment)) { break; } } @@ -791,14 +757,14 @@ void lp_projection_map_add_mgcd(lp_projection_map_t* map, lp_variable_t x, const lp_variable_list_construct(&vars); lp_polynomial_get_variables(p, &vars); lp_polynomial_get_variables(q, &vars); - lp_variable_list_order(&vars, map->ctx->var_order); + lp_variable_list_order(&vars, map->lp_data->lp_var_order); uint32_t i; for (i = 0; i < vars.list_size; ++ i) { lp_variable_t var = vars.list[i]; - const lp_value_t* v = lp_assignment_get_value(map->m, var); + const lp_value_t* v = lp_assignment_get_value(map->lp_data->lp_assignment, var); if (v->type != LP_VALUE_NONE) { - ctx_trace_printf(map->plugin_ctx, "%s -> ", lp_variable_db_get_name(map->ctx->var_db, var)); + ctx_trace_printf(map->plugin_ctx, "%s -> ", lp_variable_db_get_name(map->lp_data->lp_var_db, var)); lp_value_print(v, ctx_trace_out(map->plugin_ctx)); ctx_trace_printf(map->plugin_ctx, "\n"); } @@ -807,7 +773,7 @@ void lp_projection_map_add_mgcd(lp_projection_map_t* map, lp_variable_t x, const lp_variable_list_destruct(&vars); } - lp_polynomial_vector_t* assumptions = lp_polynomial_mgcd(p, q, map->m); + lp_polynomial_vector_t* assumptions = lp_polynomial_mgcd(p, q, map->lp_data->lp_assignment); if (ctx_trace_enabled(map->plugin_ctx, "nra::explain::mgcd")) { ctx_trace_printf(map->plugin_ctx, "mgcd done: \n"); @@ -831,11 +797,11 @@ void lp_projection_map_reduce(lp_projection_map_t* map, lp_variable_t x, const l assert(p != p_r); assert(lp_polynomial_top_variable(p) == x); - lp_polynomial_t* p_coeff = lp_polynomial_new(map->ctx); + lp_polynomial_t* p_coeff = lp_data_new_polynomial(map->lp_data); uint32_t p_deg = lp_polynomial_degree(p); - lp_polynomial_reductum_m(p_r, p, map->m); + lp_polynomial_reductum_m(p_r, p, map->lp_data->lp_assignment); uint32_t p_r_deg = lp_polynomial_top_variable(p_r) == x ? lp_polynomial_degree(p_r) : 0; // Add the vanishing initial coefficients (this includes the top reduced, hence the content) @@ -861,9 +827,9 @@ void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out, int_hse // Temps const lp_polynomial_t* p = 0; const lp_polynomial_t* q = 0; - lp_polynomial_t* p_r = lp_polynomial_new(map->ctx); - lp_polynomial_t* q_r = lp_polynomial_new(map->ctx); - lp_polynomial_t* p_r_d = lp_polynomial_new(map->ctx); + lp_polynomial_t* p_r = lp_data_new_polynomial(map->lp_data); + lp_polynomial_t* q_r = lp_data_new_polynomial(map->lp_data); + lp_polynomial_t* p_r_d = lp_data_new_polynomial(map->lp_data); // PSC buffer lp_polynomial_t** polynomial_buffer = 0; @@ -871,8 +837,8 @@ void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out, int_hse const lp_polynomial_t* x_cell_a_p = NULL; const lp_polynomial_t* x_cell_b_p = NULL; - lp_polynomial_t* x_cell_a_p_r = lp_polynomial_new(map->ctx); - lp_polynomial_t* x_cell_b_p_r = lp_polynomial_new(map->ctx); + lp_polynomial_t* x_cell_a_p_r = lp_data_new_polynomial(map->lp_data); + lp_polynomial_t* x_cell_b_p_r = lp_data_new_polynomial(map->lp_data); // Order the variables lp_projection_map_order_vars(map); @@ -893,7 +859,7 @@ void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out, int_hse } if (ctx_trace_enabled(map->plugin_ctx, "nra::explain::projection")) { - ctx_trace_printf(map->plugin_ctx, "x = %s\n", lp_variable_db_get_name(map->ctx->var_db, x)); + ctx_trace_printf(map->plugin_ctx, "x = %s\n", lp_variable_db_get_name(map->lp_data->lp_var_db, x)); } // Get the set of polynomials to project @@ -910,11 +876,11 @@ void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out, int_hse // - relationship between p in L, and l // - relationship between p in U, and u // - relationship between l and u - bool top = lp_assignment_get_value(map->m, x)->type == LP_VALUE_NONE; + bool top = lp_assignment_get_value(map->lp_data->lp_assignment, x)->type == LP_VALUE_NONE; bool output_cell = (cell_variables == NULL || int_hset_member(cell_variables, x)); #if TRACE - fprintf(stderr, "Projecting variable: %s\n", lp_variable_db_get_name(map->ctx->var_db, x)); + fprintf(stderr, "Projecting variable: %s\n", lp_variable_db_get_name(map->lp_data->lp_var_db, x)); #endif if (!top) { @@ -1007,7 +973,7 @@ void lp_projection_map_project(lp_projection_map_t* map, ivector_t* out, int_hse } // Reductum - lp_polynomial_reductum_m(q_r, q, map->m); + lp_polynomial_reductum_m(q_r, q, map->lp_data->lp_assignment); uint32_t q_r_deg = lp_polynomial_top_variable(q_r) == x ? lp_polynomial_degree(q_r) : 0; // No need to work on univariate ones @@ -1078,6 +1044,98 @@ bool constraint_get_value(const mcsat_trail_t* trail, const int_mset_t* pos, con return false; } +/** Try to resolve the two constraints with Fourier-Motzkin resolution */ +static +bool +poly_constraint_resolve_fm(nra_plugin_t *nra, + const poly_constraint_t *c0, bool c0_negated, + const poly_constraint_t *c1, bool c1_negated, + ivector_t *out) { + + lp_polynomial_context_t* ctx = nra->lp_data.lp_ctx; + lp_assignment_t* m = nra->lp_data.lp_assignment; + + if (poly_constraint_is_root_constraint(c0) || poly_constraint_is_root_constraint(c1)) { + return false; + } + + if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { + ctx_trace_printf(nra->ctx, "c0 %s: ", c0_negated ? "(negated)" : ""); + poly_constraint_print(c0, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + ctx_trace_printf(nra->ctx, "c1 %s: ", c1_negated ? "(negated)" : ""); + poly_constraint_print(c1, ctx_trace_out(nra->ctx)); + ctx_trace_printf(nra->ctx, "\n"); + } + + lp_polynomial_vector_t* assumptions = lp_polynomial_vector_new(ctx); + + lp_sign_condition_t R_sgn_condition; + lp_polynomial_t* R = lp_polynomial_new(ctx); + lp_sign_condition_t c0_sgn_condition = c0_negated ? lp_sign_condition_negate(c0->sgn_condition) : c0->sgn_condition; + lp_sign_condition_t c1_sgn_condition = c1_negated ? lp_sign_condition_negate(c1->sgn_condition) : c1->sgn_condition; + bool ok = lp_polynomial_constraint_resolve_fm(c0->polynomial, c0_sgn_condition, c1->polynomial, c1_sgn_condition, m, R, &R_sgn_condition, assumptions); + if (ok) { + // (C1 && C2 && assumptions && !(p R2 0)) => false + term_manager_t* tm = nra->ctx->tm; + size_t n = lp_polynomial_vector_size(assumptions); + size_t i; + for (i = 0; i < n; ++ i) { + lp_polynomial_t* assumption_p_i = lp_polynomial_vector_at(assumptions, i); + term_t assumption_i_p_term = lp_polynomial_to_yices_term_nra(nra, assumption_p_i); + int assumption_i_p_sgn = lp_polynomial_sgn(assumption_p_i, m); + // term_t assumption_i = NULL_TERM; // infer dead store + term_t assumption_i; + if (assumption_i_p_sgn < 0) { + assumption_i = mk_arith_term_lt0(tm, assumption_i_p_term); + } else if (assumption_i_p_sgn > 0) { + assumption_i = mk_arith_term_gt0(tm, assumption_i_p_term); + } else { + assumption_i = mk_arith_term_eq0(tm, assumption_i_p_term); + } + if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { + ctx_trace_printf(nra->ctx, "adding FM assumption: "); + ctx_trace_term(nra->ctx, assumption_i); + } + ivector_push(out, assumption_i); + lp_polynomial_delete(assumption_p_i); + } + term_t R_p_term = lp_polynomial_to_yices_term_nra(nra, R); + term_t R_term = NULL_TERM; + switch (R_sgn_condition) { + case LP_SGN_LT_0: + R_term = mk_arith_term_lt0(tm, R_p_term); + break; + case LP_SGN_LE_0: + R_term = mk_arith_term_leq0(tm, R_p_term); + break; + case LP_SGN_EQ_0: + R_term = mk_arith_term_eq0(tm, R_p_term); + break; + case LP_SGN_NE_0: + R_term = mk_arith_term_neq0(tm, R_p_term); + break; + case LP_SGN_GT_0: + R_term = mk_arith_term_gt0(tm, R_p_term); + break; + case LP_SGN_GE_0: + R_term = mk_arith_term_geq0(tm, R_p_term); + break; + } + R_term = opposite_term(R_term); + if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { + ctx_trace_printf(nra->ctx, "adding resolvent: "); + ctx_trace_term(nra->ctx, R_term); + } + ivector_push(out, R_term); + } + + lp_polynomial_delete(R); + lp_polynomial_vector_delete(assumptions); + + return ok; +} + void nra_plugin_explain_conflict(nra_plugin_t* nra, const int_mset_t* pos, const int_mset_t* neg, const ivector_t* core, const ivector_t* lemma_reasons, ivector_t* conflict) { @@ -1115,7 +1173,7 @@ void nra_plugin_explain_conflict(nra_plugin_t* nra, const int_mset_t* pos, const bool c1_negated = !constraint_get_value(nra->ctx->trail, pos, neg, c1_var); const poly_constraint_t* c0 = poly_constraint_db_get(nra->constraint_db, c0_var); const poly_constraint_t* c1 = poly_constraint_db_get(nra->constraint_db, c1_var); - bool resolved = poly_constraint_resolve_fm(c0, c0_negated, c1, c1_negated, nra, conflict); + bool resolved = poly_constraint_resolve_fm(nra, c0, c0_negated, c1, c1_negated, conflict); if (resolved) { term_t c0_term = variable_db_get_term(nra->ctx->var_db, c0_var); if (c0_negated) c0_term = opposite_term(c0_term); @@ -1147,7 +1205,8 @@ void nra_plugin_explain_conflict(nra_plugin_t* nra, const int_mset_t* pos, const bool negated = !trail_get_boolean_value(nra->ctx->trail, constraint_var); variable_t conflict_var = nra->conflict_variable; if (conflict_var == variable_null) conflict_var = nra->conflict_variable_int; - lp_variable_t x = nra_plugin_get_lp_variable(nra, conflict_var); + term_t t = variable_db_get_term(nra->ctx->var_db, conflict_var); + lp_variable_t x = lp_data_get_lp_variable_from_term(&nra->lp_data, t); lp_polynomial_t* p_inference_reason = lp_polynomial_constraint_explain_infer_bounds(p, sgn_condition, negated, x); if (p_inference_reason != NULL) { is_inference = true; @@ -1217,8 +1276,8 @@ void nra_plugin_describe_cell(nra_plugin_t* nra, term_t p, ivector_t* out_litera * Add the polynomial from the constraint to the projection map. * - We don't care about polarity, we just care about the polynomial. */ -void lp_projection_map_add_constraint(lp_projection_map_t* map, term_t cstr, int_hmap_t* term_to_lp_map) { - +static +void lp_projection_map_add_constraint(lp_projection_map_t* map, term_t cstr, lp_data_t* lp_data) { term_t t1, t2; term_table_t* terms = map->tm->terms; @@ -1230,13 +1289,13 @@ void lp_projection_map_add_constraint(lp_projection_map_t* map, term_t cstr, int case ARITH_EQ_ATOM: { // p == 0 t1 = arith_atom_arg(terms, cstr); - cstr_polynomial = lp_polynomial_from_term(t1, terms, term_to_lp_map, map->ctx, NULL); + cstr_polynomial = lp_polynomial_from_term(lp_data, t1, terms, NULL); break; } case ARITH_GE_ATOM: // p >= 0 t1 = arith_atom_arg(terms, cstr); - cstr_polynomial = lp_polynomial_from_term(t1, terms, term_to_lp_map, map->ctx, NULL); + cstr_polynomial = lp_polynomial_from_term(lp_data, t1, terms, NULL); break; case EQ_TERM: case ARITH_BINEQ_ATOM: { @@ -1247,15 +1306,15 @@ void lp_projection_map_add_constraint(lp_projection_map_t* map, term_t cstr, int lp_integer_t t1_c, t2_c; lp_integer_construct(&t1_c); lp_integer_construct(&t2_c); - lp_polynomial_t* t1_p = lp_polynomial_from_term(t1, terms, term_to_lp_map, map->ctx, &t1_c); - lp_polynomial_t* t2_p = lp_polynomial_from_term(t2, terms, term_to_lp_map, map->ctx, &t2_c); + lp_polynomial_t* t1_p = lp_polynomial_from_term(lp_data, t1, terms, &t1_c); + lp_polynomial_t* t2_p = lp_polynomial_from_term(lp_data, t2, terms, &t2_c); // t1_p/t1_c = t2_p/t2_c // t1_p*t2_c - t2_p*t1_c lp_integer_neg(lp_Z, &t1_c, &t1_c); lp_polynomial_mul_integer(t1_p, t1_p, &t2_c); lp_polynomial_mul_integer(t2_p, t2_p, &t1_c); // Add them - cstr_polynomial = lp_polynomial_new(map->ctx); + cstr_polynomial = lp_data_new_polynomial(map->lp_data); lp_polynomial_add(cstr_polynomial, t1_p, t2_p); // Remove temps lp_polynomial_delete(t1_p); @@ -1281,24 +1340,8 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag uint32_t i; - // Mapping from terms to libpoly variables and back - int_hmap_t lp_var_to_term_map; - int_hmap_t term_to_lp_var_map; - init_int_hmap(&lp_var_to_term_map, 0); - init_int_hmap(&term_to_lp_var_map, 0); - - // Variable database - lp_variable_db_t* lp_var_db = lp_variable_db_new(); - - // The variable order - lp_variable_order_t* lp_var_order = lp_variable_order_new(); - - // Libpoly context - lp_polynomial_context_t* lp_ctx = lp_polynomial_context_new(lp_Z, lp_var_db, lp_var_order); - - // Assignment - lp_assignment_t lp_assignment; - lp_assignment_construct(&lp_assignment, lp_var_db); + lp_data_t lp_data; + lp_data_init(&lp_data, NULL, NULL); // Set of variables we're keeping int_hset_t vars_to_keep_set; @@ -1313,7 +1356,7 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag continue; } - lp_variable_t x_lp = lp_variable_from_term(x, tm->terms, lp_var_db); + lp_variable_t x_lp = lp_data_add_lp_variable(&lp_data, tm->terms, x); // We're keeping this var int_hset_add(&vars_to_keep_set, x_lp); @@ -1322,10 +1365,6 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag fprintf(stderr, "Adding variable to keep: %s\n", lp_variable_db_get_name(lp_var_db, x_lp)); #endif - // Add variables to map - int_hmap_add(&lp_var_to_term_map, x_lp, x); - int_hmap_add(&term_to_lp_var_map, x, x_lp); - // Get the value in the model value_t x_value = model_get_term_value(mdl, x); assert(x_value >= 0); @@ -1333,17 +1372,14 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag mcsat_value_construct_from_value(&x_value_mcsat, &mdl->vtbl, x_value); const mcsat_value_t *x_value_lp = ensure_lp_value(&x_value_mcsat, &x_value_tmp); - // Set the model value - lp_assignment_set_value(&lp_assignment, x_lp, &x_value_lp->lp_value); + // Set the model value and push to order + lp_data_add_to_model_and_context(&lp_data, x_lp, &x_value_lp->lp_value); // Delete the temps mcsat_value_destruct(&x_value_mcsat); if (x_value_lp == &x_value_tmp) { mcsat_value_destruct(&x_value_tmp); } - - // Also add to the order - lp_variable_order_push(lp_var_order, x_lp); } // Add all the variables we're eliminating @@ -1355,16 +1391,12 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag continue; } - lp_variable_t x_lp = lp_variable_from_term(x, tm->terms, lp_var_db); + lp_variable_t x_lp = lp_data_add_lp_variable(&lp_data, tm->terms, x); #if TRACE fprintf(stderr, "Adding variable to eliminate: %s\n", lp_variable_db_get_name(lp_var_db, x_lp)); #endif - // Add variables to map - int_hmap_add(&lp_var_to_term_map, x_lp, x); - int_hmap_add(&term_to_lp_var_map, x, x_lp); - // Get the value in the model value_t x_value = model_get_term_value(mdl, x); assert(x_value >= 0); @@ -1372,22 +1404,19 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag mcsat_value_construct_from_value(&x_value_mcsat, &mdl->vtbl, x_value); const mcsat_value_t *x_value_lp = ensure_lp_value(&x_value_mcsat, &x_value_tmp); - // Set the model value - lp_assignment_set_value(&lp_assignment, x_lp, &x_value_lp->lp_value); + // Set the model value and push to order + lp_data_add_to_model_and_context(&lp_data, x_lp, &x_value_lp->lp_value); // Delete the temps mcsat_value_destruct(&x_value_mcsat); if (x_value_lp == &x_value_tmp) { mcsat_value_destruct(&x_value_tmp); } - - // Also add to the order - lp_variable_order_push(lp_var_order, x_lp); } - // Setup the projection + // Set up the projection lp_projection_map_t projector; - lp_projection_map_construct(&projector, lp_ctx, &lp_assignment, tm, NULL, &lp_var_to_term_map, false, false); + lp_projection_map_construct(&projector, tm, &lp_data, NULL, NULL, false, false); projector.use_root_constraints_for_cells = false; // Add all the literals @@ -1398,7 +1427,7 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag print_term(stderr, tm->terms, l); fprintf(stderr, "\n"); #endif - lp_projection_map_add_constraint(&projector, l, &term_to_lp_var_map); + lp_projection_map_add_constraint(&projector, l, &lp_data); } // Project @@ -1417,14 +1446,7 @@ int32_t nra_project_arith_literals(ivector_t* literals, model_t* mdl, term_manag // Delete temps lp_projection_map_destruct(&projector); delete_int_hset(&vars_to_keep_set); - lp_variable_db_detach(lp_var_db); - lp_variable_order_detach(lp_var_order); - lp_polynomial_context_detach(lp_ctx); - lp_assignment_destruct(&lp_assignment); - delete_int_hmap(&term_to_lp_var_map); - delete_int_hmap(&lp_var_to_term_map); + lp_data_destruct(&lp_data); return 0; } - - diff --git a/src/mcsat/nra/nra_plugin_explain.h b/src/mcsat/nra/nra_plugin_explain.h index 101ac62f2..afe45ea3c 100644 --- a/src/mcsat/nra/nra_plugin_explain.h +++ b/src/mcsat/nra/nra_plugin_explain.h @@ -43,12 +43,12 @@ void nra_plugin_explain_conflict(nra_plugin_t* nra, const int_mset_t* pos, const */ void nra_plugin_describe_cell(nra_plugin_t* nra, term_t p, ivector_t* out_literals); -/* +/** * Project a set of literals. * * Given a set of literals L satisfied by the model M, this function returns a new set of literals L' such that * - * - L' is aslo satisfied by M + * - L' is also satisfied by M * - L' only contains the variables in vars_to_keep; * - any satisfying assignment of L' can be extended to an assignment of L * diff --git a/src/mcsat/nra/nra_plugin_internal.c b/src/mcsat/nra/nra_plugin_internal.c index 7cd67d384..667b7ff33 100644 --- a/src/mcsat/nra/nra_plugin_internal.c +++ b/src/mcsat/nra/nra_plugin_internal.c @@ -15,14 +15,9 @@ * You should have received a copy of the GNU General Public License * along with Yices. If not, see . */ - -#include -#include "mcsat/nra/nra_plugin_internal.h" #include "mcsat/tracing.h" - -#include "utils/int_hash_map.h" -#include "libpoly_utils.h" +#include "mcsat/nra/nra_plugin_internal.h" void nra_plugin_get_constraint_variables(nra_plugin_t* nra, term_t constraint, int_mset_t* vars_out) { @@ -114,121 +109,6 @@ void nra_plugin_get_term_variables(nra_plugin_t* nra, term_t t, int_mset_t* vars } } -void nra_plugin_set_unit_info(nra_plugin_t* nra, variable_t constraint, variable_t unit_var, constraint_unit_info_t value) { - - int_hmap_pair_t* find = NULL; - int_hmap_pair_t* unit_find = NULL; - - // Add unit tag - find = int_hmap_find(&nra->constraint_unit_info, constraint); - if (find == NULL) { - // First time, just set - int_hmap_add(&nra->constraint_unit_info, constraint, value); - } else { - assert(find->val != value); - find->val = value; - } - - // Add unit variable - unit_find = int_hmap_find(&nra->constraint_unit_var, constraint); - if (value == CONSTRAINT_UNIT) { - if (unit_find == NULL) { - int_hmap_add(&nra->constraint_unit_var, constraint, unit_var); - } else { - unit_find->val = unit_var; - } - } else { - if (unit_find != NULL) { - unit_find->val = variable_null; - } - } -} - -bool nra_plugin_has_unit_info(const nra_plugin_t* nra, variable_t constraint) { - int_hmap_pair_t* find = int_hmap_find(&((nra_plugin_t*)nra)->constraint_unit_info, constraint); - return find != NULL; -} - -constraint_unit_info_t nra_plugin_get_unit_info(nra_plugin_t* nra, variable_t constraint) { - int_hmap_pair_t* find = int_hmap_find(&nra->constraint_unit_info, constraint); - if (find == NULL) { - return CONSTRAINT_UNKNOWN; - } else { - return find->val; - } -} - -variable_t nra_plugin_get_unit_var(nra_plugin_t* nra, variable_t constraint) { - int_hmap_pair_t* find = int_hmap_find(&nra->constraint_unit_var, constraint); - if (find == NULL) { - return variable_null; - } else { - return find->val; - } -} - -int nra_plugin_term_has_lp_variable(nra_plugin_t* nra, term_t t) { - variable_t mcsat_var = variable_db_get_variable(nra->ctx->var_db, t); - int_hmap_pair_t* find = int_hmap_find(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var); - return find != NULL; -} - -int nra_plugin_variable_has_lp_variable(nra_plugin_t* nra, variable_t mcsat_var) { - int_hmap_pair_t* find = int_hmap_find(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var); - return find != NULL; -} - -void nra_plugin_add_lp_variable_from_term(nra_plugin_t* nra, term_t t) { - - lp_variable_t lp_var = lp_variable_from_term(t, nra->ctx->terms, nra->lp_data.lp_var_db); - variable_t mcsat_var = variable_db_get_variable(nra->ctx->var_db, t); - - assert(int_hmap_find(&nra->lp_data.lp_to_mcsat_var_map, lp_var) == NULL); - assert(int_hmap_find(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var) == NULL); - - int_hmap_add(&nra->lp_data.lp_to_mcsat_var_map, lp_var, mcsat_var); - int_hmap_add(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var, lp_var); -} - -void nra_plugin_add_lp_variable(nra_plugin_t* nra, variable_t mcsat_var) { - - term_t t = variable_db_get_term(nra->ctx->var_db, mcsat_var); - - // Name of the term - char buffer[100]; - char* var_name = term_name(nra->ctx->terms, t); - if (var_name == NULL) { - var_name = buffer; - sprintf(var_name, "#%d", t); - if (ctx_trace_enabled(nra->ctx, "nra::vars")) { - ctx_trace_printf(nra->ctx, "%s -> ", var_name); - variable_db_print_variable(nra->ctx->var_db, mcsat_var, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, "\n"); - } - } - - // Make the variable - lp_variable_t lp_var = lp_variable_db_new_variable(nra->lp_data.lp_var_db, var_name); - - assert(int_hmap_find(&nra->lp_data.lp_to_mcsat_var_map, lp_var) == NULL); - assert(int_hmap_find(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var) == NULL); - - int_hmap_add(&nra->lp_data.lp_to_mcsat_var_map, lp_var, mcsat_var); - int_hmap_add(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var, lp_var); -} - -lp_variable_t nra_plugin_get_lp_variable(nra_plugin_t* nra, variable_t mcsat_var) { - int_hmap_pair_t* find = int_hmap_find(&nra->lp_data.mcsat_to_lp_var_map, mcsat_var); - assert(find != NULL); - return find->val; -} - -variable_t nra_plugin_get_variable_from_lp_variable(nra_plugin_t* nra, lp_variable_t lp_var) { - int_hmap_pair_t* find = int_hmap_find(&nra->lp_data.lp_to_mcsat_var_map, lp_var); - assert(find != NULL); - return find->val; -} - void nra_plugin_report_conflict(nra_plugin_t* nra, trail_token_t* prop, variable_t variable) { prop->conflict(prop); nra->conflict_variable = variable; diff --git a/src/mcsat/nra/nra_plugin_internal.h b/src/mcsat/nra/nra_plugin_internal.h index e7f9decbf..b6e3832f5 100644 --- a/src/mcsat/nra/nra_plugin_internal.h +++ b/src/mcsat/nra/nra_plugin_internal.h @@ -25,13 +25,12 @@ #include "mcsat/watch_list_manager.h" #include "mcsat/utils/scope_holder.h" #include "mcsat/utils/int_mset.h" +#include "mcsat/utils/lp_data.h" +#include "mcsat/utils/lp_constraint_db.h" #include "mcsat/nra/feasible_set_db.h" #include "terms/term_manager.h" -typedef struct poly_constraint_db_struct poly_constraint_db_t; -typedef struct poly_constraint_struct poly_constraint_t; - struct nra_plugin_s { /** The plugin interface */ @@ -43,15 +42,12 @@ struct nra_plugin_s { /** The watch list manager */ watch_list_manager_t wlm; + /** The unit info */ + constraint_unit_info_t unit_info; + /** Last variable that was decided, but yet unprocessed */ variable_t last_decided_and_unprocessed; - /** Map from constraint variables to the constraint_unit_info_t enum */ - int_hmap_t constraint_unit_info; - - /** Map from constraint variables to the variables they are unit in */ - int_hmap_t constraint_unit_var; - /** Next index of the trail to process */ uint32_t trail_i; @@ -97,32 +93,13 @@ struct nra_plugin_s { feasible_set_db_t* feasible_set_db; /** Data related to libpoly */ - struct { - - /** Libpoly variable database */ - lp_variable_db_t* lp_var_db; - /** Libpoly Variable order */ - lp_variable_order_t* lp_var_order; - /** Size of the variable order (for backtracking) */ - uint32_t lp_var_order_size; - /** Libpoly polynomioal context */ - lp_polynomial_context_t* lp_ctx; - /** Libpoly model */ - lp_assignment_t* lp_assignment; - /** Interval assignment for bound inference */ - lp_interval_assignment_t* lp_interval_assignment; - - /** Map from libpoly variables to mcsat variables */ - int_hmap_t lp_to_mcsat_var_map; - /** Map from mcsat variables to libpoly variables */ - int_hmap_t mcsat_to_lp_var_map; - } lp_data; + lp_data_t lp_data; /** Buffer for evaluation */ int_hmap_t evaluation_value_cache; int_hmap_t evaluation_timestamp_cache; - /** Buffer for feasible set computation (for true/false */ + /** Buffer for feasible set computation (for true/false) */ int_hmap_t feasible_set_cache_top_var[2]; // Top var when cached int_hmap_t feasible_set_cache_timestamp[2]; // Top timestamp of other variables when cached ptr_hmap_t feasible_set_cache[2]; // The cache @@ -148,36 +125,6 @@ void nra_plugin_get_term_variables(nra_plugin_t* nra, term_t t, int_mset_t* vars */ void nra_plugin_get_constraint_variables(nra_plugin_t* nra, term_t c, int_mset_t* vars_out); -/** Check if there term has an lp variable */ -int nra_plugin_term_has_lp_variable(nra_plugin_t* nra, term_t t); - -/** Check if the mcsat variable has an lp variable */ -int nra_plugin_variable_has_lp_variable(nra_plugin_t* nra, variable_t mcsat_var); - -/** Add a variable corresponding to the term t to libpoly */ -void nra_plugin_add_lp_variable_from_term(nra_plugin_t* nra, term_t t); - -/** Add a variable corresponding to the mcsat variable to libpoly */ -void nra_plugin_add_lp_variable(nra_plugin_t* nra, variable_t mcsat_var); - -/** Get the libpoly variable corresponding to term t (should have been added first) */ -lp_variable_t nra_plugin_get_lp_variable(nra_plugin_t* nra, variable_t t); - -/** Get the mcsat variable from the libpoly variable */ -variable_t nra_plugin_get_variable_from_lp_variable(nra_plugin_t* nra, lp_variable_t lp_var); - -/** Set the unit info for the given constraint */ -void nra_plugin_set_unit_info(nra_plugin_t* nra, variable_t constraint, variable_t unit_var, constraint_unit_info_t value); - -/** Are we tracking this constraint */ -bool nra_plugin_has_unit_info(const nra_plugin_t* nra, variable_t constraint); - -/** Get the unit info for the given constraint */ -constraint_unit_info_t nra_plugin_get_unit_info(nra_plugin_t* nra, variable_t constraint); - -/** Get the unit variable for the given constraint */ -variable_t nra_plugin_get_unit_var(nra_plugin_t* nra, variable_t constraint); - /** Report a conflict (variable is the one with an empty feasible set) */ void nra_plugin_report_conflict(nra_plugin_t* nra, trail_token_t* prop, variable_t variable); diff --git a/src/mcsat/nra/poly_constraint.c b/src/mcsat/nra/poly_constraint.c deleted file mode 100644 index b8ff39fa9..000000000 --- a/src/mcsat/nra/poly_constraint.c +++ /dev/null @@ -1,713 +0,0 @@ -/* - * This file is part of the Yices SMT Solver. - * Copyright (C) 2017 SRI International. - * - * Yices is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Yices is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Yices. If not, see . - */ - -#include "mcsat/nra/poly_constraint.h" -#include "mcsat/nra/libpoly_utils.h" -#include "terms/terms.h" -#include "mcsat/tracing.h" - -#include -#include -#include -#include -#include -#include - -/** - * A constraint of the form sgn(p(x)) = sgn_conition. - */ -struct poly_constraint_struct { - - /** The polynomial of the constraint */ - lp_polynomial_t* polynomial; - - /** The sign condition */ - lp_sign_condition_t sgn_condition; - - /** If this is a root constraint, this is the variable */ - lp_variable_t x; - - /** If this is a root constraint, this is the root index */ - size_t root_index; -}; - -static -bool poly_constraint_ok(const poly_constraint_t* cstr) { - switch (cstr->sgn_condition) { - case LP_SGN_LT_0: - case LP_SGN_LE_0: - case LP_SGN_EQ_0: - case LP_SGN_NE_0: - case LP_SGN_GT_0: - case LP_SGN_GE_0: - break; - default: - return false; - } - return lp_polynomial_check_integrity(cstr->polynomial);; -} - -/** Database of constraints */ -struct poly_constraint_db_struct { - /** The plugin */ - nra_plugin_t* nra; - - /** Vector of constraints */ - pvector_t constraints; - - /** Map from variables to constraint references */ - int_hmap_t var_to_constraint_map; - - /** List of all constraint variables */ - ivector_t all_constraint_variables; -}; - -void poly_constraint_db_gc_sweep(poly_constraint_db_t* db, const gc_info_t* gc_vars) { - - pvector_t new_constraints; - int_hmap_t new_var_to_constraint_map; - - init_pvector(&new_constraints, 0); - init_int_hmap(&new_var_to_constraint_map, 0); - - // Move the constraints - uint32_t i, to_keep = 0; - for (i = 0; i < db->all_constraint_variables.size; ++ i) { - variable_t old_constraint_var = db->all_constraint_variables.data[i]; - int_hmap_pair_t* it = int_hmap_find(&db->var_to_constraint_map, old_constraint_var); - // Do we keep it - variable_t new_constraint_var = gc_info_get_reloc(gc_vars, old_constraint_var); - poly_constraint_t* constraint = db->constraints.data[it->val]; - if (new_constraint_var != gc_vars->null_value) { - // Move it - uint32_t new_index = new_constraints.size; - pvector_push(&new_constraints, constraint); - int_hmap_add(&new_var_to_constraint_map, new_constraint_var, new_index); - db->all_constraint_variables.data[to_keep ++] = new_constraint_var; - } else { - if (ctx_trace_enabled(db->nra->ctx, "nra::gc")) { - ctx_trace_printf(db->nra->ctx, "Removing constraint :"); - poly_constraint_print(constraint, ctx_trace_out(db->nra->ctx)); - ctx_trace_printf(db->nra->ctx, "\n"); - } - // Delete it - poly_constraint_delete(constraint); - } - } - - // Destroy and swap in the new ones - delete_pvector(&db->constraints); - delete_int_hmap(&db->var_to_constraint_map); - db->constraints = new_constraints; - db->var_to_constraint_map = new_var_to_constraint_map; - ivector_shrink(&db->all_constraint_variables, to_keep); -} - - -void poly_constraint_construct_regular(poly_constraint_t* cstr, lp_polynomial_t* p, lp_sign_condition_t sgn_contition) { - cstr->polynomial = p; - cstr->sgn_condition = sgn_contition; - cstr->x = lp_variable_null; - cstr->root_index = 0; -} - -void poly_constraint_construct_root(poly_constraint_t* cstr, lp_polynomial_t* p, lp_sign_condition_t sgn_contition, lp_variable_t x, uint32_t root_index) { - cstr->polynomial = p; - cstr->sgn_condition = sgn_contition; - cstr->x = x; - cstr->root_index = root_index; -} - -poly_constraint_t* poly_constraint_new_regular(lp_polynomial_t* p, lp_sign_condition_t sgn_contition) { - poly_constraint_t* cstr = safe_malloc(sizeof(poly_constraint_t)); - poly_constraint_construct_regular(cstr, p, sgn_contition); - lp_polynomial_set_external(p); - return cstr; -} - -poly_constraint_t* poly_constraint_new_root(lp_polynomial_t* p, lp_sign_condition_t sgn_contition, lp_variable_t x, uint32_t root_index) { - poly_constraint_t* cstr = safe_malloc(sizeof(poly_constraint_t)); - poly_constraint_construct_root(cstr, p, sgn_contition, x, root_index); - lp_polynomial_set_external(p); - return cstr; -} - -void poly_constraint_destruct(poly_constraint_t* cstr) { - lp_polynomial_delete(cstr->polynomial); -} - -void poly_constraint_delete(poly_constraint_t* cstr) { - poly_constraint_destruct(cstr); - safe_free(cstr); -} - -void mathematica_print_traverse(const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* data) { - FILE* out = data; - - fprintf(out, "("); - lp_integer_print(&m->a, out); - uint32_t i = 0; - for (i = 0; i < m->n; ++ i) { - fprintf(out, "*x%zu^%zu", m->p[i].x, m->p[i].d); - } - fprintf(out, ") + "); -} - -void poly_constraint_print_mathematica(const poly_constraint_t* cstr, bool negated, FILE* out) { - - if (poly_constraint_is_root_constraint(cstr)) { - fprintf(out, "(x%zu - ", cstr->x); - fprintf(out, "Root["); - lp_polynomial_traverse(cstr->polynomial, mathematica_print_traverse, out); - fprintf(out, "0, %zu]", cstr->root_index); - lp_sign_condition_print(cstr->sgn_condition, out); - fprintf(out, ")"); - } else { - fprintf(out, "("); - lp_polynomial_traverse(cstr->polynomial, mathematica_print_traverse, out); - fprintf(out, "0 "); - lp_sign_condition_t sgn_condition = cstr->sgn_condition; - if (negated) { - sgn_condition = lp_sign_condition_negate(sgn_condition); - } - lp_sign_condition_print(sgn_condition, out); - fprintf(out, ")"); - } -} - -void poly_constraint_print(const poly_constraint_t* cstr, FILE* out) { - - const lp_polynomial_context_t* ctx = lp_polynomial_get_context(cstr->polynomial); - - if (poly_constraint_is_root_constraint(cstr)) { - fprintf(out, "(%s - ", lp_variable_db_get_name(ctx->var_db, cstr->x)); - fprintf(out, "root(%zu, ", cstr->root_index); - lp_polynomial_print(cstr->polynomial, out); - fprintf(out, ") "); - lp_sign_condition_print(cstr->sgn_condition, out); - fprintf(out, ")"); - } else { - fprintf(out, "("); - lp_sign_condition_print(cstr->sgn_condition, out); - fprintf(out, " ("); - lp_polynomial_print(cstr->polynomial, out); - fprintf(out, ") 0)"); - } -} - -lp_sign_condition_t poly_constraint_get_sign_condition(const poly_constraint_t* cstr) { - return cstr->sgn_condition; -} - -const lp_polynomial_t* poly_constraint_get_polynomial(const poly_constraint_t* cstr) { - return cstr->polynomial; -} - -bool poly_constraint_is_root_constraint(const poly_constraint_t* cstr) { - return cstr->x != lp_variable_null; -} - -lp_variable_t poly_constraint_get_variable(const poly_constraint_t* cstr) { - return cstr->x; -} - -uint32_t poly_constraint_get_root_index(const poly_constraint_t* cstr) { - return cstr->root_index; -} - -lp_feasibility_set_t* poly_constraint_get_feasible_set(const poly_constraint_t* cstr, const lp_assignment_t* m, bool negated) { - - lp_feasibility_set_t* feasible = 0; - - if (poly_constraint_is_root_constraint(cstr)) { - // Get the root constraint feasible set - if (cstr->x != lp_polynomial_top_variable(cstr->polynomial)) { - // x not top => constraint ignored - feasible = lp_feasibility_set_new_full(); - } else { - feasible = lp_polynomial_root_constraint_get_feasible_set(cstr->polynomial, cstr->root_index, cstr->sgn_condition, negated, m); - } - } else { - // Get the polynomial feasible set - feasible = lp_polynomial_constraint_get_feasible_set(cstr->polynomial, cstr->sgn_condition, negated, m); - } - - return feasible; -} - -bool poly_constraint_infer_bounds(const poly_constraint_t* cstr, bool negated, lp_interval_assignment_t* m, ivector_t* inferred_vars) { - - // TODO: is it possible to support root constraints - if (poly_constraint_is_root_constraint(cstr)) { - return false; - } - - // Infer some bounds - int inference_result = lp_polynomial_constraint_infer_bounds(cstr->polynomial, cstr->sgn_condition, negated, m); - if (inference_result == 0) { - return false; - } - if (inference_result == -1) { - return true; - } - - lp_variable_list_t vars; - lp_variable_list_construct(&vars); - lp_polynomial_get_variables(cstr->polynomial, &vars); - uint32_t var_i; - for (var_i = 0; var_i < vars.list_size; ++ var_i) { - lp_variable_t x_lp = vars.list[var_i]; - const lp_interval_t* x_interval = lp_interval_assignment_get_interval(m, x_lp); - if (x_interval != NULL) { - // something is inferred - ivector_push(inferred_vars, x_lp); - } - } - lp_variable_list_destruct(&vars); - - return false; -} - -bool poly_constraint_resolve_fm(const poly_constraint_t* c0, bool c0_negated, const poly_constraint_t* c1, bool c1_negated, nra_plugin_t* nra, ivector_t* out) { - - lp_polynomial_context_t* ctx = nra->lp_data.lp_ctx; - lp_assignment_t* m = nra->lp_data.lp_assignment; - - if (poly_constraint_is_root_constraint(c0) || poly_constraint_is_root_constraint(c1)) { - return false; - } - - if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { - ctx_trace_printf(nra->ctx, "c0 %s: ", c0_negated ? "(negated)" : ""); - poly_constraint_print(c0, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, "\n"); - ctx_trace_printf(nra->ctx, "c1 %s: ", c1_negated ? "(negated)" : ""); - poly_constraint_print(c1, ctx_trace_out(nra->ctx)); - ctx_trace_printf(nra->ctx, "\n"); - } - - lp_polynomial_vector_t* assumptions = lp_polynomial_vector_new(ctx); - - lp_sign_condition_t R_sgn_condition; - lp_polynomial_t* R = lp_polynomial_new(ctx); - lp_sign_condition_t c0_sgn_condition = c0_negated ? lp_sign_condition_negate(c0->sgn_condition) : c0->sgn_condition; - lp_sign_condition_t c1_sgn_condition = c1_negated ? lp_sign_condition_negate(c1->sgn_condition) : c1->sgn_condition; - bool ok = lp_polynomial_constraint_resolve_fm(c0->polynomial, c0_sgn_condition, c1->polynomial, c1_sgn_condition, m, R, &R_sgn_condition, assumptions); - if (ok) { - // (C1 && C2 && assumptions && !(p R2 0)) => false - term_manager_t* tm = nra->ctx->tm; - size_t n = lp_polynomial_vector_size(assumptions); - size_t i; - for (i = 0; i < n; ++ i) { - lp_polynomial_t* assumption_p_i = lp_polynomial_vector_at(assumptions, i); - term_t assumption_i_p_term = lp_polynomial_to_yices_term_nra(assumption_p_i, nra); - int assumption_i_p_sgn = lp_polynomial_sgn(assumption_p_i, m); - // term_t assumption_i = NULL_TERM; // infer dead store - term_t assumption_i; - if (assumption_i_p_sgn < 0) { - assumption_i = mk_arith_term_lt0(tm, assumption_i_p_term); - } else if (assumption_i_p_sgn > 0) { - assumption_i = mk_arith_term_gt0(tm, assumption_i_p_term); - } else { - assumption_i = mk_arith_term_eq0(tm, assumption_i_p_term); - } - if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { - ctx_trace_printf(nra->ctx, "adding FM assumption: "); - ctx_trace_term(nra->ctx, assumption_i); - } - ivector_push(out, assumption_i); - lp_polynomial_delete(assumption_p_i); - } - term_t R_p_term = lp_polynomial_to_yices_term_nra(R, nra); - term_t R_term = NULL_TERM; - switch (R_sgn_condition) { - case LP_SGN_LT_0: - R_term = mk_arith_term_lt0(tm, R_p_term); - break; - case LP_SGN_LE_0: - R_term = mk_arith_term_leq0(tm, R_p_term); - break; - case LP_SGN_EQ_0: - R_term = mk_arith_term_eq0(tm, R_p_term); - break; - case LP_SGN_NE_0: - R_term = mk_arith_term_neq0(tm, R_p_term); - break; - case LP_SGN_GT_0: - R_term = mk_arith_term_gt0(tm, R_p_term); - break; - case LP_SGN_GE_0: - R_term = mk_arith_term_geq0(tm, R_p_term); - break; - } - R_term = opposite_term(R_term); - if (ctx_trace_enabled(nra->ctx, "mcsat::nra::explain")) { - ctx_trace_printf(nra->ctx, "adding resolvent: "); - ctx_trace_term(nra->ctx, R_term); - } - ivector_push(out, R_term); - } - - lp_polynomial_delete(R); - lp_polynomial_vector_delete(assumptions); - - return ok; -} - - -bool poly_constraint_is_unit(const poly_constraint_t* cstr, const lp_assignment_t* M) { - lp_variable_t x = lp_polynomial_top_variable(cstr->polynomial); - if (lp_assignment_get_value(M, x)->type != LP_VALUE_NONE) { - return false; - } - - bool result = true; - - lp_variable_list_t vars; - lp_variable_list_construct(&vars); - lp_polynomial_get_variables(cstr->polynomial, &vars); - uint32_t var_i; - for (var_i = 0; var_i < vars.list_size; ++ var_i) { - lp_variable_t x_lp = vars.list[var_i]; - if (x_lp != x && lp_assignment_get_value(M, x_lp)->type == LP_VALUE_NONE) { - result = false; - break; - } - } - lp_variable_list_destruct(&vars); - - return result; -} - -lp_variable_t poly_constraint_get_top_variable(const poly_constraint_t* cstr) { - return lp_polynomial_top_variable(cstr->polynomial); -} - -void poly_constraint_db_construct(poly_constraint_db_t* db, nra_plugin_t* nra) { - db->nra = nra; - - init_pvector(&db->constraints, 0); - init_int_hmap(&db->var_to_constraint_map, 0); - init_ivector(&db->all_constraint_variables, 0); -} - -poly_constraint_db_t* poly_constraint_db_new(nra_plugin_t* nra) { - poly_constraint_db_t* db; - db = safe_malloc(sizeof(poly_constraint_db_t)); - poly_constraint_db_construct(db, nra); - return db; -} - -void poly_constraint_db_destruct(poly_constraint_db_t* db) { - uint32_t i; - poly_constraint_t* cstr; - - for (i = 0; i < db->constraints.size; i ++) { - cstr = db->constraints.data[i]; - poly_constraint_delete(cstr); - } - - delete_pvector(&db->constraints); - delete_int_hmap(&db->var_to_constraint_map); - delete_ivector(&db->all_constraint_variables); -} - -void poly_constraint_db_delete(poly_constraint_db_t* db) { - poly_constraint_db_destruct(db); - safe_free(db); -} - -const ivector_t* poly_constraint_db_get_constraints(const poly_constraint_db_t* db) { - return &db->all_constraint_variables; -} - - -bool poly_constraint_db_check(const poly_constraint_db_t* db) { - uint32_t i; - for (i = 0; i < db->constraints.size; ++ i) { - if (!poly_constraint_ok(db->constraints.data[i])) { - return false; - } - } - return true; -} - -const poly_constraint_t* poly_constraint_db_get(poly_constraint_db_t* db, variable_t constraint_var) { - - // assert(poly_constraint_db_check(db)); - int_hmap_pair_t* find; - find = int_hmap_find(&db->var_to_constraint_map, constraint_var); - assert(find != NULL); - assert(find->val < db->constraints.size); - poly_constraint_t* constraint = db->constraints.data[find->val]; - // assert(poly_constraint_ok(constraint)); - // assert(poly_constraint_db_check(db)); - return constraint; -} - -void poly_constraint_db_add(poly_constraint_db_t* db, variable_t constraint_var) { - // assert(poly_constraint_db_check(db)); - - if (int_hmap_find(&db->var_to_constraint_map, constraint_var) != NULL) { - // Already added - return; - } - - term_t t1, t2; - term_kind_t kind; - term_t constraint_var_term; - - // Constraint components - lp_polynomial_t* cstr_polynomial = 0; - lp_variable_t cstr_root_variable = lp_variable_null; - uint32_t cstr_root_index = 0; - lp_sign_condition_t sgn_condition; - - // Result constraint - poly_constraint_t* cstr; - - // Context - variable_db_t* var_db = db->nra->ctx->var_db; - term_table_t* terms = db->nra->ctx->terms; - - // Get the term of the variable - constraint_var_term = variable_db_get_term(var_db, constraint_var); - - // Depending on the kind, make the constraints - kind = term_kind(terms, constraint_var_term); - switch (kind) { - case ARITH_EQ_ATOM: { - // p == 0 - t1 = arith_atom_arg(terms, constraint_var_term); - cstr_polynomial = lp_polynomial_from_term_nra(db->nra, t1, NULL); - sgn_condition = LP_SGN_EQ_0; - break; - } - case ARITH_GE_ATOM: - // p >= 0 - t1 = arith_atom_arg(terms, constraint_var_term); - cstr_polynomial = lp_polynomial_from_term_nra(db->nra, t1, NULL); - sgn_condition = LP_SGN_GE_0; - break; - case EQ_TERM: - case ARITH_BINEQ_ATOM: { - // LHS = RHS - t1 = composite_term_arg(terms, constraint_var_term, 0); - t2 = composite_term_arg(terms, constraint_var_term, 1); - // Get the polynomials - lp_integer_t t1_c, t2_c; - lp_integer_construct(&t1_c); - lp_integer_construct(&t2_c); - lp_polynomial_t* t1_p = lp_polynomial_from_term_nra(db->nra, t1, &t1_c); - lp_polynomial_t* t2_p = lp_polynomial_from_term_nra(db->nra, t2, &t2_c); - // t1_p/t1_c = t2_p/t2_c - // t1_p*t2_c - t2_p*t1_c - lp_integer_neg(lp_Z, &t1_c, &t1_c); - lp_polynomial_mul_integer(t1_p, t1_p, &t2_c); - lp_polynomial_mul_integer(t2_p, t2_p, &t1_c); - // Add them - cstr_polynomial = lp_polynomial_new(db->nra->lp_data.lp_ctx); - lp_polynomial_add(cstr_polynomial, t1_p, t2_p); - // p1 = p2 - sgn_condition = LP_SGN_EQ_0; - // Remove temps - lp_polynomial_delete(t1_p); - lp_polynomial_delete(t2_p); - lp_integer_destruct(&t1_c); - lp_integer_destruct(&t2_c); - break; - } - case ARITH_ROOT_ATOM: { - root_atom_t* r = arith_root_atom_desc(terms, constraint_var_term); - cstr_polynomial = lp_polynomial_from_term_nra(db->nra, r->p, NULL); - variable_t x = variable_db_get_variable_if_exists(db->nra->ctx->var_db, r->x); - assert(x != variable_null); - cstr_root_variable = nra_plugin_get_lp_variable(db->nra, x); - cstr_root_index = r->k; - switch (r->r) { - case ROOT_ATOM_LT: - sgn_condition = LP_SGN_LT_0; - break; - case ROOT_ATOM_LEQ: - sgn_condition = LP_SGN_LE_0; - break; - case ROOT_ATOM_EQ: - sgn_condition = LP_SGN_EQ_0; - break; - case ROOT_ATOM_NEQ: - sgn_condition = LP_SGN_NE_0; - break; - case ROOT_ATOM_GEQ: - sgn_condition = LP_SGN_GE_0; - break; - case ROOT_ATOM_GT: - sgn_condition = LP_SGN_GT_0; - break; - default: - sgn_condition = LP_SGN_EQ_0; - assert(false); - break; - } - break; - } - default: { - // terms like (x+y), we create regular constraint (x+y) = x + y - lp_integer_t t1_c, t2_c; - lp_integer_construct_from_int(lp_Z, &t1_c, 1); - lp_integer_construct(&t2_c); - lp_polynomial_t* t1_p = lp_polynomial_alloc(); - lp_variable_t constraint_lp_var = nra_plugin_get_lp_variable(db->nra, constraint_var); - lp_polynomial_construct_simple(t1_p, db->nra->lp_data.lp_ctx, &t1_c, constraint_lp_var, 1); - lp_polynomial_t* t2_p = lp_polynomial_from_term_nra(db->nra, constraint_var_term, &t2_c); - // t1_p/t1_c = t2_p/t2_c - // t1_p*t2_c - t2_p*t1_c - lp_integer_neg(lp_Z, &t1_c, &t1_c); - lp_polynomial_mul_integer(t2_p, t2_p, &t1_c); - lp_polynomial_mul_integer(t1_p, t1_p, &t2_c); - // Add them - cstr_polynomial = lp_polynomial_new(db->nra->lp_data.lp_ctx); - lp_polynomial_add(cstr_polynomial, t1_p, t2_p); - // p1 = p2 - sgn_condition = LP_SGN_EQ_0; - // Remove temps - lp_polynomial_delete(t1_p); - lp_polynomial_delete(t2_p); - lp_integer_destruct(&t1_c); - lp_integer_destruct(&t2_c); - - break; - } - } - - // Id of the new constraint - uint32_t index = db->constraints.size; - - // Create the appropriate constraint - if (cstr_root_variable == lp_variable_null) { - cstr = poly_constraint_new_regular(cstr_polynomial, sgn_condition); - (*db->nra->stats.constraint_regular) ++; - } else { - cstr = poly_constraint_new_root(cstr_polynomial, sgn_condition, cstr_root_variable, cstr_root_index); - (*db->nra->stats.constraint_root) ++; - } - - - if (ctx_trace_enabled(db->nra->ctx, "mcsat::new_term")) { - ctx_trace_printf(db->nra->ctx, "poly_constraint_add: "); - poly_constraint_print(cstr, ctx_trace_out(db->nra->ctx)); - ctx_trace_printf(db->nra->ctx, "\n"); - } - - assert(poly_constraint_ok(cstr)); - - // Add the constraint - pvector_push(&db->constraints, cstr); - int_hmap_add(&db->var_to_constraint_map, constraint_var, index); - ivector_push(&db->all_constraint_variables, constraint_var); - - // assert(poly_constraint_db_check(db)); -} - -bool poly_constraint_is_valid(const poly_constraint_t* cstr) { - // Evaluate - if (poly_constraint_is_root_constraint(cstr)) { - return (cstr->x == lp_polynomial_top_variable(cstr->polynomial)); - } else { - return true; - } -} - -bool poly_constraint_evaluate(const poly_constraint_t* cstr, nra_plugin_t* nra, bool* value_out) { - - assert(poly_constraint_ok(cstr)); - - // Evaluate - if (poly_constraint_is_root_constraint(cstr)) { - if (cstr->x != lp_polynomial_top_variable(cstr->polynomial)) { - // if not top, ignore - return false; - } else { - *value_out = lp_polynomial_root_constraint_evaluate(cstr->polynomial, cstr->root_index, cstr->sgn_condition, nra->lp_data.lp_assignment); - } - } else { - *value_out = lp_polynomial_constraint_evaluate(cstr->polynomial, cstr->sgn_condition, nra->lp_data.lp_assignment); - } - - return true; -} - -const mcsat_value_t* poly_constraint_db_approximate(poly_constraint_db_t* db, variable_t constraint_var, nra_plugin_t* nra) { - const mcsat_value_t* result = NULL; - - // Get the constraints - const poly_constraint_t* cstr = poly_constraint_db_get(db, constraint_var); - if (poly_constraint_is_root_constraint(cstr)) { - // TODO: check if possible - return NULL; - } - - // Reset the interval assignment - lp_interval_assignment_t* m = nra->lp_data.lp_interval_assignment; - lp_interval_assignment_reset(m); - - // Setup the assignment x -> I(x) - assert(watch_list_manager_has_constraint(&nra->wlm, constraint_var)); - variable_list_ref_t var_list_ref = watch_list_manager_get_list_of(&nra->wlm, constraint_var); - variable_t* vars = watch_list_manager_get_list(&nra->wlm, var_list_ref); - for (; *vars != variable_null; vars++) { - variable_t x = *vars; - lp_variable_t x_lp = nra_plugin_get_lp_variable(nra, x); - lp_interval_t x_interval; - lp_interval_construct_full(&x_interval); - feasible_set_db_approximate_value(nra->feasible_set_db, x, &x_interval); - if (ctx_trace_enabled(nra->ctx, "mcsat::nra::learn")) { - ctx_trace_printf(db->nra->ctx, " "); - ctx_trace_term(db->nra->ctx, variable_db_get_term(db->nra->ctx->var_db, x)); - ctx_trace_printf(db->nra->ctx, " "); - lp_interval_print(&x_interval, ctx_trace_out(db->nra->ctx)); - ctx_trace_printf(db->nra->ctx, "\n"); - } - lp_interval_assignment_set_interval(m, x_lp, &x_interval); - lp_interval_destruct(&x_interval); - } - - // Evaluate the polynomial - lp_interval_t value; - lp_interval_construct_full(&value); - lp_polynomial_interval_value(cstr->polynomial, m, &value); - if (ctx_trace_enabled(nra->ctx, "mcsat::nra::learn")) { - poly_constraint_print(cstr, ctx_trace_out(db->nra->ctx)); - ctx_trace_printf(db->nra->ctx, " -> "); - lp_interval_print(&value, ctx_trace_out(db->nra->ctx)); - ctx_trace_printf(db->nra->ctx, "\n"); - } - - lp_sign_condition_t pos = cstr->sgn_condition; - lp_sign_condition_t neg = lp_sign_condition_negate(cstr->sgn_condition); - - if (lp_sign_condition_consistent_interval(pos, &value)) { - result = &mcsat_value_true; - } else if (lp_sign_condition_consistent_interval(neg, &value)) { - result = &mcsat_value_false; - } - - // Remove temps - lp_interval_destruct(&value); - - return result; -} diff --git a/src/mcsat/plugin.h b/src/mcsat/plugin.h index 88ceded83..fbe4d4511 100644 --- a/src/mcsat/plugin.h +++ b/src/mcsat/plugin.h @@ -156,7 +156,7 @@ struct trail_token_s { }; /** - * Allocator for plugins. An allocator shoudld + * Allocator for plugins. An allocator should * - Allocate the plugin, basically malloc(sizeof(actual_plugin_size)) * - Setup all the interface methods * - All other construction goes into the construct method @@ -235,7 +235,7 @@ struct plugin_s { */ void (*decide_assignment) (plugin_t* plugin, variable_t x, const mcsat_value_t* value, trail_token_t* decide); - /* + /** * Optional: learn using the given trail token. This is called at base level after * propagation is done and there is no conflict. This is a chance to perform some * more expensive reasoning and propagate consequences. diff --git a/src/mcsat/preprocessor.c b/src/mcsat/preprocessor.c index 04fe15489..4564e023d 100644 --- a/src/mcsat/preprocessor.c +++ b/src/mcsat/preprocessor.c @@ -121,6 +121,13 @@ composite_term_t* get_composite(term_table_t* terms, term_kind_t kind, term_t t) composite_for_noncomposite.arg[0] = arith_ge_arg(terms, t); return (composite_term_t*)&composite_for_noncomposite; } + case ARITH_FF_BINEQ_ATOM: + return arith_ff_bineq_atom_desc(terms, t); + case ARITH_FF_EQ_ATOM: { + composite_for_noncomposite.arity = 1; + composite_for_noncomposite.arg[0] = arith_ff_eq_arg(terms, t); + return (composite_term_t*)&composite_for_noncomposite; + } case APP_TERM: // application of an uninterpreted function return app_term_desc(terms, t); case ARITH_RDIV: // division: (/ x y) @@ -263,14 +270,14 @@ term_t preprocessor_purify(preprocessor_t* pre, term_t t, ivector_t* out) { // Negated terms must be purified if (is_pos_term(t)) { // We don't purify variables - term_kind_t t_kind = term_kind(terms, t); - switch (t_kind) { + switch (term_kind(terms, t)) { case UNINTERPRETED_TERM: // Variables are already pure return t; case CONSTANT_TERM: return t; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: case BV64_CONSTANT: case BV_CONSTANT: // Constants are also pure (except for false) @@ -422,6 +429,7 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is case BOOL_TYPE: case INT_TYPE: case REAL_TYPE: + case FF_TYPE: case UNINTERPRETED_TYPE: case FUNCTION_TYPE: case BITVECTOR_TYPE: @@ -439,8 +447,10 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is case BV64_CONSTANT: // compact bitvector constant (64 bits at most) case BV_CONSTANT: // generic bitvector constant (more than 64 bits) case ARITH_CONSTANT: // rational constant + case ARITH_FF_CONSTANT: // finite field constant current_pre = current; break; + case UNINTERPRETED_TERM: // (i.e., global variables, can't be bound). current_pre = current; // Unless we want special slicing @@ -474,8 +484,10 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is case OR_TERM: // n-ary OR case XOR_TERM: // n-ary XOR case ARITH_EQ_ATOM: // equality (t == 0) - case ARITH_BINEQ_ATOM: // equality: (t1 == t2) (between two arithmetic terms) + case ARITH_BINEQ_ATOM: // equality (t1 == t2) (between two arithmetic terms) case ARITH_GE_ATOM: // inequality (t >= 0) + case ARITH_FF_EQ_ATOM: // finite field equality (t == 0) + case ARITH_FF_BINEQ_ATOM: // finite field equality (t1 == t2) (between two arithmetic terms) case BV_DIV: case BV_REM: case BV_SMOD: @@ -503,8 +515,10 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is bool is_equality = current_kind == EQ_TERM || current_kind == BV_EQ_ATOM || + current_kind == ARITH_EQ_ATOM || current_kind == ARITH_BINEQ_ATOM || - current_kind == ARITH_EQ_ATOM; + current_kind == ARITH_FF_EQ_ATOM || + current_kind == ARITH_FF_BINEQ_ATOM; // don't rewrite if the equality is between Boolean terms bool is_boolean = is_boolean_type(term_type(pre->terms, desc->arg[0])); @@ -932,6 +946,48 @@ term_t preprocessor_apply(preprocessor_t* pre, term_t t, ivector_t* out, bool is break; } + case ARITH_FF_POLY: // polynomial with finite field coefficients + { + polynomial_t* p = finitefield_poly_term_desc(terms, current); + const rational_t *mod = finitefield_term_order(terms, current); + + bool children_done = true; + bool children_same = true; + + n = p->nterms; + + ivector_t children; + init_ivector(&children, n); + + for (i = 0; i < n; ++ i) { + term_t x = p->mono[i].var; + term_t x_pre = (x == const_idx ? const_idx : preprocessor_get(pre, x)); + + if (x_pre != const_idx) { + if (x_pre == NULL_TERM) { + children_done = false; + ivector_push(pre_stack, x); + } else if (x_pre != x) { + children_same = false; + } + } + + if (children_done) { ivector_push(&children, x_pre); } + } + + if (children_done) { + if (children_same) { + current_pre = current; + } else { + current_pre = mk_arith_ff_poly(tm, p, n, children.data, mod); + } + } + + delete_ivector(&children); + + break; + } + // FOLLOWING ARE UNINTEPRETED, SO WE PURIFY THE ARGUMENTS case APP_TERM: // application of an uninterpreted function diff --git a/src/mcsat/solver.c b/src/mcsat/solver.c index fa7f6f3e8..4ac73fdaa 100644 --- a/src/mcsat/solver.c +++ b/src/mcsat/solver.c @@ -46,6 +46,7 @@ #include "mcsat/nra/nra_plugin.h" #include "mcsat/uf/uf_plugin.h" #include "mcsat/bv/bv_plugin.h" +#include "mcsat/ff/ff_plugin.h" #include "mcsat/preprocessor.h" @@ -302,6 +303,7 @@ struct mcsat_solver_s { uint32_t ite_plugin_id; uint32_t nra_plugin_id; uint32_t bv_plugin_id; + uint32_t ff_plugin_id; }; static @@ -358,9 +360,10 @@ bool mcsat_evaluates_at(const mcsat_evaluator_interface_t* self, term_t t, int_m kind = term_kind(mcsat->terms, t); bool is_equality = false; switch (kind) { - case BV_EQ_ATOM: case EQ_TERM: + case BV_EQ_ATOM: case ARITH_BINEQ_ATOM: + case ARITH_FF_BINEQ_ATOM: is_equality = true; break; default: @@ -821,6 +824,7 @@ void mcsat_add_plugins(mcsat_solver_t* mcsat) { mcsat->ite_plugin_id = mcsat_add_plugin(mcsat, ite_plugin_allocator, "ite_plugin"); mcsat->nra_plugin_id = mcsat_add_plugin(mcsat, nra_plugin_allocator, "nra_plugin"); mcsat->bv_plugin_id = mcsat_add_plugin(mcsat, bv_plugin_allocator, "bv_plugin"); + mcsat->ff_plugin_id = mcsat_add_plugin(mcsat, ff_plugin_allocator, "ff_plugin"); } static @@ -2447,6 +2451,19 @@ bool mcsat_decide(mcsat_solver_t* mcsat) { var = variable_null; } + // then try the variables a plugin requested + if (var == variable_null) { + while (!int_queue_is_empty(&mcsat->hinted_decision_vars)) { + var = int_queue_pop(&mcsat->hinted_decision_vars); + assert(var != variable_null); + if (!trail_has_value(mcsat->trail, var)) { + force_decision = true; + break; + } + var = variable_null; + } + } + // If there is a fixed order that was passed in, try that if (var == variable_null) { const ivector_t* order = &mcsat->ctx->mcsat_var_order; @@ -2939,7 +2956,7 @@ void mcsat_build_model(mcsat_solver_t* mcsat, model_t* model) { term_kind_t x_kind = term_kind(mcsat->terms, x_term); if (x_kind == UNINTERPRETED_TERM && - term_type_kind(mcsat->terms, x_term) != FUNCTION_TYPE) { + term_type_kind(mcsat->terms, x_term) != FUNCTION_TYPE) { if (trace_enabled(mcsat->ctx->trace, "mcsat")) { mcsat_trace_printf(mcsat->ctx->trace, "var = "); @@ -2958,7 +2975,7 @@ void mcsat_build_model(mcsat_solver_t* mcsat, model_t* model) { mcsat_trace_printf(mcsat->ctx->trace, "\n"); } - // Setup the yices value + // Set up the yices value value_t x_value = mcsat_value_to_value(x_value_mcsat, mcsat->types, x_type, vtbl); if (trace_enabled(mcsat->ctx->trace, "mcsat")) { diff --git a/src/mcsat/trail.c b/src/mcsat/trail.c index 54321b094..7d434fd13 100644 --- a/src/mcsat/trail.c +++ b/src/mcsat/trail.c @@ -297,3 +297,39 @@ void trail_gc_sweep(mcsat_trail_t* trail, const gc_info_t* gc_vars) { } } } + +bool trail_variable_compare(const mcsat_trail_t *trail, variable_t t1, variable_t t2) { + bool t1_has_value, t2_has_value; + uint32_t t1_index, t2_index; + + // We compare variables based on the trail level, unassigned to the front, + // then assigned ones by decreasing level + + // Literals with no value + t1_has_value = trail_has_value(trail, t1); + t2_has_value = trail_has_value(trail, t2); + if (!t1_has_value && !t2_has_value) { + // Both have no value, just order by variable + return t1 < t2; + } + + // At least one has a value + if (!t1_has_value) { + // t1 < t2, goes to front + return true; + } + if (!t2_has_value) { + // t2 < t1, goes to front + return false; + } + + // Both literals have a value, sort by decreasing level + t1_index = trail_get_index(trail, t1); + t2_index = trail_get_index(trail, t2); + if (t1_index != t2_index) { + // t1 > t2 goes to front + return t1_index > t2_index; + } else { + return t1 < t2; + } +} diff --git a/src/mcsat/trail.h b/src/mcsat/trail.h index ab5f5e104..eda3ae899 100644 --- a/src/mcsat/trail.h +++ b/src/mcsat/trail.h @@ -34,12 +34,12 @@ typedef enum { } assignment_type_t; /* - * Trail of the solver containing all informations that the plugins need to + * Trail of the solver containing all information that the plugins need to * reason. It contains: - * - the trail itself, i.e. he sequence of variable assignments, + * - the trail itself, i.e. the sequence of variable assignments, * - the model, so that plugins can query the values of variables, and * - information about the levels of variables (so that plugins can compute - * propagation levels. + * propagation levels). */ struct mcsat_trail_s { @@ -64,7 +64,7 @@ struct mcsat_trail_s { /** The values per variable */ mcsat_model_t model; - /** Type of the assignment per variable (assignment_tyep_t) */ + /** Type of the assignment per variable (assignment_type_t) */ ivector_t type; /** Levels per variable (-1) for unassigned */ @@ -260,4 +260,7 @@ void trail_gc_mark(mcsat_trail_t* trail, gc_info_t* gc_vars); /** Sweep any data associated with the unmarked variables */ void trail_gc_sweep(mcsat_trail_t* trail, const gc_info_t* gc_vars); +/** compare variables based on the trail level, unassigned to the front, then assigned ones by decreasing level */ +bool trail_variable_compare(const mcsat_trail_t *trail, variable_t t1, variable_t t2); + #endif /* MCSAT_TRAIL_H_ */ diff --git a/src/mcsat/uf/uf_plugin.c b/src/mcsat/uf/uf_plugin.c index 9a02803f2..4e8f95858 100644 --- a/src/mcsat/uf/uf_plugin.c +++ b/src/mcsat/uf/uf_plugin.c @@ -902,8 +902,8 @@ plugin_t* uf_plugin_allocator(void) { plugin->plugin_interface.construct = uf_plugin_construct; plugin->plugin_interface.destruct = uf_plugin_destruct; plugin->plugin_interface.new_term_notify = uf_plugin_new_term_notify; - plugin->plugin_interface.new_lemma_notify = 0; - plugin->plugin_interface.event_notify = 0; + plugin->plugin_interface.new_lemma_notify = NULL; + plugin->plugin_interface.event_notify = NULL; plugin->plugin_interface.propagate = uf_plugin_propagate; plugin->plugin_interface.decide = uf_plugin_decide; plugin->plugin_interface.decide_assignment = NULL; diff --git a/src/mcsat/unit_info.c b/src/mcsat/unit_info.c new file mode 100644 index 000000000..ddebede78 --- /dev/null +++ b/src/mcsat/unit_info.c @@ -0,0 +1,82 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "unit_info.h" +#include + +void constraint_unit_info_init(constraint_unit_info_t *unit_info) { + init_int_hmap(&unit_info->constraint_unit_info, 0); + init_int_hmap(&unit_info->constraint_unit_var, 0); +} + +void constraint_unit_info_destruct(constraint_unit_info_t *unit_info) { + delete_int_hmap(&unit_info->constraint_unit_info); + delete_int_hmap(&unit_info->constraint_unit_var); +} + +void constraint_unit_info_gc_sweep(constraint_unit_info_t *unit_info, const gc_info_t* gc_vars) { + gc_info_sweep_int_hmap_keys(gc_vars, &unit_info->constraint_unit_info); + gc_info_sweep_int_hmap_keys(gc_vars, &unit_info->constraint_unit_var); +} + +void constraint_unit_info_set(constraint_unit_info_t *unit_info, variable_t constraint, variable_t unit_var, constraint_unit_state_t value) { + int_hmap_pair_t* find = NULL; + int_hmap_pair_t* unit_find = NULL; + + // Add unit tag + find = int_hmap_find(&unit_info->constraint_unit_info, constraint); + if (find == NULL) { + // First time, just set + int_hmap_add(&unit_info->constraint_unit_info, constraint, value); + } else { + assert(find->val != value); + find->val = value; + } + + // Add unit variable + unit_find = int_hmap_find(&unit_info->constraint_unit_var, constraint); + if (value == CONSTRAINT_UNIT) { + if (unit_find == NULL) { + int_hmap_add(&unit_info->constraint_unit_var, constraint, unit_var); + } else { + unit_find->val = unit_var; + } + } else { + assert(unit_var == variable_null); + if (unit_find != NULL) { + unit_find->val = variable_null; + } + } +} + +void constraint_unit_info_demote(constraint_unit_info_t *unit_info, variable_t constraint, variable_t x) { + constraint_unit_state_t state = constraint_unit_info_get(unit_info, constraint); + switch (state) { + case CONSTRAINT_UNKNOWN: + // Nothing to do + break; + case CONSTRAINT_UNIT: + // If it was unit it becomes not unit + constraint_unit_info_set(unit_info, constraint, variable_null, CONSTRAINT_UNKNOWN); + break; + case CONSTRAINT_FULLY_ASSIGNED: + // It is unit now + constraint_unit_info_set(unit_info, constraint, x, CONSTRAINT_UNIT); + break; + } +} diff --git a/src/mcsat/unit_info.h b/src/mcsat/unit_info.h index eb24db429..3bfae2933 100644 --- a/src/mcsat/unit_info.h +++ b/src/mcsat/unit_info.h @@ -18,6 +18,10 @@ #pragma once +#include "mcsat/variable_db.h" +#include "mcsat/gc.h" +#include "utils/int_hash_map.h" + typedef enum { /** The constraint is not unit, nor fully assigned */ CONSTRAINT_UNKNOWN, @@ -25,4 +29,56 @@ typedef enum { CONSTRAINT_UNIT, /** All variables of the constraint are assigned */ CONSTRAINT_FULLY_ASSIGNED +} constraint_unit_state_t; + +typedef struct { + /** Map from constraint variables to the constraint_unit_state_t enum */ + int_hmap_t constraint_unit_info; + + /** Map from constraint variables to the variables they are unit in */ + int_hmap_t constraint_unit_var; } constraint_unit_info_t; + +/** Init the constraint_unit_info_t */ +void constraint_unit_info_init(constraint_unit_info_t *unit_info); + +/** Destructs the constraint_unit_info_t */ +void constraint_unit_info_destruct(constraint_unit_info_t *unit_info); + +/** Sweeps the constraint maps to remove old variables */ +void constraint_unit_info_gc_sweep(constraint_unit_info_t *unit_info, const gc_info_t* gc_vars); + +/** + * Setting status of constraint: if value is CONSTRAINT_UNIT, then unit_var is the variable in which constraint is unit; + * otherwise unit_var is variable_null + */ +void constraint_unit_info_set(constraint_unit_info_t *unit_info, variable_t constraint, variable_t unit_var, constraint_unit_state_t value); + +/** + * Updates the unit info when variable x gets unassigned. + */ +void constraint_unit_info_demote(constraint_unit_info_t *unit_info, variable_t constraint, variable_t x); + +/** Are we tracking this constraint */ +static inline +bool constraint_unit_info_has(const constraint_unit_info_t* unit_info, variable_t constraint) { + return int_hmap_find(&unit_info->constraint_unit_info, constraint) != NULL; +} + +/** + * Getting status of constraint: if return value is CONSTRAINT_UNIT, + * then bv_plugin_get_unit_var returns the variable in which constraint is unit + * (otherwise it returns variable_null) + */ +static inline +constraint_unit_state_t constraint_unit_info_get(const constraint_unit_info_t* unit_info, variable_t constraint) { + int_hmap_pair_t* find = int_hmap_find(&unit_info->constraint_unit_info, constraint); + return find == NULL ? CONSTRAINT_UNKNOWN : find->val; +} + +/** Get the unit variable for the given constraint */ +static inline +variable_t constraint_unit_info_get_unit_var(const constraint_unit_info_t* unit_info, variable_t constraint) { + int_hmap_pair_t* find = int_hmap_find(&unit_info->constraint_unit_var, constraint); + return find == NULL ? variable_null : find->val; +} diff --git a/src/mcsat/utils/lp_constraint_db.c b/src/mcsat/utils/lp_constraint_db.c new file mode 100644 index 000000000..78d95f6e5 --- /dev/null +++ b/src/mcsat/utils/lp_constraint_db.c @@ -0,0 +1,362 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "lp_constraint_db.h" + +#include +#include +#include +#include + +#include "mcsat/tracing.h" + +#ifndef NDEBUG +static +bool poly_constraint_ok(const poly_constraint_t* cstr) { + switch (cstr->sgn_condition) { + case LP_SGN_LT_0: + case LP_SGN_LE_0: + case LP_SGN_EQ_0: + case LP_SGN_NE_0: + case LP_SGN_GT_0: + case LP_SGN_GE_0: + break; + default: + return false; + } + return lp_polynomial_check_integrity(cstr->polynomial);; +} + +static +bool poly_constraint_db_check(const poly_constraint_db_t* db) { + uint32_t i; + for (i = 0; i < db->constraints.size; ++ i) { + if (!poly_constraint_ok(db->constraints.data[i])) { + return false; + } + } + return true; +} +#endif + +void poly_constraint_db_construct(poly_constraint_db_t* db, lp_data_t* lp_data) { + db->lp_data = lp_data; + + init_pvector(&db->constraints, 0); + init_int_hmap(&db->var_to_constraint_map, 0); + init_ivector(&db->all_constraint_variables, 0); +} + +poly_constraint_db_t* poly_constraint_db_new(lp_data_t* lp_data) { + poly_constraint_db_t* db; + db = safe_malloc(sizeof(poly_constraint_db_t)); + poly_constraint_db_construct(db, lp_data); + return db; +} + +void poly_constraint_db_destruct(poly_constraint_db_t* db) { + uint32_t i; + poly_constraint_t* cstr; + + for (i = 0; i < db->constraints.size; i ++) { + cstr = db->constraints.data[i]; + poly_constraint_delete(cstr); + } + + delete_pvector(&db->constraints); + delete_int_hmap(&db->var_to_constraint_map); + delete_ivector(&db->all_constraint_variables); +} + +void poly_constraint_db_delete(poly_constraint_db_t* db) { + poly_constraint_db_destruct(db); + safe_free(db); +} + +void poly_constraint_construct_regular(poly_constraint_t* cstr, lp_polynomial_t* p, lp_sign_condition_t sgn_contition) { + cstr->polynomial = p; + cstr->sgn_condition = sgn_contition; + cstr->x = lp_variable_null; + cstr->root_index = 0; +} + +void poly_constraint_construct_root(poly_constraint_t* cstr, lp_polynomial_t* p, lp_sign_condition_t sgn_contition, lp_variable_t x, uint32_t root_index) { + cstr->polynomial = p; + cstr->sgn_condition = sgn_contition; + cstr->x = x; + cstr->root_index = root_index; +} + +poly_constraint_t* poly_constraint_new_regular(lp_polynomial_t* p, lp_sign_condition_t sgn_contition) { + poly_constraint_t* cstr = safe_malloc(sizeof(poly_constraint_t)); + poly_constraint_construct_regular(cstr, p, sgn_contition); + lp_polynomial_set_external(p); + return cstr; +} + +poly_constraint_t* poly_constraint_new_root(lp_polynomial_t* p, lp_sign_condition_t sgn_contition, lp_variable_t x, uint32_t root_index) { + poly_constraint_t* cstr = safe_malloc(sizeof(poly_constraint_t)); + poly_constraint_construct_root(cstr, p, sgn_contition, x, root_index); + lp_polynomial_set_external(p); + return cstr; +} + +void poly_constraint_destruct(poly_constraint_t* cstr) { + lp_polynomial_delete(cstr->polynomial); +} + +void poly_constraint_delete(poly_constraint_t* cstr) { + poly_constraint_destruct(cstr); + safe_free(cstr); +} + +void poly_constraint_db_add_constraint(poly_constraint_db_t* db, variable_t constraint_var, poly_constraint_t* cstr) { + assert(poly_constraint_ok(cstr)); + + // id of the new constraint + uint32_t index = db->constraints.size; + // add the constraint + pvector_push(&db->constraints, cstr); + int_hmap_add(&db->var_to_constraint_map, constraint_var, index); + ivector_push(&db->all_constraint_variables, constraint_var); +} + +static +void mathematica_print_traverse(const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* data) { + FILE* out = data; + + fprintf(out, "("); + lp_integer_print(&m->a, out); + uint32_t i = 0; + for (i = 0; i < m->n; ++ i) { + fprintf(out, "*x%zu^%zu", m->p[i].x, m->p[i].d); + } + fprintf(out, ") + "); +} + +void poly_constraint_print_mathematica(const poly_constraint_t* cstr, bool negated, FILE* out) { + + if (poly_constraint_is_root_constraint(cstr)) { + fprintf(out, "(x%zu - ", cstr->x); + fprintf(out, "Root["); + lp_polynomial_traverse(cstr->polynomial, mathematica_print_traverse, out); + fprintf(out, "0, %zu]", cstr->root_index); + lp_sign_condition_print(cstr->sgn_condition, out); + fprintf(out, ")"); + } else { + fprintf(out, "("); + lp_polynomial_traverse(cstr->polynomial, mathematica_print_traverse, out); + fprintf(out, "0 "); + lp_sign_condition_t sgn_condition = cstr->sgn_condition; + if (negated) { + sgn_condition = lp_sign_condition_negate(sgn_condition); + } + lp_sign_condition_print(sgn_condition, out); + fprintf(out, ")"); + } +} + +void poly_constraint_print(const poly_constraint_t* cstr, FILE* out) { + + const lp_polynomial_context_t* ctx = lp_polynomial_get_context(cstr->polynomial); + + if (poly_constraint_is_root_constraint(cstr)) { + fprintf(out, "(%s - ", lp_variable_db_get_name(ctx->var_db, cstr->x)); + fprintf(out, "root(%zu, ", cstr->root_index); + lp_polynomial_print(cstr->polynomial, out); + fprintf(out, ") "); + lp_sign_condition_print(cstr->sgn_condition, out); + fprintf(out, ")"); + } else { + fprintf(out, "("); + lp_sign_condition_print(cstr->sgn_condition, out); + fprintf(out, " ("); + lp_polynomial_print(cstr->polynomial, out); + fprintf(out, ") 0)"); + } +} + +bool poly_constraint_evaluate(const poly_constraint_t* cstr, lp_data_t *lp_data, bool* value_out) { + + assert(poly_constraint_ok(cstr)); + + // Evaluate + if (lp_data_get_ring(lp_data) == lp_Z) { + if (poly_constraint_is_root_constraint(cstr)) { + if (cstr->x != lp_polynomial_top_variable(cstr->polynomial)) { + // if not top, ignore + return false; + } else { + *value_out = lp_polynomial_root_constraint_evaluate(cstr->polynomial, cstr->root_index, cstr->sgn_condition, lp_data->lp_assignment); + } + } else { + *value_out = lp_polynomial_constraint_evaluate(cstr->polynomial, cstr->sgn_condition, lp_data->lp_assignment); + } + } else { + *value_out = lp_polynomial_constraint_evaluate_Zp(cstr->polynomial, cstr->sgn_condition, lp_data->lp_assignment); + } + + return true; +} + +lp_feasibility_set_t* poly_constraint_get_feasible_set(const poly_constraint_t* cstr, const lp_assignment_t* m, bool negated) { + + lp_feasibility_set_t* feasible = NULL; + + if (poly_constraint_is_root_constraint(cstr)) { + // Get the root constraint feasible set + if (cstr->x != lp_polynomial_top_variable(cstr->polynomial)) { + // x not top => constraint ignored + feasible = lp_feasibility_set_new_full(); + } else { + feasible = lp_polynomial_root_constraint_get_feasible_set(cstr->polynomial, cstr->root_index, cstr->sgn_condition, negated, m); + } + } else { + // Get the polynomial feasible set + feasible = lp_polynomial_constraint_get_feasible_set(cstr->polynomial, cstr->sgn_condition, negated, m); + } + + return feasible; +} + +bool poly_constraint_infer_bounds(const poly_constraint_t* cstr, bool negated, lp_interval_assignment_t* m, ivector_t* inferred_vars) { + + // TODO: is it possible to support root constraints + if (poly_constraint_is_root_constraint(cstr)) { + return false; + } + + // Infer some bounds + int inference_result = lp_polynomial_constraint_infer_bounds(cstr->polynomial, cstr->sgn_condition, negated, m); + if (inference_result == 0) { + return false; + } + if (inference_result == -1) { + return true; + } + + lp_variable_list_t vars; + lp_variable_list_construct(&vars); + lp_polynomial_get_variables(cstr->polynomial, &vars); + uint32_t var_i; + for (var_i = 0; var_i < vars.list_size; ++ var_i) { + lp_variable_t x_lp = vars.list[var_i]; + const lp_interval_t* x_interval = lp_interval_assignment_get_interval(m, x_lp); + if (x_interval != NULL) { + // something is inferred + ivector_push(inferred_vars, x_lp); + } + } + lp_variable_list_destruct(&vars); + + return false; +} + +lp_variable_t poly_constraint_get_top_variable(const poly_constraint_t* cstr) { + return lp_polynomial_top_variable(cstr->polynomial); +} + +bool poly_constraint_is_valid(const poly_constraint_t* cstr) { + // Evaluate + if (poly_constraint_is_root_constraint(cstr)) { + return (cstr->x == lp_polynomial_top_variable(cstr->polynomial)); + } else { + return true; + } +} + +bool poly_constraint_is_unit(const poly_constraint_t* cstr, const lp_assignment_t* M) { + lp_variable_t x = lp_polynomial_top_variable(cstr->polynomial); + if (lp_assignment_get_value(M, x)->type != LP_VALUE_NONE) { + return false; + } + + bool result = true; + + lp_variable_list_t vars; + lp_variable_list_construct(&vars); + lp_polynomial_get_variables(cstr->polynomial, &vars); + uint32_t var_i; + for (var_i = 0; var_i < vars.list_size; ++ var_i) { + lp_variable_t x_lp = vars.list[var_i]; + if (x_lp != x && lp_assignment_get_value(M, x_lp)->type == LP_VALUE_NONE) { + result = false; + break; + } + } + lp_variable_list_destruct(&vars); + + return result; +} + +bool poly_constraint_db_has(poly_constraint_db_t* db, variable_t constraint_var) { + return int_hmap_find(&db->var_to_constraint_map, constraint_var) != NULL; +} + +const poly_constraint_t* poly_constraint_db_get(poly_constraint_db_t* db, variable_t constraint_var) { + assert(poly_constraint_db_check(db)); + int_hmap_pair_t* find; + find = int_hmap_find(&db->var_to_constraint_map, constraint_var); + assert(find != NULL); + assert(find->val < db->constraints.size); + poly_constraint_t* constraint = db->constraints.data[find->val]; + assert(poly_constraint_ok(constraint)); + assert(poly_constraint_db_check(db)); + return constraint; +} + +void poly_constraint_db_gc_sweep(poly_constraint_db_t* db, plugin_context_t* ctx, const gc_info_t* gc_vars) { + + pvector_t new_constraints; + int_hmap_t new_var_to_constraint_map; + + init_pvector(&new_constraints, 0); + init_int_hmap(&new_var_to_constraint_map, 0); + + // Move the constraints + uint32_t i, to_keep = 0; + for (i = 0; i < db->all_constraint_variables.size; ++ i) { + variable_t old_constraint_var = db->all_constraint_variables.data[i]; + int_hmap_pair_t* it = int_hmap_find(&db->var_to_constraint_map, old_constraint_var); + // Do we keep it + variable_t new_constraint_var = gc_info_get_reloc(gc_vars, old_constraint_var); + poly_constraint_t* constraint = db->constraints.data[it->val]; + if (new_constraint_var != gc_vars->null_value) { + // Move it + uint32_t new_index = new_constraints.size; + pvector_push(&new_constraints, constraint); + int_hmap_add(&new_var_to_constraint_map, new_constraint_var, new_index); + db->all_constraint_variables.data[to_keep ++] = new_constraint_var; + } else { + if (ctx_trace_enabled(ctx, "lp::gc")) { + ctx_trace_printf(ctx, "Removing constraint :"); + poly_constraint_print(constraint, ctx_trace_out(ctx)); + ctx_trace_printf(ctx, "\n"); + } + // Delete it + poly_constraint_delete(constraint); + } + } + + // Destroy and swap in the new ones + delete_pvector(&db->constraints); + delete_int_hmap(&db->var_to_constraint_map); + db->constraints = new_constraints; + db->var_to_constraint_map = new_var_to_constraint_map; + ivector_shrink(&db->all_constraint_variables, to_keep); +} diff --git a/src/mcsat/nra/poly_constraint.h b/src/mcsat/utils/lp_constraint_db.h similarity index 66% rename from src/mcsat/nra/poly_constraint.h rename to src/mcsat/utils/lp_constraint_db.h index 2a1aa0cc5..ae128b874 100644 --- a/src/mcsat/nra/poly_constraint.h +++ b/src/mcsat/utils/lp_constraint_db.h @@ -1,4 +1,4 @@ - /* +/* * This file is part of the Yices SMT Solver. * Copyright (C) 2017 SRI International. * @@ -15,15 +15,94 @@ * You should have received a copy of the GNU General Public License * along with Yices. If not, see . */ - -#pragma once +#ifndef LP_CONSTRAINT_DB_H +#define LP_CONSTRAINT_DB_H + +#include #include -#include + +#include #include -#include "mcsat/nra/nra_plugin_internal.h" -#include "utils/int_hash_map.h" +#include "mcsat/utils/lp_data.h" +#include "mcsat/variable_db.h" + +/** + * A constraint of the form sgn(p(x)) = sgn_conition. + */ +typedef struct { + /** The polynomial of the constraint */ + lp_polynomial_t* polynomial; + + /** The sign condition */ + lp_sign_condition_t sgn_condition; + + /** If this is a root constraint, this is the variable */ + lp_variable_t x; + + /** If this is a root constraint, this is the root index */ + size_t root_index; +} poly_constraint_t; + +/** + * Database of constraints + */ +typedef struct { + /** The lp_data context */ + lp_data_t *lp_data; + + /** Vector of constraints */ + pvector_t constraints; + + /** Map from variables to constraint references */ + int_hmap_t var_to_constraint_map; + + /** List of all constraint variables */ + ivector_t all_constraint_variables; +} poly_constraint_db_t; + +/** Construct the database */ +void poly_constraint_db_construct(poly_constraint_db_t* db, lp_data_t* lp_data); + +/** Construct the database */ +poly_constraint_db_t* poly_constraint_db_new(lp_data_t* lp_data); + +/** Destruct the database */ +void poly_constraint_db_destruct(poly_constraint_db_t* db); + +/** Delete the database */ +void poly_constraint_db_delete(poly_constraint_db_t* db); + +/** Get the sign condition of the constraint */ +static inline +lp_sign_condition_t poly_constraint_get_sign_condition(const poly_constraint_t* cstr) { + return cstr->sgn_condition; +} + +/** Get the polynomial of the constraint */ +static inline +const lp_polynomial_t* poly_constraint_get_polynomial(const poly_constraint_t* cstr) { + return cstr->polynomial; +} + +/** Is this a root constraint */ +static inline +bool poly_constraint_is_root_constraint(const poly_constraint_t* cstr) { + return cstr->x != lp_variable_null; +} + +/** Get the variable of the root constraint */ +static inline +lp_variable_t poly_constraint_get_variable(const poly_constraint_t* cstr) { + return cstr->x; +} + +/** Get the root index (if a root constraint) */ +static inline +uint32_t poly_constraint_get_root_index(const poly_constraint_t* cstr) { + return cstr->root_index; +} /** Construct a regular constraint, takes over the polynomial */ void poly_constraint_construct_regular(poly_constraint_t* cstr, lp_polynomial_t* p, lp_sign_condition_t sgn_contition); @@ -43,78 +122,51 @@ void poly_constraint_destruct(poly_constraint_t* cstr); /** Destruct and free the constraint */ void poly_constraint_delete(poly_constraint_t* cstr); +/** Adds one constraint to the constraint db. */ +void poly_constraint_db_add_constraint(poly_constraint_db_t* db, variable_t constraint_var, poly_constraint_t* constraint); + /** Print the constraint */ void poly_constraint_print(const poly_constraint_t* cstr, FILE* out); /** Print the constraint to mathematica */ void poly_constraint_print_mathematica(const poly_constraint_t* cstr, bool neageted, FILE* out); -/** Get the feasible set of the constraint */ -lp_feasibility_set_t* poly_constraint_get_feasible_set(const poly_constraint_t* cstr, const lp_assignment_t* m, bool negated); - -/** Infer the bounds for this constraint (inferred_vars are lp_variables). Returns true if conflict detected. */ -bool poly_constraint_infer_bounds(const poly_constraint_t* cstr, bool negated, lp_interval_assignment_t* m, ivector_t* inferred_vars); - -/** - * Is this a valid constraint in the current order. - */ +/** Is this a valid constraint in the current order. */ bool poly_constraint_is_valid(const poly_constraint_t* cstr); -/** - * Evaluate the constraint. Returns the value, and sets the level to the level of the constraint. - * recomputed. The return value is true if evaluation is OK. If return value is false, - * it means that the top variable of a root constraint is not top anymore, so we - * can ignore it. - */ -bool poly_constraint_evaluate(const poly_constraint_t* cstr, nra_plugin_t* nra, bool* value_out); - -/** Get the top variable of the constraint */ -lp_variable_t poly_constraint_get_top_variable(const poly_constraint_t* cstr); - -/** Get the sign condition of the constraint */ -lp_sign_condition_t poly_constraint_get_sign_condition(const poly_constraint_t* cstr); - -/** Get the polynomial of the constraint */ -const lp_polynomial_t* poly_constraint_get_polynomial(const poly_constraint_t* cstr); - -/** Is this a root constraint */ -bool poly_constraint_is_root_constraint(const poly_constraint_t* cstr); - -/** Get the variable of the root constraint */ -lp_variable_t poly_constraint_get_variable(const poly_constraint_t* cstr); - -/** Get the root index (if a root constraint) */ -uint32_t poly_constraint_get_root_index(const poly_constraint_t* cstr); - /** Check if the constraint is unit */ bool poly_constraint_is_unit(const poly_constraint_t* cstr, const lp_assignment_t* M); -/** Try to resolve the two constraints with Fourier-Motzkin resolution */ -bool poly_constraint_resolve_fm(const poly_constraint_t* c0, bool c0_negated, const poly_constraint_t* c1, bool c1_negated, nra_plugin_t* nra, ivector_t* out); - -/** Construct the database */ -void poly_constraint_db_construct(poly_constraint_db_t* db, nra_plugin_t* nra); - -/** Construct the database */ -poly_constraint_db_t* poly_constraint_db_new(nra_plugin_t* nra); - -/** Destruct the database */ -void poly_constraint_db_destruct(poly_constraint_db_t* db); - -/** Delete the database */ -void poly_constraint_db_delete(poly_constraint_db_t* db); +/** Get the top variable of the constraint */ +lp_variable_t poly_constraint_get_top_variable(const poly_constraint_t* cstr); /** Get all constraints (as variables) */ -const ivector_t* poly_constraint_db_get_constraints(const poly_constraint_db_t* db); +static inline +const ivector_t* poly_constraint_db_get_constraints(const poly_constraint_db_t* db) { + return &db->all_constraint_variables; +} + +/** Returns true when the constraint_var has an associated polynomial in the db */ +bool poly_constraint_db_has(poly_constraint_db_t* db, variable_t constraint_var); /** Get the constraint of the variable (must exist) */ const poly_constraint_t* poly_constraint_db_get(poly_constraint_db_t* db, variable_t constraint_var); -/** Compute an approximation of the constraint value with interval computation */ -const mcsat_value_t* poly_constraint_db_approximate(poly_constraint_db_t* db, variable_t constraint_var, nra_plugin_t* nra); +/** + * Evaluate the constraint. Returns the value, and sets the level to the level of the constraint. + * recomputed. The return value is true if evaluation is OK. If return value is false, + * it means that the top variable of a root constraint is not top anymore, so we + * can ignore it. + */ +bool poly_constraint_evaluate(const poly_constraint_t* cstr, lp_data_t *lp_data, bool* value_out); + +/** Get the feasible set of the constraint */ +lp_feasibility_set_t* poly_constraint_get_feasible_set(const poly_constraint_t* cstr, const lp_assignment_t* m, bool negated); + +/** Infer the bounds for this constraint (inferred_vars are lp_variables). Returns true if conflict detected. */ +bool poly_constraint_infer_bounds(const poly_constraint_t* cstr, bool negated, lp_interval_assignment_t* m, ivector_t* inferred_vars); -/** Add a new constraint */ -void poly_constraint_db_add(poly_constraint_db_t* db, variable_t constraint_var); +/** Remove unused constraints */ +void poly_constraint_db_gc_sweep(poly_constraint_db_t* db, plugin_context_t* ctx, const gc_info_t* gc_vars); -/** Remove unised constraints */ -void poly_constraint_db_gc_sweep(poly_constraint_db_t* db, const gc_info_t* gc_vars); +#endif /* LP_CONSTRAINT_DB_H */ diff --git a/src/mcsat/utils/lp_data.c b/src/mcsat/utils/lp_data.c new file mode 100644 index 000000000..7baa0d88e --- /dev/null +++ b/src/mcsat/utils/lp_data.c @@ -0,0 +1,234 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "lp_data.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mcsat/mcsat_types.h" +#include "mcsat/plugin.h" +#include "mcsat/tracing.h" +#include "mcsat/gc.h" +#include "terms/terms.h" + +void lp_data_init(lp_data_t *lp_data, mpz_t order, const plugin_context_t *plugin_ctx) { + init_int_hmap(&lp_data->term_to_lp_var_map, 0); + init_int_hmap(&lp_data->lp_var_to_term_map, 0); + + lp_int_ring_t *ring = (order == NULL || mpz_sgn(order) <= 0) ? lp_Z : lp_int_ring_create(order, true); + + lp_data->lp_var_db = lp_variable_db_new(); + lp_data->lp_var_order = lp_variable_order_new(); + lp_data->lp_var_order_size = 0; + lp_data->lp_ctx = lp_polynomial_context_new(ring, lp_data->lp_var_db, lp_data->lp_var_order); + lp_data->lp_assignment = lp_assignment_new(lp_data->lp_var_db); + lp_data->lp_interval_assignment = lp_interval_assignment_new(lp_data->lp_var_db); + + scope_holder_construct(&lp_data->scope); + + lp_data->plugin_ctx = plugin_ctx; + + // Tracing in libpoly + if (false) { + lp_trace_enable("coefficient"); + lp_trace_enable("coefficient::sgn"); + lp_trace_enable("coefficient::interval"); + lp_trace_enable("polynomial::expensive"); + } +} + +void lp_data_destruct(lp_data_t *lp_data) { + delete_int_hmap(&lp_data->term_to_lp_var_map); + delete_int_hmap(&lp_data->lp_var_to_term_map); + + lp_polynomial_context_detach(lp_data->lp_ctx); + lp_variable_order_detach(lp_data->lp_var_order); + lp_variable_db_detach(lp_data->lp_var_db); + lp_assignment_delete(lp_data->lp_assignment); + lp_interval_assignment_delete(lp_data->lp_interval_assignment); + + scope_holder_destruct(&lp_data->scope); +} + +lp_data_t* lp_data_new(mpz_t order, const plugin_context_t *plugin_ctx) { + lp_data_t *lp_data = safe_malloc(sizeof(lp_data_t)); + lp_data_init(lp_data, order, plugin_ctx); + return lp_data; +} + +void lp_data_delete(lp_data_t* lp_data) { + lp_data_destruct(lp_data); + safe_free(lp_data); +} + +bool lp_data_is_order(lp_data_t *lp_data, mpz_t order) { + assert(order == NULL || mpz_sgn(order) >= 0); + + if (order == NULL || mpz_sgn(order) == 0) { // expecting Z + return lp_data->lp_ctx->K == lp_Z; + } else if (lp_data->lp_ctx->K == lp_Z) { // expecting not Z, but is Z + return false; + } + return mpz_cmp(&lp_data->lp_ctx->K->M, order) == 0; +} + +static +void lp_data_variable_link(lp_data_t *lp_data, lp_variable_t lp_var, int32_t var) { + assert(int_hmap_find(&lp_data->lp_var_to_term_map, lp_var) == NULL); + assert(int_hmap_find(&lp_data->term_to_lp_var_map, var) == NULL); + + int_hmap_add(&lp_data->lp_var_to_term_map, lp_var, var); + int_hmap_add(&lp_data->term_to_lp_var_map, var, lp_var); +} + +lp_variable_t lp_data_add_lp_variable(lp_data_t *lp_data, term_table_t *terms, term_t t) { + assert(t != NULL_TERM && good_term_idx(terms, index_of(t))); + assert(is_pos_term(t)); + // Name of the term + char buffer[100]; + char* var_name = term_name(terms, t); + if (var_name == NULL) { + var_name = buffer; + sprintf(var_name, "#%d", t); + } + + // Make the variable + lp_variable_t lp_var = lp_variable_db_new_variable(lp_data->lp_var_db, var_name); + lp_data_variable_link(lp_data, lp_var, t); + + return lp_var; +} + +void lp_data_variable_order_push(lp_data_t *lp_data) { + scope_holder_push(&lp_data->scope, + &lp_data->lp_var_order_size, + NULL); +} + +void lp_data_variable_order_pop(lp_data_t *lp_data) { + scope_holder_pop(&lp_data->scope, + &lp_data->lp_var_order_size, + NULL); + + lp_variable_order_t* order = lp_data->lp_var_order; + lp_assignment_t* assignment = lp_data->lp_assignment; + while (lp_variable_order_size(order) > lp_data->lp_var_order_size) { + lp_variable_t lp_var = lp_variable_order_top(order); + lp_variable_order_pop(order); + lp_assignment_set_value(assignment, lp_var, 0); + } +} + +void lp_data_add_to_model_and_context(lp_data_t *lp_data, lp_variable_t lp_var, const lp_value_t *lp_value) { + lp_assignment_set_value(lp_data->lp_assignment, lp_var, lp_value); + lp_variable_order_push(lp_data->lp_var_order, lp_var); + lp_data->lp_var_order_size ++; +} + +lp_variable_t lp_data_new_variable(const lp_data_t *lp_data, const char* var_name) { + return lp_variable_db_new_variable(lp_data->lp_var_db, var_name); +} + +lp_polynomial_t* lp_data_new_polynomial(const lp_data_t *lp_data) { + return lp_polynomial_new(lp_data->lp_ctx); +} + +void lp_data_variable_order_print(const lp_data_t *lp_data, FILE *file) { + lp_variable_order_print(lp_data->lp_var_order, lp_data->lp_var_db, file); +} + +const lp_int_ring_t* lp_data_get_ring(const lp_data_t *lp_data) { + return lp_data->lp_ctx->K; +} + +#ifndef NDEBUG +static +bool lp_data_check_consistency(lp_data_t *lp_data) { + if (lp_data->term_to_lp_var_map.size != lp_data->lp_var_to_term_map.size) { + return false; + } + + int_hmap_t *objs = &lp_data->term_to_lp_var_map; + int_hmap_pair_t* it = int_hmap_first_record(objs); + for (; it != NULL; it = int_hmap_next_record(objs, it)) { + int_hmap_pair_t *p = int_hmap_get(&lp_data->lp_var_to_term_map, it->val); + if (p == NULL || p->val != it->key) { + return false; + } + } + + return true; +} +#endif + +void lp_data_gc_sweep(lp_data_t *lp_data, const gc_info_t *gc_vars) { + // - lp_data.lp_var_to_term_map (values) + // - lp_data.term_to_lp_var_map (keys) + + const variable_db_t *var_db = lp_data->plugin_ctx->var_db; + + assert(lp_data_check_consistency(lp_data)); + + // New map + int_hmap_t new_objs_t2lv, new_objs_lv2t, *objs = &lp_data->term_to_lp_var_map; + init_int_hmap(&new_objs_t2lv, 0); + init_int_hmap(&new_objs_lv2t, 0); + + // Relocate + int_hmap_pair_t* it = int_hmap_first_record(objs); + for (; it != NULL; it = int_hmap_next_record(objs, it)) { + term_t old_term = it->key; + variable_t old_var = variable_db_get_variable_if_exists(var_db, old_term); + if (old_var == variable_null) { + // the term doesn't have a variable assigned anymore, there is no need to keep it's lp_var + continue; + } + variable_t new_var = gc_info_get_reloc(gc_vars, old_var); + if (new_var != gc_vars->null_value) { + term_t new_term = variable_db_get_term(var_db, new_var); + int_hmap_add(&new_objs_t2lv, new_term, it->val); + int_hmap_add(&new_objs_lv2t, it->val, new_term); + } + } + + // Destroy and swap in + delete_int_hmap(&lp_data->term_to_lp_var_map); + delete_int_hmap(&lp_data->lp_var_to_term_map); + lp_data->term_to_lp_var_map = new_objs_t2lv; + lp_data->lp_var_to_term_map = new_objs_lv2t; + + assert(lp_data_check_consistency(lp_data)); +} + +void lp_data_print(const lp_data_t *lp_data, FILE *out) { + fprintf(out, "Var DB: "); + lp_variable_db_print(lp_data->lp_var_db, out); + fprintf(out, "\nVar order: "); + lp_variable_order_print(lp_data->lp_var_order, lp_data->lp_var_db, out); + fprintf(out, "\nVar assignment: "); + lp_assignment_print(lp_data->lp_assignment, out); + fprintf(out, "\n"); +} diff --git a/src/mcsat/utils/lp_data.h b/src/mcsat/utils/lp_data.h new file mode 100644 index 000000000..9dbabf5d3 --- /dev/null +++ b/src/mcsat/utils/lp_data.h @@ -0,0 +1,117 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef MCSAT_LP_MANAGER_H +#define MCSAT_LP_MANAGER_H + +#include +#include +#include + +#include "terms/terms.h" + +#include "mcsat/mcsat_types.h" +#include "mcsat/gc.h" +#include "mcsat/utils/scope_holder.h" + +#include "utils/int_hash_map.h" + +/** + * To be used by plugins that utilize libPoly. + */ +typedef struct lp_data_s { + /** Libpoly variable database */ + lp_variable_db_t* lp_var_db; + /** Libpoly Variable order */ + lp_variable_order_t* lp_var_order; + /** Size of the variable order (for backtracking) */ + uint32_t lp_var_order_size; + /** Push/Pop scope for lp_var_order_size */ + scope_holder_t scope; + /** Libpoly polynomial context */ + lp_polynomial_context_t* lp_ctx; + /** Libpoly model */ + lp_assignment_t* lp_assignment; + /** Interval assignment for bound inference */ + lp_interval_assignment_t* lp_interval_assignment; + + /** Map from libpoly variables to mcsat variables */ + int_hmap_t lp_var_to_term_map; + /** Map from mcsat variables to libpoly variables */ + int_hmap_t term_to_lp_var_map; + + /** The plugin, for printing (can be NULL) */ + const plugin_context_t* plugin_ctx; +} lp_data_t; + + +void lp_data_init(lp_data_t *lp_data, mpz_t order, const plugin_context_t *plugin_ctx); + +void lp_data_destruct(lp_data_t *lp_data); + +lp_data_t *lp_data_new(mpz_t order, const plugin_context_t *plugin_ctx); + +void lp_data_delete(lp_data_t *lp_data); + +/** Returns true when the lp_data is of given order */ +bool lp_data_is_order(lp_data_t *lp_data, mpz_t order); + +/** Add a variable corresponding to the term */ +lp_variable_t lp_data_add_lp_variable(lp_data_t *lp_data, term_table_t *terms, term_t t); + +void lp_data_variable_order_push(lp_data_t *lp_data); + +void lp_data_variable_order_pop(lp_data_t *lp_data); + +void lp_data_add_to_model_and_context(lp_data_t *lp_data, lp_variable_t lp_var, const lp_value_t *lp_value); + +void lp_data_variable_order_print(const lp_data_t *lp_data, FILE *file); + +void lp_data_gc_sweep(lp_data_t *lp_data, const gc_info_t *gc_vars); + +/** Creates a new lp_variable with a given name. */ +lp_variable_t lp_data_new_variable(const lp_data_t *lp_data, const char* var_name); + +/** Crates a new lp_polynomial with the current context */ +lp_polynomial_t* lp_data_new_polynomial(const lp_data_t *lp_data); + +/** Check if the mcsat variable has a term */ +static inline bool lp_data_variable_has_term(lp_data_t* lp_data, term_t t) { + return int_hmap_find(&lp_data->term_to_lp_var_map, t) != NULL; +} + +/** Get the libpoly variable corresponding to term t (should have been added first) */ +static inline lp_variable_t lp_data_get_lp_variable_from_term(const lp_data_t *lp_data, term_t t) { + int_hmap_pair_t* find = int_hmap_find(&lp_data->term_to_lp_var_map, t); + assert(find != NULL); + return find->val; +} + +/** Get the term from the libpoly variable */ +static inline term_t lp_data_get_term_from_lp_variable(const lp_data_t *lp_data, lp_variable_t lp_var) { + int_hmap_pair_t* find = int_hmap_find(&lp_data->lp_var_to_term_map, lp_var); + assert(find != NULL); + return find->val; +} + +/** Gets the ring of the lp_data */ +const lp_int_ring_t* lp_data_get_ring(const lp_data_t *lp_data); + +void lp_data_print(const lp_data_t *lp_data, FILE *out); + +#endif /* MCSAT_LP_MANAGER_H */ diff --git a/src/mcsat/utils/lp_utils.c b/src/mcsat/utils/lp_utils.c new file mode 100644 index 000000000..3ea346604 --- /dev/null +++ b/src/mcsat/utils/lp_utils.c @@ -0,0 +1,420 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#include "mcsat/utils/lp_utils.h" + +#include "terms/rba_buffer_terms.h" +#include "terms/term_manager.h" + +#include +#include + +#include "mcsat/value.h" + +/** + * Construct an p/q from a rational constant. If any of p or q are NULL, they are ignored. + */ +static +void lp_integer_construct_from_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q) { + if (lp_p != NULL) { + rational_t q_num; + q_init(&q_num); + q_get_num(&q_num, q); + mpq_t q_num_mpq; + mpq_init(q_num_mpq); + q_get_mpq(&q_num, q_num_mpq); + lp_integer_construct_from_rational(lp_Z, lp_p, q_num_mpq); + mpq_clear(q_num_mpq); + q_clear(&q_num); + } + if (lp_q != NULL) { + rational_t q_den; + q_init(&q_den); + q_get_den(&q_den, q); + mpq_t q_den_mpq; + mpq_init(q_den_mpq); + q_get_mpq(&q_den, q_den_mpq); + lp_integer_construct_from_rational(lp_Z, lp_q, q_den_mpq); + mpq_clear(q_den_mpq); + q_clear(&q_den); + } +} + +/** + * Assign p/q from a yices rational constant. + */ +static +void lp_integer_assign_yices_rational(lp_integer_t* lp_p, lp_integer_t* lp_q, const rational_t* q) { + if (lp_p != NULL) lp_integer_destruct(lp_p); + if (lp_q != NULL) lp_integer_destruct(lp_q); + lp_integer_construct_from_yices_rational(lp_p, lp_q, q); +} + +/** + * Create a libpoly polynomial from a yices power product. Returns lp_p = pp * c. + * + * @param term_to_lp_map a map from variables (terms) to variables (libpoly). + */ +static +lp_polynomial_t* lp_polynomial_from_power_product(lp_data_t *lp_data, pprod_t* pp, lp_integer_t* c) { + const lp_int_ring_t* K = lp_data->lp_ctx->K; + + // The monomials + lp_monomial_t lp_monomial; + lp_monomial_construct(lp_data->lp_ctx, &lp_monomial); + + // Set monomial coefficient to 1 + lp_integer_t one; + lp_integer_construct_from_int(K, &one, 1); + lp_monomial_set_coefficient(lp_data->lp_ctx, &lp_monomial, &one); + lp_integer_destruct(&one); + + // Get the product terms + for (uint32_t i = 0; i < pp->len; ++ i) { + lp_variable_t lp_var = lp_data_get_lp_variable_from_term(lp_data, pp->prod[i].var); + lp_monomial_push(&lp_monomial, lp_var, pp->prod[i].exp); + } + + lp_polynomial_t* result = lp_polynomial_new(lp_data->lp_ctx); + lp_polynomial_add_monomial(result, &lp_monomial); + + if (c) { + lp_integer_assign_int(K, c, 1); + } + + lp_monomial_destruct(&lp_monomial); + + return result; +} + +static +lp_polynomial_t* lp_polynomial_from_polynomial_integer(lp_data_t* lp_data, polynomial_t* p, term_table_t* terms, lp_integer_t* c) { + const lp_int_ring_t* K = lp_data->lp_ctx->K; + + uint32_t i, j; + lp_variable_t lp_var; + + lp_polynomial_t *result = lp_data_new_polynomial(lp_data); + + lp_integer_t a; + lp_integer_construct(&a); + + lp_monomial_t lp_monomial; + lp_monomial_construct(lp_data->lp_ctx, &lp_monomial); + + // Add up all the monomials + for (i = 0; i < p->nterms; ++ i) { + + term_t product = p->mono[i].var; + lp_monomial_clear(lp_data->lp_ctx, &lp_monomial); + + assert(q_is_integer(&p->mono[i].coeff)); + lp_integer_assign_yices_rational(&a, NULL, &p->mono[i].coeff); + lp_monomial_set_coefficient(lp_data->lp_ctx, &lp_monomial, &a); + + if (product == const_idx) { + // Constant polynomial, nothing to do + } else if (term_kind(terms, product) == POWER_PRODUCT) { + // Add all the variables + pprod_t* pprod = pprod_for_term(terms, product); + for (j = 0; j < pprod->len; ++j) { + lp_var = lp_data_get_lp_variable_from_term(lp_data, pprod->prod[j].var); + lp_monomial_push(&lp_monomial, lp_var, pprod->prod[j].exp); + } + } else { + // Variable, or foreign term + lp_var = lp_data_get_lp_variable_from_term(lp_data, product); + lp_monomial_push(&lp_monomial, lp_var, 1); + } + + // Add the monomial to the polynomial + lp_polynomial_add_monomial(result, &lp_monomial); + } + + // Remove temps + lp_monomial_destruct(&lp_monomial); + lp_integer_destruct(&a); + + if (c) { + lp_integer_assign_int(K, c, 1); + } + + return result; +} + +/** + * Create a libpoly polynomial from a yices polynomial. Returns the polynomial + * lp_p and a positive integer constant c, such that lp_p = p * c. If c is NULL + * it is ignored. + */ +static +lp_polynomial_t* lp_polynomial_from_polynomial_rational(lp_data_t* lp_data, polynomial_t* p, term_table_t* terms, lp_integer_t* c) { + assert(lp_data->lp_ctx->K == lp_Z); + + uint32_t i, j; + lp_variable_t lp_var; + + lp_polynomial_t *result = lp_data_new_polynomial(lp_data); + + // + // we have + // q_1 + q_2*p_2 + ... + q_n p_n + // + // with q rationals, and p power products + // + // we get the lcm of the denominators first, and multiply it out + // + + // Integers to represent rationals + lp_integer_t a, b; + lp_integer_construct(&a); + lp_integer_construct(&b); + + // Compute the lcm + lp_integer_t lcm; + lp_integer_construct_from_int(lp_Z, &lcm, 1); + for (i = 0; i < p->nterms; ++ i) { + lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); + lp_integer_lcm_Z(&lcm, &lcm, &b); + } + + // Assign to c + if (c) { + lp_integer_assign(lp_Z, c, &lcm); + } + + // The monomials + lp_monomial_t lp_monomial; + lp_monomial_construct(lp_data->lp_ctx, &lp_monomial); + + // Add up all the monomials + for (i = 0; i < p->nterms; ++ i) { + + term_t product = p->mono[i].var; + lp_monomial_clear(lp_data->lp_ctx, &lp_monomial); + + // The constant (a/b)*lcm + lp_integer_assign_yices_rational(&a, &b, &p->mono[i].coeff); + lp_integer_div_exact(lp_Z, &b, &lcm, &b); + lp_integer_mul(lp_Z, &a, &a, &b); + lp_monomial_set_coefficient(lp_data->lp_ctx, &lp_monomial, &a); + + if (product == const_idx) { + // Constant polynomial, nothing to do + } else if (term_kind(terms, product) == POWER_PRODUCT) { + // Add all the variables + pprod_t* pprod = pprod_for_term(terms, product); + for (j = 0; j < pprod->len; ++j) { + lp_var = lp_data_get_lp_variable_from_term(lp_data, pprod->prod[j].var); + lp_monomial_push(&lp_monomial, lp_var, pprod->prod[j].exp); + } + } else { + // Variable, or foreign term + lp_var = lp_data_get_lp_variable_from_term(lp_data, product); + lp_monomial_push(&lp_monomial, lp_var, 1); + } + + // Add the monomial to the polynomial + lp_polynomial_add_monomial(result, &lp_monomial); + } + + // Remove temps + lp_monomial_destruct(&lp_monomial); + lp_integer_destruct(&a); + lp_integer_destruct(&b); + lp_integer_destruct(&lcm); + + return result; +} + +static +lp_polynomial_t* lp_polynomial_from_constant_rational(lp_data_t* lp_data, rational_t* q, lp_integer_t* c) { + // Get the constant numerator and denominator + lp_integer_t lp_p; + lp_integer_construct_from_int(lp_data->lp_ctx->K, &lp_p, 0); + lp_integer_assign_yices_rational(&lp_p, c, q); + // polynomial a*x^0 + lp_polynomial_t* result = lp_polynomial_alloc(); + lp_polynomial_construct_simple(result, lp_data->lp_ctx, &lp_p, 0, 0); + // Remove temp + lp_integer_destruct(&lp_p); + + return result; +} + +static +lp_polynomial_t* lp_polynomial_from_constant_integer(lp_data_t* lp_data, rational_t *q, lp_integer_t* c) { + const lp_int_ring_t *K = lp_data->lp_ctx->K; + + // Get the constant numerator and denominator + lp_integer_t lp_p; + lp_integer_construct_from_int(K, &lp_p, 0); + assert(q_is_integer(q)); + lp_integer_assign_yices_rational(&lp_p, NULL, q); + // polynomial a*x^0 + lp_polynomial_t* result = lp_polynomial_alloc(); + lp_polynomial_construct_simple(result, lp_data->lp_ctx, &lp_p, 0, 0); + // Remove temp + lp_integer_destruct(&lp_p); + + if (c) { + lp_integer_assign_int(K, c, 1); + } + + return result; +} + +static +lp_polynomial_t* lp_polynomial_from_variable(lp_data_t *lp_data, lp_variable_t lp_var, lp_integer_t *c) { + const lp_int_ring_t *K = lp_data->lp_ctx->K; + + // Constant 1 + lp_integer_t one; + lp_integer_construct_from_int(K, &one, 1); + // Polynomial 1*x^1 + lp_polynomial_t* result = lp_polynomial_alloc(); + lp_polynomial_construct_simple(result, lp_data->lp_ctx, &one, lp_var, 1); + // Put 1 if requested + if (c != NULL) { + lp_integer_assign(lp_Z, c, &one); + } + // Remove temp + lp_integer_destruct(&one); + + return result; +} + +lp_polynomial_t* lp_polynomial_from_term(lp_data_t* lp_data, term_t t, term_table_t* terms, lp_integer_t* c) { + const lp_int_ring_t *K = lp_data->lp_ctx->K; + (void)K; + + switch (term_kind(terms, t)) { + case ARITH_POLY: + assert(K == lp_Z); + return lp_polynomial_from_polynomial_rational(lp_data, poly_term_desc(terms, t), terms, c); + case ARITH_FF_POLY: + assert(K != lp_Z); + return lp_polynomial_from_polynomial_integer(lp_data, finitefield_poly_term_desc(terms, t), terms, c); + case ARITH_CONSTANT: + assert(K == lp_Z); + return lp_polynomial_from_constant_rational(lp_data, rational_term_desc(terms, t), c); + case ARITH_FF_CONSTANT: + assert(K != lp_Z); + return lp_polynomial_from_constant_integer(lp_data, finitefield_term_desc(terms, t), c); + case POWER_PRODUCT: + return lp_polynomial_from_power_product(lp_data, pprod_term_desc(terms, t), c); + default: + return lp_polynomial_from_variable(lp_data, lp_data_get_lp_variable_from_term(lp_data, t), c); + } +} + +typedef struct { + const lp_data_t* lp_data; + rba_buffer_t* b; + term_table_t* terms; +} lp_polynomial_to_yices_term_data; + +static +void lp_polynomial_to_yices_traverse_f(const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* void_data) { + + lp_polynomial_to_yices_term_data* data = (lp_polynomial_to_yices_term_data*) void_data; + + // Constant + rational_t a; + q_init(&a); + q_set_mpz(&a, &m->a); + + if (m->n == 0) { + // Just constant + rba_buffer_add_const(data->b, &a); + } else { + // Actual monomial + pp_buffer_t pp; + init_pp_buffer(&pp, 0); + for (uint32_t i = 0; i < m->n; ++ i) { + lp_variable_t lp_x = m->p[i].x; + term_t x_term = lp_data_get_term_from_lp_variable(data->lp_data, lp_x); + pp_buffer_mul_varexp(&pp, x_term, m->p[i].d); + } + pprod_t* pprod = pprod_from_buffer(data->terms->pprods, &pp); + term_t pp_term = pp_is_var(pprod) ? var_of_pp(pprod) : pprod_term(data->terms, pprod); + rba_buffer_add_const_times_term(data->b, data->terms, &a, pp_term); + delete_pp_buffer(&pp); + } + + q_clear(&a); +} + +static +void lp_polynomial_to_yices_term(const lp_data_t *lp_data, const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b) { + + // Buffer for building + lp_polynomial_to_yices_term_data data; + data.lp_data = lp_data; + data.b = b; + data.terms = terms; + + reset_rba_buffer(data.b); + + // Traverse and build + lp_polynomial_traverse(lp_p, lp_polynomial_to_yices_traverse_f, &data); +} + +term_t lp_polynomial_to_yices_arith_term(const lp_data_t *lp_data, const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b) { + lp_polynomial_to_yices_term(lp_data, lp_p, terms, b); + + // Make the term + return mk_direct_arith_term(terms, b); +} + +term_t lp_polynomial_to_yices_arith_ff_term(const lp_data_t *lp_data, const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b) { + lp_polynomial_to_yices_term(lp_data, lp_p, terms, b); + + // assure it's a finite field lp_data + assert(lp_data_get_ring(lp_data)); + + rational_t mod; + q_init(&mod); + q_set_mpz(&mod, &lp_data_get_ring(lp_data)->M); + + // Make the term + term_t result = mk_direct_arith_ff_term(terms, b, &mod); + q_clear(&mod); + + return result; +} + +const mcsat_value_t* ensure_lp_value(const mcsat_value_t* value, mcsat_value_t* alternative) { + lp_value_t lp_value; + lp_rational_t rat_value; + switch (value->type) { + case VALUE_LIBPOLY: + return value; + case VALUE_RATIONAL: + lp_rational_construct(&rat_value); + q_get_mpq((rational_t*)&value->q, &rat_value); + lp_value_construct(&lp_value, LP_VALUE_RATIONAL, &rat_value); + mcsat_value_construct_lp_value(alternative, &lp_value); + lp_value_destruct(&lp_value); + lp_rational_destruct(&rat_value); + return alternative; + default: + assert(false); + } + return NULL; +} diff --git a/src/mcsat/utils/lp_utils.h b/src/mcsat/utils/lp_utils.h new file mode 100644 index 000000000..cd992f9f0 --- /dev/null +++ b/src/mcsat/utils/lp_utils.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Yices SMT Solver. + * Copyright (C) 2017 SRI International. + * + * Yices is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Yices is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Yices. If not, see . + */ + +#ifndef LP_UTILS_H_ +#define LP_UTILS_H_ + +#include + +#include "mcsat/utils/lp_data.h" + +/** + * Create a libpoly polynomial from a yices term. Returns the polynomial + * lp_p and a positive integer constant c, such that lp_p = p * c. If c is + * NULL it is ignored. + */ +lp_polynomial_t* lp_polynomial_from_term(lp_data_t* lp_data, term_t t, term_table_t* terms, lp_integer_t* c); + +/** + * Get yices arith term from polynomial (direct version). + */ +term_t lp_polynomial_to_yices_arith_term(const lp_data_t *lp_data, const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b); + +/** + * Get yices finite field term from polynomial (direct version). + */ +term_t lp_polynomial_to_yices_arith_ff_term(const lp_data_t *lp_data, const lp_polynomial_t* lp_p, term_table_t* terms, rba_buffer_t* b); + +/** + * Ensure value is an lp_value. If not the passed alternative will be constructed to an equivalent lp_value. + */ +const mcsat_value_t* ensure_lp_value(const mcsat_value_t* value, mcsat_value_t* alternative); + +#endif /* LP_UTILS_H_ */ diff --git a/src/mcsat/utils/substitution.c b/src/mcsat/utils/substitution.c index 71dd45c2a..12070d5c4 100644 --- a/src/mcsat/utils/substitution.c +++ b/src/mcsat/utils/substitution.c @@ -104,6 +104,10 @@ term_t mk_composite(term_manager_t* tm, term_kind_t kind, uint32_t n, term_t* c) assert(n == 2); result = mk_arith_eq(tm, c[0], c[1]); break; + case ARITH_FF_BINEQ_ATOM: + assert(n == 2); + result = mk_arith_ff_eq(tm, c[0], c[1]); + break; default: assert(false); } @@ -196,6 +200,7 @@ term_t substitution_run_core(substitution_t* subst, term_t t, int_hmap_t* cache, case BV64_CONSTANT: // compact bitvector constant (64 bits at most) case BV_CONSTANT: // generic bitvector constant (more than 64 bits) case ARITH_CONSTANT: // arithmetic constants + case ARITH_FF_CONSTANT: // finite field arithmetic constants current_subst = current; break; @@ -223,6 +228,7 @@ term_t substitution_run_core(substitution_t* subst, term_t t, int_hmap_t* cache, case BV_GE_ATOM: case BV_SGE_ATOM: case ARITH_BINEQ_ATOM: + case ARITH_FF_BINEQ_ATOM: { composite_term_t* desc = composite_term_desc(terms, current); n = desc->arity; @@ -349,6 +355,60 @@ term_t substitution_run_core(substitution_t* subst, term_t t, int_hmap_t* cache, break; } + case ARITH_FF_EQ_ATOM: { + term_t arg = arith_ff_eq_arg(terms, current); + find = int_hmap_find(cache, arg); + if (find == NULL) { + ivector_push(&substitution_stack, arg); + } else { + if (find->val == arg) { + current_subst = current; + } else { + current_subst = arith_ff_eq_atom(terms, find->val); + } + } + break; + } + + case ARITH_FF_POLY: { + polynomial_t* p = finitefield_poly_term_desc(terms, current); + const rational_t* mod = finitefield_term_order(terms, current); + n = p->nterms; + + bool children_done = true; + bool children_same = true; + + ivector_t children; + init_ivector(&children, n); + for (i = 0; i < n; ++ i) { + term_t x = p->mono[i].var; + if (x != const_idx) { + find = int_hmap_find(cache, x); + if (find == NULL) { + children_done = false; + ivector_push(&substitution_stack, x); + } else if (find->val != x) { + children_same = false; + } + if (children_done) { ivector_push(&children, find->val); } + } else { + if (children_done) { ivector_push(&children, const_idx); } + } + } + + if (children_done) { + if (children_same) { + current_subst = current; + } else { + current_subst = mk_arith_ff_poly(tm, p, n, children.data, mod); + } + } + + delete_ivector(&children); + + break; + } + case BIT_TERM: // bit-select current = child[i] { uint32_t index = bit_term_index(terms, current); diff --git a/src/mcsat/utils/value_hash_map.c b/src/mcsat/utils/value_hash_map.c index e51222430..0a015a6c5 100644 --- a/src/mcsat/utils/value_hash_map.c +++ b/src/mcsat/utils/value_hash_map.c @@ -374,12 +374,12 @@ void value_hmap_reset(value_hmap_t *hmap) { /* * First non-empty record in the table, starting from p */ -static value_hmap_pair_t *value_hmap_get_next(value_hmap_t *hmap, value_hmap_pair_t *p) { +static value_hmap_pair_t *value_hmap_get_next(const value_hmap_t *hmap, value_hmap_pair_t *p) { value_hmap_pair_t *end; end = hmap->data + hmap->size; while (p < end) { - if (p->key != VALUE_HMAP_EMPTY_KEY) return p; + if (value_hmap_valid_key(p->key)) return p; p ++; } @@ -390,7 +390,7 @@ static value_hmap_pair_t *value_hmap_get_next(value_hmap_t *hmap, value_hmap_pai /* * Get the first non-empty record or NULL if the table is empty */ -value_hmap_pair_t *value_hmap_first_record(value_hmap_t *hmap) { +value_hmap_pair_t *value_hmap_first_record(const value_hmap_t *hmap) { return value_hmap_get_next(hmap, hmap->data); } @@ -398,13 +398,12 @@ value_hmap_pair_t *value_hmap_first_record(value_hmap_t *hmap) { /* * Next record after p or NULL */ -value_hmap_pair_t *value_hmap_next_record(value_hmap_t *hmap, value_hmap_pair_t *p) { +value_hmap_pair_t *value_hmap_next_record(const value_hmap_t *hmap, value_hmap_pair_t *p) { assert(p != NULL && pdata + hmap->size && p->key != VALUE_HMAP_EMPTY_KEY); return value_hmap_get_next(hmap, p+1); } - /* * Remove the records that satisfy filter f * - calls f(aux, p) on every record p stored in hmap @@ -437,6 +436,25 @@ void value_hmap_remove_records(value_hmap_t *hmap, void *aux, value_hmap_filter_ } +/* + * Updates the value of the records + * - calls f(aux, p) on every record p stored in hmap + * - p->val is set according to the return value of f(aux, p) + */ +void value_hmap_update_records(value_hmap_t *hmap, void *aux, value_hmap_map_t f) { + value_hmap_pair_t *d; + uint32_t i, n; + + n = hmap->size; + d = hmap->data; + for (i=0; ikey)) { + d->val = (int32_t) f(aux, d); + } + d ++; + } +} + /* * Iterator: call f(aux, p) on every record p @@ -448,7 +466,7 @@ void value_hmap_iterate(value_hmap_t *hmap, void *aux, value_hmap_iterator_t f) n = hmap->size; d = hmap->data; for (i=0; ikey != VALUE_HMAP_DELETED_KEY && d->key != VALUE_HMAP_EMPTY_KEY) { + if (value_hmap_valid_key(d->key)) { f(aux, d); } d ++; diff --git a/src/mcsat/utils/value_hash_map.h b/src/mcsat/utils/value_hash_map.h index fa90e72cc..1fcbdcfe1 100644 --- a/src/mcsat/utils/value_hash_map.h +++ b/src/mcsat/utils/value_hash_map.h @@ -109,7 +109,6 @@ void value_hmap_erase(value_hmap_t *hmap, value_hmap_pair_t *r); void value_hmap_reset(value_hmap_t *hmap); - /* * Remove all records that satisfy f * - calls f(aux, p) on every record p stored in hmap @@ -120,6 +119,16 @@ typedef bool (*value_hmap_filter_t)(void *aux, const value_hmap_pair_t *p); void value_hmap_remove_records(value_hmap_t *hmap, void *aux, value_hmap_filter_t f); +/* + * Updates the value of the records + * - calls f(aux, p) on every record p stored in hmap + * - p->val is set according to the return value of f(aux, p) + */ +typedef uint32_t (*value_hmap_map_t)(void *aux, const value_hmap_pair_t *p); + +void value_hmap_update_records(value_hmap_t *hmap, void *aux, value_hmap_map_t f); + + /* * Iterator: call f(aux, p) on every record p stored in hmap * - f must not have any side effect on the hmap @@ -129,13 +138,11 @@ typedef void (*value_hmap_iterator_t)(void *aux, const value_hmap_pair_t *p); void value_hmap_iterate(value_hmap_t *hmap, void *aux, value_hmap_iterator_t f); - - /* * Support for scanning all records: * - first gives the first non-null record in the table or NULL * - next(p) gives the next record after p or NULL - * IMPORTANT: The hmap must not be modified between calls to next + * IMPORTANT: The hmap keys must not be modified between calls to next */ -value_hmap_pair_t *value_hmap_first_record(value_hmap_t *hmap); -value_hmap_pair_t *value_hmap_next_record(value_hmap_t *hmap, value_hmap_pair_t *p); +value_hmap_pair_t *value_hmap_first_record(const value_hmap_t *hmap); +value_hmap_pair_t *value_hmap_next_record(const value_hmap_t *hmap, value_hmap_pair_t *p); diff --git a/src/mcsat/value.c b/src/mcsat/value.c index 25b1a440a..c9e68ec12 100644 --- a/src/mcsat/value.c +++ b/src/mcsat/value.c @@ -404,12 +404,17 @@ value_t mcsat_value_to_value(const mcsat_value_t* mcsat_value, type_table_t *typ (void) ok; // unused in release build assert(ok); value = vtbl_mk_const(vtbl, type, id, NULL); - } else { + } else if (kind == REAL_TYPE || kind == INT_TYPE) { value = vtbl_mk_rational(vtbl, &q); + } else if (kind == FF_TYPE) { + value = vtbl_mk_finitefield(vtbl, &q, ff_type_size(types, type)); + } else { + assert(false); } q_clear(&q); lp_rational_destruct(&lp_q); } else { + assert(is_real_type(type)); value = vtbl_mk_algebraic(vtbl, (void*) &mcsat_value->lp_value.value.a); } break; @@ -448,6 +453,18 @@ void mcsat_value_construct_from_value(mcsat_value_t* mcsat_value, value_table_t* lp_value_destruct(&value_lp); break; } + case FINITEFIELD_VALUE: { + value_ff_t* value_ff = vtbl_finitefield(vtbl, v); + mpz_t value_mpz; + mpz_init(value_mpz); + q_get_mpz(&value_ff->value, value_mpz); + lp_value_t value_lp; + lp_value_construct(&value_lp, LP_VALUE_INTEGER, value_mpz); + mcsat_value_construct_lp_value(mcsat_value, &value_lp); + lp_value_destruct(&value_lp); + mpz_clear(value_mpz); + break; + } case BITVECTOR_VALUE: { value_bv_t* v1 = vtbl_bitvector(vtbl, v); bvconstant_t v2; diff --git a/src/mcsat/value.h b/src/mcsat/value.h index 8417625a3..a4623c35f 100644 --- a/src/mcsat/value.h +++ b/src/mcsat/value.h @@ -22,6 +22,8 @@ #include #include +#include "mcsat_types.h" + #include "terms/terms.h" #include "terms/term_manager.h" #include "terms/rationals.h" @@ -41,7 +43,7 @@ typedef enum { VALUE_BV } mcsat_value_type_t; -typedef struct value_s { +struct mcsat_value_s { mcsat_value_type_t type; union { bool b; @@ -49,7 +51,7 @@ typedef struct value_s { lp_value_t lp_value; bvconstant_t bv_value; }; -} mcsat_value_t; +}; /** Predefined none value for convenience */ extern const mcsat_value_t mcsat_value_none; diff --git a/src/mcsat/variable_db.c b/src/mcsat/variable_db.c index 61c7303cc..3e37b835a 100644 --- a/src/mcsat/variable_db.c +++ b/src/mcsat/variable_db.c @@ -130,12 +130,6 @@ variable_t variable_db_get_variable_if_exists(const variable_db_t* var_db, term_ } } -term_t variable_db_get_term(const variable_db_t* var_db, variable_t x) { - assert(x > 0 && x < var_db->variable_to_term_map.size); - return var_db->variable_to_term_map.data[x]; -} - - void variable_db_add_new_variable_listener(variable_db_t* var_db, variable_db_new_variable_notify_t* listener) { pvector_push(&var_db->notify_new_variable, listener); } @@ -199,6 +193,10 @@ bool variable_db_is_bitvector(const variable_db_t* var_db, variable_t x) { return term_type_kind(var_db->terms, variable_db_get_term(var_db, x)) == BITVECTOR_TYPE; } +bool variable_db_is_finitefield(const variable_db_t* var_db, variable_t x) { + return term_type_kind(var_db->terms, variable_db_get_term(var_db, x)) == FF_TYPE; +} + uint32_t variable_db_get_bitsize(const variable_db_t* var_db, variable_t x) { assert(variable_db_is_bitvector(var_db, x)); return term_bitsize(var_db->terms, variable_db_get_term(var_db, x)); diff --git a/src/mcsat/variable_db.h b/src/mcsat/variable_db.h index d1b1f44da..6c3a470d6 100644 --- a/src/mcsat/variable_db.h +++ b/src/mcsat/variable_db.h @@ -87,8 +87,17 @@ variable_t variable_db_get_variable_if_exists(const variable_db_t* var_db, term_ /** * Returns the term associated with the variable. The variable should exist. */ -term_t variable_db_get_term(const variable_db_t* var_db, variable_t term); - +static inline +term_t variable_db_get_term(const variable_db_t* var_db, variable_t x) { + assert(x > 0 && x < var_db->variable_to_term_map.size); + return var_db->variable_to_term_map.data[x]; +} + +static inline +type_t variable_db_get_type(const variable_db_t* var_db, variable_t x) { + term_t t = variable_db_get_term(var_db, x); + return term_type(var_db->terms, t); +} typedef struct variable_db_new_variable_notify_s { void (*new_variable) (struct variable_db_new_variable_notify_s* self, variable_t x); @@ -112,6 +121,9 @@ bool variable_db_is_real(const variable_db_t* var_db, variable_t x); /** Returns true if the type of the variable is bitvector */ bool variable_db_is_bitvector(const variable_db_t* var_db, variable_t x); +/** Returns true if the type of the variable is finite field */ +bool variable_db_is_finitefield(const variable_db_t* var_db, variable_t x); + /** Get the bitsize of a bit-vector variable */ uint32_t variable_db_get_bitsize(const variable_db_t* var_db, variable_t x); diff --git a/src/mcsat/watch_list_manager.c b/src/mcsat/watch_list_manager.c index 9b3fedbdf..36e0dd558 100644 --- a/src/mcsat/watch_list_manager.c +++ b/src/mcsat/watch_list_manager.c @@ -53,6 +53,7 @@ ivector_t* watch_list_manager_get_list_of_lists(watch_list_manager_t* wlm, varia return wlm->wlist_memory.data[watcher]; } } + variable_list_ref_t watch_list_manager_new_list(watch_list_manager_t* wlm, const variable_t* list, uint32_t size, variable_t constraint) { uint32_t i; variable_list_ref_t ref; @@ -276,7 +277,7 @@ void watch_list_manager_print(watch_list_manager_t* wlm, FILE* out) { variable_list_ref_t list_ref = list_of_lists->data[i]; variable_t* list = watch_list_manager_get_list(wlm, list_ref); while (*list != variable_null) { - variable_db_print_variable(wlm->var_db, x, out); + variable_db_print_variable(wlm->var_db, *list, out); fprintf(out, " "); list ++; } diff --git a/src/model/concrete_values.c b/src/model/concrete_values.c index f4ce1c3a3..01d172d6f 100644 --- a/src/model/concrete_values.c +++ b/src/model/concrete_values.c @@ -758,6 +758,10 @@ static void vtbl_delete_descriptors(value_table_t *table, uint32_t k) { case FUNCTION_VALUE: delete_value_fun(table->desc[i].ptr); break; + case FINITEFIELD_VALUE: + q_clear(&((value_ff_t*)table->desc[i].ptr)->value); + q_clear(&((value_ff_t*)table->desc[i].ptr)->mod); + /* fall through */ case BITVECTOR_VALUE: case TUPLE_VALUE: case MAP_VALUE: @@ -1287,6 +1291,13 @@ typedef struct { void *a; } algebraic_hobj_t; +typedef struct { + int_hobj_t m; + value_table_t *table; + rational_t *v; + rational_t *mod; +} ff_hobj_t; + typedef struct { int_hobj_t m; value_table_t *table; @@ -1364,6 +1375,11 @@ static uint32_t hash_rational_value(rational_hobj_t *o) { return jenkins_hash_mix2(h_num, h_den); } +static uint32_t hash_finitefield_value(ff_hobj_t *o) { + assert(q_is_integer(o->v)); + return jenkins_hash_mix2(q_hash_numerator(o->v), q_hash_numerator(o->mod)); +} + static uint32_t hash_algebraic_value(algebraic_hobj_t *a) { #ifdef HAVE_MCSAT return lp_algebraic_number_hash_approx(a->a, 5); @@ -1419,6 +1435,18 @@ static bool equal_rational_value(rational_hobj_t *o, value_t i) { return table->kind[i] == RATIONAL_VALUE && q_eq(&table->desc[i].rational, o->v); } +static bool equal_finitefield_value(ff_hobj_t *o, value_t i) { + value_table_t *table; + value_ff_t *v_ff; + + table = o->table; + if (table->kind[i] != FINITEFIELD_VALUE) { + return false; + } + v_ff = table->desc[i].ptr; + return q_eq(&v_ff->value, o->v) && q_eq(&v_ff->mod, o->mod); +} + static bool equal_algebraic_value(algebraic_hobj_t *o, value_t i) { #ifdef HAVE_MCSAT value_table_t *table; @@ -1558,6 +1586,7 @@ static value_t build_rational_value(rational_hobj_t *o) { return i; } + static value_t build_algebraic_value(algebraic_hobj_t *o) { #ifdef HAVE_MCSAT value_table_t *table; @@ -1597,6 +1626,28 @@ static value_t build_const_value(const_hobj_t *o) { return i; } + +static value_t build_finitefield_value(ff_hobj_t *o) { + value_table_t *table; + value_ff_t *v_ff; + value_t i; + + v_ff = (value_ff_t *) safe_malloc(sizeof(value_bv_t)); + q_init(&v_ff->value); + q_init(&v_ff->mod); + q_set(&v_ff->value, o->v); + q_set(&v_ff->mod, o->mod); + + table = o->table; + i = allocate_object(table); + table->kind[i] = FINITEFIELD_VALUE; + table->desc[i].ptr = v_ff; + set_bit(table->canonical, i); + + return i; +} + + static value_t build_bv_value(bv_hobj_t *o) { value_table_t *table; value_bv_t *d; @@ -1788,6 +1839,46 @@ value_t vtbl_mk_int32(value_table_t *table, int32_t i) { return k; } +/* + * Return a finitefield constant = v + */ +value_t vtbl_mk_finitefield(value_table_t *table, rational_t *v, const rational_t *mod) { + ff_hobj_t ff_hobj; + ff_hobj.m.hash = (hobj_hash_t) hash_finitefield_value; + ff_hobj.m.eq = (hobj_eq_t) equal_finitefield_value; + ff_hobj.m.build = (hobj_build_t) build_finitefield_value; + ff_hobj.table = table; + ff_hobj.v = v; + ff_hobj.mod = (rational_t*)mod; + + return int_htbl_get_obj(&table->htbl, (int_hobj_t *) &ff_hobj); +} + +/* + * Return a finitefield constant = v + * no check for field size is performed + */ +static value_t vtbl_mk_int32_ff(value_table_t *table, int32_t v, const rational_t *mod) { + rational_t aux; + value_t k; + + q_init(&aux); + q_set32(&aux, v); + + ff_hobj_t ff_hobj; + ff_hobj.m.hash = (hobj_hash_t) hash_finitefield_value; + ff_hobj.m.eq = (hobj_eq_t) equal_finitefield_value; + ff_hobj.m.build = (hobj_build_t) build_finitefield_value; + ff_hobj.table = table; + ff_hobj.v = &aux; + ff_hobj.mod = (rational_t*)mod; + + k = int_htbl_get_obj(&table->htbl, (int_hobj_t *) &ff_hobj); + q_clear(&aux); + + return k; +} + /* * Copy of the algebraic number @@ -2266,6 +2357,10 @@ value_t vtbl_make_object(value_table_t *vtbl, type_t tau) { v = vtbl_mk_int32(vtbl, 0); break; + case FF_TYPE: + v = vtbl_mk_int32_ff(vtbl, 0, ff_type_size(types, tau)); + break; + case BITVECTOR_TYPE: v = vtbl_mk_bv_zero(vtbl, bv_type_size(types, tau)); break; @@ -2370,6 +2465,11 @@ bool vtbl_make_two_objects(value_table_t *vtbl, type_t tau, value_t a[2]) { a[1] = vtbl_mk_int32(vtbl, 1); break; + case FF_TYPE: + a[0] = vtbl_mk_int32_ff(vtbl, 0, ff_type_size(types, tau)); + a[1] = vtbl_mk_int32_ff(vtbl, 1, ff_type_size(types, tau)); + break; + case BITVECTOR_TYPE: a[0] = vtbl_mk_bv_zero(vtbl, bv_type_size(types, tau)); a[1] = vtbl_mk_bv_one(vtbl, bv_type_size(types, tau)); diff --git a/src/model/concrete_values.h b/src/model/concrete_values.h index d45acd281..6e3f45ec2 100644 --- a/src/model/concrete_values.h +++ b/src/model/concrete_values.h @@ -110,6 +110,7 @@ typedef enum { BOOLEAN_VALUE, RATIONAL_VALUE, ALGEBRAIC_VALUE, + FINITEFIELD_VALUE, BITVECTOR_VALUE, TUPLE_VALUE, UNINTERPRETED_VALUE, @@ -135,6 +136,12 @@ typedef union value_desc_u { /* * Descriptors: encode the actual value */ +// finite field constant +typedef struct value_ff_s { + rational_t value; + rational_t mod; +} value_ff_t; + // bitvector constant typedef struct value_bv_s { uint32_t nbits; @@ -418,6 +425,10 @@ extern value_t vtbl_mk_not(value_table_t *table, value_t v); extern value_t vtbl_mk_rational(value_table_t *table, rational_t *v); extern value_t vtbl_mk_int32(value_table_t *table, int32_t x); +/* + * Finite field constants (make a copy). + */ +extern value_t vtbl_mk_finitefield(value_table_t *table, rational_t *v, const rational_t *mod); /* * Algebraic number (make a copy). @@ -837,6 +848,10 @@ static inline bool object_is_rational(value_table_t *table, value_t v) { return object_kind(table, v) == RATIONAL_VALUE; } +static inline bool object_is_finitefield(value_table_t *table, value_t v) { + return object_kind(table, v) == FINITEFIELD_VALUE; +} + static inline bool object_is_algebraic(value_table_t *table, value_t v) { return object_kind(table, v) == ALGEBRAIC_VALUE; } @@ -912,6 +927,15 @@ static inline void *vtbl_algebraic_number(value_table_t *table, value_t v) { return table->desc[v].ptr; } +static inline value_ff_t *vtbl_finitefield(value_table_t *table, value_t v) { + assert(object_is_finitefield(table, v)); + value_ff_t *v_ff = (value_ff_t*)table->desc[v].ptr; + assert(q_is_integer(&v_ff->value) && q_is_integer(&v_ff->mod) + && q_is_pos(&v_ff->mod) && q_is_nonneg(&v_ff->value) + && q_lt(&v_ff->value, &v_ff->mod)); + return v_ff; +} + static inline value_bv_t *vtbl_bitvector(value_table_t *table, value_t v) { assert(object_is_bitvector(table, v)); return (value_bv_t *) table->desc[v].ptr; diff --git a/src/model/model_eval.c b/src/model/model_eval.c index 13f67abcb..58dee0594 100644 --- a/src/model/model_eval.c +++ b/src/model/model_eval.c @@ -446,6 +446,20 @@ static value_t eval_arith_bineq(evaluator_t *eval, composite_term_t *eq) { return vtbl_mk_bool(eval->vtbl, result); } +static value_t eval_finitefield_bineq(evaluator_t *eval, composite_term_t *eq) { + value_t v1, v2; + + assert(eq->arity == 2); + + v1 = eval_term(eval, eq->arg[0]); + v2 = eval_term(eval, eq->arg[1]); + + assert(object_is_finitefield(eval->vtbl, v1)); + assert(object_is_finitefield(eval->vtbl, v2)); + + return vtbl_mk_bool(eval->vtbl, v1 == v2); +} + /* * Compute division when one of the arguments is algebraic and return the result. */ @@ -694,20 +708,32 @@ static value_t eval_arith_pprod_algebraic(evaluator_t *eval, pprod_t *p) { } #endif +/* + * Finite field atom: t == 0 + */ +static value_t eval_arith_ff_eq(evaluator_t *eval, term_t t) { + value_t v; + + v = eval_term(eval, t); + assert(object_is_finitefield(eval->vtbl, v)); + + value_ff_t *v_ff = vtbl_finitefield(eval->vtbl, v); + return vtbl_mk_bool(eval->vtbl, q_is_zero(&v_ff->value)); +} + /* * Power product: arithmetic */ static value_t eval_arith_pprod(evaluator_t *eval, pprod_t *p) { rational_t prod; - uint32_t i, n; term_t t; value_t o; q_init(&prod); q_set_one(&prod); - n = p->len; - for (i=0; ilen; + for (uint32_t i=0; iprod[i].var; o = eval_term(eval, t); // prod[i] is v ^ k so q := q * (o ^ k) @@ -733,6 +759,38 @@ static value_t eval_arith_pprod(evaluator_t *eval, pprod_t *p) { return o; } +/* + * Power product: finite field arithmetic + */ +static value_t eval_arith_ff_pprod(evaluator_t *eval, pprod_t *p, const rational_t *mod) { + rational_t prod; + term_t t; + value_t o; + + assert(mod && q_is_integer(mod)); + + q_init(&prod); + q_set_one(&prod); + + uint32_t n = p->len; + for (uint32_t i=0; iprod[i].var; + o = eval_term(eval, t); + assert(object_is_finitefield(eval->vtbl, o)); + value_ff_t *v_ff = vtbl_finitefield(eval->vtbl, o); + assert(q_eq(&v_ff->mod, mod)); + // prod[i] is v ^ k so q := q * (o ^ k) + q_mulexp(&prod, &v_ff->value, p->prod[i].exp); + } + + assert(q_is_integer(&prod)); + q_integer_rem(&prod, mod); + o = vtbl_mk_finitefield(eval->vtbl, &prod, mod); + clear_rational(&prod); + + return o; +} + #ifdef HAVE_MCSAT static value_t eval_arith_poly_algebraic(evaluator_t *eval, polynomial_t *p) { lp_algebraic_number_t sum, tmp_c, tmp_t; @@ -817,6 +875,40 @@ static value_t eval_arith_poly(evaluator_t *eval, polynomial_t *p) { return v; } +static value_t eval_arith_ff_poly(evaluator_t *eval, polynomial_t *p, const rational_t *mod) { + rational_t sum; + uint32_t i, n; + term_t t; + value_t v; + value_ff_t *v_ff; + + q_init(&sum); // sum = 0 + + n = p->nterms; + for (i=0; imono[i].var; + if (t == const_idx) { + q_add(&sum, &p->mono[i].coeff); + } else { + v = eval_term(eval, t); + assert(object_is_finitefield(eval->vtbl, v)); + v_ff = vtbl_finitefield(eval->vtbl, v); + assert(q_eq(&v_ff->mod, mod)); + q_addmul(&sum, &p->mono[i].coeff, &v_ff->value); // sum := sum + coeff * aux + } + } + + // convert sum to an object + assert(q_is_integer(&sum)); + assert(q_is_integer(mod)); + q_integer_rem(&sum, mod); + v = vtbl_mk_finitefield(eval->vtbl, &sum, mod); + + clear_rational(&sum); + + return v; +} + /* @@ -1530,6 +1622,10 @@ static value_t eval_uninterpreted(evaluator_t *eval, term_t t) { return v; } +static inline const rational_t* arith_get_mod(term_table_t *table, term_t t) { + type_t tau = term_type(table, t); + return is_finite_type(table->types, tau) ? ff_type_size(table->types, tau) : NULL; +} /* @@ -1574,6 +1670,12 @@ static value_t eval_term(evaluator_t *eval, term_t t) { v = vtbl_mk_rational(eval->vtbl, rational_term_desc(terms, t)); break; + case ARITH_FF_CONSTANT: + assert(arith_get_mod(terms, t) && q_is_pos(arith_get_mod(terms, t))); + assert(q_lt(finitefield_term_desc(terms, t), arith_get_mod(terms, t))); + v = vtbl_mk_finitefield(eval->vtbl, finitefield_term_desc(terms, t), arith_get_mod(terms, t)); + break; + case BV64_CONSTANT: v = eval_bv64_constant(eval, bvconst64_term_desc(terms, t)); break; @@ -1621,9 +1723,13 @@ static value_t eval_term(evaluator_t *eval, term_t t) { break; case ARITH_ROOT_ATOM: - // not supported (but don't crash if we see them) - v = vtbl_mk_unknown(eval->vtbl); - break; + // not supported (but don't crash if we see them) + v = vtbl_mk_unknown(eval->vtbl); + break; + + case ARITH_FF_EQ_ATOM: + v = eval_arith_ff_eq(eval, arith_ff_eq_arg(terms, t)); + break; case ITE_TERM: case ITE_SPECIAL: @@ -1689,6 +1795,10 @@ static value_t eval_term(evaluator_t *eval, term_t t) { v = eval_arith_divides(eval, arith_divides_atom_desc(terms, t)); break; + case ARITH_FF_BINEQ_ATOM: + v = eval_finitefield_bineq(eval, arith_ff_bineq_atom_desc(terms, t)); + break; + case BV_ARRAY: v = eval_bv_array(eval, bvarray_term_desc(terms, t)); break; @@ -1748,9 +1858,13 @@ static value_t eval_term(evaluator_t *eval, term_t t) { case POWER_PRODUCT: if (is_bitvector_term(terms, t)) { v = eval_bv_pprod(eval, pprod_term_desc(terms, t), term_bitsize(terms, t)); - } else { - assert(is_arithmetic_term(terms, t)); + } else if (is_arithmetic_term(terms, t)) { v = eval_arith_pprod(eval, pprod_term_desc(terms, t)); + } else if (is_finitefield_term(terms, t)) { + v = eval_arith_ff_pprod(eval, pprod_term_desc(terms, t), arith_get_mod(terms, t)); + } else { + assert(false); + v = vtbl_mk_unknown(eval->vtbl); } break; @@ -1758,6 +1872,10 @@ static value_t eval_term(evaluator_t *eval, term_t t) { v = eval_arith_poly(eval, poly_term_desc(terms, t)); break; + case ARITH_FF_POLY: + v = eval_arith_ff_poly(eval, finitefield_poly_term_desc(terms, t), arith_get_mod(terms, t)); + break; + case BV64_POLY: v = eval_bv64_poly(eval, bvpoly64_term_desc(terms, t)); break; diff --git a/src/model/presburger.c b/src/model/presburger.c index 0661058dd..4e7a431d9 100644 --- a/src/model/presburger.c +++ b/src/model/presburger.c @@ -608,6 +608,7 @@ static bool trivial_constraint_in_buffer(poly_buffer_t *buffer, presburger_tag_t case PRES_NEG_DIVIDES: q_init(&aux); q_set(&aux, &buffer->mono[0].coeff); + q_normalize(divisor); q_integer_rem(&aux, divisor); r = q_is_zero(&aux); if (tag == PRES_NEG_DIVIDES) { @@ -657,6 +658,7 @@ static bool presburger_good_constraint(presburger_t *pres, presburger_constraint case PRES_POS_DIVIDES: case PRES_NEG_DIVIDES: + q_normalize(&c->divisor); q_integer_rem(&aux, &c->divisor); result = q_is_zero(&aux); if (tag == PRES_NEG_DIVIDES) { @@ -1370,9 +1372,10 @@ static polynomial_t *presburger_solve(presburger_t *pres, term_t y, cooper_t *co * of k = delta if rem(val(y) - val(L), delta) is 0. */ q_sub(&yval, &cooper->glbv); // yval := val(y) - val(L) + q_normalize(&cooper->delta); q_integer_rem(&yval, &cooper->delta); // rem(val(y) - val(L), delta) if (q_is_zero(&yval)) { - q_set(&yval, &cooper->delta); + q_set(&yval, &cooper->delta); } assert(q_is_pos(&yval)); @@ -1394,9 +1397,10 @@ static polynomial_t *presburger_solve(presburger_t *pres, term_t y, cooper_t *co q_init(&tmp); q_set(&tmp, &cooper->lubv); // lubv is val(U) q_sub(&tmp, &yval); // tmp := val(U) - val(y) + q_normalize(&cooper->delta); q_integer_rem(&tmp, &cooper->delta); if (q_is_zero(&tmp)) { - q_set(&tmp, &cooper->delta); + q_set(&tmp, &cooper->delta); } assert(q_is_pos(&tmp)); @@ -1413,6 +1417,7 @@ static polynomial_t *presburger_solve(presburger_t *pres, term_t y, cooper_t *co * We pick the constant polynomial e such that 0 <= e < delta * and (val(y) == e) mod delta. */ + q_normalize(&cooper->delta); q_integer_rem(&yval, &cooper->delta); poly_buffer_add_const(solution, &yval); } diff --git a/src/model/val_to_term.c b/src/model/val_to_term.c index 4e8e605a8..97447e4f8 100644 --- a/src/model/val_to_term.c +++ b/src/model/val_to_term.c @@ -73,6 +73,11 @@ static term_t convert_rational(term_table_t *terms, value_table_t *vtbl, value_t return arith_constant(terms, vtbl_rational(vtbl, v)); } +static term_t convert_finitefield(term_table_t *terms, value_table_t *vtbl, value_t v) { + value_ff_t *v_ff = vtbl_finitefield(vtbl, v); + return arith_ff_constant(terms, &v_ff->value, &v_ff->mod); +} + static term_t convert_bitvector(term_table_t *terms, value_table_t *vtbl, value_t v) { value_bv_t *b; uint64_t x; @@ -219,6 +224,10 @@ term_t convert_simple_value(term_table_t *terms, value_table_t *vtbl, value_t v) t = convert_rational(terms, vtbl, v); break; + case FINITEFIELD_VALUE: + t = convert_finitefield(terms, vtbl, v); + break; + case BITVECTOR_VALUE: t = convert_bitvector(terms, vtbl, v); break; @@ -316,6 +325,8 @@ static const int32_t convert_code[NUM_VALUE_KIND] = { CONVERT_UNKNOWN_VALUE, // UNKNOWN_VALUE 0, // BOOLEAN_VALUE 0, // RATIONAL_VALUE + 0, // ALGEBRAIC_VALUE + 0, // FINITEFIELD_VALUE 0, // BITVECTOR_VALUE 0, // TUPLE_VALUE 0, // UNINTERPRETED_VALUE @@ -451,7 +462,7 @@ uint32_t convert_value_array(term_manager_t *mgr, term_table_t *terms, value_tab t = convert_value(&convert, b[i]); b[i] = t; if (t >= 0) { // no error - s ++; + s++; } } delete_val_converter(&convert); diff --git a/src/parser_utils/term_stack2.c b/src/parser_utils/term_stack2.c index f54389f20..bc2d8909c 100644 --- a/src/parser_utils/term_stack2.c +++ b/src/parser_utils/term_stack2.c @@ -30,30 +30,26 @@ #include #include +#include "parser_utils/term_stack2.h" + #include "api/yices_extensions.h" #include "api/yices_api_lock_free.h" #include "api/yices_globals.h" -#include "parser_utils/term_stack2.h" #include "parser_utils/tstack_internals.h" #include "terms/bv64_constants.h" -#include "terms/bv_constants.h" #include "terms/bvarith64_buffer_terms.h" #include "terms/bvarith_buffer_terms.h" #include "terms/rba_buffer_terms.h" #include "utils/hash_functions.h" -#include "utils/memalloc.h" #include "yices.h" - #ifndef NDEBUG - #include #include #include "io/type_printer.h" #include "io/term_printer.h" - #endif @@ -880,6 +876,10 @@ static void tstack_free_val(tstack_t *stack, stack_elem_t *e) { k = (e->val.bv.bitsize + 31) >> 5; bvconst_free(e->val.bv.data, k); break; + case TAG_FINITEFIELD: + q_clear(&e->val.ff.val); + q_clear(&e->val.ff.mod); + break; case TAG_RATIONAL: q_clear(&e->val.rational); break; @@ -892,6 +892,10 @@ static void tstack_free_val(tstack_t *stack, stack_elem_t *e) { case TAG_ARITH_BUFFER: recycle_abuffer(stack, e->val.arith_buffer); break; + case TAG_ARITH_FF_BUFFER: + recycle_abuffer(stack, e->val.mod_arith_buffer.b); + q_clear(&e->val.mod_arith_buffer.mod); + break; case TAG_BVARITH64_BUFFER: recycle_bva64buffer(stack, e->val.bvarith64_buffer); break; @@ -1061,6 +1065,22 @@ void set_arith_result(tstack_t *stack, rba_buffer_t *b) { e->val.arith_buffer = b; } +// b must be equal to stack->abuffer. We reset stack->abuffer to NULL +void set_arith_ff_result(tstack_t *stack, rba_buffer_t *b, rational_t *mod) { + stack_elem_t *e; + + assert(b == stack->abuffer); + stack->abuffer = NULL; + + assert(q_is_pos(mod)); + + e = stack->elem + (stack->top - 1); + e->tag = TAG_ARITH_FF_BUFFER; + e->val.mod_arith_buffer.b = b; + q_init(&e->val.mod_arith_buffer.mod); + q_set(&e->val.mod_arith_buffer.mod, mod); +} + // b must be stack->bva64buffer void set_bvarith64_result(tstack_t *stack, bvarith64_buffer_t *b) { stack_elem_t *e; @@ -1124,6 +1144,17 @@ void set_bv_result(tstack_t *stack, uint32_t nbits, uint32_t *bv) { e->val.bv.data = bv; } +void set_ff_result(tstack_t *stack, rational_t *r, rational_t *m) { + stack_elem_t *e; + + e = stack->elem + (stack->top - 1); + e->tag = TAG_FINITEFIELD; + q_init(&e->val.ff.val); + q_init(&e->val.ff.mod); + q_set(&e->val.ff.val, r); + q_set(&e->val.ff.mod, m); +} + void set_type_binding_result(tstack_t *stack, type_t tau, char *symbol) { stack_elem_t *e; @@ -1149,16 +1180,12 @@ void set_aval_result(tstack_t *stack, aval_t v) { } - - - - -#if 0 +#ifndef NDEBUG /* * Print element e (for debugging) */ -static void print_elem(tstack_t *stack, stack_elem_t *e) { +void print_elem(tstack_t *stack, stack_elem_t *e) { switch (e->tag) { case TAG_NONE: printf(""); @@ -1181,6 +1208,10 @@ static void print_elem(tstack_t *stack, stack_elem_t *e) { printf("", e->val.string); break; + case TAG_STRING: + printf("", e->val.string); + break; + case TAG_BV64: printf("val.bv64.value, e->val.bv64.bitsize); @@ -1199,6 +1230,14 @@ static void print_elem(tstack_t *stack, stack_elem_t *e) { printf(">"); break; + case TAG_FINITEFIELD: + printf("val.ff.val); + printf(" mod "); + q_print(stdout, &e->val.ff.mod); + printf(">"); + break; + case TAG_TERM: printf("val.term); @@ -1227,6 +1266,14 @@ static void print_elem(tstack_t *stack, stack_elem_t *e) { printf(">"); break; + case TAG_ARITH_FF_BUFFER: + printf("val.mod_arith_buffer.b); + printf(" mod "); + q_print(stdout, &e->val.mod_arith_buffer.mod); + printf(">"); + break; + case TAG_BVARITH64_BUFFER: printf("val.bvarith64_buffer); @@ -1257,6 +1304,8 @@ static void print_elem(tstack_t *stack, stack_elem_t *e) { printf(">"); break; + case TAG_ATTRIBUTE: + // no attribute printing implemented yet default: printf(""); break; @@ -1266,7 +1315,6 @@ static void print_elem(tstack_t *stack, stack_elem_t *e) { #endif - /*************************************** * EVALUATION OF INDIVIDUAL COMMANDS * **************************************/ @@ -1465,6 +1513,10 @@ term_t get_term(tstack_t *stack, stack_elem_t *e) { t = yices_bvconst_term(e->val.bv.bitsize, e->val.bv.data); break; + case TAG_FINITEFIELD: + t = yices_ffconst_term(&e->val.ff.val, &e->val.ff.mod); + break; + case TAG_RATIONAL: t = yices_rational_term(&e->val.rational); break; @@ -1473,6 +1525,10 @@ term_t get_term(tstack_t *stack, stack_elem_t *e) { t = arith_buffer_get_term(e->val.arith_buffer); break; + case TAG_ARITH_FF_BUFFER: + t = arith_ff_buffer_get_term(e->val.mod_arith_buffer.b, &e->val.mod_arith_buffer.mod); + break; + case TAG_BVARITH64_BUFFER: t = bvarith64_buffer_get_term(e->val.bvarith64_buffer); break; @@ -1515,6 +1571,17 @@ int32_t get_integer(tstack_t *stack, stack_elem_t *e) { } } +/* + * Return integer value of e as mpz integer (e must have rational tag) + * Raise an exception if e is not an integer. + */ +void get_integer_mpz(tstack_t *stack, stack_elem_t *e, mpz_t z) { + assert(e->tag == TAG_RATIONAL); + + if (! q_get_mpz(&e->val.rational, z)) { + raise_exception(stack, e, TSTACK_NOT_AN_INTEGER); + } +} /* * Support for division: return a rational constant equal to den @@ -1604,8 +1671,8 @@ bool elem_is_nz_constant(stack_elem_t *e, rational_t *result) { if (term_kind(terms, t) == ARITH_CONSTANT) { d = rational_term_desc(terms, t); if (q_is_nonzero(d)) { - q_set(result, d); - ok =true; + q_set(result, d); + ok = true; } } break; @@ -1615,9 +1682,9 @@ bool elem_is_nz_constant(stack_elem_t *e, rational_t *result) { if (rba_buffer_is_constant(c)) { m = rba_buffer_get_constant_mono(c); if (m != NULL) { - assert(q_is_nonzero(&m->coeff)); - q_set(result, &m->coeff); - ok = true; + assert(q_is_nonzero(&m->coeff)); + q_set(result, &m->coeff); + ok = true; } } break; @@ -1794,7 +1861,7 @@ void add_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { case TAG_TERM: case TAG_SPECIAL_TERM: - if (! yices_check_arith_term(e->val.term)) { + if (! yices_check_arith_term(e->val.term) && ! yices_check_arith_ff_term(e->val.term)) { report_yices_error(stack); } rba_buffer_add_term(b, __yices_globals.terms, e->val.term); @@ -1900,8 +1967,7 @@ void mul_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { case TAG_TERM: case TAG_SPECIAL_TERM: - if (! yices_check_arith_term(e->val.term) || - ! yices_check_mul_term(b, e->val.term)) { + if (! yices_check_arith_term(e->val.term) || ! yices_check_mul_term(b, e->val.term)) { report_yices_error(stack); } rba_buffer_mul_term(b, __yices_globals.terms, e->val.term); @@ -1921,6 +1987,88 @@ void mul_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { } } +/* + * FINITE FIELD ARITHMETIC + */ + +void ff_add_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { + + switch (e->tag) { + case TAG_FINITEFIELD: + rba_buffer_add_const(b, &e->val.ff.val); + break; + + case TAG_TERM: + case TAG_SPECIAL_TERM: + if (! yices_check_arith_ff_term(e->val.term)) { + report_yices_error(stack); + } + rba_buffer_add_term(b, __yices_globals.terms, e->val.term); + break; + + case TAG_ARITH_FF_BUFFER: + rba_buffer_add_buffer(b, e->val.mod_arith_buffer.b); + break; + + default: + raise_exception(stack, e, TSTACK_ARITH_ERROR); + break; + } +} + +void ff_sub_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { + + switch (e->tag) { + case TAG_FINITEFIELD: + rba_buffer_sub_const(b, &e->val.ff.val); + break; + + case TAG_TERM: + case TAG_SPECIAL_TERM: + if (! yices_check_arith_ff_term(e->val.term)) { + report_yices_error(stack); + } + rba_buffer_sub_term(b, __yices_globals.terms, e->val.term); + break; + + case TAG_ARITH_FF_BUFFER: + rba_buffer_sub_buffer(b, e->val.mod_arith_buffer.b); + break; + + default: + raise_exception(stack, e, TSTACK_ARITH_ERROR); + break; + } +} + +void ff_mul_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e) { + + switch (e->tag) { + case TAG_FINITEFIELD: + rba_buffer_mul_const(b, &e->val.ff.val); + break; + + case TAG_TERM: + case TAG_SPECIAL_TERM: + if (! yices_check_arith_ff_term(e->val.term) || ! yices_check_mul_term(b, e->val.term)) { + report_yices_error(stack); + } + rba_buffer_mul_term(b, __yices_globals.terms, e->val.term); + break; + + case TAG_ARITH_FF_BUFFER: + if (! yices_check_mul_buffer(b, e->val.mod_arith_buffer.b)) { + // degree overflow + report_yices_error(stack); + } + rba_buffer_mul_buffer(b, e->val.mod_arith_buffer.b); + break; + + default: + raise_exception(stack, e, TSTACK_ARITH_ERROR); + break; + } +} /* @@ -3136,6 +3284,34 @@ static void eval_mk_bv_type(tstack_t *stack, stack_elem_t *f, uint32_t n) { set_type_result(stack, tau); } +/* + * [mk-ff-type ] + */ +static void check_mk_ff_type(tstack_t *stack, stack_elem_t *f, uint32_t n) { + check_op(stack, MK_FF_TYPE); + check_size(stack, n == 1); + check_tag(stack, f, TAG_RATIONAL); +} + +static void eval_mk_ff_type(tstack_t *stack, stack_elem_t *f, uint32_t n) { + type_t tau; + mpz_t order; + + mpz_init(order); + get_integer_mpz(stack, f, order); + + tau = _o_yices_ff_type(order); + mpz_clear(order); + + if (tau == NULL_TYPE) { + raise_exception(stack, f, TSTACK_INVALID_FFSIZE); + } + + check_type(stack, tau); + + tstack_pop_frame(stack); + set_type_result(stack, tau); +} /* * [mk-scalar-type ... ] @@ -5378,7 +5554,6 @@ static void eval_mk_is_int(tstack_t *stack, stack_elem_t *f, uint32_t n) { } - /* * [mk-divides ] * - the first term must be an arithmetic constant @@ -5400,9 +5575,164 @@ static void eval_mk_divides(tstack_t *stack, stack_elem_t *f, uint32_t n) { set_term_result(stack, t1); } +/* + * FINITE FIELD FUNCTIONS + */ + +/* + * [mk-ff-const [] + */ +static void check_mk_ff_const(tstack_t *stack, stack_elem_t *f, uint32_t n) { + check_op(stack, MK_FF_CONST); + check_size(stack, n == 1 || n == 2); + check_tag(stack, f, TAG_RATIONAL); + if (n == 2) + check_tag(stack, f+1, TAG_RATIONAL); +} + +static void eval_mk_ff_const(tstack_t *stack, stack_elem_t *f, uint32_t n) { + rational_t val, mod; + + q_init(&val); + q_init(&mod); + + q_set(&val, &f->val.rational); + if (! q_is_integer(&val)) { + raise_exception(stack, f, TSTACK_INVALID_FFCONSTANT); + } + + if (n == 2) { + q_set(&mod, &(f+1)->val.rational); + if (! q_is_integer(&mod) || ! q_is_nonneg(&mod) || ! q_lt(&val, &mod)) { + raise_exception(stack, f, TSTACK_INVALID_FFCONSTANT); + } + } else { + // we don't know the field size yet + q_set_minus_one(&mod); + } + tstack_pop_frame(stack); + // the field order (i.e. type) may be yet unknown (requires an `as` to set it) + set_ff_result(stack, &val, &mod); + + q_clear(&val); + q_clear(&mod); +} + +static const rational_t* ff_get_mod(tstack_t *stack, stack_elem_t *e) { + term_t t; + + switch (e->tag) { + case TAG_FINITEFIELD: + return &e->val.ff.mod; + + case TAG_TERM: + case TAG_SPECIAL_TERM: + t = e->val.term; + if (! yices_check_arith_ff_term(t)) { + report_yices_error(stack); + } + return finitefield_term_order(__yices_globals.terms, t); + case TAG_ARITH_FF_BUFFER: + return &e->val.mod_arith_buffer.mod; + default: + raise_exception(stack, e, TSTACK_INCOMPATIBLE_FFSIZES); + break; + } +} + +static void ff_calc_mod(tstack_t *stack, rational_t *mod, stack_elem_t *f, uint32_t n) { + uint32_t i; + const rational_t *tmp; + + q_set_minus_one(mod); + for (i=0; i ] + */ +static void check_mk_ff_add(tstack_t *stack, stack_elem_t *f, uint32_t n) { + check_op(stack, MK_FF_ADD); + check_size(stack, n >= 1); +} + +static void eval_mk_ff_add(tstack_t *stack, stack_elem_t *f, uint32_t n) { + uint32_t i; + rba_buffer_t *b; + rational_t mod; + + q_init(&mod); + ff_calc_mod(stack, &mod, f, n); + b = tstack_get_abuffer(stack); + for (i=0; i ] + */ +static void check_mk_ff_sub(tstack_t *stack, stack_elem_t *f, uint32_t n) { + check_op(stack, MK_FF_SUB); + check_size(stack, n >= 1); +} + +static void eval_mk_ff_sub(tstack_t *stack, stack_elem_t *f, uint32_t n) { + uint32_t i; + rba_buffer_t *b; + rational_t mod; + + q_init(&mod); + ff_calc_mod(stack, &mod, f, n); + b = tstack_get_abuffer(stack); + for (i=0; i ] + */ +static void check_mk_ff_mul(tstack_t *stack, stack_elem_t *f, uint32_t n) { + check_op(stack, MK_FF_MUL); + check_size(stack, n >= 1); +} + +static void eval_mk_ff_mul(tstack_t *stack, stack_elem_t *f, uint32_t n) { + uint32_t i; + rba_buffer_t *b; + rational_t mod; + + q_init(&mod); + ff_calc_mod(stack, &mod, f, n); + b = tstack_get_abuffer(stack); + ff_add_elem(stack, b, f); + for (i=1; i ] + MK_FF_TYPE, // [mk-ff-type ] MK_SCALAR_TYPE, // [mk-scalar-type ... ] MK_TUPLE_TYPE, // [mk-tuple-type ... ] MK_FUN_TYPE, // [mk-fun-type ... ] @@ -500,6 +517,10 @@ enum base_opcodes { MK_DIVIDES, // [mk-divides ] MK_IS_INT, // [mk-is-int ] + MK_FF_CONST, // [mk-ff-const ] + MK_FF_ADD, // [mk-ff-add ... ] + MK_FF_MUL, // [mk-ff-mul ... ] + // collect result BUILD_TERM, // [build-term ] BUILD_TYPE, // [build-type ] @@ -722,6 +743,8 @@ static inline term_t tstack_get_type(tstack_t *stack) { return stack->result.type; } - +#ifndef NDEBUG +extern void print_elem(tstack_t *stack, stack_elem_t *e); +#endif #endif /* __TERM_STACK2_H */ diff --git a/src/parser_utils/term_stack_error.c b/src/parser_utils/term_stack_error.c index 92b1193c4..92e212884 100644 --- a/src/parser_utils/term_stack_error.c +++ b/src/parser_utils/term_stack_error.c @@ -76,8 +76,14 @@ static const char * const code2string[NUM_TSTACK_ERRORS] = { "number cannot be converted to a bitvector", "error in bitvector arithmetic operation", "error in bitvector operation", + "invalid finite field constant", + "invalid finite field order", + "incompatible finite field sizes", "incompatible types in define", "strings are not terms", + "variable values are not matching", + "not a constant", + "not a variable", "yices error", }; @@ -100,6 +106,7 @@ static const char * const opcode2smt_string[NUM_BASE_OPCODES] = { "let", "bitvector type", + "finite field type", // not in SMT2 "scalar type", // not in SMT "tuple type", // not in SMT "function type", @@ -197,6 +204,10 @@ static const char * const opcode2smt_string[NUM_BASE_OPCODES] = { "divides", // not in SMT "is_int", // not in SMT + "ff constant", // not in SMT + "ff.add", // not in SMT + "ff.mul", // not in SMT + "build term", "build type", }; @@ -218,6 +229,7 @@ static const char * const opcode2yices_string[NUM_YICES_OPCODES] = { "let", "bitvector type", + "finite field type", "scalar type", "tuple type", "function type", @@ -314,6 +326,11 @@ static const char * const opcode2yices_string[NUM_YICES_OPCODES] = { "mod", "divides", "is_int", + + "ff constant", + "ff-add", + "ff-mul", + "build term", "build type", @@ -452,6 +469,7 @@ static void base_term_stack_error(FILE *f, const char *name, tstack_t *tstack, t case TSTACK_DIVIDE_BY_ZERO: case TSTACK_NON_CONSTANT_DIVISOR: case TSTACK_INVALID_BVCONSTANT: + case TSTACK_INVALID_FFCONSTANT: case TSTACK_INCOMPATIBLE_BVSIZES: case TSTACK_BVARITH_ERROR: case TSTACK_BVLOGIC_ERROR: diff --git a/src/parser_utils/tstack_internals.h b/src/parser_utils/tstack_internals.h index ae7d4713f..8e6b686e8 100644 --- a/src/parser_utils/tstack_internals.h +++ b/src/parser_utils/tstack_internals.h @@ -281,6 +281,11 @@ extern void add_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); extern void sub_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); extern void mul_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); +// finite field arithmetic +extern void ff_add_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); +extern void ff_sub_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); +extern void ff_mul_elem(tstack_t *stack, rba_buffer_t *b, stack_elem_t *e); + // bitvector arithmetic for size <= 64 extern void bva64_add_elem(tstack_t *stack, bvarith64_buffer_t *b, stack_elem_t *e); extern void bva64_sub_elem(tstack_t *stack, bvarith64_buffer_t *b, stack_elem_t *e); @@ -353,7 +358,9 @@ extern void set_binding_result(tstack_t *stack, term_t t, char *symbol); extern void set_type_binding_result(tstack_t *stack, type_t, char *symbol); extern void set_bv64_result(tstack_t *stack, uint32_t nbits, uint64_t c); extern void set_bv_result(tstack_t *stack, uint32_t nbits, uint32_t *bv); +extern void set_ff_result(tstack_t *stack, rational_t *r, rational_t *m); extern void set_arith_result(tstack_t *stack, rba_buffer_t *b); +extern void set_arith_ff_result(tstack_t *stack, rba_buffer_t *b, rational_t *mod); extern void set_bvarith64_result(tstack_t *stack, bvarith64_buffer_t *b); extern void set_bvarith_result(tstack_t *stack, bvarith_buffer_t *b); extern void set_bvlogic_result(tstack_t *stack, bvlogic_buffer_t *b); diff --git a/src/solvers/egraph/egraph.c b/src/solvers/egraph/egraph.c index b7a17b9b5..e8b6a1b6c 100644 --- a/src/solvers/egraph/egraph.c +++ b/src/solvers/egraph/egraph.c @@ -2894,6 +2894,7 @@ static const uint8_t type_kind2etype[NUM_TYPE_KINDS] = { ETYPE_BOOL, // BOOL_TYPE ETYPE_INT, // INT_TYPE ETYPE_REAL, // REAL_TYPE + ETYPE_NONE, // FF_TYPE // TODO not implemented ETYPE_BV, // BITVECTOR_TYPE ETYPE_NONE, // SCALAR_TYPE ETYPE_NONE, // UNINTERPRETED_TYPE diff --git a/src/solvers/simplex/diophantine_systems.c b/src/solvers/simplex/diophantine_systems.c index cce392296..dd2884215 100644 --- a/src/solvers/simplex/diophantine_systems.c +++ b/src/solvers/simplex/diophantine_systems.c @@ -350,6 +350,7 @@ static dcolumn_t *column_add_unit(dcolumn_t *c, int32_t i, int32_t k) { static void column_reduce_factor(rational_t *a, dcolumn_t *c1, dcolumn_t *c2) { assert(active_row(c1) == active_row(c2)); q_set(a, active_coeff(c1)); + q_normalize(active_coeff(c2)); q_integer_div(a, active_coeff(c2)); #if 0 @@ -1975,6 +1976,7 @@ static void dsolver_reduce_columns(dsolver_t *solver, int32_t r) { k = find_row(c2, r); if (k >= 0) { q_set(f, &c2->data[k].coeff); + q_normalize(active_coeff(c1)); q_integer_div(f, active_coeff(c1)); if (q_is_nonzero(f)) { if (q_is_one(f)) { @@ -2037,6 +2039,7 @@ static void dsolver_reduce_columns(dsolver_t *solver, int32_t r) { k = find_row(c2, r); if (k >= 0) { q_set(f, &c2->data[k].coeff); + q_normalize(active_coeff(c1)); q_integer_div(f, active_coeff(c1)); if (q_is_nonzero(f)) { if (q_is_one(f)) { diff --git a/src/solvers/simplex/simplex.c b/src/solvers/simplex/simplex.c index 91ff5dce2..4a5c4aeee 100644 --- a/src/solvers/simplex/simplex.c +++ b/src/solvers/simplex/simplex.c @@ -7693,7 +7693,7 @@ static bool strengthen_bounds_on_integer_variable(simplex_solver_t *solver, dsol assert(xq_is_integer(solver->bstack.bound + k)); q_set(aux, &solver->bstack.bound[k].main); q_sub(aux, constant); - + q_normalize(gcd); q_integer_rem(aux, gcd); // remainder of (l - b) divided by d if (q_is_pos(aux)) { @@ -7733,6 +7733,7 @@ static bool strengthen_bounds_on_integer_variable(simplex_solver_t *solver, dsol assert(xq_is_integer(solver->bstack.bound + k)); q_set(aux, &solver->bstack.bound[k].main); q_sub(aux, constant); + q_normalize(gcd); q_integer_rem(aux, gcd); // remainder of (u - b) divided by d if (q_is_pos(aux)) { // the bound can be strengthened diff --git a/src/terms/balanced_arith_buffers.c b/src/terms/balanced_arith_buffers.c index 36ceb8a9a..250b5c95e 100644 --- a/src/terms/balanced_arith_buffers.c +++ b/src/terms/balanced_arith_buffers.c @@ -332,30 +332,30 @@ static void rba_balance_after_delete(rba_buffer_t *b, uint32_t p, uint32_t q) { // q is either null if p is the root (then we're done) // or q is p's parent and we loop if (q != rba_null) { - assert(is_parent_node(b, q, p)); - goto loop; + assert(is_parent_node(b, q, p)); + goto loop; } } else { // at least one red child if (is_black(b, t)) { - // rotate s and r - // change r's color to red - // change s's color to black - b->child[r][i] = b->child[s][1 - i]; - b->child[s][1 - i] = r; - b->child[q][1 - i] = s; - mark_red(b, r); - mark_black(b, s); - - t = r; - r = s; - s = b->child[r][i]; + // rotate s and r + // change r's color to red + // change s's color to black + b->child[r][i] = b->child[s][1 - i]; + b->child[s][1 - i] = r; + b->child[q][1 - i] = s; + mark_red(b, r); + mark_black(b, s); + + t = r; + r = s; + s = b->child[r][i]; } assert(is_black(b, p) && is_black(b, r) && is_red(b, t) && - p == b->child[q][i] && r == b->child[q][1 - i] && - s == b->child[r][i] && t == b->child[r][1 - i]); + p == b->child[q][i] && r == b->child[q][1 - i] && + s == b->child[r][i] && t == b->child[r][1 - i]); // rotate r and q and change colors // r takes the same color as q @@ -1102,6 +1102,59 @@ void rba_buffer_div_const(rba_buffer_t *b, const rational_t *a) { } +/* + * Take all coefficients mode m + */ +static void mod_const_tree(rba_buffer_t *b, const rational_t *m, uint32_t x, uint32_t *tbd_s, pprod_t **tbd) { + assert(q_is_integer(m) && q_is_pos(m)); + assert(x < b->num_nodes); + if (x != rba_null) { + q_integer_rem(&b->mono[x].coeff, m); + if (q_is_zero(&b->mono[x].coeff)) { + tbd[(*tbd_s)++] = b->mono[x].prod; + } + mod_const_tree(b, m, b->child[x][0], tbd_s, tbd); + mod_const_tree(b, m, b->child[x][1], tbd_s, tbd); + } +} + +void rba_buffer_mod_const(rba_buffer_t *b, const rational_t *m) { + uint32_t i, j, n, tbd_s; + bool new_node; + + assert(q_is_integer(m) && q_is_pos(m)); + + // to be deleted + pprod_t **tbd = safe_malloc(b->nterms * sizeof(pprod_t*)); + tbd_s = 0; + + if (rba_tree_is_small(b)) { + mod_const_tree(b, m, b->root, &tbd_s, tbd); + } else { + n = b->num_nodes; + for (i = 1; i < n; i++) { + if (!q_is_zero(&b->mono[i].coeff)) { + assert(q_is_integer(&b->mono[i].coeff)); + q_integer_rem(&b->mono[i].coeff, m); + if (q_is_zero(&b->mono[i].coeff)) { + tbd[tbd_s++] = b->mono[i].prod; + } + } + } + } + assert(tbd_s <= b->nterms); + while(tbd_s) { + // "find" the node to set b->stack and delete it + j = rba_get_node(b, tbd[--tbd_s], &new_node); + assert(!new_node); + rba_delete_node(b, j); + } + safe_free(tbd); +} + + + + /* * Multiply by a power product r * - the monomial ordering is compatible with product: @@ -1132,7 +1185,7 @@ void rba_buffer_mul_pp(rba_buffer_t *b, pprod_t *r) { n --; p ++; if (q_is_nonzero(&p->coeff)) { - p->prod = pprod_mul(tbl, p->prod, r); + p->prod = pprod_mul(tbl, p->prod, r); } } } @@ -1151,7 +1204,6 @@ static void mul_negpp_tree(rba_buffer_t *b, pprod_t *r, uint32_t x) { mul_pp_tree(b, r, b->child[x][0]); mul_pp_tree(b, r, b->child[x][1]); } - } void rba_buffer_mul_negpp(rba_buffer_t *b, pprod_t *r) { @@ -1169,8 +1221,8 @@ void rba_buffer_mul_negpp(rba_buffer_t *b, pprod_t *r) { n --; p ++; if (q_is_nonzero(&p->coeff)) { - p->prod = pprod_mul(tbl, p->prod, r); - q_neg(&p->coeff); + p->prod = pprod_mul(tbl, p->prod, r); + q_neg(&p->coeff); } } } @@ -1210,8 +1262,8 @@ void rba_buffer_mul_mono(rba_buffer_t *b, const rational_t *a, pprod_t *r) { n --; p ++; if (q_is_nonzero(&p->coeff)) { - p->prod = pprod_mul(tbl, p->prod, r); - q_mul(&p->coeff, a); + p->prod = pprod_mul(tbl, p->prod, r); + q_mul(&p->coeff, a); } } } @@ -1368,7 +1420,7 @@ void rba_buffer_add_buffer(rba_buffer_t *b, rba_buffer_t *b1) { n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_add_mono(b, &p->coeff, p->prod); + rba_add_mono(b, &p->coeff, p->prod); } } } @@ -1404,7 +1456,7 @@ void rba_buffer_sub_buffer(rba_buffer_t *b, rba_buffer_t *b1) { n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_sub_mono(b, &p->coeff, p->prod); + rba_sub_mono(b, &p->coeff, p->prod); } } } @@ -1440,7 +1492,7 @@ void rba_add_const_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, const rationa n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_addmul_mono(b, a, &p->coeff, p->prod); + rba_addmul_mono(b, a, &p->coeff, p->prod); } } } @@ -1476,7 +1528,7 @@ void rba_sub_const_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, const rationa n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_submul_mono(b, a, &p->coeff, p->prod); + rba_submul_mono(b, a, &p->coeff, p->prod); } } } @@ -1519,8 +1571,8 @@ void rba_buffer_add_pp_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, pprod_t * n --; p ++; if (q_is_nonzero(&p->coeff)) { - q = pprod_mul(tbl, r, p->prod); - rba_add_mono(b, &p->coeff, q); + q = pprod_mul(tbl, r, p->prod); + rba_add_mono(b, &p->coeff, q); } } } @@ -1562,8 +1614,8 @@ void rba_buffer_sub_pp_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, pprod_t * n --; p ++; if (q_is_nonzero(&p->coeff)) { - q = pprod_mul(tbl, r, p->prod); - rba_sub_mono(b, &p->coeff, q); + q = pprod_mul(tbl, r, p->prod); + rba_sub_mono(b, &p->coeff, q); } } } @@ -1603,12 +1655,12 @@ void rba_buffer_add_mono_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, const r p = b1->mono; n = b1->num_nodes - 1; while (n > 0) { - n --; - p ++; - if (q_is_nonzero(&p->coeff)) { - q = pprod_mul(tbl, r, p->prod); - rba_addmul_mono(b, a, &p->coeff, q); - } + n--; + p++; + if (q_is_nonzero(&p->coeff)) { + q = pprod_mul(tbl, r, p->prod); + rba_addmul_mono(b, a, &p->coeff, q); + } } } } @@ -1648,12 +1700,12 @@ void rba_buffer_sub_mono_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, const r p = b1->mono; n = b1->num_nodes - 1; while (n > 0) { - n --; - p ++; - if (q_is_nonzero(&p->coeff)) { - q = pprod_mul(tbl, r, p->prod); - rba_submul_mono(b, a, &p->coeff, q); - } + n--; + p++; + if (q_is_nonzero(&p->coeff)) { + q = pprod_mul(tbl, r, p->prod); + rba_submul_mono(b, a, &p->coeff, q); + } } } } @@ -1690,7 +1742,7 @@ void rba_buffer_add_buffer_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, rba_b n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_buffer_add_mono_times_buffer(b, b1, &p->coeff, p->prod); + rba_buffer_add_mono_times_buffer(b, b1, &p->coeff, p->prod); } } } @@ -1727,7 +1779,7 @@ void rba_buffer_sub_buffer_times_buffer(rba_buffer_t *b, rba_buffer_t *b1, rba_b n --; p ++; if (q_is_nonzero(&p->coeff)) { - rba_buffer_sub_mono_times_buffer(b, b1, &p->coeff, p->prod); + rba_buffer_sub_mono_times_buffer(b, b1, &p->coeff, p->prod); } } } @@ -1906,7 +1958,7 @@ void rba_buffer_mul_monarray_power(rba_buffer_t *b, monomial_t *poly, pprod_t ** */ assert(d > 0); if ((d & 1) != 0) { - rba_buffer_mul_buffer(b, aux); // b := b * aux + rba_buffer_mul_buffer(b, aux); // b := b * aux } d >>= 1; // d := d/2 if (d == 0) break; @@ -2073,7 +2125,7 @@ uint32_t hash_rba_buffer(rba_buffer_t *b, int32_t *v) { */ // aux function: check where p->mono[*i] is equal to the node x // if so increment *i -static bool rba_equal_node(polynomial_t *p, rba_buffer_t *b, int32_t *v, uint32_t *i, uint32_t x) { +static bool rba_equal_node(const polynomial_t *p, const rba_buffer_t *b, int32_t *v, uint32_t *i, uint32_t x) { uint32_t j; assert(0 < x && x < b->num_nodes && q_is_nonzero(&b->mono[x].coeff)); @@ -2089,7 +2141,7 @@ static bool rba_equal_node(polynomial_t *p, rba_buffer_t *b, int32_t *v, uint32_ return false; } -static bool rba_equal_tree(polynomial_t *p, rba_buffer_t *b, int32_t *v, uint32_t *i, uint32_t x) { +static bool rba_equal_tree(const polynomial_t *p, const rba_buffer_t *b, int32_t *v, uint32_t *i, uint32_t x) { assert(x < b->num_nodes); return (x == rba_null) || (rba_equal_tree(p, b, v, i, b->child[x][0]) && @@ -2101,7 +2153,7 @@ static bool rba_equal_tree(polynomial_t *p, rba_buffer_t *b, int32_t *v, uint32_ /* * Check where P(b, v) is equal to p */ -bool rba_buffer_equal_poly(rba_buffer_t *b, int32_t *v, polynomial_t *p) { +bool rba_buffer_equal_poly(const rba_buffer_t *b, int32_t *v, const polynomial_t *p) { uint32_t n; bool result; @@ -2131,7 +2183,7 @@ bool rba_buffer_equal_poly(rba_buffer_t *b, int32_t *v, polynomial_t *p) { /* * Check whether monomial m is integral: */ -static bool monomial_is_int(mono_t *m, void *aux, var_type_fun_t var_is_int) { +static bool monomial_is_int(const mono_t *m, void *aux, var_type_fun_t var_is_int) { pprod_t *p; uint32_t i, n; @@ -2166,28 +2218,54 @@ static bool monomial_is_int(mono_t *m, void *aux, var_type_fun_t var_is_int) { /* * Check whether all monomials in x's subtree are integral */ -static bool tree_is_int(rba_buffer_t *b, uint32_t x, void *aux, var_type_fun_t var_is_int) { +static bool tree_is_int(const rba_buffer_t *b, uint32_t x, void *aux, var_type_fun_t var_is_int) { return x == rba_null || (monomial_is_int(b->mono + x, aux, var_is_int) && tree_is_int(b, b->child[x][0], aux, var_is_int) && tree_is_int(b, b->child[x][1], aux, var_is_int)); } - - /* * Check whether b is an integral polynomial */ -bool rba_buffer_is_int(rba_buffer_t *b, void *aux, var_type_fun_t var_is_int) { - uint32_t i, n; - +bool rba_buffer_is_int(const rba_buffer_t *b, void *aux, var_type_fun_t var_is_int) { if (rba_tree_is_small(b)) { return tree_is_int(b, b->root, aux, var_is_int); } else { - n = b->num_nodes; - for (i=1; inum_nodes; + for (uint32_t i=1; imono[i], aux, var_is_int)) { - return false; + return false; + } + } + return true; + } +} + +static inline bool is_mod(const rational_t *r, const rational_t *mod) { + return q_is_integer(r) && q_is_nonneg(r) && q_lt(r, mod); +} + +static bool tree_is_mod(const rba_buffer_t *b, uint32_t x, const rational_t *mod) { + return x == rba_null || + (is_mod(&b->mono[x].coeff, mod) && + tree_is_mod(b, b->child[x][0], mod) && + tree_is_mod(b, b->child[x][1], mod)); +} + +/* + * Check whether the every coefficient c of b is int and 0 <= c < mod + */ +bool rba_buffer_is_mod(const rba_buffer_t *b, const rational_t *mod) { + assert(q_is_integer(mod) && q_is_pos(mod)); + + if (rba_tree_is_small(b)) { + return tree_is_mod(b, b->root, mod); + } else { + uint32_t n = b->num_nodes; + for (uint32_t i=1; imono[i].coeff, mod)) { + return false; } } return true; diff --git a/src/terms/balanced_arith_buffers.h b/src/terms/balanced_arith_buffers.h index ada36ad55..18f1e8b1d 100644 --- a/src/terms/balanced_arith_buffers.h +++ b/src/terms/balanced_arith_buffers.h @@ -342,6 +342,14 @@ extern void rba_buffer_mul_const(rba_buffer_t *b, const rational_t *a); extern void rba_buffer_div_const(rba_buffer_t *b, const rational_t *a); +/* + * Take all coefficients mod m + * - b must have integer coefficients only + * - m must be a positive integer + */ +extern void rba_buffer_mod_const(rba_buffer_t *b, const rational_t *m); + + /* * Multiply b by power product r */ @@ -592,7 +600,7 @@ extern uint32_t hash_rba_buffer(rba_buffer_t *b, int32_t *v); /* * Check where P(b, v) is equal to p */ -extern bool rba_buffer_equal_poly(rba_buffer_t *b, int32_t *v, polynomial_t *p); +extern bool rba_buffer_equal_poly(const rba_buffer_t *b, int32_t *v, const polynomial_t *p); /* @@ -611,12 +619,12 @@ extern polynomial_t *rba_buffer_get_poly(rba_buffer_t *b, int32_t *v); * integer, false if x is real. */ typedef bool (*var_type_fun_t)(void *aux, int32_t x); +extern bool rba_buffer_is_int(const rba_buffer_t *b, void *aux, var_type_fun_t var_is_int); -extern bool rba_buffer_is_int(rba_buffer_t *b, void *aux, var_type_fun_t var_is_int); - - - - +/* + * Check whether the every coefficient c of b is int and 0 <= c < mod + */ +extern bool rba_buffer_is_mod(const rba_buffer_t *b, const rational_t *mod); /**************** diff --git a/src/terms/polynomials.c b/src/terms/polynomials.c index 500c9fa74..349aa59dd 100644 --- a/src/terms/polynomials.c +++ b/src/terms/polynomials.c @@ -846,3 +846,16 @@ bool polynomial_is_var(polynomial_t *p, int32_t x) { return p->nterms == 1 && p->mono[0].var == x && q_is_one(&p->mono[0].coeff); } + +/* + * Check if all coefficients are integer and less than mod + */ +bool polynomial_is_integer_mod(const polynomial_t *p, const rational_t *mod) { + uint32_t i; + for (i = 0; i < p->nterms; ++i) { + const rational_t *c = &p->mono[i].coeff; + if (! (q_is_nonneg(c) && q_lt(c, mod)) ) + return false; + } + return true; +} diff --git a/src/terms/polynomials.h b/src/terms/polynomials.h index 0378c5ded..64321a59b 100644 --- a/src/terms/polynomials.h +++ b/src/terms/polynomials.h @@ -428,5 +428,9 @@ extern bool polynomial_is_const_plus_var(polynomial_t *p, int32_t x); extern bool polynomial_is_var(polynomial_t *p, int32_t x); +/* + * Check if all coefficients are integer and less than mod + */ +extern bool polynomial_is_integer_mod(const polynomial_t *p, const rational_t *mod); #endif /* __POLYNOMIALS_H */ diff --git a/src/terms/rationals.c b/src/terms/rationals.c index 067f7dd19..98e143cae 100644 --- a/src/terms/rationals.c +++ b/src/terms/rationals.c @@ -439,13 +439,23 @@ void q_normalize(rational_t *r) { num = mpz_get_si(mpq_numref(q)); den = mpz_get_ui(mpq_denref(q)); if (MIN_NUMERATOR <= num && num <= MAX_NUMERATOR && den <= MAX_DENOMINATOR) { - mpqstore_free(&mpq_store, q); + mpqstore_free(&mpq_store, q); set_rat32(r, (int32_t) num, (uint32_t) den); } } } } +static void q_denormalize(rational_t *r) { + mpq_ptr q; + + if (is_rat32(r)) { + q = new_mpq(); + mpq_set_si(q, get_num(r), get_den(r)); + set_ratgmp(r, q); + } + assert(is_ratgmp(r)); +} /* * Prepare to assign an mpq number to r @@ -1005,6 +1015,31 @@ void q_inv(rational_t *r) { } } +/* + * Invert r mod m + */ +void q_inv_mod(rational_t *r, const rational_t *mod) { + assert(q_is_integer(r) && q_is_integer(mod) && q_is_pos(mod)); + + rational_t tmp, *mm; + if (is_rat32(mod)) { + tmp = *mod; mm = &tmp; + q_denormalize(mm); + } else { + mm = (rational_t*)mod; + } + + q_denormalize(r); + assert(is_ratgmp(r) && is_ratgmp(mm)); + mpz_ptr z = get_gmp_num(r); + mpz_ptr m = get_gmp_num(mm); + mpz_invert(z, z, m); + q_normalize(r); + + if (mm != mod) + q_clear(mm); +} + /* * Multiply r1 by r2 */ @@ -1349,12 +1384,10 @@ void q_gcd(rational_t *r1, const rational_t *r2) { * - r1 and r2 must be integer * - r2 must be positive */ -void q_integer_div(rational_t *r1, rational_t *r2) { +void q_integer_div(rational_t *r1, const rational_t *r2) { int32_t n; mpq_ptr q1, q2; - q_normalize(r2); - if (is_rat32(r2)) { assert(r2->s.den == ONE_DEN && r2->s.num > 0); if (is_rat32(r1)) { @@ -1400,12 +1433,10 @@ void q_integer_div(rational_t *r1, rational_t *r2) { * - both r1 and r2 must be integer * - r2 must be positive */ -void q_integer_rem(rational_t *r1, rational_t *r2) { +void q_integer_rem(rational_t *r1, const rational_t *r2) { int32_t n; mpq_ptr q1, q2; - q_normalize(r2); - if (is_rat32(r2)){ assert(r2->s.den == ONE_DEN && r2->s.num > 0); if (is_rat32(r1)) { @@ -1545,7 +1576,7 @@ bool q_smt2_divides(const rational_t *r1, const rational_t *r2) { * lcm(a1, a2)/gcd(b1, b2). * - the result is stored in r1 */ -void q_generalized_lcm(rational_t *r1, rational_t *r2) { +void q_generalized_lcm(rational_t *r1, const rational_t *r2) { rational_t a1, b1; rational_t a2, b2; @@ -1583,7 +1614,7 @@ void q_generalized_lcm(rational_t *r1, rational_t *r2) { * gcd(a1, a2)/lcm(b1, b2). * - the result is stored in r1 */ -void q_generalized_gcd(rational_t *r1, rational_t *r2) { +void q_generalized_gcd(rational_t *r1, const rational_t *r2) { rational_t a1, b1; rational_t a2, b2; @@ -1915,7 +1946,7 @@ uint32_t q_size(rational_t *r) { * Convert r to a GMP integer * - return false if r is not an integer */ -bool q_get_mpz(rational_t *r, mpz_t z) { +bool q_get_mpz(const rational_t *r, mpz_t z) { if (r->s.den == ONE_DEN) { assert(is_rat32(r)); mpz_set_si(z, r->s.num); @@ -1933,7 +1964,7 @@ bool q_get_mpz(rational_t *r, mpz_t z) { /* * Convert r to a GMP rational */ -void q_get_mpq(rational_t *r, mpq_t q) { +void q_get_mpq(const rational_t *r, mpq_t q) { if (is_ratgmp(r)) { mpq_set(q, get_gmp(r)); } else { diff --git a/src/terms/rationals.h b/src/terms/rationals.h index 16689b897..0fff8be69 100644 --- a/src/terms/rationals.h +++ b/src/terms/rationals.h @@ -110,6 +110,16 @@ static inline mpq_ptr get_gmp(const rational_t *r) { return (mpq_ptr) (r->p.gmp ^ IS_RATGMP); } +static inline mpz_ptr get_gmp_num(const rational_t *r) { + assert(is_ratgmp(r)); + return mpq_numref(get_gmp(r)); +} + +static inline mpz_ptr get_gmp_den(const rational_t *r) { + assert(is_ratgmp(r)); + return mpq_denref(get_gmp(r)); +} + static inline int32_t get_num(const rational_t *r) { assert(is_rat32(r)); return r->s.num; @@ -273,6 +283,7 @@ extern void q_gcd(rational_t *r1, const rational_t *r2); extern void q_floor(rational_t *r); extern void q_ceil(rational_t *r); +extern void q_inv_mod(rational_t *r, const rational_t *mod); /* @@ -286,7 +297,7 @@ extern void q_mulexp(rational_t *r1, const rational_t *r2, uint32_t n); * Integer division and remainder * - r1 and r2 must both be integer * - r2 must be positive. - * - side effect: r2 is normalized + * - Consider normalizing r2 before * * q_integer_div(r1, r2) stores the quotient of r1 divided by r2 into r1 * q_integer_rem(r1, r2) stores the remainder into r1 @@ -295,8 +306,8 @@ extern void q_mulexp(rational_t *r1, const rational_t *r2, uint32_t n); * If r = remainder and q = quotient then we have * 0 <= r < r2 and r1 = q * r2 + r */ -extern void q_integer_div(rational_t *r1, rational_t *r2); -extern void q_integer_rem(rational_t *r1, rational_t *r2); +extern void q_integer_div(rational_t *r1, const rational_t *r2); +extern void q_integer_rem(rational_t *r1, const rational_t *r2); /* @@ -305,14 +316,14 @@ extern void q_integer_rem(rational_t *r1, rational_t *r2); * - r1 and r2 can be arbitrary rationals. * - the result is stored in r1 */ -extern void q_generalized_lcm(rational_t *r1, rational_t *r2); +extern void q_generalized_lcm(rational_t *r1, const rational_t *r2); /* * Generalized GCD: compute the largest positive rational q * such that r1/q and r2/q are both integer. - * - the result is stored in r2 + * - the result is stored in r1 */ -extern void q_generalized_gcd(rational_t *r1, rational_t *r2); +extern void q_generalized_gcd(rational_t *r1, const rational_t *r2); @@ -484,6 +495,17 @@ extern bool q_divides(const rational_t *r1, const rational_t *r2); extern bool q_smt2_divides(const rational_t *r1, const rational_t *r2); +/* + * Tests on integer rational r mod m + */ +static inline bool q_is_zero_mod(const rational_t *r, const rational_t *m) { + assert(q_is_integer(r) && q_is_integer(m) && q_is_pos(m)); + return q_integer_divides((rational_t *)m, r); +} + +static inline bool q_is_nonzero_mod(const rational_t *r, const rational_t *m) { + return !q_is_zero_mod(r, m); +} /* @@ -549,13 +571,13 @@ extern uint32_t q_size(rational_t *r); * Store r into the GMP integer z. * - return false if r is not a integer, true otherwise */ -extern bool q_get_mpz(rational_t *r, mpz_t z); +extern bool q_get_mpz(const rational_t *r, mpz_t z); /* * Store r into q */ -extern void q_get_mpq(rational_t *r, mpq_t q); +extern void q_get_mpq(const rational_t *r, mpq_t q); /* diff --git a/src/terms/rba_buffer_terms.c b/src/terms/rba_buffer_terms.c index 5744502b4..dbd93a3db 100644 --- a/src/terms/rba_buffer_terms.c +++ b/src/terms/rba_buffer_terms.c @@ -36,7 +36,7 @@ void rba_buffer_add_term(rba_buffer_t *b, term_table_t *table, term_t t) { int32_t i; assert(b->ptbl == table->pprods); - assert(pos_term(t) && good_term(table, t) && is_arithmetic_term(table, t)); + assert(pos_term(t) && good_term(table, t) && (is_arithmetic_term(table, t) || is_finitefield_term(table, t))); i = index_of(t); switch (kind_for_idx(table, i)) { @@ -45,10 +45,12 @@ void rba_buffer_add_term(rba_buffer_t *b, term_table_t *table, term_t t) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: rba_buffer_add_const(b, rational_for_idx(table, i)); break; case ARITH_POLY: + case ARITH_FF_POLY: p = polynomial_for_idx(table, i); v = pprods_for_poly(table, p); rba_buffer_add_monarray(b, p->mono, v); @@ -73,7 +75,7 @@ void rba_buffer_sub_term(rba_buffer_t *b, term_table_t *table, term_t t) { int32_t i; assert(b->ptbl == table->pprods); - assert(pos_term(t) && good_term(table, t) && is_arithmetic_term(table, t)); + assert(pos_term(t) && good_term(table, t) && (is_arithmetic_term(table, t) || is_finitefield_term(table, t))); i = index_of(t); switch (kind_for_idx(table, i)) { @@ -82,10 +84,12 @@ void rba_buffer_sub_term(rba_buffer_t *b, term_table_t *table, term_t t) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: rba_buffer_sub_const(b, rational_for_idx(table, i)); break; case ARITH_POLY: + case ARITH_FF_POLY: p = polynomial_for_idx(table, i); v = pprods_for_poly(table, p); rba_buffer_sub_monarray(b, p->mono, v); @@ -110,7 +114,7 @@ void rba_buffer_mul_term(rba_buffer_t *b, term_table_t *table, term_t t) { int32_t i; assert(b->ptbl == table->pprods); - assert(pos_term(t) && good_term(table, t) && is_arithmetic_term(table, t)); + assert(pos_term(t) && good_term(table, t) && (is_arithmetic_term(table, t) || is_finitefield_term(table, t))); i = index_of(t); switch (kind_for_idx(table, i)) { @@ -119,10 +123,12 @@ void rba_buffer_mul_term(rba_buffer_t *b, term_table_t *table, term_t t) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: rba_buffer_mul_const(b, rational_for_idx(table, i)); break; case ARITH_POLY: + case ARITH_FF_POLY: p = polynomial_for_idx(table, i); v = pprods_for_poly(table, p); rba_buffer_mul_monarray(b, p->mono, v); @@ -148,7 +154,7 @@ void rba_buffer_add_const_times_term(rba_buffer_t *b, term_table_t *table, ratio int32_t i; assert(b->ptbl == table->pprods); - assert(pos_term(t) && good_term(table, t) && is_arithmetic_term(table, t)); + assert(pos_term(t) && good_term(table, t) && (is_arithmetic_term(table, t) || is_finitefield_term(table, t))); i = index_of(t); switch (kind_for_idx(table, i)) { @@ -157,6 +163,7 @@ void rba_buffer_add_const_times_term(rba_buffer_t *b, term_table_t *table, ratio break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: q_init(&q); q_set(&q, a); q_mul(&q, rational_for_idx(table, i)); @@ -165,6 +172,7 @@ void rba_buffer_add_const_times_term(rba_buffer_t *b, term_table_t *table, ratio break; case ARITH_POLY: + case ARITH_FF_POLY: p = polynomial_for_idx(table, i); v = pprods_for_poly(table, p); rba_buffer_add_const_times_monarray(b, p->mono, v, a); @@ -193,7 +201,7 @@ void rba_buffer_mul_term_power(rba_buffer_t *b, term_table_t *table, term_t t, u int32_t i; assert(b->ptbl == table->pprods); - assert(pos_term(t) && good_term(table, t) && is_arithmetic_term(table, t)); + assert(pos_term(t) && good_term(table, t) && (is_arithmetic_term(table, t) || is_finitefield_term(table, t))); i = index_of(t); switch (kind_for_idx(table, i)) { @@ -203,6 +211,7 @@ void rba_buffer_mul_term_power(rba_buffer_t *b, term_table_t *table, term_t t, u break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: q_init(&q); q_set_one(&q); q_mulexp(&q, rational_for_idx(table, i), d); // q = t^d @@ -211,6 +220,7 @@ void rba_buffer_mul_term_power(rba_buffer_t *b, term_table_t *table, term_t t, u break; case ARITH_POLY: + case ARITH_FF_POLY: p = polynomial_for_idx(table, i); v = pprods_for_poly(table, p); init_rba_buffer(&aux, b->ptbl); diff --git a/src/terms/term_explorer.c b/src/terms/term_explorer.c index 5cd524559..b2d93487c 100644 --- a/src/terms/term_explorer.c +++ b/src/terms/term_explorer.c @@ -30,6 +30,7 @@ static const uint8_t atomic_term_flag[NUM_TERM_KINDS] = { false, // RESERVED_TERM true, // CONSTANT_TERM true, // ARITH_CONSTANT + true, // ARITH_FF_CONSTANT true, // BV64_CONSTANT true, // BV_CONSTANT true, // VARIABLE @@ -41,6 +42,7 @@ static const uint8_t atomic_term_flag[NUM_TERM_KINDS] = { false, // ARITH_CEIL false, // ARITH_ABS false, // ARITH_ROOT_ATOM + false, // ARITH_FF_EQ_ATOM false, // ITE_TERM false, // ITE_SPECIAL false, // APP_TERM @@ -57,6 +59,7 @@ static const uint8_t atomic_term_flag[NUM_TERM_KINDS] = { false, // ARITH_IDIV false, // ARITH_MOD false, // ARITH_DIVIDES + false, // ARITH_FF_BINEQ_ATOM false, // BV_ARRAY false, // BV_DIV false, // BV_REM @@ -73,6 +76,7 @@ static const uint8_t atomic_term_flag[NUM_TERM_KINDS] = { false, // BIT_TERM false, // POWER_PRODUCT false, // ARITH_POLY + false, // ARITH_FF_POLY false, // BV64_POLY false, // BV_POLY }; @@ -82,6 +86,7 @@ static const uint8_t composite_term_flag[NUM_TERM_KINDS] = { false, // RESERVED_TERM false, // CONSTANT_TERM false, // ARITH_CONSTANT + false, // ARITH_FF_CONSTANT false, // BV64_CONSTANT false, // BV_CONSTANT false, // VARIABLE @@ -93,6 +98,7 @@ static const uint8_t composite_term_flag[NUM_TERM_KINDS] = { true, // ARITH_CEIL true, // ARITH_ABS true, // ARITH_ROOT_ATOM + true, // ARITH_FF_EQ_ATOM true, // ITE_TERM true, // ITE_SPECIAL true, // APP_TERM @@ -109,6 +115,7 @@ static const uint8_t composite_term_flag[NUM_TERM_KINDS] = { true, // ARITH_IDIV true, // ARITH_MOD true, // ARITH_DIVIDES + true, // ARITH_FF_BINEQ_ATOM true, // BV_ARRAY true, // BV_DIV true, // BV_REM @@ -125,6 +132,7 @@ static const uint8_t composite_term_flag[NUM_TERM_KINDS] = { false, // BIT_TERM false, // POWER_PRODUCT false, // ARITH_POLY + false, // ARITH_FF_POLY false, // BV64_POLY false, // BV_POLY }; @@ -135,6 +143,7 @@ static const term_constructor_t constructor_term_table[NUM_TERM_KINDS] = { YICES_CONSTRUCTOR_ERROR, // RESERVED_TERM YICES_SCALAR_CONSTANT, // CONSTANT_TERM YICES_ARITH_CONSTANT, // ARITH_CONSTANT + YICES_ARITH_FF_CONSTANT, // ARITH_FF_CONSTANT YICES_BV_CONSTANT, // BV64_CONSTANT YICES_BV_CONSTANT, // BV_CONSTANT YICES_VARIABLE, // VARIABLE @@ -146,6 +155,7 @@ static const term_constructor_t constructor_term_table[NUM_TERM_KINDS] = { YICES_CEIL, // ARITH_CEIL YICES_ABS, // ARITH_ABS YICES_ARITH_ROOT_ATOM, // ARITH_ROOT_ATOM + YICES_EQ_TERM, // ARITH_FF_EQ_ATOM YICES_ITE_TERM, // ITE_TERM YICES_ITE_TERM, // ITE_SPECIAL YICES_APP_TERM, // APP_TERM @@ -162,6 +172,7 @@ static const term_constructor_t constructor_term_table[NUM_TERM_KINDS] = { YICES_IDIV, // ARITH_IDIV YICES_IMOD, // ARITH_MOD YICES_DIVIDES_ATOM, // ARITH_DIVIDES_ATOM + YICES_EQ_TERM, // ARITH_FF_BINEQ_ATOM YICES_BV_ARRAY, // BV_ARRAY YICES_BV_DIV, // BV_DIV YICES_BV_REM, // BV_REM @@ -178,6 +189,7 @@ static const term_constructor_t constructor_term_table[NUM_TERM_KINDS] = { YICES_BIT_TERM, // BIT_TERM YICES_POWER_PRODUCT, // POWER_PRODUCT YICES_ARITH_SUM, // ARITH_POLY + YICES_ARITH_FF_SUM, // ARITH_FF_POLY YICES_BV_SUM, // BV64_POLY YICES_BV_SUM, // BV_POLY }; @@ -304,6 +316,7 @@ uint32_t term_num_children(term_table_t *table, term_t t) { assert(false); // fall through to prevent compile-time warning case CONSTANT_TERM: case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: case BV64_CONSTANT: case BV_CONSTANT: case VARIABLE: @@ -312,6 +325,7 @@ uint32_t term_num_children(term_table_t *table, term_t t) { break; case ARITH_EQ_ATOM: + case ARITH_FF_EQ_ATOM: case ARITH_GE_ATOM: // internally, these are terms of the form t==0 or t >= 0 // to be uniform, we report them as binary operators @@ -347,6 +361,7 @@ uint32_t term_num_children(term_table_t *table, term_t t) { case ARITH_IDIV: case ARITH_MOD: case ARITH_DIVIDES_ATOM: + case ARITH_FF_BINEQ_ATOM: case BV_ARRAY: case BV_DIV: case BV_REM: @@ -373,7 +388,11 @@ uint32_t term_num_children(term_table_t *table, term_t t) { case ARITH_POLY: result = poly_term_desc(table, t)->nterms; - break; + break; + + case ARITH_FF_POLY: + result = finitefield_poly_term_desc(table, t)->nterms; + break; case BV64_POLY: result = bvpoly64_term_desc(table, t)->nterms; @@ -634,6 +653,11 @@ void arith_const_value(term_table_t *table, term_t t, mpq_t q) { q_get_mpq(rational_term_desc(table, t), q); } +void arith_ff_const_value(term_table_t *table, term_t t, mpz_t z) { + assert(is_pos_term(t)); + q_get_mpz(finitefield_term_desc(table, t), z); +} + void bv_const_value(term_table_t *table, term_t t, int32_t a[]) { bvconst64_term_t *bv64; bvconst_term_t *bv; diff --git a/src/terms/term_explorer.h b/src/terms/term_explorer.h index 0c357cfc3..5e64b1738 100644 --- a/src/terms/term_explorer.h +++ b/src/terms/term_explorer.h @@ -142,6 +142,7 @@ extern void product_term_component(term_table_t *table, term_t t, uint32_t i, te */ extern bool bool_const_value(term_table_t *table, term_t t); extern void arith_const_value(term_table_t *table, term_t t, mpq_t q); +extern void arith_ff_const_value(term_table_t *table, term_t t, mpz_t z); extern void bv_const_value(term_table_t *table, term_t t, int32_t a[]); extern int32_t generic_const_value(term_table_t *table, term_t t); diff --git a/src/terms/term_manager.c b/src/terms/term_manager.c index 38e1132eb..afc103821 100644 --- a/src/terms/term_manager.c +++ b/src/terms/term_manager.c @@ -773,6 +773,8 @@ static term_t try_iff_bveq_simplification(term_manager_t *manager, term_t x, ter * ARITHMETIC TERMS * *********************/ +// TODO make some rewriting functions finite field compatible + /* * Arithmetic constant (rational) * - r must be normalized @@ -827,6 +829,57 @@ static term_t arith_buffer_to_term(term_table_t *tbl, rba_buffer_t *b) { return t; } +/* + * Arithmetic constant (finite field) + * - r must be normalized wrt. mod + */ +term_t mk_arith_ff_constant(term_manager_t *manager, rational_t *r, rational_t *mod) { + return arith_ff_constant(manager->terms, r, mod); +} + +static term_t arith_ff_buffer_to_term(term_table_t *tbl, rba_buffer_t *b, const rational_t *mod) { + mono_t *m; + pprod_t *r; + uint32_t n; + term_t t; + + assert(b->ptbl == tbl->pprods); + + rba_buffer_mod_const(b, mod); + + n = b->nterms; + if (n == 0) { + rational_t zero; + q_init(&zero); + // generate/get a zero term mod mod + t = arith_ff_constant(tbl, &zero, mod); + q_clear(&zero); + } else if (n == 1) { + m = rba_buffer_root_mono(b); // unique monomial of b + r = m->prod; + if (r == empty_pp) { + // constant polynomial + t = arith_ff_constant(tbl, &m->coeff, mod); + } else if (q_is_one(&m->coeff)) { + // term or power product + t = pp_is_var(r) ? var_of_pp(r) : pprod_term(tbl, r); + } else { + // can't simplify + t = arith_ff_poly(tbl, b, mod); + } + } else { + t = arith_ff_poly(tbl, b, mod); + } + + reset_rba_buffer(b); + + // check that mod is type t's ff-size + assert(q_eq(mod, ff_type_size(tbl->types, term_type(tbl, t)))); + + return t; +} + + term_t mk_arith_term(term_manager_t *manager, rba_buffer_t *b) { return arith_buffer_to_term(manager->terms, b); @@ -836,6 +889,14 @@ term_t mk_direct_arith_term(term_table_t *tbl, rba_buffer_t *b) { return arith_buffer_to_term(tbl, b); } +term_t mk_arith_ff_term(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod) { + return arith_ff_buffer_to_term(manager->terms, b, mod); +} + +term_t mk_direct_arith_ff_term(term_table_t *tbl, rba_buffer_t *b, const rational_t *mod) { + return arith_ff_buffer_to_term(tbl, b, mod); +} + /********************************* @@ -2421,7 +2482,7 @@ static term_t mk_arith_bineq_atom(term_table_t *tbl, term_t t1, term_t t2, bool * - try to simplify and normalize then build (arith-eq0 t) */ static term_t mk_arith_eq0_atom(term_table_t *tbl, term_t t, bool simplify_ite) { - term_t aux; + term_t aux; assert(is_arithmetic_term(tbl, t)); @@ -2561,7 +2622,7 @@ term_t mk_direct_arith_eq0(term_table_t *tbl, rba_buffer_t *b, bool simplify_ite * b is a1 * r1 + a2 * r2 * Simplifications: * - rewrite (b == 0) to (r2 == -a1/a2) if r1 is the empty product - * - rewrite (b == 0) to (r1 == r2) is a1 + a2 = 0 + * - rewrite (b == 0) to (r1 == r2) if a1 + a2 = 0 */ rba_buffer_monomial_pair(b, m); m1 = m[0]; @@ -3547,14 +3608,14 @@ term_t mk_arith_divides(term_manager_t *manager, term_t t1, term_t t2) { case ARITH_CONSTANT: t = false_term; if (q_divides(q, rational_term_desc(tbl, t2))) { - t = true_term; + t = true_term; } break; default: // force t1 to be positive if (q_is_neg(q)) { - t1 = neg_rational(manager, q); + t1 = neg_rational(manager, q); } t = arith_divides(tbl, t1, t2); break; @@ -3577,6 +3638,195 @@ term_t mk_direct_arith_root_atom_geq(rba_buffer_t* b, term_table_t* terms, uint3 } +/* + * Finite field Arithmetic + */ + +term_t mk_arith_ff_eq0(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod) { + return mk_direct_arith_ff_eq0(manager->terms, b, mod, manager->simplify_ite); +} + +term_t mk_arith_ff_neq0(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod) { + return opposite_term(mk_arith_ff_eq0(manager, b, mod)); +} + +term_t mk_arith_ff_term_eq0(term_manager_t *manager, term_t t) { + rba_buffer_t *b; + + assert(is_finitefield_term(manager->terms, t)); + + b = term_manager_get_arith_buffer(manager); + reset_rba_buffer(b); + rba_buffer_add_term(b, manager->terms, t); + + return mk_arith_ff_eq0(manager, b, finitefield_term_order(manager->terms, t)); +} + +term_t mk_arith_ff_term_neq0(term_manager_t *manager, term_t t) { + rba_buffer_t *b; + + assert(is_finitefield_term(manager->terms, t)); + + b = term_manager_get_arith_buffer(manager); + reset_rba_buffer(b); + rba_buffer_add_term(b, manager->terms, t); + + return mk_arith_ff_neq0(manager, b, finitefield_term_order(manager->terms, t)); +} + +term_t mk_arith_ff_eq(term_manager_t *manager, term_t t1, term_t t2) { + rba_buffer_t *b; + + assert(is_finitefield_term(manager->terms, t1) && + is_finitefield_term(manager->terms, t2)); + assert(compatible_types(manager->types, term_type(manager->terms, t1), term_type(manager->terms, t2))); + + b = term_manager_get_arith_buffer(manager); + mk_arith_diff(manager, b, t1, t2); // use regular arith diff + return mk_arith_ff_eq0(manager, b, finitefield_term_order(manager->terms, t1)); +} + +term_t mk_arith_ff_neq(term_manager_t *manager, term_t t1, term_t t2) { + return opposite_term(mk_arith_ff_eq(manager, t1, t2)); +} + +static term_t mk_arith_ff_eq0_atom(term_table_t *tbl, term_t t, bool simplify_ite) { + assert(is_finitefield_term(tbl, t)); + + if (arith_ff_term_is_nonzero(tbl, t, simplify_ite)) { + return false_term; + } + + if (simplify_ite) { + // TODO implement simplification + } + + return arith_ff_eq_atom(tbl, t); +} + +static term_t mk_arith_ff_bineq_atom(term_table_t *tbl, term_t t1, term_t t2, bool simplify_ite) { + term_t aux; + + assert(is_finitefield_term(tbl, t1) && is_finitefield_term(tbl, t2)); + + if (disequal_arith_ff_terms(tbl, t1, t2, simplify_ite)) { + return false_term; + } + + if (simplify_ite) { + // TODO implement simplification + } + + // normalize: put the smallest term on the left + if (t1 > t2) { + aux = t1; t1 = t2; t2 = aux; + } + + return arith_ff_bineq_atom(tbl, t1, t2); +} + +/* + * Construct the atom (b == 0) then reset b. + * + * Normalize b first. + * - simplify to true if b is the zero polynomial + * - simplify to false if b is constant and non-zero + * - rewrite to (t1 == t2) if that's possible. + * - otherwise, create a polynomial term t from b + * and return the atom (t == 0). + */ +term_t mk_direct_arith_ff_eq0(term_table_t *tbl, rba_buffer_t *b, const rational_t *mod, bool simplify_ite) { + mono_t *m[2], *m1, *m2; + pprod_t *r1, *r2; + rational_t r0; + term_t t1, t2, t; + uint32_t n; + + assert(b->ptbl == tbl->pprods); + + // normalize the tree wrt. to mod + rba_buffer_mod_const(b, mod); + + n = b->nterms; + if (n == 0) { + // b is zero + t = true_term; + + } else if (n == 1) { + /* + * b is a1 * r1 with a_1 != 0 + * (a1 * r1 == 0) is false if r1 is the empty product + * (a1 * r1 == 0) simplifies to (r1 == 0) otherwise + */ + m1 = rba_buffer_root_mono(b); + r1 = m1->prod; + assert(q_is_nonzero_mod(&m1->coeff, mod)); + if (r1 == empty_pp) { + t = false_term; + } else { + t1 = pp_is_var(r1) ? var_of_pp(r1) : pprod_term(tbl, r1); + t = mk_arith_ff_eq0_atom(tbl, t1, simplify_ite); // atom r1 = 0 + } + + } else if (n == 2) { + /* + * b is a1 * r1 + a2 * r2 + * Simplifications: + * - rewrite (b == 0) to (r2 == -a1/a2) if r1 is the empty product + * - rewrite (b == 0) to (r1 == r2) if a1 + a2 = 0 + */ + rba_buffer_monomial_pair(b, m); + m1 = m[0]; + m2 = m[1]; + r1 = m1->prod; + r2 = m2->prod; + assert(q_is_nonzero_mod(&m1->coeff, mod) && q_is_nonzero_mod(&m2->coeff, mod)); + + q_init(&r0); + + if (r1 == empty_pp) { + q_set(&r0, &m2->coeff); // r0 = a2 + q_inv_mod(&r0, mod); // r0 = a2^-1 + q_mul(&r0, &m1->coeff); // r0 = a1*a2^-1 + q_neg(&r0); // r0 = -a1*a2^-1 + q_integer_rem(&r0, mod); + + t1 = arith_ff_constant(tbl, &r0, mod); + t2 = pp_is_var(r2) ? var_of_pp(r2) : pprod_term(tbl, r2); + t = mk_arith_ff_bineq_atom(tbl, t1, t2, simplify_ite); + + } else { + q_set(&r0, &m1->coeff); + q_add(&r0, &m2->coeff); + if (q_is_zero(&r0)) { + t1 = pp_is_var(r1) ? var_of_pp(r1) : pprod_term(tbl, r1); + t2 = pp_is_var(r2) ? var_of_pp(r2) : pprod_term(tbl, r2); + t = mk_arith_ff_bineq_atom(tbl, t1, t2, simplify_ite); + + } else { + // no simplification + t = arith_ff_poly(tbl, b, mod); + t = arith_ff_eq_atom(tbl, t); + } + } + + q_clear(&r0); + + } else { + /* + * more than 2 monomials: don't simplify + */ + t = arith_ff_poly(tbl, b, mod); + t = arith_ff_eq_atom(tbl, t); + } + + reset_rba_buffer(b); + assert(good_term(tbl, t) && is_boolean_term(tbl, t)); + + return t; +} + + /**************** * EQUALITIES * ***************/ @@ -3612,6 +3862,11 @@ term_t mk_eq(term_manager_t *manager, term_t t1, term_t t2) { return mk_arith_eq(manager, t1, t2); } + if (is_finitefield_term(tbl, t1)) { + assert(is_finitefield_term(tbl, t2)); + return mk_arith_ff_eq(manager, t1, t2); + } + if (is_bitvector_term(tbl, t1)) { assert(is_bitvector_term(tbl, t2)); return mk_bveq(manager, t1, t2); @@ -3654,6 +3909,11 @@ term_t mk_neq(term_manager_t *manager, term_t t1, term_t t2) { return mk_arith_neq(manager, t1, t2); } + if (is_finitefield_term(tbl, t1)) { + assert(is_finitefield_term(tbl, t2)); + return mk_arith_ff_neq(manager, t1, t2); + } + if (is_bitvector_term(tbl, t1)) { assert(is_bitvector_term(tbl, t2)); return mk_bvneq(manager, t1, t2); @@ -6053,6 +6313,31 @@ term_t mk_arith_pprod(term_manager_t *mngr, pprod_t *p, uint32_t n, const term_t return mk_arith_term(mngr, b); } +/* + * Arithmetic product: + * - p is a power product descriptor: t_0^e_0 ... t_{n-1}^e_{n-1} + * - a is an array of n arithmetic terms + * - this function constructs the term a[0]^e_0 ... a[n-1]^e_{n-1} + */ +term_t mk_arith_ff_pprod(term_manager_t *mngr, pprod_t *p, uint32_t n, const term_t *a, const rational_t *mod) { + rba_buffer_t *b; + term_table_t *tbl; + uint32_t i; + + assert(n == p->len); + + tbl = term_manager_get_terms(mngr); + b = term_manager_get_arith_buffer(mngr); + + rba_buffer_set_one(b); // b := 1 + for (i=0; iprod[i].exp); + } + + return mk_arith_ff_term(mngr, b, mod); +} + /* * Bitvector product: 1 to 64 bits vector @@ -6115,15 +6400,17 @@ term_t mk_bvarith_pprod(term_manager_t *mngr, pprod_t *p, uint32_t n, const term */ term_t mk_pprod(term_manager_t *mngr, pprod_t *p, uint32_t n, const term_t *a) { type_t tau; - uint32_t nbits; assert(n > 0); tau = term_type(mngr->terms, a[0]); if (is_arithmetic_type(tau)) { return mk_arith_pprod(mngr, p, n, a); + } else if (is_ff_type(mngr->types, tau)) { + const rational_t *mod = ff_type_size(mngr->types, tau); + return mk_arith_ff_pprod(mngr, p, n, a, mod); } else { - nbits = bv_type_size(mngr->types, tau); + uint32_t nbits = bv_type_size(mngr->types, tau); if (nbits <= 64) { return mk_bvarith64_pprod(mngr, p, n, a, nbits); } else { @@ -6161,6 +6448,31 @@ term_t mk_arith_poly(term_manager_t *mngr, polynomial_t *p, uint32_t n, const te return mk_arith_term(mngr, b); } +/* + * Same thing for a finite field polynomial + */ +term_t mk_arith_ff_poly(term_manager_t *mngr, polynomial_t *p, uint32_t n, const term_t *a, const rational_t *mod) { + rba_buffer_t *b; + term_table_t *tbl; + uint32_t i; + + assert(p->nterms == n); + + tbl = term_manager_get_terms(mngr); + b = term_manager_get_arith_buffer(mngr); + reset_rba_buffer(b); + + for (i=0; imono[i].coeff); + } else { + rba_buffer_add_const_times_term(b, tbl, &p->mono[i].coeff, a[i]); + } + } + + return mk_arith_ff_term(mngr, b, mod); +} + /* * Same thing for a bitvector polynomial (1 to 64bits) diff --git a/src/terms/term_manager.h b/src/terms/term_manager.h index 4a36ac991..255a6b2e7 100644 --- a/src/terms/term_manager.h +++ b/src/terms/term_manager.h @@ -459,6 +459,69 @@ extern term_t mk_arith_ceil(term_manager_t *manager, term_t t); // smallest in extern term_t mk_arith_rdiv(term_manager_t *manager, term_t t1, term_t t2); +/* + * FINITE FIELD ARITHMETIC + */ + +/* + * Finite field arithmetic constant + * - r must be normalized wrt. mod + */ +extern term_t mk_arith_ff_constant(term_manager_t *manager, rational_t *r, rational_t *mod); + +/* + * Convert b to a finite field arithmetic term: + * - b->ptbl must be equal to manager->pprods + * - b may be the same as manager->arith_buffer + * - tau must be a type in manager->types + * - side effect: b is reset + * + * - if b is a constant then a constant finite field is created + * - if b is of the form 1. t then t is returned + * - if b is of the from 1. t_1^d_1 ... t_n^d_n then a power product is returned + * - otherwise a polynomial term is created + */ +extern term_t mk_arith_ff_term(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod); + +/* + * Variant: use the term table + */ +extern term_t mk_direct_arith_ff_term(term_table_t *tbl, rba_buffer_t *b, const rational_t *mod); + +/* + * Create a finite field arithmetic atom from the content of buffer b: + * - b->ptbl must be equal to manager->pprods + * - b may be the same as manager->arith_buffer + * - all functions normalize b first + * - tau must be a type in manager->types + * - side effect: b is reset + */ +extern term_t mk_arith_ff_eq0(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod); // b == 0 +extern term_t mk_arith_ff_neq0(term_manager_t *manager, rba_buffer_t *b, const rational_t *mod); // b != 0 + +/* + * Variant: create an arithmetic atom from term t + */ +extern term_t mk_arith_ff_term_eq0(term_manager_t *manager, term_t t); // t == 0 +extern term_t mk_arith_ff_term_neq0(term_manager_t *manager, term_t t); // t != 0 + +/* + * Binary atoms + * - t1 and t2 must be finite field arithmetic terms in manager->terms + * - t1 and t2 must have the same finite field type tau + */ +extern term_t mk_arith_ff_eq(term_manager_t *manager, term_t t1, term_t t2); // t1 == t2 +extern term_t mk_arith_ff_neq(term_manager_t *manager, term_t t1, term_t t2); // t1 != t2 + +/* + * Variants: direct construction/simplification from a term table + * These functions normalize b then create an atom + * - side effect: b is reset + * If simplify_ite is true, simplifications are enabled + */ +extern term_t mk_direct_arith_ff_eq0(term_table_t *tbl, rba_buffer_t *b, const rational_t *mod, bool simplify_ite); // b == 0 + + /* * BITVECTOR TERMS AND ATOMS */ @@ -602,6 +665,17 @@ extern term_t mk_bvslt(term_manager_t *manager, term_t t1, term_t t2); // t1 < */ extern term_t mk_arith_pprod(term_manager_t *manager, pprod_t *p, uint32_t n, const term_t *a); +/* + * Arithmetic product: + * - p is a power product descriptor: t_0^e_0 ... t_{n-1}^e_{n-1} + * - a is an array of n finite field arithmetic terms + * - this function constructs the term a[0]^e_0 ... a[n-1]^e_{n-1} + * + * IMPORTANT: make sure the total degree is no more than YICES_MAX_DEGREE + * before calling this function. + */ +extern term_t mk_arith_ff_pprod(term_manager_t *mngr, pprod_t *p, uint32_t n, const term_t *a, const rational_t *mod); + /* * Bitvector product: 1 to 64 bits vector * - p is a power product descriptor: t_0^e_0 ... t_{n-1}^e_{n-1} @@ -649,6 +723,13 @@ extern term_t mk_pprod(term_manager_t *manager, pprod_t *p, uint32_t n, const te */ extern term_t mk_arith_poly(term_manager_t *manager, polynomial_t *p, uint32_t n, const term_t *a); +/* + * Finite Field polynomial: same as mk_arith_poly but all elements of a + * must be either const_idx of finite field terms of the same order + * - the order must be the same as the coefficients of p + */ +extern term_t mk_arith_ff_poly(term_manager_t *mngr, polynomial_t *p, uint32_t n, const term_t *a, const rational_t *mod); + /* * Bitvector polynomial: same as mk_arith_poly but all elements of a * must be either const_idx of bitvector terms of the equal size diff --git a/src/terms/term_utils.c b/src/terms/term_utils.c index 2b3258cac..e67328491 100644 --- a/src/terms/term_utils.c +++ b/src/terms/term_utils.c @@ -921,8 +921,44 @@ bool disequal_arith_terms(term_table_t *tbl, term_t x, term_t y, bool check_ite) return false; } +bool disequal_arith_ff_terms(term_table_t *tbl, term_t x, term_t y, bool check_ite) { + term_kind_t kx, ky; + kx = term_kind(tbl, x); + ky = term_kind(tbl, y); + +#ifndef NDEBUG + type_t tau; + tau = term_type(tbl, x); + assert(tau == term_type(tbl, y)); +#endif + if (kx == ARITH_FF_CONSTANT && ky == ARITH_FF_CONSTANT) { + return x != y; // because of hash consing. + } + + if (check_ite) { + // TODO implement simplification + } + + if (kx == ARITH_FF_POLY && ky == ARITH_FF_POLY) { + assert(polynomial_is_integer_mod(poly_term_desc(tbl, x), ff_type_size(tbl->types, tau))); + assert(polynomial_is_integer_mod(poly_term_desc(tbl, y), ff_type_size(tbl->types, tau))); + return disequal_polynomials(poly_term_desc(tbl, x), poly_term_desc(tbl, y)); + } + + if (kx == ARITH_FF_POLY && ky != ARITH_FF_CONSTANT) { + assert(polynomial_is_integer_mod(poly_term_desc(tbl, x), ff_type_size(tbl->types, tau))); + return polynomial_is_const_plus_var(poly_term_desc(tbl, x), y); + } + + if (ky == ARITH_FF_POLY && kx != ARITH_FF_CONSTANT) { + assert(polynomial_is_integer_mod(poly_term_desc(tbl, y), ff_type_size(tbl->types, tau))); + return polynomial_is_const_plus_var(poly_term_desc(tbl, y), x); + } + + return false; +} /* * Bitvectors: x and y are bitvector terms of 1 to 64 bits @@ -1289,6 +1325,27 @@ bool arith_term_is_nonzero(term_table_t *tbl, term_t t, bool check_ite) { } } +bool arith_ff_term_is_nonzero(term_table_t *tbl, term_t t, bool check_ite) { + assert(is_finitefield_term(tbl, t)); + + switch (term_kind(tbl, t)) { + case ARITH_FF_CONSTANT: + return t != ff_zero_term(tbl, term_type(tbl, t)); + + case ITE_SPECIAL: + // TODO implement me + return false; + //return check_ite && term_has_nonzero_finite_domain(tbl, t); + + case ARITH_FF_POLY: + assert(polynomial_is_integer_mod(poly_term_desc(tbl, t), ff_type_size(tbl->types, term_type(tbl, t)))); + return polynomial_is_nonzero(poly_term_desc(tbl, t)); + + default: + return false; + } +} + diff --git a/src/terms/term_utils.h b/src/terms/term_utils.h index 874dac7be..31c4c4346 100644 --- a/src/terms/term_utils.h +++ b/src/terms/term_utils.h @@ -150,7 +150,7 @@ extern bool disequal_terms(term_table_t *tbl, term_t x, term_t y, bool check_ite */ extern bool disequal_bitvector_terms(term_table_t *tbl, term_t x, term_t y); extern bool disequal_arith_terms(term_table_t *tbl, term_t x, term_t y, bool check_ite); - +extern bool disequal_arith_ff_terms(term_table_t *tbl, term_t x, term_t y, bool check_ite); /* * Check whether a[i] can't equal b[i] for all i in 0 .. n-1 @@ -216,6 +216,13 @@ extern bool arith_term_is_negative(term_table_t *tbl, term_t t, bool check_ite); extern bool arith_term_is_nonzero(term_table_t *tbl, term_t t, bool check_ite); +/* + * Check whether t is a non-zero finite field term (incomplete) + * - return true if the checks succeed and determine that t != 0 + * - return false otherwise + */ +extern bool arith_ff_term_is_nonzero(term_table_t *tbl, term_t t, bool check_ite); + /* * BOUNDS ON BITVECTOR TERMS diff --git a/src/terms/terms.c b/src/terms/terms.c index 6724cdaca..ec333d29e 100644 --- a/src/terms/terms.c +++ b/src/terms/terms.c @@ -132,15 +132,15 @@ static void term_table_extend(indexed_table_t *t) { static void term_table_init(term_table_t *table, uint32_t n, type_table_t *ttbl, pprod_table_t *ptbl) { /* The indexed_table_elem_t must be first. */ assert(offsetof(term_desc_t, elem) == 0); - + static const indexed_table_vtbl_t vtbl = { .elem_size = sizeof(term_desc_t), .max_elems = YICES_MAX_TERMS, .extend = term_table_extend }; - + indexed_table_init(&table->terms, n, &vtbl); - + table->mark = allocate_bitvector(n); table->types = ttbl; @@ -759,7 +759,7 @@ typedef struct { * Polynomial * - a polynomial is constructed from a buffer b * and an array of term indices v - * - tau can be int or real + * - tau can be int, real, or a finite field type */ typedef struct { int_hobj_t m; @@ -882,6 +882,10 @@ static uint32_t hash_poly_hobj(poly_term_hobj_t *o) { return hash_rba_buffer(o->b, o->v); } +static uint32_t hash_ff_poly_hobj(poly_term_hobj_t *o) { + return hash_rba_buffer(o->b, o->v); +} + static uint32_t hash_bvpoly_hobj(bvpoly_term_hobj_t *o) { return hash_bvarith_buffer(o->b, o->v); } @@ -926,6 +930,7 @@ static bool eq_rational_hobj(rational_term_hobj_t *o, int32_t i) { table = o->tbl; return kind_for_idx(table, i) == o->tag + && type_for_idx(table, i) == o->tau && q_eq(rational_for_idx(table, i), o->a); } @@ -1056,6 +1061,16 @@ static bool eq_poly_hobj(poly_term_hobj_t *o, int32_t i) { rba_buffer_equal_poly(o->b, o->v, polynomial_for_idx(table, i)); } +static bool eq_ff_poly_hobj(poly_term_hobj_t *o, int32_t i) { + term_table_t *table; + + table = o->tbl; + assert(good_term_idx(table, i)); + + return kind_for_idx(table, i) == ARITH_FF_POLY && + rba_buffer_equal_poly(o->b, o->v, polynomial_for_idx(table, i)); +} + static bool eq_bvpoly_hobj(bvpoly_term_hobj_t *o, int32_t i) { term_table_t *table; @@ -1193,6 +1208,13 @@ static int32_t build_poly_hobj(poly_term_hobj_t *o) { return new_ptr_term(o->tbl, ARITH_POLY, o->tau, p); } +static int32_t build_ff_poly_hobj(poly_term_hobj_t *o) { + polynomial_t *p; + + p = rba_buffer_get_poly(o->b, o->v); + return new_ptr_term(o->tbl, ARITH_FF_POLY, o->tau, p); +} + static int32_t build_bvpoly_hobj(bvpoly_term_hobj_t *o) { bvpoly_t *p; @@ -1755,6 +1777,7 @@ static void delete_term_descriptors(term_table_t *table) { case VARIABLE: case POWER_PRODUCT: case ARITH_EQ_ATOM: + case ARITH_FF_EQ_ATOM: case ARITH_GE_ATOM: case ARITH_IS_INT_ATOM: case ARITH_FLOOR: @@ -1780,6 +1803,7 @@ static void delete_term_descriptors(term_table_t *table) { case ARITH_MOD: case ARITH_DIVIDES_ATOM: case ARITH_ROOT_ATOM: + case ARITH_FF_BINEQ_ATOM: case BV64_CONSTANT: case BV_CONSTANT: case BV_ARRAY: @@ -1803,11 +1827,13 @@ static void delete_term_descriptors(term_table_t *table) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: // Free the rational q_clear(rational_for_idx(table, i)); break; case ARITH_POLY: + case ARITH_FF_POLY: free_polynomial(polynomial_for_idx(table, i)); break; @@ -2391,6 +2417,7 @@ term_t bit_term(term_table_t *table, uint32_t k, term_t bv) { * The type of the result is determined from the x_i's types: * - if all x_i's are int, the result is int * - if some x_i's are int, some are real, the result is real + * - if all x_i's have type (finitefield k), the result has type (finitefield k) * - if all x_i's have type (bitvector k), the result has type (bitvector k) */ term_t pprod_term(term_table_t *table, pprod_t *r) { @@ -2436,6 +2463,37 @@ term_t arith_constant(term_table_t *table, rational_t *a) { return pos_term(i); } +term_t arith_ff_constant(term_table_t *table, rational_t *a, const rational_t *mod) { + type_t tau; + int32_t i; + rational_term_hobj_t rational_hobj; + + // find (or create) the type (_ FiniteField mod) + assert(q_is_integer(mod)); + tau = ff_type_r(table->types, mod); + assert (q_is_pos(mod)); + q_integer_rem(a, mod); + + rational_hobj.m.hash = (hobj_hash_t) hash_rational_hobj; + rational_hobj.m.eq = (hobj_eq_t) eq_rational_hobj; + rational_hobj.m.build = (hobj_build_t) build_rational_hobj; + rational_hobj.tbl = table; + rational_hobj.tag = ARITH_FF_CONSTANT; + rational_hobj.tau = tau; + rational_hobj.a = a; + + i = int_htbl_get_obj(&table->htbl, &rational_hobj.m); + + return pos_term(i); +} + +term_t arith_ff_zero(term_table_t *table, const rational_t *mod) { + rational_t z; + q_init(&z); + term_t rslt = arith_ff_constant(table, &z, mod); + q_clear(&z); + return rslt; +} /* * Atom t == 0 for an arithmetic term t @@ -2445,6 +2503,11 @@ term_t arith_eq_atom(term_table_t *table, term_t t) { } +term_t arith_ff_eq_atom(term_table_t * table, term_t t) { + return unary_term(table, ARITH_FF_EQ_ATOM, bool_type(table->types), t); +} + + /* * Atom (t >= 0) for an arithmetic term t */ @@ -2461,6 +2524,14 @@ term_t arith_bineq_atom(term_table_t *table, term_t left, term_t right) { } +/* + * Equality between two finit field arithmetic terms (left == right) + */ +term_t arith_ff_bineq_atom(term_table_t *table, term_t left, term_t right) { + return binary_term(table, ARITH_FF_BINEQ_ATOM, bool_type(table->types), left, right); +} + + /* * Test for integrality: (is_int x) */ @@ -2753,6 +2824,29 @@ static bool all_integer_terms(term_table_t *table, const term_t *v, uint32_t n) return true; } +#ifndef NDEBUG +/* + * Check whether all terms in array v are of type tau + * - skip const_idx if it's in v (it should be first) + */ +static bool check_term_type(term_table_t *table, const term_t *v, uint32_t n, type_t tau) { + uint32_t i; + + if (n > 0) { + if (v[0] == const_idx) { + v++; + n--; + } + + for (i = 0; i < n; i++) { + if (!compatible_types(table->types, term_type(table, v[i]), tau)) + return false; + } + } + + return true; +} +#endif /* * Auxiliary function: convert power products of subtree rooted at x @@ -2842,6 +2936,65 @@ term_t arith_poly(term_table_t *table, rba_buffer_t *b) { return pos_term(i); } +/* + * Finite field arithmetic term + * - all variables of b must be finite field terms mod m + * - b must be normalized and b->ptbl must be the same as table->ptbl + * - if b contains a non-linear polynomial then the power products that + * occur in p are converted to terms (using pprod_term) + * - then b is turned into a polynomial object a_1 x_1 + ... + a_n x_n, + * where x_i is a term. + * + * SIDE EFFECT: b is reset to zero + */ +term_t arith_ff_poly(term_table_t *table, rba_buffer_t *b, const rational_t *mod) { + int32_t *v; + type_t tau; + int32_t i; + bool all_int; + uint32_t j, n; + poly_term_hobj_t poly_hobj; + + assert(b->ptbl == table->pprods); + assert(rba_buffer_is_mod(b, mod)); + + n = b->nterms; + + /* + * convert the power products to indices + * store the result in ibuffer. + * also check whether all coefficients are integer. + */ + assert(table->ibuffer.size == 0); + + resize_ivector(&table->ibuffer, n + 1); + v = table->ibuffer.data; + all_int = true; + j = convert_rba_tree(table, b, v, &all_int, 0, b->root); + assert(j == n); + assert(all_int); + v[j] = max_idx; + + tau = ff_type_r(table->types, mod); + assert(check_term_type(table, v, n, tau)); + + // hash consing + poly_hobj.m.hash = (hobj_hash_t) hash_ff_poly_hobj; + poly_hobj.m.eq = (hobj_eq_t) eq_ff_poly_hobj; + poly_hobj.m.build = (hobj_build_t) build_ff_poly_hobj; + poly_hobj.tbl = table; + poly_hobj.tau = tau; + poly_hobj.b = b; + poly_hobj.v = v; + + i = int_htbl_get_obj(&table->htbl, &poly_hobj.m); + + // cleanup ibuffer + ivector_reset(&table->ibuffer); + + return pos_term(i); +} + /* * Bitvector polynomials are constructed from a buffer b @@ -2984,7 +3137,7 @@ pprod_t *pprod_for_term(const term_table_t *table, term_t t) { int32_t i; assert(is_pos_term(t) && good_term(table, t)); - assert(is_arithmetic_term(table, t) || is_bitvector_term(table, t)); + assert(is_arithmetic_term(table, t) || is_finitefield_term(table, t) || is_bitvector_term(table, t)); r = var_pp(t); i = index_of(t); @@ -3025,7 +3178,7 @@ uint32_t term_degree(const term_table_t *table, term_t t) { int32_t i; assert(is_pos_term(t) && good_term(table, t)); - assert(is_arithmetic_term(table, t) || is_bitvector_term(table, t)); + assert(is_arithmetic_term(table, t) || is_finitefield_term(table, t) || is_bitvector_term(table, t)); d = 1; i = index_of(t); @@ -3035,16 +3188,17 @@ uint32_t term_degree(const term_table_t *table, term_t t) { break; case ARITH_CONSTANT: + case ARITH_FF_CONSTANT: case BV64_CONSTANT: case BV_CONSTANT: d = 0; break; case ARITH_POLY: - d = main_var_degree(table, - polynomial_main_var(polynomial_for_idx(table, i))); + case ARITH_FF_POLY: + d = main_var_degree(table,polynomial_main_var(polynomial_for_idx(table, i))); break; - + case BV64_POLY: d = main_var_degree(table, bvpoly64_main_var(bvpoly64_for_idx(table, i))); break; @@ -3087,8 +3241,8 @@ bool is_linear_poly(const term_table_t *table, term_t t) { i = index_of(t); switch (kind_for_idx(table, i)) { case ARITH_POLY: - result = not_pprod(table, - polynomial_main_var(polynomial_for_idx(table, i))); + case ARITH_FF_POLY: + result = not_pprod(table, polynomial_main_var(polynomial_for_idx(table, i))); break; case BV64_POLY: @@ -3549,6 +3703,7 @@ static void mark_reachable_terms(term_table_t *table, int32_t ptr, int32_t i) { break; case ARITH_POLY: + case ARITH_FF_POLY: mark_polynomial(table, ptr, polynomial_for_idx(table, i)); break; diff --git a/src/terms/terms.h b/src/terms/terms.h index aaaacdfed..87227bee0 100644 --- a/src/terms/terms.h +++ b/src/terms/terms.h @@ -247,6 +247,7 @@ typedef enum { */ CONSTANT_TERM, // constant of uninterpreted/scalar/boolean types ARITH_CONSTANT, // rational constant + ARITH_FF_CONSTANT, // finite field constant BV64_CONSTANT, // compact bitvector constant (64 bits at most) BV_CONSTANT, // generic bitvector constant (more than 64 bits) @@ -267,6 +268,8 @@ typedef enum { ARITH_ABS, // absolute value ARITH_ROOT_ATOM, // atom (k <= root_count(f) && (x r root_k(f)), for f in Z[x, ...], r in { <, <=, == , !=, >=, > } + ARITH_FF_EQ_ATOM, // atom t == 0 + ITE_TERM, // if-then-else ITE_SPECIAL, // special if-then-else term (NEW: EXPERIMENTAL) APP_TERM, // application of an uninterpreted function @@ -285,6 +288,8 @@ typedef enum { ARITH_MOD, // remainder: (mod x y) is y - x * (div x y) ARITH_DIVIDES_ATOM, // divisibility test: (divides x y) is true if y = n * x for an integer n + ARITH_FF_BINEQ_ATOM, // equality: (t1 == t2) (between two finite field arithmetic terms) + BV_ARRAY, // array of boolean terms BV_DIV, // unsigned division BV_REM, // unsigned remainder @@ -304,6 +309,7 @@ typedef enum { // Polynomials POWER_PRODUCT, // power products: (t1^d1 * ... * t_n^d_n) ARITH_POLY, // polynomial with rational coefficients + ARITH_FF_POLY, // polynomial with fintie field coefficients BV64_POLY, // polynomial with 64bit coefficients BV_POLY, // polynomial with generic bitvector coefficients } term_kind_t; @@ -364,7 +370,7 @@ typedef struct select_term_s { /* - * Comparison relations for arithmetic root atoms.. + * Comparison relations for arithmetic root atoms */ typedef enum { ROOT_ATOM_LT, @@ -730,6 +736,45 @@ extern term_t arith_divides(term_table_t *table, term_t x, term_t y); extern bool arith_poly_is_integer(const term_table_t *table, rba_buffer_t *b); +/* + * FINITE FIELD TERMS + */ + +extern term_t arith_ff_zero(term_table_t *table, const rational_t *mod); + +extern term_t arith_ff_constant(term_table_t *table, rational_t *a, const rational_t *mod); + +/* + * for finite field types zero_term depends on the type of finite field + */ +static inline term_t ff_zero_term(term_table_t *table, type_t ff) { + return arith_ff_zero(table, ff_type_size(table->types, ff)); +} + +/* + * Finite field arithmetic term + * - all variables of b must be finite field terms mod m + * - b must be normalized and b->ptbl must be the same as table->ptbl + * - if b contains a non-linear polynomial then the power products that + * occur in p are converted to terms (using pprod_term) + * - then b is turned into a polynomial object a_1 x_1 + ... + a_n x_n, + * where x_i is a term. + * + * SIDE EFFECT: b is reset to zero + */ +extern term_t arith_ff_poly(term_table_t *table, rba_buffer_t *b, const rational_t *mod); + +/* + * Atom (t == 0) + * - t must be an arithmetic term + */ +extern term_t arith_ff_eq_atom(term_table_t *table, term_t t); + +/* + * Simple equality between two arithmetic terms (left == right) + */ +extern term_t arith_ff_bineq_atom(term_table_t *table, term_t left, term_t right); + /* * BITVECTOR TERMS @@ -1139,8 +1184,7 @@ static inline bool valid_term_idx(const term_table_t *table, int32_t i) { return 0 <= i && i < nterms(table); } -static inline term_desc_t *term_desc(const term_table_t *table, - int32_t i) { +static inline term_desc_t *term_desc(const term_table_t *table, int32_t i) { return indexed_table_elem(term_desc_t, &table->terms, i); } @@ -1279,6 +1323,10 @@ static inline bool is_bitvector_term(const term_table_t *table, term_t t) { return term_type_kind(table, t) == BITVECTOR_TYPE; } +static inline bool is_finitefield_term(const term_table_t *table, term_t t) { + return term_type_kind(table, t) == FF_TYPE; +} + static inline bool is_scalar_term(const term_table_t *table, term_t t) { return term_type_kind(table, t) == SCALAR_TYPE; } @@ -1415,11 +1463,23 @@ static inline rational_t *rational_term_desc(const term_table_t *table, term_t t return rational_for_idx(table, index_of(t)); } +static inline rational_t *finitefield_term_desc(const term_table_t *table, term_t t) { + assert(term_kind(table, t) == ARITH_FF_CONSTANT); + rational_t *q = rational_for_idx(table, index_of(t)); + assert(q_is_integer(q)); + return q; +} + static inline polynomial_t *poly_term_desc(const term_table_t *table, term_t t) { assert(term_kind(table, t) == ARITH_POLY); return polynomial_for_idx(table, index_of(t)); } +static inline polynomial_t *finitefield_poly_term_desc(const term_table_t *table, term_t t) { + assert(term_kind(table, t) == ARITH_FF_POLY); + return polynomial_for_idx(table, index_of(t)); +} + static inline bvconst64_term_t *bvconst64_term_desc(const term_table_t *table, term_t t) { assert(term_kind(table, t) == BV64_CONSTANT); return bvconst64_for_idx(table, index_of(t)); @@ -1497,11 +1557,21 @@ static inline term_t arith_ge_arg(const term_table_t *table, term_t t) { return integer_value_for_idx(table, index_of(t)); } +static inline term_t arith_ff_eq_arg(const term_table_t *table, term_t t) { + assert(term_kind(table, t) == ARITH_FF_EQ_ATOM); + return integer_value_for_idx(table, index_of(t)); +} + static inline root_atom_t *arith_root_atom_desc(const term_table_t *table, term_t t) { assert(term_kind(table, t) == ARITH_ROOT_ATOM); return root_atom_for_idx(table, index_of(t)); } +static inline term_t finitefield_atom_arg(const term_table_t *table, term_t t) { + assert(term_kind(table, t) == ARITH_FF_EQ_ATOM); + return integer_value_for_idx(table, index_of(t)); +} + /* * Other unary terms */ @@ -1525,6 +1595,10 @@ static inline term_t arith_abs_arg(const term_table_t *table, term_t t) { return integer_value_for_idx(table, index_of(t)); } +static inline const rational_t* finitefield_term_order(const term_table_t *table, term_t t) { + assert(is_ff_type(table->types, term_type(table, t))); + return ff_type_size(table->types, term_type(table, t)); +} /* * All the following functions are equivalent to composite_term_desc, but, @@ -1605,6 +1679,11 @@ static inline composite_term_t *arith_divides_atom_desc(const term_table_t *tabl return composite_for_idx(table, index_of(t)); } +static inline composite_term_t *arith_ff_bineq_atom_desc(const term_table_t *table, term_t t) { + assert(term_kind(table, t) == ARITH_FF_BINEQ_ATOM); + return composite_for_idx(table, index_of(t)); +} + static inline composite_term_t *bvarray_term_desc(const term_table_t *table, term_t t) { assert(term_kind(table, t) == BV_ARRAY); return composite_for_idx(table, index_of(t)); diff --git a/src/terms/types.c b/src/terms/types.c index 7b1ce9ef7..e2fd79689 100644 --- a/src/terms/types.c +++ b/src/terms/types.c @@ -273,6 +273,11 @@ static void erase_type(type_table_t *table, type_t i) { case UNINTERPRETED_TYPE: break; + case FF_TYPE: + q_clear((rational_t*)type_desc(table, i)->ptr); + safe_free(type_desc(table, i)->ptr); + break; + case TUPLE_TYPE: case FUNCTION_TYPE: case INSTANCE_TYPE: @@ -530,6 +535,31 @@ static type_t new_bitvector_type(type_table_t *table, uint32_t k) { return i; } +/* + * Add type (FiniteField k) and return its id + * - k must be positive + */ +static type_t new_finite_field_type(type_table_t *table, const rational_t *order) { + assert(q_is_integer(order) && q_is_pos(order)); + + rational_t *mod = safe_malloc(sizeof(rational_t)); + q_init(mod); + q_set(mod, order); + + mpz_t z; + mpz_init(z); + q_get_mpz(mod, z); + + bool small = mpz_fits_ulong_p(z); + type_t i = allocate_type_id(table, FF_TYPE, + /*card=*/small ? mpz_get_ui(z) : UINT32_MAX, + /*depth=*/0, + /*flags=*/small ? SMALL_TYPE_FLAGS : LARGE_TYPE_FLAGS); + type_desc(table, i)->ptr = mod; + + mpz_clear(z); + return i; +} /* * Add a scalar type and return its id @@ -761,6 +791,12 @@ typedef struct bv_type_hobj_s { uint32_t size; } bv_type_hobj_t; +typedef struct ff_type_hobj_s { + int_hobj_t m; + type_table_t *tbl; + const rational_t *order; +} ff_type_hobj_t; + typedef struct tuple_type_hobj_s { int_hobj_t m; type_table_t *tbl; @@ -798,6 +834,11 @@ static uint32_t hash_bv_type(bv_type_hobj_t *p) { return jenkins_hash_pair(p->size, 0, 0x7838abe2); } +static uint32_t hash_ff_type(ff_type_hobj_t *p) { + assert(q_is_integer(p->order)); + return jenkins_hash_pair(q_hash_numerator(p->order), 0, 0x78210bea); +} + static uint32_t hash_tuple_type(tuple_type_hobj_t *p) { return jenkins_hash_intarray2(p->elem, p->n, 0x8193ea92); } @@ -829,6 +870,11 @@ static uint32_t hash_bvtype(int32_t size) { return jenkins_hash_pair(size, 0, 0x7838abe2); } +static uint32_t hash_fftype(rational_t *order) { + assert(q_is_integer(order)); + return jenkins_hash_pair(q_hash_numerator(order), 0, 0x78210bea); +} + static uint32_t hash_tupletype(tuple_type_t *p) { return jenkins_hash_intarray2(p->elem, p->nelem, 0x8193ea92); } @@ -862,6 +908,13 @@ static bool eq_bv_type(bv_type_hobj_t *p, type_t i) { return type_desc(table, i)->kind == BITVECTOR_TYPE && type_desc(table, i)->integer == p->size; } +static bool eq_ff_type(ff_type_hobj_t *p, type_t i) { + type_table_t *table; + + table = p->tbl; + return type_kind(table, i) == FF_TYPE && q_eq(type_desc(table, i)->ptr, p->order); +} + static bool eq_tuple_type(tuple_type_hobj_t *p, type_t i) { type_table_t *table; tuple_type_t *d; @@ -931,6 +984,10 @@ static type_t build_bv_type(bv_type_hobj_t *p) { return new_bitvector_type(p->tbl, p->size); } +static type_t build_ff_type(ff_type_hobj_t *p) { + return new_finite_field_type(p->tbl, p->order); +} + static type_t build_tuple_type(tuple_type_hobj_t *p) { return new_tuple_type(p->tbl, p->n, p->elem); } @@ -1085,6 +1142,36 @@ type_t bv_type(type_table_t *table, uint32_t size) { return int_htbl_get_obj(&table->htbl, &bv_hobj.m); } +/* + * FiniteField type + * - order must be a positive prime + */ +type_t ff_type(type_table_t *table, mpz_t order) { + rational_t mod; + + q_init(&mod); + q_set_mpz(&mod, order); + type_t result = ff_type_r(table, &mod); + q_clear(&mod); + return result; +} + +/* + * The same as above, but accepts a rational_t + */ +type_t ff_type_r(type_table_t *table, const rational_t *order) { + ff_type_hobj_t ff_hobj; + + assert(q_is_integer(order) && q_is_pos(order)); + + ff_hobj.m.hash = (hobj_hash_t) hash_ff_type; + ff_hobj.m.eq = (hobj_eq_t) eq_ff_type; + ff_hobj.m.build = (hobj_build_t) build_ff_type; + ff_hobj.tbl = table; + ff_hobj.order = order; // build_ff_type copies the mod on creation + return int_htbl_get_obj(&table->htbl, &ff_hobj.m); +} + /* * Tuple type */ @@ -1653,6 +1740,7 @@ bool type_matcher_add_constraint(type_matcher_t *matcher, type_t tau, type_t sig case BOOL_TYPE: case INT_TYPE: case BITVECTOR_TYPE: + case FF_TYPE: case SCALAR_TYPE: case UNINTERPRETED_TYPE: // tau is a minimal type to (sigma subtype of tau) is the same as tau == sigma @@ -2005,6 +2093,13 @@ static type_t cheap_sup(type_table_t *table, type_t tau1, type_t tau2) { } break; + case FF_TYPE: + // a finite field of size any is less than any other finite field type + if (ff_type_size_any(table, tau1)) return tau2; + if (ff_type_size_any(table, tau2)) return tau1; + assert(q_neq(ff_type_size(table, tau1), ff_type_size(table, tau2))); // otherwise, it was the same type + return NULL_TYPE; + default: return NULL_TYPE; } @@ -2689,6 +2784,10 @@ static void erase_hcons_type(type_table_t *table, type_t i) { k = hash_bvtype(type_desc(table, i)->integer); break; + case FF_TYPE: + k = hash_fftype((rational_t *)type_desc(table, i)->ptr); + break; + case VARIABLE_TYPE: k = hash_typevar(type_desc(table, i)->integer); break; diff --git a/src/terms/types.h b/src/terms/types.h index cfe97ccd0..f685161e2 100644 --- a/src/terms/types.h +++ b/src/terms/types.h @@ -71,6 +71,7 @@ #include "utils/symbol_tables.h" #include "utils/tuple_hash_map.h" +#include "rationals.h" #include "yices_types.h" @@ -95,6 +96,7 @@ typedef enum { BOOL_TYPE, INT_TYPE, REAL_TYPE, + FF_TYPE, BITVECTOR_TYPE, SCALAR_TYPE, UNINTERPRETED_TYPE, @@ -447,8 +449,7 @@ extern void reset_type_table(type_table_t *table); /* * Return the ith type descriptor. */ -static inline type_desc_t *type_desc(const type_table_t *table, - int32_t i) { +static inline type_desc_t *type_desc(const type_table_t *table, int32_t i) { return indexed_table_elem(type_desc_t, &table->types, i); } @@ -489,6 +490,18 @@ static inline type_t real_type(type_table_t *table) { */ extern type_t bv_type(type_table_t *table, uint32_t size); +/* + * FiniteFiled types + * This requires order to be a positive prime + */ +extern type_t ff_type(type_table_t *table, mpz_t order); + +/* + * FiniteFiled types + * The same as above, but accepts a rational_t + */ +extern type_t ff_type_r(type_table_t *table, const rational_t *order); + /* * Declare a new scalar of cardinality size * Require size > 0. @@ -817,6 +830,21 @@ static inline uint32_t bv_type_size(type_table_t *tbl, type_t i) { return type_desc(tbl, i)->integer; } +// finite field types +static inline bool is_ff_type(type_table_t *tbl, type_t i) { + return type_kind(tbl, i) == FF_TYPE; +} + +static inline const rational_t* ff_type_size(type_table_t *tbl, type_t i) { + assert(is_ff_type(tbl, i)); + return (const rational_t*)type_desc(tbl, i)->ptr; +} + +static inline bool ff_type_size_any(type_table_t *tbl, type_t i) { + assert(is_ff_type(tbl, i)); + return q_is_minus_one(type_desc(tbl, i)->ptr); +} + // uninterpreted types static inline bool is_uninterpreted_type(type_table_t *tbl, type_t i) { return type_kind(tbl, i) == UNINTERPRETED_TYPE; diff --git a/src/utils/string_buffers.c b/src/utils/string_buffers.c index ca06ab9e0..453151d69 100644 --- a/src/utils/string_buffers.c +++ b/src/utils/string_buffers.c @@ -204,7 +204,7 @@ void string_buffer_append_mpq(string_buffer_t *s, mpq_t q) { s->index += strlen(s0); } -void string_buffer_append_rational(string_buffer_t *s, rational_t *r) { +void string_buffer_append_rational(string_buffer_t *s, const rational_t *r) { if (is_ratgmp(r)) { string_buffer_append_mpq(s, get_gmp(r)); } else { diff --git a/src/utils/string_buffers.h b/src/utils/string_buffers.h index 081eb0530..d75c4f72f 100644 --- a/src/utils/string_buffers.h +++ b/src/utils/string_buffers.h @@ -83,7 +83,7 @@ extern void string_buffer_append_uint32(string_buffer_t *s, uint32_t x); extern void string_buffer_append_double(string_buffer_t *s, double x); extern void string_buffer_append_mpz(string_buffer_t *s, mpz_t z); extern void string_buffer_append_mpq(string_buffer_t *s, mpq_t q); -extern void string_buffer_append_rational(string_buffer_t *s, rational_t *q); +extern void string_buffer_append_rational(string_buffer_t *s, const rational_t *q); /* * bv = bitvector, n = size in bits diff --git a/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2 b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2 new file mode 100644 index 000000000..51bf78526 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2 @@ -0,0 +1,45 @@ +(set-info :smt-lib-version 2.6) +(set-info :category "crafted") +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 52435875175126190479447740508185965837690552500527637822603658699938581184513)) +(declare-fun _2_alt () FF0) +(declare-fun _1_alt () FF0) +(declare-fun _0_alt () FF0) +(declare-fun _2 () FF0) +(declare-fun _1 () FF0) +(declare-fun _0 () FF0) +(declare-fun out_alt () FF0) +(declare-fun out () FF0) +(assert + (let ((let0 _2_alt)) + (let ((let1 _1_alt)) + (let ((let2 _0_alt)) + (let ((let3 (ff.mul let2 let1))) + (let ((let4 (= let3 let0))) + (let ((let5 (ff.mul let1 let1))) + (let ((let6 (= let5 let1))) + (let ((let7 (ff.mul let2 let2))) + (let ((let8 (= let7 let2))) + (let ((let9 (and let8 let6 let4))) + (let ((let10 _2)) + (let ((let11 _1)) + (let ((let12 _0)) + (let ((let13 (ff.mul let12 let11))) + (let ((let14 (= let13 let10))) + (let ((let15 (ff.mul let11 let11))) + (let ((let16 (= let15 let11))) + (let ((let17 (ff.mul let12 let12))) + (let ((let18 (= let17 let12))) + (let ((let19 (and let18 let16 let14))) + (let ((let20 out_alt)) + (let ((let21 out)) + (let ((let22 (= let21 let20))) + (let ((let23 (not let22))) + (let ((let24 (= let12 let2))) + (let ((let25 (= let11 let1))) + (let ((let26 (and let25 let24))) + (let ((let27 (and let26 let23 let19 let9))) + let27 +)))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2.gold b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-000t-ff-zokref-255b-ands.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2 b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2 new file mode 100644 index 000000000..6ec94c81b --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2 @@ -0,0 +1,61 @@ +(set-info :smt-lib-version 2.6) +(set-info :category "crafted") +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 2053)) +(declare-fun a_n2_alt () FF0) +(declare-fun b_n1_alt () FF0) +(declare-fun is_zero_n4_alt () FF0) +(declare-fun is_zero_inv_n3_alt () FF0) +(declare-fun a_n2 () FF0) +(declare-fun b_n1 () FF0) +(declare-fun is_zero_n4 () FF0) +(declare-fun is_zero_inv_n3 () FF0) +(declare-fun return_n0_alt () FF0) +(declare-fun return_n0 () FF0) +(assert + (let ((let0 (as ff0 FF0))) + (let ((let1 (as ff5 FF0))) + (let ((let2 (as ff2050 FF0))) + (let ((let3 a_n2_alt)) + (let ((let4 (ff.mul let3 let2))) + (let ((let5 (as ff2051 FF0))) + (let ((let6 b_n1_alt)) + (let ((let7 (ff.mul let6 let5))) + (let ((let8 (ff.add let7 let4 let1))) + (let ((let9 is_zero_n4_alt)) + (let ((let10 (ff.mul let9 let8))) + (let ((let11 (= let10 let0))) + (let ((let12 (as ff1 FF0))) + (let ((let13 (as ff2052 FF0))) + (let ((let14 (ff.mul let9 let13))) + (let ((let15 (ff.add let14 let12))) + (let ((let16 is_zero_inv_n3_alt)) + (let ((let17 (ff.mul let16 let8))) + (let ((let18 (= let17 let15))) + (let ((let19 (and let18 let11))) + (let ((let20 a_n2)) + (let ((let21 (ff.mul let20 let2))) + (let ((let22 b_n1)) + (let ((let23 (ff.mul let22 let5))) + (let ((let24 (ff.add let23 let21 let1))) + (let ((let25 is_zero_n4)) + (let ((let26 (ff.mul let25 let24))) + (let ((let27 (= let26 let0))) + (let ((let28 (ff.mul let25 let13))) + (let ((let29 (ff.add let28 let12))) + (let ((let30 is_zero_inv_n3)) + (let ((let31 (ff.mul let30 let24))) + (let ((let32 (= let31 let29))) + (let ((let33 (and let32 let27))) + (let ((let34 return_n0_alt)) + (let ((let35 return_n0)) + (let ((let36 (= let35 let34))) + (let ((let37 (not let36))) + (let ((let38 (= let20 let3))) + (let ((let39 (= let22 let6))) + (let ((let40 (and let39 let38))) + (let ((let41 (and let40 let37 let33 let19))) + let41 +)))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2.gold b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-deterministic-last-02v-004t-ff-circ-12b-0s.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2 b/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2 new file mode 100644 index 000000000..538c244c0 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2 @@ -0,0 +1,53 @@ +(set-info :smt-lib-version 2.6) +(set-info :category "crafted") +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 17)) +(declare-fun a () Bool) +(declare-fun b () Bool) +(declare-fun return_n0 () FF0) +(declare-fun mul_n4 () FF0) +(declare-fun a_n2 () FF0) +(declare-fun b_n1 () FF0) +(declare-fun mul_n3 () FF0) +(assert + (let ((let0 a)) + (let ((let1 b)) + (let ((let2 (=> let1 let0))) + (let ((let3 (not let2))) + (let ((let4 (ite let2 let1 let0))) + (let ((let5 (and let4 let3))) + (let ((let6 return_n0)) + (let ((let7 (as ff1 FF0))) + (let ((let8 (= let7 let6))) + (let ((let9 (= let8 let5))) + (let ((let10 (as ff0 FF0))) + (let ((let11 (= let10 let6))) + (let ((let12 (or let8 let11))) + (let ((let13 (and let12 let9))) + (let ((let14 mul_n4)) + (let ((let15 (as ff16 FF0))) + (let ((let16 a_n2)) + (let ((let17 (ff.mul let16 let15))) + (let ((let18 b_n1)) + (let ((let19 (ff.add let18 let17))) + (let ((let20 mul_n3)) + (let ((let21 (ff.mul let20 let15))) + (let ((let22 (ff.add let21 let7))) + (let ((let23 (ff.mul let22 let19))) + (let ((let24 (= let23 let14))) + (let ((let25 (ff.add let17 let7))) + (let ((let26 (ff.mul let18 let25))) + (let ((let27 (= let26 let20))) + (let ((let28 (and let27 let24))) + (let ((let29 (ite let0 let7 let10))) + (let ((let30 (= let16 let29))) + (let ((let31 (ite let1 let7 let10))) + (let ((let32 (= let18 let31))) + (let ((let33 (and let32 let30))) + (let ((let34 (and let33 let28))) + (let ((let35 (=> let34 let13))) + (let ((let36 (not let35))) + let36 +))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2.gold b/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-sound-last-02v-004t-ff-circ-5b-0s.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2 b/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2 new file mode 100644 index 000000000..ddf7cb8f3 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2 @@ -0,0 +1,56 @@ +(set-info :smt-lib-version 2.6) +(set-info :category "crafted") +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 17)) +(declare-fun a () Bool) +(declare-fun b () Bool) +(declare-fun return_n0 () FF0) +(declare-fun mul_n3 () FF0) +(declare-fun a_n2 () FF0) +(declare-fun mul_n4 () FF0) +(declare-fun b_n1 () FF0) +(assert + (let ((let0 a)) + (let ((let1 b)) + (let ((let2 (=> let1 let0))) + (let ((let3 (not let2))) + (let ((let4 (ite let2 let1 let0))) + (let ((let5 (and let4 let3))) + (let ((let6 return_n0)) + (let ((let7 (as ff1 FF0))) + (let ((let8 (= let7 let6))) + (let ((let9 (= let8 let5))) + (let ((let10 (as ff0 FF0))) + (let ((let11 (= let10 let6))) + (let ((let12 (or let8 let11))) + (let ((let13 (and let12 let9))) + (let ((let14 mul_n3)) + (let ((let15 a_n2)) + (let ((let16 mul_n4)) + (let ((let17 (ff.add let16 let15))) + (let ((let18 (ff.mul let17 let14))) + (let ((let19 (= let18 let6))) + (let ((let20 (as ff16 FF0))) + (let ((let21 (ff.mul let15 let20))) + (let ((let22 b_n1)) + (let ((let23 (ff.add let22 let21))) + (let ((let24 (ff.mul let14 let20))) + (let ((let25 (ff.add let24 let7))) + (let ((let26 (ff.mul let25 let23))) + (let ((let27 (= let26 let16))) + (let ((let28 (ff.add let21 let7))) + (let ((let29 (ff.mul let22 let28))) + (let ((let30 (= let29 let14))) + (let ((let31 (and let30 let27 let19))) + (let ((let32 (ite let0 let7 let10))) + (let ((let33 (= let15 let32))) + (let ((let34 (ite let1 let7 let10))) + (let ((let35 (= let22 let34))) + (let ((let36 (and let35 let33))) + (let ((let37 (and let36 let31))) + (let ((let38 (=> let37 let13))) + (let ((let39 (not let38))) + let39 +)))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2.gold b/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2.gold new file mode 100644 index 000000000..3f65111b0 --- /dev/null +++ b/tests/regress/mcsat/ff/compilation-sound-none-02v-004t-ff-circ-5b-0s.smt2.gold @@ -0,0 +1 @@ +unsat diff --git a/tests/regress/mcsat/ff/invalid-fieldsize.smt2 b/tests/regress/mcsat/ff/invalid-fieldsize.smt2 new file mode 100644 index 000000000..17ab9f2dc --- /dev/null +++ b/tests/regress/mcsat/ff/invalid-fieldsize.smt2 @@ -0,0 +1,8 @@ +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +; 6 is not prime +(define-sort FF0 () (_ FiniteField 6)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) + +(assert (= (ff.add x0 x1) (as ff0 FF0))) diff --git a/tests/regress/mcsat/ff/invalid-fieldsize.smt2.gold b/tests/regress/mcsat/ff/invalid-fieldsize.smt2.gold new file mode 100644 index 000000000..4b1e25d32 --- /dev/null +++ b/tests/regress/mcsat/ff/invalid-fieldsize.smt2.gold @@ -0,0 +1 @@ +(error "at line 4, column 36: invalid finite field order") diff --git a/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2 b/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2 new file mode 100644 index 000000000..8a28b156a --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2 @@ -0,0 +1,74 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 13)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(assert + (let ((let0 (ff.mul (as ff12 FF0) x1 x3 x3))) + (let ((let1 (ff.mul (as ff6 FF0) x4 x6 x7))) + (let ((let2 (ff.mul (as ff3 FF0) x1 x5))) + (let ((let3 (ff.mul (as ff10 FF0) x3 x5))) + (let ((let4 (ff.mul (as ff12 FF0) x2 x7))) + (let ((let5 (ff.add let0 let1 let2 let3 let4))) + (let ((let6 (= let5 (as ff0 FF0)))) + (let ((let7 (ff.mul (as ff11 FF0) x1 x3 x3))) + (let ((let8 (ff.mul x0 x4 x5))) + (let ((let9 (ff.mul (as ff7 FF0) x0 x0 x7))) + (let ((let10 (ff.mul (as ff12 FF0) x1 x1 x7))) + (let ((let11 (ff.mul (as ff12 FF0) x2))) + (let ((let12 (as ff1 FF0))) + (let ((let13 (ff.add let7 let8 let9 let10 let11 let12))) + (let ((let14 (= let13 (as ff0 FF0)))) + (let ((let15 (ff.mul (as ff3 FF0) x0 x0 x1))) + (let ((let16 (ff.mul (as ff2 FF0) x0 x4 x4))) + (let ((let17 (ff.mul (as ff2 FF0) x2 x7 x7))) + (let ((let18 (ff.mul (as ff8 FF0) x7 x7 x7))) + (let ((let19 (ff.mul x1 x7))) + (let ((let20 (ff.add let15 let16 let17 let18 let19))) + (let ((let21 (= let20 (as ff0 FF0)))) + (let ((let22 (ff.mul (as ff7 FF0) x4 x5 x6))) + (let ((let23 (ff.mul (as ff2 FF0) x2 x3 x7))) + (let ((let24 (ff.mul (as ff9 FF0) x3 x5 x7))) + (let ((let25 (ff.mul (as ff3 FF0) x3 x7 x7))) + (let ((let26 (ff.mul (as ff5 FF0) x3 x4))) + (let ((let27 (ff.add let22 let23 let24 let25 let26))) + (let ((let28 (= let27 (as ff0 FF0)))) + (let ((let29 (ff.mul (as ff10 FF0) x3 x4 x5))) + (let ((let30 (ff.mul (as ff7 FF0) x1 x5 x5))) + (let ((let31 (ff.mul (as ff8 FF0) x0 x1))) + (let ((let32 (ff.mul (as ff3 FF0) x1 x7))) + (let ((let33 (ff.add let29 let30 let31 let32))) + (let ((let34 (= let33 (as ff0 FF0)))) + (let ((let35 (ff.mul (as ff9 FF0) x0 x0 x3))) + (let ((let36 (ff.mul (as ff12 FF0) x2 x2 x4))) + (let ((let37 (ff.mul (as ff5 FF0) x4 x4 x5))) + (let ((let38 (ff.mul x4 x5 x7))) + (let ((let39 (ff.mul (as ff4 FF0) x1 x6 x7))) + (let ((let40 (as ff1 FF0))) + (let ((let41 (ff.add let35 let36 let37 let38 let39 let40))) + (let ((let42 (= let41 (as ff0 FF0)))) + (let ((let43 (ff.mul (as ff8 FF0) x0 x3 x3))) + (let ((let44 (ff.mul (as ff8 FF0) x1 x3 x7))) + (let ((let45 (ff.mul (as ff10 FF0) x2 x3 x7))) + (let ((let46 (ff.mul (as ff7 FF0) x0 x0))) + (let ((let47 (ff.mul (as ff12 FF0) x2 x2))) + (let ((let48 (ff.add let43 let44 let45 let46 let47))) + (let ((let49 (= let48 (as ff0 FF0)))) + (let ((let50 (ff.mul (as ff8 FF0) x0 x2 x6))) + (let ((let51 (ff.mul (as ff7 FF0) x2 x2 x7))) + (let ((let52 (ff.mul (as ff3 FF0) x0 x1))) + (let ((let53 (as ff1 FF0))) + (let ((let54 (ff.add let50 let51 let52 let53))) + (let ((let55 (= let54 (as ff0 FF0)))) + (let ((let56 (and let6 let14 let21 let28 let34 let42 let49 let55))) + let56 +))))))))))))))))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2.gold b/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_13_8_8.001.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2 b/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2 new file mode 100644 index 000000000..f9d870ab0 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2 @@ -0,0 +1,58 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 3)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(assert + (let ((let0 (ff.mul (as ff2 FF0) x1 x1 x2))) + (let ((let1 (ff.mul x0 x4 x7))) + (let ((let2 (ff.mul x1 x6 x7))) + (let ((let3 (ff.mul x5 x6 x7))) + (let ((let4 (as ff1 FF0))) + (let ((let5 (ff.add let0 let1 let2 let3 let4))) + (let ((let6 (= let5 (as ff0 FF0)))) + (let ((let7 (ff.mul x0 x0 x3))) + (let ((let8 (ff.mul x0 x6 x6))) + (let ((let9 x4)) + (let ((let10 (ff.add let7 let8 let9))) + (let ((let11 (= let10 (as ff0 FF0)))) + (let ((let12 (ff.mul x3 x4 x4))) + (let ((let13 (ff.mul x0 x3 x6))) + (let ((let14 (ff.mul x6 x7 x7))) + (let ((let15 (ff.add let12 let13 let14))) + (let ((let16 (= let15 (as ff0 FF0)))) + (let ((let17 (ff.mul x0 x1 x6))) + (let ((let18 (ff.mul x2 x6 x6))) + (let ((let19 (ff.mul (as ff2 FF0) x4 x7 x7))) + (let ((let20 (ff.add let17 let18 let19))) + (let ((let21 (= let20 (as ff0 FF0)))) + (let ((let22 (ff.mul (as ff2 FF0) x1 x3 x4))) + (let ((let23 (ff.mul x1 x4))) + (let ((let24 (ff.add let22 let23))) + (let ((let25 (= let24 (as ff0 FF0)))) + (let ((let26 (ff.mul (as ff2 FF0) x1 x5 x7))) + (let ((let27 x3)) + (let ((let28 (ff.add let26 let27))) + (let ((let29 (= let28 (as ff0 FF0)))) + (let ((let30 (ff.mul x2 x3 x3))) + (let ((let31 (ff.mul (as ff2 FF0) x1 x3 x6))) + (let ((let32 (ff.mul (as ff2 FF0) x5 x7 x7))) + (let ((let33 (ff.add let30 let31 let32))) + (let ((let34 (= let33 (as ff0 FF0)))) + (let ((let35 (ff.mul (as ff2 FF0) x1 x6 x7))) + (let ((let36 (ff.mul (as ff2 FF0) x0 x3))) + (let ((let37 x1)) + (let ((let38 (ff.add let35 let36 let37))) + (let ((let39 (= let38 (as ff0 FF0)))) + (let ((let40 (and let6 let11 let16 let21 let25 let29 let34 let39))) + let40 +))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2.gold b/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2.gold new file mode 100644 index 000000000..3f65111b0 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.004.smt2.gold @@ -0,0 +1 @@ +unsat diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2 b/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2 new file mode 100644 index 000000000..84b05b816 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2 @@ -0,0 +1,61 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 3)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(assert + (let ((let0 (ff.mul (as ff2 FF0) x1 x4 x7))) + (let ((let1 (ff.mul x4 x7))) + (let ((let2 (ff.add let0 let1))) + (let ((let3 (= let2 (as ff0 FF0)))) + (let ((let4 (ff.mul x1 x1 x6))) + (let ((let5 (ff.mul (as ff2 FF0) x0 x6 x7))) + (let ((let6 (ff.mul (as ff2 FF0) x2 x7))) + (let ((let7 (ff.mul (as ff2 FF0) x0))) + (let ((let8 (ff.add let4 let5 let6 let7))) + (let ((let9 (= let8 (as ff0 FF0)))) + (let ((let10 (ff.mul x1 x2 x3))) + (let ((let11 (ff.mul (as ff2 FF0) x2 x2 x4))) + (let ((let12 (ff.mul (as ff2 FF0) x3 x6 x6))) + (let ((let13 (ff.mul x6 x6 x6))) + (let ((let14 (as ff1 FF0))) + (let ((let15 (ff.add let10 let11 let12 let13 let14))) + (let ((let16 (= let15 (as ff0 FF0)))) + (let ((let17 (ff.mul (as ff2 FF0) x0 x0))) + (let ((let18 (ff.mul x3 x6))) + (let ((let19 (ff.mul (as ff2 FF0) x7))) + (let ((let20 (ff.add let17 let18 let19))) + (let ((let21 (= let20 (as ff0 FF0)))) + (let ((let22 (ff.mul (as ff2 FF0) x2 x4 x4))) + (let ((let23 (ff.mul x2 x3 x6))) + (let ((let24 (ff.add let22 let23))) + (let ((let25 (= let24 (as ff0 FF0)))) + (let ((let26 (ff.mul (as ff2 FF0) x0 x3 x3))) + (let ((let27 (ff.mul (as ff2 FF0) x2 x5 x6))) + (let ((let28 (ff.mul (as ff2 FF0) x0 x4 x7))) + (let ((let29 (as ff1 FF0))) + (let ((let30 (ff.add let26 let27 let28 let29))) + (let ((let31 (= let30 (as ff0 FF0)))) + (let ((let32 (ff.mul x1 x3 x6))) + (let ((let33 (ff.mul x2 x6 x6))) + (let ((let34 (ff.mul x0 x3 x7))) + (let ((let35 (ff.add let32 let33 let34))) + (let ((let36 (= let35 (as ff0 FF0)))) + (let ((let37 (ff.mul (as ff2 FF0) x0 x3 x5))) + (let ((let38 (ff.mul x3 x5 x7))) + (let ((let39 (ff.mul x1 x6 x7))) + (let ((let40 (ff.mul (as ff2 FF0) x0 x7))) + (let ((let41 (ff.add let37 let38 let39 let40))) + (let ((let42 (= let41 (as ff0 FF0)))) + (let ((let43 (and let3 let9 let16 let21 let25 let31 let36 let42))) + let43 +)))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2.gold b/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.008.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2 b/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2 new file mode 100644 index 000000000..025b2b23b --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2 @@ -0,0 +1,63 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 3)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(assert + (let ((let0 (ff.mul x2 x2 x2))) + (let ((let1 (ff.mul x0 x2 x3))) + (let ((let2 (ff.mul x1 x4 x5))) + (let ((let3 (ff.mul (as ff2 FF0) x0 x2 x6))) + (let ((let4 (ff.mul (as ff2 FF0) x0 x5))) + (let ((let5 (as ff1 FF0))) + (let ((let6 (ff.add let0 let1 let2 let3 let4 let5))) + (let ((let7 (= let6 (as ff0 FF0)))) + (let ((let8 (ff.mul (as ff2 FF0) x2 x4 x5))) + (let ((let9 (ff.mul x2 x2 x6))) + (let ((let10 (ff.mul (as ff2 FF0) x0 x1 x7))) + (let ((let11 (ff.mul (as ff2 FF0) x2 x5 x7))) + (let ((let12 (ff.mul x2 x7))) + (let ((let13 (ff.add let8 let9 let10 let11 let12))) + (let ((let14 (= let13 (as ff0 FF0)))) + (let ((let15 (ff.mul (as ff2 FF0) x6 x6 x6))) + (let ((let16 (ff.mul (as ff2 FF0) x3 x7 x7))) + (let ((let17 (ff.mul (as ff2 FF0) x1 x5))) + (let ((let18 (ff.mul (as ff2 FF0) x1 x6))) + (let ((let19 (ff.add let15 let16 let17 let18))) + (let ((let20 (= let19 (as ff0 FF0)))) + (let ((let21 (ff.mul x2 x2 x5))) + (let ((let22 (ff.mul x4 x7))) + (let ((let23 (ff.add let21 let22))) + (let ((let24 (= let23 (as ff0 FF0)))) + (let ((let25 (ff.mul x0 x0 x2))) + (let ((let26 (= let25 (as ff0 FF0)))) + (let ((let27 (ff.mul (as ff2 FF0) x0 x3 x7))) + (let ((let28 (ff.mul (as ff2 FF0) x2 x4 x7))) + (let ((let29 (as ff2 FF0))) + (let ((let30 (ff.add let27 let28 let29))) + (let ((let31 (= let30 (as ff0 FF0)))) + (let ((let32 (ff.mul (as ff2 FF0) x1 x3 x4))) + (let ((let33 (ff.mul (as ff2 FF0) x6 x6 x6))) + (let ((let34 (ff.mul x2 x2))) + (let ((let35 (ff.mul (as ff2 FF0) x4 x4))) + (let ((let36 (ff.mul (as ff2 FF0) x4 x6))) + (let ((let37 (ff.add let32 let33 let34 let35 let36))) + (let ((let38 (= let37 (as ff0 FF0)))) + (let ((let39 (ff.mul (as ff2 FF0) x4 x5 x5))) + (let ((let40 (ff.mul (as ff2 FF0) x1 x2 x6))) + (let ((let41 (ff.mul x5 x6 x6))) + (let ((let42 (ff.mul x0 x4))) + (let ((let43 (ff.add let39 let40 let41 let42))) + (let ((let44 (= let43 (as ff0 FF0)))) + (let ((let45 (and let7 let14 let20 let24 let26 let31 let38 let44))) + let45 +)))))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2.gold b/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2.gold new file mode 100644 index 000000000..3f65111b0 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_i_3_8_8.016.smt2.gold @@ -0,0 +1 @@ +unsat diff --git a/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2 b/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2 new file mode 100644 index 000000000..5d0f47e6f --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2 @@ -0,0 +1,96 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 13)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(declare-fun x8 () FF0) +(declare-fun x9 () FF0) +(declare-fun x10 () FF0) +(declare-fun x11 () FF0) +(declare-fun x12 () FF0) +(declare-fun x13 () FF0) +(declare-fun x14 () FF0) +(declare-fun x15 () FF0) +(declare-fun x16 () FF0) +(declare-fun x17 () FF0) +(declare-fun x18 () FF0) +(declare-fun x19 () FF0) +(declare-fun x20 () FF0) +(declare-fun x21 () FF0) +(declare-fun x22 () FF0) +(declare-fun x23 () FF0) +(declare-fun x24 () FF0) +(declare-fun x25 () FF0) +(declare-fun x26 () FF0) +(declare-fun x27 () FF0) +(declare-fun x28 () FF0) +(declare-fun x29 () FF0) +(declare-fun x30 () FF0) +(declare-fun x31 () FF0) +(assert + (let ((let0 x20)) + (let ((let1 (as ff3 FF0))) + (let ((let2 (ff.add let0 let1))) + (let ((let3 (= let2 (as ff0 FF0)))) + (let ((let4 (ff.mul x15 x15 x15 x26 x26 x26))) + (let ((let5 (ff.mul (as ff12 FF0) x15 x15 x15 x26 x26))) + (let ((let6 (ff.mul (as ff5 FF0) x15 x15 x26 x26 x26))) + (let ((let7 (ff.mul (as ff6 FF0) x15 x15 x15 x26))) + (let ((let8 (ff.mul (as ff8 FF0) x15 x15 x26 x26))) + (let ((let9 (ff.mul (as ff11 FF0) x15 x26 x26 x26))) + (let ((let10 (ff.mul (as ff4 FF0) x15 x15 x26))) + (let ((let11 (ff.mul (as ff2 FF0) x15 x26 x26))) + (let ((let12 (ff.mul (as ff2 FF0) x26 x26 x26))) + (let ((let13 (ff.mul x15 x26))) + (let ((let14 (ff.mul (as ff11 FF0) x26 x26))) + (let ((let15 (ff.mul (as ff12 FF0) x26))) + (let ((let16 (ff.add let4 let5 let6 let7 let8 let9 let10 let11 let12 let13 let14 let15))) + (let ((let17 (= let16 (as ff0 FF0)))) + (let ((let18 (ff.mul x20 x20 x20))) + (let ((let19 (ff.mul (as ff7 FF0) x20 x20))) + (let ((let20 (ff.mul (as ff10 FF0) x20))) + (let ((let21 (ff.add let18 let19 let20))) + (let ((let22 (= let21 (as ff0 FF0)))) + (let ((let23 x1)) + (let ((let24 (as ff4 FF0))) + (let ((let25 (ff.add let23 let24))) + (let ((let26 (= let25 (as ff0 FF0)))) + (let ((let27 (ff.mul x8 x8 x20))) + (let ((let28 (ff.mul (as ff6 FF0) x8 x8))) + (let ((let29 (ff.mul (as ff3 FF0) x8 x20))) + (let ((let30 (ff.mul (as ff5 FF0) x8))) + (let ((let31 (ff.mul (as ff8 FF0) x20))) + (let ((let32 (as ff9 FF0))) + (let ((let33 (ff.add let27 let28 let29 let30 let31 let32))) + (let ((let34 (= let33 (as ff0 FF0)))) + (let ((let35 (ff.mul x25 x25 x27 x27))) + (let ((let36 (ff.mul (as ff9 FF0) x25 x25 x27))) + (let ((let37 (ff.mul x25 x27 x27))) + (let ((let38 (ff.mul (as ff3 FF0) x25 x25))) + (let ((let39 (ff.mul (as ff9 FF0) x25 x27))) + (let ((let40 (ff.mul (as ff9 FF0) x27 x27))) + (let ((let41 (ff.mul (as ff3 FF0) x25))) + (let ((let42 (ff.mul (as ff3 FF0) x27))) + (let ((let43 (as ff1 FF0))) + (let ((let44 (ff.add let35 let36 let37 let38 let39 let40 let41 let42 let43))) + (let ((let45 (= let44 (as ff0 FF0)))) + (let ((let46 x0)) + (let ((let47 (as ff8 FF0))) + (let ((let48 (ff.add let46 let47))) + (let ((let49 (= let48 (as ff0 FF0)))) + (let ((let50 x28)) + (let ((let51 (as ff4 FF0))) + (let ((let52 (ff.add let50 let51))) + (let ((let53 (= let52 (as ff0 FF0)))) + (let ((let54 (and let3 let17 let22 let26 let34 let45 let49 let53))) + let54 +))))))))))))))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2.gold b/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2.gold new file mode 100644 index 000000000..3f65111b0 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_13_32_8.012.smt2.gold @@ -0,0 +1 @@ +unsat diff --git a/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2 b/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2 new file mode 100644 index 000000000..6b4afa728 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2 @@ -0,0 +1,211 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 211)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(declare-fun x8 () FF0) +(declare-fun x9 () FF0) +(declare-fun x10 () FF0) +(declare-fun x11 () FF0) +(declare-fun x12 () FF0) +(declare-fun x13 () FF0) +(declare-fun x14 () FF0) +(declare-fun x15 () FF0) +(assert + (let ((let0 x10)) + (let ((let1 (as ff200 FF0))) + (let ((let2 (ff.add let0 let1))) + (let ((let3 (= let2 (as ff0 FF0)))) + (let ((let4 (ff.mul x5 x5))) + (let ((let5 (ff.mul (as ff98 FF0) x5))) + (let ((let6 (as ff14 FF0))) + (let ((let7 (ff.add let4 let5 let6))) + (let ((let8 (= let7 (as ff0 FF0)))) + (let ((let9 (ff.mul x5 x6 x6 x6 x15 x15))) + (let ((let10 (ff.mul (as ff186 FF0) x5 x6 x6 x6 x15))) + (let ((let11 (ff.mul (as ff176 FF0) x5 x6 x6 x15 x15))) + (let ((let12 (ff.mul (as ff105 FF0) x6 x6 x6 x15 x15))) + (let ((let13 (ff.mul (as ff65 FF0) x5 x6 x6 x6))) + (let ((let14 (ff.mul (as ff31 FF0) x5 x6 x6 x15))) + (let ((let15 (ff.mul (as ff118 FF0) x6 x6 x6 x15))) + (let ((let16 (ff.mul (as ff127 FF0) x5 x6 x15 x15))) + (let ((let17 (ff.mul (as ff123 FF0) x6 x6 x15 x15))) + (let ((let18 (ff.mul (as ff46 FF0) x5 x6 x6))) + (let ((let19 (ff.mul (as ff73 FF0) x6 x6 x6))) + (let ((let20 (ff.mul (as ff201 FF0) x5 x6 x15))) + (let ((let21 (ff.mul (as ff90 FF0) x6 x6 x15))) + (let ((let22 (ff.mul (as ff33 FF0) x5 x15 x15))) + (let ((let23 (ff.mul (as ff42 FF0) x6 x15 x15))) + (let ((let24 (ff.mul (as ff26 FF0) x5 x6))) + (let ((let25 (ff.mul (as ff188 FF0) x6 x6))) + (let ((let26 (ff.mul (as ff19 FF0) x5 x15))) + (let ((let27 (ff.mul (as ff5 FF0) x6 x15))) + (let ((let28 (ff.mul (as ff89 FF0) x15 x15))) + (let ((let29 (ff.mul (as ff35 FF0) x5))) + (let ((let30 (ff.mul (as ff198 FF0) x6))) + (let ((let31 (ff.mul (as ff96 FF0) x15))) + (let ((let32 (as ff88 FF0))) + (let ((let33 (ff.add let9 let10 let11 let12 let13 let14 let15 let16 let17 let18 let19 let20 let21 let22 let23 let24 let25 let26 let27 let28 let29 let30 let31 let32))) + (let ((let34 (= let33 (as ff0 FF0)))) + (let ((let35 (ff.mul x1 x1 x1 x14 x14 x14))) + (let ((let36 (ff.mul (as ff46 FF0) x1 x1 x1 x14 x14))) + (let ((let37 (ff.mul (as ff54 FF0) x1 x1 x14 x14 x14))) + (let ((let38 (ff.mul (as ff58 FF0) x1 x1 x1 x14))) + (let ((let39 (ff.mul (as ff163 FF0) x1 x1 x14 x14))) + (let ((let40 (ff.mul (as ff63 FF0) x1 x14 x14 x14))) + (let ((let41 (ff.mul (as ff57 FF0) x1 x1 x1))) + (let ((let42 (ff.mul (as ff178 FF0) x1 x1 x14))) + (let ((let43 (ff.mul (as ff155 FF0) x1 x14 x14))) + (let ((let44 (ff.mul (as ff9 FF0) x14 x14 x14))) + (let ((let45 (ff.mul (as ff124 FF0) x1 x1))) + (let ((let46 (ff.mul (as ff67 FF0) x1 x14))) + (let ((let47 (ff.mul (as ff203 FF0) x14 x14))) + (let ((let48 (ff.mul (as ff4 FF0) x1))) + (let ((let49 (ff.mul (as ff100 FF0) x14))) + (let ((let50 (as ff91 FF0))) + (let ((let51 (ff.add let35 let36 let37 let38 let39 let40 let41 let42 let43 let44 let45 let46 let47 let48 let49 let50))) + (let ((let52 (= let51 (as ff0 FF0)))) + (let ((let53 (ff.mul x1 x1 x3 x3 x3 x12 x12 x12))) + (let ((let54 (ff.mul (as ff107 FF0) x1 x1 x3 x3 x3 x12 x12))) + (let ((let55 (ff.mul (as ff153 FF0) x1 x1 x3 x3 x12 x12 x12))) + (let ((let56 (ff.mul (as ff132 FF0) x1 x3 x3 x3 x12 x12 x12))) + (let ((let57 (ff.mul (as ff199 FF0) x1 x1 x3 x3 x3 x12))) + (let ((let58 (ff.mul (as ff124 FF0) x1 x1 x3 x3 x12 x12))) + (let ((let59 (ff.mul (as ff198 FF0) x1 x3 x3 x3 x12 x12))) + (let ((let60 (ff.mul (as ff139 FF0) x1 x1 x3 x12 x12 x12))) + (let ((let61 (ff.mul (as ff151 FF0) x1 x3 x3 x12 x12 x12))) + (let ((let62 (ff.mul (as ff127 FF0) x3 x3 x3 x12 x12 x12))) + (let ((let63 (ff.mul (as ff13 FF0) x1 x1 x3 x3 x3))) + (let ((let64 (ff.mul (as ff63 FF0) x1 x1 x3 x3 x12))) + (let ((let65 (ff.mul (as ff104 FF0) x1 x3 x3 x3 x12))) + (let ((let66 (ff.mul (as ff103 FF0) x1 x1 x3 x12 x12))) + (let ((let67 (ff.mul (as ff121 FF0) x1 x3 x3 x12 x12))) + (let ((let68 (ff.mul (as ff85 FF0) x3 x3 x3 x12 x12))) + (let ((let69 (ff.mul (as ff134 FF0) x1 x1 x12 x12 x12))) + (let ((let70 (ff.mul (as ff202 FF0) x1 x3 x12 x12 x12))) + (let ((let71 (ff.mul (as ff19 FF0) x3 x3 x12 x12 x12))) + (let ((let72 (ff.mul (as ff90 FF0) x1 x1 x3 x3))) + (let ((let73 (ff.mul (as ff28 FF0) x1 x3 x3 x3))) + (let ((let74 (ff.mul (as ff20 FF0) x1 x1 x3 x12))) + (let ((let75 (ff.mul (as ff87 FF0) x1 x3 x3 x12))) + (let ((let76 (ff.mul (as ff164 FF0) x3 x3 x3 x12))) + (let ((let77 (ff.mul (as ff201 FF0) x1 x1 x12 x12))) + (let ((let78 (ff.mul (as ff92 FF0) x1 x3 x12 x12))) + (let ((let79 (ff.mul (as ff134 FF0) x3 x3 x12 x12))) + (let ((let80 (ff.mul (as ff175 FF0) x1 x12 x12 x12))) + (let ((let81 (ff.mul (as ff140 FF0) x3 x12 x12 x12))) + (let ((let82 (ff.mul (as ff119 FF0) x1 x1 x3))) + (let ((let83 (ff.mul (as ff64 FF0) x1 x3 x3))) + (let ((let84 (ff.mul (as ff174 FF0) x3 x3 x3))) + (let ((let85 (ff.mul (as ff80 FF0) x1 x1 x12))) + (let ((let86 (ff.mul (as ff108 FF0) x1 x3 x12))) + (let ((let87 (ff.mul (as ff194 FF0) x3 x3 x12))) + (let ((let88 (ff.mul (as ff157 FF0) x1 x12 x12))) + (let ((let89 (ff.mul (as ff210 FF0) x3 x12 x12))) + (let ((let90 (ff.mul (as ff138 FF0) x12 x12 x12))) + (let ((let91 (ff.mul (as ff54 FF0) x1 x1))) + (let ((let92 (ff.mul (as ff94 FF0) x1 x3))) + (let ((let93 (ff.mul (as ff36 FF0) x3 x3))) + (let ((let94 (ff.mul (as ff10 FF0) x1 x12))) + (let ((let95 (ff.mul (as ff8 FF0) x3 x12))) + (let ((let96 (ff.mul (as ff207 FF0) x12 x12))) + (let ((let97 (ff.mul (as ff165 FF0) x1))) + (let ((let98 (ff.mul (as ff132 FF0) x3))) + (let ((let99 (ff.mul (as ff32 FF0) x12))) + (let ((let100 (as ff106 FF0))) + (let ((let101 (ff.add let53 let54 let55 let56 let57 let58 let59 let60 let61 let62 let63 let64 let65 let66 let67 let68 let69 let70 let71 let72 let73 let74 let75 let76 let77 let78 let79 let80 let81 let82 let83 let84 let85 let86 let87 let88 let89 let90 let91 let92 let93 let94 let95 let96 let97 let98 let99 let100))) + (let ((let102 (= let101 (as ff0 FF0)))) + (let ((let103 (ff.mul x0 x0 x0))) + (let ((let104 (ff.mul (as ff19 FF0) x0 x0))) + (let ((let105 (ff.mul (as ff37 FF0) x0))) + (let ((let106 (as ff176 FF0))) + (let ((let107 (ff.add let103 let104 let105 let106))) + (let ((let108 (= let107 (as ff0 FF0)))) + (let ((let109 (ff.mul x3 x3 x12 x12 x12 x14 x14))) + (let ((let110 (ff.mul (as ff176 FF0) x3 x3 x12 x12 x12 x14))) + (let ((let111 (ff.mul (as ff144 FF0) x3 x3 x12 x12 x14 x14))) + (let ((let112 (ff.mul (as ff186 FF0) x3 x12 x12 x12 x14 x14))) + (let ((let113 (ff.mul (as ff43 FF0) x3 x3 x12 x12 x12))) + (let ((let114 (ff.mul (as ff24 FF0) x3 x3 x12 x12 x14))) + (let ((let115 (ff.mul (as ff31 FF0) x3 x12 x12 x12 x14))) + (let ((let116 (ff.mul (as ff68 FF0) x3 x3 x12 x14 x14))) + (let ((let117 (ff.mul (as ff198 FF0) x3 x12 x12 x14 x14))) + (let ((let118 (ff.mul (as ff55 FF0) x12 x12 x12 x14 x14))) + (let ((let119 (ff.mul (as ff73 FF0) x3 x3 x12 x12))) + (let ((let120 (ff.mul (as ff191 FF0) x3 x12 x12 x12))) + (let ((let121 (ff.mul (as ff152 FF0) x3 x3 x12 x14))) + (let ((let122 (ff.mul (as ff33 FF0) x3 x12 x12 x14))) + (let ((let123 (ff.mul (as ff185 FF0) x12 x12 x12 x14))) + (let ((let124 (ff.mul (as ff204 FF0) x3 x3 x14 x14))) + (let ((let125 (ff.mul (as ff199 FF0) x3 x12 x14 x14))) + (let ((let126 (ff.mul (as ff113 FF0) x12 x12 x14 x14))) + (let ((let127 (ff.mul (as ff181 FF0) x3 x3 x12))) + (let ((let128 (ff.mul (as ff74 FF0) x3 x12 x12))) + (let ((let129 (ff.mul (as ff44 FF0) x12 x12 x12))) + (let ((let130 (ff.mul (as ff34 FF0) x3 x3 x14))) + (let ((let131 (ff.mul (as ff209 FF0) x3 x12 x14))) + (let ((let132 (ff.mul (as ff54 FF0) x12 x12 x14))) + (let ((let133 (ff.mul (as ff175 FF0) x3 x14 x14))) + (let ((let134 (ff.mul (as ff153 FF0) x12 x14 x14))) + (let ((let135 (ff.mul (as ff121 FF0) x3 x3))) + (let ((let136 (ff.mul (as ff117 FF0) x3 x12))) + (let ((let137 (ff.mul (as ff6 FF0) x12 x12))) + (let ((let138 (ff.mul (as ff205 FF0) x3 x14))) + (let ((let139 (ff.mul (as ff131 FF0) x12 x14))) + (let ((let140 (ff.mul (as ff37 FF0) x14 x14))) + (let ((let141 (ff.mul (as ff140 FF0) x3))) + (let ((let142 (ff.mul (as ff38 FF0) x12))) + (let ((let143 (ff.mul (as ff182 FF0) x14))) + (let ((let144 (as ff114 FF0))) + (let ((let145 (ff.add let109 let110 let111 let112 let113 let114 let115 let116 let117 let118 let119 let120 let121 let122 let123 let124 let125 let126 let127 let128 let129 let130 let131 let132 let133 let134 let135 let136 let137 let138 let139 let140 let141 let142 let143 let144))) + (let ((let146 (= let145 (as ff0 FF0)))) + (let ((let147 (ff.mul x3 x3 x8 x8 x13 x13 x13))) + (let ((let148 (ff.mul (as ff161 FF0) x3 x3 x8 x8 x13 x13))) + (let ((let149 (ff.mul (as ff178 FF0) x3 x3 x8 x13 x13 x13))) + (let ((let150 (ff.mul (as ff4 FF0) x3 x8 x8 x13 x13 x13))) + (let ((let151 (ff.mul (as ff121 FF0) x3 x3 x8 x8 x13))) + (let ((let152 (ff.mul (as ff173 FF0) x3 x3 x8 x13 x13))) + (let ((let153 (ff.mul (as ff11 FF0) x3 x8 x8 x13 x13))) + (let ((let154 (ff.mul (as ff69 FF0) x3 x3 x13 x13 x13))) + (let ((let155 (ff.mul (as ff79 FF0) x3 x8 x13 x13 x13))) + (let ((let156 (ff.mul (as ff151 FF0) x8 x8 x13 x13 x13))) + (let ((let157 (ff.mul (as ff36 FF0) x3 x3 x8 x8))) + (let ((let158 (ff.mul (as ff16 FF0) x3 x3 x8 x13))) + (let ((let159 (ff.mul (as ff62 FF0) x3 x8 x8 x13))) + (let ((let160 (ff.mul (as ff137 FF0) x3 x3 x13 x13))) + (let ((let161 (ff.mul (as ff59 FF0) x3 x8 x13 x13))) + (let ((let162 (ff.mul (as ff46 FF0) x8 x8 x13 x13))) + (let ((let163 (ff.mul (as ff65 FF0) x3 x13 x13 x13))) + (let ((let164 (ff.mul (as ff81 FF0) x8 x13 x13 x13))) + (let ((let165 (ff.mul (as ff78 FF0) x3 x3 x8))) + (let ((let166 (ff.mul (as ff144 FF0) x3 x8 x8))) + (let ((let167 (ff.mul (as ff120 FF0) x3 x3 x13))) + (let ((let168 (ff.mul (as ff64 FF0) x3 x8 x13))) + (let ((let169 (ff.mul (as ff125 FF0) x8 x8 x13))) + (let ((let170 (ff.mul (as ff126 FF0) x3 x13 x13))) + (let ((let171 (ff.mul (as ff170 FF0) x8 x13 x13))) + (let ((let172 (ff.mul (as ff80 FF0) x13 x13 x13))) + (let ((let173 (ff.mul (as ff163 FF0) x3 x3))) + (let ((let174 (ff.mul (as ff101 FF0) x3 x8))) + (let ((let175 (ff.mul (as ff161 FF0) x8 x8))) + (let ((let176 (ff.mul (as ff58 FF0) x3 x13))) + (let ((let177 (ff.mul (as ff95 FF0) x8 x13))) + (let ((let178 (ff.mul (as ff9 FF0) x13 x13))) + (let ((let179 (ff.mul (as ff19 FF0) x3))) + (let ((let180 (ff.mul (as ff173 FF0) x8))) + (let ((let181 (ff.mul (as ff185 FF0) x13))) + (let ((let182 (as ff137 FF0))) + (let ((let183 (ff.add let147 let148 let149 let150 let151 let152 let153 let154 let155 let156 let157 let158 let159 let160 let161 let162 let163 let164 let165 let166 let167 let168 let169 let170 let171 let172 let173 let174 let175 let176 let177 let178 let179 let180 let181 let182))) + (let ((let184 (= let183 (as ff0 FF0)))) + (let ((let185 (and let3 let8 let34 let52 let102 let108 let146 let184))) + let185 +)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2.gold b/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_211_16_8.016.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2 b/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2 new file mode 100644 index 000000000..4c38e4eba --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2 @@ -0,0 +1,117 @@ + +(set-info :smt-lib-version 2.6) +(set-logic QF_FFA) +(define-sort FF0 () (_ FiniteField 211)) +(declare-fun x0 () FF0) +(declare-fun x1 () FF0) +(declare-fun x2 () FF0) +(declare-fun x3 () FF0) +(declare-fun x4 () FF0) +(declare-fun x5 () FF0) +(declare-fun x6 () FF0) +(declare-fun x7 () FF0) +(declare-fun x8 () FF0) +(declare-fun x9 () FF0) +(declare-fun x10 () FF0) +(declare-fun x11 () FF0) +(declare-fun x12 () FF0) +(declare-fun x13 () FF0) +(declare-fun x14 () FF0) +(declare-fun x15 () FF0) +(assert + (let ((let0 (ff.mul x8 x8 x13))) + (let ((let1 (ff.mul (as ff93 FF0) x8 x8))) + (let ((let2 (ff.mul (as ff173 FF0) x8 x13))) + (let ((let3 (ff.mul (as ff53 FF0) x8))) + (let ((let4 (ff.mul (as ff36 FF0) x13))) + (let ((let5 (as ff183 FF0))) + (let ((let6 (ff.add let0 let1 let2 let3 let4 let5))) + (let ((let7 (= let6 (as ff0 FF0)))) + (let ((let8 (ff.mul x8 x8 x10))) + (let ((let9 (ff.mul (as ff52 FF0) x8 x8))) + (let ((let10 (ff.mul (as ff141 FF0) x8 x10))) + (let ((let11 (ff.mul (as ff158 FF0) x8))) + (let ((let12 (ff.mul (as ff164 FF0) x10))) + (let ((let13 (as ff88 FF0))) + (let ((let14 (ff.add let8 let9 let10 let11 let12 let13))) + (let ((let15 (= let14 (as ff0 FF0)))) + (let ((let16 (ff.mul x3 x14 x14 x14))) + (let ((let17 (ff.mul (as ff125 FF0) x3 x14 x14))) + (let ((let18 (ff.mul (as ff203 FF0) x14 x14 x14))) + (let ((let19 (ff.mul (as ff32 FF0) x3 x14))) + (let ((let20 (ff.mul (as ff55 FF0) x14 x14))) + (let ((let21 (ff.mul (as ff207 FF0) x3))) + (let ((let22 (ff.mul (as ff166 FF0) x14))) + (let ((let23 (as ff32 FF0))) + (let ((let24 (ff.add let16 let17 let18 let19 let20 let21 let22 let23))) + (let ((let25 (= let24 (as ff0 FF0)))) + (let ((let26 (ff.mul x3 x3))) + (let ((let27 (ff.mul (as ff88 FF0) x3))) + (let ((let28 (as ff201 FF0))) + (let ((let29 (ff.add let26 let27 let28))) + (let ((let30 (= let29 (as ff0 FF0)))) + (let ((let31 (ff.mul x10 x14 x14 x14))) + (let ((let32 (ff.mul (as ff107 FF0) x10 x14 x14))) + (let ((let33 (ff.mul (as ff172 FF0) x14 x14 x14))) + (let ((let34 (ff.mul (as ff146 FF0) x10 x14))) + (let ((let35 (ff.mul (as ff47 FF0) x14 x14))) + (let ((let36 (ff.mul (as ff128 FF0) x10))) + (let ((let37 (ff.mul (as ff3 FF0) x14))) + (let ((let38 (as ff72 FF0))) + (let ((let39 (ff.add let31 let32 let33 let34 let35 let36 let37 let38))) + (let ((let40 (= let39 (as ff0 FF0)))) + (let ((let41 (ff.mul x0 x0 x0 x6 x6 x9 x9))) + (let ((let42 (ff.mul (as ff154 FF0) x0 x0 x0 x6 x6 x9))) + (let ((let43 (ff.mul (as ff96 FF0) x0 x0 x0 x6 x9 x9))) + (let ((let44 (ff.mul (as ff16 FF0) x0 x0 x6 x6 x9 x9))) + (let ((let45 (ff.mul (as ff133 FF0) x0 x0 x0 x6 x6))) + (let ((let46 (ff.mul (as ff14 FF0) x0 x0 x0 x6 x9))) + (let ((let47 (ff.mul (as ff143 FF0) x0 x0 x6 x6 x9))) + (let ((let48 (ff.mul (as ff23 FF0) x0 x0 x0 x9 x9))) + (let ((let49 (ff.mul (as ff59 FF0) x0 x0 x6 x9 x9))) + (let ((let50 (ff.mul (as ff21 FF0) x0 x6 x6 x9 x9))) + (let ((let51 (ff.mul (as ff108 FF0) x0 x0 x0 x6))) + (let ((let52 (ff.mul (as ff18 FF0) x0 x0 x6 x6))) + (let ((let53 (ff.mul (as ff166 FF0) x0 x0 x0 x9))) + (let ((let54 (ff.mul (as ff13 FF0) x0 x0 x6 x9))) + (let ((let55 (ff.mul (as ff69 FF0) x0 x6 x6 x9))) + (let ((let56 (ff.mul (as ff157 FF0) x0 x0 x9 x9))) + (let ((let57 (ff.mul (as ff117 FF0) x0 x6 x9 x9))) + (let ((let58 (ff.mul (as ff180 FF0) x6 x6 x9 x9))) + (let ((let59 (ff.mul (as ff105 FF0) x0 x0 x0))) + (let ((let60 (ff.mul (as ff40 FF0) x0 x0 x6))) + (let ((let61 (ff.mul (as ff50 FF0) x0 x6 x6))) + (let ((let62 (ff.mul (as ff124 FF0) x0 x0 x9))) + (let ((let63 (ff.mul (as ff83 FF0) x0 x6 x9))) + (let ((let64 (ff.mul (as ff79 FF0) x6 x6 x9))) + (let ((let65 (ff.mul (as ff61 FF0) x0 x9 x9))) + (let ((let66 (ff.mul (as ff189 FF0) x6 x9 x9))) + (let ((let67 (ff.mul (as ff203 FF0) x0 x0))) + (let ((let68 (ff.mul (as ff158 FF0) x0 x6))) + (let ((let69 (ff.mul (as ff97 FF0) x6 x6))) + (let ((let70 (ff.mul (as ff110 FF0) x0 x9))) + (let ((let71 (ff.mul (as ff199 FF0) x6 x9))) + (let ((let72 (ff.mul (as ff131 FF0) x9 x9))) + (let ((let73 (ff.mul (as ff95 FF0) x0))) + (let ((let74 (ff.mul (as ff28 FF0) x6))) + (let ((let75 (ff.mul (as ff129 FF0) x9))) + (let ((let76 (as ff121 FF0))) + (let ((let77 (ff.add let41 let42 let43 let44 let45 let46 let47 let48 let49 let50 let51 let52 let53 let54 let55 let56 let57 let58 let59 let60 let61 let62 let63 let64 let65 let66 let67 let68 let69 let70 let71 let72 let73 let74 let75 let76))) + (let ((let78 (= let77 (as ff0 FF0)))) + (let ((let79 (ff.mul x7 x7 x7))) + (let ((let80 (ff.mul (as ff98 FF0) x7 x7))) + (let ((let81 (ff.mul (as ff72 FF0) x7))) + (let ((let82 (as ff44 FF0))) + (let ((let83 (ff.add let79 let80 let81 let82))) + (let ((let84 (= let83 (as ff0 FF0)))) + (let ((let85 (ff.mul x9 x9 x9))) + (let ((let86 (ff.mul (as ff69 FF0) x9 x9))) + (let ((let87 (ff.mul (as ff180 FF0) x9))) + (let ((let88 (as ff65 FF0))) + (let ((let89 (ff.add let85 let86 let87 let88))) + (let ((let90 (= let89 (as ff0 FF0)))) + (let ((let91 (and let7 let15 let25 let30 let40 let78 let84 let90))) + let91 +)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) +) +(check-sat) diff --git a/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2.gold b/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2.gold new file mode 100644 index 000000000..6b8a2c3d2 --- /dev/null +++ b/tests/regress/mcsat/ff/testdata_r_211_16_8.021.smt2.gold @@ -0,0 +1 @@ +sat diff --git a/tests/regress/run_test.sh b/tests/regress/run_test.sh index 2ef34be9d..2ddbcefc7 100755 --- a/tests/regress/run_test.sh +++ b/tests/regress/run_test.sh @@ -157,7 +157,7 @@ fi if [ -d "$out_dir" ] ; then # replace _ with __ and / with _ - log_file="$out_dir/_$(echo "${test_file//_/__}" | tr '/' '_')}" + log_file="$out_dir/_$(echo "${test_file//_/__}" | tr '/' '_')" fi # Run the binary @@ -178,6 +178,7 @@ then if [ -n "$log_file" ] ; then log_file="$log_file.pass" echo "$test_string" > "$log_file" + echo "$runtime" >> "$log_file" fi code=0 else @@ -185,6 +186,7 @@ else if [ -n "$log_file" ] ; then log_file="$log_file.error" echo "$test_string" > "$log_file" + echo "$runtime" >> "$log_file" echo "$DIFF" >> "$log_file" fi code=1