summaryrefslogtreecommitdiff
path: root/src/theory/arith
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/arith')
-rw-r--r--src/theory/arith/Makefile.am28
-rw-r--r--src/theory/arith/approx_simplex.cpp583
-rw-r--r--src/theory/arith/approx_simplex.h90
-rw-r--r--src/theory/arith/arith_heuristic_pivot_rule.cpp14
-rw-r--r--src/theory/arith/arith_heuristic_pivot_rule.h11
-rw-r--r--src/theory/arith/arith_priority_queue.cpp346
-rw-r--r--src/theory/arith/arith_priority_queue.h337
-rw-r--r--src/theory/arith/arith_static_learner.cpp6
-rw-r--r--src/theory/arith/arith_utilities.h27
-rw-r--r--src/theory/arith/arithvar.h52
-rw-r--r--src/theory/arith/bound_counts.h144
-rw-r--r--src/theory/arith/callbacks.cpp37
-rw-r--r--src/theory/arith/callbacks.h92
-rw-r--r--src/theory/arith/congruence_manager.cpp8
-rw-r--r--src/theory/arith/congruence_manager.h25
-rw-r--r--src/theory/arith/constraint.cpp34
-rw-r--r--src/theory/arith/constraint.h25
-rw-r--r--src/theory/arith/delta_rational.h28
-rw-r--r--src/theory/arith/dio_solver.cpp73
-rw-r--r--src/theory/arith/dio_solver.h34
-rw-r--r--src/theory/arith/dual_simplex.cpp259
-rw-r--r--src/theory/arith/dual_simplex.h115
-rw-r--r--src/theory/arith/error_set.cpp493
-rw-r--r--src/theory/arith/error_set.h407
-rw-r--r--src/theory/arith/fc_simplex.cpp853
-rw-r--r--src/theory/arith/fc_simplex.h252
-rw-r--r--src/theory/arith/linear_equality.cpp1326
-rw-r--r--src/theory/arith/linear_equality.h629
-rw-r--r--src/theory/arith/matrix.cpp548
-rw-r--r--src/theory/arith/matrix.h320
-rw-r--r--src/theory/arith/options27
-rw-r--r--src/theory/arith/options_handlers.h20
-rw-r--r--src/theory/arith/partial_model.cpp549
-rw-r--r--src/theory/arith/partial_model.h277
-rw-r--r--src/theory/arith/pure_update_simplex.cpp261
-rw-r--r--src/theory/arith/pure_update_simplex.h118
-rw-r--r--src/theory/arith/simplex-converge.cpp1674
-rw-r--r--src/theory/arith/simplex-converge.h531
-rw-r--r--src/theory/arith/simplex.cpp671
-rw-r--r--src/theory/arith/simplex.h264
-rw-r--r--src/theory/arith/simplex_update.cpp192
-rw-r--r--src/theory/arith/simplex_update.h352
-rw-r--r--src/theory/arith/soi_simplex.cpp791
-rw-r--r--src/theory/arith/soi_simplex.h228
-rw-r--r--src/theory/arith/tableau.cpp179
-rw-r--r--src/theory/arith/tableau.h143
-rw-r--r--src/theory/arith/tableau_sizes.cpp18
-rw-r--r--src/theory/arith/tableau_sizes.h28
-rw-r--r--src/theory/arith/theory_arith.cpp2530
-rw-r--r--src/theory/arith/theory_arith.h525
-rw-r--r--src/theory/arith/theory_arith_private.cpp2692
-rw-r--r--src/theory/arith/theory_arith_private.h605
-rw-r--r--src/theory/arith/theory_arith_private_forward.h14
53 files changed, 14391 insertions, 5494 deletions
diff --git a/src/theory/arith/Makefile.am b/src/theory/arith/Makefile.am
index 8077148c3..c5b07c3a5 100644
--- a/src/theory/arith/Makefile.am
+++ b/src/theory/arith/Makefile.am
@@ -10,11 +10,11 @@ libarith_la_SOURCES = \
type_enumerator.h \
arithvar.h \
arithvar.cpp \
+ bound_counts.h \
arith_rewriter.h \
arith_rewriter.cpp \
arith_static_learner.h \
arith_static_learner.cpp \
- arithvar_node_map.h \
constraint_forward.h \
constraint.h \
constraint.cpp \
@@ -29,14 +29,34 @@ libarith_la_SOURCES = \
partial_model.cpp \
linear_equality.h \
linear_equality.cpp \
+ simplex_update.h \
+ simplex_update.cpp \
+ callbacks.h \
+ callbacks.cpp \
matrix.h \
matrix.cpp \
- arith_priority_queue.h \
- arith_priority_queue.cpp \
+ tableau.h \
+ tableau.cpp \
+ tableau_sizes.h \
+ tableau_sizes.cpp \
+ error_set.h \
+ error_set.cpp \
simplex.h \
simplex.cpp \
+ dual_simplex.h \
+ dual_simplex.cpp \
+ fc_simplex.h \
+ fc_simplex.cpp \
+ soi_simplex.h \
+ soi_simplex.cpp \
+ approx_simplex.h \
+ approx_simplex.cpp \
+ pure_update_simplex.h \
+ pure_update_simplex.cpp \
theory_arith.h \
theory_arith.cpp \
+ theory_arith_private.h \
+ theory_arith_private.cpp \
dio_solver.h \
dio_solver.cpp \
arith_heuristic_pivot_rule.h \
@@ -44,7 +64,7 @@ libarith_la_SOURCES = \
arith_unate_lemma_mode.h \
arith_unate_lemma_mode.cpp \
arith_propagation_mode.h \
- arith_propagation_mode.cpp
+ arith_propagation_mode.cpp
EXTRA_DIST = \
kinds \
diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp
new file mode 100644
index 000000000..d6be9f657
--- /dev/null
+++ b/src/theory/arith/approx_simplex.cpp
@@ -0,0 +1,583 @@
+#include "cvc4autoconfig.h"
+
+#include "theory/arith/approx_simplex.h"
+#include "theory/arith/normal_form.h"
+#include <math.h>
+#include <cmath>
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+ApproximateSimplex::ApproximateSimplex() :
+ d_pivotLimit(std::numeric_limits<int>::max())
+{}
+
+void ApproximateSimplex::setPivotLimit(int pivotLimit){
+ Assert(pivotLimit >= 0);
+ d_pivotLimit = pivotLimit;
+}
+
+const double ApproximateSimplex::SMALL_FIXED_DELTA = .000000001;
+const double ApproximateSimplex::TOLERENCE = 1 + .000000001;
+
+bool ApproximateSimplex::roughlyEqual(double a, double b){
+ if (a == 0){
+ return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
+ }else if (b == 0){
+ return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
+ }else{
+ return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
+ }
+}
+
+Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
+ if(exp.empty()){
+ return Rational(0);
+ }else{
+ Rational result = exp.back();
+ vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
+ vector<Integer>::const_reverse_iterator exp_end = exp.rend();
+ ++exp_iter;
+ while(exp_iter != exp_end){
+ result = result.inverse();
+ const Integer& i = *exp_iter;
+ result += i;
+ ++exp_iter;
+ }
+ return result;
+ }
+}
+std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
+ vector<Integer> mods;
+ if(!q.isZero()){
+ Rational carry = q;
+ for(int i = 0; i <= depth; ++i){
+ Assert(!carry.isZero());
+ mods.push_back(Integer());
+ Integer& back = mods.back();
+ back = carry.floor();
+ //cout << " cfe["<<i<<"]: " << back << endl;
+ carry -= back;
+ if(carry.isZero()){
+ break;
+ }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
+ break;
+ }else{
+ carry = carry.inverse();
+ }
+ }
+ }
+ return mods;
+}
+
+Rational ApproximateSimplex::estimateWithCFE(const Rational& q, int depth){
+ std::vector<Integer> cfe = rationalToCfe(q,depth);
+ return cfeToRational(cfe);
+}
+
+Rational ApproximateSimplex::estimateWithCFE(double d){
+ return estimateWithCFE(Rational::fromDouble(d), 10);
+}
+
+class ApproxNoOp : public ApproximateSimplex {
+public:
+ ApproxNoOp(const ArithVariables& vars){}
+ ~ApproxNoOp(){}
+
+ virtual ApproxResult solveRelaxation(){
+ return ApproxError;
+ }
+ virtual Solution extractRelaxation() const{
+ return Solution();
+ }
+
+ virtual ApproxResult solveMIP(){
+ return ApproxError;
+ }
+ virtual Solution extractMIP() const{
+ return Solution();
+ }
+
+ virtual void setOptCoeffs(const ArithRatPairVec& ref){}
+};
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+/* Begin the declaration of GLPK specific code. */
+#ifdef CVC4_USE_GLPK
+extern "C" {
+#include <glpk.h>
+}
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class ApproxGLPK : public ApproximateSimplex {
+private:
+ glp_prob* d_prob;
+ const ArithVariables& d_vars;
+
+ DenseMap<int> d_colIndices;
+ DenseMap<int> d_rowIndices;
+
+
+ int d_instanceID;
+
+ bool d_solvedRelaxation;
+ bool d_solvedMIP;
+
+public:
+ ApproxGLPK(const ArithVariables& vars);
+ ~ApproxGLPK();
+
+ virtual ApproxResult solveRelaxation();
+ virtual Solution extractRelaxation() const{
+ return extractSolution(false);
+ }
+
+ virtual ApproxResult solveMIP();
+ virtual Solution extractMIP() const{
+ return extractSolution(true);
+ }
+ virtual void setOptCoeffs(const ArithRatPairVec& ref);
+
+private:
+ Solution extractSolution(bool mip) const;
+};
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+#endif /*#ifdef CVC4_USE_GLPK */
+/* End the declaration of GLPK specific code. */
+
+/* Begin GPLK/NOGLPK Glue code. */
+namespace CVC4 {
+namespace theory {
+namespace arith {
+ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars){
+#ifdef CVC4_USE_GLPK
+ return new ApproxGLPK(vars);
+#else
+ return new ApproxNoOp(vars);
+#endif
+}
+bool ApproximateSimplex::enabled() {
+#ifdef CVC4_USE_GLPK
+ return true;
+#else
+ return false;
+#endif
+}
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+/* End GPLK/NOGLPK Glue code. */
+
+
+/* Begin GPLK implementation. */
+#ifdef CVC4_USE_GLPK
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+ApproxGLPK::ApproxGLPK(const ArithVariables& avars) :
+ d_vars(avars), d_solvedRelaxation(false), d_solvedMIP(false)
+{
+ static int instance = 0;
+ ++instance;
+ d_instanceID = instance;
+
+ d_prob = glp_create_prob();
+ glp_set_obj_dir(d_prob, GLP_MAX);
+ glp_set_prob_name(d_prob, "ApproximateSimplex::approximateFindModel");
+
+ int numRows = 0;
+ int numCols = 0;
+
+ // Assign each variable to a row and column variable as it appears in the input
+ for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+ ArithVar v = *vi;
+
+ if(d_vars.isSlack(v)){
+ ++numRows;
+ d_rowIndices.set(v, numRows);
+ }else{
+ ++numCols;
+ d_colIndices.set(v, numCols);
+ }
+ }
+ glp_add_rows(d_prob, numRows);
+ glp_add_cols(d_prob, numCols);
+
+ // Assign the upper/lower bounds and types to each variable
+ for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+ ArithVar v = *vi;
+
+ //cout << v << " ";
+ //d_vars.printModel(v, cout);
+
+ int type;
+ double lb = 0.0;
+ double ub = 0.0;
+ if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ if(d_vars.boundsAreEqual(v)){
+ type = GLP_FX;
+ }else{
+ type = GLP_DB;
+ }
+ lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+ ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+ }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+ type = GLP_UP;
+ ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+ }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ type = GLP_LO;
+ lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+ }else{
+ type = GLP_FR;
+ }
+
+ if(d_vars.isSlack(v)){
+ int rowIndex = d_rowIndices[v];
+ glp_set_row_bnds(d_prob, rowIndex, type, lb, ub);
+ }else{
+ int colIndex = d_colIndices[v];
+ int kind = d_vars.isInteger(v) ? GLP_IV : GLP_CV;
+ glp_set_col_kind(d_prob, colIndex, kind);
+ glp_set_col_bnds(d_prob, colIndex, type, lb, ub);
+ }
+ }
+
+ // Count the number of entries
+ int numEntries = 0;
+ for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+ for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
+ ++numEntries;
+ }
+ }
+
+ int* ia = new int[numEntries+1];
+ int* ja = new int[numEntries+1];
+ double* ar = new double[numEntries+1];
+
+ int entryCounter = 0;
+ for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ int rowIndex = d_rowIndices[v];
+
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+
+ for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
+
+ const Monomial& mono = *i;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Assert(d_vars.hasArithVar(n));
+ ArithVar av = d_vars.asArithVar(n);
+ int colIndex = d_colIndices[av];
+ double coeff = constant.getValue().getDouble();
+
+ ++entryCounter;
+ ia[entryCounter] = rowIndex;
+ ja[entryCounter] = colIndex;
+ ar[entryCounter] = coeff;
+ }
+ }
+ glp_load_matrix(d_prob, numEntries, ia, ja, ar);
+
+ delete[] ia;
+ delete[] ja;
+ delete[] ar;
+}
+
+void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
+ DenseMap<double> nbCoeffs;
+
+ for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
+ ArithVar v = (*i).first;
+ const Rational& q = (*i).second;
+
+ if(d_vars.isSlack(v)){
+ // replace the variable by its definition and multiply by q
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+ Polynomial pq = p * q;
+
+ for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
+ const Monomial& mono = *j;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Assert(d_vars.hasArithVar(n));
+ ArithVar av = d_vars.asArithVar(n);
+ int colIndex = d_colIndices[av];
+ double coeff = constant.getValue().getDouble();
+ if(!nbCoeffs.isKey(colIndex)){
+ nbCoeffs.set(colIndex, 0.0);
+ }
+ nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+ }
+ }else{
+ int colIndex = d_colIndices[v];
+ double coeff = q.getDouble();
+ if(!nbCoeffs.isKey(colIndex)){
+ nbCoeffs.set(colIndex, 0.0);
+ }
+ nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+ }
+ }
+ for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
+ Index colIndex = *ci;
+ double coeff = nbCoeffs[colIndex];
+ glp_set_obj_coef(d_prob, colIndex, coeff);
+ }
+}
+
+/*
+ * rough strategy:
+ * real relaxation
+ * try approximate real optimization of error function
+ * pivot in its basis
+ * update to its assignment
+ * check with FCSimplex
+ * check integer solution
+ * try approximate mixed integer problem
+ * stop at the first feasible point
+ * pivot in its basis
+ * update to its assignment
+ * check with FCSimplex
+ */
+
+static void printGLPKStatus(int status, std::ostream& out){
+ switch(status){
+ case GLP_OPT:
+ out << "GLP_OPT" << endl;
+ break;
+ case GLP_FEAS:
+ out << "GLP_FEAS" << endl;
+ break;
+ case GLP_INFEAS:
+ out << "GLP_INFEAS" << endl;
+ break;
+ case GLP_NOFEAS:
+ out << "GLP_NOFEAS" << endl;
+ break;
+ case GLP_UNBND:
+ out << "GLP_UNBND" << endl;
+ break;
+ case GLP_UNDEF:
+ out << "GLP_UNDEF" << endl;
+ break;
+ default:
+ out << "Status unknown" << endl;
+ break;
+ }
+}
+
+ApproxGLPK::~ApproxGLPK(){
+ glp_delete_prob(d_prob);
+}
+
+
+ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{
+ Assert(d_solvedRelaxation);
+ Assert(!mip || d_solvedMIP);
+
+ ApproximateSimplex::Solution sol;
+ DenseSet& newBasis = sol.newBasis;
+ DenseMap<DeltaRational>& newValues = sol.newValues;
+
+ for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
+ ArithVar vi = *i;
+ bool isSlack = d_vars.isSlack(vi);
+ int glpk_index = isSlack ? d_rowIndices[vi] : d_colIndices[vi];
+
+ int status = isSlack ? glp_get_row_stat(d_prob, glpk_index) : glp_get_col_stat(d_prob, glpk_index);
+ //cout << "assignment " << vi << endl;
+
+ switch(status){
+ case GLP_BS:
+ //cout << "basic" << endl;
+ newBasis.add(vi);
+ break;
+ case GLP_NL:
+ case GLP_NS:
+ if(!mip){
+ //cout << "non-basic lb" << endl;
+ newValues.set(vi, d_vars.getLowerBound(vi));
+ break;
+ }// intentionally fall through otherwise
+ case GLP_NU:
+ if(!mip){
+ // cout << "non-basic ub" << endl;
+ newValues.set(vi, d_vars.getUpperBound(vi));
+ break;
+ }// intentionally fall through otherwise
+ default:
+ {
+ // cout << "non-basic other" << endl;
+
+ double newAssign =
+ mip ?
+ (isSlack ? glp_mip_row_val(d_prob, glpk_index) : glp_mip_col_val(d_prob, glpk_index))
+ : (isSlack ? glp_get_row_prim(d_prob, glpk_index) : glp_get_col_prim(d_prob, glpk_index));
+ const DeltaRational& oldAssign = d_vars.getAssignment(vi);
+
+
+ if(d_vars.hasLowerBound(vi) &&
+ roughlyEqual(newAssign, d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA))){
+ //cout << " to lb" << endl;
+
+ newValues.set(vi, d_vars.getLowerBound(vi));
+ }else if(d_vars.hasUpperBound(vi) &&
+ roughlyEqual(newAssign, d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA))){
+ newValues.set(vi, d_vars.getUpperBound(vi));
+ // cout << " to ub" << endl;
+ }else{
+
+ double rounded = round(newAssign);
+ if(roughlyEqual(newAssign, rounded)){
+ // cout << "roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
+ newAssign = rounded;
+ }else{
+ // cout << "not roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
+ }
+
+ DeltaRational proposal = estimateWithCFE(newAssign);
+
+
+ if(roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA))){
+ // cout << " to prev value" << newAssign << " " << oldAssign << endl;
+ proposal = d_vars.getAssignment(vi);
+ }
+
+
+ if(d_vars.strictlyLessThanLowerBound(vi, proposal)){
+ //cout << " round to lb " << d_vars.getLowerBound(vi) << endl;
+ proposal = d_vars.getLowerBound(vi);
+ }else if(d_vars.strictlyGreaterThanUpperBound(vi, proposal)){
+ //cout << " round to ub " << d_vars.getUpperBound(vi) << endl;
+ proposal = d_vars.getUpperBound(vi);
+ }else{
+ //cout << " use proposal" << proposal << " " << oldAssign << endl;
+ }
+ newValues.set(vi, proposal);
+ }
+ break;
+ }
+ }
+ }
+ return sol;
+}
+
+ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){
+ Assert(!d_solvedRelaxation);
+
+ glp_smcp parm;
+ glp_init_smcp(&parm);
+ parm.presolve = GLP_OFF;
+ parm.meth = GLP_PRIMAL;
+ parm.pricing = GLP_PT_PSE;
+ parm.it_lim = d_pivotLimit;
+ //parm.msg_lev = GLP_MSG_ALL;
+ parm.msg_lev = GLP_MSG_OFF;
+
+ int res = glp_simplex(d_prob, &parm);
+ switch(res){
+ case 0:
+ {
+ int status = glp_get_status(d_prob);
+ switch(status){
+ case GLP_OPT:
+ case GLP_FEAS:
+ case GLP_UNBND:
+ d_solvedRelaxation = true;
+ return ApproxSat;
+ case GLP_INFEAS:
+ case GLP_NOFEAS:
+ d_solvedRelaxation = true;
+ return ApproxUnsat;
+ default:
+ return ApproxError;
+ }
+ }
+ default:
+ return ApproxError;
+ }
+}
+
+void stopAtBingoOrPivotLimit(glp_tree *tree, void *info){
+ int pivotLimit = *((int*)info);
+ switch(glp_ios_reason(tree)){
+ case GLP_IBINGO:
+ glp_ios_terminate(tree);
+ break;
+ default:
+ glp_prob* prob = glp_ios_get_prob(tree);
+ int iterationcount = lpx_get_int_parm(prob, LPX_K_ITCNT);
+ if(iterationcount > pivotLimit){
+ glp_ios_terminate(tree);
+ }
+ break;
+ }
+}
+
+ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){
+ Assert(d_solvedRelaxation);
+ // Explicitly disable presolving
+ // We need the basis thus the presolver must be off!
+ // This is default, but this is just being cautious.
+ glp_iocp parm;
+ glp_init_iocp(&parm);
+ parm.presolve = GLP_OFF;
+ parm.pp_tech = GLP_PP_NONE;
+ parm.fp_heur = GLP_ON;
+ parm.gmi_cuts = GLP_ON;
+ parm.mir_cuts = GLP_ON;
+ parm.cov_cuts = GLP_ON;
+ parm.cb_func = stopAtBingoOrPivotLimit;
+ parm.cb_info = &d_pivotLimit;
+ //parm.msg_lev = GLP_MSG_ALL;
+ parm.msg_lev = GLP_MSG_OFF;
+ int res = glp_intopt(d_prob, &parm);
+
+ switch(res){
+ case 0:
+ case GLP_ESTOP:
+ {
+ int status = glp_mip_status(d_prob);
+ switch(status){
+ case GLP_OPT:
+ case GLP_FEAS:
+ d_solvedMIP = true;
+ return ApproxSat;
+ case GLP_NOFEAS:
+ d_solvedMIP = true;
+ return ApproxUnsat;
+ default:
+ return ApproxError;
+ }
+ }
+ default:
+ return ApproxError;
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+#endif /*#ifdef CVC4_USE_GLPK */
+/* End GPLK implementation. */
diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h
new file mode 100644
index 000000000..a2f3cde24
--- /dev/null
+++ b/src/theory/arith/approx_simplex.h
@@ -0,0 +1,90 @@
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "util/statistics_registry.h"
+#include "theory/arith/arithvar.h"
+#include "theory/arith/linear_equality.h"
+#include "util/dense_map.h"
+#include <vector>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+class ApproximateSimplex{
+protected:
+ int d_pivotLimit;
+
+public:
+
+ static bool enabled();
+
+ /**
+ * If glpk is enabled, return a subclass that can do something.
+ * If glpk is disabled, return a sublass that does nothing.
+ */
+ static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars);
+ ApproximateSimplex();
+ virtual ~ApproximateSimplex(){}
+
+ /** A result is either sat, unsat or unknown.*/
+ enum ApproxResult {ApproxError, ApproxSat, ApproxUnsat};
+ struct Solution {
+ DenseSet newBasis;
+ DenseMap<DeltaRational> newValues;
+ Solution() : newBasis(), newValues(){}
+ };
+
+ /** Sets a deterministic effort limit. */
+ void setPivotLimit(int pivotLimit);
+
+ /** Sets a maximization criteria for the approximate solver.*/
+ virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
+
+ virtual ApproxResult solveRelaxation() = 0;
+ virtual Solution extractRelaxation() const = 0;
+
+ virtual ApproxResult solveMIP() = 0;
+ virtual Solution extractMIP() const = 0;
+
+ static void applySolution(LinearEqualityModule& linEq, const Solution& sol){
+ linEq.forceNewBasis(sol.newBasis);
+ linEq.updateMany(sol.newValues);
+ }
+
+ /** UTILIES FOR DEALING WITH ESTIMATES */
+
+ static const double SMALL_FIXED_DELTA;
+ static const double TOLERENCE;
+
+ /** Returns true if two doubles are roughly equal based on TOLERENCE and SMALL_FIXED_DELTA.*/
+ static bool roughlyEqual(double a, double b);
+
+ /**
+ * Estimates a double as a Rational using continued fraction expansion that
+ * cuts off the estimate once the value is approximately zero.
+ * This is designed for removing rounding artifacts.
+ */
+ static Rational estimateWithCFE(double d);
+
+ /**
+ * Converts a rational to a continued fraction expansion representation
+ * using a maximum number of expansions equal to depth as long as the expression
+ * is not roughlyEqual() to 0.
+ */
+ static std::vector<Integer> rationalToCfe(const Rational& q, int depth);
+
+ /** Converts a continued fraction expansion representation to a rational. */
+ static Rational cfeToRational(const std::vector<Integer>& exp);
+
+ /** Estimates a rational as a continued fraction expansion.*/
+ static Rational estimateWithCFE(const Rational& q, int depth);
+};/* class ApproximateSimplex */
+
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/arith_heuristic_pivot_rule.cpp b/src/theory/arith/arith_heuristic_pivot_rule.cpp
index 4fbc84892..62b5fdff1 100644
--- a/src/theory/arith/arith_heuristic_pivot_rule.cpp
+++ b/src/theory/arith/arith_heuristic_pivot_rule.cpp
@@ -19,16 +19,16 @@
namespace CVC4 {
-std::ostream& operator<<(std::ostream& out, ArithHeuristicPivotRule rule) {
+std::ostream& operator<<(std::ostream& out, ErrorSelectionRule rule) {
switch(rule) {
- case MINIMUM:
- out << "MINIMUM";
+ case MINIMUM_AMOUNT:
+ out << "MINIMUM_AMOUNT";
break;
- case BREAK_TIES:
- out << "BREAK_TIES";
+ case VAR_ORDER:
+ out << "VAR_ORDER";
break;
- case MAXIMUM:
- out << "MAXIMUM";
+ case MAXIMUM_AMOUNT:
+ out << "MAXIMUM_AMOUNT";
break;
default:
out << "ArithHeuristicPivotRule!UNKNOWN";
diff --git a/src/theory/arith/arith_heuristic_pivot_rule.h b/src/theory/arith/arith_heuristic_pivot_rule.h
index a1ecc5b2c..705c3e2f3 100644
--- a/src/theory/arith/arith_heuristic_pivot_rule.h
+++ b/src/theory/arith/arith_heuristic_pivot_rule.h
@@ -25,12 +25,13 @@
namespace CVC4 {
typedef enum {
- MINIMUM,
- BREAK_TIES,
- MAXIMUM
-} ArithHeuristicPivotRule;
+ VAR_ORDER,
+ MINIMUM_AMOUNT,
+ MAXIMUM_AMOUNT,
+ SUM_METRIC
+} ErrorSelectionRule;
-std::ostream& operator<<(std::ostream& out, ArithHeuristicPivotRule rule) CVC4_PUBLIC;
+std::ostream& operator<<(std::ostream& out, ErrorSelectionRule rule) CVC4_PUBLIC;
}/* CVC4 namespace */
diff --git a/src/theory/arith/arith_priority_queue.cpp b/src/theory/arith/arith_priority_queue.cpp
deleted file mode 100644
index 9fc30c136..000000000
--- a/src/theory/arith/arith_priority_queue.cpp
+++ /dev/null
@@ -1,346 +0,0 @@
-/********************* */
-/*! \file arith_priority_queue.cpp
- ** \verbatim
- ** Original author: Tim King
- ** Major contributors: Morgan Deters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2013 New York University and The University of Iowa
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief [[ Add one-line brief description here ]]
- **
- ** [[ Add lengthier description here ]]
- ** \todo document this file
- **/
-
-
-#include "theory/arith/arith_priority_queue.h"
-
-#include <algorithm>
-
-using namespace std;
-
-using namespace CVC4;
-using namespace CVC4::kind;
-
-using namespace CVC4::theory;
-using namespace CVC4::theory::arith;
-
-ArithPriorityQueue::Statistics::Statistics():
- d_enqueues("theory::arith::pqueue::enqueues", 0),
- d_enqueuesCollection("theory::arith::pqueue::enqueuesCollection", 0),
- d_enqueuesDiffMode("theory::arith::pqueue::enqueuesDiffMode", 0),
- d_enqueuesVarOrderMode("theory::arith::pqueue::enqueuesVarOrderMode", 0),
- d_enqueuesCollectionDuplicates("theory::arith::pqueue::enqueuesCollectionDuplicates", 0),
- d_enqueuesVarOrderModeDuplicates("theory::arith::pqueue::enqueuesVarOrderModeDuplicates", 0)
-{
- StatisticsRegistry::registerStat(&d_enqueues);
- StatisticsRegistry::registerStat(&d_enqueuesCollection);
- StatisticsRegistry::registerStat(&d_enqueuesDiffMode);
- StatisticsRegistry::registerStat(&d_enqueuesVarOrderMode);
- StatisticsRegistry::registerStat(&d_enqueuesCollectionDuplicates);
- StatisticsRegistry::registerStat(&d_enqueuesVarOrderModeDuplicates);
-}
-
-ArithPriorityQueue::Statistics::~Statistics(){
- StatisticsRegistry::unregisterStat(&d_enqueues);
- StatisticsRegistry::unregisterStat(&d_enqueuesCollection);
- StatisticsRegistry::unregisterStat(&d_enqueuesDiffMode);
- StatisticsRegistry::unregisterStat(&d_enqueuesVarOrderMode);
- StatisticsRegistry::unregisterStat(&d_enqueuesCollectionDuplicates);
- StatisticsRegistry::unregisterStat(&d_enqueuesVarOrderModeDuplicates);
-}
-
-ArithPriorityQueue::ArithPriorityQueue(ArithPartialModel& pm, const Tableau& tableau):
- d_pivotRule(MINIMUM),
- d_partialModel(pm),
- d_tableau(tableau),
- d_modeInUse(Collection),
- d_ZERO_DELTA(0,0)
-{}
-
-void ArithPriorityQueue::setPivotRule(PivotRule rule){
- Assert(!inDifferenceMode());
- Debug("arith::setPivotRule") << "setting pivot rule " << rule << endl;
- d_pivotRule = rule;
-}
-
-ArithVar ArithPriorityQueue::dequeueInconsistentBasicVariable(){
- AlwaysAssert(!inCollectionMode());
-
- Debug("arith_update") << "dequeueInconsistentBasicVariable()" << endl;
-
- if(inDifferenceMode()){
- while(!d_diffQueue.empty()){
- ArithVar var = d_diffQueue.front().variable();
- switch(d_pivotRule){
- case MINIMUM:
- pop_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::minimumRule);
- break;
- case BREAK_TIES:
- pop_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::breakTiesRules);
- break;
- case MAXIMUM:
- pop_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::maximumRule);
- break;
- }
- d_diffQueue.pop_back();
- Debug("arith_update") << "possiblyInconsistentGriggio var" << var << endl;
- if(basicAndInconsistent(var)){
- return var;
- }
- }
- }else{
- Assert(inVariableOrderMode());
- Debug("arith_update") << "possiblyInconsistent.size()"
- << d_varOrderQueue.size() << endl;
-
- while(!d_varOrderQueue.empty()){
- ArithVar var = d_varOrderQueue.front();
- pop_heap(d_varOrderQueue.begin(), d_varOrderQueue.end(), std::greater<ArithVar>());
- d_varOrderQueue.pop_back();
-
- d_varSet.remove(var);
-
- Debug("arith_update") << "possiblyInconsistent var" << var << endl;
- if(basicAndInconsistent(var)){
- return var;
- }
- }
- }
- return ARITHVAR_SENTINEL;
-}
-
-ArithPriorityQueue::VarDRatPair ArithPriorityQueue::computeDiff(ArithVar basic){
- Assert(basicAndInconsistent(basic));
- const DeltaRational& beta = d_partialModel.getAssignment(basic);
- DeltaRational diff = d_partialModel.strictlyLessThanLowerBound(basic,beta) ?
- d_partialModel.getLowerBound(basic) - beta:
- beta - d_partialModel.getUpperBound(basic);
-
- Assert(d_ZERO_DELTA < diff);
- return VarDRatPair(basic,diff);
-}
-
-void ArithPriorityQueue::enqueueIfInconsistent(ArithVar basic){
- Assert(d_tableau.isBasic(basic));
-
- if(basicAndInconsistent(basic)){
- ++d_statistics.d_enqueues;
-
- switch(d_modeInUse){
- case Collection:
- ++d_statistics.d_enqueuesCollection;
- if(!d_varSet.isMember(basic)){
- d_varSet.add(basic);
- d_candidates.push_back(basic);
- }else{
- ++d_statistics.d_enqueuesCollectionDuplicates;
- }
- break;
- case VariableOrder:
- ++d_statistics.d_enqueuesVarOrderMode;
- if(!d_varSet.isMember(basic)){
- d_varSet.add(basic);
- d_varOrderQueue.push_back(basic);
- push_heap(d_varOrderQueue.begin(), d_varOrderQueue.end(), std::greater<ArithVar>());
- }else{
- ++d_statistics.d_enqueuesVarOrderModeDuplicates;
- }
- break;
- case Difference:
- ++d_statistics.d_enqueuesDiffMode;
- d_diffQueue.push_back(computeDiff(basic));
- switch(d_pivotRule){
- case MINIMUM:
- push_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::minimumRule);
- break;
- case BREAK_TIES:
- push_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::breakTiesRules);
- break;
- case MAXIMUM:
- push_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::maximumRule);
- break;
- }
- break;
- default:
- Unreachable();
- }
- }
-}
-
-void ArithPriorityQueue::transitionToDifferenceMode() {
- Assert(inCollectionMode());
- Assert(d_varOrderQueue.empty());
- Assert(d_diffQueue.empty());
-
- Debug("arith::priorityqueue") << "transitionToDifferenceMode()" << endl;
- d_varSet.purge();
-
- ArithVarArray::const_iterator i = d_candidates.begin(), end = d_candidates.end();
- for(; i != end; ++i){
- ArithVar var = *i;
- if(basicAndInconsistent(var)){
- d_diffQueue.push_back(computeDiff(var));
- }
- }
-
- switch(d_pivotRule){
- case MINIMUM:
- Debug("arith::pivotRule") << "Making the minimum heap." << endl;
- make_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::minimumRule);
- break;
- case BREAK_TIES:
- Debug("arith::pivotRule") << "Making the break ties heap." << endl;
- make_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::breakTiesRules);
- break;
- case MAXIMUM:
- Debug("arith::pivotRule") << "Making the maximum heap." << endl;
- make_heap(d_diffQueue.begin(), d_diffQueue.end(), VarDRatPair::maximumRule);
- break;
- }
-
- d_candidates.clear();
- d_modeInUse = Difference;
-
- Assert(d_varSet.empty());
- Assert(inDifferenceMode());
- Assert(d_varOrderQueue.empty());
- Assert(d_candidates.empty());
-}
-
-void ArithPriorityQueue::transitionToVariableOrderMode() {
- Assert(inDifferenceMode());
- Assert(d_varOrderQueue.empty());
- Assert(d_candidates.empty());
- Assert(d_varSet.empty());
-
- Debug("arith::priorityqueue") << "transitionToVariableOrderMode()" << endl;
-
- DifferenceArray::const_iterator i = d_diffQueue.begin(), end = d_diffQueue.end();
- for(; i != end; ++i){
- ArithVar var = (*i).variable();
- if(basicAndInconsistent(var) && !d_varSet.isMember(var)){
- d_varSet.add(var);
- d_varOrderQueue.push_back(var);
- }
- }
- make_heap(d_varOrderQueue.begin(), d_varOrderQueue.end(), std::greater<ArithVar>());
- d_diffQueue.clear();
- d_modeInUse = VariableOrder;
-
- Assert(inVariableOrderMode());
- Assert(d_diffQueue.empty());
- Assert(d_candidates.empty());
-}
-
-void ArithPriorityQueue::transitionToCollectionMode() {
- Assert(inDifferenceMode() || inVariableOrderMode());
- Assert(d_candidates.empty());
-
- if(inDifferenceMode()){
- Assert(d_varSet.empty());
- Assert(d_varOrderQueue.empty());
- Assert(inDifferenceMode());
-
- DifferenceArray::const_iterator i = d_diffQueue.begin(), end = d_diffQueue.end();
- for(; i != end; ++i){
- ArithVar var = (*i).variable();
- if(basicAndInconsistent(var) && !d_varSet.isMember(var)){
- d_candidates.push_back(var);
- d_varSet.add(var);
- }
- }
- d_diffQueue.clear();
- }else{
- Assert(d_diffQueue.empty());
- Assert(inVariableOrderMode());
-
- d_varSet.purge();
-
- ArithVarArray::const_iterator i = d_varOrderQueue.begin(), end = d_varOrderQueue.end();
- for(; i != end; ++i){
- ArithVar var = *i;
- if(basicAndInconsistent(var)){
- d_candidates.push_back(var);
- d_varSet.add(var); // cannot have duplicates.
- }
- }
- d_varOrderQueue.clear();
- }
-
- Assert(d_diffQueue.empty());
- Assert(d_varOrderQueue.empty());
-
- Debug("arith::priorityqueue") << "transitionToCollectionMode()" << endl;
-
- d_modeInUse = Collection;
-}
-
-void ArithPriorityQueue::clear(){
- switch(d_modeInUse){
- case Collection:
- d_candidates.clear();
- d_varSet.purge();
- break;
- case VariableOrder:
- if(!d_varOrderQueue.empty()) {
- d_varOrderQueue.clear();
- d_varSet.purge();
- }
- break;
- case Difference:
- if(!d_diffQueue.empty()){
- d_diffQueue.clear();
- d_varSet.purge();
- }
- break;
- default:
- Unreachable();
- }
-
- Assert(d_varSet.empty());
- Assert(d_candidates.empty());
- Assert(d_varOrderQueue.empty());
- Assert(d_diffQueue.empty());
-}
-
-std::ostream& CVC4::theory::arith::operator<<(std::ostream& out, ArithPriorityQueue::PivotRule rule) {
- switch(rule) {
- case ArithPriorityQueue::MINIMUM:
- out << "MINIMUM";
- break;
- case ArithPriorityQueue::BREAK_TIES:
- out << "BREAK_TIES";
- break;
- case ArithPriorityQueue::MAXIMUM:
- out << "MAXIMUM";
- break;
- default:
- out << "PivotRule!UNKNOWN";
- }
-
- return out;
-}
-
-void ArithPriorityQueue::reduce(){
- vector<ArithVar> contents;
-
- if(inCollectionMode()){
- contents = d_candidates;
- } else {
- ArithVar res = ARITHVAR_SENTINEL;
- while((res = dequeueInconsistentBasicVariable()) != ARITHVAR_SENTINEL){
- contents.push_back(res);
- }
- }
- clear();
- for(vector<ArithVar>::const_iterator iter = contents.begin(), end = contents.end(); iter != end; ++iter){
- ArithVar curr = *iter;
- if(d_tableau.isBasic(curr)){
- enqueueIfInconsistent(curr);
- }
- }
-}
diff --git a/src/theory/arith/arith_priority_queue.h b/src/theory/arith/arith_priority_queue.h
deleted file mode 100644
index 912799dde..000000000
--- a/src/theory/arith/arith_priority_queue.h
+++ /dev/null
@@ -1,337 +0,0 @@
-/********************* */
-/*! \file arith_priority_queue.h
- ** \verbatim
- ** Original author: Tim King
- ** Major contributors: none
- ** Minor contributors (to current version): Morgan Deters
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2013 New York University and The University of Iowa
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief [[ Add one-line brief description here ]]
- **
- ** [[ Add lengthier description here ]]
- ** \todo document this file
- **/
-
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__ARITH__ARITH_PRIORITY_QUEUE_H
-#define __CVC4__THEORY__ARITH__ARITH_PRIORITY_QUEUE_H
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/partial_model.h"
-
-#include "util/statistics_registry.h"
-
-#include <vector>
-
-namespace CVC4 {
-namespace theory {
-namespace arith {
-
-
-/**
- * The priority queue has 3 different modes of operation:
- * - Collection
- * This passively collects arithmetic variables that may be inconsistent.
- * This does not maintain any heap structure.
- * dequeueInconsistentBasicVariable() does not work in this mode!
- * Entering this mode requires the queue to be empty.
- *
- * - Difference Queue
- * This mode uses the difference between a variables and its bound
- * to determine which to dequeue first.
- *
- * - Variable Order Queue
- * This mode uses the variable order to determine which ArithVar is dequeued first.
- *
- * The transitions between the modes of operation are:
- * Collection => Difference Queue
- * Difference Queue => Variable Order Queue
- * Difference Queue => Collection (queue must be empty!)
- * Variable Order Queue => Collection (queue must be empty!)
- *
- * The queue begins in Collection mode.
- */
-class ArithPriorityQueue {
-public:
- enum PivotRule {MINIMUM, BREAK_TIES, MAXIMUM};
-
-private:
- class VarDRatPair {
- private:
- ArithVar d_variable;
- DeltaRational d_orderBy;
- public:
- VarDRatPair(ArithVar var, const DeltaRational& dr):
- d_variable(var), d_orderBy(dr)
- { }
-
- ArithVar variable() const {
- return d_variable;
- }
-
- static bool minimumRule(const VarDRatPair& a, const VarDRatPair& b){
- return a.d_orderBy > b.d_orderBy;
- }
- static bool maximumRule(const VarDRatPair& a, const VarDRatPair& b){
- return a.d_orderBy < b.d_orderBy;
- }
-
- static bool breakTiesRules(const VarDRatPair& a, const VarDRatPair& b){
- const Rational& nonInfA = a.d_orderBy.getNoninfinitesimalPart();
- const Rational& nonInfB = b.d_orderBy.getNoninfinitesimalPart();
- int cmpNonInf = nonInfA.cmp(nonInfB);
- if(cmpNonInf == 0){
- const Rational& infA = a.d_orderBy.getInfinitesimalPart();
- const Rational& infB = b.d_orderBy.getInfinitesimalPart();
- int cmpInf = infA.cmp(infB);
- if(cmpInf == 0){
- return a.d_variable > b.d_variable;
- }else{
- return cmpInf > 0;
- }
- }else{
- return cmpNonInf > 0;
- }
-
- return a.d_orderBy > b.d_orderBy;
- }
- };
-
- typedef std::vector<VarDRatPair> DifferenceArray;
- typedef std::vector<ArithVar> ArithVarArray;
-
- PivotRule d_pivotRule;
-
- /**
- * An unordered array with no heap structure for use during collection mode.
- */
- ArithVarArray d_candidates;
-
- /**
- * Priority Queue of the basic variables that may be inconsistent.
- * Variables are ordered according to which violates its bound the most.
- * This is a heuristic and makes no guarantees to terminate!
- * This heuristic comes from Alberto Griggio's thesis.
- */
- DifferenceArray d_diffQueue;
-
- /**
- * Priority Queue of the basic variables that may be inconsistent.
- *
- * This is required to contain at least 1 instance of every inconsistent
- * basic variable. This is only required to be a superset though so its
- * contents must be checked to still be basic and inconsistent.
- *
- * This is also required to agree with the row on variable order for termination.
- * Effectively this means that this must be a min-heap.
- */
- ArithVarArray d_varOrderQueue;
-
- /**
- * A superset of the basic variables that may be inconsistent.
- * This is empty during DiffOrderMode, and otherwise it is the same set as candidates
- * or varOrderQueue.
- */
- DenseSet d_varSet;
-
- /**
- * Reference to the arithmetic partial model for checking if a variable
- * is consistent with its upper and lower bounds.
- */
- ArithPartialModel& d_partialModel;
-
- /** Reference to the Tableau for checking if a variable is basic. */
- const Tableau& d_tableau;
-
- enum Mode {Collection, Difference, VariableOrder};
- /**
- * Controls which priority queue is in use.
- * If true, d_griggioRuleQueue is used.
- * If false, d_possiblyInconsistent is used.
- */
- Mode d_modeInUse;
-
- /** Storage of Delta Rational 0 */
- DeltaRational d_ZERO_DELTA;
-
- VarDRatPair computeDiff(ArithVar basic);
-
-public:
-
- ArithPriorityQueue(ArithPartialModel& pm, const Tableau& tableau);
-
- /** precondition: !inDifferenceMode() */
- void setPivotRule(PivotRule rule);
-
- ArithVar dequeueInconsistentBasicVariable();
-
- void enqueueIfInconsistent(ArithVar basic);
-
- inline bool basicAndInconsistent(ArithVar var) const{
- return d_tableau.isBasic(var)
- && !d_partialModel.assignmentIsConsistent(var) ;
- }
-
- void transitionToDifferenceMode();
- void transitionToVariableOrderMode();
- void transitionToCollectionMode();
-
- inline bool inDifferenceMode() const{
- return d_modeInUse == Difference;
- }
- inline bool inCollectionMode() const{
- return d_modeInUse == Collection;
- }
- inline bool inVariableOrderMode() const{
- return d_modeInUse == VariableOrder;
- }
-
- inline bool empty() const{
- switch(d_modeInUse){
- case Collection: return d_candidates.empty();
- case VariableOrder: return d_varOrderQueue.empty();
- case Difference: return d_diffQueue.empty();
- default: Unreachable();
- }
- }
-
- inline size_t size() const {
- switch(d_modeInUse){
- case Collection: return d_candidates.size();
- case VariableOrder: return d_varOrderQueue.size();
- case Difference: return d_diffQueue.size();
- default: Unreachable();
- }
- }
-
- /** Clears the queue. */
- void clear();
-
-
- /**
- * Reduces the queue to only contain the subset that is still basic
- * and inconsistent.
- *Currently, O(n log n) for an easy obviously correct implementation in all modes..
- */
- void reduce();
-
- bool collectionModeContains(ArithVar v) const {
- Assert(inCollectionMode());
- return d_varSet.isMember(v);
- }
-
- class const_iterator {
- private:
- Mode d_mode;
- ArithVarArray::const_iterator d_avIter;
- DifferenceArray::const_iterator d_diffIter;
- public:
- const_iterator(Mode m,
- ArithVarArray::const_iterator av,
- DifferenceArray::const_iterator diff):
- d_mode(m), d_avIter(av), d_diffIter(diff)
- {}
- const_iterator(const const_iterator& other):
- d_mode(other.d_mode), d_avIter(other.d_avIter), d_diffIter(other.d_diffIter)
- {}
- bool operator==(const const_iterator& other) const{
- AlwaysAssert(d_mode == other.d_mode);
- switch(d_mode){
- case Collection:
- case VariableOrder:
- return d_avIter == other.d_avIter;
- case Difference:
- return d_diffIter == other.d_diffIter;
- default:
- Unreachable();
- }
- }
- bool operator!=(const const_iterator& other) const{
- return !(*this == other);
- }
- const_iterator& operator++(){
- switch(d_mode){
- case Collection:
- case VariableOrder:
- ++d_avIter;
- break;
- case Difference:
- ++d_diffIter;
- break;
- default:
- Unreachable();
- }
- return *this;
- }
-
- ArithVar operator*() const{
- switch(d_mode){
- case Collection:
- case VariableOrder:
- return *d_avIter;
- case Difference:
- return (*d_diffIter).variable();
- default:
- Unreachable();
- }
- }
- };
-
- const_iterator begin() const{
- switch(d_modeInUse){
- case Collection:
- return const_iterator(Collection, d_candidates.begin(), d_diffQueue.end());
- case VariableOrder:
- return const_iterator(VariableOrder, d_varOrderQueue.begin(), d_diffQueue.end());
- case Difference:
- return const_iterator(Difference, d_varOrderQueue.end(), d_diffQueue.begin());
- default:
- Unreachable();
- }
- }
-
- const_iterator end() const{
- switch(d_modeInUse){
- case Collection:
- return const_iterator(Collection, d_candidates.end(), d_diffQueue.end());
- case VariableOrder:
- return const_iterator(VariableOrder, d_varOrderQueue.end(), d_diffQueue.end());
- case Difference:
- return const_iterator(Difference, d_varOrderQueue.end(), d_diffQueue.end());
- default:
- Unreachable();
- }
- }
-
-private:
- class Statistics {
- public:
- IntStat d_enqueues;
- IntStat d_enqueuesCollection;
- IntStat d_enqueuesDiffMode;
- IntStat d_enqueuesVarOrderMode;
-
- IntStat d_enqueuesCollectionDuplicates;
- IntStat d_enqueuesVarOrderModeDuplicates;
-
- Statistics();
- ~Statistics();
- };
-
- Statistics d_statistics;
-};
-
-std::ostream& operator<<(std::ostream& out, ArithPriorityQueue::PivotRule rule);
-
-}/* CVC4::theory::arith namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__ARITH_PRIORITY_QUEUE_H */
diff --git a/src/theory/arith/arith_static_learner.cpp b/src/theory/arith/arith_static_learner.cpp
index a4dfb1fa4..bf5261439 100644
--- a/src/theory/arith/arith_static_learner.cpp
+++ b/src/theory/arith/arith_static_learner.cpp
@@ -182,6 +182,12 @@ void ArithStaticLearner::iteMinMax(TNode n, NodeBuilder<>& learned){
e = tmp;
k = reverseRelationKind(k);
}
+ //(ite (< x y) x y)
+ //(ite (x < y) x y)
+ //(ite (x - y < 0) x y)
+ // ----------------
+ // (ite (x - y < -c) )
+
if(t == cleft && e == cright){
// t == cleft && e == cright
Assert( t == cleft );
diff --git a/src/theory/arith/arith_utilities.h b/src/theory/arith/arith_utilities.h
index 2efd895f2..6b297f2ab 100644
--- a/src/theory/arith/arith_utilities.h
+++ b/src/theory/arith/arith_utilities.h
@@ -21,8 +21,10 @@
#include "util/rational.h"
#include "util/integer.h"
+#include "util/dense_map.h"
#include "expr/node.h"
#include "theory/arith/delta_rational.h"
+#include "theory/arith/arithvar.h"
#include "context/cdhashset.h"
#include <ext/hash_map>
#include <vector>
@@ -36,6 +38,10 @@ typedef __gnu_cxx::hash_set<Node, NodeHashFunction> NodeSet;
typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet;
typedef context::CDHashSet<Node, NodeHashFunction> CDNodeSet;
+//Maps from Nodes -> ArithVars, and vice versa
+typedef __gnu_cxx::hash_map<Node, ArithVar, NodeHashFunction> NodeToArithVarMap;
+typedef DenseMap<Node> ArithVarToNodeMap;
+
inline Node mkRationalNode(const Rational& q){
return NodeManager::currentNM()->mkConst<Rational>(q);
}
@@ -52,6 +58,27 @@ inline Node mkRealSkolem(const std::string& name){
return NodeManager::currentNM()->mkSkolem(name, NodeManager::currentNM()->realType());
}
+inline Node skolemFunction(const std::string& name, TypeNode dom, TypeNode range){
+ NodeManager* currNM = NodeManager::currentNM();
+ TypeNode functionType = currNM->mkFunctionType(dom, range);
+ return currNM->mkSkolem(name, functionType);
+}
+
+/**
+ * (For the moment) the type hierarchy goes as:
+ * Integer <: Real
+ * The type number of a variable is an integer representing the most specific
+ * type of the variable. The possible values of type number are:
+ */
+enum ArithType {
+ ATReal = 0,
+ ATInteger = 1
+};
+
+inline ArithType nodeToArithType(TNode x) {
+ return (x.getType().isInteger() ? ATInteger : ATReal);
+}
+
/** \f$ k \in {LT, LEQ, EQ, GEQ, GT} \f$ */
inline bool isRelationOperator(Kind k){
using namespace kind;
diff --git a/src/theory/arith/arithvar.h b/src/theory/arith/arithvar.h
index e6cecff3f..66b68339d 100644
--- a/src/theory/arith/arithvar.h
+++ b/src/theory/arith/arithvar.h
@@ -19,15 +19,12 @@
#include "cvc4_private.h"
-#ifndef __CVC4__THEORY__ARITH__ARITHVAR_H
-#define __CVC4__THEORY__ARITH__ARITHVAR_H
-
-#include <ext/hash_map>
-#include "expr/node.h"
-#include "context/cdhashset.h"
+#pragma once
+#include <vector>
#include "util/index.h"
-#include "util/dense_map.h"
+#include "util/rational.h"
+
namespace CVC4 {
namespace theory {
@@ -36,46 +33,11 @@ namespace arith {
typedef Index ArithVar;
extern const ArithVar ARITHVAR_SENTINEL;
-//Maps from Nodes -> ArithVars, and vice versa
-typedef __gnu_cxx::hash_map<Node, ArithVar, NodeHashFunction> NodeToArithVarMap;
-typedef DenseMap<Node> ArithVarToNodeMap;
-
-/**
- * ArithVarCallBack provides a mechanism for agreeing on callbacks while
- * breaking mutual recursion inclusion order problems.
- */
-class ArithVarCallBack {
-public:
- virtual void operator()(ArithVar x) = 0;
-};
-
-/**
- * Requests arithmetic variables for internal use,
- * and releases arithmetic variables that are no longer being used.
- */
-class ArithVarMalloc {
-public:
- virtual ArithVar request() = 0;
- virtual void release(ArithVar v) = 0;
-};
-
-class TNodeCallBack {
-public:
- virtual void operator()(TNode n) = 0;
-};
-
-class NodeCallBack {
-public:
- virtual void operator()(Node n) = 0;
-};
-
-class RationalCallBack {
-public:
- virtual Rational operator()() const = 0;
-};
+typedef std::vector<ArithVar> ArithVarVec;
+typedef std::pair<ArithVar, Rational> ArithRatPair;
+typedef std::vector< ArithRatPair > ArithRatPairVec;
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
-#endif /* __CVC4__THEORY__ARITH__ARITHVAR_H */
diff --git a/src/theory/arith/bound_counts.h b/src/theory/arith/bound_counts.h
new file mode 100644
index 000000000..954cc056a
--- /dev/null
+++ b/src/theory/arith/bound_counts.h
@@ -0,0 +1,144 @@
+#include "cvc4_private.h"
+#pragma once
+
+#include <stdint.h>
+#include "theory/arith/arithvar.h"
+#include "util/cvc4_assert.h"
+#include "util/dense_map.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+/**
+ * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
+ *
+ * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
+ * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
+ */
+class BoundCounts {
+private:
+ uint32_t d_atLowerBounds;
+ uint32_t d_atUpperBounds;
+
+public:
+ BoundCounts() : d_atLowerBounds(0), d_atUpperBounds(0) {}
+ BoundCounts(uint32_t lbs, uint32_t ubs)
+ : d_atLowerBounds(lbs), d_atUpperBounds(ubs) {}
+
+ bool operator==(BoundCounts bc) const {
+ return d_atLowerBounds == bc.d_atLowerBounds
+ && d_atUpperBounds == bc.d_atUpperBounds;
+ }
+ bool operator!=(BoundCounts bc) const {
+ return d_atLowerBounds != bc.d_atLowerBounds
+ || d_atUpperBounds != bc.d_atUpperBounds;
+ }
+ inline bool isZero() const{ return d_atLowerBounds == 0 && d_atUpperBounds == 0; }
+ inline uint32_t atLowerBounds() const{
+ return d_atLowerBounds;
+ }
+ inline uint32_t atUpperBounds() const{
+ return d_atUpperBounds;
+ }
+
+ inline BoundCounts operator+(BoundCounts bc) const{
+ return BoundCounts(d_atLowerBounds + bc.d_atLowerBounds,
+ d_atUpperBounds + bc.d_atUpperBounds);
+ }
+
+ inline BoundCounts operator-(BoundCounts bc) const {
+ Assert(d_atLowerBounds >= bc.d_atLowerBounds);
+ Assert(d_atUpperBounds >= bc.d_atUpperBounds);
+ return BoundCounts(d_atLowerBounds - bc.d_atLowerBounds,
+ d_atUpperBounds - bc.d_atUpperBounds);
+ }
+
+ inline void addInChange(int sgn, BoundCounts before, BoundCounts after){
+ Assert(before != after);
+ if(sgn < 0){
+ Assert(d_atUpperBounds >= before.d_atLowerBounds);
+ Assert(d_atLowerBounds >= before.d_atUpperBounds);
+ d_atUpperBounds += after.d_atLowerBounds - before.d_atLowerBounds;
+ d_atLowerBounds += after.d_atUpperBounds - before.d_atUpperBounds;
+ }else if(sgn > 0){
+ Assert(d_atUpperBounds >= before.d_atUpperBounds);
+ Assert(d_atLowerBounds >= before.d_atLowerBounds);
+ d_atUpperBounds += after.d_atUpperBounds - before.d_atUpperBounds;
+ d_atLowerBounds += after.d_atLowerBounds - before.d_atLowerBounds;
+ }
+ }
+
+ inline void addInSgn(BoundCounts bc, int before, int after){
+ Assert(before != after);
+ Assert(!bc.isZero());
+
+ if(before < 0){
+ d_atUpperBounds -= bc.d_atLowerBounds;
+ d_atLowerBounds -= bc.d_atUpperBounds;
+ }else if(before > 0){
+ d_atUpperBounds -= bc.d_atUpperBounds;
+ d_atLowerBounds -= bc.d_atLowerBounds;
+ }
+ if(after < 0){
+ d_atUpperBounds += bc.d_atLowerBounds;
+ d_atLowerBounds += bc.d_atUpperBounds;
+ }else if(after > 0){
+ d_atUpperBounds += bc.d_atUpperBounds;
+ d_atLowerBounds += bc.d_atLowerBounds;
+ }
+ }
+
+ inline BoundCounts& operator+=(BoundCounts bc) {
+ d_atUpperBounds += bc.d_atUpperBounds;
+ d_atLowerBounds += bc.d_atLowerBounds;
+ return *this;
+ }
+
+ inline BoundCounts& operator-=(BoundCounts bc) {
+ Assert(d_atLowerBounds >= bc.d_atLowerBounds);
+ Assert(d_atUpperBounds >= bc.d_atUpperBounds);
+ d_atUpperBounds -= bc.d_atUpperBounds;
+ d_atLowerBounds -= bc.d_atLowerBounds;
+
+ return *this;
+ }
+
+ inline BoundCounts multiplyBySgn(int sgn) const{
+ if(sgn > 0){
+ return *this;
+ }else if(sgn == 0){
+ return BoundCounts(0,0);
+ }else{
+ return BoundCounts(d_atUpperBounds, d_atLowerBounds);
+ }
+ }
+};
+
+typedef DenseMap<BoundCounts> BoundCountingVector;
+
+class BoundCountingLookup {
+private:
+ BoundCountingVector* d_bc;
+public:
+ BoundCountingLookup(BoundCountingVector* bc) : d_bc(bc) {}
+ BoundCounts boundCounts(ArithVar v) const {
+ Assert(d_bc->isKey(v));
+ return (*d_bc)[v];
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){
+ os << "[bc " << bc.atLowerBounds() << ", "
+ << bc.atUpperBounds() << "]";
+ return os;
+}
+
+class BoundCountsCallback {
+public:
+ virtual void operator()(ArithVar v, BoundCounts bc) = 0;
+};
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp
new file mode 100644
index 000000000..6b6170b20
--- /dev/null
+++ b/src/theory/arith/callbacks.cpp
@@ -0,0 +1,37 @@
+#include "theory/arith/callbacks.h"
+#include "theory/arith/theory_arith_private.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+void SetupLiteralCallBack::operator()(TNode lit){
+ TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit;
+ if(!d_arith.isSetup(atom)){
+ d_arith.setupAtom(atom);
+ }
+}
+
+Rational DeltaComputeCallback::operator()() const{
+ return d_ta.deltaValueForTotalOrder();
+}
+
+ArithVar TempVarMalloc::request(){
+ Node skolem = mkRealSkolem("tmpVar");
+ return d_ta.requestArithVar(skolem, false);
+}
+void TempVarMalloc::release(ArithVar v){
+ d_ta.releaseArithVar(v);
+}
+
+void BasicVarModelUpdateCallBack::operator()(ArithVar x){
+ d_ta.signal(x);
+}
+
+void RaiseConflict::operator()(Node n){
+ d_ta.raiseConflict(n);
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h
new file mode 100644
index 000000000..0d754d159
--- /dev/null
+++ b/src/theory/arith/callbacks.h
@@ -0,0 +1,92 @@
+
+#pragma once
+
+#include "expr/node.h"
+#include "util/rational.h"
+#include "context/cdlist.h"
+
+#include "theory/arith/theory_arith_private_forward.h"
+#include "theory/arith/arithvar.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+/**
+ * ArithVarCallBack provides a mechanism for agreeing on callbacks while
+ * breaking mutual recursion inclusion order problems.
+ */
+class ArithVarCallBack {
+public:
+ virtual void operator()(ArithVar x) = 0;
+};
+
+/**
+ * Requests arithmetic variables for internal use,
+ * and releases arithmetic variables that are no longer being used.
+ */
+class ArithVarMalloc {
+public:
+ virtual ArithVar request() = 0;
+ virtual void release(ArithVar v) = 0;
+};
+
+class TNodeCallBack {
+public:
+ virtual void operator()(TNode n) = 0;
+};
+
+class NodeCallBack {
+public:
+ virtual void operator()(Node n) = 0;
+};
+
+class RationalCallBack {
+public:
+ virtual Rational operator()() const = 0;
+};
+
+class SetupLiteralCallBack : public TNodeCallBack {
+private:
+ TheoryArithPrivate& d_arith;
+public:
+ SetupLiteralCallBack(TheoryArithPrivate& ta) : d_arith(ta){}
+ void operator()(TNode lit);
+};
+
+class DeltaComputeCallback : public RationalCallBack {
+private:
+ const TheoryArithPrivate& d_ta;
+public:
+ DeltaComputeCallback(const TheoryArithPrivate& ta) : d_ta(ta){}
+ Rational operator()() const;
+};
+
+class BasicVarModelUpdateCallBack : public ArithVarCallBack{
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ BasicVarModelUpdateCallBack(TheoryArithPrivate& ta) : d_ta(ta) {}
+ void operator()(ArithVar x);
+};
+
+class TempVarMalloc : public ArithVarMalloc {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ TempVarMalloc(TheoryArithPrivate& ta) : d_ta(ta) {}
+ ArithVar request();
+ void release(ArithVar v);
+};
+
+class RaiseConflict : public NodeCallBack {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ RaiseConflict(TheoryArithPrivate& ta) : d_ta(ta) {}
+ void operator()(Node n);
+};
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp
index c10d59234..e3e9055a7 100644
--- a/src/theory/arith/congruence_manager.cpp
+++ b/src/theory/arith/congruence_manager.cpp
@@ -24,7 +24,7 @@ namespace CVC4 {
namespace theory {
namespace arith {
-ArithCongruenceManager::ArithCongruenceManager(context::Context* c, ConstraintDatabase& cd, TNodeCallBack& setup, const ArithVarNodeMap& av2Node, NodeCallBack& raiseConflict)
+ArithCongruenceManager::ArithCongruenceManager(context::Context* c, ConstraintDatabase& cd, SetupLiteralCallBack setup, const ArithVariables& avars, RaiseConflict raiseConflict)
: d_inConflict(c),
d_raiseConflict(raiseConflict),
d_notify(*this),
@@ -33,7 +33,7 @@ ArithCongruenceManager::ArithCongruenceManager(context::Context* c, ConstraintDa
d_explanationMap(c),
d_constraintDatabase(cd),
d_setupLiteral(setup),
- d_av2Node(av2Node),
+ d_avariables(avars),
d_ee(d_notify, c, "theory::arith::ArithCongruenceManager")
{}
@@ -295,7 +295,7 @@ void ArithCongruenceManager::equalsConstant(Constraint c){
Debug("equalsConstant") << "equals constant " << c << std::endl;
ArithVar x = c->getVariable();
- Node xAsNode = d_av2Node.asNode(x);
+ Node xAsNode = d_avariables.asNode(x);
Node asRational = mkRationalNode(c->getValue().getNoninfinitesimalPart());
@@ -321,7 +321,7 @@ void ArithCongruenceManager::equalsConstant(Constraint lb, Constraint ub){
ArithVar x = lb->getVariable();
Node reason = ConstraintValue::explainConflict(lb,ub);
- Node xAsNode = d_av2Node.asNode(x);
+ Node xAsNode = d_avariables.asNode(x);
Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart());
//No guarentee this is in normal form!
diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h
index b0a4467bf..b4e009169 100644
--- a/src/theory/arith/congruence_manager.h
+++ b/src/theory/arith/congruence_manager.h
@@ -20,8 +20,8 @@
#pragma once
#include "theory/arith/arithvar.h"
-#include "theory/arith/arithvar_node_map.h"
#include "theory/arith/constraint_forward.h"
+#include "theory/arith/partial_model.h"
#include "theory/uf/equality_engine.h"
@@ -41,7 +41,7 @@ namespace arith {
class ArithCongruenceManager {
private:
context::CDRaised d_inConflict;
- NodeCallBack& d_raiseConflict;
+ RaiseConflict d_raiseConflict;
/**
* The set of ArithVars equivalent to a pair of terms.
@@ -111,9 +111,9 @@ private:
ExplainMap d_explanationMap;
ConstraintDatabase& d_constraintDatabase;
- TNodeCallBack& d_setupLiteral;
+ SetupLiteralCallBack d_setupLiteral;
- const ArithVarNodeMap& d_av2Node;
+ const ArithVariables& d_avariables;
eq::EqualityEngine d_ee;
@@ -181,23 +181,8 @@ private:
bool propagate(TNode x);
void explain(TNode literal, std::vector<TNode>& assumptions);
- /**
- * This is set to true when the first shared term is added.
- * When this is set to true in the context, d_queue is emptied
- * and not used again in the context.
- */
- //context::CDO<bool> d_hasSharedTerms;
-
-
- /**
- * The generalization of asserting an equality or a disequality.
- * If there are shared equalities, this is added to the equality engine.
- * Otherwise, this is put on a queue until there is a shared term.
- */
- //void assertLiteral(bool eq, ArithVar s, TNode reason);
/** This sends a shared term to the uninterpreted equality engine. */
- //void addAssertionToEqualityEngine(bool eq, ArithVar s, TNode reason);
void assertionToEqualityEngine(bool eq, ArithVar s, TNode reason);
/** Dequeues the delay queue and asserts these equalities.*/
@@ -210,7 +195,7 @@ private:
public:
- ArithCongruenceManager(context::Context* satContext, ConstraintDatabase&, TNodeCallBack& setLiteral, const ArithVarNodeMap&, NodeCallBack& raiseConflict);
+ ArithCongruenceManager(context::Context* satContext, ConstraintDatabase&, SetupLiteralCallBack, const ArithVariables&, RaiseConflict raiseConflict);
Node explain(TNode literal);
void explain(TNode lit, NodeBuilder<>& out);
diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp
index 28b999852..e26687bf1 100644
--- a/src/theory/arith/constraint.cpp
+++ b/src/theory/arith/constraint.cpp
@@ -371,8 +371,22 @@ void ConstraintValue::setAssertedToTheTheory(TNode witness) {
d_database->pushAssertionOrderWatch(this, witness);
}
-// bool ConstraintValue::isPseudoConstraint() const {
-// return d_proof == d_database->d_pseudoConstraintProof;
+bool ConstraintValue::satisfiedBy(const DeltaRational& dr) const {
+ switch(getType()){
+ case LowerBound:
+ return getValue() <= dr;
+ case Equality:
+ return getValue() == dr;
+ case UpperBound:
+ return getValue() >= dr;
+ case Disequality:
+ return getValue() != dr;
+ }
+ Unreachable();
+}
+
+// bool ConstraintValue::isPsuedoConstraint() const {
+// return d_proof == d_database->d_psuedoConstraintProof;
// }
bool ConstraintValue::isSelfExplaining() const {
@@ -394,7 +408,7 @@ bool ConstraintValue::sanityChecking(Node n) const {
TNode left = pleft.getNode();
DeltaRational right = cmp.normalizedDeltaRational();
- const ArithVarNodeMap& av2node = d_database->getArithVarNodeMap();
+ const ArithVariables& avariables = d_database->getArithVariables();
Debug("nf::tmp") << cmp.getNode() << endl;
Debug("nf::tmp") << k << endl;
@@ -402,13 +416,13 @@ bool ConstraintValue::sanityChecking(Node n) const {
Debug("nf::tmp") << left << endl;
Debug("nf::tmp") << right << endl;
Debug("nf::tmp") << getValue() << endl;
- Debug("nf::tmp") << av2node.hasArithVar(left) << endl;
- Debug("nf::tmp") << av2node.asArithVar(left) << endl;
+ Debug("nf::tmp") << avariables.hasArithVar(left) << endl;
+ Debug("nf::tmp") << avariables.asArithVar(left) << endl;
Debug("nf::tmp") << getVariable() << endl;
- if(av2node.hasArithVar(left) &&
- av2node.asArithVar(left) == getVariable() &&
+ if(avariables.hasArithVar(left) &&
+ avariables.asArithVar(left) == getVariable() &&
getValue() == right){
switch(getType()){
case LowerBound:
@@ -469,12 +483,12 @@ Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const Del
}
}
-ConstraintDatabase::ConstraintDatabase(context::Context* satContext, context::Context* userContext, const ArithVarNodeMap& av2nodeMap, ArithCongruenceManager& cm, NodeCallBack& raiseConflict)
+ConstraintDatabase::ConstraintDatabase(context::Context* satContext, context::Context* userContext, const ArithVariables& avars, ArithCongruenceManager& cm, RaiseConflict raiseConflict)
: d_varDatabases(),
d_toPropagate(satContext),
d_proofs(satContext, false),
d_watches(new Watches(satContext, userContext)),
- d_av2nodeMap(av2nodeMap),
+ d_avariables(avars),
d_congruenceManager(cm),
d_satContext(satContext),
d_satAllocationLevel(d_satContext->getLevel()),
@@ -670,7 +684,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){
Polynomial nvp = posCmp.normalizedVariablePart();
Debug("nf::tmp") << "here " << nvp.getNode() << " " << endl;
- ArithVar v = d_av2nodeMap.asArithVar(nvp.getNode());
+ ArithVar v = d_avariables.asArithVar(nvp.getNode());
DeltaRational posDR = posCmp.normalizedDeltaRational();
diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h
index e1c55f113..a5d64a652 100644
--- a/src/theory/arith/constraint.h
+++ b/src/theory/arith/constraint.h
@@ -72,12 +72,10 @@
#include "context/cdqueue.h"
#include "theory/arith/arithvar.h"
-#include "theory/arith/arithvar_node_map.h"
#include "theory/arith/delta_rational.h"
-
#include "theory/arith/congruence_manager.h"
-
#include "theory/arith/constraint_forward.h"
+#include "theory/arith/callbacks.h"
#include <vector>
#include <list>
@@ -604,6 +602,8 @@ public:
/** The node must have a proof already and be eligible for propagation! */
void propagate();
+ bool satisfiedBy(const DeltaRational& dr) const;
+
private:
/**
* Marks the node as having a proof and being selfExplaining.
@@ -620,11 +620,6 @@ private:
void markAsTrue(Constraint a, Constraint b);
void markAsTrue(const std::vector<Constraint>& b);
-public:
-
-
-private:
-
void debugPrint() const;
/**
@@ -776,20 +771,18 @@ private:
static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec);
/** Map from nodes to arithvars. */
- const ArithVarNodeMap& d_av2nodeMap;
+ const ArithVariables& d_avariables;
- const ArithVarNodeMap& getArithVarNodeMap() const{
- return d_av2nodeMap;
+ const ArithVariables& getArithVariables() const{
+ return d_avariables;
}
ArithCongruenceManager& d_congruenceManager;
- //Constraint allocateConstraintForLiteral(ArithVar v, Node literal);
-
const context::Context * const d_satContext;
const int d_satAllocationLevel;
- NodeCallBack& d_raiseConflict;
+ RaiseConflict d_raiseConflict;
friend class ConstraintValue;
@@ -797,9 +790,9 @@ public:
ConstraintDatabase( context::Context* satContext,
context::Context* userContext,
- const ArithVarNodeMap& av2nodeMap,
+ const ArithVariables& variables,
ArithCongruenceManager& dm,
- NodeCallBack& conflictCallBack);
+ RaiseConflict conflictCallBack);
~ConstraintDatabase();
diff --git a/src/theory/arith/delta_rational.h b/src/theory/arith/delta_rational.h
index e97bde555..7945b44c3 100644
--- a/src/theory/arith/delta_rational.h
+++ b/src/theory/arith/delta_rational.h
@@ -160,6 +160,14 @@ public:
}
+ DeltaRational abs() const {
+ if(sgn() >= 0){
+ return *this;
+ }else{
+ return (*this) * Rational(-1);
+ }
+ }
+
bool operator==(const DeltaRational& other) const{
return (k == other.k) && (c == other.c);
}
@@ -196,13 +204,20 @@ public:
return *(this);
}
- DeltaRational& operator+=(DeltaRational& other){
+ DeltaRational& operator+=(const DeltaRational& other){
c += other.c;
k += other.k;
return *(this);
}
+ DeltaRational& operator/=(const Rational& a){
+ Assert(!a.isZero());
+ c /= a;
+ k /= a;
+ return *(this);
+ }
+
bool isIntegral() const {
if(infinitesimalIsZero()){
return getNoninfinitesimalPart().isIntegral();
@@ -260,6 +275,17 @@ public:
*/
static void seperatingDelta(Rational& res, const DeltaRational& a, const DeltaRational& b);
+ uint32_t complexity() const {
+ return c.complexity() + k.complexity();
+ }
+
+ double approx(double deltaSub) const {
+ double maj = getNoninfinitesimalPart().getDouble();
+ double min = deltaSub * (getInfinitesimalPart().getDouble());
+ return maj + min;
+ }
+
+
};
std::ostream& operator<<(std::ostream& os, const DeltaRational& n);
diff --git a/src/theory/arith/dio_solver.cpp b/src/theory/arith/dio_solver.cpp
index a0a932bb4..92cd50864 100644
--- a/src/theory/arith/dio_solver.cpp
+++ b/src/theory/arith/dio_solver.cpp
@@ -15,6 +15,7 @@
**/
#include "theory/arith/dio_solver.h"
+#include "theory/arith/options.h"
#include <iostream>
@@ -30,7 +31,7 @@ inline Node makeIntegerVariable(){
}
DioSolver::DioSolver(context::Context* ctxt) :
- d_lastUsedVariable(ctxt,0),
+ d_lastUsedProofVariable(ctxt,0),
d_inputConstraints(ctxt),
d_nextInputConstraintToEnqueue(ctxt, 0),
d_trail(ctxt),
@@ -42,7 +43,8 @@ DioSolver::DioSolver(context::Context* ctxt) :
d_maxInputCoefficientLength(ctxt, 0),
d_usedDecomposeIndex(ctxt, false),
d_lastPureSubstitution(ctxt, 0),
- d_pureSubstitionIter(ctxt, 0)
+ d_pureSubstitionIter(ctxt, 0),
+ d_decompositionLemmaQueue(ctxt)
{}
DioSolver::Statistics::Statistics() :
@@ -90,34 +92,34 @@ bool DioSolver::queueConditions(TrailIndex t){
!triviallyUnsat(t);
}
-size_t DioSolver::allocateVariableInPool() {
- Assert(d_lastUsedVariable <= d_variablePool.size());
- if(d_lastUsedVariable == d_variablePool.size()){
- Assert(d_lastUsedVariable == d_variablePool.size());
+size_t DioSolver::allocateProofVariable() {
+ Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
+ if(d_lastUsedProofVariable == d_proofVariablePool.size()){
+ Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
Node intVar = makeIntegerVariable();
- d_variablePool.push_back(Variable(intVar));
+ d_proofVariablePool.push_back(Variable(intVar));
}
- size_t res = d_lastUsedVariable;
- d_lastUsedVariable = d_lastUsedVariable + 1;
+ size_t res = d_lastUsedProofVariable;
+ d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
return res;
}
- Node DioSolver::nextPureSubstitution(){
- Assert(hasMorePureSubstitutions());
- SubIndex curr = d_pureSubstitionIter;
- d_pureSubstitionIter = d_pureSubstitionIter + 1;
+Node DioSolver::nextPureSubstitution(){
+ Assert(hasMorePureSubstitutions());
+ SubIndex curr = d_pureSubstitionIter;
+ d_pureSubstitionIter = d_pureSubstitionIter + 1;
- Assert(d_subs[curr].d_fresh.isNull());
- Variable v = d_subs[curr].d_eliminated;
+ Assert(d_subs[curr].d_fresh.isNull());
+ Variable v = d_subs[curr].d_eliminated;
- SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
- Polynomial p = sp.getPolynomial();
- Constant c = -sp.getConstant();
- Polynomial cancelV = p + Polynomial::mkPolynomial(v);
- Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
- return eq;
- }
+ SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
+ Polynomial p = sp.getPolynomial();
+ Constant c = -sp.getConstant();
+ Polynomial cancelV = p + Polynomial::mkPolynomial(v);
+ Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
+ return eq;
+}
bool DioSolver::debugEqualityInInputEquations(Node eq){
@@ -144,8 +146,9 @@ void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
d_maxInputCoefficientLength = length;
}
- size_t varIndex = allocateVariableInPool();
- Variable proofVariable(d_variablePool[varIndex]);
+ size_t varIndex = allocateProofVariable();
+ Variable proofVariable(d_proofVariablePool[varIndex]);
+ //Variable proofVariable(makeIntegerVariable());
TrailIndex posInTrail = d_trail.size();
d_trail.push_back(Constraint(sp,Polynomial(Monomial(VarList(proofVariable)))));
@@ -429,7 +432,7 @@ bool DioSolver::processEquations(bool allowDecomposition){
reduceIndex = minimum;
}else{
TrailIndex implied = impliedGcdOfOne();
-
+
if(implied != 0){
p = solveIndex(implied);
reduceIndex = implied;
@@ -466,7 +469,7 @@ Node DioSolver::processEquationsForConflict(){
++(d_statistics.d_conflictCalls);
Assert(!inConflict());
- if(processEquations(false)){
+ if(processEquations(true)){
++(d_statistics.d_conflicts);
return proveIndex(getConflictIndex());
}else{
@@ -604,7 +607,7 @@ std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioS
Assert(p.isIntegral());
Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
- const Monomial& av = d_trail[i].d_minimalMonomial;
+ const Monomial av = d_trail[i].d_minimalMonomial;
VarList vl = av.getVarList();
Assert(vl.singleton());
@@ -648,7 +651,7 @@ std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(
Integer a_abs = a.getValue().getNumerator().abs();
Assert(a_abs > 1);
-
+
//It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
//We need to handle both cases seperately to ensure termination.
Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
@@ -673,6 +676,7 @@ std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(
TrailIndex ci = d_trail.size();
d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
// no longer reference av safely!
+ addTrailElementAsLemma(ci);
Debug("arith::dio") << "Decompose ci(" << ci <<":" << d_trail[ci].d_eq.getNode()
<< ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
@@ -796,6 +800,19 @@ void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
}
}
+void DioSolver::addTrailElementAsLemma(TrailIndex i) {
+ if(options::exportDioDecompositions()){
+ d_decompositionLemmaQueue.push(i);
+ }
+}
+
+Node DioSolver::trailIndexToEquality(TrailIndex i) const {
+ const SumPair& sp = d_trail[i].d_eq;
+ Node zero = mkRationalNode(0);
+ Node eq = (sp.getNode()).eqNode(zero);
+ return eq;
+}
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/dio_solver.h b/src/theory/arith/dio_solver.h
index b940bce76..f19291c98 100644
--- a/src/theory/arith/dio_solver.h
+++ b/src/theory/arith/dio_solver.h
@@ -20,10 +20,13 @@
#define __CVC4__THEORY__ARITH__DIO_SOLVER_H
#include "context/context.h"
+#include "context/cdo.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
-#include "theory/arith/matrix.h"
#include "theory/arith/partial_model.h"
#include "util/rational.h"
+#include "theory/arith/normal_form.h"
#include "util/statistics_registry.h"
@@ -40,8 +43,9 @@ private:
typedef size_t InputConstraintIndex;
typedef size_t SubIndex;
- std::vector<Variable> d_variablePool;
- context::CDO<size_t> d_lastUsedVariable;
+ std::vector<Variable> d_proofVariablePool;
+ /** Sat context dependent. */
+ context::CDO<size_t> d_lastUsedProofVariable;
/**
* The set of input constraints is stored in a CDList.
@@ -164,6 +168,11 @@ private:
context::CDO<SubIndex> d_lastPureSubstitution;
context::CDO<SubIndex> d_pureSubstitionIter;
+ /**
+ * Decomposition lemma queue.
+ */
+ context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
+
public:
/** Construct a Diophantine equation solver with the given context. */
@@ -231,11 +240,11 @@ private:
}
/**
- * Allocates a "unique" variables from the pool of integer variables.
+ * Allocates a "unique" proof variable.
* This variable is fresh with respect to the context.
* Returns index of the variable in d_variablePool;
*/
- size_t allocateVariableInPool();
+ size_t allocateProofVariable();
/** Empties the unproccessed input constraints into the queue. */
@@ -376,6 +385,21 @@ private:
void debugPrintTrail(TrailIndex i) const;
public:
+ bool hasMoreDecompositionLemmas() const{
+ return !d_decompositionLemmaQueue.empty();
+ }
+ Node nextDecompositionLemma() {
+ Assert(hasMoreDecompositionLemmas());
+ TrailIndex front = d_decompositionLemmaQueue.front();
+ d_decompositionLemmaQueue.pop();
+ return trailIndexToEquality(front);
+ }
+private:
+ Node trailIndexToEquality(TrailIndex i) const;
+ void addTrailElementAsLemma(TrailIndex i);
+
+public:
+
/** These fields are designed to be accessible to TheoryArith methods. */
class Statistics {
public:
diff --git a/src/theory/arith/dual_simplex.cpp b/src/theory/arith/dual_simplex.cpp
new file mode 100644
index 000000000..7caee6708
--- /dev/null
+++ b/src/theory/arith/dual_simplex.cpp
@@ -0,0 +1,259 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/dual_simplex.h"
+#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+DualSimplexDecisionProcedure::DualSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
+ , d_pivotsInRound()
+ , d_statistics(d_pivots)
+{ }
+
+DualSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots):
+ d_statUpdateConflicts("theory::arith::dual::UpdateConflicts", 0),
+ d_processSignalsTime("theory::arith::dual::findConflictOnTheQueueTime"),
+ d_simplexConflicts("theory::arith::dual::simplexConflicts",0),
+ d_recentViolationCatches("theory::arith::dual::recentViolationCatches",0),
+ d_searchTime("theory::arith::dual::searchTime"),
+ d_finalCheckPivotCounter("theory::arith::dual::lastPivots", pivots)
+{
+ StatisticsRegistry::registerStat(&d_statUpdateConflicts);
+ StatisticsRegistry::registerStat(&d_processSignalsTime);
+ StatisticsRegistry::registerStat(&d_simplexConflicts);
+ StatisticsRegistry::registerStat(&d_recentViolationCatches);
+ StatisticsRegistry::registerStat(&d_searchTime);
+ StatisticsRegistry::registerStat(&d_finalCheckPivotCounter);
+}
+
+DualSimplexDecisionProcedure::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_statUpdateConflicts);
+ StatisticsRegistry::unregisterStat(&d_processSignalsTime);
+ StatisticsRegistry::unregisterStat(&d_simplexConflicts);
+ StatisticsRegistry::unregisterStat(&d_recentViolationCatches);
+ StatisticsRegistry::unregisterStat(&d_searchTime);
+ StatisticsRegistry::unregisterStat(&d_finalCheckPivotCounter);
+}
+
+Result::Sat DualSimplexDecisionProcedure::dualFindModel(bool exactResult){
+ Assert(d_conflictVariables.empty());
+
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+ d_pivots = 0;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Debug("arith::findModel") << "dualFindModel("<< instance <<") trivial" << endl;
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+ d_errorSet.setSelectionRule(VAR_ORDER);
+
+ if(processSignals()){
+ d_conflictVariables.purge();
+
+ Debug("arith::findModel") << "dualFindModel("<< instance <<") early conflict" << endl;
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Debug("arith::findModel") << "dualFindModel("<< instance <<") fixed itself" << endl;
+ Assert(!d_errorSet.moreSignals());
+ return Result::SAT;
+ }
+
+ Debug("arith::findModel") << "dualFindModel(" << instance <<") start non-trivial" << endl;
+
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ static const bool verbose = false;
+ exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
+
+
+ uint32_t checkPeriod = options::arithSimplexCheckPeriod();
+ if(result == Result::SAT_UNKNOWN){
+ uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ?
+ d_numVariables + 1 : options::arithHeuristicPivots();
+ // The signed to unsigned conversion is safe.
+ if(numDifferencePivots > 0){
+
+ d_errorSet.setSelectionRule(d_heuristicRule);
+ if(searchForFeasibleSolution(numDifferencePivots)){
+ result = Result::UNSAT;
+ }
+ }
+
+ if(verbose && numDifferencePivots > 0){
+ if(result == Result::UNSAT){
+ Message() << "diff order found unsat" << endl;
+ }else if(d_errorSet.errorEmpty()){
+ Message() << "diff order found model" << endl;
+ }else{
+ Message() << "diff order missed" << endl;
+ }
+ }
+ }
+ Assert(!d_errorSet.moreSignals());
+
+ if(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+ if(exactResult){
+ d_errorSet.setSelectionRule(VAR_ORDER);
+ while(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+ Assert(checkPeriod > 0);
+ if(searchForFeasibleSolution(checkPeriod)){
+ result = Result::UNSAT;
+ }
+ }
+ }else if( options::arithStandardCheckVarOrderPivots() > 0){
+ d_errorSet.setSelectionRule(VAR_ORDER);
+ if(searchForFeasibleSolution(options::arithStandardCheckVarOrderPivots())){
+ result = Result::UNSAT;
+ }
+ if(verbose){
+ if(result == Result::UNSAT){
+ Message() << "restricted var order found unsat" << endl;
+ }else if(d_errorSet.errorEmpty()){
+ Message() << "restricted var order found model" << endl;
+ }else{
+ Message() << "restricted var order missed" << endl;
+ }
+ }
+ }
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
+ result = Result::SAT;
+ }
+
+ d_pivotsInRound.purge();
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
+
+ return result;
+}
+
+//corresponds to Check() in dM06
+//template <SimplexDecisionProcedure::PreferenceFunction pf>
+bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_searchTime);
+
+ Debug("arith") << "searchForFeasibleSolution" << endl;
+ Assert(remainingIterations > 0);
+
+ while(remainingIterations > 0 && !d_errorSet.focusEmpty()){
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+ Assert(d_conflictVariables.empty());
+ ArithVar x_i = d_errorSet.topFocusVariable();
+
+ Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
+ if(x_i == ARITHVAR_SENTINEL){
+ Debug("arith::update") << "No inconsistent variables" << endl;
+ return false; //sat
+ }
+
+ --remainingIterations;
+
+ bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold();
+ if(!useVarOrderPivot){
+ d_pivotsInRound.add(x_i);
+ }
+
+
+ Debug("arith::update")
+ << "pivots in rounds: " << d_pivotsInRound.count(x_i)
+ << " use " << useVarOrderPivot
+ << " threshold " << options::arithPivotThreshold()
+ << endl;
+
+ LinearEqualityModule::VarPreferenceFunction pf = useVarOrderPivot ?
+ &LinearEqualityModule::minVarOrder : &LinearEqualityModule::minBoundAndColLength;
+
+ //DeltaRational beta_i = d_variables.getAssignment(x_i);
+ ArithVar x_j = ARITHVAR_SENTINEL;
+
+ int32_t prevErrorSize = d_errorSet.errorSize();
+
+ if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){
+ x_j = d_linEq.selectSlackUpperBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ Unreachable();
+ ++(d_statistics.d_statUpdateConflicts);
+ reportConflict(x_i);
+ ++(d_statistics.d_simplexConflicts);
+ // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat
+ // d_conflictVariable = x_i;
+ // reportConflict(conflict);
+ return true;
+ }else{
+ const DeltaRational& l_i = d_variables.getLowerBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, l_i);
+ }
+ }else if(d_variables.cmpAssignmentUpperBound(x_i) > 0){
+ x_j = d_linEq.selectSlackLowerBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ Unreachable();
+ ++(d_statistics.d_statUpdateConflicts);
+ reportConflict(x_i);
+ ++(d_statistics.d_simplexConflicts);
+ // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat
+ // d_conflictVariable = x_i;
+ // reportConflict(conflict);
+ return true;
+ }else{
+ const DeltaRational& u_i = d_variables.getUpperBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, u_i);
+ }
+ }
+ Assert(x_j != ARITHVAR_SENTINEL);
+
+ bool conflict = processSignals();
+ int32_t currErrorSize = d_errorSet.errorSize();
+ d_pivots++;
+
+ // cout << "#" << d_pivots
+ // << " c" << conflict
+ // << " d" << (prevErrorSize - currErrorSize)
+ // << " f" << d_errorSet.inError(x_j)
+ // << " h" << d_conflictVariables.isMember(x_j)
+ // << " " << x_i << "->" << x_j
+ // << endl;
+
+ if(conflict){
+ return true;
+ }
+ }
+ Assert(!d_errorSet.focusEmpty() || d_errorSet.errorEmpty());
+ Assert(remainingIterations == 0 || d_errorSet.focusEmpty());
+ Assert(d_errorSet.noSignals());
+
+ return false;
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/dual_simplex.h b/src/theory/arith/dual_simplex.h
new file mode 100644
index 000000000..0ec4bc238
--- /dev/null
+++ b/src/theory/arith/dual_simplex.h
@@ -0,0 +1,115 @@
+/********************* */
+/*! \file simplex.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T)
+ ** decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only
+ ** variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction
+ ** Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that
+ ** Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the
+ ** current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the
+ ** conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "util/statistics_registry.h"
+#include "theory/arith/simplex.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class DualSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ DualSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
+
+ Result::Sat findModel(bool exactResult) {
+ return dualFindModel(exactResult);
+ }
+
+private:
+
+ /**
+ * Maps a variable to how many times they have been used as a pivot in the
+ * simplex search.
+ */
+ DenseMultiset d_pivotsInRound;
+
+ Result::Sat dualFindModel(bool exactResult);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+
+ bool processSignals(){
+ TimerStat &timer = d_statistics.d_processSignalsTime;
+ IntStat& conflictStat = d_statistics.d_recentViolationCatches;
+ return standardProcessSignals(timer, conflictStat);
+ }
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statUpdateConflicts;
+ TimerStat d_processSignalsTime;
+ IntStat d_simplexConflicts;
+ IntStat d_recentViolationCatches;
+ TimerStat d_searchTime;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(uint32_t& pivots);
+ ~Statistics();
+ } d_statistics;
+};/* class DualSimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp
new file mode 100644
index 000000000..ee72d1949
--- /dev/null
+++ b/src/theory/arith/error_set.cpp
@@ -0,0 +1,493 @@
+/********************* */
+/*! \file arith_priority_queue.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: mdeters
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/error_set.h"
+#include "theory/arith/constraint.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+ErrorInformation::ErrorInformation()
+ : d_variable(ARITHVAR_SENTINEL)
+ , d_violated(NullConstraint)
+ , d_sgn(0)
+ , d_relaxed(false)
+ , d_inFocus(false)
+ , d_handle()
+ , d_amount(NULL)
+ , d_metric(0)
+{
+ Debug("arith::error::mem") << "def constructor " << d_variable << " " << d_amount << endl;
+}
+
+ErrorInformation::ErrorInformation(ArithVar var, Constraint vio, int sgn)
+ : d_variable(var)
+ , d_violated(vio)
+ , d_sgn(sgn)
+ , d_relaxed(false)
+ , d_inFocus(false)
+ , d_handle()
+ , d_amount(NULL)
+ , d_metric(0)
+{
+ Assert(debugInitialized());
+ Debug("arith::error::mem") << "constructor " << d_variable << " " << d_amount << endl;
+}
+
+
+ErrorInformation::~ErrorInformation() {
+ Assert(d_relaxed != true);
+ if(d_amount != NULL){
+ Debug("arith::error::mem") << d_amount << endl;
+ Debug("arith::error::mem") << "destroy " << d_variable << " " << d_amount << endl;
+ delete d_amount;
+ d_amount = NULL;
+ }
+}
+
+ErrorInformation::ErrorInformation(const ErrorInformation& ei)
+ : d_variable(ei.d_variable)
+ , d_violated(ei.d_violated)
+ , d_sgn(ei.d_sgn)
+ , d_relaxed(ei.d_relaxed)
+ , d_inFocus(ei.d_inFocus)
+ , d_handle(ei.d_handle)
+ , d_metric(0)
+{
+ if(ei.d_amount == NULL){
+ d_amount = NULL;
+ }else{
+ d_amount = new DeltaRational(*ei.d_amount);
+ }
+ Debug("arith::error::mem") << "copy const " << d_variable << " " << d_amount << endl;
+}
+
+ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){
+ d_variable = ei.d_variable;
+ d_violated = ei.d_violated;
+ d_sgn = ei.d_sgn;
+ d_relaxed = (ei.d_relaxed);
+ d_inFocus = (ei.d_inFocus);
+ d_handle = (ei.d_handle);
+ d_metric = ei.d_metric;
+ if(d_amount != NULL && ei.d_amount != NULL){
+ Debug("arith::error::mem") << "assignment assign " << d_variable << " " << d_amount << endl;
+ *d_amount = *ei.d_amount;
+ }else if(ei.d_amount != NULL){
+ d_amount = new DeltaRational(*ei.d_amount);
+ Debug("arith::error::mem") << "assignment alloc " << d_variable << " " << d_amount << endl;
+ }else if(d_amount != NULL){
+ Debug("arith::error::mem") << "assignment release " << d_variable << " " << d_amount << endl;
+ delete d_amount;
+ d_amount = NULL;
+ }else{
+ d_amount = NULL;
+ }
+ return *this;
+}
+
+void ErrorInformation::reset(Constraint c, int sgn){
+ Assert(!isRelaxed());
+ Assert(c != NullConstraint);
+ d_violated = c;
+ d_sgn = sgn;
+
+ if(d_amount != NULL){
+ delete d_amount;
+ Debug("arith::error::mem") << "reset " << d_variable << " " << d_amount << endl;
+ d_amount = NULL;
+ }
+}
+
+void ErrorInformation::setAmount(const DeltaRational& am){
+ if(d_amount == NULL){
+ d_amount = new DeltaRational;
+ Debug("arith::error::mem") << "setAmount " << d_variable << " " << d_amount << endl;
+ }
+ (*d_amount) = am;
+}
+
+ErrorSet::Statistics::Statistics():
+ d_enqueues("theory::arith::pqueue::enqueues", 0),
+ d_enqueuesCollection("theory::arith::pqueue::enqueuesCollection", 0),
+ d_enqueuesDiffMode("theory::arith::pqueue::enqueuesDiffMode", 0),
+ d_enqueuesVarOrderMode("theory::arith::pqueue::enqueuesVarOrderMode", 0),
+ d_enqueuesCollectionDuplicates("theory::arith::pqueue::enqueuesCollectionDuplicates", 0),
+ d_enqueuesVarOrderModeDuplicates("theory::arith::pqueue::enqueuesVarOrderModeDuplicates", 0)
+{
+ StatisticsRegistry::registerStat(&d_enqueues);
+ StatisticsRegistry::registerStat(&d_enqueuesCollection);
+ StatisticsRegistry::registerStat(&d_enqueuesDiffMode);
+ StatisticsRegistry::registerStat(&d_enqueuesVarOrderMode);
+ StatisticsRegistry::registerStat(&d_enqueuesCollectionDuplicates);
+ StatisticsRegistry::registerStat(&d_enqueuesVarOrderModeDuplicates);
+}
+
+ErrorSet::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_enqueues);
+ StatisticsRegistry::unregisterStat(&d_enqueuesCollection);
+ StatisticsRegistry::unregisterStat(&d_enqueuesDiffMode);
+ StatisticsRegistry::unregisterStat(&d_enqueuesVarOrderMode);
+ StatisticsRegistry::unregisterStat(&d_enqueuesCollectionDuplicates);
+ StatisticsRegistry::unregisterStat(&d_enqueuesVarOrderModeDuplicates);
+}
+
+ErrorSet::ErrorSet(ArithVariables& vars, TableauSizes tabSizes, BoundCountingLookup lookups):
+ d_variables(vars),
+ d_errInfo(),
+ d_selectionRule(VAR_ORDER),
+ d_focus(ComparatorPivotRule(this,d_selectionRule)),
+ d_outOfFocus(),
+ d_signals(),
+ d_tableauSizes(tabSizes),
+ d_boundLookup(lookups)
+{}
+
+ErrorSelectionRule ErrorSet::getSelectionRule() const{
+ return d_selectionRule;
+}
+
+void ErrorSet::recomputeAmount(ErrorInformation& ei, ErrorSelectionRule rule){
+ switch(rule){
+ case MINIMUM_AMOUNT:
+ case MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(ei.getVariable()));
+ break;
+ case SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ break;
+ case VAR_ORDER:
+ //do nothing
+ break;
+ }
+}
+
+void ErrorSet::setSelectionRule(ErrorSelectionRule rule){
+ if(rule != getSelectionRule()){
+ FocusSet into(ComparatorPivotRule(this, rule));
+ FocusSet::const_iterator iter = d_focus.begin();
+ FocusSet::const_iterator i_end = d_focus.end();
+ for(; iter != i_end; ++iter){
+ ArithVar v = *iter;
+ ErrorInformation& ei = d_errInfo.get(v);
+ if(ei.inFocus()){
+ recomputeAmount(ei, rule);
+ FocusSetHandle handle = into.push(v);
+ ei.setHandle(handle);
+ }
+ }
+ d_focus.swap(into);
+ d_selectionRule = rule;
+ }
+ Assert(getSelectionRule() == rule);
+}
+
+ComparatorPivotRule::ComparatorPivotRule(const ErrorSet* es, ErrorSelectionRule r):
+ d_errorSet(es), d_rule (r)
+{}
+
+bool ComparatorPivotRule::operator()(ArithVar v, ArithVar u) const {
+ switch(d_rule){
+ case VAR_ORDER:
+ // This needs to be the reverse of the minVariableOrder
+ return v > u;
+ case SUM_METRIC:
+ {
+ uint32_t v_metric = d_errorSet->getMetric(v);
+ uint32_t u_metric = d_errorSet->getMetric(u);
+ if(v_metric == u_metric){
+ return v > u;
+ }else{
+ return v_metric > u_metric;
+ }
+ }
+ case MINIMUM_AMOUNT:
+ {
+ const DeltaRational& vamt = d_errorSet->getAmount(v);
+ const DeltaRational& uamt = d_errorSet->getAmount(u);
+ int cmp = vamt.cmp(uamt);
+ if(cmp == 0){
+ return v > u;
+ }else{
+ return cmp > 0;
+ }
+ }
+ case MAXIMUM_AMOUNT:
+ {
+ const DeltaRational& vamt = d_errorSet->getAmount(v);
+ const DeltaRational& uamt = d_errorSet->getAmount(u);
+ int cmp = vamt.cmp(uamt);
+ if(cmp == 0){
+ return v > u;
+ }else{
+ return cmp < 0;
+ }
+ }
+ }
+ Unreachable();
+}
+
+void ErrorSet::update(ErrorInformation& ei){
+ if(ei.inFocus()){
+
+ switch(getSelectionRule()){
+ case MINIMUM_AMOUNT:
+ case MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(ei.getVariable()));
+ d_focus.modify(ei.getHandle(), ei.getVariable());
+ break;
+ case SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ d_focus.modify(ei.getHandle(), ei.getVariable());
+ break;
+ case VAR_ORDER:
+ //do nothing
+ break;
+ }
+ }
+}
+
+/** A variable becomes satisfied. */
+void ErrorSet::transitionVariableOutOfError(ArithVar v) {
+ Assert(!inconsistent(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(ei.debugInitialized());
+ if(ei.isRelaxed()){
+ Constraint viol = ei.getViolated();
+ if(ei.sgn() > 0){
+ d_variables.setLowerBoundConstraint(viol);
+ }else{
+ d_variables.setUpperBoundConstraint(viol);
+ }
+ Assert(!inconsistent(v));
+ ei.setUnrelaxed();
+ }
+ if(ei.inFocus()){
+ d_focus.erase(ei.getHandle());
+ ei.setInFocus(false);
+ }
+ d_errInfo.remove(v);
+}
+
+
+void ErrorSet::transitionVariableIntoError(ArithVar v) {
+ Assert(inconsistent(v));
+ bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0;
+ int sgn = vilb ? 1 : -1;
+ Constraint c = vilb ?
+ d_variables.getLowerBoundConstraint(v) : d_variables.getUpperBoundConstraint(v);
+ d_errInfo.set(v, ErrorInformation(v, c, sgn));
+ ErrorInformation& ei = d_errInfo.get(v);
+
+ switch(getSelectionRule()){
+ case MINIMUM_AMOUNT:
+ case MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(v));
+ break;
+ case SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ break;
+ case VAR_ORDER:
+ //do nothing
+ break;
+ }
+ ei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ ei.setHandle(handle);
+}
+
+void ErrorSet::dropFromFocus(ArithVar v) {
+ Assert(inError(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(ei.inFocus());
+ d_focus.erase(ei.getHandle());
+ ei.setInFocus(false);
+ d_outOfFocus.push_back(v);
+}
+
+void ErrorSet::addBackIntoFocus(ArithVar v) {
+ Assert(inError(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(!ei.inFocus());
+ switch(getSelectionRule()){
+ case MINIMUM_AMOUNT:
+ case MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(v));
+ break;
+ case SUM_METRIC:
+ ei.setMetric(sumMetric(v));
+ break;
+ case VAR_ORDER:
+ //do nothing
+ break;
+ }
+
+ ei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ ei.setHandle(handle);
+}
+
+void ErrorSet::blur(){
+ while(!d_outOfFocus.empty()){
+ ArithVar v = d_outOfFocus.back();
+ d_outOfFocus.pop_back();
+
+ if(inError(v) && !inFocus(v)){
+ addBackIntoFocus(v);
+ }
+ }
+}
+
+
+
+int ErrorSet::popSignal() {
+ ArithVar back = d_signals.back();
+ d_signals.pop_back();
+
+ if(inError(back)){
+ ErrorInformation& ei = d_errInfo.get(back);
+ int prevSgn = ei.sgn();
+ int focusSgn = ei.focusSgn();
+ bool vilb = d_variables.cmpAssignmentLowerBound(back) < 0;
+ bool viub = d_variables.cmpAssignmentUpperBound(back) > 0;
+ if(vilb || viub){
+ Assert(!vilb || !viub);
+ int currSgn = vilb ? 1 : -1;
+ if(currSgn != prevSgn){
+ Constraint curr = vilb ? d_variables.getLowerBoundConstraint(back)
+ : d_variables.getUpperBoundConstraint(back);
+ ei.reset(curr, currSgn);
+ }
+ update(ei);
+ }else{
+ transitionVariableOutOfError(back);
+ }
+ return focusSgn;
+ }else if(inconsistent(back)){
+ transitionVariableIntoError(back);
+ }
+ return 0;
+}
+
+void ErrorSet::clear(){
+ // Nothing should be relaxed!
+ d_signals.clear();
+ d_errInfo.purge();
+ d_focus.clear();
+}
+
+void ErrorSet::clearFocus(){
+ for(ErrorSet::focus_iterator i =focusBegin(), i_end = focusEnd(); i != i_end; ++i){
+ ArithVar f = *i;
+ ErrorInformation& fei = d_errInfo.get(f);
+ fei.setInFocus(false);
+ d_outOfFocus.push_back(f);
+ }
+ d_focus.clear();
+}
+
+void ErrorSet::reduceToSignals(){
+ for(error_iterator ei=errorBegin(), ei_end=errorEnd(); ei != ei_end; ++ei){
+ ArithVar curr = *ei;
+ signalVariable(curr);
+ }
+
+ d_errInfo.purge();
+ d_focus.clear();
+ d_outOfFocus.clear();
+}
+
+DeltaRational ErrorSet::computeDiff(ArithVar v) const{
+ Assert(inconsistent(v));
+ const DeltaRational& beta = d_variables.getAssignment(v);
+ DeltaRational diff = d_variables.cmpAssignmentLowerBound(v) < 0 ?
+ d_variables.getLowerBound(v) - beta:
+ beta - d_variables.getUpperBound(v);
+
+ Assert(diff.sgn() > 0);
+ return diff;
+}
+
+ostream& operator<<(ostream& out, ErrorSelectionRule rule) {
+ switch(rule) {
+ case VAR_ORDER:
+ out << "VAR_ORDER";
+ break;
+ case MINIMUM_AMOUNT:
+ out << "MINIMUM_AMOUNT";
+ break;
+ case MAXIMUM_AMOUNT:
+ out << "MAXIMUM_AMOUNT";
+ break;
+ case SUM_METRIC:
+ out << "SUM_METRIC";
+ break;
+ }
+
+ return out;
+}
+
+void ErrorSet::debugPrint(std::ostream& out) const {
+ static int instance = 0;
+ ++instance;
+ out << "error set debugprint " << instance << endl;
+ for(error_iterator i = errorBegin(), i_end = errorEnd();
+ i != i_end; ++i){
+ ArithVar e = *i;
+ const ErrorInformation& ei = d_errInfo[e];
+ ei.print(out);
+ out << " ";
+ d_variables.printModel(e, out);
+ out << endl;
+ }
+ out << "focus ";
+ for(focus_iterator i = focusBegin(), i_end = focusEnd();
+ i != i_end; ++i){
+ out << *i << " ";
+ }
+ out << ";" << endl;
+}
+
+void ErrorSet::focusDownToJust(ArithVar v) {
+ clearFocus();
+
+ ErrorInformation& vei = d_errInfo.get(v);
+ vei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ vei.setHandle(handle);
+}
+
+void ErrorSet::pushErrorInto(ArithVarVec& vec) const{
+ for(error_iterator i = errorBegin(), e = errorEnd(); i != e; ++i ){
+ vec.push_back(*i);
+ }
+}
+
+void ErrorSet::pushFocusInto(ArithVarVec& vec) const{
+ for(focus_iterator i = focusBegin(), e = focusEnd(); i != e; ++i ){
+ vec.push_back(*i);
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h
new file mode 100644
index 000000000..ce6412573
--- /dev/null
+++ b/src/theory/arith/error_set.h
@@ -0,0 +1,407 @@
+/********************* */
+/*! \file arith_priority_queue.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "theory/arith/arithvar.h"
+#include "theory/arith/bound_counts.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/arith_heuristic_pivot_rule.h"
+#include "theory/arith/tableau_sizes.h"
+
+#include "util/statistics_registry.h"
+//#include <boost/heap/d_ary_heap.hpp>
+#include <ext/pb_ds/priority_queue.hpp>
+
+#include <vector>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+/**
+ * The priority queue has 3 different modes of operation:
+ * - Collection
+ * This passively collects arithmetic variables that may be inconsistent.
+ * This does not maintain any heap structure.
+ * dequeueInconsistentBasicVariable() does not work in this mode!
+ * Entering this mode requires the queue to be empty.
+ *
+ * - Difference Queue
+ * This mode uses the difference between a variables and its bound
+ * to determine which to dequeue first.
+ *
+ * - Variable Order Queue
+ * This mode uses the variable order to determine which ArithVar is dequeued first.
+ *
+ * The transitions between the modes of operation are:
+ * Collection => Difference Queue
+ * Difference Queue => Variable Order Queue
+ * Difference Queue => Collection (queue must be empty!)
+ * Variable Order Queue => Collection (queue must be empty!)
+ *
+ * The queue begins in Collection mode.
+ */
+
+
+class ErrorSet;
+class ErrorInfoMap;
+
+class ComparatorPivotRule {
+private:
+ const ErrorSet* d_errorSet;
+
+ ErrorSelectionRule d_rule;
+public:
+ ComparatorPivotRule();
+ ComparatorPivotRule(const ErrorSet* es, ErrorSelectionRule r);
+
+ bool operator()(ArithVar v, ArithVar u) const;
+ ErrorSelectionRule getRule() const { return d_rule; }
+};
+
+// typedef boost::heap::d_ary_heap<
+// ArithVar,
+// boost::heap::arity<2>,
+// boost::heap::compare<ComparatorPivotRule>,
+// boost::heap::mutable_<true> > FocusSet;
+//
+// typedef FocusSet::handle_type FocusSetHandle;
+
+typedef __gnu_pbds::priority_queue<
+ ArithVar,
+ ComparatorPivotRule,
+ __gnu_pbds::pairing_heap_tag> FocusSet;
+
+typedef FocusSet::point_iterator FocusSetHandle;
+
+class ErrorInformation {
+private:
+ /** The variable that is in error. */
+ ArithVar d_variable;
+
+ /**
+ * The constraint that was violated.
+ * This needs to be saved in case that the
+ * violated constraint
+ */
+ Constraint d_violated;
+
+ /**
+ * This is the sgn of the first derivate the variable must move to satisfy
+ * the bound violated.
+ * If d_sgn > 0, then d_violated was a lowerbound.
+ * If d_sgn < 0, then d_violated was an upperbound.
+ */
+ int d_sgn;
+
+ /**
+ * If this is true, then the bound is no longer set on d_variables.
+ * This MUST be undone before this is deleted.
+ */
+ bool d_relaxed;
+
+ /**
+ * If this is true, then the variable is in the focus set and the focus heap.
+ * d_handle is then a reasonable thing to interpret.
+ * If this is false, the variable is somewhere in
+ */
+ bool d_inFocus;
+ FocusSetHandle d_handle;
+
+ /**
+ * Auxillary information for storing the difference between a variable and its bound.
+ * Only set on signals.
+ */
+ DeltaRational* d_amount;
+
+ /** */
+ uint32_t d_metric;
+
+public:
+ ErrorInformation();
+ ErrorInformation(ArithVar var, Constraint vio, int sgn);
+ ~ErrorInformation();
+ ErrorInformation(const ErrorInformation& ei);
+ ErrorInformation& operator=(const ErrorInformation& ei);
+
+ void reset(Constraint c, int sgn);
+
+ inline ArithVar getVariable() const { return d_variable; }
+
+ bool isRelaxed() const { return d_relaxed; }
+ void setRelaxed(){ Assert(!d_relaxed); d_relaxed = true; }
+ void setUnrelaxed(){ Assert(d_relaxed); d_relaxed = false; }
+
+ inline int sgn() const { return d_sgn; }
+
+ inline bool inFocus() const { return d_inFocus; }
+ inline int focusSgn() const {
+ return (d_inFocus) ? sgn() : 0;
+ }
+
+ inline void setInFocus(bool inFocus) { d_inFocus = inFocus; }
+
+ const DeltaRational& getAmount() const {
+ Assert(d_amount != NULL);
+ return *d_amount;
+ }
+
+ void setAmount(const DeltaRational& am);
+ void setMetric(uint32_t m) { d_metric = m; }
+ uint32_t getMetric() const { return d_metric; }
+
+ inline void setHandle(FocusSetHandle h) {
+ Assert(d_inFocus);
+ d_handle = h;
+ }
+ inline const FocusSetHandle& getHandle() const{ return d_handle; }
+
+ inline Constraint getViolated() const { return d_violated; }
+
+ bool debugInitialized() const {
+ return
+ d_variable != ARITHVAR_SENTINEL &&
+ d_violated != NullConstraint &&
+ d_sgn != 0;
+ }
+ void print(std::ostream& os) const {
+ os << "{ErrorInfo: " << d_variable
+ << ", " << d_violated
+ << ", " << d_sgn
+ << ", " << d_relaxed
+ << ", " << d_inFocus;
+ if(d_amount == NULL){
+ os << "NULL";
+ }else{
+ os << (*d_amount);
+ }
+ os << "}";
+ }
+};
+
+class ErrorInfoMap : public DenseMap<ErrorInformation> {};
+
+class ErrorSet {
+private:
+ /**
+ * Reference to the arithmetic partial model for checking if a variable
+ * is consistent with its upper and lower bounds.
+ */
+ ArithVariables& d_variables;
+
+ /**
+ * The set of all variables that violate exactly one of their bounds.
+ */
+ ErrorInfoMap d_errInfo;
+
+ ErrorSelectionRule d_selectionRule;
+ /**
+ * The ordered heap for the variables that are in ErrorSet.
+ */
+ FocusSet d_focus;
+
+
+ /**
+ * A strict subset of the error set.
+ * d_outOfFocus \neq d_errInfo.
+ *
+ * Its symbolic complement is Focus.
+ * d_outOfFocus \intersect Focus == \emptyset
+ * d_outOfFocus \union Focus == d_errInfo
+ */
+ ArithVarVec d_outOfFocus;
+
+ /**
+ * Before a variable is added to the error set, it is added to the signals list.
+ * A variable may appear on the list multiple times.
+ * This introduces a delay.
+ */
+ ArithVarVec d_signals;
+
+ TableauSizes d_tableauSizes;
+
+ BoundCountingLookup d_boundLookup;
+
+ /**
+ * Computes the difference between the assignment and its bound for x.
+ */
+public:
+ DeltaRational computeDiff(ArithVar x) const;
+private:
+ void recomputeAmount(ErrorInformation& ei, ErrorSelectionRule r);
+
+ void update(ErrorInformation& ei);
+ void transitionVariableOutOfError(ArithVar v);
+ void transitionVariableIntoError(ArithVar v);
+ void addBackIntoFocus(ArithVar v);
+
+public:
+
+ /** The new focus set is the entire error set. */
+ void blur();
+ void dropFromFocus(ArithVar v);
+
+ void dropFromFocusAll(const ArithVarVec& vec) {
+ for(ArithVarVec::const_iterator i = vec.begin(), i_end = vec.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ dropFromFocus(v);
+ }
+ }
+
+ ErrorSet(ArithVariables& var, TableauSizes tabSizes, BoundCountingLookup boundLookup);
+
+ typedef ErrorInfoMap::const_iterator error_iterator;
+ error_iterator errorBegin() const { return d_errInfo.begin(); }
+ error_iterator errorEnd() const { return d_errInfo.end(); }
+
+ bool inError(ArithVar v) const { return d_errInfo.isKey(v); }
+ bool inFocus(ArithVar v) const { return d_errInfo[v].inFocus(); }
+
+ void pushErrorInto(ArithVarVec& vec) const;
+ void pushFocusInto(ArithVarVec& vec) const;
+
+ ErrorSelectionRule getSelectionRule() const;
+ void setSelectionRule(ErrorSelectionRule rule);
+
+ inline ArithVar topFocusVariable() const{
+ Assert(!focusEmpty());
+ return d_focus.top();
+ }
+
+ inline void signalVariable(ArithVar var){
+ d_signals.push_back(var);
+ }
+
+ inline void signalUnderCnd(ArithVar var, bool b){
+ if(b){ signalVariable(var); }
+ }
+
+ inline bool inconsistent(ArithVar var) const{
+ return !d_variables.assignmentIsConsistent(var) ;
+ }
+ inline void signalIfInconsistent(ArithVar var){
+ signalUnderCnd(var, inconsistent(var));
+ }
+
+ inline bool errorEmpty() const{
+ return d_errInfo.empty();
+ }
+ inline uint32_t errorSize() const{
+ return d_errInfo.size();
+ }
+
+ inline bool focusEmpty() const {
+ return d_focus.empty();
+ }
+ inline uint32_t focusSize() const{
+ return d_focus.size();
+ }
+
+ inline int getSgn(ArithVar x) const {
+ Assert(inError(x));
+ return d_errInfo[x].sgn();
+ }
+ inline int focusSgn(ArithVar v) const {
+ if(inError(v)){
+ return d_errInfo[v].focusSgn();
+ }else{
+ return 0;
+ }
+ }
+
+ void focusDownToJust(ArithVar v);
+
+ void clearFocus();
+
+ /** Clears the set. */
+ void clear();
+ void reduceToSignals();
+
+ bool noSignals() const {
+ return d_signals.empty();
+ }
+ bool moreSignals() const {
+ return !noSignals();
+ }
+ ArithVar topSignal() const {
+ Assert(moreSignals());
+ return d_signals.back();
+ }
+
+ /**
+ * Moves a variable out of the signals.
+ * This moves it into the error set.
+ * Return the previous focus sign.
+ */
+ int popSignal();
+
+ const DeltaRational& getAmount(ArithVar v) const {
+ return d_errInfo[v].getAmount();
+ }
+
+ uint32_t sumMetric(ArithVar a) const{
+ Assert(inError(a));
+ BoundCounts bcs = d_boundLookup.boundCounts(a);
+ uint32_t count = getSgn(a) > 0 ? bcs.atUpperBounds() : bcs.atLowerBounds();
+
+ uint32_t length = d_tableauSizes.getRowLength(a);
+
+ return (length - count);
+ }
+
+ uint32_t getMetric(ArithVar a) const {
+ return d_errInfo[a].getMetric();
+ }
+
+ Constraint getViolated(ArithVar a) const {
+ return d_errInfo[a].getViolated();
+ }
+
+
+ typedef FocusSet::const_iterator focus_iterator;
+ focus_iterator focusBegin() const { return d_focus.begin(); }
+ focus_iterator focusEnd() const { return d_focus.end(); }
+
+ void debugPrint(std::ostream& out) const;
+
+private:
+ class Statistics {
+ public:
+ IntStat d_enqueues;
+ IntStat d_enqueuesCollection;
+ IntStat d_enqueuesDiffMode;
+ IntStat d_enqueuesVarOrderMode;
+
+ IntStat d_enqueuesCollectionDuplicates;
+ IntStat d_enqueuesVarOrderModeDuplicates;
+
+ Statistics();
+ ~Statistics();
+ };
+
+ Statistics d_statistics;
+};
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp
new file mode 100644
index 000000000..ac4625ba3
--- /dev/null
+++ b/src/theory/arith/fc_simplex.cpp
@@ -0,0 +1,853 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/fc_simplex.h"
+#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
+
+#include "util/statistics_registry.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+FCSimplexDecisionProcedure::FCSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
+ , d_focusSize(0)
+ , d_focusErrorVar(ARITHVAR_SENTINEL)
+ , d_focusCoefficients()
+ , d_pivotBudget(0)
+ , d_prevWitnessImprovement(AntiProductive)
+ , d_witnessImprovementInARow(0)
+ , d_sgnDisagreements()
+ , d_statistics(d_pivots)
+{ }
+
+FCSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots):
+ d_initialSignalsTime("theory::arith::FC::initialProcessTime"),
+ d_initialConflicts("theory::arith::FC::UpdateConflicts", 0),
+ d_fcFoundUnsat("theory::arith::FC::FoundUnsat", 0),
+ d_fcFoundSat("theory::arith::FC::FoundSat", 0),
+ d_fcMissed("theory::arith::FC::Missed", 0),
+ d_fcTimer("theory::arith::FC::Timer"),
+ d_fcFocusConstructionTimer("theory::arith::FC::Construction"),
+ d_selectUpdateForDualLike("theory::arith::FC::selectUpdateForDualLike"),
+ d_selectUpdateForPrimal("theory::arith::FC::selectUpdateForPrimal"),
+ d_finalCheckPivotCounter("theory::arith::FC::lastPivots", pivots)
+{
+ StatisticsRegistry::registerStat(&d_initialSignalsTime);
+ StatisticsRegistry::registerStat(&d_initialConflicts);
+
+ StatisticsRegistry::registerStat(&d_fcFoundUnsat);
+ StatisticsRegistry::registerStat(&d_fcFoundSat);
+ StatisticsRegistry::registerStat(&d_fcMissed);
+
+ StatisticsRegistry::registerStat(&d_fcTimer);
+ StatisticsRegistry::registerStat(&d_fcFocusConstructionTimer);
+
+ StatisticsRegistry::registerStat(&d_selectUpdateForDualLike);
+ StatisticsRegistry::registerStat(&d_selectUpdateForPrimal);
+
+ StatisticsRegistry::registerStat(&d_finalCheckPivotCounter);
+}
+
+FCSimplexDecisionProcedure::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_initialSignalsTime);
+ StatisticsRegistry::unregisterStat(&d_initialConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_fcFoundUnsat);
+ StatisticsRegistry::unregisterStat(&d_fcFoundSat);
+ StatisticsRegistry::unregisterStat(&d_fcMissed);
+
+ StatisticsRegistry::unregisterStat(&d_fcTimer);
+ StatisticsRegistry::unregisterStat(&d_fcFocusConstructionTimer);
+
+ StatisticsRegistry::unregisterStat(&d_selectUpdateForDualLike);
+ StatisticsRegistry::unregisterStat(&d_selectUpdateForPrimal);
+
+ StatisticsRegistry::unregisterStat(&d_finalCheckPivotCounter);
+}
+
+Result::Sat FCSimplexDecisionProcedure::findModel(bool exactResult){
+ Assert(d_conflictVariables.empty());
+ Assert(d_sgnDisagreements.empty());
+
+ d_pivots = 0;
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+ static const bool verbose = false;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Debug("arith::findModel") << "fcFindModel("<< instance <<") trivial" << endl;
+ Assert(d_conflictVariables.empty());
+ //if(verbose){ Message() << "fcFindModel("<< instance <<") trivial" << endl; }
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+
+ // We must start tracking NOW
+ d_errorSet.setSelectionRule(SUM_METRIC);
+
+
+ if(initialProcessSignals()){
+ d_conflictVariables.purge();
+ if(verbose){ Message() << "fcFindModel("<< instance <<") early conflict" << endl; }
+ Debug("arith::findModel") << "fcFindModel("<< instance <<") early conflict" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ //if(verbose){ Message() << "fcFindModel("<< instance <<") fixed itself" << endl; }
+ Debug("arith::findModel") << "fcFindModel("<< instance <<") fixed itself" << endl;
+ if(verbose)
+ Assert(!d_errorSet.moreSignals());
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ Debug("arith::findModel") << "fcFindModel(" << instance <<") start non-trivial" << endl;
+
+ exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
+
+ d_prevWitnessImprovement = HeuristicDegenerate;
+ d_witnessImprovementInARow = 0;
+
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ if(result == Result::SAT_UNKNOWN){
+ if(exactResult){
+ d_pivotBudget = -1;
+ }else{
+ d_pivotBudget = options::arithStandardCheckVarOrderPivots();
+ }
+
+ result = dualLike();
+
+ if(result == Result::UNSAT){
+ ++(d_statistics.d_fcFoundUnsat);
+ if(verbose){ Message() << "fc found unsat";}
+ }else if(d_errorSet.errorEmpty()){
+ ++(d_statistics.d_fcFoundSat);
+ if(verbose){ Message() << "fc found model"; }
+ }else{
+ ++(d_statistics.d_fcMissed);
+ if(verbose){ Message() << "fc missed"; }
+ }
+ }
+ if(verbose){
+ Message() << "(" << instance << ") pivots " << d_pivots << endl;
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
+ result = Result::SAT;
+ }
+
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
+
+ Assert(d_conflictVariables.empty());
+ return result;
+}
+
+
+void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){
+ if(d_pivotBudget > 0) {
+ --d_pivotBudget;
+ }
+ Assert(w != AntiProductive);
+
+ if(w == d_prevWitnessImprovement){
+ ++d_witnessImprovementInARow;
+ // ignore overflow : probably never reached
+ if(d_witnessImprovementInARow == 0){
+ --d_witnessImprovementInARow;
+ }
+ }else{
+ if(w != BlandsDegenerate){
+ d_witnessImprovementInARow = 1;
+ }
+ // if w == BlandsDegenerate do not reset the counter
+ d_prevWitnessImprovement = w;
+ }
+ if(strongImprovement(w)){
+ d_leavingCountSinceImprovement.purge();
+ }
+
+ Debug("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
+
+}
+
+uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const {
+ switch(d_prevWitnessImprovement){
+ case ConflictFound:
+ case ErrorDropped:
+ case FocusImproved:
+ return 0;
+ case HeuristicDegenerate:
+ case BlandsDegenerate:
+ return d_witnessImprovementInARow;
+ // Degenerate is unreachable for its own reasons
+ case Degenerate:
+ case FocusShrank:
+ case AntiProductive:
+ Unreachable();
+ return -1;
+ }
+ Unreachable();
+}
+
+void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+ uint32_t newErrorSize = d_errorSet.errorSize();
+ uint32_t newFocusSize = d_errorSet.focusSize();
+
+ //Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize);
+ Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize);
+
+ if(newFocusSize == 0 || !d_conflictVariables.empty() ){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = ARITHVAR_SENTINEL;
+ }else if(2*newFocusSize < d_focusSize ){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+ }else{
+ adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges);
+ }
+
+ d_errorSize = newErrorSize;
+ d_focusSize = newFocusSize;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){
+ Assert(dropped.size() > 0);
+ Assert(d_errorSet.focusSize() == d_focusSize);
+ Assert(d_errorSet.focusSize() > dropped.size());
+
+ uint32_t newFocusSize = d_focusSize - dropped.size();
+ Assert(newFocusSize > 0);
+
+ if(2 * newFocusSize <= d_focusSize){
+ d_errorSet.dropFromFocusAll(dropped);
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+ }else{
+ shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped);
+ d_errorSet.dropFromFocusAll(dropped);
+ }
+
+ d_focusSize = newFocusSize;
+ Assert(d_errorSet.focusSize() == d_focusSize);
+ return FocusShrank;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){
+ // uint32_t newErrorSize = d_errorSet.errorSize();
+ // uint32_t newFocusSize = d_errorSet.focusSize();
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_focusSize > 1);
+ Assert(d_errorSet.inFocus(v));
+
+ d_errorSet.focusDownToJust(v);
+ Assert(d_errorSet.focusSize() == 1);
+ d_focusSize = 1;
+
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+ return FocusShrank;
+}
+
+
+
+UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+ UpdateInfo selected;
+
+ static int instance = 0 ;
+ ++instance;
+
+ Debug("arith::selectPrimalUpdate")
+ << "selectPrimalUpdate " << instance << endl
+ << basic << " " << d_tableau.basicRowLength(basic)
+ << " " << d_linEq._countBounds(basic) << endl;
+
+ static const int s_maxCandidatesAfterImprove = 3;
+ bool isFocus = basic == d_focusErrorVar;
+ Assert(isFocus || d_errorSet.inError(basic));
+ int basicDir = isFocus? 1 : d_errorSet.getSgn(basic);
+ bool dualLike = !isFocus && d_focusSize > 1;
+
+ if(!isFocus){
+ loadFocusSigns();
+ }
+
+ decreasePenalties();
+
+ typedef std::vector<Cand> CandVector;
+ CandVector candidates;
+
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ if(curr == basic){ continue; }
+
+ int sgn = e.getCoefficient().sgn();
+ int curr_movement = basicDir * sgn;
+
+ bool candidate =
+ (curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+ (curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+ Debug("arith::selectPrimalUpdate")
+ << "storing " << basic
+ << " " << curr
+ << " " << candidate
+ << " " << e.getCoefficient()
+ << " " << curr_movement
+ << " " << focusCoefficient(curr) << endl;
+
+ if(!candidate) { continue; }
+
+ if(!isFocus){
+ const Rational& focusC = focusCoefficient(curr);
+ Assert(dualLike || !focusC.isZero());
+ if(dualLike && curr_movement != focusC.sgn()){
+ Debug("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl;
+ d_sgnDisagreements.push_back(curr);
+ continue;
+ }else{
+ candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC));
+ }
+ }else{
+ candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient()));
+ }
+ }
+
+ CompPenaltyColLength colCmp(&d_linEq);
+ CandVector::iterator i = candidates.begin();
+ CandVector::iterator end = candidates.end();
+ std::make_heap(i, end, colCmp);
+
+ bool checkEverything = d_pivots == 0;
+
+ int candidatesAfterFocusImprove = 0;
+ while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){
+ std::pop_heap(i, end, colCmp);
+ --end;
+ Cand& cand = (*end);
+ ArithVar curr = cand.d_nb;
+ const Rational& coeff = *cand.d_coeff;
+
+#warning "Who is using computeSafeUpdate?"
+ LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+ UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+ //int curr_movement = cand.d_sgn;
+ // if(isFocus){
+ // currProposal = d_linEq.speculativeUpdate(curr, coeff, upf);
+ // }else{
+ // currProposal = UpdateInfo(curr, curr_movement);
+ // d_linEq.computeSafeUpdate(currProposal, bpf);
+ // }
+
+ Debug("arith::selectPrimalUpdate")
+ << "selected " << selected << endl
+ << "currProp " << currProposal << endl
+ << "coeff " << coeff << endl;
+
+ Assert(!currProposal.uninitialized());
+
+ if(candidatesAfterFocusImprove > 0){
+ candidatesAfterFocusImprove++;
+ }
+
+ if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+
+ selected = currProposal;
+ WitnessImprovement w = selected.getWitness(false);
+ Debug("arith::selectPrimalUpdate") << "selected " << w << endl;
+ setPenalty(curr, w);
+ if(improvement(w)){
+ bool exitEarly;
+ switch(w){
+ case ConflictFound: exitEarly = true; break;
+ case ErrorDropped:
+ if(checkEverything){
+ exitEarly = d_errorSize + selected.errorsChange() == 0;
+ Debug("arith::selectPrimalUpdate")
+ << "ee " << d_errorSize << " "
+ << selected.errorsChange() << " "
+ << d_errorSize + selected.errorsChange() << endl;
+ }else{
+ exitEarly = true;
+ }
+ break;
+ case FocusImproved:
+ candidatesAfterFocusImprove = 1;
+ exitEarly = false;
+ break;
+ default:
+ exitEarly = false; break;
+ }
+ if(exitEarly){ break; }
+ }
+ }else{
+ Debug("arith::selectPrimalUpdate") << "dropped "<< endl;
+ }
+
+ }
+
+ if(!isFocus){
+ unloadFocusSigns();
+ }
+ return selected;
+}
+
+bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+ if(inf.getWitness(useBlands) == w){
+ switch(w){
+ case ConflictFound: return inf.foundConflict();
+ case ErrorDropped: return inf.errorsChange() < 0;
+ case FocusImproved: return inf.focusDirection() > 0;
+ case FocusShrank: return false; // This is not a valid output
+ case Degenerate: return false; // This is not a valid output
+ case BlandsDegenerate: return useBlands;
+ case HeuristicDegenerate: return !useBlands;
+ case AntiProductive: return false;
+ }
+ }
+ return false;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){
+ bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+ UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands);
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(useBlands);
+ Assert(debugCheckWitness(selected, w, useBlands));
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+}
+
+
+WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){
+ Assert(!d_sgnDisagreements.empty());
+ Assert(d_errorSet.focusSize() >= 2);
+
+ if(Debug.isOn("arith::focus")){
+ d_errorSet.debugPrint(Debug("arith::focus"));
+ }
+
+ ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength);
+ const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb);
+ int oppositeSgn = - (e_evar_nb.getCoefficient().sgn());
+ Debug("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl;
+
+ ArithVarVec dropped;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == nb);
+
+ int sgn = entry.getCoefficient().sgn();
+ Debug("arith::focus")
+ << "on row "
+ << d_tableau.rowIndexToBasic(entry.getRowIndex())
+ << " "
+ << entry.getCoefficient() << endl;
+ ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){
+ int errSgn = d_errorSet.getSgn(currRow);
+
+ if(errSgn * sgn == oppositeSgn){
+ dropped.push_back(currRow);
+ Debug("arith::focus") << "dropping from focus " << currRow << endl;
+ }
+ }
+ }
+
+ d_sgnDisagreements.clear();
+ return adjustFocusShrank(dropped);
+}
+
+bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){
+ int diff = currErrorSize - prevErrorSize;
+ return selected.foundConflict() || diff == selected.errorsChange();
+}
+
+void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
+ Debug("updateAndSignal") << "updated basic " << updated;
+ Debug("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+ Debug("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+ int dir = !d_variables.assignmentIsConsistent(updated) ?
+ d_errorSet.getSgn(updated) : 0;
+ Debug("updateAndSignal") << " dir " << dir;
+ Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl;
+}
+
+bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
+ if(selected.describesPivot() && updated == selected.leaving()){
+ return selected.foundConflict();
+ }else{
+ return true;
+ }
+}
+
+void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+ ArithVar nonbasic = selected.nonbasic();
+
+ static bool verbose = false;
+
+ Debug("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+ stringstream ss;
+ if(verbose){
+ d_errorSet.debugPrint(ss);
+ if(selected.describesPivot()){
+ ArithVar leaving = selected.leaving();
+ ss << "leaving " << leaving
+ << " " << d_tableau.basicRowLength(leaving)
+ << " " << d_linEq._countBounds(leaving)
+ << endl;
+ }
+ if(degenerate(w) && selected.describesPivot()){
+ ArithVar leaving = selected.leaving();
+ Message()
+ << "degenerate " << leaving
+ << ", atBounds " << d_linEq.basicsAtBounds(selected)
+ << ", len " << d_tableau.basicRowLength(leaving)
+ << ", bc " << d_linEq._countBounds(leaving)
+ << endl;
+ }
+ }
+
+ if(selected.describesPivot()){
+ Constraint limiting = selected.limiting();
+ ArithVar basic = limiting->getVariable();
+ Assert(d_linEq.basicIsTracked(basic));
+ d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+ }else{
+ Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+ DeltaRational newAssignment =
+ d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+ d_linEq.updateTracked(nonbasic, newAssignment);
+ }
+ d_pivots++;
+
+ increaseLeavingCount(nonbasic);
+
+ vector< pair<ArithVar, int> > focusChanges;
+ while(d_errorSet.moreSignals()){
+ ArithVar updated = d_errorSet.topSignal();
+ int prevFocusSgn = d_errorSet.popSignal();
+
+ if(d_tableau.isBasic(updated)){
+ Assert(!d_variables.assignmentIsConsistent(updated) == d_errorSet.inError(updated));
+ if(Debug.isOn("updateAndSignal")){debugPrintSignal(updated);}
+ if(!d_variables.assignmentIsConsistent(updated)){
+ if(checkBasicForConflict(updated)){
+ reportConflict(updated);
+ Assert(debugUpdatedBasic(selected, updated));
+ }
+ }
+ }else{
+ Debug("updateAndSignal") << "updated nonbasic " << updated << endl;
+ }
+ int currFocusSgn = d_errorSet.focusSgn(updated);
+ if(currFocusSgn != prevFocusSgn){
+ int change = currFocusSgn - prevFocusSgn;
+ focusChanges.push_back(make_pair(updated, change));
+ }
+ }
+
+ if(verbose){
+ Message() << "conflict variable " << selected << endl;
+ Message() << ss.str();
+ }
+ if(Debug.isOn("error")){ d_errorSet.debugPrint(Debug("error")); }
+
+ Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+ adjustFocusAndError(selected, focusChanges);
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_focusSize > 1);
+
+ UpdateInfo selected = selectUpdateForDualLike(errorVar);
+
+ if(selected.uninitialized()){
+ // we found no proposals
+ // If this is empty, there must be an error on this variable!
+ // this should not be possible. It Should have been caught as a signal earlier
+ WitnessImprovement dropped = focusUsingSignDisagreements(errorVar);
+ Assert(d_sgnDisagreements.empty());
+
+ return dropped;
+ }else{
+ d_sgnDisagreements.clear();
+ }
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(!selected.uninitialized());
+
+ if(selected.focusDirection() == 0 &&
+ d_prevWitnessImprovement == HeuristicDegenerate &&
+ d_witnessImprovementInARow >= s_focusThreshold){
+
+ Debug("focusDownToJust") << "focusDownToJust " << errorVar << endl;
+
+ return focusDownToJust(errorVar);
+ }else{
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+ }
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){
+ Assert(d_focusSize >= 2);
+
+ Debug("focusDownToLastHalf") << "focusDownToLastHalf "
+ << d_errorSet.errorSize() << " "
+ << d_errorSet.focusSize() << " ";
+
+ uint32_t half = d_focusSize/2;
+ ArithVarVec buf;
+ for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(),
+ i_end = d_errorSet.focusEnd(); i != i_end; ++i){
+ if(half > 0){
+ --half;
+ } else{
+ buf.push_back(*i);
+ }
+ }
+ WitnessImprovement w = adjustFocusShrank(buf);
+ Debug("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl;
+ return w;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() {
+ Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+ Assert(d_focusSize >= 2);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf =
+ &LinearEqualityModule::preferWitness<true>;
+
+ LinearEqualityModule::VarPreferenceFunction bpf =
+ &LinearEqualityModule::minRowLength;
+
+ UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf);
+
+ if(selected.uninitialized()){
+ Debug("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl;
+
+ return focusDownToLastHalf();
+ }
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+
+ if(degenerate(w)){
+ Debug("selectFocusImproving") << "only degenerate" << endl;
+ if(d_prevWitnessImprovement == HeuristicDegenerate &&
+ d_witnessImprovementInARow >= s_focusThreshold){
+ Debug("selectFocusImproving") << "focus down been degenerate too long" << endl;
+ return focusDownToLastHalf();
+ }else{
+ Debug("selectFocusImproving") << "taking degenerate" << endl;
+ }
+ }
+ Debug("selectFocusImproving") << "selectFocusImproving did this " << selected << endl;
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+}
+
+bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w, ostream& out, int instance, uint32_t prevFocusSize, uint32_t prevErrorSize ) const{
+ out << "DLV("<<instance<<") ";
+ switch(w){
+ case ConflictFound:
+ out << "found conflict" << endl;
+ return !d_conflictVariables.empty();
+ case ErrorDropped:
+ out << "dropped " << prevErrorSize - d_errorSize << endl;
+ return d_errorSize < prevErrorSize;
+ case FocusImproved:
+ out << "focus improved"<< endl;
+ return d_errorSize == prevErrorSize;
+ case FocusShrank:
+ out << "focus shrank"<< endl;
+ return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize;
+ case BlandsDegenerate:
+ out << "bland degenerate"<< endl;
+ return true;
+ case HeuristicDegenerate:
+ out << "heuristic degenerate"<< endl;
+ return true;
+ case AntiProductive:
+ out << "focus blur" << endl;
+ return prevFocusSize == 0;
+ case Degenerate:
+ return false;
+ }
+ return false;
+}
+
+Result::Sat FCSimplexDecisionProcedure::dualLike(){
+ static int instance = 0;
+ static bool verbose = false;
+
+ TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer);
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_pivotBudget != 0);
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_errorSize > 0);
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_focusSize > 0);
+ Assert(d_conflictVariables.empty());
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+
+ d_scores.purge();
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+
+ while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
+ ++instance;
+ Debug("dualLike") << "dualLike " << instance << endl;
+
+ Assert(d_errorSet.noSignals());
+
+ WitnessImprovement w = AntiProductive;
+ uint32_t prevFocusSize = d_focusSize;
+ uint32_t prevErrorSize = d_errorSize;
+
+ if(d_focusSize == 0){
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+ d_errorSet.blur();
+
+ d_focusSize = d_errorSet.focusSize();
+
+ Assert( d_errorSize == d_focusSize);
+ Assert( d_errorSize >= 1 );
+
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+ Debug("dualLike") << "blur " << d_focusSize << endl;
+ }else if(d_focusSize == 1){
+ // Possible outcomes:
+ // - errorSet size shrunk
+ // -- fixed v
+ // -- fixed something other than v
+ // - conflict
+ // - budget was exhausted
+
+ ArithVar e = d_errorSet.topFocusVariable();
+ Debug("dualLike") << "primalImproveError " << e << endl;
+ w = primalImproveError(e);
+ }else{
+
+ // Possible outcomes:
+ // - errorSet size shrunk
+ // -- fixed v
+ // -- fixed something other than v
+ // - conflict
+ // - budget was exhausted
+ // - focus went down
+ Assert(d_focusSize > 1);
+ ArithVar e = d_errorSet.topFocusVariable();
+ static const unsigned s_sumMetricThreshold = 1;
+ if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){
+ Debug("dualLike") << "dualLikeImproveError " << e << endl;
+ w = dualLikeImproveError(e);
+ }else{
+ Debug("dualLike") << "selectFocusImproving " << endl;
+ w = selectFocusImproving();
+ }
+ }
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_errorSize == d_errorSet.errorSize());
+
+ if(verbose){
+ debugDualLike(w, Message(), instance, prevFocusSize, prevErrorSize);
+ }
+ Assert(debugDualLike(w, Debug("dualLike"), instance, prevFocusSize, prevErrorSize));
+ }
+
+
+ if(d_focusErrorVar != ARITHVAR_SENTINEL){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = ARITHVAR_SENTINEL;
+ }
+
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+ if(!d_conflictVariables.empty()){
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Assert(d_errorSet.noSignals());
+ return Result::SAT;
+ }else{
+ Assert(d_pivotBudget == 0);
+ return Result::SAT_UNKNOWN;
+ }
+}
+
+
+void FCSimplexDecisionProcedure::loadFocusSigns(){
+ Assert(d_focusCoefficients.empty());
+ Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ d_focusCoefficients.set(curr, &e.getCoefficient());
+ }
+}
+
+void FCSimplexDecisionProcedure::unloadFocusSigns(){
+ d_focusCoefficients.purge();
+}
+
+const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const {
+ if(d_focusCoefficients.isKey(nb)){
+ return *(d_focusCoefficients[nb]);
+ }else{
+ return d_zero;
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/fc_simplex.h b/src/theory/arith/fc_simplex.h
new file mode 100644
index 000000000..0dafa83ff
--- /dev/null
+++ b/src/theory/arith/fc_simplex.h
@@ -0,0 +1,252 @@
+/********************* */
+/*! \file simplex.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T)
+ ** decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only
+ ** variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction
+ ** Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that
+ ** Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the
+ ** current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the
+ ** conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "theory/arith/simplex.h"
+#include "util/dense_map.h"
+#include "util/statistics_registry.h"
+#include <stdint.h>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class FCSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ FCSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
+
+ Result::Sat findModel(bool exactResult);
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+ // dual like
+ // - found conflict
+ // - satisfied error set
+ Result::Sat dualLike();
+
+private:
+ static const uint32_t PENALTY = 4;
+ DenseMultiset d_scores;
+ void decreasePenalties(){ d_scores.removeOneOfEverything(); }
+ uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
+ void setPenalty(ArithVar x, WitnessImprovement w){
+ if(improvement(w)){
+ if(d_scores.count(x) > 0){
+ d_scores.removeAll(x);
+ }
+ }else{
+ d_scores.setCount(x, PENALTY);
+ }
+ }
+
+ /** The size of the focus set. */
+ uint32_t d_focusSize;
+
+ /** The current error focus variable. */
+ ArithVar d_focusErrorVar;
+
+ /**
+ * The signs of the coefficients in the focus set.
+ * This is empty until this has been loaded.
+ */
+ DenseMap<const Rational*> d_focusCoefficients;
+
+ /**
+ * Loads the signs of the coefficients of the variables on the row d_focusErrorVar
+ * into d_focusSgns.
+ */
+ void loadFocusSigns();
+
+ /** Unloads the information from d_focusSgns. */
+ void unloadFocusSigns();
+
+ /**
+ * The signs of a variable in the row of d_focusErrorVar.
+ * d_focusSgns must be loaded.
+ */
+ const Rational& focusCoefficient(ArithVar nb) const;
+
+ int32_t d_pivotBudget;
+ // enum PivotImprovement {
+ // ErrorDropped,
+ // NonDegenerate,
+ // HeuristicDegenerate,
+ // BlandsDegenerate
+ // };
+
+ WitnessImprovement d_prevWitnessImprovement;
+ uint32_t d_witnessImprovementInARow;
+
+ uint32_t degeneratePivotsInARow() const;
+
+ static const uint32_t s_focusThreshold = 6;
+ static const uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+ static const uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+ DenseMap<uint32_t> d_leavingCountSinceImprovement;
+ void increaseLeavingCount(ArithVar x){
+ if(!d_leavingCountSinceImprovement.isKey(x)){
+ d_leavingCountSinceImprovement.set(x,1);
+ }else{
+ (d_leavingCountSinceImprovement.get(x))++;
+ }
+ }
+ LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+ bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+ d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+ return useBlands ?
+ &LinearEqualityModule::preferWitness<false>:
+ &LinearEqualityModule::preferWitness<true>;
+ }
+
+ bool debugDualLike(WitnessImprovement w, std::ostream& out,
+ int instance,
+ uint32_t prevFocusSize, uint32_t prevErrorSize) const;
+
+ void debugPrintSignal(ArithVar updated) const;
+
+ ArithVarVec d_sgnDisagreements;
+
+ //static PivotImprovement pivotImprovement(const UpdateInfo& selected, bool useBlands = false);
+
+ void logPivot(WitnessImprovement w);
+
+ void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+ UpdateInfo selectPrimalUpdate(ArithVar error,
+ LinearEqualityModule::UpdatePreferenceFunction upf,
+ LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+ UpdateInfo selectUpdateForDualLike(ArithVar basic){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf =
+ &LinearEqualityModule::preferWitness<true>;
+ LinearEqualityModule::VarPreferenceFunction bpf =
+ &LinearEqualityModule::minVarOrder;
+ return selectPrimalUpdate(basic, upf, bpf);
+ }
+
+ UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
+ &LinearEqualityModule::preferWitness<false>:
+ &LinearEqualityModule::preferWitness<true>;
+
+ LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ &LinearEqualityModule::minVarOrder :
+ &LinearEqualityModule::minRowLength;
+ bpf = &LinearEqualityModule::minVarOrder;
+
+ return selectPrimalUpdate(basic, upf, bpf);
+ }
+ WitnessImprovement selectFocusImproving() ;
+
+ WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+ WitnessImprovement focusDownToLastHalf();
+ WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+ WitnessImprovement focusDownToJust(ArithVar v);
+
+
+ void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ bool initialProcessSignals(){
+ TimerStat &timer = d_statistics.d_initialSignalsTime;
+ IntStat& conflictStat = d_statistics.d_initialConflicts;
+ bool res = standardProcessSignals(timer, conflictStat);
+ d_focusSize = d_errorSet.focusSize();
+ return res;
+ }
+
+ static bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ TimerStat d_initialSignalsTime;
+ IntStat d_initialConflicts;
+
+ IntStat d_fcFoundUnsat;
+ IntStat d_fcFoundSat;
+ IntStat d_fcMissed;
+
+ TimerStat d_fcTimer;
+ TimerStat d_fcFocusConstructionTimer;
+
+ TimerStat d_selectUpdateForDualLike;
+ TimerStat d_selectUpdateForPrimal;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(uint32_t& pivots);
+ ~Statistics();
+ } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp
index 7229eba95..42d8b41f8 100644
--- a/src/theory/arith/linear_equality.cpp
+++ b/src/theory/arith/linear_equality.cpp
@@ -16,6 +16,7 @@
#include "theory/arith/linear_equality.h"
+#include "theory/arith/constraint.h"
using namespace std;
@@ -23,109 +24,283 @@ namespace CVC4 {
namespace theory {
namespace arith {
-/* Explicitly instatiate this function. */
+/* Explicitly instatiate these functions. */
template void LinearEqualityModule::propagateNonbasics<true>(ArithVar basic, Constraint c);
template void LinearEqualityModule::propagateNonbasics<false>(ArithVar basic, Constraint c);
+template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
+template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
+
+// template bool LinearEqualityModule::preferNonDegenerate<true>(const UpdateInfo& a, const UpdateInfo& b) const;
+// template bool LinearEqualityModule::preferNonDegenerate<false>(const UpdateInfo& a, const UpdateInfo& b) const;
+
+// template bool LinearEqualityModule::preferErrorsFixed<true>(const UpdateInfo& a, const UpdateInfo& b) const;
+// template bool LinearEqualityModule::preferErrorsFixed<false>(const UpdateInfo& a, const UpdateInfo& b) const;
+
+template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
+template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
+
+
+void Border::output(std::ostream& out) const{
+ out << "{Border"
+ << ", " << d_bound->getVariable()
+ << ", " << d_bound->getValue()
+ << ", " << d_diff
+ << ", " << d_areFixing
+ << ", " << d_upperbound;
+ if(ownBorder()){
+ out << ", ownBorder";
+ }else{
+ out << ", " << d_entry->getCoefficient();
+ }
+ out << ", " << d_bound
+ << "}";
+}
+
+LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f):
+ d_variables(vars),
+ d_tableau(t),
+ d_basicVariableUpdates(f),
+ d_increasing(1),
+ d_decreasing(-1),
+ d_relevantErrorBuffer(),
+ d_boundTracking(boundTracking),
+ d_areTracking(false),
+ d_trackCallback(this)
+{}
+
LinearEqualityModule::Statistics::Statistics():
d_statPivots("theory::arith::pivots",0),
d_statUpdates("theory::arith::updates",0),
- d_pivotTime("theory::arith::pivotTime")
+ d_pivotTime("theory::arith::pivotTime"),
+ d_adjTime("theory::arith::adjTime"),
+ d_weakeningAttempts("theory::arith::weakening::attempts",0),
+ d_weakeningSuccesses("theory::arith::weakening::success",0),
+ d_weakenings("theory::arith::weakening::total",0),
+ d_weakenTime("theory::arith::weakening::time")
{
StatisticsRegistry::registerStat(&d_statPivots);
StatisticsRegistry::registerStat(&d_statUpdates);
StatisticsRegistry::registerStat(&d_pivotTime);
+ StatisticsRegistry::registerStat(&d_adjTime);
+
+ StatisticsRegistry::registerStat(&d_weakeningAttempts);
+ StatisticsRegistry::registerStat(&d_weakeningSuccesses);
+ StatisticsRegistry::registerStat(&d_weakenings);
+ StatisticsRegistry::registerStat(&d_weakenTime);
}
LinearEqualityModule::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_statPivots);
StatisticsRegistry::unregisterStat(&d_statUpdates);
StatisticsRegistry::unregisterStat(&d_pivotTime);
+ StatisticsRegistry::unregisterStat(&d_adjTime);
+
+
+ StatisticsRegistry::unregisterStat(&d_weakeningAttempts);
+ StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
+ StatisticsRegistry::unregisterStat(&d_weakenings);
+ StatisticsRegistry::unregisterStat(&d_weakenTime);
+}
+void LinearEqualityModule::includeBoundCountChange(ArithVar nb, BoundCounts prev){
+ if(d_tableau.isBasic(nb)){
+ return;
+ }
+ Assert(!d_tableau.isBasic(nb));
+ Assert(!d_areTracking);
+
+ BoundCounts curr = d_variables.boundCounts(nb);
+
+ Assert(prev != curr);
+ Tableau::ColIterator basicIter = d_tableau.colIterator(nb);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ Assert(entry.getColVar() == nb);
+ int a_ijSgn = entry.getCoefficient().sgn();
+
+ ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+
+ BoundCounts& counts = d_boundTracking.get(basic);
+ Debug("includeBoundCountChange") << basic << " " << counts << " to " ;
+ counts -= prev.multiplyBySgn(a_ijSgn);
+ counts += curr.multiplyBySgn(a_ijSgn);
+ Debug("includeBoundCountChange") << counts << " " << a_ijSgn << std::endl;
+ }
+ d_boundTracking.set(nb, curr);
+}
+
+void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
+ for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
+ ArithVar nb = *i;
+ Assert(!d_tableau.isBasic(nb));
+ const DeltaRational& newValue = many[nb];
+ if(newValue != d_variables.getAssignment(nb)){
+ Trace("arith::updateMany")
+ << "updateMany:" << nb << " "
+ << d_variables.getAssignment(nb) << " to "<< newValue << endl;
+ update(nb, newValue);
+ }
+ }
}
-void LinearEqualityModule::update(ArithVar x_i, const DeltaRational& v){
+
+void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
+ DenseSet needsToBeAdded;
+ for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
+ ArithVar b = *i;
+ if(!d_tableau.isBasic(b)){
+ needsToBeAdded.add(b);
+ }
+ }
+
+ while(!needsToBeAdded.empty()){
+ ArithVar toRemove = ARITHVAR_SENTINEL;
+ ArithVar toAdd = ARITHVAR_SENTINEL;
+ DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
+ for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
+ ArithVar v = *i;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(v);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == v);
+ ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ if(!newBasis.isMember(b)){
+ toAdd = v;
+ if(toRemove == ARITHVAR_SENTINEL ||
+ d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)){
+ toRemove = b;
+ }
+ }
+ }
+ }
+ Assert(toRemove != ARITHVAR_SENTINEL);
+ Assert(toAdd != ARITHVAR_SENTINEL);
+
+ Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+ d_tableau.pivot(toRemove, toAdd, d_trackCallback);
+ d_basicVariableUpdates(toAdd);
+
+ Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+ needsToBeAdded.remove(toAdd);
+ }
+}
+
+void LinearEqualityModule::updateUntracked(ArithVar x_i, const DeltaRational& v){
Assert(!d_tableau.isBasic(x_i));
- DeltaRational assignment_x_i = d_partialModel.getAssignment(x_i);
+ Assert(!d_areTracking);
+ const DeltaRational& assignment_x_i = d_variables.getAssignment(x_i);
++(d_statistics.d_statUpdates);
+
Debug("arith") <<"update " << x_i << ": "
<< assignment_x_i << "|-> " << v << endl;
DeltaRational diff = v - assignment_x_i;
- //Assert(matchingSets(d_tableau, x_i));
- Tableau::ColIterator basicIter = d_tableau.colIterator(x_i);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
+ Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
Assert(entry.getColVar() == x_i);
ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
- //ReducedRowVector& row_j = d_tableau.lookup(x_j);
-
- //const Rational& a_ji = row_j.lookup(x_i);
const Rational& a_ji = entry.getCoefficient();
- const DeltaRational& assignment = d_partialModel.getAssignment(x_j);
+ const DeltaRational& assignment = d_variables.getAssignment(x_j);
DeltaRational nAssignment = assignment+(diff * a_ji);
- d_partialModel.setAssignment(x_j, nAssignment);
+ d_variables.setAssignment(x_j, nAssignment);
d_basicVariableUpdates(x_j);
}
- d_partialModel.setAssignment(x_i, v);
+ d_variables.setAssignment(x_i, v);
+
+ if(Debug.isOn("paranoid:check_tableau")){ debugCheckTableau(); }
+}
+
+void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_adjTime);
+
+ Assert(!d_tableau.isBasic(x_i));
+ Assert(d_areTracking);
+
+ ++(d_statistics.d_statUpdates);
+
+ DeltaRational diff = v - d_variables.getAssignment(x_i);
+ Debug("arith") <<"update " << x_i << ": "
+ << d_variables.getAssignment(x_i) << "|-> " << v << endl;
+
+
+ BoundCounts before = d_variables.boundCounts(x_i);
+ d_variables.setAssignment(x_i, v);
+ BoundCounts after = d_variables.boundCounts(x_i);
+
+ bool anyChange = before != after;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == x_i);
- //double difference = ((double)d_tableau.getNumRows()) - ((double) d_tableau.getRowLength(x_i));
+ ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ const Rational& a_ji = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(x_j);
+ DeltaRational nAssignment = assignment+(diff * a_ji);
+ Debug("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl;
+ d_variables.setAssignment(x_j, nAssignment);
+
+ if(anyChange && basicIsTracked(x_j)){
+ BoundCounts& next_bc_k = d_boundTracking.get(x_j);
+ next_bc_k.addInChange(a_ji.sgn(), before, after);
+ }
+
+ d_basicVariableUpdates(x_j);
+ }
- //(d_statistics.d_avgNumRowsNotContainingOnUpdate).addEntry(difference);
if(Debug.isOn("paranoid:check_tableau")){ debugCheckTableau(); }
}
-void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v){
+void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& x_i_value){
Assert(x_i != x_j);
TimerStat::CodeTimer codeTimer(d_statistics.d_pivotTime);
+ static int instance = 0;
+
+ if(Debug.isOn("arith::tracking::pre")){
+ ++instance;
+ Debug("arith::tracking") << "pre update #" << instance << endl;
+ debugCheckTracking();
+ }
+
if(Debug.isOn("arith::simplex:row")){ debugPivot(x_i, x_j); }
RowIndex ridx = d_tableau.basicToRowIndex(x_i);
const Tableau::Entry& entry_ij = d_tableau.findEntry(ridx, x_j);
Assert(!entry_ij.blank());
-
const Rational& a_ij = entry_ij.getCoefficient();
+ const DeltaRational& betaX_i = d_variables.getAssignment(x_i);
+ DeltaRational theta = (x_i_value - betaX_i)/a_ij;
+ DeltaRational x_j_value = d_variables.getAssignment(x_j) + theta;
+ updateTracked(x_j, x_j_value);
- const DeltaRational& betaX_i = d_partialModel.getAssignment(x_i);
-
- Rational inv_aij = a_ij.inverse();
- DeltaRational theta = (v - betaX_i)*inv_aij;
-
- d_partialModel.setAssignment(x_i, v);
-
-
- DeltaRational tmp = d_partialModel.getAssignment(x_j) + theta;
- d_partialModel.setAssignment(x_j, tmp);
-
-
- //Assert(matchingSets(d_tableau, x_j));
- for(Tableau::ColIterator iter = d_tableau.colIterator(x_j); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- Assert(entry.getColVar() == x_j);
- RowIndex currRow = entry.getRowIndex();
- if(ridx != currRow ){
- ArithVar x_k = d_tableau.rowIndexToBasic(currRow);
- const Rational& a_kj = entry.getCoefficient();
- DeltaRational nextAssignment = d_partialModel.getAssignment(x_k) + (theta * a_kj);
- d_partialModel.setAssignment(x_k, nextAssignment);
-
- d_basicVariableUpdates(x_k);
- }
+ if(Debug.isOn("arith::tracking::mid")){
+ Debug("arith::tracking") << "postupdate prepivot #" << instance << endl;
+ debugCheckTracking();
}
// Pivots
++(d_statistics.d_statPivots);
- d_tableau.pivot(x_i, x_j);
+ d_tableau.pivot(x_i, x_j, d_trackCallback);
+
+ if(Debug.isOn("arith::tracking::post")){
+ Debug("arith::tracking") << "postpivot #" << instance << endl;
+ debugCheckTracking();
+ }
d_basicVariableUpdates(x_j);
@@ -134,6 +309,51 @@ void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const Delt
}
}
+uint32_t LinearEqualityModule::updateProduct(const UpdateInfo& inf) const {
+ uint32_t colLen = d_tableau.getColLength(inf.nonbasic());
+ if(inf.describesPivot()){
+ Assert(inf.leaving() != inf.nonbasic());
+ return colLen + d_tableau.basicRowLength(inf.leaving());
+ }else{
+ return colLen;
+ }
+}
+
+void LinearEqualityModule::debugCheckTracking(){
+ Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
+ endIter = d_tableau.endBasic();
+ for(; basicIter != endIter; ++basicIter){
+ ArithVar basic = *basicIter;
+ Debug("arith::tracking") << "arith::tracking row basic: " << basic << endl;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd() && Debug.isOn("arith::tracking"); ++iter){
+ const Tableau::Entry& entry = *iter;
+
+ ArithVar var = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ DeltaRational beta = d_variables.getAssignment(var);
+ Debug("arith::tracking") << var << " " << d_variables.boundCounts(var)
+ << " " << beta << coeff;
+ if(d_variables.hasLowerBound(var)){
+ Debug("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
+ }
+ if(d_variables.hasUpperBound(var)){
+ Debug("arith::tracking") << "(up " << d_variables.getUpperBound(var) << ")";
+ }
+ Debug("arith::tracking") << endl;
+ }
+ Debug("arith::tracking") << "end row"<< endl;
+
+ if(basicIsTracked(basic)){
+ BoundCounts computed = computeBoundCounts(basic);
+ Debug("arith::tracking")
+ << "computed " << computed
+ << " tracking " << d_boundTracking[basic] << endl;
+ Assert(computed == d_boundTracking[basic]);
+
+ }
+ }
+}
void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
Debug("arith::pivot") << "debugPivot("<< x_i <<"|->"<< x_j << ")" << endl;
@@ -143,13 +363,13 @@ void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
ArithVar var = entry.getColVar();
const Rational& coeff = entry.getCoefficient();
- DeltaRational beta = d_partialModel.getAssignment(var);
+ DeltaRational beta = d_variables.getAssignment(var);
Debug("arith::pivot") << var << beta << coeff;
- if(d_partialModel.hasLowerBound(var)){
- Debug("arith::pivot") << "(lb " << d_partialModel.getLowerBound(var) << ")";
+ if(d_variables.hasLowerBound(var)){
+ Debug("arith::pivot") << "(lb " << d_variables.getLowerBound(var) << ")";
}
- if(d_partialModel.hasUpperBound(var)){
- Debug("arith::pivot") << "(up " << d_partialModel.getUpperBound(var) << ")";
+ if(d_variables.hasUpperBound(var)){
+ Debug("arith::pivot") << "(up " << d_variables.getUpperBound(var) << ")";
}
Debug("arith::pivot") << endl;
}
@@ -177,11 +397,11 @@ void LinearEqualityModule::debugCheckTableau(){
if(basic == nonbasic) continue;
const Rational& coeff = entry.getCoefficient();
- DeltaRational beta = d_partialModel.getAssignment(nonbasic);
+ DeltaRational beta = d_variables.getAssignment(nonbasic);
Debug("paranoid:check_tableau") << nonbasic << beta << coeff<<endl;
sum = sum + (beta*coeff);
}
- DeltaRational shouldBe = d_partialModel.getAssignment(basic);
+ DeltaRational shouldBe = d_variables.getAssignment(basic);
Debug("paranoid:check_tableau") << "ending row" << sum
<< "," << shouldBe << endl;
@@ -193,8 +413,8 @@ bool LinearEqualityModule::debugEntireLinEqIsConsistent(const string& s){
for(ArithVar var = 0, end = d_tableau.getNumColumns(); var != end; ++var){
// for(VarIter i = d_variables.begin(), end = d_variables.end(); i != end; ++i){
//ArithVar var = d_arithvarNodeMap.asArithVar(*i);
- if(!d_partialModel.assignmentIsConsistent(var)){
- d_partialModel.printModel(var);
+ if(!d_variables.assignmentIsConsistent(var)){
+ d_variables.printModel(var);
Warning() << s << ":" << "Assignment is not consistent for " << var ;
if(d_tableau.isBasic(var)){
Warning() << " (basic)";
@@ -217,8 +437,8 @@ DeltaRational LinearEqualityModule::computeBound(ArithVar basic, bool upperBound
bool ub = upperBound ? (sgn > 0) : (sgn < 0);
const DeltaRational& bound = ub ?
- d_partialModel.getUpperBound(nonbasic):
- d_partialModel.getLowerBound(nonbasic);
+ d_variables.getUpperBound(nonbasic):
+ d_variables.getLowerBound(nonbasic);
DeltaRational diff = bound * coeff;
sum = sum + diff;
@@ -239,7 +459,7 @@ DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe){
if(nonbasic == x) continue;
const Rational& coeff = entry.getCoefficient();
- const DeltaRational& assignment = d_partialModel.getAssignment(nonbasic, useSafe);
+ const DeltaRational& assignment = d_variables.getAssignment(nonbasic, useSafe);
sum = sum + (assignment * coeff);
}
return sum;
@@ -253,13 +473,13 @@ bool LinearEqualityModule::hasBounds(ArithVar basic, bool upperBound){
if(var == basic) continue;
int sgn = entry.getCoefficient().sgn();
if(upperBound){
- if( (sgn < 0 && !d_partialModel.hasLowerBound(var)) ||
- (sgn > 0 && !d_partialModel.hasUpperBound(var))){
+ if( (sgn < 0 && !d_variables.hasLowerBound(var)) ||
+ (sgn > 0 && !d_variables.hasUpperBound(var))){
return false;
}
}else{
- if( (sgn < 0 && !d_partialModel.hasUpperBound(var)) ||
- (sgn > 0 && !d_partialModel.hasLowerBound(var))){
+ if( (sgn < 0 && !d_variables.hasUpperBound(var)) ||
+ (sgn > 0 && !d_variables.hasLowerBound(var))){
return false;
}
}
@@ -272,6 +492,8 @@ void LinearEqualityModule::propagateNonbasics(ArithVar basic, Constraint c){
Assert(d_tableau.isBasic(basic));
Assert(c->getVariable() == basic);
Assert(!c->assertedToTheTheory());
+ Assert(!upperBound || c->isUpperBound()); // upperbound implies c is an upperbound
+ Assert(upperBound || c->isLowerBound()); // !upperbound implies c is a lowerbound
//Assert(c->canBePropagated());
Assert(!c->hasProof());
@@ -293,17 +515,17 @@ void LinearEqualityModule::propagateNonbasics(ArithVar basic, Constraint c){
Constraint bound = NullConstraint;
if(upperBound){
if(sgn < 0){
- bound = d_partialModel.getLowerBoundConstraint(nonbasic);
+ bound = d_variables.getLowerBoundConstraint(nonbasic);
}else{
Assert(sgn > 0);
- bound = d_partialModel.getUpperBoundConstraint(nonbasic);
+ bound = d_variables.getUpperBoundConstraint(nonbasic);
}
}else{
if(sgn < 0){
- bound = d_partialModel.getUpperBoundConstraint(nonbasic);
+ bound = d_variables.getUpperBoundConstraint(nonbasic);
}else{
Assert(sgn > 0);
- bound = d_partialModel.getLowerBoundConstraint(nonbasic);
+ bound = d_variables.getLowerBoundConstraint(nonbasic);
}
}
Assert(bound != NullConstraint);
@@ -315,6 +537,982 @@ void LinearEqualityModule::propagateNonbasics(ArithVar basic, Constraint c){
<< basic << ") done" << endl;
}
+Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const {
+
+ int sgn = coeff.sgn();
+ bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
+
+ Constraint c = ub ?
+ d_variables.getUpperBoundConstraint(v) :
+ d_variables.getLowerBoundConstraint(v);
+
+ bool weakened;
+ do{
+ const DeltaRational& bound = c->getValue();
+
+ weakened = false;
+
+ Constraint weaker = ub?
+ c->getStrictlyWeakerUpperBound(true, true):
+ c->getStrictlyWeakerLowerBound(true, true);
+
+ if(weaker != NullConstraint){
+ const DeltaRational& weakerBound = weaker->getValue();
+
+ DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
+ //if var == basic,
+ // if aboveUpper, weakerBound > bound, multiply by -1
+ // if !aboveUpper, weakerBound < bound, multiply by -1
+ diff = diff * coeff;
+ if(surplus > diff){
+ ++d_statistics.d_weakenings;
+ weakened = true;
+ anyWeakening = true;
+ surplus = surplus - diff;
+
+ Debug("weak") << "found:" << endl;
+ if(v == basic){
+ Debug("weak") << " basic: ";
+ }
+ Debug("weak") << " " << surplus << " "<< diff << endl
+ << " " << bound << c << endl
+ << " " << weakerBound << weaker << endl;
+
+ Assert(diff.sgn() > 0);
+ c = weaker;
+ }
+ }
+ }while(weakened);
+
+ return c;
+}
+
+Node LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar) const {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+
+ const DeltaRational& assignment = d_variables.getAssignment(basicVar);
+ DeltaRational surplus;
+ if(aboveUpper){
+ Assert(d_variables.hasUpperBound(basicVar));
+ Assert(assignment > d_variables.getUpperBound(basicVar));
+ surplus = assignment - d_variables.getUpperBound(basicVar);
+ }else{
+ Assert(d_variables.hasLowerBound(basicVar));
+ Assert(assignment < d_variables.getLowerBound(basicVar));
+ surplus = d_variables.getLowerBound(basicVar) - assignment;
+ }
+
+ NodeBuilder<> conflict(kind::AND);
+ bool anyWeakenings = false;
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ bool weakening = false;
+ Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
+ Debug("weak") << "weak : " << weakening << " "
+ << c->assertedToTheTheory() << " "
+ << d_variables.getAssignment(v) << " "
+ << c << endl
+ << c->explainForConflict() << endl;
+ anyWeakenings = anyWeakenings || weakening;
+
+ Debug("weak") << "weak : " << c->explainForConflict() << endl;
+ c->explainForConflict(conflict);
+ }
+ ++d_statistics.d_weakeningAttempts;
+ if(anyWeakenings){
+ ++d_statistics.d_weakeningSuccesses;
+ }
+ return conflict;
+}
+
+ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ if(x <= y){
+ return x;
+ } else {
+ return y;
+ }
+}
+
+ArithVar LinearEqualityModule::minColLength(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!d_tableau.isBasic(x));
+ Assert(!d_tableau.isBasic(y));
+ uint32_t xLen = d_tableau.getColLength(x);
+ uint32_t yLen = d_tableau.getColLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar LinearEqualityModule::minRowLength(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(x));
+ Assert(d_tableau.isBasic(y));
+ uint32_t xLen = d_tableau.basicRowLength(x);
+ uint32_t yLen = d_tableau.basicRowLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar LinearEqualityModule::minBoundAndColLength(ArithVar x, ArithVar y) const{
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!d_tableau.isBasic(x));
+ Assert(!d_tableau.isBasic(y));
+ if(d_variables.hasEitherBound(x) && !d_variables.hasEitherBound(y)){
+ return y;
+ }else if(!d_variables.hasEitherBound(x) && d_variables.hasEitherBound(y)){
+ return x;
+ }else {
+ return minColLength(x, y);
+ }
+}
+
+template <bool above>
+ArithVar LinearEqualityModule::selectSlack(ArithVar x_i, VarPreferenceFunction pref) const{
+ ArithVar slack = ARITHVAR_SENTINEL;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(isAcceptableSlack<above>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : (this->*pref)(slack, nonbasic);
+ }
+ }
+
+ return slack;
+}
+
+const Tableau::Entry* LinearEqualityModule::selectSlackEntry(ArithVar x_i, bool above) const{
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(above && isAcceptableSlack<true>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ return &entry;
+ }else if(!above && isAcceptableSlack<false>(sgn, nonbasic)){
+ return &entry;
+ }
+ }
+
+ return NULL;
+}
+
+void LinearEqualityModule::startTrackingBoundCounts(){
+ Assert(!d_areTracking);
+ d_areTracking = true;
+ if(Debug.isOn("arith::tracking")){
+ debugCheckTracking();
+ }
+ Assert(d_areTracking);
+}
+
+void LinearEqualityModule::stopTrackingBoundCounts(){
+ Assert(d_areTracking);
+ d_areTracking = false;
+ if(Debug.isOn("arith::tracking")){
+ debugCheckTracking();
+ }
+ Assert(!d_areTracking);
+}
+
+
+void LinearEqualityModule::trackVariable(ArithVar x_i){
+ Assert(!basicIsTracked(x_i));
+ BoundCounts counts(0,0);
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ counts += (d_variables.oldBoundCounts(nonbasic)).multiplyBySgn(a_ij.sgn());
+ }
+ d_boundTracking.set(x_i, counts);
+}
+
+BoundCounts LinearEqualityModule::computeBoundCounts(ArithVar x_i) const{
+ BoundCounts counts(0,0);
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ counts += (d_variables.boundCounts(nonbasic)).multiplyBySgn(a_ij.sgn());
+ }
+
+ return counts;
+}
+
+// BoundCounts LinearEqualityModule::cachingCountBounds(ArithVar x_i) const{
+// if(d_boundTracking.isKey(x_i)){
+// return d_boundTracking[x_i];
+// }else{
+// return computeBoundCounts(x_i);
+// }
+// }
+BoundCounts LinearEqualityModule::_countBounds(ArithVar x_i) const {
+ Assert(d_boundTracking.isKey(x_i));
+ return d_boundTracking[x_i];
+}
+
+// BoundCounts LinearEqualityModule::countBounds(ArithVar x_i){
+// if(d_boundTracking.isKey(x_i)){
+// return d_boundTracking[x_i];
+// }else{
+// BoundCounts bc = computeBoundCounts(x_i);
+// if(d_areTracking){
+// d_boundTracking.set(x_i,bc);
+// }
+// return bc;
+// }
+// }
+
+bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
+ Assert(u.describesPivot());
+
+ ArithVar nonbasic = u.nonbasic();
+ ArithVar basic = u.leaving();
+ Assert(basicIsTracked(basic));
+ int coeffSgn = u.getCoefficient().sgn();
+ int nbdir = u.nonbasicDirection();
+
+ Constraint c = u.limiting();
+ int toUB = (c->getType() == UpperBound ||
+ c->getType() == Equality) ? 1 : 0;
+ int toLB = (c->getType() == LowerBound ||
+ c->getType() == Equality) ? 1 : 0;
+
+
+ BoundCounts bcs = d_boundTracking[basic];
+ // x = c*n + \sum d*m
+ // n = 1/c * x + -1/c * (\sum d*m)
+ BoundCounts nonb = bcs - d_variables.boundCounts(nonbasic).multiplyBySgn(coeffSgn);
+ nonb = nonb.multiplyBySgn(-coeffSgn);
+ nonb += BoundCounts(toLB, toUB).multiplyBySgn(coeffSgn);
+
+ uint32_t length = d_tableau.basicRowLength(basic);
+ Debug("basicsAtBounds")
+ << "bcs " << bcs
+ << "nonb " << nonb
+ << "length " << length << endl;
+
+ if(nbdir < 0){
+ return bcs.atLowerBounds() + 1 == length;
+ }else{
+ Assert(nbdir > 0);
+ return bcs.atUpperBounds() + 1 == length;
+ }
+}
+
+bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
+ Assert(basicIsTracked(basic));
+ BoundCounts bcs = d_boundTracking[basic];
+ uint32_t length = d_tableau.basicRowLength(basic);
+
+ return bcs.atLowerBounds() + 1 == length;
+}
+
+bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const {
+ Assert(basicIsTracked(basic));
+ BoundCounts bcs = d_boundTracking[basic];
+ uint32_t length = d_tableau.basicRowLength(basic);
+
+ return bcs.atUpperBounds() + 1 == length;
+}
+
+void LinearEqualityModule::trackingSwap(ArithVar basic, ArithVar nb, int nbSgn) {
+ Assert(basicIsTracked(basic));
+
+ // z = a*x + \sum b*y
+ // x = (1/a) z + \sum (-1/a)*b*y
+ // basicCount(z) = bc(a*x) + bc(\sum b y)
+ // basicCount(x) = bc(z/a) + bc(\sum -b/a * y)
+
+ // sgn(1/a) = sgn(a)
+ // bc(a*x) = bc(x).multiply(sgn(a))
+ // bc(z/a) = bc(z).multiply(sgn(a))
+ // bc(\sum -b/a * y) = bc(\sum b y).multiplyBySgn(-sgn(a))
+ // bc(\sum b y) = basicCount(z) - bc(a*x)
+ // basicCount(x) =
+ // = bc(z).multiply(sgn(a)) + (basicCount(z) - bc(a*x)).multiplyBySgn(-sgn(a))
+
+ BoundCounts bc = d_boundTracking[basic];
+ bc -= (d_variables.boundCounts(nb)).multiplyBySgn(nbSgn);
+ bc = bc.multiplyBySgn(-nbSgn);
+ bc += d_variables.boundCounts(basic).multiplyBySgn(nbSgn);
+ d_boundTracking.set(nb, bc);
+ d_boundTracking.remove(basic);
+}
+
+void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
+ Assert(oldSgn != currSgn);
+ BoundCounts nb_bc = d_variables.boundCounts(nb);
+
+ if(!nb_bc.isZero()){
+ ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+ Assert(basicIsTracked(basic));
+
+ BoundCounts& basic_bc = d_boundTracking.get(basic);
+ basic_bc.addInSgn(nb_bc, oldSgn, currSgn);
+ }
+}
+
+void LinearEqualityModule::computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction pref){
+ ArithVar nb = inf.nonbasic();
+ int sgn = inf.nonbasicDirection();
+ Assert(sgn != 0);
+ Assert(!d_tableau.isBasic(nb));
+
+ //inf.setErrorsChange(0);
+ //inf.setlimiting = NullConstraint;
+
+
+ // Error variables moving in the correct direction
+ Assert(d_relevantErrorBuffer.empty());
+
+ // phases :
+ enum ComputeSafePhase {
+ NoBoundSelected,
+ NbsBoundSelected,
+ BasicBoundSelected,
+ DegenerateBoundSelected
+ } phase;
+
+ phase = NoBoundSelected;
+
+ static int instance = 0;
+ Debug("computeSafeUpdate") << "computeSafeUpdate " << (++instance) << endl;
+
+ if(sgn > 0 && d_variables.hasUpperBound(nb)){
+ Constraint ub = d_variables.getUpperBoundConstraint(nb);
+ inf.updatePureFocus(ub->getValue() - d_variables.getAssignment(nb), ub);
+
+ Assert(inf.nonbasicDelta().sgn() == sgn);
+ Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl;
+ phase = NbsBoundSelected;
+ }else if(sgn < 0 && d_variables.hasLowerBound(nb)){
+ Constraint lb = d_variables.getLowerBoundConstraint(nb);
+ inf.updatePureFocus(lb->getValue() - d_variables.getAssignment(nb), lb);
+
+ Assert(inf.nonbasicDelta().sgn() == sgn);
+
+ Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl;
+ phase = NbsBoundSelected;
+ }
+
+ Tableau::ColIterator basicIter = d_tableau.colIterator(nb);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ Assert(entry.getColVar() == nb);
+
+ ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ const Rational& a_ji = entry.getCoefficient();
+ int basic_movement = sgn * a_ji.sgn();
+
+ Debug("computeSafeUpdate")
+ << "computeSafeUpdate: "
+ << basic << ", "
+ << basic_movement << ", "
+ << d_variables.cmpAssignmentUpperBound(basic) << ", "
+ << d_variables.cmpAssignmentLowerBound(basic) << ", "
+ << a_ji << ", "
+ << d_variables.getAssignment(basic) << endl;
+
+ Constraint proposal = NullConstraint;
+
+ if(basic_movement > 0){
+ if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+ d_relevantErrorBuffer.push_back(&entry);
+ }
+ if(d_variables.hasUpperBound(basic) &&
+ d_variables.cmpAssignmentUpperBound(basic) <= 0){
+ proposal = d_variables.getUpperBoundConstraint(basic);
+ }
+ }else if(basic_movement < 0){
+ if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+ d_relevantErrorBuffer.push_back(&entry);
+ }
+ if(d_variables.hasLowerBound(basic) &&
+ d_variables.cmpAssignmentLowerBound(basic) >= 0){
+ proposal = d_variables.getLowerBoundConstraint(basic);
+ }
+ }
+ if(proposal != NullConstraint){
+ const Rational& coeff = entry.getCoefficient();
+ DeltaRational diff = proposal->getValue() - d_variables.getAssignment(basic);
+ diff /= coeff;
+ int cmp = phase == NoBoundSelected ? 0 : diff.cmp(inf.nonbasicDelta());
+ Assert(diff.sgn() == sgn || diff.sgn() == 0);
+ bool prefer = false;
+ switch(phase){
+ case NoBoundSelected:
+ prefer = true;
+ break;
+ case NbsBoundSelected:
+ prefer = (sgn > 0 && cmp < 0 ) || (sgn < 0 && cmp > 0);
+ break;
+ case BasicBoundSelected:
+ prefer =
+ (sgn > 0 && cmp < 0 ) ||
+ (sgn < 0 && cmp > 0) ||
+ (cmp == 0 && basic == (this->*pref)(basic, inf.leaving()));
+ break;
+ case DegenerateBoundSelected:
+ prefer = cmp == 0 && basic == (this->*pref)(basic, inf.leaving());
+ break;
+ }
+ if(prefer){
+ inf.updatePivot(diff, coeff, proposal);
+
+ phase = (diff.sgn() != 0) ? BasicBoundSelected : DegenerateBoundSelected;
+ }
+ }
+ }
+
+ if(phase == DegenerateBoundSelected){
+ inf.setErrorsChange(0);
+ }else{
+ computedFixed(inf);
+ }
+ inf.determineFocusDirection();
+
+ d_relevantErrorBuffer.clear();
+}
+
+void LinearEqualityModule::computedFixed(UpdateInfo& proposal){
+ Assert(proposal.nonbasicDirection() != 0);
+ Assert(!d_tableau.isBasic(proposal.nonbasic()));
+
+ //bool unconstrained = (proposal.d_limiting == NullConstraint);
+
+ Assert(!proposal.unbounded() || !d_relevantErrorBuffer.empty());
+
+ Assert(proposal.unbounded() ||
+ proposal.nonbasicDelta().sgn() == proposal.nonbasicDirection());
+
+ // proposal.d_value is the max
+
+ UpdateInfo max;
+ int dropped = 0;
+ //Constraint maxFix = NullConstraint;
+ //DeltaRational maxAmount;
+
+ EntryPointerVector::const_iterator i = d_relevantErrorBuffer.begin();
+ EntryPointerVector::const_iterator i_end = d_relevantErrorBuffer.end();
+ for(; i != i_end; ++i){
+ const Tableau::Entry& entry = *(*i);
+ Assert(entry.getColVar() == proposal.nonbasic());
+
+ ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ const Rational& a_ji = entry.getCoefficient();
+ int basic_movement = proposal.nonbasicDirection() * a_ji.sgn();
+
+ DeltaRational theta;
+ DeltaRational proposedValue;
+ if(!proposal.unbounded()){
+ theta = proposal.nonbasicDelta() * a_ji;
+ proposedValue = theta + d_variables.getAssignment(basic);
+ }
+
+ Constraint fixed = NullConstraint;
+
+ if(basic_movement < 0){
+ Assert(d_variables.cmpAssignmentUpperBound(basic) > 0);
+
+ if(proposal.unbounded() || d_variables.cmpToUpperBound(basic, proposedValue) <= 0){
+ --dropped;
+ fixed = d_variables.getUpperBoundConstraint(basic);
+ }
+ }else if(basic_movement > 0){
+ Assert(d_variables.cmpAssignmentLowerBound(basic) < 0);
+
+ if(proposal.unbounded() || d_variables.cmpToLowerBound(basic, proposedValue) >= 0){
+ --dropped;
+ fixed = d_variables.getLowerBoundConstraint(basic);
+ }
+ }
+ if(fixed != NullConstraint){
+ DeltaRational amount = fixed->getValue() - d_variables.getAssignment(basic);
+ amount /= a_ji;
+ Assert(amount.sgn() == proposal.nonbasicDirection());
+
+ if(max.uninitialized()){
+ max = UpdateInfo(proposal.nonbasic(), proposal.nonbasicDirection());
+ max.updatePivot(amount, a_ji, fixed, dropped);
+ }else{
+ int cmp = amount.cmp(max.nonbasicDelta());
+ bool prefer =
+ (proposal.nonbasicDirection() < 0 && cmp < 0) ||
+ (proposal.nonbasicDirection() > 0 && cmp > 0) ||
+ (cmp == 0 && fixed->getVariable() < max.limiting()->getVariable());
+
+ if(prefer){
+ max.updatePivot(amount, a_ji, fixed, dropped);
+ }else{
+ max.setErrorsChange(dropped);
+ }
+ }
+ }
+ }
+ Assert(dropped < 0 || !proposal.unbounded());
+
+ if(dropped < 0){
+ proposal = max;
+ }else{
+ Assert(dropped == 0);
+ Assert(proposal.nonbasicDelta().sgn() != 0);
+ Assert(proposal.nonbasicDirection() != 0);
+ proposal.setErrorsChange(0);
+ }
+ Assert(proposal.errorsChange() == dropped);
+}
+
+ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
+ if(vec.empty()) {
+ return ARITHVAR_SENTINEL;
+ }else {
+ ArithVar sel = vec.front();
+ ArithVarVec::const_iterator i = vec.begin() + 1;
+ ArithVarVec::const_iterator i_end = vec.end();
+ for(; i != i_end; ++i){
+ sel = (this->*pf)(sel, *i);
+ }
+ return sel;
+ }
+}
+
+bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub){
+ ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+
+ Assert(basicIsTracked(currBasic));
+
+ Constraint bound = ub ?
+ d_variables.getUpperBoundConstraint(currBasic):
+ d_variables.getLowerBoundConstraint(currBasic);
+
+ if(bound == NullConstraint){ return false; }
+ Assert(bound != NullConstraint);
+
+ const Rational& coeff = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+ DeltaRational toBound = bound->getValue() - assignment;
+ DeltaRational nbDiff = toBound/coeff;
+
+ // if ub
+ // if toUB >= 0
+ // then ub >= currBasic
+ // if sgn > 0,
+ // then diff >= 0, so nb must increase for G
+ // else diff <= 0, so nb must decrease for G
+ // else ub < currBasic
+ // if sgn > 0,
+ // then diff < 0, so nb must decrease for G
+ // else diff > 0, so nb must increase for G
+
+ int diffSgn = nbDiff.sgn();
+
+ if(diffSgn != 0 && willBeInConflictAfterPivot(entry, nbDiff, ub)){
+ return true;
+ }else{
+ bool areFixing = ub ? (toBound.sgn() < 0 ) : (toBound.sgn() > 0);
+ Border border(bound, nbDiff, areFixing, &entry, ub);
+ bool increasing =
+ (diffSgn > 0) ||
+ (diffSgn == 0 && ((coeff.sgn() > 0) == ub));
+
+ // assume diffSgn == 0
+ // if coeff > 0,
+ // if ub, inc
+ // else, dec
+ // else coeff < 0
+ // if ub, dec
+ // else, inc
+
+ if(increasing){
+ Debug("handleBorders") << "push back increasing " << border << endl;
+ d_increasing.push_back(border);
+ }else{
+ Debug("handleBorders") << "push back decreasing " << border << endl;
+ d_decreasing.push_back(border);
+ }
+ return false;
+ }
+}
+
+bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const{
+ int nbSgn = nbDiff.sgn();
+ Assert(nbSgn != 0);
+
+ if(nbSgn > 0){
+ if(d_upperBoundDifference.nothing() || nbDiff <= d_upperBoundDifference){
+ return false;
+ }
+ }else{
+ if(d_lowerBoundDifference.nothing() || nbDiff >= d_lowerBoundDifference){
+ return false;
+ }
+ }
+
+ // Assume past this point, nb will be in error if this pivot is done
+ ArithVar nb = entry.getColVar();
+ ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ Assert(basicIsTracked(basic));
+ int coeffSgn = entry.getCoefficient().sgn();
+
+
+ // if bToUB, then basic is going to be set to its upperbound
+ // if not bToUB, then basic is going to be set to its lowerbound
+
+ // Different steps of solving for this:
+ // 1) y = a * x + \sum b * z
+ // 2) -a * x = -y + \sum b * z
+ // 3) x = (-1/a) * ( -y + \sum b * z)
+
+ Assert(basicIsTracked(basic));
+ BoundCounts bc = d_boundTracking[basic];
+
+ // 1) y = a * x + \sum b * z
+ // Get bc(\sum b * z)
+ BoundCounts sumOnly = bc - d_variables.boundCounts(nb).multiplyBySgn(coeffSgn);
+
+ // y's bounds in the proposed model
+ int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+ int yWillBeAtLb = (!bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+ BoundCounts ysBounds(yWillBeAtLb, yWillBeAtUb);
+
+ // 2) -a * x = -y + \sum b * z
+ // Get bc(-y + \sum b * z)
+ BoundCounts withNegY = sumOnly + ysBounds.multiplyBySgn(-1);
+
+ // 3) x = (-1/a) * ( -y + \sum b * z)
+ // Get bc((-1/a) * ( -y + \sum b * z))
+ BoundCounts xsBoundsAfterPivot = withNegY.multiplyBySgn(-coeffSgn);
+
+ uint32_t length = d_tableau.basicRowLength(basic);
+ if(nbSgn > 0){
+ // Only check for the upper bound being violated
+ return xsBoundsAfterPivot.atLowerBounds() + 1 == length;
+ }else{
+ // Only check for the lower bound being violated
+ return xsBoundsAfterPivot.atUpperBounds() + 1 == length;
+ }
+}
+
+UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, bool ub) const{
+ ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ ArithVar nb = entry.getColVar();
+
+ Constraint bound = ub ?
+ d_variables.getUpperBoundConstraint(currBasic):
+ d_variables.getLowerBoundConstraint(currBasic);
+
+
+ const Rational& coeff = entry.getCoefficient();
+ const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+ DeltaRational toBound = bound->getValue() - assignment;
+ DeltaRational nbDiff = toBound/coeff;
+
+ return UpdateInfo::conflict(nb, nbDiff, coeff, bound);
+}
+
+UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref){
+ Assert(d_increasing.empty());
+ Assert(d_decreasing.empty());
+ Assert(d_lowerBoundDifference.nothing());
+ Assert(d_upperBoundDifference.nothing());
+
+ int focusCoeffSgn = focusCoeff.sgn();
+
+ static int instance = 0;
+ ++instance;
+ Debug("speculativeUpdate") << "speculativeUpdate " << instance << endl;
+ Debug("speculativeUpdate") << "nb " << nb << endl;
+ Debug("speculativeUpdate") << "focusCoeff " << focusCoeff << endl;
+
+ if(d_variables.hasUpperBound(nb)){
+ Constraint ub = d_variables.getUpperBoundConstraint(nb);
+ d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb);
+ Border border(ub, d_upperBoundDifference, false, NULL, true);
+ Debug("handleBorders") << "push back increasing " << border << endl;
+ d_increasing.push_back(border);
+ }
+ if(d_variables.hasLowerBound(nb)){
+ Constraint lb = d_variables.getLowerBoundConstraint(nb);
+ d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb);
+ Border border(lb, d_lowerBoundDifference, false, NULL, false);
+ Debug("handleBorders") << "push back decreasing " << border << endl;
+ d_decreasing.push_back(border);
+ }
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == nb);
+
+ if(accumulateBorder(entry, true)){
+ clearSpeculative();
+ return mkConflictUpdate(entry, true);
+ }
+ if(accumulateBorder(entry, false)){
+ clearSpeculative();
+ return mkConflictUpdate(entry, false);
+ }
+ }
+
+ UpdateInfo selected;
+ BorderHeap& withSgn = focusCoeffSgn > 0 ? d_increasing : d_decreasing;
+ BorderHeap& againstSgn = focusCoeffSgn > 0 ? d_decreasing : d_increasing;
+
+ handleBorders(selected, nb, focusCoeff, withSgn, 0, pref);
+ int m = 1 - selected.errorsChangeSafe(0);
+ handleBorders(selected, nb, focusCoeff, againstSgn, m, pref);
+
+ clearSpeculative();
+ return selected;
+}
+
+void LinearEqualityModule::clearSpeculative(){
+ // clear everything away
+ d_increasing.clear();
+ d_decreasing.clear();
+ d_lowerBoundDifference.clear();
+ d_upperBoundDifference.clear();
+}
+
+void LinearEqualityModule::handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref){
+ Assert(minimumFixes >= 0);
+
+ // The values popped off of the heap
+ // should be popped with the values closest to 0
+ // being first and larger in absolute value last
+
+
+ int fixesRemaining = heap.possibleFixes();
+
+ Debug("handleBorders")
+ << "handleBorders "
+ << "nb " << nb
+ << "fc " << focusCoeff
+ << "h.e " << heap.empty()
+ << "h.dir " << heap.direction()
+ << "h.rem " << fixesRemaining
+ << "h.0s " << heap.numZeroes()
+ << "min " << minimumFixes
+ << endl;
+
+ if(heap.empty()){
+ // if the heap is empty, return
+ return;
+ }
+
+ bool zeroesWillDominate = fixesRemaining - heap.numZeroes() < minimumFixes;
+
+ // can the number of fixes ever exceed the minimum?
+ // no more than the number of possible fixes can be fixed in total
+ // nothing can be fixed before the zeroes are taken care of
+ if(minimumFixes > 0 && zeroesWillDominate){
+ return;
+ }
+
+
+ int negErrorChange = 0;
+ int nbDir = heap.direction();
+
+ // points at the beginning of the heap
+ if(zeroesWillDominate){
+ heap.dropNonZeroes();
+ }
+ heap.make_heap();
+
+
+ // pretend like the previous block had a value of zero.
+ // The block that actually has a value of 0 must handle this.
+ const DeltaRational zero(0);
+ const DeltaRational* prevBlockValue = &zero;
+
+ /** The coefficient changes as the value crosses border. */
+ Rational effectiveCoefficient = focusCoeff;
+
+ /* Keeps track of the change to the value of the focus function.*/
+ DeltaRational totalFocusChange(0);
+
+ const int focusCoeffSgn = focusCoeff.sgn();
+
+ while(heap.more() &&
+ (fixesRemaining + negErrorChange > minimumFixes ||
+ (fixesRemaining + negErrorChange == minimumFixes &&
+ effectiveCoefficient.sgn() == focusCoeffSgn))){
+ // There are more elements &&
+ // we can either fix at least 1 more variable in the error function
+ // or we can improve the error function
+
+
+ int brokenInBlock = 0;
+ BorderVec::const_iterator endBlock = heap.end();
+
+ pop_block(heap, brokenInBlock, fixesRemaining, negErrorChange);
+
+ // if endVec == beginVec, block starts there
+ // other wise, block starts at endVec
+ BorderVec::const_iterator startBlock
+ = heap.more() ? heap.end() : heap.begin();
+
+ const DeltaRational& blockValue = (*startBlock).d_diff;
+
+ // if decreasing
+ // blockValue < prevBlockValue
+ // diff.sgn() = -1
+ DeltaRational diff = blockValue - (*prevBlockValue);
+ DeltaRational blockChangeToFocus = diff * effectiveCoefficient;
+ totalFocusChange += blockChangeToFocus;
+
+ Debug("handleBorders")
+ << "blockValue " << (blockValue)
+ << "diff " << diff
+ << "blockChangeToFocus " << totalFocusChange
+ << "blockChangeToFocus " << totalFocusChange
+ << "negErrorChange " << negErrorChange
+ << "brokenInBlock " << brokenInBlock
+ << "fixesRemaining " << fixesRemaining
+ << endl;
+
+ int currFocusChangeSgn = totalFocusChange.sgn();
+ for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+ const Border& b = *i;
+
+ Debug("handleBorders") << b << endl;
+
+ bool makesImprovement = negErrorChange > 0 ||
+ (negErrorChange == 0 && currFocusChangeSgn > 0);
+
+ if(!makesImprovement){
+ if(b.ownBorder() || minimumFixes > 0){
+ continue;
+ }
+ }
+
+ UpdateInfo proposal(nb, nbDir);
+ if(b.ownBorder()){
+ proposal.witnessedUpdate(b.d_diff, b.d_bound, -negErrorChange, currFocusChangeSgn);
+ }else{
+ proposal.update(b.d_diff, b.getCoefficient(), b.d_bound, -negErrorChange, currFocusChangeSgn);
+ }
+
+ if(selected.unbounded() || (this->*pref)(selected, proposal)){
+ selected = proposal;
+ }
+ }
+
+ effectiveCoefficient += updateCoefficient(startBlock, endBlock);
+ prevBlockValue = &blockValue;
+ negErrorChange -= brokenInBlock;
+ }
+}
+
+Rational LinearEqualityModule::updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock){
+ //update coefficient
+ Rational changeToCoefficient(0);
+ for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+ const Border& curr = *i;
+ if(curr.ownBorder()){// breaking its own bound
+ if(curr.d_upperbound){
+ changeToCoefficient -= 1;
+ }else{
+ changeToCoefficient += 1;
+ }
+ }else{
+ const Rational& coeff = curr.d_entry->getCoefficient();
+ if(curr.d_areFixing){
+ if(curr.d_upperbound){// fixing an upper bound
+ changeToCoefficient += coeff;
+ }else{// fixing a lower bound
+ changeToCoefficient -= coeff;
+ }
+ }else{
+ if(curr.d_upperbound){// breaking an upper bound
+ changeToCoefficient -= coeff;
+ }else{
+ // breaking a lower bound
+ changeToCoefficient += coeff;
+ }
+ }
+ }
+ }
+ return changeToCoefficient;
+}
+
+void LinearEqualityModule::pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange){
+ Assert(heap.more());
+
+ if(heap.top().d_areFixing){
+ fixesRemaining--;
+ negErrorChange++;
+ }else{
+ brokenInBlock++;
+ }
+ heap.pop_heap();
+ const DeltaRational& blockValue = (*heap.end()).d_diff;
+
+ while(heap.more()){
+ const Border& top = heap.top();
+ if(blockValue == top.d_diff){
+ // belongs to the block
+ if(top.d_areFixing){
+ fixesRemaining--;
+ negErrorChange++;
+ }else{
+ brokenInBlock++;
+ }
+ heap.pop_heap();
+ }else{
+ // does not belong to the block
+ Assert((heap.direction() > 0) ?
+ (blockValue < top.d_diff) : (blockValue > top.d_diff));
+ break;
+ }
+ }
+}
+
+void LinearEqualityModule::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult){
+ d_tableau.substitutePlusTimesConstant(to, from, mult, d_trackCallback);
+}
+void LinearEqualityModule::directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult){
+ d_tableau.directlyAddToCoefficient(row, col, mult, d_trackCallback);
+}
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h
index 14df8d819..8b9b888f2 100644
--- a/src/theory/arith/linear_equality.h
+++ b/src/theory/arith/linear_equality.h
@@ -27,36 +27,198 @@
#include "cvc4_private.h"
-#ifndef __CVC4__THEORY__ARITH__LINEAR_EQUALITY_H
-#define __CVC4__THEORY__ARITH__LINEAR_EQUALITY_H
+#pragma once
#include "theory/arith/delta_rational.h"
#include "theory/arith/arithvar.h"
#include "theory/arith/partial_model.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/constraint.h"
+#include "theory/arith/tableau.h"
+#include "theory/arith/constraint_forward.h"
+#include "theory/arith/simplex_update.h"
+#include "theory/arith/options.h"
+#include "util/maybe.h"
#include "util/statistics_registry.h"
namespace CVC4 {
namespace theory {
namespace arith {
+struct Border{
+ // The constraint for the border
+ Constraint d_bound;
+
+ // The change to the nonbasic to reach the border
+ DeltaRational d_diff;
+
+ // Is reach this value fixing the constraint
+ // or is going past this value hurting the constraint
+ bool d_areFixing;
+
+ // Entry into the tableau
+ const Tableau::Entry* d_entry;
+
+ // Was this an upper bound or a lower bound?
+ bool d_upperbound;
+
+ Border():
+ d_bound(NullConstraint) // ignore the other values
+ {}
+
+ Border(Constraint l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub):
+ d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(en), d_upperbound(ub)
+ {}
+
+ Border(Constraint l, const DeltaRational& diff, bool areFixing, bool ub):
+ d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(NULL), d_upperbound(ub)
+ {}
+ bool operator<(const Border& other) const{
+ return d_diff < other.d_diff;
+ }
+
+ /** d_lim is the nonbasic variable's own bound. */
+ bool ownBorder() const { return d_entry == NULL; }
+
+ bool isZero() const { return d_diff.sgn() == 0; }
+ static bool nonZero(const Border& b) { return !b.isZero(); }
+
+ const Rational& getCoefficient() const {
+ Assert(!ownBorder());
+ return d_entry->getCoefficient();
+ }
+ void output(std::ostream& out) const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Border& b){
+ b.output(out);
+ return out;
+}
+
+typedef std::vector<Border> BorderVec;
+
+class BorderHeap {
+ const int d_dir;
+
+ class BorderHeapCmp {
+ private:
+ int d_nbDirection;
+ public:
+ BorderHeapCmp(int dir): d_nbDirection(dir){}
+ bool operator()(const Border& a, const Border& b) const{
+ if(d_nbDirection > 0){
+ // if nb is increasing,
+ // this needs to act like a max
+ // in order to have a min heap
+ return b < a;
+ }else{
+ // if nb is decreasing,
+ // this needs to act like a min
+ // in order to have a max heap
+ return a < b;
+ }
+ }
+ };
+ const BorderHeapCmp d_cmp;
+
+ BorderVec d_vec;
+
+ BorderVec::iterator d_begin;
+
+ /**
+ * Once this is initialized the top of the heap will always
+ * be at d_end - 1
+ */
+ BorderVec::iterator d_end;
+
+ int d_possibleFixes;
+ int d_numZeroes;
+
+public:
+ BorderHeap(int dir)
+ : d_dir(dir), d_cmp(dir), d_possibleFixes(0), d_numZeroes(0)
+ {}
+
+ void push_back(const Border& b){
+ d_vec.push_back(b);
+ if(b.d_areFixing){
+ d_possibleFixes++;
+ }
+ if(b.d_diff.sgn() == 0){
+ d_numZeroes++;
+ }
+ }
+
+ int numZeroes() const { return d_numZeroes; }
+ int possibleFixes() const { return d_possibleFixes; }
+ int direction() const { return d_dir; }
+
+ void make_heap(){
+ d_begin = d_vec.begin();
+ d_end = d_vec.end();
+ std::make_heap(d_begin, d_end, d_cmp);
+ }
+
+ void dropNonZeroes(){
+ std::remove_if(d_vec.begin(), d_vec.end(), &Border::nonZero);
+ }
+
+ const Border& top() const {
+ Assert(more());
+ return *d_begin;
+ }
+ void pop_heap(){
+ Assert(more());
+
+ std::pop_heap(d_begin, d_end, d_cmp);
+ --d_end;
+ }
+
+ BorderVec::const_iterator end() const{
+ return BorderVec::const_iterator(d_end);
+ }
+ BorderVec::const_iterator begin() const{
+ return BorderVec::const_iterator(d_begin);
+ }
+
+ inline bool more() const{ return d_begin != d_end; }
+
+ inline bool empty() const{ return d_vec.empty(); }
+
+ void clear(){
+ d_possibleFixes = 0;
+ d_numZeroes = 0;
+ d_vec.clear();
+ }
+};
+
+
+
+
+
class LinearEqualityModule {
+public:
+ typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
+
+
+ typedef bool (LinearEqualityModule::*UpdatePreferenceFunction)(const UpdateInfo&, const UpdateInfo&) const;
+
private:
/**
* Manages information about the assignment and upper and lower bounds on the
* variables.
*/
- ArithPartialModel& d_partialModel;
+ ArithVariables& d_variables;
- /**
- * Reference to the Tableau to operate upon.
- */
+ /** Reference to the Tableau to operate upon. */
Tableau& d_tableau;
/** Called whenever the value of a basic variable is updated. */
- ArithVarCallBack& d_basicVariableUpdates;
+ BasicVarModelUpdateCallBack d_basicVariableUpdates;
+
+ BorderHeap d_increasing;
+ BorderHeap d_decreasing;
+ Maybe<DeltaRational> d_upperBoundDifference;
+ Maybe<DeltaRational> d_lowerBoundDifference;
public:
@@ -64,15 +226,22 @@ public:
* Initializes a LinearEqualityModule with a partial model, a tableau,
* and a callback function for when basic variables update their values.
*/
- LinearEqualityModule(ArithPartialModel& pm, Tableau& t, ArithVarCallBack& f):
- d_partialModel(pm), d_tableau(t), d_basicVariableUpdates(f)
- {}
+ LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f);
/**
* Updates the assignment of a nonbasic variable x_i to v.
* Also updates the assignment of basic variables accordingly.
*/
- void update(ArithVar x_i, const DeltaRational& v);
+ void updateUntracked(ArithVar x_i, const DeltaRational& v);
+ void updateTracked(ArithVar x_i, const DeltaRational& v);
+ void update(ArithVar x_i, const DeltaRational& v){
+ if(d_areTracking){
+ updateTracked(x_i,v);
+ }else{
+ updateUntracked(x_i,v);
+ }
+ }
+ void updateMany(const DenseMap<DeltaRational>& many);
/**
* Updates the value of a basic variable x_i to v,
@@ -80,11 +249,12 @@ public:
* Updates the assignment of the other basic variables accordingly.
*/
void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
+ //void pivotAndUpdateAdj(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
-
- ArithPartialModel& getPartialModel() const{ return d_partialModel; }
+ ArithVariables& getVariables() const{ return d_variables; }
Tableau& getTableau() const{ return d_tableau; }
+ void forceNewBasis(const DenseSet& newBasis);
bool hasBounds(ArithVar basic, bool upperBound);
bool hasLowerBounds(ArithVar basic){
@@ -94,7 +264,180 @@ public:
return hasBounds(basic, true);
}
+ void startTrackingBoundCounts();
+ void stopTrackingBoundCounts();
+
+
+ void includeBoundCountChange(ArithVar nb, BoundCounts prev);
+
+ void computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction basic);
+
+
+ uint32_t updateProduct(const UpdateInfo& inf) const;
+
+ inline bool minNonBasicVarOrder(const UpdateInfo& a, const UpdateInfo& b) const{
+ return a.nonbasic() >= b.nonbasic();
+ }
+
+ /**
+ * Prefer the update that touch the fewest entries in the matrix.
+ *
+ * The intuition is that this operation will be cheaper.
+ * This strongly biases the system towards updates instead of pivots.
+ */
+ inline bool minProduct(const UpdateInfo& a, const UpdateInfo& b) const{
+ uint32_t aprod = updateProduct(a);
+ uint32_t bprod = updateProduct(b);
+
+ if(aprod == bprod){
+ return minNonBasicVarOrder(a,b);
+ }else{
+ return aprod > bprod;
+ }
+ }
+ inline bool constrainedMin(const UpdateInfo& a, const UpdateInfo& b) const{
+ if(a.describesPivot() && b.describesPivot()){
+ bool aAtBounds = basicsAtBounds(a);
+ bool bAtBounds = basicsAtBounds(b);
+ if(aAtBounds != bAtBounds){
+ return bAtBounds;
+ }
+ }
+ return minProduct(a,b);
+ }
+
+ /**
+ * If both a and b are pivots, prefer the pivot with the leaving variables that has equal bounds.
+ * The intuition is that such variables will be less likely to lead to future problems.
+ */
+ inline bool preferFrozen(const UpdateInfo& a, const UpdateInfo& b) const {
+ if(a.describesPivot() && b.describesPivot()){
+ bool aFrozen = d_variables.boundsAreEqual(a.leaving());
+ bool bFrozen = d_variables.boundsAreEqual(b.leaving());
+
+ if(aFrozen != bFrozen){
+ return bFrozen;
+ }
+ }
+ return constrainedMin(a,b);
+ }
+
+ /**
+ * Prefer pivots with entering variables that do not have bounds.
+ * The intuition is that such variables will be less likely to lead to future problems.
+ */
+ bool preferNeitherBound(const UpdateInfo& a, const UpdateInfo& b) const {
+ if(d_variables.hasEitherBound(a.nonbasic()) == d_variables.hasEitherBound(b.nonbasic())){
+ return preferFrozen(a,b);
+ }else{
+ return d_variables.hasEitherBound(a.nonbasic());
+ }
+ }
+
+ // template<bool heuristic>
+ // bool preferNonDegenerate(const UpdateInfo& a, const UpdateInfo& b) const{
+ // if(a.focusDirection() == b.focusDirection()){
+ // if(heuristic){
+ // return preferNeitherBound(a,b);
+ // }else{
+ // return minNonBasicVarOrder(a,b);
+ // }
+ // }else{
+ // return a.focusDirection() < b.focusDirection();
+ // }
+ // }
+
+ // template <bool heuristic>
+ // bool preferErrorsFixed(const UpdateInfo& a, const UpdateInfo& b) const{
+ // if( a.errorsChange() == b.errorsChange() ){
+ // return preferNonDegenerate<heuristic>(a,b);
+ // }else{
+ // return a.errorsChange() > b.errorsChange();
+ // }
+ // }
+
+ // template <bool heuristic>
+ // bool preferConflictFound(const UpdateInfo& a, const UpdateInfo& b) const{
+ // if(a.d_foundConflict && b.d_foundConflict){
+ // // if both are true, determinize the preference
+ // return minNonBasicVarOrder(a,b);
+ // }else if( a.d_foundConflict || b.d_foundConflict ){
+ // return b.d_foundConflict;
+ // }else{
+ // return preferErrorsFixed<heuristic>(a,b);
+ // }
+ // }
+
+ bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ if(a.nonbasic() == b.nonbasic()){
+ bool aIsZero = a.nonbasicDelta().sgn() == 0;
+ bool bIsZero = b.nonbasicDelta().sgn() == 0;
+
+ if((aIsZero || bIsZero) && (!aIsZero || !bIsZero)){
+ return bIsZero;
+ }else{
+ return a.leaving() >= b.leaving();
+ }
+ }else{
+ return a.nonbasic() > b.nonbasic();
+ }
+ }
+
+ template <bool heuristic>
+ bool preferWitness(const UpdateInfo& a, const UpdateInfo& b) const{
+ WitnessImprovement aImp = a.getWitness(!heuristic);
+ WitnessImprovement bImp = b.getWitness(!heuristic);
+
+ if(aImp == bImp){
+ switch(aImp){
+ case ConflictFound:
+ return preferNeitherBound(a,b);
+ case ErrorDropped:
+ if(a.errorsChange() == b.errorsChange()){
+ return preferNeitherBound(a,b);
+ }else{
+ return a.errorsChange() > b.errorsChange();
+ }
+ case FocusImproved:
+ return preferNeitherBound(a,b);
+ case BlandsDegenerate:
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ return modifiedBlands(a,b);
+ case HeuristicDegenerate:
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ return preferNeitherBound(a,b);
+ case AntiProductive:
+ return minNonBasicVarOrder(a, b);
+ // Not valid responses
+ case Degenerate:
+ case FocusShrank:
+ Unreachable();
+ }
+ Unreachable();
+ }else{
+ return aImp > bImp;
+ }
+ }
+
private:
+ typedef std::vector<const Tableau::Entry*> EntryPointerVector;
+ EntryPointerVector d_relevantErrorBuffer;
+
+ //uint32_t computeUnconstrainedUpdate(ArithVar nb, int sgn, DeltaRational& am);
+ //uint32_t computedFixed(ArithVar nb, int sgn, const DeltaRational& am);
+ void computedFixed(UpdateInfo&);
+
+ // RowIndex -> BoundCount
+ BoundCountingVector& d_boundTracking;
+ bool d_areTracking;
+
/**
* Exports either the explanation of an upperbound or a lower bound
* of the basic variable basic, using the non-basic variables in the row.
@@ -104,11 +447,9 @@ private:
public:
void propagateNonbasicsLowerBound(ArithVar basic, Constraint c){
- Assert(c->isLowerBound());
propagateNonbasics<false>(basic, c);
}
void propagateNonbasicsUpperBound(ArithVar basic, Constraint c){
- Assert(c->isUpperBound());
propagateNonbasics<true>(basic, c);
}
@@ -128,42 +469,284 @@ public:
return computeBound(basic, true);
}
+ /**
+ * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
+ * and 2 ArithVar variables and returns one of the ArithVar variables
+ * potentially using the internals of the SimplexDecisionProcedure.
+ */
+
+ ArithVar noPreference(ArithVar x, ArithVar y) const{
+ return x;
+ }
+
+ /**
+ * minVarOrder is a PreferenceFunction for selecting the smaller of the 2
+ * ArithVars. This PreferenceFunction is used during the VarOrder stage of
+ * findModel.
+ */
+ ArithVar minVarOrder(ArithVar x, ArithVar y) const;
+
+ /**
+ * minColLength is a PreferenceFunction for selecting the variable with the
+ * smaller row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minColLength(ArithVar x, ArithVar y) const;
+
+ /**
+ * minRowLength is a PreferenceFunction for selecting the variable with the
+ * smaller row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minRowLength(ArithVar x, ArithVar y) const;
+
+ /**
+ * minBoundAndRowCount is a PreferenceFunction for preferring a variable
+ * without an asserted bound over variables with an asserted bound.
+ * If both have bounds or both do not have bounds,
+ * the rule falls back to minRowCount(...).
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minBoundAndColLength(ArithVar x, ArithVar y) const;
+
+
+ template <bool above>
+ inline bool isAcceptableSlack(int sgn, ArithVar nonbasic) const {
+ return
+ ( above && sgn < 0 && d_variables.strictlyBelowUpperBound(nonbasic)) ||
+ ( above && sgn > 0 && d_variables.strictlyAboveLowerBound(nonbasic)) ||
+ (!above && sgn > 0 && d_variables.strictlyBelowUpperBound(nonbasic)) ||
+ (!above && sgn < 0 && d_variables.strictlyAboveLowerBound(nonbasic));
+ }
+
+ /**
+ * Given the basic variable x_i,
+ * this function finds the smallest nonbasic variable x_j in the row of x_i
+ * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
+ * This returns ARITHVAR_SENTINEL if none exists.
+ *
+ * More formally one of the following conditions must be satisfied:
+ * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
+ * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
+ * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
+ * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
+ *
+ */
+ template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, VarPreferenceFunction pf) const;
+ ArithVar selectSlackLowerBound(ArithVar x_i, VarPreferenceFunction pf) const {
+ return selectSlack<true>(x_i, pf);
+ }
+ ArithVar selectSlackUpperBound(ArithVar x_i, VarPreferenceFunction pf) const {
+ return selectSlack<false>(x_i, pf);
+ }
+
+ const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const;
+
+ inline bool basicIsTracked(ArithVar v) const {
+ return d_boundTracking.isKey(v);
+ }
+ void trackVariable(ArithVar x_i);
+
+ void maybeRemoveTracking(ArithVar v){
+ Assert(!d_tableau.isBasic(v));
+ if(d_boundTracking.isKey(v)){
+ d_boundTracking.remove(v);
+ }
+ }
+
+ // void trackVariable(ArithVar x_i){
+ // Assert(!basicIsTracked(x_i));
+ // d_boundTracking.set(x_i,computeBoundCounts(x_i));
+ // }
+ bool basicsAtBounds(const UpdateInfo& u) const;
+private:
+ BoundCounts computeBoundCounts(ArithVar x_i) const;
+public:
+ //BoundCounts cachingCountBounds(ArithVar x_i) const;
+ BoundCounts _countBounds(ArithVar x_i) const;
+ void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
+
+ void trackingSwap(ArithVar basic, ArithVar nb, int sgn);
+
+ bool nonbasicsAtLowerBounds(ArithVar x_i) const;
+ bool nonbasicsAtUpperBounds(ArithVar x_i) const;
+
+ ArithVar _anySlackLowerBound(ArithVar x_i) const {
+ return selectSlack<true>(x_i, &LinearEqualityModule::noPreference);
+ }
+ ArithVar _anySlackUpperBound(ArithVar x_i) const {
+ return selectSlack<false>(x_i, &LinearEqualityModule::noPreference);
+ }
+
+private:
+ class TrackingCallback : public CoefficientChangeCallback {
+ private:
+ LinearEqualityModule* d_linEq;
+ public:
+ TrackingCallback(LinearEqualityModule* le) : d_linEq(le) {}
+ void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
+ d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
+ }
+ void swap(ArithVar basic, ArithVar nb, int oldNbSgn){
+ d_linEq->trackingSwap(basic, nb, oldNbSgn);
+ }
+ bool canUseRow(RowIndex ridx) const {
+ ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
+ return d_linEq->basicIsTracked(basic);
+ }
+ } d_trackCallback;
+
+ /**
+ * Selects the constraint for the variable v on the row for basic
+ * with the weakest possible constraint that is consistent with the surplus
+ * surplus.
+ */
+ Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v,
+ const Rational& coeff, bool& anyWeakening, ArithVar basic) const;
+
+public:
+ /**
+ * Constructs a minimally weak conflict for the basic variable basicVar.
+ */
+ Node minimallyWeakConflict(bool aboveUpper, ArithVar basicVar) const;
+
+ /**
+ * Given a non-basic variable that is know to have a conflict on it,
+ * construct and return a conflict.
+ * Follows section 4.2 in the CAV06 paper.
+ */
+ inline Node generateConflictAboveUpperBound(ArithVar conflictVar) const {
+ return minimallyWeakConflict(true, conflictVar);
+ }
+
+ inline Node generateConflictBelowLowerBound(ArithVar conflictVar) const {
+ return minimallyWeakConflict(false, conflictVar);
+ }
+
private:
DeltaRational computeBound(ArithVar basic, bool upperBound);
public:
+ void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult);
+ void directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult);
+
+
/**
* Checks to make sure the assignment is consistent with the tableau.
* This code is for debugging.
*/
void debugCheckTableau();
+ void debugCheckTracking();
+
/** Debugging information for a pivot. */
void debugPivot(ArithVar x_i, ArithVar x_j);
+ /** Checks the tableau + partial model for consistency. */
+ bool debugEntireLinEqIsConsistent(const std::string& s);
+
+
+ ArithVar minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const;
+
/**
- *
+ * Returns true if there would be a conflict on this row after a pivot
+ * and update using its basic variable and one of the non-basic variables on
+ * the row.
*/
- bool debugEntireLinEqIsConsistent(const std::string& s);
+ bool willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const;
+ UpdateInfo mkConflictUpdate(const Tableau::Entry& entry, bool ub) const;
+ /**
+ * Looks more an update for fcSimplex on the nonbasic variable nb with the focus coefficient.
+ */
+ UpdateInfo speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref);
+
+private:
+
+ /**
+ * Examines the effects of pivoting the entries column variable
+ * with the row's basic variable and setting the variable s.t.
+ * the basic variable is equal to one of its bounds.
+ *
+ * If ub, then the basic variable will be equal its upper bound.
+ * If not ub,then the basic variable will be equal its lower bound.
+ *
+ * Returns iff this row will be in conflict after the pivot.
+ *
+ * If this is false, add the bound to the relevant heap.
+ * If the bound is +/-infinity, this is ignored.
+
+ *
+ * Returns true if this would be a conflict.
+ * If it returns false, this
+ */
+ bool accumulateBorder(const Tableau::Entry& entry, bool ub);
+
+ void handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref);
+ void pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange);
+ void clearSpeculative();
+ Rational updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock);
private:
/** These fields are designed to be accessible to TheoryArith methods. */
class Statistics {
public:
IntStat d_statPivots, d_statUpdates;
-
TimerStat d_pivotTime;
+ TimerStat d_adjTime;
+
+ IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
+ TimerStat d_weakenTime;
Statistics();
~Statistics();
};
- Statistics d_statistics;
+ mutable Statistics d_statistics;
};/* class LinearEqualityModule */
+struct Cand {
+ ArithVar d_nb;
+ uint32_t d_penalty;
+ int d_sgn;
+ const Rational* d_coeff;
+
+ Cand(ArithVar nb, uint32_t penalty, int s, const Rational* c) :
+ d_nb(nb), d_penalty(penalty), d_sgn(s), d_coeff(c){}
+};
+
+
+class CompPenaltyColLength {
+private:
+ LinearEqualityModule* d_mod;
+public:
+ CompPenaltyColLength(LinearEqualityModule* mod): d_mod(mod){}
+
+ bool operator()(const Cand& x, const Cand& y) const {
+ if(x.d_penalty == y.d_penalty || !options::havePenalties()){
+ return x.d_nb == d_mod->minBoundAndColLength(x.d_nb,y.d_nb);
+ }else{
+ return x.d_penalty < y.d_penalty;
+ }
+ }
+};
+
+class UpdateTrackingCallback : public BoundCountsCallback {
+private:
+ LinearEqualityModule* d_mod;
+public:
+ UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
+ void operator()(ArithVar v, BoundCounts bc){
+ d_mod->includeBoundCountChange(v, bc);
+ }
+};
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__ARITH__LINEAR_EQUALITY_H */
diff --git a/src/theory/arith/matrix.cpp b/src/theory/arith/matrix.cpp
index 9fee74324..7136c3fa8 100644
--- a/src/theory/arith/matrix.cpp
+++ b/src/theory/arith/matrix.cpp
@@ -23,551 +23,9 @@ namespace CVC4 {
namespace theory {
namespace arith {
-
-/*
-void Tableau::addRow(ArithVar basicVar,
- const std::vector<Rational>& coeffs,
- const std::vector<ArithVar>& variables){
-
- Assert(coeffs.size() == variables.size());
-
- //The new basic variable cannot already be a basic variable
- Assert(!d_basicVariables.isMember(basicVar));
- d_basicVariables.add(basicVar);
- ReducedRowVector* row_current = new ReducedRowVector(basicVar,variables, coeffs,d_rowCount, d_columnMatrix);
- d_rowsTable[basicVar] = row_current;
-
- //A variable in the row may have been made non-basic already.
- //If this is the case we fake pivoting this variable
- vector<ArithVar>::const_iterator varsIter = variables.begin();
- vector<ArithVar>::const_iterator varsEnd = variables.end();
-
- for( ; varsIter != varsEnd; ++varsIter){
- ArithVar var = *varsIter;
-
- if(d_basicVariables.isMember(var)){
- EntryID varID = find(basicVar, var);
- TableauEntry& entry = d_entryManager.get(varID);
- const Rational& coeff = entry.getCoefficient();
-
- loadRowIntoMergeBuffer(var);
- rowPlusRowTimesConstant(coeff, basicVar, var);
- emptyRowFromMergeBuffer(var);
- }
- }
-}
-*/
-
-/*
-ReducedRowVector* Tableau::removeRow(ArithVar basic){
- Assert(isBasic(basic));
-
- ReducedRowVector* row = d_rowsTable[basic];
-
- d_basicVariables.remove(basic);
- d_rowsTable[basic] = NULL;
-
- return row;
-}
-*/
-
-void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic){
- Assert(isBasic(oldBasic));
- Assert(!isBasic(newBasic));
- Assert(d_mergeBuffer.empty());
-
- Debug("tableau") << "Tableau::pivot(" << oldBasic <<", " << newBasic <<")" << endl;
-
- RowIndex ridx = basicToRowIndex(oldBasic);
-
- rowPivot(oldBasic, newBasic);
- Assert(ridx == basicToRowIndex(newBasic));
-
- loadRowIntoBuffer(ridx);
-
- ColIterator colIter = colIterator(newBasic);
- while(!colIter.atEnd()){
- EntryID id = colIter.getID();
- Entry& entry = d_entries.get(id);
-
- ++colIter; //needs to be incremented before the variable is removed
- if(entry.getRowIndex() == ridx){ continue; }
-
- RowIndex to = entry.getRowIndex();
- Rational coeff = entry.getCoefficient();
-
- rowPlusBufferTimesConstant(to, coeff);
- }
- clearBuffer();
-
- //Clear the column for used for this variable
-
- Assert(d_mergeBuffer.empty());
- Assert(!isBasic(oldBasic));
- Assert(isBasic(newBasic));
- Assert(getColLength(newBasic) == 1);
-}
-
-// void Tableau::printTableau() const {
-// Debug("tableau") << "Tableau::d_activeRows" << endl;
-
-// ArithVarSet::const_iterator basicIter = beginBasic(), endIter = endBasic();
-// for(; basicIter != endIter; ++basicIter){
-// ArithVar basic = *basicIter;
-// printRow(basic);
-// }
-// }
-
-// void Tableau::printRow(ArithVar basic) const {
-// Debug("tableau") << "{" << basic << ":";
-// for(RowIterator entryIter = rowIterator(basic); !entryIter.atEnd(); ++entryIter){
-// const TableauEntry& entry = *entryIter;
-// printEntry(entry);
-// Debug("tableau") << ",";
-// }
-// Debug("tableau") << "}" << endl;
-// }
-
-// void Tableau::printEntry(const TableauEntry& entry) const {
-// Debug("tableau") << entry.getColVar() << "*" << entry.getCoefficient();
-// }
-
-// uint32_t Tableau::numNonZeroEntriesByRow() const {
-// uint32_t rowSum = 0;
-// ArithVarSet::const_iterator i = d_basicVariables.begin(), end = d_basicVariables.end();
-// for(; i != end; ++i){
-// ArithVar basic = *i;
-// rowSum += getRowLength(basic);
-// }
-// return rowSum;
-// }
-
-// uint32_t Tableau::numNonZeroEntriesByCol() const {
-// uint32_t colSum = 0;
-// VectorSizeTable::const_iterator i = d_colLengths.begin();
-// VectorSizeTable::const_iterator end = d_colLengths.end();
-// for(; i != end; ++i){
-// colSum += *i;
-// }
-// return colSum;
-// }
-
-
-// EntryID Tableau::findOnRow(ArithVar row, ArithVar col){
-// for(RowIterator i = rowIterator(row); !i.atEnd(); ++i){
-// EntryID id = i.getID();
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-
-// if(colVar == col){
-// return id;
-// }
-// }
-// return ENTRYID_SENTINEL;
-// }
-
-// EntryID Tableau::findOnCol(ArithVar row, ArithVar col){
-// for(ColIterator i = colIterator(col); !i.atEnd(); ++i){
-// EntryID id = i.getID();
-// const TableauEntry& entry = *i;
-// ArithVar rowVar = entry.getRowVar();
-
-// if(rowVar == row){
-// return id;
-// }
-// }
-// return ENTRYID_SENTINEL;
-// }
-
-// const TableauEntry& Tableau::findEntry(ArithVar row, ArithVar col){
-// bool colIsShorter = getColLength(col) < getRowLength(row);
-// EntryID id = colIsShorter ? findOnCol(row,col) : findOnRow(row,col);
-// if(id == ENTRYID_SENTINEL){
-// return d_failedFind;
-// }else{
-// return d_entryManager.get(id);
-// }
-// }
-
-// void Tableau::removeRow(ArithVar basic){
-// RowIterator i = rowIterator(basic);
-// while(!i.atEnd()){
-// EntryID id = i.getID();
-// ++i;
-// removeEntry(id);
-// }
-// d_basicVariables.remove(basic);
-// }
-
-// void Tableau::loadRowIntoMergeBuffer(ArithVar basic){
-// Assert(mergeBufferIsEmpty());
-// for(RowIterator i = rowIterator(basic); !i.atEnd(); ++i){
-// EntryID id = i.getID();
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-// d_mergeBuffer[colVar] = make_pair(id, false);
-// }
-// }
-
-// void Tableau::emptyRowFromMergeBuffer(ArithVar basic){
-// Assert(isBasic(basic));
-// for(RowIterator i = rowIterator(basic); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-// Assert(d_mergeBuffer[colVar].first == i.getID());
-// d_mergeBuffer[colVar] = make_pair(ENTRYID_SENTINEL, false);
-// }
-
-// Assert(mergeBufferIsEmpty());
-// }
-
-
-/**
- * Changes basic to newbasic (a variable on the row).
- */
-void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew){
- Assert(isBasic(basicOld));
- Assert(!isBasic(basicNew));
-
- RowIndex rid = basicToRowIndex(basicOld);
-
- EntryID newBasicID = findOnRow(rid, basicNew);
-
- Assert(newBasicID != ENTRYID_SENTINEL);
-
- Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
- Rational negInverseA_rs = -(newBasicEntry.getCoefficient().inverse());
-
- for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
- EntryID id = i.getID();
- Tableau::Entry& entry = d_entries.get(id);
-
- entry.getCoefficient() *= negInverseA_rs;
- }
-
- d_basic2RowIndex.remove(basicOld);
- d_basic2RowIndex.set(basicNew, rid);
- d_rowIndex2basic.set(rid, basicNew);
-}
-
-// void Tableau::addEntry(ArithVar row, ArithVar col, const Rational& coeff){
-// Assert(coeff != 0);
-
-// EntryID newId = d_entryManager.newEntry();
-// TableauEntry& newEntry = d_entryManager.get(newId);
-// newEntry = TableauEntry( row, col,
-// d_rowHeads[row], d_colHeads[col],
-// ENTRYID_SENTINEL, ENTRYID_SENTINEL,
-// coeff);
-// Assert(newEntry.getCoefficient() != 0);
-
-// Debug("tableau") << "addEntry(" << row << "," << col <<"," << coeff << ")" << endl;
-
-// ++d_entriesInUse;
-
-// if(d_rowHeads[row] != ENTRYID_SENTINEL)
-// d_entryManager.get(d_rowHeads[row]).setPrevRowID(newId);
-
-// if(d_colHeads[col] != ENTRYID_SENTINEL)
-// d_entryManager.get(d_colHeads[col]).setPrevColID(newId);
-
-// d_rowHeads[row] = newId;
-// d_colHeads[col] = newId;
-// ++d_rowLengths[row];
-// ++d_colLengths[col];
-// }
-
-// void Tableau::removeEntry(EntryID id){
-// Assert(d_entriesInUse > 0);
-// --d_entriesInUse;
-
-// TableauEntry& entry = d_entryManager.get(id);
-
-// ArithVar row = entry.getRowVar();
-// ArithVar col = entry.getColVar();
-
-// Assert(d_rowLengths[row] > 0);
-// Assert(d_colLengths[col] > 0);
-
-
-// --d_rowLengths[row];
-// --d_colLengths[col];
-
-// EntryID prevRow = entry.getPrevRowID();
-// EntryID prevCol = entry.getPrevColID();
-
-// EntryID nextRow = entry.getNextRowID();
-// EntryID nextCol = entry.getNextColID();
-
-// if(d_rowHeads[row] == id){
-// d_rowHeads[row] = nextRow;
-// }
-// if(d_colHeads[col] == id){
-// d_colHeads[col] = nextCol;
-// }
-
-// entry.markBlank();
-
-// if(prevRow != ENTRYID_SENTINEL){
-// d_entryManager.get(prevRow).setNextRowID(nextRow);
-// }
-// if(nextRow != ENTRYID_SENTINEL){
-// d_entryManager.get(nextRow).setPrevRowID(prevRow);
-// }
-
-// if(prevCol != ENTRYID_SENTINEL){
-// d_entryManager.get(prevCol).setNextColID(nextCol);
-// }
-// if(nextCol != ENTRYID_SENTINEL){
-// d_entryManager.get(nextCol).setPrevColID(prevCol);
-// }
-
-// d_entryManager.freeEntry(id);
-// }
-
-// void Tableau::rowPlusRowTimesConstant(ArithVar basicTo, const Rational& c, ArithVar basicFrom){
-
-// Debug("tableau") << "rowPlusRowTimesConstant("
-// << basicTo << "," << c << "," << basicFrom << ")"
-// << endl;
-
-// Assert(debugNoZeroCoefficients(basicTo));
-// Assert(debugNoZeroCoefficients(basicFrom));
-
-// Assert(c != 0);
-// Assert(isBasic(basicTo));
-// Assert(isBasic(basicFrom));
-// Assert( d_usedList.empty() );
-
-
-// RowIterator i = rowIterator(basicTo);
-// while(!i.atEnd()){
-// EntryID id = i.getID();
-// TableauEntry& entry = d_entryManager.get(id);
-// ArithVar colVar = entry.getColVar();
-
-// ++i;
-// if(bufferPairIsNotEmpty(d_mergeBuffer[colVar])){
-// d_mergeBuffer[colVar].second = true;
-// d_usedList.push_back(colVar);
-
-// EntryID inOtherRow = d_mergeBuffer[colVar].first;
-// const TableauEntry& other = d_entryManager.get(inOtherRow);
-// entry.getCoefficient() += c * other.getCoefficient();
-
-// if(entry.getCoefficient().sgn() == 0){
-// removeEntry(id);
-// }
-// }
-// }
-
-// for(RowIterator i = rowIterator(basicFrom); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-
-// if(!(d_mergeBuffer[colVar]).second){
-// Rational newCoeff = c * entry.getCoefficient();
-// addEntry(basicTo, colVar, newCoeff);
-// }
-// }
-
-// clearUsedList();
-
-// if(Debug.isOn("tableau")) { printTableau(); }
-// }
-
-// void Tableau::clearUsedList(){
-// ArithVarArray::iterator i, end;
-// for(i = d_usedList.begin(), end = d_usedList.end(); i != end; ++i){
-// ArithVar pos = *i;
-// d_mergeBuffer[pos].second = false;
-// }
-// d_usedList.clear();
-// }
-
-void Tableau::addRow(ArithVar basic,
- const std::vector<Rational>& coefficients,
- const std::vector<ArithVar>& variables)
-{
- Assert(basic < getNumColumns());
-
- Assert(coefficients.size() == variables.size() );
- Assert(!isBasic(basic));
-
- RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
- addEntry(newRow, basic, Rational(-1));
-
- Assert(!d_basic2RowIndex.isKey(basic));
- Assert(!d_rowIndex2basic.isKey(newRow));
-
- d_basic2RowIndex.set(basic, newRow);
- d_rowIndex2basic.set(newRow, basic);
-
-
- if(Debug.isOn("matrix")){ printMatrix(); }
-
- vector<Rational>::const_iterator coeffIter = coefficients.begin();
- vector<ArithVar>::const_iterator varsIter = variables.begin();
- vector<ArithVar>::const_iterator varsEnd = variables.end();
- for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
- ArithVar var = *varsIter;
-
- if(isBasic(var)){
- Rational coeff = *coeffIter;
-
- RowIndex ri = basicToRowIndex(var);
-
- loadRowIntoBuffer(ri);
- rowPlusBufferTimesConstant(newRow, coeff);
- clearBuffer();
- }
- }
-
- if(Debug.isOn("matrix")) { printMatrix(); }
-
- Assert(debugNoZeroCoefficients(newRow));
- Assert(debugMatchingCountsForRow(newRow));
- Assert(getColLength(basic) == 1);
-}
-
-// bool Tableau::debugNoZeroCoefficients(ArithVar basic){
-// for(RowIterator i=rowIterator(basic); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// if(entry.getCoefficient() == 0){
-// return false;
-// }
-// }
-// return true;
-// }
-// bool Tableau::debugMatchingCountsForRow(ArithVar basic){
-// for(RowIterator i=rowIterator(basic); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-// uint32_t count = debugCountColLength(colVar);
-// Debug("tableau") << "debugMatchingCountsForRow "
-// << basic << ":" << colVar << " " << count
-// <<" "<< d_colLengths[colVar] << endl;
-// if( count != d_colLengths[colVar] ){
-// return false;
-// }
-// }
-// return true;
-// }
-
-
-// uint32_t Tableau::debugCountColLength(ArithVar var){
-// Debug("tableau") << var << " ";
-// uint32_t count = 0;
-// for(ColIterator i=colIterator(var); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// Debug("tableau") << "(" << entry.getRowVar() << ", " << i.getID() << ") ";
-// ++count;
-// }
-// Debug("tableau") << endl;
-// return count;
-// }
-
-// uint32_t Tableau::debugCountRowLength(ArithVar var){
-// uint32_t count = 0;
-// for(RowIterator i=rowIterator(var); !i.atEnd(); ++i){
-// ++count;
-// }
-// return count;
-// }
-
-/*
-void ReducedRowVector::enqueueNonBasicVariablesAndCoefficients(std::vector< ArithVar >& variables,std::vector< Rational >& coefficients) const{
- for(const_iterator i=begin(), endEntries=end(); i != endEntries; ++i){
- ArithVar var = (*i).getArithVar();
- const Rational& q = (*i).getCoefficient();
- if(var != basic()){
- variables.push_back(var);
- coefficients.push_back(q);
- }
- }
- }*/
-
-// Node Tableau::rowAsEquality(ArithVar basic, const ArithVarToNodeMap& map){
-// using namespace CVC4::kind;
-
-// Assert(getRowLength(basic) >= 2);
-
-// vector<Node> nonBasicPairs;
-// for(RowIterator i = rowIterator(basic); !i.atEnd(); ++i){
-// const TableauEntry& entry = *i;
-// ArithVar colVar = entry.getColVar();
-// if(colVar == basic) continue;
-// Node var = (map.find(colVar))->second;
-// Node coeff = mkRationalNode(entry.getCoefficient());
-
-// Node mult = NodeBuilder<2>(MULT) << coeff << var;
-// nonBasicPairs.push_back(mult);
-// }
-
-// Node sum = Node::null();
-// if(nonBasicPairs.size() == 1 ){
-// sum = nonBasicPairs.front();
-// }else{
-// Assert(nonBasicPairs.size() >= 2);
-// NodeBuilder<> sumBuilder(PLUS);
-// sumBuilder.append(nonBasicPairs);
-// sum = sumBuilder;
-// }
-// Node basicVar = (map.find(basic))->second;
-// return NodeBuilder<2>(EQUAL) << basicVar << sum;
-// }
-
-// double Tableau::densityMeasure() const{
-// Assert(numNonZeroEntriesByRow() == numNonZeroEntries());
-// Assert(numNonZeroEntriesByCol() == numNonZeroEntries());
-
-// uint32_t n = getNumRows();
-// if(n == 0){
-// return 1.0;
-// }else {
-// uint32_t s = numNonZeroEntries();
-// uint32_t m = d_colHeads.size();
-// uint32_t divisor = (n *(m - n + 1));
-
-// Assert(n >= 1);
-// Assert(m >= n);
-// Assert(divisor > 0);
-// Assert(divisor >= s);
-
-// return (double(s)) / divisor;
-// }
-// }
-
-// void TableauEntryManager::freeEntry(EntryID id){
-// Assert(get(id).blank());
-// Assert(d_size > 0);
-
-// d_freedEntries.push(id);
-// --d_size;
-// }
-
-// EntryID TableauEntryManager::newEntry(){
-// EntryID newId;
-// if(d_freedEntries.empty()){
-// newId = d_entries.size();
-// d_entries.push_back(TableauEntry());
-// }else{
-// newId = d_freedEntries.front();
-// d_freedEntries.pop();
-// }
-// ++d_size;
-// return newId;
-// }
-
-void Tableau::removeBasicRow(ArithVar basic){
- RowIndex rid = basicToRowIndex(basic);
-
- removeRow(rid);
- d_basic2RowIndex.remove(basic);
- d_rowIndex2basic.remove(rid);
-
-}
-
+void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
+void NoEffectCCCB::swap(ArithVar basic, ArithVar nb, int nbSgn){}
+bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h
index 51c2114a0..100f999e0 100644
--- a/src/theory/arith/matrix.h
+++ b/src/theory/arith/matrix.h
@@ -19,14 +19,9 @@
#pragma once
-#include "expr/node.h"
-
#include "util/index.h"
#include "util/dense_map.h"
-
#include "theory/arith/arithvar.h"
-#include "theory/arith/arithvar_node_map.h"
-#include "theory/arith/normal_form.h"
#include <queue>
#include <vector>
@@ -42,6 +37,20 @@ const EntryID ENTRYID_SENTINEL = std::numeric_limits<EntryID>::max();
typedef Index RowIndex;
const RowIndex ROW_INDEX_SENTINEL = std::numeric_limits<RowIndex>::max();
+class CoefficientChangeCallback {
+public:
+ virtual void update(RowIndex basic, ArithVar nb, int oldSgn, int currSgn) = 0;
+ virtual void swap(ArithVar basic, ArithVar nb, int nbSgn) = 0;
+ virtual bool canUseRow(RowIndex ridx) const = 0;
+};
+
+class NoEffectCCCB : public CoefficientChangeCallback {
+public:
+ void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
+ void swap(ArithVar basic, ArithVar nb, int nbSgn);
+ bool canUseRow(RowIndex ridx) const;
+};
+
template<class T>
class MatrixEntry {
private:
@@ -334,6 +343,8 @@ public:
typedef typename SuperT::const_iterator const_iterator;
RowVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+ RowVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+ : SuperT(head, size, mev){}
};/* class RowVector<T> */
template <class T>
@@ -345,6 +356,8 @@ public:
typedef typename SuperT::const_iterator const_iterator;
ColumnVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+ ColumnVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+ : SuperT(head, size, mev){}
};/* class ColumnVector<T> */
template <class T>
@@ -406,6 +419,45 @@ public:
d_zero(zero)
{}
+ Matrix(const Matrix& m)
+ : d_rows(),
+ d_columns(),
+ d_mergeBuffer(m.d_mergeBuffer),
+ d_rowInMergeBuffer(m.d_rowInMergeBuffer),
+ d_entriesInUse(m.d_entriesInUse),
+ d_entries(m.d_entries),
+ d_zero(m.d_zero)
+ {
+ d_columns.clear();
+ for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+ const ColumnVectorT& col = *c;
+ d_columns.push_back(ColumnVectorT(col.getHead(),col.getSize(),&d_entries));
+ }
+ d_rows.clear();
+ for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+ const RowVectorT& row = *r;
+ d_rows.push_back(RowVectorT(row.getHead(),row.getSize(),&d_entries));
+ }
+ }
+
+ Matrix& operator=(const Matrix& m){
+ d_mergeBuffer = (m.d_mergeBuffer);
+ d_rowInMergeBuffer = (m.d_rowInMergeBuffer);
+ d_entriesInUse = (m.d_entriesInUse);
+ d_entries = (m.d_entries);
+ d_zero = (m.d_zero);
+ d_columns.clear();
+ for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+ const ColumnVector<T>& col = *c;
+ d_columns.push_back(ColumnVector<T>(col.getHead(), col.getSize(), &d_entries));
+ }
+ d_rows.clear();
+ for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+ const RowVector<T>& row = *r;
+ d_rows.push_back(RowVector<T>(row.getHead(), row.getSize(), &d_entries));
+ }
+ return *this;
+ }
protected:
@@ -511,12 +563,12 @@ public:
//RowIndex ridx = d_rows.size();
//d_rows.push_back(RowVectorT(&d_entries));
- std::vector<Rational>::const_iterator coeffIter = coeffs.begin();
+ typename std::vector<T>::const_iterator coeffIter = coeffs.begin();
std::vector<ArithVar>::const_iterator varsIter = variables.begin();
std::vector<ArithVar>::const_iterator varsEnd = variables.end();
for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
- const Rational& coeff = *coeffIter;
+ const T& coeff = *coeffIter;
ArithVar var_i = *varsIter;
Assert(var_i < getNumColumns());
addEntry(ridx, var_i, coeff);
@@ -578,9 +630,10 @@ public:
d_mergeBuffer.get(colVar).second = true;
const Entry& other = d_entries.get(bufferEntry);
- entry.getCoefficient() += mult * other.getCoefficient();
+ T& coeff = entry.getCoefficient();
+ coeff += mult * other.getCoefficient();
- if(entry.getCoefficient() == d_zero){
+ if(coeff.sgn() == 0){
removeEntry(id);
}
}
@@ -607,6 +660,74 @@ public:
if(Debug.isOn("matrix")) { printMatrix(); }
}
+ /** to += mult * buffer. */
+ void rowPlusBufferTimesConstant(RowIndex to, const T& mult, CoefficientChangeCallback& cb){
+ Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+ Assert(to != ROW_INDEX_SENTINEL);
+
+ Debug("tableau") << "rowPlusRowTimesConstant("
+ << to << "," << mult << "," << d_rowInMergeBuffer << ")"
+ << std::endl;
+
+ Assert(debugNoZeroCoefficients(to));
+ Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
+
+ Assert(mult != 0);
+
+
+ RowIterator i = getRow(to).begin();
+ RowIterator i_end = getRow(to).end();
+ while(i != i_end){
+ EntryID id = i.getID();
+ Entry& entry = d_entries.get(id);
+ ArithVar colVar = entry.getColVar();
+
+ ++i;
+
+ if(d_mergeBuffer.isKey(colVar)){
+ EntryID bufferEntry = d_mergeBuffer[colVar].first;
+ Assert(!d_mergeBuffer[colVar].second);
+ d_mergeBuffer.get(colVar).second = true;
+
+ const Entry& other = d_entries.get(bufferEntry);
+ T& coeff = entry.getCoefficient();
+ int coeffOldSgn = coeff.sgn();
+ coeff += mult * other.getCoefficient();
+ int coeffNewSgn = coeff.sgn();
+
+ if(coeffOldSgn != coeffNewSgn){
+ cb.update(to, colVar, coeffOldSgn, coeffNewSgn);
+
+ if(coeffNewSgn == 0){
+ removeEntry(id);
+ }
+ }
+ }
+ }
+
+ i = getRow(d_rowInMergeBuffer).begin();
+ i_end = getRow(d_rowInMergeBuffer).end();
+
+ for(; i != i_end; ++i){
+ const Entry& entry = *i;
+ ArithVar colVar = entry.getColVar();
+
+ if(d_mergeBuffer[colVar].second){
+ d_mergeBuffer.get(colVar).second = false;
+ }else{
+ Assert(!(d_mergeBuffer[colVar]).second);
+ T newCoeff = mult * entry.getCoefficient();
+ addEntry(to, colVar, newCoeff);
+
+ cb.update(to, colVar, 0, newCoeff.sgn());
+ }
+ }
+
+ Assert(mergeBufferIsClear());
+
+ if(Debug.isOn("matrix")) { printMatrix(); }
+ }
+
bool mergeBufferIsClear() const{
RowToPosUsedPairMap::const_iterator i = d_mergeBuffer.begin();
RowToPosUsedPairMap::const_iterator i_end = d_mergeBuffer.end();
@@ -621,7 +742,7 @@ public:
protected:
- EntryID findOnRow(RowIndex rid, ArithVar column){
+ EntryID findOnRow(RowIndex rid, ArithVar column) const {
RowIterator i = d_rows[rid].begin(), i_end = d_rows[rid].end();
for(; i != i_end; ++i){
EntryID id = i.getID();
@@ -635,7 +756,7 @@ protected:
return ENTRYID_SENTINEL;
}
- EntryID findOnCol(RowIndex rid, ArithVar column){
+ EntryID findOnCol(RowIndex rid, ArithVar column) const{
ColIterator i = d_columns[column].begin(), i_end = d_columns[column].end();
for(; i != i_end; ++i){
EntryID id = i.getID();
@@ -649,63 +770,59 @@ protected:
return ENTRYID_SENTINEL;
}
+ EntryID findEntryID(RowIndex rid, ArithVar col) const{
+ bool colIsShorter = getColLength(col) < getRowLength(rid);
+ EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
+ return id;
+ }
MatrixEntry<T> d_failedFind;
public:
/** If the find fails, isUnused is true on the entry. */
- const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col){
- bool colIsShorter = getColLength(col) < getRowLength(rid);
- EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
+ const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col) const{
+ EntryID id = findEntryID(rid, col);
if(id == ENTRYID_SENTINEL){
return d_failedFind;
}else{
- return d_entries.get(id);
+ return d_entries[id];
}
}
/**
* Prints the contents of the Matrix to Debug("matrix")
*/
- void printMatrix() const {
- Debug("matrix") << "Matrix::printMatrix" << std::endl;
+ void printMatrix(std::ostream& out) const {
+ out << "Matrix::printMatrix" << std::endl;
for(RowIndex i = 0, N = d_rows.size(); i < N; ++i){
- printRow(i);
+ printRow(i, out);
}
}
+ void printMatrix() const {
+ printMatrix(Debug("matrix"));
+ }
- void printRow(RowIndex rid) const {
- Debug("matrix") << "{" << rid << ":";
+ void printRow(RowIndex rid, std::ostream& out) const {
+ out << "{" << rid << ":";
const RowVector<T>& row = getRow(rid);
RowIterator i = row.begin();
RowIterator i_end = row.end();
for(; i != i_end; ++i){
- printEntry(*i);
- Debug("matrix") << ",";
+ printEntry(*i, out);
+ out << ",";
}
- Debug("matrix") << "}" << std::endl;
+ out << "}" << std::endl;
+ }
+ void printRow(RowIndex rid) const {
+ printRow(rid, Debug("matrix"));
}
+ void printEntry(const MatrixEntry<T>& entry, std::ostream& out) const {
+ out << entry.getColVar() << "*" << entry.getCoefficient();
+ }
void printEntry(const MatrixEntry<T>& entry) const {
- Debug("matrix") << entry.getColVar() << "*" << entry.getCoefficient();
+ printEntry(entry, Debug("matrix"));
}
-
-
-protected:
-
- // static bool bufferPairIsNotEmpty(const PosUsedPair& p){
- // return !(p.first == ENTRYID_SENTINEL && p.second == false);
- // }
-
- // static bool bufferPairIsEmpty(const PosUsedPair& p){
- // return (p.first == ENTRYID_SENTINEL && p.second == false);
- // }
- // bool mergeBufferIsEmpty() const {
- // return d_mergeBuffer.end() == std::find_if(d_mergeBuffer.begin(),
- // d_mergeBuffer.end(),
- // bufferPairIsNotEmpty);
- // }
-
public:
uint32_t size() const {
return d_entriesInUse;
@@ -717,6 +834,31 @@ public:
return d_entries.capacity();
}
+ void manipulateRowEntry(RowIndex row, ArithVar col, const T& c, CoefficientChangeCallback& cb){
+ int coeffOldSgn;
+ int coeffNewSgn;
+
+ EntryID id = findEntryID(row, col);
+ if(id == ENTRYID_SENTINEL){
+ coeffOldSgn = 0;
+ addEntry(row, col, c);
+ coeffNewSgn = c.sgn();
+ }else{
+ Entry& e = d_entries.get(id);
+ T& t = e.getCoefficient();
+ coeffOldSgn = t.sgn();
+ t += c;
+ coeffNewSgn = t.sgn();
+ }
+
+ if(coeffOldSgn != coeffNewSgn){
+ cb.update(row, col, coeffOldSgn, coeffNewSgn);
+ }
+ if(coeffNewSgn == 0){
+ removeEntry(id);
+ }
+ }
+
void removeRow(RowIndex rid){
RowIterator i = getRow(rid).begin();
RowIterator i_end = getRow(rid).end();
@@ -822,102 +964,6 @@ protected:
};/* class Matrix<T> */
-
-/**
- * A Tableau is a Rational matrix that keeps its rows in solved form.
- * Each row has a basic variable with coefficient -1 that is solved.
- * Tableau is optimized for pivoting.
- * The tableau should only be updated via pivot calls.
- */
-class Tableau : public Matrix<Rational> {
-public:
-private:
- typedef DenseMap<RowIndex> BasicToRowMap;
- // Set of all of the basic variables in the tableau.
- // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
- BasicToRowMap d_basic2RowIndex;
-
- // RowIndex |-> Basic Variable
- typedef DenseMap<ArithVar> RowIndexToBasicMap;
- RowIndexToBasicMap d_rowIndex2basic;
-
-public:
-
- Tableau() : Matrix<Rational>(Rational(0)) {}
-
- typedef Matrix<Rational>::ColIterator ColIterator;
- typedef Matrix<Rational>::RowIterator RowIterator;
- typedef BasicToRowMap::const_iterator BasicIterator;
-
- typedef MatrixEntry<Rational> Entry;
-
- bool isBasic(ArithVar v) const{
- return d_basic2RowIndex.isKey(v);
- }
-
- void debugPrintIsBasic(ArithVar v) const {
- if(isBasic(v)){
- Warning() << v << " is basic." << std::endl;
- }else{
- Warning() << v << " is non-basic." << std::endl;
- }
- }
-
- BasicIterator beginBasic() const {
- return d_basic2RowIndex.begin();
- }
- BasicIterator endBasic() const {
- return d_basic2RowIndex.end();
- }
-
- RowIndex basicToRowIndex(ArithVar x) const {
- return d_basic2RowIndex[x];
- }
-
- ArithVar rowIndexToBasic(RowIndex rid) const {
- Assert(rid < d_rowIndex2basic.size());
- return d_rowIndex2basic[rid];
- }
-
- ColIterator colIterator(ArithVar x) const {
- return getColumn(x).begin();
- }
-
- RowIterator basicRowIterator(ArithVar basic) const {
- return getRow(basicToRowIndex(basic)).begin();
- }
-
- /**
- * Adds a row to the tableau.
- * The new row is equivalent to:
- * basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
- * preconditions:
- * basicVar is already declared to be basic
- * basicVar does not have a row associated with it in the tableau.
- *
- * Note: each variables[i] does not have to be non-basic.
- * Pivoting will be mimicked if it is basic.
- */
- void addRow(ArithVar basicVar,
- const std::vector<Rational>& coeffs,
- const std::vector<ArithVar>& variables);
-
- /**
- * preconditions:
- * x_r is basic,
- * x_s is non-basic, and
- * a_rs != 0.
- */
- void pivot(ArithVar basicOld, ArithVar basicNew);
-
- void removeBasicRow(ArithVar basic);
-
-private:
- /* Changes the basic variable on the row for basicOld to basicNew. */
- void rowPivot(ArithVar basicOld, ArithVar basicNew);
-
-};/* class Tableau */
-
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/options b/src/theory/arith/options
index 87278fa61..977d6cb32 100644
--- a/src/theory/arith/options
+++ b/src/theory/arith/options
@@ -25,7 +25,7 @@ option arithHeuristicPivots --heuristic-pivots=N int16_t :default 0 :read-write
expert-option arithStandardCheckVarOrderPivots --standard-effort-variable-order-pivots=N int16_t :default -1 :read-write
limits the number of pivots in a single invocation of check() at a non-full effort level using Bland's pivot rule
-option arithHeuristicPivotRule --heuristic-pivot-rule=RULE ArithHeuristicPivotRule :handler CVC4::theory::arith::stringToArithHeuristicPivotRule :default MINIMUM :handler-include "theory/arith/options_handlers.h" :include "theory/arith/arith_heuristic_pivot_rule.h"
+option arithErrorSelectionRule --error-selection-rule=RULE ErrorSelectionRule :handler CVC4::theory::arith::stringToErrorSelectionRule :default MINIMUM_AMOUNT :handler-include "theory/arith/options_handlers.h" :include "theory/arith/arith_heuristic_pivot_rule.h"
change the pivot rule for the basic variable (default is 'min', see --pivot-rule help)
# The number of pivots before simplex rechecks every basic variable for a conflict
@@ -51,6 +51,7 @@ option arithRewriteEq --enable-arith-rewrite-equalities/--disable-arith-rewrite-
turns on the preprocessing rewrite turning equalities into a conjunction of inequalities
/turns off the preprocessing rewrite turning equalities into a conjunction of inequalities
+
option arithMLTrick miplib-trick --enable-miplib-trick/--disable-miplib-trick bool :default false
turns on the preprocessing step of attempting to infer bounds on miplib problems
/turns off the preprocessing step of attempting to infer bounds on miplib problems
@@ -58,7 +59,7 @@ option arithMLTrick miplib-trick --enable-miplib-trick/--disable-miplib-trick bo
option arithMLTrickSubstitutions miplib-trick-subs --miplib-trick-subs unsigned :default 1
do substitution for miplib 'tmp' vars if defined in <= N eliminated vars
-option doCutAllBounded --enable-cut-all-bounded/--disable-cut-all-bounded bool :default false :read-write
+option doCutAllBounded --cut-all-bounded bool :default false :read-write
turns on the integer solving step of periodically cutting all integer variables that have both upper and lower bounds
/turns off the integer solving step of periodically cutting all integer variables that have both upper and lower bounds
@@ -68,4 +69,26 @@ option maxCutsInContext --maxCutsInContext unsigned :default 65535
option revertArithModels --revert-arith-models-on-unsat bool :default false
Revert the arithmetic model to a known safe model on unsat if one is cached
+option havePenalties --fc-penalties bool :default false :read-write
+ turns on degenerate pivot penalties
+/ turns off degenerate pivot penalties
+
+option useFC --use-fcsimplex bool :default false :read-write
+ use focusing and converging simplex (FMCAD 2013 submission)
+
+option useSOI --use-soi bool :default false :read-write
+ use sum of infeasibility simplex (FMCAD 2013 submission)
+
+option restrictedPivots --restrict-pivots bool :default true :read-write
+ have a pivot cap for simplex at effort levels below fullEffort.
+
+option collectPivots --collect-pivot-stats bool :default false :read-write
+ collect the pivot history
+
+option fancyFinal --fancy-final bool :default false :read-write
+ Tuning how final check works for really hard problems.
+
+option exportDioDecompositions --dio-decomps bool :default false :read-write
+ Let skolem variables for integer divisibility constraints leak from the dio solver.
+
endmodule
diff --git a/src/theory/arith/options_handlers.h b/src/theory/arith/options_handlers.h
index 1e2ab379a..899a3a7c6 100644
--- a/src/theory/arith/options_handlers.h
+++ b/src/theory/arith/options_handlers.h
@@ -51,16 +51,16 @@ This decides on kind of propagation arithmetic attempts to do during the search.
+both\n\
";
-static const std::string heuristicPivotRulesHelp = "\
-This decides on the rule used by simplex during heuristic rounds\n\
+static const std::string errorSelectionRulesHelp = "\
+This decides on the rule used by simplex during hueristic rounds\n\
for deciding the next basic variable to select.\n\
Heuristic pivot rules available:\n\
+min\n\
The minimum abs() value of the variable's violation of its bound. (default)\n\
-+min-break-ties\n\
- The minimum violation with ties broken by variable order (total)\n\
+max\n\
The maximum violation the bound\n\
++varord\n\
+ The variable order\n\
";
inline ArithUnateLemmaMode stringToArithUnateLemmaMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) {
@@ -99,15 +99,15 @@ inline ArithPropagationMode stringToArithPropagationMode(std::string option, std
}
}
-inline ArithHeuristicPivotRule stringToArithHeuristicPivotRule(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) {
+inline ErrorSelectionRule stringToErrorSelectionRule(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) {
if(optarg == "min") {
- return MINIMUM;
- } else if(optarg == "min-break-ties") {
- return BREAK_TIES;
+ return MINIMUM_AMOUNT;
+ } else if(optarg == "varord") {
+ return VAR_ORDER;
} else if(optarg == "max") {
- return MAXIMUM;
+ return MAXIMUM_AMOUNT;
} else if(optarg == "help") {
- puts(heuristicPivotRulesHelp.c_str());
+ puts(errorSelectionRulesHelp.c_str());
exit(1);
} else {
throw OptionException(std::string("unknown option for --heuristic-pivot-rule: `") +
diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp
index 6bbaa1e5e..695d9df25 100644
--- a/src/theory/arith/partial_model.cpp
+++ b/src/theory/arith/partial_model.cpp
@@ -19,6 +19,7 @@
#include "theory/arith/partial_model.h"
#include "util/output.h"
#include "theory/arith/constraint.h"
+#include "theory/arith/normal_form.h"
using namespace std;
@@ -26,21 +27,164 @@ namespace CVC4 {
namespace theory {
namespace arith {
-ArithPartialModel::ArithPartialModel(context::Context* c, RationalCallBack& deltaComputingFunc)
- : d_mapSize(0),
- d_hasSafeAssignment(),
- d_assignment(),
+ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaComputingFunc)
+ : d_vars(),
d_safeAssignment(),
- d_ubc(c),
- d_lbc(c),
+ d_numberOfVariables(0),
+ d_pool(),
+ d_released(),
+ d_releasedIterator(d_released.begin()),
+ d_nodeToArithVarMap(),
+ d_lbRevertHistory(c, true, LowerBoundCleanUp(this)),
+ d_ubRevertHistory(c, true, UpperBoundCleanUp(this)),
d_deltaIsSafe(false),
d_delta(-1,1),
d_deltaComputingFunc(deltaComputingFunc),
- d_history()
+ d_enqueueingBoundCounts(true)
{ }
+ArithVariables::VarInfo::VarInfo()
+ : d_var(ARITHVAR_SENTINEL),
+ d_assignment(0),
+ d_lb(NullConstraint),
+ d_ub(NullConstraint),
+ d_cmpAssignmentLB(1),
+ d_cmpAssignmentUB(-1),
+ d_pushCount(0),
+ d_node(Node::null()),
+ d_slack(false)
+{ }
+
+void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){
+ Assert(!initialized());
+ Assert(d_lb == NullConstraint);
+ Assert(d_ub == NullConstraint);
+ Assert(d_cmpAssignmentLB > 0);
+ Assert(d_cmpAssignmentUB < 0);
+ d_var = v;
+ d_node = n;
+ d_slack = slack;
+
+ if(d_slack){
+ //The type computation is not quite accurate for Rationals that are
+ //integral.
+ //We'll use the isIntegral check from the polynomial package instead.
+ Polynomial p = Polynomial::parsePolynomial(n);
+ d_type = p.isIntegral() ? ATInteger : ATReal;
+ }else{
+ d_type = nodeToArithType(n);
+ }
+
+ Assert(initialized());
+}
+void ArithVariables::VarInfo::uninitialize(){
+ d_var = ARITHVAR_SENTINEL;
+ d_node = Node::null();
+}
+
+bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundCounts & prev){
+ Assert(initialized());
+ d_assignment = a;
+ int cmpUB = (d_ub == NullConstraint) ? -1 :
+ d_assignment.cmp(d_ub->getValue());
+
+ int cmpLB = (d_lb == NullConstraint) ? 1 :
+ d_assignment.cmp(d_lb->getValue());
+
+ bool lbChanged = cmpLB != d_cmpAssignmentLB &&
+ (cmpLB == 0 || d_cmpAssignmentLB == 0);
+ bool ubChanged = cmpUB != d_cmpAssignmentUB &&
+ (cmpUB == 0 || d_cmpAssignmentUB == 0);
+
+ if(lbChanged || ubChanged){
+ prev = boundCounts();
+ }
+
+ d_cmpAssignmentUB = cmpUB;
+ d_cmpAssignmentLB = cmpLB;
+ return lbChanged || ubChanged;
+}
+
+void ArithVariables::releaseArithVar(ArithVar v){
+ VarInfo& vi = d_vars.get(v);
+ vi.uninitialize();
+
+ if(d_safeAssignment.isKey(v)){
+ d_safeAssignment.remove(v);
+ }
+ if(vi.canBeReclaimed()){
+ d_pool.push_back(v);
+ }else{
+ d_released.push_back(v);
+ }
+}
+
+bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundCounts& prev){
+ Assert(initialized());
+ d_ub = ub;
+ int cmpUB = (d_ub == NullConstraint) ? -1 : d_assignment.cmp(d_ub->getValue());
+ bool ubChanged = cmpUB != d_cmpAssignmentUB &&
+ (cmpUB == 0 || d_cmpAssignmentUB == 0);
+ if(ubChanged){
+ prev = boundCounts();
+ }
+ d_cmpAssignmentUB = cmpUB;
+ return ubChanged;
+}
-const Rational& ArithPartialModel::getDelta(){
+bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundCounts& prev){
+ Assert(initialized());
+ d_lb = lb;
+ int cmpLB = (d_lb == NullConstraint) ? 1 : d_assignment.cmp(d_lb->getValue());
+
+ bool lbChanged = cmpLB != d_cmpAssignmentLB &&
+ (cmpLB == 0 || d_cmpAssignmentLB == 0);
+ if(lbChanged){
+ prev = boundCounts();
+ }
+ d_cmpAssignmentLB = cmpLB;
+ return lbChanged;
+}
+
+void ArithVariables::attemptToReclaimReleased(){
+ std::list<ArithVar>::iterator i_end = d_released.end();
+ for(int iter = 0; iter < 20 && d_releasedIterator != i_end; ++d_releasedIterator){
+ ArithVar v = *d_releasedIterator;
+ VarInfo& vi = d_vars.get(v);
+ if(vi.canBeReclaimed()){
+ d_pool.push_back(v);
+ std::list<ArithVar>::iterator curr = d_releasedIterator;
+ ++d_releasedIterator;
+ d_released.erase(curr);
+ }else{
+ ++d_releasedIterator;
+ }
+ }
+ if(d_releasedIterator == i_end){
+ d_releasedIterator = d_released.begin();
+ }
+}
+
+ArithVar ArithVariables::allocateVariable(){
+ if(d_pool.empty()){
+ attemptToReclaimReleased();
+ }
+ bool reclaim = !d_pool.empty();
+
+ ArithVar varX;
+ if(reclaim){
+ varX = d_pool.back();
+ d_pool.pop_back();
+ }else{
+ varX = d_numberOfVariables;
+ ++d_numberOfVariables;
+ }
+ d_vars.set(varX, VarInfo());
+ return varX;
+}
+
+
+const Rational& ArithVariables::getDelta(){
if(!d_deltaIsSafe){
Rational nextDelta = d_deltaComputingFunc();
setDelta(nextDelta);
@@ -49,7 +193,7 @@ const Rational& ArithPartialModel::getDelta(){
return d_delta;
}
-bool ArithPartialModel::boundsAreEqual(ArithVar x) const{
+bool ArithVariables::boundsAreEqual(ArithVar x) const{
if(hasLowerBound(x) && hasUpperBound(x)){
return getUpperBound(x) == getLowerBound(x);
}else{
@@ -57,102 +201,108 @@ bool ArithPartialModel::boundsAreEqual(ArithVar x) const{
}
}
-void ArithPartialModel::setAssignment(ArithVar x, const DeltaRational& r){
- Debug("partial_model") << "pm: updating the assignment to" << x
- << " now " << r <<endl;
- if(!d_hasSafeAssignment[x]){
- d_safeAssignment[x] = d_assignment[x];
- d_hasSafeAssignment[x] = true;
- d_history.push_back(x);
+void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){
+ Debug("partial_model") << "pm: updating the assignment to" << x
+ << " now " << r <<endl;
+ VarInfo& vi = d_vars.get(x);
+ if(!d_safeAssignment.isKey(x)){
+ d_safeAssignment.set(x, vi.d_assignment);
}
invalidateDelta();
- d_assignment[x] = r;
+
+ BoundCounts prev;
+ if(vi.setAssignment(r, prev)){
+ addToBoundQueue(x, prev);
+ }
}
-void ArithPartialModel::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
- Debug("partial_model") << "pm: updating the assignment to" << x
- << " now " << r <<endl;
+
+void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
+ Debug("partial_model") << "pm: updating the assignment to" << x
+ << " now " << r <<endl;
if(safe == r){
- if(d_hasSafeAssignment[x]){
- d_safeAssignment[x] = safe;
+ if(d_safeAssignment.isKey(x)){
+ d_safeAssignment.remove(x);
}
}else{
- d_safeAssignment[x] = safe;
-
- if(!d_hasSafeAssignment[x]){
- d_hasSafeAssignment[x] = true;
- d_history.push_back(x);
- }
+ d_safeAssignment.set(x, safe);
}
invalidateDelta();
- d_assignment[x] = r;
+ VarInfo& vi = d_vars.get(x);
+ BoundCounts prev;
+ if(vi.setAssignment(r, prev)){
+ addToBoundQueue(x, prev);
+ }
}
-bool ArithPartialModel::equalSizes(){
- return
- d_mapSize == d_hasSafeAssignment.size() &&
- d_mapSize == d_assignment.size() &&
- d_mapSize == d_safeAssignment.size() &&
- d_mapSize == d_ubc.size() &&
- d_mapSize == d_lbc.size();
+void ArithVariables::initialize(ArithVar x, Node n, bool slack){
+ VarInfo& vi = d_vars.get(x);
+ vi.initialize(x, n, slack);
+ d_nodeToArithVarMap[n] = x;
}
-void ArithPartialModel::initialize(ArithVar x, const DeltaRational& r){
- Assert(x == d_mapSize);
- Assert(equalSizes());
- ++d_mapSize;
+ArithVar ArithVariables::allocate(Node n, bool slack){
+ ArithVar v = allocateVariable();
+ initialize(v, n, slack);
+ return v;
+}
- d_hasSafeAssignment.push_back( false );
- // Is wirth mentioning that this is not strictly necessary, but this maintains the internal invariant
- // that when d_assignment is set this gets set.
- invalidateDelta();
- d_assignment.push_back( r );
- d_safeAssignment.push_back( DeltaRational(0) );
+// void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
+// Assert(x == d_mapSize);
+// Assert(equalSizes());
+// ++d_mapSize;
- d_ubc.push_back(NullConstraint);
- d_lbc.push_back(NullConstraint);
-}
+// // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
+// // that when d_assignment is set this gets set.
+// invalidateDelta();
+// d_assignment.push_back( r );
+
+// d_boundRel.push_back(BetweenBounds);
+
+// d_ubc.push_back(NullConstraint);
+// d_lbc.push_back(NullConstraint);
+// }
/** Must know that the bound exists both calling this! */
-const DeltaRational& ArithPartialModel::getUpperBound(ArithVar x) const {
+const DeltaRational& ArithVariables::getUpperBound(ArithVar x) const {
Assert(inMaps(x));
Assert(hasUpperBound(x));
return getUpperBoundConstraint(x)->getValue();
}
-const DeltaRational& ArithPartialModel::getLowerBound(ArithVar x) const {
+const DeltaRational& ArithVariables::getLowerBound(ArithVar x) const {
Assert(inMaps(x));
Assert(hasLowerBound(x));
return getLowerBoundConstraint(x)->getValue();
}
-const DeltaRational& ArithPartialModel::getSafeAssignment(ArithVar x) const{
+const DeltaRational& ArithVariables::getSafeAssignment(ArithVar x) const{
Assert(inMaps(x));
- if(d_hasSafeAssignment[x]){
+ if(d_safeAssignment.isKey(x)){
return d_safeAssignment[x];
}else{
- return d_assignment[x];
+ return d_vars[x].d_assignment;
}
}
-const DeltaRational& ArithPartialModel::getAssignment(ArithVar x, bool safe) const{
+const DeltaRational& ArithVariables::getAssignment(ArithVar x, bool safe) const{
Assert(inMaps(x));
- if(safe && d_hasSafeAssignment[x]){
+ if(safe && d_safeAssignment.isKey(x)){
return d_safeAssignment[x];
}else{
- return d_assignment[x];
+ return d_vars[x].d_assignment;
}
}
-const DeltaRational& ArithPartialModel::getAssignment(ArithVar x) const{
+const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{
Assert(inMaps(x));
- return d_assignment[x];
+ return d_vars[x].d_assignment;
}
-void ArithPartialModel::setLowerBoundConstraint(Constraint c){
+void ArithVariables::setLowerBoundConstraint(Constraint c){
AssertArgument(c != NullConstraint, "Cannot set a lower bound to NullConstraint.");
AssertArgument(c->isEquality() || c->isLowerBound(),
"Constraint type must be set to an equality or UpperBound.");
@@ -162,10 +312,15 @@ void ArithPartialModel::setLowerBoundConstraint(Constraint c){
Assert(greaterThanLowerBound(x, c->getValue()));
invalidateDelta();
- d_lbc.set(x, c);
+ VarInfo& vi = d_vars.get(x);
+ pushLowerBound(vi);
+ BoundCounts prev;
+ if(vi.setLowerBound(c, prev)){
+ addToBoundQueue(x, prev);
+ }
}
-void ArithPartialModel::setUpperBoundConstraint(Constraint c){
+void ArithVariables::setUpperBoundConstraint(Constraint c){
AssertArgument(c != NullConstraint, "Cannot set a upper bound to NullConstraint.");
AssertArgument(c->isEquality() || c->isUpperBound(),
"Constraint type must be set to an equality or UpperBound.");
@@ -176,29 +331,40 @@ void ArithPartialModel::setUpperBoundConstraint(Constraint c){
Assert(lessThanUpperBound(x, c->getValue()));
invalidateDelta();
- d_ubc.set(x, c);
+ VarInfo& vi = d_vars.get(x);
+ pushUpperBound(vi);
+ BoundCounts prev;
+ if(vi.setUpperBound(c, prev)){
+ addToBoundQueue(x, prev);
+ }
}
-void ArithPartialModel::forceRelaxLowerBound(ArithVar v){
- AssertArgument(inMaps(v), "Calling forceRelaxLowerBound on a variable that is not properly setup.");
- AssertArgument(hasLowerBound(v), "Calling forceRelaxLowerBound on a variable without a lowerbound.");
+// void ArithVariables::forceRelaxLowerBound(ArithVar v){
+// AssertArgument(inMaps(v), "Calling forceRelaxLowerBound on a variable that is not properly setup.");
+// AssertArgument(hasLowerBound(v), "Calling forceRelaxLowerBound on a variable without a lowerbound.");
- Debug("partial_model") << "forceRelaxLowerBound(" << v << ") dropping :" << getLowerBoundConstraint(v) << endl;
+// Debug("partial_model") << "forceRelaxLowerBound(" << v << ") dropping :" << getLowerBoundConstraint(v) << endl;
- d_lbc.set(v, NullConstraint);
-}
+// invalidateDelta();
+// VarInfo& vi = d_vars.get(v);
+// pushLowerBound(vi);
+// vi.setLowerBound(NullConstraint);
+// }
-void ArithPartialModel::forceRelaxUpperBound(ArithVar v){
- AssertArgument(inMaps(v), "Calling forceRelaxUpperBound on a variable that is not properly setup.");
- AssertArgument(hasUpperBound(v), "Calling forceRelaxUpperBound on a variable without an upper bound.");
+// void ArithVariables::forceRelaxUpperBound(ArithVar v){
+// AssertArgument(inMaps(v), "Calling forceRelaxUpperBound on a variable that is not properly setup.");
+// AssertArgument(hasUpperBound(v), "Calling forceRelaxUpperBound on a variable without an upper bound.");
- Debug("partial_model") << "forceRelaxUpperBound(" << v << ") dropping :" << getUpperBoundConstraint(v) << endl;
+// Debug("partial_model") << "forceRelaxUpperBound(" << v << ") dropping :" << getUpperBoundConstraint(v) << endl;
- d_ubc.set(v, NullConstraint);
-}
+// invalidateDelta();
+// VarInfo& vi = d_vars.get(v);
+// pushUpperBound(vi);
+// vi.setUpperBound(NullConstraint);
+// }
-int ArithPartialModel::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
+int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
if(!hasLowerBound(x)){
// l = -\intfy
// ? c < -\infty |- _|_
@@ -208,7 +374,7 @@ int ArithPartialModel::cmpToLowerBound(ArithVar x, const DeltaRational& c) const
}
}
-int ArithPartialModel::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
+int ArithVariables::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
if(!hasUpperBound(x)){
//u = \intfy
// ? c > \infty |- _|_
@@ -218,14 +384,14 @@ int ArithPartialModel::cmpToUpperBound(ArithVar x, const DeltaRational& c) const
}
}
-bool ArithPartialModel::equalsLowerBound(ArithVar x, const DeltaRational& c){
+bool ArithVariables::equalsLowerBound(ArithVar x, const DeltaRational& c){
if(!hasLowerBound(x)){
return false;
}else{
return c == getLowerBound(x);
}
}
-bool ArithPartialModel::equalsUpperBound(ArithVar x, const DeltaRational& c){
+bool ArithVariables::equalsUpperBound(ArithVar x, const DeltaRational& c){
if(!hasUpperBound(x)){
return false;
}else{
@@ -233,111 +399,210 @@ bool ArithPartialModel::equalsUpperBound(ArithVar x, const DeltaRational& c){
}
}
-bool ArithPartialModel::hasEitherBound(ArithVar x) const{
+bool ArithVariables::hasEitherBound(ArithVar x) const{
return hasLowerBound(x) || hasUpperBound(x);
}
-bool ArithPartialModel::strictlyBelowUpperBound(ArithVar x) const{
- Assert(inMaps(x));
- if(!hasUpperBound(x)){ // u = \infty
- return true;
- }else{
- return d_assignment[x] < getUpperBound(x);
- }
+bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentUB < 0;
+ // if(!hasUpperBound(x)){ // u = \infty
+ // return true;
+ // }else{
+ // return d_assignment[x] < getUpperBound(x);
+ // }
}
-bool ArithPartialModel::strictlyAboveLowerBound(ArithVar x) const{
- Assert(inMaps(x));
- if(!hasLowerBound(x)){ // l = -\infty
- return true;
- }else{
- return getLowerBound(x) < d_assignment[x];
- }
+bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentLB > 0;
+ // if(!hasLowerBound(x)){ // l = -\infty
+ // return true;
+ // }else{
+ // return getLowerBound(x) < d_assignment[x];
+ // }
}
-bool ArithPartialModel::assignmentIsConsistent(ArithVar x) const{
- const DeltaRational& beta = getAssignment(x);
+bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
+ return
+ d_vars[x].d_cmpAssignmentLB >= 0 &&
+ d_vars[x].d_cmpAssignmentUB <= 0;
+ // const DeltaRational& beta = getAssignment(x);
- //l_i <= beta(x_i) <= u_i
- return greaterThanLowerBound(x,beta) && lessThanUpperBound(x,beta);
+ // //l_i <= beta(x_i) <= u_i
+ // return greaterThanLowerBound(x,beta) && lessThanUpperBound(x,beta);
}
-void ArithPartialModel::clearSafeAssignments(bool revert){
+void ArithVariables::clearSafeAssignments(bool revert){
- for(HistoryList::iterator i = d_history.begin(); i != d_history.end(); ++i){
- ArithVar x = *i;
- Assert(d_hasSafeAssignment[x]);
- d_hasSafeAssignment[x] = false;
+ if(revert && !d_safeAssignment.empty()){
+ invalidateDelta();
+ }
+ while(!d_safeAssignment.empty()){
+ ArithVar atBack = d_safeAssignment.back();
if(revert){
- d_assignment[x] = d_safeAssignment[x];
+ VarInfo& vi = d_vars.get(atBack);
+ BoundCounts prev;
+ if(vi.setAssignment(d_safeAssignment[atBack], prev)){
+ addToBoundQueue(atBack, prev);
+ }
}
+ d_safeAssignment.pop_back();
}
-
- if(revert && !d_history.empty()){
- invalidateDelta();
- }
-
- d_history.clear();
}
-void ArithPartialModel::revertAssignmentChanges(){
+void ArithVariables::revertAssignmentChanges(){
clearSafeAssignments(true);
}
-void ArithPartialModel::commitAssignmentChanges(){
+void ArithVariables::commitAssignmentChanges(){
clearSafeAssignments(false);
}
-void ArithPartialModel::printModel(ArithVar x){
- Debug("model") << "model" << x << ":"<< getAssignment(x) << " ";
+void ArithVariables::printEntireModel(std::ostream& out) const{
+ out << "---Printing Model ---" << std::endl;
+ for(var_iterator i = var_begin(), iend = var_end(); i != iend; ++i){
+ printModel(*i, out);
+ }
+ out << "---Done Model ---" << std::endl;
+}
+
+void ArithVariables::printModel(ArithVar x, std::ostream& out) const{
+ out << "model" << x << ": "
+ << asNode(x) << " "
+ << getAssignment(x) << " ";
if(!hasLowerBound(x)){
- Debug("model") << "no lb ";
+ out << "no lb ";
}else{
- Debug("model") << getLowerBound(x) << " ";
- Debug("model") << getLowerBoundConstraint(x) << " ";
+ out << getLowerBound(x) << " ";
+ out << getLowerBoundConstraint(x) << " ";
}
if(!hasUpperBound(x)){
- Debug("model") << "no ub ";
+ out << "no ub ";
}else{
- Debug("model") << getUpperBound(x) << " ";
- Debug("model") << getUpperBoundConstraint(x) << " ";
+ out << getUpperBound(x) << " ";
+ out << getUpperBoundConstraint(x) << " ";
}
- Debug("model") << endl;
+ out << endl;
}
-// void ArithPartialModel::deltaIsSmallerThan(const DeltaRational& l, const DeltaRational& u){
-// const Rational& c = l.getNoninfinitesimalPart();
-// const Rational& k = l.getInfinitesimalPart();
-// const Rational& d = u.getNoninfinitesimalPart();
-// const Rational& h = u.getInfinitesimalPart();
+void ArithVariables::printModel(ArithVar x) const{
+ printModel(x, Debug("model"));
+}
-// if(c < d && k > h){
-// Rational ep = (d-c)/(k-h);
-// if(ep < d_delta){
-// d_delta = ep;
+// BoundRelationship ArithVariables::boundRelationship(Constraint lb, const DeltaRational& d, Constraint ub){
+// if(lb == NullConstraint && ub == NullConstraint){
+// return BetweenBounds;
+// }else if(lb == NullConstraint){
+// int cmp = d.cmp(ub->getValue());
+// return (cmp < 0) ? BetweenBounds :
+// (cmp == 0 ? AtUpperBound : AboveUpperBound);
+// }else if(ub == NullConstraint){
+// int cmp = d.cmp(lb->getValue());
+// return (cmp > 0) ? BetweenBounds :
+// (cmp == 0 ? AtLowerBound : BelowLowerBound);
+// }else{
+// Assert(lb->getValue() <= ub->getValue());
+// int cmpToLB = d.cmp(lb->getValue());
+// if(cmpToLB < 0){
+// return BelowLowerBound;
+// }else if(cmpToLB == 0){
+// return (d == ub->getValue()) ? AtBothBounds : AtLowerBound;
+// }else{
+// // d > 0
+// int cmpToUB = d.cmp(ub->getValue());
+// return (cmpToUB > 0) ? BetweenBounds :
+// (cmpToUB == 0 ? AtLowerBound : BelowLowerBound);
// }
// }
// }
-// void ArithPartialModel::computeDelta(const Rational& init){
-// Assert(!d_deltaIsSafe);
-// d_delta = init;
+void ArithVariables::pushUpperBound(VarInfo& vi){
+ ++vi.d_pushCount;
+ d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
+}
+void ArithVariables::pushLowerBound(VarInfo& vi){
+ ++vi.d_pushCount;
+ d_lbRevertHistory.push_back(make_pair(vi.d_var, vi.d_lb));
+}
+
+void ArithVariables::popUpperBound(AVCPair* c){
+ ArithVar x = c->first;
+ VarInfo& vi = d_vars.get(x);
+ BoundCounts prev;
+ if(vi.setUpperBound(c->second, prev)){
+ addToBoundQueue(x, prev);
+ }
+ --vi.d_pushCount;
+}
+
+void ArithVariables::popLowerBound(AVCPair* c){
+ ArithVar x = c->first;
+ VarInfo& vi = d_vars.get(x);
+ BoundCounts prev;
+ if(vi.setLowerBound(c->second, prev)){
+ addToBoundQueue(x, prev);
+ }
+ --vi.d_pushCount;
+}
-// for(ArithVar x = 0; x < d_mapSize; ++x){
-// const DeltaRational& a = getAssignment(x);
-// if(hasLowerBound(x)){
-// const DeltaRational& l = getLowerBound(x);
-// deltaIsSmallerThan(l,a);
+/* To ensure that the final deallocation stuff works,
+ * we need to ensure that we need to not reference any of the other vectors
+ */
+// void ArithVariables::relaxUpperBound(Constraint curr, Constraint afterPop){
+// BoundRelation next = Undefined;
+// switch(d_boundRel[x]){
+// case BelowLowerBound:
+// case BetweenBounds:
+// case AtLowerBound:
+// return; // do nothing
+// case AtUpperBound:
+// if(afterPop != NullConstraint
+// && curr->getValue() == afterPop->getValue()){
+// next = AtUpperBound;
+// }else{
+// next = BetweenBounds;
+// }
+// break;
+// case AtBothBounds:
+// if(afterPop != NullConstraint
+// && curr->getValue() == afterPop->getValue()){
+// next = AtUpperBound;
+// }else{
+// next = AtLowerBound;
// }
-// if(hasUpperBound(x)){
-// const DeltaRational& u = getUpperBound(x);
-// deltaIsSmallerThan(a,u);
+// break;
+// case AboveUpperBound:
+// if(afterPop == NullConstraint){
+// next = BetweenBounds;
+// }else{
+// int cmp = d_assignment[x].cmp(afterPop->getValue());
+// next = (cmp < 0) ? BetweenBounds :
+// (cmp == 0) ? AtUpperBound : AboveUpperBound;
// }
+// break;
+// default:
+// Unreachable();
// }
-// d_deltaIsSafe = true;
+// d_boundRel[x] = next;
// }
+
+
+// void ArithVariables::relaxLowerBound(Constraint curr, Constraint afterPop){
+// // TODO this can be optimized using the automata induced by d_boundRel and
+// // the knowledge that lb <= ub
+// ArithVar x = curr->getVariable();
+// d_boundRel[x] = boundRelationship(afterPop, d_assignment[x], d_ubc[x]);
+// }
+
+void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){
+ d_pm->popLowerBound(p);
+}
+
+void ArithVariables::UpperBoundCleanUp::operator()(AVCPair* p){
+ d_pm->popUpperBound(p);
+}
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h
index 820b1b909..dcfe97079 100644
--- a/src/theory/arith/partial_model.h
+++ b/src/theory/arith/partial_model.h
@@ -20,53 +20,216 @@
#include "expr/node.h"
#include "context/context.h"
-#include "context/cdvector.h"
-#include "context/cdo.h"
+#include "context/cdlist.h"
+
#include "theory/arith/arithvar.h"
+#include "theory/arith/arith_utilities.h"
#include "theory/arith/delta_rational.h"
#include "theory/arith/constraint_forward.h"
+#include "theory/arith/callbacks.h"
+#include "theory/arith/bound_counts.h"
#include <vector>
+#include <list>
-#ifndef __CVC4__THEORY__ARITH__PARTIAL_MODEL_H
-#define __CVC4__THEORY__ARITH__PARTIAL_MODEL_H
+#pragma once
namespace CVC4 {
namespace theory {
namespace arith {
-class ArithPartialModel {
+
+
+class ArithVariables {
private:
+ class VarInfo {
+ friend class ArithVariables;
+ ArithVar d_var;
+
+ DeltaRational d_assignment;
+ Constraint d_lb;
+ Constraint d_ub;
+ int d_cmpAssignmentLB;
+ int d_cmpAssignmentUB;
+
+ unsigned d_pushCount;
+ ArithType d_type;
+ Node d_node;
+ bool d_slack;
+
+ public:
+ VarInfo();
+
+ bool setAssignment(const DeltaRational& r, BoundCounts& prev);
+ bool setLowerBound(Constraint c, BoundCounts& prev);
+ bool setUpperBound(Constraint c, BoundCounts& prev);
+
+ bool initialized() const {
+ return d_var != ARITHVAR_SENTINEL;
+ }
+ void initialize(ArithVar v, Node n, bool slack);
+ void uninitialize();
+
+ bool canBeReclaimed() const{
+ return d_pushCount == 0;
+ }
+
+ BoundCounts boundCounts() const {
+ uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
+ uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
+ return BoundCounts(lbIndc, ubIndc);
+ }
+ };
+
+ //Maps from ArithVar -> VarInfo
+ typedef DenseMap<VarInfo> VarInfoVec;
+ VarInfoVec d_vars;
+
+ // Partial Map from Arithvar -> PreviousAssignment
+ DenseMap<DeltaRational> d_safeAssignment;
+
+ // if d_vars.isKey(x), then x < d_numberOfVariables
+ ArithVar d_numberOfVariables;
+
+ /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */
+ // Everything in the pool is fair game.
+ // There must be NO outstanding assertions
+ std::vector<ArithVar> d_pool;
+ std::list<ArithVar> d_released;
+ std::list<ArithVar>::iterator d_releasedIterator;
+
+ // Reverse Map from Node to ArithVar
+ // Inverse of d_vars[x].d_node
+ NodeToArithVarMap d_nodeToArithVarMap;
+
+ DenseMap<BoundCounts> d_atBoundsQueue;
+
+ public:
+
+ inline ArithVar getNumberOfVariables() const {
+ return d_numberOfVariables;
+ }
+
+ inline bool hasArithVar(TNode x) const {
+ return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
+ }
- unsigned d_mapSize;
+ inline bool hasNode(ArithVar a) const {
+ return d_vars.isKey(a);
+ }
- //Maps from ArithVar -> T
- std::vector<bool> d_hasSafeAssignment;
- std::vector<DeltaRational> d_assignment;
- std::vector<DeltaRational> d_safeAssignment;
+ inline ArithVar asArithVar(TNode x) const{
+ Assert(hasArithVar(x));
+ Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL);
+ return (d_nodeToArithVarMap.find(x))->second;
+ }
- context::CDVector<Constraint> d_ubc;
- context::CDVector<Constraint> d_lbc;
+
+ inline Node asNode(ArithVar a) const{
+ Assert(hasNode(a));
+ return d_vars[a].d_node;
+ }
+
+ ArithVar allocateVariable();
+
+ class var_iterator {
+ private:
+ const VarInfoVec* d_vars;
+ VarInfoVec::const_iterator d_wrapped;
+ public:
+ var_iterator(){}
+ var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci)
+ : d_vars(vars), d_wrapped(ci)
+ {
+ nextInitialized();
+ }
+
+ var_iterator& operator++(){
+ ++d_wrapped;
+ nextInitialized();
+ return *this;
+ }
+ bool operator==(const var_iterator& other) const{
+ return d_wrapped == other.d_wrapped;
+ }
+ bool operator!=(const var_iterator& other) const{
+ return d_wrapped != other.d_wrapped;
+ }
+ ArithVar operator*() const{
+ return *d_wrapped;
+ }
+ private:
+ void nextInitialized(){
+ VarInfoVec::const_iterator end = d_vars->end();
+ while(d_wrapped != end &&
+ !((*d_vars)[*d_wrapped].initialized())){
+ ++d_wrapped;
+ }
+ }
+ };
+ var_iterator var_begin() const {
+ return var_iterator(&d_vars, d_vars.begin());
+ }
+
+ var_iterator var_end() const {
+ return var_iterator(&d_vars, d_vars.end());
+ }
+
+
+ bool canBeReleased(ArithVar v) const;
+ void releaseArithVar(ArithVar v);
+ void attemptToReclaimReleased();
+
+ bool isInteger(ArithVar x) const {
+ return d_vars[x].d_type >= ATInteger;
+ }
+ bool isSlack(ArithVar x) const {
+ return d_vars[x].d_slack;
+ }
+ private:
+
+ typedef std::pair<ArithVar, Constraint> AVCPair;
+ class LowerBoundCleanUp {
+ private:
+ ArithVariables* d_pm;
+ public:
+ LowerBoundCleanUp(ArithVariables* pm) : d_pm(pm) {}
+ void operator()(AVCPair* restore);
+ };
+
+ class UpperBoundCleanUp {
+ private:
+ ArithVariables* d_pm;
+ public:
+ UpperBoundCleanUp(ArithVariables* pm) : d_pm(pm) {}
+ void operator()(AVCPair* restore);
+ };
+
+ typedef context::CDList<AVCPair, LowerBoundCleanUp> LBReverts;
+ LBReverts d_lbRevertHistory;
+
+ typedef context::CDList<AVCPair, UpperBoundCleanUp> UBReverts;
+ UBReverts d_ubRevertHistory;
+
+ void pushUpperBound(VarInfo&);
+ void popUpperBound(AVCPair*);
+ void pushLowerBound(VarInfo&);
+ void popLowerBound(AVCPair*);
// This is true when setDelta() is called, until invalidateDelta is called
bool d_deltaIsSafe;
// Cache of a value of delta to ensure a total order.
Rational d_delta;
// Function to call if the value of delta needs to be recomputed.
- RationalCallBack& d_deltaComputingFunc;
-
- /**
- * List contains all of the variables that have an unsafe assignment.
- */
- typedef std::vector<ArithVar> HistoryList;
- HistoryList d_history;
+ DeltaComputeCallback d_deltaComputingFunc;
+ bool d_enqueueingBoundCounts;
public:
- ArithPartialModel(context::Context* c, RationalCallBack& deltaComputation);
+ ArithVariables(context::Context* c, DeltaComputeCallback deltaComputation);
/**
* This sets the lower bound for a variable in the current context.
@@ -82,11 +245,11 @@ public:
/** Returns the constraint for the upper bound of a variable. */
inline Constraint getUpperBoundConstraint(ArithVar x) const{
- return d_ubc[x];
+ return d_vars[x].d_ub;
}
/** Returns the constraint for the lower bound of a variable. */
inline Constraint getLowerBoundConstraint(ArithVar x) const{
- return d_lbc[x];
+ return d_vars[x].d_lb;
}
/**
@@ -94,17 +257,20 @@ public:
* This is done by forcing the lower bound to be NullConstraint.
* This is an expert only operation! (See primal simplex for an example.)
*/
- void forceRelaxLowerBound(ArithVar x);
+ //void forceRelaxLowerBound(ArithVar x);
/**
* This forces the upper bound for a variable to be relaxed in the current context.
* This is done by forcing the upper bound to be NullConstraint.
* This is an expert only operation! (See primal simplex for an example.)
*/
- void forceRelaxUpperBound(ArithVar x);
+ //void forceRelaxUpperBound(ArithVar x);
/* Initializes a variable to a safe value.*/
- void initialize(ArithVar x, const DeltaRational& r);
+ //void initialize(ArithVar x, const DeltaRational& r);
+ void initialize(ArithVar x, Node n, bool slack);
+
+ ArithVar allocate(Node n, bool slack = false);
/* Gets the last assignment to a variable that is known to be consistent. */
const DeltaRational& getSafeAssignment(ArithVar x) const;
@@ -187,20 +353,31 @@ public:
return cmpToUpperBound(x, c) >= 0;
}
+ inline int cmpAssignmentLowerBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentLB;
+ }
+ inline int cmpAssignmentUpperBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentUB;
+ }
+
+ inline BoundCounts boundCounts(ArithVar x) const {
+ return d_vars[x].boundCounts();
+ }
bool strictlyBelowUpperBound(ArithVar x) const;
bool strictlyAboveLowerBound(ArithVar x) const;
bool assignmentIsConsistent(ArithVar x) const;
- void printModel(ArithVar x);
+ void printModel(ArithVar x, std::ostream& out) const;
+ void printModel(ArithVar x) const;
/** returns true iff x has both a lower and upper bound. */
bool hasEitherBound(ArithVar x) const;
inline bool hasLowerBound(ArithVar x) const{
- return d_lbc[x] != NullConstraint;
+ return d_vars[x].d_lb != NullConstraint;
}
inline bool hasUpperBound(ArithVar x) const{
- return d_ubc[x] != NullConstraint;
+ return d_vars[x].d_ub != NullConstraint;
}
const Rational& getDelta();
@@ -214,6 +391,41 @@ public:
d_deltaIsSafe = true;
}
+ // inline bool initialized(ArithVar x) const {
+ // return d_vars[x].initialized();
+ // }
+
+ void addToBoundQueue(ArithVar v, BoundCounts prev){
+ if(d_enqueueingBoundCounts && !d_atBoundsQueue.isKey(v)){
+ d_atBoundsQueue.set(v, prev);
+ }
+ }
+
+ BoundCounts oldBoundCounts(ArithVar v) const {
+ if(d_atBoundsQueue.isKey(v)){
+ return d_atBoundsQueue[v];
+ }else{
+ return boundCounts(v);
+ }
+ }
+
+ void startQueueingAtBoundQueue(){ d_enqueueingBoundCounts = true; }
+ void stopQueueingAtBoundQueue(){ d_enqueueingBoundCounts = false; }
+
+ void processAtBoundQueue(BoundCountsCallback& changed){
+ while(!d_atBoundsQueue.empty()){
+ ArithVar v = d_atBoundsQueue.back();
+ BoundCounts prev = d_atBoundsQueue[v];
+ d_atBoundsQueue.pop_back();
+ BoundCounts curr = boundCounts(v);
+ if(prev != curr){
+ changed(v, prev);
+ }
+ }
+ }
+
+ void printEntireModel(std::ostream& out) const;
+
private:
/**
@@ -222,19 +434,16 @@ private:
*/
void clearSafeAssignments(bool revert);
- bool equalSizes();
+ bool debugEqualSizes();
bool inMaps(ArithVar x) const{
- return x < d_mapSize;
+ return x < getNumberOfVariables();
}
-};/* class ArithPartialModel */
+};/* class ArithVariables */
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
-
-
-#endif /* __CVC4__THEORY__ARITH__PARTIAL_MODEL_H */
diff --git a/src/theory/arith/pure_update_simplex.cpp b/src/theory/arith/pure_update_simplex.cpp
new file mode 100644
index 000000000..9b3edfa6f
--- /dev/null
+++ b/src/theory/arith/pure_update_simplex.cpp
@@ -0,0 +1,261 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/pure_update_simplex.h"
+#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+PureUpdateSimplexDecisionProcedure::PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
+{ }
+
+Result::Sat PureUpdateSimplexDecisionProcedure::findModel(bool exactResult){
+ Assert(d_conflictVariables.empty());
+
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Debug("arith::findModel") << "puFindModel("<< instance <<") "
+ << "trivial" << endl;
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+ d_errorSet.setSelectionRule(VAR_ORDER);
+
+ if(processSignals()){
+ d_conflictVariables.purge();
+
+ Debug("arith::findModel") << "puFindModel("<< instance <<") "
+ << "early conflict" << endl;
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Debug("arith::findModel") << "puFindModel("<< instance <<") "
+ << "fixed itself" << endl;
+ Assert(!d_errorSet.moreSignals());
+ return Result::SAT;
+ }
+
+ Debug("arith::findModel") << "puFindModel(" << instance <<") "
+ << "start non-trivial" << endl;
+
+ static const bool verbose = false;
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ if(result == Result::SAT_UNKNOWN){
+ if(attemptPureUpdates()){
+ result = Result::UNSAT;
+ }
+ if(result == Result::UNSAT){
+ ++(d_statistics.d_pureUpdateFoundUnsat);
+ if(verbose){ Message() << "pure updates found unsat" << endl; }
+ }else if(d_errorSet.errorEmpty()){
+ ++(d_statistics.d_pureUpdateFoundSat);
+ if(verbose){ Message() << "pure updates found model" << endl; }
+ }else{
+ ++(d_statistics.d_pureUpdateMissed);
+ if(verbose){ Message() << "pure updates missed" << endl; }
+ }
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
+ result = Result::SAT;
+ }
+
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+ Debug("arith::findModel") << "end findModel() " << instance << " "
+ << result << endl;
+
+ return result;
+}
+
+
+
+PureUpdateSimplexDecisionProcedure::Statistics::Statistics():
+ d_pureUpdateFoundUnsat("theory::arith::PureUpdate::FoundUnsat", 0),
+ d_pureUpdateFoundSat("theory::arith::PureUpdate::FoundSat", 0),
+ d_pureUpdateMissed("theory::arith::PureUpdate::Missed", 0),
+ d_pureUpdates("theory::arith::PureUpdate::updates", 0),
+ d_pureUpdateDropped("theory::arith::PureUpdate::dropped", 0),
+ d_pureUpdateConflicts("theory::arith::PureUpdate::conflicts", 0),
+ d_foundConflicts("theory::arith::PureUpdate::foundConflicts", 0),
+ d_attemptPureUpdatesTimer("theory::arith::PureUpdate::timer"),
+ d_processSignalsTime("theory::arith::PureUpdate::process::timer"),
+ d_constructionTimer("theory::arith::PureUpdate::construction::timer")
+{
+ StatisticsRegistry::registerStat(&d_pureUpdateFoundUnsat);
+ StatisticsRegistry::registerStat(&d_pureUpdateFoundSat);
+ StatisticsRegistry::registerStat(&d_pureUpdateMissed);
+ StatisticsRegistry::registerStat(&d_pureUpdates);
+ StatisticsRegistry::registerStat(&d_pureUpdateDropped);
+ StatisticsRegistry::registerStat(&d_pureUpdateConflicts);
+
+ StatisticsRegistry::registerStat(&d_foundConflicts);
+
+ StatisticsRegistry::registerStat(&d_attemptPureUpdatesTimer);
+ StatisticsRegistry::registerStat(&d_processSignalsTime);
+ StatisticsRegistry::registerStat(&d_constructionTimer);
+}
+
+PureUpdateSimplexDecisionProcedure::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_pureUpdateFoundUnsat);
+ StatisticsRegistry::unregisterStat(&d_pureUpdateFoundSat);
+ StatisticsRegistry::unregisterStat(&d_pureUpdateMissed);
+ StatisticsRegistry::unregisterStat(&d_pureUpdates);
+ StatisticsRegistry::unregisterStat(&d_pureUpdateDropped);
+ StatisticsRegistry::unregisterStat(&d_pureUpdateConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_foundConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_attemptPureUpdatesTimer);
+ StatisticsRegistry::unregisterStat(&d_processSignalsTime);
+ StatisticsRegistry::unregisterStat(&d_constructionTimer);
+}
+
+bool PureUpdateSimplexDecisionProcedure::attemptPureUpdates(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_attemptPureUpdatesTimer);
+
+ Assert(!d_errorSet.focusEmpty());
+ Assert(d_errorSet.noSignals());
+
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_constructionTimer);
+
+ UpdateInfo proposal;
+ int boundImprovements = 0;
+ int dropped = 0;
+ int computations = 0;
+
+ for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+
+ ArithVar curr = e.getColVar();
+ if(curr == d_focusErrorVar){ continue; }
+
+ int dir = e.getCoefficient().sgn();
+ Assert(dir != 0);
+
+ bool worthwhile = false;
+
+ if( (dir > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+ (dir < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0) ){
+
+ ++computations;
+ proposal = UpdateInfo(curr, dir);
+ d_linEq.computeSafeUpdate(proposal, &LinearEqualityModule::noPreference);
+
+ Assert(proposal.errorsChange() <= 0);
+ Assert(proposal.focusDirection() >= 0);
+
+ worthwhile = proposal.errorsChange() < 0 ||
+ (proposal.focusDirection() > 0 &&
+ d_variables.boundCounts(curr).isZero() &&
+ !proposal.describesPivot());
+
+ Debug("pu::refined")
+ << "pure update proposal "
+ << curr << " "
+ << worthwhile << " "
+ << proposal
+ << endl;
+ }
+ if(worthwhile){
+ Debug("pu") << d_variables.getAssignment(d_focusErrorVar) << endl;
+
+ BoundCounts before = d_variables.boundCounts(curr);
+ DeltaRational newAssignment =
+ d_variables.getAssignment(curr) + proposal.nonbasicDelta();
+ d_linEq.updateTracked(curr, newAssignment);
+ BoundCounts after = d_variables.boundCounts(curr);
+
+ ++d_statistics.d_pureUpdates;
+ ++boundImprovements;
+ Debug("pu") << boundImprovements << ": " << curr
+ << " before: " << before
+ << " after: " << after
+ << e.getCoefficient()
+ << d_variables.getAssignment(d_focusErrorVar) << endl;
+
+ uint32_t prevSize = d_errorSet.errorSize();
+ Assert(d_errorSet.moreSignals());
+ if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); }
+ while(d_errorSet.moreSignals()){
+ ArithVar updated = d_errorSet.topSignal();
+ bool wasInError = d_errorSet.inError(updated);
+ d_errorSet.popSignal();
+ if(updated == curr){ continue; }
+ Assert(d_tableau.isBasic(updated));
+ if(!d_linEq.basicIsTracked(updated)){continue;}
+
+
+ Assert(d_linEq.basicIsTracked(updated));
+ Assert(wasInError || d_variables.assignmentIsConsistent(updated));
+
+ if(!d_variables.assignmentIsConsistent(updated)
+ && checkBasicForConflict(updated)){
+ Assert(!d_conflictVariables.isMember(updated) );
+ Debug("pu")
+ << "It worked? "
+ << d_statistics.d_pureUpdateConflicts.getData()
+ << " " << curr
+ << " " << checkBasicForConflict(updated) << endl;
+ reportConflict(updated);
+ ++(d_statistics.d_foundConflicts);
+ ++(d_statistics.d_pureUpdateConflicts);
+ }
+ }
+ if(d_conflictVariables.empty()){
+ if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); }
+ uint32_t currSize = d_errorSet.errorSize();
+ Assert(currSize <= prevSize);
+ if(currSize < prevSize){
+ dropped+= prevSize - currSize;
+ if(currSize == 0){
+ break;
+ }
+ }
+ }else{
+ break;
+ }
+ }
+ }
+
+ tearDownInfeasiblityFunction(d_statistics.d_constructionTimer, d_focusErrorVar);
+ d_focusErrorVar = ARITHVAR_SENTINEL;
+
+ (d_statistics.d_pureUpdateDropped) += dropped;
+
+ Assert(d_errorSet.noSignals());
+ return !d_conflictVariables.empty();
+}
+
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/pure_update_simplex.h b/src/theory/arith/pure_update_simplex.h
new file mode 100644
index 000000000..50b751d7b
--- /dev/null
+++ b/src/theory/arith/pure_update_simplex.h
@@ -0,0 +1,118 @@
+/********************* */
+/*! \file simplex.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T)
+ ** decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only
+ ** variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction
+ ** Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that
+ ** Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the
+ ** current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the
+ ** conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "theory/arith/simplex.h"
+#include "util/dense_map.h"
+#include "util/statistics_registry.h"
+#include <stdint.h>
+#include "theory/arith/arithvar.h"
+#include "theory/arith/delta_rational.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class PureUpdateSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
+
+ Result::Sat findModel(bool exactResult);
+
+private:
+ ArithVar d_focusErrorVar;
+
+ bool attemptPureUpdates();
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ bool processSignals(){
+ TimerStat &timer = d_statistics.d_processSignalsTime;
+ IntStat& conflictStat = d_statistics.d_foundConflicts;
+ return standardProcessSignals(timer, conflictStat);
+ }
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_pureUpdateFoundUnsat;
+ IntStat d_pureUpdateFoundSat;
+ IntStat d_pureUpdateMissed;
+ IntStat d_pureUpdates;
+ IntStat d_pureUpdateDropped;
+ IntStat d_pureUpdateConflicts;
+
+ IntStat d_foundConflicts;
+
+ TimerStat d_attemptPureUpdatesTimer;
+ TimerStat d_processSignalsTime;
+
+ TimerStat d_constructionTimer;
+
+ Statistics();
+ ~Statistics();
+ } d_statistics;
+};/* class PureUpdateSimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/simplex-converge.cpp b/src/theory/arith/simplex-converge.cpp
new file mode 100644
index 000000000..decce3882
--- /dev/null
+++ b/src/theory/arith/simplex-converge.cpp
@@ -0,0 +1,1674 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/simplex.h"
+#include "theory/arith/options.h"
+
+using namespace std;
+
+using namespace CVC4;
+using namespace CVC4::kind;
+
+using namespace CVC4::theory;
+using namespace CVC4::theory::arith;
+
+static const bool CHECK_AFTER_PIVOT = true;
+
+SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel, ArithVarMalloc& malloc, ConstraintDatabase& cd) :
+ d_conflictVariable(ARITHVAR_SENTINEL),
+ d_linEq(linEq),
+ d_partialModel(d_linEq.getPartialModel()),
+ d_tableau(d_linEq.getTableau()),
+ d_queue(d_partialModel, d_tableau),
+ d_numVariables(0),
+ d_conflictChannel(conflictChannel),
+ d_pivotsInRound(),
+ d_DELTA_ZERO(0,0),
+ d_arithVarMalloc(malloc),
+ d_constraintDatabase(cd),
+ d_optRow(ARITHVAR_SENTINEL),
+ d_negOptConstant(d_DELTA_ZERO)
+{
+ switch(ArithHeuristicPivotRule rule = options::arithHeuristicPivotRule()) {
+ case MINIMUM:
+ d_queue.setPivotRule(ArithPriorityQueue::MINIMUM);
+ break;
+ case BREAK_TIES:
+ d_queue.setPivotRule(ArithPriorityQueue::BREAK_TIES);
+ break;
+ case MAXIMUM:
+ d_queue.setPivotRule(ArithPriorityQueue::MAXIMUM);
+ break;
+ default:
+ Unhandled(rule);
+ }
+
+ srand(62047);
+}
+
+SimplexDecisionProcedure::Statistics::Statistics():
+ d_statUpdateConflicts("theory::arith::UpdateConflicts", 0),
+ d_findConflictOnTheQueueTime("theory::arith::findConflictOnTheQueueTime"),
+ d_attemptBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::attempt",0),
+ d_successBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::success",0),
+ d_attemptAfterDiffSearch("theory::arith::qi::AfterDiffSearch::attempt",0),
+ d_successAfterDiffSearch("theory::arith::qi::AfterDiffSearch::success",0),
+ d_attemptDuringDiffSearch("theory::arith::qi::DuringDiffSearch::attempt",0),
+ d_successDuringDiffSearch("theory::arith::qi::DuringDiffSearch::success",0),
+ d_attemptDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::attempt",0),
+ d_successDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::success",0),
+ d_attemptAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::attempt",0),
+ d_successAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::success",0),
+ d_weakeningAttempts("theory::arith::weakening::attempts",0),
+ d_weakeningSuccesses("theory::arith::weakening::success",0),
+ d_weakenings("theory::arith::weakening::total",0),
+ d_weakenTime("theory::arith::weakening::time"),
+ d_simplexConflicts("theory::arith::simplexConflicts",0),
+ // primal
+ d_primalTimer("theory::arith::primal::overall::timer"),
+ d_internalTimer("theory::arith::primal::internal::timer"),
+ d_primalCalls("theory::arith::primal::calls",0),
+ d_primalSatCalls("theory::arith::primal::calls::sat",0),
+ d_primalUnsatCalls("theory::arith::primal::calls::unsat",0),
+ d_primalPivots("theory::arith::primal::pivots",0),
+ d_primalImprovingPivots("theory::arith::primal::pivots::improving",0),
+ d_primalThresholdReachedPivot("theory::arith::primal::thresholds",0),
+ d_primalThresholdReachedPivot_dropped("theory::arith::primal::thresholds::dropped",0),
+ d_primalReachedMaxPivots("theory::arith::primal::maxpivots",0),
+ d_primalReachedMaxPivots_contractMadeProgress("theory::arith::primal::maxpivots::contract",0),
+ d_primalReachedMaxPivots_checkForConflictWorked("theory::arith::primal::maxpivots::checkworked",0),
+ d_primalGlobalMinimum("theory::arith::primal::minimum",0),
+ d_primalGlobalMinimum_rowConflictWorked("theory::arith::primal::minimum::checkworked",0),
+ d_primalGlobalMinimum_firstHalfWasSat("theory::arith::primal::minimum::firsthalf::sat",0),
+ d_primalGlobalMinimum_firstHalfWasUnsat("theory::arith::primal::minimum::firsthalf::unsat",0),
+ d_primalGlobalMinimum_contractMadeProgress("theory::arith::primal::minimum::progress",0),
+ d_unboundedFound("theory::arith::primal::unbounded",0),
+ d_unboundedFound_drive("theory::arith::primal::unbounded::drive",0),
+ d_unboundedFound_dropped("theory::arith::primal::unbounded::dropped",0)
+{
+ StatisticsRegistry::registerStat(&d_statUpdateConflicts);
+
+ StatisticsRegistry::registerStat(&d_findConflictOnTheQueueTime);
+
+ StatisticsRegistry::registerStat(&d_attemptBeforeDiffSearch);
+ StatisticsRegistry::registerStat(&d_successBeforeDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptAfterDiffSearch);
+ StatisticsRegistry::registerStat(&d_successAfterDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptDuringDiffSearch);
+ StatisticsRegistry::registerStat(&d_successDuringDiffSearch);
+ StatisticsRegistry::registerStat(&d_attemptDuringVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_successDuringVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_attemptAfterVarOrderSearch);
+ StatisticsRegistry::registerStat(&d_successAfterVarOrderSearch);
+
+ StatisticsRegistry::registerStat(&d_weakeningAttempts);
+ StatisticsRegistry::registerStat(&d_weakeningSuccesses);
+ StatisticsRegistry::registerStat(&d_weakenings);
+ StatisticsRegistry::registerStat(&d_weakenTime);
+
+ StatisticsRegistry::registerStat(&d_simplexConflicts);
+
+ //primal
+ StatisticsRegistry::registerStat(&d_primalTimer);
+ StatisticsRegistry::registerStat(&d_internalTimer);
+
+ StatisticsRegistry::registerStat(&d_primalCalls);
+ StatisticsRegistry::registerStat(&d_primalSatCalls);
+ StatisticsRegistry::registerStat(&d_primalUnsatCalls);
+
+ StatisticsRegistry::registerStat(&d_primalPivots);
+ StatisticsRegistry::registerStat(&d_primalImprovingPivots);
+
+ StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot);
+ StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot_dropped);
+
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots);
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_contractMadeProgress);
+ StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_checkForConflictWorked);
+
+
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_rowConflictWorked);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasSat);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
+ StatisticsRegistry::registerStat(&d_primalGlobalMinimum_contractMadeProgress);
+
+
+ StatisticsRegistry::registerStat(&d_unboundedFound);
+ StatisticsRegistry::registerStat(&d_unboundedFound_drive);
+ StatisticsRegistry::registerStat(&d_unboundedFound_dropped);
+
+}
+
+SimplexDecisionProcedure::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_statUpdateConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_findConflictOnTheQueueTime);
+
+ StatisticsRegistry::unregisterStat(&d_attemptBeforeDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successBeforeDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptAfterDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successAfterDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptDuringDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_successDuringDiffSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptDuringVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_successDuringVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_attemptAfterVarOrderSearch);
+ StatisticsRegistry::unregisterStat(&d_successAfterVarOrderSearch);
+
+ StatisticsRegistry::unregisterStat(&d_weakeningAttempts);
+ StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
+ StatisticsRegistry::unregisterStat(&d_weakenings);
+ StatisticsRegistry::unregisterStat(&d_weakenTime);
+
+ StatisticsRegistry::unregisterStat(&d_simplexConflicts);
+
+ //primal
+ StatisticsRegistry::unregisterStat(&d_primalTimer);
+ StatisticsRegistry::unregisterStat(&d_internalTimer);
+
+ StatisticsRegistry::unregisterStat(&d_primalCalls);
+ StatisticsRegistry::unregisterStat(&d_primalSatCalls);
+ StatisticsRegistry::unregisterStat(&d_primalUnsatCalls);
+
+ StatisticsRegistry::unregisterStat(&d_primalPivots);
+ StatisticsRegistry::unregisterStat(&d_primalImprovingPivots);
+
+ StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot);
+ StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot_dropped);
+
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots);
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_contractMadeProgress);
+ StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_checkForConflictWorked);
+
+
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_rowConflictWorked);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasSat);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
+ StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_contractMadeProgress);
+
+ StatisticsRegistry::unregisterStat(&d_unboundedFound);
+ StatisticsRegistry::unregisterStat(&d_unboundedFound_drive);
+ StatisticsRegistry::unregisterStat(&d_unboundedFound_dropped);
+}
+
+
+
+
+ArithVar SimplexDecisionProcedure::minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ // Assert(!simp.d_tableau.isBasic(x));
+ // Assert(!simp.d_tableau.isBasic(y));
+ if(x <= y){
+ return x;
+ } else {
+ return y;
+ }
+}
+
+ArithVar SimplexDecisionProcedure::minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!simp.d_tableau.isBasic(x));
+ Assert(!simp.d_tableau.isBasic(y));
+ uint32_t xLen = simp.d_tableau.getColLength(x);
+ uint32_t yLen = simp.d_tableau.getColLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(simp,x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar SimplexDecisionProcedure::minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(simp.d_tableau.isBasic(x));
+ Assert(simp.d_tableau.isBasic(y));
+ uint32_t xLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(x));
+ uint32_t yLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(y));
+ if( xLen > yLen){
+ return y;
+ } else if( xLen == yLen ){
+ return minVarOrder(simp,x,y);
+ }else{
+ return x;
+ }
+}
+ArithVar SimplexDecisionProcedure::minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!simp.d_tableau.isBasic(x));
+ Assert(!simp.d_tableau.isBasic(y));
+ if(simp.d_partialModel.hasEitherBound(x) && !simp.d_partialModel.hasEitherBound(y)){
+ return y;
+ }else if(!simp.d_partialModel.hasEitherBound(x) && simp.d_partialModel.hasEitherBound(y)){
+ return x;
+ }else {
+ return minColLength(simp, x, y);
+ }
+}
+
+template <bool above>
+ArithVar SimplexDecisionProcedure::selectSlack(ArithVar x_i, SimplexDecisionProcedure::PreferenceFunction pref){
+ ArithVar slack = ARITHVAR_SENTINEL;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(isAcceptableSlack<above>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : pref(*this, slack, nonbasic);
+ }
+ }
+
+ return slack;
+}
+
+Node betterConflict(TNode x, TNode y){
+ if(x.isNull()) return y;
+ else if(y.isNull()) return x;
+ else if(x.getNumChildren() <= y.getNumChildren()) return x;
+ else return y;
+}
+
+bool SimplexDecisionProcedure::findConflictOnTheQueue(SearchPeriod type) {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_findConflictOnTheQueueTime);
+ Assert(d_successes.empty());
+
+ switch(type){
+ case BeforeDiffSearch: ++(d_statistics.d_attemptBeforeDiffSearch); break;
+ case DuringDiffSearch: ++(d_statistics.d_attemptDuringDiffSearch); break;
+ case AfterDiffSearch: ++(d_statistics.d_attemptAfterDiffSearch); break;
+ case DuringVarOrderSearch: ++(d_statistics.d_attemptDuringVarOrderSearch); break;
+ case AfterVarOrderSearch: ++(d_statistics.d_attemptAfterVarOrderSearch); break;
+ }
+
+ ArithPriorityQueue::const_iterator i = d_queue.begin();
+ ArithPriorityQueue::const_iterator end = d_queue.end();
+ for(; i != end; ++i){
+ ArithVar x_i = *i;
+
+ if(x_i != d_conflictVariable && d_tableau.isBasic(x_i) && !d_successes.isMember(x_i)){
+ Node possibleConflict = checkBasicForConflict(x_i);
+ if(!possibleConflict.isNull()){
+ d_successes.add(x_i);
+ reportConflict(possibleConflict);
+ }
+ }
+ }
+ if(!d_successes.empty()){
+ switch(type){
+ case BeforeDiffSearch: ++(d_statistics.d_successBeforeDiffSearch); break;
+ case DuringDiffSearch: ++(d_statistics.d_successDuringDiffSearch); break;
+ case AfterDiffSearch: ++(d_statistics.d_successAfterDiffSearch); break;
+ case DuringVarOrderSearch: ++(d_statistics.d_successDuringVarOrderSearch); break;
+ case AfterVarOrderSearch: ++(d_statistics.d_successAfterVarOrderSearch); break;
+ }
+ d_successes.purge();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Result::Sat SimplexDecisionProcedure::dualFindModel(bool exactResult){
+ Assert(d_conflictVariable == ARITHVAR_SENTINEL);
+ Assert(d_queue.inCollectionMode());
+
+ if(d_queue.empty()){
+ return Result::SAT;
+ }
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+ Debug("arith::findModel") << "begin findModel()" << instance << endl;
+
+ d_queue.transitionToDifferenceMode();
+
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ if(d_queue.empty()){
+ result = Result::SAT;
+ }else if(d_queue.size() > 1){
+ if(findConflictOnTheQueue(BeforeDiffSearch)){
+ result = Result::UNSAT;
+ }
+ }
+ static const bool verbose = false;
+ exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
+ const uint32_t inexactResultsVarOrderPivots = exactResult ? 0 : options::arithStandardCheckVarOrderPivots();
+
+ uint32_t checkPeriod = options::arithSimplexCheckPeriod();
+ if(result == Result::SAT_UNKNOWN){
+ uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ?
+ d_numVariables + 1 : options::arithHeuristicPivots();
+ // The signed to unsigned conversion is safe.
+ uint32_t pivotsRemaining = numDifferencePivots;
+ while(!d_queue.empty() &&
+ result != Result::UNSAT &&
+ pivotsRemaining > 0){
+ uint32_t pivotsToDo = min(checkPeriod, pivotsRemaining);
+ pivotsRemaining -= pivotsToDo;
+ if(searchForFeasibleSolution(pivotsToDo)){
+ result = Result::UNSAT;
+ }//Once every CHECK_PERIOD examine the entire queue for conflicts
+ if(result != Result::UNSAT){
+ if(findConflictOnTheQueue(DuringDiffSearch)) { result = Result::UNSAT; }
+ }else{
+ findConflictOnTheQueue(AfterDiffSearch); // already unsat
+ }
+ }
+
+ if(verbose && numDifferencePivots > 0){
+ if(result == Result::UNSAT){
+ Message() << "diff order found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "diff order found model" << endl;
+ }else{
+ Message() << "diff order missed" << endl;
+ }
+ }
+ }
+
+ if(!d_queue.empty() && result != Result::UNSAT){
+ if(exactResult){
+ d_queue.transitionToVariableOrderMode();
+
+ while(!d_queue.empty() && result != Result::UNSAT){
+ if(searchForFeasibleSolution(checkPeriod)){
+ result = Result::UNSAT;
+ }
+
+ //Once every CHECK_PERIOD examine the entire queue for conflicts
+ if(result != Result::UNSAT){
+ if(findConflictOnTheQueue(DuringVarOrderSearch)){
+ result = Result::UNSAT;
+ }
+ } else{
+ findConflictOnTheQueue(AfterVarOrderSearch);
+ }
+ }
+ if(verbose){
+ if(result == Result::UNSAT){
+ Message() << "bland found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "bland found model" << endl;
+ }else{
+ Message() << "bland order missed" << endl;
+ }
+ }
+ }else{
+ d_queue.transitionToVariableOrderMode();
+
+ if(searchForFeasibleSolution(inexactResultsVarOrderPivots)){
+ result = Result::UNSAT;
+ findConflictOnTheQueue(AfterVarOrderSearch); // already unsat
+ }else{
+ if(findConflictOnTheQueue(AfterVarOrderSearch)) { result = Result::UNSAT; }
+ }
+
+ if(verbose){
+ if(result == Result::UNSAT){
+ Message() << "restricted var order found unsat" << endl;
+ }else if(d_queue.empty()){
+ Message() << "restricted var order found model" << endl;
+ }else{
+ Message() << "restricted var order missed" << endl;
+ }
+ }
+ }
+ }
+
+ if(result == Result::SAT_UNKNOWN && d_queue.empty()){
+ result = Result::SAT;
+ }
+
+
+
+ d_pivotsInRound.purge();
+ // ensure that the conflict variable is still in the queue.
+ if(d_conflictVariable != ARITHVAR_SENTINEL){
+ d_queue.enqueueIfInconsistent(d_conflictVariable);
+ }
+ d_conflictVariable = ARITHVAR_SENTINEL;
+
+ d_queue.transitionToCollectionMode();
+ Assert(d_queue.inCollectionMode());
+ Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
+ return result;
+
+
+ // Assert(foundConflict || d_queue.empty());
+
+ // // Curiously the invariant that we always do a full check
+ // // means that the assignment we can always empty these queues.
+ // d_queue.clear();
+ // d_pivotsInRound.purge();
+ // d_conflictVariable = ARITHVAR_SENTINEL;
+
+ // Assert(!d_queue.inCollectionMode());
+ // d_queue.transitionToCollectionMode();
+
+
+ // Assert(d_queue.inCollectionMode());
+
+ // Debug("arith::findModel") << "end findModel() " << instance << endl;
+
+ // return foundConflict;
+}
+
+
+
+Node SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic){
+
+ Assert(d_tableau.isBasic(basic));
+ const DeltaRational& beta = d_partialModel.getAssignment(basic);
+
+ if(d_partialModel.strictlyLessThanLowerBound(basic, beta)){
+ ArithVar x_j = selectSlackUpperBound(basic);
+ if(x_j == ARITHVAR_SENTINEL ){
+ return generateConflictBelowLowerBound(basic);
+ }
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(basic, beta)){
+ ArithVar x_j = selectSlackLowerBound(basic);
+ if(x_j == ARITHVAR_SENTINEL ){
+ return generateConflictAboveUpperBound(basic);
+ }
+ }
+ return Node::null();
+}
+
+//corresponds to Check() in dM06
+//template <SimplexDecisionProcedure::PreferenceFunction pf>
+bool SimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
+ Debug("arith") << "searchForFeasibleSolution" << endl;
+ Assert(remainingIterations > 0);
+
+ while(remainingIterations > 0){
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ ArithVar x_i = d_queue.dequeueInconsistentBasicVariable();
+ Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
+ if(x_i == ARITHVAR_SENTINEL){
+ Debug("arith_update") << "No inconsistent variables" << endl;
+ return false; //sat
+ }
+
+ --remainingIterations;
+
+ bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold();
+ if(!useVarOrderPivot){
+ d_pivotsInRound.add(x_i);
+ }
+
+
+ Debug("playground") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
+ << " use " << useVarOrderPivot
+ << " threshold " << options::arithPivotThreshold()
+ << endl;
+
+ PreferenceFunction pf = useVarOrderPivot ? minVarOrder : minBoundAndRowCount;
+
+ DeltaRational beta_i = d_partialModel.getAssignment(x_i);
+ ArithVar x_j = ARITHVAR_SENTINEL;
+
+ if(d_partialModel.strictlyLessThanLowerBound(x_i, beta_i)){
+ x_j = selectSlackUpperBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ ++(d_statistics.d_statUpdateConflicts);
+ Node conflict = generateConflictBelowLowerBound(x_i); //unsat
+ d_conflictVariable = x_i;
+ reportConflict(conflict);
+ return true;
+ }
+ DeltaRational l_i = d_partialModel.getLowerBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, l_i);
+
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, beta_i)){
+ x_j = selectSlackLowerBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ ++(d_statistics.d_statUpdateConflicts);
+ Node conflict = generateConflictAboveUpperBound(x_i); //unsat
+
+ d_conflictVariable = x_i;
+ reportConflict(conflict);
+ return true;
+ }
+ DeltaRational u_i = d_partialModel.getUpperBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, u_i);
+ }
+ Assert(x_j != ARITHVAR_SENTINEL);
+
+ //Check to see if we already have a conflict with x_j to prevent wasteful work
+ if(CHECK_AFTER_PIVOT){
+ Node possibleConflict = checkBasicForConflict(x_j);
+ if(!possibleConflict.isNull()){
+ d_conflictVariable = x_j;
+ reportConflict(possibleConflict);
+ return true; // unsat
+ }
+ }
+ }
+ Assert(remainingIterations == 0);
+
+ return false;
+}
+
+
+
+Constraint SimplexDecisionProcedure::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic){
+
+ int sgn = coeff.sgn();
+ bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
+
+ Constraint c = ub ?
+ d_partialModel.getUpperBoundConstraint(v) :
+ d_partialModel.getLowerBoundConstraint(v);
+
+// #warning "revisit"
+// Node exp = ub ?
+// d_partialModel.explainUpperBound(v) :
+// d_partialModel.explainLowerBound(v);
+
+ bool weakened;
+ do{
+ const DeltaRational& bound = c->getValue();
+
+ weakened = false;
+
+ Constraint weaker = ub?
+ c->getStrictlyWeakerUpperBound(true, true):
+ c->getStrictlyWeakerLowerBound(true, true);
+
+ // Node weaker = ub?
+ // d_propManager.strictlyWeakerAssertedUpperBound(v, bound):
+ // d_propManager.strictlyWeakerAssertedLowerBound(v, bound);
+
+ if(weaker != NullConstraint){
+ //if(!weaker.isNull()){
+ const DeltaRational& weakerBound = weaker->getValue();
+ //DeltaRational weakerBound = asDeltaRational(weaker);
+
+ DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
+ //if var == basic,
+ // if aboveUpper, weakerBound > bound, multiply by -1
+ // if !aboveUpper, weakerBound < bound, multiply by -1
+ diff = diff * coeff;
+ if(surplus > diff){
+ ++d_statistics.d_weakenings;
+ weakened = true;
+ anyWeakening = true;
+ surplus = surplus - diff;
+
+ Debug("weak") << "found:" << endl;
+ if(v == basic){
+ Debug("weak") << " basic: ";
+ }
+ Debug("weak") << " " << surplus << " "<< diff << endl
+ << " " << bound << c << endl
+ << " " << weakerBound << weaker << endl;
+
+ Assert(diff > d_DELTA_ZERO);
+ c = weaker;
+ }
+ }
+ }while(weakened);
+
+ return c;
+}
+
+Node SimplexDecisionProcedure::weakenConflict(bool aboveUpper, ArithVar basicVar){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+
+ const DeltaRational& assignment = d_partialModel.getAssignment(basicVar);
+ DeltaRational surplus;
+ if(aboveUpper){
+ Assert(d_partialModel.hasUpperBound(basicVar));
+ Assert(assignment > d_partialModel.getUpperBound(basicVar));
+ surplus = assignment - d_partialModel.getUpperBound(basicVar);
+ }else{
+ Assert(d_partialModel.hasLowerBound(basicVar));
+ Assert(assignment < d_partialModel.getLowerBound(basicVar));
+ surplus = d_partialModel.getLowerBound(basicVar) - assignment;
+ }
+
+ NodeBuilder<> conflict(kind::AND);
+ bool anyWeakenings = false;
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ bool weakening = false;
+ Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
+ Debug("weak") << "weak : " << weakening << " "
+ << c->assertedToTheTheory() << " "
+ << d_partialModel.getAssignment(v) << " "
+ << c << endl
+ << c->explainForConflict() << endl;
+ anyWeakenings = anyWeakenings || weakening;
+
+ Debug("weak") << "weak : " << c->explainForConflict() << endl;
+ c->explainForConflict(conflict);
+ }
+ ++d_statistics.d_weakeningAttempts;
+ if(anyWeakenings){
+ ++d_statistics.d_weakeningSuccesses;
+ }
+ return conflict;
+}
+
+
+Node SimplexDecisionProcedure::generateConflictAboveUpperBound(ArithVar conflictVar){
+ return weakenConflict(true, conflictVar);
+}
+
+Node SimplexDecisionProcedure::generateConflictBelowLowerBound(ArithVar conflictVar){
+ return weakenConflict(false, conflictVar);
+}
+
+
+// responses
+// unbounded below(arithvar)
+// reached threshold
+// reached maxpivots
+// reached GlobalMinimumd
+//
+SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primal(uint32_t maxIterations)
+{
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+
+ for(uint32_t iteration = 0; iteration < maxIterations; iteration++){
+ if(belowThreshold()){
+ return ReachedThresholdValue;
+ }
+
+ PrimalResponse res = primalCheck();
+ switch(res){
+ case GlobalMinimum:
+ case FoundUnboundedVariable:
+ return res;
+ case NoProgressOnLeaving:
+ ++d_statistics.d_primalPivots;
+ ++d_pivotsSinceOptProgress;
+ ++d_pivotsSinceLastCheck;
+ ++d_pivotsSinceErrorProgress;
+
+ d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_partialModel.getAssignment(d_primalCarry.d_entering));
+
+ if(Debug.isOn("primal::tableau")){
+ d_linEq.debugCheckTableau();
+ }
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
+
+ break;
+ case MakeProgressOnLeaving:
+ {
+ ++d_statistics.d_primalPivots;
+ ++d_statistics.d_primalImprovingPivots;
+
+ d_pivotsSinceOptProgress = 0;
+ ++d_pivotsSinceErrorProgress;
+ ++d_pivotsSinceLastCheck;
+
+ d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_primalCarry.d_nextEnteringValue);
+
+ static int helpful = 0;
+ static int hurtful = 0;
+ static int penguin = 0;
+ if(d_currentErrorVariables.isKey(d_primalCarry.d_entering)){
+ cout << "entering is error " << d_primalCarry.d_entering;
+ if(d_currentErrorVariables[d_primalCarry.d_entering].errorIsLeqZero(d_partialModel)){
+ cout << " now below threshold (" << (++helpful) << ") " << d_pivotsSinceErrorProgress << endl;
+ }else{
+ cout << "ouch (" << (++hurtful) << ")"<< d_pivotsSinceErrorProgress << endl;
+ }
+ }else if(d_currentErrorVariables.isKey(d_primalCarry.d_leaving)){
+ cout << "leaving is error " << d_primalCarry.d_leaving;
+ if(d_currentErrorVariables[d_primalCarry.d_leaving].errorIsLeqZero(d_partialModel)){
+ cout << " now below threshold(" << (++helpful) << ")" << d_pivotsSinceErrorProgress << endl;
+ }else{
+ cout << "ouch (" << (++hurtful) << ")" << d_pivotsSinceErrorProgress<< endl;
+ }
+ }else{
+ cout << " penguin (" << (++penguin) << ")" << d_pivotsSinceErrorProgress<< endl;
+ }
+
+ if(Debug.isOn("primal::tableau")){
+ d_linEq.debugCheckTableau();
+ }
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
+ }
+ break;
+ default:
+ Unreachable();
+ }
+ }
+ return UsedMaxPivots;
+}
+
+
+/**
+ * Error set
+ * ErrorVariable |-> {ErrorVariable, InputVariable, InputConstraint}
+ */
+
+/**
+ * Returns SAT if it was able to satisfy all of the constraints in the error set
+ * Returns UNSAT if it was able to able to find an error
+ */
+Result::Sat SimplexDecisionProcedure::primalConverge(int depth){
+ d_pivotsSinceLastCheck = 0;
+
+ while(!d_currentErrorVariables.empty()){
+ PrimalResponse res = primal(MAX_ITERATIONS - d_pivotsSinceLastCheck);
+
+ switch(res){
+ case FoundUnboundedVariable:
+
+ // Drive the variable to at least 0
+ // TODO This variable should be driven to a value that makes all of the error functions including it 0
+ // It'll or another unbounded will be selected in the next round anyways so ignore for now.
+ ++d_statistics.d_unboundedFound;
+ if( !belowThreshold() ){
+ driveOptToZero(d_primalCarry.d_unbounded);
+ d_linEq.debugCheckTableau();
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("primalConverge")); }
+
+ ++d_statistics.d_unboundedFound_drive;
+ }
+ Assert(belowThreshold());
+ {
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> FoundUnboundedVariable -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ d_statistics.d_unboundedFound_dropped += dropped;
+ }
+ break;
+ case ReachedThresholdValue:
+ ++d_statistics.d_primalThresholdReachedPivot;
+
+ Assert(belowThreshold());
+ {
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> ReachedThresholdValue -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ d_statistics.d_primalThresholdReachedPivot_dropped += dropped;
+ }
+ break;
+ case UsedMaxPivots:
+ {
+ d_pivotsSinceLastCheck = 0;
+ ++d_statistics.d_primalReachedMaxPivots;
+
+ // periodically attempt to do the following :
+ // contract the error variable
+ // check for a conflict on an error variable
+ uint32_t dropped = contractErrorVariables(false);
+
+ if( checkForRowConflicts() ){ // try to periodically look for a row
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> unsat " << endl;
+
+ ++d_statistics.d_primalReachedMaxPivots_checkForConflictWorked;
+ return Result::UNSAT; // row conflicts are minimal so stop.
+ }
+
+ if(dropped > 0){
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ ++d_statistics.d_primalReachedMaxPivots_contractMadeProgress;
+ }else{
+ Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> nothing " << endl;
+ }
+ }
+ break;
+ case GlobalMinimum:
+ ++d_statistics.d_primalGlobalMinimum;
+
+ // If the minimum is positive, this is unsat.
+ // However, the optimization row is not necessarily a minimal conflict
+ if(!belowThreshold()){
+
+ if(d_currentErrorVariables.size() == 1){
+ // The optimization function is exactly the same as the last remaining variable
+ // The conflict for the row is the same as the conflict for the optimization function.
+ bool foundConflict = checkForRowConflicts();
+ Assert(foundConflict);
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> one variable" << endl;
+
+ return Result::UNSAT;
+ }else{
+ // There are at least 2 error variables.
+ // Look for a minimal conflict
+
+
+ if(checkForRowConflicts() ){
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> rowconflict " << endl;
+
+ ++d_statistics.d_primalGlobalMinimum_rowConflictWorked;
+ return Result::UNSAT;
+ }
+
+ uint32_t dropped = contractErrorVariables(false);
+
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
+ if(dropped > 0){
+ ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
+ }
+
+ ErrorMap half;
+ d_currentErrorVariables.splitInto(half);
+
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << endl;
+
+
+ reconstructOptimizationFunction();
+ Result::Sat resultOnRemaining = primalConverge(depth + 1);
+
+ if(resultOnRemaining == Result::UNSAT){
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was unsat " << endl;
+ ++d_statistics.d_primalGlobalMinimum_firstHalfWasUnsat;
+ restoreErrorVariables(half);
+ return Result::UNSAT;
+ }else{
+ ++d_statistics.d_primalGlobalMinimum_firstHalfWasSat;
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was sat " << endl;
+
+ Assert(resultOnRemaining == Result::SAT);
+ Assert(d_currentErrorVariables.empty());
+ d_currentErrorVariables.addAll(half);
+ reconstructOptimizationFunction();
+ return primalConverge(depth + 1);
+ }
+ }
+
+ }else{
+
+ // if the optimum is <= 0
+ // drop all of the satisfied variables and continue;
+ uint32_t dropped = contractErrorVariables(true);
+ Debug("primal::converge") << "primalConverge -> GlobalMinimum -> negative -> dropped "<< dropped << " to " << d_currentErrorVariables.size() << endl;
+
+ ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
+ }
+ break;
+ default:
+ Unreachable();
+ }
+ }
+
+ return Result::SAT;
+}
+
+
+Result::Sat SimplexDecisionProcedure::primalFindModel(){
+ Assert(d_primalCarry.isClear());
+
+ // Reduce the queue to only contain violations
+ reduceQueue();
+
+ if(d_queue.empty()){
+ return Result::SAT;
+ }
+ TimerStat::CodeTimer codeTimer(d_statistics.d_primalTimer);
+
+ ++d_statistics.d_primalCalls;
+
+ Debug("primalFindModel") << "primalFindModel() begin" << endl;
+
+ const int PAUSE_RATE = 100;
+ if(Debug.isOn("primal::pause") && d_statistics.d_primalCalls.getData() % PAUSE_RATE == 0){
+ Debug("primal::pause") << "waiting for input: ";
+ std::string dummy;
+ std::getline(std::cin, dummy);
+ }
+
+ // TODO restore the tableau by ejecting variables
+ // Tableau copy(d_tableau);
+
+ Result::Sat answer;
+ {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_internalTimer);
+
+ // This is needed because of the fiddling with the partial model
+ //context::Context::ScopedPush speculativePush(satContext);
+
+ constructErrorVariables();
+ constructOptimizationFunction();
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel 1"); }
+ answer = primalConverge(0);
+ }
+ removeOptimizationFunction();
+
+
+ // exit
+ uint32_t nc = d_tableau.getNumColumns();
+ //d_tableau = copy;
+ while(d_tableau.getNumColumns() < nc){
+ d_tableau.increaseSize();
+ }
+ restoreErrorVariables(d_currentErrorVariables);
+
+ reduceQueue();
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel2"); }
+ Debug("primalFindModel") << "primalFindModel() end " << answer << endl;
+
+ // The set of variables in conflict with their bounds will still be a subset of the
+ // variables that are in conflict with their bounds in the beginning.
+ // The basic variables are the same because of the copy.
+ // Thus it is safe to not try to not recompute the queue of violating variables
+
+ if(answer == Result::UNSAT){
+ // This needs to be done in a different context level than the push
+ ++d_statistics.d_primalUnsatCalls;
+ }else{
+ ++d_statistics.d_primalSatCalls;
+ }
+
+ d_primalCarry.clear();
+
+ return answer;
+}
+
+/** Clears the ErrorMap and relase the resources associated with it.
+ * There are a couple of error maps around
+ */
+void SimplexDecisionProcedure::restoreErrorVariables(SimplexDecisionProcedure::ErrorMap& es){
+ while(!es.empty()){
+ ArithVar e = es.back();
+
+ reassertErrorConstraint(e, es, false, false);
+ es.pop_back();
+ }
+}
+
+void SimplexDecisionProcedure::constructErrorVariables(){
+ Assert(d_currentErrorVariables.empty());
+ Assert(!d_queue.empty());
+
+ for(ArithPriorityQueue::const_iterator iter = d_queue.begin(), end = d_queue.end(); iter != end; ++iter){
+ ArithVar input = *iter;
+
+ Assert(d_tableau.isBasic(input));
+ Assert(!d_partialModel.assignmentIsConsistent(input));
+
+ Assert(!d_currentErrorVariables.isKey(input));
+
+ bool ub = d_partialModel.strictlyGreaterThanUpperBound(input, d_partialModel.getAssignment(input));
+
+ Constraint original = ub ? d_partialModel.getUpperBoundConstraint(input)
+ : d_partialModel.getLowerBoundConstraint(input);
+
+ d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
+
+ if(ub){
+ d_partialModel.forceRelaxUpperBound(input);
+ }else{
+ d_partialModel.forceRelaxLowerBound(input);
+ }
+ Debug("primal") << "adding error variable (" << input << ", " << ", " << original <<") ";
+ Debug("primal") << "ub "<< ub << " " << d_partialModel.getAssignment(input) << " " << original->getValue() <<")" << endl;
+ d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
+
+ // Constraint boundIsValue = d_constraintDatabase.getConstraint(bound, Equality, original->getValue());
+ // boundIsValue->setPsuedoConstraint();
+
+ // d_partialModel.setAssignment(bound, boundIsValue->getValue());
+ // d_partialModel.setUpperBoundConstraint(boundIsValue);
+ // d_partialModel.setLowerBoundConstraint(boundIsValue);
+
+ // // if ub
+ // // then error = x - boundIsValue
+ // // else error = boundIsValue - x
+
+ // ArithVar error = requestVariable();
+
+ // DeltaRational diff = ub ?
+ // d_partialModel.getAssignment(input) - boundIsValue->getValue() :
+ // boundIsValue->getValue() - d_partialModel.getAssignment(input);
+
+ // d_partialModel.setAssignment(error, diff);
+
+ // vector<Rational> coeffs;
+ // vector<ArithVar> variables;
+ // variables.push_back(input);
+ // coeffs.push_back(ub ? Rational(1) : Rational(-1));
+ // variables.push_back(bound);
+ // coeffs.push_back(ub ? Rational(-1) : Rational(1));
+
+ // d_tableau.addRow(error, coeffs, variables);
+
+
+ }
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+ if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("constructErrorVariables");}
+ Assert(!d_currentErrorVariables.empty());
+}
+
+
+
+/** Returns true if it has found a row conflict for any of the error variables. */
+bool SimplexDecisionProcedure::checkForRowConflicts(){
+ vector<ArithVar> inConflict;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar error = *iter;
+ const ErrorInfo& info = d_currentErrorVariables[error];
+ if(d_tableau.isBasic(error) && !info.errorIsLeqZero(d_partialModel)){
+
+ ArithVar x_j = info.isUpperbound() ?
+ selectSlackLowerBound(error) :
+ selectSlackUpperBound(error);
+
+ if(x_j == ARITHVAR_SENTINEL ){
+ inConflict.push_back(error);
+ }
+ }
+ }
+
+ if(!inConflict.empty()){
+ while(!inConflict.empty()){
+ ArithVar error = inConflict.back();
+ inConflict.pop_back();
+
+ reassertErrorConstraint(error, d_currentErrorVariables, false, true);
+
+ Node conflict = d_currentErrorVariables[error].isUpperbound() ?
+ generateConflictAboveUpperBound(error) :
+ generateConflictBelowLowerBound(error);
+ Assert(!conflict.isNull());
+
+ d_currentErrorVariables.remove(error);
+
+ reportConflict(conflict);
+ }
+ reconstructOptimizationFunction();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+void SimplexDecisionProcedure::reassertErrorConstraint(ArithVar e, SimplexDecisionProcedure::ErrorMap& es, bool definitelyTrue, bool definitelyFalse){
+ Assert(es.isKey(e));
+ const ErrorInfo& info = es.get(e);
+ Constraint original = info.getConstraint();
+
+ if(info.isUpperbound()){
+ d_partialModel.setUpperBoundConstraint(original);
+ }else if(original->isLowerBound()){
+ d_partialModel.setLowerBoundConstraint(original);
+ }
+
+ Assert(!definitelyTrue || d_partialModel.assignmentIsConsistent(e));
+ Assert(!definitelyFalse || !d_partialModel.assignmentIsConsistent(e));
+}
+
+uint32_t SimplexDecisionProcedure::contractErrorVariables(bool guaranteedSuccess){
+ uint32_t entrySize = d_currentErrorVariables.size();
+ Debug("primal::contract") << "contractErrorVariables() begin : " << d_currentErrorVariables.size() << endl;
+
+ std::vector<ArithVar> toRemove;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+ if(d_currentErrorVariables[e].errorIsLeqZero(d_partialModel)){
+ toRemove.push_back(e);
+ }
+ }
+
+ Assert(!guaranteedSuccess || !toRemove.empty());
+
+ if(!toRemove.empty()){
+ while(!toRemove.empty()){
+ ArithVar e = toRemove.back();
+ toRemove.pop_back();
+ reassertErrorConstraint(e, d_currentErrorVariables, true, false);
+ d_currentErrorVariables.remove(e);
+ }
+
+ reconstructOptimizationFunction();
+ }
+
+ Debug("primal::contract") << "contractErrorVariables() end : " << d_currentErrorVariables.size() << endl;
+
+ uint32_t exitSize = d_currentErrorVariables.size();
+
+ Assert(exitSize <= entrySize);
+ Assert(!guaranteedSuccess|| exitSize < entrySize);
+ return entrySize - exitSize;
+}
+
+void SimplexDecisionProcedure::removeOptimizationFunction(){
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(d_optRow));
+
+ d_tableau.removeBasicRow(d_optRow);
+ releaseVariable(d_optRow);
+
+ d_optRow = ARITHVAR_SENTINEL;
+ d_negOptConstant = d_DELTA_ZERO;
+
+ Assert(d_optRow == ARITHVAR_SENTINEL);
+}
+
+void SimplexDecisionProcedure::constructOptimizationFunction(){
+ Assert(d_optRow == ARITHVAR_SENTINEL);
+ Assert(d_negOptConstant == d_DELTA_ZERO);
+
+ d_optRow = requestVariable();
+
+
+ std::vector<Rational> coeffs;
+ std::vector<ArithVar> variables;
+ for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+
+ if(d_currentErrorVariables[e].isUpperbound()){
+ coeffs.push_back(Rational(1));
+ variables.push_back(e);
+ d_negOptConstant = d_negOptConstant + (d_currentErrorVariables[e].getConstraint()->getValue());
+ }else{
+ coeffs.push_back(Rational(-1));
+ variables.push_back(e);
+ d_negOptConstant = d_negOptConstant - (d_currentErrorVariables[e].getConstraint()->getValue());
+ }
+ }
+ d_tableau.addRow(d_optRow, coeffs, variables);
+
+ DeltaRational newAssignment = d_linEq.computeRowValue(d_optRow, false);
+ d_partialModel.setAssignment(d_optRow, newAssignment);
+
+ if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
+
+
+ if(Debug.isOn("primal::consistent")){
+ d_linEq.debugEntireLinEqIsConsistent("constructOptimizationFunction");
+ }
+
+ d_pivotsSinceOptProgress = 0;
+ d_pivotsSinceErrorProgress = 0;
+
+ Assert(d_optRow != ARITHVAR_SENTINEL);
+}
+
+void SimplexDecisionProcedure::reconstructOptimizationFunction(){
+ removeOptimizationFunction();
+ constructOptimizationFunction();
+}
+
+
+
+/* TODO:
+ * Very naive implementation. Recomputes everything every time.
+ * Currently looks for the variable that can decrease the optimization function the most.
+ *
+ */
+SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primalCheck()
+{
+ Debug("primal") << "primalCheck() begin" << endl;
+
+ ArithVar leaving = ARITHVAR_SENTINEL;
+ ArithVar entering = ARITHVAR_SENTINEL;
+ DeltaRational leavingShift = d_DELTA_ZERO; // The amount the leaving variable can change by without making the tableau inconsistent
+ DeltaRational leavingDelta = d_DELTA_ZERO; // The amount the optimization function changes by selecting leaving
+
+ Assert(d_improvementCandidates.empty());
+
+ for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_optRow); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+
+ ArithVar curr = e.getColVar();
+ if(curr == d_optRow){ continue; }
+
+
+ int sgn = e.getCoefficient().sgn();
+ Assert(sgn != 0);
+ if( (sgn < 0 && d_partialModel.strictlyBelowUpperBound(curr)) ||
+ (sgn > 0 && d_partialModel.strictlyAboveLowerBound(curr)) ){
+
+ d_improvementCandidates.push_back(&e);
+ }
+ }
+
+ if(d_improvementCandidates.empty()){
+ Debug("primal") << "primalCheck() end : global" << endl;
+ return GlobalMinimum; // No variable in the optimization function can be improved
+ }
+
+ DeltaRational minimumShift;
+ DeltaRational currShift;
+ for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
+ const Tableau::Entry& e = *(*ci);
+ ArithVar curr = e.getColVar();
+
+ ArithVar currEntering;
+ bool progress;
+
+ minimumShift = (leaving == ARITHVAR_SENTINEL ) ? leavingDelta/(e.getCoefficient().abs()) : d_DELTA_ZERO;
+ int sgn = e.getCoefficient().sgn();
+ computeShift(curr, sgn < 0, progress, currEntering, currShift, minimumShift);
+
+ if(currEntering == ARITHVAR_SENTINEL){
+ d_improvementCandidates.clear();
+
+ Debug("primal") << "primalCheck() end : unbounded" << endl;
+ d_primalCarry.d_unbounded = curr;
+ return FoundUnboundedVariable;
+ }else if(progress) {
+ leaving = curr;
+ leavingShift = currShift;
+ leavingDelta = currShift * e.getCoefficient();
+ entering = currEntering;
+
+ Assert(leavingDelta < d_DELTA_ZERO);
+
+ const int RECHECK_PERIOD = 10;
+ if(d_pivotsSinceErrorProgress % RECHECK_PERIOD != 0){
+ // we can make progress, stop
+ break;
+ }
+ }
+ }
+
+ if(leaving == ARITHVAR_SENTINEL){
+ cout << "Nothing can make progress " << endl;
+
+ const uint32_t THRESHOLD = 20;
+ if(d_pivotsSinceOptProgress <= THRESHOLD){
+
+ int index = rand() % d_improvementCandidates.size();
+ leaving = (*d_improvementCandidates[index]).getColVar();
+ entering = selectFirstValid(leaving, (*d_improvementCandidates[index]).getCoefficient().sgn() < 0);
+ }else{ // Bland's rule
+ bool increasing;
+ for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
+ const Tableau::Entry& e = *(*ci);
+ ArithVar curr = e.getColVar();
+ leaving = (leaving == ARITHVAR_SENTINEL) ? curr : minVarOrder(*this, curr, leaving);
+ if(leaving == curr){
+ increasing = (e.getCoefficient().sgn() < 0);
+ }
+ }
+
+ entering = selectMinimumValid(leaving, increasing);
+ }
+ Assert(leaving != ARITHVAR_SENTINEL);
+ Assert(entering != ARITHVAR_SENTINEL);
+
+ d_primalCarry.d_leaving = leaving;
+ d_primalCarry.d_entering = entering;
+
+ d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering);
+
+ Debug("primal") << "primalCheck() end : no progress made " << leaving << " to " << entering << " (" << d_pivotsSinceOptProgress << ")"<< endl;
+ d_improvementCandidates.clear();
+ return NoProgressOnLeaving;
+ }else{
+ const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
+ Assert(!enterLeavingEntry.blank());
+
+ d_primalCarry.d_leaving = leaving;
+ d_primalCarry.d_entering = entering;
+ d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
+ + leavingShift * enterLeavingEntry.getCoefficient();
+
+ Debug("primal") << "primalCheck() end : progress" << endl
+ << leaving << " to " << entering << " ~ "
+ << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
+ << " ~ " << enterLeavingEntry.getCoefficient()
+ << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
+
+ d_improvementCandidates.clear();
+ return MakeProgressOnLeaving;
+ }
+
+ // anyProgress = true;
+
+ // DeltaRational currDelta = currShift * e.getCoefficient();
+
+ // int cmp = currDelta.cmp(leavingDelta);
+
+ // // Cases:
+ // // 1) No candidate yet,
+ // // 2) there is a candidate with a strictly better update, or
+ // // 3) there is a candidate with the same update value that has a smaller value in the variable ordering.
+ // //
+ // // Case 3 covers Bland's rule.
+ // if(entering == ARITHVAR_SENTINEL || cmp < 0){
+ // leaving = curr;
+ // }else if( cmp == 0 ){
+ // leaving = minVarOrder(*this, curr, leaving);
+ // }
+
+ // if(leaving == curr){
+ // leavingShift = currShift;
+ // leavingDelta = currDelta;
+ // entering = currEntering;
+ // }
+ // }
+ // }
+
+ // if(leaving == ARITHVAR_SENTINEL){
+ // Debug("primal") << "primalCheck() end : global" << endl;
+ // return GlobalMinimum; // No variable in the optimization function can be improved
+ // }else{
+ // const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
+ // Assert(!enterLeavingEntry.blank());
+
+ // d_primalCarry.d_leaving = leaving;
+ // d_primalCarry.d_entering = entering;
+ // d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
+ // + leavingShift * enterLeavingEntry.getCoefficient();
+
+ // Debug("primal") << "primalCheck() end : progress" << endl
+ // << leaving << " to " << entering << " ~ "
+ // << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
+ // << " ~ " << enterLeavingEntry.getCoefficient()
+ // << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
+ // return MakeProgressOnLeaving;
+ // }
+}
+
+ArithVar SimplexDecisionProcedure::selectMinimumValid(ArithVar v, bool increasing){
+ ArithVar minimum = ARITHVAR_SENTINEL;
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ if(minimum == ARITHVAR_SENTINEL){
+ minimum = basic;
+ }else{
+ minimum = minVarOrder(*this, basic, minimum);
+ }
+ }
+ }
+ return minimum;
+}
+
+ArithVar SimplexDecisionProcedure::selectFirstValid(ArithVar v, bool increasing){
+ ArithVar minimum = ARITHVAR_SENTINEL;
+
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ if(minimum == ARITHVAR_SENTINEL){
+ minimum = basic;
+ }else{
+ minimum = minRowLength(*this, basic, minimum);
+ }
+ }
+ }
+ return minimum;
+}
+
+
+
+void SimplexDecisionProcedure::computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift){
+ Assert(increasing ? (minimumShift >= d_DELTA_ZERO) : (minimumShift <= d_DELTA_ZERO) );
+
+ static int instance = 0;
+ Debug("primal") << "computeshift " << ++instance << " " << leaving << endl;
+
+ // The selection for the leaving variable
+ entering = ARITHVAR_SENTINEL;
+
+ // no progress is initially made
+ progress = false;
+
+ bool bounded = false;
+
+ if(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving)){
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+
+ bounded = true;
+
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ Assert(increasing ? diff.sgn() >=0 : diff.sgn() <= 0);
+ if((increasing) ? (diff < minimumShift) : ( diff > minimumShift)){
+ Assert(!progress);
+ entering = leaving; // My my my, what an ugly hack
+ return; // no progress is possible stop
+ }
+ }
+
+ // shift has a meaningful value once entering has a meaningful value
+ // if increasing,
+ // then shift > minimumShift >= 0
+ // else shift < minimumShift <= 0
+ //
+ // Maintain the following invariant:
+ //
+ // if increasing,
+ // if e_ij > 0, diff >= shift > minimumShift >= 0
+ // if e_ij < 0, diff >= shift > minimumShift >= 0
+ // if !increasing,
+ // if e_ij > 0, diff <= shift < minimumShift <= 0
+ // if e_ij < 0, diff <= shift < minimumShift <= 0
+ // if increasing == (e_ij > 0), diff = (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
+ // if increasing != (e_ij > 0), diff = (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
+
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow) continue;
+
+ int esgn = e.getCoefficient().sgn();
+ bool basicInc = (increasing == (esgn > 0));
+ // If both are true, increasing the variable entering increases the basic variable
+ // If both are false, the entering variable is decreasing, but the coefficient is negative and the basic variable is increasing
+ // If exactly one is false, the basic variable is decreasing.
+
+ Debug("primal::shift") << basic << " " << d_partialModel.hasUpperBound(basic) << " "
+ << d_partialModel.hasLowerBound(basic) << " "
+ << e.getCoefficient() << endl;
+
+ if( (basicInc && d_partialModel.hasUpperBound(basic))||
+ (!basicInc && d_partialModel.hasLowerBound(basic))){
+
+ if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
+ d_partialModel.strictlyAboveLowerBound(basic))){
+ // diff == 0, as diff > minimumShift >= 0 or diff < minimumShift <= 0
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ DeltaRational diff = basicInc ?
+ (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
+ (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
+
+ if( entering == ARITHVAR_SENTINEL ){
+ if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
+ shift = diff;
+ entering = basic;
+ bounded = true;
+ }
+ }else{
+ if( increasing ? (diff < shift) : diff > shift){
+ // a new min for increasing
+ // a new max for decreasing
+
+ if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
+ Assert(!progress);
+ entering = basic;
+ return;
+ }else{
+ Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
+ shift = diff;
+ entering = basic;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!bounded){
+ // A totally unbounded variable
+ Assert(entering == ARITHVAR_SENTINEL);
+ progress = true;
+ return;
+ }else if(entering == ARITHVAR_SENTINEL){
+ // We have a variable that is bounded only by its maximum
+ for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
+ const Tableau::Entry& e = *colIter;
+
+ ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
+ if(basic == d_optRow){
+ continue;
+ } else{
+ entering = basic;
+ break;
+ }
+ }
+ Assert(entering != ARITHVAR_SENTINEL);
+
+ Assert(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving));
+
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+
+ shift = diff;
+
+ Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+ Assert(increasing ? shift > minimumShift : shift < minimumShift);
+
+ progress = true;
+ return;
+ }else{
+ Assert(bounded);
+ progress = true;
+
+ if((increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
+ Assert(entering != ARITHVAR_SENTINEL);
+ const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
+ DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ if((increasing) ? (diff < shift) : ( diff > shift)){
+ shift = diff;
+ }
+ }
+
+ Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+ Assert(increasing ? shift > minimumShift : shift < minimumShift);
+ return;
+ }
+
+
+ // if(! bounded ||
+ // (increasing && diff < shift) || // a new min for increasing
+ // (!increasing && diff > shift)){ // a new max for decreasing
+ // bounded = true;
+ // shift = diff;
+ // entering = basic;
+ // }
+ // }
+
+ // if(notAtTheBound && !blandMode){
+ // DeltaRational diff = basicInc ?
+ // (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
+ // (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
+
+ // if(! bounded ||
+ // (increasing && diff < shift) || // a new min for increasing
+ // (!increasing && diff > shift)){ // a new max for decreasing
+ // bounded = true;
+ // shift = diff;
+ // entering = basic;
+ // }
+ // }else if (!notAtTheBound) { // basic is already exactly at the bound
+ // if(!blandMode){ // Enter into using Bland's rule
+ // blandMode = true;
+ // bounded = true;
+ // shift = d_DELTA_ZERO;
+ // entering = basic;
+ // }else{
+ // entering = minVarOrder(*this, entering, basic); // Bland's rule.
+ // }
+ // }
+ // else this basic variable cannot be violated by increasing/decreasing entering
+
+
+
+
+ // if(!blandMode && (increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
+ // Assert(entering != ARITHVAR_SENTINEL);
+ // bounded = true;
+ // DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
+ // if((increasing) ? (diff < shift) : ( diff > shift)){
+ // shift = diff;
+ // }
+ // }
+
+ // Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
+
+ // return shift;
+}
+
+
+/**
+ * Given an variable on the optimization row that can be used to decrease the value of the optimization function
+ * arbitrarily and an optimization function that is strictly positive in the current model,
+ * driveOptToZero updates the value of unbounded s.t. the value of d_opt is exactly 0.
+ */
+void SimplexDecisionProcedure::driveOptToZero(ArithVar unbounded){
+ Assert(!belowThreshold());
+
+ const Tableau::Entry& e = d_tableau.findEntry(d_tableau.basicToRowIndex(d_optRow), unbounded);
+ Assert(!e.blank());
+
+ DeltaRational theta = (d_negOptConstant-d_partialModel.getAssignment(d_optRow))/ (e.getCoefficient());
+ Assert((e.getCoefficient().sgn() > 0) ? (theta.sgn() < 0) : (theta.sgn() > 0));
+
+ DeltaRational newAssignment = d_partialModel.getAssignment(unbounded) + theta;
+ d_linEq.update(unbounded, newAssignment);
+
+ if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("driveOptToZero")); }
+
+ Assert(belowThreshold());
+}
diff --git a/src/theory/arith/simplex-converge.h b/src/theory/arith/simplex-converge.h
new file mode 100644
index 000000000..dac3a9b49
--- /dev/null
+++ b/src/theory/arith/simplex-converge.h
@@ -0,0 +1,531 @@
+/********************* */
+/*! \file simplex.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H
+#define __CVC4__THEORY__ARITH__SIMPLEX_H
+
+#include "theory/arith/arithvar.h"
+#include "theory/arith/arith_priority_queue.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/matrix.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/linear_equality.h"
+
+#include "context/cdlist.h"
+
+#include "util/dense_map.h"
+#include "options/options.h"
+#include "util/stats.h"
+#include "util/result.h"
+
+#include <queue>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class SimplexDecisionProcedure {
+private:
+ ArithVar d_conflictVariable;
+ DenseSet d_successes;
+
+ /** Linear equality module. */
+ LinearEqualityModule& d_linEq;
+
+ /**
+ * Manages information about the assignment and upper and lower bounds on
+ * variables.
+ * Partial model matches that in LinearEqualityModule.
+ */
+ ArithPartialModel& d_partialModel;
+
+ /**
+ * Stores the linear equalities used by Simplex.
+ * Tableau from the LinearEquality module.
+ */
+ Tableau& d_tableau;
+
+ /** Contains a superset of the basic variables in violation of their bounds. */
+ ArithPriorityQueue d_queue;
+
+ /** Number of variables in the system. This is used for tuning heuristics. */
+ ArithVar d_numVariables;
+
+ /** This is the call back channel for Simplex to report conflicts. */
+ NodeCallBack& d_conflictChannel;
+
+ /** Maps a variable to how many times they have been used as a pivot in the simplex search. */
+ DenseMultiset d_pivotsInRound;
+
+ /** Stores to the DeltaRational constant 0. */
+ DeltaRational d_DELTA_ZERO;
+
+ //TODO make an option
+ const static uint32_t MAX_ITERATIONS = 20;
+
+
+ /** Used for requesting d_opt, bound and error variables for primal.*/
+ ArithVarMalloc& d_arithVarMalloc;
+
+ /** Used for constructing temporary variables, bound and error variables for primal.*/
+ ConstraintDatabase& d_constraintDatabase;
+
+public:
+ SimplexDecisionProcedure(LinearEqualityModule& linEq,
+ NodeCallBack& conflictChannel,
+ ArithVarMalloc& variables,
+ ConstraintDatabase& constraintDatabase);
+
+ /**
+ * This must be called when the value of a basic variable may now voilate one
+ * of its bounds.
+ */
+ void updateBasic(ArithVar x){
+ d_queue.enqueueIfInconsistent(x);
+ }
+
+ /**
+ * Tries to update the assignments of variables such that all of the
+ * assignments are consistent with their bounds.
+ * This is done by a simplex search through the possible bases of the tableau.
+ *
+ * If all of the variables can be made consistent with their bounds
+ * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
+ * was reported on the conflictCallback passed to the Module.
+ *
+ * Tableau pivoting is performed so variables may switch from being basic to
+ * nonbasic and vice versa.
+ *
+ * Corresponds to the "check()" procedure in [Cav06].
+ */
+ Result::Sat dualFindModel(bool exactResult);
+
+
+ /**
+ * Tries to update the assignments of the variables s.t. all of the assignments
+ * are consistent with their bounds.
+ *
+ * This is a REALLY heavy hammer consider calling dualFindModel(false) first.
+ *
+ * !!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!
+ * This procedure needs to temporarily relax contraints to contruct a satisfiable system.
+ * To do this, it is going to do a sat push.
+ */
+ Result::Sat primalFindModel();
+
+private:
+
+
+ /**
+ * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
+ * and 2 ArithVar variables and returns one of the ArithVar variables potentially
+ * using the internals of the SimplexDecisionProcedure.
+ *
+ * Both ArithVar must be non-basic in d_tableau.
+ */
+ typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar);
+
+ /**
+ * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars.
+ * This PreferenceFunction is used during the VarOrder stage of
+ * findModel.
+ */
+ static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+ /**
+ * minRowCount is a PreferenceFunction for selecting the variable with the smaller
+ * row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used
+ * during the VarOrder stage of findModel.
+ */
+ static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+ static ArithVar minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+ /**
+ * minBoundAndRowCount is a PreferenceFunction for preferring a variable
+ * without an asserted bound over variables with an asserted bound.
+ * If both have bounds or both do not have bounds,
+ * the rule falls back to minRowCount(...).
+ *
+ * This is a heuristic rule and should not be used
+ * during the VarOrder stage of findModel.
+ */
+ static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch};
+
+ bool findConflictOnTheQueue(SearchPeriod period);
+
+
+ /**
+ * Given the basic variable x_i,
+ * this function finds the smallest nonbasic variable x_j in the row of x_i
+ * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
+ * This returns ARITHVAR_SENTINEL if none exists.
+ *
+ * More formally one of the following conditions must be satisfied:
+ * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
+ * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
+ * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
+ * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
+ *
+ */
+ template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf);
+ ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
+ return selectSlack<true>(x_i, pf);
+ }
+ ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
+ return selectSlack<false>(x_i, pf);
+ }
+ /**
+ * Returns the smallest basic variable whose assignment is not consistent
+ * with its upper and lower bounds.
+ */
+ ArithVar selectSmallestInconsistentVar();
+
+ /**
+ * Given a non-basic variable that is know to not be updatable
+ * to a consistent value, construct and return a conflict.
+ * Follows section 4.2 in the CAV06 paper.
+ */
+ Node generateConflictAboveUpperBound(ArithVar conflictVar);
+ Node generateConflictBelowLowerBound(ArithVar conflictVar);
+
+public:
+ void increaseMax() {d_numVariables++;}
+
+
+ void clearQueue() {
+ d_queue.clear();
+ }
+
+
+ bool debugIsInCollectionQueue(ArithVar var) const{
+ Assert(d_queue.inCollectionMode());
+ return d_queue.collectionModeContains(var);
+ }
+
+ void reduceQueue(){
+ d_queue.reduce();
+ }
+
+ ArithPriorityQueue::const_iterator queueBegin() const{
+ return d_queue.begin();
+ }
+
+ ArithPriorityQueue::const_iterator queueEnd() const{
+ return d_queue.end();
+ }
+
+private:
+
+ /** Reports a conflict to on the output channel. */
+ void reportConflict(Node conflict){
+ d_conflictChannel(conflict);
+ ++(d_statistics.d_simplexConflicts);
+ }
+
+ template <bool above>
+ inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){
+ return
+ ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
+ ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) ||
+ (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
+ (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic));
+ }
+
+ /**
+ * Checks a basic variable, b, to see if it is in conflict.
+ * If a conflict is discovered a node summarizing the conflict is returned.
+ * Otherwise, Node::null() is returned.
+ */
+ Node checkBasicForConflict(ArithVar b);
+
+ Node weakenConflict(bool aboveUpper, ArithVar basicVar);
+ Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic);
+
+ /** Gets a fresh variable from TheoryArith. */
+ ArithVar requestVariable(){
+ return d_arithVarMalloc.request();
+ }
+
+ /** Releases a requested variable from TheoryArith.*/
+ void releaseVariable(ArithVar v){
+ d_arithVarMalloc.release(v);
+ }
+
+
+ /** An error info keeps the information associated with the construction of an ErrorVariable. */
+ class ErrorInfo {
+ private:
+ /** The variable for which the error variable was constructed.*/
+ ArithVar d_variable;
+
+ // If false -> lowerbound
+ bool d_upperbound;
+
+ /** The violated constraint this was constructed to try to satisfy.*/
+ Constraint d_violated;
+
+ public:
+ ErrorInfo(ArithVar error, bool ub, const Constraint original)
+ : d_variable(error), d_upperbound(ub), d_violated(original) {}
+
+ ErrorInfo() :
+ d_variable(ARITHVAR_SENTINEL), d_upperbound(false), d_violated(NullConstraint){}
+
+ inline ArithVar getVariable() const {
+ return d_variable;
+ }
+
+ inline bool isUpperbound() const {
+ return d_upperbound;
+ }
+
+ inline bool errorIsLeqZero(const ArithPartialModel& d_pm) const{
+ return isUpperbound() ?
+ (d_pm.getAssignment(d_variable) <= d_violated->getValue()) :
+ (d_pm.getAssignment(d_variable) >= d_violated->getValue());
+ }
+
+ inline Constraint getConstraint() const {
+ return d_violated;
+ }
+
+ inline DeltaRational getErrorAmount(const ArithPartialModel& d_pm) const {
+ return isUpperbound() ?
+ (d_pm.getAssignment(d_variable) - d_violated->getValue()) :
+ (d_violated->getValue() - d_pm.getAssignment(d_variable));
+ }
+ };
+
+ typedef DenseMap<ErrorInfo> ErrorMap;
+
+ /** A map from the error variables to the associated ErrorInfo.*/
+ ErrorMap d_currentErrorVariables;
+
+ /** The optimization function is implicitly defined as
+ * f_i = d_optRow - d_negOptConstant
+ *
+ * d_optRow is a basic variable in the tableau.
+ * The tableau maintains that it is equal to the sum of -1^{!ub} * the error variables in
+ * d_currentErrorVariables.
+ *
+ * d_negOptConstant is explicitly the negation of the sum of the bounds that were violated
+ *
+ * assignment(f_i) <= 0 iff assignment(d_optRow) <= d_negOptConstant
+ */
+ /** The variable for the variable part of the optimization function.*/
+ ArithVar d_optRow;
+
+ /** The constant part of the optimization function.*/
+ DeltaRational d_negOptConstant;
+
+ inline bool belowThreshold() const {
+ return d_partialModel.getAssignment(d_optRow) <= d_negOptConstant;
+ }
+
+ /**
+ * A PrimalResponse represents the state that the primal simplex solver is in.
+ */
+ enum PrimalResponse {
+ // The optimization can decrease arbitrarily on some variable in the function
+ FoundUnboundedVariable,
+
+ // The optimization function has reached a threshold value and is checking back in
+ ReachedThresholdValue,
+
+ // Simplex has used up its pivot bound and is checking back in with its caller
+ UsedMaxPivots,
+
+ //Simplex can make progress on the pair of entering and leaving variables
+ MakeProgressOnLeaving,
+
+ //Simplex is not at a minimum but no leaving variable can be changed to help
+ NoProgressOnLeaving,
+
+ // Simplex has reached a minimum for its optimization function
+ GlobalMinimum
+ };
+
+ /**
+ * These fields are for sticking information for passing information back with the PrimalRespones.
+ * These fields should be ignored as unsafe/unknown unless you have a PrimalResponse that states
+ * the field makes sense.
+ */
+ struct PrimalPassBack {
+ public:
+ /**
+ * A variable s.t. its value can be increased/decreased arbitrarily to change the optimization function
+ * arbitrarily low.
+ */
+ ArithVar d_unbounded;
+
+ /** The leaving variable selection for primal simplex. */
+ ArithVar d_leaving;
+
+ /** The entering variable selection for primal simplex. */
+ ArithVar d_entering;
+
+ /** The new value for the leaving variable value for primal simplex.*/
+ DeltaRational d_nextEnteringValue;
+
+ PrimalPassBack() { clear(); }
+ void clear(){
+ d_unbounded = (d_leaving = (d_entering = ARITHVAR_SENTINEL));
+ d_nextEnteringValue = DeltaRational();
+ }
+
+ bool isClear() const {
+ return d_unbounded == ARITHVAR_SENTINEL &&
+ d_leaving == ARITHVAR_SENTINEL &&
+ d_entering == ARITHVAR_SENTINEL &&
+ d_nextEnteringValue.sgn() == 0;
+ }
+ } d_primalCarry;
+
+ uint32_t d_pivotsSinceErrorProgress;
+ uint32_t d_pivotsSinceOptProgress;
+ uint32_t d_pivotsSinceLastCheck;
+
+ typedef std::vector< const Tableau::Entry* > EntryVector;
+ EntryVector d_improvementCandidates;
+
+ PrimalResponse primal(uint32_t maxIterations);
+ PrimalResponse primalCheck();
+ Result::Sat primalConverge(int depth);
+ void driveOptToZero(ArithVar unbounded);
+ uint32_t contractErrorVariables(bool guaranteedSuccess);
+ bool checkForRowConflicts();
+ void restoreErrorVariables(ErrorMap& es);
+ void constructErrorVariables();
+ void constructOptimizationFunction();
+ void removeOptimizationFunction();
+ void reconstructOptimizationFunction();
+ ArithVar selectMinimumValid(ArithVar v, bool increasing);
+ ArithVar selectFirstValid(ArithVar v, bool increasing);
+
+ void reassertErrorConstraint(ArithVar e, ErrorMap& es, bool definitelyTrue, bool definitelyFalse);
+ void computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statUpdateConflicts;
+
+ TimerStat d_findConflictOnTheQueueTime;
+
+ IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch;
+ IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch;
+ IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch;
+ IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch;
+ IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch;
+
+ IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
+ TimerStat d_weakenTime;
+
+
+ IntStat d_simplexConflicts;
+
+ // Primal stuffs
+ TimerStat d_primalTimer;
+ TimerStat d_internalTimer;
+
+ IntStat d_primalCalls;
+ IntStat d_primalSatCalls;
+ IntStat d_primalUnsatCalls;
+
+ IntStat d_primalPivots;
+ IntStat d_primalImprovingPivots;
+
+ IntStat d_primalThresholdReachedPivot;
+ IntStat d_primalThresholdReachedPivot_dropped;
+
+ IntStat d_primalReachedMaxPivots;
+ IntStat d_primalReachedMaxPivots_contractMadeProgress;
+ IntStat d_primalReachedMaxPivots_checkForConflictWorked;
+
+
+ IntStat d_primalGlobalMinimum;
+ IntStat d_primalGlobalMinimum_rowConflictWorked;
+ IntStat d_primalGlobalMinimum_firstHalfWasSat;
+ IntStat d_primalGlobalMinimum_firstHalfWasUnsat;
+ IntStat d_primalGlobalMinimum_contractMadeProgress;
+
+
+ IntStat d_unboundedFound;
+ IntStat d_unboundedFound_drive;
+ IntStat d_unboundedFound_dropped;
+
+
+ Statistics();
+ ~Statistics();
+ };
+
+ Statistics d_statistics;
+
+};/* class SimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */
+
diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp
index ea8fefc6f..02e49258c 100644
--- a/src/theory/arith/simplex.cpp
+++ b/src/theory/arith/simplex.cpp
@@ -18,575 +18,234 @@
#include "theory/arith/simplex.h"
#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
using namespace std;
-using namespace CVC4;
-using namespace CVC4::kind;
-
-using namespace CVC4::theory;
-using namespace CVC4::theory::arith;
-
-static const bool CHECK_AFTER_PIVOT = true;
-
-SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel) :
- d_conflictVariable(ARITHVAR_SENTINEL),
- d_linEq(linEq),
- d_partialModel(d_linEq.getPartialModel()),
- d_tableau(d_linEq.getTableau()),
- d_queue(d_partialModel, d_tableau),
- d_numVariables(0),
- d_conflictChannel(conflictChannel),
- d_pivotsInRound(),
- d_DELTA_ZERO(0,0)
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : d_conflictVariables()
+ , d_linEq(linEq)
+ , d_variables(d_linEq.getVariables())
+ , d_tableau(d_linEq.getTableau())
+ , d_errorSet(errors)
+ , d_numVariables(0)
+ , d_conflictChannel(conflictChannel)
+ , d_arithVarMalloc(tvmalloc)
+ , d_errorSize(0)
+ , d_zero(0)
{
- switch(ArithHeuristicPivotRule rule = options::arithHeuristicPivotRule()) {
- case MINIMUM:
- d_queue.setPivotRule(ArithPriorityQueue::MINIMUM);
- break;
- case BREAK_TIES:
- d_queue.setPivotRule(ArithPriorityQueue::BREAK_TIES);
- break;
- case MAXIMUM:
- d_queue.setPivotRule(ArithPriorityQueue::MAXIMUM);
- break;
- default:
- Unhandled(rule);
- }
+ d_heuristicRule = options::arithErrorSelectionRule();
+ d_errorSet.setSelectionRule(d_heuristicRule);
}
-SimplexDecisionProcedure::Statistics::Statistics():
- d_statUpdateConflicts("theory::arith::UpdateConflicts", 0),
- d_findConflictOnTheQueueTime("theory::arith::findConflictOnTheQueueTime"),
- d_attemptBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::attempt",0),
- d_successBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::success",0),
- d_attemptAfterDiffSearch("theory::arith::qi::AfterDiffSearch::attempt",0),
- d_successAfterDiffSearch("theory::arith::qi::AfterDiffSearch::success",0),
- d_attemptDuringDiffSearch("theory::arith::qi::DuringDiffSearch::attempt",0),
- d_successDuringDiffSearch("theory::arith::qi::DuringDiffSearch::success",0),
- d_attemptDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::attempt",0),
- d_successDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::success",0),
- d_attemptAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::attempt",0),
- d_successAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::success",0),
- d_weakeningAttempts("theory::arith::weakening::attempts",0),
- d_weakeningSuccesses("theory::arith::weakening::success",0),
- d_weakenings("theory::arith::weakening::total",0),
- d_weakenTime("theory::arith::weakening::time"),
- d_simplexConflicts("theory::arith::simplexConflicts",0)
-{
- StatisticsRegistry::registerStat(&d_statUpdateConflicts);
-
- StatisticsRegistry::registerStat(&d_findConflictOnTheQueueTime);
-
- StatisticsRegistry::registerStat(&d_attemptBeforeDiffSearch);
- StatisticsRegistry::registerStat(&d_successBeforeDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptAfterDiffSearch);
- StatisticsRegistry::registerStat(&d_successAfterDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptDuringDiffSearch);
- StatisticsRegistry::registerStat(&d_successDuringDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptDuringVarOrderSearch);
- StatisticsRegistry::registerStat(&d_successDuringVarOrderSearch);
- StatisticsRegistry::registerStat(&d_attemptAfterVarOrderSearch);
- StatisticsRegistry::registerStat(&d_successAfterVarOrderSearch);
-
- StatisticsRegistry::registerStat(&d_weakeningAttempts);
- StatisticsRegistry::registerStat(&d_weakeningSuccesses);
- StatisticsRegistry::registerStat(&d_weakenings);
- StatisticsRegistry::registerStat(&d_weakenTime);
-
- StatisticsRegistry::registerStat(&d_simplexConflicts);
-}
+bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& conflicts) {
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert( d_conflictVariables.empty() );
-SimplexDecisionProcedure::Statistics::~Statistics(){
- StatisticsRegistry::unregisterStat(&d_statUpdateConflicts);
- StatisticsRegistry::unregisterStat(&d_findConflictOnTheQueueTime);
+ while(d_errorSet.moreSignals()){
+ ArithVar curr = d_errorSet.topSignal();
+ if(d_tableau.isBasic(curr) && !d_variables.assignmentIsConsistent(curr)){
+ Assert(d_linEq.basicIsTracked(curr));
- StatisticsRegistry::unregisterStat(&d_attemptBeforeDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successBeforeDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptAfterDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successAfterDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptDuringDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successDuringDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptDuringVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_successDuringVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_attemptAfterVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_successAfterVarOrderSearch);
+ if(!d_conflictVariables.isMember(curr) && checkBasicForConflict(curr)){
- StatisticsRegistry::unregisterStat(&d_weakeningAttempts);
- StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
- StatisticsRegistry::unregisterStat(&d_weakenings);
- StatisticsRegistry::unregisterStat(&d_weakenTime);
+ Debug("recentlyViolated")
+ << "It worked? "
+ << conflicts.getData()
+ << " " << curr
+ << " " << checkBasicForConflict(curr) << endl;
+ reportConflict(curr);
+ ++conflicts;
+ }
+ }
+ // Pop signal afterwards in case d_linEq.trackVariable(curr);
+ // is needed for for the ErrorSet
+ d_errorSet.popSignal();
+ }
+ d_errorSize = d_errorSet.errorSize();
- StatisticsRegistry::unregisterStat(&d_simplexConflicts);
+ Assert(d_errorSet.noSignals());
+ return !d_conflictVariables.empty();
}
+/** Reports a conflict to on the output channel. */
+void SimplexDecisionProcedure::reportConflict(ArithVar basic){
+ Assert(!d_conflictVariables.isMember(basic));
+ Assert(checkBasicForConflict(basic));
+ Node conflict = generateConflictForBasic(basic);
-
-
-
-
-
-ArithVar SimplexDecisionProcedure::minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!simp.d_tableau.isBasic(x));
- Assert(!simp.d_tableau.isBasic(y));
- if(x <= y){
- return x;
- } else {
- return y;
- }
+ static bool verbose = false;
+ if(verbose) { Message() << "conflict " << basic << " " << conflict << endl; }
+ Assert(!conflict.isNull());
+ d_conflictChannel(conflict);
+ d_conflictVariables.add(basic);
}
-ArithVar SimplexDecisionProcedure::minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!simp.d_tableau.isBasic(x));
- Assert(!simp.d_tableau.isBasic(y));
- uint32_t xLen = simp.d_tableau.getColLength(x);
- uint32_t yLen = simp.d_tableau.getColLength(y);
- if( xLen > yLen){
- return y;
- } else if( xLen== yLen ){
- return minVarOrder(simp,x,y);
- }else{
- return x;
- }
-}
+Node SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) const {
-ArithVar SimplexDecisionProcedure::minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!simp.d_tableau.isBasic(x));
- Assert(!simp.d_tableau.isBasic(y));
- if(simp.d_partialModel.hasEitherBound(x) && !simp.d_partialModel.hasEitherBound(y)){
- return y;
- }else if(!simp.d_partialModel.hasEitherBound(x) && simp.d_partialModel.hasEitherBound(y)){
- return x;
- }else {
- return minColLength(simp, x, y);
+ Assert(d_tableau.isBasic(basic));
+ Assert(checkBasicForConflict(basic));
+
+ if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+ Assert(d_linEq.nonbasicsAtUpperBounds(basic));
+ return d_linEq.generateConflictBelowLowerBound(basic);
+ }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+ Assert(d_linEq.nonbasicsAtLowerBounds(basic));
+ return d_linEq.generateConflictAboveUpperBound(basic);
+ }else{
+ Unreachable();
}
}
-
-template <bool above>
-ArithVar SimplexDecisionProcedure::selectSlack(ArithVar x_i, SimplexDecisionProcedure::PreferenceFunction pref){
- ArithVar slack = ARITHVAR_SENTINEL;
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
- const Rational& a_ij = entry.getCoefficient();
- int sgn = a_ij.sgn();
- if(isAcceptableSlack<above>(sgn, nonbasic)){
- //If one of the above conditions is met, we have found an acceptable
- //nonbasic variable to pivot x_i with. We can now choose which one we
- //prefer the most.
- slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : pref(*this, slack, nonbasic);
- }
+Node SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const {
+ if(checkBasicForConflict(basic)){
+ return generateConflictForBasic(basic);
+ }else{
+ return Node::null();
}
-
- return slack;
}
-Node betterConflict(TNode x, TNode y){
- if(x.isNull()) return y;
- else if(y.isNull()) return x;
- else if(x.getNumChildren() <= y.getNumChildren()) return x;
- else return y;
-}
-
-bool SimplexDecisionProcedure::findConflictOnTheQueue(SearchPeriod type) {
- TimerStat::CodeTimer codeTimer(d_statistics.d_findConflictOnTheQueueTime);
- Assert(d_successes.empty());
-
- switch(type){
- case BeforeDiffSearch: ++(d_statistics.d_attemptBeforeDiffSearch); break;
- case DuringDiffSearch: ++(d_statistics.d_attemptDuringDiffSearch); break;
- case AfterDiffSearch: ++(d_statistics.d_attemptAfterDiffSearch); break;
- case DuringVarOrderSearch: ++(d_statistics.d_attemptDuringVarOrderSearch); break;
- case AfterVarOrderSearch: ++(d_statistics.d_attemptAfterVarOrderSearch); break;
- }
-
- ArithPriorityQueue::const_iterator i = d_queue.begin();
- ArithPriorityQueue::const_iterator end = d_queue.end();
- for(; i != end; ++i){
- ArithVar x_i = *i;
+bool SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic) const {
+ Assert(d_tableau.isBasic(basic));
+ Assert(d_linEq.basicIsTracked(basic));
- if(x_i != d_conflictVariable && d_tableau.isBasic(x_i) && !d_successes.isMember(x_i)){
- Node possibleConflict = checkBasicForConflict(x_i);
- if(!possibleConflict.isNull()){
- d_successes.add(x_i);
- reportConflict(possibleConflict);
- }
+ if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+ if(d_linEq.nonbasicsAtUpperBounds(basic)){
+ return true;
}
- }
- if(!d_successes.empty()){
- switch(type){
- case BeforeDiffSearch: ++(d_statistics.d_successBeforeDiffSearch); break;
- case DuringDiffSearch: ++(d_statistics.d_successDuringDiffSearch); break;
- case AfterDiffSearch: ++(d_statistics.d_successAfterDiffSearch); break;
- case DuringVarOrderSearch: ++(d_statistics.d_successDuringVarOrderSearch); break;
- case AfterVarOrderSearch: ++(d_statistics.d_successAfterVarOrderSearch); break;
+ }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+ if(d_linEq.nonbasicsAtLowerBounds(basic)){
+ return true;
}
- d_successes.purge();
- return true;
- }else{
- return false;
}
+ return false;
}
-Result::Sat SimplexDecisionProcedure::findModel(bool exactResult){
- Assert(d_conflictVariable == ARITHVAR_SENTINEL);
- Assert(d_queue.inCollectionMode());
+void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, ArithVar tmp){
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert(tmp != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(tmp));
- if(d_queue.empty()){
- return Result::SAT;
- }
- static CVC4_THREADLOCAL(unsigned int) instance = 0;
- instance = instance + 1;
- Debug("arith::findModel") << "begin findModel()" << instance << endl;
+ d_tableau.removeBasicRow(tmp);
+ releaseVariable(tmp);
+}
- d_queue.transitionToDifferenceMode();
+void SimplexDecisionProcedure::shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped){
+ TimerStat::CodeTimer codeTimer(timer);
+ for(ArithVarVec::const_iterator i=dropped.begin(), i_end = dropped.end(); i != i_end; ++i){
+ ArithVar back = *i;
- Result::Sat result = Result::SAT_UNKNOWN;
+ int focusSgn = d_errorSet.focusSgn(back);
+ Rational chg(-focusSgn);
- if(d_queue.empty()){
- result = Result::SAT;
- }else if(d_queue.size() > 1){
- if(findConflictOnTheQueue(BeforeDiffSearch)){
- result = Result::UNSAT;
- }
+ d_linEq.substitutePlusTimesConstant(inf, back, chg);
}
- static const bool verbose = false;
- exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
- const uint32_t inexactResultsVarOrderPivots = exactResult ? 0 : options::arithStandardCheckVarOrderPivots();
-
- uint32_t checkPeriod = options::arithSimplexCheckPeriod();
- if(result == Result::SAT_UNKNOWN){
- uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ?
- d_numVariables + 1 : options::arithHeuristicPivots();
- // The signed to unsigned conversion is safe.
- uint32_t pivotsRemaining = numDifferencePivots;
- while(!d_queue.empty() &&
- result != Result::UNSAT &&
- pivotsRemaining > 0){
- uint32_t pivotsToDo = min(checkPeriod, pivotsRemaining);
- pivotsRemaining -= pivotsToDo;
- if(searchForFeasibleSolution(pivotsToDo)){
- result = Result::UNSAT;
- }//Once every CHECK_PERIOD examine the entire queue for conflicts
- if(result != Result::UNSAT){
- if(findConflictOnTheQueue(DuringDiffSearch)) { result = Result::UNSAT; }
- }else{
- findConflictOnTheQueue(AfterDiffSearch); // already unsat
- }
- }
+}
- if(verbose && numDifferencePivots > 0){
- if(result == Result::UNSAT){
- Message() << "diff order found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "diff order found model" << endl;
- }else{
- Message() << "diff order missed" << endl;
- }
- }
- }
+void SimplexDecisionProcedure::adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges){
+ TimerStat::CodeTimer codeTimer(timer);
+ for(AVIntPairVec::const_iterator i=focusChanges.begin(), i_end = focusChanges.end(); i != i_end; ++i){
+ ArithVar v = (*i).first;
+ int focusChange = (*i).second;
- if(!d_queue.empty() && result != Result::UNSAT){
- if(exactResult){
- d_queue.transitionToVariableOrderMode();
-
- while(!d_queue.empty() && result != Result::UNSAT){
- if(searchForFeasibleSolution(checkPeriod)){
- result = Result::UNSAT;
- }
-
- //Once every CHECK_PERIOD examine the entire queue for conflicts
- if(result != Result::UNSAT){
- if(findConflictOnTheQueue(DuringVarOrderSearch)){
- result = Result::UNSAT;
- }
- } else{
- findConflictOnTheQueue(AfterVarOrderSearch);
- }
- }
- if(verbose){
- if(result == Result::UNSAT){
- Message() << "bland found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "bland found model" << endl;
- }else{
- Message() << "bland order missed" << endl;
- }
- }
+ Rational chg(focusChange);
+ if(d_tableau.isBasic(v)){
+ d_linEq.substitutePlusTimesConstant(inf, v, chg);
}else{
- d_queue.transitionToVariableOrderMode();
-
- if(searchForFeasibleSolution(inexactResultsVarOrderPivots)){
- result = Result::UNSAT;
- findConflictOnTheQueue(AfterVarOrderSearch); // already unsat
- }else{
- if(findConflictOnTheQueue(AfterVarOrderSearch)) { result = Result::UNSAT; }
- }
-
- if(verbose){
- if(result == Result::UNSAT){
- Message() << "restricted var order found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "restricted var order found model" << endl;
- }else{
- Message() << "restricted var order missed" << endl;
- }
- }
+ d_linEq.directlyAddToCoefficient(inf, v, chg);
}
}
-
- if(result == Result::SAT_UNKNOWN && d_queue.empty()){
- result = Result::SAT;
- }
-
-
-
- d_pivotsInRound.purge();
- // ensure that the conflict variable is still in the queue.
- if(d_conflictVariable != ARITHVAR_SENTINEL){
- d_queue.enqueueIfInconsistent(d_conflictVariable);
- }
- d_conflictVariable = ARITHVAR_SENTINEL;
-
- d_queue.transitionToCollectionMode();
- Assert(d_queue.inCollectionMode());
- Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
- return result;
-
-
- // Assert(foundConflict || d_queue.empty());
-
- // // Curiously the invariant that we always do a full check
- // // means that the assignment we can always empty these queues.
- // d_queue.clear();
- // d_pivotsInRound.purge();
- // d_conflictVariable = ARITHVAR_SENTINEL;
-
- // Assert(!d_queue.inCollectionMode());
- // d_queue.transitionToCollectionMode();
-
-
- // Assert(d_queue.inCollectionMode());
-
- // Debug("arith::findModel") << "end findModel() " << instance << endl;
-
- // return foundConflict;
}
-Node SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic){
-
- Assert(d_tableau.isBasic(basic));
- const DeltaRational& beta = d_partialModel.getAssignment(basic);
-
- if(d_partialModel.strictlyLessThanLowerBound(basic, beta)){
- ArithVar x_j = selectSlackUpperBound(basic);
- if(x_j == ARITHVAR_SENTINEL ){
- return generateConflictBelowLowerBound(basic);
- }
- }else if(d_partialModel.strictlyGreaterThanUpperBound(basic, beta)){
- ArithVar x_j = selectSlackLowerBound(basic);
- if(x_j == ARITHVAR_SENTINEL ){
- return generateConflictAboveUpperBound(basic);
- }
- }
- return Node::null();
+void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
+ AVIntPairVec justE;
+ int sgn = d_errorSet.getSgn(e);
+ justE.push_back(make_pair(e, sgn));
+ adjustInfeasFunc(timer, inf, justE);
}
-//corresponds to Check() in dM06
-//template <SimplexDecisionProcedure::PreferenceFunction pf>
-bool SimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
- Debug("arith") << "searchForFeasibleSolution" << endl;
- Assert(remainingIterations > 0);
- while(remainingIterations > 0){
- if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert(!d_errorSet.focusEmpty());
- ArithVar x_i = d_queue.dequeueInconsistentBasicVariable();
- Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
- if(x_i == ARITHVAR_SENTINEL){
- Debug("arith_update") << "No inconsistent variables" << endl;
- return false; //sat
- }
-
- --remainingIterations;
-
- bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold();
- if(!useVarOrderPivot){
- d_pivotsInRound.add(x_i);
- }
+ ArithVar inf = requestVariable();
+ Assert(inf != ARITHVAR_SENTINEL);
+ std::vector<Rational> coeffs;
+ std::vector<ArithVar> variables;
- Debug("playground") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
- << " use " << useVarOrderPivot
- << " threshold " << options::arithPivotThreshold()
- << endl;
+ for(ArithVarVec::const_iterator iter = set.begin(), iend = set.end(); iter != iend; ++iter){
+ ArithVar e = *iter;
- PreferenceFunction pf = useVarOrderPivot ? minVarOrder : minBoundAndRowCount;
+ Assert(d_tableau.isBasic(e));
+ Assert(!d_variables.assignmentIsConsistent(e));
- DeltaRational beta_i = d_partialModel.getAssignment(x_i);
- ArithVar x_j = ARITHVAR_SENTINEL;
-
- if(d_partialModel.strictlyLessThanLowerBound(x_i, beta_i)){
- x_j = selectSlackUpperBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- ++(d_statistics.d_statUpdateConflicts);
- Node conflict = generateConflictBelowLowerBound(x_i); //unsat
- d_conflictVariable = x_i;
- reportConflict(conflict);
- return true;
- }
- DeltaRational l_i = d_partialModel.getLowerBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, l_i);
-
- }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, beta_i)){
- x_j = selectSlackLowerBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- ++(d_statistics.d_statUpdateConflicts);
- Node conflict = generateConflictAboveUpperBound(x_i); //unsat
-
- d_conflictVariable = x_i;
- reportConflict(conflict);
- return true;
- }
- DeltaRational u_i = d_partialModel.getUpperBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, u_i);
- }
- Assert(x_j != ARITHVAR_SENTINEL);
-
- //Check to see if we already have a conflict with x_j to prevent wasteful work
- if(CHECK_AFTER_PIVOT){
- Node possibleConflict = checkBasicForConflict(x_j);
- if(!possibleConflict.isNull()){
- d_conflictVariable = x_j;
- reportConflict(possibleConflict);
- return true; // unsat
- }
- }
+ int sgn = d_errorSet.getSgn(e);
+ coeffs.push_back(Rational(sgn));
+ variables.push_back(e);
}
- Assert(remainingIterations == 0);
+ d_tableau.addRow(inf, coeffs, variables);
+ DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
+ d_variables.setAssignment(inf, newAssignment);
- return false;
-}
-
-
-
-Constraint SimplexDecisionProcedure::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic){
-
- int sgn = coeff.sgn();
- bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
-
- Constraint c = ub ?
- d_partialModel.getUpperBoundConstraint(v) :
- d_partialModel.getLowerBoundConstraint(v);
-
-// #warning "revisit"
-// Node exp = ub ?
-// d_partialModel.explainUpperBound(v) :
-// d_partialModel.explainLowerBound(v);
-
- bool weakened;
- do{
- const DeltaRational& bound = c->getValue();
-
- weakened = false;
-
- Constraint weaker = ub?
- c->getStrictlyWeakerUpperBound(true, true):
- c->getStrictlyWeakerLowerBound(true, true);
-
- // Node weaker = ub?
- // d_propManager.strictlyWeakerAssertedUpperBound(v, bound):
- // d_propManager.strictlyWeakerAssertedLowerBound(v, bound);
-
- if(weaker != NullConstraint){
- //if(!weaker.isNull()){
- const DeltaRational& weakerBound = weaker->getValue();
- //DeltaRational weakerBound = asDeltaRational(weaker);
+ d_linEq.trackVariable(inf);
- DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
- //if var == basic,
- // if aboveUpper, weakerBound > bound, multiply by -1
- // if !aboveUpper, weakerBound < bound, multiply by -1
- diff = diff * coeff;
- if(surplus > diff){
- ++d_statistics.d_weakenings;
- weakened = true;
- anyWeakening = true;
- surplus = surplus - diff;
+ Debug("Inf") << inf << " " << newAssignment << endl;
- Debug("weak") << "found:" << endl;
- if(v == basic){
- Debug("weak") << " basic: ";
- }
- Debug("weak") << " " << surplus << " "<< diff << endl
- << " " << bound << c << endl
- << " " << weakerBound << weaker << endl;
-
- Assert(diff > d_DELTA_ZERO);
- c = weaker;
- }
- }
- }while(weakened);
+ return inf;
+}
- return c;
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer){
+ ArithVarVec inError;
+ d_errorSet.pushFocusInto(inError);
+ return constructInfeasiblityFunction(timer, inError);
}
-Node SimplexDecisionProcedure::weakenConflict(bool aboveUpper, ArithVar basicVar){
- TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, ArithVar e){
+ ArithVarVec justE;
+ justE.push_back(e);
+ return constructInfeasiblityFunction(timer, justE);
+}
- const DeltaRational& assignment = d_partialModel.getAssignment(basicVar);
- DeltaRational surplus;
- if(aboveUpper){
- Assert(d_partialModel.hasUpperBound(basicVar));
- Assert(assignment > d_partialModel.getUpperBound(basicVar));
- surplus = assignment - d_partialModel.getUpperBound(basicVar);
- }else{
- Assert(d_partialModel.hasLowerBound(basicVar));
- Assert(assignment < d_partialModel.getLowerBound(basicVar));
- surplus = d_partialModel.getLowerBound(basicVar) - assignment;
- }
+void SimplexDecisionProcedure::addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ sgns[p].push_back(basic);
+}
- NodeBuilder<> conflict(kind::AND);
- bool anyWeakenings = false;
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
+void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int norm){
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
const Tableau::Entry& entry = *i;
ArithVar v = entry.getColVar();
- const Rational& coeff = entry.getCoefficient();
- bool weakening = false;
- Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
- Debug("weak") << "weak : " << weakening << " "
- << c->assertedToTheTheory() << " "
- << d_partialModel.getAssignment(v) << " "
- << c << endl
- << c->explainForConflict() << endl;
- anyWeakenings = anyWeakenings || weakening;
-
- Debug("weak") << "weak : " << c->explainForConflict() << endl;
- c->explainForConflict(conflict);
- }
- ++d_statistics.d_weakeningAttempts;
- if(anyWeakenings){
- ++d_statistics.d_weakeningSuccesses;
+ int sgn = (entry.getCoefficient().sgn());
+ addSgn(sgns, v, norm * sgn, basic);
}
- return conflict;
}
+ArithVar SimplexDecisionProcedure::find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ sgn_table::const_iterator i = sgns.find(p);
-Node SimplexDecisionProcedure::generateConflictAboveUpperBound(ArithVar conflictVar){
- return weakenConflict(true, conflictVar);
+ if(i != sgns.end()){
+ const ArithVarVec& vec = (*i).second;
+ for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
+ ArithVar curr = *viter;
+ if(!m.isMember(curr)){
+ return curr;
+ }
+ }
+ }
+ return ARITHVAR_SENTINEL;
}
-Node SimplexDecisionProcedure::generateConflictBelowLowerBound(ArithVar conflictVar){
- return weakenConflict(false, conflictVar);
+SimplexDecisionProcedure::sgn_table::const_iterator SimplexDecisionProcedure::find_sgns(const sgn_table& sgns, ArithVar col, int sgn){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ return sgns.find(p);
}
-
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h
index a20920257..bc47a128a 100644
--- a/src/theory/arith/simplex.h
+++ b/src/theory/arith/simplex.h
@@ -9,7 +9,8 @@
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
- ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure.
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T)
+ ** decision procedure.
**
** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
** See the Simplex for DPLL(T) technical report for more background.(citation?)
@@ -22,19 +23,24 @@
** During the Simplex search we maintain a queue of variables.
** The queue is required to contain all of the basic variables that voilate their bounds.
** As elimination from the queue is more efficient to be done lazily,
- ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds.
+ ** we do not maintain that the queue of variables needs to be only basic variables or only
+ ** variables that satisfy their bounds.
**
** The simplex procedure roughly follows Alberto's thesis. (citation?)
- ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.)
- ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction
+ ** Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that
+ ** Leonardo invented this first.)
** After this, Bland's pivot rule is invoked.
**
** During this proccess, we periodically inspect the queue of variables to
** 1) remove now extraneous extries,
- ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the
+ ** current queue heuristics, and
** 3) detect multiple conflicts.
**
- ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict.
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the
+ ** conflict.
**
** Extra things tracked atm: (Subject to change at Tim's whims)
** - A superset of all of the newly pivoted variables.
@@ -45,33 +51,34 @@
#include "cvc4_private.h"
-#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H
-#define __CVC4__THEORY__ARITH__SIMPLEX_H
+#pragma once
#include "theory/arith/arithvar.h"
-#include "theory/arith/arith_priority_queue.h"
+#include "theory/arith/error_set.h"
#include "theory/arith/delta_rational.h"
-#include "theory/arith/matrix.h"
+#include "theory/arith/tableau.h"
#include "theory/arith/partial_model.h"
#include "theory/arith/linear_equality.h"
-#include "context/cdlist.h"
-
#include "util/dense_map.h"
-#include "options/options.h"
-#include "util/statistics_registry.h"
#include "util/result.h"
-#include <queue>
-
namespace CVC4 {
namespace theory {
namespace arith {
class SimplexDecisionProcedure {
-private:
- ArithVar d_conflictVariable;
- DenseSet d_successes;
+protected:
+ typedef std::vector< std::pair<ArithVar, int> > AVIntPairVec;
+
+ /** Pivot count of the current round of pivoting. */
+ uint32_t d_pivots;
+
+ /** The set of variables that are in conflict in this round. */
+ DenseSet d_conflictVariables;
+
+ /** The rule to use for heuristic selection mode. */
+ ErrorSelectionRule d_heuristicRule;
/** Linear equality module. */
LinearEqualityModule& d_linEq;
@@ -81,7 +88,7 @@ private:
* variables.
* Partial model matches that in LinearEqualityModule.
*/
- ArithPartialModel& d_partialModel;
+ ArithVariables& d_variables;
/**
* Stores the linear equalities used by Simplex.
@@ -90,30 +97,34 @@ private:
Tableau& d_tableau;
/** Contains a superset of the basic variables in violation of their bounds. */
- ArithPriorityQueue d_queue;
+ ErrorSet& d_errorSet;
/** Number of variables in the system. This is used for tuning heuristics. */
ArithVar d_numVariables;
/** This is the call back channel for Simplex to report conflicts. */
- NodeCallBack& d_conflictChannel;
+ RaiseConflict d_conflictChannel;
- /** Maps a variable to how many times they have been used as a pivot in the simplex search. */
- DenseMultiset d_pivotsInRound;
+ /** Used for requesting d_opt, bound and error variables for primal.*/
+ TempVarMalloc d_arithVarMalloc;
- /** Stores to the DeltaRational constant 0. */
- DeltaRational d_DELTA_ZERO;
+ /** The size of the error set. */
+ uint32_t d_errorSize;
-public:
- SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel);
+ /** A local copy of 0. */
+ const Rational d_zero;
- /**
- * This must be called when the value of a basic variable may now voilate one
- * of its bounds.
- */
- void updateBasic(ArithVar x){
- d_queue.enqueueIfInconsistent(x);
- }
+ ArithVar constructInfeasiblityFunction(TimerStat& timer);
+ ArithVar constructInfeasiblityFunction(TimerStat& timer, ArithVar e);
+ ArithVar constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set);
+
+ void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf);
+ void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges);
+ void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
+ void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped);
+
+public:
+ SimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
/**
* Tries to update the assignments of variables such that all of the
@@ -121,7 +132,7 @@ public:
* This is done by a simplex search through the possible bases of the tableau.
*
* If all of the variables can be made consistent with their bounds
- * false is returned. Otherwise true is returned, and at least 1 conflict
+ * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
* was reported on the conflictCallback passed to the Module.
*
* Tableau pivoting is performed so variables may switch from being basic to
@@ -129,170 +140,65 @@ public:
*
* Corresponds to the "check()" procedure in [Cav06].
*/
- Result::Sat findModel(bool exactResult);
+ virtual Result::Sat findModel(bool exactResult) = 0;
-private:
+ void increaseMax() { d_numVariables++; }
- /**
- * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
- * and 2 ArithVar variables and returns one of the ArithVar variables potentially
- * using the internals of the SimplexDecisionProcedure.
- *
- * Both ArithVar must be non-basic in d_tableau.
- */
- typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar);
- /**
- * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars.
- * This PreferenceFunction is used during the VarOrder stage of
- * findModel.
- */
- static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+ uint32_t getPivots() const { return d_pivots; }
+protected:
- /**
- * minRowCount is a PreferenceFunction for selecting the variable with the smaller
- * row count in the tableau.
- *
- * This is a heuristic rule and should not be used
- * during the VarOrder stage of findModel.
- */
- static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
+ /** Reports a conflict to on the output channel. */
+ void reportConflict(ArithVar basic);
/**
- * minBoundAndRowCount is a PreferenceFunction for preferring a variable
- * without an asserted bound over variables with an asserted bound.
- * If both have bounds or both do not have bounds,
- * the rule falls back to minRowCount(...).
- *
- * This is a heuristic rule and should not be used
- * during the VarOrder stage of findModel.
+ * Checks a basic variable, b, to see if it is in conflict.
+ * If a conflict is discovered a node summarizing the conflict is returned.
+ * Otherwise, Node::null() is returned.
*/
- static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
-
-
-
-
-private:
- bool searchForFeasibleSolution(uint32_t maxIterations);
-
- enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch};
-
- bool findConflictOnTheQueue(SearchPeriod period);
+ Node maybeGenerateConflictForBasic(ArithVar basic) const;
+ /** Returns true if a tracked basic variable has a conflict on it. */
+ bool checkBasicForConflict(ArithVar b) const;
/**
- * Given the basic variable x_i,
- * this function finds the smallest nonbasic variable x_j in the row of x_i
- * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
- * This returns ARITHVAR_SENTINEL if none exists.
- *
- * More formally one of the following conditions must be satisfied:
- * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
- * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
- * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
- * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
- *
+ * If a basic variable has a conflict on its row,
+ * this produces a minimized row.
*/
- template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf);
- ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
- return selectSlack<true>(x_i, pf);
- }
- ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
- return selectSlack<false>(x_i, pf);
- }
- /**
- * Returns the smallest basic variable whose assignment is not consistent
- * with its upper and lower bounds.
- */
- ArithVar selectSmallestInconsistentVar();
+ Node generateConflictForBasic(ArithVar basic) const;
- /**
- * Given a non-basic variable that is know to not be updatable
- * to a consistent value, construct and return a conflict.
- * Follows section 4.2 in the CAV06 paper.
- */
- Node generateConflictAboveUpperBound(ArithVar conflictVar);
- Node generateConflictBelowLowerBound(ArithVar conflictVar);
-public:
- void increaseMax() {d_numVariables++;}
-
-
- void clearQueue() {
- d_queue.clear();
+ /** Gets a fresh variable from TheoryArith. */
+ ArithVar requestVariable(){
+ return d_arithVarMalloc.request();
}
-
- bool debugIsInCollectionQueue(ArithVar var) const{
- Assert(d_queue.inCollectionMode());
- return d_queue.collectionModeContains(var);
+ /** Releases a requested variable from TheoryArith.*/
+ void releaseVariable(ArithVar v){
+ d_arithVarMalloc.release(v);
}
- void reduceQueue(){
- d_queue.reduce();
- }
+ /** Post condition: !d_queue.moreSignals() */
+ bool standardProcessSignals(TimerStat &timer, IntStat& conflictStat);
- ArithPriorityQueue::const_iterator queueBegin() const{
- return d_queue.begin();
- }
-
- ArithPriorityQueue::const_iterator queueEnd() const{
- return d_queue.end();
- }
-
-private:
+ struct ArithVarIntPairHashFunc {
+ size_t operator()(const std::pair<ArithVar, int>& p) const {
+ size_t h1 = std::hash<ArithVar>()(p.first);
+ size_t h2 = std::hash<int>()(p.second);
+ return h1 + 3389 * h2;
+ }
+ };
- /** Reports a conflict to on the output channel. */
- void reportConflict(Node conflict){
- d_conflictChannel(conflict);
- ++(d_statistics.d_simplexConflicts);
- }
+ typedef std::hash_map< std::pair<ArithVar, int>, ArithVarVec, ArithVarIntPairHashFunc> sgn_table;
- template <bool above>
- inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){
- return
- ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
- ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) ||
- (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
- (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic));
+ static inline int determinizeSgn(int sgn){
+ return sgn < 0 ? -1 : (sgn == 0 ? 0 : 1);
}
- /**
- * Checks a basic variable, b, to see if it is in conflict.
- * If a conflict is discovered a node summarizing the conflict is returned.
- * Otherwise, Node::null() is returned.
- */
- Node checkBasicForConflict(ArithVar b);
-
- Node weakenConflict(bool aboveUpper, ArithVar basicVar);
- Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic);
-
-
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statUpdateConflicts;
-
- TimerStat d_findConflictOnTheQueueTime;
-
- IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch;
- IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch;
- IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch;
- IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch;
- IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch;
-
- IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
- TimerStat d_weakenTime;
-
-
- IntStat d_simplexConflicts;
-
- Statistics();
- ~Statistics();
- };
-
- Statistics d_statistics;
+ void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
+ void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
+ ArithVar find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m);
+ sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn);
};/* class SimplexDecisionProcedure */
@@ -300,5 +206,3 @@ private:
}/* CVC4::theory namespace */
}/* CVC4 namespace */
-#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */
-
diff --git a/src/theory/arith/simplex_update.cpp b/src/theory/arith/simplex_update.cpp
new file mode 100644
index 000000000..cc3b6a460
--- /dev/null
+++ b/src/theory/arith/simplex_update.cpp
@@ -0,0 +1,192 @@
+/********************* */
+/*! \file simplex_update.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This implements UpdateInfo.
+ **
+ ** This implements the UpdateInfo.
+ **/
+
+
+#include "theory/arith/simplex_update.h"
+#include "theory/arith/constraint.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+UpdateInfo::UpdateInfo():
+ d_nonbasic(ARITHVAR_SENTINEL),
+ d_nonbasicDirection(0),
+ d_nonbasicDelta(),
+ d_foundConflict(false),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(),
+ d_limiting(NullConstraint),
+ d_witness(AntiProductive)
+{}
+
+UpdateInfo::UpdateInfo(ArithVar nb, int dir):
+ d_nonbasic(nb),
+ d_nonbasicDirection(dir),
+ d_nonbasicDelta(),
+ d_foundConflict(false),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(),
+ d_limiting(NullConstraint),
+ d_witness(AntiProductive)
+{
+ Assert(dir == 1 || dir == -1);
+}
+
+UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint c):
+ d_nonbasic(nb),
+ d_nonbasicDirection(delta.sgn()),
+ d_nonbasicDelta(delta),
+ d_foundConflict(true),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(&r),
+ d_limiting(c),
+ d_witness(ConflictFound)
+{
+ Assert(conflict);
+}
+
+UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim){
+ return UpdateInfo(true, nb, delta, r, lim);
+}
+
+void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){
+ d_limiting = NullConstraint;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = f;
+ d_tableauCoefficient.clear();
+ updateWitness();
+ Assert(unbounded());
+ Assert(improvement(d_witness));
+ Assert(!describesPivot());
+ Assert(debugSgnAgreement());
+}
+void UpdateInfo::updatePureFocus(const DeltaRational& delta, Constraint c){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange.clear();
+ d_focusDirection = 1;
+ d_tableauCoefficient.clear();
+ updateWitness();
+ Assert(!describesPivot());
+ Assert(improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange.clear();
+ d_focusDirection.clear();
+ updateWitness();
+ Assert(describesPivot());
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c, int ec){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection.clear();
+ d_tableauCoefficient = &r;
+ updateWitness();
+ Assert(describesPivot());
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::witnessedUpdate(const DeltaRational& delta, Constraint c, int ec, int fd){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = fd;
+ d_tableauCoefficient.clear();
+ updateWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::update(const DeltaRational& delta, const Rational& r, Constraint c, int ec, int fd){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = fd;
+ d_tableauCoefficient = &r;
+ updateWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+bool UpdateInfo::describesPivot() const {
+ return !unbounded() && d_nonbasic != d_limiting->getVariable();
+}
+
+void UpdateInfo::output(std::ostream& out) const{
+ out << "{UpdateInfo"
+ << ", nb = " << d_nonbasic
+ << ", dir = " << d_nonbasicDirection
+ << ", delta = " << d_nonbasicDelta
+ << ", conflict = " << d_foundConflict
+ << ", errorChange = " << d_errorsChange
+ << ", focusDir = " << d_focusDirection
+ << ", witness = " << d_witness
+ << ", limiting = " << d_limiting
+ << "}";
+}
+
+ArithVar UpdateInfo::leaving() const{
+ Assert(describesPivot());
+
+ return d_limiting->getVariable();
+}
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up){
+ up.output(out);
+ return out;
+}
+
+
+std::ostream& operator<<(std::ostream& out, WitnessImprovement w){
+ switch(w){
+ case ConflictFound:
+ out << "ConflictFound"; break;
+ case ErrorDropped:
+ out << "ErrorDropped"; break;
+ case FocusImproved:
+ out << "FocusImproved"; break;
+ case FocusShrank:
+ out << "FocusShrank"; break;
+ case Degenerate:
+ out << "Degenerate"; break;
+ case BlandsDegenerate:
+ out << "BlandsDegenerate"; break;
+ case HeuristicDegenerate:
+ out << "HeuristicDegenerate"; break;
+ case AntiProductive:
+ out << "AntiProductive"; break;
+ }
+ return out;
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h
new file mode 100644
index 000000000..516586568
--- /dev/null
+++ b/src/theory/arith/simplex_update.h
@@ -0,0 +1,352 @@
+/********************* */
+/*! \file linear_equality.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This provides a class for summarizing pivot proposals.
+ **
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This maintains the relationship needed by the SimplexDecisionProcedure.
+ **
+ ** In the language of Simplex for DPLL(T), this provides:
+ ** - update()
+ ** - pivotAndUpdate()
+ **
+ ** This class also provides utility functions that require
+ ** using both the Tableau and PartialModel.
+ **/
+
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/arithvar.h"
+#include "theory/arith/constraint_forward.h"
+#include "util/maybe.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+enum WitnessImprovement {
+ ConflictFound = 0,
+ ErrorDropped = 1,
+ FocusImproved = 2,
+ FocusShrank = 3,
+ Degenerate = 4,
+ BlandsDegenerate = 5,
+ HeuristicDegenerate = 6,
+ AntiProductive = 7
+};
+
+inline bool strongImprovement(WitnessImprovement w){
+ return w <= FocusImproved;
+}
+
+inline bool improvement(WitnessImprovement w){
+ return w <= FocusShrank;
+}
+
+inline bool degenerate(WitnessImprovement w){
+ switch(w){
+ case Degenerate:
+ case BlandsDegenerate:
+ case HeuristicDegenerate:
+ return true;
+ default:
+ return false;
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, WitnessImprovement w);
+
+/**
+ * This class summarizes both potential:
+ * - pivot-and-update operations or
+ * - a pure update operation.
+ * This stores enough information for the various algorithms hat consider these operations.
+ * These require slightly different pieces of information at different points
+ * so they are a bit verbose and paranoid.
+ */
+class UpdateInfo {
+private:
+
+ /**
+ * The nonbasic variables under consideration.
+ * This is either the entering variable on a pivot and update
+ * or the variable being updated.
+ * This can only be set in the constructor or assignment.
+ *
+ * If this uninitialized, then this is ARITHVAR_SENTINEL.
+ */
+ ArithVar d_nonbasic;
+
+ /**
+ * The sgn of the "intended" derivative (delta) of the update to d_nonbasic.
+ * This is either 1, -1, or 0.
+ * It is "intended" as the delta is always allowed to be 0.
+ * (See debugSgnAgreement().)
+ *
+ * If this uninitialized, then this is 0.
+ * If this is initialized, then it is -1 or 1.
+ *
+ * This can only be set in the constructor or assignment.
+ */
+ int d_nonbasicDirection;
+
+ /**
+ * The change in the assignment of d_nonbasic.
+ * This is changed via the updateProposal(...) methods.
+ * The value needs to satisfy debugSgnAgreement() or it is in conflict.
+ */
+ Maybe<DeltaRational> d_nonbasicDelta;
+
+ /**
+ * This is true if the pivot-and-update is *known* to cause a conflict.
+ * This can only be true if it was constructed through the static conflict(...) method.
+ */
+ bool d_foundConflict;
+
+ /** This is the change in the size of the error set. */
+ Maybe<int> d_errorsChange;
+
+ /** This is the sgn of the change in the value of the focus set.*/
+ Maybe<int> d_focusDirection;
+
+ /** This is the sgn of the change in the value of the focus set.*/
+ Maybe<DeltaRational> d_focusChange;
+
+ /** This is the coefficient in the tableau for the entry.*/
+ Maybe<const Rational*> d_tableauCoefficient;
+
+ /**
+ * This is the constraint that nonbasic is basic is updating s.t. its variable is against it.
+ * This has 3 different possibilities:
+ * - Unbounded : then this is NullConstraint and unbounded() is true.
+ * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic.
+ * - Update: then this is not NullConstraint and the variable is d_nonbasic.
+ */
+ Constraint d_limiting;
+
+ WitnessImprovement d_witness;
+
+ /**
+ * This returns true if
+ * d_nonbasicDelta is zero() or its sgn() must agree with d_nonbasicDirection.
+ */
+ bool debugSgnAgreement() const {
+ int deltaSgn = d_nonbasicDelta.constValue().sgn();
+ return deltaSgn == 0 || deltaSgn == d_nonbasicDirection;
+ }
+
+ /** This private constructor allows for setting conflict to true. */
+ UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim);
+
+public:
+
+ /** This constructs an uninitialized UpdateInfo. */
+ UpdateInfo();
+
+ /**
+ * This constructs an initialized UpdateInfo.
+ * dir must be 1 or -1.
+ */
+ UpdateInfo(ArithVar nb, int dir);
+
+ /**
+ * This updates the nonBasicDelta to d and limiting to NullConstraint.
+ * This describes an unbounded() update.
+ */
+ void updateUnbounded(const DeltaRational& d, int ec, int f);
+
+
+ void updatePureFocus(const DeltaRational& d, Constraint c);
+ //void updatePureError(const DeltaRational& d, Constraint c, int e);
+ //void updatePure(const DeltaRational& d, Constraint c, int e, int f);
+
+ /**
+ * This updates the nonBasicDelta to d and limiting to c.
+ * This clears errorChange() and focusDir().
+ */
+ void updatePivot(const DeltaRational& d, const Rational& r, Constraint c);
+
+ /**
+ * This updates the nonBasicDelta to d, limiting to c, and errorChange to e.
+ * This clears focusDir().
+ */
+ void updatePivot(const DeltaRational& d, const Rational& r, Constraint c, int e);
+
+ /**
+ * This updates the nonBasicDelta to d, limiting to c, errorChange to e and
+ * focusDir to f.
+ */
+ void witnessedUpdate(const DeltaRational& d, Constraint c, int e, int f);
+ void update(const DeltaRational& d, const Rational& r, Constraint c, int e, int f);
+
+
+ static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim);
+
+ inline ArithVar nonbasic() const { return d_nonbasic; }
+ inline bool uninitialized() const {
+ return d_nonbasic == ARITHVAR_SENTINEL;
+ }
+
+ /**
+ * There is no limiting value to the improvement of the focus.
+ * If this is true, this never describes an update.
+ */
+ inline bool unbounded() const {
+ return d_limiting == NullConstraint;
+ }
+
+ /**
+ * The update either describes a pivotAndUpdate operation
+ * or it describes just an update.
+ */
+ bool describesPivot() const;
+
+ /** Returns the . describesPivot() must be true. */
+ ArithVar leaving() const;
+
+ /**
+ * Returns true if this is *known* to find a conflict.
+ * If true, this must have been made through the static conflict(...) function.
+ */
+ bool foundConflict() const { return d_foundConflict; }
+
+ /** Returns the direction nonbasic is supposed to move. */
+ inline int nonbasicDirection() const{ return d_nonbasicDirection; }
+
+ /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+ inline int errorsChange() const { return d_errorsChange; }
+
+ /**
+ * If errorsChange has been set, return errorsChange().
+ * Otherwise, return def.
+ */
+ inline int errorsChangeSafe(int def) const {
+ if(d_errorsChange.just()){
+ return d_errorsChange;
+ }else{
+ return def;
+ }
+ }
+
+ /** Sets the errorChange. */
+ void setErrorsChange(int ec){
+ d_errorsChange = ec;
+ updateWitness();
+ }
+
+
+ /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+ inline int focusDirection() const{ return d_focusDirection; }
+
+ /** Sets the focusDirection. */
+ void setFocusDirection(int fd){
+ Assert(-1 <= fd && fd <= 1);
+ d_focusDirection = fd;
+ updateWitness();
+ }
+
+ /**
+ * nonbasicDirection must be the same as the sign for the focus function's
+ * coefficient for this to be safe.
+ * The burden for this being safe is on the user!
+ */
+ void determineFocusDirection(){
+ int deltaSgn = d_nonbasicDelta.constValue().sgn();
+ setFocusDirection(deltaSgn * d_nonbasicDirection);
+ }
+
+ /** Requires nonbasicDelta to be set through updateProposal(...). */
+ const DeltaRational& nonbasicDelta() const {
+ return d_nonbasicDelta;
+ }
+ const Rational& getCoefficient() const {
+ Assert(describesPivot());
+ Assert(d_tableauCoefficient.constValue() != NULL);
+ return *(d_tableauCoefficient.constValue());
+ }
+ int basicDirection() const {
+ return nonbasicDirection() * (getCoefficient().sgn());
+ }
+
+ /** Returns the limiting constraint. */
+ inline Constraint limiting() const {
+ return d_limiting;
+ }
+
+ WitnessImprovement getWitness(bool useBlands = false) const{
+ Assert(d_witness == computeWitness());
+
+ if(d_witness == Degenerate){
+ if(useBlands){
+ return BlandsDegenerate;
+ }else{
+ return HeuristicDegenerate;
+ }
+ }else{
+ return d_witness;
+ }
+ }
+
+ const DeltaRational& focusChange() const {
+ return d_focusChange;
+ }
+ void setFocusChange(const DeltaRational& fc) {
+ d_focusChange = fc;
+ }
+
+ /** Outputs the UpdateInfo into out. */
+ void output(std::ostream& out) const;
+
+private:
+ void updateWitness() {
+ d_witness = computeWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ }
+
+ /**
+ * Determines the appropraite WitnessImprovement for the update.
+ * useBlands breaks ties for degenerate pivots.
+ *
+ * This is safe if:
+ * - d_foundConflict is true, or
+ * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange < 0, or
+ * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange >= 0 and d_focusDirection has been set.
+ */
+ WitnessImprovement computeWitness() const {
+ if(d_foundConflict){
+ return ConflictFound;
+ }else if(d_errorsChange.just() && d_errorsChange < 0){
+ return ErrorDropped;
+ }else if(d_errorsChange.nothing() || d_errorsChange == 0){
+ if(d_focusDirection.just()){
+ if(d_focusDirection > 0){
+ return FocusImproved;
+ }else if(d_focusDirection == 0){
+ return Degenerate;
+ }
+ }
+ }
+ return AntiProductive;
+ }
+
+};
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up);
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp
new file mode 100644
index 000000000..f19b13fa5
--- /dev/null
+++ b/src/theory/arith/soi_simplex.cpp
@@ -0,0 +1,791 @@
+/********************* */
+/*! \file simplex.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+
+#include "theory/arith/soi_simplex.h"
+#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
+
+#include "util/statistics_registry.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
+ , d_soiVar(ARITHVAR_SENTINEL)
+ , d_pivotBudget(0)
+ , d_prevWitnessImprovement(AntiProductive)
+ , d_witnessImprovementInARow(0)
+ , d_sgnDisagreements()
+ , d_statistics(d_pivots)
+{ }
+
+SumOfInfeasibilitiesSPD::Statistics::Statistics(uint32_t& pivots):
+ d_initialSignalsTime("theory::arith::SOI::initialProcessTime"),
+ d_initialConflicts("theory::arith::SOI::UpdateConflicts", 0),
+ d_soiFoundUnsat("theory::arith::SOI::FoundUnsat", 0),
+ d_soiFoundSat("theory::arith::SOI::FoundSat", 0),
+ d_soiMissed("theory::arith::SOI::Missed", 0),
+ d_soiConflicts("theory::arith::SOI::ConfMin::num", 0),
+ d_hasToBeMinimal("theory::arith::SOI::HasToBeMin", 0),
+ d_maybeNotMinimal("theory::arith::SOI::MaybeNotMin", 0),
+ d_soiTimer("theory::arith::SOI::Time"),
+ d_soiFocusConstructionTimer("theory::arith::SOI::Construction"),
+ d_soiConflictMinimization("theory::arith::SOI::Conflict::Minimization"),
+ d_selectUpdateForSOI("theory::arith::SOI::selectSOI"),
+ d_finalCheckPivotCounter("theory::arith::SOI::lastPivots", pivots)
+{
+ StatisticsRegistry::registerStat(&d_initialSignalsTime);
+ StatisticsRegistry::registerStat(&d_initialConflicts);
+
+ StatisticsRegistry::registerStat(&d_soiFoundUnsat);
+ StatisticsRegistry::registerStat(&d_soiFoundSat);
+ StatisticsRegistry::registerStat(&d_soiMissed);
+
+ StatisticsRegistry::registerStat(&d_soiConflicts);
+ StatisticsRegistry::registerStat(&d_hasToBeMinimal);
+ StatisticsRegistry::registerStat(&d_maybeNotMinimal);
+
+ StatisticsRegistry::registerStat(&d_soiTimer);
+ StatisticsRegistry::registerStat(&d_soiFocusConstructionTimer);
+
+ StatisticsRegistry::registerStat(&d_soiConflictMinimization);
+
+ StatisticsRegistry::registerStat(&d_selectUpdateForSOI);
+
+ StatisticsRegistry::registerStat(&d_finalCheckPivotCounter);
+}
+
+SumOfInfeasibilitiesSPD::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_initialSignalsTime);
+ StatisticsRegistry::unregisterStat(&d_initialConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_soiFoundUnsat);
+ StatisticsRegistry::unregisterStat(&d_soiFoundSat);
+ StatisticsRegistry::unregisterStat(&d_soiMissed);
+
+ StatisticsRegistry::unregisterStat(&d_soiConflicts);
+ StatisticsRegistry::unregisterStat(&d_hasToBeMinimal);
+ StatisticsRegistry::unregisterStat(&d_maybeNotMinimal);
+
+ StatisticsRegistry::unregisterStat(&d_soiTimer);
+ StatisticsRegistry::unregisterStat(&d_soiFocusConstructionTimer);
+
+ StatisticsRegistry::unregisterStat(&d_soiConflictMinimization);
+
+ StatisticsRegistry::unregisterStat(&d_selectUpdateForSOI);
+ StatisticsRegistry::unregisterStat(&d_finalCheckPivotCounter);
+}
+
+Result::Sat SumOfInfeasibilitiesSPD::findModel(bool exactResult){
+ Assert(d_conflictVariables.empty());
+ Assert(d_sgnDisagreements.empty());
+
+ d_pivots = 0;
+ static CVC4_THREADLOCAL(unsigned int) instance = 0;
+ instance = instance + 1;
+ static const bool verbose = false;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Debug("soi::findModel") << "soiFindModel("<< instance <<") trivial" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+
+ // We must start tracking NOW
+ d_errorSet.setSelectionRule(SUM_METRIC);
+
+ if(initialProcessSignals()){
+ d_conflictVariables.purge();
+ if(verbose){ Message() << "fcFindModel("<< instance <<") early conflict" << endl; }
+ Debug("soi::findModel") << "fcFindModel("<< instance <<") early conflict" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Debug("soi::findModel") << "fcFindModel("<< instance <<") fixed itself" << endl;
+ Assert(!d_errorSet.moreSignals());
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ Debug("soi::findModel") << "fcFindModel(" << instance <<") start non-trivial" << endl;
+
+ exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
+
+ d_prevWitnessImprovement = HeuristicDegenerate;
+ d_witnessImprovementInARow = 0;
+
+ Result::Sat result = Result::SAT_UNKNOWN;
+
+ if(result == Result::SAT_UNKNOWN){
+ if(exactResult){
+ d_pivotBudget = -1;
+ }else{
+ d_pivotBudget = options::arithStandardCheckVarOrderPivots();
+ }
+
+ result = sumOfInfeasibilities();
+
+ if(result == Result::UNSAT){
+ ++(d_statistics.d_soiFoundUnsat);
+ if(verbose){ Message() << "fc found unsat";}
+ }else if(d_errorSet.errorEmpty()){
+ ++(d_statistics.d_soiFoundSat);
+ if(verbose){ Message() << "fc found model"; }
+ }else{
+ ++(d_statistics.d_soiMissed);
+ if(verbose){ Message() << "fc missed"; }
+ }
+ }
+ if(verbose){
+ Message() << "(" << instance << ") pivots " << d_pivots << endl;
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
+ result = Result::SAT;
+ }
+
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Debug("soi::findModel") << "end findModel() " << instance << " " << result << endl;
+
+ Assert(d_conflictVariables.empty());
+ return result;
+}
+
+
+void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
+ if(d_pivotBudget > 0) {
+ --d_pivotBudget;
+ }
+ Assert(w != AntiProductive);
+
+ if(w == d_prevWitnessImprovement){
+ ++d_witnessImprovementInARow;
+ if(d_witnessImprovementInARow == 0){
+ --d_witnessImprovementInARow;
+ }
+ }else{
+ if(w != BlandsDegenerate){
+ d_witnessImprovementInARow = 1;
+ }
+ d_prevWitnessImprovement = w;
+ }
+ if(strongImprovement(w)){
+ d_leavingCountSinceImprovement.purge();
+ }
+
+ Debug("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
+}
+
+uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
+ switch(d_prevWitnessImprovement){
+ case ConflictFound:
+ case ErrorDropped:
+ case FocusImproved:
+ return 0;
+ case HeuristicDegenerate:
+ case BlandsDegenerate:
+ return d_witnessImprovementInARow;
+ // Degenerate is unreachable for its own reasons
+ case Degenerate:
+ case FocusShrank:
+ case AntiProductive:
+ Unreachable();
+ return -1;
+ }
+ Unreachable();
+}
+
+void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+ uint32_t newErrorSize = d_errorSet.errorSize();
+ adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
+ d_errorSize = newErrorSize;
+}
+
+
+UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+ UpdateInfo selected;
+
+ static int instance = 0 ;
+ ++instance;
+
+ Debug("soi::selectPrimalUpdate")
+ << "selectPrimalUpdate " << instance << endl
+ << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar)
+ << " " << d_linEq._countBounds(d_soiVar) << endl;
+
+ typedef std::vector<Cand> CandVector;
+ CandVector candidates;
+
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ if(curr == d_soiVar){ continue; }
+
+ int sgn = e.getCoefficient().sgn();
+ bool candidate =
+ (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+ (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+ Debug("soi::selectPrimalUpdate")
+ << "storing " << d_soiVar
+ << " " << curr
+ << " " << candidate
+ << " " << e.getCoefficient()
+ << " " << sgn << endl;
+
+ if(candidate) {
+ candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
+ }
+ }
+
+ CompPenaltyColLength colCmp(&d_linEq);
+ CandVector::iterator i = candidates.begin();
+ CandVector::iterator end = candidates.end();
+ std::make_heap(i, end, colCmp);
+
+ // For the first 3 pivots take the best
+ // After that, once an improvement is found on look at a
+ // small number of pivots after finding an improvement
+ // the longer the search to more willing we are to look at more candidates
+ int maxCandidatesAfterImprove =
+ (d_pivots <= 2) ? std::numeric_limits<int>::max() : d_pivots/5;
+
+ int candidatesAfterFocusImprove = 0;
+ while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
+ std::pop_heap(i, end, colCmp);
+ --end;
+ Cand& cand = (*end);
+ ArithVar curr = cand.d_nb;
+ const Rational& coeff = *cand.d_coeff;
+
+#warning "Who is using computeSafeUpdate?"
+ LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+ UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+ Debug("soi::selectPrimalUpdate")
+ << "selected " << selected << endl
+ << "currProp " << currProposal << endl
+ << "coeff " << coeff << endl;
+
+ Assert(!currProposal.uninitialized());
+
+ if(candidatesAfterFocusImprove > 0){
+ candidatesAfterFocusImprove++;
+ }
+
+ if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+ selected = currProposal;
+ WitnessImprovement w = selected.getWitness(false);
+ Debug("soi::selectPrimalUpdate") << "selected " << w << endl;
+ //setPenalty(curr, w);
+ if(improvement(w)){
+ bool exitEarly;
+ switch(w){
+ case ConflictFound: exitEarly = true; break;
+ case FocusImproved:
+ candidatesAfterFocusImprove = 1;
+ exitEarly = false;
+ break;
+ default:
+ exitEarly = false; break;
+ }
+ if(exitEarly){ break; }
+ }
+ }else{
+ Debug("soi::selectPrimalUpdate") << "dropped "<< endl;
+ }
+
+ }
+ return selected;
+}
+
+bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+ if(inf.getWitness(useBlands) == w){
+ switch(w){
+ case ConflictFound: return inf.foundConflict();
+ case ErrorDropped: return inf.errorsChange() < 0;
+ case FocusImproved: return inf.focusDirection() > 0;
+ case FocusShrank: return false; // This is not a valid output
+ case Degenerate: return false; // This is not a valid output
+ case BlandsDegenerate: return useBlands;
+ case HeuristicDegenerate: return !useBlands;
+ case AntiProductive: return false;
+ }
+ }
+ return false;
+}
+
+
+void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
+ Debug("updateAndSignal") << "updated basic " << updated;
+ Debug("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+ Debug("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+ int dir = !d_variables.assignmentIsConsistent(updated) ?
+ d_errorSet.getSgn(updated) : 0;
+ Debug("updateAndSignal") << " dir " << dir;
+ Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl;
+}
+
+
+void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+ ArithVar nonbasic = selected.nonbasic();
+
+ static bool verbose = false;
+
+ Debug("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+ stringstream ss;
+ if(verbose){
+ d_errorSet.debugPrint(ss);
+ if(selected.describesPivot()){
+ ArithVar leaving = selected.leaving();
+ ss << "leaving " << leaving
+ << " " << d_tableau.basicRowLength(leaving)
+ << " " << d_linEq._countBounds(leaving)
+ << endl;
+ }
+ if(degenerate(w) && selected.describesPivot()){
+ ArithVar leaving = selected.leaving();
+ Message()
+ << "degenerate " << leaving
+ << ", atBounds " << d_linEq.basicsAtBounds(selected)
+ << ", len " << d_tableau.basicRowLength(leaving)
+ << ", bc " << d_linEq._countBounds(leaving)
+ << endl;
+ }
+ }
+
+ if(selected.describesPivot()){
+ Constraint limiting = selected.limiting();
+ ArithVar basic = limiting->getVariable();
+ Assert(d_linEq.basicIsTracked(basic));
+ d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+ }else{
+ Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+ DeltaRational newAssignment =
+ d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+ d_linEq.updateTracked(nonbasic, newAssignment);
+ }
+ d_pivots++;
+
+ increaseLeavingCount(nonbasic);
+
+ vector< pair<ArithVar, int> > focusChanges;
+ while(d_errorSet.moreSignals()){
+ ArithVar updated = d_errorSet.topSignal();
+ int prevFocusSgn = d_errorSet.popSignal();
+
+ if(d_tableau.isBasic(updated)){
+ Assert(!d_variables.assignmentIsConsistent(updated) == d_errorSet.inError(updated));
+ if(Debug.isOn("updateAndSignal")){debugPrintSignal(updated);}
+ if(!d_variables.assignmentIsConsistent(updated)){
+ if(checkBasicForConflict(updated)){
+ reportConflict(updated);
+ //Assert(debugUpdatedBasic(selected, updated));
+ }
+ }
+ }else{
+ Debug("updateAndSignal") << "updated nonbasic " << updated << endl;
+ }
+ int currFocusSgn = d_errorSet.focusSgn(updated);
+ if(currFocusSgn != prevFocusSgn){
+ int change = currFocusSgn - prevFocusSgn;
+ focusChanges.push_back(make_pair(updated, change));
+ }
+ }
+
+ if(verbose){
+ Message() << "conflict variable " << selected << endl;
+ Message() << ss.str();
+ }
+ if(Debug.isOn("error")){ d_errorSet.debugPrint(Debug("error")); }
+
+ //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+ adjustFocusAndError(selected, focusChanges);
+}
+
+unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ bool success = false;
+ if(set.size() >= 2){
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
+ success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
+
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ }
+ return success ? set.size() : std::numeric_limits<int>::max();
+}
+
+unsigned SumOfInfeasibilitiesSPD::tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp) {
+ if(depth < set.size()){
+ unsigned resWithout = tryAllSubsets(set, depth+1, tmp);
+ if(resWithout == tmp.size() && resWithout < set.size()){
+ for(unsigned i = 0; i < tmp.size(); ++i){
+ cout << tmp[i] << " ";
+ }
+ cout << endl;
+ }
+ tmp.push_back(set[depth]);
+ unsigned resWith = tryAllSubsets(set, depth+1, tmp);
+ if(resWith == tmp.size() && resWith < set.size()){
+ for(unsigned i = 0; i < tmp.size(); ++i){
+ cout << tmp[i] << " ";
+ }
+ cout << endl;
+ }
+ tmp.pop_back();
+ return std::min(resWith, resWithout);
+ }else{
+ return trySet(tmp);
+ }
+}
+
+std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
+ std::vector< ArithVarVec > subsets;
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+ if(d_errorSize <= 2){
+ ArithVarVec inError;
+ d_errorSet.pushFocusInto(inError);
+ subsets.push_back(inError);
+ return subsets;
+ }
+ Assert(d_errorSize > 2);
+
+ //sgns_table< <nonbasic,sgn>, [basics] >;
+ // Phase 0: Construct the sgns table
+ sgn_table sgns;
+ DenseSet hasParticipated; //Has participated in a conflict
+ for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+ ArithVar e = *iter;
+ addRowSgns(sgns, e, d_errorSet.getSgn(e));
+
+ //cout << "basic error var: " << e << endl;
+ //d_tableau.debugPrintIsBasic(e);
+ //d_tableau.printBasicRow(e, cout);
+ }
+
+ // Phase 1: Try to find at least 1 pair for every element
+ ArithVarVec tmp;
+ tmp.push_back(0);
+ tmp.push_back(0);
+ for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+ ArithVar e = *iter;
+ tmp[0] = e;
+
+ int errSgn = d_errorSet.getSgn(e);
+ bool decreasing = errSgn < 0;
+ const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
+ Assert(spoiler != NULL);
+ ArithVar nb = spoiler->getColVar();
+ int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
+
+ sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
+ Assert(opposites != sgns.end());
+
+ const ArithVarVec& choices = (*opposites).second;
+ for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
+ ArithVar b = *j;
+ if(b < e){ continue; }
+ tmp[0] = e;
+ tmp[1] = b;
+ if(trySet(tmp) == 2){
+ //cout << "found a pair" << endl;
+ hasParticipated.softAdd(b);
+ hasParticipated.softAdd(e);
+ subsets.push_back(tmp);
+ ++(d_statistics.d_soiConflicts);
+ ++(d_statistics.d_hasToBeMinimal);
+ }
+ }
+ }
+
+
+ // Phase 2: If there is a variable that has not participated attempt to start a conflict
+ ArithVarVec possibleStarts; //List of elements that can be tried for starts.
+ d_errorSet.pushFocusInto(possibleStarts);
+ while(!possibleStarts.empty()){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+ ArithVar v = possibleStarts.back();
+ possibleStarts.pop_back();
+ if(hasParticipated.isMember(v)){ continue; }
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ //d_soiVar's row = \sumofinfeasibilites underConstruction
+ ArithVarVec underConstruction;
+ underConstruction.push_back(v);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
+
+ bool uniqueChoices = true;
+
+ //cout << "trying " << v << endl;
+
+ const Tableau::Entry* spoiler = NULL;
+ while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
+ ArithVar nb = spoiler->getColVar();
+ int oppositeSgn = -(spoiler->getCoefficient().sgn());
+ Assert(oppositeSgn != 0);
+
+ //cout << "looking for " << nb << " " << oppositeSgn << endl;
+
+ ArithVar basicWithOp = find_basic_outside(sgns, nb, oppositeSgn, hasParticipated);
+
+ if(basicWithOp == ARITHVAR_SENTINEL){
+ //cout << "search did not work for " << nb << endl;
+ // greedy construction has failed
+ break;
+ }else{
+ //cout << "found " << basicWithOp << endl;
+
+ addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
+ hasParticipated.softAdd(basicWithOp);
+ underConstruction.push_back(basicWithOp);
+ }
+ }
+ if(spoiler == NULL){
+ //cout << "success" << endl;
+ //then underConstruction contains a conflicting subset
+ subsets.push_back(underConstruction);
+ ++d_statistics.d_soiConflicts;
+ if(underConstruction.size() == 3){
+ ++d_statistics.d_hasToBeMinimal;
+ }else{
+ ++d_statistics.d_maybeNotMinimal;
+ }
+ }else{
+ //cout << "failure" << endl;
+ }
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ // if(false && spoiler == NULL){
+ // ArithVarVec tmp;
+ // int smallest = tryAllSubsets(underConstruction, 0, tmp);
+ // cout << underConstruction.size() << " " << smallest << endl;
+ // Assert(smallest >= underConstruction.size());
+ // if(smallest < underConstruction.size()){
+ // exit(-1);
+ // }
+ // }
+ }
+
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ return subsets;
+}
+
+Node SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
+
+ NodeBuilder<> conflict(kind::AND);
+ for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+ Constraint violated = d_errorSet.getViolated(e);
+ //cout << "basic error var: " << violated << endl;
+ violated->explainForConflict(conflict);
+
+ //d_tableau.debugPrintIsBasic(e);
+ //d_tableau.printBasicRow(e, cout);
+ }
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ if(v == d_soiVar){ continue; }
+ const Rational& coeff = entry.getCoefficient();
+
+ Constraint c = (coeff.sgn() > 0) ?
+ d_variables.getUpperBoundConstraint(v) :
+ d_variables.getLowerBoundConstraint(v);
+
+ //cout << "nb : " << c << endl;
+ c->explainForConflict(conflict);
+ }
+
+ Node conf = conflict;
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ return conf;
+}
+
+
+WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
+ static int instance = 0;
+ instance++;
+ //cout << "SOI conflict " << instance << ": |E| = " << d_errorSize << endl;
+ //d_errorSet.debugPrint(cout);
+ //cout << endl;
+
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ vector<ArithVarVec> subsets = greedyConflictSubsets();
+ Assert( d_soiVar == ARITHVAR_SENTINEL);
+
+ Assert(!subsets.empty());
+ for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
+ const ArithVarVec& subset = *i;
+ Node conflict = generateSOIConflict(subset);
+ //cout << conflict << endl;
+
+ //reportConflict(conf); do not do this. We need a custom explanations!
+ d_conflictChannel(conflict);
+ }
+ Assert( d_soiVar == ARITHVAR_SENTINEL);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
+
+ //reportConflict(conf); do not do this. We need a custom explanations!
+ d_conflictVariables.add(d_soiVar);
+
+ //cout << "SOI conflict " << instance << "end" << endl;
+ return ConflictFound;
+}
+
+WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
+ Assert(d_soiVar != ARITHVAR_SENTINEL);
+
+ bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+ LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
+ &LinearEqualityModule::preferWitness<false>:
+ &LinearEqualityModule::preferWitness<true>;
+
+ LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ &LinearEqualityModule::minVarOrder :
+ &LinearEqualityModule::minRowLength;
+ bpf = &LinearEqualityModule::minVarOrder;
+
+ UpdateInfo selected = selectUpdate(upf, bpf);
+
+ if(selected.uninitialized()){
+ Debug("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
+ return SOIConflict();
+ }else{
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+ }
+}
+
+bool SumOfInfeasibilitiesSPD::debugSOI(WitnessImprovement w, ostream& out, int instance) const{
+#warning "Redo SOI"
+ return true;
+ // out << "DLV("<<instance<<") ";
+ // switch(w){
+ // case ConflictFound:
+ // out << "found conflict" << endl;
+ // return !d_conflictVariables.empty();
+ // case ErrorDropped:
+ // return false;
+ // // out << "dropped " << prevErrorSize - d_errorSize << endl;
+ // // return d_errorSize < prevErrorSize;
+ // case FocusImproved:
+ // out << "focus improved"<< endl;
+ // return d_errorSize == prevErrorSize;
+ // case FocusShrank:
+ // Unreachable();
+ // return false;
+ // case BlandsDegenerate:
+ // out << "bland degenerate"<< endl;
+ // return true;
+ // case HeuristicDegenerate:
+ // out << "heuristic degenerate"<< endl;
+ // return true;
+ // case AntiProductive:
+ // case Degenerate:
+ // return false;
+ // }
+ // return false;
+}
+
+Result::Sat SumOfInfeasibilitiesSPD::sumOfInfeasibilities(){
+ static int instance = 0;
+ static bool verbose = false;
+
+ TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_pivotBudget != 0);
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_errorSize > 0);
+ Assert(d_conflictVariables.empty());
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+
+ //d_scores.purge();
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
+
+
+ while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
+ ++instance;
+ Debug("dualLike") << "dualLike " << instance << endl;
+
+ Assert(d_errorSet.noSignals());
+ // Possible outcomes:
+ // - conflict
+ // - budget was exhausted
+ // - focus went down
+ Debug("dualLike") << "selectFocusImproving " << endl;
+ WitnessImprovement w = soiRound();
+
+ Assert(d_errorSize == d_errorSet.errorSize());
+
+ if(verbose){
+ debugSOI(w, Message(), instance);
+ }
+ Assert(debugSOI(w, Debug("dualLike"), instance));
+ }
+
+
+ if(d_soiVar != ARITHVAR_SENTINEL){
+ tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ }
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ if(!d_conflictVariables.empty()){
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Assert(d_errorSet.noSignals());
+ return Result::SAT;
+ }else{
+ Assert(d_pivotBudget == 0);
+ return Result::SAT_UNKNOWN;
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h
new file mode 100644
index 000000000..1a6becccb
--- /dev/null
+++ b/src/theory/arith/soi_simplex.h
@@ -0,0 +1,228 @@
+/********************* */
+/*! \file simplex.h
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, mdeters
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T)
+ ** decision procedure.
+ **
+ ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
+ ** See the Simplex for DPLL(T) technical report for more background.(citation?)
+ ** This shares with the theory a Tableau, and a PartialModel that:
+ ** - satisfies the equalities in the Tableau, and
+ ** - the assignment for the non-basic variables satisfies their bounds.
+ ** This is required to either produce a conflict or satisifying PartialModel.
+ ** Further, we require being told when a basic variable updates its value.
+ **
+ ** During the Simplex search we maintain a queue of variables.
+ ** The queue is required to contain all of the basic variables that voilate their bounds.
+ ** As elimination from the queue is more efficient to be done lazily,
+ ** we do not maintain that the queue of variables needs to be only basic variables or only
+ ** variables that satisfy their bounds.
+ **
+ ** The simplex procedure roughly follows Alberto's thesis. (citation?)
+ ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction
+ ** Documentation for the available options.)
+ ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that
+ ** Leonardo invented this first.)
+ ** After this, Bland's pivot rule is invoked.
+ **
+ ** During this proccess, we periodically inspect the queue of variables to
+ ** 1) remove now extraneous extries,
+ ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the
+ ** current queue heuristics, and
+ ** 3) detect multiple conflicts.
+ **
+ ** Conflicts are greedily slackened to use the weakest bounds that still produce the
+ ** conflict.
+ **
+ ** Extra things tracked atm: (Subject to change at Tim's whims)
+ ** - A superset of all of the newly pivoted variables.
+ ** - A queue of additional conflicts that were discovered by Simplex.
+ ** These are theory valid and are currently turned into lemmas
+ **/
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "theory/arith/simplex.h"
+#include "util/dense_map.h"
+#include "util/statistics_registry.h"
+#include <stdint.h>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class SumOfInfeasibilitiesSPD : public SimplexDecisionProcedure {
+public:
+ SumOfInfeasibilitiesSPD(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
+
+ Result::Sat findModel(bool exactResult);
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+private:
+ /** The current sum of infeasibilities variable. */
+ ArithVar d_soiVar;
+
+ // dual like
+ // - found conflict
+ // - satisfied error set
+ Result::Sat sumOfInfeasibilities();
+
+ // static const uint32_t PENALTY = 4;
+ // DenseMultiset d_scores;
+ // void decreasePenalties(){ d_scores.removeOneOfEverything(); }
+ // uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
+ // void setPenalty(ArithVar x, WitnessImprovement w){
+ // if(improvement(w)){
+ // if(d_scores.count(x) > 0){
+ // d_scores.removeAll(x);
+ // }
+ // }else{
+ // d_scores.setCount(x, PENALTY);
+ // }
+ // }
+
+ int32_t d_pivotBudget;
+ // enum PivotImprovement {
+ // ErrorDropped,
+ // NonDegenerate,
+ // HeuristicDegenerate,
+ // BlandsDegenerate
+ // };
+
+ WitnessImprovement d_prevWitnessImprovement;
+ uint32_t d_witnessImprovementInARow;
+
+ uint32_t degeneratePivotsInARow() const;
+
+ static const uint32_t s_focusThreshold = 6;
+ static const uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+ static const uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+ DenseMap<uint32_t> d_leavingCountSinceImprovement;
+ void increaseLeavingCount(ArithVar x){
+ if(!d_leavingCountSinceImprovement.isKey(x)){
+ d_leavingCountSinceImprovement.set(x,1);
+ }else{
+ (d_leavingCountSinceImprovement.get(x))++;
+ }
+ }
+ LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+ bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+ d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+ return useBlands ?
+ &LinearEqualityModule::preferWitness<false>:
+ &LinearEqualityModule::preferWitness<true>;
+ }
+
+ bool debugSOI(WitnessImprovement w, std::ostream& out, int instance) const;
+
+ void debugPrintSignal(ArithVar updated) const;
+
+ ArithVarVec d_sgnDisagreements;
+
+ void logPivot(WitnessImprovement w);
+
+ void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+ UpdateInfo selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf,
+ LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+ // UpdateInfo selectUpdateForDualLike(ArithVar basic){
+ // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+ // LinearEqualityModule::UpdatePreferenceFunction upf =
+ // &LinearEqualityModule::preferWitness<true>;
+ // LinearEqualityModule::VarPreferenceFunction bpf =
+ // &LinearEqualityModule::minVarOrder;
+ // return selectPrimalUpdate(basic, upf, bpf);
+ // }
+
+ // UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+ // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+ // LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
+ // &LinearEqualityModule::preferWitness<false>:
+ // &LinearEqualityModule::preferWitness<true>;
+
+ // LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ // &LinearEqualityModule::minVarOrder :
+ // &LinearEqualityModule::minRowLength;
+ // bpf = &LinearEqualityModule::minVarOrder;
+
+ // return selectPrimalUpdate(basic, upf, bpf);
+ // }
+ // WitnessImprovement selectFocusImproving() ;
+ WitnessImprovement soiRound();
+ WitnessImprovement SOIConflict();
+ std::vector< ArithVarVec > greedyConflictSubsets();
+ Node generateSOIConflict(const ArithVarVec& subset);
+
+ // WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+ // WitnessImprovement focusDownToLastHalf();
+ // WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+ // WitnessImprovement focusDownToJust(ArithVar v);
+
+
+ void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ bool initialProcessSignals(){
+ TimerStat &timer = d_statistics.d_initialSignalsTime;
+ IntStat& conflictStat = d_statistics.d_initialConflicts;
+ return standardProcessSignals(timer, conflictStat);
+ }
+ unsigned trySet(const ArithVarVec& set);
+ unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ TimerStat d_initialSignalsTime;
+ IntStat d_initialConflicts;
+
+ IntStat d_soiFoundUnsat;
+ IntStat d_soiFoundSat;
+ IntStat d_soiMissed;
+
+ IntStat d_soiConflicts;
+ IntStat d_hasToBeMinimal;
+ IntStat d_maybeNotMinimal;
+
+ TimerStat d_soiTimer;
+ TimerStat d_soiFocusConstructionTimer;
+ TimerStat d_soiConflictMinimization;
+ TimerStat d_selectUpdateForSOI;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(uint32_t& pivots);
+ ~Statistics();
+ } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/tableau.cpp b/src/theory/arith/tableau.cpp
new file mode 100644
index 000000000..c54b0857a
--- /dev/null
+++ b/src/theory/arith/tableau.cpp
@@ -0,0 +1,179 @@
+#include "theory/arith/tableau.h"
+
+using namespace std;
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+
+void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic, CoefficientChangeCallback& cb){
+ Assert(isBasic(oldBasic));
+ Assert(!isBasic(newBasic));
+ Assert(d_mergeBuffer.empty());
+
+ Debug("tableau") << "Tableau::pivot(" << oldBasic <<", " << newBasic <<")" << endl;
+
+ RowIndex ridx = basicToRowIndex(oldBasic);
+
+ rowPivot(oldBasic, newBasic, cb);
+ Assert(ridx == basicToRowIndex(newBasic));
+
+ loadRowIntoBuffer(ridx);
+
+ ColIterator colIter = colIterator(newBasic);
+ while(!colIter.atEnd()){
+ EntryID id = colIter.getID();
+ Entry& entry = d_entries.get(id);
+
+ ++colIter; //needs to be incremented before the variable is removed
+ if(entry.getRowIndex() == ridx){ continue; }
+
+ RowIndex to = entry.getRowIndex();
+ Rational coeff = entry.getCoefficient();
+ if(cb.canUseRow(to)){
+ rowPlusBufferTimesConstant(to, coeff, cb);
+ }else{
+ rowPlusBufferTimesConstant(to, coeff);
+ }
+ }
+ clearBuffer();
+
+ //Clear the column for used for this variable
+
+ Assert(d_mergeBuffer.empty());
+ Assert(!isBasic(oldBasic));
+ Assert(isBasic(newBasic));
+ Assert(getColLength(newBasic) == 1);
+}
+
+/**
+ * Changes basic to newbasic (a variable on the row).
+ */
+void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb){
+ Assert(isBasic(basicOld));
+ Assert(!isBasic(basicNew));
+
+ RowIndex rid = basicToRowIndex(basicOld);
+
+ EntryID newBasicID = findOnRow(rid, basicNew);
+
+ Assert(newBasicID != ENTRYID_SENTINEL);
+
+ Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
+ const Rational& a_rs = newBasicEntry.getCoefficient();
+ int a_rs_sgn = a_rs.sgn();
+ Rational negInverseA_rs = -(a_rs.inverse());
+
+ for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
+ EntryID id = i.getID();
+ Tableau::Entry& entry = d_entries.get(id);
+
+ entry.getCoefficient() *= negInverseA_rs;
+ }
+
+ d_basic2RowIndex.remove(basicOld);
+ d_basic2RowIndex.set(basicNew, rid);
+ d_rowIndex2basic.set(rid, basicNew);
+
+ cb.swap(basicOld, basicNew, a_rs_sgn);
+}
+
+
+
+void Tableau::addRow(ArithVar basic,
+ const std::vector<Rational>& coefficients,
+ const std::vector<ArithVar>& variables)
+{
+ Assert(basic < getNumColumns());
+
+ Assert(coefficients.size() == variables.size() );
+ Assert(!isBasic(basic));
+
+ RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
+ addEntry(newRow, basic, Rational(-1));
+
+ Assert(!d_basic2RowIndex.isKey(basic));
+ Assert(!d_rowIndex2basic.isKey(newRow));
+
+ d_basic2RowIndex.set(basic, newRow);
+ d_rowIndex2basic.set(newRow, basic);
+
+
+ if(Debug.isOn("matrix")){ printMatrix(); }
+
+ NoEffectCCCB noeffect;
+ NoEffectCCCB* nep = &noeffect;
+ CoefficientChangeCallback* cccb = static_cast<CoefficientChangeCallback*>(nep);
+
+ vector<Rational>::const_iterator coeffIter = coefficients.begin();
+ vector<ArithVar>::const_iterator varsIter = variables.begin();
+ vector<ArithVar>::const_iterator varsEnd = variables.end();
+ for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
+ ArithVar var = *varsIter;
+
+ if(isBasic(var)){
+ Rational coeff = *coeffIter;
+
+ RowIndex ri = basicToRowIndex(var);
+
+ loadRowIntoBuffer(ri);
+ rowPlusBufferTimesConstant(newRow, coeff, *cccb);
+ clearBuffer();
+ }
+ }
+
+ if(Debug.isOn("matrix")) { printMatrix(); }
+
+ Assert(debugNoZeroCoefficients(newRow));
+ Assert(debugMatchingCountsForRow(newRow));
+ Assert(getColLength(basic) == 1);
+}
+
+void Tableau::removeBasicRow(ArithVar basic){
+ RowIndex rid = basicToRowIndex(basic);
+
+ removeRow(rid);
+ d_basic2RowIndex.remove(basic);
+ d_rowIndex2basic.remove(rid);
+}
+
+void Tableau::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb){
+ if(!mult.isZero()){
+ RowIndex to_idx = basicToRowIndex(to);
+ addEntry(to_idx, from, mult); // Add an entry to be cancelled out
+ RowIndex from_idx = basicToRowIndex(from);
+
+ cb.update(to_idx, from, 0, mult.sgn());
+
+ loadRowIntoBuffer(from_idx);
+ rowPlusBufferTimesConstant(to_idx, mult, cb);
+ clearBuffer();
+ }
+}
+
+uint32_t Tableau::rowComplexity(ArithVar basic) const{
+ uint32_t complexity = 0;
+ for(RowIterator i = basicRowIterator(basic); !i.atEnd(); ++i){
+ const Entry& e = *i;
+ complexity += e.getCoefficient().complexity();
+ }
+ return complexity;
+}
+
+double Tableau::avgRowComplexity() const{
+ double sum = 0;
+ uint32_t rows = 0;
+ for(BasicIterator i = beginBasic(), i_end = endBasic(); i != i_end; ++i){
+ sum += rowComplexity(*i);
+ rows++;
+ }
+ return (rows == 0) ? 0 : (sum/rows);
+}
+
+void Tableau::printBasicRow(ArithVar basic, std::ostream& out){
+ printRow(basicToRowIndex(basic), out);
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h
new file mode 100644
index 000000000..8b6ef1df6
--- /dev/null
+++ b/src/theory/arith/tableau.h
@@ -0,0 +1,143 @@
+#include "cvc4_private.h"
+
+#pragma once
+
+#include "util/dense_map.h"
+#include "util/rational.h"
+#include "theory/arith/arithvar.h"
+#include "theory/arith/matrix.h"
+#include <vector>
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+/**
+ * A Tableau is a Rational matrix that keeps its rows in solved form.
+ * Each row has a basic variable with coefficient -1 that is solved.
+ * Tableau is optimized for pivoting.
+ * The tableau should only be updated via pivot calls.
+ */
+class Tableau : public Matrix<Rational> {
+public:
+private:
+ typedef DenseMap<RowIndex> BasicToRowMap;
+ // Set of all of the basic variables in the tableau.
+ // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
+ BasicToRowMap d_basic2RowIndex;
+
+ // RowIndex |-> Basic Variable
+ typedef DenseMap<ArithVar> RowIndexToBasicMap;
+ RowIndexToBasicMap d_rowIndex2basic;
+
+public:
+
+ Tableau() : Matrix<Rational>(Rational(0)) {}
+
+ typedef Matrix<Rational>::ColIterator ColIterator;
+ typedef Matrix<Rational>::RowIterator RowIterator;
+ typedef BasicToRowMap::const_iterator BasicIterator;
+
+ typedef MatrixEntry<Rational> Entry;
+
+ bool isBasic(ArithVar v) const{
+ return d_basic2RowIndex.isKey(v);
+ }
+
+ void debugPrintIsBasic(ArithVar v) const {
+ if(isBasic(v)){
+ Warning() << v << " is basic." << std::endl;
+ }else{
+ Warning() << v << " is non-basic." << std::endl;
+ }
+ }
+
+ BasicIterator beginBasic() const {
+ return d_basic2RowIndex.begin();
+ }
+ BasicIterator endBasic() const {
+ return d_basic2RowIndex.end();
+ }
+
+ RowIndex basicToRowIndex(ArithVar x) const {
+ return d_basic2RowIndex[x];
+ }
+
+ ArithVar rowIndexToBasic(RowIndex rid) const {
+ Assert(rid < d_rowIndex2basic.size());
+ return d_rowIndex2basic[rid];
+ }
+
+ ColIterator colIterator(ArithVar x) const {
+ return getColumn(x).begin();
+ }
+
+ RowIterator basicRowIterator(ArithVar basic) const {
+ return getRow(basicToRowIndex(basic)).begin();
+ }
+
+ const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
+ return findEntry(basicToRowIndex(basic), col);
+ }
+
+ /**
+ * Adds a row to the tableau.
+ * The new row is equivalent to:
+ * basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
+ * preconditions:
+ * basicVar is already declared to be basic
+ * basicVar does not have a row associated with it in the tableau.
+ *
+ * Note: each variables[i] does not have to be non-basic.
+ * Pivoting will be mimicked if it is basic.
+ */
+ void addRow(ArithVar basicVar,
+ const std::vector<Rational>& coeffs,
+ const std::vector<ArithVar>& variables);
+
+ /**
+ * preconditions:
+ * x_r is basic,
+ * x_s is non-basic, and
+ * a_rs != 0.
+ */
+ void pivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+ void removeBasicRow(ArithVar basic);
+
+ uint32_t basicRowLength(ArithVar basic) const{
+ RowIndex ridx = basicToRowIndex(basic);
+ return getRowLength(ridx);
+ }
+
+ /**
+ * to += mult * from
+ * replacing from with its row.
+ */
+ void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb);
+
+ void directlyAddToCoefficient(ArithVar rowVar, ArithVar col, const Rational& mult, CoefficientChangeCallback& cb){
+ RowIndex ridx = basicToRowIndex(rowVar);
+ manipulateRowEntry(ridx, col, mult, cb);
+ }
+
+ /* Returns the complexity of a row in the tableau. */
+ uint32_t rowComplexity(ArithVar basic) const;
+
+ /* Returns the average complexity of the rows in the tableau. */
+ double avgRowComplexity() const;
+
+ void printBasicRow(ArithVar basic, std::ostream& out);
+
+private:
+ /* Changes the basic variable on the row for basicOld to basicNew. */
+ void rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+};/* class Tableau */
+
+
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/tableau_sizes.cpp b/src/theory/arith/tableau_sizes.cpp
new file mode 100644
index 000000000..7c44f6791
--- /dev/null
+++ b/src/theory/arith/tableau_sizes.cpp
@@ -0,0 +1,18 @@
+#include "theory/arith/tableau_sizes.h"
+#include "theory/arith/tableau.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+uint32_t TableauSizes::getRowLength(ArithVar b) const {
+ return d_tab->basicRowLength(b);
+}
+
+uint32_t TableauSizes::getColumnLength(ArithVar x) const {
+ return d_tab->getColLength(x);
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/tableau_sizes.h b/src/theory/arith/tableau_sizes.h
new file mode 100644
index 000000000..d6b820300
--- /dev/null
+++ b/src/theory/arith/tableau_sizes.h
@@ -0,0 +1,28 @@
+
+#include "cvc4_private.h"
+
+#pragma once
+
+#include <stdint.h>
+#include "theory/arith/arithvar.h"
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class Tableau;
+
+class TableauSizes {
+private:
+ const Tableau* d_tab;
+public:
+ TableauSizes(const Tableau* tab): d_tab(tab){}
+
+ uint32_t getRowLength(ArithVar b) const;
+ uint32_t getColumnLength(ArithVar x) const;
+}; /* TableauSizes */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp
index bc7b4b278..c0442da90 100644
--- a/src/theory/arith/theory_arith.cpp
+++ b/src/theory/arith/theory_arith.cpp
@@ -1,11 +1,11 @@
/********************* */
/*! \file theory_arith.cpp
** \verbatim
- ** Original author: Tim King
+ ** Original author: taking
** Major contributors: none
- ** Minor contributors (to current version): Kshitij Bansal, Andrew Reynolds, Morgan Deters, Dejan Jovanovic
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** Minor contributors (to current version): kshitij, ajreynol, mdeters, dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
@@ -15,35 +15,8 @@
** \todo document this file
**/
-#include "expr/node.h"
-#include "expr/kind.h"
-#include "expr/metakind.h"
-#include "expr/node_builder.h"
-
-#include "theory/valuation.h"
-#include "theory/rewriter.h"
-
-#include "util/rational.h"
-#include "util/integer.h"
-#include "util/boolean_simplification.h"
-#include "util/dense_map.h"
-
-#include "smt/logic_exception.h"
-
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/matrix.h"
-
-#include "theory/arith/arith_rewriter.h"
-#include "theory/arith/constraint.h"
#include "theory/arith/theory_arith.h"
-#include "theory/arith/normal_form.h"
-#include "theory/model.h"
-
-#include "theory/arith/options.h"
-
-#include <stdint.h>
+#include "theory/arith/theory_arith_private.h"
using namespace std;
using namespace CVC4::kind;
@@ -52,2516 +25,65 @@ namespace CVC4 {
namespace theory {
namespace arith {
-const uint32_t RESET_START = 2;
-
-
-TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) :
- Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe),
- d_nlIncomplete( false),
- d_qflraStatus(Result::SAT_UNKNOWN),
- d_unknownsInARow(0),
- d_hasDoneWorkSinceCut(false),
- d_learner(u),
- d_numberOfVariables(0),
- d_pool(),
- d_setupLiteralCallback(this),
- d_assertionsThatDoNotMatchTheirLiterals(c),
- d_nextIntegerCheckVar(0),
- d_constantIntegerVariables(c),
- d_diseqQueue(c, false),
- d_currentPropagationList(),
- d_learnedBounds(c),
- d_partialModel(c, d_deltaComputeCallback),
- d_tableau(),
- d_linEq(d_partialModel, d_tableau, d_basicVarModelUpdateCallBack),
- d_diosolver(c),
- d_restartsCounter(0),
- d_tableauSizeHasBeenModified(false),
- d_tableauResetDensity(1.6),
- d_tableauResetPeriod(10),
- d_conflicts(c),
- d_raiseConflict(d_conflicts),
- d_tempVarMalloc(*this),
- d_congruenceManager(c, d_constraintDatabase, d_setupLiteralCallback, d_arithvarNodeMap, d_raiseConflict),
- d_simplex(d_linEq, d_raiseConflict),
- d_constraintDatabase(c, u, d_arithvarNodeMap, d_congruenceManager, d_raiseConflict),
- d_deltaComputeCallback(this),
- d_basicVarModelUpdateCallBack(d_simplex),
- d_DELTA_ZERO(0),
- d_fullCheckCounter(0),
- d_cutsInContext(c,0),
- d_statistics()
-{
-}
-
-TheoryArith::~TheoryArith(){}
-
-void TheoryArith::setMasterEqualityEngine(eq::EqualityEngine* eq) {
- d_congruenceManager.setMasterEqualityEngine(eq);
-}
-
-Node skolemFunction(const std::string& name, TypeNode dom, TypeNode range){
- NodeManager* currNM = NodeManager::currentNM();
- TypeNode functionType = currNM->mkFunctionType(dom, range);
- return currNM->mkSkolem(name, functionType);
-}
-
-Node TheoryArith::getRealDivideBy0Func(){
- Assert(!getLogicInfo().isLinear());
- Assert(getLogicInfo().areRealsUsed());
-
- if(d_realDivideBy0Func.isNull()){
- TypeNode realType = NodeManager::currentNM()->realType();
- d_realDivideBy0Func = skolemFunction("/by0_$$", realType, realType);
- }
- return d_realDivideBy0Func;
-}
-
-Node TheoryArith::getIntDivideBy0Func(){
- Assert(!getLogicInfo().isLinear());
- Assert(getLogicInfo().areIntegersUsed());
-
- if(d_intDivideBy0Func.isNull()){
- TypeNode intType = NodeManager::currentNM()->integerType();
- d_intDivideBy0Func = skolemFunction("divby0_$$", intType, intType);
- }
- return d_intDivideBy0Func;
-}
-
-Node TheoryArith::getIntModulusBy0Func(){
- Assert(!getLogicInfo().isLinear());
- Assert(getLogicInfo().areIntegersUsed());
-
- if(d_intModulusBy0Func.isNull()){
- TypeNode intType = NodeManager::currentNM()->integerType();
- d_intModulusBy0Func = skolemFunction("modby0_$$", intType, intType);
- }
- return d_intModulusBy0Func;
-}
-
-TheoryArith::ModelException::ModelException(TNode n, const char* msg) throw (){
- stringstream ss;
- ss << "Cannot construct a model for " << n << " as " << endl << msg;
- setMessage(ss.str());
-}
-TheoryArith::ModelException::~ModelException() throw (){ }
-
-
-TheoryArith::Statistics::Statistics():
- d_statAssertUpperConflicts("theory::arith::AssertUpperConflicts", 0),
- d_statAssertLowerConflicts("theory::arith::AssertLowerConflicts", 0),
- d_statUserVariables("theory::arith::UserVariables", 0),
- d_statSlackVariables("theory::arith::SlackVariables", 0),
- d_statDisequalitySplits("theory::arith::DisequalitySplits", 0),
- d_statDisequalityConflicts("theory::arith::DisequalityConflicts", 0),
- d_simplifyTimer("theory::arith::simplifyTimer"),
- d_staticLearningTimer("theory::arith::staticLearningTimer"),
- d_presolveTime("theory::arith::presolveTime"),
- d_newPropTime("theory::arith::newPropTimer"),
- d_externalBranchAndBounds("theory::arith::externalBranchAndBounds",0),
- d_initialTableauSize("theory::arith::initialTableauSize", 0),
- d_currSetToSmaller("theory::arith::currSetToSmaller", 0),
- d_smallerSetToCurr("theory::arith::smallerSetToCurr", 0),
- d_restartTimer("theory::arith::restartTimer"),
- d_boundComputationTime("theory::arith::bound::time"),
- d_boundComputations("theory::arith::bound::boundComputations",0),
- d_boundPropagations("theory::arith::bound::boundPropagations",0),
- d_unknownChecks("theory::arith::status::unknowns", 0),
- d_maxUnknownsInARow("theory::arith::status::maxUnknownsInARow", 0),
- d_avgUnknownsInARow("theory::arith::status::avgUnknownsInARow"),
- d_revertsOnConflicts("theory::arith::status::revertsOnConflicts",0),
- d_commitsOnConflicts("theory::arith::status::commitsOnConflicts",0),
- d_nontrivialSatChecks("theory::arith::status::nontrivialSatChecks",0)
-{
- StatisticsRegistry::registerStat(&d_statAssertUpperConflicts);
- StatisticsRegistry::registerStat(&d_statAssertLowerConflicts);
-
- StatisticsRegistry::registerStat(&d_statUserVariables);
- StatisticsRegistry::registerStat(&d_statSlackVariables);
- StatisticsRegistry::registerStat(&d_statDisequalitySplits);
- StatisticsRegistry::registerStat(&d_statDisequalityConflicts);
- StatisticsRegistry::registerStat(&d_simplifyTimer);
- StatisticsRegistry::registerStat(&d_staticLearningTimer);
-
- StatisticsRegistry::registerStat(&d_presolveTime);
- StatisticsRegistry::registerStat(&d_newPropTime);
-
- StatisticsRegistry::registerStat(&d_externalBranchAndBounds);
-
- StatisticsRegistry::registerStat(&d_initialTableauSize);
- StatisticsRegistry::registerStat(&d_currSetToSmaller);
- StatisticsRegistry::registerStat(&d_smallerSetToCurr);
- StatisticsRegistry::registerStat(&d_restartTimer);
-
- StatisticsRegistry::registerStat(&d_boundComputationTime);
- StatisticsRegistry::registerStat(&d_boundComputations);
- StatisticsRegistry::registerStat(&d_boundPropagations);
-
- StatisticsRegistry::registerStat(&d_unknownChecks);
- StatisticsRegistry::registerStat(&d_maxUnknownsInARow);
- StatisticsRegistry::registerStat(&d_avgUnknownsInARow);
- StatisticsRegistry::registerStat(&d_revertsOnConflicts);
- StatisticsRegistry::registerStat(&d_commitsOnConflicts);
- StatisticsRegistry::registerStat(&d_nontrivialSatChecks);
-}
-
-TheoryArith::Statistics::~Statistics(){
- StatisticsRegistry::unregisterStat(&d_statAssertUpperConflicts);
- StatisticsRegistry::unregisterStat(&d_statAssertLowerConflicts);
-
- StatisticsRegistry::unregisterStat(&d_statUserVariables);
- StatisticsRegistry::unregisterStat(&d_statSlackVariables);
- StatisticsRegistry::unregisterStat(&d_statDisequalitySplits);
- StatisticsRegistry::unregisterStat(&d_statDisequalityConflicts);
- StatisticsRegistry::unregisterStat(&d_simplifyTimer);
- StatisticsRegistry::unregisterStat(&d_staticLearningTimer);
-
- StatisticsRegistry::unregisterStat(&d_presolveTime);
- StatisticsRegistry::unregisterStat(&d_newPropTime);
-
- StatisticsRegistry::unregisterStat(&d_externalBranchAndBounds);
-
- StatisticsRegistry::unregisterStat(&d_initialTableauSize);
- StatisticsRegistry::unregisterStat(&d_currSetToSmaller);
- StatisticsRegistry::unregisterStat(&d_smallerSetToCurr);
- StatisticsRegistry::unregisterStat(&d_restartTimer);
-
- StatisticsRegistry::unregisterStat(&d_boundComputationTime);
- StatisticsRegistry::unregisterStat(&d_boundComputations);
- StatisticsRegistry::unregisterStat(&d_boundPropagations);
-
- StatisticsRegistry::unregisterStat(&d_unknownChecks);
- StatisticsRegistry::unregisterStat(&d_maxUnknownsInARow);
- StatisticsRegistry::unregisterStat(&d_avgUnknownsInARow);
- StatisticsRegistry::unregisterStat(&d_revertsOnConflicts);
- StatisticsRegistry::unregisterStat(&d_commitsOnConflicts);
- StatisticsRegistry::unregisterStat(&d_nontrivialSatChecks);
-}
-
-void TheoryArith::revertOutOfConflict(){
- d_partialModel.revertAssignmentChanges();
- clearUpdates();
- d_currentPropagationList.clear();
-}
-
-void TheoryArith::clearUpdates(){
- d_updatedBounds.purge();
-}
-
-void TheoryArith::zeroDifferenceDetected(ArithVar x){
- Assert(d_congruenceManager.isWatchedVariable(x));
- Assert(d_partialModel.upperBoundIsZero(x));
- Assert(d_partialModel.lowerBoundIsZero(x));
-
- Constraint lb = d_partialModel.getLowerBoundConstraint(x);
- Constraint ub = d_partialModel.getUpperBoundConstraint(x);
-
- if(lb->isEquality()){
- d_congruenceManager.watchedVariableIsZero(lb);
- }else if(ub->isEquality()){
- d_congruenceManager.watchedVariableIsZero(ub);
- }else{
- d_congruenceManager.watchedVariableIsZero(lb, ub);
- }
-}
-
-/* procedure AssertLower( x_i >= c_i ) */
-bool TheoryArith::AssertLower(Constraint constraint){
- Assert(constraint != NullConstraint);
- Assert(constraint->isLowerBound());
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Debug("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
-
- Assert(!isInteger(x_i) || c_i.isIntegral());
+TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe)
+ : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe)
+ , d_internal(new TheoryArithPrivate(*this, c, u, out, valuation, logicInfo, qe))
+{}
- //TODO Relax to less than?
- if(d_partialModel.lessThanLowerBound(x_i, c_i)){
- return false; //sat
- }
-
- int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
- if(cmpToUB > 0){ // c_i < \lowerbound(x_i)
- Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i);
- Node conflict = ConstraintValue::explainConflict(ubc, constraint);
- Debug("arith") << "AssertLower conflict " << conflict << endl;
- ++(d_statistics.d_statAssertLowerConflicts);
- d_raiseConflict(conflict);
- return true;
- }else if(cmpToUB == 0){
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Debug("dio::push") << x_i << endl;
- }
- Constraint ub = d_partialModel.getUpperBoundConstraint(x_i);
-
- if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
- // if it is not a watched variable report it
- // if it is is a watched variable and c_i == 0,
- // let zeroDifferenceDetected(x_i) catch this
- d_congruenceManager.equalsConstant(constraint, ub);
- }
-
- const ValueCollection& vc = constraint->getValueCollection();
- if(vc.hasDisequality()){
- Assert(vc.hasEquality());
- const Constraint eq = vc.getEquality();
- const Constraint diseq = vc.getDisequality();
- if(diseq->isTrue()){
- //const Constraint ub = vc.getUpperBound();
- Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint);
-
- ++(d_statistics.d_statDisequalityConflicts);
- Debug("eq") << " assert lower conflict " << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }else if(!eq->isTrue()){
- Debug("eq") << "lb == ub, propagate eq" << eq << endl;
- eq->impliedBy(constraint, d_partialModel.getUpperBoundConstraint(x_i));
- // do not need to add to d_learnedBounds
- }
- }
- }else{
- Assert(cmpToUB < 0);
- const ValueCollection& vc = constraint->getValueCollection();
-
- if(vc.hasDisequality()){
- const Constraint diseq = vc.getDisequality();
- if(diseq->isTrue()){
- const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
-
- if(ub->hasProof()){
- Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint);
- Debug("eq") << " assert upper conflict " << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }else if(!ub->negationHasProof()){
- Constraint negUb = ub->getNegation();
- negUb->impliedBy(constraint, diseq);
- d_learnedBounds.push_back(negUb);
- }
- }
- }
- }
-
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
-
- d_partialModel.setLowerBoundConstraint(constraint);
-
- if(d_congruenceManager.isWatchedVariable(x_i)){
- int sgn = c_i.sgn();
- if(sgn > 0){
- d_congruenceManager.watchedVariableCannotBeZero(constraint);
- }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){
- zeroDifferenceDetected(x_i);
- }
- }
-
- d_updatedBounds.softAdd(x_i);
-
- if(Debug.isOn("model")) {
- Debug("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(d_partialModel.getAssignment(x_i) < c_i){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_simplex.updateBasic(x_i);
- }
-
- if(Debug.isOn("model")) {
- Debug("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false; //sat
-}
-
-/* procedure AssertUpper( x_i <= c_i) */
-bool TheoryArith::AssertUpper(Constraint constraint){
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Debug("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
- AssertArgument(constraint != NullConstraint,
- "AssertUpper() called on a NullConstraint.");
- Assert(constraint->isUpperBound());
-
- //Too strong because of rounding with integers
- //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- Debug("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
-
- if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
- return false; //sat
- }
-
- // cmpToLb = \lowerbound(x_i).cmp(c_i)
- int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
- if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i)
- Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i);
- Node conflict = ConstraintValue::explainConflict(lbc, constraint);
- Debug("arith") << "AssertUpper conflict " << conflict << endl;
- ++(d_statistics.d_statAssertUpperConflicts);
- d_raiseConflict(conflict);
- return true;
- }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Debug("dio::push") << x_i << endl;
- }
- Constraint lb = d_partialModel.getLowerBoundConstraint(x_i);
- if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
- // if it is not a watched variable report it
- // if it is is a watched variable and c_i == 0,
- // let zeroDifferenceDetected(x_i) catch this
- d_congruenceManager.equalsConstant(lb, constraint);
- }
-
- const ValueCollection& vc = constraint->getValueCollection();
- if(vc.hasDisequality()){
- Assert(vc.hasEquality());
- const Constraint diseq = vc.getDisequality();
- const Constraint eq = vc.getEquality();
- if(diseq->isTrue()){
- Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint);
- Debug("eq") << " assert upper conflict " << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }else if(!eq->isTrue()){
- Debug("eq") << "lb == ub, propagate eq" << eq << endl;
- eq->impliedBy(constraint, d_partialModel.getLowerBoundConstraint(x_i));
- //do not bother to add to d_learnedBounds
- }
- }
- }else if(cmpToLB > 0){
- const ValueCollection& vc = constraint->getValueCollection();
- if(vc.hasDisequality()){
- const Constraint diseq = vc.getDisequality();
- if(diseq->isTrue()){
- const Constraint lb =
- d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
- if(lb->hasProof()){
- Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint);
- Debug("eq") << " assert upper conflict " << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }else if(!lb->negationHasProof()){
- Constraint negLb = lb->getNegation();
- negLb->impliedBy(constraint, diseq);
- //if(!negLb->canBePropagated()){
- d_learnedBounds.push_back(negLb);
- //}//otherwise let this be propagated/asserted later
- }
- }
- }
- }
-
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
- //It is fine if this is NullConstraint
-
- d_partialModel.setUpperBoundConstraint(constraint);
-
- if(d_congruenceManager.isWatchedVariable(x_i)){
- int sgn = c_i.sgn();
- if(sgn < 0){
- d_congruenceManager.watchedVariableCannotBeZero(constraint);
- }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){
- zeroDifferenceDetected(x_i);
- }
- }
-
- d_updatedBounds.softAdd(x_i);
-
- if(Debug.isOn("model")) {
- Debug("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(d_partialModel.getAssignment(x_i) > c_i){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_simplex.updateBasic(x_i);
- }
-
- if(Debug.isOn("model")) {
- Debug("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false; //sat
+TheoryArith::~TheoryArith(){
+ delete d_internal;
}
-
-/* procedure AssertEquality( x_i == c_i ) */
-bool TheoryArith::AssertEquality(Constraint constraint){
- AssertArgument(constraint != NullConstraint,
- "AssertUpper() called on a NullConstraint.");
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Debug("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
-
- //Should be fine in integers
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
- int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
-
- // u_i <= c_i <= l_i
- // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
- if(cmpToUB >= 0 && cmpToLB <= 0){
- return false; //sat
- }
-
- if(cmpToUB > 0){
- Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i);
- Node conflict = ConstraintValue::explainConflict(ubc, constraint);
- Debug("arith") << "AssertEquality conflicts with upper bound " << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }
-
- if(cmpToLB < 0){
- Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i);
- Node conflict = ConstraintValue::explainConflict(lbc, constraint);
- Debug("arith") << "AssertEquality conflicts with lower bound" << conflict << endl;
- d_raiseConflict(conflict);
- return true;
- }
-
- Assert(cmpToUB <= 0);
- Assert(cmpToLB >= 0);
- Assert(cmpToUB < 0 || cmpToLB > 0);
-
-
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Debug("dio::push") << x_i << endl;
- }
-
- // Don't bother to check whether x_i != c_i is in d_diseq
- // The a and (not a) should never be on the fact queue
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
- d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
-
- d_partialModel.setUpperBoundConstraint(constraint);
- d_partialModel.setLowerBoundConstraint(constraint);
-
- if(d_congruenceManager.isWatchedVariable(x_i)){
- int sgn = c_i.sgn();
- if(sgn == 0){
- zeroDifferenceDetected(x_i);
- }else{
- d_congruenceManager.watchedVariableCannotBeZero(constraint);
- d_congruenceManager.equalsConstant(constraint);
- }
- }else{
- d_congruenceManager.equalsConstant(constraint);
- }
-
- d_updatedBounds.softAdd(x_i);
-
- if(Debug.isOn("model")) {
- Debug("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(!(d_partialModel.getAssignment(x_i) == c_i)){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_simplex.updateBasic(x_i);
- }
-
- if(Debug.isOn("model")) {
- Debug("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false;
+void TheoryArith::preRegisterTerm(TNode n){
+ d_internal->preRegisterTerm(n);
}
-
-/* procedure AssertDisequality( x_i != c_i ) */
-bool TheoryArith::AssertDisequality(Constraint constraint){
-
- AssertArgument(constraint != NullConstraint,
- "AssertUpper() called on a NullConstraint.");
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Debug("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
-
- //Should be fine in integers
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- if(d_congruenceManager.isWatchedVariable(x_i)){
- int sgn = c_i.sgn();
- if(sgn == 0){
- d_congruenceManager.watchedVariableCannotBeZero(constraint);
- }
- }
-
- const ValueCollection& vc = constraint->getValueCollection();
- if(vc.hasLowerBound() && vc.hasUpperBound()){
- const Constraint lb = vc.getLowerBound();
- const Constraint ub = vc.getUpperBound();
- if(lb->isTrue() && ub->isTrue()){
- //in conflict
- Debug("eq") << "explaining" << endl;
- ++(d_statistics.d_statDisequalityConflicts);
- Node conflict = ConstraintValue::explainConflict(constraint, lb, ub);
- d_raiseConflict(conflict);
- return true;
- }
- }
- if(vc.hasLowerBound() ){
- const Constraint lb = vc.getLowerBound();
- if(lb->isTrue()){
- const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
- Debug("eq") << "propagate UpperBound " << constraint << lb << ub << endl;
- const Constraint negUb = ub->getNegation();
- if(!negUb->isTrue()){
- negUb->impliedBy(constraint, lb);
- d_learnedBounds.push_back(negUb);
- }
- }
- }
- if(vc.hasUpperBound()){
- const Constraint ub = vc.getUpperBound();
- if(ub->isTrue()){
- const Constraint lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
-
- Debug("eq") << "propagate LowerBound " << constraint << lb << ub << endl;
- const Constraint negLb = lb->getNegation();
- if(!negLb->isTrue()){
- negLb->impliedBy(constraint, ub);
- d_learnedBounds.push_back(negLb);
- }
- }
- }
-
- bool split = constraint->isSplit();
-
- if(!split && c_i == d_partialModel.getAssignment(x_i)){
- Debug("eq") << "lemma now! " << constraint << endl;
- d_out->lemma(constraint->split());
- return false;
- }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
- Debug("eq") << "can drop as less than lb" << constraint << endl;
- }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
- Debug("eq") << "can drop as less than ub" << constraint << endl;
- }else if(!split){
- Debug("eq") << "push back" << constraint << endl;
- d_diseqQueue.push(constraint);
- d_partialModel.invalidateDelta();
- }else{
- Debug("eq") << "skipping already split " << constraint << endl;
- }
- return false;
+void TheoryArith::setMasterEqualityEngine(eq::EqualityEngine* eq) {
+ d_internal->setMasterEqualityEngine(eq);
}
void TheoryArith::addSharedTerm(TNode n){
- Debug("arith::addSharedTerm") << "addSharedTerm: " << n << endl;
- if(n.isConst()){
- d_partialModel.invalidateDelta();
- }
-
- d_congruenceManager.addSharedTerm(n);
- if(!n.isConst() && !isSetup(n)){
- Polynomial poly = Polynomial::parsePolynomial(n);
- Polynomial::iterator it = poly.begin();
- Polynomial::iterator it_end = poly.end();
- for (; it != it_end; ++ it) {
- Monomial m = *it;
- if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
- setupVariableList(m.getVarList());
- }
- }
- }
+ d_internal->addSharedTerm(n);
}
Node TheoryArith::ppRewrite(TNode atom) {
- Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
-
-
- if (atom.getKind() == kind::EQUAL && options::arithRewriteEq()) {
- Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1];
- Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1];
- Node rewritten = Rewriter::rewrite(leq.andNode(geq));
- Debug("arith::preprocess") << "arith::preprocess() : returning "
- << rewritten << endl;
- return rewritten;
- } else {
- return atom;
- }
+ return d_internal->ppRewrite(atom);
}
Theory::PPAssertStatus TheoryArith::ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
- TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
- Debug("simplify") << "TheoryArith::solve(" << in << ")" << endl;
-
-
- // Solve equalities
- Rational minConstant = 0;
- Node minMonomial;
- Node minVar;
- if (in.getKind() == kind::EQUAL) {
- Comparison cmp = Comparison::parseNormalForm(in);
-
- Polynomial left = cmp.getLeft();
- Polynomial right = cmp.getRight();
-
- Monomial m = left.getHead();
- if (m.getVarList().singleton()){
- VarList vl = m.getVarList();
- Node var = vl.getNode();
- if (var.getKind() == kind::VARIABLE){
- // if vl.isIntegral then m.getConstant().isOne()
- if(!vl.isIntegral() || m.getConstant().isOne()){
- minVar = var;
- }
- }
- }
-
- // Solve for variable
- if (!minVar.isNull()) {
- Polynomial right = cmp.getRight();
- Node elim = right.getNode();
- // ax + p = c -> (ax + p) -ax - c = -ax
- // x = (p - ax - c) * -1/a
- // Add the substitution if not recursive
- Assert(elim == Rewriter::rewrite(elim));
-
-
- static const unsigned MAX_SUB_SIZE = 2;
- if(right.size() > MAX_SUB_SIZE){
- Debug("simplify") << "TheoryArith::solve(): did not substitute due to the right hand side containing too many terms: " << minVar << ":" << elim << endl;
- Debug("simplify") << right.size() << endl;
- }else if(elim.hasSubterm(minVar)){
- Debug("simplify") << "TheoryArith::solve(): can't substitute due to recursive pattern with sharing: " << minVar << ":" << elim << endl;
- }else if (!minVar.getType().isInteger() || right.isIntegral()) {
- Assert(!elim.hasSubterm(minVar));
- // cannot eliminate integers here unless we know the resulting
- // substitution is integral
- Debug("simplify") << "TheoryArith::solve(): substitution " << minVar << " |-> " << elim << endl;
-
- outSubstitutions.addSubstitution(minVar, elim);
- return PP_ASSERT_STATUS_SOLVED;
- } else {
- Debug("simplify") << "TheoryArith::solve(): can't substitute b/c it's integer: " << minVar << ":" << minVar.getType() << " |-> " << elim << ":" << elim.getType() << endl;
- }
- }
- }
-
- // If a relation, remember the bound
- switch(in.getKind()) {
- case kind::LEQ:
- case kind::LT:
- case kind::GEQ:
- case kind::GT:
- if (in[0].isVar()) {
- d_learner.addBound(in);
- }
- break;
- default:
- // Do nothing
- break;
- }
-
- return PP_ASSERT_STATUS_UNSOLVED;
+ return d_internal->ppAssert(in, outSubstitutions);
}
void TheoryArith::ppStaticLearn(TNode n, NodeBuilder<>& learned) {
- TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
-
- d_learner.staticLearning(n, learned);
-}
-
-
-
-ArithVar TheoryArith::findShortestBasicRow(ArithVar variable){
- ArithVar bestBasic = ARITHVAR_SENTINEL;
- uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
-
- Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- Assert(entry.getColVar() == variable);
- RowIndex ridx = entry.getRowIndex();
- ArithVar basic = d_tableau.rowIndexToBasic(ridx);
- uint32_t rowLength = d_tableau.getRowLength(ridx);
- if((rowLength < bestRowLength) ||
- (rowLength == bestRowLength && basic < bestBasic)){
- bestBasic = basic;
- bestRowLength = rowLength;
- }
- }
- Assert(bestBasic == ARITHVAR_SENTINEL || bestRowLength < std::numeric_limits<uint32_t>::max());
- return bestBasic;
-}
-
-void TheoryArith::setupVariable(const Variable& x){
- Node n = x.getNode();
-
- Assert(!isSetup(n));
-
- ++(d_statistics.d_statUserVariables);
- requestArithVar(n,false);
- //ArithVar varN = requestArithVar(n,false);
- //setupInitialValue(varN);
-
- markSetup(n);
-
-
- if(x.isDivLike()){
- setupDivLike(x);
- }
-
-}
-
-void TheoryArith::setupVariableList(const VarList& vl){
- Assert(!vl.empty());
-
- TNode vlNode = vl.getNode();
- Assert(!isSetup(vlNode));
- Assert(!d_arithvarNodeMap.hasArithVar(vlNode));
-
- for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
- Variable var = *i;
-
- if(!isSetup(var.getNode())){
- setupVariable(var);
- }
- }
-
- if(!vl.singleton()){
- // vl is the product of at least 2 variables
- // vl : (* v1 v2 ...)
- if(getLogicInfo().isLinear()){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
- }
-
- d_out->setIncomplete();
- d_nlIncomplete = true;
-
- ++(d_statistics.d_statUserVariables);
- requestArithVar(vlNode, false);
- //ArithVar av = requestArithVar(vlNode, false);
- //setupInitialValue(av);
-
- markSetup(vlNode);
- }
-
- /* Note:
- * Only call markSetup if the VarList is not a singleton.
- * See the comment in setupPolynomail for more.
- */
-}
-
-void TheoryArith::cautiousSetupPolynomial(const Polynomial& p){
- if(p.containsConstant()){
- if(!p.isConstant()){
- Polynomial noConstant = p.getTail();
- if(!isSetup(noConstant.getNode())){
- setupPolynomial(noConstant);
- }
- }
- }else if(!isSetup(p.getNode())){
- setupPolynomial(p);
- }
-}
-
-void TheoryArith::setupDivLike(const Variable& v){
- Assert(v.isDivLike());
-
- if(getLogicInfo().isLinear()){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
- }
-
- Node vnode = v.getNode();
- Assert(isSetup(vnode)); // Otherwise there is some invariant breaking recursion
- Polynomial m = Polynomial::parsePolynomial(vnode[0]);
- Polynomial n = Polynomial::parsePolynomial(vnode[1]);
-
- cautiousSetupPolynomial(m);
- cautiousSetupPolynomial(n);
-
- Node lem;
- switch(vnode.getKind()){
- case DIVISION:
- case INTS_DIVISION:
- case INTS_MODULUS:
- lem = definingIteForDivLike(vnode);
- break;
- case DIVISION_TOTAL:
- lem = axiomIteForTotalDivision(vnode);
- break;
- case INTS_DIVISION_TOTAL:
- case INTS_MODULUS_TOTAL:
- lem = axiomIteForTotalIntDivision(vnode);
- break;
- default:
- /* intentionally blank */
- break;
- }
-
- if(!lem.isNull()){
- Debug("arith::div") << lem << endl;
- d_out->lemma(lem);
- }
-}
-
-Node TheoryArith::definingIteForDivLike(Node divLike){
- Kind k = divLike.getKind();
- Assert(k == DIVISION || k == INTS_DIVISION || k == INTS_MODULUS);
- // (for all ((n Real) (d Real))
- // (=
- // (DIVISION n d)
- // (ite (= d 0)
- // (APPLY [div_0_skolem_function] n)
- // (DIVISION_TOTAL x y))))
-
- Polynomial n = Polynomial::parsePolynomial(divLike[0]);
- Polynomial d = Polynomial::parsePolynomial(divLike[1]);
-
- NodeManager* currNM = NodeManager::currentNM();
- Node dEq0 = currNM->mkNode(EQUAL, d.getNode(), mkRationalNode(0));
-
- Kind kTotal = (k == DIVISION) ? DIVISION_TOTAL :
- (k == INTS_DIVISION) ? INTS_DIVISION_TOTAL : INTS_MODULUS_TOTAL;
-
- Node by0Func = (k == DIVISION) ? getRealDivideBy0Func():
- (k == INTS_DIVISION) ? getIntDivideBy0Func() : getIntModulusBy0Func();
-
-
- Debug("arith::div") << divLike << endl;
- Debug("arith::div") << by0Func << endl;
-
- Node divTotal = currNM->mkNode(kTotal, n.getNode(), d.getNode());
- Node divZero = currNM->mkNode(APPLY_UF, by0Func, n.getNode());
-
- Node defining = divLike.eqNode(dEq0.iteNode( divZero, divTotal));
-
- return defining;
-}
-
-Node TheoryArith::axiomIteForTotalDivision(Node div_tot){
- Assert(div_tot.getKind() == DIVISION_TOTAL);
-
- // Inverse of multiplication axiom:
- // (for all ((n Real) (d Real))
- // (ite (= d 0)
- // (= (DIVISION_TOTAL n d) 0)
- // (= (* d (DIVISION_TOTAL n d)) n)))
-
-
- Polynomial n = Polynomial::parsePolynomial(div_tot[0]);
- Polynomial d = Polynomial::parsePolynomial(div_tot[1]);
- Polynomial div_tot_p = Polynomial::parsePolynomial(div_tot);
-
- Comparison invEq = Comparison::mkComparison(EQUAL, n, d * div_tot_p);
- Comparison zeroEq = Comparison::mkComparison(EQUAL, div_tot_p, Polynomial::mkZero());
- Node dEq0 = (d.getNode()).eqNode(mkRationalNode(0));
- Node ite = dEq0.iteNode(zeroEq.getNode(), invEq.getNode());
-
- return ite;
-}
-
-Node TheoryArith::axiomIteForTotalIntDivision(Node int_div_like){
- Kind k = int_div_like.getKind();
- Assert(k == INTS_DIVISION_TOTAL || k == INTS_MODULUS_TOTAL);
-
- // (for all ((m Int) (n Int))
- // (=> (distinct n 0)
- // (let ((q (div m n)) (r (mod m n)))
- // (and (= m (+ (* n q) r))
- // (<= 0 r (- (abs n) 1))))))
-
- // Updated for div 0 functions
- // (for all ((m Int) (n Int))
- // (let ((q (div m n)) (r (mod m n)))
- // (ite (= n 0)
- // (and (= q (div_0_func m)) (= r (mod_0_func m)))
- // (and (= m (+ (* n q) r))
- // (<= 0 r (- (abs n) 1)))))))
-
- Polynomial n = Polynomial::parsePolynomial(int_div_like[0]);
- Polynomial d = Polynomial::parsePolynomial(int_div_like[1]);
-
- NodeManager* currNM = NodeManager::currentNM();
- Node zero = mkRationalNode(0);
-
- Node q = (k == INTS_DIVISION_TOTAL) ? int_div_like : currNM->mkNode(INTS_DIVISION_TOTAL, n.getNode(), d.getNode());
- Node r = (k == INTS_MODULUS_TOTAL) ? int_div_like : currNM->mkNode(INTS_MODULUS_TOTAL, n.getNode(), d.getNode());
-
- Node dEq0 = (d.getNode()).eqNode(zero);
- Node qEq0 = q.eqNode(zero);
- Node rEq0 = r.eqNode(zero);
-
- Polynomial rp = Polynomial::parsePolynomial(r);
- Polynomial qp = Polynomial::parsePolynomial(q);
-
- Node abs_d = (n.isConstant()) ?
- d.getHead().getConstant().abs().getNode() : mkIntSkolem("abs_$$");
-
- Node eq = Comparison::mkComparison(EQUAL, n, d * qp + rp).getNode();
- Node leq0 = currNM->mkNode(LEQ, zero, r);
- Node leq1 = currNM->mkNode(LT, r, abs_d);
-
- Node andE = currNM->mkNode(AND, eq, leq0, leq1);
- Node defDivMode = dEq0.iteNode(qEq0.andNode(rEq0), andE);
- Node lem = abs_d.getMetaKind () == metakind::VARIABLE ?
- defDivMode.andNode(d.makeAbsCondition(Variable(abs_d))) : defDivMode;
-
- return lem;
-}
-
-
-void TheoryArith::setupPolynomial(const Polynomial& poly) {
- Assert(!poly.containsConstant());
- TNode polyNode = poly.getNode();
- Assert(!isSetup(polyNode));
- Assert(!d_arithvarNodeMap.hasArithVar(polyNode));
-
- for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
- Monomial mono = *i;
- const VarList& vl = mono.getVarList();
- if(!isSetup(vl.getNode())){
- setupVariableList(vl);
- }
- }
-
- if(polyNode.getKind() == PLUS){
- d_tableauSizeHasBeenModified = true;
-
- vector<ArithVar> variables;
- vector<Rational> coefficients;
- asVectors(poly, coefficients, variables);
-
- ArithVar varSlack = requestArithVar(polyNode, true);
- d_tableau.addRow(varSlack, coefficients, variables);
- setupBasicValue(varSlack);
-
- //Add differences to the difference manager
- Polynomial::iterator i = poly.begin(), end = poly.end();
- if(i != end){
- Monomial first = *i;
- ++i;
- if(i != end){
- Monomial second = *i;
- ++i;
- if(i == end){
- if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
- VarList vl0 = first.getVarList();
- VarList vl1 = second.getVarList();
- if(vl0.singleton() && vl1.singleton()){
- d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
- }
- }
- }
- }
- }
-
- ++(d_statistics.d_statSlackVariables);
- markSetup(polyNode);
- }
-
- /* Note:
- * It is worth documenting that polyNode should only be marked as
- * being setup by this function if it has kind PLUS.
- * Other kinds will be marked as being setup by lower levels of setup
- * specifically setupVariableList.
- */
-}
-
-void TheoryArith::setupAtom(TNode atom) {
- Assert(isRelationOperator(atom.getKind()));
- Assert(Comparison::isNormalAtom(atom));
- Assert(!isSetup(atom));
- Assert(!d_constraintDatabase.hasLiteral(atom));
-
- Comparison cmp = Comparison::parseNormalForm(atom);
- Polynomial nvp = cmp.normalizedVariablePart();
- Assert(!nvp.isZero());
-
- if(!isSetup(nvp.getNode())){
- setupPolynomial(nvp);
- }
-
- d_constraintDatabase.addLiteral(atom);
-
- markSetup(atom);
-}
-
-void TheoryArith::preRegisterTerm(TNode n) {
- Debug("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
-
- if(isRelationOperator(n.getKind())){
- if(!isSetup(n)){
- setupAtom(n);
- }
- Constraint c = d_constraintDatabase.lookup(n);
- Assert(c != NullConstraint);
-
- Debug("arith::preregister") << "setup constraint" << c << endl;
- Assert(!c->canBePropagated());
- c->setPreregistered();
- }
-
- Debug("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
-}
-
-void TheoryArith::releaseArithVar(ArithVar v){
- Assert(d_arithvarNodeMap.hasNode(v));
-
- d_constraintDatabase.removeVariable(v);
- d_arithvarNodeMap.remove(v);
-
- d_pool.push_back(v);
-}
-
-ArithVar TheoryArith::requestArithVar(TNode x, bool slack){
- //TODO : The VarList trick is good enough?
- Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS);
- if(getLogicInfo().isLinear() && Variable::isDivMember(x)){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
- }
- Assert(!d_arithvarNodeMap.hasArithVar(x));
- Assert(x.getType().isReal());// real or integer
-
- // ArithVar varX = d_variables.size();
- // d_variables.push_back(Node(x));
-
- bool reclaim = !d_pool.empty();
- ArithVar varX;
-
- if(reclaim){
- varX = d_pool.back();
- d_pool.pop_back();
-
- d_partialModel.setAssignment(varX, d_DELTA_ZERO, d_DELTA_ZERO);
- }else{
- varX = d_numberOfVariables;
- ++d_numberOfVariables;
-
- d_slackVars.push_back(true);
- d_variableTypes.push_back(ATReal);
-
- d_simplex.increaseMax();
-
- d_tableau.increaseSize();
- d_tableauSizeHasBeenModified = true;
-
- d_partialModel.initialize(varX, d_DELTA_ZERO);
- }
-
- ArithType type;
- if(slack){
- //The type computation is not quite accurate for Rationals that are integral.
- //We'll use the isIntegral check from the polynomial package instead.
- Polynomial p = Polynomial::parsePolynomial(x);
- type = p.isIntegral() ? ATInteger : ATReal;
- }else{
- type = nodeToArithType(x);
- }
- d_variableTypes[varX] = type;
- d_slackVars[varX] = slack;
-
- d_constraintDatabase.addVariable(varX);
-
- d_arithvarNodeMap.setArithVar(x,varX);
-
- // Debug("integers") << "isInteger[[" << x << "]]: " << x.getType().isInteger() << endl;
-
- // if(slack){
- // //The type computation is not quite accurate for Rationals that are integral.
- // //We'll use the isIntegral check from the polynomial package instead.
- // Polynomial p = Polynomial::parsePolynomial(x);
- // d_variableTypes.push_back(p.isIntegral() ? ATInteger : ATReal);
- // }else{
- // d_variableTypes.push_back(nodeToArithType(x));
- // }
-
- // d_slackVars.push_back(slack);
-
- // d_simplex.increaseMax();
-
- // d_tableau.increaseSize();
- // d_tableauSizeHasBeenModified = true;
-
- // d_constraintDatabase.addVariable(varX);
-
- Debug("arith::arithvar") << x << " |-> " << varX << endl;
-
- Assert(!d_partialModel.hasUpperBound(varX));
- Assert(!d_partialModel.hasLowerBound(varX));
-
- return varX;
-}
-
-void TheoryArith::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
- for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
- const Monomial& mono = *i;
- const Constant& constant = mono.getConstant();
- const VarList& variable = mono.getVarList();
-
- Node n = variable.getNode();
-
- Debug("rewriter") << "should be var: " << n << endl;
-
- // TODO: This VarList::isMember(n) can be stronger
- Assert(isLeaf(n) || VarList::isMember(n));
- Assert(theoryOf(n) != THEORY_ARITH || d_arithvarNodeMap.hasArithVar(n));
-
- Assert(d_arithvarNodeMap.hasArithVar(n));
- ArithVar av = d_arithvarNodeMap.asArithVar(n);
-
- coeffs.push_back(constant.getValue());
- variables.push_back(av);
- }
-}
-
-/* Requirements:
- * For basic variables the row must have been added to the tableau.
- */
-void TheoryArith::setupBasicValue(ArithVar x){
- Assert(d_tableau.isBasic(x));
-
- // if(!d_tableau.isBasic(x)){
- // d_partialModel.setAssignment(x, d_DELTA_ZERO, d_DELTA_ZERO);
- // }else{
- //If the variable is basic, assertions may have already happened and updates
- //may have occured before setting this variable up.
-
- //This can go away if the tableau creation is done at preregister
- //time instead of register
- DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
- DeltaRational assignment = d_linEq.computeRowValue(x, false);
- //d_partialModel.initialize(x,safeAssignment);
- //d_partialModel.setAssignment(x,assignment);
- d_partialModel.setAssignment(x,safeAssignment,assignment);
-
- // }
- Debug("arith") << "setupVariable("<<x<<")"<<std::endl;
-}
-
-ArithVar TheoryArith::determineArithVar(const Polynomial& p) const{
- Assert(!p.containsConstant());
- Assert(p.getHead().constantIsPositive());
- TNode n = p.getNode();
- Debug("determineArithVar") << "determineArithVar(" << n << ")" << endl;
- return d_arithvarNodeMap.asArithVar(n);
-}
-
-ArithVar TheoryArith::determineArithVar(TNode assertion) const{
- Debug("determineArithVar") << "determineArithVar " << assertion << endl;
- Comparison cmp = Comparison::parseNormalForm(assertion);
- Polynomial variablePart = cmp.normalizedVariablePart();
- return determineArithVar(variablePart);
-}
-
-
-bool TheoryArith::canSafelyAvoidEqualitySetup(TNode equality){
- Assert(equality.getKind() == EQUAL);
- return d_arithvarNodeMap.hasArithVar(equality[0]);
-}
-
-Comparison TheoryArith::mkIntegerEqualityFromAssignment(ArithVar v){
- const DeltaRational& beta = d_partialModel.getAssignment(v);
-
- Assert(beta.isIntegral());
- Polynomial betaAsPolynomial( Constant::mkConstant(beta.floor()) );
-
- TNode var = d_arithvarNodeMap.asNode(v);
- Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
- return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
-}
-
-Node TheoryArith::dioCutting(){
- context::Context::ScopedPush speculativePush(getSatContext());
- //DO NOT TOUCH THE OUTPUTSTREAM
-
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
- if(isInteger(v)){
- const DeltaRational& dr = d_partialModel.getAssignment(v);
- if(d_partialModel.equalsUpperBound(v, dr) || d_partialModel.equalsLowerBound(v, dr)){
- if(!d_partialModel.boundsAreEqual(v)){
- // If the bounds are equal this is already in the dioSolver
- //Add v = dr as a speculation.
- Comparison eq = mkIntegerEqualityFromAssignment(v);
- Debug("dio::push") <<v << " " << eq.getNode() << endl;
- Assert(!eq.isBoolean());
- d_diosolver.pushInputConstraint(eq, eq.getNode());
- // It does not matter what the explanation of eq is.
- // It cannot be used in a conflict
- }
- }
- }
- }
-
- SumPair plane = d_diosolver.processEquationsForCut();
- if(plane.isZero()){
- return Node::null();
- }else{
- Polynomial p = plane.getPolynomial();
- Polynomial c(plane.getConstant() * Constant::mkConstant(-1));
- Integer gcd = p.gcd();
- Assert(p.isIntegral());
- Assert(c.isIntegral());
- Assert(gcd > 1);
- Assert(!gcd.divides(c.asConstant().getNumerator()));
- Comparison leq = Comparison::mkComparison(LEQ, p, c);
- Comparison geq = Comparison::mkComparison(GEQ, p, c);
- Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
- Node rewrittenLemma = Rewriter::rewrite(lemma);
- Debug("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
- Debug("arith::dio") << "resulting in the cut: " << lemma << endl;
- Debug("arith::dio") << "rewritten " << rewrittenLemma << endl;
- return rewrittenLemma;
- }
-}
-
-Node TheoryArith::callDioSolver(){
- while(!d_constantIntegerVariables.empty()){
- ArithVar v = d_constantIntegerVariables.front();
- d_constantIntegerVariables.pop();
-
- Debug("arith::dio") << v << endl;
-
- Assert(isInteger(v));
- Assert(d_partialModel.boundsAreEqual(v));
-
-
- Constraint lb = d_partialModel.getLowerBoundConstraint(v);
- Constraint ub = d_partialModel.getUpperBoundConstraint(v);
-
- Node orig = Node::null();
- if(lb->isEquality()){
- orig = lb->explainForConflict();
- }else if(ub->isEquality()){
- orig = ub->explainForConflict();
- }else {
- orig = ConstraintValue::explainConflict(ub, lb);
- }
-
- Assert(d_partialModel.assignmentIsConsistent(v));
-
- Comparison eq = mkIntegerEqualityFromAssignment(v);
-
- if(eq.isBoolean()){
- //This can only be a conflict
- Assert(!eq.getNode().getConst<bool>());
-
- //This should be handled by the normal form earlier in the case of equality
- Assert(orig.getKind() != EQUAL);
- return orig;
- }else{
- Debug("dio::push") << v << " " << eq.getNode() << " with reason " << orig << endl;
- d_diosolver.pushInputConstraint(eq, orig);
- }
- }
-
- return d_diosolver.processEquationsForConflict();
-}
-
-Constraint TheoryArith::constraintFromFactQueue(){
- Assert(!done());
- TNode assertion = get();
-
- Kind simpleKind = Comparison::comparisonKind(assertion);
- Constraint constraint = d_constraintDatabase.lookup(assertion);
- if(constraint == NullConstraint){
- Assert(simpleKind == EQUAL || simpleKind == DISTINCT );
- bool isDistinct = simpleKind == DISTINCT;
- Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
- Assert(!isSetup(eq));
- Node reEq = Rewriter::rewrite(eq);
- if(reEq.getKind() == CONST_BOOLEAN){
- if(reEq.getConst<bool>() == isDistinct){
- // if is (not true), or false
- Assert((reEq.getConst<bool>() && isDistinct) ||
- (!reEq.getConst<bool>() && !isDistinct));
- d_raiseConflict(assertion);
- }
- return NullConstraint;
- }
- Assert(reEq.getKind() != CONST_BOOLEAN);
- if(!isSetup(reEq)){
- setupAtom(reEq);
- }
- Node reAssertion = isDistinct ? reEq.notNode() : reEq;
- constraint = d_constraintDatabase.lookup(reAssertion);
-
- if(assertion != reAssertion){
- Debug("arith::nf") << "getting non-nf assertion " << assertion << " |-> " << reAssertion << endl;
- Assert(constraint != NullConstraint);
- d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
- }
- }
-
- // Kind simpleKind = Comparison::comparisonKind(assertion);
- // Assert(simpleKind != UNDEFINED_KIND);
- // Assert(constraint != NullConstraint ||
- // simpleKind == EQUAL ||
- // simpleKind == DISTINCT );
- // if(simpleKind == EQUAL || simpleKind == DISTINCT){
- // Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
-
- // if(!isSetup(eq)){
- // //The previous code was equivalent to:
- // setupAtom(eq);
- // constraint = d_constraintDatabase.lookup(assertion);
- // }
- // }
- Assert(constraint != NullConstraint);
-
- if(constraint->negationHasProof()){
- Constraint negation = constraint->getNegation();
- if(negation->isSelfExplaining()){
- if(Debug.isOn("whytheoryenginewhy")){
- debugPrintFacts();
- }
- }
- Debug("arith::eq") << constraint << endl;
- Debug("arith::eq") << negation << endl;
-
- NodeBuilder<> nb(kind::AND);
- nb << assertion;
- negation->explainForConflict(nb);
- Node conflict = nb;
- Debug("arith::eq") << "conflict" << conflict << endl;
- d_raiseConflict(conflict);
- return NullConstraint;
- }
- Assert(!constraint->negationHasProof());
-
- if(constraint->assertedToTheTheory()){
- //Do nothing
- return NullConstraint;
- }else{
- Debug("arith::constraint") << "arith constraint " << constraint << std::endl;
- constraint->setAssertedToTheTheory(assertion);
-
- if(!constraint->hasProof()){
- Debug("arith::constraint") << "marking as constraint as self explaining " << endl;
- constraint->selfExplaining();
- }else{
- Debug("arith::constraint") << "already has proof: " << constraint->explainForConflict() << endl;
- }
-
- return constraint;
- }
-}
-
-bool TheoryArith::assertionCases(Constraint constraint){
- Assert(constraint->hasProof());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
-
- switch(constraint->getType()){
- case UpperBound:
- if(isInteger(x_i) && constraint->isStrictUpperBound()){
- Constraint floorConstraint = constraint->getFloor();
- if(!floorConstraint->isTrue()){
- if(floorConstraint->negationHasProof()){
- Node conf = ConstraintValue::explainConflict(constraint, floorConstraint->getNegation());
- d_raiseConflict(conf);
- return true;
- }else{
- floorConstraint->impliedBy(constraint);
- // Do not need to add to d_learnedBounds
- }
- }
- return AssertUpper(floorConstraint);
- }else{
- return AssertUpper(constraint);
- }
- case LowerBound:
- if(isInteger(x_i) && constraint->isStrictLowerBound()){
- Constraint ceilingConstraint = constraint->getCeiling();
- if(!ceilingConstraint->isTrue()){
- if(ceilingConstraint->negationHasProof()){
- Node conf = ConstraintValue::explainConflict(constraint, ceilingConstraint->getNegation());
- d_raiseConflict(conf);
- return true;
- }
- ceilingConstraint->impliedBy(constraint);
- // Do not need to add to learnedBounds
- }
- return AssertLower(ceilingConstraint);
- }else{
- return AssertLower(constraint);
- }
- case Equality:
- return AssertEquality(constraint);
- case Disequality:
- return AssertDisequality(constraint);
- default:
- Unreachable();
- return false;
- }
-}
-
-/**
- * Looks for the next integer variable without an integer assignment in a round robin fashion.
- * Changes the value of d_nextIntegerCheckVar.
- *
- * If this returns false, d_nextIntegerCheckVar does not have an integer assignment.
- * If this returns true, all integer variables have an integer assignment.
- */
-bool TheoryArith::hasIntegerModel(){
- //if(d_variables.size() > 0){
- if(getNumberOfVariables()){
- const ArithVar rrEnd = d_nextIntegerCheckVar;
- do {
- //Do not include slack variables
- if(isInteger(d_nextIntegerCheckVar) && !isSlackVariable(d_nextIntegerCheckVar)) { // integer
- const DeltaRational& d = d_partialModel.getAssignment(d_nextIntegerCheckVar);
- if(!d.isIntegral()){
- return false;
- }
- }
- } while((d_nextIntegerCheckVar = (1 + d_nextIntegerCheckVar == getNumberOfVariables() ? 0 : 1 + d_nextIntegerCheckVar)) != rrEnd);
- }
- return true;
-}
-
-/** Outputs conflicts to the output channel. */
-void TheoryArith::outputConflicts(){
- Assert(!d_conflicts.empty());
- for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
- Node conflict = d_conflicts[i];
- Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << endl;
- d_out->conflict(conflict);
- }
+ d_internal->ppStaticLearn(n, learned);
}
void TheoryArith::check(Effort effortLevel){
- Assert(d_currentPropagationList.empty());
- Debug("effortlevel") << "TheoryArith::check " << effortLevel << std::endl;
- Debug("arith") << "TheoryArith::check begun " << effortLevel << std::endl;
-
- if(Debug.isOn("arith::consistency")){
- Assert(unenqueuedVariablesAreConsistent());
- }
-
- bool newFacts = !done();
- //If previous == SAT, then reverts on conflicts are safe
- //Otherwise, they are not and must be committed.
- Result::Sat previous = d_qflraStatus;
- if(newFacts){
- d_qflraStatus = Result::SAT_UNKNOWN;
- d_hasDoneWorkSinceCut = true;
- }
-
- while(!done()){
- Constraint curr = constraintFromFactQueue();
- if(curr != NullConstraint){
- bool res CVC4_UNUSED = assertionCases(curr);
- Assert(!res || inConflict());
- }
- if(inConflict()){ break; }
- }
- if(!inConflict()){
- while(!d_learnedBounds.empty()){
- // we may attempt some constraints twice. this is okay!
- Constraint curr = d_learnedBounds.front();
- d_learnedBounds.pop();
- Debug("arith::learned") << curr << endl;
-
- bool res CVC4_UNUSED = assertionCases(curr);
- Assert(!res || inConflict());
-
- if(inConflict()){ break; }
- }
- }
-
- if(inConflict()){
- d_qflraStatus = Result::UNSAT;
- if(previous == Result::SAT){
- ++d_statistics.d_revertsOnConflicts;
- Debug("arith::bt") << "clearing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- revertOutOfConflict();
- d_simplex.clearQueue();
- }else{
- ++d_statistics.d_commitsOnConflicts;
- Debug("arith::bt") << "committing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- revertOutOfConflict();
- }
- outputConflicts();
- return;
- }
-
-
- if(Debug.isOn("arith::print_assertions")) {
- debugPrintAssertions();
- }
-
- bool emmittedConflictOrSplit = false;
- Assert(d_conflicts.empty());
-
- d_qflraStatus = d_simplex.findModel(fullEffort(effortLevel));
-
- switch(d_qflraStatus){
- case Result::SAT:
- if(newFacts){
- ++d_statistics.d_nontrivialSatChecks;
- }
-
- Debug("arith::bt") << "committing sap inConflit" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- d_unknownsInARow = 0;
- if(Debug.isOn("arith::consistency")){
- Assert(entireStateIsConsistent("sat comit"));
- }
- break;
- case Result::SAT_UNKNOWN:
- ++d_unknownsInARow;
- ++(d_statistics.d_unknownChecks);
- Assert(!fullEffort(effortLevel));
- Debug("arith::bt") << "committing unknown" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
- break;
- case Result::UNSAT:
- d_unknownsInARow = 0;
- if(options::revertArithModels() && previous == Result::SAT){
- ++d_statistics.d_revertsOnConflicts;
- Debug("arith::bt") << "clearing on conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- revertOutOfConflict();
- d_simplex.clearQueue();
- }else{
- ++d_statistics.d_commitsOnConflicts;
-
- Debug("arith::bt") << "committing on conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- revertOutOfConflict();
-
- if(Debug.isOn("arith::consistency::comitonconflict")){
- entireStateIsConsistent("commit on conflict");
- }
- }
- outputConflicts();
- emmittedConflictOrSplit = true;
- break;
- default:
- Unimplemented();
- }
- d_statistics.d_avgUnknownsInARow.addEntry(d_unknownsInARow);
-
- // This should be fine if sat or unknown
- if(!emmittedConflictOrSplit &&
- (options::arithPropagationMode() == UNATE_PROP ||
- options::arithPropagationMode() == BOTH_PROP)){
- TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime);
- Assert(d_qflraStatus != Result::UNSAT);
-
- while(!d_currentPropagationList.empty() && !inConflict()){
- Constraint curr = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
-
- ConstraintType t = curr->getType();
- Assert(t != Disequality, "Disequalities are not allowed in d_currentPropagation");
-
-
- switch(t){
- case LowerBound:
- {
- Constraint prev = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropLowerBound(curr, prev);
- break;
- }
- case UpperBound:
- {
- Constraint prev = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropUpperBound(curr, prev);
- break;
- }
- case Equality:
- {
- Constraint prevLB = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- Constraint prevUB = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
- break;
- }
- default:
- Unhandled(curr->getType());
- }
- }
-
- if(inConflict()){
- Debug("arith::unate") << "unate conflict" << endl;
- revertOutOfConflict();
- d_qflraStatus = Result::UNSAT;
- outputConflicts();
- emmittedConflictOrSplit = true;
- Debug("arith::bt") << "committing on unate conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
-
- }
- }else{
- TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime);
- d_currentPropagationList.clear();
- }
- Assert( d_currentPropagationList.empty());
-
- if(!emmittedConflictOrSplit && fullEffort(effortLevel)){
- ++d_fullCheckCounter;
- }
- static const int CUT_ALL_BOUNDED_PERIOD = 10;
- if(!emmittedConflictOrSplit && fullEffort(effortLevel) &&
- d_fullCheckCounter % CUT_ALL_BOUNDED_PERIOD == 1){
-
- Debug("arith::demand-restart") << options::maxCutsInContext() << " " << d_cutsInContext << "cut all" << endl;
-
- vector<Node> lemmas = cutAllBounded();
- //output the lemmas
- for(vector<Node>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){
- d_out->lemma(*i);
- Debug("arith::demand-restart") << options::maxCutsInContext() << " " << d_cutsInContext << "cut all " << *i << endl;
- ++(d_statistics.d_externalBranchAndBounds);
- d_cutsInContext = d_cutsInContext + 1;
- emmittedConflictOrSplit = lemmas.size() > 0;
- }
- if(options::maxCutsInContext() <= d_cutsInContext){
- d_out->demandRestart();
- }
- }
-
- if(!emmittedConflictOrSplit && fullEffort(effortLevel)){
- emmittedConflictOrSplit = splitDisequalities();
- }
-
- if(!emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()){
- Node possibleConflict = Node::null();
- if(!emmittedConflictOrSplit && options::arithDioSolver()){
- possibleConflict = callDioSolver();
- if(possibleConflict != Node::null()){
- revertOutOfConflict();
- Debug("arith::conflict") << "dio conflict " << possibleConflict << endl;
- d_out->conflict(possibleConflict);
- emmittedConflictOrSplit = true;
- }
- }
-
- if(!emmittedConflictOrSplit && d_hasDoneWorkSinceCut && options::arithDioSolver()){
- Node possibleLemma = dioCutting();
- if(!possibleLemma.isNull()){
- Debug("arith") << "dio cut " << possibleLemma << endl;
- emmittedConflictOrSplit = true;
- d_hasDoneWorkSinceCut = false;
- d_out->lemma(possibleLemma);
-
- d_cutsInContext = d_cutsInContext + 1;
- Debug("arith::demand-restart") << options::maxCutsInContext() << " " << d_cutsInContext << " dio cut" << endl;
- if(options::maxCutsInContext() <= d_cutsInContext){
- d_out->demandRestart();
- }
- }
- }
-
- if(!emmittedConflictOrSplit) {
- Node possibleLemma = roundRobinBranch();
- if(!possibleLemma.isNull()){
-
- d_cutsInContext = d_cutsInContext + 1;
-
- Debug("arith::demand-restart") << options::maxCutsInContext() << " " << d_cutsInContext << " branch" << endl;
-
- ++(d_statistics.d_externalBranchAndBounds);
- emmittedConflictOrSplit = true;
- d_out->lemma(possibleLemma);
- d_cutsInContext = d_cutsInContext + 1;
- if(options::maxCutsInContext() <= d_cutsInContext){
- d_out->demandRestart();
- }
- }
- }
- }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
- if(fullEffort(effortLevel) && d_nlIncomplete){
- // TODO this is total paranoia
- d_out->setIncomplete();
- }
-
- if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
- if(Debug.isOn("arith::print_model")) { debugPrintModel(); }
- Debug("arith") << "TheoryArith::check end" << std::endl;
-}
-
-Node TheoryArith::branchIntegerVariable(ArithVar x) const {
- const DeltaRational& d = d_partialModel.getAssignment(x);
- Assert(!d.isIntegral());
- const Rational& r = d.getNoninfinitesimalPart();
- const Rational& i = d.getInfinitesimalPart();
- Trace("integers") << "integers: assignment to [[" << d_arithvarNodeMap.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
-
- Assert(! (r.getDenominator() == 1 && i.getNumerator() == 0));
- Assert(!d.isIntegral());
- TNode var = d_arithvarNodeMap.asNode(x);
- Integer floor_d = d.floor();
-
- Node ub = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::LEQ, var, mkRationalNode(floor_d)));
- Node lb = ub.notNode();
-
-
- Node lem = NodeManager::currentNM()->mkNode(kind::OR, ub, lb);
- Trace("integers") << "integers: branch & bound: " << lem << endl;
- if(d_valuation.isSatLiteral(lem[0])) {
- Debug("integers") << " " << lem[0] << " == " << d_valuation.getSatValue(lem[0]) << endl;
- } else {
- Debug("integers") << " " << lem[0] << " is not assigned a SAT literal" << endl;
- }
- if(d_valuation.isSatLiteral(lem[1])) {
- Debug("integers") << " " << lem[1] << " == " << d_valuation.getSatValue(lem[1]) << endl;
- } else {
- Debug("integers") << " " << lem[1] << " is not assigned a SAT literal" << endl;
- }
- return lem;
+ d_internal->check(effortLevel);
}
-std::vector<Node> TheoryArith::cutAllBounded() const{
- vector<Node> lemmas;
- if(options::doCutAllBounded() && getNumberOfVariables() > 0){
- for(ArithVar iter = 0; iter != getNumberOfVariables(); ++iter){
- //Do not include slack variables
- const DeltaRational& d = d_partialModel.getAssignment(iter);
- if(isInteger(iter) && !isSlackVariable(iter) &&
- d_partialModel.hasUpperBound(iter) &&
- d_partialModel.hasLowerBound(iter) &&
- !d.isIntegral()){
- Node lem = branchIntegerVariable(iter);
- lemmas.push_back(lem);
- }
- }
- }
- return lemmas;
-}
-
-/** Returns true if the roundRobinBranching() issues a lemma. */
-Node TheoryArith::roundRobinBranch(){
- if(hasIntegerModel()){
- return Node::null();
- }else{
- ArithVar v = d_nextIntegerCheckVar;
-
- Assert(isInteger(v));
- Assert(!isSlackVariable(v));
- return branchIntegerVariable(v);
-
- // Assert(isInteger(v));
- // Assert(!isSlackVariable(v));
-
- // const DeltaRational& d = d_partialModel.getAssignment(v);
- // const Rational& r = d.getNoninfinitesimalPart();
- // const Rational& i = d.getInfinitesimalPart();
- // Trace("integers") << "integers: assignment to [[" << d_arithvarNodeMap.asNode(v) << "]] is " << r << "[" << i << "]" << endl;
-
- // Assert(! (r.getDenominator() == 1 && i.getNumerator() == 0));
- // Assert(!d.isIntegral());
-
- // TNode var = d_arithvarNodeMap.asNode(v);
- // Integer floor_d = d.floor();
- // Integer ceil_d = d.ceiling();
-
- // Node leq = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::LEQ, var, mkRationalNode(floor_d)));
- // Node geq = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::GEQ, var, mkRationalNode(ceil_d)));
-
-
- // Node lem = NodeManager::currentNM()->mkNode(kind::OR, leq, geq);
- // Trace("integers") << "integers: branch & bound: " << lem << endl;
- // if(d_valuation.isSatLiteral(lem[0])) {
- // Debug("integers") << " " << lem[0] << " == " << d_valuation.getSatValue(lem[0]) << endl;
- // } else {
- // Debug("integers") << " " << lem[0] << " is not assigned a SAT literal" << endl;
- // }
- // if(d_valuation.isSatLiteral(lem[1])) {
- // Debug("integers") << " " << lem[1] << " == " << d_valuation.getSatValue(lem[1]) << endl;
- // } else {
- // Debug("integers") << " " << lem[1] << " is not assigned a SAT literal" << endl;
- // }
- // return lem;
- }
-}
-
-bool TheoryArith::splitDisequalities(){
- bool splitSomething = false;
-
- vector<Constraint> save;
-
- while(!d_diseqQueue.empty()){
- Constraint front = d_diseqQueue.front();
- d_diseqQueue.pop();
-
- if(front->isSplit()){
- Debug("eq") << "split already" << endl;
- }else{
- Debug("eq") << "not split already" << endl;
-
- ArithVar lhsVar = front->getVariable();
-
- const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
- const DeltaRational& rhsValue = front->getValue();
- if(lhsValue == rhsValue){
- Debug("arith::lemma") << "Splitting on " << front << endl;
- Debug("arith::lemma") << "LHS value = " << lhsValue << endl;
- Debug("arith::lemma") << "RHS value = " << rhsValue << endl;
- Node lemma = front->split();
- ++(d_statistics.d_statDisequalitySplits);
- Node relem = Rewriter::rewrite(lemma);
-
- Debug("arith::lemma") << "Now " << relem << endl;
- d_out->lemma(lemma);
- splitSomething = true;
- }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
- Debug("eq") << "can drop as less than lb" << front << endl;
- }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
- Debug("eq") << "can drop as greater than ub" << front << endl;
- }else{
- Debug("eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
- save.push_back(front);
- }
- }
- }
- vector<Constraint>::const_iterator i=save.begin(), i_end = save.end();
- for(; i != i_end; ++i){
- d_diseqQueue.push(*i);
- }
- return splitSomething;
-}
-
-/**
- * Should be guarded by at least Debug.isOn("arith::print_assertions").
- * Prints to Debug("arith::print_assertions")
- */
-void TheoryArith::debugPrintAssertions() {
- Debug("arith::print_assertions") << "Assertions:" << endl;
- for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar i = *vi;
- if (d_partialModel.hasLowerBound(i)) {
- Constraint lConstr = d_partialModel.getLowerBoundConstraint(i);
- Debug("arith::print_assertions") << lConstr << endl;
- }
-
- if (d_partialModel.hasUpperBound(i)) {
- Constraint uConstr = d_partialModel.getUpperBoundConstraint(i);
- Debug("arith::print_assertions") << uConstr << endl;
- }
- }
- context::CDQueue<Constraint>::const_iterator it = d_diseqQueue.begin();
- context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end();
- for(; it != it_end; ++ it) {
- Debug("arith::print_assertions") << *it << endl;
- }
-}
-
-void TheoryArith::debugPrintModel(){
- Debug("arith::print_model") << "Model:" << endl;
- for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar i = *vi;
- if(d_arithvarNodeMap.hasNode(i)){
- Debug("arith::print_model") << d_arithvarNodeMap.asNode(i) << " : " <<
- d_partialModel.getAssignment(i);
- if(d_tableau.isBasic(i))
- Debug("arith::print_model") << " (basic)";
- Debug("arith::print_model") << endl;
- }
- }
-}
-
-
-
Node TheoryArith::explain(TNode n) {
-
- Debug("arith::explain") << "explain @" << getSatContext()->getLevel() << ": " << n << endl;
-
- Constraint c = d_constraintDatabase.lookup(n);
- if(c != NullConstraint){
- Assert(!c->isSelfExplaining());
- Node exp = c->explainForPropagation();
- Debug("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
- return exp;
- }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
- c = d_assertionsThatDoNotMatchTheirLiterals[n];
- if(!c->isSelfExplaining()){
- Node exp = c->explainForPropagation();
- Debug("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
- return exp;
- }else{
- Debug("arith::explain") << "this is a strange mismatch" << n << endl;
- Assert(d_congruenceManager.canExplain(n));
- Debug("arith::explain") << "this is a strange mismatch" << n << endl;
- return d_congruenceManager.explain(n);
- }
- }else{
- Assert(d_congruenceManager.canExplain(n));
- Debug("arith::explain") << "dm explanation" << n << endl;
- return d_congruenceManager.explain(n);
- }
+ return d_internal->explain(n);
}
-
void TheoryArith::propagate(Effort e) {
- // This uses model values for safety. Disable for now.
- if(d_qflraStatus == Result::SAT &&
- (options::arithPropagationMode() == BOUND_INFERENCE_PROP ||
- options::arithPropagationMode() == BOTH_PROP)
- && hasAnyUpdates()){
- propagateCandidates();
- }else{
- clearUpdates();
- }
-
- while(d_constraintDatabase.hasMorePropagations()){
- Constraint c = d_constraintDatabase.nextPropagation();
- Debug("arith::prop") << "next prop" << getSatContext()->getLevel() << ": " << c << endl;
-
- if(c->negationHasProof()){
- Debug("arith::prop") << "negation has proof " << c->getNegation() << endl
- << c->getNegation()->explainForConflict() << endl;
- }
- Assert(!c->negationHasProof(), "A constraint has been propagated on the constraint propagation queue, but the negation has been set to true. Contact Tim now!");
-
- if(!c->assertedToTheTheory()){
- Node literal = c->getLiteral();
- Debug("arith::prop") << "propagating @" << getSatContext()->getLevel() << " " << literal << endl;
-
- d_out->propagate(literal);
- }else{
- Debug("arith::prop") << "already asserted to the theory " << c->getLiteral() << endl;
- }
- }
-
- while(d_congruenceManager.hasMorePropagations()){
- TNode toProp = d_congruenceManager.getNextPropagation();
-
- //Currently if the flag is set this came from an equality detected by the
- //equality engine in the the difference manager.
- Node normalized = Rewriter::rewrite(toProp);
-
- Constraint constraint = d_constraintDatabase.lookup(normalized);
- if(constraint == NullConstraint){
- Debug("arith::prop") << "propagating on non-constraint? " << toProp << endl;
- d_out->propagate(toProp);
- }else if(constraint->negationHasProof()){
- Node exp = d_congruenceManager.explain(toProp);
- Node notNormalized = normalized.getKind() == NOT ?
- normalized[0] : normalized.notNode();
- Node lp = flattenAnd(exp.andNode(notNormalized));
- Debug("arith::prop") << "propagate conflict" << lp << endl;
- d_out->conflict(lp);
- return;
- }else{
- Debug("arith::prop") << "propagating still?" << toProp << endl;
-
- d_out->propagate(toProp);
- }
- }
-}
-
-DeltaRational TheoryArith::getDeltaValue(TNode n) const throw (DeltaRationalException, ModelException) {
- AlwaysAssert(d_qflraStatus != Result::SAT_UNKNOWN);
- Debug("arith::value") << n << std::endl;
-
- switch(n.getKind()) {
-
- case kind::CONST_RATIONAL:
- return n.getConst<Rational>();
-
- case kind::PLUS: { // 2+ args
- DeltaRational value(0);
- for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
- value = value + getDeltaValue(*i);
- }
- return value;
- }
-
- case kind::MULT: { // 2+ args
- DeltaRational value(1);
- unsigned variableParts = 0;
- for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
- TNode curr = *i;
- value = value * getDeltaValue(curr);
- if(!curr.isConst()){
- ++variableParts;
- }
- }
- // TODO: This is a bit of a weak check
- if(isSetup(n)){
- ArithVar var = d_arithvarNodeMap.asArithVar(n);
- const DeltaRational& assign = d_partialModel.getAssignment(var);
- if(assign != value){
- throw ModelException(n, "Model disagrees on non-linear term.");
- }
- }
- return value;
- }
- case kind::MINUS:{ // 2 args
- return getDeltaValue(n[0]) - getDeltaValue(n[1]);
- }
-
- case kind::UMINUS:{ // 1 arg
- return (- getDeltaValue(n[0]));
- }
-
- case kind::DIVISION:{ // 2 args
- DeltaRational res = getDeltaValue(n[0]) / getDeltaValue(n[1]);
- if(isSetup(n)){
- ArithVar var = d_arithvarNodeMap.asArithVar(n);
- if(d_partialModel.getAssignment(var) != res){
- throw ModelException(n, "Model disagrees on non-linear term.");
- }
- }
- return res;
- }
- case kind::DIVISION_TOTAL:
- case kind::INTS_DIVISION_TOTAL:
- case kind::INTS_MODULUS_TOTAL: { // 2 args
- DeltaRational denom = getDeltaValue(n[1]);
- if(denom.isZero()){
- return DeltaRational(0,0);
- }else{
- DeltaRational numer = getDeltaValue(n[0]);
- DeltaRational res;
- if(n.getKind() == kind::DIVISION_TOTAL){
- res = numer / denom;
- }else if(n.getKind() == kind::INTS_DIVISION_TOTAL){
- res = Rational(numer.euclidianDivideQuotient(denom));
- }else{
- Assert(n.getKind() == kind::INTS_MODULUS_TOTAL);
- res = Rational(numer.euclidianDivideRemainder(denom));
- }
- if(isSetup(n)){
- ArithVar var = d_arithvarNodeMap.asArithVar(n);
- if(d_partialModel.getAssignment(var) != res){
- throw ModelException(n, "Model disagrees on non-linear term.");
- }
- }
- return res;
- }
- }
-
- default:
- if(isSetup(n)){
- ArithVar var = d_arithvarNodeMap.asArithVar(n);
- return d_partialModel.getAssignment(var);
- }else{
- throw ModelException(n, "Expected a setup node.");
- }
- }
-}
-
-Rational TheoryArith::deltaValueForTotalOrder() const{
- Rational min(2);
- std::set<DeltaRational> relevantDeltaValues;
- context::CDQueue<Constraint>::const_iterator qiter = d_diseqQueue.begin();
- context::CDQueue<Constraint>::const_iterator qiter_end = d_diseqQueue.end();
-
- for(; qiter != qiter_end; ++qiter){
- Constraint curr = *qiter;
-
- const DeltaRational& rhsValue = curr->getValue();
- relevantDeltaValues.insert(rhsValue);
- }
-
- for(shared_terms_iterator shared_iter = shared_terms_begin(),
- shared_end = shared_terms_end(); shared_iter != shared_end; ++shared_iter){
- Node sharedCurr = *shared_iter;
-
- // ModelException is fatal as this point. Don't catch!
- // DeltaRationalException is fatal as this point. Don't catch!
- DeltaRational val = getDeltaValue(sharedCurr);
- relevantDeltaValues.insert(val);
- }
-
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
- const DeltaRational& value = d_partialModel.getAssignment(v);
- relevantDeltaValues.insert(value);
- if( d_partialModel.hasLowerBound(v)){
- const DeltaRational& lb = d_partialModel.getLowerBound(v);
- relevantDeltaValues.insert(lb);
- }
- if( d_partialModel.hasUpperBound(v)){
- const DeltaRational& ub = d_partialModel.getUpperBound(v);
- relevantDeltaValues.insert(ub);
- }
- }
-
- if(relevantDeltaValues.size() >= 2){
- std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
- std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
- DeltaRational prev = *iter;
- ++iter;
- for(; iter != iter_end; ++iter){
- const DeltaRational& curr = *iter;
-
- Assert(prev < curr);
-
- DeltaRational::seperatingDelta(min, prev, curr);
- prev = curr;
- }
- }
-
- Assert(min.sgn() > 0);
- Rational belowMin = min/Rational(2);
- return belowMin;
+ d_internal->propagate(e);
}
void TheoryArith::collectModelInfo( TheoryModel* m, bool fullModel ){
- AlwaysAssert(d_qflraStatus == Result::SAT);
- //AlwaysAssert(!d_nlIncomplete, "Arithmetic solver cannot currently produce models for input with nonlinear arithmetic constraints");
-
- if(Debug.isOn("arith::collectModelInfo")){
- debugPrintFacts();
- }
-
- Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
-
-
- // Delta lasts at least the duration of the function call
- const Rational& delta = d_partialModel.getDelta();
- std::hash_set<TNode, TNodeHashFunction> shared = currentlySharedTerms();
-
- // TODO:
- // This is not very good for user push/pop....
- // Revisit when implementing push/pop
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
- if(!isSlackVariable(v)){
- Node term = d_arithvarNodeMap.asNode(v);
-
- if(theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end()){
- const DeltaRational& mod = d_partialModel.getAssignment(v);
- Rational qmodel = mod.substituteDelta(delta);
-
- Node qNode = mkRationalNode(qmodel);
- Debug("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
-
- m->assertEquality(term, qNode, true);
- }else{
- Debug("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
-
- }
- }
- }
-
- // Iterate over equivalence classes in LinearEqualityModule
- // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
- // m->assertEqualityEngine(&ee);
-
- Debug("arith::collectModelInfo") << "collectModelInfo() end " << endl;
-}
-
-bool TheoryArith::safeToReset() const {
- Assert(!d_tableauSizeHasBeenModified);
-
- ArithPriorityQueue::const_iterator conf_iter = d_simplex.queueBegin();
- ArithPriorityQueue::const_iterator conf_end = d_simplex.queueEnd();
- for(; conf_iter != conf_end; ++conf_iter){
- ArithVar basic = *conf_iter;
- if(!d_smallTableauCopy.isBasic(basic)){
- return false;
- }
- }
-
- return true;
+ d_internal->collectModelInfo(m, fullModel);
}
void TheoryArith::notifyRestart(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
-
- if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
- ++d_restartsCounter;
-
- uint32_t currSize = d_tableau.size();
- uint32_t copySize = d_smallTableauCopy.size();
-
- Debug("arith::reset") << "resetting" << d_restartsCounter << endl;
- Debug("arith::reset") << "curr " << currSize << " copy " << copySize << endl;
- Debug("arith::reset") << "tableauSizeHasBeenModified " << d_tableauSizeHasBeenModified << endl;
-
- if(d_tableauSizeHasBeenModified){
- Debug("arith::reset") << "row has been added must copy " << d_restartsCounter << endl;
- d_smallTableauCopy = d_tableau;
- d_tableauSizeHasBeenModified = false;
- }else if( d_restartsCounter >= RESET_START){
- if(copySize >= currSize * 1.1 ){
- Debug("arith::reset") << "size has shrunk " << d_restartsCounter << endl;
- ++d_statistics.d_smallerSetToCurr;
- d_smallTableauCopy = d_tableau;
- }else if(d_tableauResetDensity * copySize <= currSize){
- d_simplex.reduceQueue();
- if(safeToReset()){
- Debug("arith::reset") << "resetting " << d_restartsCounter << endl;
- ++d_statistics.d_currSetToSmaller;
- d_tableau = d_smallTableauCopy;
- }else{
- Debug("arith::reset") << "not safe to reset at the moment " << d_restartsCounter << endl;
- }
- }
- }
- Assert(unenqueuedVariablesAreConsistent());
-}
-
-bool TheoryArith::entireStateIsConsistent(const string& s){
- bool result = true;
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar var = *vi;
- //ArithVar var = d_arithvarNodeMap.asArithVar(*i);
- if(!d_partialModel.assignmentIsConsistent(var)){
- d_partialModel.printModel(var);
- Warning() << s << ":" << "Assignment is not consistent for " << var << d_arithvarNodeMap.asNode(var);
- if(d_tableau.isBasic(var)){
- Warning() << " (basic)";
- }
- Warning() << endl;
- result = false;
- }
- }
- return result;
-}
-
-bool TheoryArith::unenqueuedVariablesAreConsistent(){
- bool result = true;
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar var = *vi;
- if(!d_partialModel.assignmentIsConsistent(var)){
- if(!d_simplex.debugIsInCollectionQueue(var)){
-
- d_partialModel.printModel(var);
- Warning() << "Unenqueued var is not consistent for " << var << d_arithvarNodeMap.asNode(var);
- if(d_tableau.isBasic(var)){
- Warning() << " (basic)";
- }
- Warning() << endl;
- result = false;
- } else if(Debug.isOn("arith::consistency::initial")){
- d_partialModel.printModel(var);
- Warning() << "Initial var is not consistent for " << var << d_arithvarNodeMap.asNode(var);
- if(d_tableau.isBasic(var)){
- Warning() << " (basic)";
- }
- Warning() << endl;
- }
- }
- }
- return result;
+ d_internal->notifyRestart();
}
void TheoryArith::presolve(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
-
- d_statistics.d_initialTableauSize.setData(d_tableau.size());
-
- if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
- static CVC4_THREADLOCAL(unsigned) callCount = 0;
- if(Debug.isOn("arith::presolve")) {
- Debug("arith::presolve") << "TheoryArith::presolve #" << callCount << endl;
- callCount = callCount + 1;
- }
-
- vector<Node> lemmas;
- switch(options::arithUnateLemmaMode()){
- case NO_PRESOLVE_LEMMAS:
- break;
- case INEQUALITY_PRESOLVE_LEMMAS:
- d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
- break;
- case EQUALITY_PRESOLVE_LEMMAS:
- d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
- break;
- case ALL_PRESOLVE_LEMMAS:
- d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
- d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
- break;
- default:
- Unhandled(options::arithUnateLemmaMode());
- }
-
- vector<Node>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
- for(; i != i_end; ++i){
- Node lem = *i;
- Debug("arith::oldprop") << " lemma lemma duck " <<lem << endl;
- d_out->lemma(lem);
- }
-
- // if(options::arithUnateLemmaMode() == Options::ALL_UNATE){
- // vector<Node> lemmas;
- // d_constraintDatabase.outputAllUnateLemmas(lemmas);
- // vector<Node>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
- // for(; i != i_end; ++i){
- // Node lem = *i;
- // Debug("arith::oldprop") << " lemma lemma duck " <<lem << endl;
- // d_out->lemma(lem);
- // }
- // }
+ d_internal->presolve();
}
EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
- if(d_qflraStatus == Result::SAT_UNKNOWN){
- return EQUALITY_UNKNOWN;
- }else{
- try {
- if (getDeltaValue(a) == getDeltaValue(b)) {
- return EQUALITY_TRUE_IN_MODEL;
- } else {
- return EQUALITY_FALSE_IN_MODEL;
- }
- } catch (DeltaRationalException& dr) {
- return EQUALITY_UNKNOWN;
- } catch (ModelException& me) {
- return EQUALITY_UNKNOWN;
- }
- }
-}
-
-bool TheoryArith::propagateCandidateBound(ArithVar basic, bool upperBound){
- ++d_statistics.d_boundComputations;
-
- DeltaRational bound = upperBound ?
- d_linEq.computeUpperBound(basic):
- d_linEq.computeLowerBound(basic);
-
- if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
- (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
-
- // TODO: "Policy point"
- //We are only going to recreate the functionality for now.
- //In the future this can be improved to generate a temporary constraint
- //if none exists.
- //Experiment with doing this everytime or only when the new constraint
- //implies an unknown fact.
-
- ConstraintType t = upperBound ? UpperBound : LowerBound;
- Constraint bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
-
- // Node bestImplied = upperBound ?
- // d_apm.getBestImpliedUpperBound(basic, bound):
- // d_apm.getBestImpliedLowerBound(basic, bound);
-
- if(bestImplied != NullConstraint){
- //This should be stronger
- Assert(!upperBound || bound <= bestImplied->getValue());
- Assert(!upperBound || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
-
- Assert( upperBound || bound >= bestImplied->getValue());
- Assert( upperBound || d_partialModel.greaterThanLowerBound(basic, bestImplied->getValue()));
- //slightly changed
-
- // Constraint c = d_constraintDatabase.lookup(bestImplied);
- // Assert(c != NullConstraint);
-
- bool assertedToTheTheory = bestImplied->assertedToTheTheory();
- bool canBePropagated = bestImplied->canBePropagated();
- bool hasProof = bestImplied->hasProof();
-
- Debug("arith::prop") << "arith::prop" << basic
- << " " << assertedToTheTheory
- << " " << canBePropagated
- << " " << hasProof
- << endl;
-
- if(bestImplied->negationHasProof()){
- Warning() << "the negation of " << bestImplied << " : " << endl
- << "has proof " << bestImplied->getNegation() << endl
- << bestImplied->getNegation()->explainForConflict() << endl;
- }
-
- if(!assertedToTheTheory && canBePropagated && !hasProof ){
- if(upperBound){
- Assert(bestImplied != d_partialModel.getUpperBoundConstraint(basic));
- d_linEq.propagateNonbasicsUpperBound(basic, bestImplied);
- }else{
- Assert(bestImplied != d_partialModel.getLowerBoundConstraint(basic));
- d_linEq.propagateNonbasicsLowerBound(basic, bestImplied);
- }
- // I think this can be skipped if canBePropagated is true
- //d_learnedBounds.push(bestImplied);
- return true;
- }
- }
- }
- return false;
-}
-
-void TheoryArith::propagateCandidate(ArithVar basic){
- bool success = false;
- if(d_partialModel.strictlyAboveLowerBound(basic) && d_linEq.hasLowerBounds(basic)){
- success |= propagateCandidateLowerBound(basic);
- }
- if(d_partialModel.strictlyBelowUpperBound(basic) && d_linEq.hasUpperBounds(basic)){
- success |= propagateCandidateUpperBound(basic);
- }
- if(success){
- ++d_statistics.d_boundPropagations;
- }
-}
-
-void TheoryArith::propagateCandidates(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
-
- Assert(d_candidateBasics.empty());
-
- if(d_updatedBounds.empty()){ return; }
-
- DenseSet::const_iterator i = d_updatedBounds.begin();
- DenseSet::const_iterator end = d_updatedBounds.end();
- for(; i != end; ++i){
- ArithVar var = *i;
- if(d_tableau.isBasic(var) &&
- d_tableau.getRowLength(d_tableau.basicToRowIndex(var)) <= options::arithPropagateMaxLength()){
- d_candidateBasics.softAdd(var);
- }else{
- Tableau::ColIterator basicIter = d_tableau.colIterator(var);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- RowIndex ridx = entry.getRowIndex();
- ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
- Assert(entry.getColVar() == var);
- Assert(d_tableau.isBasic(rowVar));
- if(d_tableau.getRowLength(ridx) <= options::arithPropagateMaxLength()){
- d_candidateBasics.softAdd(rowVar);
- }
- }
- }
- }
- d_updatedBounds.purge();
-
- while(!d_candidateBasics.empty()){
- ArithVar candidate = d_candidateBasics.back();
- d_candidateBasics.pop_back();
- Assert(d_tableau.isBasic(candidate));
- propagateCandidate(candidate);
- }
+ return d_internal->getEqualityStatus(a,b);
}
}/* CVC4::theory::arith namespace */
diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h
index 0e5a7e7e4..10c79b293 100644
--- a/src/theory/arith/theory_arith.h
+++ b/src/theory/arith/theory_arith.h
@@ -10,44 +10,16 @@
** information.\endverbatim
**
** \brief Arithmetic theory.
- ** ** Arithmetic theory.
+ ** Arithmetic theory.
**/
#include "cvc4_private.h"
-#ifndef __CVC4__THEORY__ARITH__THEORY_ARITH_H
-#define __CVC4__THEORY__ARITH__THEORY_ARITH_H
+#pragma once
#include "theory/theory.h"
-#include "context/context.h"
-#include "context/cdlist.h"
-#include "context/cdhashset.h"
-#include "context/cdinsert_hashmap.h"
-#include "context/cdqueue.h"
#include "expr/node.h"
-
-#include "util/dense_map.h"
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/arith_rewriter.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arithvar_node_map.h"
-#include "theory/arith/dio_solver.h"
-#include "theory/arith/congruence_manager.h"
-
-#include "theory/arith/constraint.h"
-
-#include "util/statistics_registry.h"
-#include "util/result.h"
-
-#include <vector>
-#include <map>
-#include <queue>
+#include "theory/arith/theory_arith_private_forward.h"
namespace CVC4 {
namespace theory {
@@ -64,292 +36,12 @@ namespace arith {
* http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
*/
class TheoryArith : public Theory {
- friend class quantifiers::InstStrategySimplex;
private:
- bool d_nlIncomplete;
- // TODO A better would be:
- //context::CDO<bool> d_nlIncomplete;
-
- enum Result::Sat d_qflraStatus;
- // check()
- // !done() -> d_qflraStatus = Unknown
- // fullEffort(e) -> simplex returns either sat or unsat
- // !fullEffort(e) -> simplex returns either sat, unsat or unknown
- // if unknown, save the assignment
- // if unknown, the simplex priority queue cannot be emptied
- int d_unknownsInARow;
-
-
- /**
- * This counter is false if nothing has been done since the last cut.
- * This is used to break an infinite loop.
- */
- bool d_hasDoneWorkSinceCut;
-
- /** Static learner. */
- ArithStaticLearner d_learner;
-
-
- ArithVar d_numberOfVariables;
- inline ArithVar getNumberOfVariables() const { return d_numberOfVariables; }
- std::vector<ArithVar> d_pool;
- void releaseArithVar(ArithVar v);
-
- /**
- * The map between arith variables to nodes.
- */
- ArithVarNodeMap d_arithvarNodeMap;
-
- typedef ArithVarNodeMap::var_iterator var_iterator;
- var_iterator var_begin() const { return d_arithvarNodeMap.var_begin(); }
- var_iterator var_end() const { return d_arithvarNodeMap.var_end(); }
-
- NodeSet d_setupNodes;
- bool isSetup(Node n) const {
- return d_setupNodes.find(n) != d_setupNodes.end();
- }
- void markSetup(Node n){
- Assert(!isSetup(n));
- d_setupNodes.insert(n);
- }
-
- void setupDivLike(const Variable& x);
-
- void setupVariable(const Variable& x);
- void setupVariableList(const VarList& vl);
- void setupPolynomial(const Polynomial& poly);
- void setupAtom(TNode atom);
-
- void cautiousSetupPolynomial(const Polynomial& p);
-
- class SetupLiteralCallBack : public TNodeCallBack {
- private:
- TheoryArith* d_arith;
- public:
- SetupLiteralCallBack(TheoryArith* ta) : d_arith(ta){}
- void operator()(TNode lit){
- TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit;
- if(!d_arith->isSetup(atom)){
- d_arith->setupAtom(atom);
- }
- }
- } d_setupLiteralCallback;
-
- /**
- * A superset of all of the assertions that currently are not the literal for
- * their constraint do not match constraint literals. Not just the witnesses.
- */
- context::CDInsertHashMap<Node, Constraint, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals;
-
- /**
- * (For the moment) the type hierarchy goes as:
- * Integer <: Real
- * The type number of a variable is an integer representing the most specific
- * type of the variable. The possible values of type number are:
- */
- enum ArithType
- {
- ATReal = 0,
- ATInteger = 1
- };
-
- std::vector<ArithType> d_variableTypes;
- inline ArithType nodeToArithType(TNode x) const {
- return (x.getType().isInteger() ? ATInteger : ATReal);
- }
-
- /** Returns true if x is of type Integer. */
- inline bool isInteger(ArithVar x) const {
- return d_variableTypes[x] >= ATInteger;
- }
-
- /** This is the set of variables initially introduced as slack variables. */
- std::vector<bool> d_slackVars;
-
- /** Returns true if the variable was initially introduced as a slack variable. */
- inline bool isSlackVariable(ArithVar x) const{
- return d_slackVars[x];
- }
-
- /**
- * On full effort checks (after determining LA(Q) satisfiability), we
- * consider integer vars, but we make sure to do so fairly to avoid
- * nontermination (although this isn't a guarantee). To do it fairly,
- * we consider variables in round-robin fashion. This is the
- * round-robin index.
- */
- ArithVar d_nextIntegerCheckVar;
-
- /**
- * Queue of Integer variables that are known to be equal to a constant.
- */
- context::CDQueue<ArithVar> d_constantIntegerVariables;
-
- Node callDioSolver();
- Node dioCutting();
-
- Comparison mkIntegerEqualityFromAssignment(ArithVar v);
-
- /**
- * List of all of the disequalities asserted in the current context that are not known
- * to be satisfied.
- */
- context::CDQueue<Constraint> d_diseqQueue;
-
- /**
- * Constraints that have yet to be processed by proagation work list.
- * All of the elements have type of LowerBound, UpperBound, or
- * Equality.
- *
- * This is empty at the beginning of every check call.
- *
- * If head()->getType() == LowerBound or UpperBound,
- * then d_cPL[1] is the previous constraint in d_partialModel for the
- * corresponding bound.
- * If head()->getType() == Equality,
- * then d_cPL[1] is the previous lowerBound in d_partialModel,
- * and d_cPL[2] is the previous upperBound in d_partialModel.
- */
- std::deque<Constraint> d_currentPropagationList;
-
- context::CDQueue<Constraint> d_learnedBounds;
-
- /**
- * Manages information about the assignment and upper and lower bounds on
- * variables.
- */
- ArithPartialModel d_partialModel;
-
- /**
- * The tableau for all of the constraints seen thus far in the system.
- */
- Tableau d_tableau;
-
- /**
- * Maintains the relationship between the PartialModel and the Tableau.
- */
- LinearEqualityModule d_linEq;
-
- /**
- * A Diophantine equation solver. Accesses the tableau and partial
- * model (each in a read-only fashion).
- */
- DioSolver d_diosolver;
-
- /** Counts the number of notifyRestart() calls to the theory. */
- uint32_t d_restartsCounter;
-
- /**
- * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
- * the density of the tableau, d, is computed.
- * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
- * is set to d_initialTableau.
- */
- bool d_tableauSizeHasBeenModified;
- double d_tableauResetDensity;
- uint32_t d_tableauResetPeriod;
- static const uint32_t s_TABLEAU_RESET_INCREMENT = 5;
-
-
- /** This is only used by simplex at the moment. */
- context::CDList<Node> d_conflicts;
- class PushCallBack : public NodeCallBack {
- private:
- context::CDList<Node>& d_list;
- public:
- PushCallBack(context::CDList<Node>& l)
- : d_list(l)
- {}
- void operator()(Node n){
- d_list.push_back(n);
- }
- } d_raiseConflict;
-
- /** Returns true iff a conflict has been raised. */
- inline bool inConflict() const {
- return !d_conflicts.empty();
- }
- /**
- * Outputs the contents of d_conflicts onto d_out.
- * Must be inConflict().
- */
- void outputConflicts();
-
-
- class TempVarMalloc : public ArithVarMalloc {
- private:
- TheoryArith& d_ta;
- public:
- TempVarMalloc(TheoryArith& ta) : d_ta(ta) {}
- ArithVar request(){
- Node skolem = mkRealSkolem("tmpVar");
- return d_ta.requestArithVar(skolem, false);
- }
- void release(ArithVar v){ d_ta.releaseArithVar(v); }
- } d_tempVarMalloc;
-
- /**
- * A copy of the tableau.
- * This is equivalent to the original tableau if d_tableauSizeHasBeenModified
- * is false.
- * The set of basic and non-basic variables may differ from d_tableau.
- */
- Tableau d_smallTableauCopy;
-
- /**
- * Returns true if all of the basic variables in the simplex queue of
- * basic variables that violate their bounds in the current tableau
- * are basic in d_smallTableauCopy.
- *
- * d_tableauSizeHasBeenModified must be false when calling this.
- * Simplex's priority queue must be in collection mode.
- */
- bool safeToReset() const;
-
- /** This keeps track of difference equalities. Mostly for sharing. */
- ArithCongruenceManager d_congruenceManager;
-
- /** This implements the Simplex decision procedure. */
- SimplexDecisionProcedure d_simplex;
-
-
- /** The constraint database associated with the theory. */
- ConstraintDatabase d_constraintDatabase;
-
- class ModelException : public Exception {
- public:
- ModelException(TNode n, const char* msg) throw ();
- virtual ~ModelException() throw ();
- };
-
- /** Internal model value for the node */
- DeltaRational getDeltaValue(TNode n) const throw (DeltaRationalException, ModelException);
-
- /** Uninterpretted function symbol for use when interpreting
- * division by zero.
- */
- Node d_realDivideBy0Func;
- Node d_intDivideBy0Func;
- Node d_intModulusBy0Func;
- Node getRealDivideBy0Func();
- Node getIntDivideBy0Func();
- Node getIntModulusBy0Func();
-
- Node definingIteForDivLike(Node divLike);
- Node axiomIteForTotalDivision(Node div_tot);
- Node axiomIteForTotalIntDivision(Node int_div_like);
-
+ friend class quantifiers::InstStrategySimplex;
+ friend class TheoryArithPrivate;
- class DeltaComputeCallback : public RationalCallBack {
- private:
- const TheoryArith* d_ta;
- public:
- DeltaComputeCallback(const TheoryArith* ta) : d_ta(ta){}
+ TheoryArithPrivate* d_internal;
- Rational operator()() const{
- return d_ta->deltaValueForTotalOrder();
- }
- } d_deltaComputeCallback;
public:
TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
@@ -366,7 +58,6 @@ public:
void propagate(Effort e);
Node explain(TNode n);
- Rational deltaValueForTotalOrder() const;
void collectModelInfo( TheoryModel* m, bool fullModel );
void shutdown(){ }
@@ -382,212 +73,8 @@ public:
EqualityStatus getEqualityStatus(TNode a, TNode b);
void addSharedTerm(TNode n);
-
-private:
-
- class BasicVarModelUpdateCallBack : public ArithVarCallBack{
- private:
- SimplexDecisionProcedure& d_simplex;
-
- public:
- BasicVarModelUpdateCallBack(SimplexDecisionProcedure& s):
- d_simplex(s)
- {}
-
- void operator()(ArithVar x){
- d_simplex.updateBasic(x);
- }
- };
-
- BasicVarModelUpdateCallBack d_basicVarModelUpdateCallBack;
-
- /** The constant zero. */
- DeltaRational d_DELTA_ZERO;
-
- /** propagates an arithvar */
- void propagateArithVar(bool upperbound, ArithVar var );
-
- /**
- * Using the simpleKind return the ArithVar associated with the assertion.
- */
- ArithVar determineArithVar(const Polynomial& p) const;
- ArithVar determineArithVar(TNode assertion) const;
-
- /**
- * Splits the disequalities in d_diseq that are violated using lemmas on demand.
- * returns true if any lemmas were issued.
- * returns false if all disequalities are satisfied in the current model.
- */
- bool splitDisequalities();
-
- /** A Difference variable is known to be 0.*/
- void zeroDifferenceDetected(ArithVar x);
-
-
- /**
- * Looks for the next integer variable without an integer assignment in a round robin fashion.
- * Changes the value of d_nextIntegerCheckVar.
- *
- * If this returns false, d_nextIntegerCheckVar does not have an integer assignment.
- * If this returns true, all integer variables have an integer assignment.
- */
- bool hasIntegerModel();
-
- /**
- * Issues branches for non-slack integer variables with non-integer assignments.
- * Returns a cut for a lemma.
- * If there is an integer model, this returns Node::null().
- */
- Node roundRobinBranch();
-
- /**
- * This requests a new unique ArithVar value for x.
- * This also does initial (not context dependent) set up for a variable,
- * except for setting up the initial.
- */
- ArithVar requestArithVar(TNode x, bool slack);
-
- /** Initial (not context dependent) sets up for a variable.*/
- void setupBasicValue(ArithVar x);
-
- /** Initial (not context dependent) sets up for a new slack variable.*/
- void setupSlack(TNode left);
-
-
- /**
- * Assert*(n, orig) takes an bound n that is implied by orig.
- * and asserts that as a new bound if it is tighter than the current bound
- * and updates the value of a basic variable if needed.
- *
- * orig must be a literal in the SAT solver so that it can be used for
- * conflict analysis.
- *
- * x is the variable getting the new bound,
- * c is the value of the new bound.
- *
- * If this new bound is in conflict with the other bound,
- * a node describing this conflict is returned.
- * If this new bound is not in conflict, Node::null() is returned.
- */
- bool AssertLower(Constraint constraint);
- bool AssertUpper(Constraint constraint);
- bool AssertEquality(Constraint constraint);
- bool AssertDisequality(Constraint constraint);
-
- /** Tracks the bounds that were updated in the current round. */
- DenseSet d_updatedBounds;
-
- /** Tracks the basic variables where propagation might be possible. */
- DenseSet d_candidateBasics;
-
- bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
- void clearUpdates();
-
- void revertOutOfConflict();
-
- void propagateCandidates();
- void propagateCandidate(ArithVar basic);
- bool propagateCandidateBound(ArithVar basic, bool upperBound);
-
- inline bool propagateCandidateLowerBound(ArithVar basic){
- return propagateCandidateBound(basic, false);
- }
- inline bool propagateCandidateUpperBound(ArithVar basic){
- return propagateCandidateBound(basic, true);
- }
-
- /**
- * Performs a check to see if it is definitely true that setup can be avoided.
- */
- bool canSafelyAvoidEqualitySetup(TNode equality);
-
- /**
- * Handles the case splitting for check() for a new assertion.
- * Returns a conflict if one was found.
- * Returns Node::null if no conflict was found.
- */
- Constraint constraintFromFactQueue();
- bool assertionCases(Constraint c);
-
- /**
- * Returns the basic variable with the shorted row containing a non-basic variable.
- * If no such row exists, return ARITHVAR_SENTINEL.
- */
- ArithVar findShortestBasicRow(ArithVar variable);
-
- /**
- * Debugging only routine!
- * Returns true iff every variable is consistent in the partial model.
- */
- bool entireStateIsConsistent(const std::string& locationHint);
- bool unenqueuedVariablesAreConsistent();
-
- bool isImpliedUpperBound(ArithVar var, Node exp);
- bool isImpliedLowerBound(ArithVar var, Node exp);
-
- void internalExplain(TNode n, NodeBuilder<>& explainBuilder);
-
-
- void asVectors(const Polynomial& p,
- std::vector<Rational>& coeffs,
- std::vector<ArithVar>& variables);
-
- /** Routine for debugging. Print the assertions the theory is aware of. */
- void debugPrintAssertions();
- /** Debugging only routine. Prints the model. */
- void debugPrintModel();
-
- /** Counts the number of fullCheck calls to arithmetic. */
- uint32_t d_fullCheckCounter;
- std::vector<Node> cutAllBounded() const;
- Node branchIntegerVariable(ArithVar x) const;
-
- context::CDO<unsigned> d_cutsInContext;
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
-
- IntStat d_statUserVariables, d_statSlackVariables;
- IntStat d_statDisequalitySplits;
- IntStat d_statDisequalityConflicts;
- TimerStat d_simplifyTimer;
- TimerStat d_staticLearningTimer;
-
- TimerStat d_presolveTime;
-
- TimerStat d_newPropTime;
-
- IntStat d_externalBranchAndBounds;
-
- IntStat d_initialTableauSize;
- IntStat d_currSetToSmaller;
- IntStat d_smallerSetToCurr;
- TimerStat d_restartTimer;
-
- TimerStat d_boundComputationTime;
- IntStat d_boundComputations, d_boundPropagations;
-
- IntStat d_unknownChecks;
- IntStat d_maxUnknownsInARow;
- AverageStat d_avgUnknownsInARow;
-
- IntStat d_revertsOnConflicts;
- IntStat d_commitsOnConflicts;
- IntStat d_nontrivialSatChecks;
-
- Statistics();
- ~Statistics();
- };
-
- Statistics d_statistics;
-
-
};/* class TheoryArith */
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__ARITH__THEORY_ARITH_H */
diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp
new file mode 100644
index 000000000..263f9536b
--- /dev/null
+++ b/src/theory/arith/theory_arith_private.cpp
@@ -0,0 +1,2692 @@
+/********************* */
+/*! \file theory_arith.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, ajreynol, mdeters, dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "theory/arith/theory_arith_private.h"
+
+#include "expr/node.h"
+#include "expr/kind.h"
+#include "expr/metakind.h"
+#include "expr/node_builder.h"
+
+#include "context/context.h"
+#include "context/cdlist.h"
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdqueue.h"
+
+#include "theory/valuation.h"
+#include "theory/rewriter.h"
+
+#include "util/rational.h"
+#include "util/integer.h"
+#include "util/boolean_simplification.h"
+#include "util/dense_map.h"
+#include "util/statistics_registry.h"
+#include "util/result.h"
+
+#include "smt/logic_exception.h"
+
+#include "theory/arith/arithvar.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/matrix.h"
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/linear_equality.h"
+#include "theory/arith/simplex.h"
+#include "theory/arith/arith_static_learner.h"
+#include "theory/arith/dio_solver.h"
+#include "theory/arith/congruence_manager.h"
+
+#include "theory/arith/approx_simplex.h"
+#include "theory/arith/constraint.h"
+
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/matrix.h"
+
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/constraint.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/arith/normal_form.h"
+#include "theory/model.h"
+
+#include "theory/arith/options.h"
+
+#include <stdint.h>
+
+#include <vector>
+#include <map>
+#include <queue>
+
+using namespace std;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) :
+ d_containing(containing),
+ d_nlIncomplete( false),
+ d_boundTracking(),
+ d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this)),
+ d_qflraStatus(Result::SAT_UNKNOWN),
+ d_unknownsInARow(0),
+ d_hasDoneWorkSinceCut(false),
+ d_learner(u),
+ d_assertionsThatDoNotMatchTheirLiterals(c),
+ d_nextIntegerCheckVar(0),
+ d_constantIntegerVariables(c),
+ d_diseqQueue(c, false),
+ d_currentPropagationList(),
+ d_learnedBounds(c),
+ d_partialModel(c, DeltaComputeCallback(*this)),
+ d_errorSet(d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(&d_boundTracking)),
+ d_tableau(),
+ d_linEq(d_partialModel, d_tableau, d_boundTracking, BasicVarModelUpdateCallBack(*this)),
+ d_diosolver(c),
+ d_restartsCounter(0),
+ d_tableauSizeHasBeenModified(false),
+ d_tableauResetDensity(1.6),
+ d_tableauResetPeriod(10),
+ d_conflicts(c),
+ d_congruenceManager(c, d_constraintDatabase, SetupLiteralCallBack(*this), d_partialModel, RaiseConflict(*this)),
+ d_dualSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_pureUpdate(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_DELTA_ZERO(0),
+ d_fullCheckCounter(0),
+ d_cutCount(c, 0),
+ d_cutInContext(c),
+ d_statistics()
+{
+}
+
+TheoryArithPrivate::~TheoryArithPrivate(){ }
+
+
+void TheoryArithPrivate::setMasterEqualityEngine(eq::EqualityEngine* eq) {
+ d_congruenceManager.setMasterEqualityEngine(eq);
+}
+
+Node TheoryArithPrivate::getRealDivideBy0Func(){
+ Assert(!getLogicInfo().isLinear());
+ Assert(getLogicInfo().areRealsUsed());
+
+ if(d_realDivideBy0Func.isNull()){
+ TypeNode realType = NodeManager::currentNM()->realType();
+ d_realDivideBy0Func = skolemFunction("/by0_$$", realType, realType);
+ }
+ return d_realDivideBy0Func;
+}
+
+Node TheoryArithPrivate::getIntDivideBy0Func(){
+ Assert(!getLogicInfo().isLinear());
+ Assert(getLogicInfo().areIntegersUsed());
+
+ if(d_intDivideBy0Func.isNull()){
+ TypeNode intType = NodeManager::currentNM()->integerType();
+ d_intDivideBy0Func = skolemFunction("divby0_$$", intType, intType);
+ }
+ return d_intDivideBy0Func;
+}
+
+Node TheoryArithPrivate::getIntModulusBy0Func(){
+ Assert(!getLogicInfo().isLinear());
+ Assert(getLogicInfo().areIntegersUsed());
+
+ if(d_intModulusBy0Func.isNull()){
+ TypeNode intType = NodeManager::currentNM()->integerType();
+ d_intModulusBy0Func = skolemFunction("modby0_$$", intType, intType);
+ }
+ return d_intModulusBy0Func;
+}
+
+TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg) throw (){
+ stringstream ss;
+ ss << "Cannot construct a model for " << n << " as " << endl << msg;
+ setMessage(ss.str());
+}
+TheoryArithPrivate::ModelException::~ModelException() throw (){ }
+
+
+TheoryArithPrivate::Statistics::Statistics():
+ d_statAssertUpperConflicts("theory::arith::AssertUpperConflicts", 0),
+ d_statAssertLowerConflicts("theory::arith::AssertLowerConflicts", 0),
+ d_statUserVariables("theory::arith::UserVariables", 0),
+ d_statSlackVariables("theory::arith::SlackVariables", 0),
+ d_statDisequalitySplits("theory::arith::DisequalitySplits", 0),
+ d_statDisequalityConflicts("theory::arith::DisequalityConflicts", 0),
+ d_simplifyTimer("theory::arith::simplifyTimer"),
+ d_staticLearningTimer("theory::arith::staticLearningTimer"),
+ d_presolveTime("theory::arith::presolveTime"),
+ d_newPropTime("theory::arith::newPropTimer"),
+ d_externalBranchAndBounds("theory::arith::externalBranchAndBounds",0),
+ d_initialTableauSize("theory::arith::initialTableauSize", 0),
+ d_currSetToSmaller("theory::arith::currSetToSmaller", 0),
+ d_smallerSetToCurr("theory::arith::smallerSetToCurr", 0),
+ d_restartTimer("theory::arith::restartTimer"),
+ d_boundComputationTime("theory::arith::bound::time"),
+ d_boundComputations("theory::arith::bound::boundComputations",0),
+ d_boundPropagations("theory::arith::bound::boundPropagations",0),
+ d_unknownChecks("theory::arith::status::unknowns", 0),
+ d_maxUnknownsInARow("theory::arith::status::maxUnknownsInARow", 0),
+ d_avgUnknownsInARow("theory::arith::status::avgUnknownsInARow"),
+ d_revertsOnConflicts("theory::arith::status::revertsOnConflicts",0),
+ d_commitsOnConflicts("theory::arith::status::commitsOnConflicts",0),
+ d_nontrivialSatChecks("theory::arith::status::nontrivialSatChecks",0),
+ d_satPivots("pivots::sat"),
+ d_unsatPivots("pivots::unsat"),
+ d_unknownPivots("pivots::unkown")
+{
+ StatisticsRegistry::registerStat(&d_statAssertUpperConflicts);
+ StatisticsRegistry::registerStat(&d_statAssertLowerConflicts);
+
+ StatisticsRegistry::registerStat(&d_statUserVariables);
+ StatisticsRegistry::registerStat(&d_statSlackVariables);
+ StatisticsRegistry::registerStat(&d_statDisequalitySplits);
+ StatisticsRegistry::registerStat(&d_statDisequalityConflicts);
+ StatisticsRegistry::registerStat(&d_simplifyTimer);
+ StatisticsRegistry::registerStat(&d_staticLearningTimer);
+
+ StatisticsRegistry::registerStat(&d_presolveTime);
+ StatisticsRegistry::registerStat(&d_newPropTime);
+
+ StatisticsRegistry::registerStat(&d_externalBranchAndBounds);
+
+ StatisticsRegistry::registerStat(&d_initialTableauSize);
+ StatisticsRegistry::registerStat(&d_currSetToSmaller);
+ StatisticsRegistry::registerStat(&d_smallerSetToCurr);
+ StatisticsRegistry::registerStat(&d_restartTimer);
+
+ StatisticsRegistry::registerStat(&d_boundComputationTime);
+ StatisticsRegistry::registerStat(&d_boundComputations);
+ StatisticsRegistry::registerStat(&d_boundPropagations);
+
+ StatisticsRegistry::registerStat(&d_unknownChecks);
+ StatisticsRegistry::registerStat(&d_maxUnknownsInARow);
+ StatisticsRegistry::registerStat(&d_avgUnknownsInARow);
+ StatisticsRegistry::registerStat(&d_revertsOnConflicts);
+ StatisticsRegistry::registerStat(&d_commitsOnConflicts);
+ StatisticsRegistry::registerStat(&d_nontrivialSatChecks);
+
+
+ StatisticsRegistry::registerStat(&d_satPivots);
+ StatisticsRegistry::registerStat(&d_unsatPivots);
+ StatisticsRegistry::registerStat(&d_unknownPivots);
+}
+
+TheoryArithPrivate::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_statAssertUpperConflicts);
+ StatisticsRegistry::unregisterStat(&d_statAssertLowerConflicts);
+
+ StatisticsRegistry::unregisterStat(&d_statUserVariables);
+ StatisticsRegistry::unregisterStat(&d_statSlackVariables);
+ StatisticsRegistry::unregisterStat(&d_statDisequalitySplits);
+ StatisticsRegistry::unregisterStat(&d_statDisequalityConflicts);
+ StatisticsRegistry::unregisterStat(&d_simplifyTimer);
+ StatisticsRegistry::unregisterStat(&d_staticLearningTimer);
+
+ StatisticsRegistry::unregisterStat(&d_presolveTime);
+ StatisticsRegistry::unregisterStat(&d_newPropTime);
+
+ StatisticsRegistry::unregisterStat(&d_externalBranchAndBounds);
+
+ StatisticsRegistry::unregisterStat(&d_initialTableauSize);
+ StatisticsRegistry::unregisterStat(&d_currSetToSmaller);
+ StatisticsRegistry::unregisterStat(&d_smallerSetToCurr);
+ StatisticsRegistry::unregisterStat(&d_restartTimer);
+
+ StatisticsRegistry::unregisterStat(&d_boundComputationTime);
+ StatisticsRegistry::unregisterStat(&d_boundComputations);
+ StatisticsRegistry::unregisterStat(&d_boundPropagations);
+
+ StatisticsRegistry::unregisterStat(&d_unknownChecks);
+ StatisticsRegistry::unregisterStat(&d_maxUnknownsInARow);
+ StatisticsRegistry::unregisterStat(&d_avgUnknownsInARow);
+ StatisticsRegistry::unregisterStat(&d_revertsOnConflicts);
+ StatisticsRegistry::unregisterStat(&d_commitsOnConflicts);
+ StatisticsRegistry::unregisterStat(&d_nontrivialSatChecks);
+
+ StatisticsRegistry::unregisterStat(&d_satPivots);
+ StatisticsRegistry::unregisterStat(&d_unsatPivots);
+ StatisticsRegistry::unregisterStat(&d_unknownPivots);
+}
+
+void TheoryArithPrivate::revertOutOfConflict(){
+ d_partialModel.revertAssignmentChanges();
+ clearUpdates();
+ d_currentPropagationList.clear();
+}
+
+void TheoryArithPrivate::clearUpdates(){
+ d_updatedBounds.purge();
+}
+
+void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){
+ Assert(d_congruenceManager.isWatchedVariable(x));
+ Assert(d_partialModel.upperBoundIsZero(x));
+ Assert(d_partialModel.lowerBoundIsZero(x));
+
+ Constraint lb = d_partialModel.getLowerBoundConstraint(x);
+ Constraint ub = d_partialModel.getUpperBoundConstraint(x);
+
+ if(lb->isEquality()){
+ d_congruenceManager.watchedVariableIsZero(lb);
+ }else if(ub->isEquality()){
+ d_congruenceManager.watchedVariableIsZero(ub);
+ }else{
+ d_congruenceManager.watchedVariableIsZero(lb, ub);
+ }
+}
+
+/* procedure AssertLower( x_i >= c_i ) */
+bool TheoryArithPrivate::AssertLower(Constraint constraint){
+ Assert(constraint != NullConstraint);
+ Assert(constraint->isLowerBound());
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Debug("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
+
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ //TODO Relax to less than?
+ if(d_partialModel.lessThanLowerBound(x_i, c_i)){
+ return false; //sat
+ }
+
+ int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+ if(cmpToUB > 0){ // c_i < \lowerbound(x_i)
+ Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i);
+ Node conflict = ConstraintValue::explainConflict(ubc, constraint);
+ Debug("arith") << "AssertLower conflict " << conflict << endl;
+ ++(d_statistics.d_statAssertLowerConflicts);
+ raiseConflict(conflict);
+ return true;
+ }else if(cmpToUB == 0){
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Debug("dio::push") << x_i << endl;
+ }
+ Constraint ub = d_partialModel.getUpperBoundConstraint(x_i);
+
+ if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
+ // if it is not a watched variable report it
+ // if it is is a watched variable and c_i == 0,
+ // let zeroDifferenceDetected(x_i) catch this
+ d_congruenceManager.equalsConstant(constraint, ub);
+ }
+
+ const ValueCollection& vc = constraint->getValueCollection();
+ if(vc.hasDisequality()){
+ Assert(vc.hasEquality());
+ const Constraint eq = vc.getEquality();
+ const Constraint diseq = vc.getDisequality();
+ if(diseq->isTrue()){
+ //const Constraint ub = vc.getUpperBound();
+ Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint);
+
+ ++(d_statistics.d_statDisequalityConflicts);
+ Debug("eq") << " assert lower conflict " << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }else if(!eq->isTrue()){
+ Debug("eq") << "lb == ub, propagate eq" << eq << endl;
+ eq->impliedBy(constraint, d_partialModel.getUpperBoundConstraint(x_i));
+ // do not need to add to d_learnedBounds
+ }
+ }
+ }else{
+ Assert(cmpToUB < 0);
+ const ValueCollection& vc = constraint->getValueCollection();
+
+ if(vc.hasDisequality()){
+ const Constraint diseq = vc.getDisequality();
+ if(diseq->isTrue()){
+ const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+
+ if(ub->hasProof()){
+ Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint);
+ Debug("eq") << " assert upper conflict " << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }else if(!ub->negationHasProof()){
+ Constraint negUb = ub->getNegation();
+ negUb->impliedBy(constraint, diseq);
+ d_learnedBounds.push_back(negUb);
+ }
+ }
+ }
+ }
+
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+
+ d_partialModel.setLowerBoundConstraint(constraint);
+
+ if(d_congruenceManager.isWatchedVariable(x_i)){
+ int sgn = c_i.sgn();
+ if(sgn > 0){
+ d_congruenceManager.watchedVariableCannotBeZero(constraint);
+ }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){
+ zeroDifferenceDetected(x_i);
+ }
+ }
+
+ d_updatedBounds.softAdd(x_i);
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(d_partialModel.getAssignment(x_i) < c_i){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false; //sat
+}
+
+/* procedure AssertUpper( x_i <= c_i) */
+bool TheoryArithPrivate::AssertUpper(Constraint constraint){
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Debug("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+ AssertArgument(constraint != NullConstraint,
+ "AssertUpper() called on a NullConstraint.");
+ Assert(constraint->isUpperBound());
+
+ //Too strong because of rounding with integers
+ //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ Debug("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+
+ if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
+ return false; //sat
+ }
+
+ // cmpToLb = \lowerbound(x_i).cmp(c_i)
+ int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+ if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i)
+ Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i);
+ Node conflict = ConstraintValue::explainConflict(lbc, constraint);
+ Debug("arith") << "AssertUpper conflict " << conflict << endl;
+ ++(d_statistics.d_statAssertUpperConflicts);
+ raiseConflict(conflict);
+ return true;
+ }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Debug("dio::push") << x_i << endl;
+ }
+ Constraint lb = d_partialModel.getLowerBoundConstraint(x_i);
+ if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){
+ // if it is not a watched variable report it
+ // if it is is a watched variable and c_i == 0,
+ // let zeroDifferenceDetected(x_i) catch this
+ d_congruenceManager.equalsConstant(lb, constraint);
+ }
+
+ const ValueCollection& vc = constraint->getValueCollection();
+ if(vc.hasDisequality()){
+ Assert(vc.hasEquality());
+ const Constraint diseq = vc.getDisequality();
+ const Constraint eq = vc.getEquality();
+ if(diseq->isTrue()){
+ Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint);
+ Debug("eq") << " assert upper conflict " << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }else if(!eq->isTrue()){
+ Debug("eq") << "lb == ub, propagate eq" << eq << endl;
+ eq->impliedBy(constraint, d_partialModel.getLowerBoundConstraint(x_i));
+ //do not bother to add to d_learnedBounds
+ }
+ }
+ }else if(cmpToLB > 0){
+ const ValueCollection& vc = constraint->getValueCollection();
+ if(vc.hasDisequality()){
+ const Constraint diseq = vc.getDisequality();
+ if(diseq->isTrue()){
+ const Constraint lb =
+ d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+ if(lb->hasProof()){
+ Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint);
+ Debug("eq") << " assert upper conflict " << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }else if(!lb->negationHasProof()){
+ Constraint negLb = lb->getNegation();
+ negLb->impliedBy(constraint, diseq);
+ //if(!negLb->canBePropagated()){
+ d_learnedBounds.push_back(negLb);
+ //}//otherwise let this be propagated/asserted later
+ }
+ }
+ }
+ }
+
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+ //It is fine if this is NullConstraint
+
+ d_partialModel.setUpperBoundConstraint(constraint);
+
+ if(d_congruenceManager.isWatchedVariable(x_i)){
+ int sgn = c_i.sgn();
+ if(sgn < 0){
+ d_congruenceManager.watchedVariableCannotBeZero(constraint);
+ }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){
+ zeroDifferenceDetected(x_i);
+ }
+ }
+
+ d_updatedBounds.softAdd(x_i);
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(d_partialModel.getAssignment(x_i) > c_i){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false; //sat
+}
+
+
+/* procedure AssertEquality( x_i == c_i ) */
+bool TheoryArithPrivate::AssertEquality(Constraint constraint){
+ AssertArgument(constraint != NullConstraint,
+ "AssertUpper() called on a NullConstraint.");
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Debug("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
+
+ //Should be fine in integers
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+ int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+
+ // u_i <= c_i <= l_i
+ // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
+ if(cmpToUB >= 0 && cmpToLB <= 0){
+ return false; //sat
+ }
+
+ if(cmpToUB > 0){
+ Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i);
+ Node conflict = ConstraintValue::explainConflict(ubc, constraint);
+ Debug("arith") << "AssertEquality conflicts with upper bound " << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }
+
+ if(cmpToLB < 0){
+ Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i);
+ Node conflict = ConstraintValue::explainConflict(lbc, constraint);
+ Debug("arith") << "AssertEquality conflicts with lower bound" << conflict << endl;
+ raiseConflict(conflict);
+ return true;
+ }
+
+ Assert(cmpToUB <= 0);
+ Assert(cmpToLB >= 0);
+ Assert(cmpToUB < 0 || cmpToLB > 0);
+
+
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Debug("dio::push") << x_i << endl;
+ }
+
+ // Don't bother to check whether x_i != c_i is in d_diseq
+ // The a and (not a) should never be on the fact queue
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+ d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+
+ d_partialModel.setUpperBoundConstraint(constraint);
+ d_partialModel.setLowerBoundConstraint(constraint);
+
+ if(d_congruenceManager.isWatchedVariable(x_i)){
+ int sgn = c_i.sgn();
+ if(sgn == 0){
+ zeroDifferenceDetected(x_i);
+ }else{
+ d_congruenceManager.watchedVariableCannotBeZero(constraint);
+ d_congruenceManager.equalsConstant(constraint);
+ }
+ }else{
+ d_congruenceManager.equalsConstant(constraint);
+ }
+
+ d_updatedBounds.softAdd(x_i);
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(!(d_partialModel.getAssignment(x_i) == c_i)){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(Debug.isOn("model")) {
+ Debug("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false;
+}
+
+
+/* procedure AssertDisequality( x_i != c_i ) */
+bool TheoryArithPrivate::AssertDisequality(Constraint constraint){
+
+ AssertArgument(constraint != NullConstraint,
+ "AssertUpper() called on a NullConstraint.");
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Debug("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
+
+ //Should be fine in integers
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ if(d_congruenceManager.isWatchedVariable(x_i)){
+ int sgn = c_i.sgn();
+ if(sgn == 0){
+ d_congruenceManager.watchedVariableCannotBeZero(constraint);
+ }
+ }
+
+ const ValueCollection& vc = constraint->getValueCollection();
+ if(vc.hasLowerBound() && vc.hasUpperBound()){
+ const Constraint lb = vc.getLowerBound();
+ const Constraint ub = vc.getUpperBound();
+ if(lb->isTrue() && ub->isTrue()){
+ //in conflict
+ Debug("eq") << "explaining" << endl;
+ ++(d_statistics.d_statDisequalityConflicts);
+ Node conflict = ConstraintValue::explainConflict(constraint, lb, ub);
+ raiseConflict(conflict);
+ return true;
+ }
+ }
+ if(vc.hasLowerBound() ){
+ const Constraint lb = vc.getLowerBound();
+ if(lb->isTrue()){
+ const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+ Debug("eq") << "propagate UpperBound " << constraint << lb << ub << endl;
+ const Constraint negUb = ub->getNegation();
+ if(!negUb->isTrue()){
+ negUb->impliedBy(constraint, lb);
+ d_learnedBounds.push_back(negUb);
+ }
+ }
+ }
+ if(vc.hasUpperBound()){
+ const Constraint ub = vc.getUpperBound();
+ if(ub->isTrue()){
+ const Constraint lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+
+ Debug("eq") << "propagate LowerBound " << constraint << lb << ub << endl;
+ const Constraint negLb = lb->getNegation();
+ if(!negLb->isTrue()){
+ negLb->impliedBy(constraint, ub);
+ d_learnedBounds.push_back(negLb);
+ }
+ }
+ }
+
+ bool split = constraint->isSplit();
+
+ if(!split && c_i == d_partialModel.getAssignment(x_i)){
+ Debug("eq") << "lemma now! " << constraint << endl;
+ outputLemma(constraint->split());
+ return false;
+ }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
+ Debug("eq") << "can drop as less than lb" << constraint << endl;
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
+ Debug("eq") << "can drop as less than ub" << constraint << endl;
+ }else if(!split){
+ Debug("eq") << "push back" << constraint << endl;
+ d_diseqQueue.push(constraint);
+ d_partialModel.invalidateDelta();
+ }else{
+ Debug("eq") << "skipping already split " << constraint << endl;
+ }
+ return false;
+}
+
+void TheoryArithPrivate::addSharedTerm(TNode n){
+ Debug("arith::addSharedTerm") << "addSharedTerm: " << n << endl;
+ if(n.isConst()){
+ d_partialModel.invalidateDelta();
+ }
+
+ d_congruenceManager.addSharedTerm(n);
+ if(!n.isConst() && !isSetup(n)){
+ Polynomial poly = Polynomial::parsePolynomial(n);
+ Polynomial::iterator it = poly.begin();
+ Polynomial::iterator it_end = poly.end();
+ for (; it != it_end; ++ it) {
+ Monomial m = *it;
+ if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
+ setupVariableList(m.getVarList());
+ }
+ }
+ }
+}
+
+Node TheoryArithPrivate::ppRewrite(TNode atom) {
+ Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
+
+
+ if (atom.getKind() == kind::EQUAL && options::arithRewriteEq()) {
+ Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1];
+ Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1];
+ Node rewritten = Rewriter::rewrite(leq.andNode(geq));
+ Debug("arith::preprocess") << "arith::preprocess() : returning "
+ << rewritten << endl;
+ return rewritten;
+ } else {
+ return atom;
+ }
+}
+
+Theory::PPAssertStatus TheoryArithPrivate::ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
+ Debug("simplify") << "TheoryArithPrivate::solve(" << in << ")" << endl;
+
+
+ // Solve equalities
+ Rational minConstant = 0;
+ Node minMonomial;
+ Node minVar;
+ if (in.getKind() == kind::EQUAL) {
+ Comparison cmp = Comparison::parseNormalForm(in);
+
+ Polynomial left = cmp.getLeft();
+ Polynomial right = cmp.getRight();
+
+ Monomial m = left.getHead();
+ if (m.getVarList().singleton()){
+ VarList vl = m.getVarList();
+ Node var = vl.getNode();
+ if (var.getKind() == kind::VARIABLE){
+ // if vl.isIntegral then m.getConstant().isOne()
+ if(!vl.isIntegral() || m.getConstant().isOne()){
+ minVar = var;
+ }
+ }
+ }
+
+ // Solve for variable
+ if (!minVar.isNull()) {
+ Polynomial right = cmp.getRight();
+ Node elim = right.getNode();
+ // ax + p = c -> (ax + p) -ax - c = -ax
+ // x = (p - ax - c) * -1/a
+ // Add the substitution if not recursive
+ Assert(elim == Rewriter::rewrite(elim));
+
+
+ static const unsigned MAX_SUB_SIZE = 2;
+ if(right.size() > MAX_SUB_SIZE){
+ Debug("simplify") << "TheoryArithPrivate::solve(): did not substitute due to the right hand side containing too many terms: " << minVar << ":" << elim << endl;
+ Debug("simplify") << right.size() << endl;
+ // cout << "TheoryArithPrivate::solve(): did not substitute due to the right hand side containing too many terms: " << minVar << ":" << elim << endl;
+ // cout << right.size() << endl;
+ }else if(elim.hasSubterm(minVar)){
+ Debug("simplify") << "TheoryArithPrivate::solve(): can't substitute due to recursive pattern with sharing: " << minVar << ":" << elim << endl;
+ // cout << "TheoryArithPrivate::solve(): can't substitute due to recursive pattern with sharing: " << minVar << ":" << elim << endl;
+
+ }else if (!minVar.getType().isInteger() || right.isIntegral()) {
+ Assert(!elim.hasSubterm(minVar));
+ // cannot eliminate integers here unless we know the resulting
+ // substitution is integral
+ Debug("simplify") << "TheoryArithPrivate::solve(): substitution " << minVar << " |-> " << elim << endl;
+ //cout << "TheoryArithPrivate::solve(): substitution " << minVar << " |-> " << elim << endl;
+
+ outSubstitutions.addSubstitution(minVar, elim);
+ return Theory::PP_ASSERT_STATUS_SOLVED;
+ } else {
+ Debug("simplify") << "TheoryArithPrivate::solve(): can't substitute b/c it's integer: " << minVar << ":" << minVar.getType() << " |-> " << elim << ":" << elim.getType() << endl;
+ //cout << "TheoryArithPrivate::solve(): can't substitute b/c it's integer: " << minVar << ":" << minVar.getType() << " |-> " << elim << ":" << elim.getType() << endl;
+
+ }
+ }
+ }
+
+ // If a relation, remember the bound
+ switch(in.getKind()) {
+ case kind::LEQ:
+ case kind::LT:
+ case kind::GEQ:
+ case kind::GT:
+ if (in[0].isVar()) {
+ d_learner.addBound(in);
+ }
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+
+ return Theory::PP_ASSERT_STATUS_UNSOLVED;
+}
+
+void TheoryArithPrivate::ppStaticLearn(TNode n, NodeBuilder<>& learned) {
+ TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
+
+ d_learner.staticLearning(n, learned);
+}
+
+
+
+ArithVar TheoryArithPrivate::findShortestBasicRow(ArithVar variable){
+ ArithVar bestBasic = ARITHVAR_SENTINEL;
+ uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
+
+ Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ Assert(entry.getColVar() == variable);
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+ uint32_t rowLength = d_tableau.getRowLength(ridx);
+ if((rowLength < bestRowLength) ||
+ (rowLength == bestRowLength && basic < bestBasic)){
+ bestBasic = basic;
+ bestRowLength = rowLength;
+ }
+ }
+ Assert(bestBasic == ARITHVAR_SENTINEL || bestRowLength < std::numeric_limits<uint32_t>::max());
+ return bestBasic;
+}
+
+void TheoryArithPrivate::setupVariable(const Variable& x){
+ Node n = x.getNode();
+
+ Assert(!isSetup(n));
+
+ ++(d_statistics.d_statUserVariables);
+ requestArithVar(n,false);
+ //ArithVar varN = requestArithVar(n,false);
+ //setupInitialValue(varN);
+
+ markSetup(n);
+
+
+ if(x.isDivLike()){
+ setupDivLike(x);
+ }
+
+}
+
+void TheoryArithPrivate::setupVariableList(const VarList& vl){
+ Assert(!vl.empty());
+
+ TNode vlNode = vl.getNode();
+ Assert(!isSetup(vlNode));
+ Assert(!d_partialModel.hasArithVar(vlNode));
+
+ for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
+ Variable var = *i;
+
+ if(!isSetup(var.getNode())){
+ setupVariable(var);
+ }
+ }
+
+ if(!vl.singleton()){
+ // vl is the product of at least 2 variables
+ // vl : (* v1 v2 ...)
+ if(getLogicInfo().isLinear()){
+ throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ }
+
+ setIncomplete();
+ d_nlIncomplete = true;
+
+ ++(d_statistics.d_statUserVariables);
+ requestArithVar(vlNode, false);
+ //ArithVar av = requestArithVar(vlNode, false);
+ //setupInitialValue(av);
+
+ markSetup(vlNode);
+ }
+
+ /* Note:
+ * Only call markSetup if the VarList is not a singleton.
+ * See the comment in setupPolynomail for more.
+ */
+}
+
+void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
+ if(p.containsConstant()){
+ if(!p.isConstant()){
+ Polynomial noConstant = p.getTail();
+ if(!isSetup(noConstant.getNode())){
+ setupPolynomial(noConstant);
+ }
+ }
+ }else if(!isSetup(p.getNode())){
+ setupPolynomial(p);
+ }
+}
+
+void TheoryArithPrivate::setupDivLike(const Variable& v){
+ Assert(v.isDivLike());
+
+ if(getLogicInfo().isLinear()){
+ throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ }
+
+ Node vnode = v.getNode();
+ Assert(isSetup(vnode)); // Otherwise there is some invariant breaking recursion
+ Polynomial m = Polynomial::parsePolynomial(vnode[0]);
+ Polynomial n = Polynomial::parsePolynomial(vnode[1]);
+
+ cautiousSetupPolynomial(m);
+ cautiousSetupPolynomial(n);
+
+ Node lem;
+ switch(vnode.getKind()){
+ case DIVISION:
+ case INTS_DIVISION:
+ case INTS_MODULUS:
+ lem = definingIteForDivLike(vnode);
+ break;
+ case DIVISION_TOTAL:
+ lem = axiomIteForTotalDivision(vnode);
+ break;
+ case INTS_DIVISION_TOTAL:
+ case INTS_MODULUS_TOTAL:
+ lem = axiomIteForTotalIntDivision(vnode);
+ break;
+ default:
+ /* intentionally blank */
+ break;
+ }
+
+ if(!lem.isNull()){
+ Debug("arith::div") << lem << endl;
+ outputLemma(lem);
+ }
+}
+
+Node TheoryArithPrivate::definingIteForDivLike(Node divLike){
+ Kind k = divLike.getKind();
+ Assert(k == DIVISION || k == INTS_DIVISION || k == INTS_MODULUS);
+ // (for all ((n Real) (d Real))
+ // (=
+ // (DIVISION n d)
+ // (ite (= d 0)
+ // (APPLY [div_0_skolem_function] n)
+ // (DIVISION_TOTAL x y))))
+
+ Polynomial n = Polynomial::parsePolynomial(divLike[0]);
+ Polynomial d = Polynomial::parsePolynomial(divLike[1]);
+
+ NodeManager* currNM = NodeManager::currentNM();
+ Node dEq0 = currNM->mkNode(EQUAL, d.getNode(), mkRationalNode(0));
+
+ Kind kTotal = (k == DIVISION) ? DIVISION_TOTAL :
+ (k == INTS_DIVISION) ? INTS_DIVISION_TOTAL : INTS_MODULUS_TOTAL;
+
+ Node by0Func = (k == DIVISION) ? getRealDivideBy0Func():
+ (k == INTS_DIVISION) ? getIntDivideBy0Func() : getIntModulusBy0Func();
+
+
+ Debug("arith::div") << divLike << endl;
+ Debug("arith::div") << by0Func << endl;
+
+ Node divTotal = currNM->mkNode(kTotal, n.getNode(), d.getNode());
+ Node divZero = currNM->mkNode(APPLY_UF, by0Func, n.getNode());
+
+ Node defining = divLike.eqNode(dEq0.iteNode( divZero, divTotal));
+
+ return defining;
+}
+
+Node TheoryArithPrivate::axiomIteForTotalDivision(Node div_tot){
+ Assert(div_tot.getKind() == DIVISION_TOTAL);
+
+ // Inverse of multiplication axiom:
+ // (for all ((n Real) (d Real))
+ // (ite (= d 0)
+ // (= (DIVISION_TOTAL n d) 0)
+ // (= (* d (DIVISION_TOTAL n d)) n)))
+
+
+ Polynomial n = Polynomial::parsePolynomial(div_tot[0]);
+ Polynomial d = Polynomial::parsePolynomial(div_tot[1]);
+ Polynomial div_tot_p = Polynomial::parsePolynomial(div_tot);
+
+ Comparison invEq = Comparison::mkComparison(EQUAL, n, d * div_tot_p);
+ Comparison zeroEq = Comparison::mkComparison(EQUAL, div_tot_p, Polynomial::mkZero());
+ Node dEq0 = (d.getNode()).eqNode(mkRationalNode(0));
+ Node ite = dEq0.iteNode(zeroEq.getNode(), invEq.getNode());
+
+ return ite;
+}
+
+Node TheoryArithPrivate::axiomIteForTotalIntDivision(Node int_div_like){
+ Kind k = int_div_like.getKind();
+ Assert(k == INTS_DIVISION_TOTAL || k == INTS_MODULUS_TOTAL);
+
+ // (for all ((m Int) (n Int))
+ // (=> (distinct n 0)
+ // (let ((q (div m n)) (r (mod m n)))
+ // (and (= m (+ (* n q) r))
+ // (<= 0 r (- (abs n) 1))))))
+
+ // Updated for div 0 functions
+ // (for all ((m Int) (n Int))
+ // (let ((q (div m n)) (r (mod m n)))
+ // (ite (= n 0)
+ // (and (= q (div_0_func m)) (= r (mod_0_func m)))
+ // (and (= m (+ (* n q) r))
+ // (<= 0 r (- (abs n) 1)))))))
+
+ Polynomial n = Polynomial::parsePolynomial(int_div_like[0]);
+ Polynomial d = Polynomial::parsePolynomial(int_div_like[1]);
+
+ NodeManager* currNM = NodeManager::currentNM();
+ Node zero = mkRationalNode(0);
+
+ Node q = (k == INTS_DIVISION_TOTAL) ? int_div_like : currNM->mkNode(INTS_DIVISION_TOTAL, n.getNode(), d.getNode());
+ Node r = (k == INTS_MODULUS_TOTAL) ? int_div_like : currNM->mkNode(INTS_MODULUS_TOTAL, n.getNode(), d.getNode());
+
+ Node dEq0 = (d.getNode()).eqNode(zero);
+ Node qEq0 = q.eqNode(zero);
+ Node rEq0 = r.eqNode(zero);
+
+ Polynomial rp = Polynomial::parsePolynomial(r);
+ Polynomial qp = Polynomial::parsePolynomial(q);
+
+ Node abs_d = (n.isConstant()) ?
+ d.getHead().getConstant().abs().getNode() : mkIntSkolem("abs_$$");
+
+ Node eq = Comparison::mkComparison(EQUAL, n, d * qp + rp).getNode();
+ Node leq0 = currNM->mkNode(LEQ, zero, r);
+ Node leq1 = currNM->mkNode(LT, r, abs_d);
+
+ Node andE = currNM->mkNode(AND, eq, leq0, leq1);
+ Node defDivMode = dEq0.iteNode(qEq0.andNode(rEq0), andE);
+ Node lem = abs_d.getMetaKind () == metakind::VARIABLE ?
+ defDivMode.andNode(d.makeAbsCondition(Variable(abs_d))) : defDivMode;
+
+ return lem;
+}
+
+
+void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
+ Assert(!poly.containsConstant());
+ TNode polyNode = poly.getNode();
+ Assert(!isSetup(polyNode));
+ Assert(!d_partialModel.hasArithVar(polyNode));
+
+ for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
+ Monomial mono = *i;
+ const VarList& vl = mono.getVarList();
+ if(!isSetup(vl.getNode())){
+ setupVariableList(vl);
+ }
+ }
+
+ if(polyNode.getKind() == PLUS){
+ d_tableauSizeHasBeenModified = true;
+
+ vector<ArithVar> variables;
+ vector<Rational> coefficients;
+ asVectors(poly, coefficients, variables);
+
+ ArithVar varSlack = requestArithVar(polyNode, true);
+ d_tableau.addRow(varSlack, coefficients, variables);
+ setupBasicValue(varSlack);
+ d_linEq.trackVariable(varSlack);
+
+ //Add differences to the difference manager
+ Polynomial::iterator i = poly.begin(), end = poly.end();
+ if(i != end){
+ Monomial first = *i;
+ ++i;
+ if(i != end){
+ Monomial second = *i;
+ ++i;
+ if(i == end){
+ if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
+ VarList vl0 = first.getVarList();
+ VarList vl1 = second.getVarList();
+ if(vl0.singleton() && vl1.singleton()){
+ d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
+ }
+ }
+ }
+ }
+ }
+
+ ++(d_statistics.d_statSlackVariables);
+ markSetup(polyNode);
+ }
+
+ /* Note:
+ * It is worth documenting that polyNode should only be marked as
+ * being setup by this function if it has kind PLUS.
+ * Other kinds will be marked as being setup by lower levels of setup
+ * specifically setupVariableList.
+ */
+}
+
+void TheoryArithPrivate::setupAtom(TNode atom) {
+ Assert(isRelationOperator(atom.getKind()));
+ Assert(Comparison::isNormalAtom(atom));
+ Assert(!isSetup(atom));
+ Assert(!d_constraintDatabase.hasLiteral(atom));
+
+ Comparison cmp = Comparison::parseNormalForm(atom);
+ Polynomial nvp = cmp.normalizedVariablePart();
+ Assert(!nvp.isZero());
+
+ if(!isSetup(nvp.getNode())){
+ setupPolynomial(nvp);
+ }
+
+ d_constraintDatabase.addLiteral(atom);
+
+ markSetup(atom);
+}
+
+void TheoryArithPrivate::preRegisterTerm(TNode n) {
+ Debug("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
+
+ if(isRelationOperator(n.getKind())){
+ if(!isSetup(n)){
+ setupAtom(n);
+ }
+ Constraint c = d_constraintDatabase.lookup(n);
+ Assert(c != NullConstraint);
+
+ Debug("arith::preregister") << "setup constraint" << c << endl;
+ Assert(!c->canBePropagated());
+ c->setPreregistered();
+ }
+
+ Debug("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
+}
+
+void TheoryArithPrivate::releaseArithVar(ArithVar v){
+ Assert(d_partialModel.hasNode(v));
+
+ d_constraintDatabase.removeVariable(v);
+ d_partialModel.releaseArithVar(v);
+ d_linEq.maybeRemoveTracking(v);
+}
+
+ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){
+ //TODO : The VarList trick is good enough?
+ Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS);
+ if(getLogicInfo().isLinear() && Variable::isDivMember(x)){
+ throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ }
+ Assert(!d_partialModel.hasArithVar(x));
+ Assert(x.getType().isReal()); // real or integer
+
+ // ArithVar varX = d_variables.size();
+ // d_variables.push_back(Node(x));
+
+ ArithVar max = d_partialModel.getNumberOfVariables();
+ ArithVar varX = d_partialModel.allocate(x, slack);
+
+ bool reclaim = max >= d_partialModel.getNumberOfVariables();;
+
+ if(reclaim){
+ // varX = d_pool.back();
+ // d_pool.pop_back();
+
+ // d_partialModel.setAssignment(varX, d_DELTA_ZERO, d_DELTA_ZERO);
+ }else{
+ // varX = d_numberOfVariables;
+ // ++d_numberOfVariables;
+
+ // d_slackVars.push_back(true);
+ // d_variableTypes.push_back(ATReal);
+
+ d_dualSimplex.increaseMax();
+
+ d_tableau.increaseSize();
+ d_tableauSizeHasBeenModified = true;
+
+ //d_partialModel.initialize(varX, d_DELTA_ZERO);
+ }
+
+ // ArithType type;
+ // if(slack){
+ // //The type computation is not quite accurate for Rationals that are integral.
+ // //We'll use the isIntegral check from the polynomial package instead.
+ // Polynomial p = Polynomial::parsePolynomial(x);
+ // type = p.isIntegral() ? ATInteger : ATReal;
+ // }else{
+ // type = nodeToArithType(x);
+ // }
+ // d_variableTypes[varX] = type;
+ // d_slackVars[varX] = slack;
+
+ d_constraintDatabase.addVariable(varX);
+
+ //d_partialModel.setArithVar(x,varX);
+
+ // Debug("integers") << "isInteger[[" << x << "]]: " << x.getType().isInteger() << endl;
+
+ // if(slack){
+ // //The type computation is not quite accurate for Rationals that are integral.
+ // //We'll use the isIntegral check from the polynomial package instead.
+ // Polynomial p = Polynomial::parsePolynomial(x);
+ // d_variableTypes.push_back(p.isIntegral() ? ATInteger : ATReal);
+ // }else{
+ // d_variableTypes.push_back(nodeToArithType(x));
+ // }
+
+ // d_slackVars.push_back(slack);
+
+ // d_simplex.increaseMax();
+
+ // d_tableau.increaseSize();
+ // d_tableauSizeHasBeenModified = true;
+
+ // d_constraintDatabase.addVariable(varX);
+
+ Debug("arith::arithvar") << x << " |-> " << varX << endl;
+
+ Assert(!d_partialModel.hasUpperBound(varX));
+ Assert(!d_partialModel.hasLowerBound(varX));
+
+ return varX;
+}
+
+void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
+ for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
+ const Monomial& mono = *i;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Debug("rewriter") << "should be var: " << n << endl;
+
+ // TODO: This VarList::isMember(n) can be stronger
+ Assert(isLeaf(n) || VarList::isMember(n));
+ Assert(theoryOf(n) != THEORY_ARITH || d_partialModel.hasArithVar(n));
+
+ Assert(d_partialModel.hasArithVar(n));
+ ArithVar av = d_partialModel.asArithVar(n);
+
+ coeffs.push_back(constant.getValue());
+ variables.push_back(av);
+ }
+}
+
+/* Requirements:
+ * For basic variables the row must have been added to the tableau.
+ */
+void TheoryArithPrivate::setupBasicValue(ArithVar x){
+ Assert(d_tableau.isBasic(x));
+ //If the variable is basic, assertions may have already happened and updates
+ //may have occured before setting this variable up.
+
+ //This can go away if the tableau creation is done at preregister
+ //time instead of register
+ DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
+ DeltaRational assignment = d_linEq.computeRowValue(x, false);
+ d_partialModel.setAssignment(x,safeAssignment,assignment);
+
+ Debug("arith") << "setupVariable("<<x<<")"<<std::endl;
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(const Polynomial& p) const{
+ Assert(!p.containsConstant());
+ Assert(p.getHead().constantIsPositive());
+ TNode n = p.getNode();
+ Debug("determineArithVar") << "determineArithVar(" << n << ")" << endl;
+ return d_partialModel.asArithVar(n);
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(TNode assertion) const{
+ Debug("determineArithVar") << "determineArithVar " << assertion << endl;
+ Comparison cmp = Comparison::parseNormalForm(assertion);
+ Polynomial variablePart = cmp.normalizedVariablePart();
+ return determineArithVar(variablePart);
+}
+
+
+bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality){
+ Assert(equality.getKind() == EQUAL);
+ return d_partialModel.hasArithVar(equality[0]);
+}
+
+Comparison TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v){
+ const DeltaRational& beta = d_partialModel.getAssignment(v);
+
+ Assert(beta.isIntegral());
+ Polynomial betaAsPolynomial( Constant::mkConstant(beta.floor()) );
+
+ TNode var = d_partialModel.asNode(v);
+ Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
+ return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
+}
+
+Node TheoryArithPrivate::dioCutting(){
+ context::Context::ScopedPush speculativePush(getSatContext());
+ //DO NOT TOUCH THE OUTPUTSTREAM
+
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+ if(isInteger(v)){
+ if(d_partialModel.cmpAssignmentUpperBound(v) == 0 ||
+ d_partialModel.cmpAssignmentLowerBound(v) == 0){
+ if(!d_partialModel.boundsAreEqual(v)){
+ // If the bounds are equal this is already in the dioSolver
+ //Add v = dr as a speculation.
+ Comparison eq = mkIntegerEqualityFromAssignment(v);
+ Debug("dio::push") <<v << " " << eq.getNode() << endl;
+ Assert(!eq.isBoolean());
+ d_diosolver.pushInputConstraint(eq, eq.getNode());
+ // It does not matter what the explanation of eq is.
+ // It cannot be used in a conflict
+ }
+ }
+ }
+ }
+
+ SumPair plane = d_diosolver.processEquationsForCut();
+ if(plane.isZero()){
+ return Node::null();
+ }else{
+ Polynomial p = plane.getPolynomial();
+ Polynomial c(plane.getConstant() * Constant::mkConstant(-1));
+ Integer gcd = p.gcd();
+ Assert(p.isIntegral());
+ Assert(c.isIntegral());
+ Assert(gcd > 1);
+ Assert(!gcd.divides(c.asConstant().getNumerator()));
+ Comparison leq = Comparison::mkComparison(LEQ, p, c);
+ Comparison geq = Comparison::mkComparison(GEQ, p, c);
+ Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
+ Node rewrittenLemma = Rewriter::rewrite(lemma);
+ Debug("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
+ Debug("arith::dio") << "resulting in the cut: " << lemma << endl;
+ Debug("arith::dio") << "rewritten " << rewrittenLemma << endl;
+ return rewrittenLemma;
+ }
+}
+
+Node TheoryArithPrivate::callDioSolver(){
+ while(!d_constantIntegerVariables.empty()){
+ ArithVar v = d_constantIntegerVariables.front();
+ d_constantIntegerVariables.pop();
+
+ Debug("arith::dio") << v << endl;
+
+ Assert(isInteger(v));
+ Assert(d_partialModel.boundsAreEqual(v));
+
+
+ Constraint lb = d_partialModel.getLowerBoundConstraint(v);
+ Constraint ub = d_partialModel.getUpperBoundConstraint(v);
+
+ Node orig = Node::null();
+ if(lb->isEquality()){
+ orig = lb->explainForConflict();
+ }else if(ub->isEquality()){
+ orig = ub->explainForConflict();
+ }else {
+ orig = ConstraintValue::explainConflict(ub, lb);
+ }
+
+ Assert(d_partialModel.assignmentIsConsistent(v));
+
+ Comparison eq = mkIntegerEqualityFromAssignment(v);
+
+ if(eq.isBoolean()){
+ //This can only be a conflict
+ Assert(!eq.getNode().getConst<bool>());
+
+ //This should be handled by the normal form earlier in the case of equality
+ Assert(orig.getKind() != EQUAL);
+ return orig;
+ }else{
+ Debug("dio::push") << v << " " << eq.getNode() << " with reason " << orig << endl;
+ d_diosolver.pushInputConstraint(eq, orig);
+ }
+ }
+
+ return d_diosolver.processEquationsForConflict();
+}
+
+Constraint TheoryArithPrivate::constraintFromFactQueue(){
+ Assert(!done());
+ TNode assertion = get();
+
+ Kind simpleKind = Comparison::comparisonKind(assertion);
+ Constraint constraint = d_constraintDatabase.lookup(assertion);
+ if(constraint == NullConstraint){
+ Assert(simpleKind == EQUAL || simpleKind == DISTINCT );
+ bool isDistinct = simpleKind == DISTINCT;
+ Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
+ Assert(!isSetup(eq));
+ Node reEq = Rewriter::rewrite(eq);
+ if(reEq.getKind() == CONST_BOOLEAN){
+ if(reEq.getConst<bool>() == isDistinct){
+ // if is (not true), or false
+ Assert((reEq.getConst<bool>() && isDistinct) ||
+ (!reEq.getConst<bool>() && !isDistinct));
+ raiseConflict(assertion);
+ }
+ return NullConstraint;
+ }
+ Assert(reEq.getKind() != CONST_BOOLEAN);
+ if(!isSetup(reEq)){
+ setupAtom(reEq);
+ }
+ Node reAssertion = isDistinct ? reEq.notNode() : reEq;
+ constraint = d_constraintDatabase.lookup(reAssertion);
+
+ if(assertion != reAssertion){
+ Debug("arith::nf") << "getting non-nf assertion " << assertion << " |-> " << reAssertion << endl;
+ Assert(constraint != NullConstraint);
+ d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
+ }
+ }
+
+ // Kind simpleKind = Comparison::comparisonKind(assertion);
+ // Assert(simpleKind != UNDEFINED_KIND);
+ // Assert(constraint != NullConstraint ||
+ // simpleKind == EQUAL ||
+ // simpleKind == DISTINCT );
+ // if(simpleKind == EQUAL || simpleKind == DISTINCT){
+ // Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
+
+ // if(!isSetup(eq)){
+ // //The previous code was equivalent to:
+ // setupAtom(eq);
+ // constraint = d_constraintDatabase.lookup(assertion);
+ // }
+ // }
+ Assert(constraint != NullConstraint);
+
+ if(constraint->negationHasProof()){
+ Constraint negation = constraint->getNegation();
+ if(negation->isSelfExplaining()){
+ if(Debug.isOn("whytheoryenginewhy")){
+ debugPrintFacts();
+ }
+ }
+ Debug("arith::eq") << constraint << endl;
+ Debug("arith::eq") << negation << endl;
+
+ NodeBuilder<> nb(kind::AND);
+ nb << assertion;
+ negation->explainForConflict(nb);
+ Node conflict = nb;
+ Debug("arith::eq") << "conflict" << conflict << endl;
+ raiseConflict(conflict);
+ return NullConstraint;
+ }
+ Assert(!constraint->negationHasProof());
+
+ if(constraint->assertedToTheTheory()){
+ //Do nothing
+ return NullConstraint;
+ }else{
+ Debug("arith::constraint") << "arith constraint " << constraint << std::endl;
+ constraint->setAssertedToTheTheory(assertion);
+
+ if(!constraint->hasProof()){
+ Debug("arith::constraint") << "marking as constraint as self explaining " << endl;
+ constraint->selfExplaining();
+ }else{
+ Debug("arith::constraint") << "already has proof: " << constraint->explainForConflict() << endl;
+ }
+
+ return constraint;
+ }
+}
+
+bool TheoryArithPrivate::assertionCases(Constraint constraint){
+ Assert(constraint->hasProof());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+
+ switch(constraint->getType()){
+ case UpperBound:
+ if(isInteger(x_i) && constraint->isStrictUpperBound()){
+ Constraint floorConstraint = constraint->getFloor();
+ if(!floorConstraint->isTrue()){
+ if(floorConstraint->negationHasProof()){
+ Node conf = ConstraintValue::explainConflict(constraint, floorConstraint->getNegation());
+ raiseConflict(conf);
+ return true;
+ }else{
+ floorConstraint->impliedBy(constraint);
+ // Do not need to add to d_learnedBounds
+ }
+ }
+ return AssertUpper(floorConstraint);
+ }else{
+ return AssertUpper(constraint);
+ }
+ case LowerBound:
+ if(isInteger(x_i) && constraint->isStrictLowerBound()){
+ Constraint ceilingConstraint = constraint->getCeiling();
+ if(!ceilingConstraint->isTrue()){
+ if(ceilingConstraint->negationHasProof()){
+ Node conf = ConstraintValue::explainConflict(constraint, ceilingConstraint->getNegation());
+ raiseConflict(conf);
+ return true;
+ }
+ ceilingConstraint->impliedBy(constraint);
+ // Do not need to add to learnedBounds
+ }
+ return AssertLower(ceilingConstraint);
+ }else{
+ return AssertLower(constraint);
+ }
+ case Equality:
+ return AssertEquality(constraint);
+ case Disequality:
+ return AssertDisequality(constraint);
+ default:
+ Unreachable();
+ return false;
+ }
+}
+
+/**
+ * Looks for the next integer variable without an integer assignment in a round robin fashion.
+ * Changes the value of d_nextIntegerCheckVar.
+ *
+ * If this returns false, d_nextIntegerCheckVar does not have an integer assignment.
+ * If this returns true, all integer variables have an integer assignment.
+ */
+bool TheoryArithPrivate::hasIntegerModel(){
+ //if(d_variables.size() > 0){
+ ArithVar numVars = d_partialModel.getNumberOfVariables();
+ if(numVars > 0){
+ const ArithVar rrEnd = d_nextIntegerCheckVar;
+ do {
+ //Do not include slack variables
+ if(isInteger(d_nextIntegerCheckVar) && !isSlackVariable(d_nextIntegerCheckVar)) { // integer
+ const DeltaRational& d = d_partialModel.getAssignment(d_nextIntegerCheckVar);
+ if(!d.isIntegral()){
+ return false;
+ }
+ }
+ } while((d_nextIntegerCheckVar = (1 + d_nextIntegerCheckVar == numVars ? 0 : 1 + d_nextIntegerCheckVar)) != rrEnd);
+ }
+ return true;
+}
+
+/** Outputs conflicts to the output channel. */
+void TheoryArithPrivate::outputConflicts(){
+ Assert(!d_conflicts.empty());
+ for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
+ Node conflict = d_conflicts[i];
+ Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << endl;
+ (d_containing.d_out)->conflict(conflict);
+ }
+}
+
+void TheoryArithPrivate::branchVector(const std::vector<ArithVar>& lemmas){
+ //output the lemmas
+ for(vector<ArithVar>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){
+ ArithVar v = *i;
+ Assert(!d_cutInContext.contains(v));
+ d_cutInContext.insert(v);
+ d_cutCount = d_cutCount + 1;
+ Node lem = branchIntegerVariable(v);
+ outputLemma(lem);
+ ++(d_statistics.d_externalBranchAndBounds);
+ }
+}
+
+bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
+ Assert(d_qflraStatus != Result::SAT);
+
+ d_partialModel.stopQueueingAtBoundQueue();
+ UpdateTrackingCallback utcb(&d_linEq);
+ d_partialModel.processAtBoundQueue(utcb);
+ d_linEq.startTrackingBoundCounts();
+
+ bool noPivotLimit = Theory::fullEffort(effortLevel) ||
+ !options::restrictedPivots();
+
+ bool emmittedConflictOrSplit = false;
+
+ SimplexDecisionProcedure& simplex =
+ options::useFC() ? (SimplexDecisionProcedure&)d_fcSimplex :
+ (options::useSOI() ? (SimplexDecisionProcedure&)d_soiSimplex :
+ (SimplexDecisionProcedure&)d_dualSimplex);
+
+ bool useFancyFinal = options::fancyFinal() && ApproximateSimplex::enabled();
+
+ if(!useFancyFinal){
+ d_qflraStatus = simplex.findModel(noPivotLimit);
+ }else{
+ // Fancy final tries the following strategy
+ // At final check, try the preferred simplex solver with a pivot cap
+ // If that failed, swap the the other simplex solver
+ // If that failed, check if there are integer variables to cut
+ // If that failed, do a simplex without a pivot limit
+
+ int16_t oldCap = options::arithStandardCheckVarOrderPivots();
+
+ static const int32_t pass2Limit = 10;
+ static const int32_t relaxationLimit = 10000;
+ static const int32_t mipLimit = 200000;
+
+ d_qflraStatus = simplex.findModel(false);
+ if(d_qflraStatus == Result::SAT_UNKNOWN ||
+ (d_qflraStatus == Result::SAT && !hasIntegerModel())){
+
+ ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel);
+ approxSolver->setPivotLimit(relaxationLimit);
+
+ ApproximateSimplex::ApproxResult relaxRes, mipRes;
+ ApproximateSimplex::Solution relaxSolution, mipSolution;
+ relaxRes = approxSolver->solveRelaxation();
+ switch(relaxRes){
+ case ApproximateSimplex::ApproxSat:
+ {
+ relaxSolution = approxSolver->extractRelaxation();
+ approxSolver->setPivotLimit(mipLimit);
+ mipRes = approxSolver->solveMIP();
+ d_errorSet.reduceToSignals();
+ if(mipRes == ApproximateSimplex::ApproxSat){
+ mipSolution = approxSolver->extractMIP();
+ ApproximateSimplex::applySolution(d_linEq, mipSolution);
+ }else{
+ ApproximateSimplex::applySolution(d_linEq, relaxSolution);
+ // if(d_qflraStatus != UNSAT){
+ // d_likelyIntegerUnsat = true;
+ // }
+ }
+ options::arithStandardCheckVarOrderPivots.set(pass2Limit);
+ d_qflraStatus = simplex.findModel(false);
+ }
+ break;
+ case ApproximateSimplex::ApproxUnsat:
+ {
+ ApproximateSimplex::Solution sol = approxSolver->extractRelaxation();
+ d_errorSet.reduceToSignals();
+ ApproximateSimplex::applySolution(d_linEq, sol);
+ options::arithStandardCheckVarOrderPivots.set(100);
+
+ d_qflraStatus = simplex.findModel(false);
+ }
+ break;
+ default:
+ break;
+ }
+ delete approxSolver;
+ }
+
+ if(d_qflraStatus == Result::SAT_UNKNOWN){
+ vector<ArithVar> toCut = cutAllBounded();
+ if(toCut.size() > 0){
+ branchVector(toCut);
+ emmittedConflictOrSplit = true;
+ }else{
+ d_qflraStatus = simplex.findModel(noPivotLimit);
+ }
+ }
+ options::arithStandardCheckVarOrderPivots.set(oldCap);
+ }
+
+ // TODO Save zeroes with no conflicts
+ d_linEq.stopTrackingBoundCounts();
+ d_partialModel.startQueueingAtBoundQueue();
+
+ return emmittedConflictOrSplit;
+}
+
+void TheoryArithPrivate::check(Theory::Effort effortLevel){
+ Assert(d_currentPropagationList.empty());
+ Debug("effortlevel") << "TheoryArithPrivate::check " << effortLevel << std::endl;
+ Debug("arith") << "TheoryArithPrivate::check begun " << effortLevel << std::endl;
+
+ if(Debug.isOn("arith::consistency")){
+ Assert(unenqueuedVariablesAreConsistent());
+ }
+
+ bool newFacts = !done();
+ //If previous == SAT, then reverts on conflicts are safe
+ //Otherwise, they are not and must be committed.
+ Result::Sat previous = d_qflraStatus;
+ if(newFacts){
+ d_qflraStatus = Result::SAT_UNKNOWN;
+ d_hasDoneWorkSinceCut = true;
+ }
+
+ while(!done()){
+ Constraint curr = constraintFromFactQueue();
+ if(curr != NullConstraint){
+ bool res CVC4_UNUSED = assertionCases(curr);
+ Assert(!res || inConflict());
+ }
+ if(inConflict()){ break; }
+ }
+ if(!inConflict()){
+ while(!d_learnedBounds.empty()){
+ // we may attempt some constraints twice. this is okay!
+ Constraint curr = d_learnedBounds.front();
+ d_learnedBounds.pop();
+ Debug("arith::learned") << curr << endl;
+
+ bool res CVC4_UNUSED = assertionCases(curr);
+ Assert(!res || inConflict());
+
+ if(inConflict()){ break; }
+ }
+ }
+
+ if(inConflict()){
+ d_qflraStatus = Result::UNSAT;
+ if(options::revertArithModels() && previous == Result::SAT){
+ ++d_statistics.d_revertsOnConflicts;
+ Debug("arith::bt") << "clearing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ revertOutOfConflict();
+ d_errorSet.clear();
+ }else{
+ ++d_statistics.d_commitsOnConflicts;
+ Debug("arith::bt") << "committing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ revertOutOfConflict();
+ }
+ outputConflicts();
+ return;
+ }
+
+
+ if(Debug.isOn("arith::print_assertions")) {
+ debugPrintAssertions();
+ }
+
+ bool emmittedConflictOrSplit = false;
+ Assert(d_conflicts.empty());
+
+ bool useSimplex = d_qflraStatus != Result::SAT;
+ if(useSimplex){
+ emmittedConflictOrSplit = solveRealRelaxation(effortLevel);
+ }
+
+ switch(d_qflraStatus){
+ case Result::SAT:
+ if(newFacts){
+ ++d_statistics.d_nontrivialSatChecks;
+ }
+
+ Debug("arith::bt") << "committing sap inConflit" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ d_unknownsInARow = 0;
+ if(Debug.isOn("arith::consistency")){
+ Assert(entireStateIsConsistent("sat comit"));
+ }
+ if(useSimplex && options::collectPivots()){
+ if(options::useFC()){
+ d_statistics.d_satPivots << d_fcSimplex.getPivots();
+ }else{
+ d_statistics.d_satPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ case Result::SAT_UNKNOWN:
+ ++d_unknownsInARow;
+ ++(d_statistics.d_unknownChecks);
+ Assert(!Theory::fullEffort(effortLevel));
+ Debug("arith::bt") << "committing unknown" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
+
+ if(useSimplex && options::collectPivots()){
+ if(options::useFC()){
+ d_statistics.d_unknownPivots << d_fcSimplex.getPivots();
+ }else{
+ d_statistics.d_unknownPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ case Result::UNSAT:
+ d_unknownsInARow = 0;
+ if(false && previous == Result::SAT){
+ ++d_statistics.d_revertsOnConflicts;
+ Debug("arith::bt") << "clearing on conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ revertOutOfConflict();
+ d_errorSet.clear();
+ }else{
+ ++d_statistics.d_commitsOnConflicts;
+
+ Debug("arith::bt") << "committing on conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ revertOutOfConflict();
+
+ if(Debug.isOn("arith::consistency::comitonconflict")){
+ entireStateIsConsistent("commit on conflict");
+ }
+ }
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+
+ if(useSimplex && options::collectPivots()){
+ if(options::useFC()){
+ d_statistics.d_unsatPivots << d_fcSimplex.getPivots();
+ }else{
+ d_statistics.d_unsatPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ default:
+ Unimplemented();
+ }
+ d_statistics.d_avgUnknownsInARow.addEntry(d_unknownsInARow);
+
+ // This should be fine if sat or unknown
+ if(!emmittedConflictOrSplit &&
+ (options::arithPropagationMode() == UNATE_PROP ||
+ options::arithPropagationMode() == BOTH_PROP)){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime);
+ Assert(d_qflraStatus != Result::UNSAT);
+
+ while(!d_currentPropagationList.empty() && !inConflict()){
+ Constraint curr = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+
+ ConstraintType t = curr->getType();
+ Assert(t != Disequality, "Disequalities are not allowed in d_currentPropagation");
+
+
+ switch(t){
+ case LowerBound:
+ {
+ Constraint prev = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropLowerBound(curr, prev);
+ break;
+ }
+ case UpperBound:
+ {
+ Constraint prev = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropUpperBound(curr, prev);
+ break;
+ }
+ case Equality:
+ {
+ Constraint prevLB = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ Constraint prevUB = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
+ break;
+ }
+ default:
+ Unhandled(curr->getType());
+ }
+ }
+
+ if(inConflict()){
+ Debug("arith::unate") << "unate conflict" << endl;
+ revertOutOfConflict();
+ d_qflraStatus = Result::UNSAT;
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+ Debug("arith::bt") << "committing on unate conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl;
+
+ }
+ }else{
+ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime);
+ d_currentPropagationList.clear();
+ }
+ Assert( d_currentPropagationList.empty());
+
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+ ++d_fullCheckCounter;
+ }
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+ emmittedConflictOrSplit = splitDisequalities();
+ }
+ emmittedConflictOrSplit = false;
+
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
+ Node possibleConflict = Node::null();
+ if(!emmittedConflictOrSplit && options::arithDioSolver()){
+ possibleConflict = callDioSolver();
+ if(possibleConflict != Node::null()){
+ revertOutOfConflict();
+ Debug("arith::conflict") << "dio conflict " << possibleConflict << endl;
+ //cout << "dio conflict " << possibleConflict << endl;
+ raiseConflict(possibleConflict);
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+ }
+ }
+
+ if(!emmittedConflictOrSplit && d_hasDoneWorkSinceCut && options::arithDioSolver()){
+ Node possibleLemma = dioCutting();
+ if(!possibleLemma.isNull()){
+ Debug("arith") << "dio cut " << possibleLemma << endl;
+ //cout << "dio cut " << possibleLemma << endl;
+ emmittedConflictOrSplit = true;
+ d_hasDoneWorkSinceCut = false;
+ d_cutCount = d_cutCount + 1;
+ outputLemma(possibleLemma);
+ }
+ }
+
+ if(!emmittedConflictOrSplit) {
+ Node possibleLemma = roundRobinBranch();
+ if(!possibleLemma.isNull()){
+ ++(d_statistics.d_externalBranchAndBounds);
+ d_cutCount = d_cutCount + 1;
+ emmittedConflictOrSplit = true;
+ outputLemma(possibleLemma);
+ }
+ }
+
+ if(options::maxCutsInContext() <= d_cutCount){
+ if(d_diosolver.hasMoreDecompositionLemmas()){
+ while(d_diosolver.hasMoreDecompositionLemmas()){
+ Node decompositionLemma = d_diosolver.nextDecompositionLemma();
+ Debug("arith") << "dio decomposition lemma " << decompositionLemma << endl;
+ outputLemma(decompositionLemma);
+ }
+ }else{
+ outputRestart();
+ }
+ }
+ }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
+ if(Theory::fullEffort(effortLevel) && d_nlIncomplete){
+ // TODO this is total paranoia
+ setIncomplete();
+ }
+
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+ if(Debug.isOn("arith::print_model")) { debugPrintModel(); }
+ Debug("arith") << "TheoryArithPrivate::check end" << std::endl;
+}
+
+Node TheoryArithPrivate::branchIntegerVariable(ArithVar x) const {
+ const DeltaRational& d = d_partialModel.getAssignment(x);
+ Assert(!d.isIntegral());
+ const Rational& r = d.getNoninfinitesimalPart();
+ const Rational& i = d.getInfinitesimalPart();
+ Trace("integers") << "integers: assignment to [[" << d_partialModel.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
+
+ Assert(! (r.getDenominator() == 1 && i.getNumerator() == 0));
+ Assert(!d.isIntegral());
+ TNode var = d_partialModel.asNode(x);
+ Integer floor_d = d.floor();
+
+ //Node eq = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::EQUAL, var, mkRationalNode(floor_d+1)));
+ //Node diseq = eq.notNode();
+
+ Node ub = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::LEQ, var, mkRationalNode(floor_d)));
+ Node lb = ub.notNode();
+
+
+ //Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, diseq);
+ Node lem = NodeManager::currentNM()->mkNode(kind::OR, ub, lb);
+ Trace("integers") << "integers: branch & bound: " << lem << endl;
+ if(isSatLiteral(lem[0])) {
+ Debug("integers") << " " << lem[0] << " == " << getSatValue(lem[0]) << endl;
+ } else {
+ Debug("integers") << " " << lem[0] << " is not assigned a SAT literal" << endl;
+ }
+ if(isSatLiteral(lem[1])) {
+ Debug("integers") << " " << lem[1] << " == " << getSatValue(lem[1]) << endl;
+ } else {
+ Debug("integers") << " " << lem[1] << " is not assigned a SAT literal" << endl;
+ }
+ return lem;
+}
+
+std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{
+ vector<ArithVar> lemmas;
+ ArithVar max = d_partialModel.getNumberOfVariables();
+
+ if(options::doCutAllBounded() && max > 0){
+ for(ArithVar iter = 0; iter != max; ++iter){
+ //Do not include slack variables
+ const DeltaRational& d = d_partialModel.getAssignment(iter);
+ if(isInteger(iter) && !isSlackVariable(iter) &&
+ !d_cutInContext.contains(iter) &&
+ d_partialModel.hasUpperBound(iter) &&
+ d_partialModel.hasLowerBound(iter) &&
+ !d.isIntegral()){
+ lemmas.push_back(iter);
+ }
+ }
+ }
+ return lemmas;
+}
+
+/** Returns true if the roundRobinBranching() issues a lemma. */
+Node TheoryArithPrivate::roundRobinBranch(){
+ if(hasIntegerModel()){
+ return Node::null();
+ }else{
+ ArithVar v = d_nextIntegerCheckVar;
+
+ Assert(isInteger(v));
+ Assert(!isSlackVariable(v));
+ return branchIntegerVariable(v);
+ }
+}
+
+bool TheoryArithPrivate::splitDisequalities(){
+ bool splitSomething = false;
+
+ vector<Constraint> save;
+
+ while(!d_diseqQueue.empty()){
+ Constraint front = d_diseqQueue.front();
+ d_diseqQueue.pop();
+
+ if(front->isSplit()){
+ Debug("eq") << "split already" << endl;
+ }else{
+ Debug("eq") << "not split already" << endl;
+
+ ArithVar lhsVar = front->getVariable();
+
+ const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
+ const DeltaRational& rhsValue = front->getValue();
+ if(lhsValue == rhsValue){
+ Debug("arith::lemma") << "Splitting on " << front << endl;
+ Debug("arith::lemma") << "LHS value = " << lhsValue << endl;
+ Debug("arith::lemma") << "RHS value = " << rhsValue << endl;
+ Node lemma = front->split();
+ ++(d_statistics.d_statDisequalitySplits);
+
+ Debug("arith::lemma") << "Now " << Rewriter::rewrite(lemma) << endl;
+ outputLemma(lemma);
+ splitSomething = true;
+ }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
+ Debug("eq") << "can drop as less than lb" << front << endl;
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
+ Debug("eq") << "can drop as greater than ub" << front << endl;
+ }else{
+ Debug("eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
+ save.push_back(front);
+ }
+ }
+ }
+ vector<Constraint>::const_iterator i=save.begin(), i_end = save.end();
+ for(; i != i_end; ++i){
+ d_diseqQueue.push(*i);
+ }
+ return splitSomething;
+}
+
+/**
+ * Should be guarded by at least Debug.isOn("arith::print_assertions").
+ * Prints to Debug("arith::print_assertions")
+ */
+void TheoryArithPrivate::debugPrintAssertions() {
+ Debug("arith::print_assertions") << "Assertions:" << endl;
+ for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar i = *vi;
+ if (d_partialModel.hasLowerBound(i)) {
+ Constraint lConstr = d_partialModel.getLowerBoundConstraint(i);
+ Debug("arith::print_assertions") << lConstr << endl;
+ }
+
+ if (d_partialModel.hasUpperBound(i)) {
+ Constraint uConstr = d_partialModel.getUpperBoundConstraint(i);
+ Debug("arith::print_assertions") << uConstr << endl;
+ }
+ }
+ context::CDQueue<Constraint>::const_iterator it = d_diseqQueue.begin();
+ context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end();
+ for(; it != it_end; ++ it) {
+ Debug("arith::print_assertions") << *it << endl;
+ }
+}
+
+void TheoryArithPrivate::debugPrintModel(){
+ Debug("arith::print_model") << "Model:" << endl;
+ for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar i = *vi;
+ if(d_partialModel.hasNode(i)){
+ Debug("arith::print_model") << d_partialModel.asNode(i) << " : " <<
+ d_partialModel.getAssignment(i);
+ if(d_tableau.isBasic(i))
+ Debug("arith::print_model") << " (basic)";
+ Debug("arith::print_model") << endl;
+ }
+ }
+}
+
+
+
+Node TheoryArithPrivate::explain(TNode n) {
+
+ Debug("arith::explain") << "explain @" << getSatContext()->getLevel() << ": " << n << endl;
+
+ Constraint c = d_constraintDatabase.lookup(n);
+ if(c != NullConstraint){
+ Assert(!c->isSelfExplaining());
+ Node exp = c->explainForPropagation();
+ Debug("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
+ return exp;
+ }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
+ c = d_assertionsThatDoNotMatchTheirLiterals[n];
+ if(!c->isSelfExplaining()){
+ Node exp = c->explainForPropagation();
+ Debug("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
+ return exp;
+ }else{
+ Debug("arith::explain") << "this is a strange mismatch" << n << endl;
+ Assert(d_congruenceManager.canExplain(n));
+ Debug("arith::explain") << "this is a strange mismatch" << n << endl;
+ return d_congruenceManager.explain(n);
+ }
+ }else{
+ Assert(d_congruenceManager.canExplain(n));
+ Debug("arith::explain") << "dm explanation" << n << endl;
+ return d_congruenceManager.explain(n);
+ }
+}
+
+
+void TheoryArithPrivate::propagate(Theory::Effort e) {
+ // This uses model values for safety. Disable for now.
+ if(d_qflraStatus == Result::SAT &&
+ (options::arithPropagationMode() == BOUND_INFERENCE_PROP ||
+ options::arithPropagationMode() == BOTH_PROP)
+ && hasAnyUpdates()){
+ propagateCandidates();
+ }else{
+ clearUpdates();
+ }
+
+ while(d_constraintDatabase.hasMorePropagations()){
+ Constraint c = d_constraintDatabase.nextPropagation();
+ Debug("arith::prop") << "next prop" << getSatContext()->getLevel() << ": " << c << endl;
+
+ if(c->negationHasProof()){
+ Debug("arith::prop") << "negation has proof " << c->getNegation() << endl
+ << c->getNegation()->explainForConflict() << endl;
+ }
+ Assert(!c->negationHasProof(), "A constraint has been propagated on the constraint propagation queue, but the negation has been set to true. Contact Tim now!");
+
+ if(!c->assertedToTheTheory()){
+ Node literal = c->getLiteral();
+ Debug("arith::prop") << "propagating @" << getSatContext()->getLevel() << " " << literal << endl;
+
+ outputPropagate(literal);
+ }else{
+ Debug("arith::prop") << "already asserted to the theory " << c->getLiteral() << endl;
+ }
+ }
+
+ while(d_congruenceManager.hasMorePropagations()){
+ TNode toProp = d_congruenceManager.getNextPropagation();
+
+ //Currently if the flag is set this came from an equality detected by the
+ //equality engine in the the difference manager.
+ Node normalized = Rewriter::rewrite(toProp);
+
+ Constraint constraint = d_constraintDatabase.lookup(normalized);
+ if(constraint == NullConstraint){
+ Debug("arith::prop") << "propagating on non-constraint? " << toProp << endl;
+
+ outputPropagate(toProp);
+ }else if(constraint->negationHasProof()){
+ Node exp = d_congruenceManager.explain(toProp);
+ Node notNormalized = normalized.getKind() == NOT ?
+ normalized[0] : normalized.notNode();
+ Node lp = flattenAnd(exp.andNode(notNormalized));
+ Debug("arith::prop") << "propagate conflict" << lp << endl;
+ raiseConflict(lp);
+ outputConflicts();
+ return;
+ }else{
+ Debug("arith::prop") << "propagating still?" << toProp << endl;
+ outputPropagate(toProp);
+ }
+ }
+}
+
+DeltaRational TheoryArithPrivate::getDeltaValue(TNode n) const throw (DeltaRationalException, ModelException) {
+ AlwaysAssert(d_qflraStatus != Result::SAT_UNKNOWN);
+ Debug("arith::value") << n << std::endl;
+
+ switch(n.getKind()) {
+
+ case kind::CONST_RATIONAL:
+ return n.getConst<Rational>();
+
+ case kind::PLUS: { // 2+ args
+ DeltaRational value(0);
+ for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+ value = value + getDeltaValue(*i);
+ }
+ return value;
+ }
+
+ case kind::MULT: { // 2+ args
+ DeltaRational value(1);
+ unsigned variableParts = 0;
+ for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+ TNode curr = *i;
+ value = value * getDeltaValue(curr);
+ if(!curr.isConst()){
+ ++variableParts;
+ }
+ }
+ // TODO: This is a bit of a weak check
+ if(isSetup(n)){
+ ArithVar var = d_partialModel.asArithVar(n);
+ const DeltaRational& assign = d_partialModel.getAssignment(var);
+ if(assign != value){
+ throw ModelException(n, "Model disagrees on non-linear term.");
+ }
+ }
+ return value;
+ }
+ case kind::MINUS:{ // 2 args
+ return getDeltaValue(n[0]) - getDeltaValue(n[1]);
+ }
+
+ case kind::UMINUS:{ // 1 arg
+ return (- getDeltaValue(n[0]));
+ }
+
+ case kind::DIVISION:{ // 2 args
+ DeltaRational res = getDeltaValue(n[0]) / getDeltaValue(n[1]);
+ if(isSetup(n)){
+ ArithVar var = d_partialModel.asArithVar(n);
+ if(d_partialModel.getAssignment(var) != res){
+ throw ModelException(n, "Model disagrees on non-linear term.");
+ }
+ }
+ return res;
+ }
+ case kind::DIVISION_TOTAL:
+ case kind::INTS_DIVISION_TOTAL:
+ case kind::INTS_MODULUS_TOTAL: { // 2 args
+ DeltaRational denom = getDeltaValue(n[1]);
+ if(denom.isZero()){
+ return DeltaRational(0,0);
+ }else{
+ DeltaRational numer = getDeltaValue(n[0]);
+ DeltaRational res;
+ if(n.getKind() == kind::DIVISION_TOTAL){
+ res = numer / denom;
+ }else if(n.getKind() == kind::INTS_DIVISION_TOTAL){
+ res = Rational(numer.euclidianDivideQuotient(denom));
+ }else{
+ Assert(n.getKind() == kind::INTS_MODULUS_TOTAL);
+ res = Rational(numer.euclidianDivideRemainder(denom));
+ }
+ if(isSetup(n)){
+ ArithVar var = d_partialModel.asArithVar(n);
+ if(d_partialModel.getAssignment(var) != res){
+ throw ModelException(n, "Model disagrees on non-linear term.");
+ }
+ }
+ return res;
+ }
+ }
+
+ default:
+ if(isSetup(n)){
+ ArithVar var = d_partialModel.asArithVar(n);
+ return d_partialModel.getAssignment(var);
+ }else{
+ throw ModelException(n, "Expected a setup node.");
+ }
+ }
+}
+
+Rational TheoryArithPrivate::deltaValueForTotalOrder() const{
+ Rational min(2);
+ std::set<DeltaRational> relevantDeltaValues;
+ context::CDQueue<Constraint>::const_iterator qiter = d_diseqQueue.begin();
+ context::CDQueue<Constraint>::const_iterator qiter_end = d_diseqQueue.end();
+
+ for(; qiter != qiter_end; ++qiter){
+ Constraint curr = *qiter;
+
+ const DeltaRational& rhsValue = curr->getValue();
+ relevantDeltaValues.insert(rhsValue);
+ }
+
+ Theory::shared_terms_iterator shared_iter = d_containing.shared_terms_begin();
+ Theory::shared_terms_iterator shared_end = d_containing.shared_terms_end();
+ for(; shared_iter != shared_end; ++shared_iter){
+ Node sharedCurr = *shared_iter;
+
+ // ModelException is fatal as this point. Don't catch!
+ // DeltaRationalException is fatal as this point. Don't catch!
+ DeltaRational val = getDeltaValue(sharedCurr);
+ relevantDeltaValues.insert(val);
+ }
+
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+ const DeltaRational& value = d_partialModel.getAssignment(v);
+ relevantDeltaValues.insert(value);
+ if( d_partialModel.hasLowerBound(v)){
+ const DeltaRational& lb = d_partialModel.getLowerBound(v);
+ relevantDeltaValues.insert(lb);
+ }
+ if( d_partialModel.hasUpperBound(v)){
+ const DeltaRational& ub = d_partialModel.getUpperBound(v);
+ relevantDeltaValues.insert(ub);
+ }
+ }
+
+ if(relevantDeltaValues.size() >= 2){
+ std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
+ std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
+ DeltaRational prev = *iter;
+ ++iter;
+ for(; iter != iter_end; ++iter){
+ const DeltaRational& curr = *iter;
+
+ Assert(prev < curr);
+
+ DeltaRational::seperatingDelta(min, prev, curr);
+ prev = curr;
+ }
+ }
+
+ Assert(min.sgn() > 0);
+ Rational belowMin = min/Rational(2);
+ return belowMin;
+}
+
+void TheoryArithPrivate::collectModelInfo( TheoryModel* m, bool fullModel ){
+ AlwaysAssert(d_qflraStatus == Result::SAT);
+ //AlwaysAssert(!d_nlIncomplete, "Arithmetic solver cannot currently produce models for input with nonlinear arithmetic constraints");
+
+ if(Debug.isOn("arith::collectModelInfo")){
+ debugPrintFacts();
+ }
+
+ Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
+
+
+ // Delta lasts at least the duration of the function call
+ const Rational& delta = d_partialModel.getDelta();
+ std::hash_set<TNode, TNodeHashFunction> shared = d_containing.currentlySharedTerms();
+
+ // TODO:
+ // This is not very good for user push/pop....
+ // Revisit when implementing push/pop
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+
+ if(!isSlackVariable(v)){
+ Node term = d_partialModel.asNode(v);
+
+ if(theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end()){
+ const DeltaRational& mod = d_partialModel.getAssignment(v);
+ Rational qmodel = mod.substituteDelta(delta);
+
+ Node qNode = mkRationalNode(qmodel);
+ Debug("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
+
+ m->assertEquality(term, qNode, true);
+ }else{
+ Debug("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
+
+ }
+ }
+ }
+
+ // Iterate over equivalence classes in LinearEqualityModule
+ // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
+ // m->assertEqualityEngine(&ee);
+
+ Debug("arith::collectModelInfo") << "collectModelInfo() end " << endl;
+}
+
+bool TheoryArithPrivate::safeToReset() const {
+ Assert(!d_tableauSizeHasBeenModified);
+ Assert(d_errorSet.noSignals());
+
+ ErrorSet::error_iterator error_iter = d_errorSet.errorBegin();
+ ErrorSet::error_iterator error_end = d_errorSet.errorEnd();
+ for(; error_iter != error_end; ++error_iter){
+ ArithVar basic = *error_iter;
+ if(!d_smallTableauCopy.isBasic(basic)){
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TheoryArithPrivate::notifyRestart(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
+
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ ++d_restartsCounter;
+#warning "removing restart"
+ // return;
+
+ // uint32_t currSize = d_tableau.size();
+ // uint32_t copySize = d_smallTableauCopy.size();
+
+ // Debug("arith::reset") << "resetting" << d_restartsCounter << endl;
+ // Debug("arith::reset") << "curr " << currSize << " copy " << copySize << endl;
+ // Debug("arith::reset") << "tableauSizeHasBeenModified " << d_tableauSizeHasBeenModified << endl;
+
+ // if(d_tableauSizeHasBeenModified){
+ // Debug("arith::reset") << "row has been added must copy " << d_restartsCounter << endl;
+ // d_smallTableauCopy = d_tableau;
+ // d_tableauSizeHasBeenModified = false;
+ // }else if( d_restartsCounter >= RESET_START){
+ // if(copySize >= currSize * 1.1 ){
+ // Debug("arith::reset") << "size has shrunk " << d_restartsCounter << endl;
+ // ++d_statistics.d_smallerSetToCurr;
+ // d_smallTableauCopy = d_tableau;
+ // }else if(d_tableauResetDensity * copySize <= currSize){
+ // d_errorSet.popAllSignals();
+ // if(safeToReset()){
+ // Debug("arith::reset") << "resetting " << d_restartsCounter << endl;
+ // ++d_statistics.d_currSetToSmaller;
+ // d_tableau = d_smallTableauCopy;
+ // }else{
+ // Debug("arith::reset") << "not safe to reset at the moment " << d_restartsCounter << endl;
+ // }
+ // }
+ // }
+ // Assert(unenqueuedVariablesAreConsistent());
+}
+
+bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
+ bool result = true;
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar var = *vi;
+ //ArithVar var = d_partialModel.asArithVar(*i);
+ if(!d_partialModel.assignmentIsConsistent(var)){
+ d_partialModel.printModel(var);
+ Warning() << s << ":" << "Assignment is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ Warning() << " (basic)";
+ }
+ Warning() << endl;
+ result = false;
+ }
+ }
+ return result;
+}
+
+bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
+ bool result = true;
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar var = *vi;
+ if(!d_partialModel.assignmentIsConsistent(var)){
+ if(!d_errorSet.inError(var)){
+
+ d_partialModel.printModel(var);
+ Warning() << "Unenqueued var is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ Warning() << " (basic)";
+ }
+ Warning() << endl;
+ result = false;
+ } else if(Debug.isOn("arith::consistency::initial")){
+ d_partialModel.printModel(var);
+ Warning() << "Initial var is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ Warning() << " (basic)";
+ }
+ Warning() << endl;
+ }
+ }
+ }
+ return result;
+}
+
+void TheoryArithPrivate::presolve(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
+
+ d_statistics.d_initialTableauSize.setData(d_tableau.size());
+
+ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ static CVC4_THREADLOCAL(unsigned) callCount = 0;
+ if(Debug.isOn("arith::presolve")) {
+ Debug("arith::presolve") << "TheoryArithPrivate::presolve #" << callCount << endl;
+ callCount = callCount + 1;
+ }
+
+ vector<Node> lemmas;
+ switch(options::arithUnateLemmaMode()){
+ case NO_PRESOLVE_LEMMAS:
+ break;
+ case INEQUALITY_PRESOLVE_LEMMAS:
+ d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+ break;
+ case EQUALITY_PRESOLVE_LEMMAS:
+ d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+ break;
+ case ALL_PRESOLVE_LEMMAS:
+ d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+ d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+ break;
+ default:
+ Unhandled(options::arithUnateLemmaMode());
+ }
+
+ vector<Node>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
+ for(; i != i_end; ++i){
+ Node lem = *i;
+ Debug("arith::oldprop") << " lemma lemma duck " <<lem << endl;
+ outputLemma(lem);
+ }
+}
+
+EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
+ if(d_qflraStatus == Result::SAT_UNKNOWN){
+ return EQUALITY_UNKNOWN;
+ }else{
+ try {
+ if (getDeltaValue(a) == getDeltaValue(b)) {
+ return EQUALITY_TRUE_IN_MODEL;
+ } else {
+ return EQUALITY_FALSE_IN_MODEL;
+ }
+ } catch (DeltaRationalException& dr) {
+ return EQUALITY_UNKNOWN;
+ } catch (ModelException& me) {
+ return EQUALITY_UNKNOWN;
+ }
+ }
+}
+
+bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
+ ++d_statistics.d_boundComputations;
+
+ DeltaRational bound = upperBound ?
+ d_linEq.computeUpperBound(basic):
+ d_linEq.computeLowerBound(basic);
+
+ if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
+ (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
+
+ // TODO: "Policy point"
+ //We are only going to recreate the functionality for now.
+ //In the future this can be improved to generate a temporary constraint
+ //if none exists.
+ //Experiment with doing this everytime or only when the new constraint
+ //implies an unknown fact.
+
+ ConstraintType t = upperBound ? UpperBound : LowerBound;
+ Constraint bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
+
+ // Node bestImplied = upperBound ?
+ // d_apm.getBestImpliedUpperBound(basic, bound):
+ // d_apm.getBestImpliedLowerBound(basic, bound);
+
+ if(bestImplied != NullConstraint){
+ //This should be stronger
+ Assert(!upperBound || bound <= bestImplied->getValue());
+ Assert(!upperBound || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
+
+ Assert( upperBound || bound >= bestImplied->getValue());
+ Assert( upperBound || d_partialModel.greaterThanLowerBound(basic, bestImplied->getValue()));
+ //slightly changed
+
+ // Constraint c = d_constraintDatabase.lookup(bestImplied);
+ // Assert(c != NullConstraint);
+
+ bool assertedToTheTheory = bestImplied->assertedToTheTheory();
+ bool canBePropagated = bestImplied->canBePropagated();
+ bool hasProof = bestImplied->hasProof();
+
+ Debug("arith::prop") << "arith::prop" << basic
+ << " " << assertedToTheTheory
+ << " " << canBePropagated
+ << " " << hasProof
+ << endl;
+
+ if(bestImplied->negationHasProof()){
+ Warning() << "the negation of " << bestImplied << " : " << endl
+ << "has proof " << bestImplied->getNegation() << endl
+ << bestImplied->getNegation()->explainForConflict() << endl;
+ }
+
+ if(!assertedToTheTheory && canBePropagated && !hasProof ){
+ if(upperBound){
+ Assert(bestImplied != d_partialModel.getUpperBoundConstraint(basic));
+ d_linEq.propagateNonbasicsUpperBound(basic, bestImplied);
+ }else{
+ Assert(bestImplied != d_partialModel.getLowerBoundConstraint(basic));
+ d_linEq.propagateNonbasicsLowerBound(basic, bestImplied);
+ }
+ // I think this can be skipped if canBePropagated is true
+ //d_learnedBounds.push(bestImplied);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TheoryArithPrivate::propagateCandidate(ArithVar basic){
+ bool success = false;
+ if(d_partialModel.strictlyAboveLowerBound(basic) && d_linEq.hasLowerBounds(basic)){
+ success |= propagateCandidateLowerBound(basic);
+ }
+ if(d_partialModel.strictlyBelowUpperBound(basic) && d_linEq.hasUpperBounds(basic)){
+ success |= propagateCandidateUpperBound(basic);
+ }
+ if(success){
+ ++d_statistics.d_boundPropagations;
+ }
+}
+
+void TheoryArithPrivate::propagateCandidates(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+
+ Assert(d_candidateBasics.empty());
+
+ if(d_updatedBounds.empty()){ return; }
+
+ DenseSet::const_iterator i = d_updatedBounds.begin();
+ DenseSet::const_iterator end = d_updatedBounds.end();
+ for(; i != end; ++i){
+ ArithVar var = *i;
+ if(d_tableau.isBasic(var) &&
+ d_tableau.basicRowLength(var) <= options::arithPropagateMaxLength()){
+ d_candidateBasics.softAdd(var);
+ }else{
+ Tableau::ColIterator basicIter = d_tableau.colIterator(var);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
+ Assert(entry.getColVar() == var);
+ Assert(d_tableau.isBasic(rowVar));
+ if(d_tableau.getRowLength(ridx) <= options::arithPropagateMaxLength()){
+ d_candidateBasics.softAdd(rowVar);
+ }
+ }
+ }
+ }
+ d_updatedBounds.purge();
+
+ while(!d_candidateBasics.empty()){
+ ArithVar candidate = d_candidateBasics.back();
+ d_candidateBasics.pop_back();
+ Assert(d_tableau.isBasic(candidate));
+ propagateCandidate(candidate);
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h
new file mode 100644
index 000000000..7b37a813f
--- /dev/null
+++ b/src/theory/arith/theory_arith_private.h
@@ -0,0 +1,605 @@
+/********************* */
+/*! \file theory_arith.cpp
+ ** \verbatim
+ ** Original author: taking
+ ** Major contributors: none
+ ** Minor contributors (to current version): kshitij, ajreynol, mdeters, dejan
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#pragma once
+
+#include "theory/arith/theory_arith_private_forward.h"
+
+#include "expr/node.h"
+#include "expr/kind.h"
+#include "expr/metakind.h"
+#include "expr/node_builder.h"
+
+#include "context/context.h"
+#include "context/cdlist.h"
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdqueue.h"
+
+#include "theory/valuation.h"
+#include "theory/rewriter.h"
+
+#include "util/rational.h"
+#include "util/integer.h"
+#include "util/boolean_simplification.h"
+#include "util/dense_map.h"
+#include "util/statistics_registry.h"
+#include "util/result.h"
+
+#include "smt/logic_exception.h"
+
+
+
+#include "theory/arith/arithvar.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/matrix.h"
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/linear_equality.h"
+#include "theory/arith/arith_static_learner.h"
+#include "theory/arith/dio_solver.h"
+#include "theory/arith/congruence_manager.h"
+
+#include "theory/arith/simplex.h"
+#include "theory/arith/dual_simplex.h"
+#include "theory/arith/fc_simplex.h"
+#include "theory/arith/soi_simplex.h"
+#include "theory/arith/pure_update_simplex.h"
+
+#include "theory/arith/constraint.h"
+
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/partial_model.h"
+#include "theory/arith/matrix.h"
+
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/constraint.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/arith/normal_form.h"
+#include "theory/model.h"
+
+#include "theory/arith/options.h"
+
+#include <stdint.h>
+
+#include <vector>
+#include <map>
+#include <queue>
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+ class InstStrategySimplex;
+}
+namespace arith {
+
+/**
+ * Implementation of QF_LRA.
+ * Based upon:
+ * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
+ */
+class TheoryArithPrivate {
+private:
+ friend class quantifiers::InstStrategySimplex;
+
+ static const uint32_t RESET_START = 2;
+
+ TheoryArith& d_containing;
+
+ bool d_nlIncomplete;
+ // TODO A better would be:
+ //context::CDO<bool> d_nlIncomplete;
+
+ BoundCountingVector d_boundTracking;
+
+ /**
+ * The constraint database associated with the theory.
+ * This must be declared before ArithPartialModel.
+ */
+ ConstraintDatabase d_constraintDatabase;
+
+ enum Result::Sat d_qflraStatus;
+ // check()
+ // !done() -> d_qflraStatus = Unknown
+ // fullEffort(e) -> simplex returns either sat or unsat
+ // !fullEffort(e) -> simplex returns either sat, unsat or unknown
+ // if unknown, save the assignment
+ // if unknown, the simplex priority queue cannot be emptied
+ int d_unknownsInARow;
+
+
+ /**
+ * This counter is false if nothing has been done since the last cut.
+ * This is used to break an infinite loop.
+ */
+ bool d_hasDoneWorkSinceCut;
+
+ /** Static learner. */
+ ArithStaticLearner d_learner;
+
+
+ //std::vector<ArithVar> d_pool;
+public:
+ void releaseArithVar(ArithVar v);
+ void signal(ArithVar v){ d_errorSet.signalVariable(v); }
+
+private:
+ /**
+ * The map between arith variables to nodes.
+ */
+ //ArithVarNodeMap d_arithvarNodeMap;
+
+ typedef ArithVariables::var_iterator var_iterator;
+ var_iterator var_begin() const { return d_partialModel.var_begin(); }
+ var_iterator var_end() const { return d_partialModel.var_end(); }
+
+ NodeSet d_setupNodes;
+public:
+ bool isSetup(Node n) const {
+ return d_setupNodes.find(n) != d_setupNodes.end();
+ }
+ void markSetup(Node n){
+ Assert(!isSetup(n));
+ d_setupNodes.insert(n);
+ }
+private:
+
+ void setupDivLike(const Variable& x);
+
+ void setupVariable(const Variable& x);
+ void setupVariableList(const VarList& vl);
+ void setupPolynomial(const Polynomial& poly);
+public:
+ void setupAtom(TNode atom);
+private:
+ void cautiousSetupPolynomial(const Polynomial& p);
+
+ /**
+ * A superset of all of the assertions that currently are not the literal for
+ * their constraint do not match constraint literals. Not just the witnesses.
+ */
+ context::CDInsertHashMap<Node, Constraint, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals;
+
+
+ /** Returns true if x is of type Integer. */
+ inline bool isInteger(ArithVar x) const {
+ return d_partialModel.isInteger(x);
+ //return d_variableTypes[x] >= ATInteger;
+ }
+
+ /** This is the set of variables initially introduced as slack variables. */
+ //std::vector<bool> d_slackVars;
+
+ /** Returns true if the variable was initially introduced as a slack variable. */
+ inline bool isSlackVariable(ArithVar x) const{
+ return d_partialModel.isSlack(x);
+ //return d_slackVars[x];
+ }
+
+ /**
+ * On full effort checks (after determining LA(Q) satisfiability), we
+ * consider integer vars, but we make sure to do so fairly to avoid
+ * nontermination (although this isn't a guarantee). To do it fairly,
+ * we consider variables in round-robin fashion. This is the
+ * round-robin index.
+ */
+ ArithVar d_nextIntegerCheckVar;
+
+ /**
+ * Queue of Integer variables that are known to be equal to a constant.
+ */
+ context::CDQueue<ArithVar> d_constantIntegerVariables;
+
+ Node callDioSolver();
+ Node dioCutting();
+
+ Comparison mkIntegerEqualityFromAssignment(ArithVar v);
+
+ /**
+ * List of all of the disequalities asserted in the current context that are not known
+ * to be satisfied.
+ */
+ context::CDQueue<Constraint> d_diseqQueue;
+
+ /**
+ * Constraints that have yet to be processed by proagation work list.
+ * All of the elements have type of LowerBound, UpperBound, or
+ * Equality.
+ *
+ * This is empty at the beginning of every check call.
+ *
+ * If head()->getType() == LowerBound or UpperBound,
+ * then d_cPL[1] is the previous constraint in d_partialModel for the
+ * corresponding bound.
+ * If head()->getType() == Equality,
+ * then d_cPL[1] is the previous lowerBound in d_partialModel,
+ * and d_cPL[2] is the previous upperBound in d_partialModel.
+ */
+ std::deque<Constraint> d_currentPropagationList;
+
+ context::CDQueue<Constraint> d_learnedBounds;
+
+
+ /**
+ * Manages information about the assignment and upper and lower bounds on
+ * variables.
+ */
+ ArithVariables d_partialModel;
+
+ /** The set of variables in error in the partial model. */
+ ErrorSet d_errorSet;
+
+ /**
+ * The tableau for all of the constraints seen thus far in the system.
+ */
+ Tableau d_tableau;
+
+ /**
+ * Maintains the relationship between the PartialModel and the Tableau.
+ */
+ LinearEqualityModule d_linEq;
+
+ /**
+ * A Diophantine equation solver. Accesses the tableau and partial
+ * model (each in a read-only fashion).
+ */
+ DioSolver d_diosolver;
+
+ /** Counts the number of notifyRestart() calls to the theory. */
+ uint32_t d_restartsCounter;
+
+ /**
+ * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
+ * the density of the tableau, d, is computed.
+ * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
+ * is set to d_initialTableau.
+ */
+ bool d_tableauSizeHasBeenModified;
+ double d_tableauResetDensity;
+ uint32_t d_tableauResetPeriod;
+ static const uint32_t s_TABLEAU_RESET_INCREMENT = 5;
+
+
+ /** This is only used by simplex at the moment. */
+ context::CDList<Node> d_conflicts;
+public:
+ inline void raiseConflict(Node n){ d_conflicts.push_back(n); }
+
+private:
+
+ /** Returns true iff a conflict has been raised. */
+ inline bool inConflict() const {
+ return !d_conflicts.empty();
+ }
+
+ /**
+ * Outputs the contents of d_conflicts onto d_out.
+ * Must be inConflict().
+ */
+ void outputConflicts();
+
+ /**
+ * A copy of the tableau.
+ * This is equivalent to the original tableau if d_tableauSizeHasBeenModified
+ * is false.
+ * The set of basic and non-basic variables may differ from d_tableau.
+ */
+ Tableau d_smallTableauCopy;
+
+ /**
+ * Returns true if all of the basic variables in the simplex queue of
+ * basic variables that violate their bounds in the current tableau
+ * are basic in d_smallTableauCopy.
+ *
+ * d_tableauSizeHasBeenModified must be false when calling this.
+ * Simplex's priority queue must be in collection mode.
+ */
+ bool safeToReset() const;
+
+ /** This keeps track of difference equalities. Mostly for sharing. */
+ ArithCongruenceManager d_congruenceManager;
+
+ /** This implements the Simplex decision procedure. */
+ DualSimplexDecisionProcedure d_dualSimplex;
+ PureUpdateSimplexDecisionProcedure d_pureUpdate;
+ FCSimplexDecisionProcedure d_fcSimplex;
+ SumOfInfeasibilitiesSPD d_soiSimplex;
+
+ bool solveRealRelaxation(Theory::Effort effortLevel);
+
+ class ModelException : public Exception {
+ public:
+ ModelException(TNode n, const char* msg) throw ();
+ virtual ~ModelException() throw ();
+ };
+
+ /** Internal model value for the node */
+ DeltaRational getDeltaValue(TNode n) const throw (DeltaRationalException, ModelException);
+
+ /** Uninterpretted function symbol for use when interpreting
+ * division by zero.
+ */
+ Node d_realDivideBy0Func;
+ Node d_intDivideBy0Func;
+ Node d_intModulusBy0Func;
+ Node getRealDivideBy0Func();
+ Node getIntDivideBy0Func();
+ Node getIntModulusBy0Func();
+
+ Node definingIteForDivLike(Node divLike);
+ Node axiomIteForTotalDivision(Node div_tot);
+ Node axiomIteForTotalIntDivision(Node int_div_like);
+
+
+
+public:
+ TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
+ ~TheoryArithPrivate();
+
+ /**
+ * Does non-context dependent setup for a node connected to a theory.
+ */
+ void preRegisterTerm(TNode n);
+
+ void setMasterEqualityEngine(eq::EqualityEngine* eq);
+
+ void check(Theory::Effort e);
+ void propagate(Theory::Effort e);
+ Node explain(TNode n);
+
+
+ Rational deltaValueForTotalOrder() const;
+
+ void collectModelInfo( TheoryModel* m, bool fullModel );
+
+ void shutdown(){ }
+
+ void presolve();
+ void notifyRestart();
+ Theory::PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions);
+ Node ppRewrite(TNode atom);
+ void ppStaticLearn(TNode in, NodeBuilder<>& learned);
+
+ std::string identify() const { return std::string("TheoryArith"); }
+
+ EqualityStatus getEqualityStatus(TNode a, TNode b);
+
+ void addSharedTerm(TNode n);
+
+private:
+
+ /** The constant zero. */
+ DeltaRational d_DELTA_ZERO;
+
+ /** propagates an arithvar */
+ void propagateArithVar(bool upperbound, ArithVar var );
+
+ /**
+ * Using the simpleKind return the ArithVar associated with the assertion.
+ */
+ ArithVar determineArithVar(const Polynomial& p) const;
+ ArithVar determineArithVar(TNode assertion) const;
+
+ /**
+ * Splits the disequalities in d_diseq that are violated using lemmas on demand.
+ * returns true if any lemmas were issued.
+ * returns false if all disequalities are satisfied in the current model.
+ */
+ bool splitDisequalities();
+
+ /** A Difference variable is known to be 0.*/
+ void zeroDifferenceDetected(ArithVar x);
+
+
+ /**
+ * Looks for the next integer variable without an integer assignment in a round robin fashion.
+ * Changes the value of d_nextIntegerCheckVar.
+ *
+ * If this returns false, d_nextIntegerCheckVar does not have an integer assignment.
+ * If this returns true, all integer variables have an integer assignment.
+ */
+ bool hasIntegerModel();
+
+ /**
+ * Issues branches for non-slack integer variables with non-integer assignments.
+ * Returns a cut for a lemma.
+ * If there is an integer model, this returns Node::null().
+ */
+ Node roundRobinBranch();
+
+public:
+ /**
+ * This requests a new unique ArithVar value for x.
+ * This also does initial (not context dependent) set up for a variable,
+ * except for setting up the initial.
+ */
+ ArithVar requestArithVar(TNode x, bool slack);
+
+private:
+ /** Initial (not context dependent) sets up for a variable.*/
+ void setupBasicValue(ArithVar x);
+
+ /** Initial (not context dependent) sets up for a new slack variable.*/
+ void setupSlack(TNode left);
+
+
+ /**
+ * Assert*(n, orig) takes an bound n that is implied by orig.
+ * and asserts that as a new bound if it is tighter than the current bound
+ * and updates the value of a basic variable if needed.
+ *
+ * orig must be a literal in the SAT solver so that it can be used for
+ * conflict analysis.
+ *
+ * x is the variable getting the new bound,
+ * c is the value of the new bound.
+ *
+ * If this new bound is in conflict with the other bound,
+ * a node describing this conflict is returned.
+ * If this new bound is not in conflict, Node::null() is returned.
+ */
+ bool AssertLower(Constraint constraint);
+ bool AssertUpper(Constraint constraint);
+ bool AssertEquality(Constraint constraint);
+ bool AssertDisequality(Constraint constraint);
+
+ /** Tracks the bounds that were updated in the current round. */
+ DenseSet d_updatedBounds;
+
+ /** Tracks the basic variables where propagation might be possible. */
+ DenseSet d_candidateBasics;
+
+ bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
+ void clearUpdates();
+
+ void revertOutOfConflict();
+
+ void propagateCandidates();
+ void propagateCandidate(ArithVar basic);
+ bool propagateCandidateBound(ArithVar basic, bool upperBound);
+
+ inline bool propagateCandidateLowerBound(ArithVar basic){
+ return propagateCandidateBound(basic, false);
+ }
+ inline bool propagateCandidateUpperBound(ArithVar basic){
+ return propagateCandidateBound(basic, true);
+ }
+
+ /**
+ * Performs a check to see if it is definitely true that setup can be avoided.
+ */
+ bool canSafelyAvoidEqualitySetup(TNode equality);
+
+ /**
+ * Handles the case splitting for check() for a new assertion.
+ * Returns a conflict if one was found.
+ * Returns Node::null if no conflict was found.
+ */
+ Constraint constraintFromFactQueue();
+ bool assertionCases(Constraint c);
+
+ /**
+ * Returns the basic variable with the shorted row containing a non-basic variable.
+ * If no such row exists, return ARITHVAR_SENTINEL.
+ */
+ ArithVar findShortestBasicRow(ArithVar variable);
+
+ /**
+ * Debugging only routine!
+ * Returns true iff every variable is consistent in the partial model.
+ */
+ bool entireStateIsConsistent(const std::string& locationHint);
+ bool unenqueuedVariablesAreConsistent();
+
+ bool isImpliedUpperBound(ArithVar var, Node exp);
+ bool isImpliedLowerBound(ArithVar var, Node exp);
+
+ void internalExplain(TNode n, NodeBuilder<>& explainBuilder);
+
+
+ void asVectors(const Polynomial& p,
+ std::vector<Rational>& coeffs,
+ std::vector<ArithVar>& variables);
+
+ /** Routine for debugging. Print the assertions the theory is aware of. */
+ void debugPrintAssertions();
+ /** Debugging only routine. Prints the model. */
+ void debugPrintModel();
+
+ inline LogicInfo getLogicInfo() const { return d_containing.getLogicInfo(); }
+ inline bool done() const { return d_containing.done(); }
+ inline TNode get() { return d_containing.get(); }
+ inline bool isLeaf(TNode x) const { return d_containing.isLeaf(x); }
+ inline TheoryId theoryOf(TNode x) const { return d_containing.theoryOf(x); }
+ inline void debugPrintFacts() const { d_containing.debugPrintFacts(); }
+ inline context::Context* getSatContext() const { return d_containing.getSatContext(); }
+ inline void setIncomplete() {
+ (d_containing.d_out)->setIncomplete();
+ d_nlIncomplete = true;
+ }
+ inline void outputLemma(TNode lem) { (d_containing.d_out)->lemma(lem); }
+ inline void outputPropagate(TNode lit) { (d_containing.d_out)->propagate(lit); }
+ inline void outputRestart() { (d_containing.d_out)->demandRestart(); }
+
+ inline bool isSatLiteral(TNode l) const {
+ return (d_containing.d_valuation).isSatLiteral(l);
+ }
+ inline Node getSatValue(TNode n) const {
+ return (d_containing.d_valuation).getSatValue(n);
+ }
+
+
+ /** Counts the number of fullCheck calls to arithmetic. */
+ uint32_t d_fullCheckCounter;
+ std::vector<ArithVar> cutAllBounded() const;
+ Node branchIntegerVariable(ArithVar x) const;
+ void branchVector(const std::vector<ArithVar>& lemmas);
+
+ context::CDO<unsigned> d_cutCount;
+ context::CDHashSet<ArithVar, std::hash<ArithVar> > d_cutInContext;
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
+
+ IntStat d_statUserVariables, d_statSlackVariables;
+ IntStat d_statDisequalitySplits;
+ IntStat d_statDisequalityConflicts;
+ TimerStat d_simplifyTimer;
+ TimerStat d_staticLearningTimer;
+
+ TimerStat d_presolveTime;
+
+ TimerStat d_newPropTime;
+
+ IntStat d_externalBranchAndBounds;
+
+ IntStat d_initialTableauSize;
+ IntStat d_currSetToSmaller;
+ IntStat d_smallerSetToCurr;
+ TimerStat d_restartTimer;
+
+ TimerStat d_boundComputationTime;
+ IntStat d_boundComputations, d_boundPropagations;
+
+ IntStat d_unknownChecks;
+ IntStat d_maxUnknownsInARow;
+ AverageStat d_avgUnknownsInARow;
+
+ IntStat d_revertsOnConflicts;
+ IntStat d_commitsOnConflicts;
+ IntStat d_nontrivialSatChecks;
+
+
+ HistogramStat<uint32_t> d_satPivots;
+ HistogramStat<uint32_t> d_unsatPivots;
+ HistogramStat<uint32_t> d_unknownPivots;
+
+ Statistics();
+ ~Statistics();
+ };
+
+ Statistics d_statistics;
+
+
+};/* class TheoryArithPrivate */
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/theory_arith_private_forward.h b/src/theory/arith/theory_arith_private_forward.h
new file mode 100644
index 000000000..0dc7cdd5b
--- /dev/null
+++ b/src/theory/arith/theory_arith_private_forward.h
@@ -0,0 +1,14 @@
+
+#include "cvc4_private.h"
+
+#pragma once
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+class TheoryArithPrivate;
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback