From: Tim King Date: Fri, 7 Mar 2014 23:00:37 +0000 (-0500) Subject: Merging a squash of the branch timothy-king/CVC4/glpknecfix c95bf7d4f1 into master... X-Git-Tag: cvc5-1.0.0~7035^2~3 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9ccdea06edbc72e3ecd282e9e015f6fc4b2e7173;p=cvc5.git Merging a squash of the branch timothy-king/CVC4/glpknecfix c95bf7d4f1 into See the CAV14 submission for an explanation of the changes to the integer solver's behavior. If compiled against the our custom extension of glpk, https://github.com/timothy-king/glpk-cut-log, this should have substantial differences in behavior. This should have moderate performance differences for linear real and integer arithmetic even if these features are disabled. --- diff --git a/config/glpk.m4 b/config/glpk.m4 index 6c59a3094..932b053e0 100644 --- a/config/glpk.m4 +++ b/config/glpk.m4 @@ -95,7 +95,7 @@ if test -z "$GLPK_LIBS"; then [#else] [#include ] [#endif], - [int i = lpx_get_int_parm(NULL, LPX_K_ITCNT)])], + [int i = glp_get_it_cnt(NULL)])], [GLPK_LIBS="-lglpk $1"], []) LIBS="$cvc4_save_LIBS" @@ -118,7 +118,7 @@ if test -z "$GLPK_LIBS"; then [#else] [#include ] [#endif], - [int i = lpx_get_int_parm(NULL, LPX_K_ITCNT)])], + [int i = glp_get_it_cnt(NULL)])], [GLPK_LIBS="-lglpk $1"], []) LIBS="$cvc4_save_LIBS" diff --git a/src/Makefile.am b/src/Makefile.am index 279e52e09..64e3eb932 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -329,6 +329,8 @@ libcvc4_la_SOURCES = \ theory/arith/arithvar.h \ theory/arith/arithvar.cpp \ theory/arith/bound_counts.h \ + theory/arith/arith_ite_utils.h \ + theory/arith/arith_ite_utils.cpp \ theory/arith/arith_rewriter.h \ theory/arith/arith_rewriter.cpp \ theory/arith/arith_static_learner.h \ @@ -384,6 +386,8 @@ libcvc4_la_SOURCES = \ theory/arith/arith_unate_lemma_mode.cpp \ theory/arith/arith_propagation_mode.h \ theory/arith/arith_propagation_mode.cpp \ + theory/arith/cut_log.h \ + theory/arith/cut_log.cpp \ theory/arith/options_handlers.h \ theory/booleans/type_enumerator.h \ theory/booleans/theory_bool.h \ diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp index 1b3099842..9f6b1796e 100644 --- a/src/theory/arith/approx_simplex.cpp +++ b/src/theory/arith/approx_simplex.cpp @@ -20,8 +20,12 @@ #include "theory/arith/approx_simplex.h" #include "theory/arith/normal_form.h" #include "theory/arith/constraint.h" +#include "theory/arith/cut_log.h" +#include "theory/arith/matrix.h" #include #include +#include +#include using namespace std; @@ -29,13 +33,229 @@ namespace CVC4 { namespace theory { namespace arith { -ApproximateSimplex::ApproximateSimplex() : - d_pivotLimit(std::numeric_limits::max()) +struct AuxInfo { + TreeLog* tl; + int pivotLimit; + int branchLimit; + int branchDepth; + MipResult term; /* terminatation */ +}; + +enum SlackReplace { SlackUndef=0, SlackLB, SlackUB, SlackVLB, SlackVUB }; + +std::ostream& operator<<(std::ostream& out, MipResult res){ + switch(res){ + case MipUnknown: + out << "MipUnknown"; break; + case MipBingo: + out << "MipBingo"; break; + case MipClosed: + out << "MipClosed"; break; + case BranchesExhausted: + out << "BranchesExhausted"; break; + case PivotsExhauasted: + out << "PivotsExhauasted"; break; + case ExecExhausted: + out << "ExecExhausted"; break; + default: + out << "Unexpected Mip Value!"; break; + } + return out; +} +struct VirtualBound { + // Either x <= d * y or x >= d * y + ArithVar x; // variable being bounded + Kind k; // either LEQ or GEQ + Rational d; // the multiple on y + ArithVar y; // the variable that is the upper bound + ConstraintP c; // the original constraint relating x and y + + VirtualBound() + : x(ARITHVAR_SENTINEL) + , k(kind::UNDEFINED_KIND) + , d() + , y(ARITHVAR_SENTINEL) + , c(NullConstraint) + {} + VirtualBound(ArithVar toBound, Kind rel, const Rational& coeff, ArithVar bounding, ConstraintP orig) + : x(toBound) + , k(rel) + , d(coeff) + , y(bounding) + , c(orig) + { Assert(k == kind::LEQ || k == kind::GEQ); } +}; + +struct CutScratchPad { + bool d_failure; // if the construction was unsuccessful + + /* GOMORY CUTS Datastructures */ + ArithVar d_basic; // a variable that is basic in the approximate solver + DenseVector d_tabRow; // a row in the tableau not including d_basic, equal to 0 + DenseMap d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound + + /* MIR CUTS Datastructures */ + DenseMap d_slacks;// The x'[i] selected for x[i] + DenseMap d_vub; // Virtual upper bounds. + DenseMap d_vlb; // Virtual lower bounds. + DenseMap d_compRanges; + + // a sum of rows in the tableau, with possible replacements for fixed + // sum aggLhs[i] x[i] = aggRhs; + DenseVector d_agg; + // Takes agg and replaces x[i] with a slack variable x'[i] + // Takes agg and replaces x[i] with a slack variable x'[i] + // sum modLhs[i] x'[i] = modRhs; + DenseVector d_mod; + + // Takes mod, and performs c-Mir on it + // sum alpha[i] x'[i] <= beta + DenseVector d_alpha; + + /* The constructed cut */ + // sum cut[i] x[i] <= cutRhs + DenseVector d_cut; + Kind d_cutKind; + + /* The constraints used throughout construction. */ + std::set d_explanation; // use pointer equality + CutScratchPad(){ + clear(); + } + void clear(){ + d_failure = false; + d_basic = ARITHVAR_SENTINEL; + d_tabRow.purge(); + d_toBound.purge(); + + d_slacks.purge(); + d_vub.purge(); + d_vlb.purge(); + d_compRanges.purge(); + + d_agg.purge(); + d_mod.purge(); + d_alpha.purge(); + + d_cut.purge(); + d_cutKind = kind::UNDEFINED_KIND; + d_explanation.clear(); + } +}; +ApproximateStatistics::ApproximateStatistics() + // : d_relaxCalls("z::approx::relaxCalls",0) + // , d_relaxUnknowns("z::approx::relaxUnknowns",0) + // , d_relaxFeasible("z::approx::relaxFeasible",0) + // , d_relaxInfeasible("z::approx::relaxInfeasible",0) + // , d_relaxPivotsExhausted("z::approx::relaxPivotsExhausted",0) + // , d_mipCalls("z::approx::mipCalls",0) + // , d_mipUnknowns("z::approx::mipUnknowns",0) + // , d_mipBingo("z::approx::mipBingo",0) + // , d_mipClosed("z::approx::mipClosed",0) + // , d_mipBranchesExhausted("z::approx::mipBranchesExhausted",0) + // , d_mipPivotsExhausted("z::approx::mipPivotsExhausted",0) + // , d_mipExecExhausted("z::approx::mipExecExhausted",0) + // , d_gmiGen("z::approx::gmiGen",0) + // , d_gmiReplay("z::approx::gmiReplay",0) + // , d_mipGen("z::approx::mipGen",0) + // , d_mipReplay("z::approx::mipReplay",0) + : d_branchMaxDepth("z::approx::branchMaxDepth",0) + , d_branchesMaxOnAVar("z::approx::branchesMaxOnAVar",0) + //, d_branchTotal("z::approx::branchTotal",0) + //, d_branchCuts("z::approx::branchCuts",0) + + , d_gaussianElimConstructTime("z::approx::gaussianElimConstruct::time") + , d_gaussianElimConstruct("z::approx::gaussianElimConstruct::calls",0) + , d_averageGuesses("z::approx::averageGuesses") +{ + // StatisticsRegistry::registerStat(&d_relaxCalls); + // StatisticsRegistry::registerStat(&d_relaxUnknowns); + // StatisticsRegistry::registerStat(&d_relaxFeasible); + // StatisticsRegistry::registerStat(&d_relaxInfeasible); + // StatisticsRegistry::registerStat(&d_relaxPivotsExhausted); + + // StatisticsRegistry::registerStat(&d_mipCalls); + // StatisticsRegistry::registerStat(&d_mipUnknowns); + // StatisticsRegistry::registerStat(&d_mipBingo); + // StatisticsRegistry::registerStat(&d_mipClosed); + // StatisticsRegistry::registerStat(&d_mipBranchesExhausted); + // StatisticsRegistry::registerStat(&d_mipPivotsExhausted); + // StatisticsRegistry::registerStat(&d_mipExecExhausted); + + + // StatisticsRegistry::registerStat(&d_gmiGen); + // StatisticsRegistry::registerStat(&d_gmiReplay); + // StatisticsRegistry::registerStat(&d_mipGen); + // StatisticsRegistry::registerStat(&d_mipReplay); + + StatisticsRegistry::registerStat(&d_branchMaxDepth); + //StatisticsRegistry::registerStat(&d_branchTotal); + //StatisticsRegistry::registerStat(&d_branchCuts); + StatisticsRegistry::registerStat(&d_branchesMaxOnAVar); + + StatisticsRegistry::registerStat(&d_gaussianElimConstructTime); + StatisticsRegistry::registerStat(&d_gaussianElimConstruct); + + StatisticsRegistry::registerStat(&d_averageGuesses); +} + +ApproximateStatistics::~ApproximateStatistics(){ + // StatisticsRegistry::unregisterStat(&d_relaxCalls); + // StatisticsRegistry::unregisterStat(&d_relaxUnknowns); + // StatisticsRegistry::unregisterStat(&d_relaxFeasible); + // StatisticsRegistry::unregisterStat(&d_relaxInfeasible); + // StatisticsRegistry::unregisterStat(&d_relaxPivotsExhausted); + + // StatisticsRegistry::unregisterStat(&d_mipCalls); + // StatisticsRegistry::unregisterStat(&d_mipUnknowns); + // StatisticsRegistry::unregisterStat(&d_mipBingo); + // StatisticsRegistry::unregisterStat(&d_mipClosed); + // StatisticsRegistry::unregisterStat(&d_mipBranchesExhausted); + // StatisticsRegistry::unregisterStat(&d_mipPivotsExhausted); + // StatisticsRegistry::unregisterStat(&d_mipExecExhausted); + + + // StatisticsRegistry::unregisterStat(&d_gmiGen); + // StatisticsRegistry::unregisterStat(&d_gmiReplay); + // StatisticsRegistry::unregisterStat(&d_mipGen); + // StatisticsRegistry::unregisterStat(&d_mipReplay); + + StatisticsRegistry::unregisterStat(&d_branchMaxDepth); + //StatisticsRegistry::unregisterStat(&d_branchTotal); + //StatisticsRegistry::unregisterStat(&d_branchCuts); + StatisticsRegistry::unregisterStat(&d_branchesMaxOnAVar); + + StatisticsRegistry::unregisterStat(&d_gaussianElimConstructTime); + StatisticsRegistry::unregisterStat(&d_gaussianElimConstruct); + + StatisticsRegistry::unregisterStat(&d_averageGuesses); +} + +Integer ApproximateSimplex::s_defaultMaxDenom(1<<26); + +ApproximateSimplex::ApproximateSimplex(const ArithVariables& v, TreeLog& l, + ApproximateStatistics& s) + : d_vars(v) + , d_log(l) + , d_stats(s) + , d_pivotLimit(std::numeric_limits::max()) + , d_branchLimit(std::numeric_limits::max()) + , d_maxDepth(std::numeric_limits::max()) {} -void ApproximateSimplex::setPivotLimit(int pivotLimit){ - Assert(pivotLimit >= 0); - d_pivotLimit = pivotLimit; +void ApproximateSimplex::setPivotLimit(int pl){ + Assert(pl >= 0); + d_pivotLimit = pl; +} + +void ApproximateSimplex::setBranchingDepth(int bd){ + Assert(bd >= 0); + d_maxDepth = bd; +} + +void ApproximateSimplex::setBranchOnVariableLimit(int bl){ + Assert(bl >= 0); + d_branchLimit = bl; } const double ApproximateSimplex::SMALL_FIXED_DELTA = .000000001; @@ -77,7 +297,7 @@ std::vector ApproximateSimplex::rationalToCfe(const Rational& q, int de mods.push_back(Integer()); Integer& back = mods.back(); back = carry.floor(); - //cout << " cfe["< ApproximateSimplex::rationalToCfe(const Rational& q, int de return mods; } -Rational ApproximateSimplex::estimateWithCFE(const Rational& q, int depth){ - std::vector cfe = rationalToCfe(q,depth); - return cfeToRational(cfe); + +Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){ + Debug("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <= Integer(1)); + if( r.getDenominator() <= K ){ + return r; + } + + // current numerator and denominator that has not been resolved in the cfe + Integer num = r.getNumerator(), den = r.getDenominator(); + Integer quot,rem; + + unsigned t = 0; + // For a sequence of candidate solutions q_t/p_t + // we keep only 3 time steps: 0[prev], 1[current], 2[next] + // timesteps with a fake timestep 0 (p is 0 and q is 1) + // at timestep 1 + Integer p[3]; // h + Integer q[3]; // k + // load the first 3 time steps manually + p[0] = 0; q[0] = 1; // timestep -2 + p[1] = 1; q[1] = 0; // timestep -1 + + Integer::floorQR(quot, rem, num, den); + num = den; den = rem; + + q[2] = q[0] + quot*q[1]; + p[2] = p[0] + quot*p[1]; + Debug("estimateWithCFE") << " cfe["< getValidCuts(const std::set& nodes){ + return std::vector(); + } + + virtual void tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException){} + + virtual std::vector getValidCuts(const NodeLog& node) throw(RationalFromDoubleException){ + return std::vector(); + } + + virtual ArithVar getBranchVar(const NodeLog& nl) const{ + return ARITHVAR_SENTINEL; + } + + virtual double sumInfeasibilities(bool mip) const{ + return 0.0; + } }; }/* CVC4::theory::arith namespace */ @@ -146,14 +444,33 @@ namespace CVC4 { namespace theory { namespace arith { +Kind glpk_type_to_kind(int glpk_cut_type){ + switch(glpk_cut_type){ + case GLP_LO: return kind::GEQ; + case GLP_UP: return kind::LEQ; + case GLP_FX: return kind::EQUAL; + case GLP_DB: + case GLP_FR: + default: return kind::UNDEFINED_KIND; + } +} + +class GmiInfo; +class MirInfo; +class BranchCutInfo; + class ApproxGLPK : public ApproximateSimplex { private: - glp_prob* d_prob; - const ArithVariables& d_vars; + glp_prob* d_inputProb; /* a copy of the input prob */ + glp_prob* d_realProb; /* a copy of the real relaxation output */ + glp_prob* d_mipProb; /* a copy of the integer prob */ DenseMap d_colIndices; DenseMap d_rowIndices; + NodeLog::RowIdMap d_rootRowIds; + //DenseMap d_rowToArithVar; + DenseMap d_colToArithVar; int d_instanceID; @@ -162,27 +479,127 @@ private: static int s_verbosity; + CutScratchPad d_pad; + + std::vector d_denomGuesses; + public: - ApproxGLPK(const ArithVariables& vars); + ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s); ~ApproxGLPK(); - virtual ApproxResult solveRelaxation(); - virtual Solution extractRelaxation() const{ + virtual LinResult solveRelaxation(); + virtual Solution extractRelaxation() const throw (RationalFromDoubleException){ return extractSolution(false); } virtual ArithRatPairVec heuristicOptCoeffs() const; - virtual ApproxResult solveMIP(); - virtual Solution extractMIP() const{ + virtual MipResult solveMIP(bool al); + virtual Solution extractMIP() const throw (RationalFromDoubleException){ return extractSolution(true); } virtual void setOptCoeffs(const ArithRatPairVec& ref); + //void getValidCuts(const NodeLog& con, std::vector& out); + virtual std::vector getValidCuts(const NodeLog& nodes) throw (RationalFromDoubleException); + //virtual std::vector getBranches(); + + //Node downBranchLiteral(const NodeLog& con) const; + ArithVar getBranchVar(const NodeLog& con) const; static void printGLPKStatus(int status, std::ostream& out); + + private: - Solution extractSolution(bool mip) const; + Solution extractSolution(bool mip) const throw (RationalFromDoubleException); int guessDir(ArithVar v) const; + + // get this stuff out of here + void tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException); + + ArithVar _getArithVar(int nid, int M, int ind) const; + ArithVar getArithVarFromRow(int nid, int ind) const { + if(ind >= 0){ + const NodeLog& nl = d_log.getNode(nid); + return nl.lookupRowId(ind); + } + return ARITHVAR_SENTINEL; + } + + // virtual void mapRowId(int nid, int ind, ArithVar v){ + // NodeLog& nl = d_log.getNode(nid); + // nl.mapRowId(ind, v); + // } + // virtual void applyRowsDeleted(int nid, const RowsDeleted& rd){ + // NodeLog& nl = d_log.getNode(nid); + // nl.applyRowsDeleted(rd); + // } + + ArithVar getArithVarFromStructural(int ind) const{ + if(ind >= 0){ + unsigned u = (unsigned) ind; + if(d_colToArithVar.isKey(u)){ + return d_colToArithVar[u]; + } + } + return ARITHVAR_SENTINEL; + } + + /** + * Attempts to make the row vector vec on the pad. + * If this is not in the row span of the original tableau this + * raises the failure flag. + */ + bool attemptConstructTableRow(int node, int M, const PrimitiveVec& vec); + bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec); + bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec, const Integer& D); + bool gaussianElimConstructTableRow(int node, int M, const PrimitiveVec& vec); + + /* This is a guess of a vector in the row span of the tableau. + * Attempt to cancel out all of the variables. + * returns true if this is constructable. + */ + bool guessIsConstructable(const DenseMap& guess) const; + + /** + * Loads a vector of statuses into a dense map over bounds. + * returns true on failure. + */ + bool loadToBound(int node, int M, int len, int* inds, int* statuses, + DenseMap& toBound) const; + + /** checks the cut on the pad for whether it is sufficiently similar to cut. */ + bool checkCutOnPad(int nid, const CutInfo& cut) const; + + + /** turns the pad into a node and creates an explanation. */ + //std::pair makeCutNodes(int nid, const CutInfo& cut) const; + + // true means failure! + // BRANCH CUTS + bool attemptBranchCut(int nid, const BranchCutInfo& br); + + // GOMORY CUTS + bool attemptGmi(int nid, const GmiInfo& gmi); + /** tries to turn the information on the pad into a cut. */ + bool constructGmiCut(); + + // MIR CUTS + bool attemptMir(int nid, const MirInfo& mir); + bool applyCMIRRule(int nid, const MirInfo& mir); + bool makeRangeForComplemented(int nid, const MirInfo& mir); + bool loadSlacksIntoPad(int nid, const MirInfo& mir); + bool loadVirtualBoundsIntoPad(int nid, const MirInfo& mir); + bool loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& mir); + bool buildModifiedRow(int nid, const MirInfo& mir); + bool constructMixedKnapsack(); + bool replaceSlacksOnCuts(); + bool loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp); + + + double sumInfeasibilities(bool mip) const{ + return sumInfeasibilities(mip? d_mipProb : d_realProb); + } + double sumInfeasibilities(glp_prob* prob, bool mip) const; }; int ApproxGLPK::s_verbosity = 0; @@ -197,11 +614,11 @@ int ApproxGLPK::s_verbosity = 0; namespace CVC4 { namespace theory { namespace arith { -ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars){ +ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){ #ifdef CVC4_USE_GLPK - return new ApproxGLPK(vars); + return new ApproxGLPK(vars, l, s); #else - return new ApproxNoOp(vars); + return new ApproxNoOp(vars, l, s); #endif } bool ApproximateSimplex::enabled() { @@ -223,16 +640,38 @@ namespace CVC4 { namespace theory { namespace arith { -ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : - d_vars(avars), d_solvedRelaxation(false), d_solvedMIP(false) +static CutInfoKlass fromGlpkClass(int klass){ + switch(klass){ + case GLP_RF_GMI: return GmiCutKlass; + case GLP_RF_MIR: return MirCutKlass; + case GLP_RF_COV: + case GLP_RF_CLQ: + default: return UnknownKlass; + } +} + +ApproxGLPK::ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s) + : ApproximateSimplex(v, l, s) + , d_inputProb(NULL) + , d_realProb(NULL) + , d_mipProb(NULL) + , 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"); + d_denomGuesses.push_back(Integer(1<<22)); + d_denomGuesses.push_back(ApproximateSimplex::s_defaultMaxDenom); + d_denomGuesses.push_back(Integer(1ul<<29)); + d_denomGuesses.push_back(Integer(1ul<<31)); + + d_inputProb = glp_create_prob(); + d_realProb = glp_create_prob(); + d_mipProb = glp_create_prob(); + glp_set_obj_dir(d_inputProb, GLP_MAX); + glp_set_prob_name(d_inputProb, "ApproximateSimplex::approximateFindModel"); int numRows = 0; int numCols = 0; @@ -241,24 +680,35 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : 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)){ + if(d_vars.isAuxiliary(v)){ ++numRows; d_rowIndices.set(v, numRows); + //mapRowId(d_log.getRootId(), numRows, v); + d_rootRowIds.insert(make_pair(numRows, v)); + //d_rowToArithVar.set(numRows, v); + Debug("approx") << "Row vars: " << v << "<->" << numRows << endl; }else{ ++numCols; d_colIndices.set(v, numCols); + d_colToArithVar.set(numCols, v); + Debug("approx") << "Col vars: " << v << "<->" << numCols << endl; } } - glp_add_rows(d_prob, numRows); - glp_add_cols(d_prob, numCols); + Assert(numRows > 0); + Assert(numCols > 0); + + + + glp_add_rows(d_inputProb, numRows); + glp_add_cols(d_inputProb, 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; if(s_verbosity >= 2){ - Message() << v << " "; - d_vars.printModel(v, Message()); + //Message() << v << " "; + //d_vars.printModel(v, Message()); } int type; @@ -282,14 +732,15 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : type = GLP_FR; } - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ int rowIndex = d_rowIndices[v]; - glp_set_row_bnds(d_prob, rowIndex, type, lb, ub); + glp_set_row_bnds(d_inputProb, rowIndex, type, lb, ub); }else{ int colIndex = d_colIndices[v]; + // is input is correct here 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); + glp_set_col_kind(d_inputProb, colIndex, kind); + glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub); } } @@ -333,7 +784,7 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : ar[entryCounter] = coeff; } } - glp_load_matrix(d_prob, numEntries, ia, ja, ar); + glp_load_matrix(d_inputProb, numEntries, ia, ja, ar); delete[] ia; delete[] ja; @@ -410,12 +861,12 @@ ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{ if(type != GLP_FX && type != GLP_FR){ - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v)); uint32_t len = p.size(); d_rowCandidates.set(v, len); sumRowLength += len; - maxRowLength =std::max(maxRowLength, len); + maxRowLength = std::max(maxRowLength, len); }else if(!d_vars.isInteger(v)){ d_colCandidates.set(v, BoundCounts()); } @@ -430,7 +881,7 @@ ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{ bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v); if(lbCap || ubCap){ - Constraint b = lbCap ? d_vars.getLowerBoundConstraint(v) + ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v) : d_vars.getUpperBoundConstraint(v); if(!(b->getValue()).noninfinitesimalIsZero()){ continue; } @@ -530,7 +981,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ ArithVar v = (*i).first; const Rational& q = (*i).second; - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ // replace the variable by its definition and multiply by q Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v)); Polynomial pq = p * q; @@ -563,7 +1014,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ for(DenseMap::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); + glp_set_obj_coef(d_inputProb, colIndex, coeff); } } @@ -610,11 +1061,14 @@ void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){ } ApproxGLPK::~ApproxGLPK(){ - glp_delete_prob(d_prob); + glp_delete_prob(d_inputProb); + glp_delete_prob(d_realProb); + glp_delete_prob(d_mipProb); + } -ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ +ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const throw (RationalFromDoubleException){ Assert(d_solvedRelaxation); Assert(!mip || d_solvedMIP); @@ -622,12 +1076,15 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ DenseSet& newBasis = sol.newBasis; DenseMap& newValues = sol.newValues; + glp_prob* prob = mip ? d_mipProb : d_realProb; + 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]; + bool isAux = d_vars.isAuxiliary(vi); + int glpk_index = isAux ? 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); + int status = isAux ? glp_get_row_stat(prob, glpk_index) + : glp_get_col_stat(prob, glpk_index); if(s_verbosity >= 2){ Message() << "assignment " << vi << endl; } @@ -667,10 +1124,14 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ if(useDefaultAssignment){ if(s_verbosity >= 2){ Message() << "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)); + double newAssign; + if(mip){ + newAssign = (isAux ? glp_mip_row_val(prob, glpk_index) + : glp_mip_col_val(prob, glpk_index)); + }else{ + newAssign = (isAux ? glp_get_row_prim(prob, glpk_index) + : glp_get_col_prim(prob, glpk_index)); + } const DeltaRational& oldAssign = d_vars.getAssignment(vi); @@ -718,7 +1179,51 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ return sol; } -ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){ +double ApproxGLPK::sumInfeasibilities(glp_prob* prob, bool mip) const{ + /* compute the sum of dual infeasibilities */ + double infeas = 0.0; + + for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){ + ArithVar vi = *i; + bool isAux = d_vars.isAuxiliary(vi); + int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi]; + + double newAssign; + if(mip){ + newAssign = (isAux ? glp_mip_row_val(prob, glpk_index) + : glp_mip_col_val(prob, glpk_index)); + }else{ + newAssign = (isAux ? glp_get_row_prim(prob, glpk_index) + : glp_get_col_prim(prob, glpk_index)); + } + + + double ub = isAux ? + glp_get_row_ub(prob, glpk_index) : glp_get_col_ub(prob, glpk_index); + + double lb = isAux ? + glp_get_row_lb(prob, glpk_index) : glp_get_col_lb(prob, glpk_index); + + if(ub != +DBL_MAX){ + if(newAssign > ub){ + double ubinf = newAssign - ub; + infeas += ubinf; + Debug("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl; + } + } + if(lb != -DBL_MAX){ + if(newAssign < lb){ + double lbinf = lb - newAssign; + infeas += lbinf; + + Debug("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl; + } + } + } + return infeas; +} + +LinResult ApproxGLPK::solveRelaxation(){ Assert(!d_solvedRelaxation); glp_smcp parm; @@ -732,51 +1237,563 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){ parm.msg_lev = GLP_MSG_ALL; } - int res = glp_simplex(d_prob, &parm); + glp_erase_prob(d_realProb); + glp_copy_prob(d_realProb, d_inputProb, GLP_OFF); + + int res = glp_simplex(d_realProb, &parm); switch(res){ case 0: { - int status = glp_get_status(d_prob); + int status = glp_get_status(d_realProb); + int iterationcount = glp_get_it_cnt(d_realProb); switch(status){ case GLP_OPT: case GLP_FEAS: case GLP_UNBND: d_solvedRelaxation = true; - return ApproxSat; + return LinFeasible; case GLP_INFEAS: case GLP_NOFEAS: d_solvedRelaxation = true; - return ApproxUnsat; + return LinInfeasible; default: - return ApproxError; + { + if(iterationcount >= d_pivotLimit){ + return LinExhausted; + } + return LinUnknown; + } } } default: - return ApproxError; + return LinUnknown; + } +} + + +struct MirInfo : public CutInfo { + + /** a sum of input rows. */ + PrimitiveVec row_sum; + + /* the delta used */ + double delta; + + /* all of these are length vars == N+M*/ + int nvars; + char* cset; + char* subst; + int* vlbRows; + int* vubRows; + MirInfo(int execOrd, int ord) + : CutInfo(MirCutKlass, execOrd, ord) + , nvars(0) + , cset(NULL) + , subst(NULL) + , vlbRows(NULL) + , vubRows(NULL) + {} + + ~MirInfo(){ + clearSets(); + } + void clearSets(){ + if(cset != NULL){ + delete[] cset; + delete[] subst; + delete[] vlbRows; + delete[] vubRows; + cset = NULL; + nvars = 0; + } + } + void initSet(){ + Assert(d_N >= 0); + Assert(d_mAtCreation >= 0); + clearSets(); + + int vars = 1 + d_N + d_mAtCreation; + + cset = new char[1+vars]; + subst = new char[1+vars]; + vlbRows = new int[1+vars]; + vubRows = new int[1+vars]; + } +}; + +struct GmiInfo : public CutInfo { + int basic; + PrimitiveVec tab_row; + int* tab_statuses; + /* has the length tab_row.length */ + + GmiInfo(int execOrd, int ord) + : CutInfo(GmiCutKlass, execOrd, ord) + , basic(-1) + , tab_row() + , tab_statuses(NULL) + { + Assert(!initialized_tab()); + } + + ~GmiInfo(){ + if(initialized_tab()){ + clear_tab(); + } + } + + bool initialized_tab() const { + return tab_statuses != NULL; + } + + void init_tab(int N){ + if(initialized_tab()){ + clear_tab(); + } + tab_row.setup(N); + tab_statuses = new int[1+N]; + } + + void clear_tab() { + delete[] tab_statuses; + tab_statuses = NULL; + tab_row.clear(); + basic = -1; + } +}; + + + +static void loadCut(glp_tree *tree, CutInfo* cut){ + int ord, cut_len, cut_klass; + int N, M; + int* cut_inds; + double* cut_coeffs; + int glpk_cut_type; + double cut_rhs; + glp_prob* lp; + + lp = glp_ios_get_prob(tree); + ord = cut->poolOrdinal(); + + N = glp_get_num_cols(lp); + M = glp_get_num_rows(lp); + + cut->setDimensions(N, M); + + + + // Get the cut + cut_len = glp_ios_get_cut(tree, ord, NULL, NULL, &cut_klass, NULL, NULL); + Assert(fromGlpkClass(cut_klass) == cut->getKlass()); + + PrimitiveVec& cut_vec = cut->getCutVector(); + cut_vec.setup(cut_len); + cut_inds = cut_vec.inds; + cut_coeffs = cut_vec.coeffs; + + cut_vec.len = glp_ios_get_cut(tree, ord, cut_inds, cut_coeffs, &cut_klass, &glpk_cut_type, &cut_rhs); + Assert(fromGlpkClass(cut_klass) == cut->getKlass()); + Assert(cut_vec.len == cut_len); + + cut->setRhs(cut_rhs); + + cut->setKind( glpk_type_to_kind(glpk_cut_type) ); +} + + +static MirInfo* mirCut(glp_tree *tree, int exec_ord, int cut_ord){ + Debug("approx::mirCut") << "mirCut()" << exec_ord << endl; + + MirInfo* mir; + mir = new MirInfo(exec_ord, cut_ord); + loadCut(tree, mir); + mir->initSet(); + + + int nrows = glp_ios_cut_get_aux_nrows(tree, cut_ord); + + PrimitiveVec& row_sum = mir->row_sum; + row_sum.setup(nrows); + glp_ios_cut_get_aux_rows(tree, cut_ord, row_sum.inds, row_sum.coeffs); + + glp_ios_cut_get_mir_cset(tree, cut_ord, mir->cset); + mir->delta = glp_ios_cut_get_mir_delta(tree, cut_ord); + glp_ios_cut_get_mir_subst(tree, cut_ord, mir->subst); + glp_ios_cut_get_mir_virtual_rows(tree, cut_ord, mir->vlbRows, mir->vubRows); + + if(Debug.isOn("approx::mirCut")){ + Debug("approx::mirCut") << "mir_id: " << exec_ord << endl; + row_sum.print(Debug("approx::mirCut")); + } + + return mir; +} + +static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){ + Debug("approx::gmiCut") << "gmiCut()" << exec_ord << endl; + + int gmi_var; + int write_pos; + int read_pos; + int stat; + int ind; + int i; + + GmiInfo* gmi; + glp_prob* lp; + + gmi = new GmiInfo(exec_ord, cut_ord); + loadCut(tree, gmi); + + lp = glp_ios_get_prob(tree); + + int N = gmi->getN(); + int M = gmi->getMAtCreation(); + + // Get the tableau row + int nrows CVC4_UNUSED = glp_ios_cut_get_aux_nrows(tree, gmi->poolOrdinal()); + Assert(nrows == 1); + int rows[1+1]; + glp_ios_cut_get_aux_rows(tree, gmi->poolOrdinal(), rows, NULL); + gmi_var = rows[1]; + + gmi->init_tab(N); + gmi->basic = M+gmi_var; + + Debug("approx::gmiCut") + << gmi <<" " << gmi->basic << " " + << cut_ord<<" " << M <<" " << gmi_var << endl; + + PrimitiveVec& tab_row = gmi->tab_row; + Debug("approx::gmiCut") << "Is N sufficient here?" << endl; + tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs); + + Debug("approx::gmiCut") << "gmi_var " << gmi_var << endl; + + Debug("approx::gmiCut") << "tab_pos " << tab_row.len << endl; + write_pos = 1; + for(read_pos = 1; read_pos <= tab_row.len; ++read_pos){ + if (fabs(tab_row.coeffs[read_pos]) < 1e-10){ + }else{ + tab_row.coeffs[write_pos] = tab_row.coeffs[read_pos]; + tab_row.inds[write_pos] = tab_row.inds[read_pos]; + ++write_pos; + } + } + tab_row.len = write_pos-1; + Debug("approx::gmiCut") << "write_pos " << write_pos << endl; + Assert(tab_row.len > 0); + + for(i = 1; i <= tab_row.len; ++i){ + ind = tab_row.inds[i]; + Debug("approx::gmiCut") << "ind " << i << " " << ind << endl; + stat = (ind <= M) ? + glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M); + + Debug("approx::gmiCut") << "ind " << i << " " << ind << " stat " << stat << endl; + switch (stat){ + case GLP_NL: + case GLP_NU: + case GLP_NS: + gmi->tab_statuses[i] = stat; + break; + case GLP_NF: + default: + Unreachable(); + } + } + + if(Debug.isOn("approx::gmiCut")){ + gmi->print(Debug("approx::gmiCut")); + } + return gmi; +} + +static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){ + //(tree, br_var, br_val, dn < 0); + double rhs; + Kind k; + if(down_bad){ + // down branch is infeasible + // x <= floor(v) is infeasible + // - so x >= ceiling(v) is implied + k = kind::GEQ; + rhs = std::ceil(br_val); + }else{ + // up branch is infeasible + // x >= ceiling(v) is infeasible + // - so x <= floor(v) is implied + k = kind::LEQ; + rhs = std::floor(br_val); } + BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs); + return br_cut; } -void stopAtBingoOrPivotLimit(glp_tree *tree, void *info){ - int pivotLimit = *((int*)info); +static void glpkCallback(glp_tree *tree, void *info){ + AuxInfo* aux = (AuxInfo*)(info); + TreeLog& tl = *(aux->tl); + + int exec = tl.getExecutionOrd(); + int glpk_node_p = -1; + int node_ord = -1; + + if(tl.isActivelyLogging()){ + switch(glp_ios_reason(tree)){ + case GLP_LI_DELROW: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + + int nrows = glp_ios_rows_deleted(tree, NULL); + int* num = new int[1+nrows]; + glp_ios_rows_deleted(tree, num); + + NodeLog& node = tl.getNode(node_ord); + + RowsDeleted* rd = new RowsDeleted(exec, nrows, num); + + node.addCut(rd); + delete num; + } + break; + case GLP_ICUTADDED: + { + int cut_ord = glp_ios_pool_size(tree); + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + Assert(cut_ord > 0); + Debug("approx") << "curr node " << glpk_node_p + << " cut ordinal " << cut_ord + << " node depth " << glp_ios_node_level(tree, glpk_node_p) + << endl; + int klass; + glp_ios_get_cut(tree, cut_ord, NULL, NULL, &klass, NULL, NULL); + + NodeLog& node = tl.getNode(node_ord); + switch(klass){ + case GLP_RF_GMI: + { + GmiInfo* gmi = gmiCut(tree, exec, cut_ord); + node.addCut(gmi); + } + break; + case GLP_RF_MIR: + { + MirInfo* mir = mirCut(tree, exec, cut_ord); + node.addCut(mir); + } + break; + case GLP_RF_COV: + Debug("approx") << "GLP_RF_COV" << endl; + break; + case GLP_RF_CLQ: + Debug("approx") << "GLP_RF_CLQ" << endl; + break; + default: + break; + } + } + break; + case GLP_ICUTSELECT: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + int cuts = glp_ios_pool_size(tree); + int* ords = new int[1+cuts]; + int* rows = new int[1+cuts]; + int N = glp_ios_selected_cuts(tree, ords, rows); + + NodeLog& nl = tl.getNode(node_ord); + Debug("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl; + for(int i = 1; i <= N; ++i){ + Debug("approx") << "adding to " << node_ord <<" @ i= " << i + << " ords[i] = " << ords[i] + << " rows[i] = " << rows[i] << endl; + nl.addSelected(ords[i], rows[i]); + } + delete[] ords; + delete[] rows; + nl.applySelected(); + } + break; + case GLP_LI_BRANCH: + { + // a branch was just made + int br_var; + int p, dn, up; + int p_ord, dn_ord, up_ord; + double br_val; + br_var = glp_ios_branch_log(tree, &br_val, &p, &dn, &up); + p_ord = glp_ios_node_ord(tree, p); + + dn_ord = (dn >= 0) ? glp_ios_node_ord(tree, dn) : -1; + up_ord = (up >= 0) ? glp_ios_node_ord(tree, up) : -1; + + Debug("approx::") << "branch: "<< br_var << " " << br_val << " tree " << p << " " << dn << " " << up << endl; + Debug("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl; + if(dn < 0 && up < 0){ + Debug("approx::") << "branch close " << exec << endl; + NodeLog& node = tl.getNode(p_ord); + BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0); + node.addCut(cut_br); + tl.close(p_ord); + }else if(dn < 0 || up < 0){ + Debug("approx::") << "branch cut" << exec << endl; + NodeLog& node = tl.getNode(p_ord); + BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0); + node.addCut(cut_br); + }else{ + Debug("approx::") << "normal branch" << endl; + tl.branch(p_ord, br_var, br_val, dn_ord, up_ord); + } + } + break; + case GLP_LI_CLOSE: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + Debug("approx::") << "close " << glpk_node_p << endl; + tl.close(node_ord); + } + break; + default: + break; + } + } + switch(glp_ios_reason(tree)){ case GLP_IBINGO: + Debug("approx::") << "bingo" << endl; + aux->term = MipBingo; glp_ios_terminate(tree); break; + case GLP_ICUTADDED: + { + tl.addCut(); + } + break; + case GLP_LI_BRANCH: + { + int p, dn, up; + int br_var = glp_ios_branch_log(tree, NULL, &p, &dn, &up); + + if(br_var >= 0){ + unsigned v = br_var; + tl.logBranch(v); + int depth = glp_ios_node_level(tree, p); + unsigned ubl = (aux->branchLimit) >= 0 ? ((unsigned)(aux->branchLimit)) : 0u; + if(tl.numBranches(v) >= ubl || depth >= (aux->branchDepth)){ + aux->term = BranchesExhausted; + glp_ios_terminate(tree); + } + } + } + break; + case GLP_LI_CLOSE: + 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); + { + glp_prob* prob = glp_ios_get_prob(tree); + int iterationcount = glp_get_it_cnt(prob); + if(exec > (aux->pivotLimit)){ + aux->term = ExecExhausted; + glp_ios_terminate(tree); + }else if(iterationcount > (aux->pivotLimit)){ + aux->term = PivotsExhauasted; + glp_ios_terminate(tree); + } } break; } } -ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){ +std::vector ApproxGLPK::getValidCuts(const NodeLog& con) throw (RationalFromDoubleException){ + std::vector proven; + int nid = con.getNodeId(); + for(NodeLog::const_iterator j = con.begin(), jend=con.end(); j!=jend; ++j){ + CutInfo* cut = *j; + + if(cut->getKlass() != RowsDeletedKlass){ + if(!cut->reconstructed()){ + Assert(!cut->reconstructed()); + tryCut(nid, *cut); + } + } + + if(cut->proven()){ + proven.push_back(cut); + } + } + return proven; +} + +// std::vector ApproxGLPK::getValidCuts(const std::set& nodes){ +// // assume selected has been applied +// std::vector proven; +// std::set::const_iterator i, iend; +// for(i = nodes.begin(), iend=nodes.end(); i!=iend; ++i){ +// const NodeLog* nl = *i; +// getValidCuts(*nl, proven); +// } + +// return proven; +// } + +ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{ + int br_var = con.branchVariable(); + return getArithVarFromStructural(br_var); +} + +// Node ApproxGLPK::downBranchLiteral(const NodeLog& con) const{ +// int br_var = con.branchVariable(); +// ArithVar v = getArithVarFromStructural(br_var); +// if(v != ARITHVAR_SENTINEL){ +// if(d_vars.isIntegerInput(v) && d_vars.hasNode(v)){ +// Node var = d_vars.asNode(v); +// double br_val = con.branchValue(); +// Rational val = estimateWithCFE(br_val); +// if(!val.isIntegral()){ +// NodeManager* nm = NodeManager::currentNM(); +// Node ineq = nm->mkNode(kind::LEQ, var, mkRationalNode(val)); +// return Rewriter::rewrite(ineq); +// } +// } +// } +// return Node::null(); +// } + +// std::vector ApproxGLPK::getBranches(){ +// std::vector branches; +// for(TreeLog::const_iterator i = d_log.begin(), iend=d_log.end(); i!=iend;++i){ +// const NodeLog& con = (*i).second; +// if(con.isBranch()){ +// branches.push_back(&con); +// } +// } +// return branches; +// } + +MipResult ApproxGLPK::solveMIP(bool activelyLog){ 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. + AuxInfo aux; + aux.pivotLimit = d_pivotLimit; + aux.branchLimit = d_branchLimit; + aux.branchDepth = d_maxDepth; + aux.tl = &d_log; + aux.term = MipUnknown; + + d_log.reset(d_rootRowIds); + if(activelyLog){ + d_log.makeActive(); + }else{ + d_log.makeInactive(); + } + glp_iocp parm; glp_init_iocp(&parm); parm.presolve = GLP_OFF; @@ -785,36 +1802,1361 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){ 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.cb_func = glpkCallback; + parm.cb_info = &aux; parm.msg_lev = GLP_MSG_OFF; if(s_verbosity >= 1){ parm.msg_lev = GLP_MSG_ALL; } - int res = glp_intopt(d_prob, &parm); + + glp_erase_prob(d_mipProb); + glp_copy_prob(d_mipProb, d_realProb, GLP_OFF); + + int res = glp_intopt(d_mipProb, &parm); + + Debug("approx::solveMIP") << "res "<& inp){ +// Assert(!inp.empty()); +// NodeBuilder<> nb(kind::AND); +// set::const_iterator iter, end; +// for(iter = inp.begin(), end = inp.end(); iter != end; ++iter){ +// const ConstraintP c = *iter; +// Assert(c != NullConstraint); +// c->explainForConflict(nb); +// } +// Node ret = safeConstructNary(nb); +// Node rew = Rewriter::rewrite(ret); +// if(rew.getNumChildren() < ret.getNumChildren()){ +// //Debug("approx::") << "explainSet " << ret << " " << rew << endl; +// } +// return rew; +// } + +DeltaRational sumConstraints(const DenseMap& xs, const DenseMap& cs, bool* anyinf){ + if(anyinf != NULL){ + *anyinf = false; } + + DeltaRational beta(0); + DenseMap::const_iterator iter, end; + iter = xs.begin(); + end = xs.end(); + + Debug("approx::sumConstraints") << "sumConstraints"; + for(; iter != end; ++iter){ + ArithVar x = *iter; + const Rational& psi = xs[x]; + ConstraintP c = cs[x]; + Assert(c != NullConstraint); + + const DeltaRational& bound = c->getValue(); + beta += bound * psi; + Debug("approx::sumConstraints") << " +("<& v){ + // Remove Slack variables + vector zeroes; + DenseMap::const_iterator i, iend; + for(i = v.begin(), iend = v.end(); i != iend; ++i){ + ArithVar x = *i; + if(v[x].isZero()){ + zeroes.push_back(x); + } + } + + vector::const_iterator j, jend; + for(j = zeroes.begin(), jend = zeroes.end(); j != jend; ++j){ + ArithVar x = *j; + v.remove(x); + } +} +void removeZeroes(DenseVector& v){ + removeZeroes(v.lhs); } +void removeAuxillaryVariables(const ArithVariables& vars, DenseMap& vec){ + // Remove auxillary variables + vector aux; + DenseMap::const_iterator vec_iter, vec_end; + vec_iter = vec.begin(), vec_end = vec.end(); + for(; vec_iter != vec_end; ++vec_iter){ + ArithVar x = *vec_iter; + if(vars.isAuxiliary(x)){ + aux.push_back(x); + } + } + + vector::const_iterator aux_iter, aux_end; + aux_iter = aux.begin(), aux_end = aux.end(); + for(; aux_iter != aux_end; ++aux_iter){ + ArithVar s = *aux_iter; + Rational& s_coeff = vec.get(s); + Assert(vars.isAuxiliary(s)); + Assert(vars.hasNode(s)); + Node sAsNode = vars.asNode(s); + Polynomial p = Polynomial::parsePolynomial(sAsNode); + for(Polynomial::iterator j = p.begin(), p_end=p.end(); j != p_end; ++j){ + Monomial m = *j; + const Rational& ns_coeff = m.getConstant().getValue(); + Node vl = m.getVarList().getNode(); + ArithVar ns = vars.asArithVar(vl); + Rational prod = s_coeff * ns_coeff; + if(vec.isKey(ns)){ + vec.get(ns) += prod; + }else{ + vec.set(ns, prod); + } + } + s_coeff = Rational(0); // subtract s_coeff * s from vec + } + removeZeroes(vec); +} + +ArithVar ApproxGLPK::_getArithVar(int nid, int M, int ind) const{ + if(ind <= 0){ + return ARITHVAR_SENTINEL; + }else if(ind <= M){ + return getArithVarFromRow(nid, ind); + }else{ + return getArithVarFromStructural(ind - M); + } +} + + +bool ApproxGLPK::guessIsConstructable(const DenseMap& guess) const { + // basic variable + // sum g[i] * x_i + DenseMap g = guess; + removeAuxillaryVariables(d_vars, g); + + if(Debug.isOn("guessIsConstructable")){ + if(!g.empty()){ + Debug("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl; + DenseVector::print(Debug("approx::guessIsConstructable"), g); + Debug("approx::guessIsConstructable") << endl; + } + } + return g.empty(); +} + +bool ApproxGLPK::loadToBound(int nid, int M, int len, int* inds, int* statuses, DenseMap& toBound) const{ + for(int i = 1; i <= len; ++i){ + int status = statuses[i]; + int ind = inds[i]; + ArithVar v = _getArithVar(nid, M, ind); + ConstraintP c = NullConstraint; + if(v == ARITHVAR_SENTINEL){ return true; } + + switch(status){ + case GLP_NL: + c = d_vars.getLowerBoundConstraint(v); + break; + case GLP_NU: + case GLP_NS: // upper bound sufficies for fixed variables + c = d_vars.getUpperBoundConstraint(v); + break; + case GLP_NF: + default: + return true; + } + if(c == NullConstraint){ + Debug("approx::") << "couldn't find " << v << " @ " << nid << endl; + return true; + } + Assert(c != NullConstraint); + toBound.set(v, c); + } + return false; +} + +bool ApproxGLPK::checkCutOnPad(int nid, const CutInfo& cut) const{ + + Debug("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<& constructedLhs = d_pad.d_cut.lhs; + const Rational& constructedRhs = d_pad.d_cut.rhs; + hash_set visited; + + if(constructedLhs.empty()){ + Debug("approx::checkCutOnPad") << "its empty?" <& toBound = d_pad.d_toBound; + d_pad.d_failure = loadToBound(nid, M, tab.len, tab.inds, statuses, toBound); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = constructGmiCut(); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = checkCutOnPad(nid, gmi); + if(d_pad.d_failure){ return true; } + + return false; +} + +bool ApproxGLPK::applyCMIRRule(int nid, const MirInfo& mir){ + + const DenseMap& compRanges = d_pad.d_compRanges; + + DenseMap& alpha = d_pad.d_alpha.lhs; + Rational& b = d_pad.d_alpha.rhs; + + Rational delta = estimateWithCFE(mir.delta); + d_pad.d_failure = (delta.sgn() <= 0); + if(d_pad.d_failure){ return true; } + + Debug("approx::mir") << "applyCMIRRule() " << delta << " " << mir.delta << endl; + + DenseMap::const_iterator iter, iend; + iter = alpha.begin(), iend = alpha.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& curr = alpha[v]; + Rational next = curr / delta; + if(compRanges.isKey(v)){ + b -= curr * compRanges[v]; + alpha.set(v, - next); + }else{ + alpha.set(v, next); + } + } + b = b / delta; + + Rational roundB = (b + Rational(1,2)).floor(); + d_pad.d_failure = (b - roundB).abs() < Rational(1,90); + // intensionally more generous than glpk here + if(d_pad.d_failure){ return true; } + + Rational one(1); + Rational fb = b.floor_frac(); + Rational one_sub_fb = one - fb; + Rational gamma = (one / one_sub_fb); + + DenseMap& cut = d_pad.d_cut.lhs; + Rational& beta = d_pad.d_cut.rhs; + + iter = alpha.begin(), iend = alpha.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& a_j = alpha[v]; + if(d_vars.isIntegerInput(v)){ + Rational floor_aj = a_j.floor(); + Rational frac_aj = a_j.floor_frac(); + if(frac_aj <= fb){ + cut.set(v, floor_aj); + }else{ + Rational tmp = ((frac_aj - fb) / one_sub_fb); + cut.set(v, floor_aj + tmp); + } + }else{ + cut.set(v, a_j * gamma); + } + } + beta = b.floor(); + + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + if(compRanges.isKey(v)){ + Rational neg = - cut[v]; + beta += neg * compRanges[v]; + cut.set(v, neg); + } + } + + return false; +} + +bool ApproxGLPK::attemptMir(int nid, const MirInfo& mir){ + d_pad.clear(); + + d_pad.d_cutKind = kind::LEQ; + + // virtual bounds must be done before slacks + d_pad.d_failure = loadVirtualBoundsIntoPad(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = loadSlacksIntoPad(nid, mir); + if(d_pad.d_failure){ return true; } + + + d_pad.d_failure = loadRowSumIntoAgg(nid, mir.getMAtCreation(), mir.row_sum); + if(d_pad.d_failure){ return true; } + + removeFixed(d_vars, d_pad.d_agg, d_pad.d_explanation); + + d_pad.d_failure = buildModifiedRow(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = constructMixedKnapsack(); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = makeRangeForComplemented(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = applyCMIRRule(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = replaceSlacksOnCuts(); + if(d_pad.d_failure){ return true; } + + removeAuxillaryVariables(d_vars, d_pad.d_cut.lhs); + + d_pad.d_failure = checkCutOnPad(nid, mir); + if(d_pad.d_failure){ return true; } + + return false; + //return makeCutNodes(nid, mir); +} + +bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){ + if(ri <= 0) { return true; } + + static int instance = 0; + ++instance; + Debug("glpk::loadVB") << "loadVB() " << instance << endl; + + ArithVar rowVar = _getArithVar(nid, M, ri); + ArithVar contVar = _getArithVar(nid, M, j); + if(rowVar == ARITHVAR_SENTINEL){ return true; } + if(contVar == ARITHVAR_SENTINEL){ return true; } + + if(!d_vars.isAuxiliary(rowVar)){ return true; } + // is integer is correct here + if(d_vars.isInteger(contVar)){ return true; } + + ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar); + ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar); + + if(lb != NullConstraint && ub != NullConstraint){ return true; } + + ConstraintP rcon = lb == NullConstraint ? ub : lb; + if(rcon == NullConstraint) { return true; } + + if(!rcon->getValue().isZero()){ return true; } + + if(!d_vars.hasNode(rowVar)){ return true; } + Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar)); + if(p.size() != 2) { return false; } + + Monomial first = p.getHead(), second = p.getTail().getHead(); + Rational c1 = first.getConstant().getValue(); + Rational c2 = second.getConstant().getValue(); + Node nx1 = first.getVarList().getNode(); + Node nx2 = second.getVarList().getNode(); + + if(!d_vars.hasArithVar(nx1)) { return true; } + if(!d_vars.hasArithVar(nx2)) { return true; } + ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2); + + Assert(x1 != x2); + Assert(!c1.isZero()); + Assert(!c2.isZero()); + + Debug("glpk::loadVB") + << " lb " << lb + << " ub " << ub + << " rcon " << rcon + << " x1 " << x1 + << " x2 " << x2 + << " c1 " << c1 + << " c2 " << c2 << endl; + + ArithVar iv = (x1 == contVar) ? x2 : x1; + Rational& cc = (x1 == contVar) ? c1 : c2; + Rational& ic = (x1 == contVar) ? c2 : c1; + + Debug("glpk::loadVB") + << " cv " << contVar + << " cc " << cc + << " iv " << iv + << " c2 " << ic << endl; + + if(!d_vars.isIntegerInput(iv)){ return true; } + // cc * cv + ic * iv <= 0 or + // cc * cv + ic * iv <= 0 + + if(rcon == ub){ // multiply by -1 + cc = -cc; ic = - ic; + } + Debug("glpk::loadVB") << " cv " << contVar + << " cc " << cc + << " iv " << iv + << " c2 " << ic << endl; + + // cc * cv + ic * iv >= 0 + // cc * cv >= -ic * iv + // if cc < 0: + // cv <= -ic/cc * iv + // elif cc > 0: + // cv >= -ic/cc * iv + Assert(!cc.isZero()); + Rational d = -ic/cc; + Debug("glpk::loadVB") << d << " " << cc.sgn() << endl; + bool nowUb = cc.sgn() < 0; + if(wantUb != nowUb) { return true; } + + Kind rel = wantUb ? kind::LEQ : kind::GEQ; + + tmp = VirtualBound(contVar, rel, d, iv, rcon); + return false; +} + +bool ApproxGLPK::loadVirtualBoundsIntoPad(int nid, const MirInfo& mir){ + Assert(mir.vlbRows != NULL); + Assert(mir.vubRows != NULL); + + int N = mir.getN(); + int M = mir.getMAtCreation(); + + // Load the virtual bounds first + VirtualBound tmp; + for(int j=1; j <= N+M; ++j){ + if(!loadVB(nid, M, j, mir.vlbRows[j], false, tmp)){ + if(d_pad.d_vlb.isKey(tmp.x)){ return true; } + d_pad.d_vlb.set(tmp.x, tmp); + }else if(mir.vlbRows[j] > 0){ + Debug("approx::mir") << "expected vlb to work" << endl; + } + if(!loadVB(nid, M, j, mir.vubRows[j], true, tmp)){ + if(d_pad.d_vub.isKey(tmp.x)){ return true; } + d_pad.d_vub.set(tmp.x, tmp); + }else if(mir.vubRows[j] > 0){ + Debug("approx::mir") << "expected vub to work" << endl; + } + } + return false; +} + +bool ApproxGLPK::loadSlacksIntoPad(int nid, const MirInfo& mir){ + Assert(mir.vlbRows != NULL); + Assert(mir.vubRows != NULL); + + int N = mir.getN(); + int M = mir.getMAtCreation(); + + bool useVB; + // Load the virtual bounds first + SlackReplace rep; + bool lb; + ConstraintP b; + Debug("approx::mir") << "loadSlacksIntoPad(): N="< 0) : (mir.vubRows[j] > 0); + if(useVB){ + if(lb ? d_pad.d_vlb.isKey(v) : d_pad.d_vub.isKey(v)){ + rep = lb ? SlackVLB : SlackVUB; + } + }else{ + b = lb ? d_vars.getLowerBoundConstraint(v) + : d_vars.getUpperBoundConstraint(v); + if(b != NullConstraint){ + if(b->getValue().infinitesimalIsZero()){ + rep = lb ? SlackLB : SlackUB; + } + } + } + + Debug("approx::mir") << " for: " << j << ", " << v; + Debug("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " "; + Debug("approx::mir") << sub << " " << rep << " " << mir.vlbRows[j] << " " << mir.vubRows[j] + << endl; + if(rep != SlackUndef){ + d_pad.d_slacks.set(v,rep); + } + break; + case '?': + continue; + default: + Debug("approx::mir") << " for: " << j << " got subst " << (int)sub << endl; + continue; + } + } + return false; +} + +bool ApproxGLPK::replaceSlacksOnCuts(){ + vector virtualVars; + + DenseMap& cut = d_pad.d_cut.lhs; + Rational& cutRhs = d_pad.d_cut.rhs; + + DenseMap::const_iterator iter, iend; + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar x = *iter; + SlackReplace rep = d_pad.d_slacks[x]; + if(d_vars.isIntegerInput(x)){ + Assert(rep == SlackLB || rep == SlackUB); + Rational& a = cut.get(x); + + const DeltaRational& bound = (rep == SlackLB) ? + d_vars.getLowerBound(x) : d_vars.getUpperBound(x); + Assert(bound.infinitesimalIsZero()); + Rational prod = a * bound.getNoninfinitesimalPart(); + if(rep == SlackLB){ + cutRhs += prod; + }else{ + cutRhs -= prod; + a = -a; + } + }else if(rep == SlackVLB){ + virtualVars.push_back(d_pad.d_vlb[x].y); + }else if(rep == SlackVUB){ + virtualVars.push_back(d_pad.d_vub[x].y); + } + } + + for(size_t i = 0; i < virtualVars.size(); ++i){ + ArithVar x = virtualVars[i]; + if(!cut.isKey(x)){ + cut.set(x, Rational(0)); + } + } + + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar x = *iter; + if(!d_vars.isIntegerInput(x)){ + SlackReplace rep = d_pad.d_slacks[x]; + Rational& a = cut.get(x); + switch(rep){ + case SlackLB: + { + const DeltaRational& bound = d_vars.getLowerBound(x); + Assert(bound.infinitesimalIsZero()); + cutRhs += a * bound.getNoninfinitesimalPart(); + } + break; + case SlackUB: + { + const DeltaRational& bound = d_vars.getUpperBound(x); + Assert(bound.infinitesimalIsZero()); + cutRhs -= a * bound.getNoninfinitesimalPart(); + a = -a; + } + break; + case SlackVLB: + case SlackVUB: + { + bool lb = (rep == SlackVLB); + const VirtualBound& vb = lb ? + d_pad.d_vlb[x] : d_pad.d_vub[x]; + ArithVar y = vb.y; + Assert(vb.x == x); + Assert(cut.isKey(y)); + Rational& ycoeff = cut.get(y); + if(lb){ + ycoeff -= a * vb.d; + }else{ + ycoeff += a * vb.d; + a = -a; + } + } + break; + default: + return true; + } + } + } + removeZeroes(cut); + return false; +} + +bool ApproxGLPK::loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& row_sum){ + DenseMap& lhs = d_pad.d_agg.lhs; + d_pad.d_agg.rhs = Rational(0); + + int len = row_sum.len; + for(int i = 1; i <= len; ++i){ + int aux_ind = row_sum.inds[i]; // auxillary index + double coeff = row_sum.coeffs[i]; + ArithVar x = _getArithVar(nid, M, aux_ind); + if(x == ARITHVAR_SENTINEL){ return true; } + Rational c = estimateWithCFE(coeff); + if(lhs.isKey(x)){ + lhs.get(x) -= c; + }else{ + lhs.set(x, -c); + } + } + + Debug("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl; + if(Debug.isOn("approx::mir")) { DenseVector::print(Debug("approx::mir"), lhs); } + removeAuxillaryVariables(d_vars, lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 1" << endl; + + if(Debug.isOn("approx::mir")){ + Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl; + DenseVector::print(Debug("approx::mir"), lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 2" << endl; + } + + for(int i = 1; i <= len; ++i){ + int aux_ind = row_sum.inds[i]; // auxillary index + double coeff = row_sum.coeffs[i]; + ArithVar x = _getArithVar(nid, M, aux_ind); + Assert(x != ARITHVAR_SENTINEL); + Rational c = estimateWithCFE(coeff); + Assert(!lhs.isKey(x)); + lhs.set(x, c); + } + + if(Debug.isOn("approx::mir")){ + Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl; + DenseVector::print(Debug("approx::mir"), lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 3" << endl; + } + return false; +} + +bool ApproxGLPK::buildModifiedRow(int nid, const MirInfo& mir){ + const DenseMap& agg = d_pad.d_agg.lhs; + const Rational& aggRhs = d_pad.d_agg.rhs; + DenseMap& mod = d_pad.d_mod.lhs; + Rational& modRhs = d_pad.d_mod.rhs; + + Debug("approx::mir") + << "buildModifiedRow()" + << " |agg|=" << d_pad.d_agg.lhs.size() + << " |mod|=" << d_pad.d_mod.lhs.size() + << " |slacks|=" << d_pad.d_slacks.size() + << " |vlb|=" << d_pad.d_vub.size() + << " |vub|=" << d_pad.d_vlb.size() << endl; + + mod.addAll(agg); + modRhs = aggRhs; + DenseMap::const_iterator iter, iend; + for(iter = agg.begin(), iend = agg.end(); iter != iend; ++iter){ + ArithVar x = *iter; + const Rational& c = mod[x]; + if(!d_pad.d_slacks.isKey(x)){ + Debug("approx::mir") << "missed x: " << x << endl; + return true; + } + SlackReplace rep = d_pad.d_slacks[x]; + switch(rep){ + case SlackLB: // skip for now + case SlackUB: + break; + case SlackVLB: /* x[k] = lb[k] * x[kk] + x'[k] */ + case SlackVUB: /* x[k] = ub[k] * x[kk] - x'[k] */ + { + Assert(!d_vars.isIntegerInput(x)); + bool ub = (rep == SlackVUB); + const VirtualBound& vb = + ub ? d_pad.d_vub[x] : d_pad.d_vlb[x]; + Assert(vb.x == x); + ArithVar y = vb.y; + Rational prod = c * vb.d; + if(mod.isKey(y)){ + mod.get(x) += prod; + }else{ + mod.set(y, prod); + } + if(ub){ + mod.set(x, -c); + } + Assert(vb.c != NullConstraint); + d_pad.d_explanation.insert(vb.c); + } + break; + default: + return true; + } + } + removeZeroes(mod); /* if something cancelled we don't want it in the explanation */ + for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){ + ArithVar x = *iter; + if(!d_pad.d_slacks.isKey(x)){ return true; } + + SlackReplace rep = d_pad.d_slacks[x]; + switch(rep){ + case SlackLB: /* x = lb + x' */ + case SlackUB: /* x = ub - x' */ + { + bool ub = (rep == SlackUB); + ConstraintP b = ub ? d_vars.getUpperBoundConstraint(x): + d_vars.getLowerBoundConstraint(x); + + Assert(b != NullConstraint); + Assert(b->getValue().infinitesimalIsZero()); + const Rational& c = mod.get(x); + modRhs -= c * b->getValue().getNoninfinitesimalPart(); + if(ub){ + mod.set(x, -c); + } + d_pad.d_explanation.insert(b); + } + break; + case SlackVLB: /* handled earlier */ + case SlackVUB: + break; + default: + return true; + } + } + removeZeroes(mod); + return false; +} + +bool ApproxGLPK::makeRangeForComplemented(int nid, const MirInfo& mir){ + DenseMap& alpha = d_pad.d_alpha.lhs; + int M = mir.getMAtCreation(); + int N = mir.getN(); + DenseMap& compRanges = d_pad.d_compRanges; + + int complemented = 0; + + for(int j = 1; j <= M + N; ++j){ + if(mir.cset[j] != 0){ + complemented++; + ArithVar x = _getArithVar(nid, M, j); + if(!alpha.isKey(x)){ return true; } + if(!d_vars.isIntegerInput(x)){ return true; } + Assert(d_pad.d_slacks.isKey(x)); + Assert(d_pad.d_slacks[x] == SlackLB || d_pad.d_slacks[x] == SlackUB); + + ConstraintP lb = d_vars.getLowerBoundConstraint(x); + ConstraintP ub = d_vars.getUpperBoundConstraint(x); + + if(lb == NullConstraint) { return true; } + if(ub == NullConstraint) { return true; } + + if(!lb->getValue().infinitesimalIsZero()){ + return true; + } + if(!ub->getValue().infinitesimalIsZero()){ + return true; + } + + const Rational& uval = ub->getValue().getNoninfinitesimalPart(); + const Rational& lval = lb->getValue().getNoninfinitesimalPart(); + + d_pad.d_explanation.insert(lb); + d_pad.d_explanation.insert(ub); + + Rational u = uval - lval; + // u is the same for both rep == LP and rep == UB + if(compRanges.isKey(x)) { return true; } + compRanges.set(x,u); + } + } + + Debug("approx::mir") << "makeRangeForComplemented()" << complemented << endl; + return false; +} + + +bool ApproxGLPK::constructMixedKnapsack(){ + const DenseMap& mod = d_pad.d_mod.lhs; + const Rational& modRhs = d_pad.d_mod.rhs; + DenseMap& alpha = d_pad.d_alpha.lhs; + Rational& beta = d_pad.d_alpha.rhs; + + Assert(alpha.empty()); + beta = modRhs; + + unsigned intVars = 0; + unsigned remain = 0; + unsigned dropped = 0; + DenseMap::const_iterator iter, iend; + for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& c = mod[v]; + Assert(!c.isZero()); + if(d_vars.isIntegerInput(v)){ + intVars++; + alpha.set(v, c); + }else if(c.sgn() < 0){ + remain++; + alpha.set(v, c); + }else{ + dropped++; + } + } + + Debug("approx::mir") + << "constructMixedKnapsack() " + <<" dropped " << dropped + <<" remain " << remain + <<" intVars " << intVars + << endl; + return intVars == 0; // if this is 0 we have failed +} + +bool ApproxGLPK::attemptConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + bool failed = guessCoefficientsConstructTableRow(nid, M, vec); + if(failed){ + failed = gaussianElimConstructTableRow(nid, M, vec); + } + + return failed; +} + +bool ApproxGLPK::gaussianElimConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + TimerStat::CodeTimer codeTimer(d_stats.d_gaussianElimConstructTime); + ++d_stats.d_gaussianElimConstruct; + + ArithVar basic = d_pad.d_basic; + DenseMap& tab = d_pad.d_tabRow.lhs; + tab.purge(); + d_pad.d_tabRow.rhs = Rational(0); + Assert(basic != ARITHVAR_SENTINEL); + Assert(tab.empty()); + Assert(d_pad.d_tabRow.rhs.isZero()); + + if(d_vars.isAuxiliary(basic)) { return true; } + + if(Debug.isOn("gaussianElimConstructTableRow")){ + Debug("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("< onrow; + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + ArithVar var = _getArithVar(nid, M, ind); + if(var == ARITHVAR_SENTINEL){ + Debug("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl; + return true; + } + onrow.insert(var); + } + + + Debug("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("< A; + A.increaseSizeTo(d_vars.getNumberOfVariables()); + std::vector< std::pair > rows; + set::const_iterator i, iend; + // load the rows for auxiliary variables into A + for(i=onrow.begin(), iend=onrow.end(); i!=iend; ++i){ + ArithVar v = *i; + if(d_vars.isAuxiliary(v)){ + Assert(d_vars.hasNode(v)); + + vector coeffs; + vector vars; + + coeffs.push_back(Rational(-1)); + vars.push_back(v); + + Node n = d_vars.asNode(v); + Polynomial p = Polynomial::parsePolynomial(n); + Polynomial::iterator j = p.begin(), jend=p.end(); + for(j=p.begin(), jend=p.end(); j!=jend; ++j){ + Monomial m = *j; + if(m.isConstant()) { return true; } + VarList vl = m.getVarList(); + if(!d_vars.hasArithVar(vl.getNode())){ return true; } + ArithVar x = d_vars.asArithVar(vl.getNode()); + const Rational& q = m.getConstant().getValue(); + coeffs.push_back(q); vars.push_back(x); + } + RowIndex rid = A.addRow(coeffs, vars); + rows.push_back(make_pair(rid, ARITHVAR_SENTINEL)); + } + } + Debug("gaussianElimConstructTableRow") << "3 gaussianElimConstructTableRow("<::Entry& e = A.findEntry(rid, other); + if(!e.blank()){ + // r_p : 0 = -1 * other + sum a_i x_i + // rid : 0 = e * other + sum b_i x_i + // rid += e * r_p + // : 0 = 0 * other + ... + Assert(!e.getCoefficient().isZero()); + + Rational cp = e.getCoefficient(); + Debug("gaussianElimConstructTableRow") + << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl; + A.rowPlusRowTimesConstant(rid, prevRow, cp); + } + } + if(Debug.isOn("gaussianElimConstructTableRow")){ + A.printMatrix(Debug("gaussianElimConstructTableRow")); + } + + // solve the row for anything other than non-basics + bool solveForBasic = (i + 1 == rows.size()); + Rational q; + ArithVar s = ARITHVAR_SENTINEL; + Matrix::RowIterator k = A.getRow(rid).begin(); + Matrix::RowIterator k_end = A.getRow(rid).end(); + for(; k != k_end; ++k){ + const Matrix::Entry& e = *k; + ArithVar colVar = e.getColVar(); + bool selectColVar = false; + if(colVar == basic){ + selectColVar = solveForBasic; + }else if(onrow.find(colVar) == onrow.end()) { + selectColVar = true; + } + if(selectColVar){ + s = colVar; + q = e.getCoefficient(); + } + } + if(s == ARITHVAR_SENTINEL || q.isZero()){ + Debug("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<::RowIterator k = A.getRow(rid_last).begin(); + Matrix::RowIterator k_end = A.getRow(rid_last).end(); + for(; k != k_end; ++k){ + const Matrix::Entry& e = *k; + tab.set(e.getColVar(), e.getCoefficient()); + } + Debug("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<& tabRow = d_pad.d_tabRow.lhs; + const DenseMap& toBound = d_pad.d_toBound; + DenseMap& cut = d_pad.d_cut.lhs; + std::set& explanation = d_pad.d_explanation; + Rational& rhs = d_pad.d_cut.rhs; + + DenseMap::const_iterator iter, end; + Assert(cut.empty()); + + // compute beta for a "fake" assignment + bool anyInf; + DeltaRational dbeta = sumConstraints(tabRow, toBound, &anyInf); + const Rational& beta = dbeta.getNoninfinitesimalPart(); + Debug("approx::gmi") << dbeta << endl; + if(anyInf || beta.isIntegral()){ return true; } + + Rational one = Rational(1); + Rational fbeta = beta.floor_frac(); + rhs = fbeta; + Assert(fbeta.sgn() > 0); + Assert(fbeta < one); + Rational one_sub_fbeta = one - fbeta; + for(iter = tabRow.begin(), end = tabRow.end(); iter != end; ++iter){ + ArithVar x = *iter; + const Rational& psi = tabRow[x]; + ConstraintP c = toBound[x]; + const Rational& bound = c->getValue().getNoninfinitesimalPart(); + if(d_vars.boundsAreEqual(x)){ + // do not add a coefficient + // implictly substitute the variable w/ its constraint + std::pair exp = d_vars.explainEqualBounds(x); + explanation.insert(exp.first); + if(exp.second != NullConstraint){ + explanation.insert(exp.second); + } + }else if(d_vars.isIntegerInput(x) && psi.isIntegral()){ + // do not add a coefficient + // nothing to explain + Debug("approx::gmi") << "skipping " << x << endl; + }else{ + explanation.insert(c); + Rational phi; + Rational alpha = (c->isUpperBound() ? psi : -psi); + + // x - ub <= 0 and lb - x <= 0 + if(d_vars.isIntegerInput(x)){ + Assert(!psi.isIntegral()); + // alpha = slack_sgn * psi + Rational falpha = alpha.floor_frac(); + Assert(falpha.sgn() > 0); + Assert(falpha < one); + phi = (falpha <= fbeta) ? + falpha : ((fbeta / one_sub_fbeta) * (one - falpha)); + }else{ + phi = (alpha >= 0) ? + alpha : ((fbeta / one_sub_fbeta) * (- alpha)); + } + Assert(phi.sgn() != 0); + if(c->isUpperBound()){ + cut.set(x, -phi); + rhs -= phi * bound; + }else{ + cut.set(x, phi); + rhs += phi * bound; + } + } + } + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "pre removeSlackVariables"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + removeAuxillaryVariables(d_vars, cut); + + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "post removeAuxillaryVariables"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + removeFixed(d_vars, d_pad.d_cut, explanation); + + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "post removeFixed"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + return false; +} + +void ApproxGLPK::tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException){ + Assert(!cut.reconstructed()); + Assert(cut.getKlass() != RowsDeletedKlass); + bool failure = false; + switch(cut.getKlass()){ + case GmiCutKlass: + failure = attemptGmi(nid, static_cast(cut)); + break; + case MirCutKlass: + failure = attemptMir(nid, static_cast(cut)); + break; + case BranchCutKlass: + failure = attemptBranchCut(nid, dynamic_cast(cut)); + break; + default: + break; + } + Assert(failure == d_pad.d_failure); + + if(!failure){ + // move the pad to the cut + cut.setReconstruction(d_pad.d_cut); + + if(cut.getKlass() != BranchCutKlass){ + std::set& exp = d_pad.d_explanation; + ConstraintCPVec asvec(exp.begin(), exp.end()); + cut.swapExplanation(asvec); + } + }else{ + Debug("approx") << "failure " << cut.getKlass() << endl; + } +} + + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h index b32fdef2d..15996fef8 100644 --- a/src/theory/arith/approx_simplex.h +++ b/src/theory/arith/approx_simplex.h @@ -22,7 +22,9 @@ #include "util/statistics_registry.h" #include "theory/arith/arithvar.h" -#include "theory/arith/linear_equality.h" +#include "util/rational.h" +#include "theory/arith/delta_rational.h" +//#include "theory/arith/linear_equality.h" #include "util/dense_map.h" #include @@ -30,10 +32,81 @@ namespace CVC4 { namespace theory { namespace arith { +enum LinResult { + LinUnknown, /* Unknown error */ + LinFeasible, /* Relaxation is feasible */ + LinInfeasible, /* Relaxation is infeasible/all integer branches closed */ + LinExhausted +}; + +enum MipResult { + MipUnknown, /* Unknown error */ + MipBingo, /* Integer feasible */ + MipClosed, /* All integer branches closed */ + BranchesExhausted, /* Exhausted number of branches */ + PivotsExhauasted, /* Exhausted number of pivots */ + ExecExhausted /* Exhausted total operations */ +}; +std::ostream& operator<<(std::ostream& out, MipResult res); + +class ApproximateStatistics { +public: + // IntStat d_relaxCalls; + // IntStat d_relaxUnknowns; + // IntStat d_relaxFeasible; + // IntStat d_relaxInfeasible; + // IntStat d_relaxPivotsExhausted; + + // IntStat d_mipCalls; + // IntStat d_mipUnknowns; + // IntStat d_mipBingo; + // IntStat d_mipClosed; + // IntStat d_mipBranchesExhausted; + // IntStat d_mipPivotsExhausted; + // IntStat d_mipExecExhausted; + + + // IntStat d_gmiGen; + // IntStat d_gmiReplay; + // IntStat d_mipGen; + // IntStat d_mipReplay; + + IntStat d_branchMaxDepth; + IntStat d_branchesMaxOnAVar; + + TimerStat d_gaussianElimConstructTime; + IntStat d_gaussianElimConstruct; + AverageStat d_averageGuesses; + + ApproximateStatistics(); + ~ApproximateStatistics(); +}; + + +class NodeLog; +class TreeLog; +class ArithVariables; +class CutInfo; +class RowsDeleted; class ApproximateSimplex{ protected: + const ArithVariables& d_vars; + TreeLog& d_log; + ApproximateStatistics& d_stats; + int d_pivotLimit; + /* the maximum pivots allowed in a query. */ + + int d_branchLimit; + /* maximum branches allowed on a variable */ + + int d_maxDepth; + /* maxmimum branching depth allowed.*/ + + static Integer s_defaultMaxDenom; + /* Default denominator for diophatine approximation. + * 2^{26}*/ public: @@ -43,31 +116,48 @@ public: * If glpk is enabled, return a subclass that can do something. * If glpk is disabled, return a subclass that does nothing. */ - static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars); - ApproximateSimplex(); + static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s); + ApproximateSimplex(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s); virtual ~ApproximateSimplex(){} + /* the maximum pivots allowed in a query. */ + void setPivotLimit(int pl); + + /* maximum branches allowed on a variable */ + void setBranchOnVariableLimit(int bl); + + /* maximum branches allowed on a variable */ + void setBranchingDepth(int bd); + /** A result is either sat, unsat or unknown.*/ - enum ApproxResult {ApproxError, ApproxSat, ApproxUnsat}; + //enum ApproxResult {ApproxError, ApproxSat, ApproxUnsat}; struct Solution { DenseSet newBasis; DenseMap newValues; Solution() : newBasis(), newValues(){} }; - /** Sets a deterministic effort limit. */ - void setPivotLimit(int pivotLimit); + virtual ArithVar getBranchVar(const NodeLog& nl) const = 0; + //virtual void mapRowId(int nid, int ind, ArithVar v) = 0; + //virtual void applyRowsDeleted(int nid, const RowsDeleted& rd) = 0; /** Sets a maximization criteria for the approximate solver.*/ virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0; virtual ArithRatPairVec heuristicOptCoeffs() const = 0; - virtual ApproxResult solveRelaxation() = 0; - virtual Solution extractRelaxation() const = 0; + virtual LinResult solveRelaxation() = 0; + virtual Solution extractRelaxation() const throw(RationalFromDoubleException) = 0; + + virtual MipResult solveMIP(bool activelyLog) = 0; + virtual Solution extractMIP() const throw(RationalFromDoubleException) = 0; + + virtual std::vector getValidCuts(const NodeLog& node) throw(RationalFromDoubleException) = 0; + //virtual std::vector getBranches() = 0; - virtual ApproxResult solveMIP() = 0; - virtual Solution extractMIP() const = 0; + //virtual Node downBranchLiteral(const NodeLog& con) const = 0; + + virtual void tryCut(int nid, CutInfo& cut) throw(RationalFromDoubleException) = 0; /** UTILITIES FOR DEALING WITH ESTIMATES */ @@ -82,7 +172,8 @@ public: * cuts off the estimate once the value is approximately zero. * This is designed for removing rounding artifacts. */ - static Rational estimateWithCFE(double d); + static Rational estimateWithCFE(double d) throw(RationalFromDoubleException); + static Rational estimateWithCFE(double d, const Integer& D) throw(RationalFromDoubleException); /** * Converts a rational to a continued fraction expansion representation @@ -95,7 +186,10 @@ public: static Rational cfeToRational(const std::vector& exp); /** Estimates a rational as a continued fraction expansion.*/ - static Rational estimateWithCFE(const Rational& q, int depth); + //static Rational estimateWithCFE(const Rational& q, int depth); + static Rational estimateWithCFE(const Rational& q, const Integer& K); + + virtual double sumInfeasibilities(bool mip) const = 0; };/* class ApproximateSimplex */ diff --git a/src/theory/arith/arith_ite_utils.cpp b/src/theory/arith/arith_ite_utils.cpp new file mode 100644 index 000000000..61bf5c76e --- /dev/null +++ b/src/theory/arith/arith_ite_utils.cpp @@ -0,0 +1,436 @@ +#include "theory/arith/arith_ite_utils.h" +#include "theory/arith/normal_form.h" +#include "theory/arith/arith_utilities.h" +#include "theory/ite_utilities.h" +#include "theory/theory_model.h" +#include "theory/rewriter.h" +#include "theory/substitutions.h" +#include + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace arith { + +Node ArithIteUtils::applyReduceVariablesInItes(Node n){ + NodeBuilder<> nb(n.getKind()); + if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { + nb << (n.getOperator()); + } + for(Node::iterator it = n.begin(), end = n.end(); it != end; ++it){ + nb << reduceVariablesInItes(*it); + } + Node res = nb; + return res; +} + +Node ArithIteUtils::reduceVariablesInItes(Node n){ + using namespace CVC4::kind; + if(d_reduceVar.find(n) != d_reduceVar.end()){ + Node res = d_reduceVar[n]; + return res.isNull() ? n : res; + } + + switch(n.getKind()){ + case ITE:{ + Node c = n[0], t = n[1], e = n[2]; + if(n.getType().isReal()){ + Node rc = reduceVariablesInItes(c); + Node rt = reduceVariablesInItes(t); + Node re = reduceVariablesInItes(e); + + Node vt = d_varParts[t]; + Node ve = d_varParts[e]; + Node vpite = (vt == ve) ? vt : Node::null(); + + if(vpite.isNull()){ + Node rite = rc.iteNode(rt, re); + // do not apply + d_reduceVar[n] = rite; + d_constants[n] = mkRationalNode(Rational(0)); + d_varParts[n] = rite; // treat the ite as a variable + return rite; + }else{ + NodeManager* nm = NodeManager::currentNM(); + Node constantite = rc.iteNode(d_constants[t], d_constants[e]); + Node sum = nm->mkNode(kind::PLUS, vpite, constantite); + d_reduceVar[n] = sum; + d_constants[n] = constantite; + d_varParts[n] = vpite; + return sum; + } + }else{ // non-arith ite + if(!d_contains.containsTermITE(n)){ + // don't bother adding to d_reduceVar + return n; + }else{ + Node newIte = applyReduceVariablesInItes(n); + d_reduceVar[n] = (n == newIte) ? Node::null(): newIte; + return newIte; + } + } + }break; + default: + if(n.getType().isReal() && Polynomial::isMember(n)){ + Node newn = Node::null(); + if(!d_contains.containsTermITE(n)){ + newn = n; + }else if(n.getNumChildren() > 0){ + newn = applyReduceVariablesInItes(n); + newn = Rewriter::rewrite(newn); + Assert(Polynomial::isMember(newn)); + }else{ + newn = n; + } + + Polynomial p = Polynomial::parsePolynomial(newn); + if(p.isConstant()){ + d_constants[n] = newn; + d_varParts[n] = mkRationalNode(Rational(0)); + // don't bother adding to d_reduceVar + return newn; + }else if(!p.containsConstant()){ + d_constants[n] = mkRationalNode(Rational(0)); + d_varParts[n] = newn; + d_reduceVar[n] = p.getNode(); + return p.getNode(); + }else{ + Monomial mc = p.getHead(); + d_constants[n] = mc.getConstant().getNode(); + d_varParts[n] = p.getTail().getNode(); + d_reduceVar[n] = newn; + return newn; + } + }else{ + if(!d_contains.containsTermITE(n)){ + return n; + } + if(n.getNumChildren() > 0){ + Node res = applyReduceVariablesInItes(n); + d_reduceVar[n] = res; + return res; + }else{ + return n; + } + } + break; + } + Unreachable(); + return Node::null(); +} + +ArithIteUtils::ArithIteUtils(ContainsTermITEVistor& contains, + context::Context* uc, + TheoryModel* model) + : d_contains(contains) + , d_subs(NULL) + , d_model(model) + , d_one(1) + , d_subcount(uc, 0) + , d_skolems(uc) + , d_implies() + , d_skolemsAdded() + , d_orBinEqs() +{ + d_subs = new SubstitutionMap(uc); +} + +ArithIteUtils::~ArithIteUtils(){ + delete d_subs; + d_subs = NULL; +} + +void ArithIteUtils::clear(){ + d_reduceVar.clear(); + d_constants.clear(); + d_varParts.clear(); +} + +const Integer& ArithIteUtils::gcdIte(Node n){ + if(d_gcds.find(n) != d_gcds.end()){ + return d_gcds[n]; + } + if(n.getKind() == kind::CONST_RATIONAL){ + const Rational& q = n.getConst(); + if(q.isIntegral()){ + d_gcds[n] = q.getNumerator(); + return d_gcds[n]; + }else{ + return d_one; + } + }else if(n.getKind() == kind::ITE && n.getType().isReal()){ + const Integer& tgcd = gcdIte(n[1]); + if(tgcd.isOne()){ + d_gcds[n] = d_one; + return d_one; + }else{ + const Integer& egcd = gcdIte(n[2]); + Integer ite_gcd = tgcd.gcd(egcd); + d_gcds[n] = ite_gcd; + return d_gcds[n]; + } + } + return d_one; +} + +Node ArithIteUtils::reduceIteConstantIteByGCD_rec(Node n, const Rational& q){ + if(n.isConst()){ + Assert(n.getKind() == kind::CONST_RATIONAL); + return mkRationalNode(n.getConst() * q); + }else{ + Assert(n.getKind() == kind::ITE); + Assert(n.getType().isInteger()); + Node rc = reduceConstantIteByGCD(n[0]); + Node rt = reduceIteConstantIteByGCD_rec(n[1], q); + Node re = reduceIteConstantIteByGCD_rec(n[2], q); + return rc.iteNode(rt, re); + } +} + +Node ArithIteUtils::reduceIteConstantIteByGCD(Node n){ + Assert(n.getKind() == kind::ITE); + Assert(n.getType().isReal()); + const Integer& gcd = gcdIte(n); + if(gcd.isOne()){ + Node newIte = reduceConstantIteByGCD(n[0]).iteNode(n[1],n[2]); + d_reduceGcd[n] = newIte; + return newIte; + }else if(gcd.isZero()){ + Node zeroNode = mkRationalNode(Rational(0)); + d_reduceGcd[n] = zeroNode; + return zeroNode; + }else{ + Rational divBy(Integer(1), gcd); + Node redite = reduceIteConstantIteByGCD_rec(n, divBy); + Node gcdNode = mkRationalNode(Rational(gcd)); + Node multIte = NodeManager::currentNM()->mkNode(kind::MULT, gcdNode, redite); + d_reduceGcd[n] = multIte; + return multIte; + } +} + +Node ArithIteUtils::reduceConstantIteByGCD(Node n){ + if(d_reduceGcd.find(n) != d_reduceGcd.end()){ + return d_reduceGcd[n]; + } + if(n.getKind() == kind::ITE && n.getType().isReal()){ + return reduceIteConstantIteByGCD(n); + } + + if(n.getNumChildren() > 0){ + NodeBuilder<> nb(n.getKind()); + if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { + nb << (n.getOperator()); + } + bool anychange = false; + for(Node::iterator it = n.begin(), end = n.end(); it != end; ++it){ + Node child = *it; + Node redchild = reduceConstantIteByGCD(child); + anychange = anychange || (child != redchild); + nb << redchild; + } + if(anychange){ + Node res = nb; + d_reduceGcd[n] = res; + return res; + }else{ + d_reduceGcd[n] = n; + return n; + } + }else{ + return n; + } +} + +unsigned ArithIteUtils::getSubCount() const{ + return d_subcount; +} + +void ArithIteUtils::addSubstitution(TNode f, TNode t){ + Debug("arith::ite") << "adding " << f << " -> " << t << endl; + d_subcount = d_subcount + 1; + d_subs->addSubstitution(f, t); + d_model->addSubstitution(f, t); +} + +Node ArithIteUtils::applySubstitutions(TNode f){ + return d_subs->apply(f); +} + +Node ArithIteUtils::selectForCmp(Node n) const{ + if(n.getKind() == kind::ITE){ + if(d_skolems.find(n[0]) != d_skolems.end()){ + return selectForCmp(n[1]); + } + } + return n; +} + +void ArithIteUtils::learnSubstitutions(const std::vector& assertions){ + for(size_t i=0, N=assertions.size(); i < N; ++i){ + collectAssertions(assertions[i]); + } + bool solvedSomething; + do{ + solvedSomething = false; + size_t readPos = 0, writePos = 0, N = d_orBinEqs.size(); + for(; readPos < N; readPos++){ + Node curr = d_orBinEqs[readPos]; + bool solved = solveBinOr(curr); + if(solved){ + solvedSomething = true; + }else{ + // didn't solve, push back + d_orBinEqs[writePos] = curr; + writePos++; + } + } + Assert(writePos <= N); + d_orBinEqs.resize(writePos); + }while(solvedSomething); + + for(size_t i = 0, N=d_skolemsAdded.size(); i (not x) y) + // (=> (not y) x) + + Node xneg = x.negate(); + Node yneg = y.negate(); + d_implies[xneg].insert(y); + d_implies[yneg].insert(x); +} + +void ArithIteUtils::collectAssertions(TNode assertion){ + if(assertion.getKind() == kind::OR){ + if(assertion.getNumChildren() == 2){ + TNode left = assertion[0], right = assertion[1]; + addImplications(left, right); + if(left.getKind() == kind::EQUAL && right.getKind() == kind::EQUAL){ + if(left[0].getType().isInteger() && right[0].getType().isInteger()){ + d_orBinEqs.push_back(assertion); + } + } + } + }else if(assertion.getKind() == kind::AND){ + for(unsigned i=0, N=assertion.getNumChildren(); i < N; ++i){ + collectAssertions(assertion[i]); + } + } +} + +Node ArithIteUtils::findIteCnd(TNode tb, TNode fb) const{ + Node negtb = tb.negate(); + Node negfb = fb.negate(); + ImpMap::const_iterator ti = d_implies.find(negtb); + ImpMap::const_iterator fi = d_implies.find(negfb); + + if(ti != d_implies.end() && fi != d_implies.end()){ + const std::set& negtimp = ti->second; + const std::set& negfimp = fi->second; + + // (or (not x) y) + // (or x z) + // (or y z) + // --- + // (ite x y z) return x + // --- + // (not y) => (not x) + // (not z) => x + std::set::const_iterator ci = negtimp.begin(), cend = negtimp.end(); + for(; ci != cend; ci++){ + Node impliedByNotTB = *ci; + Node impliedByNotTBNeg = impliedByNotTB.negate(); + if(negfimp.find(impliedByNotTBNeg) != negfimp.end()){ + return impliedByNotTBNeg; // implies tb + } + } + } + + return Node::null(); +} + +bool ArithIteUtils::solveBinOr(TNode binor){ + Assert(binor.getKind() == kind::OR); + Assert(binor.getNumChildren() == 2); + Assert(binor[0].getKind() == kind::EQUAL); + Assert(binor[1].getKind() == kind::EQUAL); + + Node n = applySubstitutions(binor); + Assert(n.getKind() == kind::OR); + Assert(binor.getNumChildren() == 2); + TNode l = n[0]; + TNode r = n[1]; + + Assert(l.getKind() == kind::EQUAL); + Assert(r.getKind() == kind::EQUAL); + + Debug("arith::ite") << "bin or " << n << endl; + + bool lArithEq = l.getKind() == kind::EQUAL && l[0].getType().isInteger(); + bool rArithEq = r.getKind() == kind::EQUAL && r[0].getType().isInteger(); + + if(lArithEq && rArithEq){ + TNode sel = Node::null(); + TNode otherL = Node::null(); + TNode otherR = Node::null(); + if(l[0] == r[0]) { + sel = l[0]; otherL = l[1]; otherR = r[1]; + }else if(l[0] == r[1]){ + sel = l[0]; otherL = l[1]; otherR = r[0]; + }else if(l[1] == r[0]){ + sel = l[1]; otherL = l[0]; otherR = r[1]; + }else if(l[1] == r[1]){ + sel = l[1]; otherL = l[0]; otherR = r[0]; + } + Debug("arith::ite") << "selected " << sel << endl; + if(sel.isVar() && sel.getKind() != kind::SKOLEM){ + + Debug("arith::ite") << "others l:" << otherL << " r " << otherR << endl; + Node useForCmpL = selectForCmp(otherL); + Node useForCmpR = selectForCmp(otherR); + + Assert(Polynomial::isMember(sel)); + Assert(Polynomial::isMember(useForCmpL)); + Assert(Polynomial::isMember(useForCmpR)); + Polynomial lside = Polynomial::parsePolynomial( useForCmpL ); + Polynomial rside = Polynomial::parsePolynomial( useForCmpR ); + Polynomial diff = lside-rside; + + Debug("arith::ite") << "diff: " << diff.getNode() << endl; + if(diff.isConstant()){ + // a: (sel = otherL) or (sel = otherR), otherL-otherR = c + + NodeManager* nm = NodeManager::currentNM(); + + Node cnd = findIteCnd(binor[0], binor[1]); + + Node sk = nm->mkSkolem("deor$$", nm->booleanType()); + Node ite = sk.iteNode(otherL, otherR); + d_skolems.insert(sk, cnd); + d_skolemsAdded.push_back(sk); + addSubstitution(sel, ite); + return true; + } + } + } + return false; +} + + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/arith_ite_utils.h b/src/theory/arith/arith_ite_utils.h new file mode 100644 index 000000000..fab0f32cb --- /dev/null +++ b/src/theory/arith/arith_ite_utils.h @@ -0,0 +1,100 @@ + + + + + +// Pass 1: label the ite as (constant) or (+ constant variable) + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H +#define __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H + +#include "expr/node.h" +#include +#include +#include "context/cdo.h" +#include "context/cdtrail_hashmap.h" + +namespace CVC4 { +namespace theory { +class ContainsTermITEVistor; +class SubstitutionMap; +class TheoryModel; + +namespace arith { + +class ArithIteUtils { + ContainsTermITEVistor& d_contains; + SubstitutionMap* d_subs; + TheoryModel* d_model; + + typedef std::hash_map NodeMap; + // cache for reduce vars + NodeMap d_reduceVar; // if reduceVars[n].isNull(), treat reduceVars[n] == n + + // reduceVars[n] = d_constants[n] + d_varParts[n] + NodeMap d_constants; // d_constants[n] is a constant ite tree + NodeMap d_varParts; // d_varParts[n] is a polynomial + + + NodeMap d_reduceGcd; + typedef std::hash_map NodeIntegerMap; + NodeIntegerMap d_gcds; + + Integer d_one; + + context::CDO d_subcount; + typedef context::CDTrailHashMap CDNodeMap; + CDNodeMap d_skolems; + + typedef std::map > ImpMap; + ImpMap d_implies; + + std::vector d_skolemsAdded; + + std::vector d_orBinEqs; + +public: + ArithIteUtils(ContainsTermITEVistor& contains, + context::Context* userContext, + TheoryModel* model); + ~ArithIteUtils(); + + //(ite ?v_2 ?v_1 (ite ?v_3 (- ?v_1 128) (- ?v_1 256))) + + /** removes common sums variables sums from term ites. */ + Node reduceVariablesInItes(Node n); + + Node reduceConstantIteByGCD(Node n); + + void clear(); + + Node applySubstitutions(TNode f); + unsigned getSubCount() const; + + void learnSubstitutions(const std::vector& assertions); + +private: + /* applies this to all children of n and constructs the result */ + Node applyReduceVariablesInItes(Node n); + + const Integer& gcdIte(Node n); + Node reduceIteConstantIteByGCD_rec(Node n, const Rational& q); + Node reduceIteConstantIteByGCD(Node n); + + void addSubstitution(TNode f, TNode t); + Node selectForCmp(Node n) const; + + void collectAssertions(TNode assertion); + void addImplications(Node x, Node y); + Node findIteCnd(TNode tb, TNode fb) const; + bool solveBinOr(TNode binor); + +}; /* class ArithIteUtils */ + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H */ diff --git a/src/theory/arith/arith_rewriter.cpp b/src/theory/arith/arith_rewriter.cpp index e1cab0356..5aa904aed 100644 --- a/src/theory/arith/arith_rewriter.cpp +++ b/src/theory/arith/arith_rewriter.cpp @@ -222,36 +222,94 @@ RewriteResponse ArithRewriter::postRewriteTerm(TNode t){ RewriteResponse ArithRewriter::preRewriteMult(TNode t){ Assert(t.getKind()== kind::MULT); - // Rewrite multiplications with a 0 argument and to 0 - Rational qZero(0); + if(t.getNumChildren() == 2){ + if(t[0].getKind() == kind::CONST_RATIONAL + && t[0].getConst().isOne()){ + return RewriteResponse(REWRITE_DONE, t[1]); + } + if(t[1].getKind() == kind::CONST_RATIONAL + && t[1].getConst().isOne()){ + return RewriteResponse(REWRITE_DONE, t[0]); + } + } + // Rewrite multiplications with a 0 argument and to 0 for(TNode::iterator i = t.begin(); i != t.end(); ++i) { if((*i).getKind() == kind::CONST_RATIONAL) { - if((*i).getConst() == qZero) { - return RewriteResponse(REWRITE_DONE, mkRationalNode(qZero)); + if((*i).getConst().isZero()) { + TNode zero = (*i); + return RewriteResponse(REWRITE_DONE, zero); } } } return RewriteResponse(REWRITE_DONE, t); } + +static bool canFlatten(Kind k, TNode t){ + for(TNode::iterator i = t.begin(); i != t.end(); ++i) { + TNode child = *i; + if(child.getKind() == k){ + return true; + } + } + return false; +} + +static void flatten(std::vector& pb, Kind k, TNode t){ + if(t.getKind() == k){ + for(TNode::iterator i = t.begin(); i != t.end(); ++i) { + TNode child = *i; + if(child.getKind() == k){ + flatten(pb, k, child); + }else{ + pb.push_back(child); + } + } + }else{ + pb.push_back(t); + } +} + +static Node flatten(Kind k, TNode t){ + std::vector pb; + flatten(pb, k, t); + Assert(pb.size() >= 2); + return NodeManager::currentNM()->mkNode(k, pb); +} + RewriteResponse ArithRewriter::preRewritePlus(TNode t){ Assert(t.getKind()== kind::PLUS); - return RewriteResponse(REWRITE_DONE, t); + if(canFlatten(kind::PLUS, t)){ + return RewriteResponse(REWRITE_DONE, flatten(kind::PLUS, t)); + }else{ + return RewriteResponse(REWRITE_DONE, t); + } } RewriteResponse ArithRewriter::postRewritePlus(TNode t){ Assert(t.getKind()== kind::PLUS); - Polynomial res = Polynomial::mkZero(); + std::vector monomials; + std::vector polynomials; for(TNode::iterator i = t.begin(), end = t.end(); i != end; ++i){ - Node curr = *i; - Polynomial currPoly = Polynomial::parsePolynomial(curr); + TNode curr = *i; + if(Monomial::isMember(curr)){ + monomials.push_back(Monomial::parseMonomial(curr)); + }else{ + polynomials.push_back(Polynomial::parsePolynomial(curr)); + } + } - res = res + currPoly; + if(!monomials.empty()){ + Monomial::sort(monomials); + Monomial::combineAdjacentMonomials(monomials); + polynomials.push_back(Polynomial::mkPolynomial(monomials)); } + Polynomial res = Polynomial::sumPolynomials(polynomials); + return RewriteResponse(REWRITE_DONE, res.getNode()); } diff --git a/src/theory/arith/arith_static_learner.cpp b/src/theory/arith/arith_static_learner.cpp index 8f6f75295..3854188e0 100644 --- a/src/theory/arith/arith_static_learner.cpp +++ b/src/theory/arith/arith_static_learner.cpp @@ -21,6 +21,8 @@ #include "theory/arith/arith_static_learner.h" #include "theory/arith/options.h" +#include "theory/arith/normal_form.h" + #include "expr/expr.h" #include "expr/convenience_node_builders.h" @@ -38,7 +40,11 @@ ArithStaticLearner::ArithStaticLearner(context::Context* userContext) : d_minMap(userContext), d_maxMap(userContext), d_statistics() -{} +{ +} + +ArithStaticLearner::~ArithStaticLearner(){ +} ArithStaticLearner::Statistics::Statistics(): d_iteMinMaxApplications("theory::arith::iteMinMaxApplications", 0), @@ -98,9 +104,6 @@ void ArithStaticLearner::staticLearning(TNode n, NodeBuilder<>& learned){ } - - - void ArithStaticLearner::process(TNode n, NodeBuilder<>& learned, const TNodeSet& defTrue){ Debug("arith::static") << "===================== looking at " << n << endl; @@ -116,49 +119,12 @@ void ArithStaticLearner::process(TNode n, NodeBuilder<>& learned, const TNodeSet iteConstant(n, learned); } break; + case CONST_RATIONAL: // Mark constants as minmax d_minMap.insert(n, n.getConst()); d_maxMap.insert(n, n.getConst()); break; - case OR: { - // Look for things like "x = 0 OR x = 1" (that are defTrue) and - // turn them into a pseudoboolean. We catch "x >= 0 - if(defTrue.find(n) == defTrue.end() || - n.getNumChildren() != 2 || - n[0].getKind() != EQUAL || - n[1].getKind() != EQUAL) { - break; - } - Node var, c1, c2; - if(n[0][0].isVar() && - n[0][1].isConst()) { - var = n[0][0]; - c1 = n[0][1]; - } else if(n[0][1].isVar() && - n[0][0].isConst()) { - var = n[0][1]; - c1 = n[0][0]; - } else { - break; - } - if(!var.getType().isInteger() || - !c1.getType().isReal()) { - break; - } - if(var == n[1][0]) { - c2 = n[1][1]; - } else if(var == n[1][1]) { - c2 = n[1][0]; - } else { - break; - } - if(!c2.getType().isReal()) { - break; - } - - break; - } default: // Do nothing break; } diff --git a/src/theory/arith/arith_static_learner.h b/src/theory/arith/arith_static_learner.h index d8407eeba..2615cdcd6 100644 --- a/src/theory/arith/arith_static_learner.h +++ b/src/theory/arith/arith_static_learner.h @@ -25,7 +25,6 @@ #include "theory/arith/arith_utilities.h" #include "context/context.h" -#include "context/cdlist.h" #include "context/cdtrail_hashmap.h" #include @@ -45,6 +44,7 @@ private: public: ArithStaticLearner(context::Context* userContext); + ~ArithStaticLearner(); void staticLearning(TNode n, NodeBuilder<>& learned); void addBound(TNode n); diff --git a/src/theory/arith/arith_utilities.h b/src/theory/arith/arith_utilities.h index 11626c1de..98aa43e71 100644 --- a/src/theory/arith/arith_utilities.h +++ b/src/theory/arith/arith_utilities.h @@ -238,6 +238,25 @@ inline Node flattenAnd(Node n){ return NodeManager::currentNM()->mkNode(kind::AND, out); } +inline Node getIdentity(Kind k){ + switch(k){ + case kind::AND: + return NodeManager::currentNM()->mkConst(true); + case kind::PLUS: + return NodeManager::currentNM()->mkConst(Rational(1)); + default: + Unreachable(); + } +} + +inline Node safeConstructNary(NodeBuilder<>& nb){ + switch(nb.getNumChildren()){ + case 0: return getIdentity(nb.getKind()); + case 1: return nb[0]; + default: return (Node)nb; + } +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp index d4a445b70..c4b64682f 100644 --- a/src/theory/arith/callbacks.cpp +++ b/src/theory/arith/callbacks.cpp @@ -22,6 +22,9 @@ namespace CVC4 { namespace theory { namespace arith { +SetupLiteralCallBack::SetupLiteralCallBack(TheoryArithPrivate& ta) + : d_arith(ta) +{} void SetupLiteralCallBack::operator()(TNode lit){ TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit; if(!d_arith.isSetup(atom)){ @@ -29,30 +32,72 @@ void SetupLiteralCallBack::operator()(TNode lit){ } } +DeltaComputeCallback::DeltaComputeCallback(const TheoryArithPrivate& ta) + : d_ta(ta) +{} Rational DeltaComputeCallback::operator()() const{ return d_ta.deltaValueForTotalOrder(); } +TempVarMalloc::TempVarMalloc(TheoryArithPrivate& ta) +: d_ta(ta) +{} ArithVar TempVarMalloc::request(){ Node skolem = mkRealSkolem("tmpVar"); - return d_ta.requestArithVar(skolem, false); + return d_ta.requestArithVar(skolem, false, true); } void TempVarMalloc::release(ArithVar v){ d_ta.releaseArithVar(v); } +BasicVarModelUpdateCallBack::BasicVarModelUpdateCallBack(TheoryArithPrivate& ta) + : d_ta(ta) +{} void BasicVarModelUpdateCallBack::operator()(ArithVar x){ d_ta.signal(x); } -void RaiseConflict::operator()(Node n){ - d_ta.raiseConflict(n); +RaiseConflict::RaiseConflict(TheoryArithPrivate& ta, ConstraintCPVec& buf ) + : d_ta(ta) + , d_construction(buf) +{} + +/* Adds a constraint to the constraint under construction. */ +void RaiseConflict::addConstraint(ConstraintCP c){ + d_construction.push_back(c); +} +/* Turns the vector under construction into a conflict */ +void RaiseConflict::commitConflict(){ + Assert(!d_construction.empty()); + sendConflict(d_construction); + d_construction.clear(); +} + +void RaiseConflict::sendConflict(const ConstraintCPVec& vec){ + d_ta.raiseConflict(vec); +} + +/* If you are not an equality engine, don't use this! */ +void RaiseConflict::blackBoxConflict(Node n){ + d_ta.blackBoxConflict(n); } + +BoundCountingLookup::BoundCountingLookup(TheoryArithPrivate& ta) +: d_ta(ta) +{} + const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{ return d_ta.boundsInfo(basic); } +BoundCounts BoundCountingLookup::atBounds(ArithVar basic) const{ + return boundsInfo(basic).atBounds(); +} +BoundCounts BoundCountingLookup::hasBounds(ArithVar basic) const { + return boundsInfo(basic).hasBounds(); +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h index fd9369bf1..c4c79ad75 100644 --- a/src/theory/arith/callbacks.h +++ b/src/theory/arith/callbacks.h @@ -24,6 +24,7 @@ #include "theory/arith/theory_arith_private_forward.h" #include "theory/arith/arithvar.h" #include "theory/arith/bound_counts.h" +#include "theory/arith/constraint_forward.h" namespace CVC4 { namespace theory { @@ -67,7 +68,7 @@ class SetupLiteralCallBack : public TNodeCallBack { private: TheoryArithPrivate& d_arith; public: - SetupLiteralCallBack(TheoryArithPrivate& ta) : d_arith(ta){} + SetupLiteralCallBack(TheoryArithPrivate& ta); void operator()(TNode lit); }; @@ -75,7 +76,7 @@ class DeltaComputeCallback : public RationalCallBack { private: const TheoryArithPrivate& d_ta; public: - DeltaComputeCallback(const TheoryArithPrivate& ta) : d_ta(ta){} + DeltaComputeCallback(const TheoryArithPrivate& ta); Rational operator()() const; }; @@ -83,7 +84,7 @@ class BasicVarModelUpdateCallBack : public ArithVarCallBack{ private: TheoryArithPrivate& d_ta; public: - BasicVarModelUpdateCallBack(TheoryArithPrivate& ta) : d_ta(ta) {} + BasicVarModelUpdateCallBack(TheoryArithPrivate& ta); void operator()(ArithVar x); }; @@ -91,31 +92,37 @@ class TempVarMalloc : public ArithVarMalloc { private: TheoryArithPrivate& d_ta; public: - TempVarMalloc(TheoryArithPrivate& ta) : d_ta(ta) {} + TempVarMalloc(TheoryArithPrivate& ta); ArithVar request(); void release(ArithVar v); }; -class RaiseConflict : public NodeCallBack { +class RaiseConflict { private: TheoryArithPrivate& d_ta; + ConstraintCPVec& d_construction; public: - RaiseConflict(TheoryArithPrivate& ta) : d_ta(ta) {} - void operator()(Node n); + RaiseConflict(TheoryArithPrivate& ta, ConstraintCPVec& d_construction); + + /* Adds a constraint to the constraint under construction. */ + void addConstraint(ConstraintCP c); + /* Turns the vector under construction into a conflict */ + void commitConflict(); + + void sendConflict(const ConstraintCPVec& vec); + + /* If you are not an equality engine, don't use this! */ + void blackBoxConflict(Node n); }; class BoundCountingLookup { private: TheoryArithPrivate& d_ta; public: - BoundCountingLookup(TheoryArithPrivate& ta) : d_ta(ta) {} + BoundCountingLookup(TheoryArithPrivate& ta); const BoundsInfo& boundsInfo(ArithVar basic) const; - BoundCounts atBounds(ArithVar basic) const{ - return boundsInfo(basic).atBounds(); - } - BoundCounts hasBounds(ArithVar basic) const { - return boundsInfo(basic).hasBounds(); - } + BoundCounts atBounds(ArithVar basic) const; + BoundCounts hasBounds(ArithVar basic) const; }; }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp index a828b9e7f..d1d11c86e 100644 --- a/src/theory/arith/congruence_manager.cpp +++ b/src/theory/arith/congruence_manager.cpp @@ -65,11 +65,105 @@ ArithCongruenceManager::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_conflicts); } +ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm) + : d_acm(acm) +{} + +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerEquality(TNode equality, bool value) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + return d_acm.propagate(equality); + } else { + return d_acm.propagate(equality.notNode()); + } +} +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(TNode predicate, bool value) { + Unreachable(); +} + +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + return d_acm.propagate(t1.eqNode(t2)); + } else { + return d_acm.propagate(t1.eqNode(t2).notNode()); + } +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; + if (t1.getKind() == kind::CONST_BOOLEAN) { + d_acm.propagate(t1.iffNode(t2)); + } else { + d_acm.propagate(t1.eqNode(t2)); + } +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPreMerge(TNode t1, TNode t2) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPostMerge(TNode t1, TNode t2) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { +} + +void ArithCongruenceManager::raiseConflict(Node conflict){ + Assert(!inConflict()); + Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; + d_inConflict.raise(); + d_raiseConflict.blackBoxConflict(conflict); +} +bool ArithCongruenceManager::inConflict() const{ + return d_inConflict.isRaised(); +} + +bool ArithCongruenceManager::hasMorePropagations() const { + return !d_propagatations.empty(); +} +const Node ArithCongruenceManager::getNextPropagation() { + Assert(hasMorePropagations()); + Node prop = d_propagatations.front(); + d_propagatations.dequeue(); + return prop; +} + +bool ArithCongruenceManager::canExplain(TNode n) const { + return d_explanationMap.find(n) != d_explanationMap.end(); +} + void ArithCongruenceManager::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_ee.setMasterEqualityEngine(eq); } -void ArithCongruenceManager::watchedVariableIsZero(Constraint lb, Constraint ub){ +Node ArithCongruenceManager::externalToInternal(TNode n) const{ + Assert(canExplain(n)); + ExplainMap::const_iterator iter = d_explanationMap.find(n); + size_t pos = (*iter).second; + return d_propagatations[pos]; +} + +void ArithCongruenceManager::pushBack(TNode n){ + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} +void ArithCongruenceManager::pushBack(TNode n, TNode r){ + d_explanationMap.insert(r, d_propagatations.size()); + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} +void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){ + d_explanationMap.insert(w, d_propagatations.size()); + d_explanationMap.insert(r, d_propagatations.size()); + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} + +void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){ Assert(lb->isLowerBound()); Assert(ub->isUpperBound()); Assert(lb->getVariable() == ub->getVariable()); @@ -79,13 +173,13 @@ void ArithCongruenceManager::watchedVariableIsZero(Constraint lb, Constraint ub) ++(d_statistics.d_watchedVariableIsZero); ArithVar s = lb->getVariable(); - Node reason = ConstraintValue::explainConflict(lb,ub); + Node reason = Constraint_::externalExplainByAssertions(lb,ub); d_keepAlive.push_back(reason); assertionToEqualityEngine(true, s, reason); } -void ArithCongruenceManager::watchedVariableIsZero(Constraint eq){ +void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){ Assert(eq->isEquality()); Assert(eq->getValue().sgn() == 0); @@ -96,20 +190,20 @@ void ArithCongruenceManager::watchedVariableIsZero(Constraint eq){ //Explain for conflict is correct as these proofs are generated //and stored eagerly //These will be safe for propagation later as well - Node reason = eq->explainForConflict(); + Node reason = eq->externalExplainByAssertions(); d_keepAlive.push_back(reason); assertionToEqualityEngine(true, s, reason); } -void ArithCongruenceManager::watchedVariableCannotBeZero(Constraint c){ +void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){ ++(d_statistics.d_watchedVariableIsNotZero); ArithVar s = c->getVariable(); //Explain for conflict is correct as these proofs are generated and stored eagerly //These will be safe for propagation later as well - Node reason = c->explainForConflict(); + Node reason = c->externalExplainByAssertions(); d_keepAlive.push_back(reason); assertionToEqualityEngine(false, s, reason); @@ -142,7 +236,7 @@ bool ArithCongruenceManager::propagate(TNode x){ Assert(rewritten.getKind() != kind::CONST_BOOLEAN); - Constraint c = d_constraintDatabase.lookup(rewritten); + ConstraintP c = d_constraintDatabase.lookup(rewritten); if(c == NullConstraint){ //using setup as there may not be a corresponding congruence literal yet d_setupLiteral(rewritten); @@ -158,7 +252,8 @@ bool ArithCongruenceManager::propagate(TNode x){ if(c->negationHasProof()){ Node expC = explainInternal(x); - Node neg = c->getNegation()->explainForConflict(); + ConstraintCP negC = c->getNegation(); + Node neg = negC->externalExplainByAssertions(); Node conf = expC.andNode(neg); Node final = flattenAnd(conf); @@ -288,7 +383,7 @@ void ArithCongruenceManager::assertionToEqualityEngine(bool isEquality, ArithVar } } -void ArithCongruenceManager::equalsConstant(Constraint c){ +void ArithCongruenceManager::equalsConstant(ConstraintCP c){ Assert(c->isEquality()); ++(d_statistics.d_equalsConstantCalls); @@ -303,13 +398,13 @@ void ArithCongruenceManager::equalsConstant(Constraint c){ Node eq = xAsNode.eqNode(asRational); d_keepAlive.push_back(eq); - Node reason = c->explainForConflict(); + Node reason = c->externalExplainByAssertions(); d_keepAlive.push_back(reason); d_ee.assertEquality(eq, true, reason); } -void ArithCongruenceManager::equalsConstant(Constraint lb, Constraint ub){ +void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){ Assert(lb->isLowerBound()); Assert(ub->isUpperBound()); Assert(lb->getVariable() == ub->getVariable()); @@ -319,7 +414,7 @@ void ArithCongruenceManager::equalsConstant(Constraint lb, Constraint ub){ << ub << std::endl; ArithVar x = lb->getVariable(); - Node reason = ConstraintValue::explainConflict(lb,ub); + Node reason = Constraint_::externalExplainByAssertions(lb,ub); Node xAsNode = d_avariables.asNode(x); Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart()); diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index b4e009169..8e369ff9a 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -57,43 +57,19 @@ private: private: ArithCongruenceManager& d_acm; public: - ArithCongruenceNotify(ArithCongruenceManager& acm): d_acm(acm) {} - - bool eqNotifyTriggerEquality(TNode equality, bool value) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; - if (value) { - return d_acm.propagate(equality); - } else { - return d_acm.propagate(equality.notNode()); - } - } - - bool eqNotifyTriggerPredicate(TNode predicate, bool value) { - Unreachable(); - } - - bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; - if (value) { - return d_acm.propagate(t1.eqNode(t2)); - } else { - return d_acm.propagate(t1.eqNode(t2).notNode()); - } - } - - void eqNotifyConstantTermMerge(TNode t1, TNode t2) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; - if (t1.getKind() == kind::CONST_BOOLEAN) { - d_acm.propagate(t1.iffNode(t2)); - } else { - d_acm.propagate(t1.eqNode(t2)); - } - } - - void eqNotifyNewClass(TNode t) { } - void eqNotifyPreMerge(TNode t1, TNode t2) { } - void eqNotifyPostMerge(TNode t1, TNode t2) { } - void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } + ArithCongruenceNotify(ArithCongruenceManager& acm); + + bool eqNotifyTriggerEquality(TNode equality, bool value); + + bool eqNotifyTriggerPredicate(TNode predicate, bool value); + + bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value); + + void eqNotifyConstantTermMerge(TNode t1, TNode t2); + void eqNotifyNewClass(TNode t); + void eqNotifyPreMerge(TNode t1, TNode t2); + void eqNotifyPostMerge(TNode t1, TNode t2); + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason); }; ArithCongruenceNotify d_notify; @@ -117,66 +93,27 @@ private: eq::EqualityEngine d_ee; - void raiseConflict(Node conflict){ - Assert(!inConflict()); - Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; - d_inConflict.raise(); - d_raiseConflict(conflict); - } + void raiseConflict(Node conflict); public: - bool inConflict() const{ - return d_inConflict.isRaised(); - }; + bool inConflict() const; - bool hasMorePropagations() const { - return !d_propagatations.empty(); - } + bool hasMorePropagations() const; - const Node getNextPropagation() { - Assert(hasMorePropagations()); - Node prop = d_propagatations.front(); - d_propagatations.dequeue(); - return prop; - } + const Node getNextPropagation(); - bool canExplain(TNode n) const { - return d_explanationMap.find(n) != d_explanationMap.end(); - } + bool canExplain(TNode n) const; void setMasterEqualityEngine(eq::EqualityEngine* eq); private: - Node externalToInternal(TNode n) const{ - Assert(canExplain(n)); - ExplainMap::const_iterator iter = d_explanationMap.find(n); - size_t pos = (*iter).second; - return d_propagatations[pos]; - } - - void pushBack(TNode n){ - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); - - ++(d_statistics.d_propagations); - } - - void pushBack(TNode n, TNode r){ - d_explanationMap.insert(r, d_propagatations.size()); - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); + Node externalToInternal(TNode n) const; - ++(d_statistics.d_propagations); - } + void pushBack(TNode n); - void pushBack(TNode n, TNode r, TNode w){ - d_explanationMap.insert(w, d_propagatations.size()); - d_explanationMap.insert(r, d_propagatations.size()); - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); + void pushBack(TNode n, TNode r); - ++(d_statistics.d_propagations); - } + void pushBack(TNode n, TNode r, TNode w); bool propagate(TNode x); void explain(TNode literal, std::vector& assumptions); @@ -207,21 +144,21 @@ public: } /** Assert an equality. */ - void watchedVariableIsZero(Constraint eq); + void watchedVariableIsZero(ConstraintCP eq); /** Assert a conjunction from lb and ub. */ - void watchedVariableIsZero(Constraint lb, Constraint ub); + void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub); /** Assert that the value cannot be zero. */ - void watchedVariableCannotBeZero(Constraint c); + void watchedVariableCannotBeZero(ConstraintCP c); /** Assert that the value cannot be zero. */ - void watchedVariableCannotBeZero(Constraint c, Constraint d); + void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d); /** Assert that the value is congruent to a constant. */ - void equalsConstant(Constraint eq); - void equalsConstant(Constraint lb, Constraint ub); + void equalsConstant(ConstraintCP eq); + void equalsConstant(ConstraintCP lb, ConstraintCP ub); void addSharedTerm(Node x); diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index 78b9d3494..acbd4a04b 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -64,7 +64,7 @@ ConstraintType constraintTypeOfComparison(const Comparison& cmp){ } } -ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRational& v) +Constraint_::Constraint_(ArithVar x, ConstraintType t, const DeltaRational& v) : d_variable(x), d_type(t), d_value(v), @@ -72,7 +72,7 @@ ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRatio d_literal(Node::null()), d_negation(NullConstraint), d_canBePropagated(false), - _d_assertionOrder(AssertionOrderSentinel), + d_assertionOrder(AssertionOrderSentinel), d_witness(TNode::null()), d_proof(ProofIdSentinel), d_split(false), @@ -82,7 +82,7 @@ ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRatio } -std::ostream& operator<<(std::ostream& o, const Constraint c){ +std::ostream& operator<<(std::ostream& o, const ConstraintP c){ if(c == NullConstraint){ return o << "NullConstraint"; }else{ @@ -105,7 +105,7 @@ std::ostream& operator<<(std::ostream& o, const ConstraintType t){ } } -std::ostream& operator<<(std::ostream& o, const ConstraintValue& c){ +std::ostream& operator<<(std::ostream& o, const Constraint_& c){ o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue(); if(c.hasLiteral()){ o << "(node " << c.getLiteral() << ')'; @@ -143,11 +143,67 @@ std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){ return o << "}"; } -void ConstraintValue::debugPrint() const { +std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v){ + o << "[" << v.size() << "x"; + ConstraintCPVec::const_iterator i, end; + for(i=v.begin(), end=v.end(); i != end; ++i){ + ConstraintCP c = *i; + o << ", " << (*c); + } + o << "]"; + return o; +} + +void Constraint_::debugPrint() const { Message() << *this << endl; } -void ValueCollection::push_into(std::vector& vec) const { + +ValueCollection::ValueCollection() + : d_lowerBound(NullConstraint), + d_upperBound(NullConstraint), + d_equality(NullConstraint), + d_disequality(NullConstraint) +{} + +bool ValueCollection::hasLowerBound() const{ + return d_lowerBound != NullConstraint; +} + +bool ValueCollection::hasUpperBound() const{ + return d_upperBound != NullConstraint; +} + +bool ValueCollection::hasEquality() const{ + return d_equality != NullConstraint; +} + +bool ValueCollection::hasDisequality() const { + return d_disequality != NullConstraint; +} + +ConstraintP ValueCollection::getLowerBound() const { + Assert(hasLowerBound()); + return d_lowerBound; +} + +ConstraintP ValueCollection::getUpperBound() const { + Assert(hasUpperBound()); + return d_upperBound; +} + +ConstraintP ValueCollection::getEquality() const { + Assert(hasEquality()); + return d_equality; +} + +ConstraintP ValueCollection::getDisequality() const { + Assert(hasDisequality()); + return d_disequality; +} + + +void ValueCollection::push_into(std::vector& vec) const { Debug("arith::constraint") << "push_into " << *this << endl; if(hasEquality()){ vec.push_back(d_equality); @@ -163,7 +219,7 @@ void ValueCollection::push_into(std::vector& vec) const { } } -ValueCollection ValueCollection::mkFromConstraint(Constraint c){ +ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){ ValueCollection ret; Assert(ret.empty()); switch(c->getType()){ @@ -210,7 +266,7 @@ const DeltaRational& ValueCollection::getValue() const{ return nonNull()->getValue(); } -void ValueCollection::add(Constraint c){ +void ValueCollection::add(ConstraintP c){ Assert(c != NullConstraint); Assert(empty() || getVariable() == c->getVariable()); @@ -238,7 +294,7 @@ void ValueCollection::add(Constraint c){ } } -Constraint ValueCollection::getConstraintOfType(ConstraintType t) const{ +ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{ switch(t){ case LowerBound: Assert(hasLowerBound()); @@ -288,7 +344,7 @@ bool ValueCollection::empty() const{ hasDisequality()); } -Constraint ValueCollection::nonNull() const{ +ConstraintP ValueCollection::nonNull() const{ //This can be optimized by caching, but this is not necessary yet! /* "Premature optimization is the root of all evil." */ if(hasLowerBound()){ @@ -304,18 +360,18 @@ Constraint ValueCollection::nonNull() const{ } } -bool ConstraintValue::initialized() const { +bool Constraint_::initialized() const { return d_database != NULL; } -void ConstraintValue::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, Constraint negation){ +void Constraint_::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){ Assert(!initialized()); d_database = db; d_variablePosition = v; d_negation = negation; } -ConstraintValue::~ConstraintValue() { +Constraint_::~Constraint_() { Assert(safeToGarbageCollect()); if(initialized()){ @@ -336,12 +392,12 @@ ConstraintValue::~ConstraintValue() { } } -const ValueCollection& ConstraintValue::getValueCollection() const{ +const ValueCollection& Constraint_::getValueCollection() const{ return d_variablePosition->second; } -Constraint ConstraintValue::getCeiling() { - Debug("getCeiling") << "ConstraintValue::getCeiling on " << *this << endl; +ConstraintP Constraint_::getCeiling() { + Debug("getCeiling") << "Constraint_::getCeiling on " << *this << endl; Assert(getValue().getInfinitesimalPart().sgn() > 0); DeltaRational ceiling(getValue().ceiling()); @@ -350,7 +406,7 @@ Constraint ConstraintValue::getCeiling() { return d_database->getConstraint(getVariable(), getType(), ceiling); } -Constraint ConstraintValue::getFloor() { +ConstraintP Constraint_::getFloor() { Assert(getValue().getInfinitesimalPart().sgn() < 0); DeltaRational floor(Rational(getValue().floor())); @@ -359,19 +415,26 @@ Constraint ConstraintValue::getFloor() { return d_database->getConstraint(getVariable(), getType(), floor); } -void ConstraintValue::setCanBePropagated() { +void Constraint_::setCanBePropagated() { Assert(!canBePropagated()); d_database->pushCanBePropagatedWatch(this); } -void ConstraintValue::setAssertedToTheTheory(TNode witness) { +void Constraint_::setAssertedToTheTheoryWithNegationTrue(TNode witness) { + Assert(hasLiteral()); + Assert(!assertedToTheTheory()); + Assert(d_negation->hasProof()); + d_database->pushAssertionOrderWatch(this, witness); +} + +void Constraint_::setAssertedToTheTheory(TNode witness) { Assert(hasLiteral()); Assert(!assertedToTheTheory()); Assert(!d_negation->assertedToTheTheory()); d_database->pushAssertionOrderWatch(this, witness); } -bool ConstraintValue::satisfiedBy(const DeltaRational& dr) const { +bool Constraint_::satisfiedBy(const DeltaRational& dr) const { switch(getType()){ case LowerBound: return getValue() <= dr; @@ -385,19 +448,19 @@ bool ConstraintValue::satisfiedBy(const DeltaRational& dr) const { Unreachable(); } -// bool ConstraintValue::isPsuedoConstraint() const { -// return d_proof == d_database->d_psuedoConstraintProof; -// } +bool Constraint_::isInternalDecision() const { + return d_proof == d_database->d_internalDecisionProof; +} -bool ConstraintValue::isSelfExplaining() const { +bool Constraint_::isSelfExplaining() const { return d_proof == d_database->d_selfExplainingProof; } -bool ConstraintValue::hasEqualityEngineProof() const { +bool Constraint_::hasEqualityEngineProof() const { return d_proof == d_database->d_equalityEngineProof; } -bool ConstraintValue::sanityChecking(Node n) const { +bool Constraint_::sanityChecking(Node n) const { Comparison cmp = Comparison::parseNormalForm(n); Kind k = cmp.comparisonKind(); Polynomial pleft = cmp.normalizedVariablePart(); @@ -441,7 +504,7 @@ bool ConstraintValue::sanityChecking(Node n) const { } } -Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r){ +ConstraintP Constraint_::makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r){ switch(t){ case LowerBound: { @@ -450,12 +513,12 @@ Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const Del Assert(r.getInfinitesimalPart() == 1); // make (not (v > r)), which is (v <= r) DeltaRational dropInf(r.getNoninfinitesimalPart(), 0); - return new ConstraintValue(v, UpperBound, dropInf); + return new Constraint_(v, UpperBound, dropInf); }else{ Assert(r.infinitesimalSgn() == 0); // make (not (v >= r)), which is (v < r) DeltaRational addInf(r.getNoninfinitesimalPart(), -1); - return new ConstraintValue(v, UpperBound, addInf); + return new Constraint_(v, UpperBound, addInf); } } case UpperBound: @@ -465,18 +528,18 @@ Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const Del Assert(r.getInfinitesimalPart() == -1); // make (not (v < r)), which is (v >= r) DeltaRational dropInf(r.getNoninfinitesimalPart(), 0); - return new ConstraintValue(v, LowerBound, dropInf); + return new Constraint_(v, LowerBound, dropInf); }else{ Assert(r.infinitesimalSgn() == 0); // make (not (v <= r)), which is (v > r) DeltaRational addInf(r.getNoninfinitesimalPart(), 1); - return new ConstraintValue(v, LowerBound, addInf); + return new Constraint_(v, LowerBound, addInf); } } case Equality: - return new ConstraintValue(v, Disequality, r); + return new Constraint_(v, Disequality, r); case Disequality: - return new ConstraintValue(v, Equality, r); + return new Constraint_(v, Equality, r); default: Unreachable(); return NullConstraint; @@ -500,11 +563,42 @@ ConstraintDatabase::ConstraintDatabase(context::Context* satContext, context::Co d_equalityEngineProof = d_proofs.size(); d_proofs.push_back(NullConstraint); - // d_pseudoConstraintProof = d_proofs.size(); - // d_proofs.push_back(NullConstraint); + d_internalDecisionProof = d_proofs.size(); + d_proofs.push_back(NullConstraint); +} + +SortedConstraintMap& ConstraintDatabase::getVariableSCM(ArithVar v) const{ + Assert(variableDatabaseIsSetup(v)); + return d_varDatabases[v]->d_constraints; +} + +void ConstraintDatabase::pushSplitWatch(ConstraintP c){ + Assert(!c->d_split); + c->d_split = true; + d_watches->d_splitWatches.push_back(c); +} + + +void ConstraintDatabase::pushCanBePropagatedWatch(ConstraintP c){ + Assert(!c->d_canBePropagated); + c->d_canBePropagated = true; + d_watches->d_canBePropagatedWatches.push_back(c); +} + +void ConstraintDatabase::pushAssertionOrderWatch(ConstraintP c, TNode witness){ + Assert(!c->assertedToTheTheory()); + c->d_assertionOrder = d_watches->d_assertionOrderWatches.size(); + c->d_witness = witness; + d_watches->d_assertionOrderWatches.push_back(c); } -Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){ +void ConstraintDatabase::pushProofWatch(ConstraintP c, ProofId pid){ + Assert(c->d_proof == ProofIdSentinel); + c->d_proof = pid; + d_watches->d_proofWatches.push_back(c); +} + +ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){ //This must always return a constraint. SortedConstraintMap& scm = getVariableSCM(v); @@ -516,8 +610,8 @@ Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const if(vc.hasConstraintOfType(t)){ return vc.getConstraintOfType(t); }else{ - Constraint c = new ConstraintValue(v, t, r); - Constraint negC = ConstraintValue::makeNegation(v, t, r); + ConstraintP c = new Constraint_(v, t, r); + ConstraintP negC = Constraint_::makeNegation(v, t, r); SortedConstraintMapIterator negPos; if(t == Equality || t == Disequality){ @@ -539,6 +633,15 @@ Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const return c; } } + +ConstraintP ConstraintDatabase::ensureConstraint(ValueCollection& vc, ConstraintType t){ + if(vc.hasConstraintOfType(t)){ + return vc.getConstraintOfType(t); + }else{ + return getConstraint(vc.getVariable(), t, vc.getValue()); + } +} + bool ConstraintDatabase::emptyDatabase(const std::vector& vec){ std::vector::const_iterator first = vec.begin(); std::vector::const_iterator last = vec.end(); @@ -550,7 +653,7 @@ ConstraintDatabase::~ConstraintDatabase(){ delete d_watches; - std::vector constraintList; + std::vector constraintList; while(!d_varDatabases.empty()){ PerVariableDatabase* back = d_varDatabases.back(); @@ -561,7 +664,7 @@ ConstraintDatabase::~ConstraintDatabase(){ (i->second).push_into(constraintList); } while(!constraintList.empty()){ - Constraint c = constraintList.back(); + ConstraintP c = constraintList.back(); constraintList.pop_back(); delete c; } @@ -586,17 +689,25 @@ ConstraintDatabase::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_unatePropagateImplications); } +void ConstraintDatabase::deleteConstraintAndNegation(ConstraintP c){ + Assert(c->safeToGarbageCollect()); + ConstraintP neg = c->getNegation(); + Assert(neg->safeToGarbageCollect()); + delete c; + delete neg; +} + void ConstraintDatabase::addVariable(ArithVar v){ if(d_reclaimable.isMember(v)){ SortedConstraintMap& scm = getVariableSCM(v); - std::vector constraintList; + std::vector constraintList; for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){ (i->second).push_into(constraintList); } while(!constraintList.empty()){ - Constraint c = constraintList.back(); + ConstraintP c = constraintList.back(); constraintList.pop_back(); Assert(c->safeToGarbageCollect()); delete c; @@ -605,6 +716,7 @@ void ConstraintDatabase::addVariable(ArithVar v){ d_reclaimable.remove(v); }else{ + Debug("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl; Assert(v == d_varDatabases.size()); d_varDatabases.push_back(new PerVariableDatabase(v)); } @@ -615,20 +727,20 @@ void ConstraintDatabase::removeVariable(ArithVar v){ d_reclaimable.add(v); } -bool ConstraintValue::safeToGarbageCollect() const{ +bool Constraint_::safeToGarbageCollect() const{ return !isSplit() && !canBePropagated() && !hasProof() && !assertedToTheTheory(); } -Node ConstraintValue::split(){ +Node Constraint_::split(){ Assert(isEquality() || isDisequality()); bool isEq = isEquality(); - Constraint eq = isEq ? this : d_negation; - Constraint diseq = isEq ? d_negation : this; + ConstraintP eq = isEq ? this : d_negation; + ConstraintP diseq = isEq ? d_negation : this; TNode eqNode = eq->getLiteral(); Assert(eqNode.getKind() == kind::EQUAL); @@ -651,26 +763,26 @@ bool ConstraintDatabase::hasLiteral(TNode literal) const { return lookup(literal) != NullConstraint; } -// Constraint ConstraintDatabase::addLiteral(TNode literal){ +// ConstraintP ConstraintDatabase::addLiteral(TNode literal){ // Assert(!hasLiteral(literal)); // bool isNot = (literal.getKind() == NOT); // TNode atom = isNot ? literal[0] : literal; -// Constraint atomC = addAtom(atom); +// ConstraintP atomC = addAtom(atom); // return isNot ? atomC->d_negation : atomC; // } -// Constraint ConstraintDatabase::allocateConstraintForComparison(ArithVar v, const Comparison cmp){ +// ConstraintP ConstraintDatabase::allocateConstraintForComparison(ArithVar v, const Comparison cmp){ // Debug("arith::constraint") << "allocateConstraintForLiteral(" << v << ", "<< cmp <<")" << endl; // Kind kind = cmp.comparisonKind(); // ConstraintType type = constraintTypeOfLiteral(kind); // DeltaRational dr = cmp.getDeltaRational(); -// return new ConstraintValue(v, type, dr); +// return new Constraint_(v, type, dr); // } -Constraint ConstraintDatabase::addLiteral(TNode literal){ +ConstraintP ConstraintDatabase::addLiteral(TNode literal){ Assert(!hasLiteral(literal)); bool isNot = (literal.getKind() == NOT); Node atomNode = (isNot ? literal[0] : literal); @@ -688,7 +800,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ DeltaRational posDR = posCmp.normalizedDeltaRational(); - Constraint posC = new ConstraintValue(v, posType, posDR); + ConstraintP posC = new Constraint_(v, posType, posDR); Debug("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl; Debug("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl; @@ -702,9 +814,9 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ // If the attempt fails, i points to a pre-existing ValueCollection if(posI->second.hasConstraintOfType(posC->getType())){ - //This is the situation where the Constraint exists, but + //This is the situation where the ConstraintP exists, but //the literal has not been associated with it. - Constraint hit = posI->second.getConstraintOfType(posC->getType()); + ConstraintP hit = posI->second.getConstraintOfType(posC->getType()); Debug("arith::constraint") << "hit " << hit << endl; Debug("arith::constraint") << "posC " << posC << endl; @@ -719,7 +831,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ ConstraintType negType = constraintTypeOfComparison(negCmp); DeltaRational negDR = negCmp.normalizedDeltaRational(); - Constraint negC = new ConstraintValue(v, negType, negDR); + ConstraintP negC = new Constraint_(v, negType, negDR); SortedConstraintMapIterator negI; @@ -771,7 +883,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ // } // } -Constraint ConstraintDatabase::lookup(TNode literal) const{ +ConstraintP ConstraintDatabase::lookup(TNode literal) const{ NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal); if(iter == d_nodetoConstraintMap.end()){ return NullConstraint; @@ -780,11 +892,19 @@ Constraint ConstraintDatabase::lookup(TNode literal) const{ } } -void ConstraintValue::selfExplaining(){ +void Constraint_::selfExplainingWithNegationTrue(){ + Assert(!hasProof()); + Assert(getNegation()->hasProof()); + Assert(hasLiteral()); + Assert(assertedToTheTheory()); + d_database->pushProofWatch(this, d_database->d_selfExplainingProof); +} + +void Constraint_::selfExplaining(){ markAsTrue(); } -void ConstraintValue::propagate(){ +void Constraint_::propagate(){ Assert(hasProof()); Assert(canBePropagated()); Assert(!assertedToTheTheory()); @@ -793,7 +913,7 @@ void ConstraintValue::propagate(){ d_database->d_toPropagate.push(this); } -void ConstraintValue::propagate(Constraint a){ +void Constraint_::propagate(ConstraintCP a){ Assert(!hasProof()); Assert(canBePropagated()); @@ -801,7 +921,7 @@ void ConstraintValue::propagate(Constraint a){ propagate(); } -void ConstraintValue::propagate(Constraint a, Constraint b){ +void Constraint_::propagate(ConstraintCP a, ConstraintCP b){ Assert(!hasProof()); Assert(canBePropagated()); @@ -809,7 +929,7 @@ void ConstraintValue::propagate(Constraint a, Constraint b){ propagate(); } -void ConstraintValue::propagate(const std::vector& b){ +void Constraint_::propagate(const ConstraintCPVec& b){ Assert(!hasProof()); Assert(canBePropagated()); @@ -817,9 +937,8 @@ void ConstraintValue::propagate(const std::vector& b){ propagate(); } -void ConstraintValue::impliedBy(Constraint a){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(ConstraintCP a){ + Assert(truthIsUnknown()); markAsTrue(a); if(canBePropagated()){ @@ -827,9 +946,8 @@ void ConstraintValue::impliedBy(Constraint a){ } } -void ConstraintValue::impliedBy(Constraint a, Constraint b){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(ConstraintCP a, ConstraintCP b){ + Assert(truthIsUnknown()); markAsTrue(a, b); if(canBePropagated()){ @@ -837,9 +955,8 @@ void ConstraintValue::impliedBy(Constraint a, Constraint b){ } } -void ConstraintValue::impliedBy(const std::vector& b){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(const ConstraintCPVec& b){ + Assert(truthIsUnknown()); markAsTrue(b); if(canBePropagated()){ @@ -847,30 +964,29 @@ void ConstraintValue::impliedBy(const std::vector& b){ } } -// void ConstraintValue::setPseudoConstraint(){ -// Assert(truthIsUnknown()); -// Assert(!hasLiteral()); +void Constraint_::setInternalDecision(){ + Assert(truthIsUnknown()); + Assert(!assertedToTheTheory()); -// d_database->pushProofWatch(this, d_database->d_pseudoConstraintProof); -// } + d_database->pushProofWatch(this, d_database->d_internalDecisionProof); +} -void ConstraintValue::setEqualityEngineProof(){ +void Constraint_::setEqualityEngineProof(){ Assert(truthIsUnknown()); Assert(hasLiteral()); d_database->pushProofWatch(this, d_database->d_equalityEngineProof); } -void ConstraintValue::markAsTrue(){ +void Constraint_::markAsTrue(){ Assert(truthIsUnknown()); Assert(hasLiteral()); Assert(assertedToTheTheory()); d_database->pushProofWatch(this, d_database->d_selfExplainingProof); } -void ConstraintValue::markAsTrue(Constraint imp){ +void Constraint_::markAsTrue(ConstraintCP imp){ Assert(truthIsUnknown()); Assert(imp->hasProof()); - //Assert(!imp->isPseudoConstraint()); d_database->d_proofs.push_back(NullConstraint); d_database->d_proofs.push_back(imp); @@ -878,12 +994,10 @@ void ConstraintValue::markAsTrue(Constraint imp){ d_database->pushProofWatch(this, proof); } -void ConstraintValue::markAsTrue(Constraint impA, Constraint impB){ +void Constraint_::markAsTrue(ConstraintCP impA, ConstraintCP impB){ Assert(truthIsUnknown()); Assert(impA->hasProof()); Assert(impB->hasProof()); - //Assert(!impA->isPseudoConstraint()); - //Assert(!impB->isPseudoConstraint()); d_database->d_proofs.push_back(NullConstraint); d_database->d_proofs.push_back(impA); @@ -893,12 +1007,12 @@ void ConstraintValue::markAsTrue(Constraint impA, Constraint impB){ d_database->pushProofWatch(this, proof); } -void ConstraintValue::markAsTrue(const vector& a){ +void Constraint_::markAsTrue(const ConstraintCPVec& a){ Assert(truthIsUnknown()); Assert(a.size() >= 1); d_database->d_proofs.push_back(NullConstraint); - for(vector::const_iterator i = a.begin(), end = a.end(); i != end; ++i){ - Constraint c_i = *i; + for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){ + ConstraintCP c_i = *i; Assert(c_i->hasProof()); //Assert(!c_i->isPseudoConstraint()); d_database->d_proofs.push_back(c_i); @@ -909,12 +1023,12 @@ void ConstraintValue::markAsTrue(const vector& a){ d_database->pushProofWatch(this, proof); } -SortedConstraintMap& ConstraintValue::constraintSet() const{ +SortedConstraintMap& Constraint_::constraintSet() const{ Assert(d_database->variableDatabaseIsSetup(d_variable)); return (d_database->d_varDatabases[d_variable])->d_constraints; } -bool ConstraintValue::proofIsEmpty() const{ +bool Constraint_::proofIsEmpty() const{ Assert(hasProof()); bool result = d_database->d_proofs[d_proof] == NullConstraint; //Assert((!result) || isSelfExplaining() || hasEqualityEngineProof() || isPseudoConstraint()); @@ -922,32 +1036,76 @@ bool ConstraintValue::proofIsEmpty() const{ return result; } -Node ConstraintValue::makeImplication(const std::vector& b) const{ - Node antecedent = makeConjunction(b); +Node Constraint_::externalImplication(const ConstraintCPVec& b) const{ + Assert(hasLiteral()); + Node antecedent = externalExplainByAssertions(b); Node implied = getLiteral(); return antecedent.impNode(implied); } -Node ConstraintValue::makeConjunction(const std::vector& b){ - NodeBuilder<> nb(kind::AND); - for(vector::const_iterator i = b.begin(), end = b.end(); i != end; ++i){ - Constraint b_i = *i; - b_i->explainBefore(nb, AssertionOrderSentinel); +Node Constraint_::externalExplainByAssertions(const ConstraintCPVec& b){ + return externalExplain(b, AssertionOrderSentinel); +} + +struct ConstraintCPHash { + /* Todo replace with an id */ + size_t operator()(ConstraintCP c) const{ + Assert(sizeof(ConstraintCP) > 0); + return ((size_t)c)/sizeof(ConstraintCP); } - if(nb.getNumChildren() >= 2){ - return nb; - }else if(nb.getNumChildren() == 1){ - return nb[0]; - }else{ - return mkBoolNode(true); +}; + +void Constraint_::assertionFringe(ConstraintCPVec& v){ + hash_set visited; + size_t writePos = 0; + + if(!v.empty()){ + const ConstraintDatabase* db = v.back()->d_database; + const CDConstraintList& proofs = db->d_proofs; + for(size_t i = 0; i < v.size(); ++i){ + ConstraintCP vi = v[i]; + if(visited.find(vi) == visited.end()){ + Assert(vi->hasProof()); + visited.insert(vi); + if(vi->onFringe()){ + v[writePos] = vi; + writePos++; + }else{ + Assert(!vi->isSelfExplaining()); + ProofId p = vi->d_proof; + ConstraintCP antecedent = proofs[p]; + while(antecedent != NullConstraint){ + v.push_back(antecedent); + --p; + antecedent = proofs[p]; + } + } + } + } + v.resize(writePos); + } +} + +void Constraint_::assertionFringe(ConstraintCPVec& o, const ConstraintCPVec& i){ + o.insert(o.end(), i.begin(), i.end()); + assertionFringe(o); +} + +Node Constraint_::externalExplain(const ConstraintCPVec& v, AssertionOrder order){ + NodeBuilder<> nb(kind::AND); + ConstraintCPVec::const_iterator i, end; + for(i = v.begin(), end = v.end(); i != end; ++i){ + ConstraintCP v_i = *i; + v_i->externalExplain(nb, order); } + return safeConstructNary(nb); } -void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) const{ +void Constraint_::externalExplain(NodeBuilder<>& nb, AssertionOrder order) const{ Assert(hasProof()); Assert(!isSelfExplaining() || assertedToTheTheory()); - + Assert(!isInternalDecision()); if(assertedBefore(order)){ nb << getWitness(); @@ -956,56 +1114,61 @@ void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) con }else{ Assert(!isSelfExplaining()); ProofId p = d_proof; - Constraint antecedent = d_database->d_proofs[p]; + ConstraintCP antecedent = d_database->d_proofs[p]; for(; antecedent != NullConstraint; antecedent = d_database->d_proofs[--p] ){ - antecedent->explainBefore(nb, order); + antecedent->externalExplain(nb, order); } } } -Node ConstraintValue::explainBefore(AssertionOrder order) const{ + +Node Constraint_::externalExplain(AssertionOrder order) const{ Assert(hasProof()); Assert(!isSelfExplaining() || assertedBefore(order)); + Assert(!isInternalDecision()); if(assertedBefore(order)){ return getWitness(); }else if(hasEqualityEngineProof()){ return d_database->eeExplain(this); }else{ Assert(!proofIsEmpty()); - //Force the selection of the layer above if the node is assertedToTheTheory()! + //Force the selection of the layer above if the node is + // assertedToTheTheory()! if(d_database->d_proofs[d_proof-1] == NullConstraint){ - Constraint antecedent = d_database->d_proofs[d_proof]; - return antecedent->explainBefore(order); + ConstraintCP antecedent = d_database->d_proofs[d_proof]; + return antecedent->externalExplain(order); }else{ NodeBuilder<> nb(kind::AND); Assert(!isSelfExplaining()); ProofId p = d_proof; - Constraint antecedent = d_database->d_proofs[p]; - for(; antecedent != NullConstraint; antecedent = d_database->d_proofs[--p] ){ - antecedent->explainBefore(nb, order); + ConstraintCP antecedent = d_database->d_proofs[p]; + while(antecedent != NullConstraint){ + antecedent->externalExplain(nb, order); + --p; + antecedent = d_database->d_proofs[p]; } return nb; } } } -Node ConstraintValue::explainConflict(Constraint a, Constraint b){ +Node Constraint_::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){ NodeBuilder<> nb(kind::AND); - a->explainForConflict(nb); - b->explainForConflict(nb); + a->externalExplainByAssertions(nb); + b->externalExplainByAssertions(nb); return nb; } -Node ConstraintValue::explainConflict(Constraint a, Constraint b, Constraint c){ +Node Constraint_::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){ NodeBuilder<> nb(kind::AND); - a->explainForConflict(nb); - b->explainForConflict(nb); - c->explainForConflict(nb); + a->externalExplainByAssertions(nb); + b->externalExplainByAssertions(nb); + c->externalExplainByAssertions(nb); return nb; } -Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const { +ConstraintP Constraint_::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const { Assert(initialized()); Assert(!asserted || hasLiteral); @@ -1016,7 +1179,7 @@ Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool as --i; const ValueCollection& vc = i->second; if(vc.hasLowerBound()){ - Constraint weaker = vc.getLowerBound(); + ConstraintP weaker = vc.getLowerBound(); // asserted -> hasLiteral // hasLiteral -> weaker->hasLiteral() @@ -1030,7 +1193,7 @@ Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool as return NullConstraint; } -Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const { +ConstraintP Constraint_::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const { SortedConstraintMapConstIterator i = d_variablePosition; const SortedConstraintMap& scm = constraintSet(); SortedConstraintMapConstIterator i_end = scm.end(); @@ -1039,7 +1202,7 @@ Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool as for(; i != i_end; ++i){ const ValueCollection& vc = i->second; if(vc.hasUpperBound()){ - Constraint weaker = vc.getUpperBound(); + ConstraintP weaker = vc.getUpperBound(); if((!hasLiteral || (weaker->hasLiteral())) && (!asserted || ( weaker->assertedToTheTheory()))){ return weaker; @@ -1050,7 +1213,7 @@ Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool as return NullConstraint; } -Constraint ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const { +ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const { Assert(variableDatabaseIsSetup(v)); Assert(t == UpperBound || t == LowerBound); @@ -1110,12 +1273,12 @@ Constraint ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, } } } -Node ConstraintDatabase::eeExplain(const ConstraintValue* const c) const{ +Node ConstraintDatabase::eeExplain(const Constraint_* const c) const{ Assert(c->hasLiteral()); return d_congruenceManager.explain(c->getLiteral()); } -void ConstraintDatabase::eeExplain(const ConstraintValue* const c, NodeBuilder<>& nb) const{ +void ConstraintDatabase::eeExplain(const Constraint_* const c, NodeBuilder<>& nb) const{ Assert(c->hasLiteral()); d_congruenceManager.explain(c->getLiteral(), nb); } @@ -1133,7 +1296,7 @@ ConstraintDatabase::Watches::Watches(context::Context* satContext, context::Cont {} -void ConstraintValue::setLiteral(Node n) { +void Constraint_::setLiteral(Node n) { Assert(!hasLiteral()); Assert(sanityChecking(n)); d_literal = n; @@ -1142,7 +1305,7 @@ void ConstraintValue::setLiteral(Node n) { map.insert(make_pair(d_literal, this)); } -void implies(std::vector& out, Constraint a, Constraint b){ +void implies(std::vector& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1153,7 +1316,7 @@ void implies(std::vector& out, Constraint a, Constraint b){ out.push_back(orderOr); } -void mutuallyExclusive(std::vector& out, Constraint a, Constraint b){ +void mutuallyExclusive(std::vector& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1169,13 +1332,13 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector& out, Ari SortedConstraintMap& scm = getVariableSCM(v); SortedConstraintMapConstIterator scm_iter = scm.begin(); SortedConstraintMapConstIterator scm_end = scm.end(); - Constraint prev = NullConstraint; + ConstraintP prev = NullConstraint; //get transitive unates //Only lower bounds or upperbounds should be done. for(; scm_iter != scm_end; ++scm_iter){ const ValueCollection& vc = scm_iter->second; if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->hasLiteral()){ if(prev != NullConstraint){ implies(out, prev, ub); @@ -1188,7 +1351,7 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector& out, Ari void ConstraintDatabase::outputUnateEqualityLemmas(std::vector& out, ArithVar v) const{ - vector equalities; + vector equalities; SortedConstraintMap& scm = getVariableSCM(v); SortedConstraintMapConstIterator scm_iter = scm.begin(); @@ -1197,34 +1360,34 @@ void ConstraintDatabase::outputUnateEqualityLemmas(std::vector& out, Arith for(; scm_iter != scm_end; ++scm_iter){ const ValueCollection& vc = scm_iter->second; if(vc.hasEquality()){ - Constraint eq = vc.getEquality(); + ConstraintP eq = vc.getEquality(); if(eq->hasLiteral()){ equalities.push_back(eq); } } } - vector::const_iterator i, j, eq_end = equalities.end(); + vector::const_iterator i, j, eq_end = equalities.end(); for(i = equalities.begin(); i != eq_end; ++i){ - Constraint at_i = *i; + ConstraintP at_i = *i; for(j= i + 1; j != eq_end; ++j){ - Constraint at_j = *j; + ConstraintP at_j = *j; mutuallyExclusive(out, at_i, at_j); } } for(i = equalities.begin(); i != eq_end; ++i){ - Constraint eq = *i; + ConstraintP eq = *i; const ValueCollection& vc = eq->getValueCollection(); Assert(vc.hasEquality() && vc.getEquality()->hasLiteral()); bool hasLB = vc.hasLowerBound() && vc.getLowerBound()->hasLiteral(); bool hasUB = vc.hasUpperBound() && vc.getUpperBound()->hasLiteral(); - Constraint lb = hasLB ? + ConstraintP lb = hasLB ? vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false); - Constraint ub = hasUB ? + ConstraintP ub = hasUB ? vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false); if(hasUB && hasLB && !eq->isSplit()){ @@ -1251,21 +1414,20 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector& lemmas) } } -void ConstraintDatabase::raiseUnateConflict(Constraint ant, Constraint cons){ +void ConstraintDatabase::raiseUnateConflict(ConstraintP ant, ConstraintP cons){ Assert(ant->hasProof()); - Constraint negCons = cons->getNegation(); + ConstraintP negCons = cons->getNegation(); Assert(negCons->hasProof()); Debug("arith::unate::conf") << ant << "implies " << cons << endl; Debug("arith::unate::conf") << negCons << " is true." << endl; - - Node conf = ConstraintValue::explainConflict(ant, negCons); - Debug("arith::unate::conf") << conf << std::endl; - d_raiseConflict(conf); + d_raiseConflict.addConstraint(ant); + d_raiseConflict.addConstraint(negCons); + d_raiseConflict.commitConflict(); } -void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ +void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){ Debug("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl; Assert(curr != prev); Assert(curr != NullConstraint); @@ -1297,7 +1459,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the LowerBounds! if(vc.hasLowerBound()){ - Constraint lb = vc.getLowerBound(); + ConstraintP lb = vc.getLowerBound(); if(lb->negationHasProof()){ raiseUnateConflict(curr, lb); return; @@ -1309,7 +1471,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1323,7 +1485,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ } } -void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ +void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){ Debug("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl; Assert(curr != prev); Assert(curr != NullConstraint); @@ -1348,7 +1510,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the UpperBounds! if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->negationHasProof()){ raiseUnateConflict(curr, ub); return; @@ -1359,7 +1521,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1373,7 +1535,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ } } -void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, Constraint prevUB){ +void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){ Debug("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl; Assert(curr != prevLB); Assert(curr != prevUB); @@ -1405,7 +1567,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the LowerBounds! if(vc.hasLowerBound()){ - Constraint lb = vc.getLowerBound(); + ConstraintP lb = vc.getLowerBound(); if(lb->negationHasProof()){ raiseUnateConflict(curr, lb); return; @@ -1416,7 +1578,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1440,7 +1602,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the UpperBounds! if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->negationHasProof()){ raiseUnateConflict(curr, ub); return; @@ -1452,7 +1614,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index 4966115d2..18e53660f 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -94,9 +94,9 @@ namespace arith { enum ConstraintType {LowerBound, Equality, UpperBound, Disequality}; -typedef context::CDList CDConstraintList; +typedef context::CDList CDConstraintList; -typedef __gnu_cxx::hash_map NodetoConstraintMap; +typedef __gnu_cxx::hash_map NodetoConstraintMap; typedef size_t ProofId; static ProofId ProofIdSentinel = std::numeric_limits::max(); @@ -111,54 +111,29 @@ static AssertionOrder AssertionOrderSentinel = std::numeric_limits& vec) const; + void push_into(std::vector& vec) const; - Constraint nonNull() const; + ConstraintP nonNull() const; ArithVar getVariable() const; const DeltaRational& getValue() const; @@ -220,7 +195,7 @@ struct PerVariableDatabase{ } }; -class ConstraintValue { +class Constraint_ { private: /** The ArithVar associated with the constraint. */ const ArithVar d_variable; @@ -246,7 +221,7 @@ private: Node d_literal; /** Pointer to the negation of the Constraint. */ - Constraint d_negation; + ConstraintP d_negation; /** * This is true if the associated node can be propagated. @@ -269,10 +244,11 @@ private: * Sat Context Dependent. * This is initially AssertionOrderSentinel. */ - AssertionOrder _d_assertionOrder; + AssertionOrder d_assertionOrder; + /** * This is guaranteed to be on the fact queue. - * For example if x + y = x + 1 is on the fact queue, then use this + * For example if x + y = x + 1 is on the fact queue, then use this */ TNode d_witness; @@ -309,13 +285,13 @@ private: * Because of circular dependencies a Constraint is not fully valid until * initialize has been called on it. */ - ConstraintValue(ArithVar x, ConstraintType t, const DeltaRational& v); + Constraint_(ArithVar x, ConstraintType t, const DeltaRational& v); /** * Destructor for a constraint. * This should only be called if safeToGarbageCollect() is true. */ - ~ConstraintValue(); + ~Constraint_(); bool initialized() const; @@ -323,12 +299,12 @@ private: * This initializes the fields that cannot be set in the constructor due to * circular dependencies. */ - void initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, Constraint negation); + void initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation); class ProofCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_proof != ProofIdSentinel); constraint->d_proof = ProofIdSentinel; } @@ -336,8 +312,8 @@ private: class CanBePropagatedCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_canBePropagated); constraint->d_canBePropagated = false; } @@ -345,10 +321,10 @@ private: class AssertionOrderCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->assertedToTheTheory()); - constraint->_d_assertionOrder = AssertionOrderSentinel; + constraint->d_assertionOrder = AssertionOrderSentinel; constraint->d_witness = TNode::null(); Assert(!constraint->assertedToTheTheory()); } @@ -356,8 +332,8 @@ private: class SplitCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_split); constraint->d_split = false; } @@ -389,7 +365,7 @@ public: return d_value; } - Constraint getNegation() const { + ConstraintP getNegation() const { return d_negation; } @@ -421,7 +397,7 @@ public: /** * Splits the node in the user context. - * Returns a lemma that is assumed to be true fro the rest of the user context. + * Returns a lemma that is assumed to be true for the rest of the user context. * Constraint must be an equality or disequality. */ Node split(); @@ -441,8 +417,8 @@ public: } bool assertedToTheTheory() const { - Assert((_d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull()); - return _d_assertionOrder < AssertionOrderSentinel; + Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull()); + return d_assertionOrder < AssertionOrderSentinel; } TNode getWitness() const { Assert(assertedToTheTheory()); @@ -450,12 +426,17 @@ public: } bool assertedBefore(AssertionOrder time) const { - return _d_assertionOrder < time; + return d_assertionOrder < time; } - + /** Sets the witness literal for a node being on the assertion stack. + * The negation of the node cannot be true. */ void setAssertedToTheTheory(TNode witness); + /** Sets the witness literal for a node being on the assertion stack. + * The negation of the node must be true! + * This is for conflict generation specificially! */ + void setAssertedToTheTheoryWithNegationTrue(TNode witness); bool hasLiteral() const { return !d_literal.isNull(); @@ -474,6 +455,8 @@ public: */ void selfExplaining(); + void selfExplainingWithNegationTrue(); + /** Returns true if the node is selfExplaining.*/ bool isSelfExplaining() const; @@ -485,12 +468,17 @@ public: /** - * There cannot be a literal associated with this constraint. - * The explanation is the constant true. - * explainInto() does nothing. + * A sets the constraint to be an internal decision. + * + * This does not need to have a witness or an associated literal. + * This is always itself in the explanation fringe for both conflicts + * and propagation. + * This cannot be converted back into a Node conflict or explanation. + * + * This cannot have a proof or be asserted to the theory! */ - //void setPseudoConstraint(); - //bool isPseudoConstraint() const; + void setInternalDecision(); + bool isInternalDecision() const; /** * Returns a explanation of the constraint that is appropriate for conflicts. @@ -500,8 +488,8 @@ public: * This is the minimum fringe of the implication tree s.t. * every constraint is assertedToTheTheory() or hasEqualityEngineProof(). */ - Node explainForConflict() const{ - return explainBefore(AssertionOrderSentinel); + Node externalExplainByAssertions() const { + return externalExplain(AssertionOrderSentinel); } /** @@ -515,13 +503,38 @@ public: * This is not appropriate for propagation! * Use explainForPropagation() instead. */ - void explainForConflict(NodeBuilder<>& nb) const{ - explainBefore(nb, AssertionOrderSentinel); + void externalExplainByAssertions(NodeBuilder<>& nb) const{ + externalExplain(nb, AssertionOrderSentinel); } + /* Equivalent to calling externalExplainByAssertions on all constraints in b */ + static Node externalExplainByAssertions(const ConstraintCPVec& b); + /* utilities for calling externalExplainByAssertions on 2 constraints */ + static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b); + static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c); + //static Node externalExplainByAssertions(ConstraintCP a); + + /** + * This is the minimum fringe of the implication tree s.t. every constraint is + * - assertedToTheTheory(), + * - isInternalDecision() or + * - hasEqualityEngineProof(). + */ + static void assertionFringe(ConstraintCPVec& v); + static void assertionFringe(ConstraintCPVec& out, const ConstraintCPVec& in); + /** Utility function built from explainForConflict. */ - static Node explainConflict(Constraint a, Constraint b); - static Node explainConflict(Constraint a, Constraint b, Constraint c); + //static Node explainConflict(ConstraintP a, ConstraintP b); + //static Node explainConflict(ConstraintP a, ConstraintP b, Constraint c); + + //static Node explainConflictForEE(ConstraintCP a, ConstraintCP b); + //static Node explainConflictForEE(ConstraintCP a); + //static Node explainConflictForDio(ConstraintCP a); + //static Node explainConflictForDio(ConstraintCP a, ConstraintCP b); + + bool onFringe() const { + return assertedToTheTheory() || isInternalDecision() || hasEqualityEngineProof(); + } /** * Returns an explanation of a propagation by the ConstraintDatabase. @@ -531,14 +544,20 @@ public: * This is the minimum fringe of the implication tree (excluding the constraint itself) * s.t. every constraint is assertedToTheTheory() or hasEqualityEngineProof(). */ - Node explainForPropagation() const { + Node externalExplainForPropagation() const { Assert(hasProof()); Assert(!isSelfExplaining()); - return explainBefore(_d_assertionOrder); + return externalExplain(d_assertionOrder); } + // void externalExplainForPropagation(NodeBuilder<>& nb) const{ + // Assert(hasProof()); + // Assert(!isSelfExplaining()); + // externalExplain(nb, d_assertionOrder); + // } + private: - Node explainBefore(AssertionOrder order) const; + Node externalExplain(AssertionOrder order) const; /** * Returns an explanation of that was assertedBefore(order). @@ -548,7 +567,9 @@ private: * This is the minimum fringe of the implication tree * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof(). */ - void explainBefore(NodeBuilder<>& nb, AssertionOrder order) const; + void externalExplain(NodeBuilder<>& nb, AssertionOrder order) const; + + static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order); public: bool hasProof() const { @@ -558,26 +579,38 @@ public: return d_negation->hasProof(); } + /* Neither the contraint has a proof nor the negation has a proof.*/ bool truthIsUnknown() const { return !hasProof() && !negationHasProof(); } + /* This is a synonym for hasProof(). */ bool isTrue() const { return hasProof(); } - Constraint getCeiling(); + /** + * Returns the constraint that corresponds to taking + * x r ceiling(getValue()) where r is the node's getType(). + * Esstentially this is an up branch. + */ + ConstraintP getCeiling(); - Constraint getFloor(); + /** + * Returns the constraint that corresponds to taking + * x r floor(getValue()) where r is the node's getType(). + * Esstentially this is a down branch. + */ + ConstraintP getFloor(); - static Constraint makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r); + static ConstraintP makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r); const ValueCollection& getValueCollection() const; - Constraint getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const; - Constraint getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const; + ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const; + ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const; /** * Marks the node as having a proof a. @@ -587,19 +620,23 @@ public: * canBePropagated() * !assertedToTheTheory() */ - void propagate(Constraint a); - void propagate(Constraint a, Constraint b); - void propagate(const std::vector& b); + void propagate(ConstraintCP a); + void propagate(ConstraintCP a, ConstraintCP b); + //void propagate(const std::vector& b); + void propagate(const ConstraintCPVec& b); + /** * The only restriction is that this is not known be true. * This propagates if there is a node. */ - void impliedBy(Constraint a); - void impliedBy(Constraint a, Constraint b); - void impliedBy(const std::vector& b); + void impliedBy(ConstraintCP a); + void impliedBy(ConstraintCP a, ConstraintCP b); + //void impliedBy(const std::vector& b); + void impliedBy(const ConstraintCPVec& b); - Node makeImplication(const std::vector& b) const; - static Node makeConjunction(const std::vector& b); + Node externalImplication(const ConstraintCPVec& b) const; + static Node externalConjunction(const ConstraintCPVec& b); + //static Node makeConflictNode(const ConstraintCPVec& b); /** The node must have a proof already and be eligible for propagation! */ void propagate(); @@ -617,10 +654,11 @@ private: * Marks the node as having a proof a. * This is safe if the node does not have */ - void markAsTrue(Constraint a); + void markAsTrue(ConstraintCP a); - void markAsTrue(Constraint a, Constraint b); - void markAsTrue(const std::vector& b); + void markAsTrue(ConstraintCP a, ConstraintCP b); + //void markAsTrue(const std::vector& b); + void markAsTrue(const ConstraintCPVec& b); void debugPrint() const; @@ -634,11 +672,11 @@ private: }; /* class ConstraintValue */ -std::ostream& operator<<(std::ostream& o, const ConstraintValue& c); -std::ostream& operator<<(std::ostream& o, const Constraint c); +std::ostream& operator<<(std::ostream& o, const Constraint_& c); +std::ostream& operator<<(std::ostream& o, const ConstraintP c); std::ostream& operator<<(std::ostream& o, const ConstraintType t); std::ostream& operator<<(std::ostream& o, const ValueCollection& c); - +std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v); class ConstraintDatabase { @@ -650,20 +688,17 @@ private: */ std::vector d_varDatabases; - SortedConstraintMap& getVariableSCM(ArithVar v) const{ - Assert(variableDatabaseIsSetup(v)); - return d_varDatabases[v]->d_constraints; - } + SortedConstraintMap& getVariableSCM(ArithVar v) const; /** Maps literals to constraints.*/ NodetoConstraintMap d_nodetoConstraintMap; /** * A queue of propagated constraints. - * - * As Constraint are pointers, the elements of the queue do not require destruction. + * ConstraintCP are pointers. + * The elements of the queue do not require destruction. */ - context::CDQueue d_toPropagate; + context::CDQueue d_toPropagate; /** * Proof Lists. @@ -701,17 +736,16 @@ private: ProofId d_equalityEngineProof; /** - * Marks a node as being true always. - * This is only okay for purely internal things. - * - * This is a special proof that is always a member of the list. + * Marks a constraint as being proved by making an internal + * decision. Such nodes cannot be used in external explanations + * but can be used internally. */ - //ProofId d_pseudoConstraintProof; + ProofId d_internalDecisionProof; - typedef context::CDList ProofCleanupList; - typedef context::CDList CBPList; - typedef context::CDList AOList; - typedef context::CDList SplitList; + typedef context::CDList ProofCleanupList; + typedef context::CDList CBPList; + typedef context::CDList AOList; + typedef context::CDList SplitList; /** * The watch lists are collected together as they need to be garbage collected @@ -744,30 +778,10 @@ private: }; Watches* d_watches; - void pushSplitWatch(Constraint c){ - Assert(!c->d_split); - c->d_split = true; - d_watches->d_splitWatches.push_back(c); - } - - void pushCanBePropagatedWatch(Constraint c){ - Assert(!c->d_canBePropagated); - c->d_canBePropagated = true; - d_watches->d_canBePropagatedWatches.push_back(c); - } - - void pushAssertionOrderWatch(Constraint c, TNode witness){ - Assert(!c->assertedToTheTheory()); - c->_d_assertionOrder = d_watches->d_assertionOrderWatches.size(); - c->d_witness = witness; - d_watches->d_assertionOrderWatches.push_back(c); - } - - void pushProofWatch(Constraint c, ProofId pid){ - Assert(c->d_proof == ProofIdSentinel); - c->d_proof = pid; - d_watches->d_proofWatches.push_back(c); - } + void pushSplitWatch(ConstraintP c); + void pushCanBePropagatedWatch(ConstraintP c); + void pushAssertionOrderWatch(ConstraintP c, TNode witness); + void pushProofWatch(ConstraintP c, ProofId pid); /** Returns true if all of the entries of the vector are empty. */ static bool emptyDatabase(const std::vector& vec); @@ -786,7 +800,7 @@ private: RaiseConflict d_raiseConflict; - friend class ConstraintValue; + friend class Constraint_; public: @@ -799,13 +813,13 @@ public: ~ConstraintDatabase(); /** Adds a literal to the database. */ - Constraint addLiteral(TNode lit); + ConstraintP addLiteral(TNode lit); /** * If hasLiteral() is true, returns the constraint. * Otherwise, returns NullConstraint. */ - Constraint lookup(TNode literal) const; + ConstraintP lookup(TNode literal) const; /** * Returns true if the literal has been added to the database. @@ -818,10 +832,10 @@ public: return !d_toPropagate.empty(); } - Constraint nextPropagation(){ + ConstraintCP nextPropagation(){ Assert(hasMorePropagations()); - Constraint p = d_toPropagate.front(); + ConstraintCP p = d_toPropagate.front(); d_toPropagate.pop(); return p; @@ -831,8 +845,8 @@ public: bool variableDatabaseIsSetup(ArithVar v) const; void removeVariable(ArithVar v); - Node eeExplain(ConstConstraint c) const; - void eeExplain(ConstConstraint c, NodeBuilder<>& nb) const; + Node eeExplain(ConstraintCP c) const; + void eeExplain(ConstraintCP c, NodeBuilder<>& nb) const; /** * Returns a constraint with the variable v, the constraint type t, and a value @@ -843,8 +857,13 @@ public: * The returned value v is dominated: * If t is UpperBound, r <= v * If t is LowerBound, r >= v + * + * variableDatabaseIsSetup(v) must be true. */ - Constraint getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const; + ConstraintP getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const; + + /** Returns the constraint, if it exists */ + ConstraintP lookupConstraint(ArithVar v, ConstraintType t, const DeltaRational& r) const; /** * Returns a constraint with the variable v, the constraint type t and the value r. @@ -852,22 +871,18 @@ public: * If there is no such constraint, this constraint is added to the database. * */ - Constraint getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r); + ConstraintP getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r); /** * Returns a constraint of the given type for the value and variable * for the given ValueCollection, vc. * This is made if there is no such constraint. */ - Constraint ensureConstraint(ValueCollection& vc, ConstraintType t){ - if(vc.hasConstraintOfType(t)){ - return vc.getConstraintOfType(t); - }else{ - return getConstraint(vc.getVariable(), t, vc.getValue()); - } - } + ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t); + void deleteConstraintAndNegation(ConstraintP c); + /** * Outputs a minimal set of unate implications onto the vector for the variable. * This outputs lemmas of the general forms @@ -887,12 +902,12 @@ public: void outputUnateInequalityLemmas(std::vector& lemmas, ArithVar v) const; - void unatePropLowerBound(Constraint curr, Constraint prev); - void unatePropUpperBound(Constraint curr, Constraint prev); - void unatePropEquality(Constraint curr, Constraint prevLB, Constraint prevUB); + void unatePropLowerBound(ConstraintP curr, ConstraintP prev); + void unatePropUpperBound(ConstraintP curr, ConstraintP prev); + void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB); private: - void raiseUnateConflict(Constraint ant, Constraint cons); + void raiseUnateConflict(ConstraintP ant, ConstraintP cons); DenseSet d_reclaimable; @@ -907,6 +922,7 @@ private: }; /* ConstraintDatabase */ + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/constraint_forward.h b/src/theory/arith/constraint_forward.h index f01c64b60..19326c0b3 100644 --- a/src/theory/arith/constraint_forward.h +++ b/src/theory/arith/constraint_forward.h @@ -16,23 +16,26 @@ ** minimize interaction between header files. **/ -#include "cvc4_private.h" - #ifndef __CVC4__THEORY__ARITH__CONSTRAINT_FORWARD_H #define __CVC4__THEORY__ARITH__CONSTRAINT_FORWARD_H +#include "cvc4_private.h" +#include + namespace CVC4 { namespace theory { namespace arith { -class ConstraintValue; -typedef ConstraintValue* Constraint; -typedef const ConstraintValue* const ConstConstraint; +class Constraint_; +typedef Constraint_* ConstraintP; +typedef const Constraint_* ConstraintCP; -static const Constraint NullConstraint = NULL; +const ConstraintP NullConstraint = NULL; class ConstraintDatabase; +typedef std::vector ConstraintCPVec; + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/cut_log.cpp b/src/theory/arith/cut_log.cpp new file mode 100644 index 000000000..f933516ba --- /dev/null +++ b/src/theory/arith/cut_log.cpp @@ -0,0 +1,691 @@ +#include "cvc4autoconfig.h" + + +#include "theory/arith/cut_log.h" +#include "theory/arith/approx_simplex.h" +#include "theory/arith/normal_form.h" +#include "theory/arith/constraint.h" +#include +#include +#include +#include + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace arith { + +NodeLog::const_iterator NodeLog::begin() const { return d_cuts.begin(); } +NodeLog::const_iterator NodeLog::end() const { return d_cuts.end(); } + +NodeLog& TreeLog::getNode(int nid) { + ToNodeMap::iterator i = d_toNode.find(nid); + Assert(i != d_toNode.end()); + return (*i).second; +} + +TreeLog::const_iterator TreeLog::begin() const { return d_toNode.begin(); } +TreeLog::const_iterator TreeLog::end() const { return d_toNode.end(); } + +int TreeLog::getExecutionOrd(){ + int res = next_exec_ord; + ++next_exec_ord; + return res; +} +void TreeLog::makeInactive(){ d_active = false; } +void TreeLog::makeActive(){ d_active = true; } +bool TreeLog::isActivelyLogging() const { return d_active; } + + +PrimitiveVec::PrimitiveVec() + : len(0) + , inds(NULL) + , coeffs(NULL) +{} + +PrimitiveVec::~PrimitiveVec(){ + clear(); +} +bool PrimitiveVec::initialized() const { + return inds != NULL; +} +void PrimitiveVec::clear() { + if(initialized()){ + delete[] inds; + delete[] coeffs; + len = 0; + inds = NULL; + coeffs = NULL; + } +} +void PrimitiveVec::setup(int l){ + Assert(!initialized()); + len = l; + inds = new int[1+len]; + coeffs = new double[1+len]; +} +void PrimitiveVec::print(std::ostream& out) const{ + Assert(initialized()); + out << len << " "; + out.precision(15); + for(int i = 1; i <= len; ++i){ + out << "["<< inds[i] <<", " << coeffs[i]<<"]"; + } +} +std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv){ + pv.print(os); + return os; +} + +CutInfo::CutInfo(CutInfoKlass kl, int eid, int o) + : d_klass(kl) + , d_execOrd(eid) + , d_poolOrd(o) + , d_cutType(kind::UNDEFINED_KIND) + , d_cutRhs() + , d_cutVec() + , d_mAtCreation(-1) + , d_rowId(-1) + , d_exactPrecision(NULL) + , d_explanation(NULL) +{} + +CutInfo::~CutInfo(){ + if(d_exactPrecision == NULL){ delete d_exactPrecision; } + if(d_explanation == NULL){ delete d_explanation; } +} + +int CutInfo::getId() const { + return d_execOrd; +} + +int CutInfo::getRowId() const{ + return d_rowId; +} + +void CutInfo::setRowId(int rid){ + d_rowId = rid; +} + +void CutInfo::print(ostream& out) const{ + out << "[CutInfo " << d_execOrd << " " << d_poolOrd + << " " << d_klass << " " << d_cutType << " " << d_cutRhs + << " "; + d_cutVec.print(out); + out << "]" << endl; +} + +PrimitiveVec& CutInfo::getCutVector(){ + return d_cutVec; +} + +const PrimitiveVec& CutInfo::getCutVector() const{ + return d_cutVec; +} + +// void CutInfo::init_cut(int l){ +// cut_vec.setup(l); +// } + +Kind CutInfo::getKind() const{ + return d_cutType; +} + +void CutInfo::setKind(Kind k){ + Assert(k == kind::LEQ || k == kind::GEQ); + d_cutType = k; +} + +double CutInfo::getRhs() const{ + return d_cutRhs; +} + +void CutInfo::setRhs(double r){ + d_cutRhs = r; +} + +bool CutInfo::reconstructed() const{ + return d_exactPrecision != NULL; +} + +CutInfoKlass CutInfo::getKlass() const{ + return d_klass; +} + +int CutInfo::poolOrdinal() const{ + return d_poolOrd; +} + +void CutInfo::setDimensions(int N, int M){ + d_mAtCreation = M; + d_N = N; +} + +int CutInfo::getN() const{ + return d_N; +} + +int CutInfo::getMAtCreation() const{ + return d_mAtCreation; +} + +/* Returns true if the cut has an explanation. */ +bool CutInfo::proven() const{ + return d_explanation != NULL; +} + +bool CutInfo::operator<(const CutInfo& o) const{ + return d_execOrd < o.d_execOrd; +} + + +void CutInfo::setReconstruction(const DenseVector& ep){ + Assert(!reconstructed()); + d_exactPrecision = new DenseVector(ep); +} + +void CutInfo::setExplanation(const ConstraintCPVec& ex){ + Assert(reconstructed()); + if(d_explanation == NULL){ + d_explanation = new ConstraintCPVec(ex); + }else{ + *d_explanation = ex; + } +} + +void CutInfo::swapExplanation(ConstraintCPVec& ex){ + Assert(reconstructed()); + Assert(!proven()); + if(d_explanation == NULL){ + d_explanation = new ConstraintCPVec(); + } + d_explanation->swap(ex); +} + +const DenseVector& CutInfo::getReconstruction() const { + Assert(reconstructed()); + return *d_exactPrecision; +} + +void CutInfo::clearReconstruction(){ + if(proven()){ + delete d_explanation; + d_explanation = NULL; + } + + if(reconstructed()){ + delete d_exactPrecision; + d_exactPrecision = NULL; + } + + Assert(!reconstructed()); + Assert(!proven()); +} + +const ConstraintCPVec& CutInfo::getExplanation() const { + Assert(proven()); + return *d_explanation; +} + +std::ostream& operator<<(std::ostream& os, const CutInfo& ci){ + ci.print(os); + return os; +} + +std::ostream& operator<<(std::ostream& out, CutInfoKlass kl){ + switch(kl){ + case MirCutKlass: + out << "MirCutKlass"; break; + case GmiCutKlass: + out << "GmiCutKlass"; break; + case BranchCutKlass: + out << "BranchCutKlass"; break; + case RowsDeletedKlass: + out << "RowDeletedKlass"; break; + case UnknownKlass: + out << "UnknownKlass"; break; + default: + out << "unexpected CutInfoKlass"; break; + } + return out; +} +bool NodeLog::isBranch() const{ + return d_brVar >= 0; +} + +NodeLog::NodeLog() + : d_nid(-1) + , d_parent(NULL) + , d_tl(NULL) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar() +{} + +NodeLog::NodeLog(TreeLog* tl, int node, const RowIdMap& m) + : d_nid(node) + , d_parent(NULL) + , d_tl(tl) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar(m) +{} + +NodeLog::NodeLog(TreeLog* tl, NodeLog* parent, int node) + : d_nid(node) + , d_parent(parent) + , d_tl(tl) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar() +{} + +NodeLog::~NodeLog(){ + CutSet::iterator i = d_cuts.begin(), iend = d_cuts.end(); + for(; i != iend; ++i){ + CutInfo* c = *i; + delete c; + } + d_cuts.clear(); + Assert(d_cuts.empty()); +} + +std::ostream& operator<<(std::ostream& os, const NodeLog& nl){ + nl.print(os); + return os; +} + +void NodeLog::copyParentRowIds() { + Assert(d_parent != NULL); + d_rowId2ArithVar = d_parent->d_rowId2ArithVar; +} + +int NodeLog::branchVariable() const { + return d_brVar; +} +double NodeLog::branchValue() const{ + return d_brVal; +} +int NodeLog::getNodeId() const { + return d_nid; +} +int NodeLog::getDownId() const{ + return d_downId; +} +int NodeLog::getUpId() const{ + return d_upId; +} +void NodeLog::addSelected(int ord, int sel){ + Assert(d_rowIdsSelected.find(ord) == d_rowIdsSelected.end()); + d_rowIdsSelected[ord] = sel; + Debug("approx::nodelog") << "addSelected("<< ord << ", "<< sel << ")" << endl; +} +void NodeLog::applySelected() { + CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(), todelete; + while(iter != iend){ + CutInfo* curr = *iter; + int poolOrd = curr->poolOrdinal(); + if(curr->getRowId() >= 0 ){ + // selected previously, kip + ++iter; + }else if(curr->getKlass() == RowsDeletedKlass){ + // skip + ++iter; + }else if(curr->getKlass() == BranchCutKlass){ + // skip + ++iter; + }else if(d_rowIdsSelected.find(poolOrd) == d_rowIdsSelected.end()){ + todelete = iter; + ++iter; + d_cuts.erase(todelete); + delete curr; + }else{ + Debug("approx::nodelog") << "applySelected " << curr->getId() << " " << poolOrd << "->" << d_rowIdsSelected[poolOrd] << endl; + curr->setRowId( d_rowIdsSelected[poolOrd] ); + ++iter; + } + } + d_rowIdsSelected.clear(); +} + +void NodeLog::applyRowsDeleted(const RowsDeleted& rd) { + std::map currInOrd; //sorted + + const PrimitiveVec& cv = rd.getCutVector(); + std::vector sortedRemoved (cv.inds+1, cv.inds+cv.len+1); + sortedRemoved.push_back(INT_MAX); + std::sort(sortedRemoved.begin(), sortedRemoved.end()); + + if(Debug.isOn("approx::nodelog")){ + Debug("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "..."; + for(unsigned k = 0; kgetId() < rd.getId()){ + if(d_rowId2ArithVar.find(curr->getRowId()) != d_rowId2ArithVar.end()){ + if(curr->getRowId() >= min){ + currInOrd.insert(make_pair(curr->getRowId(), curr)); + } + } + } + ++iter; + } + + RowIdMap::const_iterator i, end; + i=d_rowId2ArithVar.begin(), end = d_rowId2ArithVar.end(); + for(; i != end; ++i){ + int key = (*i).first; + if(key >= min){ + if(currInOrd.find(key) == currInOrd.end()){ + CutInfo* null = NULL; + currInOrd.insert(make_pair(key, null)); + } + } + } + + + + std::map::iterator j, jend; + + int posInSorted = 0; + for(j = currInOrd.begin(), jend=currInOrd.end(); j!=jend; ++j){ + int origOrd = (*j).first; + ArithVar v = d_rowId2ArithVar[origOrd]; + int headRemovedOrd = sortedRemoved[posInSorted]; + while(headRemovedOrd < origOrd){ + ++posInSorted; + headRemovedOrd = sortedRemoved[posInSorted]; + } + // headRemoveOrd >= origOrd + Assert(headRemovedOrd >= origOrd); + + CutInfo* ci = (*j).second; + if(headRemovedOrd == origOrd){ + + if(ci == NULL){ + Debug("approx::nodelog") << "deleting from above because of " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + }else{ + Debug("approx::nodelog") << "deleting " << ci << " because of " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + ci->setRowId(-1); + } + }else{ + Assert(headRemovedOrd > origOrd); + // headRemoveOrd > origOrd + int newOrd = origOrd - posInSorted; + Assert(newOrd > 0); + if(ci == NULL){ + Debug("approx::nodelog") << "shifting above down due to " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + Debug("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + mapRowId(newOrd, v); + }else{ + Debug("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + Debug("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl; + ci->setRowId(newOrd); + d_rowId2ArithVar.erase(origOrd); + mapRowId(newOrd, v); + } + } + } + +} + +// void NodeLog::adjustRowId(CutInfo& ci, const RowsDeleted& rd) { +// int origRowId = ci.getRowId(); +// int newRowId = ci.getRowId(); +// ArithVar v = d_rowId2ArithVar[origRowId]; + +// const PrimitiveVec& cv = rd.getCutVector(); + +// for(int j = 1, N = cv.len; j <= N; j++){ +// int ind = cv.inds[j]; +// if(ind == origRowId){ +// newRowId = -1; +// break; +// }else if(ind < origRowId){ +// newRowId--; +// } +// } + +// if(newRowId < 0){ +// cout << "deleting " << ci << " because of " << rd << endl; +// cout << "had " << origRowId << " <-> " << v << endl; +// d_rowId2ArithVar.erase(origRowId); +// ci.setRowId(-1); +// }else if(newRowId != origRowId){ +// cout << "adjusting " << ci << " because of " << rd << endl; +// cout << "had " << origRowId << " <-> " << v << endl; +// cout << "now have " << newRowId << " <-> " << v << endl; +// d_rowId2ArithVar.erase(origRowId); +// ci.setRowId(newRowId); +// mapRowId(newRowId, v); +// }else{ +// cout << "row id unchanged " << ci << " because of " << rd << endl; +// } +// } + + +ArithVar NodeLog::lookupRowId(int rowId) const{ + RowIdMap::const_iterator i = d_rowId2ArithVar.find(rowId); + if(i == d_rowId2ArithVar.end()){ + return ARITHVAR_SENTINEL; + }else{ + return (*i).second; + } +} + +void NodeLog::mapRowId(int rowId, ArithVar v){ + Assert(lookupRowId(rowId) == ARITHVAR_SENTINEL); + Debug("approx::nodelog") + << "On " << getNodeId() + << " adding row id " << rowId << " <-> " << v << endl; + d_rowId2ArithVar[rowId] = v; +} + + + +void NodeLog::addCut(CutInfo* ci){ + Assert(ci != NULL); + d_cuts.insert(ci); +} + +void NodeLog::print(ostream& o) const{ + o << "[n" << getNodeId(); + for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter ){ + CutInfo* cut = *iter; + o << ", " << cut->poolOrdinal(); + if(cut->getRowId() >= 0){ + o << " " << cut->getRowId(); + } + } + o << "]" << std::endl; +} + +void NodeLog::closeNode(){ + Assert(d_stat == Open); + d_stat = Closed; +} + +void NodeLog::setBranch(int br, double val, int d, int u){ + Assert(d_stat == Open); + d_brVar = br; + d_brVal = val; + d_downId = d; + d_upId = u; + d_stat = Branched; +} + +TreeLog::TreeLog() + : next_exec_ord(0) + , d_toNode() + , d_branches() + , d_numCuts(0) + , d_active(false) +{ + NodeLog::RowIdMap empty; + reset(empty); +} + +int TreeLog::getRootId() const{ + return 1; +} + +NodeLog& TreeLog::getRootNode(){ + return getNode(getRootId()); +} + +void TreeLog::clear(){ + next_exec_ord = 0; + d_toNode.clear(); + d_branches.purge(); + + d_numCuts = 0; + + // add root +} + +void TreeLog::reset(const NodeLog::RowIdMap& m){ + clear(); + d_toNode.insert(make_pair(getRootId(), NodeLog(this, getRootId(), m))); +} + +void TreeLog::addCut(){ d_numCuts++; } +uint32_t TreeLog::cutCount() const { return d_numCuts; } +void TreeLog::logBranch(uint32_t x){ + d_branches.add(x); +} +uint32_t TreeLog::numBranches(uint32_t x){ + return d_branches.count(x); +} + +void TreeLog::branch(int nid, int br, double val, int dn, int up){ + NodeLog& nl = getNode(nid); + nl.setBranch(br, val, dn, up); + + d_toNode.insert(make_pair(dn, NodeLog(this, &nl, dn))); + d_toNode.insert(make_pair(up, NodeLog(this, &nl, up))); +} + +void TreeLog::close(int nid){ + NodeLog& nl = getNode(nid); + nl.closeNode(); +} + + + +// void TreeLog::applySelected() { +// std::map::iterator iter, end; +// for(iter = d_toNode.begin(), end = d_toNode.end(); iter != end; ++iter){ +// NodeLog& onNode = (*iter).second; +// //onNode.applySelected(); +// } +// } + +void TreeLog::print(ostream& o) const{ + o << "TreeLog: " << d_toNode.size() << std::endl; + for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter){ + const NodeLog& onNode = (*iter).second; + onNode.print(o); + } +} + +void TreeLog::applyRowsDeleted(int nid, const RowsDeleted& rd){ + NodeLog& nl = getNode(nid); + nl.applyRowsDeleted(rd); +} + +void TreeLog::mapRowId(int nid, int ind, ArithVar v){ + NodeLog& nl = getNode(nid); + nl.mapRowId(ind, v); +} + +void DenseVector::purge() { + lhs.purge(); + rhs = Rational(0); +} + +RowsDeleted::RowsDeleted(int execOrd, int nrows, const int num[]) + : CutInfo(RowsDeletedKlass, execOrd, 0) +{ + d_cutVec.setup(nrows); + for(int j=1; j <= nrows; j++){ + d_cutVec.coeffs[j] = 0; + d_cutVec.inds[j] = num[j]; + } +} + +BranchCutInfo::BranchCutInfo(int execOrd, int br, Kind dir, double val) + : CutInfo(BranchCutKlass, execOrd, 0) +{ + d_cutVec.setup(1); + d_cutVec.inds[1] = br; + d_cutVec.coeffs[1] = +1.0; + d_cutRhs = val; + d_cutType = dir; +} + +void TreeLog::printBranchInfo(ostream& os) const{ + uint32_t total = 0; + DenseMultiset::const_iterator iter = d_branches.begin(), iend = d_branches.end(); + for(; iter != iend; ++iter){ + uint32_t el = *iter; + total += el; + } + os << "printBranchInfo() : " << total << endl; + iter = d_branches.begin(), iend = d_branches.end(); + for(; iter != iend; ++iter){ + uint32_t el = *iter; + os << "["<& v){ + out << "[DenseVec len " << v.size(); + DenseMap::const_iterator iter, end; + for(iter = v.begin(), end = v.end(); iter != end; ++iter){ + ArithVar x = *iter; + out << ", "<< x << " " << v[x]; + } + out << "]"; +} + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/cut_log.h b/src/theory/arith/cut_log.h new file mode 100644 index 000000000..123313617 --- /dev/null +++ b/src/theory/arith/cut_log.h @@ -0,0 +1,281 @@ + +#include "cvc4_private.h" + +#pragma once + +#include "expr/kind.h" +#include "util/statistics_registry.h" +#include "theory/arith/arithvar.h" +#include "theory/arith/constraint_forward.h" +#include "util/dense_map.h" +#include +#include +#include +#include + +namespace CVC4 { +namespace theory { +namespace arith { + +/** A low level vector of indexed doubles. */ +struct PrimitiveVec { + int len; + int* inds; + double* coeffs; + PrimitiveVec(); + ~PrimitiveVec(); + bool initialized() const; + void clear(); + void setup(int l); + void print(std::ostream& out) const; +}; +std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv); + +struct DenseVector { + DenseMap lhs; + Rational rhs; + void purge(); + void print(std::ostream& os) const; + + static void print(std::ostream& os, const DenseMap& lhs); +}; + +/** The different kinds of cuts. */ +enum CutInfoKlass{ MirCutKlass, GmiCutKlass, BranchCutKlass, + RowsDeletedKlass, + UnknownKlass}; +std::ostream& operator<<(std::ostream& os, CutInfoKlass kl); + +/** A general class for describing a cut. */ +class CutInfo { +protected: + CutInfoKlass d_klass; + int d_execOrd; + + int d_poolOrd; /* cut's ordinal in the current node pool */ + Kind d_cutType; /* Lowerbound, upperbound or undefined. */ + double d_cutRhs; /* right hand side of the cut */ + PrimitiveVec d_cutVec; /* vector of the cut */ + + /** + * The number of rows at the time the cut was made. + * This is required to descramble indices after the fact! + */ + int d_mAtCreation; + + /** This is the number of structural variables. */ + int d_N; + + /** if selected, make this non-zero */ + int d_rowId; + + /* If the cut has been successfully created, + * the cut is stored in exact precision in d_exactPrecision. + * If the cut has not yet been proven, this is null. + */ + DenseVector* d_exactPrecision; + + ConstraintCPVec* d_explanation; + +public: + CutInfo(CutInfoKlass kl, int cutid, int ordinal); + + virtual ~CutInfo(); + + int getId() const; + + int getRowId() const; + void setRowId(int rid); + + void print(std::ostream& out) const; + //void init_cut(int l); + PrimitiveVec& getCutVector(); + const PrimitiveVec& getCutVector() const; + + Kind getKind() const; + void setKind(Kind k); + + + void setRhs(double r); + double getRhs() const; + + CutInfoKlass getKlass() const; + int poolOrdinal() const; + + void setDimensions(int N, int M); + int getN() const; + int getMAtCreation() const; + + bool operator<(const CutInfo& o) const; + + /* Returns true if the cut was successfully made in exact precision.*/ + bool reconstructed() const; + + /* Returns true if the cut has an explanation. */ + bool proven() const; + + void setReconstruction(const DenseVector& ep); + void setExplanation(const ConstraintCPVec& ex); + void swapExplanation(ConstraintCPVec& ex); + + const DenseVector& getReconstruction() const; + const ConstraintCPVec& getExplanation() const; + + void clearReconstruction(); +}; +std::ostream& operator<<(std::ostream& os, const CutInfo& ci); + +struct BranchCutInfo : public CutInfo { + BranchCutInfo(int execOrd, int br, Kind dir, double val); +}; + +struct RowsDeleted : public CutInfo { + RowsDeleted(int execOrd, int nrows, const int num[]); +}; + +class TreeLog; + +class NodeLog { +private: + int d_nid; + NodeLog* d_parent; /* If null this is the root */ + TreeLog* d_tl; /* TreeLog containing the node. */ + + struct CmpCutPointer{ + int operator()(const CutInfo* a, const CutInfo* b) const{ + return *a < *b; + } + }; + typedef std::set CutSet; + CutSet d_cuts; + std::map d_rowIdsSelected; + + enum Status {Open, Closed, Branched}; + Status d_stat; + + int d_brVar; // branching variable + double d_brVal; + int d_downId; + int d_upId; + +public: + typedef __gnu_cxx::hash_map RowIdMap; +private: + RowIdMap d_rowId2ArithVar; + +public: + NodeLog(); /* default constructor. */ + NodeLog(TreeLog* tl, int node, const RowIdMap& m); /* makes a root node. */ + NodeLog(TreeLog* tl, NodeLog* parent, int node);/* makes a non-root node. */ + + ~NodeLog(); + + int getNodeId() const; + void addSelected(int ord, int sel); + void applySelected(); + void addCut(CutInfo* ci); + void print(std::ostream& o) const; + + bool isRoot() const; + const NodeLog& getParent() const; + + void copyParentRowIds(); + + bool isBranch() const; + int branchVariable() const; + double branchValue() const; + + typedef CutSet::const_iterator const_iterator; + const_iterator begin() const; + const_iterator end() const; + + void setBranch(int br, double val, int dn, int up); + void closeNode(); + + int getDownId() const; + int getUpId() const; + + /** + * Looks up a row id to the appropraite arith variable. + * Be careful these are deleted in context during replay! + * failure returns ARITHVAR_SENTINEL */ + ArithVar lookupRowId(int rowId) const; + + /** + * Maps a row id to an arithvar. + * Be careful these are deleted in context during replay! + */ + void mapRowId(int rowid, ArithVar v); + void applyRowsDeleted(const RowsDeleted& rd); + +}; +std::ostream& operator<<(std::ostream& os, const NodeLog& nl); + +class ApproximateSimplex; +class TreeLog { +private: + ApproximateSimplex* d_generator; + + int next_exec_ord; + typedef std::map ToNodeMap; + ToNodeMap d_toNode; + DenseMultiset d_branches; + + uint32_t d_numCuts; + + bool d_active; + +public: + TreeLog(); + + NodeLog& getNode(int nid); + void branch(int nid, int br, double val, int dn, int up); + void close(int nid); + + //void applySelected(); + void print(std::ostream& o) const; + + typedef ToNodeMap::const_iterator const_iterator; + const_iterator begin() const; + const_iterator end() const; + + int getExecutionOrd(); + + void reset(const NodeLog::RowIdMap& m); + + // Applies rd tp to the node with id nid + void applyRowsDeleted(int nid, const RowsDeleted& rd); + + // Synonym for getNode(nid).mapRowId(ind, v) + void mapRowId(int nid, int ind, ArithVar v); + +private: + void clear(); + +public: + void makeInactive(); + void makeActive(); + + bool isActivelyLogging() const; + + void addCut(); + uint32_t cutCount() const; + + void logBranch(uint32_t x); + uint32_t numBranches(uint32_t x); + + int getRootId() const; + + uint32_t numNodes() const{ + return d_toNode.size(); + } + + NodeLog& getRootNode(); + void printBranchInfo(std::ostream& os) const; +}; + + + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/dio_solver.cpp b/src/theory/arith/dio_solver.cpp index 39c2d859b..c9f9df727 100644 --- a/src/theory/arith/dio_solver.cpp +++ b/src/theory/arith/dio_solver.cpp @@ -281,7 +281,7 @@ void DioSolver::moveMinimumByAbsToQueueFront(){ size_t N = d_currentF.size(); for(size_t i=1; i < N; ++i){ Monomial curr = d_trail[d_currentF[i]].d_minimalMonomial; - if(curr.absLessThan(minMonomial)){ + if(curr.absCmp(minMonomial) < 0){ indexInQueue = i; minMonomial = curr; } diff --git a/src/theory/arith/dio_solver.h b/src/theory/arith/dio_solver.h index 5039f826c..32b8382fa 100644 --- a/src/theory/arith/dio_solver.h +++ b/src/theory/arith/dio_solver.h @@ -102,17 +102,17 @@ private: }; context::CDList d_trail; - /** Compare by d_minimal. */ - struct TrailMinimalCoefficientOrder { - const context::CDList& d_trail; - TrailMinimalCoefficientOrder(const context::CDList& trail): - d_trail(trail) - {} - - bool operator()(TrailIndex i, TrailIndex j){ - return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial); - } - }; + // /** Compare by d_minimal. */ + // struct TrailMinimalCoefficientOrder { + // const context::CDList& d_trail; + // TrailMinimalCoefficientOrder(const context::CDList& trail): + // d_trail(trail) + // {} + + // bool operator()(TrailIndex i, TrailIndex j){ + // return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial); + // } + // }; /** * A substitution is stored as a constraint in the trail together with diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp index dea78acf7..6d341ed12 100644 --- a/src/theory/arith/error_set.cpp +++ b/src/theory/arith/error_set.cpp @@ -39,7 +39,7 @@ ErrorInformation::ErrorInformation() Debug("arith::error::mem") << "def constructor " << d_variable << " " << d_amount << endl; } -ErrorInformation::ErrorInformation(ArithVar var, Constraint vio, int sgn) +ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn) : d_variable(var) , d_violated(vio) , d_sgn(sgn) @@ -105,7 +105,7 @@ ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){ return *this; } -void ErrorInformation::reset(Constraint c, int sgn){ +void ErrorInformation::reset(ConstraintP c, int sgn){ Assert(!isRelaxed()); Assert(c != NullConstraint); d_violated = c; @@ -272,7 +272,7 @@ void ErrorSet::transitionVariableOutOfError(ArithVar v) { ErrorInformation& ei = d_errInfo.get(v); Assert(ei.debugInitialized()); if(ei.isRelaxed()){ - Constraint viol = ei.getViolated(); + ConstraintP viol = ei.getViolated(); if(ei.sgn() > 0){ d_variables.setLowerBoundConstraint(viol); }else{ @@ -293,7 +293,7 @@ void ErrorSet::transitionVariableIntoError(ArithVar v) { Assert(inconsistent(v)); bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0; int sgn = vilb ? 1 : -1; - Constraint c = vilb ? + ConstraintP c = vilb ? d_variables.getLowerBoundConstraint(v) : d_variables.getUpperBoundConstraint(v); d_errInfo.set(v, ErrorInformation(v, c, sgn)); ErrorInformation& ei = d_errInfo.get(v); @@ -373,7 +373,7 @@ int ErrorSet::popSignal() { Assert(!vilb || !viub); int currSgn = vilb ? 1 : -1; if(currSgn != prevSgn){ - Constraint curr = vilb ? d_variables.getLowerBoundConstraint(back) + ConstraintP curr = vilb ? d_variables.getLowerBoundConstraint(back) : d_variables.getUpperBoundConstraint(back); ei.reset(curr, currSgn); } diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h index d1b692cb4..b87282ba0 100644 --- a/src/theory/arith/error_set.h +++ b/src/theory/arith/error_set.h @@ -120,7 +120,7 @@ private: * This needs to be saved in case that the * violated constraint */ - Constraint d_violated; + ConstraintP d_violated; /** * This is the sgn of the first derivate the variable must move to satisfy @@ -155,12 +155,12 @@ private: public: ErrorInformation(); - ErrorInformation(ArithVar var, Constraint vio, int sgn); + ErrorInformation(ArithVar var, ConstraintP vio, int sgn); ~ErrorInformation(); ErrorInformation(const ErrorInformation& ei); ErrorInformation& operator=(const ErrorInformation& ei); - void reset(Constraint c, int sgn); + void reset(ConstraintP c, int sgn); inline ArithVar getVariable() const { return d_variable; } @@ -192,7 +192,7 @@ public: } inline const FocusSetHandle& getHandle() const{ return d_handle; } - inline Constraint getViolated() const { return d_violated; } + inline ConstraintP getViolated() const { return d_violated; } bool debugInitialized() const { return @@ -389,7 +389,7 @@ public: return d_errInfo[a].getMetric(); } - Constraint getViolated(ArithVar a) const { + ConstraintP getViolated(ArithVar a) const { return d_errInfo[a].getViolated(); } diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp index c0bffdf07..70a322959 100644 --- a/src/theory/arith/fc_simplex.cpp +++ b/src/theory/arith/fc_simplex.cpp @@ -536,7 +536,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit } if(selected.describesPivot()){ - Constraint limiting = selected.limiting(); + ConstraintP limiting = selected.limiting(); ArithVar basic = limiting->getVariable(); Assert(d_linEq.basicIsTracked(basic)); d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue()); diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp index 5817a3629..f4c1ae10c 100644 --- a/src/theory/arith/linear_equality.cpp +++ b/src/theory/arith/linear_equality.cpp @@ -494,7 +494,7 @@ const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool ro return NULL; } -void LinearEqualityModule::propagateBasicFromRow(Constraint c){ +void LinearEqualityModule::propagateBasicFromRow(ConstraintP c){ Assert(c != NullConstraint); Assert(c->isUpperBound() || c->isLowerBound()); Assert(!c->assertedToTheTheory()); @@ -504,12 +504,12 @@ void LinearEqualityModule::propagateBasicFromRow(Constraint c){ ArithVar basic = c->getVariable(); RowIndex ridx = d_tableau.basicToRowIndex(basic); - vector bounds; + ConstraintCPVec bounds; propagateRow(bounds, ridx, upperBound, c); c->impliedBy(bounds); } -void LinearEqualityModule::propagateRow(vector& into, RowIndex ridx, bool rowUp, Constraint c){ +void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c){ Assert(!c->assertedToTheTheory()); Assert(c->canBePropagated()); Assert(!c->hasProof()); @@ -529,7 +529,7 @@ void LinearEqualityModule::propagateRow(vector& into, RowIndex ridx, int sgn = a_ij.sgn(); Assert(sgn != 0); bool selectUb = rowUp ? (sgn > 0) : (sgn < 0); - Constraint bound = selectUb + ConstraintCP bound = selectUb ? d_variables.getUpperBoundConstraint(nonbasic) : d_variables.getLowerBoundConstraint(nonbasic); @@ -541,12 +541,12 @@ void LinearEqualityModule::propagateRow(vector& into, RowIndex ridx, << v << ") done" << endl; } -Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const { +ConstraintP 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 ? + ConstraintP c = ub ? d_variables.getUpperBoundConstraint(v) : d_variables.getLowerBoundConstraint(v); @@ -556,7 +556,7 @@ Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRation weakened = false; - Constraint weaker = ub? + ConstraintP weaker = ub? c->getStrictlyWeakerUpperBound(true, true): c->getStrictlyWeakerLowerBound(true, true); @@ -591,7 +591,7 @@ Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRation return c; } -Node LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar) const { +void LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, RaiseConflict& rc) const { TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime); const DeltaRational& assignment = d_variables.getAssignment(basicVar); @@ -606,29 +606,25 @@ Node LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basic 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); + ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar); Debug("weak") << "weak : " << weakening << " " << c->assertedToTheTheory() << " " << d_variables.getAssignment(v) << " " - << c << endl - << c->explainForConflict() << endl; + << c << endl; anyWeakenings = anyWeakenings || weakening; - Debug("weak") << "weak : " << c->explainForConflict() << endl; - c->explainForConflict(conflict); + rc.addConstraint(c); } ++d_statistics.d_weakeningAttempts; if(anyWeakenings){ ++d_statistics.d_weakeningSuccesses; } - return conflict; } ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const { @@ -787,7 +783,7 @@ bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const { int coeffSgn = u.getCoefficient().sgn(); int nbdir = u.nonbasicDirection(); - Constraint c = u.limiting(); + ConstraintP c = u.limiting(); int toUB = (c->getType() == UpperBound || c->getType() == Equality) ? 1 : 0; int toLB = (c->getType() == LowerBound || @@ -886,7 +882,7 @@ bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub Assert(basicIsTracked(currBasic)); - Constraint bound = ub ? + ConstraintP bound = ub ? d_variables.getUpperBoundConstraint(currBasic): d_variables.getLowerBoundConstraint(currBasic); @@ -1003,7 +999,7 @@ UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, b ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex()); ArithVar nb = entry.getColVar(); - Constraint bound = ub ? + ConstraintP bound = ub ? d_variables.getUpperBoundConstraint(currBasic): d_variables.getLowerBoundConstraint(currBasic); @@ -1031,14 +1027,14 @@ UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& Debug("speculativeUpdate") << "focusCoeff " << focusCoeff << endl; if(d_variables.hasUpperBound(nb)){ - Constraint ub = d_variables.getUpperBoundConstraint(nb); + ConstraintP 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); + ConstraintP 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; diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h index 293a0ddad..804ad29ac 100644 --- a/src/theory/arith/linear_equality.h +++ b/src/theory/arith/linear_equality.h @@ -46,7 +46,7 @@ namespace arith { struct Border{ // The constraint for the border - Constraint d_bound; + ConstraintP d_bound; // The change to the nonbasic to reach the border DeltaRational d_diff; @@ -65,11 +65,11 @@ struct Border{ d_bound(NullConstraint) // ignore the other values {} - Border(Constraint l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub): + Border(ConstraintP 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): + Border(ConstraintP 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{ @@ -414,13 +414,13 @@ public: * The constraint on a basic variable b is implied by the constraints * on its row. This is a wrapper for propagateRow(). */ - void propagateBasicFromRow(Constraint c); + void propagateBasicFromRow(ConstraintP c); /** * Exports either the explanation of an upperbound or a lower bound * of the basic variable basic, using the non-basic variables in the row. */ - void propagateRow(std::vector& into, RowIndex ridx, bool rowUp, Constraint c); + void propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c); /** * Computes the value of a basic variable using the assignments @@ -592,26 +592,26 @@ private: * with the weakest possible constraint that is consistent with the surplus * surplus. */ - Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, + ConstraintP 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; + void minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, RaiseConflict& rc) 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 void generateConflictAboveUpperBound(ArithVar conflictVar, RaiseConflict& rc) const { + minimallyWeakConflict(true, conflictVar, rc); } - inline Node generateConflictBelowLowerBound(ArithVar conflictVar) const { - return minimallyWeakConflict(false, conflictVar); + inline void generateConflictBelowLowerBound(ArithVar conflictVar, RaiseConflict& rc) const { + minimallyWeakConflict(false, conflictVar, rc); } /** diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h index d93b6986e..084281c04 100644 --- a/src/theory/arith/matrix.h +++ b/src/theory/arith/matrix.h @@ -362,16 +362,18 @@ public: template class Matrix { -protected: - +public: typedef MatrixEntry Entry; +protected: typedef CVC4::theory::arith::RowVector RowVectorT; - typedef typename RowVectorT::const_iterator RowIterator; - typedef CVC4::theory::arith::ColumnVector ColumnVectorT; + +public: + typedef typename RowVectorT::const_iterator RowIterator; typedef typename ColumnVectorT::const_iterator ColIterator; +protected: // RowTable : RowID |-> RowVector typedef std::vector< RowVectorT > RowTable; RowTable d_rows; @@ -532,6 +534,12 @@ public: d_columns.push_back(ColumnVector(&d_entries)); } + void increaseSizeTo(size_t s){ + while(getNumColumns() < s){ + increaseSize(); + } + } + const RowVector& getRow(RowIndex r) const { Assert(r < d_rows.size()); return d_rows[r]; @@ -600,7 +608,33 @@ public: d_mergeBuffer.purge(); } - /** to += mult * buffer. */ + /* to *= mult */ + void multiplyRowByConstant(RowIndex to, const T& mult){ + RowIterator i = getRow(to).begin(); + RowIterator i_end = getRow(to).end(); + for( ; i != i_end; ++i){ + EntryID id = i.getID(); + Entry& entry = d_entries.get(id); + T& coeff = entry.getCoefficient(); + coeff *= mult; + } + } + + /** to += mult * from. + * Use the more efficient rowPlusBufferTimesConstant() for + * repeated use. + */ + void rowPlusRowTimesConstant(RowIndex to, RowIndex from, const T& mult){ + Assert(to != from); + loadRowIntoBuffer(from); + rowPlusBufferTimesConstant(to, mult); + clearBuffer(); + } + + /** to += mult * buffer. + * Invalidates coefficients on the row. + * (mult should never be a direct copy of a coefficient!) + */ void rowPlusBufferTimesConstant(RowIndex to, const T& mult){ Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL); Assert(to != ROW_INDEX_SENTINEL); diff --git a/src/theory/arith/normal_form.cpp b/src/theory/arith/normal_form.cpp index 4edc55cca..3adb72f37 100644 --- a/src/theory/arith/normal_form.cpp +++ b/src/theory/arith/normal_form.cpp @@ -18,6 +18,7 @@ #include "theory/arith/normal_form.h" #include "theory/arith/arith_utilities.h" #include +#include "theory/theory.h" using namespace std; @@ -25,6 +26,47 @@ namespace CVC4 { namespace theory { namespace arith { +Constant Constant::mkConstant(const Rational& rat) { + return Constant(mkRationalNode(rat)); +} + +size_t Variable::getComplexity() const{ + return 1u; +} + +size_t VarList::getComplexity() const{ + if(empty()){ + return 1; + }else if(singleton()){ + return 1; + }else{ + return size() + 1; + } +} + +size_t Monomial::getComplexity() const{ + return getConstant().getComplexity() + getVarList().getComplexity(); +} + +size_t Polynomial::getComplexity() const{ + size_t cmp = 0; + iterator i = begin(), e = end(); + for(; i != e; ++i){ + Monomial m = *i; + cmp += m.getComplexity(); + } + return cmp; +} + +size_t Constant::getComplexity() const{ + return getValue().complexity(); +} + +bool Variable::isLeafMember(Node n){ + return (!isRelationOperator(n.getKind())) && + (Theory::isLeafOf(n, theory::THEORY_ARITH)); +} + bool Variable::isDivMember(Node n){ switch(n.getKind()){ case kind::DIVISION: @@ -39,6 +81,8 @@ bool Variable::isDivMember(Node n){ } } + + bool VarList::isSorted(iterator start, iterator end) { return __gnu_cxx::is_sorted(start, end); } @@ -161,27 +205,76 @@ Monomial Monomial::operator*(const Monomial& mono) const { return Monomial::mkMonomial(newConstant, newVL); } -vector Monomial::sumLikeTerms(const std::vector & monos) { +// vector Monomial::sumLikeTerms(const std::vector & monos) { +// Assert(isSorted(monos)); +// vector outMonomials; +// typedef vector::const_iterator iterator; +// for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;) { +// Rational constant = (*rangeIter).getConstant().getValue(); +// VarList varList = (*rangeIter).getVarList(); +// ++rangeIter; +// while(rangeIter != end && varList == (*rangeIter).getVarList()) { +// constant += (*rangeIter).getConstant().getValue(); +// ++rangeIter; +// } +// if(constant != 0) { +// Constant asConstant = Constant::mkConstant(constant); +// Monomial nonZero = Monomial::mkMonomial(asConstant, varList); +// outMonomials.push_back(nonZero); +// } +// } + +// Assert(isStrictlySorted(outMonomials)); +// return outMonomials; +// } + +void Monomial::sort(std::vector& m){ + if(!isSorted(m)){ + std::sort(m.begin(), m.end()); + } +} + +void Monomial::combineAdjacentMonomials(std::vector& monos) { Assert(isSorted(monos)); - vector outMonomials; - typedef vector::const_iterator iterator; - for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;) { - Rational constant = (*rangeIter).getConstant().getValue(); - VarList varList = (*rangeIter).getVarList(); - ++rangeIter; - while(rangeIter != end && varList == (*rangeIter).getVarList()) { - constant += (*rangeIter).getConstant().getValue(); - ++rangeIter; + size_t writePos, readPos, N; + for(writePos = 0, readPos = 0, N = monos.size(); readPos < N;){ + Monomial& atRead = monos[readPos]; + const VarList& varList = atRead.getVarList(); + + size_t rangeEnd = readPos+1; + for(; rangeEnd < N; rangeEnd++){ + if(!(varList == monos[rangeEnd].getVarList())){ break; } } - if(constant != 0) { - Constant asConstant = Constant::mkConstant(constant); - Monomial nonZero = Monomial::mkMonomial(asConstant, varList); - outMonomials.push_back(nonZero); + // monos[i] for i in [readPos, rangeEnd) has the same var list + if(readPos+1 == rangeEnd){ // no addition needed + if(!atRead.getConstant().isZero()){ + Monomial cpy = atRead; // being paranoid here + monos[writePos] = cpy; + writePos++; + } + }else{ + Rational constant(monos[readPos].getConstant().getValue()); + for(size_t i=readPos+1; i < rangeEnd; ++i){ + constant += monos[i].getConstant().getValue(); + } + if(!constant.isZero()){ + Constant asConstant = Constant::mkConstant(constant); + Monomial nonZero = Monomial::mkMonomial(asConstant, varList); + monos[writePos] = nonZero; + writePos++; + } } + Assert(rangeEnd>readPos); + readPos = rangeEnd; } - - Assert(isStrictlySorted(outMonomials)); - return outMonomials; + if(writePos > 0 ){ + Monomial cp = monos[0]; + Assert(writePos <= N); + monos.resize(writePos, cp); + }else{ + monos.clear(); + } + Assert(isStrictlySorted(monos)); } void Monomial::print() const { @@ -199,12 +292,56 @@ Polynomial Polynomial::operator+(const Polynomial& vl) const { std::vector sortedMonos; merge_ranges(begin(), end(), vl.begin(), vl.end(), sortedMonos); - std::vector combined = Monomial::sumLikeTerms(sortedMonos); + Monomial::combineAdjacentMonomials(sortedMonos); + //std::vector combined = Monomial::sumLikeTerms(sortedMonos); - Polynomial result = mkPolynomial(combined); + Polynomial result = mkPolynomial(sortedMonos); return result; } + +Polynomial Polynomial::sumPolynomials(const std::vector& ps){ + if(ps.empty()){ + return mkZero(); + }else if(ps.size() <= 4){ + // if there are few enough polynomials just add them + Polynomial p = ps[0]; + for(size_t i = 1; i < ps.size(); ++i){ + p = p + ps[i]; + } + return p; + }else{ + // general case + std::map coeffs; + for(size_t i = 0, N = ps.size(); i monos; + std::map::const_iterator ci = coeffs.begin(), cend = coeffs.end(); + for(; ci != cend; ++ci){ + if(!(*ci).second.isZero()){ + Constant c = Constant::mkConstant((*ci).second); + Node n = (*ci).first; + VarList vl = VarList::parseVarList(n); + if(vl.empty()){ + monos.push_back(Monomial(c)); + }else{ + monos.push_back(Monomial(c, vl)); + } + } + } + Monomial::sort(monos); + Monomial::combineAdjacentMonomials(monos); + + Polynomial result = mkPolynomial(monos); + return result; + } +} + Polynomial Polynomial::operator-(const Polynomial& vl) const { Constant negOne = Constant::mkConstant(Rational(-1)); @@ -257,7 +394,7 @@ Polynomial Polynomial::operator*(const Monomial& mono) const { // Suppose this = (+ x y), mono = x, (* x y).getId() < (* x x).getId() // newMonos = <(* x x), (* x y)> after this loop. // This is not sorted according to the current VarList order. - std::sort(newMonos.begin(), newMonos.end()); + Monomial::sort(newMonos); return Polynomial::mkPolynomial(newMonos); } } @@ -281,7 +418,7 @@ Monomial Polynomial::selectAbsMinimum() const { ++iter; for(; iter != end(); ++iter){ Monomial curr = *iter; - if(curr.absLessThan(min)){ + if(curr.absCmp(min) < 0){ min = curr; } } @@ -315,10 +452,16 @@ Integer Polynomial::numeratorGCD() const { Assert(i!=e); Integer d = (*i).getConstant().getValue().getNumerator().abs(); + if(d.isOne()){ + return d; + } ++i; for(; i!=e; ++i){ Integer c = (*i).getConstant().getValue().getNumerator(); d = d.gcd(c); + if(d.isOne()){ + return d; + } } return d; } @@ -615,6 +758,22 @@ bool Comparison::rightIsConstant() const { } } +size_t Comparison::getComplexity() const{ + switch(comparisonKind()){ + case kind::CONST_BOOLEAN: return 1; + case kind::LT: + case kind::LEQ: + case kind::DISTINCT: + case kind::EQUAL: + case kind::GT: + case kind::GEQ: + return getLeft().getComplexity() + getRight().getComplexity(); + default: + Unhandled(comparisonKind()); + return -1; + } +} + Polynomial Comparison::getLeft() const { TNode left; Kind k = comparisonKind(); @@ -804,10 +963,10 @@ bool Comparison::isNormalEqualityOrDisequality() const { }else{ Monomial absMinRight = varRight.selectAbsMinimum(); Debug("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl; - if( mleft.absLessThan(absMinRight) ){ + if( mleft.absCmp(absMinRight) < 0){ return true; }else{ - return (!absMinRight.absLessThan(mleft)) && mleft < absMinRight; + return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight; } } } diff --git a/src/theory/arith/normal_form.h b/src/theory/arith/normal_form.h index cd5f047b5..f098d8b54 100644 --- a/src/theory/arith/normal_form.h +++ b/src/theory/arith/normal_form.h @@ -23,8 +23,8 @@ #include "expr/node.h" #include "expr/node_self_iterator.h" #include "util/rational.h" -#include "theory/theory.h" -#include "theory/arith/arith_utilities.h" +#include "theory/arith/delta_rational.h" +//#include "theory/arith/arith_utilities.h" #include #include @@ -247,11 +247,11 @@ public: // by a variable. return true; default: - return (!isRelationOperator(k)) && - (Theory::isLeafOf(n, theory::THEORY_ARITH)); + return isLeafMember(n); } } + static bool isLeafMember(Node n); static bool isDivMember(Node n); bool isDivLike() const{ return isDivMember(getNode()); @@ -286,6 +286,7 @@ public: bool operator==(const Variable& v) const { return getNode() == v.getNode();} + size_t getComplexity() const; };/* class Variable */ @@ -306,9 +307,7 @@ public: return Constant(n); } - static Constant mkConstant(const Rational& rat) { - return Constant(mkRationalNode(rat)); - } + static Constant mkConstant(const Rational& rat); static Constant mkZero() { return mkConstant(Rational(0)); @@ -322,6 +321,7 @@ public: return getNode().getConst(); } + static int absCmp(const Constant& a, const Constant& b); bool isIntegral() const { return getValue().isIntegral(); } int sgn() const { return getValue().sgn(); } @@ -373,6 +373,8 @@ public: return getValue().getNumerator().length(); } + size_t getComplexity() const; + };/* class Constant */ @@ -563,6 +565,7 @@ public: } return true; } + size_t getComplexity() const; private: bool isSorted(iterator start, iterator end); @@ -687,6 +690,9 @@ public: return isSorted(m) && std::adjacent_find(m.begin(),m.end()) == m.end(); } + static void sort(std::vector& m); + static void combineAdjacentMonomials(std::vector& m); + /** * The variable product */ @@ -717,11 +723,14 @@ public: * Given a sorted list of monomials, this function transforms this * into a strictly sorted list of monomials that does not contain zero. */ - static std::vector sumLikeTerms(const std::vector& monos); + //static std::vector sumLikeTerms(const std::vector& monos); - bool absLessThan(const Monomial& other) const{ - return getConstant().abs() < other.getConstant().abs(); + int absCmp(const Monomial& other) const{ + return getConstant().getValue().absCmp(other.getConstant().getValue()); } + // bool absLessThan(const Monomial& other) const{ + // return getConstant().abs() < other.getConstant().abs(); + // } uint32_t coefficientLength() const{ return getConstant().length(); @@ -730,6 +739,7 @@ public: void print() const; static void printList(const std::vector& list); + size_t getComplexity() const; };/* class Monomial */ class SumPair; @@ -938,9 +948,12 @@ public: return true; } + static Polynomial sumPolynomials(const std::vector& polynomials); + /** Returns true if the polynomial contains a non-linear monomial.*/ bool isNonlinear() const; + /** * Selects a minimal monomial in the polynomial by the absolute value of * the coefficient. @@ -1058,6 +1071,8 @@ public: return getHead().getVarList(); } + size_t getComplexity() const; + friend class SumPair; friend class Comparison; @@ -1173,6 +1188,10 @@ public: return getConstant().isZero() && isConstant(); } + uint32_t size() const{ + return getPolynomial().size(); + } + bool isNonlinear() const{ return getPolynomial().isNonlinear(); } @@ -1368,6 +1387,8 @@ public: return parse.isNormalForm(); } + size_t getComplexity() const; + SumPair toSumPair() const; Polynomial normalizedVariablePart() const; diff --git a/src/theory/arith/options b/src/theory/arith/options index 3fc08e18e..cf35265d6 100644 --- a/src/theory/arith/options +++ b/src/theory/arith/options @@ -85,8 +85,11 @@ option restrictedPivots --restrict-pivots bool :default true :read-write 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 useApprox --use-approx bool :default false :read-write + attempt to use an approximate solver + +option maxApproxDepth --approx-branch-depth int16_t :default 200 :read-write + maximum branch depth the approximate solver is allowed to take option exportDioDecompositions --dio-decomps bool :default false :read-write let skolem variables for integer divisibility constraints leak from the dio solver @@ -103,4 +106,46 @@ option soiQuickExplain --soi-qe bool :default false :read-write option rewriteDivk rewrite-divk --rewrite-divk bool :default false :read-write rewrite division and mod when by a constant into linear terms +option trySolveIntStandardEffort --se-solve-int bool :default false + attempt to use the approximate solve integer method on standard effort + +option replayFailureLemma --lemmas-on-replay-failure bool :default false + attempt to use external lemmas if approximate solve integer failed + +option dioSolverTurns --dio-turns int :default 10 + turns in a row dio solver cutting gets + +option rrTurns --rr-turns int :default 3 + round robin turn + +option dioRepeat --dio-repeat bool :default false + handle dio solver constraints in mass or one at a time + +option replayEarlyCloseDepths --replay-early-close-depth int :default 1 + multiples of the depths to try to close the approx log eagerly + +option replayFailurePenalty --replay-failure-penalty int :default 100 + number of solve integer attempts to skips after a numeric failure + +option replayNumericFailurePenalty --replay-num-err-penalty int :default 4194304 + number of solve integer attempts to skips after a numeric failure + +option replayRejectCutSize --replay-reject-cut unsigned :default 25500 + maximum complexity of any coefficient while replaying cuts + +option lemmaRejectCutSize --replay-lemma-reject-cut unsigned :default 25500 + maximum complexity of any coefficient while outputing replaying cut lemmas + +option soiApproxMajorFailure --replay-soi-major-threshold double :default .01 + threshold for a major tolerance failure by the approximate solver + +option soiApproxMajorFailurePen --replay-soi-major-threshold-pen int :default 50 + threshold for a major tolerance failure by the approximate solver + +option soiApproxMinorFailure --replay-soi-minor-threshold double :default .0001 + threshold for a minor tolerance failure by the approximate solver + +option soiApproxMinorFailurePen --replay-soi-minor-threshold-pen int :default 10 + threshold for a minor tolerance failure by the approximate solver + endmodule diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp index 3fae3751c..8f08de36c 100644 --- a/src/theory/arith/partial_model.cpp +++ b/src/theory/arith/partial_model.cpp @@ -33,7 +33,6 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo d_numberOfVariables(0), d_pool(), d_released(), - d_releasedIterator(d_released.begin()), d_nodeToArithVarMap(), d_boundsQueue(), d_enqueueingBoundCounts(true), @@ -44,6 +43,87 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo d_deltaComputingFunc(deltaComputingFunc) { } +ArithVar ArithVariables::getNumberOfVariables() const { + return d_numberOfVariables; +} + + +bool ArithVariables::hasArithVar(TNode x) const { + return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end(); +} + +bool ArithVariables::hasNode(ArithVar a) const { + return d_vars.isKey(a); +} + +ArithVar ArithVariables::asArithVar(TNode x) const{ + Assert(hasArithVar(x)); + Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL); + return (d_nodeToArithVarMap.find(x))->second; +} + +Node ArithVariables::asNode(ArithVar a) const{ + Assert(hasNode(a)); + return d_vars[a].d_node; +} + +ArithVariables::var_iterator::var_iterator() + : d_vars(NULL) + , d_wrapped() +{} + +ArithVariables::var_iterator::var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci) + : d_vars(vars), d_wrapped(ci) +{ + nextInitialized(); +} + +ArithVariables::var_iterator& ArithVariables::var_iterator::operator++(){ + ++d_wrapped; + nextInitialized(); + return *this; +} +bool ArithVariables::var_iterator::operator==(const ArithVariables::var_iterator& other) const{ + return d_wrapped == other.d_wrapped; +} +bool ArithVariables::var_iterator::operator!=(const ArithVariables::var_iterator& other) const{ + return d_wrapped != other.d_wrapped; +} +ArithVar ArithVariables::var_iterator::operator*() const{ + return *d_wrapped; +} + +void ArithVariables::var_iterator::nextInitialized(){ + VarInfoVec::const_iterator end = d_vars->end(); + while(d_wrapped != end && + !((*d_vars)[*d_wrapped].initialized())){ + ++d_wrapped; + } +} + +ArithVariables::var_iterator ArithVariables::var_begin() const { + return var_iterator(&d_vars, d_vars.begin()); +} + +ArithVariables::var_iterator ArithVariables::var_end() const { + return var_iterator(&d_vars, d_vars.end()); +} +bool ArithVariables::isInteger(ArithVar x) const { + return d_vars[x].d_type >= ATInteger; +} + +/** Is the assignment to x integral? */ +bool ArithVariables::integralAssignment(ArithVar x) const { + return getAssignment(x).isIntegral(); +} +bool ArithVariables::isAuxiliary(ArithVar x) const { + return d_vars[x].d_auxiliary; +} + +bool ArithVariables::isIntegerInput(ArithVar x) const { + return isInteger(x) && !isAuxiliary(x); +} + ArithVariables::VarInfo::VarInfo() : d_var(ARITHVAR_SENTINEL), d_assignment(0), @@ -53,14 +133,14 @@ ArithVariables::VarInfo::VarInfo() d_cmpAssignmentUB(-1), d_pushCount(0), d_node(Node::null()), - d_slack(false) + d_auxiliary(false) { } bool ArithVariables::VarInfo::initialized() const { return d_var != ARITHVAR_SENTINEL; } -void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){ +void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){ Assert(!initialized()); Assert(d_lb == NullConstraint); Assert(d_ub == NullConstraint); @@ -68,9 +148,9 @@ void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){ Assert(d_cmpAssignmentUB < 0); d_var = v; d_node = n; - d_slack = slack; + d_auxiliary = aux; - if(d_slack){ + if(d_auxiliary){ //The type computation is not quite accurate for Rationals that are //integral. //We'll use the isIntegral check from the polynomial package instead. @@ -112,6 +192,10 @@ bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& void ArithVariables::releaseArithVar(ArithVar v){ VarInfo& vi = d_vars.get(v); + + size_t removed CVC4_UNUSED = d_nodeToArithVarMap.erase(vi.d_node); + Assert(removed == 1); + vi.uninitialize(); if(d_safeAssignment.isKey(v)){ @@ -124,7 +208,7 @@ void ArithVariables::releaseArithVar(ArithVar v){ } } -bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){ +bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){ Assert(initialized()); bool wasNull = d_ub == NullConstraint; bool isNull = ub == NullConstraint; @@ -140,7 +224,7 @@ bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){ return ubChanged; } -bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundsInfo& prev){ +bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){ Assert(initialized()); bool wasNull = d_lb == NullConstraint; bool isNull = lb == NullConstraint; @@ -177,23 +261,22 @@ bool ArithVariables::VarInfo::canBeReclaimed() const{ return d_pushCount == 0; } +bool ArithVariables::canBeReleased(ArithVar v) const{ + return d_vars[v].canBeReclaimed(); +} + void ArithVariables::attemptToReclaimReleased(){ - std::list::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()){ + size_t readPos = 0, writePos = 0, N = d_released.size(); + for(; readPos < N; ++readPos){ + ArithVar v = d_released[readPos]; + if(canBeReleased(v)){ d_pool.push_back(v); - std::list::iterator curr = d_releasedIterator; - ++d_releasedIterator; - d_released.erase(curr); }else{ - ++d_releasedIterator; + d_released[writePos] = v; + writePos++; } } - if(d_releasedIterator == i_end){ - d_releasedIterator = d_released.begin(); - } + d_released.resize(writePos); } ArithVar ArithVariables::allocateVariable(){ @@ -232,6 +315,21 @@ bool ArithVariables::boundsAreEqual(ArithVar x) const{ } } + +std::pair ArithVariables::explainEqualBounds(ArithVar x) const{ + Assert(boundsAreEqual(x)); + + ConstraintP lb = getLowerBoundConstraint(x); + ConstraintP ub = getUpperBoundConstraint(x); + if(lb->isEquality()){ + return make_pair(lb, NullConstraint); + }else if(ub->isEquality()){ + return make_pair(ub, NullConstraint); + }else{ + return make_pair(lb, ub); + } +} + void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){ Debug("partial_model") << "pm: updating the assignment to" << x << " now " << r <isEquality() || c->isLowerBound(), "Constraint type must be set to an equality or UpperBound."); @@ -351,7 +449,7 @@ void ArithVariables::setLowerBoundConstraint(Constraint c){ } } -void ArithVariables::setUpperBoundConstraint(Constraint c){ +void ArithVariables::setUpperBoundConstraint(ConstraintP 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."); @@ -450,6 +548,14 @@ void ArithVariables::commitAssignmentChanges(){ clearSafeAssignments(false); } +bool ArithVariables::lowerBoundIsZero(ArithVar x){ + return hasLowerBound(x) && getLowerBound(x).sgn() == 0; +} + +bool ArithVariables::upperBoundIsZero(ArithVar x){ + return hasUpperBound(x) && getUpperBound(x).sgn() == 0; +} + void ArithVariables::printEntireModel(std::ostream& out) const{ out << "---Printing Model ---" << std::endl; for(var_iterator i = var_begin(), iend = var_end(); i != iend; ++i){ @@ -474,6 +580,10 @@ void ArithVariables::printModel(ArithVar x, std::ostream& out) const{ out << getUpperBound(x) << " "; out << getUpperBoundConstraint(x) << " "; } + + if(isInteger(x) && !integralAssignment(x)){ + out << "(not an integer)" << endl; + } out << endl; } @@ -540,10 +650,36 @@ void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){ } } +void ArithVariables::invalidateDelta() { + d_deltaIsSafe = false; +} + +void ArithVariables::setDelta(const Rational& d){ + d_delta = d; + d_deltaIsSafe = true; +} + +void ArithVariables::startQueueingBoundCounts(){ + d_enqueueingBoundCounts = true; +} +void ArithVariables::stopQueueingBoundCounts(){ + d_enqueueingBoundCounts = false; +} + +bool ArithVariables::inMaps(ArithVar x) const{ + return x < getNumberOfVariables(); +} + +ArithVariables::LowerBoundCleanUp::LowerBoundCleanUp(ArithVariables* pm) + : d_pm(pm) +{} void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){ d_pm->popLowerBound(p); } +ArithVariables::UpperBoundCleanUp::UpperBoundCleanUp(ArithVariables* pm) + : d_pm(pm) +{} void ArithVariables::UpperBoundCleanUp::operator()(AVCPair* p){ d_pm->popUpperBound(p); } diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h index c497adb75..33af3d4ef 100644 --- a/src/theory/arith/partial_model.h +++ b/src/theory/arith/partial_model.h @@ -9,10 +9,11 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief [[ Add one-line brief description here ]] + ** \brief Datastructures that track variable by variable information. ** - ** [[ Add lengthier description here ]] - ** \todo document this file + ** This is a datastructure that tracks variable specific information. + ** This is partially context dependent to back track upper/lower bounds + ** and information derived from these. **/ #include "cvc4_private.h" @@ -50,40 +51,44 @@ private: ArithVar d_var; DeltaRational d_assignment; - Constraint d_lb; - Constraint d_ub; + ConstraintP d_lb; + ConstraintP d_ub; int d_cmpAssignmentLB; int d_cmpAssignmentUB; unsigned d_pushCount; ArithType d_type; Node d_node; - bool d_slack; + bool d_auxiliary; public: VarInfo(); bool setAssignment(const DeltaRational& r, BoundsInfo& prev); - bool setLowerBound(Constraint c, BoundsInfo& prev); - bool setUpperBound(Constraint c, BoundsInfo& prev); + bool setLowerBound(ConstraintP c, BoundsInfo& prev); + bool setUpperBound(ConstraintP c, BoundsInfo& prev); /** Returns true if this VarInfo has been initialized. */ bool initialized() const; /** * Initializes the VarInfo with the ArithVar index it is associated with, - * the node that the variable represents, and whether it is a slack variable. + * the node that the variable represents, and whether it is an auxillary + * variable. */ - void initialize(ArithVar v, Node n, bool slack); + void initialize(ArithVar v, Node n, bool aux); + /** Uninitializes the VarInfo. */ void uninitialize(); bool canBeReclaimed() const; - /** Indicator variables for if the assignment is equal to the upper and lower bounds. */ + /** Indicator variables for if the assignment is equal to the upper + * and lower bounds. */ BoundCounts atBoundCounts() const; - /** Combination of indicator variables for whether it has upper and lower bounds. */ + /** Combination of indicator variables for whether it has upper and + * lower bounds. */ BoundCounts hasBoundCounts() const; /** Stores both atBoundCounts() and hasBoundCounts(). */ @@ -92,21 +97,22 @@ private: /**Maps from ArithVar -> VarInfo */ typedef DenseMap VarInfoVec; + /** This maps an ArithVar to its Variable information.*/ VarInfoVec d_vars; - // Partial Map from Arithvar -> PreviousAssignment + /** Partial Map from Arithvar -> PreviousAssignment */ DenseMap d_safeAssignment; - // if d_vars.isKey(x), then x < d_numberOfVariables + /** 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 d_pool; - std::list d_released; - std::list::iterator d_releasedIterator; + std::vector d_released; + //std::list::iterator d_releasedIterator; // Reverse Map from Node to ArithVar // Inverse of d_vars[x].d_node @@ -118,36 +124,29 @@ private: /** * If this is true, record the incoming changes to the bound information. - * If this is false, the responsibility of recording the changes is LinearEqualities's. + * If this is false, the responsibility of recording the changes is + * LinearEqualities's. */ bool d_enqueueingBoundCounts; public: - inline ArithVar getNumberOfVariables() const { - return d_numberOfVariables; - } + /** Returns the number of variables. */ + ArithVar getNumberOfVariables() const; - inline bool hasArithVar(TNode x) const { - return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end(); - } + /** Returns true if the node has an associated variables. */ + bool hasArithVar(TNode x) const; - inline bool hasNode(ArithVar a) const { - return d_vars.isKey(a); - } - - inline ArithVar asArithVar(TNode x) const{ - Assert(hasArithVar(x)); - Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL); - return (d_nodeToArithVarMap.find(x))->second; - } + /** Returns true if the variable has a defining node. */ + bool hasNode(ArithVar a) const; + /** Returns the ArithVar associated with a node. */ + ArithVar asArithVar(TNode x) const; - inline Node asNode(ArithVar a) const{ - Assert(hasNode(a)); - return d_vars[a].d_node; - } + /** Returns the node associated with an ArithVar. */ + Node asNode(ArithVar a) const; + /** Allocates a freshly allocated variables. */ ArithVar allocateVariable(); class var_iterator { @@ -155,68 +154,47 @@ 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; - } + var_iterator(); + var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci); + var_iterator& operator++(); + + bool operator==(const var_iterator& other) const; + bool operator!=(const var_iterator& other) const; + ArithVar operator*() const; + private: - void nextInitialized(){ - VarInfoVec::const_iterator end = d_vars->end(); - while(d_wrapped != end && - !((*d_vars)[*d_wrapped].initialized())){ - ++d_wrapped; - } - } + void nextInitialized(); }; - 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()); - } + var_iterator var_begin() const; + var_iterator var_end() const; 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; - } + /** Is this variable guaranteed to have an integer assignment? + * (Should agree with the type system.) */ + bool isInteger(ArithVar x) const; - bool integralAssignment(ArithVar x) const { - return getAssignment(x).isIntegral(); - } + /** Is the assignment to x integral? */ + bool integralAssignment(ArithVar x) const; + + /* Is this variable defined as a linear sum of other variables? */ + bool isAuxiliary(ArithVar x) const; + + /* Is the variable both input and not auxiliary? */ + bool isIntegerInput(ArithVar x) const; private: - typedef std::pair AVCPair; + typedef std::pair AVCPair; class LowerBoundCleanUp { private: ArithVariables* d_pm; public: - LowerBoundCleanUp(ArithVariables* pm) : d_pm(pm) {} + LowerBoundCleanUp(ArithVariables* pm); void operator()(AVCPair* restore); }; @@ -224,7 +202,7 @@ private: private: ArithVariables* d_pm; public: - UpperBoundCleanUp(ArithVariables* pm) : d_pm(pm) {} + UpperBoundCleanUp(ArithVariables* pm); void operator()(AVCPair* restore); }; @@ -255,27 +233,27 @@ public: * This sets the lower bound for a variable in the current context. * This must be stronger the previous constraint. */ - void setLowerBoundConstraint(Constraint lb); + void setLowerBoundConstraint(ConstraintP lb); /** * This sets the upper bound for a variable in the current context. * This must be stronger the previous constraint. */ - void setUpperBoundConstraint(Constraint ub); + void setUpperBoundConstraint(ConstraintP ub); /** Returns the constraint for the upper bound of a variable. */ - inline Constraint getUpperBoundConstraint(ArithVar x) const{ + inline ConstraintP getUpperBoundConstraint(ArithVar x) const{ return d_vars[x].d_ub; } /** Returns the constraint for the lower bound of a variable. */ - inline Constraint getLowerBoundConstraint(ArithVar x) const{ + inline ConstraintP getLowerBoundConstraint(ArithVar x) const{ return d_vars[x].d_lb; } /* Initializes a variable to a safe value.*/ - void initialize(ArithVar x, Node n, bool slack); + void initialize(ArithVar x, Node n, bool aux); - ArithVar allocate(Node n, bool slack = false); + ArithVar allocate(Node n, bool aux = false); /* Gets the last assignment to a variable that is known to be consistent. */ const DeltaRational& getSafeAssignment(ArithVar x) const; @@ -288,13 +266,8 @@ public: void commitAssignmentChanges(); - inline bool lowerBoundIsZero(ArithVar x){ - return hasLowerBound(x) && getLowerBound(x).sgn() == 0; - } - - inline bool upperBoundIsZero(ArithVar x){ - return hasUpperBound(x) && getUpperBound(x).sgn() == 0; - } + bool lowerBoundIsZero(ArithVar x); + bool upperBoundIsZero(ArithVar x); bool boundsAreEqual(ArithVar x) const; @@ -393,17 +366,12 @@ public: const Rational& getDelta(); - inline void invalidateDelta() { - d_deltaIsSafe = false; - } + void invalidateDelta(); - void setDelta(const Rational& d){ - d_delta = d; - d_deltaIsSafe = true; - } + void setDelta(const Rational& d); - void startQueueingBoundCounts(){ d_enqueueingBoundCounts = true; } - void stopQueueingBoundCounts(){ d_enqueueingBoundCounts = false; } + void startQueueingBoundCounts(); + void stopQueueingBoundCounts(); void addToBoundQueue(ArithVar v, const BoundsInfo& prev); BoundsInfo selectBoundsInfo(ArithVar v, bool old) const; @@ -413,6 +381,15 @@ public: void printEntireModel(std::ostream& out) const; + + /** + * Precondition: assumes boundsAreEqual(x). + * If the either the lower/ upper bound is an equality, eq, + * this returns make_pair(eq, NullConstraint). + * Otherwise, this returns make_pair(lb, ub). + */ + std::pair explainEqualBounds(ArithVar x) const; + private: /** @@ -423,9 +400,7 @@ private: bool debugEqualSizes(); - bool inMaps(ArithVar x) const{ - return x < getNumberOfVariables(); - } + bool inMaps(ArithVar x) const; };/* class ArithVariables */ diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp index a160f4fe2..e67f4b9fc 100644 --- a/src/theory/arith/simplex.cpp +++ b/src/theory/arith/simplex.cpp @@ -77,35 +77,40 @@ bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& void SimplexDecisionProcedure::reportConflict(ArithVar basic){ Assert(!d_conflictVariables.isMember(basic)); Assert(checkBasicForConflict(basic)); - Node conflict = generateConflictForBasic(basic); + RaiseConflict rc( d_conflictChannel); - static bool verbose = false; - if(verbose) { Message() << "conflict " << basic << " " << conflict << endl; } - Assert(!conflict.isNull()); - d_conflictChannel(conflict); + generateConflictForBasic(basic, rc); + + // static bool verbose = false; + // if(verbose) { Message() << "conflict " << basic << " " << conflict << endl; } + // Assert(!conflict.isNull()); + //d_conflictChannel(conflict); + rc.commitConflict(); d_conflictVariables.add(basic); } -Node SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) const { +void SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic, RaiseConflict& rc) const { Assert(d_tableau.isBasic(basic)); Assert(checkBasicForConflict(basic)); if(d_variables.cmpAssignmentLowerBound(basic) < 0){ Assert(d_linEq.nonbasicsAtUpperBounds(basic)); - return d_linEq.generateConflictBelowLowerBound(basic); + return d_linEq.generateConflictBelowLowerBound(basic, rc); }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){ Assert(d_linEq.nonbasicsAtLowerBounds(basic)); - return d_linEq.generateConflictAboveUpperBound(basic); + return d_linEq.generateConflictAboveUpperBound(basic, rc); }else{ Unreachable(); } } -Node SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const { +bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const { if(checkBasicForConflict(basic)){ - return generateConflictForBasic(basic); + RaiseConflict rc(d_conflictChannel); + generateConflictForBasic(basic, rc); + return true; }else{ - return Node::null(); + return false; } } diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h index b61cadaf8..f545da51e 100644 --- a/src/theory/arith/simplex.h +++ b/src/theory/arith/simplex.h @@ -157,16 +157,16 @@ protected: * If a conflict is discovered a node summarizing the conflict is returned. * Otherwise, Node::null() is returned. */ - Node maybeGenerateConflictForBasic(ArithVar basic) const; + bool maybeGenerateConflictForBasic(ArithVar basic) const; /** Returns true if a tracked basic variable has a conflict on it. */ bool checkBasicForConflict(ArithVar b) const; /** * If a basic variable has a conflict on its row, - * this produces a minimized row. + * this produces a minimized row on the conflict channel. */ - Node generateConflictForBasic(ArithVar basic) const; + void generateConflictForBasic(ArithVar basic, RaiseConflict& rc) const; /** Gets a fresh variable from TheoryArith. */ diff --git a/src/theory/arith/simplex_update.cpp b/src/theory/arith/simplex_update.cpp index ba19d01b1..416fbe745 100644 --- a/src/theory/arith/simplex_update.cpp +++ b/src/theory/arith/simplex_update.cpp @@ -51,7 +51,7 @@ UpdateInfo::UpdateInfo(ArithVar nb, int dir): Assert(dir == 1 || dir == -1); } -UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint c): +UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c): d_nonbasic(nb), d_nonbasicDirection(delta.sgn()), d_nonbasicDelta(delta), @@ -65,7 +65,7 @@ UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, c Assert(conflict); } -UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim){ +UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){ return UpdateInfo(true, nb, delta, r, lim); } @@ -81,7 +81,7 @@ void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){ Assert(!describesPivot()); Assert(debugSgnAgreement()); } -void UpdateInfo::updatePureFocus(const DeltaRational& delta, Constraint c){ +void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange.clear(); @@ -93,7 +93,7 @@ void UpdateInfo::updatePureFocus(const DeltaRational& delta, Constraint c){ Assert(debugSgnAgreement()); } -void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c){ +void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange.clear(); @@ -103,7 +103,7 @@ void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Cons Assert(debugSgnAgreement()); } -void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c, int ec){ +void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; @@ -114,7 +114,7 @@ void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Cons Assert(debugSgnAgreement()); } -void UpdateInfo::witnessedUpdate(const DeltaRational& delta, Constraint c, int ec, int fd){ +void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; @@ -125,7 +125,7 @@ void UpdateInfo::witnessedUpdate(const DeltaRational& delta, Constraint c, int e Assert(debugSgnAgreement()); } -void UpdateInfo::update(const DeltaRational& delta, const Rational& r, Constraint c, int ec, int fd){ +void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h index 5a313e305..e223bba7f 100644 --- a/src/theory/arith/simplex_update.h +++ b/src/theory/arith/simplex_update.h @@ -136,7 +136,7 @@ private: * - 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; + ConstraintP d_limiting; WitnessImprovement d_witness; @@ -150,7 +150,7 @@ private: } /** This private constructor allows for setting conflict to true. */ - UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim); + UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim); public: @@ -170,7 +170,7 @@ public: void updateUnbounded(const DeltaRational& d, int ec, int f); - void updatePureFocus(const DeltaRational& d, Constraint c); + void updatePureFocus(const DeltaRational& d, ConstraintP c); //void updatePureError(const DeltaRational& d, Constraint c, int e); //void updatePure(const DeltaRational& d, Constraint c, int e, int f); @@ -178,23 +178,23 @@ public: * 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); + void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP 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); + void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP 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); + void witnessedUpdate(const DeltaRational& d, ConstraintP c, int e, int f); + void update(const DeltaRational& d, const Rational& r, ConstraintP c, int e, int f); - static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim); + static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim); inline ArithVar nonbasic() const { return d_nonbasic; } inline bool uninitialized() const { @@ -283,7 +283,7 @@ public: } /** Returns the limiting constraint. */ - inline Constraint limiting() const { + inline ConstraintP limiting() const { return d_limiting; } diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp index 2eb258d3b..ded322f18 100644 --- a/src/theory/arith/soi_simplex.cpp +++ b/src/theory/arith/soi_simplex.cpp @@ -383,7 +383,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes } if(selected.describesPivot()){ - Constraint limiting = selected.limiting(); + ConstraintP limiting = selected.limiting(); ArithVar basic = limiting->getVariable(); Assert(d_linEq.basicIsTracked(basic)); d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue()); @@ -765,16 +765,17 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){ return subsets; } -Node SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ +void SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ Assert(d_soiVar == ARITHVAR_SENTINEL); d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset); - NodeBuilder<> conflict(kind::AND); + //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); + ConstraintP violated = d_errorSet.getViolated(e); //cout << "basic error var: " << violated << endl; - violated->explainForConflict(conflict); + d_conflictChannel.addConstraint(violated); + //violated->explainForConflict(conflict); //d_tableau.debugPrintIsBasic(e); //d_tableau.printBasicRow(e, cout); @@ -785,18 +786,19 @@ Node SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ if(v == d_soiVar){ continue; } const Rational& coeff = entry.getCoefficient(); - Constraint c = (coeff.sgn() > 0) ? + ConstraintP c = (coeff.sgn() > 0) ? d_variables.getUpperBoundConstraint(v) : d_variables.getLowerBoundConstraint(v); //cout << "nb : " << c << endl; - c->explainForConflict(conflict); + d_conflictChannel.addConstraint(c); } - Node conf = conflict; + //Node conf = conflict; tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar); d_soiVar = ARITHVAR_SENTINEL; - return conf; + d_conflictChannel.commitConflict(); + //return conf; } @@ -812,9 +814,10 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ if(options::soiQuickExplain()){ quickExplain(); - Node conflict = generateSOIConflict(d_qeConflict); + generateSOIConflict(d_qeConflict); + //Node conflict = generateSOIConflict(d_qeConflict); //cout << conflict << endl; - d_conflictChannel(conflict); + //d_conflictChannel(conflict); }else{ vector subsets = greedyConflictSubsets(); @@ -823,11 +826,12 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ Assert(!subsets.empty()); for(vector::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){ const ArithVarVec& subset = *i; - Node conflict = generateSOIConflict(subset); + generateSOIConflict(subset); + //Node conflict = generateSOIConflict(subset); //cout << conflict << endl; //reportConflict(conf); do not do this. We need a custom explanations! - d_conflictChannel(conflict); + //d_conflictChannel(conflict); } } Assert( d_soiVar == ARITHVAR_SENTINEL); diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h index cee6cf81d..89df69390 100644 --- a/src/theory/arith/soi_simplex.h +++ b/src/theory/arith/soi_simplex.h @@ -171,7 +171,7 @@ private: WitnessImprovement soiRound(); WitnessImprovement SOIConflict(); std::vector< ArithVarVec > greedyConflictSubsets(); - Node generateSOIConflict(const ArithVarVec& subset); + void generateSOIConflict(const ArithVarVec& subset); // WitnessImprovement focusUsingSignDisagreements(ArithVar basic); // WitnessImprovement focusDownToLastHalf(); diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h index 3e4cb819b..8cf92d075 100644 --- a/src/theory/arith/tableau.h +++ b/src/theory/arith/tableau.h @@ -81,7 +81,7 @@ public: } ArithVar rowIndexToBasic(RowIndex rid) const { - Assert(rid < d_rowIndex2basic.size()); + Assert(d_rowIndex2basic.isKey(rid)); return d_rowIndex2basic[rid]; } diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 40a336a4a..d920fc8ca 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -41,6 +41,7 @@ #include "smt/logic_exception.h" #include "theory/arith/arithvar.h" +#include "theory/arith/cut_log.h" #include "theory/arith/delta_rational.h" #include "theory/arith/matrix.h" #include "theory/arith/arith_rewriter.h" @@ -54,6 +55,9 @@ #include "theory/arith/approx_simplex.h" #include "theory/arith/constraint.h" +#include "theory/ite_utilities.h" +#include "theory/arith/arith_ite_utils.h" + #include "theory/arith/arith_utilities.h" #include "theory/arith/delta_rational.h" #include "theory/arith/partial_model.h" @@ -82,11 +86,17 @@ namespace CVC4 { namespace theory { namespace arith { +static Node toSumNode(const ArithVariables& vars, const DenseMap& sum); +static double fRand(double fMin, double fMax); +static bool complexityBelow(const DenseMap& row, uint32_t cap); + + TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : d_containing(containing), d_nlIncomplete( false), d_rowTracking(), - d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this)), + d_conflictBuffer(), + d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this, d_conflictBuffer)), d_qflraStatus(Result::SAT_UNKNOWN), d_unknownsInARow(0), d_hasDoneWorkSinceCut(false), @@ -108,26 +118,97 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context 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_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), - d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), - d_attemptSolSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), + d_blackBoxConflict(c, Node::null()), + d_congruenceManager(c, d_constraintDatabase, SetupLiteralCallBack(*this), d_partialModel, RaiseConflict(*this, d_conflictBuffer)), + d_cmEnabled(c, true), + d_dualSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_attemptSolSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_pass1SDP(NULL), + d_otherSDP(NULL), + d_lastContextIntegerAttempted(c,-1), d_DELTA_ZERO(0), + d_approxCuts(c), d_fullCheckCounter(0), d_cutCount(c, 0), d_cutInContext(c), d_likelyIntegerInfeasible(c, false), d_guessedCoeffSet(c, false), d_guessedCoeffs(), + d_treeLog(NULL), + d_replayVariables(), + d_replayConstraints(), + d_lhsTmp(), + d_approxStats(NULL), + d_attemptSolveIntTurnedOff(u, 0), + d_dioSolveResources(0), + d_solveIntMaybeHelp(0u), + d_solveIntAttempts(0u), d_statistics() { srand(79); } -TheoryArithPrivate::~TheoryArithPrivate(){ } +TheoryArithPrivate::~TheoryArithPrivate(){ + if(d_treeLog != NULL){ delete d_treeLog; } + if(d_approxStats != NULL) { delete d_approxStats; } +} + +static bool contains(const ConstraintCPVec& v, ConstraintP con){ + for(unsigned i = 0, N = v.size(); i < N; ++i){ + if(v[i] == con){ + return true; + } + } + return false; +} +static void drop( ConstraintCPVec& v, ConstraintP con){ + size_t readPos, writePos, N; + for(readPos = 0, writePos = 0, N = v.size(); readPos < N; ++readPos){ + ConstraintCP curr = v[readPos]; + if(curr != con){ + v[writePos] = curr; + writePos++; + } + } + v.resize(writePos); +} +static void resolve(ConstraintCPVec& buf, ConstraintP c, const ConstraintCPVec& pos, const ConstraintCPVec& neg){ + unsigned posPos CVC4_UNUSED = pos.size(); + for(unsigned i = 0, N = pos.size(); i < N; ++i){ + if(pos[i] == c){ + posPos = i; + }else{ + buf.push_back(pos[i]); + } + } + Assert(posPos < pos.size()); + ConstraintP negc = c->getNegation(); + unsigned negPos CVC4_UNUSED = neg.size(); + for(unsigned i = 0, N = neg.size(); i < N; ++i){ + if(neg[i] == negc){ + negPos = i; + }else{ + buf.push_back(neg[i]); + } + } + Assert(negPos < neg.size()); + + // Assert(dnconf.getKind() == kind::AND); + // Assert(upconf.getKind() == kind::AND); + // Assert(dnpos < dnconf.getNumChildren()); + // Assert(uppos < upconf.getNumChildren()); + // Assert(equalUpToNegation(dnconf[dnpos], upconf[uppos])); + + // NodeBuilder<> nb(kind::AND); + // dropPosition(nb, dnconf, dnpos); + // dropPosition(nb, upconf, uppos); + // return safeConstructNary(nb); +} + void TheoryArithPrivate::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_congruenceManager.setMasterEqualityEngine(eq); } @@ -177,40 +258,79 @@ TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg) thr 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") +TheoryArithPrivate::Statistics::Statistics() + : d_statAssertUpperConflicts("theory::arith::AssertUpperConflicts", 0) + , d_statAssertLowerConflicts("theory::arith::AssertLowerConflicts", 0) + , d_statUserVariables("theory::arith::UserVariables", 0) + , d_statAuxiliaryVariables("theory::arith::AuxiliaryVariables", 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_replayLogRecCount("z::approx::replay::rec",0) + , d_replayLogRecConflictEscalation("z::approx::replay::rec::escalation",0) + , d_replayLogRecEarlyExit("z::approx::replay::rec::earlyexit",0) + , d_replayBranchCloseFailures("z::approx::replay::rec::branch::closefailures",0) + , d_replayLeafCloseFailures("z::approx::replay::rec::leaf::closefailures",0) + , d_replayBranchSkips("z::approx::replay::rec::branch::skips",0) + , d_mirCutsAttempted("z::approx::cuts::mir::attempted",0) + , d_gmiCutsAttempted("z::approx::cuts::gmi::attempted",0) + , d_branchCutsAttempted("z::approx::cuts::branch::attempted",0) + , d_cutsReconstructed("z::approx::cuts::reconstructed",0) + , d_cutsReconstructionFailed("z::approx::cuts::reconstructed::failed",0) + , d_cutsProven("z::approx::cuts::proofs",0) + , d_cutsProofFailed("z::approx::cuts::proofs::failed",0) + , d_mipReplayLemmaCalls("z::approx::external::calls",0) + , d_mipExternalCuts("z::approx::external::cuts",0) + , d_mipExternalBranch("z::approx::external::branches",0) + , d_inSolveInteger("z::approx::inSolverInteger",0) + , d_branchesExhausted("z::approx::exhausted::branches",0) + , d_execExhausted("z::approx::exhausted::exec",0) + , d_pivotsExhausted("z::approx::exhausted::pivots",0) + , d_panicBranches("z::arith::paniclemmas",0) + , d_relaxCalls("z::arith::relax::calls",0) + , d_relaxLinFeas("z::arith::relax::feasible::res",0) + , d_relaxLinFeasFailures("z::arith::relax::feasible::failures",0) + , d_relaxLinInfeas("z::arith::relax::infeasible",0) + , d_relaxLinInfeasFailures("z::arith::relax::infeasible::failures",0) + , d_relaxLinExhausted("z::arith::relax::exhausted",0) + , d_relaxOthers("z::arith::relax::other",0) + , d_applyRowsDeleted("z::arith::cuts::applyRowsDeleted",0) + , d_replaySimplexTimer("z::approx::replay::simplex::timer") + , d_replayLogTimer("z::approx::replay::log::timer") + , d_solveIntTimer("z::solveInt::timer") + , d_solveRealRelaxTimer("z::solveRealRelax::timer") + , d_solveIntCalls("z::solveInt::calls", 0) + , d_solveStandardEffort("z::solveInt::calls::standardEffort", 0) + , d_approxDisabled("z::approxDisabled", 0) + , d_replayAttemptFailed("z::replayAttemptFailed",0) + , d_cutsRejectedDuringReplay("z::approx::replay::cuts::rejected", 0) + , d_cutsRejectedDuringLemmas("z::approx::external::cuts::rejected", 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_statAuxiliaryVariables); StatisticsRegistry::registerStat(&d_statDisequalitySplits); StatisticsRegistry::registerStat(&d_statDisequalityConflicts); StatisticsRegistry::registerStat(&d_simplifyTimer); @@ -241,6 +361,54 @@ TheoryArithPrivate::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_satPivots); StatisticsRegistry::registerStat(&d_unsatPivots); StatisticsRegistry::registerStat(&d_unknownPivots); + + StatisticsRegistry::registerStat(&d_replayLogRecCount); + StatisticsRegistry::registerStat(&d_replayLogRecConflictEscalation); + StatisticsRegistry::registerStat(&d_replayLogRecEarlyExit); + StatisticsRegistry::registerStat(&d_replayBranchCloseFailures); + StatisticsRegistry::registerStat(&d_replayLeafCloseFailures); + StatisticsRegistry::registerStat(&d_replayBranchSkips); + StatisticsRegistry::registerStat(&d_mirCutsAttempted); + StatisticsRegistry::registerStat(&d_gmiCutsAttempted); + StatisticsRegistry::registerStat(&d_branchCutsAttempted); + StatisticsRegistry::registerStat(&d_cutsReconstructed); + StatisticsRegistry::registerStat(&d_cutsProven); + StatisticsRegistry::registerStat(&d_cutsProofFailed); + StatisticsRegistry::registerStat(&d_cutsReconstructionFailed); + StatisticsRegistry::registerStat(&d_mipReplayLemmaCalls); + StatisticsRegistry::registerStat(&d_mipExternalCuts); + StatisticsRegistry::registerStat(&d_mipExternalBranch); + + StatisticsRegistry::registerStat(&d_inSolveInteger); + StatisticsRegistry::registerStat(&d_branchesExhausted); + StatisticsRegistry::registerStat(&d_execExhausted); + StatisticsRegistry::registerStat(&d_pivotsExhausted); + StatisticsRegistry::registerStat(&d_panicBranches); + StatisticsRegistry::registerStat(&d_relaxCalls); + StatisticsRegistry::registerStat(&d_relaxLinFeas); + StatisticsRegistry::registerStat(&d_relaxLinFeasFailures); + StatisticsRegistry::registerStat(&d_relaxLinInfeas); + StatisticsRegistry::registerStat(&d_relaxLinInfeasFailures); + StatisticsRegistry::registerStat(&d_relaxLinExhausted); + StatisticsRegistry::registerStat(&d_relaxOthers); + + StatisticsRegistry::registerStat(&d_applyRowsDeleted); + + StatisticsRegistry::registerStat(&d_replaySimplexTimer); + StatisticsRegistry::registerStat(&d_replayLogTimer); + StatisticsRegistry::registerStat(&d_solveIntTimer); + StatisticsRegistry::registerStat(&d_solveRealRelaxTimer); + + StatisticsRegistry::registerStat(&d_solveIntCalls); + StatisticsRegistry::registerStat(&d_solveStandardEffort); + + StatisticsRegistry::registerStat(&d_approxDisabled); + + StatisticsRegistry::registerStat(&d_replayAttemptFailed); + + StatisticsRegistry::registerStat(&d_cutsRejectedDuringReplay); + StatisticsRegistry::registerStat(&d_cutsRejectedDuringLemmas); + } TheoryArithPrivate::Statistics::~Statistics(){ @@ -248,7 +416,7 @@ TheoryArithPrivate::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_statAssertLowerConflicts); StatisticsRegistry::unregisterStat(&d_statUserVariables); - StatisticsRegistry::unregisterStat(&d_statSlackVariables); + StatisticsRegistry::unregisterStat(&d_statAuxiliaryVariables); StatisticsRegistry::unregisterStat(&d_statDisequalitySplits); StatisticsRegistry::unregisterStat(&d_statDisequalityConflicts); StatisticsRegistry::unregisterStat(&d_simplifyTimer); @@ -278,6 +446,66 @@ TheoryArithPrivate::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_satPivots); StatisticsRegistry::unregisterStat(&d_unsatPivots); StatisticsRegistry::unregisterStat(&d_unknownPivots); + + StatisticsRegistry::unregisterStat(&d_replayLogRecCount); + StatisticsRegistry::unregisterStat(&d_replayLogRecConflictEscalation); + StatisticsRegistry::unregisterStat(&d_replayLogRecEarlyExit); + StatisticsRegistry::unregisterStat(&d_replayBranchCloseFailures); + StatisticsRegistry::unregisterStat(&d_replayLeafCloseFailures); + StatisticsRegistry::unregisterStat(&d_replayBranchSkips); + StatisticsRegistry::unregisterStat(&d_mirCutsAttempted); + StatisticsRegistry::unregisterStat(&d_gmiCutsAttempted); + StatisticsRegistry::unregisterStat(&d_branchCutsAttempted); + StatisticsRegistry::unregisterStat(&d_cutsReconstructed); + StatisticsRegistry::unregisterStat(&d_cutsProven); + StatisticsRegistry::unregisterStat(&d_cutsProofFailed); + StatisticsRegistry::unregisterStat(&d_cutsReconstructionFailed); + StatisticsRegistry::unregisterStat(&d_mipReplayLemmaCalls); + StatisticsRegistry::unregisterStat(&d_mipExternalCuts); + StatisticsRegistry::unregisterStat(&d_mipExternalBranch); + + + StatisticsRegistry::unregisterStat(&d_inSolveInteger); + StatisticsRegistry::unregisterStat(&d_branchesExhausted); + StatisticsRegistry::unregisterStat(&d_execExhausted); + StatisticsRegistry::unregisterStat(&d_pivotsExhausted); + StatisticsRegistry::unregisterStat(&d_panicBranches); + StatisticsRegistry::unregisterStat(&d_relaxCalls); + StatisticsRegistry::unregisterStat(&d_relaxLinFeas); + StatisticsRegistry::unregisterStat(&d_relaxLinFeasFailures); + StatisticsRegistry::unregisterStat(&d_relaxLinInfeas); + StatisticsRegistry::unregisterStat(&d_relaxLinInfeasFailures); + StatisticsRegistry::unregisterStat(&d_relaxLinExhausted); + StatisticsRegistry::unregisterStat(&d_relaxOthers); + + StatisticsRegistry::unregisterStat(&d_applyRowsDeleted); + + StatisticsRegistry::unregisterStat(&d_replaySimplexTimer); + StatisticsRegistry::unregisterStat(&d_replayLogTimer); + StatisticsRegistry::unregisterStat(&d_solveIntTimer); + StatisticsRegistry::unregisterStat(&d_solveRealRelaxTimer); + + StatisticsRegistry::unregisterStat(&d_solveIntCalls); + StatisticsRegistry::unregisterStat(&d_solveStandardEffort); + + StatisticsRegistry::unregisterStat(&d_approxDisabled); + + StatisticsRegistry::unregisterStat(&d_replayAttemptFailed); + + StatisticsRegistry::unregisterStat(&d_cutsRejectedDuringReplay); + StatisticsRegistry::unregisterStat(&d_cutsRejectedDuringLemmas); +} + +bool complexityBelow(const DenseMap& row, uint32_t cap){ + DenseMap::const_iterator riter, rend; + for(riter=row.begin(), rend=row.end(); riter != rend; ++riter){ + ArithVar v = *riter; + const Rational& q = row[v]; + if(q.complexity() > cap){ + return false; + } + } + return true; } void TheoryArithPrivate::revertOutOfConflict(){ @@ -290,25 +518,67 @@ void TheoryArithPrivate::clearUpdates(){ d_updatedBounds.purge(); } +void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b){ + ConstraintCPVec v; + v.push_back(a); + v.push_back(b); + d_conflicts.push_back(v); +} + +void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c){ + ConstraintCPVec v; + v.push_back(a); + v.push_back(b); + v.push_back(c); + d_conflicts.push_back(v); +} + void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){ - Assert(d_congruenceManager.isWatchedVariable(x)); - Assert(d_partialModel.upperBoundIsZero(x)); - Assert(d_partialModel.lowerBoundIsZero(x)); + if(d_cmEnabled){ + 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); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(x); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(x); - if(lb->isEquality()){ - d_congruenceManager.watchedVariableIsZero(lb); - }else if(ub->isEquality()){ - d_congruenceManager.watchedVariableIsZero(ub); + if(lb->isEquality()){ + d_congruenceManager.watchedVariableIsZero(lb); + }else if(ub->isEquality()){ + d_congruenceManager.watchedVariableIsZero(ub); + }else{ + d_congruenceManager.watchedVariableIsZero(lb, ub); + } + } +} + +bool TheoryArithPrivate::getSolveIntegerResource(){ + if(d_attemptSolveIntTurnedOff > 0){ + d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1; + return false; }else{ - d_congruenceManager.watchedVariableIsZero(lb, ub); + return true; + } +} + +bool TheoryArithPrivate::getDioCuttingResource(){ + if(d_dioSolveResources > 0){ + d_dioSolveResources--; + if(d_dioSolveResources == 0){ + d_dioSolveResources = -options::rrTurns(); + } + return true; + }else{ + d_dioSolveResources++; + if(d_dioSolveResources >= 0){ + d_dioSolveResources = options::dioSolverTurns(); + } + return false; } } /* procedure AssertLower( x_i >= c_i ) */ -bool TheoryArithPrivate::AssertLower(Constraint constraint){ +bool TheoryArithPrivate::AssertLower(ConstraintP constraint){ Assert(constraint != NullConstraint); Assert(constraint->isLowerBound()); @@ -326,38 +596,43 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ 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; + ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i); + raiseConflict(ubc, constraint); + + // Node conflict = ConstraintValue::explainConflict(ubc, constraint); + // Debug("arith") << "AssertLower conflict " << conflict << endl; + // raiseConflict(conflict); ++(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); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(x_i); + + if(d_cmEnabled){ + 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(); + ConstraintP eq = vc.getEquality(); + ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - //const Constraint ub = vc.getUpperBound(); - Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); + //const ConstraintP ub = vc.getUpperBound(); + raiseConflict(diseq, ub, constraint); + //Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); ++(d_statistics.d_statDisequalityConflicts); - Debug("eq") << " assert lower conflict " << conflict << endl; - raiseConflict(conflict); + //Debug("eq") << " assert lower conflict " << conflict << endl; + //raiseConflict(conflict); return true; }else if(!eq->isTrue()){ Debug("eq") << "lb == ub, propagate eq" << eq << endl; @@ -370,17 +645,19 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ - const Constraint diseq = vc.getDisequality(); + const ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast(vc), UpperBound); if(ub->hasProof()){ - Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, ub, constraint); return true; + // 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(); + ConstraintP negUb = ub->getNegation(); negUb->impliedBy(constraint, diseq); d_learnedBounds.push_back(negUb); } @@ -393,12 +670,14 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ 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); + if(d_cmEnabled){ + 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); + } } } @@ -428,7 +707,7 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ } /* procedure AssertUpper( x_i <= c_i) */ -bool TheoryArithPrivate::AssertUpper(Constraint constraint){ +bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){ ArithVar x_i = constraint->getVariable(); const DeltaRational& c_i = constraint->getValue(); @@ -450,34 +729,38 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ // 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; + ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i); + raiseConflict(lbc, constraint); + //Node conflict = ConstraintValue::explainConflict(lbc, constraint); + //Debug("arith") << "AssertUpper conflict " << conflict << endl; ++(d_statistics.d_statAssertUpperConflicts); - raiseConflict(conflict); + //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); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(x_i); + if(d_cmEnabled){ + 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(); + const ConstraintP diseq = vc.getDisequality(); + const ConstraintP eq = vc.getEquality(); if(diseq->isTrue()){ - Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, lb, constraint); + //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; @@ -488,17 +771,18 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ }else if(cmpToLB > 0){ const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ - const Constraint diseq = vc.getDisequality(); + const ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - const Constraint lb = + const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast(vc), LowerBound); if(lb->hasProof()){ - Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, lb, constraint); + //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(); + ConstraintP negLb = lb->getNegation(); negLb->impliedBy(constraint, diseq); d_learnedBounds.push_back(negLb); } @@ -512,13 +796,15 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ 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); - } + if(d_cmEnabled){ + 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); @@ -548,7 +834,7 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ /* procedure AssertEquality( x_i == c_i ) */ -bool TheoryArithPrivate::AssertEquality(Constraint constraint){ +bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){ AssertArgument(constraint != NullConstraint, "AssertUpper() called on a NullConstraint."); @@ -570,18 +856,21 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ } 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); + ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i); + raiseConflict(ubc, constraint); + //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); + ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i); + raiseConflict(lbc, constraint); + + // Node conflict = ConstraintValue::explainConflict(lbc, constraint); + // Debug("arith") << "AssertEquality conflicts with lower bound" << conflict << endl; + // raiseConflict(conflict); return true; } @@ -604,16 +893,18 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ 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); + if(d_cmEnabled){ + 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.watchedVariableCannotBeZero(constraint); d_congruenceManager.equalsConstant(constraint); } - }else{ - d_congruenceManager.equalsConstant(constraint); } d_updatedBounds.softAdd(x_i); @@ -643,7 +934,7 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ /* procedure AssertDisequality( x_i != c_i ) */ -bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ +bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){ AssertArgument(constraint != NullConstraint, "AssertUpper() called on a NullConstraint."); @@ -655,32 +946,35 @@ bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ //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); + if(d_cmEnabled){ + 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(); + const ConstraintP lb = vc.getLowerBound(); + const ConstraintP 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); + raiseConflict(constraint, lb, ub); + //Node conflict = ConstraintValue::explainConflict(constraint, lb, ub); + //raiseConflict(conflict); return true; } } if(vc.hasLowerBound() ){ - const Constraint lb = vc.getLowerBound(); + const ConstraintP lb = vc.getLowerBound(); if(lb->isTrue()){ - const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast(vc), UpperBound); Debug("eq") << "propagate UpperBound " << constraint << lb << ub << endl; - const Constraint negUb = ub->getNegation(); + const ConstraintP negUb = ub->getNegation(); if(!negUb->isTrue()){ negUb->impliedBy(constraint, lb); d_learnedBounds.push_back(negUb); @@ -688,12 +982,12 @@ bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ } } if(vc.hasUpperBound()){ - const Constraint ub = vc.getUpperBound(); + const ConstraintP ub = vc.getUpperBound(); if(ub->isTrue()){ - const Constraint lb = d_constraintDatabase.ensureConstraint(const_cast(vc), LowerBound); + const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast(vc), LowerBound); Debug("eq") << "propagate LowerBound " << constraint << lb << ub << endl; - const Constraint negLb = lb->getNegation(); + const ConstraintP negLb = lb->getNegation(); if(!negLb->isTrue()){ negLb->impliedBy(constraint, ub); d_learnedBounds.push_back(negLb); @@ -930,28 +1224,23 @@ Theory::PPAssertStatus TheoryArithPrivate::ppAssert(TNode in, SubstitutionMap& o Assert(elim == Rewriter::rewrite(elim)); - static const unsigned MAX_SUB_SIZE = 2; + static const unsigned MAX_SUB_SIZE = 20; 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; } } @@ -1010,7 +1299,7 @@ void TheoryArithPrivate::setupVariable(const Variable& x){ Assert(!isSetup(n)); ++(d_statistics.d_statUserVariables); - requestArithVar(n,false); + requestArithVar(n, false, false); //ArithVar varN = requestArithVar(n,false); //setupInitialValue(varN); @@ -1049,7 +1338,7 @@ void TheoryArithPrivate::setupVariableList(const VarList& vl){ d_nlIncomplete = true; ++(d_statistics.d_statUserVariables); - requestArithVar(vlNode, false); + requestArithVar(vlNode, false, false); //ArithVar av = requestArithVar(vlNode, false); //setupInitialValue(av); @@ -1242,7 +1531,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) { vector coefficients; asVectors(poly, coefficients, variables); - ArithVar varSlack = requestArithVar(polyNode, true); + ArithVar varSlack = requestArithVar(polyNode, true, false); d_tableau.addRow(varSlack, coefficients, variables); setupBasicValue(varSlack); d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack)); @@ -1267,7 +1556,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) { } } - ++(d_statistics.d_statSlackVariables); + ++(d_statistics.d_statAuxiliaryVariables); markSetup(polyNode); } @@ -1306,7 +1595,7 @@ void TheoryArithPrivate::preRegisterTerm(TNode n) { if(!isSetup(n)){ setupAtom(n); } - Constraint c = d_constraintDatabase.lookup(n); + ConstraintP c = d_constraintDatabase.lookup(n); Assert(c != NullConstraint); Debug("arith::preregister") << "setup constraint" << c << endl; @@ -1323,15 +1612,15 @@ void TheoryArithPrivate::preRegisterTerm(TNode n) { } void TheoryArithPrivate::releaseArithVar(ArithVar v){ - Assert(d_partialModel.hasNode(v)); + //Assert(d_partialModel.hasNode(v)); d_constraintDatabase.removeVariable(v); d_partialModel.releaseArithVar(v); } -ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ +ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){ //TODO : The VarList trick is good enough? - Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS); + Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS || internal); if(getLogicInfo().isLinear() && Variable::isDivMember(x)){ stringstream ss; ss << "A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic: " << x << endl @@ -1342,7 +1631,7 @@ ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ Assert(x.getType().isReal()); // real or integer ArithVar max = d_partialModel.getNumberOfVariables(); - ArithVar varX = d_partialModel.allocate(x, slack); + ArithVar varX = d_partialModel.allocate(x, aux); bool reclaim = max >= d_partialModel.getNumberOfVariables();; @@ -1354,7 +1643,9 @@ ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ } d_constraintDatabase.addVariable(varX); - Debug("arith::arithvar") << x << " |-> " << varX << endl; + Debug("arith::arithvar") << "@" << getSatContext()->getLevel() + << " " << x << " |-> " << varX + << "(relaiming " << reclaim << ")" << endl; Assert(!d_partialModel.hasUpperBound(varX)); Assert(!d_partialModel.hasLowerBound(varX)); @@ -1370,7 +1661,7 @@ void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector& c Node n = variable.getNode(); - Debug("rewriter") << "should be var: " << n << endl; + Debug("arith::asVectors") << "should be var: " << n << endl; // TODO: This VarList::isMember(n) can be stronger Assert(isLeaf(n) || VarList::isMember(n)); @@ -1471,6 +1762,9 @@ Node TheoryArithPrivate::dioCutting(){ 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::ex") << "dioCutting found the plane: " << plane.getNode() << endl; + Debug("arith::dio::ex") << "resulting in the cut: " << lemma << endl; + Debug("arith::dio::ex") << "rewritten " << rewrittenLemma << endl; 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; @@ -1489,16 +1783,16 @@ Node TheoryArithPrivate::callDioSolver(){ Assert(d_partialModel.boundsAreEqual(v)); - Constraint lb = d_partialModel.getLowerBoundConstraint(v); - Constraint ub = d_partialModel.getUpperBoundConstraint(v); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(v); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(v); Node orig = Node::null(); if(lb->isEquality()){ - orig = lb->explainForConflict(); + orig = lb->externalExplainByAssertions(); }else if(ub->isEquality()){ - orig = ub->explainForConflict(); + orig = ub->externalExplainByAssertions(); }else { - orig = ConstraintValue::explainConflict(ub, lb); + orig = Constraint_::externalExplainByAssertions(ub, lb); } Assert(d_partialModel.assignmentIsConsistent(v)); @@ -1521,7 +1815,7 @@ Node TheoryArithPrivate::callDioSolver(){ return d_diosolver.processEquationsForConflict(); } -Constraint TheoryArithPrivate::constraintFromFactQueue(){ +ConstraintP TheoryArithPrivate::constraintFromFactQueue(){ Assert(!done()); TNode assertion = get(); @@ -1530,7 +1824,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ } Kind simpleKind = Comparison::comparisonKind(assertion); - Constraint constraint = d_constraintDatabase.lookup(assertion); + ConstraintP constraint = d_constraintDatabase.lookup(assertion); if(constraint == NullConstraint){ Assert(simpleKind == EQUAL || simpleKind == DISTINCT ); bool isDistinct = simpleKind == DISTINCT; @@ -1542,7 +1836,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ // if is (not true), or false Assert((reEq.getConst() && isDistinct) || (!reEq.getConst() && !isDistinct)); - raiseConflict(assertion); + blackBoxConflict(assertion); } return NullConstraint; } @@ -1563,7 +1857,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Assert(constraint != NullConstraint); if(constraint->negationHasProof()){ - Constraint negation = constraint->getNegation(); + ConstraintP negation = constraint->getNegation(); if(negation->isSelfExplaining()){ if(Debug.isOn("whytheoryenginewhy")){ debugPrintFacts(); @@ -1572,12 +1866,21 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ 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); + constraint->setAssertedToTheTheoryWithNegationTrue(assertion); + if(!constraint->hasProof()){ + Debug("arith::constraint") << "marking as constraint as self explaining " << endl; + constraint->selfExplainingWithNegationTrue(); + }else{ + Debug("arith::constraint") << "already has proof: " << constraint->externalExplainByAssertions() << endl; + } + + raiseConflict(constraint, negation); + // 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()); @@ -1593,14 +1896,14 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Debug("arith::constraint") << "marking as constraint as self explaining " << endl; constraint->selfExplaining(); }else{ - Debug("arith::constraint") << "already has proof: " << constraint->explainForConflict() << endl; + Debug("arith::constraint") << "already has proof: " << constraint->externalExplainByAssertions() << endl; } return constraint; } } -bool TheoryArithPrivate::assertionCases(Constraint constraint){ +bool TheoryArithPrivate::assertionCases(ConstraintP constraint){ Assert(constraint->hasProof()); Assert(!constraint->negationHasProof()); @@ -1609,11 +1912,12 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ switch(constraint->getType()){ case UpperBound: if(isInteger(x_i) && constraint->isStrictUpperBound()){ - Constraint floorConstraint = constraint->getFloor(); + ConstraintP floorConstraint = constraint->getFloor(); if(!floorConstraint->isTrue()){ if(floorConstraint->negationHasProof()){ - Node conf = ConstraintValue::explainConflict(constraint, floorConstraint->getNegation()); - raiseConflict(conf); + raiseConflict(constraint, floorConstraint->getNegation()); + //Node conf = Constraint_::explainConflict(constraint, floorConstraint->getNegation()); + //raiseConflict(conf); return true; }else{ floorConstraint->impliedBy(constraint); @@ -1626,11 +1930,12 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ } case LowerBound: if(isInteger(x_i) && constraint->isStrictLowerBound()){ - Constraint ceilingConstraint = constraint->getCeiling(); + ConstraintP ceilingConstraint = constraint->getCeiling(); if(!ceilingConstraint->isTrue()){ if(ceilingConstraint->negationHasProof()){ - Node conf = ConstraintValue::explainConflict(constraint, ceilingConstraint->getNegation()); - raiseConflict(conf); + raiseConflict(constraint, ceilingConstraint->getNegation()); + //Node conf = Constraint_::explainConflict(constraint, ceilingConstraint->getNegation()); + //raiseConflict(conf); return true; } ceilingConstraint->impliedBy(constraint); @@ -1649,168 +1954,1165 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ return false; } } - /** - * Looks for the next integer variable without an integer assignment in a round robin fashion. - * Changes the value of d_nextIntegerCheckVar. + * Looks for through the variables starting at d_nextIntegerCheckVar + * for the first integer variable that is between its upper and lower bounds + * that has a non-integer assignment. * - * If this returns false, d_nextIntegerCheckVar does not have an integer assignment. - * If this returns true, all integer variables have an integer assignment. + * If assumeBounds is true, skip the check that the variable is in bounds. + * + * If there is no such variable, returns ARITHVAR_SENTINEL; */ -bool TheoryArithPrivate::hasIntegerModel(){ - //if(d_variables.size() > 0){ +ArithVar TheoryArithPrivate::nextIntegerViolatation(bool assumeBounds) const { ArithVar numVars = d_partialModel.getNumberOfVariables(); + ArithVar v = d_nextIntegerCheckVar; 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; + if(isIntegerInput(v)){ + if(!d_partialModel.integralAssignment(v)){ + if( assumeBounds || d_partialModel.assignmentIsConsistent(v) ){ + return v; + } } } - } while((d_nextIntegerCheckVar = (1 + d_nextIntegerCheckVar == numVars ? 0 : 1 + d_nextIntegerCheckVar)) != rrEnd); + v= (1 + v == numVars) ? 0 : (1 + v); + }while(v != rrEnd); + } + return ARITHVAR_SENTINEL; +} + +/** + * Checks the set of integer variables I to see if each variable + * in I has an integer assignment. + */ +bool TheoryArithPrivate::hasIntegerModel(){ + ArithVar next = nextIntegerViolatation(true); + if(next != ARITHVAR_SENTINEL){ + d_nextIntegerCheckVar = next; + if(Debug.isOn("arith::hasIntegerModel")){ + Debug("arith::hasIntegerModel") << "has int model? " << next << endl; + d_partialModel.printModel(next, Debug("arith::hasIntegerModel")); + } + return false; + }else{ + return true; } - 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& lemmas){ - //output the lemmas - for(vector::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); + Assert(anyConflict()); + if(!conflictQueueEmpty()){ + Assert(!d_conflicts.empty()); + for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){ + const ConstraintCPVec& vec = d_conflicts[i]; + Node conflict = Constraint_::externalExplainByAssertions(vec); + Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << endl; + (d_containing.d_out)->conflict(conflict); + } } + if(!d_blackBoxConflict.get().isNull()){ + Node bb = d_blackBoxConflict.get(); + Debug("arith::conflict") << "black box conflict" << bb << endl; + (d_containing.d_out)->conflict(bb); + } +} +void TheoryArithPrivate::outputLemma(TNode lem) { + (d_containing.d_out)->lemma(lem); } -bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ - Assert(d_qflraStatus != Result::SAT); +// void TheoryArithPrivate::branchVector(const std::vector& lemmas){ +// //output the lemmas +// for(vector::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::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){ + int level = getSatContext()->getLevel(); + Debug("approx") + << "attemptSolveInteger " << d_qflraStatus + << " " << emmmittedLemmaOrSplit + << " " << effortLevel + << " " << d_lastContextIntegerAttempted + << " " << level + << " " << hasIntegerModel() + << endl; + + if(d_qflraStatus == Result::UNSAT){ return false; } + if(emmmittedLemmaOrSplit){ return false; } + if(!options::useApprox()){ return false; } + if(!ApproximateSimplex::enabled()){ return false; } + + if(Theory::fullEffort(effortLevel)){ + if(hasIntegerModel()){ + return false; + }else{ + return getSolveIntegerResource(); + } + } - d_partialModel.stopQueueingBoundCounts(); - UpdateTrackingCallback utcb(&d_linEq); - d_partialModel.processBoundsQueue(utcb); - d_linEq.startTrackingBoundCounts(); + if(d_lastContextIntegerAttempted <= 0){ + if(hasIntegerModel()){ + d_lastContextIntegerAttempted = getSatContext()->getLevel(); + return false; + }else{ + return getSolveIntegerResource(); + } + } - bool noPivotLimit = Theory::fullEffort(effortLevel) || - !options::restrictedPivots(); - bool emmittedConflictOrSplit = false; + if(!options::trySolveIntStandardEffort()){ return false; } + + if (d_lastContextIntegerAttempted <= (level >> 2)){ - SimplexDecisionProcedure& simplex = - options::useFC() ? (SimplexDecisionProcedure&)d_fcSimplex : - (options::useSOI() ? (SimplexDecisionProcedure&)d_soiSimplex : - (SimplexDecisionProcedure&)d_dualSimplex); + double d = (double)(d_solveIntMaybeHelp + 1) / (d_solveIntAttempts + 1 + level*level); + double t = fRand(0.0, 1.0); + if(t < d){ + return getSolveIntegerResource(); + } + } + return false; +} + +bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer); + + Assert(d_replayVariables.empty()); + Assert(d_replayConstraints.empty()); + + size_t enteringPropN = d_currentPropagationList.size(); + Assert(conflictQueueEmpty()); + TreeLog& tl = getTreeLog(); + //tl.applySelected(); /* set row ids */ - bool useFancyFinal = options::fancyFinal() && ApproximateSimplex::enabled(); + d_replayedLemmas = false; + + std::vector res; + try{ + /* use the try block for the purpose of pushing the sat context */ + context::Context::ScopedPush speculativePush(getSatContext()); + d_cmEnabled = false; + res = replayLogRec(approx, tl.getRootId(), NullConstraint, 1); + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + + for(size_t i =0, N = res.size(); i < N; ++i){ + raiseConflict(res[i]); + } + if(res.empty()){ + ++d_statistics.d_replayAttemptFailed; + } + if(d_currentPropagationList.size() > enteringPropN){ + d_currentPropagationList.resize(enteringPropN); + } - if(!useFancyFinal){ - d_qflraStatus = simplex.findModel(noPivotLimit); + Assert(d_replayVariables.empty()); + Assert(d_replayConstraints.empty()); + + return !conflictQueueEmpty(); +} + +std::pair TheoryArithPrivate::replayGetConstraint(const DenseMap& lhs, Kind k, const Rational& rhs, bool branch) +{ + ArithVar added = ARITHVAR_SENTINEL; + Node sum = toSumNode(d_partialModel, lhs); + if(sum.isNull()){ return make_pair(NullConstraint, added); } + + Node norm = Rewriter::rewrite(sum); + DeltaRational dr(rhs); + + ConstraintType t = (k == kind::LEQ) ? UpperBound : LowerBound; + + Assert(!branch || d_partialModel.hasArithVar(norm)); + ArithVar v = ARITHVAR_SENTINEL; + if(d_partialModel.hasArithVar(norm)){ + + v = d_partialModel.asArithVar(norm); + Debug("approx::constraint") << "replayGetConstraint found " + << norm << " |-> " << v << " @ " << getSatContext()->getLevel() << endl; + Assert(!branch || d_partialModel.isIntegerInput(v)); }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 + v = requestArithVar(norm, true, true); + d_replayVariables.push_back(v); - int16_t oldCap = options::arithStandardCheckVarOrderPivots(); + added = v; - static const int32_t pass2Limit = 10; - static const int32_t relaxationLimit = 10000; - static const int32_t mipLimit = 200000; + Debug("approx::constraint") << "replayGetConstraint adding " + << norm << " |-> " << v << " @ " << getSatContext()->getLevel() << endl; - //cout << "start" << endl; - d_qflraStatus = simplex.findModel(false); - //cout << "end" << endl; - if(d_qflraStatus == Result::SAT_UNKNOWN || - (d_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){ + Polynomial poly = Polynomial::parsePolynomial(norm); + vector variables; + vector coefficients; + asVectors(poly, coefficients, variables); + d_tableau.addRow(v, coefficients, variables); + setupBasicValue(v); + d_linEq.trackRowIndex(d_tableau.basicToRowIndex(v)); + } + Assert(d_partialModel.hasArithVar(norm)); + Assert(d_partialModel.asArithVar(norm) == v); + Assert(d_constraintDatabase.variableDatabaseIsSetup(v)); + + ConstraintP imp = d_constraintDatabase.getBestImpliedBound(v, t, dr); + if(imp != NullConstraint){ + if(imp->getValue() == dr){ + Assert(added == ARITHVAR_SENTINEL); + return make_pair(imp, added); + } + } + ConstraintP newc = d_constraintDatabase.getConstraint(v, t, dr); + d_replayConstraints.push_back(newc); + return make_pair(newc, added); +} + +std::pair TheoryArithPrivate::replayGetConstraint(ApproximateSimplex* approx, const NodeLog& nl) throw(RationalFromDoubleException){ + Assert(nl.isBranch()); + Assert(d_lhsTmp.empty()); + + ArithVar v = approx->getBranchVar(nl); + if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){ + if(d_partialModel.hasNode(v)){ + d_lhsTmp.set(v, Rational(1)); + double dval = nl.branchValue(); + Rational val = ApproximateSimplex::estimateWithCFE(dval); + Rational fl(val.floor()); + pair p; + p = replayGetConstraint(d_lhsTmp, kind::LEQ, fl, true); + d_lhsTmp.purge(); + return p; + } + } + return make_pair(NullConstraint, ARITHVAR_SENTINEL); +} + +std::pair TheoryArithPrivate::replayGetConstraint(const CutInfo& ci) { + Assert(ci.reconstructed()); + const DenseMap& lhs = ci.getReconstruction().lhs; + const Rational& rhs = ci.getReconstruction().rhs; + Kind k = ci.getKind(); + + return replayGetConstraint(lhs, k, rhs, ci.getKlass() == BranchCutKlass); +} + +// Node denseVectorToLiteral(const ArithVariables& vars, const DenseVector& dv, Kind k){ +// NodeManager* nm = NodeManager::currentNM(); +// Node sumLhs = toSumNode(vars, dv.lhs); +// Node ineq = nm->mkNode(k, sumLhs, mkRationalNode(dv.rhs) ); +// Node lit = Rewriter::rewrite(ineq); +// return lit; +// } + +Node toSumNode(const ArithVariables& vars, const DenseMap& sum){ + NodeBuilder<> nb(kind::PLUS); + NodeManager* nm = NodeManager::currentNM(); + DenseMap::const_iterator iter, end; + iter = sum.begin(), end = sum.end(); + for(; iter != end; ++iter){ + ArithVar x = *iter; + if(!vars.hasNode(x)){ return Node::null(); } + Node xNode = vars.asNode(x); + const Rational& q = sum[x]; + nb << nm->mkNode(kind::MULT, mkRationalNode(q), xNode); + } + return safeConstructNary(nb); +} + + +void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){ + Assert(conflictQueueEmpty()); + std::vector< ConstraintCPVec > conflicts; + + approx->tryCut(nid, bci); + Debug("approx::branch") << "tryBranchCut" << bci << endl; + Assert(bci.reconstructed()); + Assert(!bci.proven()); + pair p = replayGetConstraint(bci); + Assert(p.second == ARITHVAR_SENTINEL); + ConstraintP bc = p.first; + Assert(bc != NullConstraint); + if(bc->hasProof()){ + return; + } + + ConstraintP bcneg = bc->getNegation(); + { + context::Context::ScopedPush speculativePush(getSatContext()); + replayAssert(bcneg); + if(conflictQueueEmpty()){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer); + + //test for linear feasibility + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + simplex.findModel(false); + + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + } + for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){ + conflicts.push_back(d_conflicts[i]); + // remove the floor/ceiling contraint implied by bcneg + Constraint_::assertionFringe(conflicts.back()); + } + + if(Debug.isOn("approx::branch")){ + if(d_conflicts.empty()){ + entireStateIsConsistent("branchfailure"); + } + } + } + + Debug("approx::branch") << "branch constraint " << bc << endl; + for(size_t i = 0, N = conflicts.size(); i < N; ++i){ + ConstraintCPVec& conf = conflicts[i]; + + // make sure to be working on the assertion fringe! + if(!contains(conf, bcneg)){ + Debug("approx::branch") << "reraise " << conf << endl; + raiseConflict(conf); + }else if(!bci.proven()){ + drop(conf, bcneg); + bci.setExplanation(conf); + Debug("approx::branch") << "dropped " << bci << endl; + } + } +} - ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel); - approxSolver->setPivotLimit(relaxationLimit); +void TheoryArithPrivate::replayAssert(ConstraintP c) { + if(!c->assertedToTheTheory()){ + if(c->negationHasProof()){ + ConstraintP neg = c->getNegation(); + raiseConflict(c, neg); + Debug("approx::replayAssert") << "replayAssertion conflict " << neg << " : " << c << endl; + }else if(!c->hasProof()){ + c->setInternalDecision(); + assertionCases(c); + Debug("approx::replayAssert") << "replayAssert " << c << " set internal" << endl; + }else{ + assertionCases(c); + Debug("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl; + } + }else{ + Debug("approx::replayAssert") << "replayAssert " << c << " already asserted" << endl; + } +} - if(!d_guessedCoeffSet){ - d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); - d_guessedCoeffSet = true; +// ConstraintCPVec TheoryArithPrivate::toExplanation(Node n) const { +// ConstraintCPVec res; +// cout << "toExplanation" << endl; +// if(n.getKind() == kind::AND){ +// for(unsigned i = 0; i < n.getNumChildren(); ++i){ +// ConstraintP c = d_constraintDatabase.lookup(n[i]); +// if(c == NullConstraint){ return std::vector(); } +// res.push_back(c); +// cout << "\t"<(); } +// res.push_back(c); +// } +// return res; +// } + +// void TheoryArithPrivate::enqueueConstraints(std::vector& out, Node n) const{ +// if(n.getKind() == kind::AND){ +// for(unsigned i = 0, N = n.getNumChildren(); i < N; ++i){ +// enqueueConstraints(out, n[i]); +// } +// }else{ +// ConstraintP c = d_constraintDatabase.lookup(n); +// if(c == NullConstraint){ +// cout << "failing on " << n << endl; +// } +// Assert(c != NullConstraint); +// out.push_back(c); +// } +// } + +// ConstraintCPVec TheoryArithPrivate::resolveOutPropagated(const ConstraintCPVec& v, const std::set& propagated) const { +// cout << "resolveOutPropagated()" << conf << endl; +// std::set final; +// std::set processed; +// std::vector to_process; +// enqueueConstraints(to_process, conf); +// while(!to_process.empty()){ +// ConstraintP c = to_process.back(); to_process.pop_back(); +// if(processed.find(c) != processed.end()){ +// continue; +// }else{ +// if(propagated.find(c) == propagated.end()){ +// final.insert(c); +// }else{ +// Node exp = c->explainForPropagation(); +// enqueueConstraints(to_process, exp); +// } +// processed.insert(c); +// } +// } +// cout << "final size: " << final.size() << std::endl; +// NodeBuilder<> nb(kind::AND); +// std::set::const_iterator iter = final.begin(), end = final.end(); +// for(; iter != end; ++iter){ +// ConstraintP c = *iter; +// c->explainForConflict(nb); +// } +// Node newConf = safeConstructNary(nb); +// cout << "resolveOutPropagated("<" << newConf << endl; +// return newConf; +// } + +void TheoryArithPrivate::resolveOutPropagated(std::vector& confs, const std::set& propagated) const { + Debug("arith::resolveOutPropagated") + << "starting resolveOutPropagated() " << confs.size() << endl; + for(size_t i =0, N = confs.size(); i < N; ++i){ + ConstraintCPVec& conf = confs[i]; + size_t orig = conf.size(); + Constraint_::assertionFringe(conf); + Debug("arith::resolveOutPropagated") + << " conf["<& confs) const { + int checks CVC4_UNUSED = 0; + int subsumed CVC4_UNUSED = 0; + + for(size_t i =0, N= confs.size(); i < N; ++i){ + ConstraintCPVec& conf = confs[i]; + std::sort(conf.begin(), conf.end()); + } + + std::sort(confs.begin(), confs.end(), SizeOrd()); + for(size_t i = 0; i < confs.size(); i++){ + ConstraintCPVec& a = confs[i]; + // i is not subsumed + for(size_t j = i+1; j < confs.size();){ + ConstraintCPVec& b = confs[j]; + checks++; + bool subsumes = std::includes(a.begin(), a.end(), b.begin(), b.end()); + if(subsumes){ + ConstraintCPVec& back = confs.back(); + b.swap(back); + confs.pop_back(); + subsumed++; + }else{ + j++; } - if(!d_guessedCoeffs.empty()){ - approxSolver->setOptCoeffs(d_guessedCoeffs); + } + } + Debug("arith::subsumption") << "subsumed " << subsumed << "/" << checks << endl; +} + +std::vector TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){ + ++(d_statistics.d_replayLogRecCount); + Debug("approx::replayLogRec") << "replayLogRec()" + << d_statistics.d_replayLogRecCount.getData() << std::endl; + + size_t rpvars_size = d_replayVariables.size(); + size_t rpcons_size = d_replayConstraints.size(); + std::vector res; + + { /* create a block for the purpose of pushing the sat context */ + context::Context::ScopedPush speculativePush(getSatContext()); + Assert(!anyConflict()); + Assert(conflictQueueEmpty()); + set propagated; + + TreeLog& tl = getTreeLog(); + + if(bc != NullConstraint){ + replayAssert(bc); + } + + const NodeLog& nl = tl.getNode(nid); + NodeLog::const_iterator iter = nl.begin(), end = nl.end(); + for(; conflictQueueEmpty() && iter != end; ++iter){ + CutInfo* ci = *iter; + bool reject = false; + //cout << " trying " << *ci << endl; + if(ci->getKlass() == RowsDeletedKlass){ + RowsDeleted* rd = dynamic_cast(ci); + + tl.applyRowsDeleted(nid, *rd); + // The previous line modifies nl + + ++d_statistics.d_applyRowsDeleted; + }else if(ci->getKlass() == BranchCutKlass){ + BranchCutInfo* bci = dynamic_cast(ci); + Assert(bci != NULL); + tryBranchCut(approx, nid, *bci); + + ++d_statistics.d_branchCutsAttempted; + }else{ + approx->tryCut(nid, *ci); + if(ci->getKlass() == GmiCutKlass){ + ++d_statistics.d_gmiCutsAttempted; + }else if(ci->getKlass() == MirCutKlass){ + ++d_statistics.d_mirCutsAttempted; + } + + if(ci->reconstructed() && ci->proven()){ + const DenseMap& row = ci->getReconstruction().lhs; + reject = !complexityBelow(row, options::replayRejectCutSize()); + } } + if(conflictQueueEmpty()){ + if(reject){ + ++d_statistics.d_cutsRejectedDuringReplay; + }else if(ci->reconstructed()){ + // success + ++d_statistics.d_cutsReconstructed; + + pair p = replayGetConstraint(*ci); + if(p.second != ARITHVAR_SENTINEL){ + Assert(ci->getRowId() >= 1); + tl.mapRowId(nl.getNodeId(), ci->getRowId(), p.second); + } + ConstraintP con = p.first; + if(Debug.isOn("approx::replayLogRec")){ + Debug("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl; + } - ApproximateSimplex::ApproxResult relaxRes, mipRes; - ApproximateSimplex::Solution relaxSolution, mipSolution; - relaxRes = approxSolver->solveRelaxation(); - switch(relaxRes){ - case ApproximateSimplex::ApproxSat: - { - relaxSolution = approxSolver->extractRelaxation(); + if(ci->proven()){ + ++d_statistics.d_cutsProven; - if(d_likelyIntegerInfeasible){ - d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); + const ConstraintCPVec& exp = ci->getExplanation(); + // success + Assert(!con->negationHasProof()); + if(con->isTrue()){ + Debug("approx::replayLogRec") << "not asserted?" << endl; + }else{ + con->impliedBy(exp); + replayAssert(con); + Debug("approx::replayLogRec") << "cut prop" << endl; + } }else{ - approxSolver->setPivotLimit(mipLimit); - mipRes = approxSolver->solveMIP(); - d_errorSet.reduceToSignals(); - //Message() << "here" << endl; - if(mipRes == ApproximateSimplex::ApproxSat){ - mipSolution = approxSolver->extractMIP(); - d_qflraStatus = d_attemptSolSimplex.attempt(mipSolution); + ++d_statistics.d_cutsProofFailed; + Debug("approx::replayLogRec") << "failed to get proof " << *ci << endl; + } + }else if(ci->getKlass() != RowsDeletedKlass){ + ++d_statistics.d_cutsReconstructionFailed; + } + } + } + + /* check if the system is feasible under with the cuts */ + if(conflictQueueEmpty()){ + Assert(options::replayEarlyCloseDepths() >= 1); + if(!nl.isBranch() || depth % options::replayEarlyCloseDepths() == 0 ){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer); + //test for linear feasibility + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + simplex.findModel(false); + + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + } + }else{ + ++d_statistics.d_replayLogRecConflictEscalation; + } + + if(!conflictQueueEmpty()){ + /* if a conflict has been found stop */ + for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){ + res.push_back(d_conflicts[i]); + } + ++d_statistics.d_replayLogRecEarlyExit; + }else if(nl.isBranch()){ + /* if it is a branch try the branch */ + pair p = replayGetConstraint(approx, nl); + Assert(p.second == ARITHVAR_SENTINEL); + ConstraintP dnc = p.first; + if(dnc != NullConstraint){ + ConstraintP upc = dnc->getNegation(); + + int dnid = nl.getDownId(); + int upid = nl.getUpId(); + + NodeLog& dnlog = tl.getNode(dnid); + NodeLog& uplog = tl.getNode(upid); + dnlog.copyParentRowIds(); + uplog.copyParentRowIds(); + + std::vector dnres; + std::vector upres; + std::vector containsdn; + std::vector containsup; + if(res.empty()){ + dnres = replayLogRec(approx, dnid, dnc, depth+1); + for(size_t i = 0, N = dnres.size(); i < N; ++i){ + ConstraintCPVec& conf = dnres[i]; + if(contains(conf, dnc)){ + containsdn.push_back(i); }else{ - if(mipRes == ApproximateSimplex::ApproxUnsat){ - d_likelyIntegerInfeasible = true; - } - d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); + res.push_back(conf); } } - options::arithStandardCheckVarOrderPivots.set(pass2Limit); - if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } - //Message() << "done" << endl; + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl; + ++d_statistics.d_replayBranchSkips; } - break; - case ApproximateSimplex::ApproxUnsat: - { - ApproximateSimplex::Solution sol = approxSolver->extractRelaxation(); - d_qflraStatus = d_attemptSolSimplex.attempt(sol); - options::arithStandardCheckVarOrderPivots.set(pass2Limit); + if(res.empty()){ + upres = replayLogRec(approx, upid, upc, depth+1); - if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } + for(size_t i = 0, N = upres.size(); i < N; ++i){ + ConstraintCPVec& conf = upres[i]; + if(contains(conf, upc)){ + containsup.push_back(i); + }else{ + res.push_back(conf); + } + } + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping" << uplog << std::endl; + ++d_statistics.d_replayBranchSkips; + } + + if(res.empty()){ + for(size_t i = 0, N = containsdn.size(); i < N; ++i){ + ConstraintCPVec& dnconf = dnres[containsdn[i]]; + for(size_t j = 0, M = containsup.size(); j < M; ++j){ + ConstraintCPVec& upconf = upres[containsup[j]]; + + res.push_back(ConstraintCPVec()); + ConstraintCPVec& back = res.back(); + resolve(back, dnc, dnconf, upconf); + } + } + if(res.size() >= 2u){ + subsumption(res); + + if(res.size() > 100u){ + res.resize(100u); + } + } + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl; + } + Debug("approx::replayLogRec") << "found #"< rpcons_size){ + ConstraintP c = d_replayConstraints.back(); + d_replayConstraints.pop_back(); + d_constraintDatabase.deleteConstraintAndNegation(c); + } + + /* Garbage collect the ArithVars made by this call */ + if(d_replayVariables.size() > rpvars_size){ + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + while(d_replayVariables.size() > rpvars_size){ + ArithVar v = d_replayVariables.back(); + d_replayVariables.pop_back(); + Assert(d_partialModel.canBeReleased(v)); + if(!d_tableau.isBasic(v)){ + /* if it is not basic make it basic. */ + ArithVar b = ARITHVAR_SENTINEL; + for(Tableau::ColIterator ci = d_tableau.colIterator(v); !ci.atEnd(); ++ci){ + const Tableau::Entry& e = *ci; + b = d_tableau.rowIndexToBasic(e.getRowIndex()); + break; + } + Assert(b != ARITHVAR_SENTINEL); + DeltaRational cp = d_partialModel.getAssignment(b); + if(d_partialModel.cmpAssignmentLowerBound(b) < 0){ + cp = d_partialModel.getLowerBound(b); + }else if(d_partialModel.cmpAssignmentUpperBound(b) > 0){ + cp = d_partialModel.getUpperBound(b); + } + d_linEq.pivotAndUpdate(b, v, cp); + } + Assert(d_tableau.isBasic(v)); + d_linEq.stopTrackingRowIndex(d_tableau.basicToRowIndex(v)); + d_tableau.removeBasicRow(v); + + releaseArithVar(v); + Debug("approx::vars") << "releasing " << v << endl; + } + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + d_partialModel.attemptToReclaimReleased(); + } + return res; +} + +TreeLog& TheoryArithPrivate::getTreeLog(){ + if(d_treeLog == NULL){ + d_treeLog = new TreeLog(); + } + return *d_treeLog; +} + +ApproximateStatistics& TheoryArithPrivate::getApproxStats(){ + if(d_approxStats == NULL){ + d_approxStats = new ApproximateStatistics(); + } + return *d_approxStats; +} + +Node TheoryArithPrivate::branchToNode(ApproximateSimplex* approx, const NodeLog& bn) const throw(RationalFromDoubleException) { + Assert(bn.isBranch()); + ArithVar v = approx->getBranchVar(bn); + if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){ + if(d_partialModel.hasNode(v)){ + Node n = d_partialModel.asNode(v); + double dval = bn.branchValue(); + Rational val = ApproximateSimplex::estimateWithCFE(dval); + Rational fl(val.floor()); + NodeManager* nm = NodeManager::currentNM(); + Node leq = nm->mkNode(kind::LEQ, n, mkRationalNode(fl)); + Node norm = Rewriter::rewrite(leq); + return norm; + } + } + return Node::null(); +} + +Node TheoryArithPrivate::cutToLiteral(ApproximateSimplex* approx, const CutInfo& ci) const{ + Assert(ci.reconstructed()); + + const DenseMap& lhs = ci.getReconstruction().lhs; + Node sum = toSumNode(d_partialModel, lhs); + if(!sum.isNull()){ + Kind k = ci.getKind(); + Assert(k == kind::LEQ || k == kind::GEQ); + Node rhs = mkRationalNode(ci.getReconstruction().rhs); + + NodeManager* nm = NodeManager::currentNM(); + Node ineq = nm->mkNode(k, sum, rhs); + return Rewriter::rewrite(ineq); + } + return Node::null(); +} + +bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){ + try{ + ++(d_statistics.d_mipReplayLemmaCalls); + bool anythingnew = false; + + TreeLog& tl = getTreeLog(); + NodeLog& root = tl.getRootNode(); + root.applySelected(); /* set row ids */ + + vector cuts = approx->getValidCuts(root); + for(size_t i =0, N =cuts.size(); i < N; ++i){ + const CutInfo* cut = cuts[i]; + Assert(cut->reconstructed()); + Assert(cut->proven()); + + const DenseMap& row = cut->getReconstruction().lhs; + if(!complexityBelow(row, options::lemmaRejectCutSize())){ + ++(d_statistics.d_cutsRejectedDuringLemmas); + continue; + } + + Node cutConstraint = cutToLiteral(approx, *cut); + if(!cutConstraint.isNull()){ + const ConstraintCPVec& exp = cut->getExplanation(); + Node asLemma = Constraint_::externalExplainByAssertions(exp); + + Node implied = Rewriter::rewrite(cutConstraint); + anythingnew = anythingnew || !isSatLiteral(implied); + + Node implication = asLemma.impNode(implied); + // DO NOT CALL OUTPUT LEMMA! + d_approxCuts.push_back(implication); + Debug("approx::lemmas") << "cut["< 0 && numCols > 0); ++vi){ + ArithVar v = *vi; + + if(d_partialModel.isAuxiliary(v)){ + ++numRows; + }else{ + ++numCols; + } + } + return (numRows > 0 && numCols > 0); +} + +// solve() +// res = solveRealRelaxation(effortLevel); +// switch(res){ +// case LinFeas: +// case LinInfeas: +// return replay() +// case Unknown: +// case Error +// if() +void TheoryArithPrivate::solveInteger(Theory::Effort effortLevel){ + if(!safeToCallApprox()) { return; } + + Assert(safeToCallApprox()); + TimerStat::CodeTimer codeTimer(d_statistics.d_solveIntTimer); + + ++(d_statistics.d_solveIntCalls); + d_statistics.d_inSolveInteger.setData(1); + + if(!Theory::fullEffort(effortLevel)){ + d_solveIntAttempts++; + ++(d_statistics.d_solveStandardEffort); + } + + // if integers are attempted, + Assert(options::useApprox()); + Assert(ApproximateSimplex::enabled()); + + int level = getSatContext()->getLevel(); + d_lastContextIntegerAttempted = level; + + + static const int32_t mipLimit = 200000; + + TreeLog& tl = getTreeLog(); + ApproximateStatistics& stats = getApproxStats(); + ApproximateSimplex* approx = + ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats); + + try{ + approx->setPivotLimit(mipLimit); + if(!d_guessedCoeffSet){ + d_guessedCoeffs = approx->heuristicOptCoeffs(); + d_guessedCoeffSet = true; + } + if(!d_guessedCoeffs.empty()){ + approx->setOptCoeffs(d_guessedCoeffs); + } + static const int32_t depthForLikelyInfeasible = 10; + int maxDepthPass1 = d_likelyIntegerInfeasible ? + depthForLikelyInfeasible : options::maxApproxDepth(); + approx->setBranchingDepth(maxDepthPass1); + approx->setBranchOnVariableLimit(100); + LinResult relaxRes = approx->solveRelaxation(); + if( relaxRes == LinFeasible ){ + MipResult mipRes = approx->solveMIP(false); + Debug("arith::solveInteger") << "mipRes " << mipRes << endl; + switch(mipRes) { + case MipBingo: + // attempt the solution + { + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + ApproximateSimplex::Solution mipSolution; + mipSolution = approx->extractMIP(); + importSolution(mipSolution); + solveRelaxationOrPanic(effortLevel); + + // shutdown simplex + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); } break; - default: + case MipClosed: + /* All integer branches closed */ + approx->setPivotLimit(2*mipLimit); + mipRes = approx->solveMIP(true); + if(mipRes == MipClosed){ + d_likelyIntegerInfeasible = true; + replayLog(approx); + } + if(!(anyConflict() || !d_approxCuts.empty())){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + break; + case BranchesExhausted: + case ExecExhausted: + case PivotsExhauasted: + if(mipRes == BranchesExhausted){ + ++d_statistics.d_branchesExhausted; + }else if(mipRes == ExecExhausted){ + ++d_statistics.d_execExhausted; + }else if(mipRes == PivotsExhauasted){ + ++d_statistics.d_pivotsExhausted; + } + + approx->setPivotLimit(2*mipLimit); + approx->setBranchingDepth(2); + mipRes = approx->solveMIP(true); + replayLemmas(approx); + break; + case MipUnknown: break; } - delete approxSolver; } + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + delete approx; - if(d_qflraStatus == Result::SAT_UNKNOWN){ - //Message() << "got sat unknown" << endl; - vector toCut = cutAllBounded(); - if(toCut.size() > 0){ - branchVector(toCut); - emmittedConflictOrSplit = true; - }else{ - //Message() << "splitting" << endl; + if(!Theory::fullEffort(effortLevel)){ + if(anyConflict() || !d_approxCuts.empty()){ + d_solveIntMaybeHelp++; + } + } - d_qflraStatus = simplex.findModel(noPivotLimit); + d_statistics.d_inSolveInteger.setData(0); +} + +SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){ + if(pass1){ + if(d_pass1SDP == NULL){ + if(options::useFC()){ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex); + }else if(options::useSOI()){ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_soiSimplex); + }else{ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_dualSimplex); + } + } + Assert(d_pass1SDP != NULL); + return *d_pass1SDP; + }else{ + if(d_otherSDP == NULL){ + if(options::useFC()){ + d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex); + }else if(options::useSOI()){ + d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex); + }else{ + d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex); } } + Assert(d_otherSDP != NULL); + return *d_otherSDP; + } +} + +void TheoryArithPrivate::importSolution(const ApproximateSimplex::Solution& solution){ + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution before " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } + + d_qflraStatus = d_attemptSolSimplex.attempt(solution); + + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } + + if(d_qflraStatus != Result::UNSAT){ + static const int32_t pass2Limit = 20; + int16_t oldCap = options::arithStandardCheckVarOrderPivots(); + options::arithStandardCheckVarOrderPivots.set(pass2Limit); + SimplexDecisionProcedure& simplex = selectSimplex(false); + d_qflraStatus = simplex.findModel(false); options::arithStandardCheckVarOrderPivots.set(oldCap); } + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution after " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } +} + +bool TheoryArithPrivate::solveRelaxationOrPanic(Theory::Effort effortLevel){ + // if at this point the linear relaxation is still unknown, + // attempt to branch an integer variable as a last ditch effort on full check + if(d_qflraStatus == Result::SAT_UNKNOWN){ + d_qflraStatus = selectSimplex(true).findModel(false); + } + + if(Theory::fullEffort(effortLevel) && d_qflraStatus == Result::SAT_UNKNOWN){ + ArithVar canBranch = nextIntegerViolatation(false); + if(canBranch != ARITHVAR_SENTINEL){ + ++d_statistics.d_panicBranches; + Node branch = branchIntegerVariable(canBranch); + Assert(branch.getKind() == kind::OR); + Node rwbranch = Rewriter::rewrite(branch[0]); + if(!isSatLiteral(rwbranch)){ + d_approxCuts.push_back(branch); + return true; + } + } + d_qflraStatus = selectSimplex(false).findModel(true); + } + return false; +} + +bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ + TimerStat::CodeTimer codeTimer(d_statistics.d_solveRealRelaxTimer); + Assert(d_qflraStatus != Result::SAT); + + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + bool noPivotLimit = Theory::fullEffort(effortLevel) || + !options::restrictedPivots(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + + bool useApprox = options::useApprox() && ApproximateSimplex::enabled() && getSolveIntegerResource(); + + bool noPivotLimitPass1 = noPivotLimit && !useApprox; + d_qflraStatus = simplex.findModel(noPivotLimitPass1); + + if(d_qflraStatus == Result::SAT_UNKNOWN && useApprox && safeToCallApprox()){ + // pass2: fancy-final + static const int32_t relaxationLimit = 10000; + Assert(ApproximateSimplex::enabled()); + + TreeLog& tl = getTreeLog(); + ApproximateStatistics& stats = getApproxStats(); + ApproximateSimplex* approxSolver = + ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats); + + approxSolver->setPivotLimit(relaxationLimit); + + if(!d_guessedCoeffSet){ + d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); + d_guessedCoeffSet = true; + } + if(!d_guessedCoeffs.empty()){ + approxSolver->setOptCoeffs(d_guessedCoeffs); + } + + ++d_statistics.d_relaxCalls; + + ApproximateSimplex::Solution relaxSolution; + LinResult relaxRes = approxSolver->solveRelaxation(); + try{ + Debug("solveRealRelaxation") << "solve relaxation? " << endl; + switch(relaxRes){ + case LinFeasible: + Debug("solveRealRelaxation") << "lin feasible? " << endl; + ++d_statistics.d_relaxLinFeas; + relaxSolution = approxSolver->extractRelaxation(); + importSolution(relaxSolution); + if(d_qflraStatus != Result::SAT){ + ++d_statistics.d_relaxLinFeasFailures; + } + break; + case LinInfeasible: + // todo attempt to recreate approximate conflict + ++d_statistics.d_relaxLinInfeas; + Debug("solveRealRelaxation") << "lin infeasible " << endl; + relaxSolution = approxSolver->extractRelaxation(); + importSolution(relaxSolution); + if(d_qflraStatus != Result::UNSAT){ + ++d_statistics.d_relaxLinInfeasFailures; + } + break; + case LinExhausted: + ++d_statistics.d_relaxLinExhausted; + Debug("solveRealRelaxation") << "exhuasted " << endl; + break; + case LinUnknown: + default: + ++d_statistics.d_relaxOthers; + break; + } + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + delete approxSolver; + + } + + bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel); + // TODO Save zeroes with no conflicts d_linEq.stopTrackingBoundCounts(); d_partialModel.startQueueingBoundCounts(); @@ -1818,8 +3120,162 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ return emmittedConflictOrSplit; } +// LinUnknown, /* Unknown error */ +// LinFeasible, /* Relaxation is feasible */ +// LinInfeasible, /* Relaxation is infeasible/all integer branches closed */ +// LinExhausted +// // 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; + +// //cout << "start" << endl; +// d_qflraStatus = simplex.findModel(false); +// //cout << "end" << endl; +// if(d_qflraStatus == Result::SAT_UNKNOWN || +// (d_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){ + +// ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, *(getTreeLog()), *(getApproxStats())); +// approxSolver->setPivotLimit(relaxationLimit); + +// if(!d_guessedCoeffSet){ +// d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); +// d_guessedCoeffSet = true; +// } +// if(!d_guessedCoeffs.empty()){ +// approxSolver->setOptCoeffs(d_guessedCoeffs); +// } + +// MipResult mipRes; +// ApproximateSimplex::Solution relaxSolution, mipSolution; +// LinResult relaxRes = approxSolver->solveRelaxation(); +// switch(relaxRes){ +// case LinFeasible: +// { +// relaxSolution = approxSolver->extractRelaxation(); + +// /* If the approximate solver known to be integer infeasible +// * only redo*/ +// int maxDepth = +// d_likelyIntegerInfeasible ? 1 : options::arithMaxBranchDepth(); + + +// if(d_likelyIntegerInfeasible){ +// d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); +// }else{ +// approxSolver->setPivotLimit(mipLimit); +// mipRes = approxSolver->solveMIP(false); +// if(mipRes == ApproximateSimplex::ApproxUnsat){ +// mipRes = approxSolver->solveMIP(true); +// } +// d_errorSet.reduceToSignals(); +// //Message() << "here" << endl; +// if(mipRes == ApproximateSimplex::ApproxSat){ +// mipSolution = approxSolver->extractMIP(); +// d_qflraStatus = d_attemptSolSimplex.attempt(mipSolution); +// }else{ +// if(mipRes == ApproximateSimplex::ApproxUnsat){ +// d_likelyIntegerInfeasible = true; +// } +// vector lemmas = approxSolver->getValidCuts(); +// for(size_t i = 0; i < lemmas.size(); ++i){ +// d_approxCuts.pushback(lemmas[i]); +// } +// d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); +// } +// } +// options::arithStandardCheckVarOrderPivots.set(pass2Limit); +// if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } +// //Message() << "done" << endl; +// } +// break; +// case ApproximateSimplex::ApproxUnsat: +// { +// ApproximateSimplex::Solution sol = approxSolver->extractRelaxation(); + +// d_qflraStatus = d_attemptSolSimplex.attempt(sol); +// options::arithStandardCheckVarOrderPivots.set(pass2Limit); + +// if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } +// } +// break; +// default: +// break; +// } +// delete approxSolver; +// } +// } + +// if(!useFancyFinal){ +// d_qflraStatus = simplex.findModel(noPivotLimit); +// }else{ + + +// if(d_qflraStatus == Result::SAT_UNKNOWN){ +// //Message() << "got sat unknown" << endl; +// vector toCut = cutAllBounded(); +// if(toCut.size() > 0){ +// //branchVector(toCut); +// emmittedConflictOrSplit = true; +// }else{ +// //Message() << "splitting" << endl; + +// d_qflraStatus = simplex.findModel(noPivotLimit); +// } +// } +// options::arithStandardCheckVarOrderPivots.set(oldCap); +// } + +// // TODO Save zeroes with no conflicts +// d_linEq.stopTrackingBoundCounts(); +// d_partialModel.startQueueingBoundCounts(); + +// return emmittedConflictOrSplit; +// } + +bool TheoryArithPrivate::hasFreshArithLiteral(Node n) const{ + switch(n.getKind()){ + case kind::LEQ: + case kind::GEQ: + case kind::GT: + case kind::LT: + return !isSatLiteral(n); + case kind::EQUAL: + if(n[0].getType().isReal()){ + return !isSatLiteral(n); + }else if(n[0].getType().isBoolean()){ + return hasFreshArithLiteral(n[0]) || + hasFreshArithLiteral(n[1]); + }else{ + return false; + } + case kind::IMPLIES: + // try the rhs first + return hasFreshArithLiteral(n[1]) || + hasFreshArithLiteral(n[0]); + default: + if(n.getType().isBoolean()){ + for(Node::iterator ni=n.begin(), nend=n.end(); ni!=nend; ++ni){ + Node child = *ni; + if(hasFreshArithLiteral(child)){ + return true; + } + } + } + return false; + } +} + void TheoryArithPrivate::check(Theory::Effort effortLevel){ Assert(d_currentPropagationList.empty()); + //cout << "TheoryArithPrivate::check " << effortLevel << std::endl; Debug("effortlevel") << "TheoryArithPrivate::check " << effortLevel << std::endl; Debug("arith") << "TheoryArithPrivate::check begun " << effortLevel << std::endl; @@ -1837,28 +3293,28 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } while(!done()){ - Constraint curr = constraintFromFactQueue(); + ConstraintP curr = constraintFromFactQueue(); if(curr != NullConstraint){ bool res CVC4_UNUSED = assertionCases(curr); - Assert(!res || inConflict()); + Assert(!res || anyConflict()); } - if(inConflict()){ break; } + if(anyConflict()){ break; } } - if(!inConflict()){ + if(!anyConflict()){ while(!d_learnedBounds.empty()){ // we may attempt some constraints twice. this is okay! - Constraint curr = d_learnedBounds.front(); + ConstraintP curr = d_learnedBounds.front(); d_learnedBounds.pop(); Debug("arith::learned") << curr << endl; bool res CVC4_UNUSED = assertionCases(curr); - Assert(!res || inConflict()); + Assert(!res || anyConflict()); - if(inConflict()){ break; } + if(anyConflict()){ break; } } } - if(inConflict()){ + if(anyConflict()){ d_qflraStatus = Result::UNSAT; if(options::revertArithModels() && previous == Result::SAT){ ++d_statistics.d_revertsOnConflicts; @@ -1872,6 +3328,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ revertOutOfConflict(); } outputConflicts(); + //cout << "unate conflict 1 " << effortLevel << std::endl; return; } @@ -1884,9 +3341,33 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ Assert(d_conflicts.empty()); bool useSimplex = d_qflraStatus != Result::SAT; + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre realRelax" << endl; + if(useSimplex){ emmittedConflictOrSplit = solveRealRelaxation(effortLevel); } + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post realRelax" << endl; + + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre solveInteger" << endl; + + if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){ + solveInteger(effortLevel); + if(anyConflict()){ + ++d_statistics.d_commitsOnConflicts; + Debug("arith::bt") << "committing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl; + revertOutOfConflict(); + d_errorSet.clear(); + outputConflicts(); + return; + } + } + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post solveInteger" << endl; switch(d_qflraStatus){ case Result::SAT: @@ -1944,6 +3425,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } outputConflicts(); emmittedConflictOrSplit = true; + Debug("arith::conflict") << "simplex conflict" << endl; if(useSimplex && options::collectPivots()){ if(options::useFC()){ @@ -1958,6 +3440,26 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } d_statistics.d_avgUnknownsInARow.addEntry(d_unknownsInARow); + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre approx cuts" << endl; + if(!d_approxCuts.empty()){ + bool anyFresh = false; + while(!d_approxCuts.empty()){ + Node lem = d_approxCuts.front(); + d_approxCuts.pop(); + Debug("arith::approx::cuts") << "approximate cut:" << lem << endl; + anyFresh = anyFresh || hasFreshArithLiteral(lem); + Debug("arith::lemma") << "approximate cut:" << lem << endl; + outputLemma(lem); + } + if(anyFresh){ + emmittedConflictOrSplit = true; + } + } + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post approx cuts" << endl; + // This should be fine if sat or unknown if(!emmittedConflictOrSplit && (options::arithPropagationMode() == UNATE_PROP || @@ -1965,8 +3467,8 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime); Assert(d_qflraStatus != Result::UNSAT); - while(!d_currentPropagationList.empty() && !inConflict()){ - Constraint curr = d_currentPropagationList.front(); + while(!d_currentPropagationList.empty() && !anyConflict()){ + ConstraintP curr = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); ConstraintType t = curr->getType(); @@ -1976,23 +3478,23 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ switch(t){ case LowerBound: { - Constraint prev = d_currentPropagationList.front(); + ConstraintP prev = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropLowerBound(curr, prev); break; } case UpperBound: { - Constraint prev = d_currentPropagationList.front(); + ConstraintP prev = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropUpperBound(curr, prev); break; } case Equality: { - Constraint prevLB = d_currentPropagationList.front(); + ConstraintP prevLB = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); - Constraint prevUB = d_currentPropagationList.front(); + ConstraintP prevUB = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB); break; @@ -2002,14 +3504,16 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } } - if(inConflict()){ + if(anyConflict()){ Debug("arith::unate") << "unate conflict" << endl; revertOutOfConflict(); d_qflraStatus = Result::UNSAT; outputConflicts(); emmittedConflictOrSplit = true; + //cout << "unate conflict " << endl; Debug("arith::bt") << "committing on unate conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl; + Debug("arith::conflict") << "unate arith conflict" << endl; } }else{ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime); @@ -2017,12 +3521,23 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } Assert( d_currentPropagationList.empty()); + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post unate" << endl; + if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){ ++d_fullCheckCounter; } if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){ emmittedConflictOrSplit = splitDisequalities(); } + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pos splitting" << endl; + + + Debug("arith") << "integer? " + << " conf/split " << emmittedConflictOrSplit + << " fulleffort " << Theory::fullEffort(effortLevel) + << " hasintmodel " << hasIntegerModel() << endl; if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){ Node possibleConflict = Node::null(); @@ -2031,22 +3546,22 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(possibleConflict != Node::null()){ revertOutOfConflict(); Debug("arith::conflict") << "dio conflict " << possibleConflict << endl; - //cout << "dio conflict " << possibleConflict << endl; - raiseConflict(possibleConflict); + blackBoxConflict(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(getDioCuttingResource()){ + Node possibleLemma = dioCutting(); + if(!possibleLemma.isNull()){ + emmittedConflictOrSplit = true; + d_hasDoneWorkSinceCut = false; + d_cutCount = d_cutCount + 1; + Debug("arith::lemma") << "dio cut " << possibleLemma << endl; + outputLemma(possibleLemma); + } } } @@ -2056,7 +3571,10 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ ++(d_statistics.d_externalBranchAndBounds); d_cutCount = d_cutCount + 1; emmittedConflictOrSplit = true; + Debug("arith::lemma") << "rrbranch lemma" + << possibleLemma << endl; outputLemma(possibleLemma); + } } @@ -2064,10 +3582,12 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(d_diosolver.hasMoreDecompositionLemmas()){ while(d_diosolver.hasMoreDecompositionLemmas()){ Node decompositionLemma = d_diosolver.nextDecompositionLemma(); - Debug("arith") << "dio decomposition lemma " << decompositionLemma << endl; + Debug("arith::lemma") << "dio decomposition lemma " + << decompositionLemma << endl; outputLemma(decompositionLemma); } }else{ + Debug("arith::restart") << "arith restart!" << endl; outputRestart(); } } @@ -2077,6 +3597,15 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ setIncomplete(); } + if(Theory::fullEffort(effortLevel)){ + if(Debug.isOn("arith::consistency::final")){ + entireStateIsConsistent("arith::consistency::final"); + } + // cout << "fulleffort" << getSatContext()->getLevel() << endl; + // entireStateIsConsistent("arith::consistency::final"); + // cout << "emmittedConflictOrSplit" << emmittedConflictOrSplit << endl; + } + if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } if(Debug.isOn("arith::print_model")) { debugPrintModel(Debug("arith::print_model")); @@ -2127,7 +3656,7 @@ std::vector TheoryArithPrivate::cutAllBounded() const{ for(ArithVar iter = 0; iter != max; ++iter){ //Do not include slack variables const DeltaRational& d = d_partialModel.getAssignment(iter); - if(isInteger(iter) && !isSlackVariable(iter) && + if(isIntegerInput(iter) && !d_cutInContext.contains(iter) && d_partialModel.hasUpperBound(iter) && d_partialModel.hasLowerBound(iter) && @@ -2147,7 +3676,7 @@ Node TheoryArithPrivate::roundRobinBranch(){ ArithVar v = d_nextIntegerCheckVar; Assert(isInteger(v)); - Assert(!isSlackVariable(v)); + Assert(!isAuxiliaryVariable(v)); return branchIntegerVariable(v); } } @@ -2155,10 +3684,10 @@ Node TheoryArithPrivate::roundRobinBranch(){ bool TheoryArithPrivate::splitDisequalities(){ bool splitSomething = false; - vector save; + vector save; while(!d_diseqQueue.empty()){ - Constraint front = d_diseqQueue.front(); + ConstraintP front = d_diseqQueue.front(); d_diseqQueue.pop(); if(front->isSplit()){ @@ -2179,6 +3708,7 @@ bool TheoryArithPrivate::splitDisequalities(){ Debug("arith::lemma") << "Now " << Rewriter::rewrite(lemma) << endl; outputLemma(lemma); + //cout << "Now " << Rewriter::rewrite(lemma) << endl; splitSomething = true; }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){ Debug("eq") << "can drop as less than lb" << front << endl; @@ -2190,7 +3720,7 @@ bool TheoryArithPrivate::splitDisequalities(){ } } } - vector::const_iterator i=save.begin(), i_end = save.end(); + vector::const_iterator i=save.begin(), i_end = save.end(); for(; i != i_end; ++i){ d_diseqQueue.push(*i); } @@ -2206,17 +3736,17 @@ void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const { 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); + ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i); out << lConstr << endl; } if (d_partialModel.hasUpperBound(i)) { - Constraint uConstr = d_partialModel.getUpperBoundConstraint(i); + ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i); out << uConstr << endl; } } - context::CDQueue::const_iterator it = d_diseqQueue.begin(); - context::CDQueue::const_iterator it_end = d_diseqQueue.end(); + context::CDQueue::const_iterator it = d_diseqQueue.begin(); + context::CDQueue::const_iterator it_end = d_diseqQueue.end(); for(; it != it_end; ++ it) { out << *it << endl; } @@ -2243,16 +3773,16 @@ Node TheoryArithPrivate::explain(TNode n) { Debug("arith::explain") << "explain @" << getSatContext()->getLevel() << ": " << n << endl; - Constraint c = d_constraintDatabase.lookup(n); + ConstraintP c = d_constraintDatabase.lookup(n); if(c != NullConstraint){ Assert(!c->isSelfExplaining()); - Node exp = c->explainForPropagation(); + Node exp = c->externalExplainForPropagation(); 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(); + Node exp = c->externalExplainForPropagation(); Debug("arith::explain") << "assertions explanation" << n << ":" << exp << endl; return exp; }else{ @@ -2285,12 +3815,13 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { } while(d_constraintDatabase.hasMorePropagations()){ - Constraint c = d_constraintDatabase.nextPropagation(); + ConstraintCP 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; + Debug("arith::prop") << "negation has proof " << c->getNegation() << endl; + Debug("arith::prop") << c->getNegation()->externalExplainByAssertions() + << 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!"); @@ -2311,7 +3842,7 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { //equality engine in the the difference manager. Node normalized = Rewriter::rewrite(toProp); - Constraint constraint = d_constraintDatabase.lookup(normalized); + ConstraintP constraint = d_constraintDatabase.lookup(normalized); if(constraint == NullConstraint){ Debug("arith::prop") << "propagating on non-constraint? " << toProp << endl; @@ -2322,7 +3853,7 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { normalized[0] : normalized.notNode(); Node lp = flattenAnd(exp.andNode(notNormalized)); Debug("arith::prop") << "propagate conflict" << lp << endl; - raiseConflict(lp); + blackBoxConflict(lp); outputConflicts(); return; }else{ @@ -2427,11 +3958,11 @@ DeltaRational TheoryArithPrivate::getDeltaValue(TNode n) const throw (DeltaRatio Rational TheoryArithPrivate::deltaValueForTotalOrder() const{ Rational min(2); std::set relevantDeltaValues; - context::CDQueue::const_iterator qiter = d_diseqQueue.begin(); - context::CDQueue::const_iterator qiter_end = d_diseqQueue.end(); + context::CDQueue::const_iterator qiter = d_diseqQueue.begin(); + context::CDQueue::const_iterator qiter_end = d_diseqQueue.end(); for(; qiter != qiter_end; ++qiter){ - Constraint curr = *qiter; + ConstraintP curr = *qiter; const DeltaRational& rhsValue = curr->getValue(); relevantDeltaValues.insert(rhsValue); @@ -2503,7 +4034,7 @@ void TheoryArithPrivate::collectModelInfo( TheoryModel* m, bool fullModel ){ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){ ArithVar v = *vi; - if(!isSlackVariable(v)){ + if(!isAuxiliaryVariable(v)){ Node term = d_partialModel.asNode(v); if(theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end()){ @@ -2550,6 +4081,8 @@ void TheoryArithPrivate::notifyRestart(){ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } ++d_restartsCounter; + d_solveIntMaybeHelp = 0; + d_solveIntAttempts = 0; } bool TheoryArithPrivate::entireStateIsConsistent(const string& s){ @@ -2679,7 +4212,7 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound //implies an unknown fact. ConstraintType t = upperBound ? UpperBound : LowerBound; - Constraint bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound); + ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound); // Node bestImplied = upperBound ? // d_apm.getBestImpliedUpperBound(basic, bound): @@ -2694,7 +4227,7 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound Assert( upperBound || d_partialModel.greaterThanLowerBound(basic, bestImplied->getValue())); //slightly changed - // Constraint c = d_constraintDatabase.lookup(bestImplied); + // ConstraintP c = d_constraintDatabase.lookup(bestImplied); // Assert(c != NullConstraint); bool assertedToTheTheory = bestImplied->assertedToTheTheory(); @@ -2710,7 +4243,8 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound if(bestImplied->negationHasProof()){ Warning() << "the negation of " << bestImplied << " : " << endl << "has proof " << bestImplied->getNegation() << endl - << bestImplied->getNegation()->explainForConflict() << endl; + << bestImplied->getNegation()->externalExplainByAssertions() + << endl; } if(!assertedToTheTheory && canBePropagated && !hasProof ){ @@ -2724,8 +4258,11 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound return true; } if(Debug.isOn("arith::prop")){ - Debug("arith::prop") << "failed " << basic << " " << bound << assertedToTheTheory << " " << - canBePropagated << " " << hasProof << endl; + Debug("arith::prop") << "failed " << basic + << " " << bound + << " " << assertedToTheTheory + << " " << canBePropagated + << " " << hasProof << endl; d_partialModel.printModel(basic, Debug("arith::prop")); } } @@ -2848,7 +4385,7 @@ bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{ return true; } - Constraint strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a); + ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a); if(strongestPossible == NullConstraint){ return false; }else{ @@ -2944,11 +4481,7 @@ bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, b if(weaker){ ConstraintType t = vUb ? UpperBound : LowerBound; - if(isInteger(v)){ - //cout << "maybe" << endl; - //cout << bound << endl; - } - Constraint implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); + ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); if(implied != NullConstraint){ return rowImplicationCanBeApplied(ridx, rowUp, implied); } @@ -2980,7 +4513,7 @@ Node flattenImplication(Node imp){ return nb; } -bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint implied){ +bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){ Assert(implied != NullConstraint); ArithVar v = implied->getVariable(); @@ -2997,14 +4530,14 @@ bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, C if(implied->negationHasProof()){ Warning() << "the negation of " << implied << " : " << endl << "has proof " << implied->getNegation() << endl - << implied->getNegation()->explainForConflict() << endl; + << implied->getNegation()->externalExplainByAssertions() << endl; } if(!assertedToTheTheory && canBePropagated && !hasProof ){ - vector explain; + ConstraintCPVec explain; d_linEq.propagateRow(explain, ridx, rowUp, implied); if(d_tableau.getRowLength(ridx) <= options::arithPropAsLemmaLength()){ - Node implication = implied->makeImplication(explain); + Node implication = implied->externalImplication(explain); Node clause = flattenImplication(implication); outputLemma(clause); }else{ diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h index 7ff93b021..33c588ed4 100644 --- a/src/theory/arith/theory_arith_private.h +++ b/src/theory/arith/theory_arith_private.h @@ -88,6 +88,10 @@ namespace quantifiers { } namespace arith { +class BranchCutInfo; +class TreeLog; +class ApproximateStatistics; + /** * Implementation of QF_LRA. * Based upon: @@ -107,6 +111,8 @@ private: BoundInfoMap d_rowTracking; + ConstraintCPVec d_conflictBuffer; + /** * The constraint database associated with the theory. * This must be declared before ArithPartialModel. @@ -122,6 +128,7 @@ private: // if unknown, the simplex priority queue cannot be emptied int d_unknownsInARow; + bool d_replayedLemmas; /** * This counter is false if nothing has been done since the last cut. @@ -174,22 +181,22 @@ private: * 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 d_assertionsThatDoNotMatchTheirLiterals; + context::CDInsertHashMap 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 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]; + /** Returns true if the variable was initially introduced as an auxiliary variable. */ + inline bool isAuxiliaryVariable(ArithVar x) const{ + return d_partialModel.isAuxiliary(x); + } + + inline bool isIntegerInput(ArithVar x) const { + return d_partialModel.isIntegerInput(x); } /** @@ -215,7 +222,7 @@ private: * List of all of the disequalities asserted in the current context that are not known * to be satisfied. */ - context::CDQueue d_diseqQueue; + context::CDQueue d_diseqQueue; /** * Constraints that have yet to be processed by proagation work list. @@ -231,9 +238,9 @@ private: * then d_cPL[1] is the previous lowerBound in d_partialModel, * and d_cPL[2] is the previous upperBound in d_partialModel. */ - std::deque d_currentPropagationList; + std::deque d_currentPropagationList; - context::CDQueue d_learnedBounds; + context::CDQueue d_learnedBounds; /** @@ -277,15 +284,31 @@ private: /** This is only used by simplex at the moment. */ - context::CDList d_conflicts; + context::CDList d_conflicts; + context::CDO d_blackBoxConflict; public: - inline void raiseConflict(Node n){ d_conflicts.push_back(n); } + inline void raiseConflict(const ConstraintCPVec& cv){ + d_conflicts.push_back(cv); + } + + void raiseConflict(ConstraintCP a, ConstraintCP b); + void raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c); + + inline void blackBoxConflict(Node bb){ + if(d_blackBoxConflict.get().isNull()){ + d_blackBoxConflict = bb; + } + } private: + inline bool conflictQueueEmpty() const { + return d_conflicts.empty(); + } + /** Returns true iff a conflict has been raised. */ - inline bool inConflict() const { - return !d_conflicts.empty(); + inline bool anyConflict() const { + return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull(); } /** @@ -314,6 +337,7 @@ private: /** This keeps track of difference equalities. Mostly for sharing. */ ArithCongruenceManager d_congruenceManager; + context::CDO d_cmEnabled; /** This implements the Simplex decision procedure. */ DualSimplexDecisionProcedure d_dualSimplex; @@ -323,6 +347,22 @@ private: bool solveRealRelaxation(Theory::Effort effortLevel); + /* Returns true if this is heuristically a good time to try + * to solve the integers. + */ + bool attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit); + bool replayLemmas(ApproximateSimplex* approx); + void solveInteger(Theory::Effort effortLevel); + bool safeToCallApprox() const; + SimplexDecisionProcedure& selectSimplex(bool pass1); + SimplexDecisionProcedure* d_pass1SDP; + SimplexDecisionProcedure* d_otherSDP; + /* Sets d_qflraStatus */ + void importSolution(const ApproximateSimplex::Solution& solution); + bool solveRelaxationOrPanic(Theory::Effort effortLevel); + context::CDO d_lastContextIntegerAttempted; + bool replayLog(ApproximateSimplex* approx); + class ModelException : public Exception { public: ModelException(TNode n, const char* msg) throw (); @@ -412,16 +452,28 @@ private: /** - * Looks for the next integer variable without an integer assignment in a round robin fashion. - * Changes the value of d_nextIntegerCheckVar. + * 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. + * This returns true if all integer variables have integer assignments. + * If this returns false, d_nextIntegerCheckVar does not have an integer + * assignment. */ bool hasIntegerModel(); /** - * Issues branches for non-slack integer variables with non-integer assignments. + * Looks for through the variables starting at d_nextIntegerCheckVar + * for the first integer variable that is between its upper and lower bounds + * that has a non-integer assignment. + * + * If assumeBounds is true, skip the check that the variable is in bounds. + * + * If there is no such variable, returns ARITHVAR_SENTINEL; + */ + ArithVar nextIntegerViolatation(bool assumeBounds) const; + + /** + * Issues branches for non-auxiliary integer variables with non-integer assignments. * Returns a cut for a lemma. * If there is an integer model, this returns Node::null(). */ @@ -432,8 +484,11 @@ 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. + * + * If aux is true, this is an auxiliary variable. + * If internal is true, x might not be unique up to a constant multiple. */ - ArithVar requestArithVar(TNode x, bool slack); + ArithVar requestArithVar(TNode x, bool aux, bool internal); public: const BoundsInfo& boundsInfo(ArithVar basic) const; @@ -443,8 +498,8 @@ 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); + /** Initial (not context dependent) sets up for a new auxiliary variable.*/ + void setupAuxiliary(TNode left); /** @@ -462,10 +517,10 @@ private: * 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); + bool AssertLower(ConstraintP constraint); + bool AssertUpper(ConstraintP constraint); + bool AssertEquality(ConstraintP constraint); + bool AssertDisequality(ConstraintP constraint); /** Tracks the bounds that were updated in the current round. */ DenseSet d_updatedBounds; @@ -488,7 +543,14 @@ private: /** Attempt to perform a row propagation where every variable is a potential candidate.*/ bool attemptFull(RowIndex ridx, bool rowUp); bool tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUp, const DeltaRational& bound); - bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint bestImplied); + bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP bestImplied); + //void enqueueConstraints(std::vector& out, Node n) const; + //ConstraintCPVec resolveOutPropagated(const ConstraintCPVec& v, const std::set& propagated) const; + void resolveOutPropagated(std::vector& confs, const std::set& propagated) const; + void subsumption(std::vector& confs) const; + + Node cutToLiteral(ApproximateSimplex* approx, const CutInfo& cut) const; + Node branchToNode(ApproximateSimplex* approx, const NodeLog& cut) const throw(RationalFromDoubleException); void propagateCandidates(); @@ -512,8 +574,8 @@ private: * Returns a conflict if one was found. * Returns Node::null if no conflict was found. */ - Constraint constraintFromFactQueue(); - bool assertionCases(Constraint c); + ConstraintP constraintFromFactQueue(); + bool assertionCases(ConstraintP c); /** * Returns the basic variable with the shorted row containing a non-basic variable. @@ -554,7 +616,7 @@ private: (d_containing.d_out)->setIncomplete(); d_nlIncomplete = true; } - inline void outputLemma(TNode lem) { (d_containing.d_out)->lemma(lem); } + void outputLemma(TNode lem); inline void outputPropagate(TNode lit) { (d_containing.d_out)->propagate(lit); } inline void outputRestart() { (d_containing.d_out)->demandRestart(); } @@ -565,6 +627,8 @@ private: return (d_containing.d_valuation).getSatValue(n); } + context::CDQueue d_approxCuts; + std::vector d_acTmp; /** Counts the number of fullCheck calls to arithmetic. */ uint32_t d_fullCheckCounter; @@ -581,12 +645,49 @@ private: context::CDO d_guessedCoeffSet; ArithRatPairVec d_guessedCoeffs; + + TreeLog* d_treeLog; + TreeLog& getTreeLog(); + + + ArithVarVec d_replayVariables; + std::vector d_replayConstraints; + DenseMap d_lhsTmp; + + /* Approximate simpplex solvers are given a copy of their stats */ + ApproximateStatistics* d_approxStats; + ApproximateStatistics& getApproxStats(); + context::CDO d_attemptSolveIntTurnedOff; + void turnOffApproxFor(int32_t rounds); + bool getSolveIntegerResource(); + + void tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bl); + std::vector replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth); + + std::pair replayGetConstraint(const CutInfo& info); + std::pair replayGetConstraint(ApproximateSimplex* approx, const NodeLog& nl) throw(RationalFromDoubleException); + std::pair replayGetConstraint(const DenseMap& lhs, Kind k, const Rational& rhs, bool branch); + + void replayAssert(ConstraintP c); + //ConstConstraintVec toExplanation(Node n) const; + + // Returns true if the node contains a literal + // that is an arithmetic literal and is not a sat literal + // No caching is done so this should likely only + // be called carefully! + bool hasFreshArithLiteral(Node n) const; + + int32_t d_dioSolveResources; + bool getDioCuttingResource(); + + uint32_t d_solveIntMaybeHelp, d_solveIntAttempts; + /** 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_statUserVariables, d_statAuxiliaryVariables; IntStat d_statDisequalitySplits; IntStat d_statDisequalityConflicts; TimerStat d_simplifyTimer; @@ -614,6 +715,51 @@ private: IntStat d_commitsOnConflicts; IntStat d_nontrivialSatChecks; + IntStat d_replayLogRecCount, + d_replayLogRecConflictEscalation, + d_replayLogRecEarlyExit, + d_replayBranchCloseFailures, + d_replayLeafCloseFailures, + d_replayBranchSkips, + d_mirCutsAttempted, + d_gmiCutsAttempted, + d_branchCutsAttempted, + d_cutsReconstructed, + d_cutsReconstructionFailed, + d_cutsProven, + d_cutsProofFailed, + d_mipReplayLemmaCalls, + d_mipExternalCuts, + d_mipExternalBranch; + + IntStat d_inSolveInteger, + d_branchesExhausted, + d_execExhausted, + d_pivotsExhausted, + d_panicBranches, + d_relaxCalls, + d_relaxLinFeas, + d_relaxLinFeasFailures, + d_relaxLinInfeas, + d_relaxLinInfeasFailures, + d_relaxLinExhausted, + d_relaxOthers; + + IntStat d_applyRowsDeleted; + TimerStat d_replaySimplexTimer; + + TimerStat d_replayLogTimer, + d_solveIntTimer, + d_solveRealRelaxTimer; + + IntStat d_solveIntCalls, + d_solveStandardEffort; + + IntStat d_approxDisabled; + IntStat d_replayAttemptFailed; + + IntStat d_cutsRejectedDuringReplay; + IntStat d_cutsRejectedDuringLemmas; HistogramStat d_satPivots; HistogramStat d_unsatPivots; diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index c67a7c4bb..2a263857a 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -17,6 +17,8 @@ #include #include +#include "theory/arith/arith_ite_utils.h" + #include "decision/decision_engine.h" #include "expr/attribute.h" @@ -149,7 +151,8 @@ TheoryEngine::TheoryEngine(context::Context* context, d_preRegistrationVisitor(this, context), d_sharedTermsVisitor(d_sharedTerms), d_unconstrainedSimp(new UnconstrainedSimplifier(context, logicInfo)), - d_bvToBoolPreprocessor() + d_bvToBoolPreprocessor(), + d_arithSubstitutionsAdded("zzz::arith::substitutions", 0) { for(TheoryId theoryId = theory::THEORY_FIRST; theoryId != theory::THEORY_LAST; ++ theoryId) { d_theoryTable[theoryId] = NULL; @@ -167,6 +170,8 @@ TheoryEngine::TheoryEngine(context::Context* context, PROOF (ProofManager::currentPM()->initTheoryProof(); ); d_iteUtilities = new ITEUtilities(d_iteRemover.getContainsVisitor()); + + StatisticsRegistry::registerStat(&d_arithSubstitutionsAdded); } TheoryEngine::~TheoryEngine() { @@ -191,6 +196,8 @@ TheoryEngine::~TheoryEngine() { delete d_unconstrainedSimp; delete d_iteUtilities; + + StatisticsRegistry::unregisterStat(&d_arithSubstitutionsAdded); } void TheoryEngine::interrupt() throw(ModalException) { @@ -1461,7 +1468,8 @@ Node TheoryEngine::ppSimpITE(TNode assertion) bool TheoryEngine::donePPSimpITE(std::vector& assertions){ bool result = true; - if(d_iteUtilities->simpIteDidALotOfWorkHeuristic()){ + bool simpDidALotOfWork = d_iteUtilities->simpIteDidALotOfWorkHeuristic(); + if(simpDidALotOfWork){ if(options::compressItes()){ result = d_iteUtilities->compress(assertions); } @@ -1480,6 +1488,57 @@ bool TheoryEngine::donePPSimpITE(std::vector& assertions){ } } } + + // Do theory specific preprocessing passes + if(d_logicInfo.isTheoryEnabled(theory::THEORY_ARITH)){ + if(!simpDidALotOfWork){ + ContainsTermITEVistor& contains = *d_iteRemover.getContainsVisitor(); + arith::ArithIteUtils aiteu(contains, d_userContext, getModel()); + bool anyItes = false; + for(size_t i = 0; i < assertions.size(); ++i){ + Node curr = assertions[i]; + if(contains.containsTermITE(curr)){ + anyItes = true; + Node res = aiteu.reduceVariablesInItes(curr); + Debug("arith::ite::red") << "@ " << i << " ... " << curr << endl << " ->" << res << endl; + if(curr != res){ + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + assertions[i] = more; + } + } + } + if(!anyItes){ + unsigned prevSubCount = aiteu.getSubCount(); + aiteu.learnSubstitutions(assertions); + if(prevSubCount < aiteu.getSubCount()){ + d_arithSubstitutionsAdded += aiteu.getSubCount() - prevSubCount; + bool anySuccess = false; + for(size_t i = 0, N = assertions.size(); i < N; ++i){ + Node curr = assertions[i]; + Node next = Rewriter::rewrite(aiteu.applySubstitutions(curr)); + Node res = aiteu.reduceVariablesInItes(next); + Debug("arith::ite::red") << "@ " << i << " ... " << next << endl << " ->" << res << endl; + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + if(more != next){ + anySuccess = true; + break; + } + } + for(size_t i = 0, N = assertions.size(); anySuccess && i < N; ++i){ + Node curr = assertions[i]; + Node next = Rewriter::rewrite(aiteu.applySubstitutions(curr)); + Node res = aiteu.reduceVariablesInItes(next); + Debug("arith::ite::red") << "@ " << i << " ... " << next << endl << " ->" << res << endl; + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + assertions[i] = Rewriter::rewrite(more); + } + } + } + } + } return result; } diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index db31ef9b7..9a987c9d7 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -818,6 +818,9 @@ public: */ void checkTheoryAssertionsWithModel(); +private: + IntStat d_arithSubstitutionsAdded; + };/* class TheoryEngine */ }/* CVC4 namespace */ diff --git a/src/util/rational_cln_imp.cpp b/src/util/rational_cln_imp.cpp index 2b29ece22..f674481de 100644 --- a/src/util/rational_cln_imp.cpp +++ b/src/util/rational_cln_imp.cpp @@ -17,6 +17,7 @@ #include "cvc4autoconfig.h" #include "util/rational.h" #include +#include #ifndef CVC4_CLN_IMP # error "This source should only ever be built if CVC4_CLN_IMP is on !" @@ -50,3 +51,56 @@ std::ostream& CVC4::operator<<(std::ostream& os, const Rational& q){ return os << q.toString(); } + + +/** Equivalent to calling (this->abs()).cmp(b.abs()) */ +int Rational::absCmp(const Rational& q) const{ + const Rational& r = *this; + int rsgn = r.sgn(); + int qsgn = q.sgn(); + if(rsgn == 0){ + return (qsgn == 0) ? 0 : -1; + }else if(qsgn == 0){ + Assert(rsgn != 0); + return 1; + }else if((rsgn > 0) && (qsgn > 0)){ + return r.cmp(q); + }else if((rsgn < 0) && (qsgn < 0)){ + // if r < q < 0, q.cmp(r) = +1, (r.abs()).cmp(q.abs()) = +1 + // if q < r < 0, q.cmp(r) = -1, (r.abs()).cmp(q.abs()) = -1 + // if q = r < 0, q.cmp(r) = 0, (r.abs()).cmp(q.abs()) = 0 + return q.cmp(r); + }else if((rsgn < 0) && (qsgn > 0)){ + Rational rpos = -r; + return rpos.cmp(q); + }else { + Assert(rsgn > 0 && (qsgn < 0)); + Rational qpos = -q; + return r.cmp(qpos); + } +} + +Rational Rational::fromDouble(double d) throw(RationalFromDoubleException){ + try{ + cln::cl_DF fromD = d; + Rational q; + q.d_value = cln::rationalize(fromD); + return q; + }catch(cln::floating_point_underflow_exception& fpue){ + throw RationalFromDoubleException(d); + }catch(cln::floating_point_nan_exception& fpne){ + throw RationalFromDoubleException(d); + }catch(cln::floating_point_overflow_exception& fpoe){ + throw RationalFromDoubleException(d); + } +} + +RationalFromDoubleException::RationalFromDoubleException(double d) throw() + : Exception() +{ + std::stringstream ss; + ss << "RationalFromDoubleException("; + ss << d; + ss << ")"; + setMessage(ss.str()); +} diff --git a/src/util/rational_cln_imp.h b/src/util/rational_cln_imp.h index da2af6c1f..b144ab419 100644 --- a/src/util/rational_cln_imp.h +++ b/src/util/rational_cln_imp.h @@ -38,6 +38,11 @@ namespace CVC4 { +class CVC4_PUBLIC RationalFromDoubleException : public Exception { +public: + RationalFromDoubleException(double d) throw(); +}; + /** ** A multi-precision rational constant. ** This stores the rational as a pair of multi-precision integers, @@ -189,12 +194,7 @@ public: } /** Return an exact rational for a double d. */ - static Rational fromDouble(double d){ - cln::cl_DF fromD = d; - Rational q; - q.d_value = cln::rationalize(fromD); - return q; - } + static Rational fromDouble(double d) throw(RationalFromDoubleException); /** * Get a double representation of this Rational, which is @@ -259,6 +259,10 @@ public: return Integer(cln::ceiling1(d_value)); } + Rational floor_frac() const { + return (*this) - Rational(floor()); + } + Rational& operator=(const Rational& x){ if(this == &x) return *this; d_value = x.d_value; @@ -349,6 +353,9 @@ public: return getNumerator().length() + getDenominator().length(); } + /** Equivalent to calling (this->abs()).cmp(b.abs()) */ + int absCmp(const Rational& q) const; + };/* class Rational */ struct RationalHashFunction { diff --git a/src/util/rational_gmp_imp.cpp b/src/util/rational_gmp_imp.cpp index d496803dc..25c7dab59 100644 --- a/src/util/rational_gmp_imp.cpp +++ b/src/util/rational_gmp_imp.cpp @@ -17,6 +17,8 @@ #include "cvc4autoconfig.h" #include "util/rational.h" #include +#include +#include #ifndef CVC4_GMP_IMP # error "This source should only ever be built if CVC4_GMP_IMP is on !" @@ -50,3 +52,52 @@ std::ostream& CVC4::operator<<(std::ostream& os, const Rational& q){ return os << q.toString(); } + +/** Equivalent to calling (this->abs()).cmp(b.abs()) */ +int Rational::absCmp(const Rational& q) const{ + const Rational& r = *this; + int rsgn = r.sgn(); + int qsgn = q.sgn(); + if(rsgn == 0){ + return (qsgn == 0) ? 0 : -1; + }else if(qsgn == 0){ + Assert(rsgn != 0); + return 1; + }else if((rsgn > 0) && (qsgn > 0)){ + return r.cmp(q); + }else if((rsgn < 0) && (qsgn < 0)){ + // if r < q < 0, q.cmp(r) = +1, (r.abs()).cmp(q.abs()) = +1 + // if q < r < 0, q.cmp(r) = -1, (r.abs()).cmp(q.abs()) = -1 + // if q = r < 0, q.cmp(r) = 0, (r.abs()).cmp(q.abs()) = 0 + return q.cmp(r); + }else if((rsgn < 0) && (qsgn > 0)){ + Rational rpos = -r; + return rpos.cmp(q); + }else { + Assert(rsgn > 0 && (qsgn < 0)); + Rational qpos = -q; + return r.cmp(qpos); + } +} + + +/** Return an exact rational for a double d. */ +Rational Rational::fromDouble(double d) throw(RationalFromDoubleException){ + if(std::isfinite(d)){ + Rational q; + mpq_set_d(q.d_value.get_mpq_t(), d); + return q; + } + + throw RationalFromDoubleException(d); +} + +RationalFromDoubleException::RationalFromDoubleException(double d) throw() + : Exception() +{ + std::stringstream ss; + ss << "RationalFromDoubleException("; + ss << d; + ss << ")"; + setMessage(ss.str()); +} diff --git a/src/util/rational_gmp_imp.h b/src/util/rational_gmp_imp.h index 02ccc273c..273b3072d 100644 --- a/src/util/rational_gmp_imp.h +++ b/src/util/rational_gmp_imp.h @@ -28,6 +28,11 @@ namespace CVC4 { +class CVC4_PUBLIC RationalFromDoubleException : public Exception { +public: + RationalFromDoubleException(double d) throw(); +}; + /** ** A multi-precision rational constant. ** This stores the rational as a pair of multi-precision integers, @@ -172,12 +177,7 @@ public: return Integer(d_value.get_den()); } - /** Return an exact rational for a double d. */ - static Rational fromDouble(double d){ - Rational q; - mpq_set_d(q.d_value.get_mpq_t(), d); - return q; - } + static Rational fromDouble(double d) throw(RationalFromDoubleException); /** * Get a double representation of this Rational, which is @@ -234,6 +234,10 @@ public: return Integer(q); } + Rational floor_frac() const { + return (*this) - Rational(floor()); + } + Rational& operator=(const Rational& x){ if(this == &x) return *this; d_value = x.d_value; @@ -326,6 +330,10 @@ public: uint32_t denLen = getDenominator().length(); return numLen + denLen; } + + /** Equivalent to calling (this->abs()).cmp(b.abs()) */ + int absCmp(const Rational& q) const; + };/* class Rational */ struct RationalHashFunction { diff --git a/test/regress/regress0/Makefile.am b/test/regress/regress0/Makefile.am index e2d6664cd..ea74eb8ee 100644 --- a/test/regress/regress0/Makefile.am +++ b/test/regress/regress0/Makefile.am @@ -140,7 +140,6 @@ BUG_TESTS = \ bug421.smt2 \ bug421b.smt2 \ bug425.cvc \ - bug480.smt2 \ bug484.smt2 \ bug486.cvc \ bug507.smt2 \ @@ -173,7 +172,8 @@ endif EXTRA_DIST += \ subranges.cvc \ arrayinuf_error.smt2 \ - error.cvc + error.cvc \ + bug480.smt2 # synonyms for "check" in this directory .PHONY: regress regress0 test