diff options
Diffstat (limited to 'src/theory')
55 files changed, 14409 insertions, 5510 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 */ diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp index 20eb7373b..dbdf95613 100644 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp @@ -14,6 +14,8 @@ #include "theory/quantifiers/inst_strategy_cbqi.h" #include "theory/arith/theory_arith.h" +#include "theory/arith/partial_model.h" +#include "theory/arith/theory_arith_private.h" #include "theory/theory_engine.h" #include "theory/quantifiers/options.h" #include "theory/quantifiers/term_database.h" @@ -46,11 +48,11 @@ void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort d_tableaux.clear(); d_ceTableaux.clear(); //search for instantiation rows in simplex tableaux - ArithVarNodeMap& avnm = d_th->d_arithvarNodeMap; - ArithVarNodeMap::var_iterator vi, vend; + ArithVariables& avnm = d_th->d_internal->d_partialModel; + ArithVariables::var_iterator vi, vend; for(vi = avnm.var_begin(), vend = avnm.var_end(); vi != vend; ++vi ){ ArithVar x = *vi; - if( d_th->d_partialModel.hasEitherBound( x ) ){ + if( d_th->d_internal->d_partialModel.hasEitherBound( x ) ){ Node n = avnm.asNode(x); Node f; NodeBuilder<> t(kind::PLUS); @@ -168,23 +170,23 @@ void InstStrategySimplex::addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder } void InstStrategySimplex::debugPrint( const char* c ){ - const ArithVarNodeMap& avnm = d_th->d_arithvarNodeMap; - ArithVarNodeMap::var_iterator vi, vend; + ArithVariables& avnm = d_th->d_internal->d_partialModel; + ArithVariables::var_iterator vi, vend; for(vi = avnm.var_begin(), vend = avnm.var_end(); vi != vend; ++vi ){ ArithVar x = *vi; Node n = avnm.asNode(x); //if( ((TheoryArith*)getTheory())->d_partialModel.hasEitherBound( x ) ){ Debug(c) << x << " : " << n << ", bounds = "; - if( d_th->d_partialModel.hasLowerBound( x ) ){ - Debug(c) << d_th->d_partialModel.getLowerBound( x ); + if( d_th->d_internal->d_partialModel.hasLowerBound( x ) ){ + Debug(c) << d_th->d_internal->d_partialModel.getLowerBound( x ); }else{ Debug(c) << "-infty"; } Debug(c) << " <= "; - Debug(c) << d_th->d_partialModel.getAssignment( x ); + Debug(c) << d_th->d_internal->d_partialModel.getAssignment( x ); Debug(c) << " <= "; - if( d_th->d_partialModel.hasUpperBound( x ) ){ - Debug(c) << d_th->d_partialModel.getUpperBound( x ); + if( d_th->d_internal->d_partialModel.hasUpperBound( x ) ){ + Debug(c) << d_th->d_internal->d_partialModel.getUpperBound( x ); }else{ Debug(c) << "+infty"; } @@ -273,8 +275,8 @@ bool InstStrategySimplex::doInstantiation2( Node f, Node term, ArithVar x, InstM } Node InstStrategySimplex::getTableauxValue( Node n, bool minus_delta ){ - if( d_th->d_arithvarNodeMap.hasArithVar(n) ){ - ArithVar v = d_th->d_arithvarNodeMap.asArithVar( n ); + if( d_th->d_internal->d_partialModel.hasArithVar(n) ){ + ArithVar v = d_th->d_internal->d_partialModel.asArithVar( n ); return getTableauxValue( v, minus_delta ); }else{ return NodeManager::currentNM()->mkConst( Rational(0) ); @@ -282,8 +284,8 @@ Node InstStrategySimplex::getTableauxValue( Node n, bool minus_delta ){ } Node InstStrategySimplex::getTableauxValue( ArithVar v, bool minus_delta ){ - const Rational& delta = d_th->d_partialModel.getDelta(); - DeltaRational drv = d_th->d_partialModel.getAssignment( v ); + const Rational& delta = d_th->d_internal->d_partialModel.getDelta(); + DeltaRational drv = d_th->d_internal->d_partialModel.getAssignment( v ); Rational qmodel = drv.substituteDelta( minus_delta ? -delta : delta ); return mkRationalNode(qmodel); } diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h index 5528d70ea..a45318489 100644 --- a/src/theory/quantifiers/inst_strategy_cbqi.h +++ b/src/theory/quantifiers/inst_strategy_cbqi.h @@ -19,7 +19,7 @@ #define __CVC4__INST_STRATEGT_CBQI_H #include "theory/quantifiers/instantiation_engine.h" -#include "theory/arith/arithvar_node_map.h" +#include "theory/arith/arithvar.h" #include "util/statistics_registry.h" @@ -107,4 +107,4 @@ public: } } -#endif
\ No newline at end of file +#endif |