diff options
author | Tim King <taking@cs.nyu.edu> | 2014-03-07 18:00:37 -0500 |
---|---|---|
committer | Tim King <taking@cs.nyu.edu> | 2014-03-07 18:00:52 -0500 |
commit | 9ccdea06edbc72e3ecd282e9e015f6fc4b2e7173 (patch) | |
tree | cde6138cb9ab6ef0b7c15edf42e3e8cc53637002 /src | |
parent | 42be934ef4d4430944ae9074c7202a7d130c75bb (diff) |
Merging a squash of the branch timothy-king/CVC4/glpknecfix c95bf7d4f1 into master. 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.
Diffstat (limited to 'src')
46 files changed, 7744 insertions, 1252 deletions
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 <math.h> #include <cmath> +#include <cfloat> +#include <map> using namespace std; @@ -29,13 +33,229 @@ namespace CVC4 { namespace theory { namespace arith { -ApproximateSimplex::ApproximateSimplex() : - d_pivotLimit(std::numeric_limits<int>::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<ConstraintP> d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound + + /* MIR CUTS Datastructures */ + DenseMap<SlackReplace> d_slacks;// The x'[i] selected for x[i] + DenseMap<VirtualBound> d_vub; // Virtual upper bounds. + DenseMap<VirtualBound> d_vlb; // Virtual lower bounds. + DenseMap<Rational> 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<ConstraintP> 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<int>::max()) + , d_branchLimit(std::numeric_limits<int>::max()) + , d_maxDepth(std::numeric_limits<int>::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<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int de mods.push_back(Integer()); Integer& back = mods.back(); back = carry.floor(); - //cout << " cfe["<<i<<"]: " << back << endl; + Debug("rationalToCfe") << " cfe["<<i<<"]: " << back << endl; carry -= back; if(carry.isZero()){ break; @@ -91,24 +311,85 @@ std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int de return mods; } -Rational ApproximateSimplex::estimateWithCFE(const Rational& q, int depth){ - std::vector<Integer> cfe = rationalToCfe(q,depth); - return cfeToRational(cfe); + +Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){ + Debug("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <<endl; + // references + // page 4: http://carlossicoli.free.fr/C/Cassels_J.W.S.-An_introduction_to_diophantine_approximation-University_Press(1965).pdf + // http://en.wikipedia.org/wiki/Continued_fraction + Assert(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["<<t<<"]: " << p[2] <<"/"<< q[2] << endl; + while( q[2] <= K ){ + p[0] = p[1]; p[1] = p[2]; + q[0] = q[1]; q[1] = q[2]; + + + Integer::floorQR(quot, rem, num, den); + num = den; den = rem; + + p[2] = p[0]+quot*p[1]; + q[2] = q[0]+quot*q[1]; + ++t; + Debug("estimateWithCFE") << " cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl; + } + + Integer k = (K-q[0]).floorDivideQuotient(q[1]); + Rational cand_prev(p[0]+k*p[1], q[0]+k*q[1]); + Rational cand_curr(p[1], q[1]); + Rational dist_prev = (cand_prev - r).abs(); + Rational dist_curr = (cand_curr - r).abs(); + if(dist_prev <= dist_curr){ + Debug("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl; + return cand_prev; + }else{ + Debug("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl; + return cand_curr; + } +} + +Rational ApproximateSimplex::estimateWithCFE(double d, const Integer& D) throw (RationalFromDoubleException){ + return estimateWithCFE(Rational::fromDouble(d), D); } -Rational ApproximateSimplex::estimateWithCFE(double d){ - return estimateWithCFE(Rational::fromDouble(d), 10); +Rational ApproximateSimplex::estimateWithCFE(double d) throw (RationalFromDoubleException){ + return estimateWithCFE(d, s_defaultMaxDenom); } class ApproxNoOp : public ApproximateSimplex { public: - ApproxNoOp(const ArithVariables& vars){} + ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s) + : ApproximateSimplex(v,l,s) + {} ~ApproxNoOp(){} - virtual ApproxResult solveRelaxation(){ - return ApproxError; + virtual LinResult solveRelaxation(){ + return LinUnknown; } - virtual Solution extractRelaxation() const{ + virtual Solution extractRelaxation() const throw (RationalFromDoubleException){ return Solution(); } @@ -116,14 +397,31 @@ public: return ArithRatPairVec(); } - virtual ApproxResult solveMIP(){ - return ApproxError; + virtual MipResult solveMIP(bool al){ + return MipUnknown; } - virtual Solution extractMIP() const{ + virtual Solution extractMIP() const throw (RationalFromDoubleException){ return Solution(); } virtual void setOptCoeffs(const ArithRatPairVec& ref){} + virtual std::vector<const CutInfo*> getValidCuts(const std::set<const NodeLog*>& nodes){ + return std::vector<const CutInfo*>(); + } + + virtual void tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException){} + + virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) throw(RationalFromDoubleException){ + return std::vector<const CutInfo*>(); + } + + 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<int> d_colIndices; DenseMap<int> d_rowIndices; + NodeLog::RowIdMap d_rootRowIds; + //DenseMap<ArithVar> d_rowToArithVar; + DenseMap<ArithVar> d_colToArithVar; int d_instanceID; @@ -162,27 +479,127 @@ private: static int s_verbosity; + CutScratchPad d_pad; + + std::vector<Integer> 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<const CutInfo*>& out); + virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) throw (RationalFromDoubleException); + //virtual std::vector<const NodeLog*> 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<Rational>& 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<ConstraintP>& 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<Node, Node> 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<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){ Index colIndex = *ci; double coeff = nbCoeffs[colIndex]; - glp_set_obj_coef(d_prob, colIndex, coeff); + 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<DeltaRational>& 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; } -void stopAtBingoOrPivotLimit(glp_tree *tree, void *info){ - int pivotLimit = *((int*)info); +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; +} + +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<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con) throw (RationalFromDoubleException){ + std::vector<const CutInfo*> 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<const CutInfo*> ApproxGLPK::getValidCuts(const std::set<const NodeLog*>& nodes){ +// // assume selected has been applied +// std::vector<const CutInfo*> proven; +// std::set<const NodeLog*>::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<const NodeLog*> ApproxGLPK::getBranches(){ +// std::vector<const NodeLog*> 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 "<<res<<" aux.term "<< aux.term << endl; switch(res){ case 0: case GLP_ESTOP: { - int status = glp_mip_status(d_prob); + int status = glp_mip_status(d_mipProb); + Debug("approx::") << "status " << status << endl; switch(status){ case GLP_OPT: case GLP_FEAS: d_solvedMIP = true; - return ApproxSat; + Debug("approx::") << "bingo here!" << endl; + return MipBingo; case GLP_NOFEAS: d_solvedMIP = true; - return ApproxUnsat; + return MipClosed; default: - return ApproxError; + if(aux.term == MipBingo){ + d_solvedMIP = true; + Debug("approx::") << "bingo here?" << endl; + } + return aux.term; } } default: - return ApproxError; + return MipUnknown; + } +} + + + +// Node explainSet(const set<ConstraintP>& inp){ +// Assert(!inp.empty()); +// NodeBuilder<> nb(kind::AND); +// set<ConstraintP>::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<Rational>& xs, const DenseMap<ConstraintP>& cs, bool* anyinf){ + if(anyinf != NULL){ + *anyinf = false; + } + + DeltaRational beta(0); + DenseMap<Rational>::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") << " +("<<bound << "*" << psi <<")"; + if(anyinf != NULL ){ + *anyinf = *anyinf || !bound.infinitesimalIsZero(); + } + } + Debug("approx::sumConstraints") << "= " << beta << endl; + + return beta; +} + +// remove fixed variables from the vector +void removeFixed(const ArithVariables& vars, DenseVector& dv, set<ConstraintP>& exp){ + DenseMap<Rational>& vec = dv.lhs; + Rational& removed = dv.rhs; + vector<ArithVar> equal; + DenseMap<Rational>::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.boundsAreEqual(x)){ + equal.push_back(x); + } + } + vector<ArithVar>::const_iterator equal_iter, equal_end; + equal_iter = equal.begin(), equal_end = equal.end(); + for(; equal_iter != equal_end; ++equal_iter){ + ArithVar x = *equal_iter; + Assert(vars.boundsAreEqual(x)); + const DeltaRational& lb = vars.getLowerBound(x); + Assert(lb.infinitesimalIsZero()); + removed -= (vec[x]) * lb.getNoninfinitesimalPart(); + + vec.remove(x); + + std::pair<ConstraintP, ConstraintP> p = vars.explainEqualBounds(x); + exp.insert(p.first); + Debug("removeFixed") << "remove fixed " << p.first << endl; + if(p.second != NullConstraint){ + exp.insert(p.second); + Debug("removeFixed") << "remove fixed " << p.second << endl; + } + } +} +void removeZeroes(DenseMap<Rational>& v){ + // Remove Slack variables + vector<ArithVar> zeroes; + DenseMap<Rational>::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<ArithVar>::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<Rational>& vec){ + // Remove auxillary variables + vector<ArithVar> aux; + DenseMap<Rational>::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<ArithVar>::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<Rational>& guess) const { + // basic variable + // sum g[i] * x_i + DenseMap<Rational> 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<ConstraintP>& 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() <<")"<<endl; + + const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs; + const Rational& constructedRhs = d_pad.d_cut.rhs; + hash_set<ArithVar> visited; + + if(constructedLhs.empty()){ + Debug("approx::checkCutOnPad") << "its empty?" <<endl; + return true; + } + if(cut.getKind() != d_pad.d_cutKind) { + Debug("approx::checkCutOnPad") << "rel doesn't match" << endl; + return true; + } + + const PrimitiveVec& cv = cut.getCutVector(); + for(int i = 1; i <= cv.len; ++i){ + int ind = cv.inds[i]; // this is always a structural variable + double coeff = cv.coeffs[i]; + + + + if(!d_colToArithVar.isKey(ind)){ return true; } + ArithVar x = d_colToArithVar[ind]; + //if(x == ARITHVAR_SENTINEL){ return true; } + visited.insert(x); + + + if(!constructedLhs.isKey(x)){ + if(Debug.isOn("approx::checkCutOnPad")){ + Debug("approx::checkCutOnPad") << " didn't find key for " << x << std::endl; + cut.print(Debug("approx::checkCutOnPad")); + Debug("approx::checkCutOnPad") << endl; + d_pad.d_cut.print(Debug("approx::checkCutOnPad")); + Debug("approx::checkCutOnPad") << endl; + } + return true; + } + + const Rational& onConstructed = constructedLhs[x]; + + Debug("approx::checkCutOnPad") << ind << " " << coeff << " " << endl; + Debug("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl; + + if(!roughlyEqual(coeff, onConstructed.getDouble())){ + Debug("approx::checkCutOnPad") << "coeff failure" << endl; + return true; + } + } + if(visited.size() != constructedLhs.size()){ + Debug("approx::checkCutOnPad") << "size mismatch" << endl; + return true; + } + + + if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){ + Debug("approx::checkCutOnPad") + << "norm rhs is off " << cut.getRhs() << " " << constructedRhs << endl; + return true; + } + return false; +} + + + +bool ApproxGLPK::attemptBranchCut(int nid, const BranchCutInfo& br_cut){ + d_pad.clear(); + + const PrimitiveVec& cut_vec = br_cut.getCutVector(); + int structural = cut_vec.inds[1]; + Assert(roughlyEqual(cut_vec.coeffs[1], +1.0)); + + ArithVar x = getArithVarFromStructural(structural); + d_pad.d_failure = (x == ARITHVAR_SENTINEL); + if(d_pad.d_failure){ return true; } + + Kind brKind = br_cut.getKind(); + + d_pad.d_failure = (brKind != kind::LEQ && brKind != kind::GEQ); + if(d_pad.d_failure){ return true; } + + d_pad.d_cutKind = brKind; + d_pad.d_cut.lhs.set(x, Rational(1)); + + Rational& rhs = d_pad.d_cut.rhs; + rhs = estimateWithCFE(Rational::fromDouble(br_cut.getRhs()), Integer(1)); + d_pad.d_failure = !rhs.isIntegral(); + if(d_pad.d_failure) { return true; } + + d_pad.d_failure = checkCutOnPad(nid, br_cut); + if(d_pad.d_failure){ return true; } + + return false; +} + +bool ApproxGLPK::attemptGmi(int nid, const GmiInfo& gmi){ + d_pad.clear(); + + d_pad.d_cutKind = kind::GEQ; + + int M = gmi.getMAtCreation(); + ArithVar b = _getArithVar(nid, M, gmi.basic); + d_pad.d_failure = (b == ARITHVAR_SENTINEL); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = !d_vars.isIntegerInput(b); + if(d_pad.d_failure){ return true; } + + d_pad.d_basic = b; + + + const PrimitiveVec& tab = gmi.tab_row; + d_pad.d_failure = attemptConstructTableRow(nid, M, tab); + if(d_pad.d_failure){ return true; } + + int* statuses = gmi.tab_statuses; + DenseMap<ConstraintP>& 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<Rational>& compRanges = d_pad.d_compRanges; + + DenseMap<Rational>& 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<Rational>::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<Rational>& 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="<<N<<", M=" << M << std::endl; + for(int j=1; j <= N+M; ++j){ + ArithVar v = _getArithVar(nid, M, j); + if(v == ARITHVAR_SENTINEL){ + Debug("approx::mir") << " for: " << j << " no variable" << endl; + continue; + } + rep = SlackUndef; + char sub = mir.subst[j]; + switch(sub){ + case 'L': + case 'U': + lb = (sub == 'L'); + useVB = lb ? (mir.vlbRows[j] > 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<ArithVar> virtualVars; + + DenseMap<Rational>& cut = d_pad.d_cut.lhs; + Rational& cutRhs = d_pad.d_cut.rhs; + + DenseMap<Rational>::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<Rational>& 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<Rational>& agg = d_pad.d_agg.lhs; + const Rational& aggRhs = d_pad.d_agg.rhs; + DenseMap<Rational>& 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<Rational>::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<Rational>& alpha = d_pad.d_alpha.lhs; + int M = mir.getMAtCreation(); + int N = mir.getN(); + DenseMap<Rational>& 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<Rational>& mod = d_pad.d_mod.lhs; + const Rational& modRhs = d_pad.d_mod.rhs; + DenseMap<Rational>& 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<Rational>::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<Rational>& 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("<<nid <<", "<< basic<< ")"<<endl; + vec.print(Debug("gaussianElimConstructTableRow")); + Debug("gaussianElimConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl; + } + + set<ArithVar> 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("<<nid <<", "<< basic<< ")"<<endl; + + Matrix<Rational> A; + A.increaseSizeTo(d_vars.getNumberOfVariables()); + std::vector< std::pair<RowIndex, ArithVar> > rows; + set<ArithVar>::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<Rational> coeffs; + vector<ArithVar> 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("<<nid <<", "<< basic<< ")"<<endl; + + for(size_t i=0; i < rows.size(); ++i){ + RowIndex rid = rows[i].first; + Assert(rows[i].second == ARITHVAR_SENTINEL); + + // substitute previous rows + for(size_t j=0; j < i; j++){ + RowIndex prevRow = rows[j].first; + ArithVar other = rows[j].second; + Assert(other != ARITHVAR_SENTINEL); + const Matrix<Rational>::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<Rational>::RowIterator k = A.getRow(rid).begin(); + Matrix<Rational>::RowIterator k_end = A.getRow(rid).end(); + for(; k != k_end; ++k){ + const Matrix<Rational>::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("<<nid <<", "<< basic<< ")"<<endl; + return true; + }else{ + // 0 = q * s + sum c_i * x_i + Rational mult = -(q.inverse()); + Debug("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl; + Debug("gaussianElimConstructTableRow") << "selecting " << rid << " " << s << endl; + //cout << "selecting " << s << " : complexity " << mult.complexity() << " " << mult << endl; + //cout << "selecting " << rid << " " << s << endl; + A.multiplyRowByConstant(rid, mult); + rows[i].second = s; + } + } + Debug("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + if(rows.empty()) { + Debug("gaussianElimConstructTableRow") << "4 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + RowIndex rid_last = rows.back().first; + ArithVar rid_var = rows.back().second; + if(rid_var != basic){ + Debug("gaussianElimConstructTableRow") << "4 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + Assert(tab.empty()); + + Matrix<Rational>::RowIterator k = A.getRow(rid_last).begin(); + Matrix<Rational>::RowIterator k_end = A.getRow(rid_last).end(); + for(; k != k_end; ++k){ + const Matrix<Rational>::Entry& e = *k; + tab.set(e.getColVar(), e.getCoefficient()); + } + Debug("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + if(!tab.isKey(basic)){ + Debug("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + if(tab[basic] != Rational(-1)){ + Debug("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + tab.remove(basic); + Debug("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + if(vec.len < 0 ){ + Debug("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + if(tab.size() != ((unsigned)vec.len) ) { + Debug("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() << " " << vec.len << endl; + return true; + } + + Debug("gaussianElimConstructTableRow") << "7 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + double coeff = vec.coeffs[i]; + ArithVar var = _getArithVar(nid, M, ind); + Assert(var != ARITHVAR_SENTINEL); + if(!tab.isKey(var)){ + Debug("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + double est = tab[var].getDouble(); + + if(!ApproximateSimplex::roughlyEqual(coeff, est)){ + Debug("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")" + << " boink on " << ind << " " << var << " " << est <<endl; + return true; + } + Debug("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl; + } + + Debug("gaussianElimConstructTableRow") + << "gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")" + << " superduper" << endl; + + return false; +} +bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + for(size_t i=0; i < d_denomGuesses.size(); ++i){ + const Integer& D = d_denomGuesses[i]; + if(!guessCoefficientsConstructTableRow(nid, M, vec, D)){ + d_stats.d_averageGuesses.addEntry(i+1); + Debug("approx::gmi") << "guesseditat " << i << " D=" << D << endl; + return false; + } + } + return true; +} +bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec, const Integer& D){ + ArithVar basic = d_pad.d_basic; + DenseMap<Rational>& 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(Debug.isOn("guessCoefficientsConstructTableRow")){ + Debug("guessCoefficientsConstructTableRow") << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl; + vec.print(Debug("guessCoefficientsConstructTableRow")); + Debug("guessCoefficientsConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl; + } + + tab.set(basic, Rational(-1)); + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + double coeff = vec.coeffs[i]; + ArithVar var = _getArithVar(nid, M, ind); + if(var == ARITHVAR_SENTINEL){ + Debug("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl; + return true; + } + Debug("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl; + + Rational cfe = estimateWithCFE(coeff, D); + tab.set(var, cfe); + Debug("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl; + } + if(!guessIsConstructable(tab)){ + Debug("guessCoefficientsConstructTableRow") << "failed to construct with " << D << endl; + return true; + } + tab.remove(basic); + return false; +} + +/* Maps an ArithVar to either an upper/lower bound */ +bool ApproxGLPK::constructGmiCut(){ + const DenseMap<Rational>& tabRow = d_pad.d_tabRow.lhs; + const DenseMap<ConstraintP>& toBound = d_pad.d_toBound; + DenseMap<Rational>& cut = d_pad.d_cut.lhs; + std::set<ConstraintP>& explanation = d_pad.d_explanation; + Rational& rhs = d_pad.d_cut.rhs; + + DenseMap<Rational>::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<ConstraintP, ConstraintP> 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<const GmiInfo&>(cut)); + break; + case MirCutKlass: + failure = attemptMir(nid, static_cast<const MirInfo&>(cut)); + break; + case BranchCutKlass: + failure = attemptBranchCut(nid, dynamic_cast<const BranchCutInfo&>(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<ConstraintP>& 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 <vector> @@ -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<DeltaRational> 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<const CutInfo*> getValidCuts(const NodeLog& node) throw(RationalFromDoubleException) = 0; + //virtual std::vector<const NodeLog*> 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<Integer>& 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 <ostream> + +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<Rational>(); + 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<Rational>() * 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<Node>& 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<N; ++i){ + Node sk = d_skolemsAdded[i]; + Node to = d_skolems[sk]; + if(!to.isNull()){ + Node fp = applySubstitutions(to); + addSubstitution(sk, fp); + } + } + d_implies.clear(); + d_skolemsAdded.clear(); + d_orBinEqs.clear(); +} + +void ArithIteUtils::addImplications(Node x, Node y){ + // (or x y) + // (=> (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<Node>& negtimp = ti->second; + const std::set<Node>& 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<Node>::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 <ext/hash_map> +#include <ext/hash_set> +#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<Node, Node, NodeHashFunction> 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<Node, Integer, NodeHashFunction> NodeIntegerMap; + NodeIntegerMap d_gcds; + + Integer d_one; + + context::CDO<unsigned> d_subcount; + typedef context::CDTrailHashMap<Node, Node, NodeHashFunction> CDNodeMap; + CDNodeMap d_skolems; + + typedef std::map<Node, std::set<Node> > ImpMap; + ImpMap d_implies; + + std::vector<Node> d_skolemsAdded; + + std::vector<Node> 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<Node>& 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<Rational>().isOne()){ + return RewriteResponse(REWRITE_DONE, t[1]); + } + if(t[1].getKind() == kind::CONST_RATIONAL + && t[1].getConst<Rational>().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<Rational>() == qZero) { - return RewriteResponse(REWRITE_DONE, mkRationalNode(qZero)); + if((*i).getConst<Rational>().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<TNode>& 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<TNode> 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<Monomial> monomials; + std::vector<Polynomial> 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<Rational>()); d_maxMap.insert(n, n.getConst<Rational>()); 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 <set> @@ -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<bool>(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<TNode>& 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<Constraint>& 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<ConstraintP>& 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<Constraint>& 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<PerVariableDatabase>& vec){ std::vector<PerVariableDatabase>::const_iterator first = vec.begin(); std::vector<PerVariableDatabase>::const_iterator last = vec.end(); @@ -550,7 +653,7 @@ ConstraintDatabase::~ConstraintDatabase(){ delete d_watches; - std::vector<Constraint> constraintList; + std::vector<ConstraintP> 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<Constraint> constraintList; + std::vector<ConstraintP> 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<Constraint>& b){ +void Constraint_::propagate(const ConstraintCPVec& b){ Assert(!hasProof()); Assert(canBePropagated()); @@ -817,9 +937,8 @@ void ConstraintValue::propagate(const std::vector<Constraint>& 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<Constraint>& 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<Constraint>& 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<Constraint>& a){ +void Constraint_::markAsTrue(const ConstraintCPVec& a){ Assert(truthIsUnknown()); Assert(a.size() >= 1); d_database->d_proofs.push_back(NullConstraint); - for(vector<Constraint>::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<Constraint>& 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<Constraint>& 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<Constraint>& b){ - NodeBuilder<> nb(kind::AND); - for(vector<Constraint>::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<ConstraintCP, ConstraintCPHash> 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<Node>& out, Constraint a, Constraint b){ +void implies(std::vector<Node>& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1153,7 +1316,7 @@ void implies(std::vector<Node>& out, Constraint a, Constraint b){ out.push_back(orderOr); } -void mutuallyExclusive(std::vector<Node>& out, Constraint a, Constraint b){ +void mutuallyExclusive(std::vector<Node>& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1169,13 +1332,13 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector<Node>& 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<Node>& out, Ari void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<Node>& out, ArithVar v) const{ - vector<Constraint> equalities; + vector<ConstraintP> equalities; SortedConstraintMap& scm = getVariableSCM(v); SortedConstraintMapConstIterator scm_iter = scm.begin(); @@ -1197,34 +1360,34 @@ void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<Node>& 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<Constraint>::const_iterator i, j, eq_end = equalities.end(); + vector<ConstraintP>::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<Node>& 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<Constraint> CDConstraintList; +typedef context::CDList<ConstraintCP> CDConstraintList; -typedef __gnu_cxx::hash_map<Node, Constraint, NodeHashFunction> NodetoConstraintMap; +typedef __gnu_cxx::hash_map<Node, ConstraintP, NodeHashFunction> NodetoConstraintMap; typedef size_t ProofId; static ProofId ProofIdSentinel = std::numeric_limits<ProofId>::max(); @@ -111,54 +111,29 @@ static AssertionOrder AssertionOrderSentinel = std::numeric_limits<AssertionOrde class ValueCollection { private: - Constraint d_lowerBound; - Constraint d_upperBound; - Constraint d_equality; - Constraint d_disequality; + ConstraintP d_lowerBound; + ConstraintP d_upperBound; + ConstraintP d_equality; + ConstraintP d_disequality; public: - ValueCollection() - : d_lowerBound(NullConstraint), - d_upperBound(NullConstraint), - d_equality(NullConstraint), - d_disequality(NullConstraint) - {} + ValueCollection(); - static ValueCollection mkFromConstraint(Constraint c); + static ValueCollection mkFromConstraint(ConstraintP c); - bool hasLowerBound() const{ - return d_lowerBound != NullConstraint; - } - bool hasUpperBound() const{ - return d_upperBound != NullConstraint; - } - bool hasEquality() const{ - return d_equality != NullConstraint; - } - bool hasDisequality() const { - return d_disequality != NullConstraint; - } + bool hasLowerBound() const; + bool hasUpperBound() const; + bool hasEquality() const; + bool hasDisequality() const; bool hasConstraintOfType(ConstraintType t) const; - Constraint getLowerBound() const { - Assert(hasLowerBound()); - return d_lowerBound; - } - Constraint getUpperBound() const { - Assert(hasUpperBound()); - return d_upperBound; - } - Constraint getEquality() const { - Assert(hasEquality()); - return d_equality; - } - Constraint getDisequality() const { - Assert(hasDisequality()); - return d_disequality; - } + ConstraintP getLowerBound() const; + ConstraintP getUpperBound() const; + ConstraintP getEquality() const; + ConstraintP getDisequality() const; - Constraint getConstraintOfType(ConstraintType t) const; + ConstraintP getConstraintOfType(ConstraintType t) const; /** Returns true if any of the constraints are non-null. */ bool empty() const; @@ -174,11 +149,11 @@ public: * Adds a constraint to the set. * The collection must not have a constraint of that type already. */ - void add(Constraint c); + void add(ConstraintP c); - void push_into(std::vector<Constraint>& vec) const; + void push_into(std::vector<ConstraintP>& 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<Constraint>& b); + void propagate(ConstraintCP a); + void propagate(ConstraintCP a, ConstraintCP b); + //void propagate(const std::vector<Constraint>& 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<Constraint>& b); + void impliedBy(ConstraintCP a); + void impliedBy(ConstraintCP a, ConstraintCP b); + //void impliedBy(const std::vector<Constraint>& b); + void impliedBy(const ConstraintCPVec& b); - Node makeImplication(const std::vector<Constraint>& b) const; - static Node makeConjunction(const std::vector<Constraint>& 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<Constraint>& b); + void markAsTrue(ConstraintCP a, ConstraintCP b); + //void markAsTrue(const std::vector<Constraint>& 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<PerVariableDatabase*> 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<Constraint> d_toPropagate; + context::CDQueue<ConstraintCP> 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<Constraint, ConstraintValue::ProofCleanup> ProofCleanupList; - typedef context::CDList<Constraint, ConstraintValue::CanBePropagatedCleanup> CBPList; - typedef context::CDList<Constraint, ConstraintValue::AssertionOrderCleanup> AOList; - typedef context::CDList<Constraint, ConstraintValue::SplitCleanup> SplitList; + typedef context::CDList<ConstraintP, Constraint_::ProofCleanup> ProofCleanupList; + typedef context::CDList<ConstraintP, Constraint_::CanBePropagatedCleanup> CBPList; + typedef context::CDList<ConstraintP, Constraint_::AssertionOrderCleanup> AOList; + typedef context::CDList<ConstraintP, Constraint_::SplitCleanup> 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<PerVariableDatabase>& 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<Node>& 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 <vector> + 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<ConstraintCP> 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 <math.h> +#include <cmath> +#include <map> +#include <limits.h> + +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<int, CutInfo*> currInOrd; //sorted + + const PrimitiveVec& cv = rd.getCutVector(); + std::vector<int> 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; k<sortedRemoved.size(); k++){ + Debug("approx::nodelog") << ", " << sortedRemoved[k]; + } + Debug("approx::nodelog") << endl; + Debug("approx::nodelog") << "cv.len" << cv.len << endl; + } + + int min = sortedRemoved.front(); + + CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(); + while(iter != iend){ + CutInfo* curr= *iter; + if(curr->getId() < 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<int, CutInfo*>::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<int, NodeLog>::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 << "["<<el <<", " << d_branches.count(el) << "]"; + } + os << endl; +} + + +void DenseVector::print(std::ostream& os) const { + os << rhs << " + "; + print(os, lhs); +} +void DenseVector::print(ostream& out, const DenseMap<Rational>& v){ + out << "[DenseVec len " << v.size(); + DenseMap<Rational>::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 <vector> +#include <map> +#include <set> +#include <ext/hash_map> + +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<Rational> lhs; + Rational rhs; + void purge(); + void print(std::ostream& os) const; + + static void print(std::ostream& os, const DenseMap<Rational>& 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<CutInfo*, CmpCutPointer> CutSet; + CutSet d_cuts; + std::map<int, int> 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<int, ArithVar> 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<int, NodeLog> 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<Constraint> d_trail; - /** Compare by d_minimal. */ - struct TrailMinimalCoefficientOrder { - const context::CDList<Constraint>& d_trail; - TrailMinimalCoefficientOrder(const context::CDList<Constraint>& 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<Constraint>& d_trail; + // TrailMinimalCoefficientOrder(const context::CDList<Constraint>& 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<Constraint> bounds; + ConstraintCPVec bounds; propagateRow(bounds, ridx, upperBound, c); c->impliedBy(bounds); } -void LinearEqualityModule::propagateRow(vector<Constraint>& 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<Constraint>& 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<Constraint>& 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<Constraint>& 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 T> class Matrix { -protected: - +public: typedef MatrixEntry<T> Entry; +protected: typedef CVC4::theory::arith::RowVector<T> RowVectorT; - typedef typename RowVectorT::const_iterator RowIterator; - typedef CVC4::theory::arith::ColumnVector<T> 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<T>(&d_entries)); } + void increaseSizeTo(size_t s){ + while(getNumColumns() < s){ + increaseSize(); + } + } + const RowVector<T>& 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 <list> +#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> Monomial::sumLikeTerms(const std::vector<Monomial> & monos) { +// vector<Monomial> Monomial::sumLikeTerms(const std::vector<Monomial> & monos) { +// Assert(isSorted(monos)); +// vector<Monomial> outMonomials; +// typedef vector<Monomial>::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<Monomial>& m){ + if(!isSorted(m)){ + std::sort(m.begin(), m.end()); + } +} + +void Monomial::combineAdjacentMonomials(std::vector<Monomial>& monos) { Assert(isSorted(monos)); - vector<Monomial> outMonomials; - typedef vector<Monomial>::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<Monomial> sortedMonos; merge_ranges(begin(), end(), vl.begin(), vl.end(), sortedMonos); - std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos); + Monomial::combineAdjacentMonomials(sortedMonos); + //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos); - Polynomial result = mkPolynomial(combined); + Polynomial result = mkPolynomial(sortedMonos); return result; } + +Polynomial Polynomial::sumPolynomials(const std::vector<Polynomial>& 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<Node, Rational> coeffs; + for(size_t i = 0, N = ps.size(); i<N; ++i){ + const Polynomial& p = ps[i]; + for(iterator pi = p.begin(), pend = p.end(); pi != pend; ++pi) { + Monomial m = *pi; + coeffs[m.getVarList().getNode()] += m.getConstant().getValue(); + } + } + std::vector<Monomial> monos; + std::map<Node, Rational>::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 <list> #include <algorithm> @@ -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<Rational>(); } + 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<Monomial>& m); + static void combineAdjacentMonomials(std::vector<Monomial>& 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<Monomial> sumLikeTerms(const std::vector<Monomial>& monos); + //static std::vector<Monomial> sumLikeTerms(const std::vector<Monomial>& 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<Monomial>& list); + size_t getComplexity() const; };/* class Monomial */ class SumPair; @@ -938,9 +948,12 @@ public: return true; } + static Polynomial sumPolynomials(const std::vector<Polynomial>& 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<ArithVar>::iterator i_end = d_released.end(); - for(int iter = 0; iter < 20 && d_releasedIterator != i_end; ++d_releasedIterator){ - ArithVar v = *d_releasedIterator; - VarInfo& vi = d_vars.get(v); - if(vi.canBeReclaimed()){ + 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<ArithVar>::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<ConstraintP, ConstraintP> 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 <<endl; @@ -266,15 +364,15 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const } } -void ArithVariables::initialize(ArithVar x, Node n, bool slack){ +void ArithVariables::initialize(ArithVar x, Node n, bool aux){ VarInfo& vi = d_vars.get(x); - vi.initialize(x, n, slack); + vi.initialize(x, n, aux); d_nodeToArithVarMap[n] = x; } -ArithVar ArithVariables::allocate(Node n, bool slack){ +ArithVar ArithVariables::allocate(Node n, bool aux){ ArithVar v = allocateVariable(); - initialize(v, n, slack); + initialize(v, n, aux); return v; } @@ -333,7 +431,7 @@ const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{ } -void ArithVariables::setLowerBoundConstraint(Constraint c){ +void ArithVariables::setLowerBoundConstraint(ConstraintP c){ AssertArgument(c != NullConstraint, "Cannot set a lower bound to NullConstraint."); AssertArgument(c->isEquality() || c->isLowerBound(), "Constraint type must be set to an equality or UpperBound."); @@ -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<VarInfo> VarInfoVec; + /** This maps an ArithVar to its Variable information.*/ VarInfoVec d_vars; - // Partial Map from Arithvar -> PreviousAssignment + /** Partial Map from Arithvar -> PreviousAssignment */ DenseMap<DeltaRational> 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<ArithVar> d_pool; - std::list<ArithVar> d_released; - std::list<ArithVar>::iterator d_releasedIterator; + std::vector<ArithVar> d_released; + //std::list<ArithVar>::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<ArithVar, Constraint> AVCPair; + typedef std::pair<ArithVar, ConstraintP> 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<ConstraintP, ConstraintP> 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<ArithVarVec> subsets = greedyConflictSubsets(); @@ -823,11 +826,12 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ Assert(!subsets.empty()); for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){ const ArithVarVec& subset = *i; - Node conflict = generateSOIConflict(subset); + 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<Rational>& sum); +static double fRand(double fMin, double fMax); +static bool complexityBelow(const DenseMap<Rational>& 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<Rational>& row, uint32_t cap){ + DenseMap<Rational>::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<ValueCollection&>(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound); if(ub->hasProof()){ - Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + 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<ValueCollection&>(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<ValueCollection&>(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(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<ValueCollection&>(vc), LowerBound); + const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(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<Rational> 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<Rational>& 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<bool>() && isDistinct) || (!reEq.getConst<bool>() && !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<ArithVar>& lemmas){ - //output the lemmas - for(vector<ArithVar>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){ - ArithVar v = *i; - Assert(!d_cutInContext.contains(v)); - d_cutInContext.insert(v); - d_cutCount = d_cutCount + 1; - Node lem = branchIntegerVariable(v); - outputLemma(lem); - ++(d_statistics.d_externalBranchAndBounds); + 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<ArithVar>& lemmas){ +// //output the lemmas +// for(vector<ArithVar>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){ +// ArithVar v = *i; +// Assert(!d_cutInContext.contains(v)); +// d_cutInContext.insert(v); +// d_cutCount = d_cutCount + 1; +// Node lem = branchIntegerVariable(v); +// outputLemma(lem); +// ++(d_statistics.d_externalBranchAndBounds); +// } +// } + +bool TheoryArithPrivate::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<ConstraintCPVec> 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<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const DenseMap<Rational>& 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<ArithVar> variables; + vector<Rational> 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<ConstraintP, ArithVar> 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<ConstraintP, ArithVar> p; + p = replayGetConstraint(d_lhsTmp, kind::LEQ, fl, true); + d_lhsTmp.purge(); + return p; + } + } + return make_pair(NullConstraint, ARITHVAR_SENTINEL); +} + +std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const CutInfo& ci) { + Assert(ci.reconstructed()); + const DenseMap<Rational>& 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<Rational>& sum){ + NodeBuilder<> nb(kind::PLUS); + NodeManager* nm = NodeManager::currentNM(); + DenseMap<Rational>::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<ConstraintP, ArithVar> 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<Constraint>(); } +// res.push_back(c); +// cout << "\t"<<c << endl; +// } +// }else{ +// ConstraintP c = d_constraintDatabase.lookup(n); +// if(c == NullConstraint){ return std::vector<Constraint>(); } +// res.push_back(c); +// } +// return res; +// } + +// void TheoryArithPrivate::enqueueConstraints(std::vector<Constraint>& 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<ConstraintCP>& propagated) const { +// cout << "resolveOutPropagated()" << conf << endl; +// std::set<ConstraintCP> final; +// std::set<ConstraintCP> processed; +// std::vector<ConstraintCP> 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<Constraint>::const_iterator iter = final.begin(), end = final.end(); +// for(; iter != end; ++iter){ +// ConstraintP c = *iter; +// c->explainForConflict(nb); +// } +// Node newConf = safeConstructNary(nb); +// cout << "resolveOutPropagated("<<conf<<", ...) ->" << newConf << endl; +// return newConf; +// } + +void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& 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["<<i<<"] " << orig << " to " << conf.size() << endl; + } + Debug("arith::resolveOutPropagated") + << "ending resolveOutPropagated() " << confs.size() << endl; +} + +struct SizeOrd { + bool operator()(const ConstraintCPVec& a, const ConstraintCPVec& b) const{ + return a.size() < b.size(); + } +}; +void TheoryArithPrivate::subsumption(std::vector<ConstraintCPVec>& 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<ConstraintCPVec> 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<ConstraintCPVec> res; + + { /* create a block for the purpose of pushing the sat context */ + context::Context::ScopedPush speculativePush(getSatContext()); + Assert(!anyConflict()); + Assert(conflictQueueEmpty()); + set<ConstraintCP> 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<RowsDeleted*>(ci); + + tl.applyRowsDeleted(nid, *rd); + // The previous line modifies nl + + ++d_statistics.d_applyRowsDeleted; + }else if(ci->getKlass() == BranchCutKlass){ + BranchCutInfo* bci = dynamic_cast<BranchCutInfo*>(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<Rational>& 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<ConstraintP, ArithVar> 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<ConstraintP, ArithVar> 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<ConstraintCPVec> dnres; + std::vector<ConstraintCPVec> upres; + std::vector<size_t> containsdn; + std::vector<size_t> 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 #"<<res.size()<<" conflicts on branch " << nid << endl; + if(res.empty()){ + ++d_statistics.d_replayBranchCloseFailures; + } + + }else{ + Debug("approx::replayLogRec") << "failed to make a branch " << nid << endl; + } + }else{ + ++d_statistics.d_replayLeafCloseFailures; + Debug("approx::replayLogRec") << "failed on node " << nid << endl; + Assert(res.empty()); + } + resolveOutPropagated(res, propagated); + Debug("approx::replayLogRec") << "replayLogRec() ending" << std::endl; + + + if(options::replayFailureLemma()){ + // must be done inside the sat context to get things + // propagated at this level + if(res.empty() && nid == getTreeLog().getRootId()){ + Assert(!d_replayedLemmas); + d_replayedLemmas = replayLemmas(approx); + Assert(d_acTmp.empty()); + while(!d_approxCuts.empty()){ + Node lem = d_approxCuts.front(); + d_approxCuts.pop(); + d_acTmp.push_back(lem); + } + } + } + } /* pop the sat context */ + + /* move into the current context. */ + while(!d_acTmp.empty()){ + Node lem = d_acTmp.back(); + d_acTmp.pop_back(); + d_approxCuts.push_back(lem); + } + Assert(d_acTmp.empty()); + + + /* Garbage collect the constraints from this call */ + while(d_replayConstraints.size() > 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<Rational>& 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<const CutInfo*> 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<Rational>& 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["<<i<<"] " << implication << endl; + ++(d_statistics.d_mipExternalCuts); + } + } + if(root.isBranch()){ + Node lit = branchToNode(approx, root); + if(!lit.isNull()){ + anythingnew = anythingnew || !isSatLiteral(lit); + Node branch = lit.orNode(lit.notNode()); + d_approxCuts.push_back(branch); + ++(d_statistics.d_mipExternalBranch); + Debug("approx::lemmas") << "branching "<< root <<" as " << branch << endl; + } + } + return anythingnew; + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + return false; + } +} + +void TheoryArithPrivate::turnOffApproxFor(int32_t rounds){ + d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff + rounds; + ++(d_statistics.d_approxDisabled); +} + +bool TheoryArithPrivate::safeToCallApprox() const{ + unsigned numRows = 0; + unsigned numCols = 0; + var_iterator vi = var_begin(), vi_end = var_end(); + // Assign each variable to a row and column variable as it appears in the input + for(; vi != vi_end && !(numRows > 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<ArithVar> 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<Node> 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<ArithVar> 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<ArithVar> 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<Constraint> save; + vector<ConstraintP> 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<Constraint>::const_iterator i=save.begin(), i_end = save.end(); + vector<ConstraintP>::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<Constraint>::const_iterator it = d_diseqQueue.begin(); - context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end(); + context::CDQueue<ConstraintP>::const_iterator it = d_diseqQueue.begin(); + context::CDQueue<ConstraintP>::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<DeltaRational> relevantDeltaValues; - context::CDQueue<Constraint>::const_iterator qiter = d_diseqQueue.begin(); - context::CDQueue<Constraint>::const_iterator qiter_end = d_diseqQueue.end(); + context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin(); + context::CDQueue<ConstraintP>::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<Constraint> 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<Node, Constraint, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals; + context::CDInsertHashMap<Node, ConstraintP, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals; /** Returns true if x is of type Integer. */ inline bool isInteger(ArithVar x) const { return d_partialModel.isInteger(x); - //return d_variableTypes[x] >= ATInteger; } - /** This is the set of variables initially introduced as slack variables. */ - //std::vector<bool> d_slackVars; - /** Returns true if the variable was initially introduced as a slack variable. */ - inline bool isSlackVariable(ArithVar x) const{ - return d_partialModel.isSlack(x); - //return d_slackVars[x]; + /** 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<Constraint> d_diseqQueue; + context::CDQueue<ConstraintP> 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<Constraint> d_currentPropagationList; + std::deque<ConstraintP> d_currentPropagationList; - context::CDQueue<Constraint> d_learnedBounds; + context::CDQueue<ConstraintP> d_learnedBounds; /** @@ -277,15 +284,31 @@ private: /** This is only used by simplex at the moment. */ - context::CDList<Node> d_conflicts; + context::CDList<ConstraintCPVec> d_conflicts; + context::CDO<Node> 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<bool> 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<int> 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<ConstraintCP>& out, Node n) const; + //ConstraintCPVec resolveOutPropagated(const ConstraintCPVec& v, const std::set<ConstraintCP>& propagated) const; + void resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const; + void subsumption(std::vector<ConstraintCPVec>& 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<Node> d_approxCuts; + std::vector<Node> d_acTmp; /** Counts the number of fullCheck calls to arithmetic. */ uint32_t d_fullCheckCounter; @@ -581,12 +645,49 @@ private: context::CDO<bool> d_guessedCoeffSet; ArithRatPairVec d_guessedCoeffs; + + TreeLog* d_treeLog; + TreeLog& getTreeLog(); + + + ArithVarVec d_replayVariables; + std::vector<ConstraintP> d_replayConstraints; + DenseMap<Rational> d_lhsTmp; + + /* Approximate simpplex solvers are given a copy of their stats */ + ApproximateStatistics* d_approxStats; + ApproximateStatistics& getApproxStats(); + context::CDO<int32_t> d_attemptSolveIntTurnedOff; + void turnOffApproxFor(int32_t rounds); + bool getSolveIntegerResource(); + + void tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bl); + std::vector<ConstraintCPVec> replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth); + + std::pair<ConstraintP, ArithVar> replayGetConstraint(const CutInfo& info); + std::pair<ConstraintP, ArithVar> replayGetConstraint(ApproximateSimplex* approx, const NodeLog& nl) throw(RationalFromDoubleException); + std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& 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<uint32_t> d_satPivots; HistogramStat<uint32_t> 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 <vector> #include <list> +#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<Node>& 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<Node>& 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 <string> +#include <sstream> #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 <string> +#include <sstream> +#include <cmath> #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 { |