This moves the current linear arithmetic solver to src/theory/arith/linear, and inside `cvc5::internal::theory::arith::linear`.
Some code uses internal utilities from `arith::linear`, although this should be minimized.
This is preparation for breaking dependencies with the old code.
smt/witness_form.h
smt_util/boolean_simplification.cpp
smt_util/boolean_simplification.h
- theory/arith/approx_simplex.cpp
- theory/arith/approx_simplex.h
theory/arith/arith_evaluator.cpp
theory/arith/arith_evaluator.h
theory/arith/arith_ite_utils.cpp
theory/arith/arith_rewriter.h
theory/arith/arith_state.cpp
theory/arith/arith_state.h
- theory/arith/arith_static_learner.cpp
- theory/arith/arith_static_learner.h
theory/arith/arith_utilities.cpp
theory/arith/arith_utilities.h
- theory/arith/arithvar.cpp
- theory/arith/arithvar.h
- theory/arith/attempt_solution_simplex.cpp
- theory/arith/attempt_solution_simplex.h
- theory/arith/bound_counts.h
theory/arith/bound_inference.cpp
theory/arith/bound_inference.h
theory/arith/branch_and_bound.cpp
theory/arith/branch_and_bound.h
- theory/arith/callbacks.cpp
- theory/arith/callbacks.h
- theory/arith/congruence_manager.cpp
- theory/arith/congruence_manager.h
- theory/arith/constraint.cpp
- theory/arith/constraint.h
- theory/arith/constraint_forward.h
- theory/arith/cut_log.cpp
- theory/arith/cut_log.h
theory/arith/delta_rational.cpp
theory/arith/delta_rational.h
- theory/arith/dio_solver.cpp
- theory/arith/dio_solver.h
- theory/arith/dual_simplex.cpp
- theory/arith/dual_simplex.h
theory/arith/equality_solver.cpp
theory/arith/equality_solver.h
- theory/arith/error_set.cpp
- theory/arith/error_set.h
- theory/arith/fc_simplex.cpp
- theory/arith/fc_simplex.h
- theory/arith/infer_bounds.cpp
- theory/arith/infer_bounds.h
theory/arith/inference_manager.cpp
theory/arith/inference_manager.h
- theory/arith/linear_equality.cpp
- theory/arith/linear_equality.h
- theory/arith/matrix.cpp
- theory/arith/matrix.h
+ theory/arith/linear/approx_simplex.cpp
+ theory/arith/linear/approx_simplex.h
+ theory/arith/linear/arith_static_learner.cpp
+ theory/arith/linear/arith_static_learner.h
+ theory/arith/linear/arithvar.cpp
+ theory/arith/linear/arithvar.h
+ theory/arith/linear/attempt_solution_simplex.cpp
+ theory/arith/linear/attempt_solution_simplex.h
+ theory/arith/linear/bound_counts.h
+ theory/arith/linear/callbacks.cpp
+ theory/arith/linear/callbacks.h
+ theory/arith/linear/congruence_manager.cpp
+ theory/arith/linear/congruence_manager.h
+ theory/arith/linear/constraint.cpp
+ theory/arith/linear/constraint.h
+ theory/arith/linear/constraint_forward.h
+ theory/arith/linear/cut_log.cpp
+ theory/arith/linear/cut_log.h
+ theory/arith/linear/dio_solver.cpp
+ theory/arith/linear/dio_solver.h
+ theory/arith/linear/dual_simplex.cpp
+ theory/arith/linear/dual_simplex.h
+ theory/arith/linear/error_set.cpp
+ theory/arith/linear/error_set.h
+ theory/arith/linear/fc_simplex.cpp
+ theory/arith/linear/fc_simplex.h
+ theory/arith/linear/infer_bounds.cpp
+ theory/arith/linear/infer_bounds.h
+ theory/arith/linear/linear_equality.cpp
+ theory/arith/linear/linear_equality.h
+ theory/arith/linear/matrix.cpp
+ theory/arith/linear/matrix.h
+ theory/arith/linear/normal_form.cpp
+ theory/arith/linear/normal_form.h
+ theory/arith/linear/partial_model.cpp
+ theory/arith/linear/partial_model.h
+ theory/arith/linear/simplex.cpp
+ theory/arith/linear/simplex.h
+ theory/arith/linear/simplex_update.cpp
+ theory/arith/linear/simplex_update.h
+ theory/arith/linear/soi_simplex.cpp
+ theory/arith/linear/soi_simplex.h
+ theory/arith/linear/tableau.cpp
+ theory/arith/linear/tableau.h
+ theory/arith/linear/tableau_sizes.cpp
+ theory/arith/linear/tableau_sizes.h
+ theory/arith/linear/theory_arith_private.cpp
+ theory/arith/linear/theory_arith_private.h
theory/arith/nl/coverings_solver.cpp
theory/arith/nl/coverings_solver.h
theory/arith/nl/coverings/cdcac.cpp
theory/arith/nl/transcendental/transcendental_solver.h
theory/arith/nl/transcendental/transcendental_state.cpp
theory/arith/nl/transcendental/transcendental_state.h
- theory/arith/normal_form.cpp
- theory/arith/normal_form.h
theory/arith/operator_elim.cpp
theory/arith/operator_elim.h
- theory/arith/partial_model.cpp
- theory/arith/partial_model.h
theory/arith/pp_rewrite_eq.cpp
theory/arith/pp_rewrite_eq.h
theory/arith/proof_checker.cpp
theory/arith/rewriter/rewrite_atom.h
theory/arith/rewrites.cpp
theory/arith/rewrites.h
- theory/arith/simplex.cpp
- theory/arith/simplex.h
- theory/arith/simplex_update.cpp
- theory/arith/simplex_update.h
- theory/arith/soi_simplex.cpp
- theory/arith/soi_simplex.h
- theory/arith/tableau.cpp
- theory/arith/tableau.h
- theory/arith/tableau_sizes.cpp
- theory/arith/tableau_sizes.h
theory/arith/theory_arith.cpp
theory/arith/theory_arith.h
- theory/arith/theory_arith_private.cpp
- theory/arith/theory_arith_private.h
theory/arith/theory_arith_type_rules.cpp
theory/arith/theory_arith_type_rules.h
theory/arith/type_enumerator.h
#include "preprocessing/assertion_pipeline.h"
#include "preprocessing/preprocessing_pass_context.h"
#include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
#include "theory/rewriter.h"
namespace cvc5::internal {
return false;
}
- if (!Polynomial::isMember(l))
+ if (!linear::Polynomial::isMember(l))
{
Trace("pbs::rewrites") << "not polynomial" << assertion << std::endl;
return false;
}
- Polynomial p = Polynomial::parsePolynomial(l);
+ linear::Polynomial p = linear::Polynomial::parsePolynomial(l);
clear();
if (negated)
{
Assert(d_off.value().isIntegral());
int adj = negated ? -1 : 1;
- for (Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i)
+ for (linear::Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i)
{
- Monomial m = *i;
+ linear::Monomial m = *i;
const Rational& coeff = m.getConstant().getValue();
if (!(coeff.isOne() || coeff.isNegativeOne()))
{
}
Assert(coeff.sgn() != 0);
- const VarList& vl = m.getVarList();
+ const linear::VarList& vl = m.getVarList();
Node v = vl.getNode();
if (!isPseudoBoolean(v))
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/approx_simplex.h"
-
-#include <math.h>
-
-#include <cfloat>
-#include <cmath>
-#include <unordered_set>
-
-#include "base/cvc5config.h"
-#include "base/output.h"
-#include "proof/eager_proof_generator.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/cut_log.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/normal_form.h"
-
-#ifdef CVC5_USE_GLPK
-#include "theory/arith/partial_model.h"
-#endif
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-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_branchMaxDepth(
- smtStatisticsRegistry().registerInt("z::approx::branchMaxDepth")),
- d_branchesMaxOnAVar(
- smtStatisticsRegistry().registerInt("z::approx::branchesMaxOnAVar")),
- d_gaussianElimConstructTime(smtStatisticsRegistry().registerTimer(
- "z::approx::gaussianElimConstruct::time")),
- d_gaussianElimConstruct(smtStatisticsRegistry().registerInt(
- "z::approx::gaussianElimConstruct::calls")),
- d_averageGuesses(
- smtStatisticsRegistry().registerAverage("z::approx::averageGuesses"))
-{
-}
-
-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 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;
-}
-
-bool ApproximateSimplex::roughlyEqual(double a, double b){
- if (a == 0){
- return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
- }else if (b == 0){
- return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
- }else{
- return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
- }
-}
-
-Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
- if(exp.empty()){
- return Rational(0);
- }else{
- Rational result = exp.back();
- vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
- vector<Integer>::const_reverse_iterator exp_end = exp.rend();
- ++exp_iter;
- while(exp_iter != exp_end){
- result = result.inverse();
- const Integer& i = *exp_iter;
- result += i;
- ++exp_iter;
- }
- return result;
- }
-}
-std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
- vector<Integer> mods;
- if(!q.isZero()){
- Rational carry = q;
- for(int i = 0; i <= depth; ++i){
- Assert(!carry.isZero());
- mods.push_back(Integer());
- Integer& back = mods.back();
- back = carry.floor();
- Trace("rationalToCfe") << " cfe["<<i<<"]: " << back << endl;
- carry -= back;
- if(carry.isZero()){
- break;
- }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
- break;
- }else{
- carry = carry.inverse();
- }
- }
- }
- return mods;
-}
-
-
-Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){
- Trace("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];
- Trace("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;
- Trace("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){
- Trace("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl;
- return cand_prev;
- }else{
- Trace("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl;
- return cand_curr;
- }
-}
-
-std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d,
- const Integer& D)
-{
- if (std::optional<Rational> from_double = Rational::fromDouble(d))
- {
- return estimateWithCFE(*from_double, D);
- }
- return std::optional<Rational>();
-}
-
-std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d)
-{
- return estimateWithCFE(d, Integer(s_defaultMaxDenom));
-}
-
-class ApproxNoOp : public ApproximateSimplex {
-public:
- ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s)
- : ApproximateSimplex(v,l,s)
- {}
- ~ApproxNoOp(){}
-
- LinResult solveRelaxation() override { return LinUnknown; }
- Solution extractRelaxation() const override { return Solution(); }
-
- ArithRatPairVec heuristicOptCoeffs() const override
- {
- return ArithRatPairVec();
- }
-
- MipResult solveMIP(bool al) override { return MipUnknown; }
- Solution extractMIP() const override { return Solution(); }
-
- void setOptCoeffs(const ArithRatPairVec& ref) override {}
-
- void tryCut(int nid, CutInfo& cut) override {}
-
- std::vector<const CutInfo*> getValidCuts(const NodeLog& node) override
- {
- return std::vector<const CutInfo*>();
- }
-
- ArithVar getBranchVar(const NodeLog& nl) const override
- {
- return ARITHVAR_SENTINEL;
- }
-
- double sumInfeasibilities(bool mip) const override { return 0.0; }
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-/* Begin the declaration of GLPK specific code. */
-#ifdef CVC5_USE_GLPK
-extern "C" {
-#include <glpk.h>
-}/* extern "C" */
-
-namespace cvc5::internal {
-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_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;
-
- bool d_solvedRelaxation;
- bool d_solvedMIP;
-
- CutScratchPad d_pad;
-
- std::vector<Integer> d_denomGuesses;
-
-public:
- ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
- ~ApproxGLPK();
-
- LinResult solveRelaxation() override;
- Solution extractRelaxation() const override { return extractSolution(false); }
-
- ArithRatPairVec heuristicOptCoeffs() const override;
-
- MipResult solveMIP(bool al) override;
- Solution extractMIP() const override { return extractSolution(true); }
- void setOptCoeffs(const ArithRatPairVec& ref) override;
- std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) override;
- ArithVar getBranchVar(const NodeLog& con) const override;
-
- static void printGLPKStatus(int status, std::ostream& out);
-
-
-private:
- Solution extractSolution(bool mip) const;
- int guessDir(ArithVar v) const;
-
- // get this stuff out of here
- void tryCut(int nid, CutInfo& cut) override;
-
- 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 override
- {
- return sumInfeasibilities(mip? d_mipProb : d_realProb);
- }
- double sumInfeasibilities(glp_prob* prob, bool mip) const;
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-#endif /*#ifdef CVC5_USE_GLPK */
-/* End the declaration of GLPK specific code. */
-
-/* Begin GPLK/NOGLPK Glue code. */
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){
-#ifdef CVC5_USE_GLPK
- return new ApproxGLPK(vars, l, s);
-#else
- return new ApproxNoOp(vars, l, s);
-#endif
-}
-bool ApproximateSimplex::enabled() {
-#ifdef CVC5_USE_GLPK
- return true;
-#else
- return false;
-#endif
-}
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-/* End GPLK/NOGLPK Glue code. */
-
-/* Begin GPLK implementation. */
-#ifdef CVC5_USE_GLPK
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-#ifdef CVC5_ASSERTIONS
-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;
- }
-}
-#endif
-
-ApproxGLPK::ApproxGLPK(const ArithVariables& var,
- TreeLog& l,
- ApproximateStatistics& s)
- : ApproximateSimplex(var, l, s),
- d_inputProb(nullptr),
- d_realProb(nullptr),
- d_mipProb(nullptr),
- d_solvedRelaxation(false),
- d_solvedMIP(false)
-{
-
- d_denomGuesses.push_back(Integer(1<<22));
- d_denomGuesses.push_back(Integer(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;
-
- // Assign each variable to a row and column variable as it appears in the input
- for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
- ArithVar v = *vi;
-
- if(d_vars.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);
- Trace("approx") << "Row vars: " << v << "<->" << numRows << endl;
- }else{
- ++numCols;
- d_colIndices.set(v, numCols);
- d_colToArithVar.set(numCols, v);
- Trace("approx") << "Col vars: " << v << "<->" << numCols << endl;
- }
- }
- 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;
-
- int type;
- double lb = 0.0;
- double ub = 0.0;
- if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
- if(d_vars.boundsAreEqual(v)){
- type = GLP_FX;
- }else{
- type = GLP_DB;
- }
- lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
- ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
- }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
- type = GLP_UP;
- ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
- }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
- type = GLP_LO;
- lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
- }else{
- type = GLP_FR;
- }
-
- if(d_vars.isAuxiliary(v)){
- int rowIndex = d_rowIndices[v];
- 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_inputProb, colIndex, kind);
- glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub);
- }
- }
-
- // Count the number of entries
- int numEntries = 0;
- for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
- ArithVar v = *i;
- Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
- for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
- {
- ++numEntries;
- }
- }
-
- int* ia = new int[numEntries+1];
- int* ja = new int[numEntries+1];
- double* ar = new double[numEntries+1];
-
- int entryCounter = 0;
- for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
- ArithVar v = *i;
- int rowIndex = d_rowIndices[v];
-
- Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
-
- for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
- {
- const Monomial& mono = *j;
- const Constant& constant = mono.getConstant();
- const VarList& variable = mono.getVarList();
-
- Node n = variable.getNode();
-
- Assert(d_vars.hasArithVar(n));
- ArithVar av = d_vars.asArithVar(n);
- int colIndex = d_colIndices[av];
- double coeff = constant.getValue().getDouble();
-
- ++entryCounter;
- ia[entryCounter] = rowIndex;
- ja[entryCounter] = colIndex;
- ar[entryCounter] = coeff;
- }
- }
- glp_load_matrix(d_inputProb, numEntries, ia, ja, ar);
-
- delete[] ia;
- delete[] ja;
- delete[] ar;
-}
-int ApproxGLPK::guessDir(ArithVar v) const{
- if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
- return -1;
- }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
- return 1;
- }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
- return 0;
- }else{
- int ubSgn = d_vars.getUpperBound(v).sgn();
- int lbSgn = d_vars.getLowerBound(v).sgn();
-
- if(ubSgn != 0 && lbSgn == 0){
- return -1;
- }else if(ubSgn == 0 && lbSgn != 0){
- return 1;
- }
-
- return 1;
- }
-}
-
-ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{
- ArithRatPairVec ret;
-
- // Strategies are guess:
- // 1 simple shared "ceiling" variable: danoint, pk1
- // x1 >= c, x1 >= tmp1, x1 >= tmp2, ...
- // 1 large row: fixnet, vpm2, pp08a
- // (+ .......... ) <= c
- // Not yet supported:
- // 1 complex shared "ceiling" variable: opt1217
- // x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... )
- // and all of the ... are the same sign
-
-
- // Candidates:
- // 1) Upper and lower bounds are not equal.
- // 2) The variable is not integer
- // 3a) For columns look for a ceiling variable
- // 3B) For rows look for a large row with
-
- DenseMap<BoundCounts> d_colCandidates;
- DenseMap<uint32_t> d_rowCandidates;
-
- double sumRowLength = 0.0;
- uint32_t maxRowLength = 0;
- for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
- ArithVar v = *vi;
-
- int type;
- if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
- if(d_vars.boundsAreEqual(v)){
- type = GLP_FX;
- }else{
- type = GLP_DB;
- }
- }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
- type = GLP_UP;
- }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
- type = GLP_LO;
- }else{
- type = GLP_FR;
- }
-
- if(type != GLP_FX && type != GLP_FR){
-
- 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);
- }else if(!d_vars.isInteger(v)){
- d_colCandidates.set(v, BoundCounts());
- }
- }
- }
-
- uint32_t maxCount = 0;
- for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
- ArithVar v = *i;
-
- bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v);
- bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v);
-
- if(lbCap || ubCap){
- ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v)
- : d_vars.getUpperBoundConstraint(v);
-
- if(!(b->getValue()).noninfinitesimalIsZero()){ continue; }
-
- Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v));
- if(poly.size() != 2) { continue; }
-
- Polynomial::iterator j = poly.begin();
- Monomial first = *j;
- ++j;
- Monomial second = *j;
-
- bool firstIsPos = first.constantIsPositive();
- bool secondIsPos = second.constantIsPositive();
-
- if(firstIsPos == secondIsPos){ continue; }
-
- Monomial pos = firstIsPos == lbCap ? first : second;
- Monomial neg = firstIsPos != lbCap ? first : second;
- // pos >= neg
- VarList p = pos.getVarList();
- VarList n = neg.getVarList();
- if(d_vars.hasArithVar(p.getNode())){
- ArithVar ap = d_vars.asArithVar(p.getNode());
- if( d_colCandidates.isKey(ap)){
- BoundCounts bc = d_colCandidates.get(ap);
- bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1);
- maxCount = std::max(maxCount, bc.upperBoundCount());
- d_colCandidates.set(ap, bc);
- }
- }
- if(d_vars.hasArithVar(n.getNode())){
- ArithVar an = d_vars.asArithVar(n.getNode());
- if( d_colCandidates.isKey(an)){
- BoundCounts bc = d_colCandidates.get(an);
- bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount());
- maxCount = std::max(maxCount, bc.lowerBoundCount());
- d_colCandidates.set(an, bc);
- }
- }
- }
- }
-
- // Attempt row
- double avgRowLength = d_rowCandidates.size() >= 1 ?
- ( sumRowLength / d_rowCandidates.size() ) : 0.0;
-
- // There is a large row among the candidates
- bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength);
-
- double rowLengthReq = (maxRowLength * .9);
-
- if (guessARowCandidate)
- {
- for (ArithVar r : d_rowCandidates)
- {
- uint32_t len = d_rowCandidates[r];
-
- int dir = guessDir(r);
- if(len >= rowLengthReq){
- ret.push_back(ArithRatPair(r, Rational(dir)));
- }
- }
- }
-
- // Attempt columns
- bool guessAColCandidate = maxCount >= 4;
- if (guessAColCandidate)
- {
- for (ArithVar c : d_colCandidates)
- {
- BoundCounts bc = d_colCandidates[c];
-
- int dir = guessDir(c);
- double ubScore = double(bc.upperBoundCount()) / maxCount;
- double lbScore = double(bc.lowerBoundCount()) / maxCount;
- if(ubScore >= .9 || lbScore >= .9){
- ret.push_back(ArithRatPair(c, Rational(c)));
- }
- }
- }
-
- return ret;
-}
-
-void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
- DenseMap<double> nbCoeffs;
-
- for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
- ArithVar v = (*i).first;
- const Rational& q = (*i).second;
-
- if(d_vars.isAuxiliary(v)){
- // replace the variable by its definition and multiply by q
- Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
- Polynomial pq = p * q;
-
- for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
- const Monomial& mono = *j;
- const Constant& constant = mono.getConstant();
- const VarList& variable = mono.getVarList();
-
- Node n = variable.getNode();
-
- Assert(d_vars.hasArithVar(n));
- ArithVar av = d_vars.asArithVar(n);
- int colIndex = d_colIndices[av];
- double coeff = constant.getValue().getDouble();
- if(!nbCoeffs.isKey(colIndex)){
- nbCoeffs.set(colIndex, 0.0);
- }
- nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
- }
- }else{
- int colIndex = d_colIndices[v];
- double coeff = q.getDouble();
- if(!nbCoeffs.isKey(colIndex)){
- nbCoeffs.set(colIndex, 0.0);
- }
- nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
- }
- }
- for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
- Index colIndex = *ci;
- double coeff = nbCoeffs[colIndex];
- glp_set_obj_coef(d_inputProb, colIndex, coeff);
- }
-}
-
-
-/*
- * rough strategy:
- * real relaxation
- * try approximate real optimization of error function
- * pivot in its basis
- * update to its assignment
- * check with FCSimplex
- * check integer solution
- * try approximate mixed integer problem
- * stop at the first feasible point
- * pivot in its basis
- * update to its assignment
- * check with FCSimplex
- */
-
-void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
- switch(status){
- case GLP_OPT:
- out << "GLP_OPT" << endl;
- break;
- case GLP_FEAS:
- out << "GLP_FEAS" << endl;
- break;
- case GLP_INFEAS:
- out << "GLP_INFEAS" << endl;
- break;
- case GLP_NOFEAS:
- out << "GLP_NOFEAS" << endl;
- break;
- case GLP_UNBND:
- out << "GLP_UNBND" << endl;
- break;
- case GLP_UNDEF:
- out << "GLP_UNDEF" << endl;
- break;
- default:
- out << "Status unknown" << endl;
- break;
- }
-}
-
-ApproxGLPK::~ApproxGLPK(){
- glp_delete_prob(d_inputProb);
- glp_delete_prob(d_realProb);
- glp_delete_prob(d_mipProb);
-
-}
-
-ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const
-{
- Assert(d_solvedRelaxation);
- Assert(!mip || d_solvedMIP);
-
- ApproximateSimplex::Solution sol;
- DenseSet& newBasis = sol.newBasis;
- DenseMap<DeltaRational>& newValues = sol.newValues;
-
- 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 isAux = d_vars.isAuxiliary(vi);
- int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
-
- int status = isAux ? glp_get_row_stat(prob, glpk_index)
- : glp_get_col_stat(prob, glpk_index);
-
- bool useDefaultAssignment = false;
-
- switch(status){
- case GLP_BS:
- newBasis.add(vi);
- useDefaultAssignment = true;
- break;
- case GLP_NL:
- case GLP_NS:
- if(!mip){
- newValues.set(vi, d_vars.getLowerBound(vi));
- }else{// intentionally fall through otherwise
- useDefaultAssignment = true;
- }
- break;
- case GLP_NU:
- if(!mip){
- newValues.set(vi, d_vars.getUpperBound(vi));
- }else {// intentionally fall through otherwise
- useDefaultAssignment = true;
- }
- break;
- default:
- {
- useDefaultAssignment = true;
- }
- break;
- }
-
- if(useDefaultAssignment){
-
- 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);
-
- if (d_vars.hasLowerBound(vi)
- && roughlyEqual(newAssign,
- d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA)))
- {
-
- newValues.set(vi, d_vars.getLowerBound(vi));
- }
- else if (d_vars.hasUpperBound(vi)
- && roughlyEqual(
- newAssign,
- d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA)))
- {
- newValues.set(vi, d_vars.getUpperBound(vi));
- }
- else
- {
- double rounded = round(newAssign);
- if (roughlyEqual(newAssign, rounded))
- {
- newAssign = rounded;
- }
-
- DeltaRational proposal;
- if (std::optional maybe_new = estimateWithCFE(newAssign))
- {
- proposal = *maybe_new;
- }
- else
- {
- // failed to estimate the old value. defaulting to the current.
- proposal = d_vars.getAssignment(vi);
- }
-
- if (roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA)))
- {
- proposal = d_vars.getAssignment(vi);
- }
-
- if (d_vars.strictlyLessThanLowerBound(vi, proposal))
- {
- proposal = d_vars.getLowerBound(vi);
- }
- else if (d_vars.strictlyGreaterThanUpperBound(vi, proposal))
- {
- proposal = d_vars.getUpperBound(vi);
- }
- newValues.set(vi, proposal);
- }
- }
- }
- return sol;
-}
-
-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;
- Trace("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl;
- }
- }
- if(lb != -DBL_MAX){
- if(newAssign < lb){
- double lbinf = lb - newAssign;
- infeas += lbinf;
-
- Trace("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl;
- }
- }
- }
- return infeas;
-}
-
-LinResult ApproxGLPK::solveRelaxation(){
- Assert(!d_solvedRelaxation);
-
- glp_smcp parm;
- glp_init_smcp(&parm);
- parm.presolve = GLP_OFF;
- parm.meth = GLP_PRIMAL;
- parm.pricing = GLP_PT_PSE;
- parm.it_lim = d_pivotLimit;
- parm.msg_lev = GLP_MSG_OFF;
-
- 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_realProb);
- int iterationcount = glp_get_it_cnt(d_realProb);
- switch(status){
- case GLP_OPT:
- case GLP_FEAS:
- case GLP_UNBND:
- d_solvedRelaxation = true;
- return LinFeasible;
- case GLP_INFEAS:
- case GLP_NOFEAS:
- d_solvedRelaxation = true;
- return LinInfeasible;
- default:
- {
- if(iterationcount >= d_pivotLimit){
- return LinExhausted;
- }
- return LinUnknown;
- }
- }
- }
- default:
- 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){
- Trace("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(TraceIsOn("approx::mirCut")){
- Trace("approx::mirCut") << "mir_id: " << exec_ord << endl;
- row_sum.print(Trace("approx::mirCut"));
- }
-
- return mir;
-}
-
-static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){
- Trace("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 CVC5_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;
-
- Trace("approx::gmiCut")
- << gmi <<" " << gmi->basic << " "
- << cut_ord<<" " << M <<" " << gmi_var << endl;
-
- PrimitiveVec& tab_row = gmi->tab_row;
- Trace("approx::gmiCut") << "Is N sufficient here?" << endl;
- tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs);
-
- Trace("approx::gmiCut") << "gmi_var " << gmi_var << endl;
-
- Trace("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;
- Trace("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];
- Trace("approx::gmiCut") << "ind " << i << " " << ind << endl;
- stat = (ind <= M) ?
- glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M);
-
- Trace("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(TraceIsOn("approx::gmiCut")){
- gmi->print(Trace("approx::gmiCut"));
- }
- return gmi;
-}
-
-static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){
- //(tree, br_var, br_val, dn < 0);
- double rhs;
- Kind k;
- if(down_bad){
- // down branch is infeasible
- // x <= floor(v) is infeasible
- // - so x >= ceiling(v) is implied
- k = kind::GEQ;
- rhs = std::ceil(br_val);
- }else{
- // up branch is infeasible
- // x >= ceiling(v) is infeasible
- // - so x <= floor(v) is implied
- k = kind::LEQ;
- rhs = std::floor(br_val);
- }
- BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs);
- return br_cut;
-}
-
-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);
- Trace("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:
- Trace("approx") << "GLP_RF_COV" << endl;
- break;
- case GLP_RF_CLQ:
- Trace("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);
- Trace("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl;
- for(int i = 1; i <= N; ++i){
- Trace("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;
-
- Trace("approx::") << "branch: "<< br_var << " " << br_val << " tree " << p << " " << dn << " " << up << endl;
- Trace("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl;
- if(dn < 0 && up < 0){
- Trace("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){
- Trace("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{
- Trace("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);
- Trace("approx::") << "close " << glpk_node_p << endl;
- tl.close(node_ord);
- }
- break;
- default:
- break;
- }
- }
-
- switch(glp_ios_reason(tree)){
- case GLP_IBINGO:
- Trace("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 = 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;
- }
-}
-
-std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con)
-{
- 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;
-}
-
-ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{
- int br_var = con.branchVariable();
- return getArithVarFromStructural(br_var);
-}
-
-
-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;
- parm.pp_tech = GLP_PP_NONE;
- parm.fp_heur = GLP_ON;
- parm.gmi_cuts = GLP_ON;
- parm.mir_cuts = GLP_ON;
- parm.cov_cuts = GLP_ON;
- parm.cb_func = glpkCallback;
- parm.cb_info = &aux;
- parm.msg_lev = GLP_MSG_OFF;
-
- glp_erase_prob(d_mipProb);
- glp_copy_prob(d_mipProb, d_realProb, GLP_OFF);
-
- int res = glp_intopt(d_mipProb, &parm);
-
- Trace("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl;
-
- switch(res){
- case 0:
- case GLP_ESTOP:
- {
- int status = glp_mip_status(d_mipProb);
- Trace("approx::") << "status " << status << endl;
- switch(status){
- case GLP_OPT:
- case GLP_FEAS:
- d_solvedMIP = true;
- Trace("approx::") << "bingo here!" << endl;
- return MipBingo;
- case GLP_NOFEAS:
- d_solvedMIP = true;
- return MipClosed;
- default:
- if(aux.term == MipBingo){
- d_solvedMIP = true;
- Trace("approx::") << "bingo here?" << endl;
- }
- return aux.term;
- }
- }
- default:
- return MipUnknown;
- }
-}
-
-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();
-
- Trace("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;
- Trace("approx::sumConstraints") << " +("<<bound << "*" << psi <<")";
- if(anyinf != NULL ){
- *anyinf = *anyinf || !bound.infinitesimalIsZero();
- }
- }
- Trace("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);
- Trace("removeFixed") << "remove fixed " << p.first << endl;
- if(p.second != NullConstraint){
- exp.insert(p.second);
- Trace("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(TraceIsOn("guessIsConstructable")){
- if(!g.empty()){
- Trace("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl;
- DenseVector::print(Trace("approx::guessIsConstructable"), g);
- Trace("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){
- Trace("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{
-
- Trace("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl;
-
- const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs;
- const Rational& constructedRhs = d_pad.d_cut.rhs;
- std::unordered_set<ArithVar> visited;
-
- if(constructedLhs.empty()){
- Trace("approx::checkCutOnPad") << "its empty?" <<endl;
- return true;
- }
- if(cut.getKind() != d_pad.d_cutKind) {
- Trace("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(TraceIsOn("approx::checkCutOnPad")){
- Trace("approx::checkCutOnPad") << " didn't find key for " << x << std::endl;
- cut.print(Trace("approx::checkCutOnPad"));
- Trace("approx::checkCutOnPad") << endl;
- d_pad.d_cut.print(Trace("approx::checkCutOnPad"));
- Trace("approx::checkCutOnPad") << endl;
- }
- return true;
- }
-
- const Rational& onConstructed = constructedLhs[x];
-
- Trace("approx::checkCutOnPad") << ind << " " << coeff << " " << endl;
- Trace("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl;
-
- if(!roughlyEqual(coeff, onConstructed.getDouble())){
- Trace("approx::checkCutOnPad") << "coeff failure" << endl;
- return true;
- }
- }
- if(visited.size() != constructedLhs.size()){
- Trace("approx::checkCutOnPad") << "size mismatch" << endl;
- return true;
- }
-
-
- if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){
- Trace("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;
- std::optional<Rational> br_cut_rhs = Rational::fromDouble(br_cut.getRhs());
- if (!br_cut_rhs)
- {
- return true;
- }
-
- rhs = estimateWithCFE(*br_cut_rhs, 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;
-
- std::optional<Rational> delta = estimateWithCFE(mir.delta);
- if (!delta)
- {
- return true;
- }
-
- d_pad.d_failure = (delta->sgn() <= 0);
- if(d_pad.d_failure){ return true; }
-
- Trace("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);
-}
-
-/** Returns true on failure. */
-bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){
- if(ri <= 0) { return true; }
-
- Trace("glpk::loadVB") << "loadVB()" << endl;
-
- ArithVar rowVar = _getArithVar(nid, M, ri);
- ArithVar contVar = _getArithVar(nid, M, j);
- if(rowVar == ARITHVAR_SENTINEL){
- Trace("glpk::loadVB") << "loadVB()"
- << " rowVar is ARITHVAR_SENTINEL " << rowVar << endl;
- return true;
- }
- if(contVar == ARITHVAR_SENTINEL){
- Trace("glpk::loadVB") << "loadVB()"
- << " contVar is ARITHVAR_SENTINEL " << contVar
- << endl;
- return true; }
-
- if(!d_vars.isAuxiliary(rowVar)){
- Trace("glpk::loadVB") << "loadVB()"
- << " rowVar is not auxilliary " << rowVar << endl;
- return true;
- }
- // is integer is correct here
- if(d_vars.isInteger(contVar)){
- Trace("glpk::loadVB") << "loadVB()"
- << " contVar is integer " << contVar << endl;
- return true;
- }
-
- ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar);
- ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar);
-
- if(lb != NullConstraint && ub != NullConstraint){
- Trace("glpk::loadVB") << "loadVB()"
- << " lb and ub are both NULL " << lb << " " << ub
- << endl;
- return true;
- }
-
- ConstraintP rcon = lb == NullConstraint ? ub : lb;
- if(rcon == NullConstraint) {
- Trace("glpk::loadVB") << "loadVB()"
- << " rcon is NULL " << rcon << endl;
- return true;
- }
-
- if(!rcon->getValue().isZero()){
- Trace("glpk::loadVB") << "loadVB()"
- << " rcon value is not 0 " << rcon->getValue()
- << endl;
- return true;
- }
-
- if(!d_vars.hasNode(rowVar)){
- Trace("glpk::loadVB") << "loadVB()"
- << " does not have node " << rowVar << endl;
- return true;
- }
-
- Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar));
- if (p.size() != 2)
- {
- Trace("glpk::loadVB") << "loadVB()"
- << " polynomial is not binary: " << p.getNode()
- << endl;
- return true;
- }
-
- 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)) {
- Trace("glpk::loadVB") << "loadVB()"
- << " does not have a variable for nx1: " << nx1
- << endl;
- return true;
- }
- if(!d_vars.hasArithVar(nx2)) {
- Trace("glpk::loadVB") << "loadVB()"
- << " does not have a variable for nx2 " << nx2
- << endl;
- return true;
- }
- ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2);
-
- Assert(x1 != x2);
- Assert(!c1.isZero());
- Assert(!c2.isZero());
-
- Trace("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;
-
- Trace("glpk::loadVB")
- << " cv " << contVar
- << " cc " << cc
- << " iv " << iv
- << " c2 " << ic << endl;
-
- if(!d_vars.isIntegerInput(iv)){
- Trace("glpk::loadVB") << "loadVB()"
- << " iv is not an integer input variable " << iv
- << endl;
- return true;
- }
- // cc * cv + ic * iv <= 0 or
- // cc * cv + ic * iv <= 0
-
- if(rcon == ub){ // multiply by -1
- cc = -cc; ic = - ic;
- }
- Trace("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;
- Trace("glpk::loadVB") << d << " " << cc.sgn() << endl;
- bool nowUb = cc.sgn() < 0;
- if(wantUb != nowUb) {
- Trace("glpk::loadVB") << "loadVB()"
- << " wantUb is not nowUb " << wantUb << " " << nowUb
- << endl;
-
- return true;
- }
-
- Kind rel = wantUb ? kind::LEQ : kind::GEQ;
-
- tmp = VirtualBound(contVar, rel, d, iv, rcon);
- Trace("glpk::loadVB") << "loadVB()"
- << " was successful" << endl;
- 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){
- Trace("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){
- Trace("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;
- Trace("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){
- Trace("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;
- }
- }
- }
-
- Trace("approx::mir") << " for: " << j << ", " << v;
- Trace("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " ";
- Trace("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:
- Trace("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; }
- std::optional<Rational> c = estimateWithCFE(coeff);
- if (!c)
- {
- return true;
- }
-
- if (lhs.isKey(x))
- {
- lhs.get(x) -= *c;
- }
- else
- {
- lhs.set(x, -(*c));
- }
- }
-
- Trace("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl;
- if(TraceIsOn("approx::mir")) { DenseVector::print(Trace("approx::mir"), lhs); }
- removeAuxillaryVariables(d_vars, lhs);
- Trace("approx::mir") << "end loadRowSumIntoAgg() 1" << endl;
-
- if(TraceIsOn("approx::mir")){
- Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
- DenseVector::print(Trace("approx::mir"), lhs);
- Trace("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);
- std::optional<Rational> c = estimateWithCFE(coeff);
- if (!c)
- {
- return true;
- }
- Assert(!lhs.isKey(x));
- lhs.set(x, *c);
- }
-
- if(TraceIsOn("approx::mir")){
- Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
- DenseVector::print(Trace("approx::mir"), lhs);
- Trace("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;
-
- Trace("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)){
- Trace("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);
- }
- }
-
- Trace("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++;
- }
- }
-
- Trace("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(TraceIsOn("gaussianElimConstructTableRow")){
- Trace("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- vec.print(Trace("gaussianElimConstructTableRow"));
- Trace("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){
- Trace("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
- return true;
- }
- onrow.insert(var);
- }
-
-
- Trace("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
- Matrix<Rational> A;
- A.increaseSizeTo(d_vars.getNumberOfVariables());
- std::vector< std::pair<RowIndex, ArithVar> > rows;
- // load the rows for auxiliary variables into A
- for (ArithVar v : onrow)
- {
- 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));
- }
- }
- Trace("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();
- Trace("gaussianElimConstructTableRow")
- << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl;
- A.rowPlusRowTimesConstant(rid, prevRow, cp);
- }
- }
- if(TraceIsOn("gaussianElimConstructTableRow")){
- A.printMatrix(Trace("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()){
- Trace("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- return true;
- }else{
- // 0 = q * s + sum c_i * x_i
- Rational mult = -(q.inverse());
- Trace("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl;
- Trace("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;
- }
- }
- Trace("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
- if(rows.empty()) {
- Trace("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){
- Trace("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());
- }
- Trace("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- if(!tab.isKey(basic)){
- Trace("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- return true;
- }
- if(tab[basic] != Rational(-1)){
- Trace("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- return true;
- }
-
- tab.remove(basic);
- Trace("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
-
- if(vec.len < 0 ){
- Trace("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- return true;
- }
- if(tab.size() != ((unsigned)vec.len) ) {
- Trace("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() << " " << vec.len << endl;
- return true;
- }
-
- Trace("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)){
- Trace("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
- return true;
- }
-
- double est = tab[var].getDouble();
-
- if(!ApproximateSimplex::roughlyEqual(coeff, est)){
- Trace("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
- << " boink on " << ind << " " << var << " " << est <<endl;
- return true;
- }
- Trace("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl;
- }
-
- Trace("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 << i+1;
- Trace("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(TraceIsOn("guessCoefficientsConstructTableRow")){
- Trace("guessCoefficientsConstructTableRow") << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl;
- vec.print(Trace("guessCoefficientsConstructTableRow"));
- Trace("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){
- Trace("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
- return true;
- }
- Trace("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl;
-
- std::optional<Rational> cfe = estimateWithCFE(coeff, D);
- if (!cfe)
- {
- return true;
- }
- tab.set(var, *cfe);
- Trace("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl;
- }
- if(!guessIsConstructable(tab)){
- Trace("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();
- Trace("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
- Trace("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(TraceIsOn("approx::gmi")){
- Trace("approx::gmi") << "pre removeSlackVariables";
- d_pad.d_cut.print(Trace("approx::gmi"));
- Trace("approx::gmi") << endl;
- }
- removeAuxillaryVariables(d_vars, cut);
-
- if(TraceIsOn("approx::gmi")){
- Trace("approx::gmi") << "post removeAuxillaryVariables";
- d_pad.d_cut.print(Trace("approx::gmi"));
- Trace("approx::gmi") << endl;
- }
- removeFixed(d_vars, d_pad.d_cut, explanation);
-
- if(TraceIsOn("approx::gmi")){
- Trace("approx::gmi") << "post removeFixed";
- d_pad.d_cut.print(Trace("approx::gmi"));
- Trace("approx::gmi") << endl;
- }
- return false;
-}
-
-void ApproxGLPK::tryCut(int nid, CutInfo& cut)
-{
- 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{
- Trace("approx") << "failure " << cut.getKlass() << endl;
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-#endif /*#ifdef CVC5_USE_GLPK */
-/* End GPLK implementation. */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-#include <vector>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/delta_rational.h"
-#include "util/dense_map.h"
-#include "util/rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-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:
- ApproximateStatistics();
-
- IntStat d_branchMaxDepth;
- IntStat d_branchesMaxOnAVar;
-
- TimerStat d_gaussianElimConstructTime;
- IntStat d_gaussianElimConstruct;
- AverageStat d_averageGuesses;
-};
-
-
-class NodeLog;
-class TreeLog;
-class ArithVariables;
-class CutInfo;
-
-class ApproximateSimplex{
- public:
- static bool enabled();
-
- /**
- * 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, 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.*/
- struct Solution {
- DenseSet newBasis;
- DenseMap<DeltaRational> newValues;
- Solution() : newBasis(), newValues(){}
- };
-
- virtual ArithVar getBranchVar(const NodeLog& nl) const = 0;
-
- /** Sets a maximization criteria for the approximate solver.*/
- virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
-
- virtual ArithRatPairVec heuristicOptCoeffs() const = 0;
-
- virtual LinResult solveRelaxation() = 0;
- virtual Solution extractRelaxation() const = 0;
-
- virtual MipResult solveMIP(bool activelyLog) = 0;
-
- virtual Solution extractMIP() const = 0;
-
- virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) = 0;
-
- virtual void tryCut(int nid, CutInfo& cut) = 0;
-
- /** UTILITIES FOR DEALING WITH ESTIMATES */
-
- static constexpr double SMALL_FIXED_DELTA = .000000001;
- static constexpr double TOLERENCE = 1 + .000000001;
-
- /** Returns true if two doubles are roughly equal based on TOLERENCE and SMALL_FIXED_DELTA.*/
- static bool roughlyEqual(double a, double b);
-
- /**
- * Estimates a double as a Rational using continued fraction expansion that
- * cuts off the estimate once the value is approximately zero.
- * This is designed for removing rounding artifacts.
- */
- static std::optional<Rational> estimateWithCFE(double d);
- static std::optional<Rational> estimateWithCFE(double d, const Integer& D);
-
- /**
- * Converts a rational to a continued fraction expansion representation
- * using a maximum number of expansions equal to depth as long as the expression
- * is not roughlyEqual() to 0.
- */
- static std::vector<Integer> rationalToCfe(const Rational& q, int depth);
-
- /** Converts a continued fraction expansion representation to a rational. */
- static Rational cfeToRational(const std::vector<Integer>& exp);
-
- /** Estimates a rational as a continued fraction expansion.*/
- static Rational estimateWithCFE(const Rational& q, const Integer& K);
-
- virtual double sumInfeasibilities(bool mip) const = 0;
-
- protected:
- const ArithVariables& d_vars;
- TreeLog& d_log;
- ApproximateStatistics& d_stats;
-
- /* the maximum pivots allowed in a query. */
- int d_pivotLimit;
-
- /* maximum branches allowed on a variable */
- int d_branchLimit;
-
- /* maxmimum branching depth allowed.*/
- int d_maxDepth;
-
- /* Default denominator for diophatine approximation, 2^{26} .*/
- static constexpr uint64_t s_defaultMaxDenom = (1 << 26);
-};/* class ApproximateSimplex */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
#include "preprocessing/util/ite_utilities.h"
#include "smt/env.h"
#include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
#include "theory/rewriter.h"
#include "theory/substitutions.h"
#include "theory/theory_model.h"
default:
{
TypeNode tn = n.getType();
- if (tn.isRealOrInt() && Polynomial::isMember(n))
+ if (tn.isRealOrInt() && linear::Polynomial::isMember(n))
{
Node newn = Node::null();
if(!d_contains.containsTermITE(n)){
}else if(n.getNumChildren() > 0){
newn = applyReduceVariablesInItes(n);
newn = rewrite(newn);
- Assert(Polynomial::isMember(newn));
+ Assert(linear::Polynomial::isMember(newn));
}else{
newn = n;
}
NodeManager* nm = NodeManager::currentNM();
- Polynomial p = Polynomial::parsePolynomial(newn);
+ linear::Polynomial p = linear::Polynomial::parsePolynomial(newn);
if(p.isConstant()){
d_constants[n] = newn;
d_varParts[n] = nm->mkConstRealOrInt(tn, Rational(0));
d_reduceVar[n] = p.getNode();
return p.getNode();
}else{
- Monomial mc = p.getHead();
+ linear::Monomial mc = p.getHead();
d_constants[n] = mc.getConstant().getNode();
d_varParts[n] = p.getTail().getNode();
d_reduceVar[n] = newn;
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;
+ Assert(linear::Polynomial::isMember(sel));
+ Assert(linear::Polynomial::isMember(useForCmpL));
+ Assert(linear::Polynomial::isMember(useForCmpR));
+ linear::Polynomial lside = linear::Polynomial::parsePolynomial( useForCmpL );
+ linear::Polynomial rside = linear::Polynomial::parsePolynomial( useForCmpR );
+ linear::Polynomial diff = lside-rside;
Trace("arith::ite") << "diff: " << diff.getNode() << endl;
if(diff.isConstant()){
#include "smt/logic_exception.h"
#include "theory/arith/arith_msum.h"
#include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
#include "theory/arith/operator_elim.h"
#include "theory/arith/rewriter/addition.h"
#include "theory/arith/rewriter/node_utils.h"
* Rewriter for the theory of arithmetic.
*
* This rewrites to the normal form for arithmetic.
- * See theory/arith/normal_form.h for more information.
*/
#include "cvc5_private.h"
#include "theory/arith/arith_state.h"
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
namespace cvc5::internal {
namespace theory {
return d_parent->anyConflict() || d_conflict;
}
-void ArithState::setParent(TheoryArithPrivate* p) { d_parent = p; }
+void ArithState::setParent(linear::TheoryArithPrivate* p) { d_parent = p; }
} // namespace arith
} // namespace theory
namespace theory {
namespace arith {
+namespace linear {
class TheoryArithPrivate;
+}
/**
* The arithmetic state.
/** Are we currently in conflict? */
bool isInConflict() const override;
/** Set parent */
- void setParent(TheoryArithPrivate* p);
+ void setParent(linear::TheoryArithPrivate* p);
private:
/** reference to parent */
- TheoryArithPrivate* d_parent;
+ linear::TheoryArithPrivate* d_parent;
};
} // namespace arith
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Dejan Jovanovic, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include <vector>
-
-#include "base/output.h"
-#include "expr/node_algorithm.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/normal_form.h"
-#include "theory/rewriter.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithStaticLearner::ArithStaticLearner(context::Context* userContext)
- : d_minMap(userContext), d_maxMap(userContext), d_statistics()
-{
-}
-
-ArithStaticLearner::~ArithStaticLearner(){
-}
-
-ArithStaticLearner::Statistics::Statistics()
- : d_iteMinMaxApplications(smtStatisticsRegistry().registerInt(
- "theory::arith::iteMinMaxApplications")),
- d_iteConstantApplications(smtStatisticsRegistry().registerInt(
- "theory::arith::iteConstantApplications"))
-{
-}
-
-void ArithStaticLearner::staticLearning(TNode n, NodeBuilder& learned)
-{
- vector<TNode> workList;
- workList.push_back(n);
- TNodeSet processed;
-
- //Contains an underapproximation of nodes that must hold.
- TNodeSet defTrue;
-
- defTrue.insert(n);
-
- while(!workList.empty()) {
- n = workList.back();
-
- bool unprocessedChildren = false;
- for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
- if(processed.find(*i) == processed.end()) {
- // unprocessed child
- workList.push_back(*i);
- unprocessedChildren = true;
- }
- }
- if(n.getKind() == AND && defTrue.find(n) != defTrue.end() ){
- for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
- defTrue.insert(*i);
- }
- }
-
- if(unprocessedChildren) {
- continue;
- }
-
- workList.pop_back();
- // has node n been processed in the meantime ?
- if(processed.find(n) != processed.end()) {
- continue;
- }
- processed.insert(n);
-
- process(n,learned, defTrue);
-
- }
-}
-
-void ArithStaticLearner::process(TNode n,
- NodeBuilder& learned,
- const TNodeSet& defTrue)
-{
- Trace("arith::static") << "===================== looking at " << n << endl;
-
- switch(n.getKind()){
- case ITE:
- if (expr::hasBoundVar(n))
- {
- // Unsafe with non-ground ITEs; do nothing
- Trace("arith::static")
- << "(potentially) non-ground ITE, ignoring..." << endl;
- break;
- }
-
- if(n[0].getKind() != EQUAL &&
- isRelationOperator(n[0].getKind()) ){
- iteMinMax(n, learned);
- }
-
- if((d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) ||
- (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end())) {
- iteConstant(n, learned);
- }
- break;
-
- case CONST_RATIONAL:
- case CONST_INTEGER:
- // Mark constants as minmax
- d_minMap.insert(n, n.getConst<Rational>());
- d_maxMap.insert(n, n.getConst<Rational>());
- break;
- default: // Do nothing
- break;
- }
-}
-
-void ArithStaticLearner::iteMinMax(TNode n, NodeBuilder& learned)
-{
- Assert(n.getKind() == kind::ITE);
- Assert(n[0].getKind() != EQUAL);
- Assert(isRelationOperator(n[0].getKind()));
-
- TNode c = n[0];
- Kind k = oldSimplifiedKind(c);
- TNode t = n[1];
- TNode e = n[2];
- TNode cleft = (c.getKind() == NOT) ? c[0][0] : c[0];
- TNode cright = (c.getKind() == NOT) ? c[0][1] : c[1];
-
- if((t == cright) && (e == cleft)){
- TNode tmp = t;
- t = e;
- e = tmp;
- k = reverseRelationKind(k);
- }
- //(ite (< x y) x y)
- //(ite (x < y) x y)
- //(ite (x - y < 0) x y)
- // ----------------
- // (ite (x - y < -c) )
-
- if(t == cleft && e == cright){
- // t == cleft && e == cright
- Assert(t == cleft);
- Assert(e == cright);
- switch(k){
- case LT: // (ite (< x y) x y)
- case LEQ: { // (ite (<= x y) x y)
- Node nLeqX = NodeBuilder(LEQ) << n << t;
- Node nLeqY = NodeBuilder(LEQ) << n << e;
- Trace("arith::static") << n << "is a min =>" << nLeqX << nLeqY << endl;
- learned << nLeqX << nLeqY;
- ++(d_statistics.d_iteMinMaxApplications);
- break;
- }
- case GT: // (ite (> x y) x y)
- case GEQ: { // (ite (>= x y) x y)
- Node nGeqX = NodeBuilder(GEQ) << n << t;
- Node nGeqY = NodeBuilder(GEQ) << n << e;
- Trace("arith::static") << n << "is a max =>" << nGeqX << nGeqY << endl;
- learned << nGeqX << nGeqY;
- ++(d_statistics.d_iteMinMaxApplications);
- break;
- }
- default: Unreachable();
- }
- }
-}
-
-void ArithStaticLearner::iteConstant(TNode n, NodeBuilder& learned)
-{
- Assert(n.getKind() == ITE);
-
- Trace("arith::static") << "iteConstant(" << n << ")" << endl;
-
- if (d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) {
- const DeltaRational& first = d_minMap[n[1]];
- const DeltaRational& second = d_minMap[n[2]];
- DeltaRational min = std::min(first, second);
- CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n);
- if (minFind == d_minMap.end() || (*minFind).second < min) {
- d_minMap.insert(n, min);
- NodeManager* nm = NodeManager::currentNM();
- Node nGeqMin = nm->mkNode(
- min.getInfinitesimalPart() == 0 ? kind::GEQ : kind::GT,
- n,
- nm->mkConstRealOrInt(n.getType(), min.getNoninfinitesimalPart()));
- learned << nGeqMin;
- Trace("arith::static") << n << " iteConstant" << nGeqMin << endl;
- ++(d_statistics.d_iteConstantApplications);
- }
- }
-
- if (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end()) {
- const DeltaRational& first = d_maxMap[n[1]];
- const DeltaRational& second = d_maxMap[n[2]];
- DeltaRational max = std::max(first, second);
- CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n);
- if (maxFind == d_maxMap.end() || (*maxFind).second > max) {
- d_maxMap.insert(n, max);
- NodeManager* nm = NodeManager::currentNM();
- Node nLeqMax = nm->mkNode(
- max.getInfinitesimalPart() == 0 ? kind::LEQ : kind::LT,
- n,
- nm->mkConstRealOrInt(n.getType(), max.getNoninfinitesimalPart()));
- learned << nLeqMax;
- Trace("arith::static") << n << " iteConstant" << nLeqMax << endl;
- ++(d_statistics.d_iteConstantApplications);
- }
- }
-}
-
-std::set<Node> listToSet(TNode l){
- std::set<Node> ret;
- while(l.getKind() == OR){
- Assert(l.getNumChildren() == 2);
- ret.insert(l[0]);
- l = l[1];
- }
- return ret;
-}
-
-void ArithStaticLearner::addBound(TNode n) {
-
- CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n[0]);
- CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n[0]);
-
- Rational constant = n[1].getConst<Rational>();
- DeltaRational bound = constant;
-
- switch(Kind k = n.getKind()) {
- case kind::LT: bound = DeltaRational(constant, -1); CVC5_FALLTHROUGH;
- case kind::LEQ:
- if (maxFind == d_maxMap.end() || (*maxFind).second > bound)
- {
- d_maxMap.insert(n[0], bound);
- Trace("arith::static") << "adding bound " << n << endl;
- }
- break;
- case kind::GT: bound = DeltaRational(constant, 1); CVC5_FALLTHROUGH;
- case kind::GEQ:
- if (minFind == d_minMap.end() || (*minFind).second < bound)
- {
- d_minMap.insert(n[0], bound);
- Trace("arith::static") << "adding bound " << n << endl;
- }
- break;
- default: Unhandled() << k; break;
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andres Noetzli, Dejan Jovanovic
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
-#define CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
-
-#include "context/cdhashmap.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/delta_rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ArithStaticLearner {
-private:
-
- /**
- * Map from a node to it's minimum and maximum.
- */
- typedef context::CDHashMap<Node, DeltaRational> CDNodeToMinMaxMap;
- CDNodeToMinMaxMap d_minMap;
- CDNodeToMinMaxMap d_maxMap;
-
-public:
- ArithStaticLearner(context::Context* userContext);
- ~ArithStaticLearner();
- void staticLearning(TNode n, NodeBuilder& learned);
-
- void addBound(TNode n);
-
-private:
- void process(TNode n, NodeBuilder& learned, const TNodeSet& defTrue);
-
- void iteMinMax(TNode n, NodeBuilder& learned);
- void iteConstant(TNode n, NodeBuilder& learned);
-
- /**
- * These fields are designed to be accessible to ArithStaticLearner methods.
- */
- class Statistics
- {
- public:
- IntStat d_iteMinMaxApplications;
- IntStat d_iteConstantApplications;
-
- Statistics();
- };
-
- Statistics d_statistics;
-
-};/* class ArithStaticLearner */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H */
#include "context/cdhashset.h"
#include "expr/node.h"
#include "expr/subs.h"
-#include "theory/arith/arithvar.h"
#include "util/dense_map.h"
#include "util/integer.h"
#include "util/rational.h"
typedef std::unordered_set<TNode> TNodeSet;
typedef context::CDHashSet<Node> CDNodeSet;
-//Maps from Nodes -> ArithVars, and vice versa
-typedef std::unordered_map<Node, ArithVar> NodeToArithVarMap;
-typedef DenseMap<Node> ArithVarToNodeMap;
-
inline Node mkRationalNode(const Rational& q){
return NodeManager::currentNM()->mkConst(kind::CONST_RATIONAL, q);
}
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/arithvar.h"
-#include <limits>
-#include <set>
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-const ArithVar ARITHVAR_SENTINEL = std::numeric_limits<ArithVar>::max();
-
-bool debugIsASet(const std::vector<ArithVar>& variables){
- std::set<ArithVar> asSet(variables.begin(), variables.end());
- return asSet.size() == variables.size();
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Defines ArithVar which is the internal representation of variables in
- * arithmetic
- *
- * This defines ArithVar which is the internal representation of variables in
- * arithmetic. This is a typedef from Index to ArithVar.
- * This file also provides utilities for ArithVars.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <vector>
-
-#include "util/index.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-typedef Index ArithVar;
-extern const ArithVar ARITHVAR_SENTINEL;
-
-typedef std::vector<ArithVar> ArithVarVec;
-typedef std::pair<ArithVar, Rational> ArithRatPair;
-typedef std::vector< ArithRatPair > ArithRatPairVec;
-
-extern bool debugIsASet(const ArithVarVec& variables);
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
-#define CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
-
-#include "theory/arith/arithvar.h"
-#include "context/context.h"
-#include "context/cdlist.h"
-#include "context/cdhashmap.h"
-#include "context/cdo.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ArithVarNodeMap {
-private:
- /**
- * Bidirectional map between Nodes and ArithVars.
- */
- NodeToArithVarMap d_nodeToArithVarMap;
- ArithVarToNodeMap d_arithVarToNodeMap;
-
-public:
-
- typedef ArithVarToNodeMap::const_iterator var_iterator;
-
- ArithVarNodeMap() {}
-
- inline bool hasArithVar(TNode x) const {
- return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
- }
-
- inline bool hasNode(ArithVar a) const {
- return d_arithVarToNodeMap.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;
- }
-
- inline Node asNode(ArithVar a) const{
- Assert(hasNode(a));
- return d_arithVarToNodeMap[a];
- }
-
- inline void setArithVar(TNode x, ArithVar a){
- Assert(!hasArithVar(x));
- Assert(!d_arithVarToNodeMap.isKey(a));
- d_arithVarToNodeMap.set(a, x);
- d_nodeToArithVarMap[x] = a;
- }
-
- inline void remove(ArithVar x){
- Assert(hasNode(x));
- Node node = asNode(x);
-
- d_nodeToArithVarMap.erase(d_nodeToArithVarMap.find(node));
- d_arithVarToNodeMap.remove(x);
- }
-
- var_iterator var_begin() const {
- return d_arithVarToNodeMap.begin();
- }
- var_iterator var_end() const {
- return d_arithVarToNodeMap.end();
- }
-
-};/* class ArithVarNodeMap */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/attempt_solution_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/tableau.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-AttemptSolutionSDP::AttemptSolutionSDP(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc)
- : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
- d_statistics()
-{ }
-
-AttemptSolutionSDP::Statistics::Statistics()
- : d_searchTime(smtStatisticsRegistry().registerTimer(
- "theory::arith::attempt::searchTime")),
- d_queueTime(smtStatisticsRegistry().registerTimer(
- "theory::arith::attempt::queueTime")),
- d_conflicts(smtStatisticsRegistry().registerInt(
- "theory::arith::attempt::conflicts"))
-{
-}
-
-bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{
- return nv[v] == d_variables.getAssignment(v);
-}
-
-Result::Status AttemptSolutionSDP::attempt(
- const ApproximateSimplex::Solution& sol)
-{
- const DenseSet& newBasis = sol.newBasis;
- const DenseMap<DeltaRational>& newValues = sol.newValues;
-
- DenseSet needsToBeAdded;
- for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
- ArithVar b = *i;
- if(!d_tableau.isBasic(b)){
- needsToBeAdded.add(b);
- }
- }
- DenseMap<DeltaRational>::const_iterator nvi = newValues.begin(), nvi_end = newValues.end();
- for(; nvi != nvi_end; ++nvi){
- ArithVar currentlyNb = *nvi;
- if(!d_tableau.isBasic(currentlyNb)){
- if(!matchesNewValue(newValues, currentlyNb)){
- const DeltaRational& newValue = newValues[currentlyNb];
- Trace("arith::updateMany")
- << "updateMany:" << currentlyNb << " "
- << d_variables.getAssignment(currentlyNb) << " to "<< newValue << endl;
- d_linEq.update(currentlyNb, newValue);
- Assert(d_variables.assignmentIsConsistent(currentlyNb));
- }
- }
- }
- d_errorSet.reduceToSignals();
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-
- if(processSignals()){
- Trace("arith::findModel") << "attemptSolution() early conflict" << endl;
- d_conflictVariables.purge();
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Trace("arith::findModel") << "attemptSolution() fixed itself" << endl;
- return Result::SAT;
- }
-
- while(!needsToBeAdded.empty() && !d_errorSet.errorEmpty()){
- ArithVar toRemove = ARITHVAR_SENTINEL;
- ArithVar toAdd = ARITHVAR_SENTINEL;
- DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
- for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
- ArithVar v = *i;
-
- Tableau::ColIterator colIter = d_tableau.colIterator(v);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == v);
- ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
- if(!newBasis.isMember(b)){
- toAdd = v;
-
- bool favorBOverToRemove =
- (toRemove == ARITHVAR_SENTINEL) ||
- (matchesNewValue(newValues, toRemove) && !matchesNewValue(newValues, b)) ||
- (d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b));
-
- if(favorBOverToRemove){
- toRemove = b;
- }
- }
- }
- }
- Assert(toRemove != ARITHVAR_SENTINEL);
- Assert(toAdd != ARITHVAR_SENTINEL);
-
- Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
-
- d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]);
-
- Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
- needsToBeAdded.remove(toAdd);
-
- bool conflict = processSignals();
- if(conflict){
- d_errorSet.reduceToSignals();
- d_conflictVariables.purge();
-
- return Result::UNSAT;
- }
- }
- Assert(d_conflictVariables.empty());
-
- if(d_errorSet.errorEmpty()){
- return Result::SAT;
- }else{
- d_errorSet.reduceToSignals();
- return Result::UNKNOWN;
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- * by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- * These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/simplex.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class AttemptSolutionSDP : public SimplexDecisionProcedure {
-public:
- AttemptSolutionSDP(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc);
-
- Result::Status attempt(const ApproximateSimplex::Solution& sol);
-
- Result::Status findModel(bool exactResult) override { Unreachable(); }
-
-private:
- bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const;
-
- bool processSignals()
- {
- TimerStat& timer = d_statistics.d_queueTime;
- IntStat& conflictStat = d_statistics.d_conflicts;
- return standardProcessSignals(timer, conflictStat);
- }
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- TimerStat d_searchTime;
- TimerStat d_queueTime;
- IntStat d_conflicts;
-
- Statistics();
- } d_statistics;
-};/* class AttemptSolutionSDP */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Clark Barrett
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-#pragma once
-
-#include "base/check.h"
-#include "theory/arith/arithvar.h"
-#include "util/dense_map.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class BoundCounts {
-private:
- uint32_t d_lowerBoundCount;
- uint32_t d_upperBoundCount;
-
-public:
- BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {}
- BoundCounts(uint32_t lbs, uint32_t ubs)
- : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {}
-
- bool operator==(BoundCounts bc) const {
- return d_lowerBoundCount == bc.d_lowerBoundCount
- && d_upperBoundCount == bc.d_upperBoundCount;
- }
- bool operator!=(BoundCounts bc) const {
- return d_lowerBoundCount != bc.d_lowerBoundCount
- || d_upperBoundCount != bc.d_upperBoundCount;
- }
- /** This is not a total order! */
- bool operator>=(BoundCounts bc) const {
- return d_lowerBoundCount >= bc.d_lowerBoundCount &&
- d_upperBoundCount >= bc.d_upperBoundCount;
- }
-
- inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; }
- inline uint32_t lowerBoundCount() const{
- return d_lowerBoundCount;
- }
- inline uint32_t upperBoundCount() const{
- return d_upperBoundCount;
- }
-
- inline BoundCounts operator+(BoundCounts bc) const{
- return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount,
- d_upperBoundCount + bc.d_upperBoundCount);
- }
-
- inline BoundCounts operator-(BoundCounts bc) const {
- Assert(*this >= bc);
- return BoundCounts(d_lowerBoundCount - bc.d_lowerBoundCount,
- d_upperBoundCount - bc.d_upperBoundCount);
- }
-
-
- inline BoundCounts& operator+=(BoundCounts bc) {
- d_upperBoundCount += bc.d_upperBoundCount;
- d_lowerBoundCount += bc.d_lowerBoundCount;
- return *this;
- }
-
- inline BoundCounts& operator-=(BoundCounts bc) {
- Assert(d_lowerBoundCount >= bc.d_lowerBoundCount);
- Assert(d_upperBoundCount >= bc.d_upperBoundCount);
- d_upperBoundCount -= bc.d_upperBoundCount;
- d_lowerBoundCount -= bc.d_lowerBoundCount;
-
- return *this;
- }
-
- /** Based on the sign coefficient a variable is multiplied by,
- * the effects the bound counts either has no effect (sgn == 0),
- * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
- */
- inline BoundCounts multiplyBySgn(int sgn) const{
- if(sgn > 0){
- return *this;
- }else if(sgn == 0){
- return BoundCounts(0,0);
- }else{
- return BoundCounts(d_upperBoundCount, d_lowerBoundCount);
- }
- }
-
- inline void addInChange(int sgn, BoundCounts before, BoundCounts after){
- if(before == after){
- return;
- }else if(sgn < 0){
- Assert(d_upperBoundCount >= before.d_lowerBoundCount);
- Assert(d_lowerBoundCount >= before.d_upperBoundCount);
- d_upperBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
- d_lowerBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
- }else if(sgn > 0){
- Assert(d_upperBoundCount >= before.d_upperBoundCount);
- Assert(d_lowerBoundCount >= before.d_lowerBoundCount);
- d_upperBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
- d_lowerBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
- }
- }
-
- inline void addInSgn(BoundCounts bc, int before, int after){
- Assert(before != after);
- Assert(!bc.isZero());
-
- if(before < 0){
- d_upperBoundCount -= bc.d_lowerBoundCount;
- d_lowerBoundCount -= bc.d_upperBoundCount;
- }else if(before > 0){
- d_upperBoundCount -= bc.d_upperBoundCount;
- d_lowerBoundCount -= bc.d_lowerBoundCount;
- }
- if(after < 0){
- d_upperBoundCount += bc.d_lowerBoundCount;
- d_lowerBoundCount += bc.d_upperBoundCount;
- }else if(after > 0){
- d_upperBoundCount += bc.d_upperBoundCount;
- d_lowerBoundCount += bc.d_lowerBoundCount;
- }
- }
-};
-
-class BoundsInfo {
-private:
-
- /**
- * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
- *
- * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
- * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
- */
- BoundCounts d_atBounds;
-
- /** This is for counting how many upper and lower bounds a row has. */
- BoundCounts d_hasBounds;
-
-public:
- BoundsInfo() : d_atBounds(), d_hasBounds() {}
- BoundsInfo(BoundCounts atBounds, BoundCounts hasBounds)
- : d_atBounds(atBounds), d_hasBounds(hasBounds) {}
-
- BoundCounts atBounds() const { return d_atBounds; }
- BoundCounts hasBounds() const { return d_hasBounds; }
-
- /** This corresponds to adding in another variable to the row. */
- inline BoundsInfo operator+(const BoundsInfo& bc) const{
- return BoundsInfo(d_atBounds + bc.d_atBounds,
- d_hasBounds + bc.d_hasBounds);
- }
- /** This corresponds to removing a variable from the row. */
- inline BoundsInfo operator-(const BoundsInfo& bc) const {
- Assert(*this >= bc);
- return BoundsInfo(d_atBounds - bc.d_atBounds,
- d_hasBounds - bc.d_hasBounds);
- }
-
- inline BoundsInfo& operator+=(const BoundsInfo& bc) {
- d_atBounds += bc.d_atBounds;
- d_hasBounds += bc.d_hasBounds;
- return (*this);
- }
-
- /** Based on the sign coefficient a variable is multiplied by,
- * the effects the bound counts either has no effect (sgn == 0),
- * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
- */
- inline BoundsInfo multiplyBySgn(int sgn) const{
- return BoundsInfo(d_atBounds.multiplyBySgn(sgn), d_hasBounds.multiplyBySgn(sgn));
- }
-
- bool operator==(const BoundsInfo& other) const{
- return d_atBounds == other.d_atBounds && d_hasBounds == other.d_hasBounds;
- }
- bool operator!=(const BoundsInfo& other) const{
- return !(*this == other);
- }
- /** This is not a total order! */
- bool operator>=(const BoundsInfo& other) const{
- return d_atBounds >= other.d_atBounds && d_hasBounds >= other.d_hasBounds;
- }
- void addInChange(int sgn, const BoundsInfo& before, const BoundsInfo& after){
- addInAtBoundChange(sgn, before.d_atBounds, after.d_atBounds);
- addInHasBoundChange(sgn, before.d_hasBounds, after.d_hasBounds);
- }
- void addInAtBoundChange(int sgn, BoundCounts before, BoundCounts after){
- d_atBounds.addInChange(sgn, before, after);
- }
- void addInHasBoundChange(int sgn, BoundCounts before, BoundCounts after){
- d_hasBounds.addInChange(sgn, before, after);
- }
-
- inline void addInSgn(const BoundsInfo& bc, int before, int after){
- if(!bc.d_atBounds.isZero()){ d_atBounds.addInSgn(bc.d_atBounds, before, after);}
- if(!bc.d_hasBounds.isZero()){ d_hasBounds.addInSgn(bc.d_hasBounds, before, after);}
- }
-};
-
-/** This is intended to map each row to its relevant bound information. */
-typedef DenseMap<BoundsInfo> BoundInfoMap;
-
-inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){
- os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]";
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){
- os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]";
- return os;
-}
-class BoundUpdateCallback {
-public:
- virtual ~BoundUpdateCallback() {}
- virtual void operator()(ArithVar v, const BoundsInfo& up) = 0;
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
#include "theory/arith/bound_inference.h"
#include "smt/env.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
#include "theory/rewriter.h"
using namespace cvc5::internal::kind;
return false;
}
// Parse the node as a comparison
- auto comp = Comparison::parseNormalForm(tmp);
+ auto comp = linear::Comparison::parseNormalForm(tmp);
auto dec = comp.decompose(true);
if (onlyVariables && !std::get<0>(dec).isVariable())
{
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/callbacks.h"
-
-#include "expr/skolem_manager.h"
-#include "proof/proof_node.h"
-#include "theory/arith/theory_arith_private.h"
-
-namespace cvc5::internal {
-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)){
- d_arith.setupAtom(atom);
- }
-}
-
-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(){
- NodeManager* nm = NodeManager::currentNM();
- SkolemManager* sm = nm->getSkolemManager();
- Node skolem = sm->mkDummySkolem("tmpVar", nm->realType());
- 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);
-}
-
-RaiseConflict::RaiseConflict(TheoryArithPrivate& ta)
- : d_ta(ta)
-{}
-
-void RaiseConflict::raiseConflict(ConstraintCP c, InferenceId id) const{
- Assert(c->inConflict());
- d_ta.raiseConflict(c, id);
-}
-
-FarkasConflictBuilder::FarkasConflictBuilder(bool produceProofs)
- : d_farkas(),
- d_constraints(),
- d_consequent(NullConstraint),
- d_consequentSet(false),
- d_produceProofs(produceProofs)
-{
- reset();
-}
-
-bool FarkasConflictBuilder::underConstruction() const{
- return d_consequent != NullConstraint;
-}
-
-bool FarkasConflictBuilder::consequentIsSet() const{
- return d_consequentSet;
-}
-
-void FarkasConflictBuilder::reset(){
- d_consequent = NullConstraint;
- d_constraints.clear();
- d_consequentSet = false;
- if (d_produceProofs)
- {
- d_farkas.clear();
- }
- Assert(!underConstruction());
-}
-
-/* Adds a constraint to the constraint under construction. */
-void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){
- Assert(
- !d_produceProofs
- || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
- || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
- Assert(d_produceProofs || d_farkas.empty());
- Assert(c->isTrue());
-
- if(d_consequent == NullConstraint){
- d_consequent = c;
- } else {
- d_constraints.push_back(c);
- }
- if (d_produceProofs)
- {
- d_farkas.push_back(fc);
- }
- Assert(!d_produceProofs || d_constraints.size() + 1 == d_farkas.size());
- Assert(d_produceProofs || d_farkas.empty());
-}
-
-void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult){
- Assert(!mult.isZero());
- if (d_produceProofs && !mult.isOne())
- {
- Rational prod = fc * mult;
- addConstraint(c, prod);
- }
- else
- {
- addConstraint(c, fc);
- }
-}
-
-void FarkasConflictBuilder::makeLastConsequent(){
- Assert(!d_consequentSet);
- Assert(underConstruction());
-
- if(d_constraints.empty()){
- // no-op
- d_consequentSet = true;
- } else {
- Assert(d_consequent != NullConstraint);
- ConstraintCP last = d_constraints.back();
- d_constraints.back() = d_consequent;
- d_consequent = last;
- if (d_produceProofs)
- {
- std::swap(d_farkas.front(), d_farkas.back());
- }
- d_consequentSet = true;
- }
-
- Assert(!d_consequent->negationHasProof());
- Assert(d_consequentSet);
-}
-
-/* Turns the vector under construction into a conflict */
-ConstraintCP FarkasConflictBuilder::commitConflict(){
- Assert(underConstruction());
- Assert(!d_constraints.empty());
- Assert(
- !d_produceProofs
- || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
- || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
- Assert(d_produceProofs || d_farkas.empty());
- Assert(d_consequentSet);
-
- ConstraintP not_c = d_consequent->getNegation();
- RationalVectorCP coeffs = d_produceProofs ? &d_farkas : nullptr;
- not_c->impliedByFarkas(d_constraints, coeffs, true );
-
- reset();
- Assert(!underConstruction());
- Assert(not_c->inConflict());
- Assert(!d_consequentSet);
- return not_c;
-}
-
-RaiseEqualityEngineConflict::RaiseEqualityEngineConflict(TheoryArithPrivate& ta)
- : d_ta(ta)
-{}
-
-/* If you are not an equality engine, don't use this! */
-void RaiseEqualityEngineConflict::raiseEEConflict(
- Node n, std::shared_ptr<ProofNode> pf) const
-{
- d_ta.raiseBlackBoxConflict(n, pf);
-}
-
-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();
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Mathias Preiner, Clark Barrett
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#pragma once
-
-#include "expr/node.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/inference_id.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-
-class ProofNode;
-
-namespace theory {
-namespace arith {
-
-class TheoryArithPrivate;
-
-/**
- * ArithVarCallBack provides a mechanism for agreeing on callbacks while
- * breaking mutual recursion inclusion order problems.
- */
-class ArithVarCallBack {
-public:
- virtual ~ArithVarCallBack() {}
- virtual void operator()(ArithVar x) = 0;
-};
-
-/**
- * Requests arithmetic variables for internal use,
- * and releases arithmetic variables that are no longer being used.
- */
-class ArithVarMalloc {
-public:
- virtual ~ArithVarMalloc() {}
- virtual ArithVar request() = 0;
- virtual void release(ArithVar v) = 0;
-};
-
-class TNodeCallBack {
-public:
- virtual ~TNodeCallBack() {}
- virtual void operator()(TNode n) = 0;
-};
-
-class NodeCallBack {
-public:
- virtual ~NodeCallBack() {}
- virtual void operator()(Node n) = 0;
-};
-
-class RationalCallBack {
-public:
- virtual ~RationalCallBack() {}
- virtual Rational operator()() const = 0;
-};
-
-class SetupLiteralCallBack : public TNodeCallBack {
-private:
- TheoryArithPrivate& d_arith;
-public:
- SetupLiteralCallBack(TheoryArithPrivate& ta);
- void operator()(TNode lit) override;
-};
-
-class DeltaComputeCallback : public RationalCallBack {
-private:
- const TheoryArithPrivate& d_ta;
-public:
- DeltaComputeCallback(const TheoryArithPrivate& ta);
- Rational operator()() const override;
-};
-
-class BasicVarModelUpdateCallBack : public ArithVarCallBack{
-private:
- TheoryArithPrivate& d_ta;
-public:
- BasicVarModelUpdateCallBack(TheoryArithPrivate& ta);
- void operator()(ArithVar x) override;
-};
-
-class TempVarMalloc : public ArithVarMalloc {
-private:
- TheoryArithPrivate& d_ta;
-public:
- TempVarMalloc(TheoryArithPrivate& ta);
- ArithVar request() override;
- void release(ArithVar v) override;
-};
-
-class RaiseConflict {
-private:
- TheoryArithPrivate& d_ta;
-public:
- RaiseConflict(TheoryArithPrivate& ta);
-
- /** Calls d_ta.raiseConflict(c) */
- void raiseConflict(ConstraintCP c, InferenceId id) const;
-};
-
-class FarkasConflictBuilder {
-private:
- RationalVector d_farkas;
- ConstraintCPVec d_constraints;
- ConstraintCP d_consequent;
- bool d_consequentSet;
- bool d_produceProofs;
-
- public:
-
- /**
- * Constructs a new FarkasConflictBuilder.
- */
- FarkasConflictBuilder(bool produceProofs);
-
- /**
- * Adds an antecedent constraint to the conflict under construction
- * with the farkas coefficient fc * mult.
- *
- * The value mult is either 1 or -1.
- */
- void addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult);
-
- /**
- * Adds an antecedent constraint to the conflict under construction
- * with the farkas coefficient fc.
- */
- void addConstraint(ConstraintCP c, const Rational& fc);
-
- /**
- * Makes the last constraint added the consequent.
- * Can be done exactly once per reset().
- */
- void makeLastConsequent();
-
- /**
- * Turns the antecendents into a proof of the negation of one of the
- * antecedents.
- *
- * The buffer is no longer underConstruction afterwards.
- *
- * precondition:
- * - At least two constraints have been asserted.
- * - makeLastConsequent() has been called.
- *
- * postcondition: The returned constraint is in conflict.
- */
- ConstraintCP commitConflict();
-
- /** Returns true if a conflict has been pushed back since the last reset. */
- bool underConstruction() const;
-
- /** Returns true if the consequent has been set since the last reset. */
- bool consequentIsSet() const;
-
- /** Resets the state of the buffer. */
- void reset();
-};
-
-
-class RaiseEqualityEngineConflict {
-private:
- TheoryArithPrivate& d_ta;
-
-public:
- RaiseEqualityEngineConflict(TheoryArithPrivate& ta);
-
- /* If you are not an equality engine, don't use this!
- *
- * The proof should prove that `n` is a conflict.
- * */
- void raiseEEConflict(Node n, std::shared_ptr<ProofNode> pf) const;
-};
-
-class BoundCountingLookup {
-private:
- TheoryArithPrivate& d_ta;
-public:
- BoundCountingLookup(TheoryArithPrivate& ta);
- const BoundsInfo& boundsInfo(ArithVar basic) const;
- BoundCounts atBounds(ArithVar basic) const;
- BoundCounts hasBounds(ArithVar basic) const;
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Alex Ozdemir, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/congruence_manager.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "proof/proof_node.h"
-#include "proof/proof_node_manager.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/partial_model.h"
-#include "theory/ee_setup_info.h"
-#include "theory/rewriter.h"
-#include "theory/uf/equality_engine.h"
-#include "theory/uf/proof_equality_engine.h"
-
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithCongruenceManager::ArithCongruenceManager(
- Env& env,
- ConstraintDatabase& cd,
- SetupLiteralCallBack setup,
- const ArithVariables& avars,
- RaiseEqualityEngineConflict raiseConflict)
- : EnvObj(env),
- d_inConflict(context()),
- d_raiseConflict(raiseConflict),
- d_notify(*this),
- d_keepAlive(context()),
- d_propagatations(context()),
- d_explanationMap(context()),
- d_constraintDatabase(cd),
- d_setupLiteral(setup),
- d_avariables(avars),
- d_ee(nullptr),
- d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
- : nullptr),
- // Construct d_pfGenEe with the SAT context, since its proof include
- // unclosed assumptions of theory literals.
- d_pfGenEe(new EagerProofGenerator(
- d_pnm, context(), "ArithCongruenceManager::pfGenEe")),
- // Construct d_pfGenEe with the USER context, since its proofs are closed.
- d_pfGenExplain(new EagerProofGenerator(
- d_pnm, userContext(), "ArithCongruenceManager::pfGenExplain")),
- d_pfee(nullptr)
-{
-}
-
-ArithCongruenceManager::~ArithCongruenceManager() {}
-
-bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi)
-{
- Assert(!options().arith.arithEqSolver);
- esi.d_notify = &d_notify;
- esi.d_name = "arithCong::ee";
- return true;
-}
-
-void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee)
-{
- if (options().arith.arithEqSolver)
- {
- // use our own copy
- d_allocEe = std::make_unique<eq::EqualityEngine>(
- d_env, context(), d_notify, "arithCong::ee", true);
- d_ee = d_allocEe.get();
- if (d_pnm != nullptr)
- {
- // allocate an internal proof equality engine
- d_allocPfee = std::make_unique<eq::ProofEqEngine>(d_env, *d_ee);
- d_ee->setProofEqualityEngine(d_allocPfee.get());
- }
- }
- else
- {
- Assert(ee != nullptr);
- // otherwise, we use the official one
- d_ee = ee;
- }
- // set the congruence kinds on the separate equality engine
- d_ee->addFunctionKind(kind::NONLINEAR_MULT);
- d_ee->addFunctionKind(kind::EXPONENTIAL);
- d_ee->addFunctionKind(kind::SINE);
- d_ee->addFunctionKind(kind::IAND);
- d_ee->addFunctionKind(kind::POW2);
- // the proof equality engine is the one from the equality engine
- d_pfee = d_ee->getProofEqualityEngine();
- // have proof equality engine only if proofs are enabled
- Assert(isProofEnabled() == (d_pfee != nullptr));
-}
-
-ArithCongruenceManager::Statistics::Statistics()
- : d_watchedVariables(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::watchedVariables")),
- d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::watchedVariableIsZero")),
- d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::watchedVariableIsNotZero")),
- d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::equalsConstantCalls")),
- d_propagations(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::propagations")),
- d_propagateConstraints(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::propagateConstraints")),
- d_conflicts(smtStatisticsRegistry().registerInt(
- "theory::arith::congruence::conflicts"))
-{
-}
-
-ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm)
- : d_acm(acm)
-{}
-
-bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
- TNode predicate, bool value)
-{
- Assert(predicate.getKind() == kind::EQUAL);
- Trace("arith::congruences")
- << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", "
- << (value ? "true" : "false") << ")" << std::endl;
- if (value) {
- return d_acm.propagate(predicate);
- }
- return d_acm.propagate(predicate.notNode());
-}
-
-bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
- Trace("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) {
- Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl;
- d_acm.propagate(t1.eqNode(t2));
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) {
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1,
- TNode t2)
-{
-}
-void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
-}
-
-void ArithCongruenceManager::raiseConflict(Node conflict,
- std::shared_ptr<ProofNode> pf)
-{
- Assert(!inConflict());
- Trace("arith::conflict") << "difference manager conflict " << conflict << std::endl;
- d_inConflict.raise();
- d_raiseConflict.raiseEEConflict(conflict, pf);
-}
-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();
-}
-
-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());
- Assert(lb->getValue().sgn() == 0);
- Assert(ub->getValue().sgn() == 0);
-
- ++(d_statistics.d_watchedVariableIsZero);
-
- ArithVar s = lb->getVariable();
- TNode eq = d_watchedEqualities[s];
- ConstraintCP eqC = d_constraintDatabase.getConstraint(
- s, ConstraintType::Equality, lb->getValue());
- NodeBuilder reasonBuilder(Kind::AND);
- auto pfLb = lb->externalExplainByAssertions(reasonBuilder);
- auto pfUb = ub->externalExplainByAssertions(reasonBuilder);
- Node reason = mkAndFromBuilder(reasonBuilder);
- std::shared_ptr<ProofNode> pf{};
- if (isProofEnabled())
- {
- pf = d_pnm->mkNode(
- PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()});
- pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq});
- }
-
- d_keepAlive.push_back(reason);
- Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy"
- << std::endl;
- Trace("arith-ee") << " based on " << lb << std::endl;
- Trace("arith-ee") << " based on " << ub << std::endl;
- assertionToEqualityEngine(true, s, reason, pf);
-}
-
-void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){
- Trace("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl;
-
- Assert(eq->isEquality());
- Assert(eq->getValue().sgn() == 0);
-
- ++(d_statistics.d_watchedVariableIsZero);
-
- ArithVar s = eq->getVariable();
-
- //Explain for conflict is correct as these proofs are generated
- //and stored eagerly
- //These will be safe for propagation later as well
- NodeBuilder nb(Kind::AND);
- // An open proof of eq from literals now in reason.
- if (TraceIsOn("arith::cong"))
- {
- eq->printProofTree(Trace("arith::cong"));
- }
- auto pf = eq->externalExplainByAssertions(nb);
- if (isProofEnabled())
- {
- pf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]});
- }
- Node reason = mkAndFromBuilder(nb);
-
- d_keepAlive.push_back(reason);
- assertionToEqualityEngine(true, s, reason, pf);
-}
-
-void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){
- Trace("arith::cong::notzero")
- << "Cong::watchedVariableCannotBeZero " << *c << std::endl;
- ++(d_statistics.d_watchedVariableIsNotZero);
-
- ArithVar s = c->getVariable();
- Node disEq = d_watchedEqualities[s].negate();
-
- //Explain for conflict is correct as these proofs are generated and stored eagerly
- //These will be safe for propagation later as well
- NodeBuilder nb(Kind::AND);
- // An open proof of eq from literals now in reason.
- auto pf = c->externalExplainByAssertions(nb);
- if (TraceIsOn("arith::cong::notzero"))
- {
- Trace("arith::cong::notzero") << " original proof ";
- pf->printDebug(Trace("arith::cong::notzero"));
- Trace("arith::cong::notzero") << std::endl;
- }
- Node reason = mkAndFromBuilder(nb);
- if (isProofEnabled())
- {
- if (c->getType() == ConstraintType::Disequality)
- {
- Assert(c->getLiteral() == d_watchedEqualities[s].negate());
- // We have to prove equivalence to the watched disequality.
- pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq});
- }
- else
- {
- Trace("arith::cong::notzero")
- << " proof modification needed" << std::endl;
-
- // Four cases:
- // c has form x_i = d, d > 0 => multiply c by -1 in Farkas proof
- // c has form x_i = d, d > 0 => multiply c by 1 in Farkas proof
- // c has form x_i <= d, d < 0 => multiply c by 1 in Farkas proof
- // c has form x_i >= d, d > 0 => multiply c by -1 in Farkas proof
- const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound
- || (c->getType() == ConstraintType::Equality
- && c->getValue().sgn() > 0);
- const int cSign = scaleCNegatively ? -1 : 1;
- TNode isZero = d_watchedEqualities[s];
- TypeNode type = isZero[0].getType();
- const auto isZeroPf = d_pnm->mkAssume(isZero);
- const auto nm = NodeManager::currentNM();
- const auto sumPf =
- d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
- {isZeroPf, pf},
- // Trick for getting correct, opposing signs.
- {nm->mkConstRealOrInt(type, Rational(-1 * cSign)),
- nm->mkConstRealOrInt(type, Rational(cSign))});
- const auto botPf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
- std::vector<Node> assumption = {isZero};
- pf = d_pnm->mkScope(botPf, assumption, false);
- Trace("arith::cong::notzero") << " new proof ";
- pf->printDebug(Trace("arith::cong::notzero"));
- Trace("arith::cong::notzero") << std::endl;
- }
- Assert(pf->getResult() == disEq);
- }
- d_keepAlive.push_back(reason);
- assertionToEqualityEngine(false, s, reason, pf);
-}
-
-
-bool ArithCongruenceManager::propagate(TNode x){
- Trace("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl;
- if(inConflict()){
- return true;
- }
-
- Node rewritten = rewrite(x);
-
- //Need to still propagate this!
- if(rewritten.getKind() == kind::CONST_BOOLEAN){
- pushBack(x);
-
- if(rewritten.getConst<bool>()){
- return true;
- }else{
- // x rewrites to false.
- ++(d_statistics.d_conflicts);
- TrustNode trn = explainInternal(x);
- Node conf = flattenAnd(trn.getNode());
- Trace("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl;
- if (isProofEnabled())
- {
- auto pf = trn.getGenerator()->getProofFor(trn.getProven());
- auto confPf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()});
- raiseConflict(conf, confPf);
- }
- else
- {
- raiseConflict(conf);
- }
- return false;
- }
- }
-
- Assert(rewritten.getKind() != kind::CONST_BOOLEAN);
-
- ConstraintP c = d_constraintDatabase.lookup(rewritten);
- if(c == NullConstraint){
- //using setup as there may not be a corresponding congruence literal yet
- d_setupLiteral(rewritten);
- c = d_constraintDatabase.lookup(rewritten);
- Assert(c != NullConstraint);
- }
-
- Trace("arith::congruenceManager")<< "x is "
- << c->hasProof() << " "
- << (x == rewritten) << " "
- << c->canBePropagated() << " "
- << c->negationHasProof() << std::endl;
-
- if(c->negationHasProof()){
- TrustNode texpC = explainInternal(x);
- Node expC = texpC.getNode();
- ConstraintCP negC = c->getNegation();
- Node neg = Constraint::externalExplainByAssertions({negC});
- Node conf = expC.andNode(neg);
- Node final = flattenAnd(conf);
-
- ++(d_statistics.d_conflicts);
- raiseConflict(final);
- Trace("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl;
- return false;
- }
-
- // Cases for propagation
- // C : c has a proof
- // S : x == rewritten
- // P : c can be propagated
- //
- // CSP
- // 000 : propagate x, and mark C it as being explained
- // 001 : propagate x, and propagate c after marking it as being explained
- // 01* : propagate x, mark c but do not propagate c
- // 10* : propagate x, do not mark c and do not propagate c
- // 11* : drop the constraint, do not propagate x or c
-
- if(!c->hasProof() && x != rewritten){
- if(c->assertedToTheTheory()){
- pushBack(x, rewritten, c->getWitness());
- }else{
- pushBack(x, rewritten);
- }
-
- c->setEqualityEngineProof();
- if(c->canBePropagated() && !c->assertedToTheTheory()){
-
- ++(d_statistics.d_propagateConstraints);
- c->propagate();
- }
- }else if(!c->hasProof() && x == rewritten){
- if(c->assertedToTheTheory()){
- pushBack(x, c->getWitness());
- }else{
- pushBack(x);
- }
- c->setEqualityEngineProof();
- }else if(c->hasProof() && x != rewritten){
- if(c->assertedToTheTheory()){
- pushBack(x);
- }else{
- pushBack(x);
- }
- }else{
- Assert(c->hasProof() && x == rewritten);
- }
- return true;
-}
-
-void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) {
- if (literal.getKind() != kind::NOT) {
- d_ee->explainEquality(literal[0], literal[1], true, assumptions);
- } else {
- d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions);
- }
-}
-
-void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s,
- NodeBuilder& nb)
-{
- std::set<TNode>::const_iterator it = s.begin();
- std::set<TNode>::const_iterator it_end = s.end();
- for(; it != it_end; ++it) {
- nb << *it;
- }
-}
-
-TrustNode ArithCongruenceManager::explainInternal(TNode internal)
-{
- if (isProofEnabled())
- {
- return d_pfee->explain(internal);
- }
- // otherwise, explain without proof generator
- Node exp = d_ee->mkExplainLit(internal);
- return TrustNode::mkTrustPropExp(internal, exp, nullptr);
-}
-
-TrustNode ArithCongruenceManager::explain(TNode external)
-{
- Trace("arith-ee") << "Ask for explanation of " << external << std::endl;
- Node internal = externalToInternal(external);
- Trace("arith-ee") << "...internal = " << internal << std::endl;
- TrustNode trn = explainInternal(internal);
- if (isProofEnabled() && trn.getProven()[1] != external)
- {
- Assert(trn.getKind() == TrustNodeKind::PROP_EXP);
- Assert(trn.getProven().getKind() == Kind::IMPLIES);
- Assert(trn.getGenerator() != nullptr);
- Trace("arith-ee") << "tweaking proof to prove " << external << " not "
- << trn.getProven()[1] << std::endl;
- std::vector<std::shared_ptr<ProofNode>> assumptionPfs;
- std::vector<Node> assumptions = andComponents(trn.getNode());
- assumptionPfs.push_back(trn.toProofNode());
- for (const auto& a : assumptions)
- {
- assumptionPfs.push_back(
- d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {}));
- }
- auto litPf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external});
- auto extPf = d_pnm->mkScope(litPf, assumptions);
- return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf);
- }
- return trn;
-}
-
-void ArithCongruenceManager::explain(TNode external, NodeBuilder& out)
-{
- Node internal = externalToInternal(external);
-
- std::vector<TNode> assumptions;
- explain(internal, assumptions);
- std::set<TNode> assumptionSet;
- assumptionSet.insert(assumptions.begin(), assumptions.end());
-
- enqueueIntoNB(assumptionSet, out);
-}
-
-void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){
- Assert(!isWatchedVariable(s));
-
- Trace("arith::congruenceManager")
- << "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl;
-
-
- ++(d_statistics.d_watchedVariables);
-
- d_watchedVariables.add(s);
-
- Node eq = x.eqNode(y);
- d_watchedEqualities.set(s, eq);
-}
-
-void ArithCongruenceManager::assertLitToEqualityEngine(
- Node lit, TNode reason, std::shared_ptr<ProofNode> pf)
-{
- bool isEquality = lit.getKind() != Kind::NOT;
- Node eq = isEquality ? lit : lit[0];
- Assert(eq.getKind() == Kind::EQUAL);
-
- Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason
- << std::endl;
- if (isProofEnabled())
- {
- if (CDProof::isSame(lit, reason))
- {
- Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl;
- // The equality engine doesn't ref-count for us...
- d_keepAlive.push_back(eq);
- d_keepAlive.push_back(reason);
- d_ee->assertEquality(eq, isEquality, reason);
- }
- else if (hasProofFor(lit))
- {
- Trace("arith-pfee") << "Skipping b/c already done" << std::endl;
- }
- else
- {
- setProofFor(lit, pf);
- Trace("arith-pfee") << "Actually asserting" << std::endl;
- if (TraceIsOn("arith-pfee"))
- {
- Trace("arith-pfee") << "Proof: ";
- pf->printDebug(Trace("arith-pfee"));
- Trace("arith-pfee") << std::endl;
- }
- // The proof equality engine *does* ref-count for us...
- d_pfee->assertFact(lit, reason, d_pfGenEe.get());
- }
- }
- else
- {
- // The equality engine doesn't ref-count for us...
- d_keepAlive.push_back(eq);
- d_keepAlive.push_back(reason);
- d_ee->assertEquality(eq, isEquality, reason);
- }
-}
-
-void ArithCongruenceManager::assertionToEqualityEngine(
- bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf)
-{
- Assert(isWatchedVariable(s));
-
- TNode eq = d_watchedEqualities[s];
- Assert(eq.getKind() == kind::EQUAL);
-
- Node lit = isEquality ? Node(eq) : eq.notNode();
- Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality
- << ", reason " << reason << std::endl;
- assertLitToEqualityEngine(lit, reason, pf);
-}
-
-bool ArithCongruenceManager::hasProofFor(TNode f) const
-{
- Assert(isProofEnabled());
- if (d_pfGenEe->hasProofFor(f))
- {
- return true;
- }
- Node sym = CDProof::getSymmFact(f);
- Assert(!sym.isNull());
- return d_pfGenEe->hasProofFor(sym);
-}
-
-void ArithCongruenceManager::setProofFor(TNode f,
- std::shared_ptr<ProofNode> pf) const
-{
- Assert(!hasProofFor(f));
- d_pfGenEe->mkTrustNode(f, pf);
- Node symF = CDProof::getSymmFact(f);
- auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {});
- d_pfGenEe->mkTrustNode(symF, symPf);
-}
-
-void ArithCongruenceManager::equalsConstant(ConstraintCP c){
- Assert(c->isEquality());
-
- ++(d_statistics.d_equalsConstantCalls);
- Trace("equalsConstant") << "equals constant " << c << std::endl;
-
- ArithVar x = c->getVariable();
- Node xAsNode = d_avariables.asNode(x);
- NodeManager* nm = NodeManager::currentNM();
- Node asRational = nm->mkConstRealOrInt(
- xAsNode.getType(), c->getValue().getNoninfinitesimalPart());
-
- // No guarentee this is in normal form!
- // Note though, that it happens to be in proof normal form!
- Node eq = xAsNode.eqNode(asRational);
- d_keepAlive.push_back(eq);
-
- NodeBuilder nb(Kind::AND);
- auto pf = c->externalExplainByAssertions(nb);
- Node reason = mkAndFromBuilder(nb);
- d_keepAlive.push_back(reason);
-
- Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl;
- assertLitToEqualityEngine(eq, reason, pf);
-}
-
-void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){
- Assert(lb->isLowerBound());
- Assert(ub->isUpperBound());
- Assert(lb->getVariable() == ub->getVariable());
-
- ++(d_statistics.d_equalsConstantCalls);
- Trace("equalsConstant") << "equals constant " << lb << std::endl
- << ub << std::endl;
-
- ArithVar x = lb->getVariable();
- NodeBuilder nb(Kind::AND);
- auto pfLb = lb->externalExplainByAssertions(nb);
- auto pfUb = ub->externalExplainByAssertions(nb);
- Node reason = mkAndFromBuilder(nb);
-
- Node xAsNode = d_avariables.asNode(x);
- NodeManager* nm = NodeManager::currentNM();
- Node asRational = nm->mkConstRealOrInt(
- xAsNode.getType(), lb->getValue().getNoninfinitesimalPart());
-
- // No guarentee this is in normal form!
- // Note though, that it happens to be in proof normal form!
- Node eq = xAsNode.eqNode(asRational);
- std::shared_ptr<ProofNode> pf;
- if (isProofEnabled())
- {
- pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq});
- }
- d_keepAlive.push_back(eq);
- d_keepAlive.push_back(reason);
-
- Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl;
-
- assertLitToEqualityEngine(eq, reason, pf);
-}
-
-bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; }
-
-std::vector<Node> andComponents(TNode an)
-{
- auto nm = NodeManager::currentNM();
- if (an == nm->mkConst(true))
- {
- return {};
- }
- else if (an.getKind() != Kind::AND)
- {
- return {an};
- }
- std::vector<Node> a{};
- a.reserve(an.getNumChildren());
- a.insert(a.end(), an.begin(), an.end());
- return a;
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Alex Ozdemir, Tim King, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "context/cdhashmap.h"
-#include "context/cdlist.h"
-#include "context/cdmaybe.h"
-#include "context/cdtrail_queue.h"
-#include "proof/trust_node.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/uf/equality_engine_notify.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-class UserContext;
-} // namespace cvc5::context
-
-namespace cvc5::internal {
-
-class ProofNodeManager;
-class EagerProofGenerator;
-
-namespace theory {
-struct EeSetupInfo;
-
-namespace eq {
-class ProofEqEngine;
-class EqualityEngine;
-}
-
-namespace arith {
-
-class ArithVariables;
-
-class ArithCongruenceManager : protected EnvObj
-{
- private:
- context::CDRaised d_inConflict;
- RaiseEqualityEngineConflict d_raiseConflict;
-
- /**
- * The set of ArithVars equivalent to a pair of terms.
- * If this is 0 or cannot be 0, this can be signalled.
- * The pair of terms for the congruence is stored in watched equalities.
- */
- DenseSet d_watchedVariables;
- /** d_watchedVariables |-> (= x y) */
- ArithVarToNodeMap d_watchedEqualities;
-
-
- class ArithCongruenceNotify : public eq::EqualityEngineNotify {
- private:
- ArithCongruenceManager& d_acm;
- public:
- ArithCongruenceNotify(ArithCongruenceManager& acm);
-
- bool eqNotifyTriggerPredicate(TNode predicate, bool value) override;
-
- bool eqNotifyTriggerTermEquality(TheoryId tag,
- TNode t1,
- TNode t2,
- bool value) override;
-
- void eqNotifyConstantTermMerge(TNode t1, TNode t2) override;
- void eqNotifyNewClass(TNode t) override;
- void eqNotifyMerge(TNode t1, TNode t2) override;
- void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override;
- };
- ArithCongruenceNotify d_notify;
-
- context::CDList<Node> d_keepAlive;
-
- /** Store the propagations. */
- context::CDTrailQueue<Node> d_propagatations;
-
- /* This maps the node a theory engine will request on an explain call to
- * to its corresponding PropUnit.
- * This is node is potentially both the propagation or
- * rewrite(propagation).
- */
- typedef context::CDHashMap<Node, size_t> ExplainMap;
- ExplainMap d_explanationMap;
-
- ConstraintDatabase& d_constraintDatabase;
- SetupLiteralCallBack d_setupLiteral;
-
- const ArithVariables& d_avariables;
-
- /** The equality engine being used by this class */
- eq::EqualityEngine* d_ee;
- /** The equality engine we allocated */
- std::unique_ptr<eq::EqualityEngine> d_allocEe;
- /** proof manager */
- ProofNodeManager* d_pnm;
- /** A proof generator for storing proofs of facts that are asserted to the EQ
- * engine. Note that these proofs **are not closed**; they may contain
- * literals from the explanation of their fact as unclosed assumptions.
- * This makes these proofs SAT-context depdent.
- *
- * This is why this generator is separate from the TheoryArithPrivate
- * generator, which stores closed proofs.
- */
- std::unique_ptr<EagerProofGenerator> d_pfGenEe;
- /** A proof generator for TrustNodes sent to TheoryArithPrivate.
- *
- * When TheoryArithPrivate requests an explanation from
- * ArithCongruenceManager, it can request a TrustNode for that explanation.
- * This proof generator is the one used in that TrustNode: it stores the
- * (closed) proofs of implications proved by the
- * ArithCongruenceManager/EqualityEngine.
- *
- * It is insufficient to just use the ProofGenerator from the ProofEqEngine,
- * since sometimes the ArithCongruenceManager needs to add some
- * arith-specific reasoning to extend the proof (e.g. rewriting the result
- * into a normal form).
- * */
- std::unique_ptr<EagerProofGenerator> d_pfGenExplain;
-
- /** Pointer to the proof equality engine of TheoryArith */
- theory::eq::ProofEqEngine* d_pfee;
- /** The proof equality engine we allocated */
- std::unique_ptr<eq::ProofEqEngine> d_allocPfee;
-
- /** Raise a conflict node `conflict` to the theory of arithmetic.
- *
- * When proofs are enabled, a (closed) proof of the conflict should be
- * provided.
- */
- void raiseConflict(Node conflict, std::shared_ptr<ProofNode> pf = nullptr);
- /**
- * Are proofs enabled? This is true if a non-null proof manager was provided
- * to the constructor of this class.
- */
- bool isProofEnabled() const;
-
- public:
- bool inConflict() const;
-
- bool hasMorePropagations() const;
-
- const Node getNextPropagation();
-
- bool canExplain(TNode n) const;
-
-private:
- Node externalToInternal(TNode n) const;
-
- void pushBack(TNode n);
-
- void pushBack(TNode n, TNode r);
-
- void pushBack(TNode n, TNode r, TNode w);
-
- bool propagate(TNode x);
- void explain(TNode literal, std::vector<TNode>& assumptions);
-
- /** Assert this literal to the eq engine. Common functionality for
- * * assertionToEqualityEngine(..)
- * * equalsConstant(c)
- * * equalsConstant(lb, ub)
- * If proof is off, then just asserts.
- */
- void assertLitToEqualityEngine(Node lit,
- TNode reason,
- std::shared_ptr<ProofNode> pf);
- /** This sends a shared term to the uninterpreted equality engine. */
- void assertionToEqualityEngine(bool eq,
- ArithVar s,
- TNode reason,
- std::shared_ptr<ProofNode> pf);
-
- /** Check for proof for this or a symmetric fact
- *
- * The proof submitted to this method are stored in `d_pfGenEe`, and should
- * have closure properties consistent with the documentation for that member.
- *
- * @returns whether this or a symmetric fact has a proof.
- */
- bool hasProofFor(TNode f) const;
- /**
- * Sets the proof for this fact and the symmetric one.
- *
- * The proof submitted to this method are stored in `d_pfGenEe`, and should
- * have closure properties consistent with the documentation for that member.
- * */
- void setProofFor(TNode f, std::shared_ptr<ProofNode> pf) const;
-
- /** Dequeues the delay queue and asserts these equalities.*/
- void enableSharedTerms();
- void dequeueLiterals();
-
- void enqueueIntoNB(const std::set<TNode> all, NodeBuilder& nb);
-
- /**
- * Determine an explaination for `internal`. That is a conjunction of theory
- * literals which imply `internal`.
- *
- * The TrustNode here is a trusted propagation.
- */
- TrustNode explainInternal(TNode internal);
-
- public:
- ArithCongruenceManager(Env& env,
- ConstraintDatabase&,
- SetupLiteralCallBack,
- const ArithVariables&,
- RaiseEqualityEngineConflict raiseConflict);
- ~ArithCongruenceManager();
-
- //--------------------------------- initialization
- /**
- * Returns true if we need an equality engine, see
- * Theory::needsEqualityEngine.
- */
- bool needsEqualityEngine(EeSetupInfo& esi);
- /**
- * Finish initialize. This class is instructed by TheoryArithPrivate to use
- * the equality engine ee.
- */
- void finishInit(eq::EqualityEngine* ee);
- //--------------------------------- end initialization
-
- /**
- * Return the trust node for the explanation of literal. The returned
- * trust node is generated by the proof equality engine of this class.
- */
- TrustNode explain(TNode literal);
-
- void explain(TNode lit, NodeBuilder& out);
-
- void addWatchedPair(ArithVar s, TNode x, TNode y);
-
- inline bool isWatchedVariable(ArithVar s) const {
- return d_watchedVariables.isMember(s);
- }
-
- /** Assert an equality. */
- void watchedVariableIsZero(ConstraintCP eq);
-
- /** Assert a conjunction from lb and ub. */
- void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub);
-
- /** Assert that the value cannot be zero. */
- void watchedVariableCannotBeZero(ConstraintCP c);
-
- /** Assert that the value cannot be zero. */
- void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d);
-
-
- /** Assert that the value is congruent to a constant. */
- void equalsConstant(ConstraintCP eq);
- void equalsConstant(ConstraintCP lb, ConstraintCP ub);
-
- private:
- class Statistics {
- public:
- IntStat d_watchedVariables;
- IntStat d_watchedVariableIsZero;
- IntStat d_watchedVariableIsNotZero;
-
- IntStat d_equalsConstantCalls;
-
- IntStat d_propagations;
- IntStat d_propagateConstraints;
- IntStat d_conflicts;
-
- Statistics();
- } d_statistics;
-
-}; /* class ArithCongruenceManager */
-
-std::vector<Node> andComponents(TNode an);
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Alex Ozdemir, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/constraint.h"
-
-#include <algorithm>
-#include <ostream>
-#include <unordered_set>
-
-#include "base/output.h"
-#include "options/smt_options.h"
-#include "proof/eager_proof_generator.h"
-#include "proof/proof_node_manager.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/builtin/proof_checker.h"
-#include "theory/rewriter.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ConstraintRule::ConstraintRule()
- : d_constraint(NullConstraint),
- d_proofType(NoAP),
- d_antecedentEnd(AntecedentIdSentinel)
-{
- d_farkasCoefficients = RationalVectorCPSentinel;
-}
-
-ConstraintRule::ConstraintRule(ConstraintP con, ArithProofType pt)
- : d_constraint(con), d_proofType(pt), d_antecedentEnd(AntecedentIdSentinel)
-{
- d_farkasCoefficients = RationalVectorCPSentinel;
-}
-ConstraintRule::ConstraintRule(ConstraintP con,
- ArithProofType pt,
- AntecedentId antecedentEnd)
- : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
-{
- d_farkasCoefficients = RationalVectorCPSentinel;
-}
-
-ConstraintRule::ConstraintRule(ConstraintP con,
- ArithProofType pt,
- AntecedentId antecedentEnd,
- RationalVectorCP coeffs)
- : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
-{
- Assert(con->isProofProducing() || coeffs == RationalVectorCPSentinel);
- d_farkasCoefficients = coeffs;
-}
-
-/** Given a simplifiedKind this returns the corresponding ConstraintType. */
-//ConstraintType constraintTypeOfLiteral(Kind k);
-ConstraintType Constraint::constraintTypeOfComparison(const Comparison& cmp){
- Kind k = cmp.comparisonKind();
- switch(k){
- case LT:
- case LEQ:
- {
- Polynomial l = cmp.getLeft();
- if(l.leadingCoefficientIsPositive()){ // (< x c)
- return UpperBound;
- }else{
- return LowerBound; // (< (-x) c)
- }
- }
- case GT:
- case GEQ:
- {
- Polynomial l = cmp.getLeft();
- if(l.leadingCoefficientIsPositive()){
- return LowerBound; // (> x c)
- }else{
- return UpperBound; // (> (-x) c)
- }
- }
- case EQUAL:
- return Equality;
- case DISTINCT:
- return Disequality;
- default: Unhandled() << k;
- }
-}
-
-Constraint::Constraint(ArithVar x,
- ConstraintType t,
- const DeltaRational& v,
- bool produceProofs)
- : d_variable(x),
- d_type(t),
- d_value(v),
- d_database(NULL),
- d_literal(Node::null()),
- d_negation(NullConstraint),
- d_canBePropagated(false),
- d_assertionOrder(AssertionOrderSentinel),
- d_witness(TNode::null()),
- d_crid(ConstraintRuleIdSentinel),
- d_split(false),
- d_variablePosition(),
- d_produceProofs(produceProofs)
-{
- Assert(!initialized());
-}
-
-
-std::ostream& operator<<(std::ostream& o, const ArithProofType apt){
- switch(apt){
- case NoAP: o << "NoAP"; break;
- case AssumeAP: o << "AssumeAP"; break;
- case InternalAssumeAP: o << "InternalAssumeAP"; break;
- case FarkasAP: o << "FarkasAP"; break;
- case TrichotomyAP: o << "TrichotomyAP"; break;
- case EqualityEngineAP: o << "EqualityEngineAP"; break;
- case IntTightenAP: o << "IntTightenAP"; break;
- case IntHoleAP: o << "IntHoleAP"; break;
- default: break;
- }
- return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintCP c){
- if(c == NullConstraint){
- return o << "NullConstraint";
- }else{
- return o << *c;
- }
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintP c){
- if(c == NullConstraint){
- return o << "NullConstraint";
- }else{
- return o << *c;
- }
-}
-
-std::ostream& operator<<(std::ostream& o, const ConstraintType t){
- switch(t){
- case LowerBound:
- return o << ">=";
- case UpperBound:
- return o << "<=";
- case Equality:
- return o << "=";
- case Disequality:
- return o << "!=";
- default:
- Unreachable();
- }
-}
-
-std::ostream& operator<<(std::ostream& o, const Constraint& c){
- o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue();
- if(c.hasLiteral()){
- o << "(node " << c.getLiteral() << ')';
- }
- return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){
- o << "{";
- bool pending = false;
- if(vc.hasEquality()){
- o << "eq: " << vc.getEquality();
- pending = true;
- }
- if(vc.hasLowerBound()){
- if(pending){
- o << ", ";
- }
- o << "lb: " << vc.getLowerBound();
- pending = true;
- }
- if(vc.hasUpperBound()){
- if(pending){
- o << ", ";
- }
- o << "ub: " << vc.getUpperBound();
- pending = true;
- }
- if(vc.hasDisequality()){
- if(pending){
- o << ", ";
- }
- o << "de: " << vc.getDisequality();
- }
- return o << "}";
-}
-
-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;
-}
-
-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 {
- Trace("arith::constraint") << "push_into " << *this << endl;
- if(hasEquality()){
- vec.push_back(d_equality);
- }
- if(hasLowerBound()){
- vec.push_back(d_lowerBound);
- }
- if(hasUpperBound()){
- vec.push_back(d_upperBound);
- }
- if(hasDisequality()){
- vec.push_back(d_disequality);
- }
-}
-
-ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){
- ValueCollection ret;
- Assert(ret.empty());
- switch(c->getType()){
- case LowerBound:
- ret.d_lowerBound = c;
- break;
- case UpperBound:
- ret.d_upperBound = c;
- break;
- case Equality:
- ret.d_equality = c;
- break;
- case Disequality:
- ret.d_disequality = c;
- break;
- default:
- Unreachable();
- }
- return ret;
-}
-
-bool ValueCollection::hasConstraintOfType(ConstraintType t) const{
- switch(t){
- case LowerBound:
- return hasLowerBound();
- case UpperBound:
- return hasUpperBound();
- case Equality:
- return hasEquality();
- case Disequality:
- return hasDisequality();
- default:
- Unreachable();
- }
-}
-
-ArithVar ValueCollection::getVariable() const{
- Assert(!empty());
- return nonNull()->getVariable();
-}
-
-const DeltaRational& ValueCollection::getValue() const{
- Assert(!empty());
- return nonNull()->getValue();
-}
-
-void ValueCollection::add(ConstraintP c){
- Assert(c != NullConstraint);
-
- Assert(empty() || getVariable() == c->getVariable());
- Assert(empty() || getValue() == c->getValue());
-
- switch(c->getType()){
- case LowerBound:
- Assert(!hasLowerBound());
- d_lowerBound = c;
- break;
- case Equality:
- Assert(!hasEquality());
- d_equality = c;
- break;
- case UpperBound:
- Assert(!hasUpperBound());
- d_upperBound = c;
- break;
- case Disequality:
- Assert(!hasDisequality());
- d_disequality = c;
- break;
- default:
- Unreachable();
- }
-}
-
-ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{
- switch(t){
- case LowerBound: Assert(hasLowerBound()); return d_lowerBound;
- case Equality: Assert(hasEquality()); return d_equality;
- case UpperBound: Assert(hasUpperBound()); return d_upperBound;
- case Disequality: Assert(hasDisequality()); return d_disequality;
- default: Unreachable();
- }
-}
-
-void ValueCollection::remove(ConstraintType t){
- switch(t){
- case LowerBound:
- Assert(hasLowerBound());
- d_lowerBound = NullConstraint;
- break;
- case Equality:
- Assert(hasEquality());
- d_equality = NullConstraint;
- break;
- case UpperBound:
- Assert(hasUpperBound());
- d_upperBound = NullConstraint;
- break;
- case Disequality:
- Assert(hasDisequality());
- d_disequality = NullConstraint;
- break;
- default:
- Unreachable();
- }
-}
-
-bool ValueCollection::empty() const{
- return
- !(hasLowerBound() ||
- hasUpperBound() ||
- hasEquality() ||
- hasDisequality());
-}
-
-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()){
- return d_lowerBound;
- }else if(hasUpperBound()){
- return d_upperBound;
- }else if(hasEquality()){
- return d_equality;
- }else if(hasDisequality()){
- return d_disequality;
- }else{
- return NullConstraint;
- }
-}
-
-bool Constraint::initialized() const {
- return d_database != NULL;
-}
-
-const ConstraintDatabase& Constraint::getDatabase() const{
- Assert(initialized());
- return *d_database;
-}
-
-void Constraint::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){
- Assert(!initialized());
- d_database = db;
- d_variablePosition = v;
- d_negation = negation;
-}
-
-Constraint::~Constraint() {
- // Call this instead of safeToGarbageCollect()
- Assert(!contextDependentDataIsSet());
-
- if(initialized()){
- ValueCollection& vc = d_variablePosition->second;
- Trace("arith::constraint") << "removing" << vc << endl;
-
- vc.remove(getType());
-
- if(vc.empty()){
- Trace("arith::constraint") << "erasing" << vc << endl;
- SortedConstraintMap& perVariable = d_database->getVariableSCM(getVariable());
- perVariable.erase(d_variablePosition);
- }
-
- if(hasLiteral()){
- d_database->d_nodetoConstraintMap.erase(getLiteral());
- }
- }
-}
-
-const ConstraintRule& Constraint::getConstraintRule() const {
- Assert(hasProof());
- return d_database->d_watches->d_constraintProofs[d_crid];
-}
-
-const ValueCollection& Constraint::getValueCollection() const{
- return d_variablePosition->second;
-}
-
-
-ConstraintP Constraint::getCeiling() {
- Trace("getCeiling") << "Constraint_::getCeiling on " << *this << endl;
- Assert(getValue().getInfinitesimalPart().sgn() > 0);
-
- const DeltaRational ceiling(getValue().ceiling());
- return d_database->getConstraint(getVariable(), getType(), ceiling);
-}
-
-ConstraintP Constraint::getFloor() {
- Assert(getValue().getInfinitesimalPart().sgn() < 0);
-
- const DeltaRational floor(Rational(getValue().floor()));
- return d_database->getConstraint(getVariable(), getType(), floor);
-}
-
-void Constraint::setCanBePropagated() {
- Assert(!canBePropagated());
- d_database->pushCanBePropagatedWatch(this);
-}
-
-void Constraint::setAssertedToTheTheory(TNode witness, bool nowInConflict) {
- Assert(hasLiteral());
- Assert(!assertedToTheTheory());
- Assert(negationHasProof() == nowInConflict);
- d_database->pushAssertionOrderWatch(this, witness);
-
- if(TraceIsOn("constraint::conflictCommit") && nowInConflict ){
- Trace("constraint::conflictCommit") << "inConflict@setAssertedToTheTheory";
- Trace("constraint::conflictCommit") << "\t" << this << std::endl;
- Trace("constraint::conflictCommit") << "\t" << getNegation() << std::endl;
- Trace("constraint::conflictCommit") << "\t" << getNegation()->externalExplainByAssertions() << std::endl;
-
- }
-}
-
-bool Constraint::satisfiedBy(const DeltaRational& dr) const {
- switch(getType()){
- case LowerBound:
- return getValue() <= dr;
- case Equality:
- return getValue() == dr;
- case UpperBound:
- return getValue() >= dr;
- case Disequality:
- return getValue() != dr;
- }
- Unreachable();
-}
-
-bool Constraint::isInternalAssumption() const {
- return getProofType() == InternalAssumeAP;
-}
-
-TrustNode Constraint::externalExplainByAssertions() const
-{
- NodeBuilder nb(kind::AND);
- auto pfFromAssumptions = externalExplain(nb, AssertionOrderSentinel);
- Node exp = mkAndFromBuilder(nb);
- if (d_database->isProofEnabled())
- {
- std::vector<Node> assumptions;
- if (exp.getKind() == Kind::AND)
- {
- assumptions.insert(assumptions.end(), exp.begin(), exp.end());
- }
- else
- {
- assumptions.push_back(exp);
- }
- auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
- return d_database->d_pfGen->mkTrustedPropagation(
- getLiteral(), NodeManager::currentNM()->mkAnd(assumptions), pf);
- }
- return TrustNode::mkTrustPropExp(getLiteral(), exp);
-}
-
-bool Constraint::isAssumption() const {
- return getProofType() == AssumeAP;
-}
-
-bool Constraint::hasEqualityEngineProof() const {
- return getProofType() == EqualityEngineAP;
-}
-
-bool Constraint::hasFarkasProof() const {
- return getProofType() == FarkasAP;
-}
-
-bool Constraint::hasSimpleFarkasProof() const
-{
- Trace("constraints::hsfp") << "hasSimpleFarkasProof " << this << std::endl;
- if (!hasFarkasProof())
- {
- Trace("constraints::hsfp") << "There is no simple Farkas proof because "
- "there is no farkas proof."
- << std::endl;
- return false;
- }
-
- // For each antecdent ...
- AntecedentId i = getConstraintRule().d_antecedentEnd;
- for (ConstraintCP a = d_database->getAntecedent(i); a != NullConstraint;
- a = d_database->getAntecedent(--i))
- {
- // ... that antecdent must be an assumption OR a tightened assumption ...
- if (a->isPossiblyTightenedAssumption())
- {
- continue;
- }
-
- // ... otherwise, we do not have a simple Farkas proof.
- if (TraceIsOn("constraints::hsfp"))
- {
- Trace("constraints::hsfp") << "There is no simple Farkas proof b/c there "
- "is an antecdent w/ rule ";
- a->getConstraintRule().print(Trace("constraints::hsfp"), d_produceProofs);
- Trace("constraints::hsfp") << std::endl;
- }
-
- return false;
- }
- return true;
-}
-
-bool Constraint::isPossiblyTightenedAssumption() const
-{
- // ... that antecdent must be an assumption ...
-
- if (isAssumption()) return true;
- if (!hasIntTightenProof()) return false;
- if (getConstraintRule().d_antecedentEnd == AntecedentIdSentinel) return false;
- return d_database->getAntecedent(getConstraintRule().d_antecedentEnd)
- ->isAssumption();
-}
-
-bool Constraint::hasIntTightenProof() const {
- return getProofType() == IntTightenAP;
-}
-
-bool Constraint::hasIntHoleProof() const {
- return getProofType() == IntHoleAP;
-}
-
-bool Constraint::hasTrichotomyProof() const {
- return getProofType() == TrichotomyAP;
-}
-
-void Constraint::printProofTree(std::ostream& out, size_t depth) const
-{
- if (d_produceProofs)
- {
- const ConstraintRule& rule = getConstraintRule();
- out << std::string(2 * depth, ' ') << "* " << getVariable() << " [";
- out << getProofLiteral();
- if (assertedToTheTheory())
- {
- out << " | wit: " << getWitness();
- }
- out << "]" << ' ' << getType() << ' ' << getValue() << " ("
- << getProofType() << ")";
- if (getProofType() == FarkasAP)
- {
- out << " [";
- bool first = true;
- for (const auto& coeff : *rule.d_farkasCoefficients)
- {
- if (not first)
- {
- out << ", ";
- }
- first = false;
- out << coeff;
- }
- out << "]";
- }
- out << endl;
-
- for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i)
- {
- ConstraintCP antecdent = d_database->getAntecedent(i);
- if (antecdent == NullConstraint)
- {
- break;
- }
- antecdent->printProofTree(out, depth + 1);
- }
- return;
- }
- out << "Cannot print proof. This is not a proof build." << endl;
-}
-
-bool Constraint::sanityChecking(Node n) const {
- Comparison cmp = Comparison::parseNormalForm(n);
- Kind k = cmp.comparisonKind();
- Polynomial pleft = cmp.normalizedVariablePart();
- Assert(k == EQUAL || k == DISTINCT || pleft.leadingCoefficientIsPositive());
- Assert(k != EQUAL || Monomial::isMember(n[0]));
- Assert(k != DISTINCT || Monomial::isMember(n[0][0]));
-
- TNode left = pleft.getNode();
- DeltaRational right = cmp.normalizedDeltaRational();
-
- const ArithVariables& avariables = d_database->getArithVariables();
-
- Trace("Constraint::sanityChecking") << cmp.getNode() << endl;
- Trace("Constraint::sanityChecking") << k << endl;
- Trace("Constraint::sanityChecking") << pleft.getNode() << endl;
- Trace("Constraint::sanityChecking") << left << endl;
- Trace("Constraint::sanityChecking") << right << endl;
- Trace("Constraint::sanityChecking") << getValue() << endl;
- Trace("Constraint::sanityChecking") << avariables.hasArithVar(left) << endl;
- Trace("Constraint::sanityChecking") << avariables.asArithVar(left) << endl;
- Trace("Constraint::sanityChecking") << getVariable() << endl;
-
-
- if(avariables.hasArithVar(left) &&
- avariables.asArithVar(left) == getVariable() &&
- getValue() == right){
- switch(getType()){
- case LowerBound:
- case UpperBound:
- //Be overapproximate
- return k == GT || k == GEQ ||k == LT || k == LEQ;
- case Equality:
- return k == EQUAL;
- case Disequality:
- return k == DISTINCT;
- default:
- Unreachable();
- }
- }else{
- return false;
- }
-}
-
-ConstraintCP ConstraintDatabase::getAntecedent (AntecedentId p) const {
- Assert(p < d_antecedents.size());
- return d_antecedents[p];
-}
-
-void ConstraintRule::print(std::ostream& out, bool produceProofs) const
-{
- RationalVectorCP coeffs = produceProofs ? d_farkasCoefficients : nullptr;
- out << "{ConstraintRule, ";
- out << d_constraint << std::endl;
- out << "d_proofType= " << d_proofType << ", " << std::endl;
- out << "d_antecedentEnd= "<< d_antecedentEnd << std::endl;
-
- if (d_constraint != NullConstraint && d_antecedentEnd != AntecedentIdSentinel)
- {
- const ConstraintDatabase& database = d_constraint->getDatabase();
-
- size_t coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffs->size()-1 : 0;
- AntecedentId p = d_antecedentEnd;
- // must have at least one antecedent
- ConstraintCP antecedent = database.getAntecedent(p);
- while(antecedent != NullConstraint){
- if(coeffs != RationalVectorCPSentinel){
- out << coeffs->at(coeffIterator);
- } else {
- out << "_";
- }
- out << " * (" << *antecedent << ")" << std::endl;
-
- Assert((coeffs == RationalVectorCPSentinel) || coeffIterator > 0);
- --p;
- coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffIterator-1 : 0;
- antecedent = database.getAntecedent(p);
- }
- if(coeffs != RationalVectorCPSentinel){
- out << coeffs->front();
- } else {
- out << "_";
- }
- out << " * (" << *(d_constraint->getNegation()) << ")";
- out << " [not d_constraint] " << endl;
- }
- out << "}";
-}
-
-bool Constraint::wellFormedFarkasProof() const {
- Assert(hasProof());
-
- const ConstraintRule& cr = getConstraintRule();
- if(cr.d_constraint != this){ return false; }
- if(cr.d_proofType != FarkasAP){ return false; }
-
- AntecedentId p = cr.d_antecedentEnd;
-
- // must have at least one antecedent
- ConstraintCP antecedent = d_database->d_antecedents[p];
- if(antecedent == NullConstraint) { return false; }
-
- if (!d_produceProofs)
- {
- return cr.d_farkasCoefficients == RationalVectorCPSentinel;
- }
- Assert(d_produceProofs);
-
- if(cr.d_farkasCoefficients == RationalVectorCPSentinel){ return false; }
- if(cr.d_farkasCoefficients->size() < 2){ return false; }
-
- const ArithVariables& vars = d_database->getArithVariables();
-
- DeltaRational rhs(0);
- Node lhs = Polynomial::mkZero().getNode();
-
- RationalVector::const_iterator coeffIterator = cr.d_farkasCoefficients->end()-1;
- RationalVector::const_iterator coeffBegin = cr.d_farkasCoefficients->begin();
-
- while(antecedent != NullConstraint){
- Assert(lhs.isNull() || Polynomial::isMember(lhs));
-
- const Rational& coeff = *coeffIterator;
- int coeffSgn = coeff.sgn();
-
- rhs += antecedent->getValue() * coeff;
-
- ArithVar antVar = antecedent->getVariable();
- if(!lhs.isNull() && vars.hasNode(antVar)){
- Node antAsNode = vars.asNode(antVar);
- if(Polynomial::isMember(antAsNode)){
- Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
- Polynomial antPoly = Polynomial::parsePolynomial(antAsNode);
- Polynomial sum = lhsPoly + (antPoly * coeff);
- lhs = sum.getNode();
- }else{
- lhs = Node::null();
- }
- } else {
- lhs = Node::null();
- }
- Trace("constraints::wffp") << "running sum: " << lhs << " <= " << rhs << endl;
-
- switch( antecedent->getType() ){
- case LowerBound:
- // fc[l] < 0, therefore return false if coeffSgn >= 0
- if(coeffSgn >= 0){ return false; }
- break;
- case UpperBound:
- // fc[u] > 0, therefore return false if coeffSgn <= 0
- if(coeffSgn <= 0){ return false; }
- break;
- case Equality:
- if(coeffSgn == 0) { return false; }
- break;
- case Disequality:
- default:
- return false;
- }
-
- if(coeffIterator == coeffBegin){ return false; }
- --coeffIterator;
- --p;
- antecedent = d_database->d_antecedents[p];
- }
- if(coeffIterator != coeffBegin){ return false; }
-
- const Rational& firstCoeff = (*coeffBegin);
- int firstCoeffSgn = firstCoeff.sgn();
- rhs += (getNegation()->getValue()) * firstCoeff;
- if(!lhs.isNull() && vars.hasNode(getVariable())){
- Node firstAsNode = vars.asNode(getVariable());
- if(Polynomial::isMember(firstAsNode)){
- Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
- Polynomial firstPoly = Polynomial::parsePolynomial(firstAsNode);
- Polynomial sum = lhsPoly + (firstPoly * firstCoeff);
- lhs = sum.getNode();
- }else{
- lhs = Node::null();
- }
- }else{
- lhs = Node::null();
- }
-
- switch( getNegation()->getType() ){
- case LowerBound:
- // fc[l] < 0, therefore return false if coeffSgn >= 0
- if(firstCoeffSgn >= 0){ return false; }
- break;
- case UpperBound:
- // fc[u] > 0, therefore return false if coeffSgn <= 0
- if(firstCoeffSgn <= 0){ return false; }
- break;
- case Equality:
- if(firstCoeffSgn == 0) { return false; }
- break;
- case Disequality:
- default:
- return false;
- }
- Trace("constraints::wffp") << "final sum: " << lhs << " <= " << rhs << endl;
- // 0 = lhs <= rhs < 0
- return (lhs.isNull() || (Constant::isMember(lhs) && Constant(lhs).isZero()))
- && rhs.sgn() < 0;
-}
-
-ConstraintP Constraint::makeNegation(ArithVar v,
- ConstraintType t,
- const DeltaRational& r,
- bool produceProofs)
-{
- switch(t){
- case LowerBound:
- {
- Assert(r.infinitesimalSgn() >= 0);
- if(r.infinitesimalSgn() > 0){
- Assert(r.getInfinitesimalPart() == 1);
- // make (not (v > r)), which is (v <= r)
- DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
- return new Constraint(v, UpperBound, dropInf, produceProofs);
- }else{
- Assert(r.infinitesimalSgn() == 0);
- // make (not (v >= r)), which is (v < r)
- DeltaRational addInf(r.getNoninfinitesimalPart(), -1);
- return new Constraint(v, UpperBound, addInf, produceProofs);
- }
- }
- case UpperBound:
- {
- Assert(r.infinitesimalSgn() <= 0);
- if(r.infinitesimalSgn() < 0){
- Assert(r.getInfinitesimalPart() == -1);
- // make (not (v < r)), which is (v >= r)
- DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
- return new Constraint(v, LowerBound, dropInf, produceProofs);
- }else{
- Assert(r.infinitesimalSgn() == 0);
- // make (not (v <= r)), which is (v > r)
- DeltaRational addInf(r.getNoninfinitesimalPart(), 1);
- return new Constraint(v, LowerBound, addInf, produceProofs);
- }
- }
- case Equality: return new Constraint(v, Disequality, r, produceProofs);
- case Disequality: return new Constraint(v, Equality, r, produceProofs);
- default: Unreachable(); return NullConstraint;
- }
-}
-
-ConstraintDatabase::ConstraintDatabase(Env& env,
- const ArithVariables& avars,
- ArithCongruenceManager& cm,
- RaiseConflict raiseConflict,
- EagerProofGenerator* pfGen)
- : EnvObj(env),
- d_varDatabases(),
- d_toPropagate(context()),
- d_antecedents(context(), false),
- d_watches(new Watches(context(), userContext())),
- d_avariables(avars),
- d_congruenceManager(cm),
- d_pfGen(pfGen),
- d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
- : nullptr),
- d_raiseConflict(raiseConflict),
- d_one(1),
- d_negOne(-1)
-{
-}
-
-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);
-}
-
-
-void ConstraintDatabase::pushConstraintRule(const ConstraintRule& crp){
- ConstraintP c = crp.d_constraint;
- Assert(c->d_crid == ConstraintRuleIdSentinel);
- Assert(!c->hasProof());
- c->d_crid = d_watches->d_constraintProofs.size();
- d_watches->d_constraintProofs.push_back(crp);
-}
-
-ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){
- //This must always return a constraint.
-
- SortedConstraintMap& scm = getVariableSCM(v);
- pair<SortedConstraintMapIterator, bool> insertAttempt;
- insertAttempt = scm.insert(make_pair(r, ValueCollection()));
-
- SortedConstraintMapIterator pos = insertAttempt.first;
- ValueCollection& vc = pos->second;
- if(vc.hasConstraintOfType(t)){
- return vc.getConstraintOfType(t);
- }else{
- ConstraintP c = new Constraint(v, t, r, options().smt.produceProofs);
- ConstraintP negC =
- Constraint::makeNegation(v, t, r, options().smt.produceProofs);
-
- SortedConstraintMapIterator negPos;
- if(t == Equality || t == Disequality){
- negPos = pos;
- }else{
- pair<SortedConstraintMapIterator, bool> negInsertAttempt;
- negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
- Assert(negInsertAttempt.second
- || !negInsertAttempt.first->second.hasConstraintOfType(
- negC->getType()));
- negPos = negInsertAttempt.first;
- }
-
- c->initialize(this, pos, negC);
- negC->initialize(this, negPos, c);
-
- vc.add(c);
- negPos->second.add(negC);
-
- 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();
- return std::find_if(first, last, PerVariableDatabase::IsEmpty) == last;
-}
-
-ConstraintDatabase::~ConstraintDatabase(){
- delete d_watches;
-
- std::vector<ConstraintP> constraintList;
-
- while(!d_varDatabases.empty()){
- PerVariableDatabase* back = d_varDatabases.back();
-
- SortedConstraintMap& scm = back->d_constraints;
- SortedConstraintMapIterator i = scm.begin(), i_end = scm.end();
- for(; i != i_end; ++i){
- (i->second).push_into(constraintList);
- }
- while(!constraintList.empty()){
- ConstraintP c = constraintList.back();
- constraintList.pop_back();
- delete c;
- }
- Assert(scm.empty());
- d_varDatabases.pop_back();
- delete back;
- }
-
- Assert(d_nodetoConstraintMap.empty());
-}
-
-ConstraintDatabase::Statistics::Statistics()
- : d_unatePropagateCalls(smtStatisticsRegistry().registerInt(
- "theory::arith::cd::unatePropagateCalls")),
- d_unatePropagateImplications(smtStatisticsRegistry().registerInt(
- "theory::arith::cd::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<ConstraintP> constraintList;
-
- for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){
- (i->second).push_into(constraintList);
- }
- while(!constraintList.empty()){
- ConstraintP c = constraintList.back();
- constraintList.pop_back();
- Assert(c->safeToGarbageCollect());
- delete c;
- }
- Assert(scm.empty());
-
- d_reclaimable.remove(v);
- }else{
- Trace("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl;
- Assert(v == d_varDatabases.size());
- d_varDatabases.push_back(new PerVariableDatabase(v));
- }
-}
-
-void ConstraintDatabase::removeVariable(ArithVar v){
- Assert(!d_reclaimable.isMember(v));
- d_reclaimable.add(v);
-}
-
-bool Constraint::safeToGarbageCollect() const{
- // Do not call during destructor as getNegation() may be Null by this point
- Assert(getNegation() != NullConstraint);
- return !contextDependentDataIsSet() && ! getNegation()->contextDependentDataIsSet();
-}
-
-bool Constraint::contextDependentDataIsSet() const{
- return hasProof() || isSplit() || canBePropagated() || assertedToTheTheory();
-}
-
-TrustNode Constraint::split()
-{
- Assert(isEquality() || isDisequality());
-
- bool isEq = isEquality();
-
- ConstraintP eq = isEq ? this : d_negation;
- ConstraintP diseq = isEq ? d_negation : this;
-
- TNode eqNode = eq->getLiteral();
- Assert(eqNode.getKind() == kind::EQUAL);
- TNode lhs = eqNode[0];
- TNode rhs = eqNode[1];
-
- Node leqNode = NodeBuilder(kind::LEQ) << lhs << rhs;
- Node ltNode = NodeBuilder(kind::LT) << lhs << rhs;
- Node gtNode = NodeBuilder(kind::GT) << lhs << rhs;
- Node geqNode = NodeBuilder(kind::GEQ) << lhs << rhs;
-
- Node lemma = NodeBuilder(OR) << leqNode << geqNode;
-
- TrustNode trustedLemma;
- if (d_database->isProofEnabled())
- {
- TypeNode type = lhs.getType();
- // Farkas proof that this works.
- auto nm = NodeManager::currentNM();
- auto nLeqPf = d_database->d_pnm->mkAssume(leqNode.negate());
- auto gtPf = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {nLeqPf}, {gtNode});
- auto nGeqPf = d_database->d_pnm->mkAssume(geqNode.negate());
- auto ltPf = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {nGeqPf}, {ltNode});
- auto sumPf =
- d_database->d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
- {gtPf, ltPf},
- {nm->mkConstRealOrInt(type, Rational(-1)),
- nm->mkConstRealOrInt(type, Rational(1))});
- auto botPf = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
- std::vector<Node> a = {leqNode.negate(), geqNode.negate()};
- auto notAndNotPf = d_database->d_pnm->mkScope(botPf, a);
- // No need to ensure that the expected node aggrees with `a` because we are
- // not providing an expected node.
- auto orNotNotPf =
- d_database->d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
- auto orPf = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {lemma});
- trustedLemma = d_database->d_pfGen->mkTrustNode(lemma, orPf);
- }
- else
- {
- trustedLemma = TrustNode::mkTrustLemma(lemma);
- }
-
- eq->d_database->pushSplitWatch(eq);
- diseq->d_database->pushSplitWatch(diseq);
-
- return trustedLemma;
-}
-
-bool ConstraintDatabase::hasLiteral(TNode literal) const {
- return lookup(literal) != NullConstraint;
-}
-
-ConstraintP ConstraintDatabase::addLiteral(TNode literal){
- Assert(!hasLiteral(literal));
- bool isNot = (literal.getKind() == NOT);
- Node atomNode = (isNot ? literal[0] : literal);
- Node negationNode = atomNode.notNode();
-
- Assert(!hasLiteral(atomNode));
- Assert(!hasLiteral(negationNode));
- Comparison posCmp = Comparison::parseNormalForm(atomNode);
-
- ConstraintType posType = Constraint::constraintTypeOfComparison(posCmp);
-
- Polynomial nvp = posCmp.normalizedVariablePart();
- ArithVar v = d_avariables.asArithVar(nvp.getNode());
-
- DeltaRational posDR = posCmp.normalizedDeltaRational();
-
- ConstraintP posC =
- new Constraint(v, posType, posDR, options().smt.produceProofs);
-
- Trace("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl;
- Trace("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl;
-
- SortedConstraintMap& scm = getVariableSCM(posC->getVariable());
- pair<SortedConstraintMapIterator, bool> insertAttempt;
- insertAttempt = scm.insert(make_pair(posC->getValue(), ValueCollection()));
-
- SortedConstraintMapIterator posI = insertAttempt.first;
- // If the attempt succeeds, i points to a new empty ValueCollection
- // If the attempt fails, i points to a pre-existing ValueCollection
-
- if(posI->second.hasConstraintOfType(posC->getType())){
- //This is the situation where the ConstraintP exists, but
- //the literal has not been associated with it.
- ConstraintP hit = posI->second.getConstraintOfType(posC->getType());
- Trace("arith::constraint") << "hit " << hit << endl;
- Trace("arith::constraint") << "posC " << posC << endl;
-
- delete posC;
-
- hit->setLiteral(atomNode);
- hit->getNegation()->setLiteral(negationNode);
- return isNot ? hit->getNegation(): hit;
- }else{
- Comparison negCmp = Comparison::parseNormalForm(negationNode);
-
- ConstraintType negType = Constraint::constraintTypeOfComparison(negCmp);
- DeltaRational negDR = negCmp.normalizedDeltaRational();
-
- ConstraintP negC =
- new Constraint(v, negType, negDR, options().smt.produceProofs);
-
- SortedConstraintMapIterator negI;
-
- if(posC->isEquality()){
- negI = posI;
- }else{
- Assert(posC->isLowerBound() || posC->isUpperBound());
-
- pair<SortedConstraintMapIterator, bool> negInsertAttempt;
- negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
-
- Trace("nf::tmp") << "sdhjfgdhjkldfgljkhdfg" << endl;
- Trace("nf::tmp") << negC << endl;
- Trace("nf::tmp") << negC->getValue() << endl;
-
- //This should always succeed as the DeltaRational for the negation is unique!
- Assert(negInsertAttempt.second);
-
- negI = negInsertAttempt.first;
- }
-
- (posI->second).add(posC);
- (negI->second).add(negC);
-
- posC->initialize(this, posI, negC);
- negC->initialize(this, negI, posC);
-
- posC->setLiteral(atomNode);
- negC->setLiteral(negationNode);
-
- return isNot ? negC : posC;
- }
-}
-
-
-ConstraintP ConstraintDatabase::lookup(TNode literal) const{
- NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal);
- if(iter == d_nodetoConstraintMap.end()){
- return NullConstraint;
- }else{
- return iter->second;
- }
-}
-
-void Constraint::setAssumption(bool nowInConflict){
- Trace("constraints::pf") << "setAssumption(" << this << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(hasLiteral());
- Assert(assertedToTheTheory());
-
- d_database->pushConstraintRule(ConstraintRule(this, AssumeAP));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@setAssumption " << this << std::endl;
- }
-}
-
-void Constraint::tryToPropagate(){
- Assert(hasProof());
- Assert(!isAssumption());
- Assert(!isInternalAssumption());
-
- if(canBePropagated() && !assertedToTheTheory() && !isAssumption() && !isInternalAssumption()){
- propagate();
- }
-}
-
-void Constraint::propagate(){
- Assert(hasProof());
- Assert(canBePropagated());
- Assert(!assertedToTheTheory());
- Assert(!isAssumption());
- Assert(!isInternalAssumption());
-
- d_database->d_toPropagate.push(this);
-}
-
-
-/*
- * Example:
- * x <= a and a < b
- * |= x <= b
- * ---
- * 1*(x <= a) + (-1)*(x > b) => (0 <= a-b)
- */
-void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){
- Trace("constraints::pf") << "impliedByUnate(" << this << ", " << *imp << ")" << std::endl;
- Assert(!hasProof());
- Assert(imp->hasProof());
- Assert(negationHasProof() == nowInConflict);
-
- d_database->d_antecedents.push_back(NullConstraint);
- d_database->d_antecedents.push_back(imp);
-
- AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-
- RationalVectorP coeffs;
- if (d_produceProofs)
- {
- std::pair<int, int> sgns = unateFarkasSigns(getNegation(), imp);
-
- Rational first(sgns.first);
- Rational second(sgns.second);
-
- coeffs = new RationalVector();
- coeffs->push_back(first);
- coeffs->push_back(second);
- }
- else
- {
- coeffs = RationalVectorPSentinel;
- }
- // no need to delete coeffs the memory is owned by ConstraintRule
- d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffs));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@impliedByUnate " << this << std::endl;
- }
-
- if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
- getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
- }
- Assert(wellFormedFarkasProof());
-}
-
-void Constraint::impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool nowInConflict){
- Trace("constraints::pf") << "impliedByTrichotomy(" << this << ", " << *a << ", ";
- Trace("constraints::pf") << *b << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(a->hasProof());
- Assert(b->hasProof());
-
- d_database->d_antecedents.push_back(NullConstraint);
- d_database->d_antecedents.push_back(a);
- d_database->d_antecedents.push_back(b);
-
- AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
- d_database->pushConstraintRule(ConstraintRule(this, TrichotomyAP, antecedentEnd));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@impliedByTrichotomy " << this << std::endl;
- }
-}
-
-
-bool Constraint::allHaveProof(const ConstraintCPVec& b){
- for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
- ConstraintCP cp = *i;
- if(! (cp->hasProof())){ return false; }
- }
- return true;
-}
-
-void Constraint::impliedByIntTighten(ConstraintCP a, bool nowInConflict){
- Trace("constraints::pf") << "impliedByIntTighten(" << this << ", " << *a << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(a->hasProof());
- Trace("pf::arith") << "impliedByIntTighten(" << this << ", " << a << ")"
- << std::endl;
-
- d_database->d_antecedents.push_back(NullConstraint);
- d_database->d_antecedents.push_back(a);
- AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
- d_database->pushConstraintRule(ConstraintRule(this, IntTightenAP, antecedentEnd));
-
- Assert(inConflict() == nowInConflict);
- if(inConflict()){
- Trace("constraint::conflictCommit") << "inConflict impliedByIntTighten" << this << std::endl;
- }
-}
-
-void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){
- Trace("constraints::pf") << "impliedByIntHole(" << this << ", " << *a << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(a->hasProof());
- Trace("pf::arith") << "impliedByIntHole(" << this << ", " << a << ")"
- << std::endl;
-
- d_database->d_antecedents.push_back(NullConstraint);
- d_database->d_antecedents.push_back(a);
- AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
- d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict impliedByIntHole" << this << std::endl;
- }
-}
-
-void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){
- Trace("constraints::pf") << "impliedByIntHole(" << this;
- if (TraceIsOn("constraints::pf")) {
- for (const ConstraintCP& p : b)
- {
- Trace("constraints::pf") << ", " << p;
- }
- }
- Trace("constraints::pf") << ")" << std::endl;
-
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(allHaveProof(b));
-
- CDConstraintList& antecedents = d_database->d_antecedents;
- antecedents.push_back(NullConstraint);
- for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
- antecedents.push_back(*i);
- }
- AntecedentId antecedentEnd = antecedents.size() - 1;
-
- d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@impliedByIntHole[vec] " << this << std::endl;
- }
-}
-
-/*
- * If proofs are off, coeffs == RationalVectorSentinal.
- * If proofs are on,
- * coeffs != RationalVectorSentinal,
- * coeffs->size() = a.size() + 1,
- * for i in [0,a.size) : coeff[i] corresponds to a[i], and
- * coeff.back() corresponds to the current constraint.
- */
-void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){
- Trace("constraints::pf") << "impliedByFarkas(" << this;
- if (TraceIsOn("constraints::pf")) {
- for (const ConstraintCP& p : a)
- {
- Trace("constraints::pf") << ", " << p;
- }
- }
- Trace("constraints::pf") << ", <coeffs>";
- Trace("constraints::pf") << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(allHaveProof(a));
-
- Assert(d_produceProofs == (coeffs != RationalVectorCPSentinel));
- Assert(!d_produceProofs || coeffs->size() == a.size() + 1);
-
- Assert(a.size() >= 1);
-
- d_database->d_antecedents.push_back(NullConstraint);
- for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){
- ConstraintCP c_i = *i;
- Assert(c_i->hasProof());
- d_database->d_antecedents.push_back(c_i);
- }
- AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
-
- RationalVectorCP coeffsCopy;
- if (d_produceProofs)
- {
- Assert(coeffs != RationalVectorCPSentinel);
- coeffsCopy = new RationalVector(*coeffs);
- }
- else
- {
- coeffsCopy = RationalVectorCPSentinel;
- }
- d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffsCopy));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@impliedByFarkas " << this << std::endl;
- }
- if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
- getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
- }
- Assert(wellFormedFarkasProof());
-}
-
-
-void Constraint::setInternalAssumption(bool nowInConflict){
- Trace("constraints::pf") << "setInternalAssumption(" << this;
- Trace("constraints::pf") << ")" << std::endl;
- Assert(!hasProof());
- Assert(negationHasProof() == nowInConflict);
- Assert(!assertedToTheTheory());
-
- d_database->pushConstraintRule(ConstraintRule(this, InternalAssumeAP));
-
- Assert(inConflict() == nowInConflict);
- if(TraceIsOn("constraint::conflictCommit") && inConflict()){
- Trace("constraint::conflictCommit") << "inConflict@setInternalAssumption " << this << std::endl;
- }
-}
-
-
-void Constraint::setEqualityEngineProof(){
- Trace("constraints::pf") << "setEqualityEngineProof(" << this;
- Trace("constraints::pf") << ")" << std::endl;
- Assert(truthIsUnknown());
- Assert(hasLiteral());
- d_database->pushConstraintRule(ConstraintRule(this, EqualityEngineAP));
-}
-
-
-SortedConstraintMap& Constraint::constraintSet() const{
- Assert(d_database->variableDatabaseIsSetup(d_variable));
- return (d_database->d_varDatabases[d_variable])->d_constraints;
-}
-
-bool Constraint::antecentListIsEmpty() const{
- Assert(hasProof());
- return d_database->d_antecedents[getEndAntecedent()] == NullConstraint;
-}
-
-bool Constraint::antecedentListLengthIsOne() const {
- Assert(hasProof());
- return !antecentListIsEmpty() &&
- d_database->d_antecedents[getEndAntecedent()-1] == NullConstraint;
-}
-
-Node Constraint::externalImplication(const ConstraintCPVec& b) const{
- Assert(hasLiteral());
- Node antecedent = externalExplainByAssertions(b);
- Node implied = getLiteral();
- return antecedent.impNode(implied);
-}
-
-
-Node Constraint::externalExplainByAssertions(const ConstraintCPVec& b){
- return externalExplain(b, AssertionOrderSentinel);
-}
-
-TrustNode Constraint::externalExplainForPropagation(TNode lit) const
-{
- Assert(hasProof());
- Assert(!isAssumption());
- Assert(!isInternalAssumption());
- NodeBuilder nb(Kind::AND);
- auto pfFromAssumptions = externalExplain(nb, d_assertionOrder);
- Node n = mkAndFromBuilder(nb);
- if (d_database->isProofEnabled())
- {
- std::vector<Node> assumptions;
- if (n.getKind() == Kind::AND)
- {
- assumptions.insert(assumptions.end(), n.begin(), n.end());
- }
- else
- {
- assumptions.push_back(n);
- }
- if (getProofLiteral() != lit)
- {
- pfFromAssumptions = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {lit});
- }
- auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
- return d_database->d_pfGen->mkTrustedPropagation(
- lit, NodeManager::currentNM()->mkAnd(assumptions), pf);
- }
- else
- {
- return TrustNode::mkTrustPropExp(lit, n);
- }
-}
-
-TrustNode Constraint::externalExplainConflict() const
-{
- Trace("pf::arith::explain") << this << std::endl;
- Assert(inConflict());
- NodeBuilder nb(kind::AND);
- auto pf1 = externalExplainByAssertions(nb);
- auto not2 = getNegation()->getProofLiteral().negate();
- auto pf2 = getNegation()->externalExplainByAssertions(nb);
- Node n = mkAndFromBuilder(nb);
- if (d_database->isProofEnabled())
- {
- auto pfNot2 = d_database->d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pf1}, {not2});
- std::vector<Node> lits;
- if (n.getKind() == Kind::AND)
- {
- lits.insert(lits.end(), n.begin(), n.end());
- }
- else
- {
- lits.push_back(n);
- }
- if (TraceIsOn("arith::pf::externalExplainConflict"))
- {
- Trace("arith::pf::externalExplainConflict") << "Lits:" << std::endl;
- for (const auto& l : lits)
- {
- Trace("arith::pf::externalExplainConflict") << " : " << l << std::endl;
- }
- }
- std::vector<Node> contraLits = {getProofLiteral(),
- getNegation()->getProofLiteral()};
- auto bot =
- not2.getKind() == Kind::NOT
- ? d_database->d_pnm->mkNode(PfRule::CONTRA, {pf2, pfNot2}, {})
- : d_database->d_pnm->mkNode(PfRule::CONTRA, {pfNot2, pf2}, {});
- if (TraceIsOn("arith::pf::tree"))
- {
- Trace("arith::pf::tree") << *this << std::endl;
- Trace("arith::pf::tree") << *getNegation() << std::endl;
- Trace("arith::pf::tree") << "\n\nTree:\n";
- printProofTree(Trace("arith::pf::tree"));
- getNegation()->printProofTree(Trace("arith::pf::tree"));
- }
- auto confPf = d_database->d_pnm->mkScope(bot, lits);
- return d_database->d_pfGen->mkTrustNode(
- NodeManager::currentNM()->mkAnd(lits), confPf, true);
- }
- else
- {
- return TrustNode::mkTrustConflict(n);
- }
-}
-
-struct ConstraintCPHash {
- /* Todo replace with an id */
- size_t operator()(ConstraintCP c) const{
- Assert(sizeof(ConstraintCP) > 0);
- return ((size_t)c)/sizeof(ConstraintCP);
- }
-};
-
-void Constraint::assertionFringe(ConstraintCPVec& v){
- unordered_set<ConstraintCP, ConstraintCPHash> visited;
- size_t writePos = 0;
-
- if(!v.empty()){
- const ConstraintDatabase* db = v.back()->d_database;
- const CDConstraintList& antecedents = db->d_antecedents;
- 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->hasTrichotomyProof() || vi->hasFarkasProof()
- || vi->hasIntHoleProof() || vi->hasIntTightenProof());
- AntecedentId p = vi->getEndAntecedent();
-
- ConstraintCP antecedent = antecedents[p];
- while(antecedent != NullConstraint){
- v.push_back(antecedent);
- --p;
- antecedent = antecedents[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 mkAndFromBuilder(nb);
-}
-
-std::shared_ptr<ProofNode> Constraint::externalExplain(
- NodeBuilder& nb, AssertionOrder order) const
-{
- if (TraceIsOn("pf::arith::explain"))
- {
- this->printProofTree(Trace("arith::pf::tree"));
- Trace("pf::arith::explain") << "Explaining: " << this << " with rule ";
- getConstraintRule().print(Trace("pf::arith::explain"), d_produceProofs);
- Trace("pf::arith::explain") << std::endl;
- }
- Assert(hasProof());
- Assert(!isAssumption() || assertedToTheTheory());
- Assert(!isInternalAssumption());
- std::shared_ptr<ProofNode> pf{};
-
- ProofNodeManager* pnm = d_database->d_pnm;
-
- if (assertedBefore(order))
- {
- Trace("pf::arith::explain") << " already asserted" << std::endl;
- nb << getWitness();
- if (d_database->isProofEnabled())
- {
- pf = pnm->mkAssume(getWitness());
- // If the witness and literal differ, prove the difference through a
- // rewrite.
- if (getWitness() != getProofLiteral())
- {
- pf = pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {getProofLiteral()});
- }
- }
- }
- else if (hasEqualityEngineProof())
- {
- Trace("pf::arith::explain") << " going to ee:" << std::endl;
- TrustNode exp = d_database->eeExplain(this);
- if (d_database->isProofEnabled())
- {
- Assert(exp.getProven().getKind() == Kind::IMPLIES);
- std::vector<std::shared_ptr<ProofNode>> hypotheses;
- hypotheses.push_back(exp.getGenerator()->getProofFor(exp.getProven()));
- if (exp.getNode().getKind() == Kind::AND)
- {
- for (const auto& h : exp.getNode())
- {
- hypotheses.push_back(
- pnm->mkNode(PfRule::TRUE_INTRO, {pnm->mkAssume(h)}, {}));
- }
- }
- else
- {
- hypotheses.push_back(pnm->mkNode(
- PfRule::TRUE_INTRO, {pnm->mkAssume(exp.getNode())}, {}));
- }
- pf = pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {hypotheses}, {getProofLiteral()});
- }
- Trace("pf::arith::explain")
- << " explanation: " << exp.getNode() << std::endl;
- if (exp.getNode().getKind() == Kind::AND)
- {
- nb.append(exp.getNode().begin(), exp.getNode().end());
- }
- else
- {
- nb << exp.getNode();
- }
- }
- else
- {
- Trace("pf::arith::explain") << " recursion!" << std::endl;
- Assert(!isAssumption());
- AntecedentId p = getEndAntecedent();
- ConstraintCP antecedent = d_database->d_antecedents[p];
- std::vector<std::shared_ptr<ProofNode>> children;
-
- while (antecedent != NullConstraint)
- {
- Trace("pf::arith::explain") << "Explain " << antecedent << std::endl;
- auto pn = antecedent->externalExplain(nb, order);
- if (d_database->isProofEnabled())
- {
- children.push_back(pn);
- }
- --p;
- antecedent = d_database->d_antecedents[p];
- }
-
- if (d_database->isProofEnabled())
- {
- switch (getProofType())
- {
- case ArithProofType::AssumeAP:
- case ArithProofType::EqualityEngineAP:
- {
- Unreachable() << "These should be handled above";
- break;
- }
- case ArithProofType::FarkasAP:
- {
- // Per docs in constraint.h,
- // the 0th farkas coefficient is for the negation of the deduced
- // constraint the 1st corresponds to the last antecedent the nth
- // corresponds to the first antecedent Then, the farkas coefficients
- // and the antecedents are in the same order.
-
- // Enumerate child proofs (negation included) in d_farkasCoefficients
- // order
- Node plit = getNegation()->getProofLiteral();
- std::vector<std::shared_ptr<ProofNode>> farkasChildren;
- farkasChildren.push_back(pnm->mkAssume(plit));
- farkasChildren.insert(
- farkasChildren.end(), children.rbegin(), children.rend());
-
- NodeManager* nm = NodeManager::currentNM();
-
- // Enumerate d_farkasCoefficients as nodes.
- std::vector<Node> farkasCoeffs;
- TypeNode type = plit[0].getType();
- for (Rational r : *getFarkasCoefficients())
- {
- farkasCoeffs.push_back(nm->mkConstRealOrInt(type, Rational(r)));
- }
-
- // Apply the scaled-sum rule.
- std::shared_ptr<ProofNode> sumPf = pnm->mkNode(
- PfRule::MACRO_ARITH_SCALE_SUM_UB, farkasChildren, farkasCoeffs);
-
- // Provable rewrite the result
- auto botPf = pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-
- // Scope out the negated constraint, yielding a proof of the
- // constraint.
- std::vector<Node> assump{plit};
- auto maybeDoubleNotPf = pnm->mkScope(botPf, assump, false);
-
- // No need to ensure that the expected node aggrees with `assump`
- // because we are not providing an expected node.
- //
- // Prove that this is the literal (may need to clean a double-not)
- pf = pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
- {maybeDoubleNotPf},
- {getProofLiteral()});
-
- break;
- }
- case ArithProofType::IntTightenAP:
- {
- if (isUpperBound())
- {
- pf = pnm->mkNode(
- PfRule::INT_TIGHT_UB, children, {}, getProofLiteral());
- }
- else if (isLowerBound())
- {
- pf = pnm->mkNode(
- PfRule::INT_TIGHT_LB, children, {}, getProofLiteral());
- }
- else
- {
- Unreachable();
- }
- break;
- }
- case ArithProofType::IntHoleAP:
- {
- Node t =
- builtin::BuiltinProofRuleChecker::mkTheoryIdNode(THEORY_ARITH);
- pf = pnm->mkNode(PfRule::THEORY_INFERENCE,
- children,
- {getProofLiteral(), t},
- getProofLiteral());
- break;
- }
- case ArithProofType::TrichotomyAP:
- {
- pf = pnm->mkNode(PfRule::ARITH_TRICHOTOMY,
- children,
- {getProofLiteral()},
- getProofLiteral());
- break;
- }
- case ArithProofType::InternalAssumeAP:
- case ArithProofType::NoAP:
- default:
- {
- Unreachable() << getProofType()
- << " should not be visible in explanation";
- break;
- }
- }
- }
- }
- return pf;
-}
-
-Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){
- NodeBuilder nb(kind::AND);
- a->externalExplainByAssertions(nb);
- b->externalExplainByAssertions(nb);
- return nb;
-}
-
-Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){
- NodeBuilder nb(kind::AND);
- a->externalExplainByAssertions(nb);
- b->externalExplainByAssertions(nb);
- c->externalExplainByAssertions(nb);
- return nb;
-}
-
-ConstraintP Constraint::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const {
- Assert(initialized());
- Assert(!asserted || hasLiteral);
-
- SortedConstraintMapConstIterator i = d_variablePosition;
- const SortedConstraintMap& scm = constraintSet();
- SortedConstraintMapConstIterator i_begin = scm.begin();
- while(i != i_begin){
- --i;
- const ValueCollection& vc = i->second;
- if(vc.hasLowerBound()){
- ConstraintP weaker = vc.getLowerBound();
-
- // asserted -> hasLiteral
- // hasLiteral -> weaker->hasLiteral()
- // asserted -> weaker->assertedToTheTheory()
- if((!hasLiteral || (weaker->hasLiteral())) &&
- (!asserted || ( weaker->assertedToTheTheory()))){
- return weaker;
- }
- }
- }
- return NullConstraint;
-}
-
-ConstraintP Constraint::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const {
- SortedConstraintMapConstIterator i = d_variablePosition;
- const SortedConstraintMap& scm = constraintSet();
- SortedConstraintMapConstIterator i_end = scm.end();
-
- ++i;
- for(; i != i_end; ++i){
- const ValueCollection& vc = i->second;
- if(vc.hasUpperBound()){
- ConstraintP weaker = vc.getUpperBound();
- if((!hasLiteral || (weaker->hasLiteral())) &&
- (!asserted || ( weaker->assertedToTheTheory()))){
- return weaker;
- }
- }
- }
-
- return NullConstraint;
-}
-
-ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const {
- Assert(variableDatabaseIsSetup(v));
- Assert(t == UpperBound || t == LowerBound);
-
- SortedConstraintMap& scm = getVariableSCM(v);
- if(t == UpperBound){
- SortedConstraintMapConstIterator i = scm.lower_bound(r);
- SortedConstraintMapConstIterator i_end = scm.end();
- Assert(i == i_end || r <= i->first);
- for(; i != i_end; i++){
- Assert(r <= i->first);
- const ValueCollection& vc = i->second;
- if(vc.hasUpperBound()){
- return vc.getUpperBound();
- }
- }
- return NullConstraint;
- }else{
- Assert(t == LowerBound);
- if(scm.empty()){
- return NullConstraint;
- }else{
- SortedConstraintMapConstIterator i = scm.lower_bound(r);
- SortedConstraintMapConstIterator i_begin = scm.begin();
- SortedConstraintMapConstIterator i_end = scm.end();
- Assert(i == i_end || r <= i->first);
-
- int fdj = 0;
-
- if(i == i_end){
- --i;
- Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
- }else if( (i->first) > r){
- if(i == i_begin){
- return NullConstraint;
- }else{
- --i;
- Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
- }
- }
-
- do{
- Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
- Assert(r >= i->first);
- const ValueCollection& vc = i->second;
-
- if(vc.hasLowerBound()){
- return vc.getLowerBound();
- }
-
- if(i == i_begin){
- break;
- }else{
- --i;
- }
- }while(true);
- return NullConstraint;
- }
- }
-}
-TrustNode ConstraintDatabase::eeExplain(const Constraint* const c) const
-{
- Assert(c->hasLiteral());
- return d_congruenceManager.explain(c->getLiteral());
-}
-
-void ConstraintDatabase::eeExplain(ConstraintCP c, NodeBuilder& nb) const
-{
- Assert(c->hasLiteral());
- // NOTE: this is not a recommended method since it ignores proofs
- d_congruenceManager.explain(c->getLiteral(), nb);
-}
-
-bool ConstraintDatabase::variableDatabaseIsSetup(ArithVar v) const {
- return v < d_varDatabases.size();
-}
-
-ConstraintDatabase::Watches::Watches(context::Context* satContext,
- context::Context* userContext)
- : d_constraintProofs(satContext),
- d_canBePropagatedWatches(satContext),
- d_assertionOrderWatches(satContext),
- d_splitWatches(userContext)
-{}
-
-
-void Constraint::setLiteral(Node n) {
- Trace("arith::constraint") << "Mapping " << *this << " to " << n << std::endl;
- Assert(Comparison::isNormalAtom(n));
- Assert(!hasLiteral());
- Assert(sanityChecking(n));
- d_literal = n;
- NodetoConstraintMap& map = d_database->d_nodetoConstraintMap;
- Assert(map.find(n) == map.end());
- map.insert(make_pair(d_literal, this));
-}
-
-Node Constraint::getProofLiteral() const
-{
- Assert(d_database != nullptr);
- Assert(d_database->d_avariables.hasNode(d_variable));
- Node varPart = d_database->d_avariables.asNode(d_variable);
- Kind cmp;
- bool neg = false;
- switch (d_type)
- {
- case ConstraintType::UpperBound:
- {
- if (d_value.infinitesimalIsZero())
- {
- cmp = Kind::LEQ;
- }
- else
- {
- cmp = Kind::LT;
- }
- break;
- }
- case ConstraintType::LowerBound:
- {
- if (d_value.infinitesimalIsZero())
- {
- cmp = Kind::GEQ;
- }
- else
- {
- cmp = Kind::GT;
- }
- break;
- }
- case ConstraintType::Equality:
- {
- cmp = Kind::EQUAL;
- break;
- }
- case ConstraintType::Disequality:
- {
- cmp = Kind::EQUAL;
- neg = true;
- break;
- }
- default: Unreachable() << d_type;
- }
- NodeManager* nm = NodeManager::currentNM();
- Node constPart = nm->mkConstRealOrInt(
- varPart.getType(), Rational(d_value.getNoninfinitesimalPart()));
- Node posLit = nm->mkNode(cmp, varPart, constPart);
- return neg ? posLit.negate() : posLit;
-}
-
-void ConstraintDatabase::proveOr(std::vector<TrustNode>& out,
- ConstraintP a,
- ConstraintP b,
- bool negateSecond) const
-{
- Node la = a->getLiteral();
- Node lb = b->getLiteral();
- Node orN = (la < lb) ? la.orNode(lb) : lb.orNode(la);
- if (isProofEnabled())
- {
- Assert(b->getNegation()->getType() != ConstraintType::Disequality);
- auto nm = NodeManager::currentNM();
- Node alit = a->getNegation()->getProofLiteral();
- TypeNode type = alit[0].getType();
- auto pf_neg_la = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkAssume(la.negate())},
- {alit});
- Node blit = b->getNegation()->getProofLiteral();
- auto pf_neg_lb = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkAssume(lb.negate())},
- {blit});
- int sndSign = negateSecond ? -1 : 1;
- auto bot_pf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
- {pf_neg_la, pf_neg_lb},
- {nm->mkConstRealOrInt(type, Rational(-1 * sndSign)),
- nm->mkConstRealOrInt(type, Rational(sndSign))})},
- {nm->mkConst(false)});
- std::vector<Node> as;
- std::transform(orN.begin(), orN.end(), std::back_inserter(as), [](Node n) {
- return n.negate();
- });
- // No need to ensure that the expected node aggrees with `as` because we
- // are not providing an expected node.
- auto pf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkNode(PfRule::NOT_AND, {d_pnm->mkScope(bot_pf, as)}, {})},
- {orN});
- out.push_back(d_pfGen->mkTrustNode(orN, pf));
- }
- else
- {
- out.push_back(TrustNode::mkTrustLemma(orN));
- }
-}
-
-void ConstraintDatabase::implies(std::vector<TrustNode>& out,
- ConstraintP a,
- ConstraintP b) const
-{
- Node la = a->getLiteral();
- Node lb = b->getLiteral();
-
- Node neg_la = (la.getKind() == kind::NOT)? la[0] : la.notNode();
-
- Assert(lb != neg_la);
- Assert(b->getNegation()->getType() == ConstraintType::LowerBound
- || b->getNegation()->getType() == ConstraintType::UpperBound);
- proveOr(out,
- a->getNegation(),
- b,
- b->getNegation()->getType() == ConstraintType::LowerBound);
-}
-
-void ConstraintDatabase::mutuallyExclusive(std::vector<TrustNode>& out,
- ConstraintP a,
- ConstraintP b) const
-{
- Node la = a->getLiteral();
- Node lb = b->getLiteral();
-
- Node neg_la = la.negate();
- Node neg_lb = lb.negate();
- proveOr(out, a->getNegation(), b->getNegation(), true);
-}
-
-void ConstraintDatabase::outputUnateInequalityLemmas(
- std::vector<TrustNode>& out, ArithVar v) const
-{
- SortedConstraintMap& scm = getVariableSCM(v);
- SortedConstraintMapConstIterator scm_iter = scm.begin();
- SortedConstraintMapConstIterator scm_end = scm.end();
- 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()){
- ConstraintP ub = vc.getUpperBound();
- if(ub->hasLiteral()){
- if(prev != NullConstraint){
- implies(out, prev, ub);
- }
- prev = ub;
- }
- }
- }
-}
-
-void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<TrustNode>& out,
- ArithVar v) const
-{
- vector<ConstraintP> equalities;
-
- SortedConstraintMap& scm = getVariableSCM(v);
- SortedConstraintMapConstIterator scm_iter = scm.begin();
- SortedConstraintMapConstIterator scm_end = scm.end();
-
- for(; scm_iter != scm_end; ++scm_iter){
- const ValueCollection& vc = scm_iter->second;
- if(vc.hasEquality()){
- ConstraintP eq = vc.getEquality();
- if(eq->hasLiteral()){
- equalities.push_back(eq);
- }
- }
- }
-
- vector<ConstraintP>::const_iterator i, j, eq_end = equalities.end();
- for(i = equalities.begin(); i != eq_end; ++i){
- ConstraintP at_i = *i;
- for(j= i + 1; j != eq_end; ++j){
- ConstraintP at_j = *j;
-
- mutuallyExclusive(out, at_i, at_j);
- }
- }
-
- for(i = equalities.begin(); i != eq_end; ++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();
-
- ConstraintP lb = hasLB ?
- vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false);
- ConstraintP ub = hasUB ?
- vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false);
-
- if(hasUB && hasLB && !eq->isSplit()){
- out.push_back(eq->split());
- }
- if(lb != NullConstraint){
- implies(out, eq, lb);
- }
- if(ub != NullConstraint){
- implies(out, eq, ub);
- }
- }
-}
-
-void ConstraintDatabase::outputUnateEqualityLemmas(
- std::vector<TrustNode>& lemmas) const
-{
- for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
- outputUnateEqualityLemmas(lemmas, v);
- }
-}
-
-void ConstraintDatabase::outputUnateInequalityLemmas(
- std::vector<TrustNode>& lemmas) const
-{
- for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
- outputUnateInequalityLemmas(lemmas, v);
- }
-}
-
-bool ConstraintDatabase::handleUnateProp(ConstraintP ant, ConstraintP cons){
- if(cons->negationHasProof()){
- Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
- cons->impliedByUnate(ant, true);
- d_raiseConflict.raiseConflict(cons, InferenceId::ARITH_CONF_UNATE_PROP);
- return true;
- }else if(!cons->isTrue()){
- ++d_statistics.d_unatePropagateImplications;
- Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
- cons->impliedByUnate(ant, false);
- cons->tryToPropagate();
- return false;
- } else {
- return false;
- }
-}
-
-void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){
- Trace("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl;
- Assert(curr != prev);
- Assert(curr != NullConstraint);
- bool hasPrev = ! (prev == NullConstraint);
- Assert(!hasPrev || curr->getValue() > prev->getValue());
-
- ++d_statistics.d_unatePropagateCalls;
-
- const SortedConstraintMap& scm = curr->constraintSet();
- const SortedConstraintMapConstIterator scm_begin = scm.begin();
- SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
-
- //Ignore the first ValueCollection
- // NOPE: (>= p c) then (= p c) NOPE
- // NOPE: (>= p c) then (not (= p c)) NOPE
-
- while(scm_i != scm_begin){
- --scm_i; // move the iterator back
-
- const ValueCollection& vc = scm_i->second;
-
- //If it has the previous element, do nothing and stop!
- if(hasPrev &&
- vc.hasConstraintOfType(prev->getType())
- && vc.getConstraintOfType(prev->getType()) == prev){
- break;
- }
-
- //Don't worry about implying the negation of upperbound.
- //These should all be handled by propagating the LowerBounds!
- if(vc.hasLowerBound()){
- ConstraintP lb = vc.getLowerBound();
- if(handleUnateProp(curr, lb)){ return; }
- }
- if(vc.hasDisequality()){
- ConstraintP dis = vc.getDisequality();
- if(handleUnateProp(curr, dis)){ return; }
- }
- }
-}
-
-void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){
- Trace("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl;
- Assert(curr != prev);
- Assert(curr != NullConstraint);
- bool hasPrev = ! (prev == NullConstraint);
- Assert(!hasPrev || curr->getValue() < prev->getValue());
-
- ++d_statistics.d_unatePropagateCalls;
-
- const SortedConstraintMap& scm = curr->constraintSet();
- const SortedConstraintMapConstIterator scm_end = scm.end();
- SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
- ++scm_i;
- for(; scm_i != scm_end; ++scm_i){
- const ValueCollection& vc = scm_i->second;
-
- //If it has the previous element, do nothing and stop!
- if(hasPrev &&
- vc.hasConstraintOfType(prev->getType()) &&
- vc.getConstraintOfType(prev->getType()) == prev){
- break;
- }
- //Don't worry about implying the negation of upperbound.
- //These should all be handled by propagating the UpperBounds!
- if(vc.hasUpperBound()){
- ConstraintP ub = vc.getUpperBound();
- if(handleUnateProp(curr, ub)){ return; }
- }
- if(vc.hasDisequality()){
- ConstraintP dis = vc.getDisequality();
- if(handleUnateProp(curr, dis)){ return; }
- }
- }
-}
-
-void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){
- Trace("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl;
- Assert(curr != prevLB);
- Assert(curr != prevUB);
- Assert(curr != NullConstraint);
- bool hasPrevLB = ! (prevLB == NullConstraint);
- bool hasPrevUB = ! (prevUB == NullConstraint);
- Assert(!hasPrevLB || curr->getValue() >= prevLB->getValue());
- Assert(!hasPrevUB || curr->getValue() <= prevUB->getValue());
-
- ++d_statistics.d_unatePropagateCalls;
-
- const SortedConstraintMap& scm = curr->constraintSet();
- SortedConstraintMapConstIterator scm_curr = curr->d_variablePosition;
- SortedConstraintMapConstIterator scm_last = hasPrevUB ? prevUB->d_variablePosition : scm.end();
- SortedConstraintMapConstIterator scm_i;
- if(hasPrevLB){
- scm_i = prevLB->d_variablePosition;
- if(scm_i != scm_curr){ // If this does not move this past scm_curr, move it one forward
- ++scm_i;
- }
- }else{
- scm_i = scm.begin();
- }
-
- for(; scm_i != scm_curr; ++scm_i){
- // between the previous LB and the curr
- const ValueCollection& vc = scm_i->second;
-
- //Don't worry about implying the negation of upperbound.
- //These should all be handled by propagating the LowerBounds!
- if(vc.hasLowerBound()){
- ConstraintP lb = vc.getLowerBound();
- if(handleUnateProp(curr, lb)){ return; }
- }
- if(vc.hasDisequality()){
- ConstraintP dis = vc.getDisequality();
- if(handleUnateProp(curr, dis)){ return; }
- }
- }
- Assert(scm_i == scm_curr);
- if(!hasPrevUB || scm_i != scm_last){
- ++scm_i;
- } // hasPrevUB implies scm_i != scm_last
-
- for(; scm_i != scm_last; ++scm_i){
- // between the curr and the previous UB imply the upperbounds and disequalities.
- const ValueCollection& vc = scm_i->second;
-
- //Don't worry about implying the negation of upperbound.
- //These should all be handled by propagating the UpperBounds!
- if(vc.hasUpperBound()){
- ConstraintP ub = vc.getUpperBound();
- if(handleUnateProp(curr, ub)){ return; }
- }
- if(vc.hasDisequality()){
- ConstraintP dis = vc.getDisequality();
- if(handleUnateProp(curr, dis)){ return; }
- }
- }
-}
-
-std::pair<int, int> Constraint::unateFarkasSigns(ConstraintCP ca, ConstraintCP cb){
- ConstraintType a = ca->getType();
- ConstraintType b = cb->getType();
-
- Assert(a != Disequality);
- Assert(b != Disequality);
-
- int a_sgn = (a == LowerBound) ? -1 : ((a == UpperBound) ? 1 : 0);
- int b_sgn = (b == LowerBound) ? -1 : ((b == UpperBound) ? 1 : 0);
-
- if(a_sgn == 0 && b_sgn == 0){
- Assert(a == Equality);
- Assert(b == Equality);
- Assert(ca->getValue() != cb->getValue());
- if(ca->getValue() < cb->getValue()){
- a_sgn = 1;
- b_sgn = -1;
- }else{
- a_sgn = -1;
- b_sgn = 1;
- }
- }else if(a_sgn == 0){
- Assert(b_sgn != 0);
- Assert(a == Equality);
- a_sgn = -b_sgn;
- }else if(b_sgn == 0){
- Assert(a_sgn != 0);
- Assert(b == Equality);
- b_sgn = -a_sgn;
- }
- Assert(a_sgn != 0);
- Assert(b_sgn != 0);
-
- Trace("arith::unateFarkasSigns") << "Constraint::unateFarkasSigns("<<a <<", " << b << ") -> "
- << "("<<a_sgn<<", "<< b_sgn <<")"<< endl;
- return make_pair(a_sgn, b_sgn);
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Alex Ozdemir, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Defines Constraint and ConstraintDatabase which is the internal
- * representation of variables in arithmetic
- *
- * This file defines Constraint and ConstraintDatabase.
- * A Constraint is the internal representation of literals in TheoryArithmetic.
- * Constraints are fundamentally a triple:
- * - ArithVar associated with the constraint,
- * - a DeltaRational value,
- * - and a ConstraintType.
- *
- * Literals:
- * The constraint may also keep track of a node corresponding to the
- * Constraint.
- * This can be accessed by getLiteral() in O(1) if it has been set.
- * This node must be in normal form and may be used for communication with
- * the TheoryEngine.
- *
- * In addition, Constraints keep track of the following:
- * - A Constraint that is the negation of the Constraint.
- * - An iterator into a set of Constraints for the ArithVar sorted by
- * DeltaRational value.
- * - A context dependent internal proof of the node that can be used for
- * explanations.
- * - Whether an equality/disequality has been split in the user context via a
- * lemma.
- * - Whether a constraint, be be used in explanations sent to the context
- *
- * Looking up constraints:
- * - All of the Constraints with associated nodes in the ConstraintDatabase
- * can be accessed via a single hashtable lookup until the Constraint is
- * removed.
- * - Nodes that have not been associated to a constraints can be
- * inserted/associated to existing nodes in O(log n) time.
- *
- * Implications:
- * - A Constraint can be used to find unate implications.
- * - A unate implication is an implication based purely on the ArithVar
- * matching and the DeltaRational value.
- * (implies (<= x c) (<= x d)) given c <= d
- * - This is done using the iterator into the sorted set of constraints.
- * - Given a tight constraint and previous tightest constraint, this will
- * efficiently propagate internally.
- *
- * Additing and Removing Constraints
- * - Adding Constraints takes O(log n) time where n is the number of
- * constraints associated with the ArithVar.
- * - Removing Constraints takes O(1) time.
- *
- * Internals:
- * - Constraints are pointers to ConstraintValues.
- * - Undefined Constraints are NullConstraint.
- *
- * Assumption vs. Assertion:
- * - An assertion is anything on the theory d_fact queue.
- * This includes any thing propagated and returned to the fact queue.
- * These can be used in external conflicts and propagations of earlier
- * proofs.
- * - An assumption is anything on the theory d_fact queue that has no further
- * explanation i.e. this theory did not propagate it.
- * - To set something an assumption, first set it as being as assertion.
- * - Internal assumptions have no explanations and must be regressed out of the
- * proof.
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__CONSTRAINT_H
-#define CVC5__THEORY__ARITH__CONSTRAINT_H
-
-#include <unordered_map>
-#include <vector>
-
-#include "base/configuration_private.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "expr/node.h"
-#include "proof/trust_node.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-
-class ProofNodeManager;
-class EagerProofGenerator;
-
-namespace theory {
-
-namespace arith {
-
-class Comparison;
-class ArithCongruenceManager;
-class ArithVariables;
-
-/**
- * Logs the types of different proofs.
- * Current, proof types:
- * - NoAP : This constraint is not known to be true.
- * - AssumeAP : This is an input assertion. There is no proof.
- * : Something can be both asserted and have a proof.
- * - InternalAssumeAP : An internal assumption. This has no guarantee of having an external proof.
- * : This must be removed by regression.
- * - FarkasAP : A proof with Farka's coefficients, i.e.
- * : \sum lambda_i ( asNode(x_i) <= c_i ) |= 0 < 0
- * : If proofs are on, coefficients will be logged.
- * : If proofs are off, coefficients will not be logged.
- * : A unate implication is a FarkasAP.
- * - TrichotomyAP : This is any entailment using (x<= a and x >=a) => x = a
- * : Equivalently, (x > a or x < a or x = a)
- * : There are 3 candidate ways this can propagate:
- * : !(x > a) and !(x = a) => x < a
- * : !(x < a) and !(x = a) => x > a
- * : !(x > a) and !(x < a) => x = a
- * - EqualityEngineAP : This is propagated by the equality engine.
- * : Consult this for the proof.
- * - IntTightenAP : This is indicates that a bound involving integers was tightened.
- * : e.g. i < 5.5 became i <= 5, when i is an integer.
- * - IntHoleAP : This is currently a catch-all for all integer specific reason.
- */
-enum ArithProofType
- { NoAP,
- AssumeAP,
- InternalAssumeAP,
- FarkasAP,
- TrichotomyAP,
- EqualityEngineAP,
- IntTightenAP,
- IntHoleAP};
-
-/**
- * The types of constraints.
- * The convex constraints are the constraints are LowerBound, Equality,
- * and UpperBound.
- */
-enum ConstraintType {LowerBound, Equality, UpperBound, Disequality};
-
-typedef context::CDList<ConstraintCP> CDConstraintList;
-
-typedef std::unordered_map<Node, ConstraintP> NodetoConstraintMap;
-
-typedef size_t ConstraintRuleID;
-static constexpr ConstraintRuleID ConstraintRuleIdSentinel =
- std::numeric_limits<ConstraintRuleID>::max();
-
-typedef size_t AntecedentId;
-static constexpr AntecedentId AntecedentIdSentinel =
- std::numeric_limits<AntecedentId>::max();
-
-typedef size_t AssertionOrder;
-static constexpr AssertionOrder AssertionOrderSentinel =
- std::numeric_limits<AssertionOrder>::max();
-
-/**
- * A ValueCollection binds together convex constraints that have the same
- * DeltaRational value.
- */
-class ValueCollection {
-private:
-
- ConstraintP d_lowerBound;
- ConstraintP d_upperBound;
- ConstraintP d_equality;
- ConstraintP d_disequality;
-
-public:
- ValueCollection();
-
- static ValueCollection mkFromConstraint(ConstraintP c);
-
- bool hasLowerBound() const;
- bool hasUpperBound() const;
- bool hasEquality() const;
- bool hasDisequality() const;
-
- bool hasConstraintOfType(ConstraintType t) const;
-
- ConstraintP getLowerBound() const;
- ConstraintP getUpperBound() const;
- ConstraintP getEquality() const;
- ConstraintP getDisequality() const;
-
- ConstraintP getConstraintOfType(ConstraintType t) const;
-
- /** Returns true if any of the constraints are non-null. */
- bool empty() const;
-
- /**
- * Remove the constraint of the type t from the collection.
- * Returns true if the ValueCollection is now empty.
- * If true is returned, d_value is now NULL.
- */
- void remove(ConstraintType t);
-
- /**
- * Adds a constraint to the set.
- * The collection must not have a constraint of that type already.
- */
- void add(ConstraintP c);
-
- void push_into(std::vector<ConstraintP>& vec) const;
-
- ConstraintP nonNull() const;
-
- ArithVar getVariable() const;
- const DeltaRational& getValue() const;
-};
-
-/**
- * A Map of ValueCollections sorted by the associated DeltaRational values.
- *
- * Discussion:
- * While it is more natural to consider this a set, this cannot be a set as in
- * sets the type of both iterator and const_iterator in sets are
- * "constant iterators". We require iterators that dereference to
- * ValueCollection&.
- *
- * See:
- * http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#103
- */
-typedef std::map<DeltaRational, ValueCollection> SortedConstraintMap;
-typedef SortedConstraintMap::iterator SortedConstraintMapIterator;
-typedef SortedConstraintMap::const_iterator SortedConstraintMapConstIterator;
-
-/** A Pair associating a variables and a Sorted ConstraintSet. */
-struct PerVariableDatabase{
- ArithVar d_var;
- SortedConstraintMap d_constraints;
-
- // x ? c_1, x ? c_2, x ? c_3, ...
- // where ? is a non-empty subset of {lb, ub, eq}
- // c_1 < c_2 < c_3 < ...
-
- PerVariableDatabase(ArithVar v) : d_var(v), d_constraints() {}
-
- bool empty() const {
- return d_constraints.empty();
- }
-
- static bool IsEmpty(const PerVariableDatabase& p){
- return p.empty();
- }
-};
-
-/**
- * If proofs are on, there is a vector of rationals for farkas coefficients.
- * This is the owner of the memory for the vector, and calls delete upon
- * cleanup.
- *
- */
-struct ConstraintRule {
- ConstraintP d_constraint;
- ArithProofType d_proofType;
- AntecedentId d_antecedentEnd;
-
- /**
- * In this comment, we abbreviate ConstraintDatabase::d_antecedents
- * and d_farkasCoefficients as ans and fc.
- *
- * This list is always empty if proofs are not enabled.
- *
- * If proofs are enabled, the proof of constraint c at p in ans[p] of length n
- * is (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
- *
- * Farkas' proofs show a contradiction with the negation of c, c_not =
- * c->getNegation().
- *
- * We treat the position for NullConstraint (p-n) as the position for the
- * farkas coefficient for so we pretend c_not is ans[p-n]. So this correlation
- * for the constraints we are going to use: (c_not, ans[p-n+(1)], ... ,
- * ans[p-n+(n-1)], ans[p-n+(n)]) With the coefficients at positions: (fc[0],
- * fc[1)], ... fc[n])
- *
- * The index of the constraints in the proof are {i | i <= 0 <= n] } (with
- * c_not being p-n). Partition the indices into L, U, and E, the lower bounds,
- * the upper bounds and equalities.
- *
- * We standardize the proofs to be upper bound oriented following the
- * convention: A x <= b with the proof witness of the form (lambda) Ax <=
- * (lambda) b and lambda >= 0.
- *
- * To accomplish this cleanly, the fc coefficients must be negative for lower
- * bounds. The signs of equalities can be either positive or negative.
- *
- * Thus the proof corresponds to (with multiplication over inequalities):
- * \sum_{u in U} fc[u] ans[p-n+u] + \sum_{e in E} fc[e] ans[p-n+e]
- * + \sum_{l in L} fc[l] ans[p-n+l]
- * |= 0 < 0
- * where fc[u] > 0, fc[l] < 0, and fc[e] != 0 (i.e. it can be either +/-).
- *
- * There is no requirement that the proof is minimal.
- * We do however use all of the constraints by requiring non-zero
- * coefficients.
- */
- RationalVectorCP d_farkasCoefficients;
-
- ConstraintRule();
- ConstraintRule(ConstraintP con, ArithProofType pt);
- ConstraintRule(ConstraintP con,
- ArithProofType pt,
- AntecedentId antecedentEnd);
- ConstraintRule(ConstraintP con,
- ArithProofType pt,
- AntecedentId antecedentEnd,
- RationalVectorCP coeffs);
-
- void print(std::ostream& out, bool produceProofs) const;
-}; /* class ConstraintRule */
-
-class Constraint {
-
- friend class ConstraintDatabase;
-
- public:
- /**
- * This begins construction of a minimal constraint.
- *
- * This should only be called by ConstraintDatabase.
- *
- * Because of circular dependencies a Constraint is not fully valid until
- * initialize has been called on it.
- */
- Constraint(ArithVar x,
- ConstraintType t,
- const DeltaRational& v,
- bool produceProofs);
-
- /**
- * Destructor for a constraint.
- * This should only be called if safeToGarbageCollect() is true.
- */
- ~Constraint();
-
- static ConstraintType constraintTypeOfComparison(const Comparison& cmp);
-
- inline ConstraintType getType() const {
- return d_type;
- }
-
- inline ArithVar getVariable() const {
- return d_variable;
- }
-
- const DeltaRational& getValue() const {
- return d_value;
- }
-
- inline ConstraintP getNegation() const {
- return d_negation;
- }
-
- bool isEquality() const{
- return d_type == Equality;
- }
- bool isDisequality() const{
- return d_type == Disequality;
- }
- bool isLowerBound() const{
- return d_type == LowerBound;
- }
- bool isUpperBound() const{
- return d_type == UpperBound;
- }
- bool isStrictUpperBound() const{
- Assert(isUpperBound());
- return getValue().infinitesimalSgn() < 0;
- }
-
- bool isStrictLowerBound() const{
- Assert(isLowerBound());
- return getValue().infinitesimalSgn() > 0;
- }
-
- bool isSplit() const {
- return d_split;
- }
-
- /**
- * Splits the node in 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.
- */
- TrustNode split();
-
- bool canBePropagated() const {
- return d_canBePropagated;
- }
- void setCanBePropagated();
-
- /**
- * Light wrapper for calling setCanBePropagated(),
- * on this and this->d_negation.
- */
- void setPreregistered(){
- setCanBePropagated();
- d_negation->setCanBePropagated();
- }
-
- bool assertedToTheTheory() const {
- Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull());
- return d_assertionOrder < AssertionOrderSentinel;
- }
- TNode getWitness() const {
- Assert(assertedToTheTheory());
- return d_witness;
- }
-
- bool assertedBefore(AssertionOrder time) const {
- return d_assertionOrder < time;
- }
-
- /**
- * Sets the witness literal for a node being on the assertion stack.
- *
- * If the negation of the node is true, inConflict must be true.
- * If the negation of the node is false, inConflict must be false.
- * Hence, negationHasProof() == inConflict.
- *
- * This replaces:
- * void setAssertedToTheTheory(TNode witness);
- * void setAssertedToTheTheoryWithNegationTrue(TNode witness);
- */
- void setAssertedToTheTheory(TNode witness, bool inConflict);
-
- bool hasLiteral() const {
- return !d_literal.isNull();
- }
-
- void setLiteral(Node n);
-
- Node getLiteral() const {
- Assert(hasLiteral());
- return d_literal;
- }
-
- /** Gets a literal in the normal form suitable for proofs.
- * That is, (sum of non-const monomials) >< const.
- *
- * This is a sister method to `getLiteral`, which returns a normal form
- * literal, suitable for external solving use.
- */
- Node getProofLiteral() const;
-
- /**
- * Set the node as having a proof and being an assumption.
- * The node must be assertedToTheTheory().
- *
- * Precondition: negationHasProof() == inConflict.
- *
- * Replaces:
- * selfExplaining().
- * selfExplainingWithNegationTrue().
- */
- void setAssumption(bool inConflict);
-
- /** Returns true if the node is an assumption.*/
- bool isAssumption() const;
-
- /** Whether we produce proofs */
- bool isProofProducing() const { return d_produceProofs; }
-
- /** Set the constraint to have an EqualityEngine proof. */
- void setEqualityEngineProof();
- bool hasEqualityEngineProof() const;
-
- /** Returns true if the node has a Farkas' proof. */
- bool hasFarkasProof() const;
-
- /**
- * @brief Returns whether this constraint is provable using a Farkas
- * proof applied to (possibly tightened) input assertions.
- *
- * An example of a constraint that has a simple Farkas proof:
- * x <= 0 proven from x + y <= 0 and x - y <= 0.
- *
- * An example of another constraint that has a simple Farkas proof:
- * x <= 0 proven from x + y <= 0 and x - y <= 0.5 for integers x, y
- * (integer bound-tightening is applied first!).
- *
- * An example of a constraint that might be proven **without** a simple
- * Farkas proof:
- * x < 0 proven from not(x == 0) and not(x > 0).
- *
- * This could be proven internally by the arithmetic theory using
- * `TrichotomyAP` as the proof type.
- *
- */
- bool hasSimpleFarkasProof() const;
- /**
- * Returns whether this constraint is an assumption or a tightened
- * assumption.
- */
- bool isPossiblyTightenedAssumption() const;
-
- /** Returns true if the node has a int bound tightening proof. */
- bool hasIntTightenProof() const;
-
- /** Returns true if the node has a int hole proof. */
- bool hasIntHoleProof() const;
-
- /** Returns true if the node has a trichotomy proof. */
- bool hasTrichotomyProof() const;
-
- void printProofTree(std::ostream & out, size_t depth = 0) const;
-
- /**
- * A sets the constraint to be an internal assumption.
- *
- * 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 setInternalAssumption(bool inConflict);
- bool isInternalAssumption() const;
-
- /**
- * Returns a explanation of the constraint that is appropriate for conflicts.
- *
- * This is not appropriate for propagation!
- *
- * This is the minimum fringe of the implication tree s.t.
- * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
- */
- TrustNode externalExplainByAssertions() const;
-
- /**
- * Writes an explanation of a constraint into the node builder.
- * Pushes back an explanation that is acceptable to send to the sat solver.
- * nb is assumed to be an AND.
- *
- * This is the minimum fringe of the implication tree s.t.
- * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
- *
- * This is not appropriate for propagation!
- * Use explainForPropagation() instead.
- */
- std::shared_ptr<ProofNode> externalExplainByAssertions(NodeBuilder& nb) const
- {
- return externalExplain(nb, AssertionOrderSentinel);
- }
-
- /* Equivalent to calling externalExplainByAssertions on all constraints in b */
- static Node externalExplainByAssertions(const ConstraintCPVec& b);
- static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b);
- static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c);
-
- /**
- * 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);
-
- /** The fringe of a farkas' proof. */
- bool onFringe() const {
- return assertedToTheTheory() || isInternalAssumption() || hasEqualityEngineProof();
- }
-
- /**
- * Returns an explanation of a propagation by the ConstraintDatabase.
- * The constraint must have a proof.
- * The constraint cannot be an assumption.
- *
- * This is the minimum fringe of the implication tree (excluding the
- * constraint itself) s.t. every constraint is assertedToTheTheory() or
- * hasEqualityEngineProof().
- *
- * All return conjuncts were asserted before this constraint.
- *
- * Requires the given node to rewrite to the canonical literal for this
- * constraint.
- *
- * @params n the literal to prove
- * n must rewrite to the constraint's canonical literal
- *
- * @returns a trust node of the form:
- * (=> explanation n)
- */
- TrustNode externalExplainForPropagation(TNode n) const;
-
- /**
- * Explain the constraint and its negation in terms of assertions.
- * The constraint must be in conflict.
- */
- TrustNode externalExplainConflict() const;
-
- /** The constraint is known to be true. */
- inline bool hasProof() const {
- return d_crid != ConstraintRuleIdSentinel;
- }
-
- /** The negation of the constraint is known to hold. */
- inline bool negationHasProof() const {
- 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(). */
- inline bool isTrue() const {
- return hasProof();
- }
-
- /** Both the constraint and its negation are true. */
- inline bool inConflict() const {
- return hasProof() && negationHasProof();
- }
-
- /**
- * 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();
-
- /**
- * 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 ConstraintP makeNegation(ArithVar v,
- ConstraintType t,
- const DeltaRational& r,
- bool produceProofs);
-
- const ValueCollection& getValueCollection() const;
-
-
- ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const;
- ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const;
-
- /**
- * Marks a the constraint c as being entailed by a.
- * The Farkas proof 1*(a) + -1 (c) |= 0<0
- *
- * After calling impliedByUnate(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByUnate(ConstraintCP a, bool inConflict);
-
- /**
- * Marks a the constraint c as being entailed by a.
- * The reason has to do with integer bound tightening.
- *
- * After calling impliedByIntTighten(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByIntTighten(ConstraintCP a, bool inConflict);
-
- /**
- * Marks a the constraint c as being entailed by a.
- * The reason has to do with integer reasoning.
- *
- * After calling impliedByIntHole(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByIntHole(ConstraintCP a, bool inConflict);
-
- /**
- * Marks a the constraint c as being entailed by a.
- * The reason has to do with integer reasoning.
- *
- * After calling impliedByIntHole(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByIntHole(const ConstraintCPVec& b, bool inConflict);
-
- /**
- * This is a lemma of the form:
- * x < d or x = d or x > d
- * The current constraint c is one of the above constraints and {a,b}
- * are the negation of the other two constraints.
- *
- * Preconditions:
- * - negationHasProof() == inConflict.
- *
- * After calling impliedByTrichotomy(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool inConflict);
-
- /**
- * Marks the node as having a Farkas proof.
- *
- * Preconditions:
- * - coeffs == NULL if proofs are off.
- * - See the comments for ConstraintRule for the form of coeffs when
- * proofs are on.
- * - negationHasProof() == inConflict.
- *
- * After calling impliedByFarkas(), the caller should either raise a conflict
- * or try call tryToPropagate().
- */
- void impliedByFarkas(const ConstraintCPVec& b, RationalVectorCP coeffs, bool inConflict);
-
- /**
- * Generates an implication node, B => getLiteral(),
- * where B is the result of externalExplainByAssertions(b).
- * Does not guarantee b is the explanation of the constraint.
- */
- Node externalImplication(const ConstraintCPVec& b) const;
-
- /**
- * Returns true if the variable is assigned the value dr,
- * the constraint would be satisfied.
- */
- bool satisfiedBy(const DeltaRational& dr) const;
-
- /**
- * The node must have a proof already and be eligible for propagation!
- * You probably want to call tryToPropagate() instead.
- *
- * Preconditions:
- * - hasProof()
- * - canBePropagated()
- * - !assertedToTheTheory()
- */
- void propagate();
-
- /**
- * If the constraint
- * canBePropagated() and
- * !assertedToTheTheory(),
- * the constraint is added to the database's propagation queue.
- *
- * Precondition:
- * - hasProof()
- */
- void tryToPropagate();
-
- /**
- * Returns a reference to the containing database.
- * Precondition: the constraint must be initialized.
- */
- const ConstraintDatabase& getDatabase() const;
-
- /** Returns the constraint rule at the position. */
- const ConstraintRule& getConstraintRule() const;
-
- private:
- /** Returns true if the constraint has been initialized. */
- bool initialized() const;
-
- /**
- * This initializes the fields that cannot be set in the constructor due to
- * circular dependencies.
- */
- void initialize(ConstraintDatabase* db,
- SortedConstraintMapIterator v,
- ConstraintP negation);
-
- class ConstraintRuleCleanup
- {
- public:
- inline void operator()(ConstraintRule* crp)
- {
- Assert(crp != NULL);
- ConstraintP constraint = crp->d_constraint;
- Assert(constraint->d_crid != ConstraintRuleIdSentinel);
- constraint->d_crid = ConstraintRuleIdSentinel;
- if (constraint->isProofProducing())
- {
- if (crp->d_farkasCoefficients != RationalVectorCPSentinel)
- {
- delete crp->d_farkasCoefficients;
- }
- }
- }
- };
-
- class CanBePropagatedCleanup
- {
- public:
- inline void operator()(ConstraintP* p)
- {
- ConstraintP constraint = *p;
- Assert(constraint->d_canBePropagated);
- constraint->d_canBePropagated = false;
- }
- };
-
- class AssertionOrderCleanup
- {
- public:
- inline void operator()(ConstraintP* p)
- {
- ConstraintP constraint = *p;
- Assert(constraint->assertedToTheTheory());
- constraint->d_assertionOrder = AssertionOrderSentinel;
- constraint->d_witness = TNode::null();
- Assert(!constraint->assertedToTheTheory());
- }
- };
-
- class SplitCleanup
- {
- public:
- inline void operator()(ConstraintP* p)
- {
- ConstraintP constraint = *p;
- Assert(constraint->d_split);
- constraint->d_split = false;
- }
- };
-
- /**
- * Returns true if the node is safe to garbage collect.
- * Both it and its negation must have no context dependent data set.
- */
- bool safeToGarbageCollect() const;
-
- /**
- * Returns true if the constraint has no context dependent data set.
- */
- bool contextDependentDataIsSet() const;
-
- /**
- * Returns true if the node correctly corresponds to the constraint that is
- * being set.
- */
- bool sanityChecking(Node n) const;
-
- /** Returns a reference to the map for d_variable. */
- SortedConstraintMap& constraintSet() const;
-
- /** Returns coefficients for the proofs for farkas cancellation. */
- static std::pair<int, int> unateFarkasSigns(ConstraintCP a, ConstraintCP b);
-
- Node externalExplain(AssertionOrder order) const;
- /**
- * Returns an explanation of that was assertedBefore(order).
- * The constraint must have a proof.
- * The constraint cannot be selfExplaining().
- *
- * This is the minimum fringe of the implication tree
- * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof().
- */
- std::shared_ptr<ProofNode> externalExplain(NodeBuilder& nb,
- AssertionOrder order) const;
-
- static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order);
-
- inline ArithProofType getProofType() const {
- return getConstraintRule().d_proofType;
- }
-
- inline AntecedentId getEndAntecedent() const {
- return getConstraintRule().d_antecedentEnd;
- }
-
- inline RationalVectorCP getFarkasCoefficients() const
- {
- return d_produceProofs ? getConstraintRule().d_farkasCoefficients : nullptr;
- }
-
- /**
- * The proof of the node is empty.
- * The proof must be a special proof. Either
- * isSelfExplaining() or
- * hasEqualityEngineProof()
- */
- bool antecentListIsEmpty() const;
-
- bool antecedentListLengthIsOne() const;
-
- /** Return true if every element in b has a proof. */
- static bool allHaveProof(const ConstraintCPVec& b);
-
- /** Precondition: hasFarkasProof()
- * Computes the combination implied by the farkas coefficients. Sees if it is
- * a contradiction.
- */
-
- bool wellFormedFarkasProof() const;
-
- /** The ArithVar associated with the constraint. */
- const ArithVar d_variable;
-
- /** The type of the Constraint. */
- const ConstraintType d_type;
-
- /** The DeltaRational value with the constraint. */
- const DeltaRational d_value;
-
- /** A pointer to the associated database for the Constraint. */
- ConstraintDatabase* d_database;
-
- /**
- * The node to be communicated with the TheoryEngine.
- *
- * This is not context dependent, but may be set once.
- *
- * This must be set if the constraint canBePropagated().
- * This must be set if the constraint assertedToTheTheory().
- * Otherwise, this may be null().
- */
- Node d_literal;
-
- /** Pointer to the negation of the Constraint. */
- ConstraintP d_negation;
-
- /**
- * This is true if the associated node can be propagated.
- *
- * This should be enabled if the node has been preregistered.
- *
- * Sat Context Dependent.
- * This is initially false.
- */
- bool d_canBePropagated;
-
- /**
- * This is the order the constraint was asserted to the theory.
- * If this has been set, the node can be used in conflicts.
- * If this is c.d_assertedOrder < d.d_assertedOrder, then c can be used in the
- * explanation of d.
- *
- * This should be set after the literal is dequeued by Theory::get().
- *
- * Sat Context Dependent.
- * This is initially AssertionOrderSentinel.
- */
- 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
- */
- TNode d_witness;
-
- /**
- * The position of the constraint in the constraint rule id.
- *
- * Sat Context Dependent.
- * This is initially
- */
- ConstraintRuleID d_crid;
-
- /**
- * True if the equality has been split.
- * Only meaningful if ConstraintType == Equality.
- *
- * User Context Dependent.
- * This is initially false.
- */
- bool d_split;
-
- /**
- * Position in sorted constraint set for the variable.
- * Unset if d_type is Disequality.
- */
- SortedConstraintMapIterator d_variablePosition;
-
- /** Whether to produce proofs, */
- bool d_produceProofs;
-
-}; /* class ConstraintValue */
-
-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 ConstraintCP 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);
-std::ostream& operator<<(std::ostream& o, const ArithProofType);
-
-class ConstraintDatabase : protected EnvObj
-{
- private:
- /**
- * The map from ArithVars to their unique databases.
- * When the vector changes size, we cannot allow the maps to move so this
- * is a vector of pointers.
- */
- std::vector<PerVariableDatabase*> d_varDatabases;
-
- SortedConstraintMap& getVariableSCM(ArithVar v) const;
-
- /** Maps literals to constraints.*/
- NodetoConstraintMap d_nodetoConstraintMap;
-
- /**
- * A queue of propagated constraints.
- * ConstraintCP are pointers.
- * The elements of the queue do not require destruction.
- */
- context::CDQueue<ConstraintCP> d_toPropagate;
-
- /**
- * Proofs are lists of valid constraints terminated by the first null
- * sentinel value in the proof list.
- * We abbreviate d_antecedents as ans in the comment.
- *
- * The proof at p in ans[p] of length n is
- * (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
- *
- * The proof at p corresponds to the conjunction:
- * (and x_i)
- *
- * So the proof of a Constraint c corresponds to the horn clause:
- * (implies (and x_i) c)
- * where (and x_i) is the proof at c.d_crid d_antecedentEnd.
- *
- * Constraints are pointers so this list is designed not to require any destruction.
- */
- CDConstraintList d_antecedents;
-
- typedef context::CDList<ConstraintRule, Constraint::ConstraintRuleCleanup>
- ConstraintRuleList;
- 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
- * carefully.
- */
- struct Watches{
-
- /**
- * Contains the exact list of constraints that have a proof.
- * Upon pop, this unsets d_crid to NoAP.
- *
- * The index in this list is the proper ordering of the proofs.
- */
- ConstraintRuleList d_constraintProofs;
-
- /**
- * Contains the exact list of constraints that can be used for propagation.
- */
- CBPList d_canBePropagatedWatches;
-
- /**
- * Contains the exact list of constraints that have been asserted to the theory.
- */
- AOList d_assertionOrderWatches;
-
-
- /**
- * Contains the exact list of atoms that have been preregistered.
- * This is a pointer as it must be destroyed before the elements of
- * d_varDatabases.
- */
- SplitList d_splitWatches;
- Watches(context::Context* satContext, context::Context* userContext);
- };
- Watches* d_watches;
-
- void pushSplitWatch(ConstraintP c);
- void pushCanBePropagatedWatch(ConstraintP c);
- void pushAssertionOrderWatch(ConstraintP c, TNode witness);
-
- /** Assumes that antecedents have already been pushed. */
- void pushConstraintRule(const ConstraintRule& crp);
-
- /** Returns true if all of the entries of the vector are empty. */
- static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec);
-
- /** Map from nodes to arithvars. */
- const ArithVariables& d_avariables;
-
- const ArithVariables& getArithVariables() const{
- return d_avariables;
- }
-
- ArithCongruenceManager& d_congruenceManager;
-
- /** Owned by the TheoryArithPrivate, used here. */
- EagerProofGenerator* d_pfGen;
- /** Owned by the TheoryArithPrivate, used here. */
- ProofNodeManager* d_pnm;
-
- RaiseConflict d_raiseConflict;
-
-
- const Rational d_one;
- const Rational d_negOne;
-
- friend class Constraint;
-
- public:
- ConstraintDatabase(Env& env,
- const ArithVariables& variables,
- ArithCongruenceManager& dm,
- RaiseConflict conflictCallBack,
- EagerProofGenerator* pfGen);
-
- ~ConstraintDatabase();
-
- /** Adds a literal to the database. */
- ConstraintP addLiteral(TNode lit);
-
- /**
- * If hasLiteral() is true, returns the constraint.
- * Otherwise, returns NullConstraint.
- */
- ConstraintP lookup(TNode literal) const;
-
- /**
- * Returns true if the literal has been added to the database.
- * This is a hash table lookup.
- * It does not look in the database for an equivalent corresponding constraint.
- */
- bool hasLiteral(TNode literal) const;
-
- bool hasMorePropagations() const{
- return !d_toPropagate.empty();
- }
-
- ConstraintCP nextPropagation(){
- Assert(hasMorePropagations());
-
- ConstraintCP p = d_toPropagate.front();
- d_toPropagate.pop();
-
- return p;
- }
-
- void addVariable(ArithVar v);
- bool variableDatabaseIsSetup(ArithVar v) const;
- void removeVariable(ArithVar v);
-
- /** Get an explanation and proof for this constraint from the equality engine
- */
- TrustNode eeExplain(ConstraintCP c) const;
- /** Get an explanation for this constraint from the equality engine */
- void eeExplain(ConstraintCP c, NodeBuilder& nb) const;
-
- /**
- * Returns a constraint with the variable v, the constraint type t, and a value
- * dominated by r (explained below) if such a constraint exists in the database.
- * If no such constraint exists, NullConstraint is returned.
- *
- * t must be either UpperBound or LowerBound.
- * The returned value v is dominated:
- * If t is UpperBound, r <= v
- * If t is LowerBound, r >= v
- *
- * variableDatabaseIsSetup(v) must be true.
- */
- 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.
- * If there is such a constraint in the database already, it is returned.
- * If there is no such constraint, this constraint is added to the database.
- *
- */
- 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.
- */
- ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t);
-
-
- void deleteConstraintAndNegation(ConstraintP c);
-
- /** Given constraints `a` and `b` such that `a OR b` by unate reasoning,
- * adds a TrustNode to `out` which proves `a OR b` as a lemma.
- *
- * Example: `x <= 5` OR `5 <= x`.
- */
- void proveOr(std::vector<TrustNode>& out,
- ConstraintP a,
- ConstraintP b,
- bool negateSecond) const;
- /** Given constraints `a` and `b` such that `a` implies `b` by unate
- * reasoning, adds a TrustNode to `out` which proves `-a OR b` as a lemma.
- *
- * Example: `x >= 5` -> `x >= 4`.
- */
- void implies(std::vector<TrustNode>& out, ConstraintP a, ConstraintP b) const;
- /** Given constraints `a` and `b` such that `not(a AND b)` by unate reasoning,
- * adds a TrustNode to `out` which proves `-a OR -b` as a lemma.
- *
- * Example: `x >= 4` -> `x <= 3`.
- */
- void mutuallyExclusive(std::vector<TrustNode>& out,
- ConstraintP a,
- ConstraintP b) const;
-
- /**
- * Outputs a minimal set of unate implications onto the vector for the variable.
- * This outputs lemmas of the general forms
- * (= p c) implies (<= p d) for c < d, or
- * (= p c) implies (not (= p d)) for c != d.
- */
- void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas) const;
- void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas,
- ArithVar v) const;
-
- /**
- * Outputs a minimal set of unate implications onto the vector for the variable.
- *
- * If ineqs is true, this outputs lemmas of the general form
- * (<= p c) implies (<= p d) for c < d.
- */
- void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas) const;
- void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas,
- ArithVar v) const;
-
- void unatePropLowerBound(ConstraintP curr, ConstraintP prev);
- void unatePropUpperBound(ConstraintP curr, ConstraintP prev);
- void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB);
-
- /** AntecendentID must be in range. */
- ConstraintCP getAntecedent(AntecedentId p) const;
-
- bool isProofEnabled() const { return d_pnm != nullptr; }
-
- private:
- /** returns true if cons is now in conflict. */
- bool handleUnateProp(ConstraintP ant, ConstraintP cons);
-
- DenseSet d_reclaimable;
-
- class Statistics {
- public:
- IntStat d_unatePropagateCalls;
- IntStat d_unatePropagateImplications;
-
- Statistics();
- } d_statistics;
-
-}; /* ConstraintDatabase */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__CONSTRAINT_H */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Forward declarations of the ConstraintValue and ConstraintDatabase
- * classes.
- *
- * This is the forward declarations of the ConstraintValue and
- * ConstraintDatabase and the typedef for Constraint.
- * This is used to break circular dependencies and minimize interaction
- * between header files.
- */
-
-#ifndef CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
-#define CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
-
-#include <vector>
-
-#include "cvc5_private.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class Constraint;
-typedef Constraint* ConstraintP;
-typedef const Constraint* ConstraintCP;
-
-static constexpr ConstraintP NullConstraint = nullptr;
-
-class ConstraintDatabase;
-
-typedef std::vector<ConstraintCP> ConstraintCPVec;
-
-typedef std::vector<Rational> RationalVector;
-typedef RationalVector* RationalVectorP;
-typedef const RationalVector* RationalVectorCP;
-static constexpr RationalVectorCP RationalVectorCPSentinel = nullptr;
-static constexpr RationalVectorP RationalVectorPSentinel = nullptr;
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andrew Reynolds, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/cut_log.h"
-
-#include <limits.h>
-#include <math.h>
-
-#include <cmath>
-#include <iomanip>
-#include <map>
-
-#include "base/cvc5config.h"
-#include "base/output.h"
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
-#include "util/ostream_util.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-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());
- StreamFormatScope scope(out);
-
- out << len << " " << std::setprecision(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_N(-1),
- d_rowId(-1),
- d_exactPrecision(nullptr),
- d_explanation(nullptr)
-{}
-
-CutInfo::~CutInfo(){
-}
-
-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 != nullptr; }
-
-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 != nullptr; }
-
-bool CutInfo::operator<(const CutInfo& o) const{
- return d_execOrd < o.d_execOrd;
-}
-
-
-void CutInfo::setReconstruction(const DenseVector& ep){
- Assert(!reconstructed());
- d_exactPrecision.reset(new DenseVector(ep));
-}
-
-void CutInfo::setExplanation(const ConstraintCPVec& ex){
- Assert(reconstructed());
- if (d_explanation == nullptr)
- {
- d_explanation.reset(new ConstraintCPVec(ex));
- }
- else
- {
- *d_explanation = ex;
- }
-}
-
-void CutInfo::swapExplanation(ConstraintCPVec& ex){
- Assert(reconstructed());
- Assert(!proven());
- if (d_explanation == nullptr)
- {
- d_explanation.reset(new ConstraintCPVec());
- }
- d_explanation->swap(ex);
-}
-
-const DenseVector& CutInfo::getReconstruction() const {
- Assert(reconstructed());
- return *d_exactPrecision;
-}
-
-void CutInfo::clearReconstruction(){
- if(proven()){
- d_explanation = nullptr;
- }
-
- if(reconstructed()){
- d_exactPrecision = nullptr;
- }
-
- 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;
- Trace("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{
- Trace("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(TraceIsOn("approx::nodelog")){
- Trace("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "...";
- for(unsigned k = 0; k<sortedRemoved.size(); k++){
- Trace("approx::nodelog") << ", " << sortedRemoved[k];
- }
- Trace("approx::nodelog") << endl;
- Trace("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){
- Trace("approx::nodelog") << "deleting from above because of " << rd << endl;
- Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
- d_rowId2ArithVar.erase(origOrd);
- }else{
- Trace("approx::nodelog") << "deleting " << ci << " because of " << rd << endl;
- Trace("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){
- Trace("approx::nodelog") << "shifting above down due to " << rd << endl;
- Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
- Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
- d_rowId2ArithVar.erase(origOrd);
- mapRowId(newOrd, v);
- }else{
- Trace("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl;
- Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
- Trace("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);
- Trace("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 << "]";
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Morgan Deters, Kshitij Bansal
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <set>
-#include <unordered_map>
-
-#include "expr/kind.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "util/dense_map.h"
-
-namespace cvc5::internal {
-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.
- */
- std::unique_ptr<DenseVector> d_exactPrecision;
-
- std::unique_ptr<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);
-
-class BranchCutInfo : public CutInfo {
-public:
- BranchCutInfo(int execOrd, int br, Kind dir, double val);
-};
-
-class RowsDeleted : public CutInfo {
-public:
- 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 std::unordered_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 appropriate 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 TreeLog {
-private:
- 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;
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Diophantine equation solver
- *
- * A Diophantine equation solver for the theory of arithmetic.
- */
-#include "theory/arith/dio_solver.h"
-
-#include <iostream>
-
-#include "base/output.h"
-#include "expr/skolem_manager.h"
-#include "options/arith_options.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/partial_model.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-inline Node makeIntegerVariable(){
- NodeManager* nm = NodeManager::currentNM();
- SkolemManager* sm = nm->getSkolemManager();
- return sm->mkDummySkolem("intvar",
- nm->integerType(),
- "is an integer variable created by the dio solver");
-}
-
-DioSolver::DioSolver(Env& env)
- : EnvObj(env),
- d_lastUsedProofVariable(context(), 0),
- d_inputConstraints(context()),
- d_nextInputConstraintToEnqueue(context(), 0),
- d_trail(context()),
- d_subs(context()),
- d_currentF(),
- d_savedQueue(context()),
- d_savedQueueIndex(context(), 0),
- d_conflictIndex(context()),
- d_maxInputCoefficientLength(context(), 0),
- d_usedDecomposeIndex(context(), false),
- d_lastPureSubstitution(context(), 0),
- d_pureSubstitionIter(context(), 0),
- d_decompositionLemmaQueue(context())
-{
-}
-
-DioSolver::Statistics::Statistics()
- : d_conflictCalls(smtStatisticsRegistry().registerInt(
- "theory::arith::dio::conflictCalls")),
- d_cutCalls(
- smtStatisticsRegistry().registerInt("theory::arith::dio::cutCalls")),
- d_cuts(smtStatisticsRegistry().registerInt("theory::arith::dio::cuts")),
- d_conflicts(
- smtStatisticsRegistry().registerInt("theory::arith::dio::conflicts")),
- d_conflictTimer(smtStatisticsRegistry().registerTimer(
- "theory::arith::dio::conflictTimer")),
- d_cutTimer(
- smtStatisticsRegistry().registerTimer("theory::arith::dio::cutTimer"))
-{
-}
-
-bool DioSolver::queueConditions(TrailIndex t){
- Trace("queueConditions") << !inConflict() << std::endl;
- Trace("queueConditions") << gcdIsOne(t) << std::endl;
- Trace("queueConditions") << !debugAnySubstitionApplies(t) << std::endl;
- Trace("queueConditions") << !triviallySat(t) << std::endl;
- Trace("queueConditions") << !triviallyUnsat(t) << std::endl;
-
- return
- !inConflict() &&
- gcdIsOne(t) &&
- !debugAnySubstitionApplies(t) &&
- !triviallySat(t) &&
- !triviallyUnsat(t);
-}
-
-size_t DioSolver::allocateProofVariable() {
- Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
- if(d_lastUsedProofVariable == d_proofVariablePool.size()){
- Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
- Node intVar = makeIntegerVariable();
- d_proofVariablePool.push_back(Variable(intVar));
- }
- size_t res = d_lastUsedProofVariable;
- d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
- return res;
-}
-
-
-Node DioSolver::nextPureSubstitution(){
- Assert(hasMorePureSubstitutions());
- SubIndex curr = d_pureSubstitionIter;
- d_pureSubstitionIter = d_pureSubstitionIter + 1;
-
- Assert(d_subs[curr].d_fresh.isNull());
- Variable v = d_subs[curr].d_eliminated;
-
- SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
- Polynomial p = sp.getPolynomial();
- Constant c = -sp.getConstant();
- Polynomial cancelV = p + Polynomial::mkPolynomial(v);
- Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
- return eq;
-}
-
-
-bool DioSolver::debugEqualityInInputEquations(Node eq){
- typedef context::CDList<InputConstraint>::const_iterator const_iterator;
- const_iterator i=d_inputConstraints.begin(), end = d_inputConstraints.end();
- for(; i != end; ++i){
- Node reason_i = (*i).d_reason;
- if(eq == reason_i){
- return true;
- }
- }
- return false;
-}
-
-
-void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
- Assert(!debugEqualityInInputEquations(reason));
- Assert(eq.debugIsIntegral());
- Assert(eq.getNode().getKind() == kind::EQUAL);
-
- SumPair sp = eq.toSumPair();
- if(sp.isNonlinear()){
- return;
- }
-
-
-
- uint32_t length = sp.maxLength();
- if(length > d_maxInputCoefficientLength){
- d_maxInputCoefficientLength = length;
- }
-
- size_t varIndex = allocateProofVariable();
- Variable proofVariable(d_proofVariablePool[varIndex]);
- //Variable proofVariable(makeIntegerVariable());
-
- TrailIndex posInTrail = d_trail.size();
- Trace("dio::pushInputConstraint") << "pushInputConstraint @ " << posInTrail
- << " " << eq.getNode()
- << " " << reason << endl;
- d_trail.push_back(Constraint(sp,Polynomial::mkPolynomial(proofVariable)));
-
- size_t posInConstraintList = d_inputConstraints.size();
- d_inputConstraints.push_back(InputConstraint(reason, posInTrail));
-
- d_varToInputConstraintMap[proofVariable.getNode()] = posInConstraintList;
-}
-
-
-DioSolver::TrailIndex DioSolver::scaleEqAtIndex(DioSolver::TrailIndex i, const Integer& g){
- Assert(g != 0);
- Constant invg = Constant::mkConstant(Rational(Integer(1),g));
- const SumPair& sp = d_trail[i].d_eq;
- const Polynomial& proof = d_trail[i].d_proof;
-
- SumPair newSP = sp * invg;
- Polynomial newProof = proof * invg;
-
- Assert(newSP.isIntegral());
- Assert(newSP.gcd() == 1);
-
- TrailIndex j = d_trail.size();
-
- d_trail.push_back(Constraint(newSP, newProof));
-
- Trace("arith::dio") << "scaleEqAtIndex(" << i <<","<<g<<")"<<endl;
- Trace("arith::dio") << "derived "<< newSP.getNode()
- <<" with proof " << newProof.getNode() << endl;
- return j;
-}
-
-Node DioSolver::proveIndex(TrailIndex i){
- Assert(inRange(i));
- const Polynomial& proof = d_trail[i].d_proof;
- Assert(!proof.isConstant());
-
- NodeBuilder nb(kind::AND);
- for(Polynomial::iterator iter = proof.begin(), end = proof.end(); iter!= end; ++iter){
- Monomial m = (*iter);
- Assert(!m.isConstant());
- VarList vl = m.getVarList();
- Assert(vl.singleton());
- Variable v = vl.getHead();
-
- Node input = proofVariableToReason(v);
- if(input.getKind() == kind::AND){
- for(Node::iterator input_iter = input.begin(), input_end = input.end(); input_iter != input_end; ++input_iter){
- Node inputChild = *input_iter;
- nb << inputChild;
- }
- }else{
- nb << input;
- }
- }
-
- Node result = (nb.getNumChildren() == 1) ? nb[0] : (Node)nb;
- Trace("arith::dio") << "Proof at " << i << " is "
- << d_trail[i].d_eq.getNode() << endl
- << d_trail[i].d_proof.getNode() << endl
- << " which becomes " << result << endl;
- return result;
-}
-
-bool DioSolver::anyCoefficientExceedsMaximum(TrailIndex j) const{
- uint32_t length = d_trail[j].d_eq.maxLength();
- uint32_t nmonos = d_trail[j].d_eq.getPolynomial().numMonomials();
-
- bool result =
- nmonos >= 2 &&
- length > d_maxInputCoefficientLength + MAX_GROWTH_RATE;
- if(TraceIsOn("arith::dio::max") && result){
-
- const SumPair& eq = d_trail[j].d_eq;
- const Polynomial& proof = d_trail[j].d_proof;
-
- Trace("arith::dio::max") << "about to drop:" << std::endl;
- Trace("arith::dio::max") << "d_trail[" << j << "].d_eq = " << eq.getNode() << std::endl;
- Trace("arith::dio::max") << "d_trail[" << j << "].d_proof = " << proof.getNode() << std::endl;
- }
- return result;
-}
-
-void DioSolver::enqueueInputConstraints(){
- Assert(d_currentF.empty());
- while(d_savedQueueIndex < d_savedQueue.size()){
- d_currentF.push_back(d_savedQueue[d_savedQueueIndex]);
- d_savedQueueIndex = d_savedQueueIndex + 1;
- }
-
- while(d_nextInputConstraintToEnqueue < d_inputConstraints.size() && !inConflict()){
- size_t curr = d_nextInputConstraintToEnqueue;
- d_nextInputConstraintToEnqueue = d_nextInputConstraintToEnqueue + 1;
-
- TrailIndex i = d_inputConstraints[curr].d_trailPos;
- TrailIndex j = applyAllSubstitutionsToIndex(i);
-
- if(!triviallySat(j)){
- if(triviallyUnsat(j)){
- raiseConflict(j);
- }else{
- TrailIndex k = reduceByGCD(j);
-
- if(!inConflict()){
- if(triviallyUnsat(k)){
- raiseConflict(k);
- }else if(!(triviallySat(k) || anyCoefficientExceedsMaximum(k))){
- pushToQueueBack(k);
- }
- }
- }
- }
- }
-}
-
-
-/*TODO Currently linear in the size of the Queue
- *It is not clear if am O(log n) strategy would be better.
- *Right before this in the algorithm is a substitution which could potentially
- *effect the key values of everything in the queue.
- */
-void DioSolver::moveMinimumByAbsToQueueFront(){
- Assert(!queueEmpty());
-
- //Select the minimum element.
- size_t indexInQueue = 0;
- Monomial minMonomial = d_trail[d_currentF[indexInQueue]].d_minimalMonomial;
-
- 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.absCmp(minMonomial) < 0){
- indexInQueue = i;
- minMonomial = curr;
- }
- }
-
- TrailIndex tmp = d_currentF[indexInQueue];
- d_currentF[indexInQueue] = d_currentF.front();
- d_currentF.front() = tmp;
-}
-
-bool DioSolver::queueEmpty() const{
- return d_currentF.empty();
-}
-
-Node DioSolver::columnGcdIsOne() const{
- std::unordered_map<Node, Integer> gcdMap;
-
- std::deque<TrailIndex>::const_iterator iter, end;
- for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
- TrailIndex curr = *iter;
- Polynomial p = d_trail[curr].d_eq.getPolynomial();
- Polynomial::iterator monoIter = p.begin(), monoEnd = p.end();
- for(; monoIter != monoEnd; ++monoIter){
- Monomial m = *monoIter;
- VarList vl = m.getVarList();
- Node vlNode = vl.getNode();
-
- Constant c = m.getConstant();
- Integer zc = c.getValue().getNumerator();
- if(gcdMap.find(vlNode) == gcdMap.end()){
- // have not seen vl yet.
- // gcd is c
- Assert(c.isIntegral());
- Assert(!m.absCoefficientIsOne());
- gcdMap.insert(make_pair(vlNode, zc.abs()));
- }else{
- const Integer& currentGcd = gcdMap[vlNode];
- Integer newGcd = currentGcd.gcd(zc);
- if(newGcd == 1){
- return vlNode;
- }else{
- gcdMap[vlNode] = newGcd;
- }
- }
- }
- }
- return Node::null();
-}
-
-void DioSolver::saveQueue(){
- std::deque<TrailIndex>::const_iterator iter, end;
- for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
- d_savedQueue.push_back(*iter);
- }
-}
-
-DioSolver::TrailIndex DioSolver::impliedGcdOfOne(){
- Node canReduce = columnGcdIsOne();
- if(canReduce.isNull()){
- return 0;
- }else{
- VarList vl = VarList::parseVarList(canReduce);
-
- TrailIndex current;
- Integer currentCoeff, currentGcd;
-
- //step 1 find the first equation containing vl
- //Set current and currentCoefficient
- std::deque<TrailIndex>::const_iterator iter, end;
- for(iter = d_currentF.begin(), end = d_currentF.end(); true; ++iter){
- Assert(iter != end);
- current = *iter;
- Constant coeff = d_trail[current].d_eq.getPolynomial().getCoefficient(vl);
- if(!coeff.isZero()){
- currentCoeff = coeff.getValue().getNumerator();
- currentGcd = currentCoeff.abs();
-
- ++iter;
- break;
- }
- }
-
- //For the rest of the equations keep reducing until the coefficient is one
- for(; iter != end; ++iter){
- Trace("arith::dio") << "next round : " << currentCoeff << " " << currentGcd << endl;
- TrailIndex inQueue = *iter;
- Constant iqc = d_trail[inQueue].d_eq.getPolynomial().getCoefficient(vl);
- if(!iqc.isZero()){
- Integer inQueueCoeff = iqc.getValue().getNumerator();
-
- //mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, mpz_t a, mpz_t b);
- Integer g, s, t;
- // g = a*s + b*t
- Integer::extendedGcd(g, s, t, currentCoeff, inQueueCoeff);
-
- Trace("arith::dio") << "extendedReduction : " << endl;
- Trace("arith::dio") << g << " = " << s <<"*"<< currentCoeff << " + " << t <<"*"<< inQueueCoeff << endl;
-
- Assert(g <= currentGcd);
- if(g < currentGcd){
- if(s.sgn() == 0){
- Trace("arith::dio") << "extendedReduction drop" << endl;
- Assert(inQueueCoeff.divides(currentGcd));
- current = *iter;
- currentCoeff = inQueueCoeff;
- currentGcd = inQueueCoeff.abs();
- }else{
-
- Trace("arith::dio") << "extendedReduction combine" << endl;
- TrailIndex next = combineEqAtIndexes(current, s, inQueue, t);
-
- Assert(d_trail[next]
- .d_eq.getPolynomial()
- .getCoefficient(vl)
- .getValue()
- .getNumerator()
- == g);
-
- current = next;
- currentCoeff = g;
- currentGcd = g;
- if(currentGcd == 1){
- return current;
- }
- }
- }
- }
- }
- //This is not reachble as it is assured that the gcd of the column is 1
- Unreachable();
- }
-}
-
-bool DioSolver::processEquations(bool allowDecomposition){
- Assert(!inConflict());
-
- enqueueInputConstraints();
- while(! queueEmpty() && !inConflict()){
- moveMinimumByAbsToQueueFront();
-
- TrailIndex minimum = d_currentF.front();
- TrailIndex reduceIndex;
-
- Assert(inRange(minimum));
- Assert(!inConflict());
-
- Trace("arith::dio") << "processEquations " << minimum << " : " << d_trail[minimum].d_eq.getNode() << endl;
-
- Assert(queueConditions(minimum));
-
- bool canDirectlySolve = d_trail[minimum].d_minimalMonomial.absCoefficientIsOne();
-
- std::pair<SubIndex, TrailIndex> p;
- if(canDirectlySolve){
- d_currentF.pop_front();
- p = solveIndex(minimum);
- reduceIndex = minimum;
- }else{
- TrailIndex implied = impliedGcdOfOne();
-
- if(implied != 0){
- p = solveIndex(implied);
- reduceIndex = implied;
- }else if(allowDecomposition){
- d_currentF.pop_front();
- p = decomposeIndex(minimum);
- reduceIndex = minimum;
- }else {
- // Cannot make progress without decomposeIndex
- saveQueue();
- break;
- }
- }
-
- SubIndex subIndex = p.first;
- TrailIndex next = p.second;
- subAndReduceCurrentFByIndex(subIndex);
-
- if(next != reduceIndex){
- if(triviallyUnsat(next)){
- raiseConflict(next);
- }else if(! triviallySat(next) ){
- pushToQueueBack(next);
- }
- }
- }
-
- d_currentF.clear();
- return inConflict();
-}
-
-Node DioSolver::processEquationsForConflict(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_conflictTimer);
- ++(d_statistics.d_conflictCalls);
-
- Assert(!inConflict());
- if(processEquations(true)){
- ++(d_statistics.d_conflicts);
- return proveIndex(getConflictIndex());
- }else{
- return Node::null();
- }
-}
-
-SumPair DioSolver::processEquationsForCut(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_cutTimer);
- ++(d_statistics.d_cutCalls);
-
- Assert(!inConflict());
- if(processEquations(true)){
- ++(d_statistics.d_cuts);
- return purifyIndex(getConflictIndex());
- }else{
- return SumPair::mkZero();
- }
-}
-
-
-SumPair DioSolver::purifyIndex(TrailIndex i){
- // TODO: "This uses the substitution trail to reverse the substitutions from the sum term. Using the proof term should be more efficient."
-
- SumPair curr = d_trail[i].d_eq;
-
- Constant negOne = Constant::mkConstant(-1);
-
- for(uint32_t revIter = d_subs.size(); revIter > 0; --revIter){
- uint32_t i2 = revIter - 1;
- Node freshNode = d_subs[i2].d_fresh;
- if(freshNode.isNull()){
- continue;
- }else{
- Variable var(freshNode);
- Polynomial vsum = curr.getPolynomial();
-
- Constant a = vsum.getCoefficient(VarList(var));
- if(!a.isZero()){
- const SumPair& sj = d_trail[d_subs[i2].d_constraint].d_eq;
- Assert(sj.getPolynomial().getCoefficient(VarList(var)).isOne());
- SumPair newSi = (curr * negOne) + (sj * a);
- Assert(newSi.getPolynomial().getCoefficient(VarList(var)).isZero());
- curr = newSi;
- }
- }
- }
- return curr;
-}
-
-DioSolver::TrailIndex DioSolver::combineEqAtIndexes(DioSolver::TrailIndex i, const Integer& q, DioSolver::TrailIndex j, const Integer& r){
- Constant cq = Constant::mkConstant(q);
- Constant cr = Constant::mkConstant(r);
-
- const SumPair& si = d_trail[i].d_eq;
- const SumPair& sj = d_trail[j].d_eq;
-
- Trace("arith::dio") << "combineEqAtIndexes(" << i <<","<<q<<","<<j<<","<<r<<")"<<endl;
- Trace("arith::dio") << "d_facts[i] = " << si.getNode() << endl
- << "d_facts[j] = " << sj.getNode() << endl;
-
-
- SumPair newSi = (si * cq) + (sj * cr);
-
-
- const Polynomial& pi = d_trail[i].d_proof;
- const Polynomial& pj = d_trail[j].d_proof;
- Polynomial newPi = (pi * cq) + (pj * cr);
-
- TrailIndex k = d_trail.size();
- d_trail.push_back(Constraint(newSi, newPi));
-
-
- Trace("arith::dio") << "derived "<< newSi.getNode()
- <<" with proof " << newPi.getNode() << endl;
-
- return k;
-
-}
-
-void DioSolver::printQueue(){
- Trace("arith::dio") << "DioSolver::printQueue()" << endl;
- for(TrailIndex i = 0, last = d_trail.size(); i < last; ++i){
- Trace("arith::dio") << "d_trail[i].d_eq = " << d_trail[i].d_eq.getNode() << endl;
- Trace("arith::dio") << "d_trail[i].d_proof = " << d_trail[i].d_proof.getNode() << endl;
- }
-
- Trace("arith::dio") << "DioSolver::printSubs()" << endl;
- for(SubIndex si=0, sN=d_subs.size(); si < sN; ++si){
- Trace("arith::dio") << "d_subs[i] = {"
- << "d_fresh="<< d_subs[si].d_fresh <<","
- << "d_eliminated="<< d_subs[si].d_eliminated.getNode() <<","
- << "d_constraint="<< d_subs[si].d_constraint <<"}" << endl;
- Trace("arith::dio") << "d_trail[d_subs[i].d_constraint].d_eq="
- << d_trail[d_subs[si].d_constraint].d_eq.getNode() << endl;
- }
-}
-
-DioSolver::TrailIndex DioSolver::applyAllSubstitutionsToIndex(DioSolver::TrailIndex trailIndex){
- TrailIndex currentIndex = trailIndex;
- for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
- currentIndex = applySubstitution(subIter, currentIndex);
- }
- return currentIndex;
-}
-
-bool DioSolver::debugSubstitutionApplies(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
- Variable var = d_subs[si].d_eliminated;
-
- const SumPair& curr = d_trail[ti].d_eq;
- Polynomial vsum = curr.getPolynomial();
-
- Constant a = vsum.getCoefficient(VarList(var));
- return !a.isZero();
-}
-
-bool DioSolver::debugAnySubstitionApplies(DioSolver::TrailIndex i){
- for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
- if(debugSubstitutionApplies(subIter, i)){
- return true;
- }
- }
- return false;
-}
-
-std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioSolver::TrailIndex i){
- const SumPair& si = d_trail[i].d_eq;
-
- Trace("arith::dio") << "before solveIndex("<<i<<":"<<si.getNode()<< ")" << endl;
-
-#ifdef CVC5_ASSERTIONS
- const Polynomial& p = si.getPolynomial();
-#endif
-
- Assert(p.isIntegral());
-
- Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
- const Monomial av = d_trail[i].d_minimalMonomial;
-
- VarList vl = av.getVarList();
- Assert(vl.singleton());
- Variable var = vl.getHead();
- Constant a = av.getConstant();
- Integer a_abs = a.getValue().getNumerator().abs();
-
- Assert(a_abs == 1);
-
- TrailIndex ci = !a.isNegative() ? scaleEqAtIndex(i, Integer(-1)) : i;
-
- SubIndex subBy = d_subs.size();
- d_subs.push_back(Substitution(Node::null(), var, ci));
-
- Trace("arith::dio") << "after solveIndex " << d_trail[ci].d_eq.getNode() << " for " << av.getNode() << endl;
- Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
- == Constant::mkConstant(-1));
-
- return make_pair(subBy, i);
-}
-
-std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(DioSolver::TrailIndex i){
- const SumPair& si = d_trail[i].d_eq;
-
- d_usedDecomposeIndex = true;
-
- Trace("arith::dio") << "before decomposeIndex("<<i<<":"<<si.getNode()<< ")" << endl;
-
-#ifdef CVC5_ASSERTIONS
- const Polynomial& p = si.getPolynomial();
-#endif
-
- Assert(p.isIntegral());
-
- Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
- const Monomial& av = d_trail[i].d_minimalMonomial;
-
- VarList vl = av.getVarList();
- Assert(vl.singleton());
- Variable var = vl.getHead();
- Constant a = av.getConstant();
- Integer a_abs = a.getValue().getNumerator().abs();
-
- Assert(a_abs > 1);
-
- //It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
- //We need to handle both cases seperately to ensure termination.
- Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
-
- Assert(qr.getKind() == kind::ADD);
- Assert(qr.getNumChildren() == 2);
- SumPair q = SumPair::parseSumPair(qr[0]);
- SumPair r = SumPair::parseSumPair(qr[1]);
-
- Assert(q.getPolynomial().getCoefficient(vl) == Constant::mkConstant(1));
-
- Assert(!r.isZero());
- Node freshNode = makeIntegerVariable();
- Variable fresh(freshNode);
- SumPair fresh_one=SumPair::mkSumPair(fresh);
- SumPair fresh_a = fresh_one * a;
-
- SumPair newSI = SumPair(fresh_one) - q;
- // this normalizes the coefficient of var to -1
-
-
- TrailIndex ci = d_trail.size();
- d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
- // no longer reference av safely!
- addTrailElementAsLemma(ci);
-
- Trace("arith::dio") << "Decompose ci(" << ci <<":" << d_trail[ci].d_eq.getNode()
- << ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
- Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
- == Constant::mkConstant(-1));
-
- SumPair newFact = r + fresh_a;
-
- TrailIndex nextIndex = d_trail.size();
- d_trail.push_back(Constraint(newFact, d_trail[i].d_proof));
-
- SubIndex subBy = d_subs.size();
- d_subs.push_back(Substitution(freshNode, var, ci));
-
- Trace("arith::dio") << "Decompose nextIndex " << d_trail[nextIndex].d_eq.getNode() << endl;
- return make_pair(subBy, nextIndex);
-}
-
-
-DioSolver::TrailIndex DioSolver::applySubstitution(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
- Variable var = d_subs[si].d_eliminated;
- TrailIndex subIndex = d_subs[si].d_constraint;
-
- const SumPair& curr = d_trail[ti].d_eq;
- Polynomial vsum = curr.getPolynomial();
-
- Constant a = vsum.getCoefficient(VarList(var));
- Assert(a.isIntegral());
- if(!a.isZero()){
- Integer one(1);
- TrailIndex afterSub = combineEqAtIndexes(ti, one, subIndex, a.getValue().getNumerator());
- Assert(d_trail[afterSub]
- .d_eq.getPolynomial()
- .getCoefficient(VarList(var))
- .isZero());
- return afterSub;
- }else{
- return ti;
- }
-}
-
-
-DioSolver::TrailIndex DioSolver::reduceByGCD(DioSolver::TrailIndex ti){
- const SumPair& sp = d_trail[ti].d_eq;
- Polynomial vsum = sp.getPolynomial();
- Constant c = sp.getConstant();
-
- Trace("arith::dio") << "reduceByGCD " << vsum.getNode() << endl;
- Assert(!vsum.isConstant());
- Integer g = vsum.gcd();
- Assert(g >= 1);
- Trace("arith::dio") << "gcd("<< vsum.getNode() <<")=" << g << " " << c.getValue() << endl;
- if(g.divides(c.getValue().getNumerator())){
- if(g > 1){
- return scaleEqAtIndex(ti, g);
- }else{
- return ti;
- }
- }else{
- raiseConflict(ti);
- return ti;
- }
-}
-
-bool DioSolver::triviallySat(TrailIndex i){
- const SumPair& eq = d_trail[i].d_eq;
- if(eq.isConstant()){
- return eq.getConstant().isZero();
- }else{
- return false;
- }
-}
-
-bool DioSolver::triviallyUnsat(DioSolver::TrailIndex i){
- const SumPair& eq = d_trail[i].d_eq;
- if(eq.isConstant()){
- return !eq.getConstant().isZero();
- }else{
- return false;
- }
-}
-
-
-bool DioSolver::gcdIsOne(DioSolver::TrailIndex i){
- const SumPair& eq = d_trail[i].d_eq;
- return eq.gcd() == Integer(1);
-}
-
-void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
- size_t N = d_currentF.size();
-
- size_t readIter = 0, writeIter = 0;
- for(; readIter < N && !inConflict(); ++readIter){
- TrailIndex curr = d_currentF[readIter];
- TrailIndex nextTI = applySubstitution(subIndex, curr);
- if(nextTI == curr){
- d_currentF[writeIter] = curr;
- ++writeIter;
- }else{
- Assert(nextTI != curr);
-
- if(triviallyUnsat(nextTI)){
- raiseConflict(nextTI);
- }else if(!triviallySat(nextTI)){
- TrailIndex nextNextTI = reduceByGCD(nextTI);
-
- if(!(inConflict() || anyCoefficientExceedsMaximum(nextNextTI))){
- Assert(queueConditions(nextNextTI));
- d_currentF[writeIter] = nextNextTI;
- ++writeIter;
- }
- }
- }
- }
- if(!inConflict() && writeIter < N){
- d_currentF.resize(writeIter);
- }
-}
-
-void DioSolver::addTrailElementAsLemma(TrailIndex i) {
- if (options().arith.exportDioDecompositions)
- {
- d_decompositionLemmaQueue.push(i);
- }
-}
-
-Node DioSolver::trailIndexToEquality(TrailIndex i) const {
- const SumPair& sp = d_trail[i].d_eq;
- Node n = sp.getNode();
- Node zero =
- NodeManager::currentNM()->mkConstRealOrInt(n.getType(), Rational(0));
- Node eq = n.eqNode(zero);
- return eq;
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Morgan Deters, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * A Diophantine equation solver for the theory of arithmetic.
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__DIO_SOLVER_H
-#define CVC5__THEORY__ARITH__DIO_SOLVER_H
-
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include "context/cdlist.h"
-#include "context/cdmaybe.h"
-#include "context/cdo.h"
-#include "context/cdqueue.h"
-#include "smt/env_obj.h"
-#include "theory/arith/normal_form.h"
-#include "util/rational.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class DioSolver : protected EnvObj
-{
- private:
- typedef size_t TrailIndex;
- typedef size_t InputConstraintIndex;
- typedef size_t SubIndex;
-
- std::vector<Variable> d_proofVariablePool;
- /** Sat context dependent. */
- context::CDO<size_t> d_lastUsedProofVariable;
-
- /**
- * The set of input constraints is stored in a CDList.
- * Each constraint point to an element of the trail.
- */
- struct InputConstraint {
- Node d_reason;
- TrailIndex d_trailPos;
- InputConstraint(Node reason, TrailIndex pos) : d_reason(reason), d_trailPos(pos) {}
- };
- context::CDList<InputConstraint> d_inputConstraints;
-
- /**
- * This is the next input constraint to handle.
- */
- context::CDO<size_t> d_nextInputConstraintToEnqueue;
-
- /**
- * We maintain a map from the variables associated with proofs to an input constraint.
- * These variables can then be used in polynomial manipulations.
- */
- typedef std::unordered_map<Node, InputConstraintIndex>
- NodeToInputConstraintIndexMap;
- NodeToInputConstraintIndexMap d_varToInputConstraintMap;
-
- Node proofVariableToReason(const Variable& v) const{
- Assert(d_varToInputConstraintMap.find(v.getNode())
- != d_varToInputConstraintMap.end());
- InputConstraintIndex pos = (*(d_varToInputConstraintMap.find(v.getNode()))).second;
- Assert(pos < d_inputConstraints.size());
- return d_inputConstraints[pos].d_reason;
- }
-
- /**
- * The main work horse of the algorithm, the trail of constraints.
- * Each constraint is a SumPair that implicitly represents an equality against 0.
- * d_trail[i].d_eq = (+ c (+ [(* coeff var)])) representing (+ [(* coeff var)]) = -c
- * Each constraint has a proof in terms of a linear combination of the input constraints.
- * d_trail[i].d_proof
- *
- * Each Constraint also a monomial in d_eq.getPolynomial()
- * of minimal absolute value by the coefficients.
- * d_trail[i].d_minimalMonomial
- *
- * See Alberto's paper for how linear proofs are maintained for the abstract
- * state machine in rules (7), (8) and (9).
- */
- struct Constraint {
- SumPair d_eq;
- Polynomial d_proof;
- Monomial d_minimalMonomial;
- Constraint(const SumPair& eq, const Polynomial& p) :
- d_eq(eq), d_proof(p), d_minimalMonomial(d_eq.getPolynomial().selectAbsMinimum())
- {}
- };
- 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);
- // }
- // };
-
- /**
- * A substitution is stored as a constraint in the trail together with
- * the variable to be eliminated, and a fresh variable if one was introduced.
- * The variable d_subs[i].d_eliminated is substituted using the implicit equality in
- * d_trail[d_subs[i].d_constraint]
- * - d_subs[i].d_eliminated is normalized to have coefficient -1 in
- * d_trail[d_subs[i].d_constraint].
- * - d_subs[i].d_fresh is either Node::null() or it is variable it is normalized
- * to have coefficient 1 in d_trail[d_subs[i].d_constraint].
- */
- struct Substitution {
- Node d_fresh;
- Variable d_eliminated;
- TrailIndex d_constraint;
- Substitution(Node f, const Variable& e, TrailIndex c) :
- d_fresh(f), d_eliminated(e), d_constraint(c)
- {}
- };
- context::CDList<Substitution> d_subs;
-
- /**
- * This is the queue of constraints to be processed in the current context level.
- * This is to be empty upon entering solver and cleared upon leaving the solver.
- *
- * All elements in currentF:
- * - are fully substituted according to d_subs.
- * - !isConstant().
- * - If the element is (+ constant (+ [(* coeff var)] )), then the gcd(coeff) = 1
- */
- std::deque<TrailIndex> d_currentF;
- context::CDList<TrailIndex> d_savedQueue;
- context::CDO<size_t> d_savedQueueIndex;
- context::CDMaybe<TrailIndex> d_conflictIndex;
-
- /**
- * Drop derived constraints with a coefficient length larger than
- * the maximum input constraints length than 2**MAX_GROWTH_RATE.
- */
- context::CDO<uint32_t> d_maxInputCoefficientLength;
- static constexpr uint32_t MAX_GROWTH_RATE = 3;
-
- /** Returns true if the element on the trail should be dropped.*/
- bool anyCoefficientExceedsMaximum(TrailIndex j) const;
-
- /**
- * Is true if decomposeIndex has been used in this context.
- */
- context::CDO<bool> d_usedDecomposeIndex;
-
- context::CDO<SubIndex> d_lastPureSubstitution;
- context::CDO<SubIndex> d_pureSubstitionIter;
-
- /**
- * Decomposition lemma queue.
- */
- context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
-
- public:
- /** Construct a Diophantine equation solver with the given context. */
- DioSolver(Env& env);
-
- /** Returns true if the substitutions use no new variables. */
- bool hasMorePureSubstitutions() const
- {
- return d_pureSubstitionIter < d_lastPureSubstitution;
- }
-
- Node nextPureSubstitution();
-
- /**
- * Adds an equality to the queue of the DioSolver.
- * orig is blamed in a conflict.
- * orig can either be of the form (= p c) or (and ub lb).
- * where ub is either (leq p c) or (not (> p [- c 1])), and
- * where lb is either (geq p c) or (not (< p [+ c 1]))
- *
- * If eq cannot be used, this constraint is dropped.
- */
- void pushInputConstraint(const Comparison& eq, Node reason);
-
- /**
- * Processes the queue looking for any conflict.
- * If a conflict is found, this returns conflict.
- * Otherwise, it returns null.
- * The conflict is guarenteed to be over literals given in addEquality.
- */
- Node processEquationsForConflict();
-
- /**
- * Processes the queue looking for an integer unsatisfiable cutting plane.
- * If such a plane is found this returns an entailed plane using no
- * fresh variables.
- */
- SumPair processEquationsForCut();
-
-private:
- /** Returns true if the TrailIndex refers to a element in the trail. */
- bool inRange(TrailIndex i) const{
- return i < d_trail.size();
- }
-
- Node columnGcdIsOne() const;
-
-
- /**
- * Returns true if the context dependent flag for conflicts
- * has been raised.
- */
- bool inConflict() const { return d_conflictIndex.isSet(); }
-
- /** Raises a conflict at the index ti. */
- void raiseConflict(TrailIndex ti){
- Assert(!inConflict());
- d_conflictIndex.set(ti);
- }
-
- /** Returns the conflict index. */
- TrailIndex getConflictIndex() const{
- Assert(inConflict());
- return d_conflictIndex.get();
- }
-
- /**
- * Allocates a "unique" proof variable.
- * This variable is fresh with respect to the context.
- * Returns index of the variable in d_variablePool;
- */
- size_t allocateProofVariable();
-
-
- /** Empties the unproccessed input constraints into the queue. */
- void enqueueInputConstraints();
-
- /**
- * Returns true if an input equality is in the map.
- * This is expensive and is only for debug assertions.
- */
- bool debugEqualityInInputEquations(Node eq);
-
- /** Applies the substitution at subIndex to currentF. */
- void subAndReduceCurrentFByIndex(SubIndex d_subIndex);
-
- /**
- * Takes as input a TrailIndex i and an integer that divides d_trail[i].d_eq, and
- * returns a TrailIndex j s.t.
- * d_trail[j].d_eq = (1/g) d_trail[i].d_eq
- * and
- * d_trail[j].d_proof = (1/g) d_trail[i].d_proof.
- *
- * g must be non-zero.
- *
- * This corresponds to an application of Alberto's rule (7).
- */
- TrailIndex scaleEqAtIndex(TrailIndex i, const Integer& g);
-
-
- /**
- * Takes as input TrailIndex's i and j and Integer's q and r and a TrailIndex k s.t.
- * d_trail[k].d_eq == d_trail[i].d_eq * q + d_trail[j].d_eq * r
- * and
- * d_trail[k].d_proof == d_trail[i].d_proof * q + d_trail[j].d_proof * r
- *
- * This corresponds to an application of Alberto's rule (8).
- */
- TrailIndex combineEqAtIndexes(TrailIndex i, const Integer& q, TrailIndex j, const Integer& r);
-
- /**
- * Decomposes the equation at index ti of trail by the variable
- * with the lowest coefficient.
- * This corresponds to an application of Alberto's rule (9).
- *
- * Returns a pair of a SubIndex and a TrailIndex.
- * The SubIndex is the index of a newly introduced substition.
- */
- std::pair<SubIndex, TrailIndex> decomposeIndex(TrailIndex ti);
-
- /** Solves the index at ti for the value in minimumMonomial. */
- std::pair<SubIndex, TrailIndex> solveIndex(TrailIndex ti);
-
- /** Prints the queue for debugging purposes to Trace("arith::dio"). */
- void printQueue();
-
- /**
- * Exhaustively applies all substitutions discovered to an element of the trail.
- * Returns a TrailIndex corresponding to the substitutions being applied.
- */
- TrailIndex applyAllSubstitutionsToIndex(TrailIndex i);
-
- /**
- * Applies a substitution to an element in the trail.
- */
- TrailIndex applySubstitution(SubIndex s, TrailIndex i);
-
- /**
- * Reduces the trail node at i by the gcd of the variables.
- * Returns the new trail element.
- *
- * This raises the conflict flag if unsat is detected.
- */
- TrailIndex reduceByGCD(TrailIndex i);
-
- /**
- * Returns true if i'th element in the trail is trivially true.
- * (0 = 0)
- */
- bool triviallySat(TrailIndex t);
-
- /**
- * Returns true if i'th element in the trail is trivially unsatisfiable.
- * (1 = 0)
- */
- bool triviallyUnsat(TrailIndex t);
-
- /** Returns true if the gcd of the i'th element of the trail is 1.*/
- bool gcdIsOne(TrailIndex t);
-
- bool debugAnySubstitionApplies(TrailIndex t);
- bool debugSubstitutionApplies(SubIndex si, TrailIndex ti);
-
-
- /** Returns true if the queue of nodes to process is empty. */
- bool queueEmpty() const;
-
- bool queueConditions(TrailIndex t);
-
-
- void pushToQueueBack(TrailIndex t){
- Assert(queueConditions(t));
- d_currentF.push_back(t);
- }
-
- void pushToQueueFront(TrailIndex t){
- Assert(queueConditions(t));
- d_currentF.push_front(t);
- }
-
- /**
- * Moves the minimum Constraint by absolute value of the minimum coefficient to
- * the front of the queue.
- */
- void moveMinimumByAbsToQueueFront();
-
- void saveQueue();
-
- TrailIndex impliedGcdOfOne();
-
-
- /**
- * Processing the current set of equations.
- *
- * decomposeIndex() rule is only applied if allowDecomposition is true.
- */
- bool processEquations(bool allowDecomposition);
-
- /**
- * Constructs a proof from any d_trail[i] in terms of input literals.
- */
- Node proveIndex(TrailIndex i);
-
- /**
- * Returns the SumPair in d_trail[i].d_eq with all of the fresh variables purified out.
- */
- SumPair purifyIndex(TrailIndex i);
-
-public:
- bool hasMoreDecompositionLemmas() const{
- return !d_decompositionLemmaQueue.empty();
- }
- Node nextDecompositionLemma() {
- Assert(hasMoreDecompositionLemmas());
- TrailIndex front = d_decompositionLemmaQueue.front();
- d_decompositionLemmaQueue.pop();
- return trailIndexToEquality(front);
- }
-private:
- Node trailIndexToEquality(TrailIndex i) const;
- void addTrailElementAsLemma(TrailIndex i);
-
-public:
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
-
- IntStat d_conflictCalls;
- IntStat d_cutCalls;
-
- IntStat d_cuts;
- IntStat d_conflicts;
-
- TimerStat d_conflictTimer;
- TimerStat d_cutTimer;
-
- Statistics();
- };
-
- Statistics d_statistics;
-}; /* class DioSolver */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__DIO_SOLVER_H */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-#include "theory/arith/dual_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/env.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-DualSimplexDecisionProcedure::DualSimplexDecisionProcedure(
- Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc)
- : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
- d_pivotsInRound(),
- d_statistics(d_pivots)
-{ }
-
-DualSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots)
- : d_statUpdateConflicts(smtStatisticsRegistry().registerInt(
- "theory::arith::dual::UpdateConflicts")),
- d_processSignalsTime(smtStatisticsRegistry().registerTimer(
- "theory::arith::dual::findConflictOnTheQueueTime")),
- d_simplexConflicts(smtStatisticsRegistry().registerInt(
- "theory::arith::dual::simplexConflicts")),
- d_recentViolationCatches(smtStatisticsRegistry().registerInt(
- "theory::arith::dual::recentViolationCatches")),
- d_searchTime(smtStatisticsRegistry().registerTimer(
- "theory::arith::dual::searchTime")),
- d_finalCheckPivotCounter(
- smtStatisticsRegistry().registerReference<uint32_t>(
- "theory::arith::dual::lastPivots", pivots))
-{
-}
-
-Result::Status DualSimplexDecisionProcedure::dualFindModel(bool exactResult)
-{
- Assert(d_conflictVariables.empty());
-
- d_pivots = 0;
-
- if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
- Trace("arith::findModel") << "dualFindModel() trivial" << endl;
- return Result::SAT;
- }
-
- // We need to reduce this because of
- d_errorSet.reduceToSignals();
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
-
- if(processSignals()){
- d_conflictVariables.purge();
-
- Trace("arith::findModel") << "dualFindModel() early conflict" << endl;
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Trace("arith::findModel") << "dualFindModel() fixed itself" << endl;
- Assert(!d_errorSet.moreSignals());
- return Result::SAT;
- }
-
- Trace("arith::findModel") << "dualFindModel() start non-trivial" << endl;
-
- Result::Status result = Result::UNKNOWN;
-
- exactResult |= d_varOrderPivotLimit < 0;
-
- uint32_t checkPeriod = options().arith.arithSimplexCheckPeriod;
- if (result == Result::UNKNOWN)
- {
- uint32_t numDifferencePivots = options().arith.arithHeuristicPivots < 0
- ? d_numVariables + 1
- : options().arith.arithHeuristicPivots;
- // The signed to unsigned conversion is safe.
- if(numDifferencePivots > 0){
-
- d_errorSet.setSelectionRule(d_heuristicRule);
- if(searchForFeasibleSolution(numDifferencePivots)){
- result = Result::UNSAT;
- }
- }
- }
- Assert(!d_errorSet.moreSignals());
-
- if(!d_errorSet.errorEmpty() && result != Result::UNSAT){
- if(exactResult){
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
- while(!d_errorSet.errorEmpty() && result != Result::UNSAT){
- Assert(checkPeriod > 0);
- if(searchForFeasibleSolution(checkPeriod)){
- result = Result::UNSAT;
- }
- }
- }
- else if (d_varOrderPivotLimit > 0)
- {
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
- if (searchForFeasibleSolution(d_varOrderPivotLimit))
- {
- result = Result::UNSAT;
- }
- }
- }
-
- Assert(!d_errorSet.moreSignals());
- if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
- {
- result = Result::SAT;
- }
-
- d_pivotsInRound.purge();
- // ensure that the conflict variable is still in the queue.
- d_conflictVariables.purge();
-
- Trace("arith::findModel") << "end findModel() " << result << endl;
-
- return result;
-}
-
-//corresponds to Check() in dM06
-//template <SimplexDecisionProcedure::PreferenceFunction pf>
-bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
- TimerStat::CodeTimer codeTimer(d_statistics.d_searchTime);
-
- Trace("arith") << "searchForFeasibleSolution" << endl;
- Assert(remainingIterations > 0);
-
- while(remainingIterations > 0 && !d_errorSet.focusEmpty()){
- if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
- Assert(d_conflictVariables.empty());
- ArithVar x_i = d_errorSet.topFocusVariable();
-
- Trace("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
- if(x_i == ARITHVAR_SENTINEL){
- Trace("arith::update") << "No inconsistent variables" << endl;
- return false; //sat
- }
-
- --remainingIterations;
-
- bool useVarOrderPivot =
- d_pivotsInRound.count(x_i) >= options().arith.arithPivotThreshold;
- if(!useVarOrderPivot){
- d_pivotsInRound.add(x_i);
- }
-
- Trace("arith::update") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
- << " use " << useVarOrderPivot << " threshold "
- << options().arith.arithPivotThreshold << std::endl;
-
- LinearEqualityModule::VarPreferenceFunction pf = useVarOrderPivot ?
- &LinearEqualityModule::minVarOrder : &LinearEqualityModule::minBoundAndColLength;
-
- //DeltaRational beta_i = d_variables.getAssignment(x_i);
- ArithVar x_j = ARITHVAR_SENTINEL;
-
- int32_t prevErrorSize CVC5_UNUSED = d_errorSet.errorSize();
-
- if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){
- x_j = d_linEq.selectSlackUpperBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- Unreachable();
- // ++(d_statistics.d_statUpdateConflicts);
- // reportConflict(x_i);
- // ++(d_statistics.d_simplexConflicts);
- // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat
- // d_conflictVariable = x_i;
- // reportConflict(conflict);
- // return true;
- }else{
- const DeltaRational& l_i = d_variables.getLowerBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, l_i);
- }
- }else if(d_variables.cmpAssignmentUpperBound(x_i) > 0){
- x_j = d_linEq.selectSlackLowerBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- Unreachable();
- // ++(d_statistics.d_statUpdateConflicts);
- // reportConflict(x_i);
- // ++(d_statistics.d_simplexConflicts);
- // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat
- // d_conflictVariable = x_i;
- // reportConflict(conflict);
- // return true;
- }else{
- const DeltaRational& u_i = d_variables.getUpperBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, u_i);
- }
- }
- Assert(x_j != ARITHVAR_SENTINEL);
-
- bool conflict = processSignals();
- int32_t currErrorSize CVC5_UNUSED = d_errorSet.errorSize();
- d_pivots++;
-
- if(TraceIsOn("arith::dual")){
- Trace("arith::dual")
- << "#" << d_pivots
- << " c" << conflict
- << " d" << (prevErrorSize - currErrorSize)
- << " f" << d_errorSet.inError(x_j)
- << " h" << d_conflictVariables.isMember(x_j)
- << " " << x_i << "->" << x_j
- << endl;
- }
-
- if(conflict){
- return true;
- }
- }
- Assert(!d_errorSet.focusEmpty() || d_errorSet.errorEmpty());
- Assert(remainingIterations == 0 || d_errorSet.focusEmpty());
- Assert(d_errorSet.noSignals());
-
- return false;
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- * by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- * These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/simplex.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class DualSimplexDecisionProcedure : public SimplexDecisionProcedure{
-public:
- DualSimplexDecisionProcedure(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override
- {
- return dualFindModel(exactResult);
- }
-
-private:
-
- /**
- * Maps a variable to how many times they have been used as a pivot in the
- * simplex search.
- */
- DenseMultiset d_pivotsInRound;
-
- Result::Status dualFindModel(bool exactResult);
-
- /**
- * This is the main simplex for DPLL(T) loop.
- * It runs for at most maxIterations.
- *
- * Returns true iff it has found a conflict.
- * d_conflictVariable will be set and the conflict for this row is reported.
- */
- bool searchForFeasibleSolution(uint32_t maxIterations);
-
-
- bool processSignals(){
- TimerStat &timer = d_statistics.d_processSignalsTime;
- IntStat& conflictStat = d_statistics.d_recentViolationCatches;
- return standardProcessSignals(timer, conflictStat);
- }
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statUpdateConflicts;
- TimerStat d_processSignalsTime;
- IntStat d_simplexConflicts;
- IntStat d_recentViolationCatches;
- TimerStat d_searchTime;
-
- ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
- Statistics(uint32_t& pivots);
- } d_statistics;
-};/* class DualSimplexDecisionProcedure */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andres Noetzli, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/error_set.h"
-
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ErrorInformation::ErrorInformation()
- : d_variable(ARITHVAR_SENTINEL),
- d_violated(NullConstraint),
- d_sgn(0),
- d_relaxed(false),
- d_inFocus(false),
- d_handle(),
- d_amount(nullptr),
- d_metric(0)
-{
- Trace("arith::error::mem")
- << "def constructor " << d_variable << " " << d_amount.get() << endl;
-}
-
-ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn)
- : d_variable(var),
- d_violated(vio),
- d_sgn(sgn),
- d_relaxed(false),
- d_inFocus(false),
- d_handle(),
- d_amount(nullptr),
- d_metric(0)
-{
- Assert(debugInitialized());
- Trace("arith::error::mem")
- << "constructor " << d_variable << " " << d_amount.get() << endl;
-}
-
-
-ErrorInformation::~ErrorInformation() {
- Assert(d_relaxed != true);
- if (d_amount != nullptr)
- {
- Trace("arith::error::mem") << d_amount.get() << endl;
- Trace("arith::error::mem")
- << "destroy " << d_variable << " " << d_amount.get() << endl;
- d_amount = nullptr;
- }
-}
-
-ErrorInformation::ErrorInformation(const ErrorInformation& ei)
- : d_variable(ei.d_variable)
- , d_violated(ei.d_violated)
- , d_sgn(ei.d_sgn)
- , d_relaxed(ei.d_relaxed)
- , d_inFocus(ei.d_inFocus)
- , d_handle(ei.d_handle)
- , d_metric(0)
-{
- if (ei.d_amount == nullptr)
- {
- d_amount = nullptr;
- }
- else
- {
- d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
- }
- Trace("arith::error::mem")
- << "copy const " << d_variable << " " << d_amount.get() << endl;
-}
-
-ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){
- d_variable = ei.d_variable;
- d_violated = ei.d_violated;
- d_sgn = ei.d_sgn;
- d_relaxed = (ei.d_relaxed);
- d_inFocus = (ei.d_inFocus);
- d_handle = (ei.d_handle);
- d_metric = ei.d_metric;
- if (d_amount != nullptr && ei.d_amount != nullptr)
- {
- Trace("arith::error::mem")
- << "assignment assign " << d_variable << " " << d_amount.get() << endl;
- *d_amount = *ei.d_amount;
- }
- else if (ei.d_amount != nullptr)
- {
- d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
- Trace("arith::error::mem")
- << "assignment alloc " << d_variable << " " << d_amount.get() << endl;
- }
- else if (d_amount != nullptr)
- {
- Trace("arith::error::mem")
- << "assignment release " << d_variable << " " << d_amount.get() << endl;
- d_amount = nullptr;
- }
- else
- {
- d_amount = nullptr;
- }
- return *this;
-}
-
-void ErrorInformation::reset(ConstraintP c, int sgn){
- Assert(!isRelaxed());
- Assert(c != NullConstraint);
- d_violated = c;
- d_sgn = sgn;
-
- if (d_amount != nullptr)
- {
- Trace("arith::error::mem")
- << "reset " << d_variable << " " << d_amount.get() << endl;
- d_amount = nullptr;
- }
-}
-
-void ErrorInformation::setAmount(const DeltaRational& am){
- if (d_amount == nullptr)
- {
- d_amount = std::make_unique<DeltaRational>();
- Trace("arith::error::mem")
- << "setAmount " << d_variable << " " << d_amount.get() << endl;
- }
- (*d_amount) = am;
-}
-
-ErrorSet::Statistics::Statistics()
- : d_enqueues(
- smtStatisticsRegistry().registerInt("theory::arith::pqueue::enqueues")),
- d_enqueuesCollection(smtStatisticsRegistry().registerInt(
- "theory::arith::pqueue::enqueuesCollection")),
- d_enqueuesDiffMode(smtStatisticsRegistry().registerInt(
- "theory::arith::pqueue::enqueuesDiffMode")),
- d_enqueuesVarOrderMode(smtStatisticsRegistry().registerInt(
- "theory::arith::pqueue::enqueuesVarOrderMode")),
- d_enqueuesCollectionDuplicates(smtStatisticsRegistry().registerInt(
- "theory::arith::pqueue::enqueuesCollectionDuplicates")),
- d_enqueuesVarOrderModeDuplicates(smtStatisticsRegistry().registerInt(
- "theory::arith::pqueue::enqueuesVarOrderModeDuplicates"))
-{
-}
-
-ErrorSet::ErrorSet(ArithVariables& vars,
- TableauSizes tabSizes,
- BoundCountingLookup lookups)
- : d_variables(vars),
- d_errInfo(),
- d_selectionRule(options::ErrorSelectionRule::VAR_ORDER),
- d_focus(ComparatorPivotRule(this, d_selectionRule)),
- d_outOfFocus(),
- d_signals(),
- d_tableauSizes(tabSizes),
- d_boundLookup(lookups)
-{}
-
-options::ErrorSelectionRule ErrorSet::getSelectionRule() const
-{
- return d_selectionRule;
-}
-
-void ErrorSet::recomputeAmount(ErrorInformation& ei,
- options::ErrorSelectionRule rule)
-{
- switch(rule){
- case options::ErrorSelectionRule::MINIMUM_AMOUNT:
- case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
- ei.setAmount(computeDiff(ei.getVariable()));
- break;
- case options::ErrorSelectionRule::SUM_METRIC:
- ei.setMetric(sumMetric(ei.getVariable()));
- break;
- case options::ErrorSelectionRule::VAR_ORDER:
- // do nothing
- break;
- }
-}
-
-void ErrorSet::setSelectionRule(options::ErrorSelectionRule rule)
-{
- if(rule != getSelectionRule()){
- FocusSet into(ComparatorPivotRule(this, rule));
- FocusSet::const_iterator iter = d_focus.begin();
- FocusSet::const_iterator i_end = d_focus.end();
- for(; iter != i_end; ++iter){
- ArithVar v = *iter;
- ErrorInformation& ei = d_errInfo.get(v);
- if(ei.inFocus()){
- recomputeAmount(ei, rule);
- FocusSetHandle handle = into.push(v);
- ei.setHandle(handle);
- }
- }
- d_focus.swap(into);
- d_selectionRule = rule;
- }
- Assert(getSelectionRule() == rule);
-}
-
-ComparatorPivotRule::ComparatorPivotRule(const ErrorSet* es,
- options::ErrorSelectionRule r)
- : d_errorSet(es), d_rule(r)
-{}
-
-bool ComparatorPivotRule::operator()(ArithVar v, ArithVar u) const {
- switch(d_rule){
- case options::ErrorSelectionRule::VAR_ORDER:
- // This needs to be the reverse of the minVariableOrder
- return v > u;
- case options::ErrorSelectionRule::SUM_METRIC:
- {
- uint32_t v_metric = d_errorSet->getMetric(v);
- uint32_t u_metric = d_errorSet->getMetric(u);
- if(v_metric == u_metric){
- return v > u;
- }else{
- return v_metric > u_metric;
- }
- }
- case options::ErrorSelectionRule::MINIMUM_AMOUNT:
- {
- const DeltaRational& vamt = d_errorSet->getAmount(v);
- const DeltaRational& uamt = d_errorSet->getAmount(u);
- int cmp = vamt.cmp(uamt);
- if(cmp == 0){
- return v > u;
- }else{
- return cmp > 0;
- }
- }
- case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
- {
- const DeltaRational& vamt = d_errorSet->getAmount(v);
- const DeltaRational& uamt = d_errorSet->getAmount(u);
- int cmp = vamt.cmp(uamt);
- if(cmp == 0){
- return v > u;
- }else{
- return cmp < 0;
- }
- }
- }
- Unreachable();
-}
-
-void ErrorSet::update(ErrorInformation& ei){
- if(ei.inFocus()){
-
- switch(getSelectionRule()){
- case options::ErrorSelectionRule::MINIMUM_AMOUNT:
- case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
- ei.setAmount(computeDiff(ei.getVariable()));
- d_focus.update(ei.getHandle(), ei.getVariable());
- break;
- case options::ErrorSelectionRule::SUM_METRIC:
- ei.setMetric(sumMetric(ei.getVariable()));
- d_focus.update(ei.getHandle(), ei.getVariable());
- break;
- case options::ErrorSelectionRule::VAR_ORDER:
- // do nothing
- break;
- }
- }
-}
-
-/** A variable becomes satisfied. */
-void ErrorSet::transitionVariableOutOfError(ArithVar v) {
- Assert(!inconsistent(v));
- ErrorInformation& ei = d_errInfo.get(v);
- Assert(ei.debugInitialized());
- if(ei.isRelaxed()){
- ConstraintP viol = ei.getViolated();
- if(ei.sgn() > 0){
- d_variables.setLowerBoundConstraint(viol);
- }else{
- d_variables.setUpperBoundConstraint(viol);
- }
- Assert(!inconsistent(v));
- ei.setUnrelaxed();
- }
- if(ei.inFocus()){
- d_focus.erase(ei.getHandle());
- ei.setInFocus(false);
- }
- d_errInfo.remove(v);
-}
-
-
-void ErrorSet::transitionVariableIntoError(ArithVar v) {
- Assert(inconsistent(v));
- bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0;
- int sgn = vilb ? 1 : -1;
- 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);
-
- switch(getSelectionRule()){
- case options::ErrorSelectionRule::MINIMUM_AMOUNT:
- case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
- ei.setAmount(computeDiff(v));
- break;
- case options::ErrorSelectionRule::SUM_METRIC:
- ei.setMetric(sumMetric(ei.getVariable()));
- break;
- case options::ErrorSelectionRule::VAR_ORDER:
- // do nothing
- break;
- }
- ei.setInFocus(true);
- FocusSetHandle handle = d_focus.push(v);
- ei.setHandle(handle);
-}
-
-void ErrorSet::dropFromFocus(ArithVar v) {
- Assert(inError(v));
- ErrorInformation& ei = d_errInfo.get(v);
- Assert(ei.inFocus());
- d_focus.erase(ei.getHandle());
- ei.setInFocus(false);
- d_outOfFocus.push_back(v);
-}
-
-void ErrorSet::addBackIntoFocus(ArithVar v) {
- Assert(inError(v));
- ErrorInformation& ei = d_errInfo.get(v);
- Assert(!ei.inFocus());
- switch(getSelectionRule()){
- case options::ErrorSelectionRule::MINIMUM_AMOUNT:
- case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
- ei.setAmount(computeDiff(v));
- break;
- case options::ErrorSelectionRule::SUM_METRIC:
- ei.setMetric(sumMetric(v));
- break;
- case options::ErrorSelectionRule::VAR_ORDER:
- // do nothing
- break;
- }
-
- ei.setInFocus(true);
- FocusSetHandle handle = d_focus.push(v);
- ei.setHandle(handle);
-}
-
-void ErrorSet::blur(){
- while(!d_outOfFocus.empty()){
- ArithVar v = d_outOfFocus.back();
- d_outOfFocus.pop_back();
-
- if(inError(v) && !inFocus(v)){
- addBackIntoFocus(v);
- }
- }
-}
-
-
-
-int ErrorSet::popSignal() {
- ArithVar back = d_signals.back();
- d_signals.pop_back();
-
- if(inError(back)){
- ErrorInformation& ei = d_errInfo.get(back);
- int prevSgn = ei.sgn();
- int focusSgn = ei.focusSgn();
- bool vilb = d_variables.cmpAssignmentLowerBound(back) < 0;
- bool viub = d_variables.cmpAssignmentUpperBound(back) > 0;
- if(vilb || viub){
- Assert(!vilb || !viub);
- int currSgn = vilb ? 1 : -1;
- if(currSgn != prevSgn){
- ConstraintP curr = vilb ? d_variables.getLowerBoundConstraint(back)
- : d_variables.getUpperBoundConstraint(back);
- ei.reset(curr, currSgn);
- }
- update(ei);
- }else{
- transitionVariableOutOfError(back);
- }
- return focusSgn;
- }else if(inconsistent(back)){
- transitionVariableIntoError(back);
- }
- return 0;
-}
-
-void ErrorSet::clear(){
- // Nothing should be relaxed!
- d_signals.clear();
- d_errInfo.purge();
- d_focus.clear();
-}
-
-void ErrorSet::clearFocus(){
- for(ErrorSet::focus_iterator i =focusBegin(), i_end = focusEnd(); i != i_end; ++i){
- ArithVar f = *i;
- ErrorInformation& fei = d_errInfo.get(f);
- fei.setInFocus(false);
- d_outOfFocus.push_back(f);
- }
- d_focus.clear();
-}
-
-void ErrorSet::reduceToSignals(){
- for(error_iterator ei=errorBegin(), ei_end=errorEnd(); ei != ei_end; ++ei){
- ArithVar curr = *ei;
- signalVariable(curr);
- }
-
- d_errInfo.purge();
- d_focus.clear();
- d_outOfFocus.clear();
-}
-
-DeltaRational ErrorSet::computeDiff(ArithVar v) const{
- Assert(inconsistent(v));
- const DeltaRational& beta = d_variables.getAssignment(v);
- DeltaRational diff = d_variables.cmpAssignmentLowerBound(v) < 0 ?
- d_variables.getLowerBound(v) - beta:
- beta - d_variables.getUpperBound(v);
-
- Assert(diff.sgn() > 0);
- return diff;
-}
-
-void ErrorSet::debugPrint(std::ostream& out) const {
- out << "error set debugprint" << endl;
- for(error_iterator i = errorBegin(), i_end = errorEnd();
- i != i_end; ++i){
- ArithVar e = *i;
- const ErrorInformation& ei = d_errInfo[e];
- ei.print(out);
- out << " ";
- d_variables.printModel(e, out);
- out << endl;
- }
- out << "focus ";
- for(focus_iterator i = focusBegin(), i_end = focusEnd();
- i != i_end; ++i){
- out << *i << " ";
- }
- out << ";" << endl;
-}
-
-void ErrorSet::focusDownToJust(ArithVar v) {
- clearFocus();
-
- ErrorInformation& vei = d_errInfo.get(v);
- vei.setInFocus(true);
- FocusSetHandle handle = d_focus.push(v);
- vei.setHandle(handle);
-}
-
-void ErrorSet::pushErrorInto(ArithVarVec& vec) const{
- for(error_iterator i = errorBegin(), e = errorEnd(); i != e; ++i ){
- vec.push_back(*i);
- }
-}
-
-void ErrorSet::pushFocusInto(ArithVarVec& vec) const{
- for(focus_iterator i = focusBegin(), e = focusEnd(); i != e; ++i ){
- vec.push_back(*i);
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Mathias Preiner, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "options/arith_options.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/tableau_sizes.h"
-#include "util/bin_heap.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-
-/**
- * The priority queue has 3 different modes of operation:
- * - Collection
- * This passively collects arithmetic variables that may be inconsistent.
- * This does not maintain any heap structure.
- * dequeueInconsistentBasicVariable() does not work in this mode!
- * Entering this mode requires the queue to be empty.
- *
- * - Difference Queue
- * This mode uses the difference between a variables and its bound
- * to determine which to dequeue first.
- *
- * - Variable Order Queue
- * This mode uses the variable order to determine which ArithVar is dequeued first.
- *
- * The transitions between the modes of operation are:
- * Collection => Difference Queue
- * Difference Queue => Variable Order Queue
- * Difference Queue => Collection (queue must be empty!)
- * Variable Order Queue => Collection (queue must be empty!)
- *
- * The queue begins in Collection mode.
- */
-
-
-class ErrorSet;
-
-class ComparatorPivotRule {
-private:
- const ErrorSet* d_errorSet;
-
- options::ErrorSelectionRule d_rule;
-
- public:
- ComparatorPivotRule();
- ComparatorPivotRule(const ErrorSet* es, options::ErrorSelectionRule r);
-
- bool operator()(ArithVar v, ArithVar u) const;
- options::ErrorSelectionRule getRule() const { return d_rule; }
-};
-
-// typedef boost::heap::d_ary_heap<
-// ArithVar,
-// boost::heap::arity<2>,
-// boost::heap::compare<ComparatorPivotRule>,
-// boost::heap::mutable_<true> > FocusSet;
-//
-// typedef FocusSet::handle_type FocusSetHandle;
-
-// typedef CVC5_PB_DS_NAMESPACE::priority_queue<
-// ArithVar,
-// ComparatorPivotRule,
-// CVC5_PB_DS_NAMESPACE::pairing_heap_tag> FocusSet;
-
-// typedef FocusSet::point_iterator FocusSetHandle;
-
-typedef BinaryHeap<ArithVar, ComparatorPivotRule> FocusSet;
-typedef FocusSet::handle FocusSetHandle;
-
-
-class ErrorInformation {
-private:
- /** The variable that is in error. */
- ArithVar d_variable;
-
- /**
- * The constraint that was violated.
- * This needs to be saved in case that the
- * violated constraint
- */
- ConstraintP d_violated;
-
- /**
- * This is the sgn of the first derivate the variable must move to satisfy
- * the bound violated.
- * If d_sgn > 0, then d_violated was a lowerbound.
- * If d_sgn < 0, then d_violated was an upperbound.
- */
- int d_sgn;
-
- /**
- * If this is true, then the bound is no longer set on d_variables.
- * This MUST be undone before this is deleted.
- */
- bool d_relaxed;
-
- /**
- * If this is true, then the variable is in the focus set and the focus heap.
- * d_handle is then a reasonable thing to interpret.
- * If this is false, the variable is somewhere in
- */
- bool d_inFocus;
- FocusSetHandle d_handle;
-
- /**
- * Auxillary information for storing the difference between a variable and its bound.
- * Only set on signals.
- */
- std::unique_ptr<DeltaRational> d_amount;
-
- /** */
- uint32_t d_metric;
-
-public:
- ErrorInformation();
- ErrorInformation(ArithVar var, ConstraintP vio, int sgn);
- ~ErrorInformation();
- ErrorInformation(const ErrorInformation& ei);
- ErrorInformation& operator=(const ErrorInformation& ei);
-
- void reset(ConstraintP c, int sgn);
-
- inline ArithVar getVariable() const { return d_variable; }
-
- bool isRelaxed() const { return d_relaxed; }
- void setRelaxed()
- {
- Assert(!d_relaxed);
- d_relaxed = true;
- }
- void setUnrelaxed()
- {
- Assert(d_relaxed);
- d_relaxed = false;
- }
-
- inline int sgn() const { return d_sgn; }
-
- inline bool inFocus() const { return d_inFocus; }
- inline int focusSgn() const {
- return (d_inFocus) ? sgn() : 0;
- }
-
- inline void setInFocus(bool inFocus) { d_inFocus = inFocus; }
-
- const DeltaRational& getAmount() const {
- Assert(d_amount != nullptr);
- return *d_amount;
- }
-
- void setAmount(const DeltaRational& am);
- void setMetric(uint32_t m) { d_metric = m; }
- uint32_t getMetric() const { return d_metric; }
-
- inline void setHandle(FocusSetHandle h) {
- Assert(d_inFocus);
- d_handle = h;
- }
- inline const FocusSetHandle& getHandle() const{ return d_handle; }
-
- inline ConstraintP getViolated() const { return d_violated; }
-
- bool debugInitialized() const {
- return
- d_variable != ARITHVAR_SENTINEL &&
- d_violated != NullConstraint &&
- d_sgn != 0;
- }
- void print(std::ostream& os) const {
- os << "{ErrorInfo: " << d_variable
- << ", " << d_violated
- << ", " << d_sgn
- << ", " << d_relaxed
- << ", " << d_inFocus;
- if (d_amount == nullptr)
- {
- os << "nullptr";
- }
- else
- {
- os << (*d_amount);
- }
- os << "}";
- }
-};
-
-class ErrorInfoMap : public DenseMap<ErrorInformation> {};
-
-class ErrorSet {
-private:
- /**
- * Reference to the arithmetic partial model for checking if a variable
- * is consistent with its upper and lower bounds.
- */
- ArithVariables& d_variables;
-
- /**
- * The set of all variables that violate exactly one of their bounds.
- */
- ErrorInfoMap d_errInfo;
-
- options::ErrorSelectionRule d_selectionRule;
- /**
- * The ordered heap for the variables that are in ErrorSet.
- */
- FocusSet d_focus;
-
-
- /**
- * A strict subset of the error set.
- * d_outOfFocus \neq d_errInfo.
- *
- * Its symbolic complement is Focus.
- * d_outOfFocus \intersect Focus == \emptyset
- * d_outOfFocus \union Focus == d_errInfo
- */
- ArithVarVec d_outOfFocus;
-
- /**
- * Before a variable is added to the error set, it is added to the signals list.
- * A variable may appear on the list multiple times.
- * This introduces a delay.
- */
- ArithVarVec d_signals;
-
- TableauSizes d_tableauSizes;
-
- BoundCountingLookup d_boundLookup;
-
- /**
- * Computes the difference between the assignment and its bound for x.
- */
-public:
- DeltaRational computeDiff(ArithVar x) const;
-private:
- void recomputeAmount(ErrorInformation& ei, options::ErrorSelectionRule r);
-
- void update(ErrorInformation& ei);
- void transitionVariableOutOfError(ArithVar v);
- void transitionVariableIntoError(ArithVar v);
- void addBackIntoFocus(ArithVar v);
-
-public:
-
- /** The new focus set is the entire error set. */
- void blur();
- void dropFromFocus(ArithVar v);
-
- void dropFromFocusAll(const ArithVarVec& vec) {
- for(ArithVarVec::const_iterator i = vec.begin(), i_end = vec.end(); i != i_end; ++i){
- ArithVar v = *i;
- dropFromFocus(v);
- }
- }
-
- ErrorSet(ArithVariables& var, TableauSizes tabSizes, BoundCountingLookup boundLookup);
-
- typedef ErrorInfoMap::const_iterator error_iterator;
- error_iterator errorBegin() const { return d_errInfo.begin(); }
- error_iterator errorEnd() const { return d_errInfo.end(); }
-
- bool inError(ArithVar v) const { return d_errInfo.isKey(v); }
- bool inFocus(ArithVar v) const { return d_errInfo[v].inFocus(); }
-
- void pushErrorInto(ArithVarVec& vec) const;
- void pushFocusInto(ArithVarVec& vec) const;
-
- options::ErrorSelectionRule getSelectionRule() const;
- void setSelectionRule(options::ErrorSelectionRule rule);
-
- inline ArithVar topFocusVariable() const{
- Assert(!focusEmpty());
- return d_focus.top();
- }
-
- inline void signalVariable(ArithVar var){
- d_signals.push_back(var);
- }
-
- inline void signalUnderCnd(ArithVar var, bool b){
- if(b){ signalVariable(var); }
- }
-
- inline bool inconsistent(ArithVar var) const{
- return !d_variables.assignmentIsConsistent(var) ;
- }
- inline void signalIfInconsistent(ArithVar var){
- signalUnderCnd(var, inconsistent(var));
- }
-
- inline bool errorEmpty() const{
- return d_errInfo.empty();
- }
- inline uint32_t errorSize() const{
- return d_errInfo.size();
- }
-
- inline bool focusEmpty() const {
- return d_focus.empty();
- }
- inline uint32_t focusSize() const{
- return d_focus.size();
- }
-
- inline int getSgn(ArithVar x) const {
- Assert(inError(x));
- return d_errInfo[x].sgn();
- }
- inline int focusSgn(ArithVar v) const {
- if(inError(v)){
- return d_errInfo[v].focusSgn();
- }else{
- return 0;
- }
- }
-
- void focusDownToJust(ArithVar v);
-
- void clearFocus();
-
- /** Clears the set. */
- void clear();
- void reduceToSignals();
-
- bool noSignals() const {
- return d_signals.empty();
- }
- bool moreSignals() const {
- return !noSignals();
- }
- ArithVar topSignal() const {
- Assert(moreSignals());
- return d_signals.back();
- }
-
- /**
- * Moves a variable out of the signals.
- * This moves it into the error set.
- * Return the previous focus sign.
- */
- int popSignal();
-
- const DeltaRational& getAmount(ArithVar v) const {
- return d_errInfo[v].getAmount();
- }
-
- uint32_t sumMetric(ArithVar a) const{
- Assert(inError(a));
- BoundCounts bcs = d_boundLookup.atBounds(a);
- uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount();
-
- uint32_t length = d_tableauSizes.getRowLength(a);
-
- return (length - count);
- }
-
- uint32_t getMetric(ArithVar a) const {
- return d_errInfo[a].getMetric();
- }
-
- ConstraintP getViolated(ArithVar a) const {
- return d_errInfo[a].getViolated();
- }
-
-
- typedef FocusSet::const_iterator focus_iterator;
- focus_iterator focusBegin() const { return d_focus.begin(); }
- focus_iterator focusEnd() const { return d_focus.end(); }
-
- void debugPrint(std::ostream& out) const;
-
-private:
- class Statistics {
- public:
- IntStat d_enqueues;
- IntStat d_enqueuesCollection;
- IntStat d_enqueuesDiffMode;
- IntStat d_enqueuesVarOrderMode;
-
- IntStat d_enqueuesCollectionDuplicates;
- IntStat d_enqueuesVarOrderModeDuplicates;
-
- Statistics();
- };
-
- Statistics d_statistics;
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T)decision procedure.
- */
-#include "theory/arith/fc_simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-FCSimplexDecisionProcedure::FCSimplexDecisionProcedure(
- Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc)
- : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
- d_focusSize(0),
- d_focusErrorVar(ARITHVAR_SENTINEL),
- d_focusCoefficients(),
- d_pivotBudget(0),
- d_prevWitnessImprovement(AntiProductive),
- d_witnessImprovementInARow(0),
- d_sgnDisagreements(),
- d_statistics("theory::arith::FC::", d_pivots)
-{ }
-
-FCSimplexDecisionProcedure::Statistics::Statistics(const std::string& name,
- uint32_t& pivots)
- : d_initialSignalsTime(
- smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
- d_initialConflicts(
- smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
- d_fcFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
- d_fcFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
- d_fcMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
- d_fcTimer(smtStatisticsRegistry().registerTimer(name + "Timer")),
- d_fcFocusConstructionTimer(
- smtStatisticsRegistry().registerTimer(name + "Construction")),
- d_selectUpdateForDualLike(smtStatisticsRegistry().registerTimer(
- name + "selectUpdateForDualLike")),
- d_selectUpdateForPrimal(smtStatisticsRegistry().registerTimer(
- name + "selectUpdateForPrimal")),
- d_finalCheckPivotCounter(
- smtStatisticsRegistry().registerReference<uint32_t>(
- name + "lastPivots", pivots))
-{
-}
-
-Result::Status FCSimplexDecisionProcedure::findModel(bool exactResult)
-{
- Assert(d_conflictVariables.empty());
- Assert(d_sgnDisagreements.empty());
-
- d_pivots = 0;
-
- if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
- Trace("arith::findModel") << "fcFindModel() trivial" << endl;
- Assert(d_conflictVariables.empty());
- return Result::SAT;
- }
-
- // We need to reduce this because of
- d_errorSet.reduceToSignals();
-
- // We must start tracking NOW
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
-
- if(initialProcessSignals()){
- d_conflictVariables.purge();
- Trace("arith::findModel") << "fcFindModel() early conflict" << endl;
- Assert(d_conflictVariables.empty());
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Trace("arith::findModel") << "fcFindModel() fixed itself" << endl;
- Assert(d_conflictVariables.empty());
- return Result::SAT;
- }
-
- Trace("arith::findModel") << "fcFindModel() start non-trivial" << endl;
-
- exactResult |= d_varOrderPivotLimit < 0;
-
- d_prevWitnessImprovement = HeuristicDegenerate;
- d_witnessImprovementInARow = 0;
-
- Result::Status result = Result::UNKNOWN;
-
- if (result == Result::UNKNOWN)
- {
- if(exactResult){
- d_pivotBudget = -1;
- }else{
- d_pivotBudget = d_varOrderPivotLimit;
- }
-
- result = dualLike();
-
- if(result == Result::UNSAT){
- ++(d_statistics.d_fcFoundUnsat);
- }else if(d_errorSet.errorEmpty()){
- ++(d_statistics.d_fcFoundSat);
- }else{
- ++(d_statistics.d_fcMissed);
- }
- }
-
- Assert(!d_errorSet.moreSignals());
- if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
- {
- result = Result::SAT;
- }
-
- // ensure that the conflict variable is still in the queue.
- d_conflictVariables.purge();
-
- Trace("arith::findModel") << "end findModel() " << result << endl;
-
- Assert(d_conflictVariables.empty());
- return result;
-}
-
-
-void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){
- if(d_pivotBudget > 0) {
- --d_pivotBudget;
- }
- Assert(w != AntiProductive);
-
- if(w == d_prevWitnessImprovement){
- ++d_witnessImprovementInARow;
- // ignore overflow : probably never reached
- if(d_witnessImprovementInARow == 0){
- --d_witnessImprovementInARow;
- }
- }else{
- if(w != BlandsDegenerate){
- d_witnessImprovementInARow = 1;
- }
- // if w == BlandsDegenerate do not reset the counter
- d_prevWitnessImprovement = w;
- }
- if(strongImprovement(w)){
- d_leavingCountSinceImprovement.purge();
- }
-
- Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
-
-}
-
-uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const {
- switch(d_prevWitnessImprovement){
- case ConflictFound:
- case ErrorDropped:
- case FocusImproved:
- return 0;
- case HeuristicDegenerate:
- case BlandsDegenerate:
- return d_witnessImprovementInARow;
- // Degenerate is unreachable for its own reasons
- case Degenerate:
- case FocusShrank:
- case AntiProductive:
- Unreachable();
- return -1;
- }
- Unreachable();
-}
-
-void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
- uint32_t newErrorSize = d_errorSet.errorSize();
- uint32_t newFocusSize = d_errorSet.focusSize();
-
- //Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize);
- Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize);
-
- if(newFocusSize == 0 || !d_conflictVariables.empty() ){
- tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
- d_focusErrorVar = ARITHVAR_SENTINEL;
- }else if(2*newFocusSize < d_focusSize ){
- tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
- }else{
- adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges);
- }
-
- d_errorSize = newErrorSize;
- d_focusSize = newFocusSize;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){
- Assert(dropped.size() > 0);
- Assert(d_errorSet.focusSize() == d_focusSize);
- Assert(d_errorSet.focusSize() > dropped.size());
-
- uint32_t newFocusSize = d_focusSize - dropped.size();
- Assert(newFocusSize > 0);
-
- if(2 * newFocusSize <= d_focusSize){
- d_errorSet.dropFromFocusAll(dropped);
- tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
- }else{
- shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped);
- d_errorSet.dropFromFocusAll(dropped);
- }
-
- d_focusSize = newFocusSize;
- Assert(d_errorSet.focusSize() == d_focusSize);
- return FocusShrank;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){
- // uint32_t newErrorSize = d_errorSet.errorSize();
- // uint32_t newFocusSize = d_errorSet.focusSize();
- Assert(d_focusSize == d_errorSet.focusSize());
- Assert(d_focusSize > 1);
- Assert(d_errorSet.inFocus(v));
-
- d_errorSet.focusDownToJust(v);
- Assert(d_errorSet.focusSize() == 1);
- d_focusSize = 1;
-
- tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
- return FocusShrank;
-}
-
-
-
-UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
- UpdateInfo selected;
-
- Trace("arith::selectPrimalUpdate")
- << "selectPrimalUpdate" << endl
- << basic << " " << d_tableau.basicRowLength(basic) << " "
- << d_linEq.debugBasicAtBoundCount(basic) << endl;
-
- static constexpr int s_maxCandidatesAfterImprove = 3;
- bool isFocus = basic == d_focusErrorVar;
- Assert(isFocus || d_errorSet.inError(basic));
- int basicDir = isFocus? 1 : d_errorSet.getSgn(basic);
- bool dualLike = !isFocus && d_focusSize > 1;
-
- if(!isFocus){
- loadFocusSigns();
- }
-
- decreasePenalties();
-
- typedef std::vector<Cand> CandVector;
- CandVector candidates;
-
- for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){
- const Tableau::Entry& e = *ri;
- ArithVar curr = e.getColVar();
- if(curr == basic){ continue; }
-
- int sgn = e.getCoefficient().sgn();
- int curr_movement = basicDir * sgn;
-
- bool candidate =
- (curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
- (curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
-
- Trace("arith::selectPrimalUpdate")
- << "storing " << basic
- << " " << curr
- << " " << candidate
- << " " << e.getCoefficient()
- << " " << curr_movement
- << " " << focusCoefficient(curr) << endl;
-
- if(!candidate) { continue; }
-
- if(!isFocus){
- const Rational& focusC = focusCoefficient(curr);
- Assert(dualLike || !focusC.isZero());
- if(dualLike && curr_movement != focusC.sgn()){
- Trace("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl;
- d_sgnDisagreements.push_back(curr);
- continue;
- }else{
- candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC));
- }
- }else{
- candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient()));
- }
- }
-
- CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
- CandVector::iterator i = candidates.begin();
- CandVector::iterator end = candidates.end();
- std::make_heap(i, end, colCmp);
-
- bool checkEverything = d_pivots == 0;
-
- int candidatesAfterFocusImprove = 0;
- while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){
- std::pop_heap(i, end, colCmp);
- --end;
- Cand& cand = (*end);
- ArithVar curr = cand.d_nb;
- const Rational& coeff = *cand.d_coeff;
-
- LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
- UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
-
- Trace("arith::selectPrimalUpdate")
- << "selected " << selected << endl
- << "currProp " << currProposal << endl
- << "coeff " << coeff << endl;
-
- Assert(!currProposal.uninitialized());
-
- if(candidatesAfterFocusImprove > 0){
- candidatesAfterFocusImprove++;
- }
-
- if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
-
- selected = currProposal;
- WitnessImprovement w = selected.getWitness(false);
- Trace("arith::selectPrimalUpdate") << "selected " << w << endl;
- setPenalty(curr, w);
- if(improvement(w)){
- bool exitEarly;
- switch(w){
- case ConflictFound: exitEarly = true; break;
- case ErrorDropped:
- if(checkEverything){
- exitEarly = d_errorSize + selected.errorsChange() == 0;
- Trace("arith::selectPrimalUpdate")
- << "ee " << d_errorSize << " "
- << selected.errorsChange() << " "
- << d_errorSize + selected.errorsChange() << endl;
- }else{
- exitEarly = true;
- }
- break;
- case FocusImproved:
- candidatesAfterFocusImprove = 1;
- exitEarly = false;
- break;
- default:
- exitEarly = false; break;
- }
- if(exitEarly){ break; }
- }
- }else{
- Trace("arith::selectPrimalUpdate") << "dropped "<< endl;
- }
-
- }
-
- if(!isFocus){
- unloadFocusSigns();
- }
- return selected;
-}
-
-bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
- if(inf.getWitness(useBlands) == w){
- switch(w){
- case ConflictFound: return inf.foundConflict();
- case ErrorDropped: return inf.errorsChange() < 0;
- case FocusImproved: return inf.focusDirection() > 0;
- case FocusShrank: return false; // This is not a valid output
- case Degenerate: return false; // This is not a valid output
- case BlandsDegenerate: return useBlands;
- case HeuristicDegenerate: return !useBlands;
- case AntiProductive: return false;
- }
- }
- return false;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){
- bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
- UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands);
- Assert(!selected.uninitialized());
- WitnessImprovement w = selected.getWitness(useBlands);
- Assert(debugCheckWitness(selected, w, useBlands));
-
- updateAndSignal(selected, w);
- logPivot(w);
- return w;
-}
-
-
-WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){
- Assert(!d_sgnDisagreements.empty());
- Assert(d_errorSet.focusSize() >= 2);
-
- if(TraceIsOn("arith::focus")){
- d_errorSet.debugPrint(Trace("arith::focus"));
- }
-
- ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength);
- const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb);
- int oppositeSgn = - (e_evar_nb.getCoefficient().sgn());
- Trace("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl;
-
- ArithVarVec dropped;
-
- Tableau::ColIterator colIter = d_tableau.colIterator(nb);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == nb);
-
- int sgn = entry.getCoefficient().sgn();
- Trace("arith::focus")
- << "on row "
- << d_tableau.rowIndexToBasic(entry.getRowIndex())
- << " "
- << entry.getCoefficient() << endl;
- ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex());
- if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){
- int errSgn = d_errorSet.getSgn(currRow);
-
- if(errSgn * sgn == oppositeSgn){
- dropped.push_back(currRow);
- Trace("arith::focus") << "dropping from focus " << currRow << endl;
- }
- }
- }
-
- d_sgnDisagreements.clear();
- return adjustFocusShrank(dropped);
-}
-
-bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){
- int diff = currErrorSize - prevErrorSize;
- return selected.foundConflict() || diff == selected.errorsChange();
-}
-
-void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
- Trace("updateAndSignal") << "updated basic " << updated;
- Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
- Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
- int dir = !d_variables.assignmentIsConsistent(updated) ?
- d_errorSet.getSgn(updated) : 0;
- Trace("updateAndSignal") << " dir " << dir;
- Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
-}
-
-bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
- if(selected.describesPivot() && updated == selected.leaving()){
- return selected.foundConflict();
- }else{
- return true;
- }
-}
-
-void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
- ArithVar nonbasic = selected.nonbasic();
-
- Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
-
- stringstream ss;
-
- if(selected.describesPivot()){
- ConstraintP limiting = selected.limiting();
- ArithVar basic = limiting->getVariable();
- Assert(d_linEq.basicIsTracked(basic));
- d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
- }else{
- Assert(!selected.unbounded() || selected.errorsChange() < 0);
-
- DeltaRational newAssignment =
- d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
-
- d_linEq.updateTracked(nonbasic, newAssignment);
- }
- d_pivots++;
-
- increaseLeavingCount(nonbasic);
-
- vector< pair<ArithVar, int> > focusChanges;
- while(d_errorSet.moreSignals()){
- ArithVar updated = d_errorSet.topSignal();
- int prevFocusSgn = d_errorSet.popSignal();
-
- if(d_tableau.isBasic(updated)){
- Assert(!d_variables.assignmentIsConsistent(updated)
- == d_errorSet.inError(updated));
- if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
- if(!d_variables.assignmentIsConsistent(updated)){
- if(checkBasicForConflict(updated)){
- reportConflict(updated);
- Assert(debugUpdatedBasic(selected, updated));
- }
- }
- }else{
- Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
- }
- int currFocusSgn = d_errorSet.focusSgn(updated);
- if(currFocusSgn != prevFocusSgn){
- int change = currFocusSgn - prevFocusSgn;
- focusChanges.push_back(make_pair(updated, change));
- }
- }
-
- if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
-
- Assert(
- debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
-
- adjustFocusAndError(selected, focusChanges);
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){
- Assert(d_sgnDisagreements.empty());
- Assert(d_focusSize > 1);
-
- UpdateInfo selected = selectUpdateForDualLike(errorVar);
-
- if(selected.uninitialized()){
- // we found no proposals
- // If this is empty, there must be an error on this variable!
- // this should not be possible. It Should have been caught as a signal earlier
- WitnessImprovement dropped = focusUsingSignDisagreements(errorVar);
- Assert(d_sgnDisagreements.empty());
-
- return dropped;
- }else{
- d_sgnDisagreements.clear();
- }
-
- Assert(d_sgnDisagreements.empty());
- Assert(!selected.uninitialized());
-
- if(selected.focusDirection() == 0 &&
- d_prevWitnessImprovement == HeuristicDegenerate &&
- d_witnessImprovementInARow >= s_focusThreshold){
-
- Trace("focusDownToJust") << "focusDownToJust " << errorVar << endl;
-
- return focusDownToJust(errorVar);
- }else{
- WitnessImprovement w = selected.getWitness(false);
- Assert(debugCheckWitness(selected, w, false));
- updateAndSignal(selected, w);
- logPivot(w);
- return w;
- }
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){
- Assert(d_focusSize >= 2);
-
- Trace("focusDownToLastHalf") << "focusDownToLastHalf "
- << d_errorSet.errorSize() << " "
- << d_errorSet.focusSize() << " ";
-
- uint32_t half = d_focusSize/2;
- ArithVarVec buf;
- for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(),
- i_end = d_errorSet.focusEnd(); i != i_end; ++i){
- if(half > 0){
- --half;
- } else{
- buf.push_back(*i);
- }
- }
- WitnessImprovement w = adjustFocusShrank(buf);
- Trace("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl;
- return w;
-}
-
-WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() {
- Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
- Assert(d_focusSize >= 2);
-
- LinearEqualityModule::UpdatePreferenceFunction upf =
- &LinearEqualityModule::preferWitness<true>;
-
- LinearEqualityModule::VarPreferenceFunction bpf =
- &LinearEqualityModule::minRowLength;
-
- UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf);
-
- if(selected.uninitialized()){
- Trace("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl;
-
- return focusDownToLastHalf();
- }
- Assert(!selected.uninitialized());
- WitnessImprovement w = selected.getWitness(false);
- Assert(debugCheckWitness(selected, w, false));
-
- if(degenerate(w)){
- Trace("selectFocusImproving") << "only degenerate" << endl;
- if(d_prevWitnessImprovement == HeuristicDegenerate &&
- d_witnessImprovementInARow >= s_focusThreshold){
- Trace("selectFocusImproving") << "focus down been degenerate too long" << endl;
- return focusDownToLastHalf();
- }else{
- Trace("selectFocusImproving") << "taking degenerate" << endl;
- }
- }
- Trace("selectFocusImproving") << "selectFocusImproving did this " << selected << endl;
-
- updateAndSignal(selected, w);
- logPivot(w);
- return w;
-}
-
-bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w,
- ostream& out,
- uint32_t prevFocusSize,
- uint32_t prevErrorSize) const
-{
- out << "DLV() ";
- switch(w){
- case ConflictFound:
- out << "found conflict" << endl;
- return !d_conflictVariables.empty();
- case ErrorDropped:
- out << "dropped " << prevErrorSize - d_errorSize << endl;
- return d_errorSize < prevErrorSize;
- case FocusImproved:
- out << "focus improved"<< endl;
- return d_errorSize == prevErrorSize;
- case FocusShrank:
- out << "focus shrank"<< endl;
- return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize;
- case BlandsDegenerate:
- out << "bland degenerate"<< endl;
- return true;
- case HeuristicDegenerate:
- out << "heuristic degenerate"<< endl;
- return true;
- case AntiProductive:
- out << "focus blur" << endl;
- return prevFocusSize == 0;
- case Degenerate:
- return false;
- }
- return false;
-}
-
-Result::Status FCSimplexDecisionProcedure::dualLike()
-{
- TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer);
-
- Assert(d_sgnDisagreements.empty());
- Assert(d_pivotBudget != 0);
- Assert(d_errorSize == d_errorSet.errorSize());
- Assert(d_errorSize > 0);
- Assert(d_focusSize == d_errorSet.focusSize());
- Assert(d_focusSize > 0);
- Assert(d_conflictVariables.empty());
- Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
-
- d_scores.purge();
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
-
- while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
- Trace("dualLike") << "dualLike " << endl;
-
- Assert(d_errorSet.noSignals());
-
- WitnessImprovement w = AntiProductive;
- uint32_t prevFocusSize = d_focusSize;
- uint32_t prevErrorSize = d_errorSize;
-
- if(d_focusSize == 0){
- Assert(d_errorSize == d_errorSet.errorSize());
- Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
-
- d_errorSet.blur();
-
- d_focusSize = d_errorSet.focusSize();
-
- Assert(d_errorSize == d_focusSize);
- Assert(d_errorSize >= 1);
-
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
-
- Trace("dualLike") << "blur " << d_focusSize << endl;
- }else if(d_focusSize == 1){
- // Possible outcomes:
- // - errorSet size shrunk
- // -- fixed v
- // -- fixed something other than v
- // - conflict
- // - budget was exhausted
-
- ArithVar e = d_errorSet.topFocusVariable();
- Trace("dualLike") << "primalImproveError " << e << endl;
- w = primalImproveError(e);
- }else{
-
- // Possible outcomes:
- // - errorSet size shrunk
- // -- fixed v
- // -- fixed something other than v
- // - conflict
- // - budget was exhausted
- // - focus went down
- Assert(d_focusSize > 1);
- ArithVar e = d_errorSet.topFocusVariable();
- static constexpr unsigned s_sumMetricThreshold = 1;
- if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){
- Trace("dualLike") << "dualLikeImproveError " << e << endl;
- w = dualLikeImproveError(e);
- }else{
- Trace("dualLike") << "selectFocusImproving " << endl;
- w = selectFocusImproving();
- }
- }
- Trace("dualLike") << "witnessImprovement: " << w << endl;
- Assert(d_focusSize == d_errorSet.focusSize());
- Assert(d_errorSize == d_errorSet.errorSize());
-
- Assert(debugDualLike(w, Trace("dualLike"), prevFocusSize, prevErrorSize));
- Trace("dualLike") << "Focus size " << d_focusSize << " (was " << prevFocusSize << ")" << endl;
- Trace("dualLike") << "Error size " << d_errorSize << " (was " << prevErrorSize << ")" << endl;
- }
-
-
- if(d_focusErrorVar != ARITHVAR_SENTINEL){
- tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
- d_focusErrorVar = ARITHVAR_SENTINEL;
- }
-
- Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
- if(!d_conflictVariables.empty()){
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Assert(d_errorSet.noSignals());
- return Result::SAT;
- }else{
- Assert(d_pivotBudget == 0);
- return Result::UNKNOWN;
- }
-}
-
-
-void FCSimplexDecisionProcedure::loadFocusSigns(){
- Assert(d_focusCoefficients.empty());
- Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
- for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
- const Tableau::Entry& e = *ri;
- ArithVar curr = e.getColVar();
- d_focusCoefficients.set(curr, &e.getCoefficient());
- }
-}
-
-void FCSimplexDecisionProcedure::unloadFocusSigns(){
- d_focusCoefficients.purge();
-}
-
-const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const {
- if(d_focusCoefficients.isKey(nb)){
- return *(d_focusCoefficients[nb]);
- }else{
- return d_zero;
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T)decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- * by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- * These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/simplex_update.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class FCSimplexDecisionProcedure : public SimplexDecisionProcedure{
-public:
- FCSimplexDecisionProcedure(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override;
-
- // other error variables are dropping
- WitnessImprovement dualLikeImproveError(ArithVar evar);
- WitnessImprovement primalImproveError(ArithVar evar);
-
- // dual like
- // - found conflict
- // - satisfied error set
- Result::Status dualLike();
-
-private:
- static constexpr uint32_t PENALTY = 4;
- DenseMultiset d_scores;
- void decreasePenalties() { d_scores.removeOneOfEverything(); }
- uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
- void setPenalty(ArithVar x, WitnessImprovement w)
- {
- if (improvement(w))
- {
- if (d_scores.count(x) > 0)
- {
- d_scores.removeAll(x);
- }
- }
- else
- {
- d_scores.setCount(x, PENALTY);
- }
- }
-
- /** The size of the focus set. */
- uint32_t d_focusSize;
-
- /** The current error focus variable. */
- ArithVar d_focusErrorVar;
-
- /**
- * The signs of the coefficients in the focus set.
- * This is empty until this has been loaded.
- */
- DenseMap<const Rational*> d_focusCoefficients;
-
- /**
- * Loads the signs of the coefficients of the variables on the row d_focusErrorVar
- * into d_focusSgns.
- */
- void loadFocusSigns();
-
- /** Unloads the information from d_focusSgns. */
- void unloadFocusSigns();
-
- /**
- * The signs of a variable in the row of d_focusErrorVar.
- * d_focusSgns must be loaded.
- */
- const Rational& focusCoefficient(ArithVar nb) const;
-
- int32_t d_pivotBudget;
-
- WitnessImprovement d_prevWitnessImprovement;
- uint32_t d_witnessImprovementInARow;
-
- uint32_t degeneratePivotsInARow() const;
-
- static constexpr uint32_t s_focusThreshold = 6;
- static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
- static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
-
- DenseMap<uint32_t> d_leavingCountSinceImprovement;
- void increaseLeavingCount(ArithVar x){
- if(!d_leavingCountSinceImprovement.isKey(x)){
- d_leavingCountSinceImprovement.set(x,1);
- }else{
- (d_leavingCountSinceImprovement.get(x))++;
- }
- }
- LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
- bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
- d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
- if(useBlands) {
- return &LinearEqualityModule::preferWitness<false>;
- } else {
- return &LinearEqualityModule::preferWitness<true>;
- }
- }
-
- bool debugDualLike(WitnessImprovement w, std::ostream& out,
- uint32_t prevFocusSize, uint32_t prevErrorSize) const;
-
- void debugPrintSignal(ArithVar updated) const;
-
- ArithVarVec d_sgnDisagreements;
-
- void logPivot(WitnessImprovement w);
-
- void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
-
- UpdateInfo selectPrimalUpdate(ArithVar error,
- LinearEqualityModule::UpdatePreferenceFunction upf,
- LinearEqualityModule::VarPreferenceFunction bpf);
-
-
- UpdateInfo selectUpdateForDualLike(ArithVar basic){
- TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
-
- LinearEqualityModule::UpdatePreferenceFunction upf =
- &LinearEqualityModule::preferWitness<true>;
- LinearEqualityModule::VarPreferenceFunction bpf =
- &LinearEqualityModule::minVarOrder;
- return selectPrimalUpdate(basic, upf, bpf);
- }
-
- UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
- TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
-
- LinearEqualityModule::UpdatePreferenceFunction upf;
- if(useBlands) {
- upf = &LinearEqualityModule::preferWitness<false>;
- } else {
- upf = &LinearEqualityModule::preferWitness<true>;
- }
-
- LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
- &LinearEqualityModule::minVarOrder :
- &LinearEqualityModule::minRowLength;
-
- return selectPrimalUpdate(basic, upf, bpf);
- }
- WitnessImprovement selectFocusImproving() ;
-
- WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
- WitnessImprovement focusDownToLastHalf();
- WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
- WitnessImprovement focusDownToJust(ArithVar v);
-
-
- void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
-
- /**
- * This is the main simplex for DPLL(T) loop.
- * It runs for at most maxIterations.
- *
- * Returns true iff it has found a conflict.
- * d_conflictVariable will be set and the conflict for this row is reported.
- */
- bool searchForFeasibleSolution(uint32_t maxIterations);
-
- bool initialProcessSignals(){
- TimerStat &timer = d_statistics.d_initialSignalsTime;
- IntStat& conflictStat = d_statistics.d_initialConflicts;
- bool res = standardProcessSignals(timer, conflictStat);
- d_focusSize = d_errorSet.focusSize();
- return res;
- }
-
- static bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands);
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- TimerStat d_initialSignalsTime;
- IntStat d_initialConflicts;
-
- IntStat d_fcFoundUnsat;
- IntStat d_fcFoundSat;
- IntStat d_fcMissed;
-
- TimerStat d_fcTimer;
- TimerStat d_fcFocusConstructionTimer;
-
- TimerStat d_selectUpdateForDualLike;
- TimerStat d_selectUpdateForPrimal;
-
- ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
- Statistics(const std::string& name, uint32_t& pivots);
- } d_statistics;
-};/* class FCSimplexDecisionProcedure */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andres Noetzli, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/infer_bounds.h"
-#include "theory/rewriter.h"
-
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-using namespace inferbounds;
-
-InferBoundAlgorithm::InferBoundAlgorithm()
- : d_alg(None)
-{}
-
-InferBoundAlgorithm::InferBoundAlgorithm(Algorithms a)
- : d_alg(a)
-{
- Assert(a != Simplex);
-}
-
-InferBoundAlgorithm::InferBoundAlgorithm(
- const std::optional<int>& simplexRounds)
- : d_alg(Simplex)
-{}
-
-Algorithms InferBoundAlgorithm::getAlgorithm() const{
- return d_alg;
-}
-
-const std::optional<int>& InferBoundAlgorithm::getSimplexRounds() const
-{
- Assert(getAlgorithm() == Simplex);
- return d_simplexRounds;
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkLookup(){
- return InferBoundAlgorithm(Lookup);
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkRowSum(){
- return InferBoundAlgorithm(RowSum);
-}
-
-InferBoundAlgorithm InferBoundAlgorithm::mkSimplex(
- const std::optional<int>& rounds)
-{
- return InferBoundAlgorithm(rounds);
-}
-
-ArithEntailmentCheckParameters::ArithEntailmentCheckParameters()
- : d_algorithms()
-{}
-
-ArithEntailmentCheckParameters::~ArithEntailmentCheckParameters()
-{}
-
-
-void ArithEntailmentCheckParameters::addLookupRowSumAlgorithms(){
- addAlgorithm(InferBoundAlgorithm::mkLookup());
- addAlgorithm(InferBoundAlgorithm::mkRowSum());
-}
-
-void ArithEntailmentCheckParameters::addAlgorithm(const inferbounds::InferBoundAlgorithm& alg){
- d_algorithms.push_back(alg);
-}
-
-ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::begin() const{
- return d_algorithms.begin();
-}
-
-ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::end() const{
- return d_algorithms.end();
-}
-
-InferBoundsResult::InferBoundsResult()
- : d_foundBound(false)
- , d_budgetExhausted(false)
- , d_boundIsProvenOpt(false)
- , d_inconsistentState(false)
- , d_reachedThreshold(false)
- , d_value(false)
- , d_term(Node::null())
- , d_upperBound(true)
- , d_explanation(Node::null())
-{}
-
-InferBoundsResult::InferBoundsResult(Node term, bool ub)
- : d_foundBound(false)
- , d_budgetExhausted(false)
- , d_boundIsProvenOpt(false)
- , d_inconsistentState(false)
- , d_reachedThreshold(false)
- , d_value(false)
- , d_term(term)
- , d_upperBound(ub)
- , d_explanation(Node::null())
-{}
-
-bool InferBoundsResult::foundBound() const {
- return d_foundBound;
-}
-bool InferBoundsResult::boundIsOptimal() const {
- return d_boundIsProvenOpt;
-}
-bool InferBoundsResult::inconsistentState() const {
- return d_inconsistentState;
-}
-
-bool InferBoundsResult::boundIsInteger() const{
- return foundBound() && d_value.isIntegral();
-}
-
-bool InferBoundsResult::boundIsRational() const {
- return foundBound() && d_value.infinitesimalIsZero();
-}
-
-Integer InferBoundsResult::valueAsInteger() const{
- Assert(boundIsInteger());
- return getValue().floor();
-}
-const Rational& InferBoundsResult::valueAsRational() const{
- Assert(boundIsRational());
- return getValue().getNoninfinitesimalPart();
-}
-
-const DeltaRational& InferBoundsResult::getValue() const{
- return d_value;
-}
-
-Node InferBoundsResult::getTerm() const { return d_term; }
-
-Node InferBoundsResult::getLiteral() const{
- const Rational& q = getValue().getNoninfinitesimalPart();
- NodeManager* nm = NodeManager::currentNM();
- Node qnode = nm->mkConst(CONST_RATIONAL, q);
-
- Kind k;
- if(d_upperBound){
- // x <= q + c*delta
- Assert(getValue().infinitesimalSgn() <= 0);
- k = boundIsRational() ? kind::LEQ : kind::LT;
- }else{
- // x >= q + c*delta
- Assert(getValue().infinitesimalSgn() >= 0);
- k = boundIsRational() ? kind::GEQ : kind::GT;
- }
- return nm->mkNode(k, getTerm(), qnode);
-}
-
-/* If there is a bound, this is a node that explains the bound. */
-Node InferBoundsResult::getExplanation() const{
- return d_explanation;
-}
-
-
-void InferBoundsResult::setBound(const DeltaRational& dr, Node exp){
- d_foundBound = true;
- d_value = dr;
- d_explanation = exp;
-}
-
-void InferBoundsResult::setBudgetExhausted() { d_budgetExhausted = true; }
-void InferBoundsResult::setReachedThreshold() { d_reachedThreshold = true; }
-void InferBoundsResult::setIsOptimal() { d_boundIsProvenOpt = true; }
-void InferBoundsResult::setInconsistent() { d_inconsistentState = true; }
-
-bool InferBoundsResult::thresholdWasReached() const{
- return d_reachedThreshold;
-}
-bool InferBoundsResult::budgetIsExhausted() const{
- return d_budgetExhausted;
-}
-
-std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr){
- os << "{InferBoundsResult " << std::endl;
- os << "on " << ibr.getTerm() << ", ";
- if(ibr.findUpperBound()){
- os << "find upper bound, ";
- }else{
- os << "find lower bound, ";
- }
- if(ibr.foundBound()){
- os << "found a bound: ";
- if(ibr.boundIsInteger()){
- os << ibr.valueAsInteger() << "(int), ";
- }else if(ibr.boundIsRational()){
- os << ibr.valueAsRational() << "(rat), ";
- }else{
- os << ibr.getValue() << "(extended), ";
- }
-
- os << "as term " << ibr.getLiteral() << ", ";
- os << "explanation " << ibr.getExplanation() << ", ";
- }else {
- os << "did not find a bound, ";
- }
-
- if(ibr.boundIsOptimal()){
- os << "(opt), ";
- }
-
- if(ibr.inconsistentState()){
- os << "(inconsistent), ";
- }
- if(ibr.budgetIsExhausted()){
- os << "(budget exhausted), ";
- }
- if(ibr.thresholdWasReached()){
- os << "(reached threshold), ";
- }
- os << "}";
- return os;
-}
-
-ArithEntailmentCheckSideEffects::ArithEntailmentCheckSideEffects()
- : d_simplexSideEffects(NULL)
-{}
-
-ArithEntailmentCheckSideEffects::~ArithEntailmentCheckSideEffects(){
- if(d_simplexSideEffects != NULL){
- delete d_simplexSideEffects;
- d_simplexSideEffects = NULL;
- }
-}
-
-InferBoundsResult& ArithEntailmentCheckSideEffects::getSimplexSideEffects(){
- if(d_simplexSideEffects == NULL){
- d_simplexSideEffects = new InferBoundsResult;
- }
- return *d_simplexSideEffects;
-}
-
-namespace inferbounds { /* namespace arith */
-
-std::ostream& operator<<(std::ostream& os, const Algorithms a){
- switch(a){
- case None: os << "AlgNone"; break;
- case Lookup: os << "AlgLookup"; break;
- case RowSum: os << "AlgRowSum"; break;
- case Simplex: os << "AlgSimplex"; break;
- default:
- Unhandled();
- }
-
- return os;
-}
-
-} /* namespace inferbounds */
-
-} /* namespace arith */
-} /* namespace theory */
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andrew Reynolds, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-#include <ostream>
-
-#include "expr/node.h"
-#include "theory/arith/delta_rational.h"
-#include "util/integer.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-namespace inferbounds {
- enum Algorithms {None = 0, Lookup, RowSum, Simplex};
- enum SimplexParamKind { Unbounded, NumVars, Direct};
-
-class InferBoundAlgorithm {
-private:
- Algorithms d_alg;
- std::optional<int> d_simplexRounds;
- InferBoundAlgorithm(Algorithms a);
- InferBoundAlgorithm(const std::optional<int>& simplexRounds);
-
- public:
- InferBoundAlgorithm();
-
- Algorithms getAlgorithm() const;
- const std::optional<int>& getSimplexRounds() const;
-
- static InferBoundAlgorithm mkLookup();
- static InferBoundAlgorithm mkRowSum();
- static InferBoundAlgorithm mkSimplex(const std::optional<int>& rounds);
-};
-
-std::ostream& operator<<(std::ostream& os, const Algorithms a);
-} /* namespace inferbounds */
-
-class ArithEntailmentCheckParameters
-{
- private:
- typedef std::vector<inferbounds::InferBoundAlgorithm> VecInferBoundAlg;
- VecInferBoundAlg d_algorithms;
-
-public:
- typedef VecInferBoundAlg::const_iterator const_iterator;
-
- ArithEntailmentCheckParameters();
- ~ArithEntailmentCheckParameters();
-
- void addLookupRowSumAlgorithms();
- void addAlgorithm(const inferbounds::InferBoundAlgorithm& alg);
-
- const_iterator begin() const;
- const_iterator end() const;
-};
-
-
-
-class InferBoundsResult {
-public:
- InferBoundsResult();
- InferBoundsResult(Node term, bool ub);
-
- void setBound(const DeltaRational& dr, Node exp);
- bool foundBound() const;
-
- void setIsOptimal();
- bool boundIsOptimal() const;
-
- void setInconsistent();
- bool inconsistentState() const;
-
- const DeltaRational& getValue() const;
- bool boundIsRational() const;
- const Rational& valueAsRational() const;
- bool boundIsInteger() const;
- Integer valueAsInteger() const;
-
- Node getTerm() const;
- Node getLiteral() const;
- void setTerm(Node t){ d_term = t; }
-
- /* If there is a bound, this is a node that explains the bound. */
- Node getExplanation() const;
-
- bool budgetIsExhausted() const;
- void setBudgetExhausted();
-
- bool thresholdWasReached() const;
- void setReachedThreshold();
-
- bool findUpperBound() const { return d_upperBound; }
-
- void setFindLowerBound() { d_upperBound = false; }
- void setFindUpperBound() { d_upperBound = true; }
-private:
- /* was a bound found */
- bool d_foundBound;
-
- /* was the budget exhausted */
- bool d_budgetExhausted;
-
- /* does the bound have to be optimal*/
- bool d_boundIsProvenOpt;
-
- /* was this started on an inconsistent state. */
- bool d_inconsistentState;
-
- /* reached the threshold. */
- bool d_reachedThreshold;
-
- /* the value of the bound */
- DeltaRational d_value;
-
- /* The input term. */
- Node d_term;
-
- /* Was the bound found an upper or lower bound.*/
- bool d_upperBound;
-
- /* Explanation of the bound. */
- Node d_explanation;
-};
-
-std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr);
-
-class ArithEntailmentCheckSideEffects
-{
- public:
- ArithEntailmentCheckSideEffects();
- ~ArithEntailmentCheckSideEffects();
-
- InferBoundsResult& getSimplexSideEffects();
-
-private:
- InferBoundsResult* d_simplexSideEffects;
-};
-
-
-} /* namespace arith */
-} /* namespace theory */
-} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/approx_simplex.h"
+
+#include <math.h>
+
+#include <cfloat>
+#include <cmath>
+#include <unordered_set>
+
+#include "base/cvc5config.h"
+#include "base/output.h"
+#include "proof/eager_proof_generator.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/cut_log.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/linear/normal_form.h"
+
+#ifdef CVC5_USE_GLPK
+#include "theory/arith/linear/partial_model.h"
+#endif
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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_branchMaxDepth(
+ smtStatisticsRegistry().registerInt("z::approx::branchMaxDepth")),
+ d_branchesMaxOnAVar(
+ smtStatisticsRegistry().registerInt("z::approx::branchesMaxOnAVar")),
+ d_gaussianElimConstructTime(smtStatisticsRegistry().registerTimer(
+ "z::approx::gaussianElimConstruct::time")),
+ d_gaussianElimConstruct(smtStatisticsRegistry().registerInt(
+ "z::approx::gaussianElimConstruct::calls")),
+ d_averageGuesses(
+ smtStatisticsRegistry().registerAverage("z::approx::averageGuesses"))
+{
+}
+
+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 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;
+}
+
+bool ApproximateSimplex::roughlyEqual(double a, double b){
+ if (a == 0){
+ return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
+ }else if (b == 0){
+ return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
+ }else{
+ return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
+ }
+}
+
+Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
+ if(exp.empty()){
+ return Rational(0);
+ }else{
+ Rational result = exp.back();
+ vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
+ vector<Integer>::const_reverse_iterator exp_end = exp.rend();
+ ++exp_iter;
+ while(exp_iter != exp_end){
+ result = result.inverse();
+ const Integer& i = *exp_iter;
+ result += i;
+ ++exp_iter;
+ }
+ return result;
+ }
+}
+std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
+ vector<Integer> mods;
+ if(!q.isZero()){
+ Rational carry = q;
+ for(int i = 0; i <= depth; ++i){
+ Assert(!carry.isZero());
+ mods.push_back(Integer());
+ Integer& back = mods.back();
+ back = carry.floor();
+ Trace("rationalToCfe") << " cfe["<<i<<"]: " << back << endl;
+ carry -= back;
+ if(carry.isZero()){
+ break;
+ }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
+ break;
+ }else{
+ carry = carry.inverse();
+ }
+ }
+ }
+ return mods;
+}
+
+
+Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){
+ Trace("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];
+ Trace("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;
+ Trace("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){
+ Trace("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl;
+ return cand_prev;
+ }else{
+ Trace("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl;
+ return cand_curr;
+ }
+}
+
+std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d,
+ const Integer& D)
+{
+ if (std::optional<Rational> from_double = Rational::fromDouble(d))
+ {
+ return estimateWithCFE(*from_double, D);
+ }
+ return std::optional<Rational>();
+}
+
+std::optional<Rational> ApproximateSimplex::estimateWithCFE(double d)
+{
+ return estimateWithCFE(d, Integer(s_defaultMaxDenom));
+}
+
+class ApproxNoOp : public ApproximateSimplex {
+public:
+ ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s)
+ : ApproximateSimplex(v,l,s)
+ {}
+ ~ApproxNoOp(){}
+
+ LinResult solveRelaxation() override { return LinUnknown; }
+ Solution extractRelaxation() const override { return Solution(); }
+
+ ArithRatPairVec heuristicOptCoeffs() const override
+ {
+ return ArithRatPairVec();
+ }
+
+ MipResult solveMIP(bool al) override { return MipUnknown; }
+ Solution extractMIP() const override { return Solution(); }
+
+ void setOptCoeffs(const ArithRatPairVec& ref) override {}
+
+ void tryCut(int nid, CutInfo& cut) override {}
+
+ std::vector<const CutInfo*> getValidCuts(const NodeLog& node) override
+ {
+ return std::vector<const CutInfo*>();
+ }
+
+ ArithVar getBranchVar(const NodeLog& nl) const override
+ {
+ return ARITHVAR_SENTINEL;
+ }
+
+ double sumInfeasibilities(bool mip) const override { return 0.0; }
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+/* Begin the declaration of GLPK specific code. */
+#ifdef CVC5_USE_GLPK
+extern "C" {
+#include <glpk.h>
+}/* extern "C" */
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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_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;
+
+ bool d_solvedRelaxation;
+ bool d_solvedMIP;
+
+ CutScratchPad d_pad;
+
+ std::vector<Integer> d_denomGuesses;
+
+public:
+ ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
+ ~ApproxGLPK();
+
+ LinResult solveRelaxation() override;
+ Solution extractRelaxation() const override { return extractSolution(false); }
+
+ ArithRatPairVec heuristicOptCoeffs() const override;
+
+ MipResult solveMIP(bool al) override;
+ Solution extractMIP() const override { return extractSolution(true); }
+ void setOptCoeffs(const ArithRatPairVec& ref) override;
+ std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) override;
+ ArithVar getBranchVar(const NodeLog& con) const override;
+
+ static void printGLPKStatus(int status, std::ostream& out);
+
+
+private:
+ Solution extractSolution(bool mip) const;
+ int guessDir(ArithVar v) const;
+
+ // get this stuff out of here
+ void tryCut(int nid, CutInfo& cut) override;
+
+ 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 override
+ {
+ return sumInfeasibilities(mip? d_mipProb : d_realProb);
+ }
+ double sumInfeasibilities(glp_prob* prob, bool mip) const;
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+#endif /*#ifdef CVC5_USE_GLPK */
+/* End the declaration of GLPK specific code. */
+
+/* Begin GPLK/NOGLPK Glue code. */
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){
+#ifdef CVC5_USE_GLPK
+ return new ApproxGLPK(vars, l, s);
+#else
+ return new ApproxNoOp(vars, l, s);
+#endif
+}
+bool ApproximateSimplex::enabled() {
+#ifdef CVC5_USE_GLPK
+ return true;
+#else
+ return false;
+#endif
+}
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+/* End GPLK/NOGLPK Glue code. */
+
+/* Begin GPLK implementation. */
+#ifdef CVC5_USE_GLPK
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+#ifdef CVC5_ASSERTIONS
+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;
+ }
+}
+#endif
+
+ApproxGLPK::ApproxGLPK(const ArithVariables& var,
+ TreeLog& l,
+ ApproximateStatistics& s)
+ : ApproximateSimplex(var, l, s),
+ d_inputProb(nullptr),
+ d_realProb(nullptr),
+ d_mipProb(nullptr),
+ d_solvedRelaxation(false),
+ d_solvedMIP(false)
+{
+
+ d_denomGuesses.push_back(Integer(1<<22));
+ d_denomGuesses.push_back(Integer(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;
+
+ // Assign each variable to a row and column variable as it appears in the input
+ for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+ ArithVar v = *vi;
+
+ if(d_vars.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);
+ Trace("approx") << "Row vars: " << v << "<->" << numRows << endl;
+ }else{
+ ++numCols;
+ d_colIndices.set(v, numCols);
+ d_colToArithVar.set(numCols, v);
+ Trace("approx") << "Col vars: " << v << "<->" << numCols << endl;
+ }
+ }
+ 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;
+
+ int type;
+ double lb = 0.0;
+ double ub = 0.0;
+ if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ if(d_vars.boundsAreEqual(v)){
+ type = GLP_FX;
+ }else{
+ type = GLP_DB;
+ }
+ lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+ ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+ }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+ type = GLP_UP;
+ ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
+ }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ type = GLP_LO;
+ lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
+ }else{
+ type = GLP_FR;
+ }
+
+ if(d_vars.isAuxiliary(v)){
+ int rowIndex = d_rowIndices[v];
+ 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_inputProb, colIndex, kind);
+ glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub);
+ }
+ }
+
+ // Count the number of entries
+ int numEntries = 0;
+ for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+ for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
+ {
+ ++numEntries;
+ }
+ }
+
+ int* ia = new int[numEntries+1];
+ int* ja = new int[numEntries+1];
+ double* ar = new double[numEntries+1];
+
+ int entryCounter = 0;
+ for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ int rowIndex = d_rowIndices[v];
+
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+
+ for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
+ {
+ const Monomial& mono = *j;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Assert(d_vars.hasArithVar(n));
+ ArithVar av = d_vars.asArithVar(n);
+ int colIndex = d_colIndices[av];
+ double coeff = constant.getValue().getDouble();
+
+ ++entryCounter;
+ ia[entryCounter] = rowIndex;
+ ja[entryCounter] = colIndex;
+ ar[entryCounter] = coeff;
+ }
+ }
+ glp_load_matrix(d_inputProb, numEntries, ia, ja, ar);
+
+ delete[] ia;
+ delete[] ja;
+ delete[] ar;
+}
+int ApproxGLPK::guessDir(ArithVar v) const{
+ if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+ return -1;
+ }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ return 1;
+ }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+ return 0;
+ }else{
+ int ubSgn = d_vars.getUpperBound(v).sgn();
+ int lbSgn = d_vars.getLowerBound(v).sgn();
+
+ if(ubSgn != 0 && lbSgn == 0){
+ return -1;
+ }else if(ubSgn == 0 && lbSgn != 0){
+ return 1;
+ }
+
+ return 1;
+ }
+}
+
+ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{
+ ArithRatPairVec ret;
+
+ // Strategies are guess:
+ // 1 simple shared "ceiling" variable: danoint, pk1
+ // x1 >= c, x1 >= tmp1, x1 >= tmp2, ...
+ // 1 large row: fixnet, vpm2, pp08a
+ // (+ .......... ) <= c
+ // Not yet supported:
+ // 1 complex shared "ceiling" variable: opt1217
+ // x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... )
+ // and all of the ... are the same sign
+
+
+ // Candidates:
+ // 1) Upper and lower bounds are not equal.
+ // 2) The variable is not integer
+ // 3a) For columns look for a ceiling variable
+ // 3B) For rows look for a large row with
+
+ DenseMap<BoundCounts> d_colCandidates;
+ DenseMap<uint32_t> d_rowCandidates;
+
+ double sumRowLength = 0.0;
+ uint32_t maxRowLength = 0;
+ for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
+ ArithVar v = *vi;
+
+ int type;
+ if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ if(d_vars.boundsAreEqual(v)){
+ type = GLP_FX;
+ }else{
+ type = GLP_DB;
+ }
+ }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
+ type = GLP_UP;
+ }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
+ type = GLP_LO;
+ }else{
+ type = GLP_FR;
+ }
+
+ if(type != GLP_FX && type != GLP_FR){
+
+ 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);
+ }else if(!d_vars.isInteger(v)){
+ d_colCandidates.set(v, BoundCounts());
+ }
+ }
+ }
+
+ uint32_t maxCount = 0;
+ for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
+ ArithVar v = *i;
+
+ bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v);
+ bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v);
+
+ if(lbCap || ubCap){
+ ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v)
+ : d_vars.getUpperBoundConstraint(v);
+
+ if(!(b->getValue()).noninfinitesimalIsZero()){ continue; }
+
+ Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v));
+ if(poly.size() != 2) { continue; }
+
+ Polynomial::iterator j = poly.begin();
+ Monomial first = *j;
+ ++j;
+ Monomial second = *j;
+
+ bool firstIsPos = first.constantIsPositive();
+ bool secondIsPos = second.constantIsPositive();
+
+ if(firstIsPos == secondIsPos){ continue; }
+
+ Monomial pos = firstIsPos == lbCap ? first : second;
+ Monomial neg = firstIsPos != lbCap ? first : second;
+ // pos >= neg
+ VarList p = pos.getVarList();
+ VarList n = neg.getVarList();
+ if(d_vars.hasArithVar(p.getNode())){
+ ArithVar ap = d_vars.asArithVar(p.getNode());
+ if( d_colCandidates.isKey(ap)){
+ BoundCounts bc = d_colCandidates.get(ap);
+ bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1);
+ maxCount = std::max(maxCount, bc.upperBoundCount());
+ d_colCandidates.set(ap, bc);
+ }
+ }
+ if(d_vars.hasArithVar(n.getNode())){
+ ArithVar an = d_vars.asArithVar(n.getNode());
+ if( d_colCandidates.isKey(an)){
+ BoundCounts bc = d_colCandidates.get(an);
+ bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount());
+ maxCount = std::max(maxCount, bc.lowerBoundCount());
+ d_colCandidates.set(an, bc);
+ }
+ }
+ }
+ }
+
+ // Attempt row
+ double avgRowLength = d_rowCandidates.size() >= 1 ?
+ ( sumRowLength / d_rowCandidates.size() ) : 0.0;
+
+ // There is a large row among the candidates
+ bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength);
+
+ double rowLengthReq = (maxRowLength * .9);
+
+ if (guessARowCandidate)
+ {
+ for (ArithVar r : d_rowCandidates)
+ {
+ uint32_t len = d_rowCandidates[r];
+
+ int dir = guessDir(r);
+ if(len >= rowLengthReq){
+ ret.push_back(ArithRatPair(r, Rational(dir)));
+ }
+ }
+ }
+
+ // Attempt columns
+ bool guessAColCandidate = maxCount >= 4;
+ if (guessAColCandidate)
+ {
+ for (ArithVar c : d_colCandidates)
+ {
+ BoundCounts bc = d_colCandidates[c];
+
+ int dir = guessDir(c);
+ double ubScore = double(bc.upperBoundCount()) / maxCount;
+ double lbScore = double(bc.lowerBoundCount()) / maxCount;
+ if(ubScore >= .9 || lbScore >= .9){
+ ret.push_back(ArithRatPair(c, Rational(c)));
+ }
+ }
+ }
+
+ return ret;
+}
+
+void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
+ DenseMap<double> nbCoeffs;
+
+ for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
+ ArithVar v = (*i).first;
+ const Rational& q = (*i).second;
+
+ if(d_vars.isAuxiliary(v)){
+ // replace the variable by its definition and multiply by q
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
+ Polynomial pq = p * q;
+
+ for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
+ const Monomial& mono = *j;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Assert(d_vars.hasArithVar(n));
+ ArithVar av = d_vars.asArithVar(n);
+ int colIndex = d_colIndices[av];
+ double coeff = constant.getValue().getDouble();
+ if(!nbCoeffs.isKey(colIndex)){
+ nbCoeffs.set(colIndex, 0.0);
+ }
+ nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+ }
+ }else{
+ int colIndex = d_colIndices[v];
+ double coeff = q.getDouble();
+ if(!nbCoeffs.isKey(colIndex)){
+ nbCoeffs.set(colIndex, 0.0);
+ }
+ nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
+ }
+ }
+ for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
+ Index colIndex = *ci;
+ double coeff = nbCoeffs[colIndex];
+ glp_set_obj_coef(d_inputProb, colIndex, coeff);
+ }
+}
+
+
+/*
+ * rough strategy:
+ * real relaxation
+ * try approximate real optimization of error function
+ * pivot in its basis
+ * update to its assignment
+ * check with FCSimplex
+ * check integer solution
+ * try approximate mixed integer problem
+ * stop at the first feasible point
+ * pivot in its basis
+ * update to its assignment
+ * check with FCSimplex
+ */
+
+void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
+ switch(status){
+ case GLP_OPT:
+ out << "GLP_OPT" << endl;
+ break;
+ case GLP_FEAS:
+ out << "GLP_FEAS" << endl;
+ break;
+ case GLP_INFEAS:
+ out << "GLP_INFEAS" << endl;
+ break;
+ case GLP_NOFEAS:
+ out << "GLP_NOFEAS" << endl;
+ break;
+ case GLP_UNBND:
+ out << "GLP_UNBND" << endl;
+ break;
+ case GLP_UNDEF:
+ out << "GLP_UNDEF" << endl;
+ break;
+ default:
+ out << "Status unknown" << endl;
+ break;
+ }
+}
+
+ApproxGLPK::~ApproxGLPK(){
+ glp_delete_prob(d_inputProb);
+ glp_delete_prob(d_realProb);
+ glp_delete_prob(d_mipProb);
+
+}
+
+ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const
+{
+ Assert(d_solvedRelaxation);
+ Assert(!mip || d_solvedMIP);
+
+ ApproximateSimplex::Solution sol;
+ DenseSet& newBasis = sol.newBasis;
+ DenseMap<DeltaRational>& newValues = sol.newValues;
+
+ 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 isAux = d_vars.isAuxiliary(vi);
+ int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
+
+ int status = isAux ? glp_get_row_stat(prob, glpk_index)
+ : glp_get_col_stat(prob, glpk_index);
+
+ bool useDefaultAssignment = false;
+
+ switch(status){
+ case GLP_BS:
+ newBasis.add(vi);
+ useDefaultAssignment = true;
+ break;
+ case GLP_NL:
+ case GLP_NS:
+ if(!mip){
+ newValues.set(vi, d_vars.getLowerBound(vi));
+ }else{// intentionally fall through otherwise
+ useDefaultAssignment = true;
+ }
+ break;
+ case GLP_NU:
+ if(!mip){
+ newValues.set(vi, d_vars.getUpperBound(vi));
+ }else {// intentionally fall through otherwise
+ useDefaultAssignment = true;
+ }
+ break;
+ default:
+ {
+ useDefaultAssignment = true;
+ }
+ break;
+ }
+
+ if(useDefaultAssignment){
+
+ 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);
+
+ if (d_vars.hasLowerBound(vi)
+ && roughlyEqual(newAssign,
+ d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA)))
+ {
+
+ newValues.set(vi, d_vars.getLowerBound(vi));
+ }
+ else if (d_vars.hasUpperBound(vi)
+ && roughlyEqual(
+ newAssign,
+ d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA)))
+ {
+ newValues.set(vi, d_vars.getUpperBound(vi));
+ }
+ else
+ {
+ double rounded = round(newAssign);
+ if (roughlyEqual(newAssign, rounded))
+ {
+ newAssign = rounded;
+ }
+
+ DeltaRational proposal;
+ if (std::optional maybe_new = estimateWithCFE(newAssign))
+ {
+ proposal = *maybe_new;
+ }
+ else
+ {
+ // failed to estimate the old value. defaulting to the current.
+ proposal = d_vars.getAssignment(vi);
+ }
+
+ if (roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA)))
+ {
+ proposal = d_vars.getAssignment(vi);
+ }
+
+ if (d_vars.strictlyLessThanLowerBound(vi, proposal))
+ {
+ proposal = d_vars.getLowerBound(vi);
+ }
+ else if (d_vars.strictlyGreaterThanUpperBound(vi, proposal))
+ {
+ proposal = d_vars.getUpperBound(vi);
+ }
+ newValues.set(vi, proposal);
+ }
+ }
+ }
+ return sol;
+}
+
+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;
+ Trace("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl;
+ }
+ }
+ if(lb != -DBL_MAX){
+ if(newAssign < lb){
+ double lbinf = lb - newAssign;
+ infeas += lbinf;
+
+ Trace("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl;
+ }
+ }
+ }
+ return infeas;
+}
+
+LinResult ApproxGLPK::solveRelaxation(){
+ Assert(!d_solvedRelaxation);
+
+ glp_smcp parm;
+ glp_init_smcp(&parm);
+ parm.presolve = GLP_OFF;
+ parm.meth = GLP_PRIMAL;
+ parm.pricing = GLP_PT_PSE;
+ parm.it_lim = d_pivotLimit;
+ parm.msg_lev = GLP_MSG_OFF;
+
+ 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_realProb);
+ int iterationcount = glp_get_it_cnt(d_realProb);
+ switch(status){
+ case GLP_OPT:
+ case GLP_FEAS:
+ case GLP_UNBND:
+ d_solvedRelaxation = true;
+ return LinFeasible;
+ case GLP_INFEAS:
+ case GLP_NOFEAS:
+ d_solvedRelaxation = true;
+ return LinInfeasible;
+ default:
+ {
+ if(iterationcount >= d_pivotLimit){
+ return LinExhausted;
+ }
+ return LinUnknown;
+ }
+ }
+ }
+ default:
+ 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){
+ Trace("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(TraceIsOn("approx::mirCut")){
+ Trace("approx::mirCut") << "mir_id: " << exec_ord << endl;
+ row_sum.print(Trace("approx::mirCut"));
+ }
+
+ return mir;
+}
+
+static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){
+ Trace("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 CVC5_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;
+
+ Trace("approx::gmiCut")
+ << gmi <<" " << gmi->basic << " "
+ << cut_ord<<" " << M <<" " << gmi_var << endl;
+
+ PrimitiveVec& tab_row = gmi->tab_row;
+ Trace("approx::gmiCut") << "Is N sufficient here?" << endl;
+ tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs);
+
+ Trace("approx::gmiCut") << "gmi_var " << gmi_var << endl;
+
+ Trace("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;
+ Trace("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];
+ Trace("approx::gmiCut") << "ind " << i << " " << ind << endl;
+ stat = (ind <= M) ?
+ glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M);
+
+ Trace("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(TraceIsOn("approx::gmiCut")){
+ gmi->print(Trace("approx::gmiCut"));
+ }
+ return gmi;
+}
+
+static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){
+ //(tree, br_var, br_val, dn < 0);
+ double rhs;
+ Kind k;
+ if(down_bad){
+ // down branch is infeasible
+ // x <= floor(v) is infeasible
+ // - so x >= ceiling(v) is implied
+ k = kind::GEQ;
+ rhs = std::ceil(br_val);
+ }else{
+ // up branch is infeasible
+ // x >= ceiling(v) is infeasible
+ // - so x <= floor(v) is implied
+ k = kind::LEQ;
+ rhs = std::floor(br_val);
+ }
+ BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs);
+ return br_cut;
+}
+
+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);
+ Trace("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:
+ Trace("approx") << "GLP_RF_COV" << endl;
+ break;
+ case GLP_RF_CLQ:
+ Trace("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);
+ Trace("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl;
+ for(int i = 1; i <= N; ++i){
+ Trace("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;
+
+ Trace("approx::") << "branch: "<< br_var << " " << br_val << " tree " << p << " " << dn << " " << up << endl;
+ Trace("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl;
+ if(dn < 0 && up < 0){
+ Trace("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){
+ Trace("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{
+ Trace("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);
+ Trace("approx::") << "close " << glpk_node_p << endl;
+ tl.close(node_ord);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch(glp_ios_reason(tree)){
+ case GLP_IBINGO:
+ Trace("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 = 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;
+ }
+}
+
+std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con)
+{
+ 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;
+}
+
+ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{
+ int br_var = con.branchVariable();
+ return getArithVarFromStructural(br_var);
+}
+
+
+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;
+ parm.pp_tech = GLP_PP_NONE;
+ parm.fp_heur = GLP_ON;
+ parm.gmi_cuts = GLP_ON;
+ parm.mir_cuts = GLP_ON;
+ parm.cov_cuts = GLP_ON;
+ parm.cb_func = glpkCallback;
+ parm.cb_info = &aux;
+ parm.msg_lev = GLP_MSG_OFF;
+
+ glp_erase_prob(d_mipProb);
+ glp_copy_prob(d_mipProb, d_realProb, GLP_OFF);
+
+ int res = glp_intopt(d_mipProb, &parm);
+
+ Trace("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl;
+
+ switch(res){
+ case 0:
+ case GLP_ESTOP:
+ {
+ int status = glp_mip_status(d_mipProb);
+ Trace("approx::") << "status " << status << endl;
+ switch(status){
+ case GLP_OPT:
+ case GLP_FEAS:
+ d_solvedMIP = true;
+ Trace("approx::") << "bingo here!" << endl;
+ return MipBingo;
+ case GLP_NOFEAS:
+ d_solvedMIP = true;
+ return MipClosed;
+ default:
+ if(aux.term == MipBingo){
+ d_solvedMIP = true;
+ Trace("approx::") << "bingo here?" << endl;
+ }
+ return aux.term;
+ }
+ }
+ default:
+ return MipUnknown;
+ }
+}
+
+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();
+
+ Trace("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;
+ Trace("approx::sumConstraints") << " +("<<bound << "*" << psi <<")";
+ if(anyinf != NULL ){
+ *anyinf = *anyinf || !bound.infinitesimalIsZero();
+ }
+ }
+ Trace("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);
+ Trace("removeFixed") << "remove fixed " << p.first << endl;
+ if(p.second != NullConstraint){
+ exp.insert(p.second);
+ Trace("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(TraceIsOn("guessIsConstructable")){
+ if(!g.empty()){
+ Trace("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl;
+ DenseVector::print(Trace("approx::guessIsConstructable"), g);
+ Trace("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){
+ Trace("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{
+
+ Trace("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl;
+
+ const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs;
+ const Rational& constructedRhs = d_pad.d_cut.rhs;
+ std::unordered_set<ArithVar> visited;
+
+ if(constructedLhs.empty()){
+ Trace("approx::checkCutOnPad") << "its empty?" <<endl;
+ return true;
+ }
+ if(cut.getKind() != d_pad.d_cutKind) {
+ Trace("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(TraceIsOn("approx::checkCutOnPad")){
+ Trace("approx::checkCutOnPad") << " didn't find key for " << x << std::endl;
+ cut.print(Trace("approx::checkCutOnPad"));
+ Trace("approx::checkCutOnPad") << endl;
+ d_pad.d_cut.print(Trace("approx::checkCutOnPad"));
+ Trace("approx::checkCutOnPad") << endl;
+ }
+ return true;
+ }
+
+ const Rational& onConstructed = constructedLhs[x];
+
+ Trace("approx::checkCutOnPad") << ind << " " << coeff << " " << endl;
+ Trace("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl;
+
+ if(!roughlyEqual(coeff, onConstructed.getDouble())){
+ Trace("approx::checkCutOnPad") << "coeff failure" << endl;
+ return true;
+ }
+ }
+ if(visited.size() != constructedLhs.size()){
+ Trace("approx::checkCutOnPad") << "size mismatch" << endl;
+ return true;
+ }
+
+
+ if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){
+ Trace("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;
+ std::optional<Rational> br_cut_rhs = Rational::fromDouble(br_cut.getRhs());
+ if (!br_cut_rhs)
+ {
+ return true;
+ }
+
+ rhs = estimateWithCFE(*br_cut_rhs, 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;
+
+ std::optional<Rational> delta = estimateWithCFE(mir.delta);
+ if (!delta)
+ {
+ return true;
+ }
+
+ d_pad.d_failure = (delta->sgn() <= 0);
+ if(d_pad.d_failure){ return true; }
+
+ Trace("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);
+}
+
+/** Returns true on failure. */
+bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){
+ if(ri <= 0) { return true; }
+
+ Trace("glpk::loadVB") << "loadVB()" << endl;
+
+ ArithVar rowVar = _getArithVar(nid, M, ri);
+ ArithVar contVar = _getArithVar(nid, M, j);
+ if(rowVar == ARITHVAR_SENTINEL){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " rowVar is ARITHVAR_SENTINEL " << rowVar << endl;
+ return true;
+ }
+ if(contVar == ARITHVAR_SENTINEL){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " contVar is ARITHVAR_SENTINEL " << contVar
+ << endl;
+ return true; }
+
+ if(!d_vars.isAuxiliary(rowVar)){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " rowVar is not auxilliary " << rowVar << endl;
+ return true;
+ }
+ // is integer is correct here
+ if(d_vars.isInteger(contVar)){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " contVar is integer " << contVar << endl;
+ return true;
+ }
+
+ ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar);
+ ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar);
+
+ if(lb != NullConstraint && ub != NullConstraint){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " lb and ub are both NULL " << lb << " " << ub
+ << endl;
+ return true;
+ }
+
+ ConstraintP rcon = lb == NullConstraint ? ub : lb;
+ if(rcon == NullConstraint) {
+ Trace("glpk::loadVB") << "loadVB()"
+ << " rcon is NULL " << rcon << endl;
+ return true;
+ }
+
+ if(!rcon->getValue().isZero()){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " rcon value is not 0 " << rcon->getValue()
+ << endl;
+ return true;
+ }
+
+ if(!d_vars.hasNode(rowVar)){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " does not have node " << rowVar << endl;
+ return true;
+ }
+
+ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar));
+ if (p.size() != 2)
+ {
+ Trace("glpk::loadVB") << "loadVB()"
+ << " polynomial is not binary: " << p.getNode()
+ << endl;
+ return true;
+ }
+
+ 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)) {
+ Trace("glpk::loadVB") << "loadVB()"
+ << " does not have a variable for nx1: " << nx1
+ << endl;
+ return true;
+ }
+ if(!d_vars.hasArithVar(nx2)) {
+ Trace("glpk::loadVB") << "loadVB()"
+ << " does not have a variable for nx2 " << nx2
+ << endl;
+ return true;
+ }
+ ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2);
+
+ Assert(x1 != x2);
+ Assert(!c1.isZero());
+ Assert(!c2.isZero());
+
+ Trace("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;
+
+ Trace("glpk::loadVB")
+ << " cv " << contVar
+ << " cc " << cc
+ << " iv " << iv
+ << " c2 " << ic << endl;
+
+ if(!d_vars.isIntegerInput(iv)){
+ Trace("glpk::loadVB") << "loadVB()"
+ << " iv is not an integer input variable " << iv
+ << endl;
+ return true;
+ }
+ // cc * cv + ic * iv <= 0 or
+ // cc * cv + ic * iv <= 0
+
+ if(rcon == ub){ // multiply by -1
+ cc = -cc; ic = - ic;
+ }
+ Trace("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;
+ Trace("glpk::loadVB") << d << " " << cc.sgn() << endl;
+ bool nowUb = cc.sgn() < 0;
+ if(wantUb != nowUb) {
+ Trace("glpk::loadVB") << "loadVB()"
+ << " wantUb is not nowUb " << wantUb << " " << nowUb
+ << endl;
+
+ return true;
+ }
+
+ Kind rel = wantUb ? kind::LEQ : kind::GEQ;
+
+ tmp = VirtualBound(contVar, rel, d, iv, rcon);
+ Trace("glpk::loadVB") << "loadVB()"
+ << " was successful" << endl;
+ 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){
+ Trace("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){
+ Trace("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;
+ Trace("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){
+ Trace("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;
+ }
+ }
+ }
+
+ Trace("approx::mir") << " for: " << j << ", " << v;
+ Trace("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " ";
+ Trace("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:
+ Trace("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; }
+ std::optional<Rational> c = estimateWithCFE(coeff);
+ if (!c)
+ {
+ return true;
+ }
+
+ if (lhs.isKey(x))
+ {
+ lhs.get(x) -= *c;
+ }
+ else
+ {
+ lhs.set(x, -(*c));
+ }
+ }
+
+ Trace("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl;
+ if(TraceIsOn("approx::mir")) { DenseVector::print(Trace("approx::mir"), lhs); }
+ removeAuxillaryVariables(d_vars, lhs);
+ Trace("approx::mir") << "end loadRowSumIntoAgg() 1" << endl;
+
+ if(TraceIsOn("approx::mir")){
+ Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
+ DenseVector::print(Trace("approx::mir"), lhs);
+ Trace("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);
+ std::optional<Rational> c = estimateWithCFE(coeff);
+ if (!c)
+ {
+ return true;
+ }
+ Assert(!lhs.isKey(x));
+ lhs.set(x, *c);
+ }
+
+ if(TraceIsOn("approx::mir")){
+ Trace("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
+ DenseVector::print(Trace("approx::mir"), lhs);
+ Trace("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;
+
+ Trace("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)){
+ Trace("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);
+ }
+ }
+
+ Trace("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++;
+ }
+ }
+
+ Trace("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(TraceIsOn("gaussianElimConstructTableRow")){
+ Trace("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ vec.print(Trace("gaussianElimConstructTableRow"));
+ Trace("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){
+ Trace("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
+ return true;
+ }
+ onrow.insert(var);
+ }
+
+
+ Trace("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+ Matrix<Rational> A;
+ A.increaseSizeTo(d_vars.getNumberOfVariables());
+ std::vector< std::pair<RowIndex, ArithVar> > rows;
+ // load the rows for auxiliary variables into A
+ for (ArithVar v : onrow)
+ {
+ 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));
+ }
+ }
+ Trace("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();
+ Trace("gaussianElimConstructTableRow")
+ << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl;
+ A.rowPlusRowTimesConstant(rid, prevRow, cp);
+ }
+ }
+ if(TraceIsOn("gaussianElimConstructTableRow")){
+ A.printMatrix(Trace("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()){
+ Trace("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ return true;
+ }else{
+ // 0 = q * s + sum c_i * x_i
+ Rational mult = -(q.inverse());
+ Trace("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl;
+ Trace("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;
+ }
+ }
+ Trace("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+ if(rows.empty()) {
+ Trace("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){
+ Trace("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());
+ }
+ Trace("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ if(!tab.isKey(basic)){
+ Trace("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ return true;
+ }
+ if(tab[basic] != Rational(-1)){
+ Trace("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ return true;
+ }
+
+ tab.remove(basic);
+ Trace("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+
+ if(vec.len < 0 ){
+ Trace("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ return true;
+ }
+ if(tab.size() != ((unsigned)vec.len) ) {
+ Trace("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() << " " << vec.len << endl;
+ return true;
+ }
+
+ Trace("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)){
+ Trace("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
+ return true;
+ }
+
+ double est = tab[var].getDouble();
+
+ if(!ApproximateSimplex::roughlyEqual(coeff, est)){
+ Trace("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
+ << " boink on " << ind << " " << var << " " << est <<endl;
+ return true;
+ }
+ Trace("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl;
+ }
+
+ Trace("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 << i+1;
+ Trace("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(TraceIsOn("guessCoefficientsConstructTableRow")){
+ Trace("guessCoefficientsConstructTableRow") << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl;
+ vec.print(Trace("guessCoefficientsConstructTableRow"));
+ Trace("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){
+ Trace("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
+ return true;
+ }
+ Trace("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl;
+
+ std::optional<Rational> cfe = estimateWithCFE(coeff, D);
+ if (!cfe)
+ {
+ return true;
+ }
+ tab.set(var, *cfe);
+ Trace("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl;
+ }
+ if(!guessIsConstructable(tab)){
+ Trace("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();
+ Trace("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
+ Trace("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(TraceIsOn("approx::gmi")){
+ Trace("approx::gmi") << "pre removeSlackVariables";
+ d_pad.d_cut.print(Trace("approx::gmi"));
+ Trace("approx::gmi") << endl;
+ }
+ removeAuxillaryVariables(d_vars, cut);
+
+ if(TraceIsOn("approx::gmi")){
+ Trace("approx::gmi") << "post removeAuxillaryVariables";
+ d_pad.d_cut.print(Trace("approx::gmi"));
+ Trace("approx::gmi") << endl;
+ }
+ removeFixed(d_vars, d_pad.d_cut, explanation);
+
+ if(TraceIsOn("approx::gmi")){
+ Trace("approx::gmi") << "post removeFixed";
+ d_pad.d_cut.print(Trace("approx::gmi"));
+ Trace("approx::gmi") << endl;
+ }
+ return false;
+}
+
+void ApproxGLPK::tryCut(int nid, CutInfo& cut)
+{
+ 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{
+ Trace("approx") << "failure " << cut.getKlass() << endl;
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+#endif /*#ifdef CVC5_USE_GLPK */
+/* End GPLK implementation. */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+#include <vector>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/delta_rational.h"
+#include "util/dense_map.h"
+#include "util/rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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:
+ ApproximateStatistics();
+
+ IntStat d_branchMaxDepth;
+ IntStat d_branchesMaxOnAVar;
+
+ TimerStat d_gaussianElimConstructTime;
+ IntStat d_gaussianElimConstruct;
+ AverageStat d_averageGuesses;
+};
+
+
+class NodeLog;
+class TreeLog;
+class ArithVariables;
+class CutInfo;
+
+class ApproximateSimplex{
+ public:
+ static bool enabled();
+
+ /**
+ * 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, 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.*/
+ struct Solution {
+ DenseSet newBasis;
+ DenseMap<DeltaRational> newValues;
+ Solution() : newBasis(), newValues(){}
+ };
+
+ virtual ArithVar getBranchVar(const NodeLog& nl) const = 0;
+
+ /** Sets a maximization criteria for the approximate solver.*/
+ virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
+
+ virtual ArithRatPairVec heuristicOptCoeffs() const = 0;
+
+ virtual LinResult solveRelaxation() = 0;
+ virtual Solution extractRelaxation() const = 0;
+
+ virtual MipResult solveMIP(bool activelyLog) = 0;
+
+ virtual Solution extractMIP() const = 0;
+
+ virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) = 0;
+
+ virtual void tryCut(int nid, CutInfo& cut) = 0;
+
+ /** UTILITIES FOR DEALING WITH ESTIMATES */
+
+ static constexpr double SMALL_FIXED_DELTA = .000000001;
+ static constexpr double TOLERENCE = 1 + .000000001;
+
+ /** Returns true if two doubles are roughly equal based on TOLERENCE and SMALL_FIXED_DELTA.*/
+ static bool roughlyEqual(double a, double b);
+
+ /**
+ * Estimates a double as a Rational using continued fraction expansion that
+ * cuts off the estimate once the value is approximately zero.
+ * This is designed for removing rounding artifacts.
+ */
+ static std::optional<Rational> estimateWithCFE(double d);
+ static std::optional<Rational> estimateWithCFE(double d, const Integer& D);
+
+ /**
+ * Converts a rational to a continued fraction expansion representation
+ * using a maximum number of expansions equal to depth as long as the expression
+ * is not roughlyEqual() to 0.
+ */
+ static std::vector<Integer> rationalToCfe(const Rational& q, int depth);
+
+ /** Converts a continued fraction expansion representation to a rational. */
+ static Rational cfeToRational(const std::vector<Integer>& exp);
+
+ /** Estimates a rational as a continued fraction expansion.*/
+ static Rational estimateWithCFE(const Rational& q, const Integer& K);
+
+ virtual double sumInfeasibilities(bool mip) const = 0;
+
+ protected:
+ const ArithVariables& d_vars;
+ TreeLog& d_log;
+ ApproximateStatistics& d_stats;
+
+ /* the maximum pivots allowed in a query. */
+ int d_pivotLimit;
+
+ /* maximum branches allowed on a variable */
+ int d_branchLimit;
+
+ /* maxmimum branching depth allowed.*/
+ int d_maxDepth;
+
+ /* Default denominator for diophatine approximation, 2^{26} .*/
+ static constexpr uint64_t s_defaultMaxDenom = (1 << 26);
+};/* class ApproximateSimplex */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Dejan Jovanovic, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include <vector>
+
+#include "base/output.h"
+#include "expr/node_algorithm.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithStaticLearner::ArithStaticLearner(context::Context* userContext)
+ : d_minMap(userContext), d_maxMap(userContext), d_statistics()
+{
+}
+
+ArithStaticLearner::~ArithStaticLearner(){
+}
+
+ArithStaticLearner::Statistics::Statistics()
+ : d_iteMinMaxApplications(smtStatisticsRegistry().registerInt(
+ "theory::arith::iteMinMaxApplications")),
+ d_iteConstantApplications(smtStatisticsRegistry().registerInt(
+ "theory::arith::iteConstantApplications"))
+{
+}
+
+void ArithStaticLearner::staticLearning(TNode n, NodeBuilder& learned)
+{
+ vector<TNode> workList;
+ workList.push_back(n);
+ TNodeSet processed;
+
+ //Contains an underapproximation of nodes that must hold.
+ TNodeSet defTrue;
+
+ defTrue.insert(n);
+
+ while(!workList.empty()) {
+ n = workList.back();
+
+ bool unprocessedChildren = false;
+ for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+ if(processed.find(*i) == processed.end()) {
+ // unprocessed child
+ workList.push_back(*i);
+ unprocessedChildren = true;
+ }
+ }
+ if(n.getKind() == AND && defTrue.find(n) != defTrue.end() ){
+ for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) {
+ defTrue.insert(*i);
+ }
+ }
+
+ if(unprocessedChildren) {
+ continue;
+ }
+
+ workList.pop_back();
+ // has node n been processed in the meantime ?
+ if(processed.find(n) != processed.end()) {
+ continue;
+ }
+ processed.insert(n);
+
+ process(n,learned, defTrue);
+
+ }
+}
+
+void ArithStaticLearner::process(TNode n,
+ NodeBuilder& learned,
+ const TNodeSet& defTrue)
+{
+ Trace("arith::static") << "===================== looking at " << n << endl;
+
+ switch(n.getKind()){
+ case ITE:
+ if (expr::hasBoundVar(n))
+ {
+ // Unsafe with non-ground ITEs; do nothing
+ Trace("arith::static")
+ << "(potentially) non-ground ITE, ignoring..." << endl;
+ break;
+ }
+
+ if(n[0].getKind() != EQUAL &&
+ isRelationOperator(n[0].getKind()) ){
+ iteMinMax(n, learned);
+ }
+
+ if((d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) ||
+ (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end())) {
+ iteConstant(n, learned);
+ }
+ break;
+
+ case CONST_RATIONAL:
+ case CONST_INTEGER:
+ // Mark constants as minmax
+ d_minMap.insert(n, n.getConst<Rational>());
+ d_maxMap.insert(n, n.getConst<Rational>());
+ break;
+ default: // Do nothing
+ break;
+ }
+}
+
+void ArithStaticLearner::iteMinMax(TNode n, NodeBuilder& learned)
+{
+ Assert(n.getKind() == kind::ITE);
+ Assert(n[0].getKind() != EQUAL);
+ Assert(isRelationOperator(n[0].getKind()));
+
+ TNode c = n[0];
+ Kind k = oldSimplifiedKind(c);
+ TNode t = n[1];
+ TNode e = n[2];
+ TNode cleft = (c.getKind() == NOT) ? c[0][0] : c[0];
+ TNode cright = (c.getKind() == NOT) ? c[0][1] : c[1];
+
+ if((t == cright) && (e == cleft)){
+ TNode tmp = t;
+ t = e;
+ e = tmp;
+ k = reverseRelationKind(k);
+ }
+ //(ite (< x y) x y)
+ //(ite (x < y) x y)
+ //(ite (x - y < 0) x y)
+ // ----------------
+ // (ite (x - y < -c) )
+
+ if(t == cleft && e == cright){
+ // t == cleft && e == cright
+ Assert(t == cleft);
+ Assert(e == cright);
+ switch(k){
+ case LT: // (ite (< x y) x y)
+ case LEQ: { // (ite (<= x y) x y)
+ Node nLeqX = NodeBuilder(LEQ) << n << t;
+ Node nLeqY = NodeBuilder(LEQ) << n << e;
+ Trace("arith::static") << n << "is a min =>" << nLeqX << nLeqY << endl;
+ learned << nLeqX << nLeqY;
+ ++(d_statistics.d_iteMinMaxApplications);
+ break;
+ }
+ case GT: // (ite (> x y) x y)
+ case GEQ: { // (ite (>= x y) x y)
+ Node nGeqX = NodeBuilder(GEQ) << n << t;
+ Node nGeqY = NodeBuilder(GEQ) << n << e;
+ Trace("arith::static") << n << "is a max =>" << nGeqX << nGeqY << endl;
+ learned << nGeqX << nGeqY;
+ ++(d_statistics.d_iteMinMaxApplications);
+ break;
+ }
+ default: Unreachable();
+ }
+ }
+}
+
+void ArithStaticLearner::iteConstant(TNode n, NodeBuilder& learned)
+{
+ Assert(n.getKind() == ITE);
+
+ Trace("arith::static") << "iteConstant(" << n << ")" << endl;
+
+ if (d_minMap.find(n[1]) != d_minMap.end() && d_minMap.find(n[2]) != d_minMap.end()) {
+ const DeltaRational& first = d_minMap[n[1]];
+ const DeltaRational& second = d_minMap[n[2]];
+ DeltaRational min = std::min(first, second);
+ CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n);
+ if (minFind == d_minMap.end() || (*minFind).second < min) {
+ d_minMap.insert(n, min);
+ NodeManager* nm = NodeManager::currentNM();
+ Node nGeqMin = nm->mkNode(
+ min.getInfinitesimalPart() == 0 ? kind::GEQ : kind::GT,
+ n,
+ nm->mkConstRealOrInt(n.getType(), min.getNoninfinitesimalPart()));
+ learned << nGeqMin;
+ Trace("arith::static") << n << " iteConstant" << nGeqMin << endl;
+ ++(d_statistics.d_iteConstantApplications);
+ }
+ }
+
+ if (d_maxMap.find(n[1]) != d_maxMap.end() && d_maxMap.find(n[2]) != d_maxMap.end()) {
+ const DeltaRational& first = d_maxMap[n[1]];
+ const DeltaRational& second = d_maxMap[n[2]];
+ DeltaRational max = std::max(first, second);
+ CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n);
+ if (maxFind == d_maxMap.end() || (*maxFind).second > max) {
+ d_maxMap.insert(n, max);
+ NodeManager* nm = NodeManager::currentNM();
+ Node nLeqMax = nm->mkNode(
+ max.getInfinitesimalPart() == 0 ? kind::LEQ : kind::LT,
+ n,
+ nm->mkConstRealOrInt(n.getType(), max.getNoninfinitesimalPart()));
+ learned << nLeqMax;
+ Trace("arith::static") << n << " iteConstant" << nLeqMax << endl;
+ ++(d_statistics.d_iteConstantApplications);
+ }
+ }
+}
+
+std::set<Node> listToSet(TNode l){
+ std::set<Node> ret;
+ while(l.getKind() == OR){
+ Assert(l.getNumChildren() == 2);
+ ret.insert(l[0]);
+ l = l[1];
+ }
+ return ret;
+}
+
+void ArithStaticLearner::addBound(TNode n) {
+
+ CDNodeToMinMaxMap::const_iterator minFind = d_minMap.find(n[0]);
+ CDNodeToMinMaxMap::const_iterator maxFind = d_maxMap.find(n[0]);
+
+ Rational constant = n[1].getConst<Rational>();
+ DeltaRational bound = constant;
+
+ switch(Kind k = n.getKind()) {
+ case kind::LT: bound = DeltaRational(constant, -1); CVC5_FALLTHROUGH;
+ case kind::LEQ:
+ if (maxFind == d_maxMap.end() || (*maxFind).second > bound)
+ {
+ d_maxMap.insert(n[0], bound);
+ Trace("arith::static") << "adding bound " << n << endl;
+ }
+ break;
+ case kind::GT: bound = DeltaRational(constant, 1); CVC5_FALLTHROUGH;
+ case kind::GEQ:
+ if (minFind == d_minMap.end() || (*minFind).second < bound)
+ {
+ d_minMap.insert(n[0], bound);
+ Trace("arith::static") << "adding bound " << n << endl;
+ }
+ break;
+ default: Unhandled() << k; break;
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andres Noetzli, Dejan Jovanovic
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
+#define CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H
+
+#include "context/cdhashmap.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/delta_rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class ArithStaticLearner {
+private:
+
+ /**
+ * Map from a node to it's minimum and maximum.
+ */
+ typedef context::CDHashMap<Node, DeltaRational> CDNodeToMinMaxMap;
+ CDNodeToMinMaxMap d_minMap;
+ CDNodeToMinMaxMap d_maxMap;
+
+public:
+ ArithStaticLearner(context::Context* userContext);
+ ~ArithStaticLearner();
+ void staticLearning(TNode n, NodeBuilder& learned);
+
+ void addBound(TNode n);
+
+private:
+ void process(TNode n, NodeBuilder& learned, const TNodeSet& defTrue);
+
+ void iteMinMax(TNode n, NodeBuilder& learned);
+ void iteConstant(TNode n, NodeBuilder& learned);
+
+ /**
+ * These fields are designed to be accessible to ArithStaticLearner methods.
+ */
+ class Statistics
+ {
+ public:
+ IntStat d_iteMinMaxApplications;
+ IntStat d_iteConstantApplications;
+
+ Statistics();
+ };
+
+ Statistics d_statistics;
+
+};/* class ArithStaticLearner */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__ARITH_STATIC_LEARNER_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/arithvar.h"
+#include <limits>
+#include <set>
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+const ArithVar ARITHVAR_SENTINEL = std::numeric_limits<ArithVar>::max();
+
+bool debugIsASet(const std::vector<ArithVar>& variables){
+ std::set<ArithVar> asSet(variables.begin(), variables.end());
+ return asSet.size() == variables.size();
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Defines ArithVar which is the internal representation of variables in
+ * arithmetic
+ *
+ * This defines ArithVar which is the internal representation of variables in
+ * arithmetic. This is a typedef from Index to ArithVar.
+ * This file also provides utilities for ArithVars.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <vector>
+
+#include "util/index.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+typedef Index ArithVar;
+extern const ArithVar ARITHVAR_SENTINEL;
+
+typedef std::vector<ArithVar> ArithVarVec;
+typedef std::pair<ArithVar, Rational> ArithRatPair;
+typedef std::vector< ArithRatPair > ArithRatPairVec;
+
+extern bool debugIsASet(const ArithVarVec& variables);
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
+#define CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H
+
+#include "theory/arith/linear/arithvar.h"
+#include "context/context.h"
+#include "context/cdlist.h"
+#include "context/cdhashmap.h"
+#include "context/cdo.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+//Maps from Nodes -> ArithVars, and vice versa
+typedef std::unordered_map<Node, ArithVar> NodeToArithVarMap;
+typedef DenseMap<Node> ArithVarToNodeMap;
+
+class ArithVarNodeMap {
+private:
+ /**
+ * Bidirectional map between Nodes and ArithVars.
+ */
+ NodeToArithVarMap d_nodeToArithVarMap;
+ ArithVarToNodeMap d_arithVarToNodeMap;
+
+public:
+
+ typedef ArithVarToNodeMap::const_iterator var_iterator;
+
+ ArithVarNodeMap() {}
+
+ inline bool hasArithVar(TNode x) const {
+ return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end();
+ }
+
+ inline bool hasNode(ArithVar a) const {
+ return d_arithVarToNodeMap.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;
+ }
+
+ inline Node asNode(ArithVar a) const{
+ Assert(hasNode(a));
+ return d_arithVarToNodeMap[a];
+ }
+
+ inline void setArithVar(TNode x, ArithVar a){
+ Assert(!hasArithVar(x));
+ Assert(!d_arithVarToNodeMap.isKey(a));
+ d_arithVarToNodeMap.set(a, x);
+ d_nodeToArithVarMap[x] = a;
+ }
+
+ inline void remove(ArithVar x){
+ Assert(hasNode(x));
+ Node node = asNode(x);
+
+ d_nodeToArithVarMap.erase(d_nodeToArithVarMap.find(node));
+ d_arithVarToNodeMap.remove(x);
+ }
+
+ var_iterator var_begin() const {
+ return d_arithVarToNodeMap.begin();
+ }
+ var_iterator var_end() const {
+ return d_arithVarToNodeMap.end();
+ }
+
+};/* class ArithVarNodeMap */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__ARITHVAR_NODE_MAP_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/attempt_solution_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/tableau.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+AttemptSolutionSDP::AttemptSolutionSDP(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+ d_statistics()
+{ }
+
+AttemptSolutionSDP::Statistics::Statistics()
+ : d_searchTime(smtStatisticsRegistry().registerTimer(
+ "theory::arith::attempt::searchTime")),
+ d_queueTime(smtStatisticsRegistry().registerTimer(
+ "theory::arith::attempt::queueTime")),
+ d_conflicts(smtStatisticsRegistry().registerInt(
+ "theory::arith::attempt::conflicts"))
+{
+}
+
+bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{
+ return nv[v] == d_variables.getAssignment(v);
+}
+
+Result::Status AttemptSolutionSDP::attempt(
+ const ApproximateSimplex::Solution& sol)
+{
+ const DenseSet& newBasis = sol.newBasis;
+ const DenseMap<DeltaRational>& newValues = sol.newValues;
+
+ DenseSet needsToBeAdded;
+ for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
+ ArithVar b = *i;
+ if(!d_tableau.isBasic(b)){
+ needsToBeAdded.add(b);
+ }
+ }
+ DenseMap<DeltaRational>::const_iterator nvi = newValues.begin(), nvi_end = newValues.end();
+ for(; nvi != nvi_end; ++nvi){
+ ArithVar currentlyNb = *nvi;
+ if(!d_tableau.isBasic(currentlyNb)){
+ if(!matchesNewValue(newValues, currentlyNb)){
+ const DeltaRational& newValue = newValues[currentlyNb];
+ Trace("arith::updateMany")
+ << "updateMany:" << currentlyNb << " "
+ << d_variables.getAssignment(currentlyNb) << " to "<< newValue << endl;
+ d_linEq.update(currentlyNb, newValue);
+ Assert(d_variables.assignmentIsConsistent(currentlyNb));
+ }
+ }
+ }
+ d_errorSet.reduceToSignals();
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+
+ if(processSignals()){
+ Trace("arith::findModel") << "attemptSolution() early conflict" << endl;
+ d_conflictVariables.purge();
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Trace("arith::findModel") << "attemptSolution() fixed itself" << endl;
+ return Result::SAT;
+ }
+
+ while(!needsToBeAdded.empty() && !d_errorSet.errorEmpty()){
+ ArithVar toRemove = ARITHVAR_SENTINEL;
+ ArithVar toAdd = ARITHVAR_SENTINEL;
+ DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
+ for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
+ ArithVar v = *i;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(v);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == v);
+ ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ if(!newBasis.isMember(b)){
+ toAdd = v;
+
+ bool favorBOverToRemove =
+ (toRemove == ARITHVAR_SENTINEL) ||
+ (matchesNewValue(newValues, toRemove) && !matchesNewValue(newValues, b)) ||
+ (d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b));
+
+ if(favorBOverToRemove){
+ toRemove = b;
+ }
+ }
+ }
+ }
+ Assert(toRemove != ARITHVAR_SENTINEL);
+ Assert(toAdd != ARITHVAR_SENTINEL);
+
+ Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+
+ d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]);
+
+ Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+ needsToBeAdded.remove(toAdd);
+
+ bool conflict = processSignals();
+ if(conflict){
+ d_errorSet.reduceToSignals();
+ d_conflictVariables.purge();
+
+ return Result::UNSAT;
+ }
+ }
+ Assert(d_conflictVariables.empty());
+
+ if(d_errorSet.errorEmpty()){
+ return Result::SAT;
+ }else{
+ d_errorSet.reduceToSignals();
+ return Result::UNKNOWN;
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ * by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ * These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/linear/simplex.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class AttemptSolutionSDP : public SimplexDecisionProcedure {
+public:
+ AttemptSolutionSDP(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc);
+
+ Result::Status attempt(const ApproximateSimplex::Solution& sol);
+
+ Result::Status findModel(bool exactResult) override { Unreachable(); }
+
+private:
+ bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const;
+
+ bool processSignals()
+ {
+ TimerStat& timer = d_statistics.d_queueTime;
+ IntStat& conflictStat = d_statistics.d_conflicts;
+ return standardProcessSignals(timer, conflictStat);
+ }
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ TimerStat d_searchTime;
+ TimerStat d_queueTime;
+ IntStat d_conflicts;
+
+ Statistics();
+ } d_statistics;
+};/* class AttemptSolutionSDP */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Clark Barrett
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+#pragma once
+
+#include "base/check.h"
+#include "theory/arith/linear/arithvar.h"
+#include "util/dense_map.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class BoundCounts {
+private:
+ uint32_t d_lowerBoundCount;
+ uint32_t d_upperBoundCount;
+
+public:
+ BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {}
+ BoundCounts(uint32_t lbs, uint32_t ubs)
+ : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {}
+
+ bool operator==(BoundCounts bc) const {
+ return d_lowerBoundCount == bc.d_lowerBoundCount
+ && d_upperBoundCount == bc.d_upperBoundCount;
+ }
+ bool operator!=(BoundCounts bc) const {
+ return d_lowerBoundCount != bc.d_lowerBoundCount
+ || d_upperBoundCount != bc.d_upperBoundCount;
+ }
+ /** This is not a total order! */
+ bool operator>=(BoundCounts bc) const {
+ return d_lowerBoundCount >= bc.d_lowerBoundCount &&
+ d_upperBoundCount >= bc.d_upperBoundCount;
+ }
+
+ inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; }
+ inline uint32_t lowerBoundCount() const{
+ return d_lowerBoundCount;
+ }
+ inline uint32_t upperBoundCount() const{
+ return d_upperBoundCount;
+ }
+
+ inline BoundCounts operator+(BoundCounts bc) const{
+ return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount,
+ d_upperBoundCount + bc.d_upperBoundCount);
+ }
+
+ inline BoundCounts operator-(BoundCounts bc) const {
+ Assert(*this >= bc);
+ return BoundCounts(d_lowerBoundCount - bc.d_lowerBoundCount,
+ d_upperBoundCount - bc.d_upperBoundCount);
+ }
+
+
+ inline BoundCounts& operator+=(BoundCounts bc) {
+ d_upperBoundCount += bc.d_upperBoundCount;
+ d_lowerBoundCount += bc.d_lowerBoundCount;
+ return *this;
+ }
+
+ inline BoundCounts& operator-=(BoundCounts bc) {
+ Assert(d_lowerBoundCount >= bc.d_lowerBoundCount);
+ Assert(d_upperBoundCount >= bc.d_upperBoundCount);
+ d_upperBoundCount -= bc.d_upperBoundCount;
+ d_lowerBoundCount -= bc.d_lowerBoundCount;
+
+ return *this;
+ }
+
+ /** Based on the sign coefficient a variable is multiplied by,
+ * the effects the bound counts either has no effect (sgn == 0),
+ * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
+ */
+ inline BoundCounts multiplyBySgn(int sgn) const{
+ if(sgn > 0){
+ return *this;
+ }else if(sgn == 0){
+ return BoundCounts(0,0);
+ }else{
+ return BoundCounts(d_upperBoundCount, d_lowerBoundCount);
+ }
+ }
+
+ inline void addInChange(int sgn, BoundCounts before, BoundCounts after){
+ if(before == after){
+ return;
+ }else if(sgn < 0){
+ Assert(d_upperBoundCount >= before.d_lowerBoundCount);
+ Assert(d_lowerBoundCount >= before.d_upperBoundCount);
+ d_upperBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
+ d_lowerBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
+ }else if(sgn > 0){
+ Assert(d_upperBoundCount >= before.d_upperBoundCount);
+ Assert(d_lowerBoundCount >= before.d_lowerBoundCount);
+ d_upperBoundCount += after.d_upperBoundCount - before.d_upperBoundCount;
+ d_lowerBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount;
+ }
+ }
+
+ inline void addInSgn(BoundCounts bc, int before, int after){
+ Assert(before != after);
+ Assert(!bc.isZero());
+
+ if(before < 0){
+ d_upperBoundCount -= bc.d_lowerBoundCount;
+ d_lowerBoundCount -= bc.d_upperBoundCount;
+ }else if(before > 0){
+ d_upperBoundCount -= bc.d_upperBoundCount;
+ d_lowerBoundCount -= bc.d_lowerBoundCount;
+ }
+ if(after < 0){
+ d_upperBoundCount += bc.d_lowerBoundCount;
+ d_lowerBoundCount += bc.d_upperBoundCount;
+ }else if(after > 0){
+ d_upperBoundCount += bc.d_upperBoundCount;
+ d_lowerBoundCount += bc.d_lowerBoundCount;
+ }
+ }
+};
+
+class BoundsInfo {
+private:
+
+ /**
+ * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
+ *
+ * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
+ * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
+ */
+ BoundCounts d_atBounds;
+
+ /** This is for counting how many upper and lower bounds a row has. */
+ BoundCounts d_hasBounds;
+
+public:
+ BoundsInfo() : d_atBounds(), d_hasBounds() {}
+ BoundsInfo(BoundCounts atBounds, BoundCounts hasBounds)
+ : d_atBounds(atBounds), d_hasBounds(hasBounds) {}
+
+ BoundCounts atBounds() const { return d_atBounds; }
+ BoundCounts hasBounds() const { return d_hasBounds; }
+
+ /** This corresponds to adding in another variable to the row. */
+ inline BoundsInfo operator+(const BoundsInfo& bc) const{
+ return BoundsInfo(d_atBounds + bc.d_atBounds,
+ d_hasBounds + bc.d_hasBounds);
+ }
+ /** This corresponds to removing a variable from the row. */
+ inline BoundsInfo operator-(const BoundsInfo& bc) const {
+ Assert(*this >= bc);
+ return BoundsInfo(d_atBounds - bc.d_atBounds,
+ d_hasBounds - bc.d_hasBounds);
+ }
+
+ inline BoundsInfo& operator+=(const BoundsInfo& bc) {
+ d_atBounds += bc.d_atBounds;
+ d_hasBounds += bc.d_hasBounds;
+ return (*this);
+ }
+
+ /** Based on the sign coefficient a variable is multiplied by,
+ * the effects the bound counts either has no effect (sgn == 0),
+ * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0).
+ */
+ inline BoundsInfo multiplyBySgn(int sgn) const{
+ return BoundsInfo(d_atBounds.multiplyBySgn(sgn), d_hasBounds.multiplyBySgn(sgn));
+ }
+
+ bool operator==(const BoundsInfo& other) const{
+ return d_atBounds == other.d_atBounds && d_hasBounds == other.d_hasBounds;
+ }
+ bool operator!=(const BoundsInfo& other) const{
+ return !(*this == other);
+ }
+ /** This is not a total order! */
+ bool operator>=(const BoundsInfo& other) const{
+ return d_atBounds >= other.d_atBounds && d_hasBounds >= other.d_hasBounds;
+ }
+ void addInChange(int sgn, const BoundsInfo& before, const BoundsInfo& after){
+ addInAtBoundChange(sgn, before.d_atBounds, after.d_atBounds);
+ addInHasBoundChange(sgn, before.d_hasBounds, after.d_hasBounds);
+ }
+ void addInAtBoundChange(int sgn, BoundCounts before, BoundCounts after){
+ d_atBounds.addInChange(sgn, before, after);
+ }
+ void addInHasBoundChange(int sgn, BoundCounts before, BoundCounts after){
+ d_hasBounds.addInChange(sgn, before, after);
+ }
+
+ inline void addInSgn(const BoundsInfo& bc, int before, int after){
+ if(!bc.d_atBounds.isZero()){ d_atBounds.addInSgn(bc.d_atBounds, before, after);}
+ if(!bc.d_hasBounds.isZero()){ d_hasBounds.addInSgn(bc.d_hasBounds, before, after);}
+ }
+};
+
+/** This is intended to map each row to its relevant bound information. */
+typedef DenseMap<BoundsInfo> BoundInfoMap;
+
+inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){
+ os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]";
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){
+ os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]";
+ return os;
+}
+class BoundUpdateCallback {
+public:
+ virtual ~BoundUpdateCallback() {}
+ virtual void operator()(ArithVar v, const BoundsInfo& up) = 0;
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/callbacks.h"
+
+#include "expr/skolem_manager.h"
+#include "proof/proof_node.h"
+#include "theory/arith/linear/theory_arith_private.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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)){
+ d_arith.setupAtom(atom);
+ }
+}
+
+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(){
+ NodeManager* nm = NodeManager::currentNM();
+ SkolemManager* sm = nm->getSkolemManager();
+ Node skolem = sm->mkDummySkolem("tmpVar", nm->realType());
+ 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);
+}
+
+RaiseConflict::RaiseConflict(TheoryArithPrivate& ta)
+ : d_ta(ta)
+{}
+
+void RaiseConflict::raiseConflict(ConstraintCP c, InferenceId id) const{
+ Assert(c->inConflict());
+ d_ta.raiseConflict(c, id);
+}
+
+FarkasConflictBuilder::FarkasConflictBuilder(bool produceProofs)
+ : d_farkas(),
+ d_constraints(),
+ d_consequent(NullConstraint),
+ d_consequentSet(false),
+ d_produceProofs(produceProofs)
+{
+ reset();
+}
+
+bool FarkasConflictBuilder::underConstruction() const{
+ return d_consequent != NullConstraint;
+}
+
+bool FarkasConflictBuilder::consequentIsSet() const{
+ return d_consequentSet;
+}
+
+void FarkasConflictBuilder::reset(){
+ d_consequent = NullConstraint;
+ d_constraints.clear();
+ d_consequentSet = false;
+ if (d_produceProofs)
+ {
+ d_farkas.clear();
+ }
+ Assert(!underConstruction());
+}
+
+/* Adds a constraint to the constraint under construction. */
+void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){
+ Assert(
+ !d_produceProofs
+ || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
+ || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
+ Assert(d_produceProofs || d_farkas.empty());
+ Assert(c->isTrue());
+
+ if(d_consequent == NullConstraint){
+ d_consequent = c;
+ } else {
+ d_constraints.push_back(c);
+ }
+ if (d_produceProofs)
+ {
+ d_farkas.push_back(fc);
+ }
+ Assert(!d_produceProofs || d_constraints.size() + 1 == d_farkas.size());
+ Assert(d_produceProofs || d_farkas.empty());
+}
+
+void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult){
+ Assert(!mult.isZero());
+ if (d_produceProofs && !mult.isOne())
+ {
+ Rational prod = fc * mult;
+ addConstraint(c, prod);
+ }
+ else
+ {
+ addConstraint(c, fc);
+ }
+}
+
+void FarkasConflictBuilder::makeLastConsequent(){
+ Assert(!d_consequentSet);
+ Assert(underConstruction());
+
+ if(d_constraints.empty()){
+ // no-op
+ d_consequentSet = true;
+ } else {
+ Assert(d_consequent != NullConstraint);
+ ConstraintCP last = d_constraints.back();
+ d_constraints.back() = d_consequent;
+ d_consequent = last;
+ if (d_produceProofs)
+ {
+ std::swap(d_farkas.front(), d_farkas.back());
+ }
+ d_consequentSet = true;
+ }
+
+ Assert(!d_consequent->negationHasProof());
+ Assert(d_consequentSet);
+}
+
+/* Turns the vector under construction into a conflict */
+ConstraintCP FarkasConflictBuilder::commitConflict(){
+ Assert(underConstruction());
+ Assert(!d_constraints.empty());
+ Assert(
+ !d_produceProofs
+ || (!underConstruction() && d_constraints.empty() && d_farkas.empty())
+ || (underConstruction() && d_constraints.size() + 1 == d_farkas.size()));
+ Assert(d_produceProofs || d_farkas.empty());
+ Assert(d_consequentSet);
+
+ ConstraintP not_c = d_consequent->getNegation();
+ RationalVectorCP coeffs = d_produceProofs ? &d_farkas : nullptr;
+ not_c->impliedByFarkas(d_constraints, coeffs, true );
+
+ reset();
+ Assert(!underConstruction());
+ Assert(not_c->inConflict());
+ Assert(!d_consequentSet);
+ return not_c;
+}
+
+RaiseEqualityEngineConflict::RaiseEqualityEngineConflict(TheoryArithPrivate& ta)
+ : d_ta(ta)
+{}
+
+/* If you are not an equality engine, don't use this! */
+void RaiseEqualityEngineConflict::raiseEEConflict(
+ Node n, std::shared_ptr<ProofNode> pf) const
+{
+ d_ta.raiseBlackBoxConflict(n, pf);
+}
+
+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();
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Mathias Preiner, Clark Barrett
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#pragma once
+
+#include "expr/node.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/inference_id.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+
+class ProofNode;
+
+namespace theory {
+namespace arith::linear {
+
+class TheoryArithPrivate;
+
+/**
+ * ArithVarCallBack provides a mechanism for agreeing on callbacks while
+ * breaking mutual recursion inclusion order problems.
+ */
+class ArithVarCallBack {
+public:
+ virtual ~ArithVarCallBack() {}
+ virtual void operator()(ArithVar x) = 0;
+};
+
+/**
+ * Requests arithmetic variables for internal use,
+ * and releases arithmetic variables that are no longer being used.
+ */
+class ArithVarMalloc {
+public:
+ virtual ~ArithVarMalloc() {}
+ virtual ArithVar request() = 0;
+ virtual void release(ArithVar v) = 0;
+};
+
+class TNodeCallBack {
+public:
+ virtual ~TNodeCallBack() {}
+ virtual void operator()(TNode n) = 0;
+};
+
+class NodeCallBack {
+public:
+ virtual ~NodeCallBack() {}
+ virtual void operator()(Node n) = 0;
+};
+
+class RationalCallBack {
+public:
+ virtual ~RationalCallBack() {}
+ virtual Rational operator()() const = 0;
+};
+
+class SetupLiteralCallBack : public TNodeCallBack {
+private:
+ TheoryArithPrivate& d_arith;
+public:
+ SetupLiteralCallBack(TheoryArithPrivate& ta);
+ void operator()(TNode lit) override;
+};
+
+class DeltaComputeCallback : public RationalCallBack {
+private:
+ const TheoryArithPrivate& d_ta;
+public:
+ DeltaComputeCallback(const TheoryArithPrivate& ta);
+ Rational operator()() const override;
+};
+
+class BasicVarModelUpdateCallBack : public ArithVarCallBack{
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ BasicVarModelUpdateCallBack(TheoryArithPrivate& ta);
+ void operator()(ArithVar x) override;
+};
+
+class TempVarMalloc : public ArithVarMalloc {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ TempVarMalloc(TheoryArithPrivate& ta);
+ ArithVar request() override;
+ void release(ArithVar v) override;
+};
+
+class RaiseConflict {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ RaiseConflict(TheoryArithPrivate& ta);
+
+ /** Calls d_ta.raiseConflict(c) */
+ void raiseConflict(ConstraintCP c, InferenceId id) const;
+};
+
+class FarkasConflictBuilder {
+private:
+ RationalVector d_farkas;
+ ConstraintCPVec d_constraints;
+ ConstraintCP d_consequent;
+ bool d_consequentSet;
+ bool d_produceProofs;
+
+ public:
+
+ /**
+ * Constructs a new FarkasConflictBuilder.
+ */
+ FarkasConflictBuilder(bool produceProofs);
+
+ /**
+ * Adds an antecedent constraint to the conflict under construction
+ * with the farkas coefficient fc * mult.
+ *
+ * The value mult is either 1 or -1.
+ */
+ void addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult);
+
+ /**
+ * Adds an antecedent constraint to the conflict under construction
+ * with the farkas coefficient fc.
+ */
+ void addConstraint(ConstraintCP c, const Rational& fc);
+
+ /**
+ * Makes the last constraint added the consequent.
+ * Can be done exactly once per reset().
+ */
+ void makeLastConsequent();
+
+ /**
+ * Turns the antecendents into a proof of the negation of one of the
+ * antecedents.
+ *
+ * The buffer is no longer underConstruction afterwards.
+ *
+ * precondition:
+ * - At least two constraints have been asserted.
+ * - makeLastConsequent() has been called.
+ *
+ * postcondition: The returned constraint is in conflict.
+ */
+ ConstraintCP commitConflict();
+
+ /** Returns true if a conflict has been pushed back since the last reset. */
+ bool underConstruction() const;
+
+ /** Returns true if the consequent has been set since the last reset. */
+ bool consequentIsSet() const;
+
+ /** Resets the state of the buffer. */
+ void reset();
+};
+
+
+class RaiseEqualityEngineConflict {
+private:
+ TheoryArithPrivate& d_ta;
+
+public:
+ RaiseEqualityEngineConflict(TheoryArithPrivate& ta);
+
+ /* If you are not an equality engine, don't use this!
+ *
+ * The proof should prove that `n` is a conflict.
+ * */
+ void raiseEEConflict(Node n, std::shared_ptr<ProofNode> pf) const;
+};
+
+class BoundCountingLookup {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ BoundCountingLookup(TheoryArithPrivate& ta);
+ const BoundsInfo& boundsInfo(ArithVar basic) const;
+ BoundCounts atBounds(ArithVar basic) const;
+ BoundCounts hasBounds(ArithVar basic) const;
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Alex Ozdemir, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/congruence_manager.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "proof/proof_node.h"
+#include "proof/proof_node_manager.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/ee_setup_info.h"
+#include "theory/rewriter.h"
+#include "theory/uf/equality_engine.h"
+#include "theory/uf/proof_equality_engine.h"
+
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithCongruenceManager::ArithCongruenceManager(
+ Env& env,
+ ConstraintDatabase& cd,
+ SetupLiteralCallBack setup,
+ const ArithVariables& avars,
+ RaiseEqualityEngineConflict raiseConflict)
+ : EnvObj(env),
+ d_inConflict(context()),
+ d_raiseConflict(raiseConflict),
+ d_notify(*this),
+ d_keepAlive(context()),
+ d_propagatations(context()),
+ d_explanationMap(context()),
+ d_constraintDatabase(cd),
+ d_setupLiteral(setup),
+ d_avariables(avars),
+ d_ee(nullptr),
+ d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+ : nullptr),
+ // Construct d_pfGenEe with the SAT context, since its proof include
+ // unclosed assumptions of theory literals.
+ d_pfGenEe(new EagerProofGenerator(
+ d_pnm, context(), "ArithCongruenceManager::pfGenEe")),
+ // Construct d_pfGenEe with the USER context, since its proofs are closed.
+ d_pfGenExplain(new EagerProofGenerator(
+ d_pnm, userContext(), "ArithCongruenceManager::pfGenExplain")),
+ d_pfee(nullptr)
+{
+}
+
+ArithCongruenceManager::~ArithCongruenceManager() {}
+
+bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi)
+{
+ Assert(!options().arith.arithEqSolver);
+ esi.d_notify = &d_notify;
+ esi.d_name = "arithCong::ee";
+ return true;
+}
+
+void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee)
+{
+ if (options().arith.arithEqSolver)
+ {
+ // use our own copy
+ d_allocEe = std::make_unique<eq::EqualityEngine>(
+ d_env, context(), d_notify, "arithCong::ee", true);
+ d_ee = d_allocEe.get();
+ if (d_pnm != nullptr)
+ {
+ // allocate an internal proof equality engine
+ d_allocPfee = std::make_unique<eq::ProofEqEngine>(d_env, *d_ee);
+ d_ee->setProofEqualityEngine(d_allocPfee.get());
+ }
+ }
+ else
+ {
+ Assert(ee != nullptr);
+ // otherwise, we use the official one
+ d_ee = ee;
+ }
+ // set the congruence kinds on the separate equality engine
+ d_ee->addFunctionKind(kind::NONLINEAR_MULT);
+ d_ee->addFunctionKind(kind::EXPONENTIAL);
+ d_ee->addFunctionKind(kind::SINE);
+ d_ee->addFunctionKind(kind::IAND);
+ d_ee->addFunctionKind(kind::POW2);
+ // the proof equality engine is the one from the equality engine
+ d_pfee = d_ee->getProofEqualityEngine();
+ // have proof equality engine only if proofs are enabled
+ Assert(isProofEnabled() == (d_pfee != nullptr));
+}
+
+ArithCongruenceManager::Statistics::Statistics()
+ : d_watchedVariables(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::watchedVariables")),
+ d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::watchedVariableIsZero")),
+ d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::watchedVariableIsNotZero")),
+ d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::equalsConstantCalls")),
+ d_propagations(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::propagations")),
+ d_propagateConstraints(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::propagateConstraints")),
+ d_conflicts(smtStatisticsRegistry().registerInt(
+ "theory::arith::congruence::conflicts"))
+{
+}
+
+ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm)
+ : d_acm(acm)
+{}
+
+bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
+ TNode predicate, bool value)
+{
+ Assert(predicate.getKind() == kind::EQUAL);
+ Trace("arith::congruences")
+ << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", "
+ << (value ? "true" : "false") << ")" << std::endl;
+ if (value) {
+ return d_acm.propagate(predicate);
+ }
+ return d_acm.propagate(predicate.notNode());
+}
+
+bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
+ Trace("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) {
+ Trace("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl;
+ d_acm.propagate(t1.eqNode(t2));
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) {
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1,
+ TNode t2)
+{
+}
+void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
+}
+
+void ArithCongruenceManager::raiseConflict(Node conflict,
+ std::shared_ptr<ProofNode> pf)
+{
+ Assert(!inConflict());
+ Trace("arith::conflict") << "difference manager conflict " << conflict << std::endl;
+ d_inConflict.raise();
+ d_raiseConflict.raiseEEConflict(conflict, pf);
+}
+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();
+}
+
+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());
+ Assert(lb->getValue().sgn() == 0);
+ Assert(ub->getValue().sgn() == 0);
+
+ ++(d_statistics.d_watchedVariableIsZero);
+
+ ArithVar s = lb->getVariable();
+ TNode eq = d_watchedEqualities[s];
+ ConstraintCP eqC = d_constraintDatabase.getConstraint(
+ s, ConstraintType::Equality, lb->getValue());
+ NodeBuilder reasonBuilder(Kind::AND);
+ auto pfLb = lb->externalExplainByAssertions(reasonBuilder);
+ auto pfUb = ub->externalExplainByAssertions(reasonBuilder);
+ Node reason = mkAndFromBuilder(reasonBuilder);
+ std::shared_ptr<ProofNode> pf{};
+ if (isProofEnabled())
+ {
+ pf = d_pnm->mkNode(
+ PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()});
+ pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq});
+ }
+
+ d_keepAlive.push_back(reason);
+ Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy"
+ << std::endl;
+ Trace("arith-ee") << " based on " << lb << std::endl;
+ Trace("arith-ee") << " based on " << ub << std::endl;
+ assertionToEqualityEngine(true, s, reason, pf);
+}
+
+void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){
+ Trace("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl;
+
+ Assert(eq->isEquality());
+ Assert(eq->getValue().sgn() == 0);
+
+ ++(d_statistics.d_watchedVariableIsZero);
+
+ ArithVar s = eq->getVariable();
+
+ //Explain for conflict is correct as these proofs are generated
+ //and stored eagerly
+ //These will be safe for propagation later as well
+ NodeBuilder nb(Kind::AND);
+ // An open proof of eq from literals now in reason.
+ if (TraceIsOn("arith::cong"))
+ {
+ eq->printProofTree(Trace("arith::cong"));
+ }
+ auto pf = eq->externalExplainByAssertions(nb);
+ if (isProofEnabled())
+ {
+ pf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]});
+ }
+ Node reason = mkAndFromBuilder(nb);
+
+ d_keepAlive.push_back(reason);
+ assertionToEqualityEngine(true, s, reason, pf);
+}
+
+void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){
+ Trace("arith::cong::notzero")
+ << "Cong::watchedVariableCannotBeZero " << *c << std::endl;
+ ++(d_statistics.d_watchedVariableIsNotZero);
+
+ ArithVar s = c->getVariable();
+ Node disEq = d_watchedEqualities[s].negate();
+
+ //Explain for conflict is correct as these proofs are generated and stored eagerly
+ //These will be safe for propagation later as well
+ NodeBuilder nb(Kind::AND);
+ // An open proof of eq from literals now in reason.
+ auto pf = c->externalExplainByAssertions(nb);
+ if (TraceIsOn("arith::cong::notzero"))
+ {
+ Trace("arith::cong::notzero") << " original proof ";
+ pf->printDebug(Trace("arith::cong::notzero"));
+ Trace("arith::cong::notzero") << std::endl;
+ }
+ Node reason = mkAndFromBuilder(nb);
+ if (isProofEnabled())
+ {
+ if (c->getType() == ConstraintType::Disequality)
+ {
+ Assert(c->getLiteral() == d_watchedEqualities[s].negate());
+ // We have to prove equivalence to the watched disequality.
+ pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq});
+ }
+ else
+ {
+ Trace("arith::cong::notzero")
+ << " proof modification needed" << std::endl;
+
+ // Four cases:
+ // c has form x_i = d, d > 0 => multiply c by -1 in Farkas proof
+ // c has form x_i = d, d > 0 => multiply c by 1 in Farkas proof
+ // c has form x_i <= d, d < 0 => multiply c by 1 in Farkas proof
+ // c has form x_i >= d, d > 0 => multiply c by -1 in Farkas proof
+ const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound
+ || (c->getType() == ConstraintType::Equality
+ && c->getValue().sgn() > 0);
+ const int cSign = scaleCNegatively ? -1 : 1;
+ TNode isZero = d_watchedEqualities[s];
+ TypeNode type = isZero[0].getType();
+ const auto isZeroPf = d_pnm->mkAssume(isZero);
+ const auto nm = NodeManager::currentNM();
+ const auto sumPf =
+ d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+ {isZeroPf, pf},
+ // Trick for getting correct, opposing signs.
+ {nm->mkConstRealOrInt(type, Rational(-1 * cSign)),
+ nm->mkConstRealOrInt(type, Rational(cSign))});
+ const auto botPf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+ std::vector<Node> assumption = {isZero};
+ pf = d_pnm->mkScope(botPf, assumption, false);
+ Trace("arith::cong::notzero") << " new proof ";
+ pf->printDebug(Trace("arith::cong::notzero"));
+ Trace("arith::cong::notzero") << std::endl;
+ }
+ Assert(pf->getResult() == disEq);
+ }
+ d_keepAlive.push_back(reason);
+ assertionToEqualityEngine(false, s, reason, pf);
+}
+
+
+bool ArithCongruenceManager::propagate(TNode x){
+ Trace("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl;
+ if(inConflict()){
+ return true;
+ }
+
+ Node rewritten = rewrite(x);
+
+ //Need to still propagate this!
+ if(rewritten.getKind() == kind::CONST_BOOLEAN){
+ pushBack(x);
+
+ if(rewritten.getConst<bool>()){
+ return true;
+ }else{
+ // x rewrites to false.
+ ++(d_statistics.d_conflicts);
+ TrustNode trn = explainInternal(x);
+ Node conf = flattenAnd(trn.getNode());
+ Trace("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl;
+ if (isProofEnabled())
+ {
+ auto pf = trn.getGenerator()->getProofFor(trn.getProven());
+ auto confPf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()});
+ raiseConflict(conf, confPf);
+ }
+ else
+ {
+ raiseConflict(conf);
+ }
+ return false;
+ }
+ }
+
+ Assert(rewritten.getKind() != kind::CONST_BOOLEAN);
+
+ ConstraintP c = d_constraintDatabase.lookup(rewritten);
+ if(c == NullConstraint){
+ //using setup as there may not be a corresponding congruence literal yet
+ d_setupLiteral(rewritten);
+ c = d_constraintDatabase.lookup(rewritten);
+ Assert(c != NullConstraint);
+ }
+
+ Trace("arith::congruenceManager")<< "x is "
+ << c->hasProof() << " "
+ << (x == rewritten) << " "
+ << c->canBePropagated() << " "
+ << c->negationHasProof() << std::endl;
+
+ if(c->negationHasProof()){
+ TrustNode texpC = explainInternal(x);
+ Node expC = texpC.getNode();
+ ConstraintCP negC = c->getNegation();
+ Node neg = Constraint::externalExplainByAssertions({negC});
+ Node conf = expC.andNode(neg);
+ Node final = flattenAnd(conf);
+
+ ++(d_statistics.d_conflicts);
+ raiseConflict(final);
+ Trace("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl;
+ return false;
+ }
+
+ // Cases for propagation
+ // C : c has a proof
+ // S : x == rewritten
+ // P : c can be propagated
+ //
+ // CSP
+ // 000 : propagate x, and mark C it as being explained
+ // 001 : propagate x, and propagate c after marking it as being explained
+ // 01* : propagate x, mark c but do not propagate c
+ // 10* : propagate x, do not mark c and do not propagate c
+ // 11* : drop the constraint, do not propagate x or c
+
+ if(!c->hasProof() && x != rewritten){
+ if(c->assertedToTheTheory()){
+ pushBack(x, rewritten, c->getWitness());
+ }else{
+ pushBack(x, rewritten);
+ }
+
+ c->setEqualityEngineProof();
+ if(c->canBePropagated() && !c->assertedToTheTheory()){
+
+ ++(d_statistics.d_propagateConstraints);
+ c->propagate();
+ }
+ }else if(!c->hasProof() && x == rewritten){
+ if(c->assertedToTheTheory()){
+ pushBack(x, c->getWitness());
+ }else{
+ pushBack(x);
+ }
+ c->setEqualityEngineProof();
+ }else if(c->hasProof() && x != rewritten){
+ if(c->assertedToTheTheory()){
+ pushBack(x);
+ }else{
+ pushBack(x);
+ }
+ }else{
+ Assert(c->hasProof() && x == rewritten);
+ }
+ return true;
+}
+
+void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) {
+ if (literal.getKind() != kind::NOT) {
+ d_ee->explainEquality(literal[0], literal[1], true, assumptions);
+ } else {
+ d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions);
+ }
+}
+
+void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s,
+ NodeBuilder& nb)
+{
+ std::set<TNode>::const_iterator it = s.begin();
+ std::set<TNode>::const_iterator it_end = s.end();
+ for(; it != it_end; ++it) {
+ nb << *it;
+ }
+}
+
+TrustNode ArithCongruenceManager::explainInternal(TNode internal)
+{
+ if (isProofEnabled())
+ {
+ return d_pfee->explain(internal);
+ }
+ // otherwise, explain without proof generator
+ Node exp = d_ee->mkExplainLit(internal);
+ return TrustNode::mkTrustPropExp(internal, exp, nullptr);
+}
+
+TrustNode ArithCongruenceManager::explain(TNode external)
+{
+ Trace("arith-ee") << "Ask for explanation of " << external << std::endl;
+ Node internal = externalToInternal(external);
+ Trace("arith-ee") << "...internal = " << internal << std::endl;
+ TrustNode trn = explainInternal(internal);
+ if (isProofEnabled() && trn.getProven()[1] != external)
+ {
+ Assert(trn.getKind() == TrustNodeKind::PROP_EXP);
+ Assert(trn.getProven().getKind() == Kind::IMPLIES);
+ Assert(trn.getGenerator() != nullptr);
+ Trace("arith-ee") << "tweaking proof to prove " << external << " not "
+ << trn.getProven()[1] << std::endl;
+ std::vector<std::shared_ptr<ProofNode>> assumptionPfs;
+ std::vector<Node> assumptions = andComponents(trn.getNode());
+ assumptionPfs.push_back(trn.toProofNode());
+ for (const auto& a : assumptions)
+ {
+ assumptionPfs.push_back(
+ d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {}));
+ }
+ auto litPf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external});
+ auto extPf = d_pnm->mkScope(litPf, assumptions);
+ return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf);
+ }
+ return trn;
+}
+
+void ArithCongruenceManager::explain(TNode external, NodeBuilder& out)
+{
+ Node internal = externalToInternal(external);
+
+ std::vector<TNode> assumptions;
+ explain(internal, assumptions);
+ std::set<TNode> assumptionSet;
+ assumptionSet.insert(assumptions.begin(), assumptions.end());
+
+ enqueueIntoNB(assumptionSet, out);
+}
+
+void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){
+ Assert(!isWatchedVariable(s));
+
+ Trace("arith::congruenceManager")
+ << "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl;
+
+
+ ++(d_statistics.d_watchedVariables);
+
+ d_watchedVariables.add(s);
+
+ Node eq = x.eqNode(y);
+ d_watchedEqualities.set(s, eq);
+}
+
+void ArithCongruenceManager::assertLitToEqualityEngine(
+ Node lit, TNode reason, std::shared_ptr<ProofNode> pf)
+{
+ bool isEquality = lit.getKind() != Kind::NOT;
+ Node eq = isEquality ? lit : lit[0];
+ Assert(eq.getKind() == Kind::EQUAL);
+
+ Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason
+ << std::endl;
+ if (isProofEnabled())
+ {
+ if (CDProof::isSame(lit, reason))
+ {
+ Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl;
+ // The equality engine doesn't ref-count for us...
+ d_keepAlive.push_back(eq);
+ d_keepAlive.push_back(reason);
+ d_ee->assertEquality(eq, isEquality, reason);
+ }
+ else if (hasProofFor(lit))
+ {
+ Trace("arith-pfee") << "Skipping b/c already done" << std::endl;
+ }
+ else
+ {
+ setProofFor(lit, pf);
+ Trace("arith-pfee") << "Actually asserting" << std::endl;
+ if (TraceIsOn("arith-pfee"))
+ {
+ Trace("arith-pfee") << "Proof: ";
+ pf->printDebug(Trace("arith-pfee"));
+ Trace("arith-pfee") << std::endl;
+ }
+ // The proof equality engine *does* ref-count for us...
+ d_pfee->assertFact(lit, reason, d_pfGenEe.get());
+ }
+ }
+ else
+ {
+ // The equality engine doesn't ref-count for us...
+ d_keepAlive.push_back(eq);
+ d_keepAlive.push_back(reason);
+ d_ee->assertEquality(eq, isEquality, reason);
+ }
+}
+
+void ArithCongruenceManager::assertionToEqualityEngine(
+ bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf)
+{
+ Assert(isWatchedVariable(s));
+
+ TNode eq = d_watchedEqualities[s];
+ Assert(eq.getKind() == kind::EQUAL);
+
+ Node lit = isEquality ? Node(eq) : eq.notNode();
+ Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality
+ << ", reason " << reason << std::endl;
+ assertLitToEqualityEngine(lit, reason, pf);
+}
+
+bool ArithCongruenceManager::hasProofFor(TNode f) const
+{
+ Assert(isProofEnabled());
+ if (d_pfGenEe->hasProofFor(f))
+ {
+ return true;
+ }
+ Node sym = CDProof::getSymmFact(f);
+ Assert(!sym.isNull());
+ return d_pfGenEe->hasProofFor(sym);
+}
+
+void ArithCongruenceManager::setProofFor(TNode f,
+ std::shared_ptr<ProofNode> pf) const
+{
+ Assert(!hasProofFor(f));
+ d_pfGenEe->mkTrustNode(f, pf);
+ Node symF = CDProof::getSymmFact(f);
+ auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {});
+ d_pfGenEe->mkTrustNode(symF, symPf);
+}
+
+void ArithCongruenceManager::equalsConstant(ConstraintCP c){
+ Assert(c->isEquality());
+
+ ++(d_statistics.d_equalsConstantCalls);
+ Trace("equalsConstant") << "equals constant " << c << std::endl;
+
+ ArithVar x = c->getVariable();
+ Node xAsNode = d_avariables.asNode(x);
+ NodeManager* nm = NodeManager::currentNM();
+ Node asRational = nm->mkConstRealOrInt(
+ xAsNode.getType(), c->getValue().getNoninfinitesimalPart());
+
+ // No guarentee this is in normal form!
+ // Note though, that it happens to be in proof normal form!
+ Node eq = xAsNode.eqNode(asRational);
+ d_keepAlive.push_back(eq);
+
+ NodeBuilder nb(Kind::AND);
+ auto pf = c->externalExplainByAssertions(nb);
+ Node reason = mkAndFromBuilder(nb);
+ d_keepAlive.push_back(reason);
+
+ Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl;
+ assertLitToEqualityEngine(eq, reason, pf);
+}
+
+void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){
+ Assert(lb->isLowerBound());
+ Assert(ub->isUpperBound());
+ Assert(lb->getVariable() == ub->getVariable());
+
+ ++(d_statistics.d_equalsConstantCalls);
+ Trace("equalsConstant") << "equals constant " << lb << std::endl
+ << ub << std::endl;
+
+ ArithVar x = lb->getVariable();
+ NodeBuilder nb(Kind::AND);
+ auto pfLb = lb->externalExplainByAssertions(nb);
+ auto pfUb = ub->externalExplainByAssertions(nb);
+ Node reason = mkAndFromBuilder(nb);
+
+ Node xAsNode = d_avariables.asNode(x);
+ NodeManager* nm = NodeManager::currentNM();
+ Node asRational = nm->mkConstRealOrInt(
+ xAsNode.getType(), lb->getValue().getNoninfinitesimalPart());
+
+ // No guarentee this is in normal form!
+ // Note though, that it happens to be in proof normal form!
+ Node eq = xAsNode.eqNode(asRational);
+ std::shared_ptr<ProofNode> pf;
+ if (isProofEnabled())
+ {
+ pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq});
+ }
+ d_keepAlive.push_back(eq);
+ d_keepAlive.push_back(reason);
+
+ Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl;
+
+ assertLitToEqualityEngine(eq, reason, pf);
+}
+
+bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; }
+
+std::vector<Node> andComponents(TNode an)
+{
+ auto nm = NodeManager::currentNM();
+ if (an == nm->mkConst(true))
+ {
+ return {};
+ }
+ else if (an.getKind() != Kind::AND)
+ {
+ return {an};
+ }
+ std::vector<Node> a{};
+ a.reserve(an.getNumChildren());
+ a.insert(a.end(), an.begin(), an.end());
+ return a;
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Alex Ozdemir, Tim King, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "context/cdhashmap.h"
+#include "context/cdlist.h"
+#include "context/cdmaybe.h"
+#include "context/cdtrail_queue.h"
+#include "proof/trust_node.h"
+#include "smt/env_obj.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/arithvar_node_map.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/uf/equality_engine_notify.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+class UserContext;
+} // namespace cvc5::context
+
+namespace cvc5::internal {
+
+class ProofNodeManager;
+class EagerProofGenerator;
+
+namespace theory {
+struct EeSetupInfo;
+
+namespace eq {
+class ProofEqEngine;
+class EqualityEngine;
+}
+
+namespace arith::linear {
+
+class ArithVariables;
+
+class ArithCongruenceManager : protected EnvObj
+{
+ private:
+ context::CDRaised d_inConflict;
+ RaiseEqualityEngineConflict d_raiseConflict;
+
+ /**
+ * The set of ArithVars equivalent to a pair of terms.
+ * If this is 0 or cannot be 0, this can be signalled.
+ * The pair of terms for the congruence is stored in watched equalities.
+ */
+ DenseSet d_watchedVariables;
+ /** d_watchedVariables |-> (= x y) */
+ ArithVarToNodeMap d_watchedEqualities;
+
+
+ class ArithCongruenceNotify : public eq::EqualityEngineNotify {
+ private:
+ ArithCongruenceManager& d_acm;
+ public:
+ ArithCongruenceNotify(ArithCongruenceManager& acm);
+
+ bool eqNotifyTriggerPredicate(TNode predicate, bool value) override;
+
+ bool eqNotifyTriggerTermEquality(TheoryId tag,
+ TNode t1,
+ TNode t2,
+ bool value) override;
+
+ void eqNotifyConstantTermMerge(TNode t1, TNode t2) override;
+ void eqNotifyNewClass(TNode t) override;
+ void eqNotifyMerge(TNode t1, TNode t2) override;
+ void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override;
+ };
+ ArithCongruenceNotify d_notify;
+
+ context::CDList<Node> d_keepAlive;
+
+ /** Store the propagations. */
+ context::CDTrailQueue<Node> d_propagatations;
+
+ /* This maps the node a theory engine will request on an explain call to
+ * to its corresponding PropUnit.
+ * This is node is potentially both the propagation or
+ * rewrite(propagation).
+ */
+ typedef context::CDHashMap<Node, size_t> ExplainMap;
+ ExplainMap d_explanationMap;
+
+ ConstraintDatabase& d_constraintDatabase;
+ SetupLiteralCallBack d_setupLiteral;
+
+ const ArithVariables& d_avariables;
+
+ /** The equality engine being used by this class */
+ eq::EqualityEngine* d_ee;
+ /** The equality engine we allocated */
+ std::unique_ptr<eq::EqualityEngine> d_allocEe;
+ /** proof manager */
+ ProofNodeManager* d_pnm;
+ /** A proof generator for storing proofs of facts that are asserted to the EQ
+ * engine. Note that these proofs **are not closed**; they may contain
+ * literals from the explanation of their fact as unclosed assumptions.
+ * This makes these proofs SAT-context depdent.
+ *
+ * This is why this generator is separate from the TheoryArithPrivate
+ * generator, which stores closed proofs.
+ */
+ std::unique_ptr<EagerProofGenerator> d_pfGenEe;
+ /** A proof generator for TrustNodes sent to TheoryArithPrivate.
+ *
+ * When TheoryArithPrivate requests an explanation from
+ * ArithCongruenceManager, it can request a TrustNode for that explanation.
+ * This proof generator is the one used in that TrustNode: it stores the
+ * (closed) proofs of implications proved by the
+ * ArithCongruenceManager/EqualityEngine.
+ *
+ * It is insufficient to just use the ProofGenerator from the ProofEqEngine,
+ * since sometimes the ArithCongruenceManager needs to add some
+ * arith-specific reasoning to extend the proof (e.g. rewriting the result
+ * into a normal form).
+ * */
+ std::unique_ptr<EagerProofGenerator> d_pfGenExplain;
+
+ /** Pointer to the proof equality engine of TheoryArith */
+ theory::eq::ProofEqEngine* d_pfee;
+ /** The proof equality engine we allocated */
+ std::unique_ptr<eq::ProofEqEngine> d_allocPfee;
+
+ /** Raise a conflict node `conflict` to the theory of arithmetic.
+ *
+ * When proofs are enabled, a (closed) proof of the conflict should be
+ * provided.
+ */
+ void raiseConflict(Node conflict, std::shared_ptr<ProofNode> pf = nullptr);
+ /**
+ * Are proofs enabled? This is true if a non-null proof manager was provided
+ * to the constructor of this class.
+ */
+ bool isProofEnabled() const;
+
+ public:
+ bool inConflict() const;
+
+ bool hasMorePropagations() const;
+
+ const Node getNextPropagation();
+
+ bool canExplain(TNode n) const;
+
+private:
+ Node externalToInternal(TNode n) const;
+
+ void pushBack(TNode n);
+
+ void pushBack(TNode n, TNode r);
+
+ void pushBack(TNode n, TNode r, TNode w);
+
+ bool propagate(TNode x);
+ void explain(TNode literal, std::vector<TNode>& assumptions);
+
+ /** Assert this literal to the eq engine. Common functionality for
+ * * assertionToEqualityEngine(..)
+ * * equalsConstant(c)
+ * * equalsConstant(lb, ub)
+ * If proof is off, then just asserts.
+ */
+ void assertLitToEqualityEngine(Node lit,
+ TNode reason,
+ std::shared_ptr<ProofNode> pf);
+ /** This sends a shared term to the uninterpreted equality engine. */
+ void assertionToEqualityEngine(bool eq,
+ ArithVar s,
+ TNode reason,
+ std::shared_ptr<ProofNode> pf);
+
+ /** Check for proof for this or a symmetric fact
+ *
+ * The proof submitted to this method are stored in `d_pfGenEe`, and should
+ * have closure properties consistent with the documentation for that member.
+ *
+ * @returns whether this or a symmetric fact has a proof.
+ */
+ bool hasProofFor(TNode f) const;
+ /**
+ * Sets the proof for this fact and the symmetric one.
+ *
+ * The proof submitted to this method are stored in `d_pfGenEe`, and should
+ * have closure properties consistent with the documentation for that member.
+ * */
+ void setProofFor(TNode f, std::shared_ptr<ProofNode> pf) const;
+
+ /** Dequeues the delay queue and asserts these equalities.*/
+ void enableSharedTerms();
+ void dequeueLiterals();
+
+ void enqueueIntoNB(const std::set<TNode> all, NodeBuilder& nb);
+
+ /**
+ * Determine an explaination for `internal`. That is a conjunction of theory
+ * literals which imply `internal`.
+ *
+ * The TrustNode here is a trusted propagation.
+ */
+ TrustNode explainInternal(TNode internal);
+
+ public:
+ ArithCongruenceManager(Env& env,
+ ConstraintDatabase&,
+ SetupLiteralCallBack,
+ const ArithVariables&,
+ RaiseEqualityEngineConflict raiseConflict);
+ ~ArithCongruenceManager();
+
+ //--------------------------------- initialization
+ /**
+ * Returns true if we need an equality engine, see
+ * Theory::needsEqualityEngine.
+ */
+ bool needsEqualityEngine(EeSetupInfo& esi);
+ /**
+ * Finish initialize. This class is instructed by TheoryArithPrivate to use
+ * the equality engine ee.
+ */
+ void finishInit(eq::EqualityEngine* ee);
+ //--------------------------------- end initialization
+
+ /**
+ * Return the trust node for the explanation of literal. The returned
+ * trust node is generated by the proof equality engine of this class.
+ */
+ TrustNode explain(TNode literal);
+
+ void explain(TNode lit, NodeBuilder& out);
+
+ void addWatchedPair(ArithVar s, TNode x, TNode y);
+
+ inline bool isWatchedVariable(ArithVar s) const {
+ return d_watchedVariables.isMember(s);
+ }
+
+ /** Assert an equality. */
+ void watchedVariableIsZero(ConstraintCP eq);
+
+ /** Assert a conjunction from lb and ub. */
+ void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub);
+
+ /** Assert that the value cannot be zero. */
+ void watchedVariableCannotBeZero(ConstraintCP c);
+
+ /** Assert that the value cannot be zero. */
+ void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d);
+
+
+ /** Assert that the value is congruent to a constant. */
+ void equalsConstant(ConstraintCP eq);
+ void equalsConstant(ConstraintCP lb, ConstraintCP ub);
+
+ private:
+ class Statistics {
+ public:
+ IntStat d_watchedVariables;
+ IntStat d_watchedVariableIsZero;
+ IntStat d_watchedVariableIsNotZero;
+
+ IntStat d_equalsConstantCalls;
+
+ IntStat d_propagations;
+ IntStat d_propagateConstraints;
+ IntStat d_conflicts;
+
+ Statistics();
+ } d_statistics;
+
+}; /* class ArithCongruenceManager */
+
+std::vector<Node> andComponents(TNode an);
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Alex Ozdemir, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/constraint.h"
+
+#include <algorithm>
+#include <ostream>
+#include <unordered_set>
+
+#include "base/output.h"
+#include "options/smt_options.h"
+#include "proof/eager_proof_generator.h"
+#include "proof/proof_node_manager.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/builtin/proof_checker.h"
+#include "theory/rewriter.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ConstraintRule::ConstraintRule()
+ : d_constraint(NullConstraint),
+ d_proofType(NoAP),
+ d_antecedentEnd(AntecedentIdSentinel)
+{
+ d_farkasCoefficients = RationalVectorCPSentinel;
+}
+
+ConstraintRule::ConstraintRule(ConstraintP con, ArithProofType pt)
+ : d_constraint(con), d_proofType(pt), d_antecedentEnd(AntecedentIdSentinel)
+{
+ d_farkasCoefficients = RationalVectorCPSentinel;
+}
+ConstraintRule::ConstraintRule(ConstraintP con,
+ ArithProofType pt,
+ AntecedentId antecedentEnd)
+ : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
+{
+ d_farkasCoefficients = RationalVectorCPSentinel;
+}
+
+ConstraintRule::ConstraintRule(ConstraintP con,
+ ArithProofType pt,
+ AntecedentId antecedentEnd,
+ RationalVectorCP coeffs)
+ : d_constraint(con), d_proofType(pt), d_antecedentEnd(antecedentEnd)
+{
+ Assert(con->isProofProducing() || coeffs == RationalVectorCPSentinel);
+ d_farkasCoefficients = coeffs;
+}
+
+/** Given a simplifiedKind this returns the corresponding ConstraintType. */
+//ConstraintType constraintTypeOfLiteral(Kind k);
+ConstraintType Constraint::constraintTypeOfComparison(const Comparison& cmp){
+ Kind k = cmp.comparisonKind();
+ switch(k){
+ case LT:
+ case LEQ:
+ {
+ Polynomial l = cmp.getLeft();
+ if(l.leadingCoefficientIsPositive()){ // (< x c)
+ return UpperBound;
+ }else{
+ return LowerBound; // (< (-x) c)
+ }
+ }
+ case GT:
+ case GEQ:
+ {
+ Polynomial l = cmp.getLeft();
+ if(l.leadingCoefficientIsPositive()){
+ return LowerBound; // (> x c)
+ }else{
+ return UpperBound; // (> (-x) c)
+ }
+ }
+ case EQUAL:
+ return Equality;
+ case DISTINCT:
+ return Disequality;
+ default: Unhandled() << k;
+ }
+}
+
+Constraint::Constraint(ArithVar x,
+ ConstraintType t,
+ const DeltaRational& v,
+ bool produceProofs)
+ : d_variable(x),
+ d_type(t),
+ d_value(v),
+ d_database(NULL),
+ d_literal(Node::null()),
+ d_negation(NullConstraint),
+ d_canBePropagated(false),
+ d_assertionOrder(AssertionOrderSentinel),
+ d_witness(TNode::null()),
+ d_crid(ConstraintRuleIdSentinel),
+ d_split(false),
+ d_variablePosition(),
+ d_produceProofs(produceProofs)
+{
+ Assert(!initialized());
+}
+
+
+std::ostream& operator<<(std::ostream& o, const ArithProofType apt){
+ switch(apt){
+ case NoAP: o << "NoAP"; break;
+ case AssumeAP: o << "AssumeAP"; break;
+ case InternalAssumeAP: o << "InternalAssumeAP"; break;
+ case FarkasAP: o << "FarkasAP"; break;
+ case TrichotomyAP: o << "TrichotomyAP"; break;
+ case EqualityEngineAP: o << "EqualityEngineAP"; break;
+ case IntTightenAP: o << "IntTightenAP"; break;
+ case IntHoleAP: o << "IntHoleAP"; break;
+ default: break;
+ }
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintCP c){
+ if(c == NullConstraint){
+ return o << "NullConstraint";
+ }else{
+ return o << *c;
+ }
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintP c){
+ if(c == NullConstraint){
+ return o << "NullConstraint";
+ }else{
+ return o << *c;
+ }
+}
+
+std::ostream& operator<<(std::ostream& o, const ConstraintType t){
+ switch(t){
+ case LowerBound:
+ return o << ">=";
+ case UpperBound:
+ return o << "<=";
+ case Equality:
+ return o << "=";
+ case Disequality:
+ return o << "!=";
+ default:
+ Unreachable();
+ }
+}
+
+std::ostream& operator<<(std::ostream& o, const Constraint& c){
+ o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue();
+ if(c.hasLiteral()){
+ o << "(node " << c.getLiteral() << ')';
+ }
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){
+ o << "{";
+ bool pending = false;
+ if(vc.hasEquality()){
+ o << "eq: " << vc.getEquality();
+ pending = true;
+ }
+ if(vc.hasLowerBound()){
+ if(pending){
+ o << ", ";
+ }
+ o << "lb: " << vc.getLowerBound();
+ pending = true;
+ }
+ if(vc.hasUpperBound()){
+ if(pending){
+ o << ", ";
+ }
+ o << "ub: " << vc.getUpperBound();
+ pending = true;
+ }
+ if(vc.hasDisequality()){
+ if(pending){
+ o << ", ";
+ }
+ o << "de: " << vc.getDisequality();
+ }
+ return o << "}";
+}
+
+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;
+}
+
+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 {
+ Trace("arith::constraint") << "push_into " << *this << endl;
+ if(hasEquality()){
+ vec.push_back(d_equality);
+ }
+ if(hasLowerBound()){
+ vec.push_back(d_lowerBound);
+ }
+ if(hasUpperBound()){
+ vec.push_back(d_upperBound);
+ }
+ if(hasDisequality()){
+ vec.push_back(d_disequality);
+ }
+}
+
+ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){
+ ValueCollection ret;
+ Assert(ret.empty());
+ switch(c->getType()){
+ case LowerBound:
+ ret.d_lowerBound = c;
+ break;
+ case UpperBound:
+ ret.d_upperBound = c;
+ break;
+ case Equality:
+ ret.d_equality = c;
+ break;
+ case Disequality:
+ ret.d_disequality = c;
+ break;
+ default:
+ Unreachable();
+ }
+ return ret;
+}
+
+bool ValueCollection::hasConstraintOfType(ConstraintType t) const{
+ switch(t){
+ case LowerBound:
+ return hasLowerBound();
+ case UpperBound:
+ return hasUpperBound();
+ case Equality:
+ return hasEquality();
+ case Disequality:
+ return hasDisequality();
+ default:
+ Unreachable();
+ }
+}
+
+ArithVar ValueCollection::getVariable() const{
+ Assert(!empty());
+ return nonNull()->getVariable();
+}
+
+const DeltaRational& ValueCollection::getValue() const{
+ Assert(!empty());
+ return nonNull()->getValue();
+}
+
+void ValueCollection::add(ConstraintP c){
+ Assert(c != NullConstraint);
+
+ Assert(empty() || getVariable() == c->getVariable());
+ Assert(empty() || getValue() == c->getValue());
+
+ switch(c->getType()){
+ case LowerBound:
+ Assert(!hasLowerBound());
+ d_lowerBound = c;
+ break;
+ case Equality:
+ Assert(!hasEquality());
+ d_equality = c;
+ break;
+ case UpperBound:
+ Assert(!hasUpperBound());
+ d_upperBound = c;
+ break;
+ case Disequality:
+ Assert(!hasDisequality());
+ d_disequality = c;
+ break;
+ default:
+ Unreachable();
+ }
+}
+
+ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{
+ switch(t){
+ case LowerBound: Assert(hasLowerBound()); return d_lowerBound;
+ case Equality: Assert(hasEquality()); return d_equality;
+ case UpperBound: Assert(hasUpperBound()); return d_upperBound;
+ case Disequality: Assert(hasDisequality()); return d_disequality;
+ default: Unreachable();
+ }
+}
+
+void ValueCollection::remove(ConstraintType t){
+ switch(t){
+ case LowerBound:
+ Assert(hasLowerBound());
+ d_lowerBound = NullConstraint;
+ break;
+ case Equality:
+ Assert(hasEquality());
+ d_equality = NullConstraint;
+ break;
+ case UpperBound:
+ Assert(hasUpperBound());
+ d_upperBound = NullConstraint;
+ break;
+ case Disequality:
+ Assert(hasDisequality());
+ d_disequality = NullConstraint;
+ break;
+ default:
+ Unreachable();
+ }
+}
+
+bool ValueCollection::empty() const{
+ return
+ !(hasLowerBound() ||
+ hasUpperBound() ||
+ hasEquality() ||
+ hasDisequality());
+}
+
+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()){
+ return d_lowerBound;
+ }else if(hasUpperBound()){
+ return d_upperBound;
+ }else if(hasEquality()){
+ return d_equality;
+ }else if(hasDisequality()){
+ return d_disequality;
+ }else{
+ return NullConstraint;
+ }
+}
+
+bool Constraint::initialized() const {
+ return d_database != NULL;
+}
+
+const ConstraintDatabase& Constraint::getDatabase() const{
+ Assert(initialized());
+ return *d_database;
+}
+
+void Constraint::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){
+ Assert(!initialized());
+ d_database = db;
+ d_variablePosition = v;
+ d_negation = negation;
+}
+
+Constraint::~Constraint() {
+ // Call this instead of safeToGarbageCollect()
+ Assert(!contextDependentDataIsSet());
+
+ if(initialized()){
+ ValueCollection& vc = d_variablePosition->second;
+ Trace("arith::constraint") << "removing" << vc << endl;
+
+ vc.remove(getType());
+
+ if(vc.empty()){
+ Trace("arith::constraint") << "erasing" << vc << endl;
+ SortedConstraintMap& perVariable = d_database->getVariableSCM(getVariable());
+ perVariable.erase(d_variablePosition);
+ }
+
+ if(hasLiteral()){
+ d_database->d_nodetoConstraintMap.erase(getLiteral());
+ }
+ }
+}
+
+const ConstraintRule& Constraint::getConstraintRule() const {
+ Assert(hasProof());
+ return d_database->d_watches->d_constraintProofs[d_crid];
+}
+
+const ValueCollection& Constraint::getValueCollection() const{
+ return d_variablePosition->second;
+}
+
+
+ConstraintP Constraint::getCeiling() {
+ Trace("getCeiling") << "Constraint_::getCeiling on " << *this << endl;
+ Assert(getValue().getInfinitesimalPart().sgn() > 0);
+
+ const DeltaRational ceiling(getValue().ceiling());
+ return d_database->getConstraint(getVariable(), getType(), ceiling);
+}
+
+ConstraintP Constraint::getFloor() {
+ Assert(getValue().getInfinitesimalPart().sgn() < 0);
+
+ const DeltaRational floor(Rational(getValue().floor()));
+ return d_database->getConstraint(getVariable(), getType(), floor);
+}
+
+void Constraint::setCanBePropagated() {
+ Assert(!canBePropagated());
+ d_database->pushCanBePropagatedWatch(this);
+}
+
+void Constraint::setAssertedToTheTheory(TNode witness, bool nowInConflict) {
+ Assert(hasLiteral());
+ Assert(!assertedToTheTheory());
+ Assert(negationHasProof() == nowInConflict);
+ d_database->pushAssertionOrderWatch(this, witness);
+
+ if(TraceIsOn("constraint::conflictCommit") && nowInConflict ){
+ Trace("constraint::conflictCommit") << "inConflict@setAssertedToTheTheory";
+ Trace("constraint::conflictCommit") << "\t" << this << std::endl;
+ Trace("constraint::conflictCommit") << "\t" << getNegation() << std::endl;
+ Trace("constraint::conflictCommit") << "\t" << getNegation()->externalExplainByAssertions() << std::endl;
+
+ }
+}
+
+bool Constraint::satisfiedBy(const DeltaRational& dr) const {
+ switch(getType()){
+ case LowerBound:
+ return getValue() <= dr;
+ case Equality:
+ return getValue() == dr;
+ case UpperBound:
+ return getValue() >= dr;
+ case Disequality:
+ return getValue() != dr;
+ }
+ Unreachable();
+}
+
+bool Constraint::isInternalAssumption() const {
+ return getProofType() == InternalAssumeAP;
+}
+
+TrustNode Constraint::externalExplainByAssertions() const
+{
+ NodeBuilder nb(kind::AND);
+ auto pfFromAssumptions = externalExplain(nb, AssertionOrderSentinel);
+ Node exp = mkAndFromBuilder(nb);
+ if (d_database->isProofEnabled())
+ {
+ std::vector<Node> assumptions;
+ if (exp.getKind() == Kind::AND)
+ {
+ assumptions.insert(assumptions.end(), exp.begin(), exp.end());
+ }
+ else
+ {
+ assumptions.push_back(exp);
+ }
+ auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
+ return d_database->d_pfGen->mkTrustedPropagation(
+ getLiteral(), NodeManager::currentNM()->mkAnd(assumptions), pf);
+ }
+ return TrustNode::mkTrustPropExp(getLiteral(), exp);
+}
+
+bool Constraint::isAssumption() const {
+ return getProofType() == AssumeAP;
+}
+
+bool Constraint::hasEqualityEngineProof() const {
+ return getProofType() == EqualityEngineAP;
+}
+
+bool Constraint::hasFarkasProof() const {
+ return getProofType() == FarkasAP;
+}
+
+bool Constraint::hasSimpleFarkasProof() const
+{
+ Trace("constraints::hsfp") << "hasSimpleFarkasProof " << this << std::endl;
+ if (!hasFarkasProof())
+ {
+ Trace("constraints::hsfp") << "There is no simple Farkas proof because "
+ "there is no farkas proof."
+ << std::endl;
+ return false;
+ }
+
+ // For each antecdent ...
+ AntecedentId i = getConstraintRule().d_antecedentEnd;
+ for (ConstraintCP a = d_database->getAntecedent(i); a != NullConstraint;
+ a = d_database->getAntecedent(--i))
+ {
+ // ... that antecdent must be an assumption OR a tightened assumption ...
+ if (a->isPossiblyTightenedAssumption())
+ {
+ continue;
+ }
+
+ // ... otherwise, we do not have a simple Farkas proof.
+ if (TraceIsOn("constraints::hsfp"))
+ {
+ Trace("constraints::hsfp") << "There is no simple Farkas proof b/c there "
+ "is an antecdent w/ rule ";
+ a->getConstraintRule().print(Trace("constraints::hsfp"), d_produceProofs);
+ Trace("constraints::hsfp") << std::endl;
+ }
+
+ return false;
+ }
+ return true;
+}
+
+bool Constraint::isPossiblyTightenedAssumption() const
+{
+ // ... that antecdent must be an assumption ...
+
+ if (isAssumption()) return true;
+ if (!hasIntTightenProof()) return false;
+ if (getConstraintRule().d_antecedentEnd == AntecedentIdSentinel) return false;
+ return d_database->getAntecedent(getConstraintRule().d_antecedentEnd)
+ ->isAssumption();
+}
+
+bool Constraint::hasIntTightenProof() const {
+ return getProofType() == IntTightenAP;
+}
+
+bool Constraint::hasIntHoleProof() const {
+ return getProofType() == IntHoleAP;
+}
+
+bool Constraint::hasTrichotomyProof() const {
+ return getProofType() == TrichotomyAP;
+}
+
+void Constraint::printProofTree(std::ostream& out, size_t depth) const
+{
+ if (d_produceProofs)
+ {
+ const ConstraintRule& rule = getConstraintRule();
+ out << std::string(2 * depth, ' ') << "* " << getVariable() << " [";
+ out << getProofLiteral();
+ if (assertedToTheTheory())
+ {
+ out << " | wit: " << getWitness();
+ }
+ out << "]" << ' ' << getType() << ' ' << getValue() << " ("
+ << getProofType() << ")";
+ if (getProofType() == FarkasAP)
+ {
+ out << " [";
+ bool first = true;
+ for (const auto& coeff : *rule.d_farkasCoefficients)
+ {
+ if (not first)
+ {
+ out << ", ";
+ }
+ first = false;
+ out << coeff;
+ }
+ out << "]";
+ }
+ out << endl;
+
+ for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i)
+ {
+ ConstraintCP antecdent = d_database->getAntecedent(i);
+ if (antecdent == NullConstraint)
+ {
+ break;
+ }
+ antecdent->printProofTree(out, depth + 1);
+ }
+ return;
+ }
+ out << "Cannot print proof. This is not a proof build." << endl;
+}
+
+bool Constraint::sanityChecking(Node n) const {
+ Comparison cmp = Comparison::parseNormalForm(n);
+ Kind k = cmp.comparisonKind();
+ Polynomial pleft = cmp.normalizedVariablePart();
+ Assert(k == EQUAL || k == DISTINCT || pleft.leadingCoefficientIsPositive());
+ Assert(k != EQUAL || Monomial::isMember(n[0]));
+ Assert(k != DISTINCT || Monomial::isMember(n[0][0]));
+
+ TNode left = pleft.getNode();
+ DeltaRational right = cmp.normalizedDeltaRational();
+
+ const ArithVariables& avariables = d_database->getArithVariables();
+
+ Trace("Constraint::sanityChecking") << cmp.getNode() << endl;
+ Trace("Constraint::sanityChecking") << k << endl;
+ Trace("Constraint::sanityChecking") << pleft.getNode() << endl;
+ Trace("Constraint::sanityChecking") << left << endl;
+ Trace("Constraint::sanityChecking") << right << endl;
+ Trace("Constraint::sanityChecking") << getValue() << endl;
+ Trace("Constraint::sanityChecking") << avariables.hasArithVar(left) << endl;
+ Trace("Constraint::sanityChecking") << avariables.asArithVar(left) << endl;
+ Trace("Constraint::sanityChecking") << getVariable() << endl;
+
+
+ if(avariables.hasArithVar(left) &&
+ avariables.asArithVar(left) == getVariable() &&
+ getValue() == right){
+ switch(getType()){
+ case LowerBound:
+ case UpperBound:
+ //Be overapproximate
+ return k == GT || k == GEQ ||k == LT || k == LEQ;
+ case Equality:
+ return k == EQUAL;
+ case Disequality:
+ return k == DISTINCT;
+ default:
+ Unreachable();
+ }
+ }else{
+ return false;
+ }
+}
+
+ConstraintCP ConstraintDatabase::getAntecedent (AntecedentId p) const {
+ Assert(p < d_antecedents.size());
+ return d_antecedents[p];
+}
+
+void ConstraintRule::print(std::ostream& out, bool produceProofs) const
+{
+ RationalVectorCP coeffs = produceProofs ? d_farkasCoefficients : nullptr;
+ out << "{ConstraintRule, ";
+ out << d_constraint << std::endl;
+ out << "d_proofType= " << d_proofType << ", " << std::endl;
+ out << "d_antecedentEnd= "<< d_antecedentEnd << std::endl;
+
+ if (d_constraint != NullConstraint && d_antecedentEnd != AntecedentIdSentinel)
+ {
+ const ConstraintDatabase& database = d_constraint->getDatabase();
+
+ size_t coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffs->size()-1 : 0;
+ AntecedentId p = d_antecedentEnd;
+ // must have at least one antecedent
+ ConstraintCP antecedent = database.getAntecedent(p);
+ while(antecedent != NullConstraint){
+ if(coeffs != RationalVectorCPSentinel){
+ out << coeffs->at(coeffIterator);
+ } else {
+ out << "_";
+ }
+ out << " * (" << *antecedent << ")" << std::endl;
+
+ Assert((coeffs == RationalVectorCPSentinel) || coeffIterator > 0);
+ --p;
+ coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffIterator-1 : 0;
+ antecedent = database.getAntecedent(p);
+ }
+ if(coeffs != RationalVectorCPSentinel){
+ out << coeffs->front();
+ } else {
+ out << "_";
+ }
+ out << " * (" << *(d_constraint->getNegation()) << ")";
+ out << " [not d_constraint] " << endl;
+ }
+ out << "}";
+}
+
+bool Constraint::wellFormedFarkasProof() const {
+ Assert(hasProof());
+
+ const ConstraintRule& cr = getConstraintRule();
+ if(cr.d_constraint != this){ return false; }
+ if(cr.d_proofType != FarkasAP){ return false; }
+
+ AntecedentId p = cr.d_antecedentEnd;
+
+ // must have at least one antecedent
+ ConstraintCP antecedent = d_database->d_antecedents[p];
+ if(antecedent == NullConstraint) { return false; }
+
+ if (!d_produceProofs)
+ {
+ return cr.d_farkasCoefficients == RationalVectorCPSentinel;
+ }
+ Assert(d_produceProofs);
+
+ if(cr.d_farkasCoefficients == RationalVectorCPSentinel){ return false; }
+ if(cr.d_farkasCoefficients->size() < 2){ return false; }
+
+ const ArithVariables& vars = d_database->getArithVariables();
+
+ DeltaRational rhs(0);
+ Node lhs = Polynomial::mkZero().getNode();
+
+ RationalVector::const_iterator coeffIterator = cr.d_farkasCoefficients->end()-1;
+ RationalVector::const_iterator coeffBegin = cr.d_farkasCoefficients->begin();
+
+ while(antecedent != NullConstraint){
+ Assert(lhs.isNull() || Polynomial::isMember(lhs));
+
+ const Rational& coeff = *coeffIterator;
+ int coeffSgn = coeff.sgn();
+
+ rhs += antecedent->getValue() * coeff;
+
+ ArithVar antVar = antecedent->getVariable();
+ if(!lhs.isNull() && vars.hasNode(antVar)){
+ Node antAsNode = vars.asNode(antVar);
+ if(Polynomial::isMember(antAsNode)){
+ Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
+ Polynomial antPoly = Polynomial::parsePolynomial(antAsNode);
+ Polynomial sum = lhsPoly + (antPoly * coeff);
+ lhs = sum.getNode();
+ }else{
+ lhs = Node::null();
+ }
+ } else {
+ lhs = Node::null();
+ }
+ Trace("constraints::wffp") << "running sum: " << lhs << " <= " << rhs << endl;
+
+ switch( antecedent->getType() ){
+ case LowerBound:
+ // fc[l] < 0, therefore return false if coeffSgn >= 0
+ if(coeffSgn >= 0){ return false; }
+ break;
+ case UpperBound:
+ // fc[u] > 0, therefore return false if coeffSgn <= 0
+ if(coeffSgn <= 0){ return false; }
+ break;
+ case Equality:
+ if(coeffSgn == 0) { return false; }
+ break;
+ case Disequality:
+ default:
+ return false;
+ }
+
+ if(coeffIterator == coeffBegin){ return false; }
+ --coeffIterator;
+ --p;
+ antecedent = d_database->d_antecedents[p];
+ }
+ if(coeffIterator != coeffBegin){ return false; }
+
+ const Rational& firstCoeff = (*coeffBegin);
+ int firstCoeffSgn = firstCoeff.sgn();
+ rhs += (getNegation()->getValue()) * firstCoeff;
+ if(!lhs.isNull() && vars.hasNode(getVariable())){
+ Node firstAsNode = vars.asNode(getVariable());
+ if(Polynomial::isMember(firstAsNode)){
+ Polynomial lhsPoly = Polynomial::parsePolynomial(lhs);
+ Polynomial firstPoly = Polynomial::parsePolynomial(firstAsNode);
+ Polynomial sum = lhsPoly + (firstPoly * firstCoeff);
+ lhs = sum.getNode();
+ }else{
+ lhs = Node::null();
+ }
+ }else{
+ lhs = Node::null();
+ }
+
+ switch( getNegation()->getType() ){
+ case LowerBound:
+ // fc[l] < 0, therefore return false if coeffSgn >= 0
+ if(firstCoeffSgn >= 0){ return false; }
+ break;
+ case UpperBound:
+ // fc[u] > 0, therefore return false if coeffSgn <= 0
+ if(firstCoeffSgn <= 0){ return false; }
+ break;
+ case Equality:
+ if(firstCoeffSgn == 0) { return false; }
+ break;
+ case Disequality:
+ default:
+ return false;
+ }
+ Trace("constraints::wffp") << "final sum: " << lhs << " <= " << rhs << endl;
+ // 0 = lhs <= rhs < 0
+ return (lhs.isNull() || (Constant::isMember(lhs) && Constant(lhs).isZero()))
+ && rhs.sgn() < 0;
+}
+
+ConstraintP Constraint::makeNegation(ArithVar v,
+ ConstraintType t,
+ const DeltaRational& r,
+ bool produceProofs)
+{
+ switch(t){
+ case LowerBound:
+ {
+ Assert(r.infinitesimalSgn() >= 0);
+ if(r.infinitesimalSgn() > 0){
+ Assert(r.getInfinitesimalPart() == 1);
+ // make (not (v > r)), which is (v <= r)
+ DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
+ return new Constraint(v, UpperBound, dropInf, produceProofs);
+ }else{
+ Assert(r.infinitesimalSgn() == 0);
+ // make (not (v >= r)), which is (v < r)
+ DeltaRational addInf(r.getNoninfinitesimalPart(), -1);
+ return new Constraint(v, UpperBound, addInf, produceProofs);
+ }
+ }
+ case UpperBound:
+ {
+ Assert(r.infinitesimalSgn() <= 0);
+ if(r.infinitesimalSgn() < 0){
+ Assert(r.getInfinitesimalPart() == -1);
+ // make (not (v < r)), which is (v >= r)
+ DeltaRational dropInf(r.getNoninfinitesimalPart(), 0);
+ return new Constraint(v, LowerBound, dropInf, produceProofs);
+ }else{
+ Assert(r.infinitesimalSgn() == 0);
+ // make (not (v <= r)), which is (v > r)
+ DeltaRational addInf(r.getNoninfinitesimalPart(), 1);
+ return new Constraint(v, LowerBound, addInf, produceProofs);
+ }
+ }
+ case Equality: return new Constraint(v, Disequality, r, produceProofs);
+ case Disequality: return new Constraint(v, Equality, r, produceProofs);
+ default: Unreachable(); return NullConstraint;
+ }
+}
+
+ConstraintDatabase::ConstraintDatabase(Env& env,
+ const ArithVariables& avars,
+ ArithCongruenceManager& cm,
+ RaiseConflict raiseConflict,
+ EagerProofGenerator* pfGen)
+ : EnvObj(env),
+ d_varDatabases(),
+ d_toPropagate(context()),
+ d_antecedents(context(), false),
+ d_watches(new Watches(context(), userContext())),
+ d_avariables(avars),
+ d_congruenceManager(cm),
+ d_pfGen(pfGen),
+ d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+ : nullptr),
+ d_raiseConflict(raiseConflict),
+ d_one(1),
+ d_negOne(-1)
+{
+}
+
+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);
+}
+
+
+void ConstraintDatabase::pushConstraintRule(const ConstraintRule& crp){
+ ConstraintP c = crp.d_constraint;
+ Assert(c->d_crid == ConstraintRuleIdSentinel);
+ Assert(!c->hasProof());
+ c->d_crid = d_watches->d_constraintProofs.size();
+ d_watches->d_constraintProofs.push_back(crp);
+}
+
+ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){
+ //This must always return a constraint.
+
+ SortedConstraintMap& scm = getVariableSCM(v);
+ pair<SortedConstraintMapIterator, bool> insertAttempt;
+ insertAttempt = scm.insert(make_pair(r, ValueCollection()));
+
+ SortedConstraintMapIterator pos = insertAttempt.first;
+ ValueCollection& vc = pos->second;
+ if(vc.hasConstraintOfType(t)){
+ return vc.getConstraintOfType(t);
+ }else{
+ ConstraintP c = new Constraint(v, t, r, options().smt.produceProofs);
+ ConstraintP negC =
+ Constraint::makeNegation(v, t, r, options().smt.produceProofs);
+
+ SortedConstraintMapIterator negPos;
+ if(t == Equality || t == Disequality){
+ negPos = pos;
+ }else{
+ pair<SortedConstraintMapIterator, bool> negInsertAttempt;
+ negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
+ Assert(negInsertAttempt.second
+ || !negInsertAttempt.first->second.hasConstraintOfType(
+ negC->getType()));
+ negPos = negInsertAttempt.first;
+ }
+
+ c->initialize(this, pos, negC);
+ negC->initialize(this, negPos, c);
+
+ vc.add(c);
+ negPos->second.add(negC);
+
+ 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();
+ return std::find_if(first, last, PerVariableDatabase::IsEmpty) == last;
+}
+
+ConstraintDatabase::~ConstraintDatabase(){
+ delete d_watches;
+
+ std::vector<ConstraintP> constraintList;
+
+ while(!d_varDatabases.empty()){
+ PerVariableDatabase* back = d_varDatabases.back();
+
+ SortedConstraintMap& scm = back->d_constraints;
+ SortedConstraintMapIterator i = scm.begin(), i_end = scm.end();
+ for(; i != i_end; ++i){
+ (i->second).push_into(constraintList);
+ }
+ while(!constraintList.empty()){
+ ConstraintP c = constraintList.back();
+ constraintList.pop_back();
+ delete c;
+ }
+ Assert(scm.empty());
+ d_varDatabases.pop_back();
+ delete back;
+ }
+
+ Assert(d_nodetoConstraintMap.empty());
+}
+
+ConstraintDatabase::Statistics::Statistics()
+ : d_unatePropagateCalls(smtStatisticsRegistry().registerInt(
+ "theory::arith::cd::unatePropagateCalls")),
+ d_unatePropagateImplications(smtStatisticsRegistry().registerInt(
+ "theory::arith::cd::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<ConstraintP> constraintList;
+
+ for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){
+ (i->second).push_into(constraintList);
+ }
+ while(!constraintList.empty()){
+ ConstraintP c = constraintList.back();
+ constraintList.pop_back();
+ Assert(c->safeToGarbageCollect());
+ delete c;
+ }
+ Assert(scm.empty());
+
+ d_reclaimable.remove(v);
+ }else{
+ Trace("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl;
+ Assert(v == d_varDatabases.size());
+ d_varDatabases.push_back(new PerVariableDatabase(v));
+ }
+}
+
+void ConstraintDatabase::removeVariable(ArithVar v){
+ Assert(!d_reclaimable.isMember(v));
+ d_reclaimable.add(v);
+}
+
+bool Constraint::safeToGarbageCollect() const{
+ // Do not call during destructor as getNegation() may be Null by this point
+ Assert(getNegation() != NullConstraint);
+ return !contextDependentDataIsSet() && ! getNegation()->contextDependentDataIsSet();
+}
+
+bool Constraint::contextDependentDataIsSet() const{
+ return hasProof() || isSplit() || canBePropagated() || assertedToTheTheory();
+}
+
+TrustNode Constraint::split()
+{
+ Assert(isEquality() || isDisequality());
+
+ bool isEq = isEquality();
+
+ ConstraintP eq = isEq ? this : d_negation;
+ ConstraintP diseq = isEq ? d_negation : this;
+
+ TNode eqNode = eq->getLiteral();
+ Assert(eqNode.getKind() == kind::EQUAL);
+ TNode lhs = eqNode[0];
+ TNode rhs = eqNode[1];
+
+ Node leqNode = NodeBuilder(kind::LEQ) << lhs << rhs;
+ Node ltNode = NodeBuilder(kind::LT) << lhs << rhs;
+ Node gtNode = NodeBuilder(kind::GT) << lhs << rhs;
+ Node geqNode = NodeBuilder(kind::GEQ) << lhs << rhs;
+
+ Node lemma = NodeBuilder(OR) << leqNode << geqNode;
+
+ TrustNode trustedLemma;
+ if (d_database->isProofEnabled())
+ {
+ TypeNode type = lhs.getType();
+ // Farkas proof that this works.
+ auto nm = NodeManager::currentNM();
+ auto nLeqPf = d_database->d_pnm->mkAssume(leqNode.negate());
+ auto gtPf = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {nLeqPf}, {gtNode});
+ auto nGeqPf = d_database->d_pnm->mkAssume(geqNode.negate());
+ auto ltPf = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {nGeqPf}, {ltNode});
+ auto sumPf =
+ d_database->d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+ {gtPf, ltPf},
+ {nm->mkConstRealOrInt(type, Rational(-1)),
+ nm->mkConstRealOrInt(type, Rational(1))});
+ auto botPf = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+ std::vector<Node> a = {leqNode.negate(), geqNode.negate()};
+ auto notAndNotPf = d_database->d_pnm->mkScope(botPf, a);
+ // No need to ensure that the expected node aggrees with `a` because we are
+ // not providing an expected node.
+ auto orNotNotPf =
+ d_database->d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
+ auto orPf = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {lemma});
+ trustedLemma = d_database->d_pfGen->mkTrustNode(lemma, orPf);
+ }
+ else
+ {
+ trustedLemma = TrustNode::mkTrustLemma(lemma);
+ }
+
+ eq->d_database->pushSplitWatch(eq);
+ diseq->d_database->pushSplitWatch(diseq);
+
+ return trustedLemma;
+}
+
+bool ConstraintDatabase::hasLiteral(TNode literal) const {
+ return lookup(literal) != NullConstraint;
+}
+
+ConstraintP ConstraintDatabase::addLiteral(TNode literal){
+ Assert(!hasLiteral(literal));
+ bool isNot = (literal.getKind() == NOT);
+ Node atomNode = (isNot ? literal[0] : literal);
+ Node negationNode = atomNode.notNode();
+
+ Assert(!hasLiteral(atomNode));
+ Assert(!hasLiteral(negationNode));
+ Comparison posCmp = Comparison::parseNormalForm(atomNode);
+
+ ConstraintType posType = Constraint::constraintTypeOfComparison(posCmp);
+
+ Polynomial nvp = posCmp.normalizedVariablePart();
+ ArithVar v = d_avariables.asArithVar(nvp.getNode());
+
+ DeltaRational posDR = posCmp.normalizedDeltaRational();
+
+ ConstraintP posC =
+ new Constraint(v, posType, posDR, options().smt.produceProofs);
+
+ Trace("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl;
+ Trace("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl;
+
+ SortedConstraintMap& scm = getVariableSCM(posC->getVariable());
+ pair<SortedConstraintMapIterator, bool> insertAttempt;
+ insertAttempt = scm.insert(make_pair(posC->getValue(), ValueCollection()));
+
+ SortedConstraintMapIterator posI = insertAttempt.first;
+ // If the attempt succeeds, i points to a new empty ValueCollection
+ // If the attempt fails, i points to a pre-existing ValueCollection
+
+ if(posI->second.hasConstraintOfType(posC->getType())){
+ //This is the situation where the ConstraintP exists, but
+ //the literal has not been associated with it.
+ ConstraintP hit = posI->second.getConstraintOfType(posC->getType());
+ Trace("arith::constraint") << "hit " << hit << endl;
+ Trace("arith::constraint") << "posC " << posC << endl;
+
+ delete posC;
+
+ hit->setLiteral(atomNode);
+ hit->getNegation()->setLiteral(negationNode);
+ return isNot ? hit->getNegation(): hit;
+ }else{
+ Comparison negCmp = Comparison::parseNormalForm(negationNode);
+
+ ConstraintType negType = Constraint::constraintTypeOfComparison(negCmp);
+ DeltaRational negDR = negCmp.normalizedDeltaRational();
+
+ ConstraintP negC =
+ new Constraint(v, negType, negDR, options().smt.produceProofs);
+
+ SortedConstraintMapIterator negI;
+
+ if(posC->isEquality()){
+ negI = posI;
+ }else{
+ Assert(posC->isLowerBound() || posC->isUpperBound());
+
+ pair<SortedConstraintMapIterator, bool> negInsertAttempt;
+ negInsertAttempt = scm.insert(make_pair(negC->getValue(), ValueCollection()));
+
+ Trace("nf::tmp") << "sdhjfgdhjkldfgljkhdfg" << endl;
+ Trace("nf::tmp") << negC << endl;
+ Trace("nf::tmp") << negC->getValue() << endl;
+
+ //This should always succeed as the DeltaRational for the negation is unique!
+ Assert(negInsertAttempt.second);
+
+ negI = negInsertAttempt.first;
+ }
+
+ (posI->second).add(posC);
+ (negI->second).add(negC);
+
+ posC->initialize(this, posI, negC);
+ negC->initialize(this, negI, posC);
+
+ posC->setLiteral(atomNode);
+ negC->setLiteral(negationNode);
+
+ return isNot ? negC : posC;
+ }
+}
+
+
+ConstraintP ConstraintDatabase::lookup(TNode literal) const{
+ NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal);
+ if(iter == d_nodetoConstraintMap.end()){
+ return NullConstraint;
+ }else{
+ return iter->second;
+ }
+}
+
+void Constraint::setAssumption(bool nowInConflict){
+ Trace("constraints::pf") << "setAssumption(" << this << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(hasLiteral());
+ Assert(assertedToTheTheory());
+
+ d_database->pushConstraintRule(ConstraintRule(this, AssumeAP));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@setAssumption " << this << std::endl;
+ }
+}
+
+void Constraint::tryToPropagate(){
+ Assert(hasProof());
+ Assert(!isAssumption());
+ Assert(!isInternalAssumption());
+
+ if(canBePropagated() && !assertedToTheTheory() && !isAssumption() && !isInternalAssumption()){
+ propagate();
+ }
+}
+
+void Constraint::propagate(){
+ Assert(hasProof());
+ Assert(canBePropagated());
+ Assert(!assertedToTheTheory());
+ Assert(!isAssumption());
+ Assert(!isInternalAssumption());
+
+ d_database->d_toPropagate.push(this);
+}
+
+
+/*
+ * Example:
+ * x <= a and a < b
+ * |= x <= b
+ * ---
+ * 1*(x <= a) + (-1)*(x > b) => (0 <= a-b)
+ */
+void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByUnate(" << this << ", " << *imp << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(imp->hasProof());
+ Assert(negationHasProof() == nowInConflict);
+
+ d_database->d_antecedents.push_back(NullConstraint);
+ d_database->d_antecedents.push_back(imp);
+
+ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+
+ RationalVectorP coeffs;
+ if (d_produceProofs)
+ {
+ std::pair<int, int> sgns = unateFarkasSigns(getNegation(), imp);
+
+ Rational first(sgns.first);
+ Rational second(sgns.second);
+
+ coeffs = new RationalVector();
+ coeffs->push_back(first);
+ coeffs->push_back(second);
+ }
+ else
+ {
+ coeffs = RationalVectorPSentinel;
+ }
+ // no need to delete coeffs the memory is owned by ConstraintRule
+ d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffs));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@impliedByUnate " << this << std::endl;
+ }
+
+ if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
+ getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
+ }
+ Assert(wellFormedFarkasProof());
+}
+
+void Constraint::impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByTrichotomy(" << this << ", " << *a << ", ";
+ Trace("constraints::pf") << *b << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(a->hasProof());
+ Assert(b->hasProof());
+
+ d_database->d_antecedents.push_back(NullConstraint);
+ d_database->d_antecedents.push_back(a);
+ d_database->d_antecedents.push_back(b);
+
+ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+ d_database->pushConstraintRule(ConstraintRule(this, TrichotomyAP, antecedentEnd));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@impliedByTrichotomy " << this << std::endl;
+ }
+}
+
+
+bool Constraint::allHaveProof(const ConstraintCPVec& b){
+ for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
+ ConstraintCP cp = *i;
+ if(! (cp->hasProof())){ return false; }
+ }
+ return true;
+}
+
+void Constraint::impliedByIntTighten(ConstraintCP a, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByIntTighten(" << this << ", " << *a << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(a->hasProof());
+ Trace("pf::arith") << "impliedByIntTighten(" << this << ", " << a << ")"
+ << std::endl;
+
+ d_database->d_antecedents.push_back(NullConstraint);
+ d_database->d_antecedents.push_back(a);
+ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+ d_database->pushConstraintRule(ConstraintRule(this, IntTightenAP, antecedentEnd));
+
+ Assert(inConflict() == nowInConflict);
+ if(inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict impliedByIntTighten" << this << std::endl;
+ }
+}
+
+void Constraint::impliedByIntHole(ConstraintCP a, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByIntHole(" << this << ", " << *a << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(a->hasProof());
+ Trace("pf::arith") << "impliedByIntHole(" << this << ", " << a << ")"
+ << std::endl;
+
+ d_database->d_antecedents.push_back(NullConstraint);
+ d_database->d_antecedents.push_back(a);
+ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+ d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict impliedByIntHole" << this << std::endl;
+ }
+}
+
+void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByIntHole(" << this;
+ if (TraceIsOn("constraints::pf")) {
+ for (const ConstraintCP& p : b)
+ {
+ Trace("constraints::pf") << ", " << p;
+ }
+ }
+ Trace("constraints::pf") << ")" << std::endl;
+
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(allHaveProof(b));
+
+ CDConstraintList& antecedents = d_database->d_antecedents;
+ antecedents.push_back(NullConstraint);
+ for(ConstraintCPVec::const_iterator i=b.begin(), i_end=b.end(); i != i_end; ++i){
+ antecedents.push_back(*i);
+ }
+ AntecedentId antecedentEnd = antecedents.size() - 1;
+
+ d_database->pushConstraintRule(ConstraintRule(this, IntHoleAP, antecedentEnd));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@impliedByIntHole[vec] " << this << std::endl;
+ }
+}
+
+/*
+ * If proofs are off, coeffs == RationalVectorSentinal.
+ * If proofs are on,
+ * coeffs != RationalVectorSentinal,
+ * coeffs->size() = a.size() + 1,
+ * for i in [0,a.size) : coeff[i] corresponds to a[i], and
+ * coeff.back() corresponds to the current constraint.
+ */
+void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){
+ Trace("constraints::pf") << "impliedByFarkas(" << this;
+ if (TraceIsOn("constraints::pf")) {
+ for (const ConstraintCP& p : a)
+ {
+ Trace("constraints::pf") << ", " << p;
+ }
+ }
+ Trace("constraints::pf") << ", <coeffs>";
+ Trace("constraints::pf") << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(allHaveProof(a));
+
+ Assert(d_produceProofs == (coeffs != RationalVectorCPSentinel));
+ Assert(!d_produceProofs || coeffs->size() == a.size() + 1);
+
+ Assert(a.size() >= 1);
+
+ d_database->d_antecedents.push_back(NullConstraint);
+ for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){
+ ConstraintCP c_i = *i;
+ Assert(c_i->hasProof());
+ d_database->d_antecedents.push_back(c_i);
+ }
+ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1;
+
+ RationalVectorCP coeffsCopy;
+ if (d_produceProofs)
+ {
+ Assert(coeffs != RationalVectorCPSentinel);
+ coeffsCopy = new RationalVector(*coeffs);
+ }
+ else
+ {
+ coeffsCopy = RationalVectorCPSentinel;
+ }
+ d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffsCopy));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@impliedByFarkas " << this << std::endl;
+ }
+ if(TraceIsOn("constraints::wffp") && !wellFormedFarkasProof()){
+ getConstraintRule().print(Trace("constraints::wffp"), d_produceProofs);
+ }
+ Assert(wellFormedFarkasProof());
+}
+
+
+void Constraint::setInternalAssumption(bool nowInConflict){
+ Trace("constraints::pf") << "setInternalAssumption(" << this;
+ Trace("constraints::pf") << ")" << std::endl;
+ Assert(!hasProof());
+ Assert(negationHasProof() == nowInConflict);
+ Assert(!assertedToTheTheory());
+
+ d_database->pushConstraintRule(ConstraintRule(this, InternalAssumeAP));
+
+ Assert(inConflict() == nowInConflict);
+ if(TraceIsOn("constraint::conflictCommit") && inConflict()){
+ Trace("constraint::conflictCommit") << "inConflict@setInternalAssumption " << this << std::endl;
+ }
+}
+
+
+void Constraint::setEqualityEngineProof(){
+ Trace("constraints::pf") << "setEqualityEngineProof(" << this;
+ Trace("constraints::pf") << ")" << std::endl;
+ Assert(truthIsUnknown());
+ Assert(hasLiteral());
+ d_database->pushConstraintRule(ConstraintRule(this, EqualityEngineAP));
+}
+
+
+SortedConstraintMap& Constraint::constraintSet() const{
+ Assert(d_database->variableDatabaseIsSetup(d_variable));
+ return (d_database->d_varDatabases[d_variable])->d_constraints;
+}
+
+bool Constraint::antecentListIsEmpty() const{
+ Assert(hasProof());
+ return d_database->d_antecedents[getEndAntecedent()] == NullConstraint;
+}
+
+bool Constraint::antecedentListLengthIsOne() const {
+ Assert(hasProof());
+ return !antecentListIsEmpty() &&
+ d_database->d_antecedents[getEndAntecedent()-1] == NullConstraint;
+}
+
+Node Constraint::externalImplication(const ConstraintCPVec& b) const{
+ Assert(hasLiteral());
+ Node antecedent = externalExplainByAssertions(b);
+ Node implied = getLiteral();
+ return antecedent.impNode(implied);
+}
+
+
+Node Constraint::externalExplainByAssertions(const ConstraintCPVec& b){
+ return externalExplain(b, AssertionOrderSentinel);
+}
+
+TrustNode Constraint::externalExplainForPropagation(TNode lit) const
+{
+ Assert(hasProof());
+ Assert(!isAssumption());
+ Assert(!isInternalAssumption());
+ NodeBuilder nb(Kind::AND);
+ auto pfFromAssumptions = externalExplain(nb, d_assertionOrder);
+ Node n = mkAndFromBuilder(nb);
+ if (d_database->isProofEnabled())
+ {
+ std::vector<Node> assumptions;
+ if (n.getKind() == Kind::AND)
+ {
+ assumptions.insert(assumptions.end(), n.begin(), n.end());
+ }
+ else
+ {
+ assumptions.push_back(n);
+ }
+ if (getProofLiteral() != lit)
+ {
+ pfFromAssumptions = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {lit});
+ }
+ auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
+ return d_database->d_pfGen->mkTrustedPropagation(
+ lit, NodeManager::currentNM()->mkAnd(assumptions), pf);
+ }
+ else
+ {
+ return TrustNode::mkTrustPropExp(lit, n);
+ }
+}
+
+TrustNode Constraint::externalExplainConflict() const
+{
+ Trace("pf::arith::explain") << this << std::endl;
+ Assert(inConflict());
+ NodeBuilder nb(kind::AND);
+ auto pf1 = externalExplainByAssertions(nb);
+ auto not2 = getNegation()->getProofLiteral().negate();
+ auto pf2 = getNegation()->externalExplainByAssertions(nb);
+ Node n = mkAndFromBuilder(nb);
+ if (d_database->isProofEnabled())
+ {
+ auto pfNot2 = d_database->d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pf1}, {not2});
+ std::vector<Node> lits;
+ if (n.getKind() == Kind::AND)
+ {
+ lits.insert(lits.end(), n.begin(), n.end());
+ }
+ else
+ {
+ lits.push_back(n);
+ }
+ if (TraceIsOn("arith::pf::externalExplainConflict"))
+ {
+ Trace("arith::pf::externalExplainConflict") << "Lits:" << std::endl;
+ for (const auto& l : lits)
+ {
+ Trace("arith::pf::externalExplainConflict") << " : " << l << std::endl;
+ }
+ }
+ std::vector<Node> contraLits = {getProofLiteral(),
+ getNegation()->getProofLiteral()};
+ auto bot =
+ not2.getKind() == Kind::NOT
+ ? d_database->d_pnm->mkNode(PfRule::CONTRA, {pf2, pfNot2}, {})
+ : d_database->d_pnm->mkNode(PfRule::CONTRA, {pfNot2, pf2}, {});
+ if (TraceIsOn("arith::pf::tree"))
+ {
+ Trace("arith::pf::tree") << *this << std::endl;
+ Trace("arith::pf::tree") << *getNegation() << std::endl;
+ Trace("arith::pf::tree") << "\n\nTree:\n";
+ printProofTree(Trace("arith::pf::tree"));
+ getNegation()->printProofTree(Trace("arith::pf::tree"));
+ }
+ auto confPf = d_database->d_pnm->mkScope(bot, lits);
+ return d_database->d_pfGen->mkTrustNode(
+ NodeManager::currentNM()->mkAnd(lits), confPf, true);
+ }
+ else
+ {
+ return TrustNode::mkTrustConflict(n);
+ }
+}
+
+struct ConstraintCPHash {
+ /* Todo replace with an id */
+ size_t operator()(ConstraintCP c) const{
+ Assert(sizeof(ConstraintCP) > 0);
+ return ((size_t)c)/sizeof(ConstraintCP);
+ }
+};
+
+void Constraint::assertionFringe(ConstraintCPVec& v){
+ unordered_set<ConstraintCP, ConstraintCPHash> visited;
+ size_t writePos = 0;
+
+ if(!v.empty()){
+ const ConstraintDatabase* db = v.back()->d_database;
+ const CDConstraintList& antecedents = db->d_antecedents;
+ 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->hasTrichotomyProof() || vi->hasFarkasProof()
+ || vi->hasIntHoleProof() || vi->hasIntTightenProof());
+ AntecedentId p = vi->getEndAntecedent();
+
+ ConstraintCP antecedent = antecedents[p];
+ while(antecedent != NullConstraint){
+ v.push_back(antecedent);
+ --p;
+ antecedent = antecedents[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 mkAndFromBuilder(nb);
+}
+
+std::shared_ptr<ProofNode> Constraint::externalExplain(
+ NodeBuilder& nb, AssertionOrder order) const
+{
+ if (TraceIsOn("pf::arith::explain"))
+ {
+ this->printProofTree(Trace("arith::pf::tree"));
+ Trace("pf::arith::explain") << "Explaining: " << this << " with rule ";
+ getConstraintRule().print(Trace("pf::arith::explain"), d_produceProofs);
+ Trace("pf::arith::explain") << std::endl;
+ }
+ Assert(hasProof());
+ Assert(!isAssumption() || assertedToTheTheory());
+ Assert(!isInternalAssumption());
+ std::shared_ptr<ProofNode> pf{};
+
+ ProofNodeManager* pnm = d_database->d_pnm;
+
+ if (assertedBefore(order))
+ {
+ Trace("pf::arith::explain") << " already asserted" << std::endl;
+ nb << getWitness();
+ if (d_database->isProofEnabled())
+ {
+ pf = pnm->mkAssume(getWitness());
+ // If the witness and literal differ, prove the difference through a
+ // rewrite.
+ if (getWitness() != getProofLiteral())
+ {
+ pf = pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {getProofLiteral()});
+ }
+ }
+ }
+ else if (hasEqualityEngineProof())
+ {
+ Trace("pf::arith::explain") << " going to ee:" << std::endl;
+ TrustNode exp = d_database->eeExplain(this);
+ if (d_database->isProofEnabled())
+ {
+ Assert(exp.getProven().getKind() == Kind::IMPLIES);
+ std::vector<std::shared_ptr<ProofNode>> hypotheses;
+ hypotheses.push_back(exp.getGenerator()->getProofFor(exp.getProven()));
+ if (exp.getNode().getKind() == Kind::AND)
+ {
+ for (const auto& h : exp.getNode())
+ {
+ hypotheses.push_back(
+ pnm->mkNode(PfRule::TRUE_INTRO, {pnm->mkAssume(h)}, {}));
+ }
+ }
+ else
+ {
+ hypotheses.push_back(pnm->mkNode(
+ PfRule::TRUE_INTRO, {pnm->mkAssume(exp.getNode())}, {}));
+ }
+ pf = pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {hypotheses}, {getProofLiteral()});
+ }
+ Trace("pf::arith::explain")
+ << " explanation: " << exp.getNode() << std::endl;
+ if (exp.getNode().getKind() == Kind::AND)
+ {
+ nb.append(exp.getNode().begin(), exp.getNode().end());
+ }
+ else
+ {
+ nb << exp.getNode();
+ }
+ }
+ else
+ {
+ Trace("pf::arith::explain") << " recursion!" << std::endl;
+ Assert(!isAssumption());
+ AntecedentId p = getEndAntecedent();
+ ConstraintCP antecedent = d_database->d_antecedents[p];
+ std::vector<std::shared_ptr<ProofNode>> children;
+
+ while (antecedent != NullConstraint)
+ {
+ Trace("pf::arith::explain") << "Explain " << antecedent << std::endl;
+ auto pn = antecedent->externalExplain(nb, order);
+ if (d_database->isProofEnabled())
+ {
+ children.push_back(pn);
+ }
+ --p;
+ antecedent = d_database->d_antecedents[p];
+ }
+
+ if (d_database->isProofEnabled())
+ {
+ switch (getProofType())
+ {
+ case ArithProofType::AssumeAP:
+ case ArithProofType::EqualityEngineAP:
+ {
+ Unreachable() << "These should be handled above";
+ break;
+ }
+ case ArithProofType::FarkasAP:
+ {
+ // Per docs in constraint.h,
+ // the 0th farkas coefficient is for the negation of the deduced
+ // constraint the 1st corresponds to the last antecedent the nth
+ // corresponds to the first antecedent Then, the farkas coefficients
+ // and the antecedents are in the same order.
+
+ // Enumerate child proofs (negation included) in d_farkasCoefficients
+ // order
+ Node plit = getNegation()->getProofLiteral();
+ std::vector<std::shared_ptr<ProofNode>> farkasChildren;
+ farkasChildren.push_back(pnm->mkAssume(plit));
+ farkasChildren.insert(
+ farkasChildren.end(), children.rbegin(), children.rend());
+
+ NodeManager* nm = NodeManager::currentNM();
+
+ // Enumerate d_farkasCoefficients as nodes.
+ std::vector<Node> farkasCoeffs;
+ TypeNode type = plit[0].getType();
+ for (Rational r : *getFarkasCoefficients())
+ {
+ farkasCoeffs.push_back(nm->mkConstRealOrInt(type, Rational(r)));
+ }
+
+ // Apply the scaled-sum rule.
+ std::shared_ptr<ProofNode> sumPf = pnm->mkNode(
+ PfRule::MACRO_ARITH_SCALE_SUM_UB, farkasChildren, farkasCoeffs);
+
+ // Provable rewrite the result
+ auto botPf = pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+
+ // Scope out the negated constraint, yielding a proof of the
+ // constraint.
+ std::vector<Node> assump{plit};
+ auto maybeDoubleNotPf = pnm->mkScope(botPf, assump, false);
+
+ // No need to ensure that the expected node aggrees with `assump`
+ // because we are not providing an expected node.
+ //
+ // Prove that this is the literal (may need to clean a double-not)
+ pf = pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+ {maybeDoubleNotPf},
+ {getProofLiteral()});
+
+ break;
+ }
+ case ArithProofType::IntTightenAP:
+ {
+ if (isUpperBound())
+ {
+ pf = pnm->mkNode(
+ PfRule::INT_TIGHT_UB, children, {}, getProofLiteral());
+ }
+ else if (isLowerBound())
+ {
+ pf = pnm->mkNode(
+ PfRule::INT_TIGHT_LB, children, {}, getProofLiteral());
+ }
+ else
+ {
+ Unreachable();
+ }
+ break;
+ }
+ case ArithProofType::IntHoleAP:
+ {
+ Node t =
+ builtin::BuiltinProofRuleChecker::mkTheoryIdNode(THEORY_ARITH);
+ pf = pnm->mkNode(PfRule::THEORY_INFERENCE,
+ children,
+ {getProofLiteral(), t},
+ getProofLiteral());
+ break;
+ }
+ case ArithProofType::TrichotomyAP:
+ {
+ pf = pnm->mkNode(PfRule::ARITH_TRICHOTOMY,
+ children,
+ {getProofLiteral()},
+ getProofLiteral());
+ break;
+ }
+ case ArithProofType::InternalAssumeAP:
+ case ArithProofType::NoAP:
+ default:
+ {
+ Unreachable() << getProofType()
+ << " should not be visible in explanation";
+ break;
+ }
+ }
+ }
+ }
+ return pf;
+}
+
+Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){
+ NodeBuilder nb(kind::AND);
+ a->externalExplainByAssertions(nb);
+ b->externalExplainByAssertions(nb);
+ return nb;
+}
+
+Node Constraint::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){
+ NodeBuilder nb(kind::AND);
+ a->externalExplainByAssertions(nb);
+ b->externalExplainByAssertions(nb);
+ c->externalExplainByAssertions(nb);
+ return nb;
+}
+
+ConstraintP Constraint::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const {
+ Assert(initialized());
+ Assert(!asserted || hasLiteral);
+
+ SortedConstraintMapConstIterator i = d_variablePosition;
+ const SortedConstraintMap& scm = constraintSet();
+ SortedConstraintMapConstIterator i_begin = scm.begin();
+ while(i != i_begin){
+ --i;
+ const ValueCollection& vc = i->second;
+ if(vc.hasLowerBound()){
+ ConstraintP weaker = vc.getLowerBound();
+
+ // asserted -> hasLiteral
+ // hasLiteral -> weaker->hasLiteral()
+ // asserted -> weaker->assertedToTheTheory()
+ if((!hasLiteral || (weaker->hasLiteral())) &&
+ (!asserted || ( weaker->assertedToTheTheory()))){
+ return weaker;
+ }
+ }
+ }
+ return NullConstraint;
+}
+
+ConstraintP Constraint::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const {
+ SortedConstraintMapConstIterator i = d_variablePosition;
+ const SortedConstraintMap& scm = constraintSet();
+ SortedConstraintMapConstIterator i_end = scm.end();
+
+ ++i;
+ for(; i != i_end; ++i){
+ const ValueCollection& vc = i->second;
+ if(vc.hasUpperBound()){
+ ConstraintP weaker = vc.getUpperBound();
+ if((!hasLiteral || (weaker->hasLiteral())) &&
+ (!asserted || ( weaker->assertedToTheTheory()))){
+ return weaker;
+ }
+ }
+ }
+
+ return NullConstraint;
+}
+
+ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const {
+ Assert(variableDatabaseIsSetup(v));
+ Assert(t == UpperBound || t == LowerBound);
+
+ SortedConstraintMap& scm = getVariableSCM(v);
+ if(t == UpperBound){
+ SortedConstraintMapConstIterator i = scm.lower_bound(r);
+ SortedConstraintMapConstIterator i_end = scm.end();
+ Assert(i == i_end || r <= i->first);
+ for(; i != i_end; i++){
+ Assert(r <= i->first);
+ const ValueCollection& vc = i->second;
+ if(vc.hasUpperBound()){
+ return vc.getUpperBound();
+ }
+ }
+ return NullConstraint;
+ }else{
+ Assert(t == LowerBound);
+ if(scm.empty()){
+ return NullConstraint;
+ }else{
+ SortedConstraintMapConstIterator i = scm.lower_bound(r);
+ SortedConstraintMapConstIterator i_begin = scm.begin();
+ SortedConstraintMapConstIterator i_end = scm.end();
+ Assert(i == i_end || r <= i->first);
+
+ int fdj = 0;
+
+ if(i == i_end){
+ --i;
+ Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+ }else if( (i->first) > r){
+ if(i == i_begin){
+ return NullConstraint;
+ }else{
+ --i;
+ Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+ }
+ }
+
+ do{
+ Trace("getBestImpliedBound") << fdj++ << " " << r << " " << i->first << endl;
+ Assert(r >= i->first);
+ const ValueCollection& vc = i->second;
+
+ if(vc.hasLowerBound()){
+ return vc.getLowerBound();
+ }
+
+ if(i == i_begin){
+ break;
+ }else{
+ --i;
+ }
+ }while(true);
+ return NullConstraint;
+ }
+ }
+}
+TrustNode ConstraintDatabase::eeExplain(const Constraint* const c) const
+{
+ Assert(c->hasLiteral());
+ return d_congruenceManager.explain(c->getLiteral());
+}
+
+void ConstraintDatabase::eeExplain(ConstraintCP c, NodeBuilder& nb) const
+{
+ Assert(c->hasLiteral());
+ // NOTE: this is not a recommended method since it ignores proofs
+ d_congruenceManager.explain(c->getLiteral(), nb);
+}
+
+bool ConstraintDatabase::variableDatabaseIsSetup(ArithVar v) const {
+ return v < d_varDatabases.size();
+}
+
+ConstraintDatabase::Watches::Watches(context::Context* satContext,
+ context::Context* userContext)
+ : d_constraintProofs(satContext),
+ d_canBePropagatedWatches(satContext),
+ d_assertionOrderWatches(satContext),
+ d_splitWatches(userContext)
+{}
+
+
+void Constraint::setLiteral(Node n) {
+ Trace("arith::constraint") << "Mapping " << *this << " to " << n << std::endl;
+ Assert(Comparison::isNormalAtom(n));
+ Assert(!hasLiteral());
+ Assert(sanityChecking(n));
+ d_literal = n;
+ NodetoConstraintMap& map = d_database->d_nodetoConstraintMap;
+ Assert(map.find(n) == map.end());
+ map.insert(make_pair(d_literal, this));
+}
+
+Node Constraint::getProofLiteral() const
+{
+ Assert(d_database != nullptr);
+ Assert(d_database->d_avariables.hasNode(d_variable));
+ Node varPart = d_database->d_avariables.asNode(d_variable);
+ Kind cmp;
+ bool neg = false;
+ switch (d_type)
+ {
+ case ConstraintType::UpperBound:
+ {
+ if (d_value.infinitesimalIsZero())
+ {
+ cmp = Kind::LEQ;
+ }
+ else
+ {
+ cmp = Kind::LT;
+ }
+ break;
+ }
+ case ConstraintType::LowerBound:
+ {
+ if (d_value.infinitesimalIsZero())
+ {
+ cmp = Kind::GEQ;
+ }
+ else
+ {
+ cmp = Kind::GT;
+ }
+ break;
+ }
+ case ConstraintType::Equality:
+ {
+ cmp = Kind::EQUAL;
+ break;
+ }
+ case ConstraintType::Disequality:
+ {
+ cmp = Kind::EQUAL;
+ neg = true;
+ break;
+ }
+ default: Unreachable() << d_type;
+ }
+ NodeManager* nm = NodeManager::currentNM();
+ Node constPart = nm->mkConstRealOrInt(
+ varPart.getType(), Rational(d_value.getNoninfinitesimalPart()));
+ Node posLit = nm->mkNode(cmp, varPart, constPart);
+ return neg ? posLit.negate() : posLit;
+}
+
+void ConstraintDatabase::proveOr(std::vector<TrustNode>& out,
+ ConstraintP a,
+ ConstraintP b,
+ bool negateSecond) const
+{
+ Node la = a->getLiteral();
+ Node lb = b->getLiteral();
+ Node orN = (la < lb) ? la.orNode(lb) : lb.orNode(la);
+ if (isProofEnabled())
+ {
+ Assert(b->getNegation()->getType() != ConstraintType::Disequality);
+ auto nm = NodeManager::currentNM();
+ Node alit = a->getNegation()->getProofLiteral();
+ TypeNode type = alit[0].getType();
+ auto pf_neg_la = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkAssume(la.negate())},
+ {alit});
+ Node blit = b->getNegation()->getProofLiteral();
+ auto pf_neg_lb = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkAssume(lb.negate())},
+ {blit});
+ int sndSign = negateSecond ? -1 : 1;
+ auto bot_pf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB,
+ {pf_neg_la, pf_neg_lb},
+ {nm->mkConstRealOrInt(type, Rational(-1 * sndSign)),
+ nm->mkConstRealOrInt(type, Rational(sndSign))})},
+ {nm->mkConst(false)});
+ std::vector<Node> as;
+ std::transform(orN.begin(), orN.end(), std::back_inserter(as), [](Node n) {
+ return n.negate();
+ });
+ // No need to ensure that the expected node aggrees with `as` because we
+ // are not providing an expected node.
+ auto pf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkNode(PfRule::NOT_AND, {d_pnm->mkScope(bot_pf, as)}, {})},
+ {orN});
+ out.push_back(d_pfGen->mkTrustNode(orN, pf));
+ }
+ else
+ {
+ out.push_back(TrustNode::mkTrustLemma(orN));
+ }
+}
+
+void ConstraintDatabase::implies(std::vector<TrustNode>& out,
+ ConstraintP a,
+ ConstraintP b) const
+{
+ Node la = a->getLiteral();
+ Node lb = b->getLiteral();
+
+ Node neg_la = (la.getKind() == kind::NOT)? la[0] : la.notNode();
+
+ Assert(lb != neg_la);
+ Assert(b->getNegation()->getType() == ConstraintType::LowerBound
+ || b->getNegation()->getType() == ConstraintType::UpperBound);
+ proveOr(out,
+ a->getNegation(),
+ b,
+ b->getNegation()->getType() == ConstraintType::LowerBound);
+}
+
+void ConstraintDatabase::mutuallyExclusive(std::vector<TrustNode>& out,
+ ConstraintP a,
+ ConstraintP b) const
+{
+ Node la = a->getLiteral();
+ Node lb = b->getLiteral();
+
+ Node neg_la = la.negate();
+ Node neg_lb = lb.negate();
+ proveOr(out, a->getNegation(), b->getNegation(), true);
+}
+
+void ConstraintDatabase::outputUnateInequalityLemmas(
+ std::vector<TrustNode>& out, ArithVar v) const
+{
+ SortedConstraintMap& scm = getVariableSCM(v);
+ SortedConstraintMapConstIterator scm_iter = scm.begin();
+ SortedConstraintMapConstIterator scm_end = scm.end();
+ 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()){
+ ConstraintP ub = vc.getUpperBound();
+ if(ub->hasLiteral()){
+ if(prev != NullConstraint){
+ implies(out, prev, ub);
+ }
+ prev = ub;
+ }
+ }
+ }
+}
+
+void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<TrustNode>& out,
+ ArithVar v) const
+{
+ vector<ConstraintP> equalities;
+
+ SortedConstraintMap& scm = getVariableSCM(v);
+ SortedConstraintMapConstIterator scm_iter = scm.begin();
+ SortedConstraintMapConstIterator scm_end = scm.end();
+
+ for(; scm_iter != scm_end; ++scm_iter){
+ const ValueCollection& vc = scm_iter->second;
+ if(vc.hasEquality()){
+ ConstraintP eq = vc.getEquality();
+ if(eq->hasLiteral()){
+ equalities.push_back(eq);
+ }
+ }
+ }
+
+ vector<ConstraintP>::const_iterator i, j, eq_end = equalities.end();
+ for(i = equalities.begin(); i != eq_end; ++i){
+ ConstraintP at_i = *i;
+ for(j= i + 1; j != eq_end; ++j){
+ ConstraintP at_j = *j;
+
+ mutuallyExclusive(out, at_i, at_j);
+ }
+ }
+
+ for(i = equalities.begin(); i != eq_end; ++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();
+
+ ConstraintP lb = hasLB ?
+ vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false);
+ ConstraintP ub = hasUB ?
+ vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false);
+
+ if(hasUB && hasLB && !eq->isSplit()){
+ out.push_back(eq->split());
+ }
+ if(lb != NullConstraint){
+ implies(out, eq, lb);
+ }
+ if(ub != NullConstraint){
+ implies(out, eq, ub);
+ }
+ }
+}
+
+void ConstraintDatabase::outputUnateEqualityLemmas(
+ std::vector<TrustNode>& lemmas) const
+{
+ for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
+ outputUnateEqualityLemmas(lemmas, v);
+ }
+}
+
+void ConstraintDatabase::outputUnateInequalityLemmas(
+ std::vector<TrustNode>& lemmas) const
+{
+ for(ArithVar v = 0, N = d_varDatabases.size(); v < N; ++v){
+ outputUnateInequalityLemmas(lemmas, v);
+ }
+}
+
+bool ConstraintDatabase::handleUnateProp(ConstraintP ant, ConstraintP cons){
+ if(cons->negationHasProof()){
+ Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
+ cons->impliedByUnate(ant, true);
+ d_raiseConflict.raiseConflict(cons, InferenceId::ARITH_CONF_UNATE_PROP);
+ return true;
+ }else if(!cons->isTrue()){
+ ++d_statistics.d_unatePropagateImplications;
+ Trace("arith::unate") << "handleUnate: " << ant << " implies " << cons << endl;
+ cons->impliedByUnate(ant, false);
+ cons->tryToPropagate();
+ return false;
+ } else {
+ return false;
+ }
+}
+
+void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){
+ Trace("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl;
+ Assert(curr != prev);
+ Assert(curr != NullConstraint);
+ bool hasPrev = ! (prev == NullConstraint);
+ Assert(!hasPrev || curr->getValue() > prev->getValue());
+
+ ++d_statistics.d_unatePropagateCalls;
+
+ const SortedConstraintMap& scm = curr->constraintSet();
+ const SortedConstraintMapConstIterator scm_begin = scm.begin();
+ SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
+
+ //Ignore the first ValueCollection
+ // NOPE: (>= p c) then (= p c) NOPE
+ // NOPE: (>= p c) then (not (= p c)) NOPE
+
+ while(scm_i != scm_begin){
+ --scm_i; // move the iterator back
+
+ const ValueCollection& vc = scm_i->second;
+
+ //If it has the previous element, do nothing and stop!
+ if(hasPrev &&
+ vc.hasConstraintOfType(prev->getType())
+ && vc.getConstraintOfType(prev->getType()) == prev){
+ break;
+ }
+
+ //Don't worry about implying the negation of upperbound.
+ //These should all be handled by propagating the LowerBounds!
+ if(vc.hasLowerBound()){
+ ConstraintP lb = vc.getLowerBound();
+ if(handleUnateProp(curr, lb)){ return; }
+ }
+ if(vc.hasDisequality()){
+ ConstraintP dis = vc.getDisequality();
+ if(handleUnateProp(curr, dis)){ return; }
+ }
+ }
+}
+
+void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){
+ Trace("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl;
+ Assert(curr != prev);
+ Assert(curr != NullConstraint);
+ bool hasPrev = ! (prev == NullConstraint);
+ Assert(!hasPrev || curr->getValue() < prev->getValue());
+
+ ++d_statistics.d_unatePropagateCalls;
+
+ const SortedConstraintMap& scm = curr->constraintSet();
+ const SortedConstraintMapConstIterator scm_end = scm.end();
+ SortedConstraintMapConstIterator scm_i = curr->d_variablePosition;
+ ++scm_i;
+ for(; scm_i != scm_end; ++scm_i){
+ const ValueCollection& vc = scm_i->second;
+
+ //If it has the previous element, do nothing and stop!
+ if(hasPrev &&
+ vc.hasConstraintOfType(prev->getType()) &&
+ vc.getConstraintOfType(prev->getType()) == prev){
+ break;
+ }
+ //Don't worry about implying the negation of upperbound.
+ //These should all be handled by propagating the UpperBounds!
+ if(vc.hasUpperBound()){
+ ConstraintP ub = vc.getUpperBound();
+ if(handleUnateProp(curr, ub)){ return; }
+ }
+ if(vc.hasDisequality()){
+ ConstraintP dis = vc.getDisequality();
+ if(handleUnateProp(curr, dis)){ return; }
+ }
+ }
+}
+
+void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){
+ Trace("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl;
+ Assert(curr != prevLB);
+ Assert(curr != prevUB);
+ Assert(curr != NullConstraint);
+ bool hasPrevLB = ! (prevLB == NullConstraint);
+ bool hasPrevUB = ! (prevUB == NullConstraint);
+ Assert(!hasPrevLB || curr->getValue() >= prevLB->getValue());
+ Assert(!hasPrevUB || curr->getValue() <= prevUB->getValue());
+
+ ++d_statistics.d_unatePropagateCalls;
+
+ const SortedConstraintMap& scm = curr->constraintSet();
+ SortedConstraintMapConstIterator scm_curr = curr->d_variablePosition;
+ SortedConstraintMapConstIterator scm_last = hasPrevUB ? prevUB->d_variablePosition : scm.end();
+ SortedConstraintMapConstIterator scm_i;
+ if(hasPrevLB){
+ scm_i = prevLB->d_variablePosition;
+ if(scm_i != scm_curr){ // If this does not move this past scm_curr, move it one forward
+ ++scm_i;
+ }
+ }else{
+ scm_i = scm.begin();
+ }
+
+ for(; scm_i != scm_curr; ++scm_i){
+ // between the previous LB and the curr
+ const ValueCollection& vc = scm_i->second;
+
+ //Don't worry about implying the negation of upperbound.
+ //These should all be handled by propagating the LowerBounds!
+ if(vc.hasLowerBound()){
+ ConstraintP lb = vc.getLowerBound();
+ if(handleUnateProp(curr, lb)){ return; }
+ }
+ if(vc.hasDisequality()){
+ ConstraintP dis = vc.getDisequality();
+ if(handleUnateProp(curr, dis)){ return; }
+ }
+ }
+ Assert(scm_i == scm_curr);
+ if(!hasPrevUB || scm_i != scm_last){
+ ++scm_i;
+ } // hasPrevUB implies scm_i != scm_last
+
+ for(; scm_i != scm_last; ++scm_i){
+ // between the curr and the previous UB imply the upperbounds and disequalities.
+ const ValueCollection& vc = scm_i->second;
+
+ //Don't worry about implying the negation of upperbound.
+ //These should all be handled by propagating the UpperBounds!
+ if(vc.hasUpperBound()){
+ ConstraintP ub = vc.getUpperBound();
+ if(handleUnateProp(curr, ub)){ return; }
+ }
+ if(vc.hasDisequality()){
+ ConstraintP dis = vc.getDisequality();
+ if(handleUnateProp(curr, dis)){ return; }
+ }
+ }
+}
+
+std::pair<int, int> Constraint::unateFarkasSigns(ConstraintCP ca, ConstraintCP cb){
+ ConstraintType a = ca->getType();
+ ConstraintType b = cb->getType();
+
+ Assert(a != Disequality);
+ Assert(b != Disequality);
+
+ int a_sgn = (a == LowerBound) ? -1 : ((a == UpperBound) ? 1 : 0);
+ int b_sgn = (b == LowerBound) ? -1 : ((b == UpperBound) ? 1 : 0);
+
+ if(a_sgn == 0 && b_sgn == 0){
+ Assert(a == Equality);
+ Assert(b == Equality);
+ Assert(ca->getValue() != cb->getValue());
+ if(ca->getValue() < cb->getValue()){
+ a_sgn = 1;
+ b_sgn = -1;
+ }else{
+ a_sgn = -1;
+ b_sgn = 1;
+ }
+ }else if(a_sgn == 0){
+ Assert(b_sgn != 0);
+ Assert(a == Equality);
+ a_sgn = -b_sgn;
+ }else if(b_sgn == 0){
+ Assert(a_sgn != 0);
+ Assert(b == Equality);
+ b_sgn = -a_sgn;
+ }
+ Assert(a_sgn != 0);
+ Assert(b_sgn != 0);
+
+ Trace("arith::unateFarkasSigns") << "Constraint::unateFarkasSigns("<<a <<", " << b << ") -> "
+ << "("<<a_sgn<<", "<< b_sgn <<")"<< endl;
+ return make_pair(a_sgn, b_sgn);
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Alex Ozdemir, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Defines Constraint and ConstraintDatabase which is the internal
+ * representation of variables in arithmetic
+ *
+ * This file defines Constraint and ConstraintDatabase.
+ * A Constraint is the internal representation of literals in TheoryArithmetic.
+ * Constraints are fundamentally a triple:
+ * - ArithVar associated with the constraint,
+ * - a DeltaRational value,
+ * - and a ConstraintType.
+ *
+ * Literals:
+ * The constraint may also keep track of a node corresponding to the
+ * Constraint.
+ * This can be accessed by getLiteral() in O(1) if it has been set.
+ * This node must be in normal form and may be used for communication with
+ * the TheoryEngine.
+ *
+ * In addition, Constraints keep track of the following:
+ * - A Constraint that is the negation of the Constraint.
+ * - An iterator into a set of Constraints for the ArithVar sorted by
+ * DeltaRational value.
+ * - A context dependent internal proof of the node that can be used for
+ * explanations.
+ * - Whether an equality/disequality has been split in the user context via a
+ * lemma.
+ * - Whether a constraint, be be used in explanations sent to the context
+ *
+ * Looking up constraints:
+ * - All of the Constraints with associated nodes in the ConstraintDatabase
+ * can be accessed via a single hashtable lookup until the Constraint is
+ * removed.
+ * - Nodes that have not been associated to a constraints can be
+ * inserted/associated to existing nodes in O(log n) time.
+ *
+ * Implications:
+ * - A Constraint can be used to find unate implications.
+ * - A unate implication is an implication based purely on the ArithVar
+ * matching and the DeltaRational value.
+ * (implies (<= x c) (<= x d)) given c <= d
+ * - This is done using the iterator into the sorted set of constraints.
+ * - Given a tight constraint and previous tightest constraint, this will
+ * efficiently propagate internally.
+ *
+ * Additing and Removing Constraints
+ * - Adding Constraints takes O(log n) time where n is the number of
+ * constraints associated with the ArithVar.
+ * - Removing Constraints takes O(1) time.
+ *
+ * Internals:
+ * - Constraints are pointers to ConstraintValues.
+ * - Undefined Constraints are NullConstraint.
+ *
+ * Assumption vs. Assertion:
+ * - An assertion is anything on the theory d_fact queue.
+ * This includes any thing propagated and returned to the fact queue.
+ * These can be used in external conflicts and propagations of earlier
+ * proofs.
+ * - An assumption is anything on the theory d_fact queue that has no further
+ * explanation i.e. this theory did not propagate it.
+ * - To set something an assumption, first set it as being as assertion.
+ * - Internal assumptions have no explanations and must be regressed out of the
+ * proof.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__CONSTRAINT_H
+#define CVC5__THEORY__ARITH__CONSTRAINT_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "base/configuration_private.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "expr/node.h"
+#include "proof/trust_node.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+
+class ProofNodeManager;
+class EagerProofGenerator;
+
+namespace theory {
+
+namespace arith::linear {
+
+class Comparison;
+class ArithCongruenceManager;
+class ArithVariables;
+
+/**
+ * Logs the types of different proofs.
+ * Current, proof types:
+ * - NoAP : This constraint is not known to be true.
+ * - AssumeAP : This is an input assertion. There is no proof.
+ * : Something can be both asserted and have a proof.
+ * - InternalAssumeAP : An internal assumption. This has no guarantee of having an external proof.
+ * : This must be removed by regression.
+ * - FarkasAP : A proof with Farka's coefficients, i.e.
+ * : \sum lambda_i ( asNode(x_i) <= c_i ) |= 0 < 0
+ * : If proofs are on, coefficients will be logged.
+ * : If proofs are off, coefficients will not be logged.
+ * : A unate implication is a FarkasAP.
+ * - TrichotomyAP : This is any entailment using (x<= a and x >=a) => x = a
+ * : Equivalently, (x > a or x < a or x = a)
+ * : There are 3 candidate ways this can propagate:
+ * : !(x > a) and !(x = a) => x < a
+ * : !(x < a) and !(x = a) => x > a
+ * : !(x > a) and !(x < a) => x = a
+ * - EqualityEngineAP : This is propagated by the equality engine.
+ * : Consult this for the proof.
+ * - IntTightenAP : This is indicates that a bound involving integers was tightened.
+ * : e.g. i < 5.5 became i <= 5, when i is an integer.
+ * - IntHoleAP : This is currently a catch-all for all integer specific reason.
+ */
+enum ArithProofType
+ { NoAP,
+ AssumeAP,
+ InternalAssumeAP,
+ FarkasAP,
+ TrichotomyAP,
+ EqualityEngineAP,
+ IntTightenAP,
+ IntHoleAP};
+
+/**
+ * The types of constraints.
+ * The convex constraints are the constraints are LowerBound, Equality,
+ * and UpperBound.
+ */
+enum ConstraintType {LowerBound, Equality, UpperBound, Disequality};
+
+typedef context::CDList<ConstraintCP> CDConstraintList;
+
+typedef std::unordered_map<Node, ConstraintP> NodetoConstraintMap;
+
+typedef size_t ConstraintRuleID;
+static constexpr ConstraintRuleID ConstraintRuleIdSentinel =
+ std::numeric_limits<ConstraintRuleID>::max();
+
+typedef size_t AntecedentId;
+static constexpr AntecedentId AntecedentIdSentinel =
+ std::numeric_limits<AntecedentId>::max();
+
+typedef size_t AssertionOrder;
+static constexpr AssertionOrder AssertionOrderSentinel =
+ std::numeric_limits<AssertionOrder>::max();
+
+/**
+ * A ValueCollection binds together convex constraints that have the same
+ * DeltaRational value.
+ */
+class ValueCollection {
+private:
+
+ ConstraintP d_lowerBound;
+ ConstraintP d_upperBound;
+ ConstraintP d_equality;
+ ConstraintP d_disequality;
+
+public:
+ ValueCollection();
+
+ static ValueCollection mkFromConstraint(ConstraintP c);
+
+ bool hasLowerBound() const;
+ bool hasUpperBound() const;
+ bool hasEquality() const;
+ bool hasDisequality() const;
+
+ bool hasConstraintOfType(ConstraintType t) const;
+
+ ConstraintP getLowerBound() const;
+ ConstraintP getUpperBound() const;
+ ConstraintP getEquality() const;
+ ConstraintP getDisequality() const;
+
+ ConstraintP getConstraintOfType(ConstraintType t) const;
+
+ /** Returns true if any of the constraints are non-null. */
+ bool empty() const;
+
+ /**
+ * Remove the constraint of the type t from the collection.
+ * Returns true if the ValueCollection is now empty.
+ * If true is returned, d_value is now NULL.
+ */
+ void remove(ConstraintType t);
+
+ /**
+ * Adds a constraint to the set.
+ * The collection must not have a constraint of that type already.
+ */
+ void add(ConstraintP c);
+
+ void push_into(std::vector<ConstraintP>& vec) const;
+
+ ConstraintP nonNull() const;
+
+ ArithVar getVariable() const;
+ const DeltaRational& getValue() const;
+};
+
+/**
+ * A Map of ValueCollections sorted by the associated DeltaRational values.
+ *
+ * Discussion:
+ * While it is more natural to consider this a set, this cannot be a set as in
+ * sets the type of both iterator and const_iterator in sets are
+ * "constant iterators". We require iterators that dereference to
+ * ValueCollection&.
+ *
+ * See:
+ * http://gcc.gnu.org/onlinedocs/libstdc++/ext/lwg-defects.html#103
+ */
+typedef std::map<DeltaRational, ValueCollection> SortedConstraintMap;
+typedef SortedConstraintMap::iterator SortedConstraintMapIterator;
+typedef SortedConstraintMap::const_iterator SortedConstraintMapConstIterator;
+
+/** A Pair associating a variables and a Sorted ConstraintSet. */
+struct PerVariableDatabase{
+ ArithVar d_var;
+ SortedConstraintMap d_constraints;
+
+ // x ? c_1, x ? c_2, x ? c_3, ...
+ // where ? is a non-empty subset of {lb, ub, eq}
+ // c_1 < c_2 < c_3 < ...
+
+ PerVariableDatabase(ArithVar v) : d_var(v), d_constraints() {}
+
+ bool empty() const {
+ return d_constraints.empty();
+ }
+
+ static bool IsEmpty(const PerVariableDatabase& p){
+ return p.empty();
+ }
+};
+
+/**
+ * If proofs are on, there is a vector of rationals for farkas coefficients.
+ * This is the owner of the memory for the vector, and calls delete upon
+ * cleanup.
+ *
+ */
+struct ConstraintRule {
+ ConstraintP d_constraint;
+ ArithProofType d_proofType;
+ AntecedentId d_antecedentEnd;
+
+ /**
+ * In this comment, we abbreviate ConstraintDatabase::d_antecedents
+ * and d_farkasCoefficients as ans and fc.
+ *
+ * This list is always empty if proofs are not enabled.
+ *
+ * If proofs are enabled, the proof of constraint c at p in ans[p] of length n
+ * is (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
+ *
+ * Farkas' proofs show a contradiction with the negation of c, c_not =
+ * c->getNegation().
+ *
+ * We treat the position for NullConstraint (p-n) as the position for the
+ * farkas coefficient for so we pretend c_not is ans[p-n]. So this correlation
+ * for the constraints we are going to use: (c_not, ans[p-n+(1)], ... ,
+ * ans[p-n+(n-1)], ans[p-n+(n)]) With the coefficients at positions: (fc[0],
+ * fc[1)], ... fc[n])
+ *
+ * The index of the constraints in the proof are {i | i <= 0 <= n] } (with
+ * c_not being p-n). Partition the indices into L, U, and E, the lower bounds,
+ * the upper bounds and equalities.
+ *
+ * We standardize the proofs to be upper bound oriented following the
+ * convention: A x <= b with the proof witness of the form (lambda) Ax <=
+ * (lambda) b and lambda >= 0.
+ *
+ * To accomplish this cleanly, the fc coefficients must be negative for lower
+ * bounds. The signs of equalities can be either positive or negative.
+ *
+ * Thus the proof corresponds to (with multiplication over inequalities):
+ * \sum_{u in U} fc[u] ans[p-n+u] + \sum_{e in E} fc[e] ans[p-n+e]
+ * + \sum_{l in L} fc[l] ans[p-n+l]
+ * |= 0 < 0
+ * where fc[u] > 0, fc[l] < 0, and fc[e] != 0 (i.e. it can be either +/-).
+ *
+ * There is no requirement that the proof is minimal.
+ * We do however use all of the constraints by requiring non-zero
+ * coefficients.
+ */
+ RationalVectorCP d_farkasCoefficients;
+
+ ConstraintRule();
+ ConstraintRule(ConstraintP con, ArithProofType pt);
+ ConstraintRule(ConstraintP con,
+ ArithProofType pt,
+ AntecedentId antecedentEnd);
+ ConstraintRule(ConstraintP con,
+ ArithProofType pt,
+ AntecedentId antecedentEnd,
+ RationalVectorCP coeffs);
+
+ void print(std::ostream& out, bool produceProofs) const;
+}; /* class ConstraintRule */
+
+class Constraint {
+
+ friend class ConstraintDatabase;
+
+ public:
+ /**
+ * This begins construction of a minimal constraint.
+ *
+ * This should only be called by ConstraintDatabase.
+ *
+ * Because of circular dependencies a Constraint is not fully valid until
+ * initialize has been called on it.
+ */
+ Constraint(ArithVar x,
+ ConstraintType t,
+ const DeltaRational& v,
+ bool produceProofs);
+
+ /**
+ * Destructor for a constraint.
+ * This should only be called if safeToGarbageCollect() is true.
+ */
+ ~Constraint();
+
+ static ConstraintType constraintTypeOfComparison(const Comparison& cmp);
+
+ inline ConstraintType getType() const {
+ return d_type;
+ }
+
+ inline ArithVar getVariable() const {
+ return d_variable;
+ }
+
+ const DeltaRational& getValue() const {
+ return d_value;
+ }
+
+ inline ConstraintP getNegation() const {
+ return d_negation;
+ }
+
+ bool isEquality() const{
+ return d_type == Equality;
+ }
+ bool isDisequality() const{
+ return d_type == Disequality;
+ }
+ bool isLowerBound() const{
+ return d_type == LowerBound;
+ }
+ bool isUpperBound() const{
+ return d_type == UpperBound;
+ }
+ bool isStrictUpperBound() const{
+ Assert(isUpperBound());
+ return getValue().infinitesimalSgn() < 0;
+ }
+
+ bool isStrictLowerBound() const{
+ Assert(isLowerBound());
+ return getValue().infinitesimalSgn() > 0;
+ }
+
+ bool isSplit() const {
+ return d_split;
+ }
+
+ /**
+ * Splits the node in 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.
+ */
+ TrustNode split();
+
+ bool canBePropagated() const {
+ return d_canBePropagated;
+ }
+ void setCanBePropagated();
+
+ /**
+ * Light wrapper for calling setCanBePropagated(),
+ * on this and this->d_negation.
+ */
+ void setPreregistered(){
+ setCanBePropagated();
+ d_negation->setCanBePropagated();
+ }
+
+ bool assertedToTheTheory() const {
+ Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull());
+ return d_assertionOrder < AssertionOrderSentinel;
+ }
+ TNode getWitness() const {
+ Assert(assertedToTheTheory());
+ return d_witness;
+ }
+
+ bool assertedBefore(AssertionOrder time) const {
+ return d_assertionOrder < time;
+ }
+
+ /**
+ * Sets the witness literal for a node being on the assertion stack.
+ *
+ * If the negation of the node is true, inConflict must be true.
+ * If the negation of the node is false, inConflict must be false.
+ * Hence, negationHasProof() == inConflict.
+ *
+ * This replaces:
+ * void setAssertedToTheTheory(TNode witness);
+ * void setAssertedToTheTheoryWithNegationTrue(TNode witness);
+ */
+ void setAssertedToTheTheory(TNode witness, bool inConflict);
+
+ bool hasLiteral() const {
+ return !d_literal.isNull();
+ }
+
+ void setLiteral(Node n);
+
+ Node getLiteral() const {
+ Assert(hasLiteral());
+ return d_literal;
+ }
+
+ /** Gets a literal in the normal form suitable for proofs.
+ * That is, (sum of non-const monomials) >< const.
+ *
+ * This is a sister method to `getLiteral`, which returns a normal form
+ * literal, suitable for external solving use.
+ */
+ Node getProofLiteral() const;
+
+ /**
+ * Set the node as having a proof and being an assumption.
+ * The node must be assertedToTheTheory().
+ *
+ * Precondition: negationHasProof() == inConflict.
+ *
+ * Replaces:
+ * selfExplaining().
+ * selfExplainingWithNegationTrue().
+ */
+ void setAssumption(bool inConflict);
+
+ /** Returns true if the node is an assumption.*/
+ bool isAssumption() const;
+
+ /** Whether we produce proofs */
+ bool isProofProducing() const { return d_produceProofs; }
+
+ /** Set the constraint to have an EqualityEngine proof. */
+ void setEqualityEngineProof();
+ bool hasEqualityEngineProof() const;
+
+ /** Returns true if the node has a Farkas' proof. */
+ bool hasFarkasProof() const;
+
+ /**
+ * @brief Returns whether this constraint is provable using a Farkas
+ * proof applied to (possibly tightened) input assertions.
+ *
+ * An example of a constraint that has a simple Farkas proof:
+ * x <= 0 proven from x + y <= 0 and x - y <= 0.
+ *
+ * An example of another constraint that has a simple Farkas proof:
+ * x <= 0 proven from x + y <= 0 and x - y <= 0.5 for integers x, y
+ * (integer bound-tightening is applied first!).
+ *
+ * An example of a constraint that might be proven **without** a simple
+ * Farkas proof:
+ * x < 0 proven from not(x == 0) and not(x > 0).
+ *
+ * This could be proven internally by the arithmetic theory using
+ * `TrichotomyAP` as the proof type.
+ *
+ */
+ bool hasSimpleFarkasProof() const;
+ /**
+ * Returns whether this constraint is an assumption or a tightened
+ * assumption.
+ */
+ bool isPossiblyTightenedAssumption() const;
+
+ /** Returns true if the node has a int bound tightening proof. */
+ bool hasIntTightenProof() const;
+
+ /** Returns true if the node has a int hole proof. */
+ bool hasIntHoleProof() const;
+
+ /** Returns true if the node has a trichotomy proof. */
+ bool hasTrichotomyProof() const;
+
+ void printProofTree(std::ostream & out, size_t depth = 0) const;
+
+ /**
+ * A sets the constraint to be an internal assumption.
+ *
+ * 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 setInternalAssumption(bool inConflict);
+ bool isInternalAssumption() const;
+
+ /**
+ * Returns a explanation of the constraint that is appropriate for conflicts.
+ *
+ * This is not appropriate for propagation!
+ *
+ * This is the minimum fringe of the implication tree s.t.
+ * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
+ */
+ TrustNode externalExplainByAssertions() const;
+
+ /**
+ * Writes an explanation of a constraint into the node builder.
+ * Pushes back an explanation that is acceptable to send to the sat solver.
+ * nb is assumed to be an AND.
+ *
+ * This is the minimum fringe of the implication tree s.t.
+ * every constraint is assertedToTheTheory() or hasEqualityEngineProof().
+ *
+ * This is not appropriate for propagation!
+ * Use explainForPropagation() instead.
+ */
+ std::shared_ptr<ProofNode> externalExplainByAssertions(NodeBuilder& nb) const
+ {
+ return externalExplain(nb, AssertionOrderSentinel);
+ }
+
+ /* Equivalent to calling externalExplainByAssertions on all constraints in b */
+ static Node externalExplainByAssertions(const ConstraintCPVec& b);
+ static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b);
+ static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c);
+
+ /**
+ * 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);
+
+ /** The fringe of a farkas' proof. */
+ bool onFringe() const {
+ return assertedToTheTheory() || isInternalAssumption() || hasEqualityEngineProof();
+ }
+
+ /**
+ * Returns an explanation of a propagation by the ConstraintDatabase.
+ * The constraint must have a proof.
+ * The constraint cannot be an assumption.
+ *
+ * This is the minimum fringe of the implication tree (excluding the
+ * constraint itself) s.t. every constraint is assertedToTheTheory() or
+ * hasEqualityEngineProof().
+ *
+ * All return conjuncts were asserted before this constraint.
+ *
+ * Requires the given node to rewrite to the canonical literal for this
+ * constraint.
+ *
+ * @params n the literal to prove
+ * n must rewrite to the constraint's canonical literal
+ *
+ * @returns a trust node of the form:
+ * (=> explanation n)
+ */
+ TrustNode externalExplainForPropagation(TNode n) const;
+
+ /**
+ * Explain the constraint and its negation in terms of assertions.
+ * The constraint must be in conflict.
+ */
+ TrustNode externalExplainConflict() const;
+
+ /** The constraint is known to be true. */
+ inline bool hasProof() const {
+ return d_crid != ConstraintRuleIdSentinel;
+ }
+
+ /** The negation of the constraint is known to hold. */
+ inline bool negationHasProof() const {
+ 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(). */
+ inline bool isTrue() const {
+ return hasProof();
+ }
+
+ /** Both the constraint and its negation are true. */
+ inline bool inConflict() const {
+ return hasProof() && negationHasProof();
+ }
+
+ /**
+ * 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();
+
+ /**
+ * 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 ConstraintP makeNegation(ArithVar v,
+ ConstraintType t,
+ const DeltaRational& r,
+ bool produceProofs);
+
+ const ValueCollection& getValueCollection() const;
+
+
+ ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const;
+ ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const;
+
+ /**
+ * Marks a the constraint c as being entailed by a.
+ * The Farkas proof 1*(a) + -1 (c) |= 0<0
+ *
+ * After calling impliedByUnate(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByUnate(ConstraintCP a, bool inConflict);
+
+ /**
+ * Marks a the constraint c as being entailed by a.
+ * The reason has to do with integer bound tightening.
+ *
+ * After calling impliedByIntTighten(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByIntTighten(ConstraintCP a, bool inConflict);
+
+ /**
+ * Marks a the constraint c as being entailed by a.
+ * The reason has to do with integer reasoning.
+ *
+ * After calling impliedByIntHole(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByIntHole(ConstraintCP a, bool inConflict);
+
+ /**
+ * Marks a the constraint c as being entailed by a.
+ * The reason has to do with integer reasoning.
+ *
+ * After calling impliedByIntHole(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByIntHole(const ConstraintCPVec& b, bool inConflict);
+
+ /**
+ * This is a lemma of the form:
+ * x < d or x = d or x > d
+ * The current constraint c is one of the above constraints and {a,b}
+ * are the negation of the other two constraints.
+ *
+ * Preconditions:
+ * - negationHasProof() == inConflict.
+ *
+ * After calling impliedByTrichotomy(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByTrichotomy(ConstraintCP a, ConstraintCP b, bool inConflict);
+
+ /**
+ * Marks the node as having a Farkas proof.
+ *
+ * Preconditions:
+ * - coeffs == NULL if proofs are off.
+ * - See the comments for ConstraintRule for the form of coeffs when
+ * proofs are on.
+ * - negationHasProof() == inConflict.
+ *
+ * After calling impliedByFarkas(), the caller should either raise a conflict
+ * or try call tryToPropagate().
+ */
+ void impliedByFarkas(const ConstraintCPVec& b, RationalVectorCP coeffs, bool inConflict);
+
+ /**
+ * Generates an implication node, B => getLiteral(),
+ * where B is the result of externalExplainByAssertions(b).
+ * Does not guarantee b is the explanation of the constraint.
+ */
+ Node externalImplication(const ConstraintCPVec& b) const;
+
+ /**
+ * Returns true if the variable is assigned the value dr,
+ * the constraint would be satisfied.
+ */
+ bool satisfiedBy(const DeltaRational& dr) const;
+
+ /**
+ * The node must have a proof already and be eligible for propagation!
+ * You probably want to call tryToPropagate() instead.
+ *
+ * Preconditions:
+ * - hasProof()
+ * - canBePropagated()
+ * - !assertedToTheTheory()
+ */
+ void propagate();
+
+ /**
+ * If the constraint
+ * canBePropagated() and
+ * !assertedToTheTheory(),
+ * the constraint is added to the database's propagation queue.
+ *
+ * Precondition:
+ * - hasProof()
+ */
+ void tryToPropagate();
+
+ /**
+ * Returns a reference to the containing database.
+ * Precondition: the constraint must be initialized.
+ */
+ const ConstraintDatabase& getDatabase() const;
+
+ /** Returns the constraint rule at the position. */
+ const ConstraintRule& getConstraintRule() const;
+
+ private:
+ /** Returns true if the constraint has been initialized. */
+ bool initialized() const;
+
+ /**
+ * This initializes the fields that cannot be set in the constructor due to
+ * circular dependencies.
+ */
+ void initialize(ConstraintDatabase* db,
+ SortedConstraintMapIterator v,
+ ConstraintP negation);
+
+ class ConstraintRuleCleanup
+ {
+ public:
+ inline void operator()(ConstraintRule* crp)
+ {
+ Assert(crp != NULL);
+ ConstraintP constraint = crp->d_constraint;
+ Assert(constraint->d_crid != ConstraintRuleIdSentinel);
+ constraint->d_crid = ConstraintRuleIdSentinel;
+ if (constraint->isProofProducing())
+ {
+ if (crp->d_farkasCoefficients != RationalVectorCPSentinel)
+ {
+ delete crp->d_farkasCoefficients;
+ }
+ }
+ }
+ };
+
+ class CanBePropagatedCleanup
+ {
+ public:
+ inline void operator()(ConstraintP* p)
+ {
+ ConstraintP constraint = *p;
+ Assert(constraint->d_canBePropagated);
+ constraint->d_canBePropagated = false;
+ }
+ };
+
+ class AssertionOrderCleanup
+ {
+ public:
+ inline void operator()(ConstraintP* p)
+ {
+ ConstraintP constraint = *p;
+ Assert(constraint->assertedToTheTheory());
+ constraint->d_assertionOrder = AssertionOrderSentinel;
+ constraint->d_witness = TNode::null();
+ Assert(!constraint->assertedToTheTheory());
+ }
+ };
+
+ class SplitCleanup
+ {
+ public:
+ inline void operator()(ConstraintP* p)
+ {
+ ConstraintP constraint = *p;
+ Assert(constraint->d_split);
+ constraint->d_split = false;
+ }
+ };
+
+ /**
+ * Returns true if the node is safe to garbage collect.
+ * Both it and its negation must have no context dependent data set.
+ */
+ bool safeToGarbageCollect() const;
+
+ /**
+ * Returns true if the constraint has no context dependent data set.
+ */
+ bool contextDependentDataIsSet() const;
+
+ /**
+ * Returns true if the node correctly corresponds to the constraint that is
+ * being set.
+ */
+ bool sanityChecking(Node n) const;
+
+ /** Returns a reference to the map for d_variable. */
+ SortedConstraintMap& constraintSet() const;
+
+ /** Returns coefficients for the proofs for farkas cancellation. */
+ static std::pair<int, int> unateFarkasSigns(ConstraintCP a, ConstraintCP b);
+
+ Node externalExplain(AssertionOrder order) const;
+ /**
+ * Returns an explanation of that was assertedBefore(order).
+ * The constraint must have a proof.
+ * The constraint cannot be selfExplaining().
+ *
+ * This is the minimum fringe of the implication tree
+ * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof().
+ */
+ std::shared_ptr<ProofNode> externalExplain(NodeBuilder& nb,
+ AssertionOrder order) const;
+
+ static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order);
+
+ inline ArithProofType getProofType() const {
+ return getConstraintRule().d_proofType;
+ }
+
+ inline AntecedentId getEndAntecedent() const {
+ return getConstraintRule().d_antecedentEnd;
+ }
+
+ inline RationalVectorCP getFarkasCoefficients() const
+ {
+ return d_produceProofs ? getConstraintRule().d_farkasCoefficients : nullptr;
+ }
+
+ /**
+ * The proof of the node is empty.
+ * The proof must be a special proof. Either
+ * isSelfExplaining() or
+ * hasEqualityEngineProof()
+ */
+ bool antecentListIsEmpty() const;
+
+ bool antecedentListLengthIsOne() const;
+
+ /** Return true if every element in b has a proof. */
+ static bool allHaveProof(const ConstraintCPVec& b);
+
+ /** Precondition: hasFarkasProof()
+ * Computes the combination implied by the farkas coefficients. Sees if it is
+ * a contradiction.
+ */
+
+ bool wellFormedFarkasProof() const;
+
+ /** The ArithVar associated with the constraint. */
+ const ArithVar d_variable;
+
+ /** The type of the Constraint. */
+ const ConstraintType d_type;
+
+ /** The DeltaRational value with the constraint. */
+ const DeltaRational d_value;
+
+ /** A pointer to the associated database for the Constraint. */
+ ConstraintDatabase* d_database;
+
+ /**
+ * The node to be communicated with the TheoryEngine.
+ *
+ * This is not context dependent, but may be set once.
+ *
+ * This must be set if the constraint canBePropagated().
+ * This must be set if the constraint assertedToTheTheory().
+ * Otherwise, this may be null().
+ */
+ Node d_literal;
+
+ /** Pointer to the negation of the Constraint. */
+ ConstraintP d_negation;
+
+ /**
+ * This is true if the associated node can be propagated.
+ *
+ * This should be enabled if the node has been preregistered.
+ *
+ * Sat Context Dependent.
+ * This is initially false.
+ */
+ bool d_canBePropagated;
+
+ /**
+ * This is the order the constraint was asserted to the theory.
+ * If this has been set, the node can be used in conflicts.
+ * If this is c.d_assertedOrder < d.d_assertedOrder, then c can be used in the
+ * explanation of d.
+ *
+ * This should be set after the literal is dequeued by Theory::get().
+ *
+ * Sat Context Dependent.
+ * This is initially AssertionOrderSentinel.
+ */
+ 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
+ */
+ TNode d_witness;
+
+ /**
+ * The position of the constraint in the constraint rule id.
+ *
+ * Sat Context Dependent.
+ * This is initially
+ */
+ ConstraintRuleID d_crid;
+
+ /**
+ * True if the equality has been split.
+ * Only meaningful if ConstraintType == Equality.
+ *
+ * User Context Dependent.
+ * This is initially false.
+ */
+ bool d_split;
+
+ /**
+ * Position in sorted constraint set for the variable.
+ * Unset if d_type is Disequality.
+ */
+ SortedConstraintMapIterator d_variablePosition;
+
+ /** Whether to produce proofs, */
+ bool d_produceProofs;
+
+}; /* class ConstraintValue */
+
+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 ConstraintCP 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);
+std::ostream& operator<<(std::ostream& o, const ArithProofType);
+
+class ConstraintDatabase : protected EnvObj
+{
+ private:
+ /**
+ * The map from ArithVars to their unique databases.
+ * When the vector changes size, we cannot allow the maps to move so this
+ * is a vector of pointers.
+ */
+ std::vector<PerVariableDatabase*> d_varDatabases;
+
+ SortedConstraintMap& getVariableSCM(ArithVar v) const;
+
+ /** Maps literals to constraints.*/
+ NodetoConstraintMap d_nodetoConstraintMap;
+
+ /**
+ * A queue of propagated constraints.
+ * ConstraintCP are pointers.
+ * The elements of the queue do not require destruction.
+ */
+ context::CDQueue<ConstraintCP> d_toPropagate;
+
+ /**
+ * Proofs are lists of valid constraints terminated by the first null
+ * sentinel value in the proof list.
+ * We abbreviate d_antecedents as ans in the comment.
+ *
+ * The proof at p in ans[p] of length n is
+ * (NullConstraint, ans[p-(n-1)], ... , ans[p-1], ans[p])
+ *
+ * The proof at p corresponds to the conjunction:
+ * (and x_i)
+ *
+ * So the proof of a Constraint c corresponds to the horn clause:
+ * (implies (and x_i) c)
+ * where (and x_i) is the proof at c.d_crid d_antecedentEnd.
+ *
+ * Constraints are pointers so this list is designed not to require any destruction.
+ */
+ CDConstraintList d_antecedents;
+
+ typedef context::CDList<ConstraintRule, Constraint::ConstraintRuleCleanup>
+ ConstraintRuleList;
+ 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
+ * carefully.
+ */
+ struct Watches{
+
+ /**
+ * Contains the exact list of constraints that have a proof.
+ * Upon pop, this unsets d_crid to NoAP.
+ *
+ * The index in this list is the proper ordering of the proofs.
+ */
+ ConstraintRuleList d_constraintProofs;
+
+ /**
+ * Contains the exact list of constraints that can be used for propagation.
+ */
+ CBPList d_canBePropagatedWatches;
+
+ /**
+ * Contains the exact list of constraints that have been asserted to the theory.
+ */
+ AOList d_assertionOrderWatches;
+
+
+ /**
+ * Contains the exact list of atoms that have been preregistered.
+ * This is a pointer as it must be destroyed before the elements of
+ * d_varDatabases.
+ */
+ SplitList d_splitWatches;
+ Watches(context::Context* satContext, context::Context* userContext);
+ };
+ Watches* d_watches;
+
+ void pushSplitWatch(ConstraintP c);
+ void pushCanBePropagatedWatch(ConstraintP c);
+ void pushAssertionOrderWatch(ConstraintP c, TNode witness);
+
+ /** Assumes that antecedents have already been pushed. */
+ void pushConstraintRule(const ConstraintRule& crp);
+
+ /** Returns true if all of the entries of the vector are empty. */
+ static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec);
+
+ /** Map from nodes to arithvars. */
+ const ArithVariables& d_avariables;
+
+ const ArithVariables& getArithVariables() const{
+ return d_avariables;
+ }
+
+ ArithCongruenceManager& d_congruenceManager;
+
+ /** Owned by the TheoryArithPrivate, used here. */
+ EagerProofGenerator* d_pfGen;
+ /** Owned by the TheoryArithPrivate, used here. */
+ ProofNodeManager* d_pnm;
+
+ RaiseConflict d_raiseConflict;
+
+
+ const Rational d_one;
+ const Rational d_negOne;
+
+ friend class Constraint;
+
+ public:
+ ConstraintDatabase(Env& env,
+ const ArithVariables& variables,
+ ArithCongruenceManager& dm,
+ RaiseConflict conflictCallBack,
+ EagerProofGenerator* pfGen);
+
+ ~ConstraintDatabase();
+
+ /** Adds a literal to the database. */
+ ConstraintP addLiteral(TNode lit);
+
+ /**
+ * If hasLiteral() is true, returns the constraint.
+ * Otherwise, returns NullConstraint.
+ */
+ ConstraintP lookup(TNode literal) const;
+
+ /**
+ * Returns true if the literal has been added to the database.
+ * This is a hash table lookup.
+ * It does not look in the database for an equivalent corresponding constraint.
+ */
+ bool hasLiteral(TNode literal) const;
+
+ bool hasMorePropagations() const{
+ return !d_toPropagate.empty();
+ }
+
+ ConstraintCP nextPropagation(){
+ Assert(hasMorePropagations());
+
+ ConstraintCP p = d_toPropagate.front();
+ d_toPropagate.pop();
+
+ return p;
+ }
+
+ void addVariable(ArithVar v);
+ bool variableDatabaseIsSetup(ArithVar v) const;
+ void removeVariable(ArithVar v);
+
+ /** Get an explanation and proof for this constraint from the equality engine
+ */
+ TrustNode eeExplain(ConstraintCP c) const;
+ /** Get an explanation for this constraint from the equality engine */
+ void eeExplain(ConstraintCP c, NodeBuilder& nb) const;
+
+ /**
+ * Returns a constraint with the variable v, the constraint type t, and a value
+ * dominated by r (explained below) if such a constraint exists in the database.
+ * If no such constraint exists, NullConstraint is returned.
+ *
+ * t must be either UpperBound or LowerBound.
+ * The returned value v is dominated:
+ * If t is UpperBound, r <= v
+ * If t is LowerBound, r >= v
+ *
+ * variableDatabaseIsSetup(v) must be true.
+ */
+ 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.
+ * If there is such a constraint in the database already, it is returned.
+ * If there is no such constraint, this constraint is added to the database.
+ *
+ */
+ 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.
+ */
+ ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t);
+
+
+ void deleteConstraintAndNegation(ConstraintP c);
+
+ /** Given constraints `a` and `b` such that `a OR b` by unate reasoning,
+ * adds a TrustNode to `out` which proves `a OR b` as a lemma.
+ *
+ * Example: `x <= 5` OR `5 <= x`.
+ */
+ void proveOr(std::vector<TrustNode>& out,
+ ConstraintP a,
+ ConstraintP b,
+ bool negateSecond) const;
+ /** Given constraints `a` and `b` such that `a` implies `b` by unate
+ * reasoning, adds a TrustNode to `out` which proves `-a OR b` as a lemma.
+ *
+ * Example: `x >= 5` -> `x >= 4`.
+ */
+ void implies(std::vector<TrustNode>& out, ConstraintP a, ConstraintP b) const;
+ /** Given constraints `a` and `b` such that `not(a AND b)` by unate reasoning,
+ * adds a TrustNode to `out` which proves `-a OR -b` as a lemma.
+ *
+ * Example: `x >= 4` -> `x <= 3`.
+ */
+ void mutuallyExclusive(std::vector<TrustNode>& out,
+ ConstraintP a,
+ ConstraintP b) const;
+
+ /**
+ * Outputs a minimal set of unate implications onto the vector for the variable.
+ * This outputs lemmas of the general forms
+ * (= p c) implies (<= p d) for c < d, or
+ * (= p c) implies (not (= p d)) for c != d.
+ */
+ void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas) const;
+ void outputUnateEqualityLemmas(std::vector<TrustNode>& lemmas,
+ ArithVar v) const;
+
+ /**
+ * Outputs a minimal set of unate implications onto the vector for the variable.
+ *
+ * If ineqs is true, this outputs lemmas of the general form
+ * (<= p c) implies (<= p d) for c < d.
+ */
+ void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas) const;
+ void outputUnateInequalityLemmas(std::vector<TrustNode>& lemmas,
+ ArithVar v) const;
+
+ void unatePropLowerBound(ConstraintP curr, ConstraintP prev);
+ void unatePropUpperBound(ConstraintP curr, ConstraintP prev);
+ void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB);
+
+ /** AntecendentID must be in range. */
+ ConstraintCP getAntecedent(AntecedentId p) const;
+
+ bool isProofEnabled() const { return d_pnm != nullptr; }
+
+ private:
+ /** returns true if cons is now in conflict. */
+ bool handleUnateProp(ConstraintP ant, ConstraintP cons);
+
+ DenseSet d_reclaimable;
+
+ class Statistics {
+ public:
+ IntStat d_unatePropagateCalls;
+ IntStat d_unatePropagateImplications;
+
+ Statistics();
+ } d_statistics;
+
+}; /* ConstraintDatabase */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__CONSTRAINT_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Forward declarations of the ConstraintValue and ConstraintDatabase
+ * classes.
+ *
+ * This is the forward declarations of the ConstraintValue and
+ * ConstraintDatabase and the typedef for Constraint.
+ * This is used to break circular dependencies and minimize interaction
+ * between header files.
+ */
+
+#ifndef CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
+#define CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H
+
+#include <vector>
+
+#include "cvc5_private.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class Constraint;
+typedef Constraint* ConstraintP;
+typedef const Constraint* ConstraintCP;
+
+static constexpr ConstraintP NullConstraint = nullptr;
+
+class ConstraintDatabase;
+
+typedef std::vector<ConstraintCP> ConstraintCPVec;
+
+typedef std::vector<Rational> RationalVector;
+typedef RationalVector* RationalVectorP;
+typedef const RationalVector* RationalVectorCP;
+static constexpr RationalVectorCP RationalVectorCPSentinel = nullptr;
+static constexpr RationalVectorP RationalVectorPSentinel = nullptr;
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__CONSTRAINT_FORWARD_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andrew Reynolds, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/cut_log.h"
+
+#include <limits.h>
+#include <math.h>
+
+#include <cmath>
+#include <iomanip>
+#include <map>
+
+#include "base/cvc5config.h"
+#include "base/output.h"
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/normal_form.h"
+#include "util/ostream_util.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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());
+ StreamFormatScope scope(out);
+
+ out << len << " " << std::setprecision(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_N(-1),
+ d_rowId(-1),
+ d_exactPrecision(nullptr),
+ d_explanation(nullptr)
+{}
+
+CutInfo::~CutInfo(){
+}
+
+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 != nullptr; }
+
+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 != nullptr; }
+
+bool CutInfo::operator<(const CutInfo& o) const{
+ return d_execOrd < o.d_execOrd;
+}
+
+
+void CutInfo::setReconstruction(const DenseVector& ep){
+ Assert(!reconstructed());
+ d_exactPrecision.reset(new DenseVector(ep));
+}
+
+void CutInfo::setExplanation(const ConstraintCPVec& ex){
+ Assert(reconstructed());
+ if (d_explanation == nullptr)
+ {
+ d_explanation.reset(new ConstraintCPVec(ex));
+ }
+ else
+ {
+ *d_explanation = ex;
+ }
+}
+
+void CutInfo::swapExplanation(ConstraintCPVec& ex){
+ Assert(reconstructed());
+ Assert(!proven());
+ if (d_explanation == nullptr)
+ {
+ d_explanation.reset(new ConstraintCPVec());
+ }
+ d_explanation->swap(ex);
+}
+
+const DenseVector& CutInfo::getReconstruction() const {
+ Assert(reconstructed());
+ return *d_exactPrecision;
+}
+
+void CutInfo::clearReconstruction(){
+ if(proven()){
+ d_explanation = nullptr;
+ }
+
+ if(reconstructed()){
+ d_exactPrecision = nullptr;
+ }
+
+ 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;
+ Trace("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{
+ Trace("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(TraceIsOn("approx::nodelog")){
+ Trace("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "...";
+ for(unsigned k = 0; k<sortedRemoved.size(); k++){
+ Trace("approx::nodelog") << ", " << sortedRemoved[k];
+ }
+ Trace("approx::nodelog") << endl;
+ Trace("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){
+ Trace("approx::nodelog") << "deleting from above because of " << rd << endl;
+ Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+ d_rowId2ArithVar.erase(origOrd);
+ }else{
+ Trace("approx::nodelog") << "deleting " << ci << " because of " << rd << endl;
+ Trace("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){
+ Trace("approx::nodelog") << "shifting above down due to " << rd << endl;
+ Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+ Trace("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl;
+ d_rowId2ArithVar.erase(origOrd);
+ mapRowId(newOrd, v);
+ }else{
+ Trace("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl;
+ Trace("approx::nodelog") << "had " << origOrd << " <-> " << v << endl;
+ Trace("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);
+ Trace("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 << "]";
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Morgan Deters, Kshitij Bansal
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+#include "expr/kind.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "util/dense_map.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/** 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.
+ */
+ std::unique_ptr<DenseVector> d_exactPrecision;
+
+ std::unique_ptr<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);
+
+class BranchCutInfo : public CutInfo {
+public:
+ BranchCutInfo(int execOrd, int br, Kind dir, double val);
+};
+
+class RowsDeleted : public CutInfo {
+public:
+ 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 std::unordered_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 appropriate 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 TreeLog {
+private:
+ 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;
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Diophantine equation solver
+ *
+ * A Diophantine equation solver for the theory of arithmetic.
+ */
+#include "theory/arith/linear/dio_solver.h"
+
+#include <iostream>
+
+#include "base/output.h"
+#include "expr/skolem_manager.h"
+#include "options/arith_options.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/partial_model.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+inline Node makeIntegerVariable(){
+ NodeManager* nm = NodeManager::currentNM();
+ SkolemManager* sm = nm->getSkolemManager();
+ return sm->mkDummySkolem("intvar",
+ nm->integerType(),
+ "is an integer variable created by the dio solver");
+}
+
+DioSolver::DioSolver(Env& env)
+ : EnvObj(env),
+ d_lastUsedProofVariable(context(), 0),
+ d_inputConstraints(context()),
+ d_nextInputConstraintToEnqueue(context(), 0),
+ d_trail(context()),
+ d_subs(context()),
+ d_currentF(),
+ d_savedQueue(context()),
+ d_savedQueueIndex(context(), 0),
+ d_conflictIndex(context()),
+ d_maxInputCoefficientLength(context(), 0),
+ d_usedDecomposeIndex(context(), false),
+ d_lastPureSubstitution(context(), 0),
+ d_pureSubstitionIter(context(), 0),
+ d_decompositionLemmaQueue(context())
+{
+}
+
+DioSolver::Statistics::Statistics()
+ : d_conflictCalls(smtStatisticsRegistry().registerInt(
+ "theory::arith::dio::conflictCalls")),
+ d_cutCalls(
+ smtStatisticsRegistry().registerInt("theory::arith::dio::cutCalls")),
+ d_cuts(smtStatisticsRegistry().registerInt("theory::arith::dio::cuts")),
+ d_conflicts(
+ smtStatisticsRegistry().registerInt("theory::arith::dio::conflicts")),
+ d_conflictTimer(smtStatisticsRegistry().registerTimer(
+ "theory::arith::dio::conflictTimer")),
+ d_cutTimer(
+ smtStatisticsRegistry().registerTimer("theory::arith::dio::cutTimer"))
+{
+}
+
+bool DioSolver::queueConditions(TrailIndex t){
+ Trace("queueConditions") << !inConflict() << std::endl;
+ Trace("queueConditions") << gcdIsOne(t) << std::endl;
+ Trace("queueConditions") << !debugAnySubstitionApplies(t) << std::endl;
+ Trace("queueConditions") << !triviallySat(t) << std::endl;
+ Trace("queueConditions") << !triviallyUnsat(t) << std::endl;
+
+ return
+ !inConflict() &&
+ gcdIsOne(t) &&
+ !debugAnySubstitionApplies(t) &&
+ !triviallySat(t) &&
+ !triviallyUnsat(t);
+}
+
+size_t DioSolver::allocateProofVariable() {
+ Assert(d_lastUsedProofVariable <= d_proofVariablePool.size());
+ if(d_lastUsedProofVariable == d_proofVariablePool.size()){
+ Assert(d_lastUsedProofVariable == d_proofVariablePool.size());
+ Node intVar = makeIntegerVariable();
+ d_proofVariablePool.push_back(Variable(intVar));
+ }
+ size_t res = d_lastUsedProofVariable;
+ d_lastUsedProofVariable = d_lastUsedProofVariable + 1;
+ return res;
+}
+
+
+Node DioSolver::nextPureSubstitution(){
+ Assert(hasMorePureSubstitutions());
+ SubIndex curr = d_pureSubstitionIter;
+ d_pureSubstitionIter = d_pureSubstitionIter + 1;
+
+ Assert(d_subs[curr].d_fresh.isNull());
+ Variable v = d_subs[curr].d_eliminated;
+
+ SumPair sp = d_trail[d_subs[curr].d_constraint].d_eq;
+ Polynomial p = sp.getPolynomial();
+ Constant c = -sp.getConstant();
+ Polynomial cancelV = p + Polynomial::mkPolynomial(v);
+ Node eq = NodeManager::currentNM()->mkNode(kind::EQUAL, v.getNode(), cancelV.getNode());
+ return eq;
+}
+
+
+bool DioSolver::debugEqualityInInputEquations(Node eq){
+ typedef context::CDList<InputConstraint>::const_iterator const_iterator;
+ const_iterator i=d_inputConstraints.begin(), end = d_inputConstraints.end();
+ for(; i != end; ++i){
+ Node reason_i = (*i).d_reason;
+ if(eq == reason_i){
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void DioSolver::pushInputConstraint(const Comparison& eq, Node reason){
+ Assert(!debugEqualityInInputEquations(reason));
+ Assert(eq.debugIsIntegral());
+ Assert(eq.getNode().getKind() == kind::EQUAL);
+
+ SumPair sp = eq.toSumPair();
+ if(sp.isNonlinear()){
+ return;
+ }
+
+
+
+ uint32_t length = sp.maxLength();
+ if(length > d_maxInputCoefficientLength){
+ d_maxInputCoefficientLength = length;
+ }
+
+ size_t varIndex = allocateProofVariable();
+ Variable proofVariable(d_proofVariablePool[varIndex]);
+ //Variable proofVariable(makeIntegerVariable());
+
+ TrailIndex posInTrail = d_trail.size();
+ Trace("dio::pushInputConstraint") << "pushInputConstraint @ " << posInTrail
+ << " " << eq.getNode()
+ << " " << reason << endl;
+ d_trail.push_back(Constraint(sp,Polynomial::mkPolynomial(proofVariable)));
+
+ size_t posInConstraintList = d_inputConstraints.size();
+ d_inputConstraints.push_back(InputConstraint(reason, posInTrail));
+
+ d_varToInputConstraintMap[proofVariable.getNode()] = posInConstraintList;
+}
+
+
+DioSolver::TrailIndex DioSolver::scaleEqAtIndex(DioSolver::TrailIndex i, const Integer& g){
+ Assert(g != 0);
+ Constant invg = Constant::mkConstant(Rational(Integer(1),g));
+ const SumPair& sp = d_trail[i].d_eq;
+ const Polynomial& proof = d_trail[i].d_proof;
+
+ SumPair newSP = sp * invg;
+ Polynomial newProof = proof * invg;
+
+ Assert(newSP.isIntegral());
+ Assert(newSP.gcd() == 1);
+
+ TrailIndex j = d_trail.size();
+
+ d_trail.push_back(Constraint(newSP, newProof));
+
+ Trace("arith::dio") << "scaleEqAtIndex(" << i <<","<<g<<")"<<endl;
+ Trace("arith::dio") << "derived "<< newSP.getNode()
+ <<" with proof " << newProof.getNode() << endl;
+ return j;
+}
+
+Node DioSolver::proveIndex(TrailIndex i){
+ Assert(inRange(i));
+ const Polynomial& proof = d_trail[i].d_proof;
+ Assert(!proof.isConstant());
+
+ NodeBuilder nb(kind::AND);
+ for(Polynomial::iterator iter = proof.begin(), end = proof.end(); iter!= end; ++iter){
+ Monomial m = (*iter);
+ Assert(!m.isConstant());
+ VarList vl = m.getVarList();
+ Assert(vl.singleton());
+ Variable v = vl.getHead();
+
+ Node input = proofVariableToReason(v);
+ if(input.getKind() == kind::AND){
+ for(Node::iterator input_iter = input.begin(), input_end = input.end(); input_iter != input_end; ++input_iter){
+ Node inputChild = *input_iter;
+ nb << inputChild;
+ }
+ }else{
+ nb << input;
+ }
+ }
+
+ Node result = (nb.getNumChildren() == 1) ? nb[0] : (Node)nb;
+ Trace("arith::dio") << "Proof at " << i << " is "
+ << d_trail[i].d_eq.getNode() << endl
+ << d_trail[i].d_proof.getNode() << endl
+ << " which becomes " << result << endl;
+ return result;
+}
+
+bool DioSolver::anyCoefficientExceedsMaximum(TrailIndex j) const{
+ uint32_t length = d_trail[j].d_eq.maxLength();
+ uint32_t nmonos = d_trail[j].d_eq.getPolynomial().numMonomials();
+
+ bool result =
+ nmonos >= 2 &&
+ length > d_maxInputCoefficientLength + MAX_GROWTH_RATE;
+ if(TraceIsOn("arith::dio::max") && result){
+
+ const SumPair& eq = d_trail[j].d_eq;
+ const Polynomial& proof = d_trail[j].d_proof;
+
+ Trace("arith::dio::max") << "about to drop:" << std::endl;
+ Trace("arith::dio::max") << "d_trail[" << j << "].d_eq = " << eq.getNode() << std::endl;
+ Trace("arith::dio::max") << "d_trail[" << j << "].d_proof = " << proof.getNode() << std::endl;
+ }
+ return result;
+}
+
+void DioSolver::enqueueInputConstraints(){
+ Assert(d_currentF.empty());
+ while(d_savedQueueIndex < d_savedQueue.size()){
+ d_currentF.push_back(d_savedQueue[d_savedQueueIndex]);
+ d_savedQueueIndex = d_savedQueueIndex + 1;
+ }
+
+ while(d_nextInputConstraintToEnqueue < d_inputConstraints.size() && !inConflict()){
+ size_t curr = d_nextInputConstraintToEnqueue;
+ d_nextInputConstraintToEnqueue = d_nextInputConstraintToEnqueue + 1;
+
+ TrailIndex i = d_inputConstraints[curr].d_trailPos;
+ TrailIndex j = applyAllSubstitutionsToIndex(i);
+
+ if(!triviallySat(j)){
+ if(triviallyUnsat(j)){
+ raiseConflict(j);
+ }else{
+ TrailIndex k = reduceByGCD(j);
+
+ if(!inConflict()){
+ if(triviallyUnsat(k)){
+ raiseConflict(k);
+ }else if(!(triviallySat(k) || anyCoefficientExceedsMaximum(k))){
+ pushToQueueBack(k);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/*TODO Currently linear in the size of the Queue
+ *It is not clear if am O(log n) strategy would be better.
+ *Right before this in the algorithm is a substitution which could potentially
+ *effect the key values of everything in the queue.
+ */
+void DioSolver::moveMinimumByAbsToQueueFront(){
+ Assert(!queueEmpty());
+
+ //Select the minimum element.
+ size_t indexInQueue = 0;
+ Monomial minMonomial = d_trail[d_currentF[indexInQueue]].d_minimalMonomial;
+
+ 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.absCmp(minMonomial) < 0){
+ indexInQueue = i;
+ minMonomial = curr;
+ }
+ }
+
+ TrailIndex tmp = d_currentF[indexInQueue];
+ d_currentF[indexInQueue] = d_currentF.front();
+ d_currentF.front() = tmp;
+}
+
+bool DioSolver::queueEmpty() const{
+ return d_currentF.empty();
+}
+
+Node DioSolver::columnGcdIsOne() const{
+ std::unordered_map<Node, Integer> gcdMap;
+
+ std::deque<TrailIndex>::const_iterator iter, end;
+ for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
+ TrailIndex curr = *iter;
+ Polynomial p = d_trail[curr].d_eq.getPolynomial();
+ Polynomial::iterator monoIter = p.begin(), monoEnd = p.end();
+ for(; monoIter != monoEnd; ++monoIter){
+ Monomial m = *monoIter;
+ VarList vl = m.getVarList();
+ Node vlNode = vl.getNode();
+
+ Constant c = m.getConstant();
+ Integer zc = c.getValue().getNumerator();
+ if(gcdMap.find(vlNode) == gcdMap.end()){
+ // have not seen vl yet.
+ // gcd is c
+ Assert(c.isIntegral());
+ Assert(!m.absCoefficientIsOne());
+ gcdMap.insert(make_pair(vlNode, zc.abs()));
+ }else{
+ const Integer& currentGcd = gcdMap[vlNode];
+ Integer newGcd = currentGcd.gcd(zc);
+ if(newGcd == 1){
+ return vlNode;
+ }else{
+ gcdMap[vlNode] = newGcd;
+ }
+ }
+ }
+ }
+ return Node::null();
+}
+
+void DioSolver::saveQueue(){
+ std::deque<TrailIndex>::const_iterator iter, end;
+ for(iter = d_currentF.begin(), end = d_currentF.end(); iter != end; ++iter){
+ d_savedQueue.push_back(*iter);
+ }
+}
+
+DioSolver::TrailIndex DioSolver::impliedGcdOfOne(){
+ Node canReduce = columnGcdIsOne();
+ if(canReduce.isNull()){
+ return 0;
+ }else{
+ VarList vl = VarList::parseVarList(canReduce);
+
+ TrailIndex current;
+ Integer currentCoeff, currentGcd;
+
+ //step 1 find the first equation containing vl
+ //Set current and currentCoefficient
+ std::deque<TrailIndex>::const_iterator iter, end;
+ for(iter = d_currentF.begin(), end = d_currentF.end(); true; ++iter){
+ Assert(iter != end);
+ current = *iter;
+ Constant coeff = d_trail[current].d_eq.getPolynomial().getCoefficient(vl);
+ if(!coeff.isZero()){
+ currentCoeff = coeff.getValue().getNumerator();
+ currentGcd = currentCoeff.abs();
+
+ ++iter;
+ break;
+ }
+ }
+
+ //For the rest of the equations keep reducing until the coefficient is one
+ for(; iter != end; ++iter){
+ Trace("arith::dio") << "next round : " << currentCoeff << " " << currentGcd << endl;
+ TrailIndex inQueue = *iter;
+ Constant iqc = d_trail[inQueue].d_eq.getPolynomial().getCoefficient(vl);
+ if(!iqc.isZero()){
+ Integer inQueueCoeff = iqc.getValue().getNumerator();
+
+ //mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, mpz_t a, mpz_t b);
+ Integer g, s, t;
+ // g = a*s + b*t
+ Integer::extendedGcd(g, s, t, currentCoeff, inQueueCoeff);
+
+ Trace("arith::dio") << "extendedReduction : " << endl;
+ Trace("arith::dio") << g << " = " << s <<"*"<< currentCoeff << " + " << t <<"*"<< inQueueCoeff << endl;
+
+ Assert(g <= currentGcd);
+ if(g < currentGcd){
+ if(s.sgn() == 0){
+ Trace("arith::dio") << "extendedReduction drop" << endl;
+ Assert(inQueueCoeff.divides(currentGcd));
+ current = *iter;
+ currentCoeff = inQueueCoeff;
+ currentGcd = inQueueCoeff.abs();
+ }else{
+
+ Trace("arith::dio") << "extendedReduction combine" << endl;
+ TrailIndex next = combineEqAtIndexes(current, s, inQueue, t);
+
+ Assert(d_trail[next]
+ .d_eq.getPolynomial()
+ .getCoefficient(vl)
+ .getValue()
+ .getNumerator()
+ == g);
+
+ current = next;
+ currentCoeff = g;
+ currentGcd = g;
+ if(currentGcd == 1){
+ return current;
+ }
+ }
+ }
+ }
+ }
+ //This is not reachble as it is assured that the gcd of the column is 1
+ Unreachable();
+ }
+}
+
+bool DioSolver::processEquations(bool allowDecomposition){
+ Assert(!inConflict());
+
+ enqueueInputConstraints();
+ while(! queueEmpty() && !inConflict()){
+ moveMinimumByAbsToQueueFront();
+
+ TrailIndex minimum = d_currentF.front();
+ TrailIndex reduceIndex;
+
+ Assert(inRange(minimum));
+ Assert(!inConflict());
+
+ Trace("arith::dio") << "processEquations " << minimum << " : " << d_trail[minimum].d_eq.getNode() << endl;
+
+ Assert(queueConditions(minimum));
+
+ bool canDirectlySolve = d_trail[minimum].d_minimalMonomial.absCoefficientIsOne();
+
+ std::pair<SubIndex, TrailIndex> p;
+ if(canDirectlySolve){
+ d_currentF.pop_front();
+ p = solveIndex(minimum);
+ reduceIndex = minimum;
+ }else{
+ TrailIndex implied = impliedGcdOfOne();
+
+ if(implied != 0){
+ p = solveIndex(implied);
+ reduceIndex = implied;
+ }else if(allowDecomposition){
+ d_currentF.pop_front();
+ p = decomposeIndex(minimum);
+ reduceIndex = minimum;
+ }else {
+ // Cannot make progress without decomposeIndex
+ saveQueue();
+ break;
+ }
+ }
+
+ SubIndex subIndex = p.first;
+ TrailIndex next = p.second;
+ subAndReduceCurrentFByIndex(subIndex);
+
+ if(next != reduceIndex){
+ if(triviallyUnsat(next)){
+ raiseConflict(next);
+ }else if(! triviallySat(next) ){
+ pushToQueueBack(next);
+ }
+ }
+ }
+
+ d_currentF.clear();
+ return inConflict();
+}
+
+Node DioSolver::processEquationsForConflict(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_conflictTimer);
+ ++(d_statistics.d_conflictCalls);
+
+ Assert(!inConflict());
+ if(processEquations(true)){
+ ++(d_statistics.d_conflicts);
+ return proveIndex(getConflictIndex());
+ }else{
+ return Node::null();
+ }
+}
+
+SumPair DioSolver::processEquationsForCut(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_cutTimer);
+ ++(d_statistics.d_cutCalls);
+
+ Assert(!inConflict());
+ if(processEquations(true)){
+ ++(d_statistics.d_cuts);
+ return purifyIndex(getConflictIndex());
+ }else{
+ return SumPair::mkZero();
+ }
+}
+
+
+SumPair DioSolver::purifyIndex(TrailIndex i){
+ // TODO: "This uses the substitution trail to reverse the substitutions from the sum term. Using the proof term should be more efficient."
+
+ SumPair curr = d_trail[i].d_eq;
+
+ Constant negOne = Constant::mkConstant(-1);
+
+ for(uint32_t revIter = d_subs.size(); revIter > 0; --revIter){
+ uint32_t i2 = revIter - 1;
+ Node freshNode = d_subs[i2].d_fresh;
+ if(freshNode.isNull()){
+ continue;
+ }else{
+ Variable var(freshNode);
+ Polynomial vsum = curr.getPolynomial();
+
+ Constant a = vsum.getCoefficient(VarList(var));
+ if(!a.isZero()){
+ const SumPair& sj = d_trail[d_subs[i2].d_constraint].d_eq;
+ Assert(sj.getPolynomial().getCoefficient(VarList(var)).isOne());
+ SumPair newSi = (curr * negOne) + (sj * a);
+ Assert(newSi.getPolynomial().getCoefficient(VarList(var)).isZero());
+ curr = newSi;
+ }
+ }
+ }
+ return curr;
+}
+
+DioSolver::TrailIndex DioSolver::combineEqAtIndexes(DioSolver::TrailIndex i, const Integer& q, DioSolver::TrailIndex j, const Integer& r){
+ Constant cq = Constant::mkConstant(q);
+ Constant cr = Constant::mkConstant(r);
+
+ const SumPair& si = d_trail[i].d_eq;
+ const SumPair& sj = d_trail[j].d_eq;
+
+ Trace("arith::dio") << "combineEqAtIndexes(" << i <<","<<q<<","<<j<<","<<r<<")"<<endl;
+ Trace("arith::dio") << "d_facts[i] = " << si.getNode() << endl
+ << "d_facts[j] = " << sj.getNode() << endl;
+
+
+ SumPair newSi = (si * cq) + (sj * cr);
+
+
+ const Polynomial& pi = d_trail[i].d_proof;
+ const Polynomial& pj = d_trail[j].d_proof;
+ Polynomial newPi = (pi * cq) + (pj * cr);
+
+ TrailIndex k = d_trail.size();
+ d_trail.push_back(Constraint(newSi, newPi));
+
+
+ Trace("arith::dio") << "derived "<< newSi.getNode()
+ <<" with proof " << newPi.getNode() << endl;
+
+ return k;
+
+}
+
+void DioSolver::printQueue(){
+ Trace("arith::dio") << "DioSolver::printQueue()" << endl;
+ for(TrailIndex i = 0, last = d_trail.size(); i < last; ++i){
+ Trace("arith::dio") << "d_trail[i].d_eq = " << d_trail[i].d_eq.getNode() << endl;
+ Trace("arith::dio") << "d_trail[i].d_proof = " << d_trail[i].d_proof.getNode() << endl;
+ }
+
+ Trace("arith::dio") << "DioSolver::printSubs()" << endl;
+ for(SubIndex si=0, sN=d_subs.size(); si < sN; ++si){
+ Trace("arith::dio") << "d_subs[i] = {"
+ << "d_fresh="<< d_subs[si].d_fresh <<","
+ << "d_eliminated="<< d_subs[si].d_eliminated.getNode() <<","
+ << "d_constraint="<< d_subs[si].d_constraint <<"}" << endl;
+ Trace("arith::dio") << "d_trail[d_subs[i].d_constraint].d_eq="
+ << d_trail[d_subs[si].d_constraint].d_eq.getNode() << endl;
+ }
+}
+
+DioSolver::TrailIndex DioSolver::applyAllSubstitutionsToIndex(DioSolver::TrailIndex trailIndex){
+ TrailIndex currentIndex = trailIndex;
+ for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
+ currentIndex = applySubstitution(subIter, currentIndex);
+ }
+ return currentIndex;
+}
+
+bool DioSolver::debugSubstitutionApplies(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
+ Variable var = d_subs[si].d_eliminated;
+
+ const SumPair& curr = d_trail[ti].d_eq;
+ Polynomial vsum = curr.getPolynomial();
+
+ Constant a = vsum.getCoefficient(VarList(var));
+ return !a.isZero();
+}
+
+bool DioSolver::debugAnySubstitionApplies(DioSolver::TrailIndex i){
+ for(SubIndex subIter = 0, siEnd = d_subs.size(); subIter < siEnd; ++subIter){
+ if(debugSubstitutionApplies(subIter, i)){
+ return true;
+ }
+ }
+ return false;
+}
+
+std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::solveIndex(DioSolver::TrailIndex i){
+ const SumPair& si = d_trail[i].d_eq;
+
+ Trace("arith::dio") << "before solveIndex("<<i<<":"<<si.getNode()<< ")" << endl;
+
+#ifdef CVC5_ASSERTIONS
+ const Polynomial& p = si.getPolynomial();
+#endif
+
+ Assert(p.isIntegral());
+
+ Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
+ const Monomial av = d_trail[i].d_minimalMonomial;
+
+ VarList vl = av.getVarList();
+ Assert(vl.singleton());
+ Variable var = vl.getHead();
+ Constant a = av.getConstant();
+ Integer a_abs = a.getValue().getNumerator().abs();
+
+ Assert(a_abs == 1);
+
+ TrailIndex ci = !a.isNegative() ? scaleEqAtIndex(i, Integer(-1)) : i;
+
+ SubIndex subBy = d_subs.size();
+ d_subs.push_back(Substitution(Node::null(), var, ci));
+
+ Trace("arith::dio") << "after solveIndex " << d_trail[ci].d_eq.getNode() << " for " << av.getNode() << endl;
+ Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
+ == Constant::mkConstant(-1));
+
+ return make_pair(subBy, i);
+}
+
+std::pair<DioSolver::SubIndex, DioSolver::TrailIndex> DioSolver::decomposeIndex(DioSolver::TrailIndex i){
+ const SumPair& si = d_trail[i].d_eq;
+
+ d_usedDecomposeIndex = true;
+
+ Trace("arith::dio") << "before decomposeIndex("<<i<<":"<<si.getNode()<< ")" << endl;
+
+#ifdef CVC5_ASSERTIONS
+ const Polynomial& p = si.getPolynomial();
+#endif
+
+ Assert(p.isIntegral());
+
+ Assert(p.selectAbsMinimum() == d_trail[i].d_minimalMonomial);
+ const Monomial& av = d_trail[i].d_minimalMonomial;
+
+ VarList vl = av.getVarList();
+ Assert(vl.singleton());
+ Variable var = vl.getHead();
+ Constant a = av.getConstant();
+ Integer a_abs = a.getValue().getNumerator().abs();
+
+ Assert(a_abs > 1);
+
+ //It is not sufficient to reduce the case where abs(a) == 1 to abs(a) > 1.
+ //We need to handle both cases seperately to ensure termination.
+ Node qr = SumPair::computeQR(si, a.getValue().getNumerator());
+
+ Assert(qr.getKind() == kind::ADD);
+ Assert(qr.getNumChildren() == 2);
+ SumPair q = SumPair::parseSumPair(qr[0]);
+ SumPair r = SumPair::parseSumPair(qr[1]);
+
+ Assert(q.getPolynomial().getCoefficient(vl) == Constant::mkConstant(1));
+
+ Assert(!r.isZero());
+ Node freshNode = makeIntegerVariable();
+ Variable fresh(freshNode);
+ SumPair fresh_one=SumPair::mkSumPair(fresh);
+ SumPair fresh_a = fresh_one * a;
+
+ SumPair newSI = SumPair(fresh_one) - q;
+ // this normalizes the coefficient of var to -1
+
+
+ TrailIndex ci = d_trail.size();
+ d_trail.push_back(Constraint(newSI, Polynomial::mkZero()));
+ // no longer reference av safely!
+ addTrailElementAsLemma(ci);
+
+ Trace("arith::dio") << "Decompose ci(" << ci <<":" << d_trail[ci].d_eq.getNode()
+ << ") for " << d_trail[i].d_minimalMonomial.getNode() << endl;
+ Assert(d_trail[ci].d_eq.getPolynomial().getCoefficient(vl)
+ == Constant::mkConstant(-1));
+
+ SumPair newFact = r + fresh_a;
+
+ TrailIndex nextIndex = d_trail.size();
+ d_trail.push_back(Constraint(newFact, d_trail[i].d_proof));
+
+ SubIndex subBy = d_subs.size();
+ d_subs.push_back(Substitution(freshNode, var, ci));
+
+ Trace("arith::dio") << "Decompose nextIndex " << d_trail[nextIndex].d_eq.getNode() << endl;
+ return make_pair(subBy, nextIndex);
+}
+
+
+DioSolver::TrailIndex DioSolver::applySubstitution(DioSolver::SubIndex si, DioSolver::TrailIndex ti){
+ Variable var = d_subs[si].d_eliminated;
+ TrailIndex subIndex = d_subs[si].d_constraint;
+
+ const SumPair& curr = d_trail[ti].d_eq;
+ Polynomial vsum = curr.getPolynomial();
+
+ Constant a = vsum.getCoefficient(VarList(var));
+ Assert(a.isIntegral());
+ if(!a.isZero()){
+ Integer one(1);
+ TrailIndex afterSub = combineEqAtIndexes(ti, one, subIndex, a.getValue().getNumerator());
+ Assert(d_trail[afterSub]
+ .d_eq.getPolynomial()
+ .getCoefficient(VarList(var))
+ .isZero());
+ return afterSub;
+ }else{
+ return ti;
+ }
+}
+
+
+DioSolver::TrailIndex DioSolver::reduceByGCD(DioSolver::TrailIndex ti){
+ const SumPair& sp = d_trail[ti].d_eq;
+ Polynomial vsum = sp.getPolynomial();
+ Constant c = sp.getConstant();
+
+ Trace("arith::dio") << "reduceByGCD " << vsum.getNode() << endl;
+ Assert(!vsum.isConstant());
+ Integer g = vsum.gcd();
+ Assert(g >= 1);
+ Trace("arith::dio") << "gcd("<< vsum.getNode() <<")=" << g << " " << c.getValue() << endl;
+ if(g.divides(c.getValue().getNumerator())){
+ if(g > 1){
+ return scaleEqAtIndex(ti, g);
+ }else{
+ return ti;
+ }
+ }else{
+ raiseConflict(ti);
+ return ti;
+ }
+}
+
+bool DioSolver::triviallySat(TrailIndex i){
+ const SumPair& eq = d_trail[i].d_eq;
+ if(eq.isConstant()){
+ return eq.getConstant().isZero();
+ }else{
+ return false;
+ }
+}
+
+bool DioSolver::triviallyUnsat(DioSolver::TrailIndex i){
+ const SumPair& eq = d_trail[i].d_eq;
+ if(eq.isConstant()){
+ return !eq.getConstant().isZero();
+ }else{
+ return false;
+ }
+}
+
+
+bool DioSolver::gcdIsOne(DioSolver::TrailIndex i){
+ const SumPair& eq = d_trail[i].d_eq;
+ return eq.gcd() == Integer(1);
+}
+
+void DioSolver::subAndReduceCurrentFByIndex(DioSolver::SubIndex subIndex){
+ size_t N = d_currentF.size();
+
+ size_t readIter = 0, writeIter = 0;
+ for(; readIter < N && !inConflict(); ++readIter){
+ TrailIndex curr = d_currentF[readIter];
+ TrailIndex nextTI = applySubstitution(subIndex, curr);
+ if(nextTI == curr){
+ d_currentF[writeIter] = curr;
+ ++writeIter;
+ }else{
+ Assert(nextTI != curr);
+
+ if(triviallyUnsat(nextTI)){
+ raiseConflict(nextTI);
+ }else if(!triviallySat(nextTI)){
+ TrailIndex nextNextTI = reduceByGCD(nextTI);
+
+ if(!(inConflict() || anyCoefficientExceedsMaximum(nextNextTI))){
+ Assert(queueConditions(nextNextTI));
+ d_currentF[writeIter] = nextNextTI;
+ ++writeIter;
+ }
+ }
+ }
+ }
+ if(!inConflict() && writeIter < N){
+ d_currentF.resize(writeIter);
+ }
+}
+
+void DioSolver::addTrailElementAsLemma(TrailIndex i) {
+ if (options().arith.exportDioDecompositions)
+ {
+ d_decompositionLemmaQueue.push(i);
+ }
+}
+
+Node DioSolver::trailIndexToEquality(TrailIndex i) const {
+ const SumPair& sp = d_trail[i].d_eq;
+ Node n = sp.getNode();
+ Node zero =
+ NodeManager::currentNM()->mkConstRealOrInt(n.getType(), Rational(0));
+ Node eq = n.eqNode(zero);
+ return eq;
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Morgan Deters, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * A Diophantine equation solver for the theory of arithmetic.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__DIO_SOLVER_H
+#define CVC5__THEORY__ARITH__DIO_SOLVER_H
+
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "context/cdlist.h"
+#include "context/cdmaybe.h"
+#include "context/cdo.h"
+#include "context/cdqueue.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/normal_form.h"
+#include "util/rational.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class DioSolver : protected EnvObj
+{
+ private:
+ typedef size_t TrailIndex;
+ typedef size_t InputConstraintIndex;
+ typedef size_t SubIndex;
+
+ std::vector<Variable> d_proofVariablePool;
+ /** Sat context dependent. */
+ context::CDO<size_t> d_lastUsedProofVariable;
+
+ /**
+ * The set of input constraints is stored in a CDList.
+ * Each constraint point to an element of the trail.
+ */
+ struct InputConstraint {
+ Node d_reason;
+ TrailIndex d_trailPos;
+ InputConstraint(Node reason, TrailIndex pos) : d_reason(reason), d_trailPos(pos) {}
+ };
+ context::CDList<InputConstraint> d_inputConstraints;
+
+ /**
+ * This is the next input constraint to handle.
+ */
+ context::CDO<size_t> d_nextInputConstraintToEnqueue;
+
+ /**
+ * We maintain a map from the variables associated with proofs to an input constraint.
+ * These variables can then be used in polynomial manipulations.
+ */
+ typedef std::unordered_map<Node, InputConstraintIndex>
+ NodeToInputConstraintIndexMap;
+ NodeToInputConstraintIndexMap d_varToInputConstraintMap;
+
+ Node proofVariableToReason(const Variable& v) const{
+ Assert(d_varToInputConstraintMap.find(v.getNode())
+ != d_varToInputConstraintMap.end());
+ InputConstraintIndex pos = (*(d_varToInputConstraintMap.find(v.getNode()))).second;
+ Assert(pos < d_inputConstraints.size());
+ return d_inputConstraints[pos].d_reason;
+ }
+
+ /**
+ * The main work horse of the algorithm, the trail of constraints.
+ * Each constraint is a SumPair that implicitly represents an equality against 0.
+ * d_trail[i].d_eq = (+ c (+ [(* coeff var)])) representing (+ [(* coeff var)]) = -c
+ * Each constraint has a proof in terms of a linear combination of the input constraints.
+ * d_trail[i].d_proof
+ *
+ * Each Constraint also a monomial in d_eq.getPolynomial()
+ * of minimal absolute value by the coefficients.
+ * d_trail[i].d_minimalMonomial
+ *
+ * See Alberto's paper for how linear proofs are maintained for the abstract
+ * state machine in rules (7), (8) and (9).
+ */
+ struct Constraint {
+ SumPair d_eq;
+ Polynomial d_proof;
+ Monomial d_minimalMonomial;
+ Constraint(const SumPair& eq, const Polynomial& p) :
+ d_eq(eq), d_proof(p), d_minimalMonomial(d_eq.getPolynomial().selectAbsMinimum())
+ {}
+ };
+ 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);
+ // }
+ // };
+
+ /**
+ * A substitution is stored as a constraint in the trail together with
+ * the variable to be eliminated, and a fresh variable if one was introduced.
+ * The variable d_subs[i].d_eliminated is substituted using the implicit equality in
+ * d_trail[d_subs[i].d_constraint]
+ * - d_subs[i].d_eliminated is normalized to have coefficient -1 in
+ * d_trail[d_subs[i].d_constraint].
+ * - d_subs[i].d_fresh is either Node::null() or it is variable it is normalized
+ * to have coefficient 1 in d_trail[d_subs[i].d_constraint].
+ */
+ struct Substitution {
+ Node d_fresh;
+ Variable d_eliminated;
+ TrailIndex d_constraint;
+ Substitution(Node f, const Variable& e, TrailIndex c) :
+ d_fresh(f), d_eliminated(e), d_constraint(c)
+ {}
+ };
+ context::CDList<Substitution> d_subs;
+
+ /**
+ * This is the queue of constraints to be processed in the current context level.
+ * This is to be empty upon entering solver and cleared upon leaving the solver.
+ *
+ * All elements in currentF:
+ * - are fully substituted according to d_subs.
+ * - !isConstant().
+ * - If the element is (+ constant (+ [(* coeff var)] )), then the gcd(coeff) = 1
+ */
+ std::deque<TrailIndex> d_currentF;
+ context::CDList<TrailIndex> d_savedQueue;
+ context::CDO<size_t> d_savedQueueIndex;
+ context::CDMaybe<TrailIndex> d_conflictIndex;
+
+ /**
+ * Drop derived constraints with a coefficient length larger than
+ * the maximum input constraints length than 2**MAX_GROWTH_RATE.
+ */
+ context::CDO<uint32_t> d_maxInputCoefficientLength;
+ static constexpr uint32_t MAX_GROWTH_RATE = 3;
+
+ /** Returns true if the element on the trail should be dropped.*/
+ bool anyCoefficientExceedsMaximum(TrailIndex j) const;
+
+ /**
+ * Is true if decomposeIndex has been used in this context.
+ */
+ context::CDO<bool> d_usedDecomposeIndex;
+
+ context::CDO<SubIndex> d_lastPureSubstitution;
+ context::CDO<SubIndex> d_pureSubstitionIter;
+
+ /**
+ * Decomposition lemma queue.
+ */
+ context::CDQueue<TrailIndex> d_decompositionLemmaQueue;
+
+ public:
+ /** Construct a Diophantine equation solver with the given context. */
+ DioSolver(Env& env);
+
+ /** Returns true if the substitutions use no new variables. */
+ bool hasMorePureSubstitutions() const
+ {
+ return d_pureSubstitionIter < d_lastPureSubstitution;
+ }
+
+ Node nextPureSubstitution();
+
+ /**
+ * Adds an equality to the queue of the DioSolver.
+ * orig is blamed in a conflict.
+ * orig can either be of the form (= p c) or (and ub lb).
+ * where ub is either (leq p c) or (not (> p [- c 1])), and
+ * where lb is either (geq p c) or (not (< p [+ c 1]))
+ *
+ * If eq cannot be used, this constraint is dropped.
+ */
+ void pushInputConstraint(const Comparison& eq, Node reason);
+
+ /**
+ * Processes the queue looking for any conflict.
+ * If a conflict is found, this returns conflict.
+ * Otherwise, it returns null.
+ * The conflict is guarenteed to be over literals given in addEquality.
+ */
+ Node processEquationsForConflict();
+
+ /**
+ * Processes the queue looking for an integer unsatisfiable cutting plane.
+ * If such a plane is found this returns an entailed plane using no
+ * fresh variables.
+ */
+ SumPair processEquationsForCut();
+
+private:
+ /** Returns true if the TrailIndex refers to a element in the trail. */
+ bool inRange(TrailIndex i) const{
+ return i < d_trail.size();
+ }
+
+ Node columnGcdIsOne() const;
+
+
+ /**
+ * Returns true if the context dependent flag for conflicts
+ * has been raised.
+ */
+ bool inConflict() const { return d_conflictIndex.isSet(); }
+
+ /** Raises a conflict at the index ti. */
+ void raiseConflict(TrailIndex ti){
+ Assert(!inConflict());
+ d_conflictIndex.set(ti);
+ }
+
+ /** Returns the conflict index. */
+ TrailIndex getConflictIndex() const{
+ Assert(inConflict());
+ return d_conflictIndex.get();
+ }
+
+ /**
+ * Allocates a "unique" proof variable.
+ * This variable is fresh with respect to the context.
+ * Returns index of the variable in d_variablePool;
+ */
+ size_t allocateProofVariable();
+
+
+ /** Empties the unproccessed input constraints into the queue. */
+ void enqueueInputConstraints();
+
+ /**
+ * Returns true if an input equality is in the map.
+ * This is expensive and is only for debug assertions.
+ */
+ bool debugEqualityInInputEquations(Node eq);
+
+ /** Applies the substitution at subIndex to currentF. */
+ void subAndReduceCurrentFByIndex(SubIndex d_subIndex);
+
+ /**
+ * Takes as input a TrailIndex i and an integer that divides d_trail[i].d_eq, and
+ * returns a TrailIndex j s.t.
+ * d_trail[j].d_eq = (1/g) d_trail[i].d_eq
+ * and
+ * d_trail[j].d_proof = (1/g) d_trail[i].d_proof.
+ *
+ * g must be non-zero.
+ *
+ * This corresponds to an application of Alberto's rule (7).
+ */
+ TrailIndex scaleEqAtIndex(TrailIndex i, const Integer& g);
+
+
+ /**
+ * Takes as input TrailIndex's i and j and Integer's q and r and a TrailIndex k s.t.
+ * d_trail[k].d_eq == d_trail[i].d_eq * q + d_trail[j].d_eq * r
+ * and
+ * d_trail[k].d_proof == d_trail[i].d_proof * q + d_trail[j].d_proof * r
+ *
+ * This corresponds to an application of Alberto's rule (8).
+ */
+ TrailIndex combineEqAtIndexes(TrailIndex i, const Integer& q, TrailIndex j, const Integer& r);
+
+ /**
+ * Decomposes the equation at index ti of trail by the variable
+ * with the lowest coefficient.
+ * This corresponds to an application of Alberto's rule (9).
+ *
+ * Returns a pair of a SubIndex and a TrailIndex.
+ * The SubIndex is the index of a newly introduced substition.
+ */
+ std::pair<SubIndex, TrailIndex> decomposeIndex(TrailIndex ti);
+
+ /** Solves the index at ti for the value in minimumMonomial. */
+ std::pair<SubIndex, TrailIndex> solveIndex(TrailIndex ti);
+
+ /** Prints the queue for debugging purposes to Trace("arith::dio"). */
+ void printQueue();
+
+ /**
+ * Exhaustively applies all substitutions discovered to an element of the trail.
+ * Returns a TrailIndex corresponding to the substitutions being applied.
+ */
+ TrailIndex applyAllSubstitutionsToIndex(TrailIndex i);
+
+ /**
+ * Applies a substitution to an element in the trail.
+ */
+ TrailIndex applySubstitution(SubIndex s, TrailIndex i);
+
+ /**
+ * Reduces the trail node at i by the gcd of the variables.
+ * Returns the new trail element.
+ *
+ * This raises the conflict flag if unsat is detected.
+ */
+ TrailIndex reduceByGCD(TrailIndex i);
+
+ /**
+ * Returns true if i'th element in the trail is trivially true.
+ * (0 = 0)
+ */
+ bool triviallySat(TrailIndex t);
+
+ /**
+ * Returns true if i'th element in the trail is trivially unsatisfiable.
+ * (1 = 0)
+ */
+ bool triviallyUnsat(TrailIndex t);
+
+ /** Returns true if the gcd of the i'th element of the trail is 1.*/
+ bool gcdIsOne(TrailIndex t);
+
+ bool debugAnySubstitionApplies(TrailIndex t);
+ bool debugSubstitutionApplies(SubIndex si, TrailIndex ti);
+
+
+ /** Returns true if the queue of nodes to process is empty. */
+ bool queueEmpty() const;
+
+ bool queueConditions(TrailIndex t);
+
+
+ void pushToQueueBack(TrailIndex t){
+ Assert(queueConditions(t));
+ d_currentF.push_back(t);
+ }
+
+ void pushToQueueFront(TrailIndex t){
+ Assert(queueConditions(t));
+ d_currentF.push_front(t);
+ }
+
+ /**
+ * Moves the minimum Constraint by absolute value of the minimum coefficient to
+ * the front of the queue.
+ */
+ void moveMinimumByAbsToQueueFront();
+
+ void saveQueue();
+
+ TrailIndex impliedGcdOfOne();
+
+
+ /**
+ * Processing the current set of equations.
+ *
+ * decomposeIndex() rule is only applied if allowDecomposition is true.
+ */
+ bool processEquations(bool allowDecomposition);
+
+ /**
+ * Constructs a proof from any d_trail[i] in terms of input literals.
+ */
+ Node proveIndex(TrailIndex i);
+
+ /**
+ * Returns the SumPair in d_trail[i].d_eq with all of the fresh variables purified out.
+ */
+ SumPair purifyIndex(TrailIndex i);
+
+public:
+ bool hasMoreDecompositionLemmas() const{
+ return !d_decompositionLemmaQueue.empty();
+ }
+ Node nextDecompositionLemma() {
+ Assert(hasMoreDecompositionLemmas());
+ TrailIndex front = d_decompositionLemmaQueue.front();
+ d_decompositionLemmaQueue.pop();
+ return trailIndexToEquality(front);
+ }
+private:
+ Node trailIndexToEquality(TrailIndex i) const;
+ void addTrailElementAsLemma(TrailIndex i);
+
+public:
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+
+ IntStat d_conflictCalls;
+ IntStat d_cutCalls;
+
+ IntStat d_cuts;
+ IntStat d_conflicts;
+
+ TimerStat d_conflictTimer;
+ TimerStat d_cutTimer;
+
+ Statistics();
+ };
+
+ Statistics d_statistics;
+}; /* class DioSolver */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__DIO_SOLVER_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+#include "theory/arith/linear/dual_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/env.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+DualSimplexDecisionProcedure::DualSimplexDecisionProcedure(
+ Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+ d_pivotsInRound(),
+ d_statistics(d_pivots)
+{ }
+
+DualSimplexDecisionProcedure::Statistics::Statistics(uint32_t& pivots)
+ : d_statUpdateConflicts(smtStatisticsRegistry().registerInt(
+ "theory::arith::dual::UpdateConflicts")),
+ d_processSignalsTime(smtStatisticsRegistry().registerTimer(
+ "theory::arith::dual::findConflictOnTheQueueTime")),
+ d_simplexConflicts(smtStatisticsRegistry().registerInt(
+ "theory::arith::dual::simplexConflicts")),
+ d_recentViolationCatches(smtStatisticsRegistry().registerInt(
+ "theory::arith::dual::recentViolationCatches")),
+ d_searchTime(smtStatisticsRegistry().registerTimer(
+ "theory::arith::dual::searchTime")),
+ d_finalCheckPivotCounter(
+ smtStatisticsRegistry().registerReference<uint32_t>(
+ "theory::arith::dual::lastPivots", pivots))
+{
+}
+
+Result::Status DualSimplexDecisionProcedure::dualFindModel(bool exactResult)
+{
+ Assert(d_conflictVariables.empty());
+
+ d_pivots = 0;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Trace("arith::findModel") << "dualFindModel() trivial" << endl;
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+
+ if(processSignals()){
+ d_conflictVariables.purge();
+
+ Trace("arith::findModel") << "dualFindModel() early conflict" << endl;
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Trace("arith::findModel") << "dualFindModel() fixed itself" << endl;
+ Assert(!d_errorSet.moreSignals());
+ return Result::SAT;
+ }
+
+ Trace("arith::findModel") << "dualFindModel() start non-trivial" << endl;
+
+ Result::Status result = Result::UNKNOWN;
+
+ exactResult |= d_varOrderPivotLimit < 0;
+
+ uint32_t checkPeriod = options().arith.arithSimplexCheckPeriod;
+ if (result == Result::UNKNOWN)
+ {
+ uint32_t numDifferencePivots = options().arith.arithHeuristicPivots < 0
+ ? d_numVariables + 1
+ : options().arith.arithHeuristicPivots;
+ // The signed to unsigned conversion is safe.
+ if(numDifferencePivots > 0){
+
+ d_errorSet.setSelectionRule(d_heuristicRule);
+ if(searchForFeasibleSolution(numDifferencePivots)){
+ result = Result::UNSAT;
+ }
+ }
+ }
+ Assert(!d_errorSet.moreSignals());
+
+ if(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+ if(exactResult){
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+ while(!d_errorSet.errorEmpty() && result != Result::UNSAT){
+ Assert(checkPeriod > 0);
+ if(searchForFeasibleSolution(checkPeriod)){
+ result = Result::UNSAT;
+ }
+ }
+ }
+ else if (d_varOrderPivotLimit > 0)
+ {
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::VAR_ORDER);
+ if (searchForFeasibleSolution(d_varOrderPivotLimit))
+ {
+ result = Result::UNSAT;
+ }
+ }
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+ {
+ result = Result::SAT;
+ }
+
+ d_pivotsInRound.purge();
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Trace("arith::findModel") << "end findModel() " << result << endl;
+
+ return result;
+}
+
+//corresponds to Check() in dM06
+//template <SimplexDecisionProcedure::PreferenceFunction pf>
+bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_searchTime);
+
+ Trace("arith") << "searchForFeasibleSolution" << endl;
+ Assert(remainingIterations > 0);
+
+ while(remainingIterations > 0 && !d_errorSet.focusEmpty()){
+ if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+ Assert(d_conflictVariables.empty());
+ ArithVar x_i = d_errorSet.topFocusVariable();
+
+ Trace("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
+ if(x_i == ARITHVAR_SENTINEL){
+ Trace("arith::update") << "No inconsistent variables" << endl;
+ return false; //sat
+ }
+
+ --remainingIterations;
+
+ bool useVarOrderPivot =
+ d_pivotsInRound.count(x_i) >= options().arith.arithPivotThreshold;
+ if(!useVarOrderPivot){
+ d_pivotsInRound.add(x_i);
+ }
+
+ Trace("arith::update") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
+ << " use " << useVarOrderPivot << " threshold "
+ << options().arith.arithPivotThreshold << std::endl;
+
+ LinearEqualityModule::VarPreferenceFunction pf = useVarOrderPivot ?
+ &LinearEqualityModule::minVarOrder : &LinearEqualityModule::minBoundAndColLength;
+
+ //DeltaRational beta_i = d_variables.getAssignment(x_i);
+ ArithVar x_j = ARITHVAR_SENTINEL;
+
+ int32_t prevErrorSize CVC5_UNUSED = d_errorSet.errorSize();
+
+ if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){
+ x_j = d_linEq.selectSlackUpperBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ Unreachable();
+ // ++(d_statistics.d_statUpdateConflicts);
+ // reportConflict(x_i);
+ // ++(d_statistics.d_simplexConflicts);
+ // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat
+ // d_conflictVariable = x_i;
+ // reportConflict(conflict);
+ // return true;
+ }else{
+ const DeltaRational& l_i = d_variables.getLowerBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, l_i);
+ }
+ }else if(d_variables.cmpAssignmentUpperBound(x_i) > 0){
+ x_j = d_linEq.selectSlackLowerBound(x_i, pf);
+ if(x_j == ARITHVAR_SENTINEL ){
+ Unreachable();
+ // ++(d_statistics.d_statUpdateConflicts);
+ // reportConflict(x_i);
+ // ++(d_statistics.d_simplexConflicts);
+ // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat
+ // d_conflictVariable = x_i;
+ // reportConflict(conflict);
+ // return true;
+ }else{
+ const DeltaRational& u_i = d_variables.getUpperBound(x_i);
+ d_linEq.pivotAndUpdate(x_i, x_j, u_i);
+ }
+ }
+ Assert(x_j != ARITHVAR_SENTINEL);
+
+ bool conflict = processSignals();
+ int32_t currErrorSize CVC5_UNUSED = d_errorSet.errorSize();
+ d_pivots++;
+
+ if(TraceIsOn("arith::dual")){
+ Trace("arith::dual")
+ << "#" << d_pivots
+ << " c" << conflict
+ << " d" << (prevErrorSize - currErrorSize)
+ << " f" << d_errorSet.inError(x_j)
+ << " h" << d_conflictVariables.isMember(x_j)
+ << " " << x_i << "->" << x_j
+ << endl;
+ }
+
+ if(conflict){
+ return true;
+ }
+ }
+ Assert(!d_errorSet.focusEmpty() || d_errorSet.errorEmpty());
+ Assert(remainingIterations == 0 || d_errorSet.focusEmpty());
+ Assert(d_errorSet.noSignals());
+
+ return false;
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ * by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ * These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/simplex.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class DualSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ DualSimplexDecisionProcedure(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override
+ {
+ return dualFindModel(exactResult);
+ }
+
+private:
+
+ /**
+ * Maps a variable to how many times they have been used as a pivot in the
+ * simplex search.
+ */
+ DenseMultiset d_pivotsInRound;
+
+ Result::Status dualFindModel(bool exactResult);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+
+ bool processSignals(){
+ TimerStat &timer = d_statistics.d_processSignalsTime;
+ IntStat& conflictStat = d_statistics.d_recentViolationCatches;
+ return standardProcessSignals(timer, conflictStat);
+ }
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statUpdateConflicts;
+ TimerStat d_processSignalsTime;
+ IntStat d_simplexConflicts;
+ IntStat d_recentViolationCatches;
+ TimerStat d_searchTime;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(uint32_t& pivots);
+ } d_statistics;
+};/* class DualSimplexDecisionProcedure */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andres Noetzli, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/error_set.h"
+
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ErrorInformation::ErrorInformation()
+ : d_variable(ARITHVAR_SENTINEL),
+ d_violated(NullConstraint),
+ d_sgn(0),
+ d_relaxed(false),
+ d_inFocus(false),
+ d_handle(),
+ d_amount(nullptr),
+ d_metric(0)
+{
+ Trace("arith::error::mem")
+ << "def constructor " << d_variable << " " << d_amount.get() << endl;
+}
+
+ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn)
+ : d_variable(var),
+ d_violated(vio),
+ d_sgn(sgn),
+ d_relaxed(false),
+ d_inFocus(false),
+ d_handle(),
+ d_amount(nullptr),
+ d_metric(0)
+{
+ Assert(debugInitialized());
+ Trace("arith::error::mem")
+ << "constructor " << d_variable << " " << d_amount.get() << endl;
+}
+
+
+ErrorInformation::~ErrorInformation() {
+ Assert(d_relaxed != true);
+ if (d_amount != nullptr)
+ {
+ Trace("arith::error::mem") << d_amount.get() << endl;
+ Trace("arith::error::mem")
+ << "destroy " << d_variable << " " << d_amount.get() << endl;
+ d_amount = nullptr;
+ }
+}
+
+ErrorInformation::ErrorInformation(const ErrorInformation& ei)
+ : d_variable(ei.d_variable)
+ , d_violated(ei.d_violated)
+ , d_sgn(ei.d_sgn)
+ , d_relaxed(ei.d_relaxed)
+ , d_inFocus(ei.d_inFocus)
+ , d_handle(ei.d_handle)
+ , d_metric(0)
+{
+ if (ei.d_amount == nullptr)
+ {
+ d_amount = nullptr;
+ }
+ else
+ {
+ d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
+ }
+ Trace("arith::error::mem")
+ << "copy const " << d_variable << " " << d_amount.get() << endl;
+}
+
+ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){
+ d_variable = ei.d_variable;
+ d_violated = ei.d_violated;
+ d_sgn = ei.d_sgn;
+ d_relaxed = (ei.d_relaxed);
+ d_inFocus = (ei.d_inFocus);
+ d_handle = (ei.d_handle);
+ d_metric = ei.d_metric;
+ if (d_amount != nullptr && ei.d_amount != nullptr)
+ {
+ Trace("arith::error::mem")
+ << "assignment assign " << d_variable << " " << d_amount.get() << endl;
+ *d_amount = *ei.d_amount;
+ }
+ else if (ei.d_amount != nullptr)
+ {
+ d_amount = std::make_unique<DeltaRational>(*ei.d_amount);
+ Trace("arith::error::mem")
+ << "assignment alloc " << d_variable << " " << d_amount.get() << endl;
+ }
+ else if (d_amount != nullptr)
+ {
+ Trace("arith::error::mem")
+ << "assignment release " << d_variable << " " << d_amount.get() << endl;
+ d_amount = nullptr;
+ }
+ else
+ {
+ d_amount = nullptr;
+ }
+ return *this;
+}
+
+void ErrorInformation::reset(ConstraintP c, int sgn){
+ Assert(!isRelaxed());
+ Assert(c != NullConstraint);
+ d_violated = c;
+ d_sgn = sgn;
+
+ if (d_amount != nullptr)
+ {
+ Trace("arith::error::mem")
+ << "reset " << d_variable << " " << d_amount.get() << endl;
+ d_amount = nullptr;
+ }
+}
+
+void ErrorInformation::setAmount(const DeltaRational& am){
+ if (d_amount == nullptr)
+ {
+ d_amount = std::make_unique<DeltaRational>();
+ Trace("arith::error::mem")
+ << "setAmount " << d_variable << " " << d_amount.get() << endl;
+ }
+ (*d_amount) = am;
+}
+
+ErrorSet::Statistics::Statistics()
+ : d_enqueues(
+ smtStatisticsRegistry().registerInt("theory::arith::pqueue::enqueues")),
+ d_enqueuesCollection(smtStatisticsRegistry().registerInt(
+ "theory::arith::pqueue::enqueuesCollection")),
+ d_enqueuesDiffMode(smtStatisticsRegistry().registerInt(
+ "theory::arith::pqueue::enqueuesDiffMode")),
+ d_enqueuesVarOrderMode(smtStatisticsRegistry().registerInt(
+ "theory::arith::pqueue::enqueuesVarOrderMode")),
+ d_enqueuesCollectionDuplicates(smtStatisticsRegistry().registerInt(
+ "theory::arith::pqueue::enqueuesCollectionDuplicates")),
+ d_enqueuesVarOrderModeDuplicates(smtStatisticsRegistry().registerInt(
+ "theory::arith::pqueue::enqueuesVarOrderModeDuplicates"))
+{
+}
+
+ErrorSet::ErrorSet(ArithVariables& vars,
+ TableauSizes tabSizes,
+ BoundCountingLookup lookups)
+ : d_variables(vars),
+ d_errInfo(),
+ d_selectionRule(options::ErrorSelectionRule::VAR_ORDER),
+ d_focus(ComparatorPivotRule(this, d_selectionRule)),
+ d_outOfFocus(),
+ d_signals(),
+ d_tableauSizes(tabSizes),
+ d_boundLookup(lookups)
+{}
+
+options::ErrorSelectionRule ErrorSet::getSelectionRule() const
+{
+ return d_selectionRule;
+}
+
+void ErrorSet::recomputeAmount(ErrorInformation& ei,
+ options::ErrorSelectionRule rule)
+{
+ switch(rule){
+ case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+ case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(ei.getVariable()));
+ break;
+ case options::ErrorSelectionRule::SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ break;
+ case options::ErrorSelectionRule::VAR_ORDER:
+ // do nothing
+ break;
+ }
+}
+
+void ErrorSet::setSelectionRule(options::ErrorSelectionRule rule)
+{
+ if(rule != getSelectionRule()){
+ FocusSet into(ComparatorPivotRule(this, rule));
+ FocusSet::const_iterator iter = d_focus.begin();
+ FocusSet::const_iterator i_end = d_focus.end();
+ for(; iter != i_end; ++iter){
+ ArithVar v = *iter;
+ ErrorInformation& ei = d_errInfo.get(v);
+ if(ei.inFocus()){
+ recomputeAmount(ei, rule);
+ FocusSetHandle handle = into.push(v);
+ ei.setHandle(handle);
+ }
+ }
+ d_focus.swap(into);
+ d_selectionRule = rule;
+ }
+ Assert(getSelectionRule() == rule);
+}
+
+ComparatorPivotRule::ComparatorPivotRule(const ErrorSet* es,
+ options::ErrorSelectionRule r)
+ : d_errorSet(es), d_rule(r)
+{}
+
+bool ComparatorPivotRule::operator()(ArithVar v, ArithVar u) const {
+ switch(d_rule){
+ case options::ErrorSelectionRule::VAR_ORDER:
+ // This needs to be the reverse of the minVariableOrder
+ return v > u;
+ case options::ErrorSelectionRule::SUM_METRIC:
+ {
+ uint32_t v_metric = d_errorSet->getMetric(v);
+ uint32_t u_metric = d_errorSet->getMetric(u);
+ if(v_metric == u_metric){
+ return v > u;
+ }else{
+ return v_metric > u_metric;
+ }
+ }
+ case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+ {
+ const DeltaRational& vamt = d_errorSet->getAmount(v);
+ const DeltaRational& uamt = d_errorSet->getAmount(u);
+ int cmp = vamt.cmp(uamt);
+ if(cmp == 0){
+ return v > u;
+ }else{
+ return cmp > 0;
+ }
+ }
+ case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+ {
+ const DeltaRational& vamt = d_errorSet->getAmount(v);
+ const DeltaRational& uamt = d_errorSet->getAmount(u);
+ int cmp = vamt.cmp(uamt);
+ if(cmp == 0){
+ return v > u;
+ }else{
+ return cmp < 0;
+ }
+ }
+ }
+ Unreachable();
+}
+
+void ErrorSet::update(ErrorInformation& ei){
+ if(ei.inFocus()){
+
+ switch(getSelectionRule()){
+ case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+ case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(ei.getVariable()));
+ d_focus.update(ei.getHandle(), ei.getVariable());
+ break;
+ case options::ErrorSelectionRule::SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ d_focus.update(ei.getHandle(), ei.getVariable());
+ break;
+ case options::ErrorSelectionRule::VAR_ORDER:
+ // do nothing
+ break;
+ }
+ }
+}
+
+/** A variable becomes satisfied. */
+void ErrorSet::transitionVariableOutOfError(ArithVar v) {
+ Assert(!inconsistent(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(ei.debugInitialized());
+ if(ei.isRelaxed()){
+ ConstraintP viol = ei.getViolated();
+ if(ei.sgn() > 0){
+ d_variables.setLowerBoundConstraint(viol);
+ }else{
+ d_variables.setUpperBoundConstraint(viol);
+ }
+ Assert(!inconsistent(v));
+ ei.setUnrelaxed();
+ }
+ if(ei.inFocus()){
+ d_focus.erase(ei.getHandle());
+ ei.setInFocus(false);
+ }
+ d_errInfo.remove(v);
+}
+
+
+void ErrorSet::transitionVariableIntoError(ArithVar v) {
+ Assert(inconsistent(v));
+ bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0;
+ int sgn = vilb ? 1 : -1;
+ 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);
+
+ switch(getSelectionRule()){
+ case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+ case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(v));
+ break;
+ case options::ErrorSelectionRule::SUM_METRIC:
+ ei.setMetric(sumMetric(ei.getVariable()));
+ break;
+ case options::ErrorSelectionRule::VAR_ORDER:
+ // do nothing
+ break;
+ }
+ ei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ ei.setHandle(handle);
+}
+
+void ErrorSet::dropFromFocus(ArithVar v) {
+ Assert(inError(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(ei.inFocus());
+ d_focus.erase(ei.getHandle());
+ ei.setInFocus(false);
+ d_outOfFocus.push_back(v);
+}
+
+void ErrorSet::addBackIntoFocus(ArithVar v) {
+ Assert(inError(v));
+ ErrorInformation& ei = d_errInfo.get(v);
+ Assert(!ei.inFocus());
+ switch(getSelectionRule()){
+ case options::ErrorSelectionRule::MINIMUM_AMOUNT:
+ case options::ErrorSelectionRule::MAXIMUM_AMOUNT:
+ ei.setAmount(computeDiff(v));
+ break;
+ case options::ErrorSelectionRule::SUM_METRIC:
+ ei.setMetric(sumMetric(v));
+ break;
+ case options::ErrorSelectionRule::VAR_ORDER:
+ // do nothing
+ break;
+ }
+
+ ei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ ei.setHandle(handle);
+}
+
+void ErrorSet::blur(){
+ while(!d_outOfFocus.empty()){
+ ArithVar v = d_outOfFocus.back();
+ d_outOfFocus.pop_back();
+
+ if(inError(v) && !inFocus(v)){
+ addBackIntoFocus(v);
+ }
+ }
+}
+
+
+
+int ErrorSet::popSignal() {
+ ArithVar back = d_signals.back();
+ d_signals.pop_back();
+
+ if(inError(back)){
+ ErrorInformation& ei = d_errInfo.get(back);
+ int prevSgn = ei.sgn();
+ int focusSgn = ei.focusSgn();
+ bool vilb = d_variables.cmpAssignmentLowerBound(back) < 0;
+ bool viub = d_variables.cmpAssignmentUpperBound(back) > 0;
+ if(vilb || viub){
+ Assert(!vilb || !viub);
+ int currSgn = vilb ? 1 : -1;
+ if(currSgn != prevSgn){
+ ConstraintP curr = vilb ? d_variables.getLowerBoundConstraint(back)
+ : d_variables.getUpperBoundConstraint(back);
+ ei.reset(curr, currSgn);
+ }
+ update(ei);
+ }else{
+ transitionVariableOutOfError(back);
+ }
+ return focusSgn;
+ }else if(inconsistent(back)){
+ transitionVariableIntoError(back);
+ }
+ return 0;
+}
+
+void ErrorSet::clear(){
+ // Nothing should be relaxed!
+ d_signals.clear();
+ d_errInfo.purge();
+ d_focus.clear();
+}
+
+void ErrorSet::clearFocus(){
+ for(ErrorSet::focus_iterator i =focusBegin(), i_end = focusEnd(); i != i_end; ++i){
+ ArithVar f = *i;
+ ErrorInformation& fei = d_errInfo.get(f);
+ fei.setInFocus(false);
+ d_outOfFocus.push_back(f);
+ }
+ d_focus.clear();
+}
+
+void ErrorSet::reduceToSignals(){
+ for(error_iterator ei=errorBegin(), ei_end=errorEnd(); ei != ei_end; ++ei){
+ ArithVar curr = *ei;
+ signalVariable(curr);
+ }
+
+ d_errInfo.purge();
+ d_focus.clear();
+ d_outOfFocus.clear();
+}
+
+DeltaRational ErrorSet::computeDiff(ArithVar v) const{
+ Assert(inconsistent(v));
+ const DeltaRational& beta = d_variables.getAssignment(v);
+ DeltaRational diff = d_variables.cmpAssignmentLowerBound(v) < 0 ?
+ d_variables.getLowerBound(v) - beta:
+ beta - d_variables.getUpperBound(v);
+
+ Assert(diff.sgn() > 0);
+ return diff;
+}
+
+void ErrorSet::debugPrint(std::ostream& out) const {
+ out << "error set debugprint" << endl;
+ for(error_iterator i = errorBegin(), i_end = errorEnd();
+ i != i_end; ++i){
+ ArithVar e = *i;
+ const ErrorInformation& ei = d_errInfo[e];
+ ei.print(out);
+ out << " ";
+ d_variables.printModel(e, out);
+ out << endl;
+ }
+ out << "focus ";
+ for(focus_iterator i = focusBegin(), i_end = focusEnd();
+ i != i_end; ++i){
+ out << *i << " ";
+ }
+ out << ";" << endl;
+}
+
+void ErrorSet::focusDownToJust(ArithVar v) {
+ clearFocus();
+
+ ErrorInformation& vei = d_errInfo.get(v);
+ vei.setInFocus(true);
+ FocusSetHandle handle = d_focus.push(v);
+ vei.setHandle(handle);
+}
+
+void ErrorSet::pushErrorInto(ArithVarVec& vec) const{
+ for(error_iterator i = errorBegin(), e = errorEnd(); i != e; ++i ){
+ vec.push_back(*i);
+ }
+}
+
+void ErrorSet::pushFocusInto(ArithVarVec& vec) const{
+ for(focus_iterator i = focusBegin(), e = focusEnd(); i != e; ++i ){
+ vec.push_back(*i);
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Mathias Preiner, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "options/arith_options.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/tableau_sizes.h"
+#include "util/bin_heap.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+
+/**
+ * The priority queue has 3 different modes of operation:
+ * - Collection
+ * This passively collects arithmetic variables that may be inconsistent.
+ * This does not maintain any heap structure.
+ * dequeueInconsistentBasicVariable() does not work in this mode!
+ * Entering this mode requires the queue to be empty.
+ *
+ * - Difference Queue
+ * This mode uses the difference between a variables and its bound
+ * to determine which to dequeue first.
+ *
+ * - Variable Order Queue
+ * This mode uses the variable order to determine which ArithVar is dequeued first.
+ *
+ * The transitions between the modes of operation are:
+ * Collection => Difference Queue
+ * Difference Queue => Variable Order Queue
+ * Difference Queue => Collection (queue must be empty!)
+ * Variable Order Queue => Collection (queue must be empty!)
+ *
+ * The queue begins in Collection mode.
+ */
+
+
+class ErrorSet;
+
+class ComparatorPivotRule {
+private:
+ const ErrorSet* d_errorSet;
+
+ options::ErrorSelectionRule d_rule;
+
+ public:
+ ComparatorPivotRule();
+ ComparatorPivotRule(const ErrorSet* es, options::ErrorSelectionRule r);
+
+ bool operator()(ArithVar v, ArithVar u) const;
+ options::ErrorSelectionRule getRule() const { return d_rule; }
+};
+
+// typedef boost::heap::d_ary_heap<
+// ArithVar,
+// boost::heap::arity<2>,
+// boost::heap::compare<ComparatorPivotRule>,
+// boost::heap::mutable_<true> > FocusSet;
+//
+// typedef FocusSet::handle_type FocusSetHandle;
+
+// typedef CVC5_PB_DS_NAMESPACE::priority_queue<
+// ArithVar,
+// ComparatorPivotRule,
+// CVC5_PB_DS_NAMESPACE::pairing_heap_tag> FocusSet;
+
+// typedef FocusSet::point_iterator FocusSetHandle;
+
+typedef BinaryHeap<ArithVar, ComparatorPivotRule> FocusSet;
+typedef FocusSet::handle FocusSetHandle;
+
+
+class ErrorInformation {
+private:
+ /** The variable that is in error. */
+ ArithVar d_variable;
+
+ /**
+ * The constraint that was violated.
+ * This needs to be saved in case that the
+ * violated constraint
+ */
+ ConstraintP d_violated;
+
+ /**
+ * This is the sgn of the first derivate the variable must move to satisfy
+ * the bound violated.
+ * If d_sgn > 0, then d_violated was a lowerbound.
+ * If d_sgn < 0, then d_violated was an upperbound.
+ */
+ int d_sgn;
+
+ /**
+ * If this is true, then the bound is no longer set on d_variables.
+ * This MUST be undone before this is deleted.
+ */
+ bool d_relaxed;
+
+ /**
+ * If this is true, then the variable is in the focus set and the focus heap.
+ * d_handle is then a reasonable thing to interpret.
+ * If this is false, the variable is somewhere in
+ */
+ bool d_inFocus;
+ FocusSetHandle d_handle;
+
+ /**
+ * Auxillary information for storing the difference between a variable and its bound.
+ * Only set on signals.
+ */
+ std::unique_ptr<DeltaRational> d_amount;
+
+ /** */
+ uint32_t d_metric;
+
+public:
+ ErrorInformation();
+ ErrorInformation(ArithVar var, ConstraintP vio, int sgn);
+ ~ErrorInformation();
+ ErrorInformation(const ErrorInformation& ei);
+ ErrorInformation& operator=(const ErrorInformation& ei);
+
+ void reset(ConstraintP c, int sgn);
+
+ inline ArithVar getVariable() const { return d_variable; }
+
+ bool isRelaxed() const { return d_relaxed; }
+ void setRelaxed()
+ {
+ Assert(!d_relaxed);
+ d_relaxed = true;
+ }
+ void setUnrelaxed()
+ {
+ Assert(d_relaxed);
+ d_relaxed = false;
+ }
+
+ inline int sgn() const { return d_sgn; }
+
+ inline bool inFocus() const { return d_inFocus; }
+ inline int focusSgn() const {
+ return (d_inFocus) ? sgn() : 0;
+ }
+
+ inline void setInFocus(bool inFocus) { d_inFocus = inFocus; }
+
+ const DeltaRational& getAmount() const {
+ Assert(d_amount != nullptr);
+ return *d_amount;
+ }
+
+ void setAmount(const DeltaRational& am);
+ void setMetric(uint32_t m) { d_metric = m; }
+ uint32_t getMetric() const { return d_metric; }
+
+ inline void setHandle(FocusSetHandle h) {
+ Assert(d_inFocus);
+ d_handle = h;
+ }
+ inline const FocusSetHandle& getHandle() const{ return d_handle; }
+
+ inline ConstraintP getViolated() const { return d_violated; }
+
+ bool debugInitialized() const {
+ return
+ d_variable != ARITHVAR_SENTINEL &&
+ d_violated != NullConstraint &&
+ d_sgn != 0;
+ }
+ void print(std::ostream& os) const {
+ os << "{ErrorInfo: " << d_variable
+ << ", " << d_violated
+ << ", " << d_sgn
+ << ", " << d_relaxed
+ << ", " << d_inFocus;
+ if (d_amount == nullptr)
+ {
+ os << "nullptr";
+ }
+ else
+ {
+ os << (*d_amount);
+ }
+ os << "}";
+ }
+};
+
+class ErrorInfoMap : public DenseMap<ErrorInformation> {};
+
+class ErrorSet {
+private:
+ /**
+ * Reference to the arithmetic partial model for checking if a variable
+ * is consistent with its upper and lower bounds.
+ */
+ ArithVariables& d_variables;
+
+ /**
+ * The set of all variables that violate exactly one of their bounds.
+ */
+ ErrorInfoMap d_errInfo;
+
+ options::ErrorSelectionRule d_selectionRule;
+ /**
+ * The ordered heap for the variables that are in ErrorSet.
+ */
+ FocusSet d_focus;
+
+
+ /**
+ * A strict subset of the error set.
+ * d_outOfFocus \neq d_errInfo.
+ *
+ * Its symbolic complement is Focus.
+ * d_outOfFocus \intersect Focus == \emptyset
+ * d_outOfFocus \union Focus == d_errInfo
+ */
+ ArithVarVec d_outOfFocus;
+
+ /**
+ * Before a variable is added to the error set, it is added to the signals list.
+ * A variable may appear on the list multiple times.
+ * This introduces a delay.
+ */
+ ArithVarVec d_signals;
+
+ TableauSizes d_tableauSizes;
+
+ BoundCountingLookup d_boundLookup;
+
+ /**
+ * Computes the difference between the assignment and its bound for x.
+ */
+public:
+ DeltaRational computeDiff(ArithVar x) const;
+private:
+ void recomputeAmount(ErrorInformation& ei, options::ErrorSelectionRule r);
+
+ void update(ErrorInformation& ei);
+ void transitionVariableOutOfError(ArithVar v);
+ void transitionVariableIntoError(ArithVar v);
+ void addBackIntoFocus(ArithVar v);
+
+public:
+
+ /** The new focus set is the entire error set. */
+ void blur();
+ void dropFromFocus(ArithVar v);
+
+ void dropFromFocusAll(const ArithVarVec& vec) {
+ for(ArithVarVec::const_iterator i = vec.begin(), i_end = vec.end(); i != i_end; ++i){
+ ArithVar v = *i;
+ dropFromFocus(v);
+ }
+ }
+
+ ErrorSet(ArithVariables& var, TableauSizes tabSizes, BoundCountingLookup boundLookup);
+
+ typedef ErrorInfoMap::const_iterator error_iterator;
+ error_iterator errorBegin() const { return d_errInfo.begin(); }
+ error_iterator errorEnd() const { return d_errInfo.end(); }
+
+ bool inError(ArithVar v) const { return d_errInfo.isKey(v); }
+ bool inFocus(ArithVar v) const { return d_errInfo[v].inFocus(); }
+
+ void pushErrorInto(ArithVarVec& vec) const;
+ void pushFocusInto(ArithVarVec& vec) const;
+
+ options::ErrorSelectionRule getSelectionRule() const;
+ void setSelectionRule(options::ErrorSelectionRule rule);
+
+ inline ArithVar topFocusVariable() const{
+ Assert(!focusEmpty());
+ return d_focus.top();
+ }
+
+ inline void signalVariable(ArithVar var){
+ d_signals.push_back(var);
+ }
+
+ inline void signalUnderCnd(ArithVar var, bool b){
+ if(b){ signalVariable(var); }
+ }
+
+ inline bool inconsistent(ArithVar var) const{
+ return !d_variables.assignmentIsConsistent(var) ;
+ }
+ inline void signalIfInconsistent(ArithVar var){
+ signalUnderCnd(var, inconsistent(var));
+ }
+
+ inline bool errorEmpty() const{
+ return d_errInfo.empty();
+ }
+ inline uint32_t errorSize() const{
+ return d_errInfo.size();
+ }
+
+ inline bool focusEmpty() const {
+ return d_focus.empty();
+ }
+ inline uint32_t focusSize() const{
+ return d_focus.size();
+ }
+
+ inline int getSgn(ArithVar x) const {
+ Assert(inError(x));
+ return d_errInfo[x].sgn();
+ }
+ inline int focusSgn(ArithVar v) const {
+ if(inError(v)){
+ return d_errInfo[v].focusSgn();
+ }else{
+ return 0;
+ }
+ }
+
+ void focusDownToJust(ArithVar v);
+
+ void clearFocus();
+
+ /** Clears the set. */
+ void clear();
+ void reduceToSignals();
+
+ bool noSignals() const {
+ return d_signals.empty();
+ }
+ bool moreSignals() const {
+ return !noSignals();
+ }
+ ArithVar topSignal() const {
+ Assert(moreSignals());
+ return d_signals.back();
+ }
+
+ /**
+ * Moves a variable out of the signals.
+ * This moves it into the error set.
+ * Return the previous focus sign.
+ */
+ int popSignal();
+
+ const DeltaRational& getAmount(ArithVar v) const {
+ return d_errInfo[v].getAmount();
+ }
+
+ uint32_t sumMetric(ArithVar a) const{
+ Assert(inError(a));
+ BoundCounts bcs = d_boundLookup.atBounds(a);
+ uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount();
+
+ uint32_t length = d_tableauSizes.getRowLength(a);
+
+ return (length - count);
+ }
+
+ uint32_t getMetric(ArithVar a) const {
+ return d_errInfo[a].getMetric();
+ }
+
+ ConstraintP getViolated(ArithVar a) const {
+ return d_errInfo[a].getViolated();
+ }
+
+
+ typedef FocusSet::const_iterator focus_iterator;
+ focus_iterator focusBegin() const { return d_focus.begin(); }
+ focus_iterator focusEnd() const { return d_focus.end(); }
+
+ void debugPrint(std::ostream& out) const;
+
+private:
+ class Statistics {
+ public:
+ IntStat d_enqueues;
+ IntStat d_enqueuesCollection;
+ IntStat d_enqueuesDiffMode;
+ IntStat d_enqueuesVarOrderMode;
+
+ IntStat d_enqueuesCollectionDuplicates;
+ IntStat d_enqueuesVarOrderModeDuplicates;
+
+ Statistics();
+ };
+
+ Statistics d_statistics;
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T)decision procedure.
+ */
+#include "theory/arith/linear/fc_simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+FCSimplexDecisionProcedure::FCSimplexDecisionProcedure(
+ Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+ d_focusSize(0),
+ d_focusErrorVar(ARITHVAR_SENTINEL),
+ d_focusCoefficients(),
+ d_pivotBudget(0),
+ d_prevWitnessImprovement(AntiProductive),
+ d_witnessImprovementInARow(0),
+ d_sgnDisagreements(),
+ d_statistics("theory::arith::FC::", d_pivots)
+{ }
+
+FCSimplexDecisionProcedure::Statistics::Statistics(const std::string& name,
+ uint32_t& pivots)
+ : d_initialSignalsTime(
+ smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
+ d_initialConflicts(
+ smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
+ d_fcFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
+ d_fcFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
+ d_fcMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
+ d_fcTimer(smtStatisticsRegistry().registerTimer(name + "Timer")),
+ d_fcFocusConstructionTimer(
+ smtStatisticsRegistry().registerTimer(name + "Construction")),
+ d_selectUpdateForDualLike(smtStatisticsRegistry().registerTimer(
+ name + "selectUpdateForDualLike")),
+ d_selectUpdateForPrimal(smtStatisticsRegistry().registerTimer(
+ name + "selectUpdateForPrimal")),
+ d_finalCheckPivotCounter(
+ smtStatisticsRegistry().registerReference<uint32_t>(
+ name + "lastPivots", pivots))
+{
+}
+
+Result::Status FCSimplexDecisionProcedure::findModel(bool exactResult)
+{
+ Assert(d_conflictVariables.empty());
+ Assert(d_sgnDisagreements.empty());
+
+ d_pivots = 0;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Trace("arith::findModel") << "fcFindModel() trivial" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+
+ // We must start tracking NOW
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
+
+ if(initialProcessSignals()){
+ d_conflictVariables.purge();
+ Trace("arith::findModel") << "fcFindModel() early conflict" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Trace("arith::findModel") << "fcFindModel() fixed itself" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ Trace("arith::findModel") << "fcFindModel() start non-trivial" << endl;
+
+ exactResult |= d_varOrderPivotLimit < 0;
+
+ d_prevWitnessImprovement = HeuristicDegenerate;
+ d_witnessImprovementInARow = 0;
+
+ Result::Status result = Result::UNKNOWN;
+
+ if (result == Result::UNKNOWN)
+ {
+ if(exactResult){
+ d_pivotBudget = -1;
+ }else{
+ d_pivotBudget = d_varOrderPivotLimit;
+ }
+
+ result = dualLike();
+
+ if(result == Result::UNSAT){
+ ++(d_statistics.d_fcFoundUnsat);
+ }else if(d_errorSet.errorEmpty()){
+ ++(d_statistics.d_fcFoundSat);
+ }else{
+ ++(d_statistics.d_fcMissed);
+ }
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+ {
+ result = Result::SAT;
+ }
+
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Trace("arith::findModel") << "end findModel() " << result << endl;
+
+ Assert(d_conflictVariables.empty());
+ return result;
+}
+
+
+void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){
+ if(d_pivotBudget > 0) {
+ --d_pivotBudget;
+ }
+ Assert(w != AntiProductive);
+
+ if(w == d_prevWitnessImprovement){
+ ++d_witnessImprovementInARow;
+ // ignore overflow : probably never reached
+ if(d_witnessImprovementInARow == 0){
+ --d_witnessImprovementInARow;
+ }
+ }else{
+ if(w != BlandsDegenerate){
+ d_witnessImprovementInARow = 1;
+ }
+ // if w == BlandsDegenerate do not reset the counter
+ d_prevWitnessImprovement = w;
+ }
+ if(strongImprovement(w)){
+ d_leavingCountSinceImprovement.purge();
+ }
+
+ Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
+
+}
+
+uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const {
+ switch(d_prevWitnessImprovement){
+ case ConflictFound:
+ case ErrorDropped:
+ case FocusImproved:
+ return 0;
+ case HeuristicDegenerate:
+ case BlandsDegenerate:
+ return d_witnessImprovementInARow;
+ // Degenerate is unreachable for its own reasons
+ case Degenerate:
+ case FocusShrank:
+ case AntiProductive:
+ Unreachable();
+ return -1;
+ }
+ Unreachable();
+}
+
+void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+ uint32_t newErrorSize = d_errorSet.errorSize();
+ uint32_t newFocusSize = d_errorSet.focusSize();
+
+ //Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize);
+ Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize);
+
+ if(newFocusSize == 0 || !d_conflictVariables.empty() ){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = ARITHVAR_SENTINEL;
+ }else if(2*newFocusSize < d_focusSize ){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+ }else{
+ adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges);
+ }
+
+ d_errorSize = newErrorSize;
+ d_focusSize = newFocusSize;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){
+ Assert(dropped.size() > 0);
+ Assert(d_errorSet.focusSize() == d_focusSize);
+ Assert(d_errorSet.focusSize() > dropped.size());
+
+ uint32_t newFocusSize = d_focusSize - dropped.size();
+ Assert(newFocusSize > 0);
+
+ if(2 * newFocusSize <= d_focusSize){
+ d_errorSet.dropFromFocusAll(dropped);
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+ }else{
+ shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped);
+ d_errorSet.dropFromFocusAll(dropped);
+ }
+
+ d_focusSize = newFocusSize;
+ Assert(d_errorSet.focusSize() == d_focusSize);
+ return FocusShrank;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){
+ // uint32_t newErrorSize = d_errorSet.errorSize();
+ // uint32_t newFocusSize = d_errorSet.focusSize();
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_focusSize > 1);
+ Assert(d_errorSet.inFocus(v));
+
+ d_errorSet.focusDownToJust(v);
+ Assert(d_errorSet.focusSize() == 1);
+ d_focusSize = 1;
+
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+ return FocusShrank;
+}
+
+
+
+UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+ UpdateInfo selected;
+
+ Trace("arith::selectPrimalUpdate")
+ << "selectPrimalUpdate" << endl
+ << basic << " " << d_tableau.basicRowLength(basic) << " "
+ << d_linEq.debugBasicAtBoundCount(basic) << endl;
+
+ static constexpr int s_maxCandidatesAfterImprove = 3;
+ bool isFocus = basic == d_focusErrorVar;
+ Assert(isFocus || d_errorSet.inError(basic));
+ int basicDir = isFocus? 1 : d_errorSet.getSgn(basic);
+ bool dualLike = !isFocus && d_focusSize > 1;
+
+ if(!isFocus){
+ loadFocusSigns();
+ }
+
+ decreasePenalties();
+
+ typedef std::vector<Cand> CandVector;
+ CandVector candidates;
+
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ if(curr == basic){ continue; }
+
+ int sgn = e.getCoefficient().sgn();
+ int curr_movement = basicDir * sgn;
+
+ bool candidate =
+ (curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+ (curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+ Trace("arith::selectPrimalUpdate")
+ << "storing " << basic
+ << " " << curr
+ << " " << candidate
+ << " " << e.getCoefficient()
+ << " " << curr_movement
+ << " " << focusCoefficient(curr) << endl;
+
+ if(!candidate) { continue; }
+
+ if(!isFocus){
+ const Rational& focusC = focusCoefficient(curr);
+ Assert(dualLike || !focusC.isZero());
+ if(dualLike && curr_movement != focusC.sgn()){
+ Trace("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl;
+ d_sgnDisagreements.push_back(curr);
+ continue;
+ }else{
+ candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC));
+ }
+ }else{
+ candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient()));
+ }
+ }
+
+ CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
+ CandVector::iterator i = candidates.begin();
+ CandVector::iterator end = candidates.end();
+ std::make_heap(i, end, colCmp);
+
+ bool checkEverything = d_pivots == 0;
+
+ int candidatesAfterFocusImprove = 0;
+ while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){
+ std::pop_heap(i, end, colCmp);
+ --end;
+ Cand& cand = (*end);
+ ArithVar curr = cand.d_nb;
+ const Rational& coeff = *cand.d_coeff;
+
+ LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+ UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+ Trace("arith::selectPrimalUpdate")
+ << "selected " << selected << endl
+ << "currProp " << currProposal << endl
+ << "coeff " << coeff << endl;
+
+ Assert(!currProposal.uninitialized());
+
+ if(candidatesAfterFocusImprove > 0){
+ candidatesAfterFocusImprove++;
+ }
+
+ if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+
+ selected = currProposal;
+ WitnessImprovement w = selected.getWitness(false);
+ Trace("arith::selectPrimalUpdate") << "selected " << w << endl;
+ setPenalty(curr, w);
+ if(improvement(w)){
+ bool exitEarly;
+ switch(w){
+ case ConflictFound: exitEarly = true; break;
+ case ErrorDropped:
+ if(checkEverything){
+ exitEarly = d_errorSize + selected.errorsChange() == 0;
+ Trace("arith::selectPrimalUpdate")
+ << "ee " << d_errorSize << " "
+ << selected.errorsChange() << " "
+ << d_errorSize + selected.errorsChange() << endl;
+ }else{
+ exitEarly = true;
+ }
+ break;
+ case FocusImproved:
+ candidatesAfterFocusImprove = 1;
+ exitEarly = false;
+ break;
+ default:
+ exitEarly = false; break;
+ }
+ if(exitEarly){ break; }
+ }
+ }else{
+ Trace("arith::selectPrimalUpdate") << "dropped "<< endl;
+ }
+
+ }
+
+ if(!isFocus){
+ unloadFocusSigns();
+ }
+ return selected;
+}
+
+bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+ if(inf.getWitness(useBlands) == w){
+ switch(w){
+ case ConflictFound: return inf.foundConflict();
+ case ErrorDropped: return inf.errorsChange() < 0;
+ case FocusImproved: return inf.focusDirection() > 0;
+ case FocusShrank: return false; // This is not a valid output
+ case Degenerate: return false; // This is not a valid output
+ case BlandsDegenerate: return useBlands;
+ case HeuristicDegenerate: return !useBlands;
+ case AntiProductive: return false;
+ }
+ }
+ return false;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){
+ bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+ UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands);
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(useBlands);
+ Assert(debugCheckWitness(selected, w, useBlands));
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+}
+
+
+WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){
+ Assert(!d_sgnDisagreements.empty());
+ Assert(d_errorSet.focusSize() >= 2);
+
+ if(TraceIsOn("arith::focus")){
+ d_errorSet.debugPrint(Trace("arith::focus"));
+ }
+
+ ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength);
+ const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb);
+ int oppositeSgn = - (e_evar_nb.getCoefficient().sgn());
+ Trace("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl;
+
+ ArithVarVec dropped;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == nb);
+
+ int sgn = entry.getCoefficient().sgn();
+ Trace("arith::focus")
+ << "on row "
+ << d_tableau.rowIndexToBasic(entry.getRowIndex())
+ << " "
+ << entry.getCoefficient() << endl;
+ ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){
+ int errSgn = d_errorSet.getSgn(currRow);
+
+ if(errSgn * sgn == oppositeSgn){
+ dropped.push_back(currRow);
+ Trace("arith::focus") << "dropping from focus " << currRow << endl;
+ }
+ }
+ }
+
+ d_sgnDisagreements.clear();
+ return adjustFocusShrank(dropped);
+}
+
+bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){
+ int diff = currErrorSize - prevErrorSize;
+ return selected.foundConflict() || diff == selected.errorsChange();
+}
+
+void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
+ Trace("updateAndSignal") << "updated basic " << updated;
+ Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+ Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+ int dir = !d_variables.assignmentIsConsistent(updated) ?
+ d_errorSet.getSgn(updated) : 0;
+ Trace("updateAndSignal") << " dir " << dir;
+ Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
+}
+
+bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
+ if(selected.describesPivot() && updated == selected.leaving()){
+ return selected.foundConflict();
+ }else{
+ return true;
+ }
+}
+
+void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+ ArithVar nonbasic = selected.nonbasic();
+
+ Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+ stringstream ss;
+
+ if(selected.describesPivot()){
+ ConstraintP limiting = selected.limiting();
+ ArithVar basic = limiting->getVariable();
+ Assert(d_linEq.basicIsTracked(basic));
+ d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+ }else{
+ Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+ DeltaRational newAssignment =
+ d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+ d_linEq.updateTracked(nonbasic, newAssignment);
+ }
+ d_pivots++;
+
+ increaseLeavingCount(nonbasic);
+
+ vector< pair<ArithVar, int> > focusChanges;
+ while(d_errorSet.moreSignals()){
+ ArithVar updated = d_errorSet.topSignal();
+ int prevFocusSgn = d_errorSet.popSignal();
+
+ if(d_tableau.isBasic(updated)){
+ Assert(!d_variables.assignmentIsConsistent(updated)
+ == d_errorSet.inError(updated));
+ if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
+ if(!d_variables.assignmentIsConsistent(updated)){
+ if(checkBasicForConflict(updated)){
+ reportConflict(updated);
+ Assert(debugUpdatedBasic(selected, updated));
+ }
+ }
+ }else{
+ Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
+ }
+ int currFocusSgn = d_errorSet.focusSgn(updated);
+ if(currFocusSgn != prevFocusSgn){
+ int change = currFocusSgn - prevFocusSgn;
+ focusChanges.push_back(make_pair(updated, change));
+ }
+ }
+
+ if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
+
+ Assert(
+ debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+ adjustFocusAndError(selected, focusChanges);
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_focusSize > 1);
+
+ UpdateInfo selected = selectUpdateForDualLike(errorVar);
+
+ if(selected.uninitialized()){
+ // we found no proposals
+ // If this is empty, there must be an error on this variable!
+ // this should not be possible. It Should have been caught as a signal earlier
+ WitnessImprovement dropped = focusUsingSignDisagreements(errorVar);
+ Assert(d_sgnDisagreements.empty());
+
+ return dropped;
+ }else{
+ d_sgnDisagreements.clear();
+ }
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(!selected.uninitialized());
+
+ if(selected.focusDirection() == 0 &&
+ d_prevWitnessImprovement == HeuristicDegenerate &&
+ d_witnessImprovementInARow >= s_focusThreshold){
+
+ Trace("focusDownToJust") << "focusDownToJust " << errorVar << endl;
+
+ return focusDownToJust(errorVar);
+ }else{
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+ }
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){
+ Assert(d_focusSize >= 2);
+
+ Trace("focusDownToLastHalf") << "focusDownToLastHalf "
+ << d_errorSet.errorSize() << " "
+ << d_errorSet.focusSize() << " ";
+
+ uint32_t half = d_focusSize/2;
+ ArithVarVec buf;
+ for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(),
+ i_end = d_errorSet.focusEnd(); i != i_end; ++i){
+ if(half > 0){
+ --half;
+ } else{
+ buf.push_back(*i);
+ }
+ }
+ WitnessImprovement w = adjustFocusShrank(buf);
+ Trace("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl;
+ return w;
+}
+
+WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() {
+ Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+ Assert(d_focusSize >= 2);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf =
+ &LinearEqualityModule::preferWitness<true>;
+
+ LinearEqualityModule::VarPreferenceFunction bpf =
+ &LinearEqualityModule::minRowLength;
+
+ UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf);
+
+ if(selected.uninitialized()){
+ Trace("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl;
+
+ return focusDownToLastHalf();
+ }
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+
+ if(degenerate(w)){
+ Trace("selectFocusImproving") << "only degenerate" << endl;
+ if(d_prevWitnessImprovement == HeuristicDegenerate &&
+ d_witnessImprovementInARow >= s_focusThreshold){
+ Trace("selectFocusImproving") << "focus down been degenerate too long" << endl;
+ return focusDownToLastHalf();
+ }else{
+ Trace("selectFocusImproving") << "taking degenerate" << endl;
+ }
+ }
+ Trace("selectFocusImproving") << "selectFocusImproving did this " << selected << endl;
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+}
+
+bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w,
+ ostream& out,
+ uint32_t prevFocusSize,
+ uint32_t prevErrorSize) const
+{
+ out << "DLV() ";
+ switch(w){
+ case ConflictFound:
+ out << "found conflict" << endl;
+ return !d_conflictVariables.empty();
+ case ErrorDropped:
+ out << "dropped " << prevErrorSize - d_errorSize << endl;
+ return d_errorSize < prevErrorSize;
+ case FocusImproved:
+ out << "focus improved"<< endl;
+ return d_errorSize == prevErrorSize;
+ case FocusShrank:
+ out << "focus shrank"<< endl;
+ return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize;
+ case BlandsDegenerate:
+ out << "bland degenerate"<< endl;
+ return true;
+ case HeuristicDegenerate:
+ out << "heuristic degenerate"<< endl;
+ return true;
+ case AntiProductive:
+ out << "focus blur" << endl;
+ return prevFocusSize == 0;
+ case Degenerate:
+ return false;
+ }
+ return false;
+}
+
+Result::Status FCSimplexDecisionProcedure::dualLike()
+{
+ TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer);
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_pivotBudget != 0);
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_errorSize > 0);
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_focusSize > 0);
+ Assert(d_conflictVariables.empty());
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+ d_scores.purge();
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+
+ while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
+ Trace("dualLike") << "dualLike " << endl;
+
+ Assert(d_errorSet.noSignals());
+
+ WitnessImprovement w = AntiProductive;
+ uint32_t prevFocusSize = d_focusSize;
+ uint32_t prevErrorSize = d_errorSize;
+
+ if(d_focusSize == 0){
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+
+ d_errorSet.blur();
+
+ d_focusSize = d_errorSet.focusSize();
+
+ Assert(d_errorSize == d_focusSize);
+ Assert(d_errorSize >= 1);
+
+ d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer);
+
+ Trace("dualLike") << "blur " << d_focusSize << endl;
+ }else if(d_focusSize == 1){
+ // Possible outcomes:
+ // - errorSet size shrunk
+ // -- fixed v
+ // -- fixed something other than v
+ // - conflict
+ // - budget was exhausted
+
+ ArithVar e = d_errorSet.topFocusVariable();
+ Trace("dualLike") << "primalImproveError " << e << endl;
+ w = primalImproveError(e);
+ }else{
+
+ // Possible outcomes:
+ // - errorSet size shrunk
+ // -- fixed v
+ // -- fixed something other than v
+ // - conflict
+ // - budget was exhausted
+ // - focus went down
+ Assert(d_focusSize > 1);
+ ArithVar e = d_errorSet.topFocusVariable();
+ static constexpr unsigned s_sumMetricThreshold = 1;
+ if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){
+ Trace("dualLike") << "dualLikeImproveError " << e << endl;
+ w = dualLikeImproveError(e);
+ }else{
+ Trace("dualLike") << "selectFocusImproving " << endl;
+ w = selectFocusImproving();
+ }
+ }
+ Trace("dualLike") << "witnessImprovement: " << w << endl;
+ Assert(d_focusSize == d_errorSet.focusSize());
+ Assert(d_errorSize == d_errorSet.errorSize());
+
+ Assert(debugDualLike(w, Trace("dualLike"), prevFocusSize, prevErrorSize));
+ Trace("dualLike") << "Focus size " << d_focusSize << " (was " << prevFocusSize << ")" << endl;
+ Trace("dualLike") << "Error size " << d_errorSize << " (was " << prevErrorSize << ")" << endl;
+ }
+
+
+ if(d_focusErrorVar != ARITHVAR_SENTINEL){
+ tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar);
+ d_focusErrorVar = ARITHVAR_SENTINEL;
+ }
+
+ Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
+ if(!d_conflictVariables.empty()){
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Assert(d_errorSet.noSignals());
+ return Result::SAT;
+ }else{
+ Assert(d_pivotBudget == 0);
+ return Result::UNKNOWN;
+ }
+}
+
+
+void FCSimplexDecisionProcedure::loadFocusSigns(){
+ Assert(d_focusCoefficients.empty());
+ Assert(d_focusErrorVar != ARITHVAR_SENTINEL);
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ d_focusCoefficients.set(curr, &e.getCoefficient());
+ }
+}
+
+void FCSimplexDecisionProcedure::unloadFocusSigns(){
+ d_focusCoefficients.purge();
+}
+
+const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const {
+ if(d_focusCoefficients.isKey(nb)){
+ return *(d_focusCoefficients[nb]);
+ }else{
+ return d_zero;
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T)decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ * by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ * These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class FCSimplexDecisionProcedure : public SimplexDecisionProcedure{
+public:
+ FCSimplexDecisionProcedure(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override;
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+ // dual like
+ // - found conflict
+ // - satisfied error set
+ Result::Status dualLike();
+
+private:
+ static constexpr uint32_t PENALTY = 4;
+ DenseMultiset d_scores;
+ void decreasePenalties() { d_scores.removeOneOfEverything(); }
+ uint32_t penalty(ArithVar x) const { return d_scores.count(x); }
+ void setPenalty(ArithVar x, WitnessImprovement w)
+ {
+ if (improvement(w))
+ {
+ if (d_scores.count(x) > 0)
+ {
+ d_scores.removeAll(x);
+ }
+ }
+ else
+ {
+ d_scores.setCount(x, PENALTY);
+ }
+ }
+
+ /** The size of the focus set. */
+ uint32_t d_focusSize;
+
+ /** The current error focus variable. */
+ ArithVar d_focusErrorVar;
+
+ /**
+ * The signs of the coefficients in the focus set.
+ * This is empty until this has been loaded.
+ */
+ DenseMap<const Rational*> d_focusCoefficients;
+
+ /**
+ * Loads the signs of the coefficients of the variables on the row d_focusErrorVar
+ * into d_focusSgns.
+ */
+ void loadFocusSigns();
+
+ /** Unloads the information from d_focusSgns. */
+ void unloadFocusSigns();
+
+ /**
+ * The signs of a variable in the row of d_focusErrorVar.
+ * d_focusSgns must be loaded.
+ */
+ const Rational& focusCoefficient(ArithVar nb) const;
+
+ int32_t d_pivotBudget;
+
+ WitnessImprovement d_prevWitnessImprovement;
+ uint32_t d_witnessImprovementInARow;
+
+ uint32_t degeneratePivotsInARow() const;
+
+ static constexpr uint32_t s_focusThreshold = 6;
+ static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+ static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+ DenseMap<uint32_t> d_leavingCountSinceImprovement;
+ void increaseLeavingCount(ArithVar x){
+ if(!d_leavingCountSinceImprovement.isKey(x)){
+ d_leavingCountSinceImprovement.set(x,1);
+ }else{
+ (d_leavingCountSinceImprovement.get(x))++;
+ }
+ }
+ LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+ bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+ d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+ if(useBlands) {
+ return &LinearEqualityModule::preferWitness<false>;
+ } else {
+ return &LinearEqualityModule::preferWitness<true>;
+ }
+ }
+
+ bool debugDualLike(WitnessImprovement w, std::ostream& out,
+ uint32_t prevFocusSize, uint32_t prevErrorSize) const;
+
+ void debugPrintSignal(ArithVar updated) const;
+
+ ArithVarVec d_sgnDisagreements;
+
+ void logPivot(WitnessImprovement w);
+
+ void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+ UpdateInfo selectPrimalUpdate(ArithVar error,
+ LinearEqualityModule::UpdatePreferenceFunction upf,
+ LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+ UpdateInfo selectUpdateForDualLike(ArithVar basic){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf =
+ &LinearEqualityModule::preferWitness<true>;
+ LinearEqualityModule::VarPreferenceFunction bpf =
+ &LinearEqualityModule::minVarOrder;
+ return selectPrimalUpdate(basic, upf, bpf);
+ }
+
+ UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+ LinearEqualityModule::UpdatePreferenceFunction upf;
+ if(useBlands) {
+ upf = &LinearEqualityModule::preferWitness<false>;
+ } else {
+ upf = &LinearEqualityModule::preferWitness<true>;
+ }
+
+ LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ &LinearEqualityModule::minVarOrder :
+ &LinearEqualityModule::minRowLength;
+
+ return selectPrimalUpdate(basic, upf, bpf);
+ }
+ WitnessImprovement selectFocusImproving() ;
+
+ WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+ WitnessImprovement focusDownToLastHalf();
+ WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+ WitnessImprovement focusDownToJust(ArithVar v);
+
+
+ void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ bool initialProcessSignals(){
+ TimerStat &timer = d_statistics.d_initialSignalsTime;
+ IntStat& conflictStat = d_statistics.d_initialConflicts;
+ bool res = standardProcessSignals(timer, conflictStat);
+ d_focusSize = d_errorSet.focusSize();
+ return res;
+ }
+
+ static bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ TimerStat d_initialSignalsTime;
+ IntStat d_initialConflicts;
+
+ IntStat d_fcFoundUnsat;
+ IntStat d_fcFoundSat;
+ IntStat d_fcMissed;
+
+ TimerStat d_fcTimer;
+ TimerStat d_fcFocusConstructionTimer;
+
+ TimerStat d_selectUpdateForDualLike;
+ TimerStat d_selectUpdateForPrimal;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(const std::string& name, uint32_t& pivots);
+ } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andres Noetzli, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/infer_bounds.h"
+#include "theory/rewriter.h"
+
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+using namespace inferbounds;
+
+InferBoundAlgorithm::InferBoundAlgorithm()
+ : d_alg(None)
+{}
+
+InferBoundAlgorithm::InferBoundAlgorithm(Algorithms a)
+ : d_alg(a)
+{
+ Assert(a != Simplex);
+}
+
+InferBoundAlgorithm::InferBoundAlgorithm(
+ const std::optional<int>& simplexRounds)
+ : d_alg(Simplex)
+{}
+
+Algorithms InferBoundAlgorithm::getAlgorithm() const{
+ return d_alg;
+}
+
+const std::optional<int>& InferBoundAlgorithm::getSimplexRounds() const
+{
+ Assert(getAlgorithm() == Simplex);
+ return d_simplexRounds;
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkLookup(){
+ return InferBoundAlgorithm(Lookup);
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkRowSum(){
+ return InferBoundAlgorithm(RowSum);
+}
+
+InferBoundAlgorithm InferBoundAlgorithm::mkSimplex(
+ const std::optional<int>& rounds)
+{
+ return InferBoundAlgorithm(rounds);
+}
+
+ArithEntailmentCheckParameters::ArithEntailmentCheckParameters()
+ : d_algorithms()
+{}
+
+ArithEntailmentCheckParameters::~ArithEntailmentCheckParameters()
+{}
+
+
+void ArithEntailmentCheckParameters::addLookupRowSumAlgorithms(){
+ addAlgorithm(InferBoundAlgorithm::mkLookup());
+ addAlgorithm(InferBoundAlgorithm::mkRowSum());
+}
+
+void ArithEntailmentCheckParameters::addAlgorithm(const inferbounds::InferBoundAlgorithm& alg){
+ d_algorithms.push_back(alg);
+}
+
+ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::begin() const{
+ return d_algorithms.begin();
+}
+
+ArithEntailmentCheckParameters::const_iterator ArithEntailmentCheckParameters::end() const{
+ return d_algorithms.end();
+}
+
+InferBoundsResult::InferBoundsResult()
+ : d_foundBound(false)
+ , d_budgetExhausted(false)
+ , d_boundIsProvenOpt(false)
+ , d_inconsistentState(false)
+ , d_reachedThreshold(false)
+ , d_value(false)
+ , d_term(Node::null())
+ , d_upperBound(true)
+ , d_explanation(Node::null())
+{}
+
+InferBoundsResult::InferBoundsResult(Node term, bool ub)
+ : d_foundBound(false)
+ , d_budgetExhausted(false)
+ , d_boundIsProvenOpt(false)
+ , d_inconsistentState(false)
+ , d_reachedThreshold(false)
+ , d_value(false)
+ , d_term(term)
+ , d_upperBound(ub)
+ , d_explanation(Node::null())
+{}
+
+bool InferBoundsResult::foundBound() const {
+ return d_foundBound;
+}
+bool InferBoundsResult::boundIsOptimal() const {
+ return d_boundIsProvenOpt;
+}
+bool InferBoundsResult::inconsistentState() const {
+ return d_inconsistentState;
+}
+
+bool InferBoundsResult::boundIsInteger() const{
+ return foundBound() && d_value.isIntegral();
+}
+
+bool InferBoundsResult::boundIsRational() const {
+ return foundBound() && d_value.infinitesimalIsZero();
+}
+
+Integer InferBoundsResult::valueAsInteger() const{
+ Assert(boundIsInteger());
+ return getValue().floor();
+}
+const Rational& InferBoundsResult::valueAsRational() const{
+ Assert(boundIsRational());
+ return getValue().getNoninfinitesimalPart();
+}
+
+const DeltaRational& InferBoundsResult::getValue() const{
+ return d_value;
+}
+
+Node InferBoundsResult::getTerm() const { return d_term; }
+
+Node InferBoundsResult::getLiteral() const{
+ const Rational& q = getValue().getNoninfinitesimalPart();
+ NodeManager* nm = NodeManager::currentNM();
+ Node qnode = nm->mkConst(CONST_RATIONAL, q);
+
+ Kind k;
+ if(d_upperBound){
+ // x <= q + c*delta
+ Assert(getValue().infinitesimalSgn() <= 0);
+ k = boundIsRational() ? kind::LEQ : kind::LT;
+ }else{
+ // x >= q + c*delta
+ Assert(getValue().infinitesimalSgn() >= 0);
+ k = boundIsRational() ? kind::GEQ : kind::GT;
+ }
+ return nm->mkNode(k, getTerm(), qnode);
+}
+
+/* If there is a bound, this is a node that explains the bound. */
+Node InferBoundsResult::getExplanation() const{
+ return d_explanation;
+}
+
+
+void InferBoundsResult::setBound(const DeltaRational& dr, Node exp){
+ d_foundBound = true;
+ d_value = dr;
+ d_explanation = exp;
+}
+
+void InferBoundsResult::setBudgetExhausted() { d_budgetExhausted = true; }
+void InferBoundsResult::setReachedThreshold() { d_reachedThreshold = true; }
+void InferBoundsResult::setIsOptimal() { d_boundIsProvenOpt = true; }
+void InferBoundsResult::setInconsistent() { d_inconsistentState = true; }
+
+bool InferBoundsResult::thresholdWasReached() const{
+ return d_reachedThreshold;
+}
+bool InferBoundsResult::budgetIsExhausted() const{
+ return d_budgetExhausted;
+}
+
+std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr){
+ os << "{InferBoundsResult " << std::endl;
+ os << "on " << ibr.getTerm() << ", ";
+ if(ibr.findUpperBound()){
+ os << "find upper bound, ";
+ }else{
+ os << "find lower bound, ";
+ }
+ if(ibr.foundBound()){
+ os << "found a bound: ";
+ if(ibr.boundIsInteger()){
+ os << ibr.valueAsInteger() << "(int), ";
+ }else if(ibr.boundIsRational()){
+ os << ibr.valueAsRational() << "(rat), ";
+ }else{
+ os << ibr.getValue() << "(extended), ";
+ }
+
+ os << "as term " << ibr.getLiteral() << ", ";
+ os << "explanation " << ibr.getExplanation() << ", ";
+ }else {
+ os << "did not find a bound, ";
+ }
+
+ if(ibr.boundIsOptimal()){
+ os << "(opt), ";
+ }
+
+ if(ibr.inconsistentState()){
+ os << "(inconsistent), ";
+ }
+ if(ibr.budgetIsExhausted()){
+ os << "(budget exhausted), ";
+ }
+ if(ibr.thresholdWasReached()){
+ os << "(reached threshold), ";
+ }
+ os << "}";
+ return os;
+}
+
+ArithEntailmentCheckSideEffects::ArithEntailmentCheckSideEffects()
+ : d_simplexSideEffects(NULL)
+{}
+
+ArithEntailmentCheckSideEffects::~ArithEntailmentCheckSideEffects(){
+ if(d_simplexSideEffects != NULL){
+ delete d_simplexSideEffects;
+ d_simplexSideEffects = NULL;
+ }
+}
+
+InferBoundsResult& ArithEntailmentCheckSideEffects::getSimplexSideEffects(){
+ if(d_simplexSideEffects == NULL){
+ d_simplexSideEffects = new InferBoundsResult;
+ }
+ return *d_simplexSideEffects;
+}
+
+namespace inferbounds { /* namespace arith */
+
+std::ostream& operator<<(std::ostream& os, const Algorithms a){
+ switch(a){
+ case None: os << "AlgNone"; break;
+ case Lookup: os << "AlgLookup"; break;
+ case RowSum: os << "AlgRowSum"; break;
+ case Simplex: os << "AlgSimplex"; break;
+ default:
+ Unhandled();
+ }
+
+ return os;
+}
+
+} /* namespace inferbounds */
+
+} /* namespace arith */
+} /* namespace theory */
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andrew Reynolds, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+#include <ostream>
+
+#include "expr/node.h"
+#include "theory/arith/delta_rational.h"
+#include "util/integer.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+namespace inferbounds {
+ enum Algorithms {None = 0, Lookup, RowSum, Simplex};
+ enum SimplexParamKind { Unbounded, NumVars, Direct};
+
+class InferBoundAlgorithm {
+private:
+ Algorithms d_alg;
+ std::optional<int> d_simplexRounds;
+ InferBoundAlgorithm(Algorithms a);
+ InferBoundAlgorithm(const std::optional<int>& simplexRounds);
+
+ public:
+ InferBoundAlgorithm();
+
+ Algorithms getAlgorithm() const;
+ const std::optional<int>& getSimplexRounds() const;
+
+ static InferBoundAlgorithm mkLookup();
+ static InferBoundAlgorithm mkRowSum();
+ static InferBoundAlgorithm mkSimplex(const std::optional<int>& rounds);
+};
+
+std::ostream& operator<<(std::ostream& os, const Algorithms a);
+} /* namespace inferbounds */
+
+class ArithEntailmentCheckParameters
+{
+ private:
+ typedef std::vector<inferbounds::InferBoundAlgorithm> VecInferBoundAlg;
+ VecInferBoundAlg d_algorithms;
+
+public:
+ typedef VecInferBoundAlg::const_iterator const_iterator;
+
+ ArithEntailmentCheckParameters();
+ ~ArithEntailmentCheckParameters();
+
+ void addLookupRowSumAlgorithms();
+ void addAlgorithm(const inferbounds::InferBoundAlgorithm& alg);
+
+ const_iterator begin() const;
+ const_iterator end() const;
+};
+
+
+
+class InferBoundsResult {
+public:
+ InferBoundsResult();
+ InferBoundsResult(Node term, bool ub);
+
+ void setBound(const DeltaRational& dr, Node exp);
+ bool foundBound() const;
+
+ void setIsOptimal();
+ bool boundIsOptimal() const;
+
+ void setInconsistent();
+ bool inconsistentState() const;
+
+ const DeltaRational& getValue() const;
+ bool boundIsRational() const;
+ const Rational& valueAsRational() const;
+ bool boundIsInteger() const;
+ Integer valueAsInteger() const;
+
+ Node getTerm() const;
+ Node getLiteral() const;
+ void setTerm(Node t){ d_term = t; }
+
+ /* If there is a bound, this is a node that explains the bound. */
+ Node getExplanation() const;
+
+ bool budgetIsExhausted() const;
+ void setBudgetExhausted();
+
+ bool thresholdWasReached() const;
+ void setReachedThreshold();
+
+ bool findUpperBound() const { return d_upperBound; }
+
+ void setFindLowerBound() { d_upperBound = false; }
+ void setFindUpperBound() { d_upperBound = true; }
+private:
+ /* was a bound found */
+ bool d_foundBound;
+
+ /* was the budget exhausted */
+ bool d_budgetExhausted;
+
+ /* does the bound have to be optimal*/
+ bool d_boundIsProvenOpt;
+
+ /* was this started on an inconsistent state. */
+ bool d_inconsistentState;
+
+ /* reached the threshold. */
+ bool d_reachedThreshold;
+
+ /* the value of the bound */
+ DeltaRational d_value;
+
+ /* The input term. */
+ Node d_term;
+
+ /* Was the bound found an upper or lower bound.*/
+ bool d_upperBound;
+
+ /* Explanation of the bound. */
+ Node d_explanation;
+};
+
+std::ostream& operator<<(std::ostream& os, const InferBoundsResult& ibr);
+
+class ArithEntailmentCheckSideEffects
+{
+ public:
+ ArithEntailmentCheckSideEffects();
+ ~ArithEntailmentCheckSideEffects();
+
+ InferBoundsResult& getSimplexSideEffects();
+
+private:
+ InferBoundsResult* d_simplexSideEffects;
+};
+
+
+} /* namespace arith */
+} /* namespace theory */
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andres Noetzli
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This implements the LinearEqualityModule.
+ */
+#include "theory/arith/linear/linear_equality.h"
+
+#include "base/output.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/* Explicitly instatiate these functions. */
+
+template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
+template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
+
+template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
+template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
+
+
+void Border::output(std::ostream& out) const{
+ out << "{Border"
+ << ", " << d_bound->getVariable()
+ << ", " << d_bound->getValue()
+ << ", " << d_diff
+ << ", " << d_areFixing
+ << ", " << d_upperbound;
+ if(ownBorder()){
+ out << ", ownBorder";
+ }else{
+ out << ", " << d_entry->getCoefficient();
+ }
+ out << ", " << d_bound
+ << "}";
+}
+
+LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundsTracking, BasicVarModelUpdateCallBack f):
+ d_variables(vars),
+ d_tableau(t),
+ d_basicVariableUpdates(f),
+ d_increasing(1),
+ d_decreasing(-1),
+ d_upperBoundDifference(),
+ d_lowerBoundDifference(),
+ d_one(1),
+ d_negOne(-1),
+ d_btracking(boundsTracking),
+ d_areTracking(false),
+ d_trackCallback(this)
+{}
+
+LinearEqualityModule::Statistics::Statistics()
+ : d_statPivots(
+ smtStatisticsRegistry().registerInt("theory::arith::pivots")),
+ d_statUpdates(
+ smtStatisticsRegistry().registerInt("theory::arith::updates")),
+ d_pivotTime(
+ smtStatisticsRegistry().registerTimer("theory::arith::pivotTime")),
+ d_adjTime(
+ smtStatisticsRegistry().registerTimer("theory::arith::adjTime")),
+ d_weakeningAttempts(smtStatisticsRegistry().registerInt(
+ "theory::arith::weakening::attempts")),
+ d_weakeningSuccesses(smtStatisticsRegistry().registerInt(
+ "theory::arith::weakening::success")),
+ d_weakenings(smtStatisticsRegistry().registerInt(
+ "theory::arith::weakening::total")),
+ d_weakenTime(smtStatisticsRegistry().registerTimer(
+ "theory::arith::weakening::time")),
+ d_forceTime(
+ smtStatisticsRegistry().registerTimer("theory::arith::forcing::time"))
+{
+}
+
+void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){
+ Assert(!d_areTracking);
+
+ BoundsInfo curr = d_variables.boundsInfo(v);
+
+ Assert(prev != curr);
+ Tableau::ColIterator basicIter = d_tableau.colIterator(v);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ Assert(entry.getColVar() == v);
+ int a_ijSgn = entry.getCoefficient().sgn();
+
+ RowIndex ridx = entry.getRowIndex();
+ BoundsInfo& counts = d_btracking.get(ridx);
+ Trace("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ;
+ counts.addInChange(a_ijSgn, prev, curr);
+ Trace("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl;
+ }
+}
+
+void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
+ for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
+ ArithVar nb = *i;
+ if(!d_tableau.isBasic(nb)){
+ Assert(!d_tableau.isBasic(nb));
+ const DeltaRational& newValue = many[nb];
+ if(newValue != d_variables.getAssignment(nb)){
+ Trace("arith::updateMany")
+ << "updateMany:" << nb << " "
+ << d_variables.getAssignment(nb) << " to "<< newValue << endl;
+ update(nb, newValue);
+ }
+ }
+ }
+}
+
+
+
+
+void LinearEqualityModule::applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues){
+ forceNewBasis(newBasis);
+ updateMany(newValues);
+}
+
+void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_forceTime);
+ cout << "force begin" << endl;
+ DenseSet needsToBeAdded;
+ for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
+ ArithVar b = *i;
+ if(!d_tableau.isBasic(b)){
+ needsToBeAdded.add(b);
+ }
+ }
+
+ while(!needsToBeAdded.empty()){
+ ArithVar toRemove = ARITHVAR_SENTINEL;
+ ArithVar toAdd = ARITHVAR_SENTINEL;
+ DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
+ for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
+ ArithVar v = *i;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(v);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == v);
+ ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ if(!newBasis.isMember(b)){
+ toAdd = v;
+ if(toRemove == ARITHVAR_SENTINEL ||
+ d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)){
+ toRemove = b;
+ }
+ }
+ }
+ }
+ Assert(toRemove != ARITHVAR_SENTINEL);
+ Assert(toAdd != ARITHVAR_SENTINEL);
+
+ Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+ d_tableau.pivot(toRemove, toAdd, d_trackCallback);
+ d_basicVariableUpdates(toAdd);
+
+ Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+ needsToBeAdded.remove(toAdd);
+ }
+}
+
+void LinearEqualityModule::updateUntracked(ArithVar x_i, const DeltaRational& v){
+ Assert(!d_tableau.isBasic(x_i));
+ Assert(!d_areTracking);
+ const DeltaRational& assignment_x_i = d_variables.getAssignment(x_i);
+ ++(d_statistics.d_statUpdates);
+
+
+ Trace("arith") <<"update " << x_i << ": "
+ << assignment_x_i << "|-> " << v << endl;
+ DeltaRational diff = v - assignment_x_i;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == x_i);
+
+ ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ const Rational& a_ji = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(x_j);
+ DeltaRational nAssignment = assignment+(diff * a_ji);
+ d_variables.setAssignment(x_j, nAssignment);
+
+ d_basicVariableUpdates(x_j);
+ }
+
+ d_variables.setAssignment(x_i, v);
+
+ if(TraceIsOn("paranoid:check_tableau")){ debugCheckTableau(); }
+}
+
+void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_adjTime);
+
+ Assert(!d_tableau.isBasic(x_i));
+ Assert(d_areTracking);
+
+ ++(d_statistics.d_statUpdates);
+
+ DeltaRational diff = v - d_variables.getAssignment(x_i);
+ Trace("arith") <<"update " << x_i << ": "
+ << d_variables.getAssignment(x_i) << "|-> " << v << endl;
+
+
+ BoundCounts before = d_variables.atBoundCounts(x_i);
+ d_variables.setAssignment(x_i, v);
+ BoundCounts after = d_variables.atBoundCounts(x_i);
+
+ bool anyChange = before != after;
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == x_i);
+
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar x_j = d_tableau.rowIndexToBasic(ridx);
+ const Rational& a_ji = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(x_j);
+ DeltaRational nAssignment = assignment+(diff * a_ji);
+ Trace("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl;
+ BoundCounts xjBefore = d_variables.atBoundCounts(x_j);
+ d_variables.setAssignment(x_j, nAssignment);
+ BoundCounts xjAfter = d_variables.atBoundCounts(x_j);
+
+ Assert(rowIndexIsTracked(ridx));
+ BoundsInfo& next_bc_k = d_btracking.get(ridx);
+ if(anyChange){
+ next_bc_k.addInAtBoundChange(a_ji.sgn(), before, after);
+ }
+ if(xjBefore != xjAfter){
+ next_bc_k.addInAtBoundChange(-1, xjBefore, xjAfter);
+ }
+
+ d_basicVariableUpdates(x_j);
+ }
+
+ if(TraceIsOn("paranoid:check_tableau")){ debugCheckTableau(); }
+}
+
+void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& x_i_value){
+ Assert(x_i != x_j);
+
+ TimerStat::CodeTimer codeTimer(d_statistics.d_pivotTime);
+
+ if(TraceIsOn("arith::tracking::pre")){
+ Trace("arith::tracking") << "pre update" << endl;
+ debugCheckTracking();
+ }
+
+ if(TraceIsOn("arith::simplex:row")){ debugPivot(x_i, x_j); }
+
+ RowIndex ridx = d_tableau.basicToRowIndex(x_i);
+ const Tableau::Entry& entry_ij = d_tableau.findEntry(ridx, x_j);
+ Assert(!entry_ij.blank());
+
+ const Rational& a_ij = entry_ij.getCoefficient();
+ const DeltaRational& betaX_i = d_variables.getAssignment(x_i);
+ DeltaRational theta = (x_i_value - betaX_i)/a_ij;
+ DeltaRational x_j_value = d_variables.getAssignment(x_j) + theta;
+
+ updateTracked(x_j, x_j_value);
+
+ if(TraceIsOn("arith::tracking::mid")){
+ Trace("arith::tracking") << "postupdate prepivot" << endl;
+ debugCheckTracking();
+ }
+
+ // Pivots
+ ++(d_statistics.d_statPivots);
+
+ d_tableau.pivot(x_i, x_j, d_trackCallback);
+
+ if(TraceIsOn("arith::tracking::post")){
+ Trace("arith::tracking") << "postpivot" << endl;
+ debugCheckTracking();
+ }
+
+ d_basicVariableUpdates(x_j);
+
+ if(TraceIsOn("matrix")){
+ d_tableau.printMatrix();
+ }
+}
+
+uint32_t LinearEqualityModule::updateProduct(const UpdateInfo& inf) const {
+ uint32_t colLen = d_tableau.getColLength(inf.nonbasic());
+ if(inf.describesPivot()){
+ Assert(inf.leaving() != inf.nonbasic());
+ return colLen + d_tableau.basicRowLength(inf.leaving());
+ }else{
+ return colLen;
+ }
+}
+
+void LinearEqualityModule::debugCheckTracking(){
+ Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
+ endIter = d_tableau.endBasic();
+ for(; basicIter != endIter; ++basicIter){
+ ArithVar basic = *basicIter;
+ Trace("arith::tracking") << "arith::tracking row basic: " << basic << endl;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd() && TraceIsOn("arith::tracking"); ++iter){
+ const Tableau::Entry& entry = *iter;
+
+ ArithVar var = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ DeltaRational beta = d_variables.getAssignment(var);
+ Trace("arith::tracking") << var << " " << d_variables.boundsInfo(var)
+ << " " << beta << coeff;
+ if(d_variables.hasLowerBound(var)){
+ Trace("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
+ }
+ if(d_variables.hasUpperBound(var)){
+ Trace("arith::tracking") << "(up " << d_variables.getUpperBound(var) << ")";
+ }
+ Trace("arith::tracking") << endl;
+ }
+ Trace("arith::tracking") << "end row"<< endl;
+
+ if(basicIsTracked(basic)){
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+ BoundsInfo computed = computeRowBoundInfo(ridx, false);
+ Trace("arith::tracking")
+ << "computed " << computed
+ << " tracking " << d_btracking[ridx] << endl;
+ Assert(computed == d_btracking[ridx]);
+ }
+ }
+}
+
+void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
+ Trace("arith::pivot") << "debugPivot("<< x_i <<"|->"<< x_j << ")" << endl;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+
+ ArithVar var = entry.getColVar();
+ const Rational& coeff = entry.getCoefficient();
+ DeltaRational beta = d_variables.getAssignment(var);
+ Trace("arith::pivot") << var << beta << coeff;
+ if(d_variables.hasLowerBound(var)){
+ Trace("arith::pivot") << "(lb " << d_variables.getLowerBound(var) << ")";
+ }
+ if(d_variables.hasUpperBound(var)){
+ Trace("arith::pivot") << "(up " << d_variables.getUpperBound(var) << ")";
+ }
+ Trace("arith::pivot") << endl;
+ }
+ Trace("arith::pivot") << "end row"<< endl;
+}
+
+/**
+ * This check is quite expensive.
+ * It should be wrapped in a TraceIsOn() guard.
+ * if(TraceIsOn("paranoid:check_tableau")){
+ * checkTableau();
+ * }
+ */
+void LinearEqualityModule::debugCheckTableau(){
+ Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
+ endIter = d_tableau.endBasic();
+ for(; basicIter != endIter; ++basicIter){
+ ArithVar basic = *basicIter;
+ DeltaRational sum;
+ Trace("paranoid:check_tableau") << "starting row" << basic << endl;
+ Tableau::RowIterator nonbasicIter = d_tableau.basicRowIterator(basic);
+ for(; !nonbasicIter.atEnd(); ++nonbasicIter){
+ const Tableau::Entry& entry = *nonbasicIter;
+ ArithVar nonbasic = entry.getColVar();
+ if(basic == nonbasic) continue;
+
+ const Rational& coeff = entry.getCoefficient();
+ DeltaRational beta = d_variables.getAssignment(nonbasic);
+ Trace("paranoid:check_tableau") << nonbasic << beta << coeff<<endl;
+ sum = sum + (beta*coeff);
+ }
+ DeltaRational shouldBe = d_variables.getAssignment(basic);
+ Trace("paranoid:check_tableau") << "ending row" << sum
+ << "," << shouldBe << endl;
+
+ Assert(sum == shouldBe);
+ }
+}
+
+DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const {
+ DeltaRational sum(0,0);
+ for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = (*i);
+ ArithVar v = entry.getColVar();
+ if(v == skip){ continue; }
+
+ const Rational& coeff = entry.getCoefficient();
+ bool vUb = (rowUb == (coeff.sgn() > 0));
+
+ const DeltaRational& bound = vUb ?
+ d_variables.getUpperBound(v):
+ d_variables.getLowerBound(v);
+
+ DeltaRational diff = bound * coeff;
+ sum = sum + diff;
+ }
+ return sum;
+}
+
+/**
+ * Computes the value of a basic variable using the current assignment.
+ */
+DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{
+ Assert(d_tableau.isBasic(x));
+ DeltaRational sum(0);
+
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(x); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = (*i);
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x) continue;
+ const Rational& coeff = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(nonbasic, useSafe);
+ sum = sum + (assignment * coeff);
+ }
+ return sum;
+}
+
+const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool rowUb, ArithVar skip){
+ Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+ for(; !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+
+ ArithVar var = entry.getColVar();
+ if(var == skip) { continue; }
+
+ int sgn = entry.getCoefficient().sgn();
+ bool selectUb = (rowUb == (sgn > 0));
+ bool hasBound = selectUb ?
+ d_variables.hasUpperBound(var):
+ d_variables.hasLowerBound(var);
+ if(!hasBound){
+ return &entry;
+ }
+ }
+ return NULL;
+}
+
+void LinearEqualityModule::propagateBasicFromRow(ConstraintP c,
+ bool produceProofs)
+{
+ Assert(c != NullConstraint);
+ Assert(c->isUpperBound() || c->isLowerBound());
+ Assert(!c->assertedToTheTheory());
+ Assert(!c->hasProof());
+
+ bool upperBound = c->isUpperBound();
+ ArithVar basic = c->getVariable();
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+ ConstraintCPVec bounds;
+ RationalVectorP coeffs = produceProofs ? new RationalVector() : nullptr;
+ propagateRow(bounds, ridx, upperBound, c, coeffs);
+ c->impliedByFarkas(bounds, coeffs, false);
+ c->tryToPropagate();
+
+ if(coeffs != RationalVectorPSentinel) { delete coeffs; }
+}
+
+/* An explanation of the farkas coefficients.
+ *
+ * We are proving c using the other variables on the row.
+ * The proof is in terms of the other constraints and the negation of c, ~c.
+ *
+ * A row has the form:
+ * sum a_i * x_i = 0
+ * or
+ * sx + sum r y + sum q z = 0
+ * where r > 0 and q < 0.
+ *
+ * If rowUp, we are proving c
+ * g = sum r u_y + sum q l_z
+ * and c is entailed by -sx <= g
+ * If !rowUp, we are proving c
+ * g = sum r l_y + sum q u_z
+ * and c is entailed by -sx >= g
+ *
+ * | s | c | ~c | u_i | l_i
+ * if rowUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
+ * if rowUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
+ * if !rowUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
+ * if !rowUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
+ *
+ *
+ * Thus we treat !rowUp as multiplying the row by -1 and rowUp as 1
+ * for the entire row.
+ */
+void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c, RationalVectorP farkas){
+ Assert(!c->assertedToTheTheory());
+ Assert(c->canBePropagated());
+ Assert(!c->hasProof());
+
+ if(farkas != RationalVectorPSentinel){
+ Assert(farkas->empty());
+ farkas->push_back(Rational(0));
+ }
+
+ ArithVar v = c->getVariable();
+ Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
+ << ridx << ", " << rowUp << ", " << v << ") start" << endl;
+
+ const Rational& multiple = rowUp ? d_one : d_negOne;
+
+ Trace("arith::propagateRow") << "multiple: " << multiple << endl;
+
+ Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+ for(; !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ Assert(sgn != 0);
+ bool selectUb = rowUp ? (sgn > 0) : (sgn < 0);
+
+ Assert(nonbasic != v || (rowUp && a_ij.sgn() > 0 && c->isLowerBound())
+ || (rowUp && a_ij.sgn() < 0 && c->isUpperBound())
+ || (!rowUp && a_ij.sgn() > 0 && c->isUpperBound())
+ || (!rowUp && a_ij.sgn() < 0 && c->isLowerBound()));
+
+ if(TraceIsOn("arith::propagateRow")){
+ if(nonbasic == v){
+ Trace("arith::propagateRow") << "(target) "
+ << rowUp << " "
+ << a_ij.sgn() << " "
+ << c->isLowerBound() << " "
+ << c->isUpperBound() << endl;
+
+ Trace("arith::propagateRow") << "(target) ";
+ }
+ Trace("arith::propagateRow") << "propagateRow " << a_ij << " * " << nonbasic ;
+ }
+
+ if(nonbasic == v){
+ if(farkas != RationalVectorPSentinel){
+ Assert(farkas->front().isZero());
+ Rational multAij = multiple * a_ij;
+ Trace("arith::propagateRow") << "(" << multAij << ") ";
+ farkas->front() = multAij;
+ }
+
+ Trace("arith::propagateRow") << c << endl;
+ }else{
+
+ ConstraintCP bound = selectUb
+ ? d_variables.getUpperBoundConstraint(nonbasic)
+ : d_variables.getLowerBoundConstraint(nonbasic);
+
+ if(farkas != RationalVectorPSentinel){
+ Rational multAij = multiple * a_ij;
+ Trace("arith::propagateRow") << "(" << multAij << ") ";
+ farkas->push_back(multAij);
+ }
+ Assert(bound != NullConstraint);
+ Trace("arith::propagateRow") << bound << endl;
+ into.push_back(bound);
+ }
+ }
+ Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
+ << ridx << ", " << rowUp << ", " << v << ") done" << endl;
+
+}
+
+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);
+
+ ConstraintP c = ub ?
+ d_variables.getUpperBoundConstraint(v) :
+ d_variables.getLowerBoundConstraint(v);
+
+ bool weakened;
+ do{
+ const DeltaRational& bound = c->getValue();
+
+ weakened = false;
+
+ ConstraintP weaker = ub?
+ c->getStrictlyWeakerUpperBound(true, true):
+ c->getStrictlyWeakerLowerBound(true, true);
+
+ if(weaker != NullConstraint){
+ const DeltaRational& weakerBound = weaker->getValue();
+
+ DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
+ //if var == basic,
+ // if aboveUpper, weakerBound > bound, multiply by -1
+ // if !aboveUpper, weakerBound < bound, multiply by -1
+ diff = diff * coeff;
+ if(surplus > diff){
+ ++d_statistics.d_weakenings;
+ weakened = true;
+ anyWeakening = true;
+ surplus = surplus - diff;
+
+ Trace("arith::weak") << "found:" << endl;
+ if(v == basic){
+ Trace("arith::weak") << " basic: ";
+ }
+ Trace("arith::weak") << " " << surplus << " "<< diff << endl
+ << " " << bound << c << endl
+ << " " << weakerBound << weaker << endl;
+
+ Assert(diff.sgn() > 0);
+ c = weaker;
+ }
+ }
+ }while(weakened);
+
+ return c;
+}
+
+/* An explanation of the farkas coefficients.
+ *
+ * We are proving a conflict on the basic variable x_b.
+ * If aboveUpper, then the conflict is with the constraint c : x_b <= u_b.
+ * If !aboveUpper, then the conflict is with the constraint c : x_b >= l_b.
+ *
+ * A row has the form:
+ * -x_b sum a_i * x_i = 0
+ * or
+ * -x_b + sum r y + sum q z = 0,
+ * x_b = sum r y + sum q z
+ * where r > 0 and q < 0.
+ *
+ *
+ * If !aboveUp, we are proving ~c: x_b < l_b
+ * g = sum r u_y + sum q l_z
+ * x_b <= g < l_b
+ * and ~c is entailed by x_b <= g
+ *
+ * If aboveUp, we are proving ~c : x_b > u_b
+ * g = sum r l_y + sum q u_z
+ * x_b >= g > u_b
+ * and ~c is entailed by x_b >= g
+ *
+ *
+ * | s | c | ~c | u_i | l_i
+ * if !aboveUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
+ * if !aboveUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
+ * if aboveUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
+ * if aboveUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
+ *
+ * Thus we treat aboveUp as multiplying the row by -1 and !aboveUp as 1
+ * for the entire row.
+ */
+ConstraintCP LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& fcs) const {
+ Assert(!fcs.underConstruction());
+ TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
+
+ Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
+ << aboveUpper <<", "<< basicVar << ", ...) start" << endl;
+
+ const Rational& adjustSgn = aboveUpper ? d_negOne : d_one;
+ const DeltaRational& assignment = d_variables.getAssignment(basicVar);
+ DeltaRational surplus;
+ if(aboveUpper){
+ Assert(d_variables.hasUpperBound(basicVar));
+ Assert(assignment > d_variables.getUpperBound(basicVar));
+ surplus = assignment - d_variables.getUpperBound(basicVar);
+ }else{
+ Assert(d_variables.hasLowerBound(basicVar));
+ Assert(assignment < d_variables.getLowerBound(basicVar));
+ surplus = d_variables.getLowerBound(basicVar) - assignment;
+ }
+
+ 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;
+ ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
+ Trace("arith::weak") << "weak : " << weakening << " "
+ << c->assertedToTheTheory() << " "
+ << d_variables.getAssignment(v) << " "
+ << c << endl;
+ anyWeakenings = anyWeakenings || weakening;
+
+ fcs.addConstraint(c, coeff, adjustSgn);
+ if(basicVar == v){
+ Assert(!c->negationHasProof());
+ fcs.makeLastConsequent();
+ }
+ }
+ Assert(fcs.consequentIsSet());
+
+ ConstraintCP conflicted = fcs.commitConflict();
+
+ ++d_statistics.d_weakeningAttempts;
+ if(anyWeakenings){
+ ++d_statistics.d_weakeningSuccesses;
+ }
+ Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
+ << aboveUpper <<", "<< basicVar << ", ...) done" << endl;
+ return conflicted;
+}
+
+ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ if(x <= y){
+ return x;
+ } else {
+ return y;
+ }
+}
+
+ArithVar LinearEqualityModule::minColLength(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!d_tableau.isBasic(x));
+ Assert(!d_tableau.isBasic(y));
+ uint32_t xLen = d_tableau.getColLength(x);
+ uint32_t yLen = d_tableau.getColLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar LinearEqualityModule::minRowLength(ArithVar x, ArithVar y) const {
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(x));
+ Assert(d_tableau.isBasic(y));
+ uint32_t xLen = d_tableau.basicRowLength(x);
+ uint32_t yLen = d_tableau.basicRowLength(y);
+ if( xLen > yLen){
+ return y;
+ } else if( xLen== yLen ){
+ return minVarOrder(x,y);
+ }else{
+ return x;
+ }
+}
+
+ArithVar LinearEqualityModule::minBoundAndColLength(ArithVar x, ArithVar y) const{
+ Assert(x != ARITHVAR_SENTINEL);
+ Assert(y != ARITHVAR_SENTINEL);
+ Assert(!d_tableau.isBasic(x));
+ Assert(!d_tableau.isBasic(y));
+ if(d_variables.hasEitherBound(x) && !d_variables.hasEitherBound(y)){
+ return y;
+ }else if(!d_variables.hasEitherBound(x) && d_variables.hasEitherBound(y)){
+ return x;
+ }else {
+ return minColLength(x, y);
+ }
+}
+
+template <bool above>
+ArithVar LinearEqualityModule::selectSlack(ArithVar x_i, VarPreferenceFunction pref) const{
+ ArithVar slack = ARITHVAR_SENTINEL;
+
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(isAcceptableSlack<above>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : (this->*pref)(slack, nonbasic);
+ }
+ }
+
+ return slack;
+}
+
+const Tableau::Entry* LinearEqualityModule::selectSlackEntry(ArithVar x_i, bool above) const{
+ for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar nonbasic = entry.getColVar();
+ if(nonbasic == x_i) continue;
+
+ const Rational& a_ij = entry.getCoefficient();
+ int sgn = a_ij.sgn();
+ if(above && isAcceptableSlack<true>(sgn, nonbasic)){
+ //If one of the above conditions is met, we have found an acceptable
+ //nonbasic variable to pivot x_i with. We can now choose which one we
+ //prefer the most.
+ return &entry;
+ }else if(!above && isAcceptableSlack<false>(sgn, nonbasic)){
+ return &entry;
+ }
+ }
+
+ return NULL;
+}
+
+void LinearEqualityModule::startTrackingBoundCounts(){
+ Assert(!d_areTracking);
+ d_areTracking = true;
+ if(TraceIsOn("arith::tracking")){
+ debugCheckTracking();
+ }
+ Assert(d_areTracking);
+}
+
+void LinearEqualityModule::stopTrackingBoundCounts(){
+ Assert(d_areTracking);
+ d_areTracking = false;
+ if(TraceIsOn("arith::tracking")){
+ debugCheckTracking();
+ }
+ Assert(!d_areTracking);
+}
+
+
+void LinearEqualityModule::trackRowIndex(RowIndex ridx){
+ Assert(!rowIndexIsTracked(ridx));
+ BoundsInfo bi = computeRowBoundInfo(ridx, true);
+ d_btracking.set(ridx, bi);
+}
+
+BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{
+ BoundsInfo bi;
+
+ Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+ for(; !iter.atEnd(); ++iter){
+ const Tableau::Entry& entry = *iter;
+ ArithVar v = entry.getColVar();
+ const Rational& a_ij = entry.getCoefficient();
+ bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn());
+ }
+ return bi;
+}
+
+BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const {
+ return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds();
+}
+
+/**
+ * If the pivot described in u were performed,
+ * then the row would qualify as being either at the minimum/maximum
+ * to the non-basics being at their bounds.
+ * The minimum/maximum is determined by the direction the non-basic is changing.
+ */
+bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
+ Assert(u.describesPivot());
+
+ ArithVar nonbasic = u.nonbasic();
+ ArithVar basic = u.leaving();
+ Assert(basicIsTracked(basic));
+ int coeffSgn = u.getCoefficient().sgn();
+ int nbdir = u.nonbasicDirection();
+
+ ConstraintP c = u.limiting();
+ int toUB = (c->getType() == UpperBound ||
+ c->getType() == Equality) ? 1 : 0;
+ int toLB = (c->getType() == LowerBound ||
+ c->getType() == Equality) ? 1 : 0;
+
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+ BoundCounts bcs = d_btracking[ridx].atBounds();
+ // x = c*n + \sum d*m
+ // 0 = -x + c*n + \sum d*m
+ // n = 1/c * x + -1/c * (\sum d*m)
+ BoundCounts nonb = bcs - d_variables.atBoundCounts(nonbasic).multiplyBySgn(coeffSgn);
+ nonb.addInChange(-1, d_variables.atBoundCounts(basic), BoundCounts(toLB, toUB));
+ nonb = nonb.multiplyBySgn(-coeffSgn);
+
+ uint32_t length = d_tableau.basicRowLength(basic);
+ Trace("basicsAtBounds")
+ << "bcs " << bcs
+ << "nonb " << nonb
+ << "length " << length << endl;
+ // nonb has nb excluded.
+ if(nbdir < 0){
+ return nonb.lowerBoundCount() + 1 == length;
+ }else{
+ Assert(nbdir > 0);
+ return nonb.upperBoundCount() + 1 == length;
+ }
+}
+
+bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
+ Assert(basicIsTracked(basic));
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+ BoundCounts bcs = d_btracking[ridx].atBounds();
+ uint32_t length = d_tableau.basicRowLength(basic);
+
+ // return true if excluding the basic is every element is at its "lowerbound"
+ // The psuedo code is:
+ // bcs -= basic.count(basic, basic's sgn)
+ // return bcs.lowerBoundCount() + 1 == length
+ // As basic's sign is always -1, we can pull out the pieces of the count:
+ // bcs.lowerBoundCount() - basic.atUpperBoundInd() + 1 == length
+ // basic.atUpperBoundInd() is either 0 or 1
+ uint32_t lbc = bcs.lowerBoundCount();
+ return (lbc == length) ||
+ (lbc + 1 == length && d_variables.cmpAssignmentUpperBound(basic) != 0);
+}
+
+bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const {
+ Assert(basicIsTracked(basic));
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+ BoundCounts bcs = d_btracking[ridx].atBounds();
+ uint32_t length = d_tableau.basicRowLength(basic);
+ uint32_t ubc = bcs.upperBoundCount();
+ // See the comment for nonbasicsAtLowerBounds()
+
+ return (ubc == length) ||
+ (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0);
+}
+
+void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) {
+ Assert(rowIndexIsTracked(ridx));
+ Assert(sgn != 0);
+ if(sgn < 0){
+ BoundsInfo& bi = d_btracking.get(ridx);
+ bi = bi.multiplyBySgn(sgn);
+ }
+}
+
+void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
+ Assert(oldSgn != currSgn);
+ BoundsInfo nb_inf = d_variables.boundsInfo(nb);
+
+ Assert(rowIndexIsTracked(ridx));
+
+ BoundsInfo& row_bi = d_btracking.get(ridx);
+ row_bi.addInSgn(nb_inf, oldSgn, currSgn);
+}
+
+ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
+ if(vec.empty()) {
+ return ARITHVAR_SENTINEL;
+ }else {
+ ArithVar sel = vec.front();
+ ArithVarVec::const_iterator i = vec.begin() + 1;
+ ArithVarVec::const_iterator i_end = vec.end();
+ for(; i != i_end; ++i){
+ sel = (this->*pf)(sel, *i);
+ }
+ return sel;
+ }
+}
+
+bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub){
+ ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+
+ Assert(basicIsTracked(currBasic));
+
+ ConstraintP bound = ub ?
+ d_variables.getUpperBoundConstraint(currBasic):
+ d_variables.getLowerBoundConstraint(currBasic);
+
+ if(bound == NullConstraint){ return false; }
+ Assert(bound != NullConstraint);
+
+ const Rational& coeff = entry.getCoefficient();
+
+ const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+ DeltaRational toBound = bound->getValue() - assignment;
+ DeltaRational nbDiff = toBound/coeff;
+
+ // if ub
+ // if toUB >= 0
+ // then ub >= currBasic
+ // if sgn > 0,
+ // then diff >= 0, so nb must increase for G
+ // else diff <= 0, so nb must decrease for G
+ // else ub < currBasic
+ // if sgn > 0,
+ // then diff < 0, so nb must decrease for G
+ // else diff > 0, so nb must increase for G
+
+ int diffSgn = nbDiff.sgn();
+
+ if(diffSgn != 0 && willBeInConflictAfterPivot(entry, nbDiff, ub)){
+ return true;
+ }else{
+ bool areFixing = ub ? (toBound.sgn() < 0 ) : (toBound.sgn() > 0);
+ Border border(bound, nbDiff, areFixing, &entry, ub);
+ bool increasing =
+ (diffSgn > 0) ||
+ (diffSgn == 0 && ((coeff.sgn() > 0) == ub));
+
+ // assume diffSgn == 0
+ // if coeff > 0,
+ // if ub, inc
+ // else, dec
+ // else coeff < 0
+ // if ub, dec
+ // else, inc
+
+ if(increasing){
+ Trace("handleBorders") << "push back increasing " << border << endl;
+ d_increasing.push_back(border);
+ }else{
+ Trace("handleBorders") << "push back decreasing " << border << endl;
+ d_decreasing.push_back(border);
+ }
+ return false;
+ }
+}
+
+bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const{
+ int nbSgn = nbDiff.sgn();
+ Assert(nbSgn != 0);
+
+ if(nbSgn > 0){
+ if (!d_upperBoundDifference || nbDiff <= *d_upperBoundDifference)
+ {
+ return false;
+ }
+ }else{
+ if (!d_lowerBoundDifference || nbDiff >= *d_lowerBoundDifference)
+ {
+ return false;
+ }
+ }
+
+ // Assume past this point, nb will be in error if this pivot is done
+ ArithVar nb = entry.getColVar();
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+ Assert(rowIndexIsTracked(ridx));
+ int coeffSgn = entry.getCoefficient().sgn();
+
+
+ // if bToUB, then basic is going to be set to its upperbound
+ // if not bToUB, then basic is going to be set to its lowerbound
+
+ // Different steps of solving for this:
+ // 1) y = a * x + \sum b * z
+ // 2) -a * x = -y + \sum b * z
+ // 3) x = (-1/a) * ( -y + \sum b * z)
+
+ BoundCounts bc = d_btracking[ridx].atBounds();
+
+ // 1) y = a * x + \sum b * z
+ // Get bc(\sum b * z)
+ BoundCounts sumOnly = bc - d_variables.atBoundCounts(nb).multiplyBySgn(coeffSgn);
+
+ // y's bounds in the proposed model
+ int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+ int yWillBeAtLb = (!bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
+ BoundCounts ysBounds(yWillBeAtLb, yWillBeAtUb);
+
+ // 2) -a * x = -y + \sum b * z
+ // Get bc(-y + \sum b * z)
+ sumOnly.addInChange(-1, d_variables.atBoundCounts(basic), ysBounds);
+
+ // 3) x = (-1/a) * ( -y + \sum b * z)
+ // Get bc((-1/a) * ( -y + \sum b * z))
+ BoundCounts xsBoundsAfterPivot = sumOnly.multiplyBySgn(-coeffSgn);
+
+ uint32_t length = d_tableau.basicRowLength(basic);
+ if(nbSgn > 0){
+ // Only check for the upper bound being violated
+ return xsBoundsAfterPivot.lowerBoundCount() + 1 == length;
+ }else{
+ // Only check for the lower bound being violated
+ return xsBoundsAfterPivot.upperBoundCount() + 1 == length;
+ }
+}
+
+UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, bool ub) const{
+ ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ ArithVar nb = entry.getColVar();
+
+ ConstraintP bound = ub ?
+ d_variables.getUpperBoundConstraint(currBasic):
+ d_variables.getLowerBoundConstraint(currBasic);
+
+
+ const Rational& coeff = entry.getCoefficient();
+ const DeltaRational& assignment = d_variables.getAssignment(currBasic);
+ DeltaRational toBound = bound->getValue() - assignment;
+ DeltaRational nbDiff = toBound/coeff;
+
+ return UpdateInfo::conflict(nb, nbDiff, coeff, bound);
+}
+
+UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref){
+ Assert(d_increasing.empty());
+ Assert(d_decreasing.empty());
+ Assert(!d_lowerBoundDifference);
+ Assert(!d_upperBoundDifference);
+
+ int focusCoeffSgn = focusCoeff.sgn();
+
+ Trace("speculativeUpdate") << "speculativeUpdate" << endl;
+ Trace("speculativeUpdate") << "nb " << nb << endl;
+ Trace("speculativeUpdate") << "focusCoeff " << focusCoeff << endl;
+
+ if(d_variables.hasUpperBound(nb)){
+ ConstraintP ub = d_variables.getUpperBoundConstraint(nb);
+ d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb);
+ Border border(ub, *d_upperBoundDifference, false, NULL, true);
+ Trace("handleBorders") << "push back increasing " << border << endl;
+ d_increasing.push_back(border);
+ }
+ if(d_variables.hasLowerBound(nb)){
+ ConstraintP lb = d_variables.getLowerBoundConstraint(nb);
+ d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb);
+ Border border(lb, *d_lowerBoundDifference, false, NULL, false);
+ Trace("handleBorders") << "push back decreasing " << border << endl;
+ d_decreasing.push_back(border);
+ }
+
+ Tableau::ColIterator colIter = d_tableau.colIterator(nb);
+ for(; !colIter.atEnd(); ++colIter){
+ const Tableau::Entry& entry = *colIter;
+ Assert(entry.getColVar() == nb);
+
+ if(accumulateBorder(entry, true)){
+ clearSpeculative();
+ return mkConflictUpdate(entry, true);
+ }
+ if(accumulateBorder(entry, false)){
+ clearSpeculative();
+ return mkConflictUpdate(entry, false);
+ }
+ }
+
+ UpdateInfo selected;
+ BorderHeap& withSgn = focusCoeffSgn > 0 ? d_increasing : d_decreasing;
+ BorderHeap& againstSgn = focusCoeffSgn > 0 ? d_decreasing : d_increasing;
+
+ handleBorders(selected, nb, focusCoeff, withSgn, 0, pref);
+ int m = 1 - selected.errorsChangeSafe(0);
+ handleBorders(selected, nb, focusCoeff, againstSgn, m, pref);
+
+ clearSpeculative();
+ return selected;
+}
+
+void LinearEqualityModule::clearSpeculative(){
+ // clear everything away
+ d_increasing.clear();
+ d_decreasing.clear();
+ d_lowerBoundDifference.reset();
+ d_upperBoundDifference.reset();
+}
+
+void LinearEqualityModule::handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref){
+ Assert(minimumFixes >= 0);
+
+ // The values popped off of the heap
+ // should be popped with the values closest to 0
+ // being first and larger in absolute value last
+
+
+ int fixesRemaining = heap.possibleFixes();
+
+ Trace("handleBorders")
+ << "handleBorders "
+ << "nb " << nb
+ << "fc " << focusCoeff
+ << "h.e " << heap.empty()
+ << "h.dir " << heap.direction()
+ << "h.rem " << fixesRemaining
+ << "h.0s " << heap.numZeroes()
+ << "min " << minimumFixes
+ << endl;
+
+ if(heap.empty()){
+ // if the heap is empty, return
+ return;
+ }
+
+ bool zeroesWillDominate = fixesRemaining - heap.numZeroes() < minimumFixes;
+
+ // can the number of fixes ever exceed the minimum?
+ // no more than the number of possible fixes can be fixed in total
+ // nothing can be fixed before the zeroes are taken care of
+ if(minimumFixes > 0 && zeroesWillDominate){
+ return;
+ }
+
+
+ int negErrorChange = 0;
+ int nbDir = heap.direction();
+
+ // points at the beginning of the heap
+ if(zeroesWillDominate){
+ heap.dropNonZeroes();
+ }
+ heap.make_heap();
+
+
+ // pretend like the previous block had a value of zero.
+ // The block that actually has a value of 0 must handle this.
+ const DeltaRational zero(0);
+ const DeltaRational* prevBlockValue = &zero;
+
+ /** The coefficient changes as the value crosses border. */
+ Rational effectiveCoefficient = focusCoeff;
+
+ /* Keeps track of the change to the value of the focus function.*/
+ DeltaRational totalFocusChange(0);
+
+ const int focusCoeffSgn = focusCoeff.sgn();
+
+ while(heap.more() &&
+ (fixesRemaining + negErrorChange > minimumFixes ||
+ (fixesRemaining + negErrorChange == minimumFixes &&
+ effectiveCoefficient.sgn() == focusCoeffSgn))){
+ // There are more elements &&
+ // we can either fix at least 1 more variable in the error function
+ // or we can improve the error function
+
+
+ int brokenInBlock = 0;
+ BorderVec::const_iterator endBlock = heap.end();
+
+ pop_block(heap, brokenInBlock, fixesRemaining, negErrorChange);
+
+ // if endVec == beginVec, block starts there
+ // other wise, block starts at endVec
+ BorderVec::const_iterator startBlock
+ = heap.more() ? heap.end() : heap.begin();
+
+ const DeltaRational& blockValue = (*startBlock).d_diff;
+
+ // if decreasing
+ // blockValue < prevBlockValue
+ // diff.sgn() = -1
+ DeltaRational diff = blockValue - (*prevBlockValue);
+ DeltaRational blockChangeToFocus = diff * effectiveCoefficient;
+ totalFocusChange += blockChangeToFocus;
+
+ Trace("handleBorders")
+ << "blockValue " << (blockValue)
+ << "diff " << diff
+ << "blockChangeToFocus " << totalFocusChange
+ << "blockChangeToFocus " << totalFocusChange
+ << "negErrorChange " << negErrorChange
+ << "brokenInBlock " << brokenInBlock
+ << "fixesRemaining " << fixesRemaining
+ << endl;
+
+ int currFocusChangeSgn = totalFocusChange.sgn();
+ for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+ const Border& b = *i;
+
+ Trace("handleBorders") << b << endl;
+
+ bool makesImprovement = negErrorChange > 0 ||
+ (negErrorChange == 0 && currFocusChangeSgn > 0);
+
+ if(!makesImprovement){
+ if(b.ownBorder() || minimumFixes > 0){
+ continue;
+ }
+ }
+
+ UpdateInfo proposal(nb, nbDir);
+ if(b.ownBorder()){
+ proposal.witnessedUpdate(b.d_diff, b.d_bound, -negErrorChange, currFocusChangeSgn);
+ }else{
+ proposal.update(b.d_diff, b.getCoefficient(), b.d_bound, -negErrorChange, currFocusChangeSgn);
+ }
+
+ if(selected.unbounded() || (this->*pref)(selected, proposal)){
+ selected = proposal;
+ }
+ }
+
+ effectiveCoefficient += updateCoefficient(startBlock, endBlock);
+ prevBlockValue = &blockValue;
+ negErrorChange -= brokenInBlock;
+ }
+}
+
+Rational LinearEqualityModule::updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock){
+ //update coefficient
+ Rational changeToCoefficient(0);
+ for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
+ const Border& curr = *i;
+ if(curr.ownBorder()){// breaking its own bound
+ if(curr.d_upperbound){
+ changeToCoefficient -= 1;
+ }else{
+ changeToCoefficient += 1;
+ }
+ }else{
+ const Rational& coeff = curr.d_entry->getCoefficient();
+ if(curr.d_areFixing){
+ if(curr.d_upperbound){// fixing an upper bound
+ changeToCoefficient += coeff;
+ }else{// fixing a lower bound
+ changeToCoefficient -= coeff;
+ }
+ }else{
+ if(curr.d_upperbound){// breaking an upper bound
+ changeToCoefficient -= coeff;
+ }else{
+ // breaking a lower bound
+ changeToCoefficient += coeff;
+ }
+ }
+ }
+ }
+ return changeToCoefficient;
+}
+
+void LinearEqualityModule::pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange){
+ Assert(heap.more());
+
+ if(heap.top().d_areFixing){
+ fixesRemaining--;
+ negErrorChange++;
+ }else{
+ brokenInBlock++;
+ }
+ heap.pop_heap();
+ const DeltaRational& blockValue = (*heap.end()).d_diff;
+
+ while(heap.more()){
+ const Border& top = heap.top();
+ if(blockValue == top.d_diff){
+ // belongs to the block
+ if(top.d_areFixing){
+ fixesRemaining--;
+ negErrorChange++;
+ }else{
+ brokenInBlock++;
+ }
+ heap.pop_heap();
+ }else{
+ // does not belong to the block
+ Assert((heap.direction() > 0) ? (blockValue < top.d_diff)
+ : (blockValue > top.d_diff));
+ break;
+ }
+ }
+}
+
+void LinearEqualityModule::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult){
+ d_tableau.substitutePlusTimesConstant(to, from, mult, d_trackCallback);
+}
+void LinearEqualityModule::directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult){
+ d_tableau.directlyAddToCoefficient(row, col, mult, d_trackCallback);
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This module maintains the relationship between a Tableau and
+ * PartialModel.
+ *
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This maintains the relationship needed by the SimplexDecisionProcedure.
+ *
+ * In the language of Simplex for DPLL(T), this provides:
+ * - update()
+ * - pivotAndUpdate()
+ *
+ * This class also provides utility functions that require
+ * using both the Tableau and PartialModel.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "options/arith_options.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+struct Border{
+ // The constraint for the border
+ ConstraintP d_bound;
+
+ // The change to the nonbasic to reach the border
+ DeltaRational d_diff;
+
+ // Is reach this value fixing the constraint
+ // or is going past this value hurting the constraint
+ bool d_areFixing;
+
+ // Entry into the tableau
+ const Tableau::Entry* d_entry;
+
+ // Was this an upper bound or a lower bound?
+ bool d_upperbound;
+
+ Border():
+ d_bound(NullConstraint) // ignore the other values
+ {}
+
+ Border(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(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{
+ return d_diff < other.d_diff;
+ }
+
+ /** d_lim is the nonbasic variable's own bound. */
+ bool ownBorder() const { return d_entry == NULL; }
+
+ bool isZero() const { return d_diff.sgn() == 0; }
+ static bool nonZero(const Border& b) { return !b.isZero(); }
+
+ const Rational& getCoefficient() const {
+ Assert(!ownBorder());
+ return d_entry->getCoefficient();
+ }
+ void output(std::ostream& out) const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const Border& b){
+ b.output(out);
+ return out;
+}
+
+typedef std::vector<Border> BorderVec;
+
+class BorderHeap {
+ const int d_dir;
+
+ class BorderHeapCmp {
+ private:
+ int d_nbDirection;
+ public:
+ BorderHeapCmp(int dir): d_nbDirection(dir){}
+ bool operator()(const Border& a, const Border& b) const{
+ if(d_nbDirection > 0){
+ // if nb is increasing,
+ // this needs to act like a max
+ // in order to have a min heap
+ return b < a;
+ }else{
+ // if nb is decreasing,
+ // this needs to act like a min
+ // in order to have a max heap
+ return a < b;
+ }
+ }
+ };
+ const BorderHeapCmp d_cmp;
+
+ BorderVec d_vec;
+
+ BorderVec::iterator d_begin;
+
+ /**
+ * Once this is initialized the top of the heap will always
+ * be at d_end - 1
+ */
+ BorderVec::iterator d_end;
+
+ int d_possibleFixes;
+ int d_numZeroes;
+
+public:
+ BorderHeap(int dir)
+ : d_dir(dir), d_cmp(dir), d_possibleFixes(0), d_numZeroes(0)
+ {}
+
+ void push_back(const Border& b){
+ d_vec.push_back(b);
+ if(b.d_areFixing){
+ d_possibleFixes++;
+ }
+ if(b.d_diff.sgn() == 0){
+ d_numZeroes++;
+ }
+ }
+
+ int numZeroes() const { return d_numZeroes; }
+ int possibleFixes() const { return d_possibleFixes; }
+ int direction() const { return d_dir; }
+
+ void make_heap(){
+ d_begin = d_vec.begin();
+ d_end = d_vec.end();
+ std::make_heap(d_begin, d_end, d_cmp);
+ }
+
+ void dropNonZeroes(){
+ d_vec.erase(std::remove_if(d_vec.begin(), d_vec.end(), &Border::nonZero),
+ d_vec.end());
+ }
+
+ const Border& top() const {
+ Assert(more());
+ return *d_begin;
+ }
+ void pop_heap(){
+ Assert(more());
+
+ std::pop_heap(d_begin, d_end, d_cmp);
+ --d_end;
+ }
+
+ BorderVec::const_iterator end() const{
+ return BorderVec::const_iterator(d_end);
+ }
+ BorderVec::const_iterator begin() const{
+ return BorderVec::const_iterator(d_begin);
+ }
+
+ inline bool more() const{ return d_begin != d_end; }
+
+ inline bool empty() const{ return d_vec.empty(); }
+
+ void clear(){
+ d_possibleFixes = 0;
+ d_numZeroes = 0;
+ d_vec.clear();
+ }
+};
+
+
+class LinearEqualityModule {
+public:
+ typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
+
+
+ typedef bool (LinearEqualityModule::*UpdatePreferenceFunction)(const UpdateInfo&, const UpdateInfo&) const;
+
+
+private:
+ /**
+ * Manages information about the assignment and upper and lower bounds on the
+ * variables.
+ */
+ ArithVariables& d_variables;
+
+ /** Reference to the Tableau to operate upon. */
+ Tableau& d_tableau;
+
+ /** Called whenever the value of a basic variable is updated. */
+ BasicVarModelUpdateCallBack d_basicVariableUpdates;
+
+ BorderHeap d_increasing;
+ BorderHeap d_decreasing;
+ std::optional<DeltaRational> d_upperBoundDifference;
+ std::optional<DeltaRational> d_lowerBoundDifference;
+
+ Rational d_one;
+ Rational d_negOne;
+public:
+
+ /**
+ * Initializes a LinearEqualityModule with a partial model, a tableau,
+ * and a callback function for when basic variables update their values.
+ */
+ LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundTracking, BasicVarModelUpdateCallBack f);
+
+ /**
+ * Updates the assignment of a nonbasic variable x_i to v.
+ * Also updates the assignment of basic variables accordingly.
+ */
+ void update(ArithVar x_i, const DeltaRational& v){
+ if(d_areTracking){
+ updateTracked(x_i,v);
+ }else{
+ updateUntracked(x_i,v);
+ }
+ }
+
+ /** Specialization of update if the module is not tracking yet (for Assert*). */
+ void updateUntracked(ArithVar x_i, const DeltaRational& v);
+
+ /** Specialization of update if the module is not tracking yet (for Simplex). */
+ void updateTracked(ArithVar x_i, const DeltaRational& v);
+
+
+ /**
+ * Updates the value of a basic variable x_i to v,
+ * and then pivots x_i with the nonbasic variable in its row x_j.
+ * Updates the assignment of the other basic variables accordingly.
+ */
+ void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
+
+ ArithVariables& getVariables() const{ return d_variables; }
+ Tableau& getTableau() const{ return d_tableau; }
+
+ /**
+ * Updates every non-basic to reflect the assignment in many.
+ * For use with ApproximateSimplex.
+ */
+ void updateMany(const DenseMap<DeltaRational>& many);
+ void forceNewBasis(const DenseSet& newBasis);
+ void applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues);
+
+
+ /**
+ * Returns a pointer to the first Tableau entry on the row ridx that does not
+ * have an either a lower bound/upper bound for proving a bound on skip.
+ * The variable skip is always excluded. Returns NULL if there is no such element.
+ *
+ * If skip == ARITHVAR_SENTINEL, this is equivalent to considering the whole row.
+ */
+ const Tableau::Entry* rowLacksBound(RowIndex ridx, bool upperBound, ArithVar skip);
+
+
+ void startTrackingBoundCounts();
+ void stopTrackingBoundCounts();
+
+
+ void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev);
+
+
+ uint32_t updateProduct(const UpdateInfo& inf) const;
+
+ inline bool minNonBasicVarOrder(const UpdateInfo& a, const UpdateInfo& b) const{
+ return a.nonbasic() >= b.nonbasic();
+ }
+
+ /**
+ * Prefer the update that touch the fewest entries in the matrix.
+ *
+ * The intuition is that this operation will be cheaper.
+ * This strongly biases the system towards updates instead of pivots.
+ */
+ inline bool minProduct(const UpdateInfo& a, const UpdateInfo& b) const{
+ uint32_t aprod = updateProduct(a);
+ uint32_t bprod = updateProduct(b);
+
+ if(aprod == bprod){
+ return minNonBasicVarOrder(a,b);
+ }else{
+ return aprod > bprod;
+ }
+ }
+ inline bool constrainedMin(const UpdateInfo& a, const UpdateInfo& b) const{
+ if(a.describesPivot() && b.describesPivot()){
+ bool aAtBounds = basicsAtBounds(a);
+ bool bAtBounds = basicsAtBounds(b);
+ if(aAtBounds != bAtBounds){
+ return bAtBounds;
+ }
+ }
+ return minProduct(a,b);
+ }
+
+ /**
+ * If both a and b are pivots, prefer the pivot with the leaving variables that has equal bounds.
+ * The intuition is that such variables will be less likely to lead to future problems.
+ */
+ inline bool preferFrozen(const UpdateInfo& a, const UpdateInfo& b) const {
+ if(a.describesPivot() && b.describesPivot()){
+ bool aFrozen = d_variables.boundsAreEqual(a.leaving());
+ bool bFrozen = d_variables.boundsAreEqual(b.leaving());
+
+ if(aFrozen != bFrozen){
+ return bFrozen;
+ }
+ }
+ return constrainedMin(a,b);
+ }
+
+ /**
+ * Prefer pivots with entering variables that do not have bounds.
+ * The intuition is that such variables will be less likely to lead to future problems.
+ */
+ bool preferNeitherBound(const UpdateInfo& a, const UpdateInfo& b) const {
+ if(d_variables.hasEitherBound(a.nonbasic()) == d_variables.hasEitherBound(b.nonbasic())){
+ return preferFrozen(a,b);
+ }else{
+ return d_variables.hasEitherBound(a.nonbasic());
+ }
+ }
+
+ bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ if(a.nonbasic() == b.nonbasic()){
+ bool aIsZero = a.nonbasicDelta().sgn() == 0;
+ bool bIsZero = b.nonbasicDelta().sgn() == 0;
+
+ if((aIsZero || bIsZero) && (!aIsZero || !bIsZero)){
+ return bIsZero;
+ }else{
+ return a.leaving() >= b.leaving();
+ }
+ }else{
+ return a.nonbasic() > b.nonbasic();
+ }
+ }
+
+ template <bool heuristic>
+ bool preferWitness(const UpdateInfo& a, const UpdateInfo& b) const{
+ WitnessImprovement aImp = a.getWitness(!heuristic);
+ WitnessImprovement bImp = b.getWitness(!heuristic);
+
+ if(aImp == bImp){
+ switch(aImp){
+ case ConflictFound:
+ return preferNeitherBound(a,b);
+ case ErrorDropped:
+ if(a.errorsChange() == b.errorsChange()){
+ return preferNeitherBound(a,b);
+ }else{
+ return a.errorsChange() > b.errorsChange();
+ }
+ case FocusImproved:
+ return preferNeitherBound(a,b);
+ case BlandsDegenerate:
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ return modifiedBlands(a,b);
+ case HeuristicDegenerate:
+ Assert(a.describesPivot());
+ Assert(b.describesPivot());
+ Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
+ return preferNeitherBound(a,b);
+ case AntiProductive:
+ return minNonBasicVarOrder(a, b);
+ // Not valid responses
+ case Degenerate:
+ case FocusShrank:
+ Unreachable();
+ }
+ Unreachable();
+ }else{
+ return aImp > bImp;
+ }
+ }
+
+private:
+
+ /**
+ * This maps each row index to its relevant bounds info.
+ * This tracks the count for how many variables on a row have bounds
+ * and how many are assigned at their bounds.
+ */
+ BoundInfoMap& d_btracking;
+ bool d_areTracking;
+
+public:
+ /**
+ * The constraint on a basic variable b is implied by the constraints
+ * on its row. This is a wrapper for propagateRow().
+ */
+ void propagateBasicFromRow(ConstraintP c, bool produceProofs);
+
+ /**
+ * Let v be the variable for the constraint c.
+ * Exports either the explanation of an upperbound or a lower bound
+ * of v using the other variables in the row.
+ *
+ * If farkas != RationalVectorPSentinel, this function additionally
+ * stores the farkas coefficients of the constraints stored in into.
+ * Position 0 is the coefficient of v.
+ * Position i > 0, corresponds to the order of the other constraints.
+ */
+ void propagateRow(ConstraintCPVec& into,
+ RowIndex ridx,
+ bool rowUp,
+ ConstraintP c,
+ RationalVectorP farkas);
+
+ /**
+ * Computes the value of a basic variable using the assignments
+ * of the values of the variables in the basic variable's row tableau.
+ * This can compute the value using either:
+ * - the the current assignment (useSafe=false) or
+ * - the safe assignment (useSafe = true).
+ */
+ DeltaRational computeRowValue(ArithVar x, bool useSafe) const;
+
+ /**
+ * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
+ * and 2 ArithVar variables and returns one of the ArithVar variables
+ * potentially using the internals of the SimplexDecisionProcedure.
+ */
+
+ ArithVar noPreference(ArithVar x, ArithVar y) const { return x; }
+
+ /**
+ * minVarOrder is a PreferenceFunction for selecting the smaller of the 2
+ * ArithVars. This PreferenceFunction is used during the VarOrder stage of
+ * findModel.
+ */
+ ArithVar minVarOrder(ArithVar x, ArithVar y) const;
+
+ /**
+ * minColLength is a PreferenceFunction for selecting the variable with the
+ * smaller row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minColLength(ArithVar x, ArithVar y) const;
+
+ /**
+ * minRowLength is a PreferenceFunction for selecting the variable with the
+ * smaller row count in the tableau.
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minRowLength(ArithVar x, ArithVar y) const;
+
+ /**
+ * minBoundAndRowCount is a PreferenceFunction for preferring a variable
+ * without an asserted bound over variables with an asserted bound.
+ * If both have bounds or both do not have bounds,
+ * the rule falls back to minRowCount(...).
+ *
+ * This is a heuristic rule and should not be used during the VarOrder
+ * stage of findModel.
+ */
+ ArithVar minBoundAndColLength(ArithVar x, ArithVar y) const;
+
+ template <bool above>
+ inline bool isAcceptableSlack(int sgn, ArithVar nonbasic) const
+ {
+ return (above && sgn < 0 && d_variables.strictlyBelowUpperBound(nonbasic))
+ || (above && sgn > 0 && d_variables.strictlyAboveLowerBound(nonbasic))
+ || (!above && sgn > 0
+ && d_variables.strictlyBelowUpperBound(nonbasic))
+ || (!above && sgn < 0
+ && d_variables.strictlyAboveLowerBound(nonbasic));
+ }
+
+ /**
+ * Given the basic variable x_i,
+ * this function finds the smallest nonbasic variable x_j in the row of x_i
+ * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
+ * This returns ARITHVAR_SENTINEL if none exists.
+ *
+ * More formally one of the following conditions must be satisfied:
+ * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
+ * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
+ * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
+ * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
+ *
+ */
+ template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, VarPreferenceFunction pf) const;
+ ArithVar selectSlackLowerBound(ArithVar x_i, VarPreferenceFunction pf) const {
+ return selectSlack<true>(x_i, pf);
+ }
+ ArithVar selectSlackUpperBound(ArithVar x_i, VarPreferenceFunction pf) const {
+ return selectSlack<false>(x_i, pf);
+ }
+
+ const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const;
+
+ inline bool rowIndexIsTracked(RowIndex ridx) const {
+ return d_btracking.isKey(ridx);
+ }
+ inline bool basicIsTracked(ArithVar v) const {
+ return rowIndexIsTracked(d_tableau.basicToRowIndex(v));
+ }
+ void trackRowIndex(RowIndex ridx);
+ void stopTrackingRowIndex(RowIndex ridx){
+ Assert(rowIndexIsTracked(ridx));
+ d_btracking.remove(ridx);
+ }
+
+ /**
+ * If the pivot described in u were performed,
+ * then the row would qualify as being either at the minimum/maximum
+ * to the non-basics being at their bounds.
+ * The minimum/maximum is determined by the direction the non-basic is changing.
+ */
+ bool basicsAtBounds(const UpdateInfo& u) const;
+
+private:
+
+ /**
+ * Recomputes the bound info for a row using either the information
+ * in the bounds queue or the current information.
+ * O(row length of ridx)
+ */
+ BoundsInfo computeRowBoundInfo(RowIndex ridx, bool inQueue) const;
+
+public:
+ /** Debug only routine. */
+ BoundCounts debugBasicAtBoundCount(ArithVar x_i) const;
+
+ /** Track the effect of the change of coefficient for bound counting. */
+ void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
+
+ /** Track the effect of multiplying a row by a sign for bound counting. */
+ void trackingMultiplyRow(RowIndex ridx, int sgn);
+
+ /** Count for how many on a row have *an* upper/lower bounds. */
+ BoundCounts hasBoundCount(RowIndex ri) const {
+ Assert(d_variables.boundsQueueEmpty());
+ return d_btracking[ri].hasBounds();
+ }
+
+ /**
+ * Are there any non-basics on x_i's row that are not at
+ * their respective lower bounds (mod sgns).
+ * O(1) time due to the atBound() count.
+ */
+ bool nonbasicsAtLowerBounds(ArithVar x_i) const;
+
+ /**
+ * Are there any non-basics on x_i's row that are not at
+ * their respective upper bounds (mod sgns).
+ * O(1) time due to the atBound() count.
+ */
+ bool nonbasicsAtUpperBounds(ArithVar x_i) const;
+
+private:
+ class TrackingCallback : public CoefficientChangeCallback {
+ private:
+ LinearEqualityModule* d_linEq;
+ public:
+ TrackingCallback(LinearEqualityModule* le) : d_linEq(le) {}
+ void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override
+ {
+ d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
+ }
+ void multiplyRow(RowIndex ridx, int sgn) override
+ {
+ d_linEq->trackingMultiplyRow(ridx, sgn);
+ }
+ bool canUseRow(RowIndex ridx) const override
+ {
+ ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
+ return d_linEq->basicIsTracked(basic);
+ }
+ } d_trackCallback;
+
+ /**
+ * Selects the constraint for the variable v on the row for basic
+ * with the weakest possible constraint that is consistent with the surplus
+ * surplus.
+ */
+ 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.
+ *
+ * Returns a constraint that is now in conflict.
+ */
+ ConstraintCP minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& rc) const;
+
+ /**
+ * Given a 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 ConstraintCP generateConflictAboveUpperBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
+ return minimallyWeakConflict(true, conflictVar, rc);
+ }
+
+ inline ConstraintCP generateConflictBelowLowerBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
+ return minimallyWeakConflict(false, conflictVar, rc);
+ }
+
+ /**
+ * Computes the sum of the upper/lower bound of row.
+ * The variable skip is not included in the sum.
+ */
+ DeltaRational computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const;
+
+public:
+ void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult);
+ void directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult);
+
+
+ /**
+ * Checks to make sure the assignment is consistent with the tableau.
+ * This code is for debugging.
+ */
+ void debugCheckTableau();
+
+ void debugCheckTracking();
+
+ /** Debugging information for a pivot. */
+ void debugPivot(ArithVar x_i, ArithVar x_j);
+
+ ArithVar minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const;
+
+ /**
+ * Returns true if there would be a conflict on this row after a pivot
+ * and update using its basic variable and one of the non-basic variables on
+ * the row.
+ */
+ bool willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const;
+ UpdateInfo mkConflictUpdate(const Tableau::Entry& entry, bool ub) const;
+
+ /**
+ * Looks more an update for fcSimplex on the nonbasic variable nb with the focus coefficient.
+ */
+ UpdateInfo speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref);
+
+private:
+
+ /**
+ * Examines the effects of pivoting the entries column variable
+ * with the row's basic variable and setting the variable s.t.
+ * the basic variable is equal to one of its bounds.
+ *
+ * If ub, then the basic variable will be equal its upper bound.
+ * If not ub,then the basic variable will be equal its lower bound.
+ *
+ * Returns iff this row will be in conflict after the pivot.
+ *
+ * If this is false, add the bound to the relevant heap.
+ * If the bound is +/-infinity, this is ignored.
+
+ *
+ * Returns true if this would be a conflict.
+ * If it returns false, this
+ */
+ bool accumulateBorder(const Tableau::Entry& entry, bool ub);
+
+ void handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref);
+ void pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange);
+ void clearSpeculative();
+ Rational updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock);
+
+private:
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statPivots, d_statUpdates;
+ TimerStat d_pivotTime;
+ TimerStat d_adjTime;
+
+ IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
+ TimerStat d_weakenTime;
+ TimerStat d_forceTime;
+
+ Statistics();
+ };
+ mutable Statistics d_statistics;
+
+};/* class LinearEqualityModule */
+
+struct Cand {
+ ArithVar d_nb;
+ uint32_t d_penalty;
+ int d_sgn;
+ const Rational* d_coeff;
+
+ Cand(ArithVar nb, uint32_t penalty, int s, const Rational* c) :
+ d_nb(nb), d_penalty(penalty), d_sgn(s), d_coeff(c){}
+};
+
+
+class CompPenaltyColLength {
+private:
+ LinearEqualityModule* d_mod;
+ const bool d_havePenalties;
+
+ public:
+ CompPenaltyColLength(LinearEqualityModule* mod, bool havePenalties)
+ : d_mod(mod), d_havePenalties(havePenalties)
+ {
+ }
+
+ bool operator()(const Cand& x, const Cand& y) const {
+ if (x.d_penalty == y.d_penalty || !d_havePenalties)
+ {
+ return x.d_nb == d_mod->minBoundAndColLength(x.d_nb,y.d_nb);
+ }
+ else
+ {
+ return x.d_penalty < y.d_penalty;
+ }
+ }
+};
+
+class UpdateTrackingCallback : public BoundUpdateCallback {
+private:
+ LinearEqualityModule* d_mod;
+public:
+ UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
+ void operator()(ArithVar v, const BoundsInfo& bi) override
+ {
+ d_mod->includeBoundUpdate(v, bi);
+ }
+};
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Sparse matrix implementations for different types.
+ */
+
+#include "theory/arith/linear/matrix.h"
+
+using namespace std;
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
+void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){}
+bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ * Sparse matrix implementations for different types.
+ *
+ * Sparse matrix implementations for different types.
+ * This defines Matrix<T>, IntegerEqualityTables and Tableau.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "base/output.h"
+#include "theory/arith/linear/arithvar.h"
+#include "util/dense_map.h"
+#include "util/index.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+typedef Index EntryID;
+const EntryID ENTRYID_SENTINEL = std::numeric_limits<EntryID>::max();
+
+typedef Index RowIndex;
+const RowIndex ROW_INDEX_SENTINEL = std::numeric_limits<RowIndex>::max();
+
+class CoefficientChangeCallback {
+public:
+ virtual ~CoefficientChangeCallback() {}
+ virtual void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) = 0;
+ virtual void multiplyRow(RowIndex ridx, int Sgn) = 0;
+ virtual bool canUseRow(RowIndex ridx) const = 0;
+};
+
+class NoEffectCCCB : public CoefficientChangeCallback {
+public:
+ void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override;
+ void multiplyRow(RowIndex ridx, int Sgn) override;
+ bool canUseRow(RowIndex ridx) const override;
+};
+
+template<class T>
+class MatrixEntry {
+private:
+ RowIndex d_rowIndex;
+ ArithVar d_colVar;
+
+ EntryID d_nextRow;
+ EntryID d_nextCol;
+
+ EntryID d_prevRow;
+ EntryID d_prevCol;
+
+ T d_coefficient;
+
+public:
+ MatrixEntry():
+ d_rowIndex(ROW_INDEX_SENTINEL),
+ d_colVar(ARITHVAR_SENTINEL),
+ d_nextRow(ENTRYID_SENTINEL),
+ d_nextCol(ENTRYID_SENTINEL),
+ d_prevRow(ENTRYID_SENTINEL),
+ d_prevCol(ENTRYID_SENTINEL),
+ d_coefficient()
+ {}
+
+ MatrixEntry(RowIndex row, ArithVar col, const T& coeff):
+ d_rowIndex(row),
+ d_colVar(col),
+ d_nextRow(ENTRYID_SENTINEL),
+ d_nextCol(ENTRYID_SENTINEL),
+ d_prevRow(ENTRYID_SENTINEL),
+ d_prevCol(ENTRYID_SENTINEL),
+ d_coefficient(coeff)
+ {}
+
+private:
+ bool unusedConsistent() const {
+ return
+ (d_rowIndex == ROW_INDEX_SENTINEL && d_colVar == ARITHVAR_SENTINEL) ||
+ (d_rowIndex != ROW_INDEX_SENTINEL && d_colVar != ARITHVAR_SENTINEL);
+ }
+
+public:
+
+ EntryID getNextRowEntryID() const {
+ return d_nextRow;
+ }
+
+ EntryID getNextColEntryID() const {
+ return d_nextCol;
+ }
+ EntryID getPrevRowEntryID() const {
+ return d_prevRow;
+ }
+
+ EntryID getPrevColEntryID() const {
+ return d_prevCol;
+ }
+
+ void setNextRowEntryID(EntryID id) {
+ d_nextRow = id;
+ }
+ void setNextColEntryID(EntryID id) {
+ d_nextCol = id;
+ }
+ void setPrevRowEntryID(EntryID id) {
+ d_prevRow = id;
+ }
+ void setPrevColEntryID(EntryID id) {
+ d_prevCol = id;
+ }
+
+ RowIndex getRowIndex() const{
+ return d_rowIndex;
+ }
+
+ ArithVar getColVar() const{
+ return d_colVar;
+ }
+
+ const T& getCoefficient() const {
+ return d_coefficient;
+ }
+
+ T& getCoefficient(){
+ return d_coefficient;
+ }
+
+ void setCoefficient(const T& t){
+ d_coefficient = t;
+ }
+
+ void markBlank() {
+ d_rowIndex = ROW_INDEX_SENTINEL;
+ d_colVar = ARITHVAR_SENTINEL;
+ }
+
+ bool blank() const{
+ Assert(unusedConsistent());
+
+ return d_rowIndex == ROW_INDEX_SENTINEL;
+ }
+}; /* class MatrixEntry<T> */
+
+template<class T>
+class MatrixEntryVector {
+private:
+ typedef MatrixEntry<T> EntryType;
+ typedef std::vector<EntryType> EntryArray;
+
+ EntryArray d_entries;
+ std::queue<EntryID> d_freedEntries;
+
+ uint32_t d_size;
+
+public:
+ MatrixEntryVector():
+ d_entries(), d_freedEntries(), d_size(0)
+ {}
+
+ const EntryType& operator[](EntryID id) const{
+ Assert(inBounds(id));
+ return d_entries[id];
+ }
+
+ EntryType& get(EntryID id){
+ Assert(inBounds(id));
+ return d_entries[id];
+ }
+
+ void freeEntry(EntryID id){
+ Assert(get(id).blank());
+ Assert(d_size > 0);
+
+ d_freedEntries.push(id);
+ --d_size;
+ }
+
+ EntryID newEntry(){
+ EntryID newId;
+ if(d_freedEntries.empty()){
+ newId = d_entries.size();
+ d_entries.push_back(MatrixEntry<T>());
+ }else{
+ newId = d_freedEntries.front();
+ d_freedEntries.pop();
+ }
+ ++d_size;
+ return newId;
+ }
+
+ uint32_t size() const{ return d_size; }
+ uint32_t capacity() const{ return d_entries.capacity(); }
+
+
+private:
+ bool inBounds(EntryID id) const{
+ return id < d_entries.size();
+ }
+}; /* class MatrixEntryVector<T> */
+
+template <class T, bool isRow>
+class MatrixVector {
+private:
+ EntryID d_head;
+ uint32_t d_size;
+
+ MatrixEntryVector<T>* d_entries;
+
+ class Iterator {
+ private:
+ EntryID d_curr;
+ const MatrixEntryVector<T>* d_entries;
+
+ public:
+ Iterator(EntryID start, const MatrixEntryVector<T>* entries) :
+ d_curr(start), d_entries(entries)
+ {}
+
+ public:
+
+ EntryID getID() const {
+ return d_curr;
+ }
+
+ const MatrixEntry<T>& operator*() const{
+ Assert(!atEnd());
+ return (*d_entries)[d_curr];
+ }
+
+ Iterator& operator++(){
+ Assert(!atEnd());
+ const MatrixEntry<T>& entry = (*d_entries)[d_curr];
+ d_curr = isRow ? entry.getNextRowEntryID() : entry.getNextColEntryID();
+ return *this;
+ }
+
+ bool atEnd() const {
+ return d_curr == ENTRYID_SENTINEL;
+ }
+
+ bool operator==(const Iterator& i) const{
+ return d_curr == i.d_curr && d_entries == i.d_entries;
+ }
+
+ bool operator!=(const Iterator& i) const{
+ return !(d_curr == i.d_curr && d_entries == i.d_entries);
+ }
+ }; /* class MatrixVector<T, isRow>::Iterator */
+
+public:
+ MatrixVector(MatrixEntryVector<T>* mev)
+ : d_head(ENTRYID_SENTINEL), d_size(0), d_entries(mev)
+ {}
+
+ MatrixVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+ : d_head(head), d_size(size), d_entries(mev)
+ {}
+
+ typedef Iterator const_iterator;
+ const_iterator begin() const {
+ return Iterator(d_head, d_entries);
+ }
+ const_iterator end() const {
+ return Iterator(ENTRYID_SENTINEL, d_entries);
+ }
+
+ EntryID getHead() const { return d_head; }
+
+ uint32_t getSize() const { return d_size; }
+
+ void insert(EntryID newId){
+ if(isRow){
+ d_entries->get(newId).setNextRowEntryID(d_head);
+
+ if(d_head != ENTRYID_SENTINEL){
+ d_entries->get(d_head).setPrevRowEntryID(newId);
+ }
+ }else{
+ d_entries->get(newId).setNextColEntryID(d_head);
+
+ if(d_head != ENTRYID_SENTINEL){
+ d_entries->get(d_head).setPrevColEntryID(newId);
+ }
+ }
+
+ d_head = newId;
+ ++d_size;
+ }
+ void remove(EntryID id){
+ Assert(d_size > 0);
+ --d_size;
+ if(isRow){
+ EntryID prevRow = d_entries->get(id).getPrevRowEntryID();
+ EntryID nextRow = d_entries->get(id).getNextRowEntryID();
+
+ if(d_head == id){
+ d_head = nextRow;
+ }
+ if(prevRow != ENTRYID_SENTINEL){
+ d_entries->get(prevRow).setNextRowEntryID(nextRow);
+ }
+ if(nextRow != ENTRYID_SENTINEL){
+ d_entries->get(nextRow).setPrevRowEntryID(prevRow);
+ }
+ }else{
+ EntryID prevCol = d_entries->get(id).getPrevColEntryID();
+ EntryID nextCol = d_entries->get(id).getNextColEntryID();
+
+ if(d_head == id){
+ d_head = nextCol;
+ }
+
+ if(prevCol != ENTRYID_SENTINEL){
+ d_entries->get(prevCol).setNextColEntryID(nextCol);
+ }
+ if(nextCol != ENTRYID_SENTINEL){
+ d_entries->get(nextCol).setPrevColEntryID(prevCol);
+ }
+ }
+ }
+}; /* class MatrixVector<T, isRow> */
+
+template <class T>
+ class RowVector : public MatrixVector<T, true>
+{
+private:
+ typedef MatrixVector<T, true> SuperT;
+public:
+ typedef typename SuperT::const_iterator const_iterator;
+
+ RowVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+ RowVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+ : SuperT(head, size, mev){}
+};/* class RowVector<T> */
+
+template <class T>
+ class ColumnVector : public MatrixVector<T, false>
+{
+private:
+ typedef MatrixVector<T, false> SuperT;
+public:
+ typedef typename SuperT::const_iterator const_iterator;
+
+ ColumnVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
+ ColumnVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
+ : SuperT(head, size, mev){}
+};/* class ColumnVector<T> */
+
+template <class T>
+class Matrix {
+public:
+ typedef MatrixEntry<T> Entry;
+
+protected:
+ typedef RowVector<T> RowVectorT;
+ typedef 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;
+
+ // ColumnTable : ArithVar |-> ColumnVector
+ typedef std::vector< ColumnVectorT > ColumnTable;
+ ColumnTable d_columns;
+
+ /* The merge buffer is used to store a row in order to optimize row addition. */
+ typedef std::pair<EntryID, bool> PosUsedPair;
+ typedef DenseMap< PosUsedPair > RowToPosUsedPairMap;
+ RowToPosUsedPairMap d_mergeBuffer;
+
+ /* The row that is in the merge buffer. */
+ RowIndex d_rowInMergeBuffer;
+
+ uint32_t d_entriesInUse;
+ MatrixEntryVector<T> d_entries;
+
+ std::vector<RowIndex> d_pool;
+
+ T d_zero;
+
+public:
+ /**
+ * Constructs an empty Matrix.
+ */
+ Matrix()
+ : d_rows(),
+ d_columns(),
+ d_mergeBuffer(),
+ d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
+ d_entriesInUse(0),
+ d_entries(),
+ d_zero(0)
+ {}
+
+ Matrix(const T& zero)
+ : d_rows(),
+ d_columns(),
+ d_mergeBuffer(),
+ d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
+ d_entriesInUse(0),
+ d_entries(),
+ d_zero(zero)
+ {}
+
+ Matrix(const Matrix& m)
+ : d_rows(),
+ d_columns(),
+ d_mergeBuffer(m.d_mergeBuffer),
+ d_rowInMergeBuffer(m.d_rowInMergeBuffer),
+ d_entriesInUse(m.d_entriesInUse),
+ d_entries(m.d_entries),
+ d_zero(m.d_zero)
+ {
+ d_columns.clear();
+ for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+ const ColumnVectorT& col = *c;
+ d_columns.push_back(ColumnVectorT(col.getHead(),col.getSize(),&d_entries));
+ }
+ d_rows.clear();
+ for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+ const RowVectorT& row = *r;
+ d_rows.push_back(RowVectorT(row.getHead(),row.getSize(),&d_entries));
+ }
+ }
+
+ Matrix& operator=(const Matrix& m){
+ d_mergeBuffer = (m.d_mergeBuffer);
+ d_rowInMergeBuffer = (m.d_rowInMergeBuffer);
+ d_entriesInUse = (m.d_entriesInUse);
+ d_entries = (m.d_entries);
+ d_zero = (m.d_zero);
+ d_columns.clear();
+ for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
+ const ColumnVector<T>& col = *c;
+ d_columns.push_back(ColumnVector<T>(col.getHead(), col.getSize(), &d_entries));
+ }
+ d_rows.clear();
+ for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
+ const RowVector<T>& row = *r;
+ d_rows.push_back(RowVector<T>(row.getHead(), row.getSize(), &d_entries));
+ }
+ return *this;
+ }
+
+protected:
+
+ void addEntry(RowIndex row, ArithVar col, const T& coeff){
+ Trace("tableau") << "addEntry(" << row << "," << col <<"," << coeff << ")" << std::endl;
+
+ Assert(coeff != 0);
+ Assert(row < d_rows.size());
+ Assert(col < d_columns.size());
+
+ EntryID newId = d_entries.newEntry();
+ Entry& newEntry = d_entries.get(newId);
+ newEntry = Entry(row, col, coeff);
+
+ Assert(newEntry.getCoefficient() != 0);
+
+ ++d_entriesInUse;
+
+ d_rows[row].insert(newId);
+ d_columns[col].insert(newId);
+ }
+
+ void removeEntry(EntryID id){
+ Assert(d_entriesInUse > 0);
+ --d_entriesInUse;
+
+ Entry& entry = d_entries.get(id);
+
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar col = entry.getColVar();
+
+ Assert(d_rows[ridx].getSize() > 0);
+ Assert(d_columns[col].getSize() > 0);
+
+ d_rows[ridx].remove(id);
+ d_columns[col].remove(id);
+
+ entry.markBlank();
+
+ d_entries.freeEntry(id);
+ }
+
+ private:
+ RowIndex requestRowIndex(){
+ if(d_pool.empty()){
+ RowIndex ridx = d_rows.size();
+ d_rows.push_back(RowVectorT(&d_entries));
+ return ridx;
+ }else{
+ RowIndex rid = d_pool.back();
+ d_pool.pop_back();
+ return rid;
+ }
+ }
+
+ void releaseRowIndex(RowIndex rid){
+ d_pool.push_back(rid);
+ }
+
+public:
+
+ size_t getNumRows() const {
+ return d_rows.size();
+ }
+
+ size_t getNumColumns() const {
+ return d_columns.size();
+ }
+
+ void increaseSize(){
+ 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];
+ }
+
+ const ColumnVector<T>& getColumn(ArithVar v) const {
+ Assert(v < d_columns.size());
+ return d_columns[v];
+ }
+
+ uint32_t getRowLength(RowIndex r) const{
+ return getRow(r).getSize();
+ }
+
+ uint32_t getColLength(ArithVar x) const{
+ return getColumn(x).getSize();
+ }
+
+ /**
+ * Adds a row to the matrix.
+ * The new row is equivalent to:
+ * \f$\sum_i\f$ coeffs[i] * variables[i]
+ */
+ RowIndex addRow(const std::vector<T>& coeffs,
+ const std::vector<ArithVar>& variables){
+
+ RowIndex ridx = requestRowIndex();
+
+ //RowIndex ridx = d_rows.size();
+ //d_rows.push_back(RowVectorT(&d_entries));
+
+ typename std::vector<T>::const_iterator coeffIter = coeffs.begin();
+ std::vector<ArithVar>::const_iterator varsIter = variables.begin();
+ std::vector<ArithVar>::const_iterator varsEnd = variables.end();
+
+ for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
+ const T& coeff = *coeffIter;
+ ArithVar var_i = *varsIter;
+ Assert(var_i < getNumColumns());
+ addEntry(ridx, var_i, coeff);
+ }
+
+ return ridx;
+ }
+
+
+ void loadRowIntoBuffer(RowIndex rid){
+ Assert(d_mergeBuffer.empty());
+ Assert(d_rowInMergeBuffer == ROW_INDEX_SENTINEL);
+
+ RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
+ for(; i != i_end; ++i){
+ EntryID id = i.getID();
+ const MatrixEntry<T>& entry = *i;
+ ArithVar colVar = entry.getColVar();
+ d_mergeBuffer.set(colVar, std::make_pair(id, false));
+ }
+
+ d_rowInMergeBuffer = rid;
+ }
+
+ void clearBuffer() {
+ Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+
+ d_rowInMergeBuffer = ROW_INDEX_SENTINEL;
+ d_mergeBuffer.purge();
+ }
+
+ /* 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);
+
+ Trace("tableau") << "rowPlusRowTimesConstant("
+ << to << "," << mult << "," << d_rowInMergeBuffer << ")"
+ << std::endl;
+
+ Assert(debugNoZeroCoefficients(to));
+ Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
+
+ Assert(mult != 0);
+
+ RowIterator i = getRow(to).begin();
+ RowIterator i_end = getRow(to).end();
+ while(i != i_end){
+ EntryID id = i.getID();
+ Entry& entry = d_entries.get(id);
+ ArithVar colVar = entry.getColVar();
+
+ ++i;
+
+ if(d_mergeBuffer.isKey(colVar)){
+ EntryID bufferEntry = d_mergeBuffer[colVar].first;
+ Assert(!d_mergeBuffer[colVar].second);
+ d_mergeBuffer.get(colVar).second = true;
+
+ const Entry& other = d_entries.get(bufferEntry);
+ T& coeff = entry.getCoefficient();
+ coeff += mult * other.getCoefficient();
+
+ if(coeff.sgn() == 0){
+ removeEntry(id);
+ }
+ }
+ }
+
+ i = getRow(d_rowInMergeBuffer).begin();
+ i_end = getRow(d_rowInMergeBuffer).end();
+
+ for(; i != i_end; ++i){
+ const Entry& entry = *i;
+ ArithVar colVar = entry.getColVar();
+
+ if(d_mergeBuffer[colVar].second){
+ d_mergeBuffer.get(colVar).second = false;
+ }else{
+ Assert(!(d_mergeBuffer[colVar]).second);
+ T newCoeff = mult * entry.getCoefficient();
+ addEntry(to, colVar, newCoeff);
+ }
+ }
+
+ Assert(mergeBufferIsClear());
+
+ if(TraceIsOn("matrix")) { printMatrix(); }
+ }
+
+ /** to += mult * buffer. */
+ void rowPlusBufferTimesConstant(RowIndex to, const T& mult, CoefficientChangeCallback& cb){
+ Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
+ Assert(to != ROW_INDEX_SENTINEL);
+
+ Trace("tableau") << "rowPlusRowTimesConstant("
+ << to << "," << mult << "," << d_rowInMergeBuffer << ")"
+ << std::endl;
+
+ Assert(debugNoZeroCoefficients(to));
+ Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
+
+ Assert(mult != 0);
+
+ RowIterator i = getRow(to).begin();
+ RowIterator i_end = getRow(to).end();
+ while(i != i_end){
+ EntryID id = i.getID();
+ Entry& entry = d_entries.get(id);
+ ArithVar colVar = entry.getColVar();
+
+ ++i;
+
+ if(d_mergeBuffer.isKey(colVar)){
+ EntryID bufferEntry = d_mergeBuffer[colVar].first;
+ Assert(!d_mergeBuffer[colVar].second);
+ d_mergeBuffer.get(colVar).second = true;
+
+ const Entry& other = d_entries.get(bufferEntry);
+ T& coeff = entry.getCoefficient();
+ int coeffOldSgn = coeff.sgn();
+ coeff += mult * other.getCoefficient();
+ int coeffNewSgn = coeff.sgn();
+
+ if(coeffOldSgn != coeffNewSgn){
+ cb.update(to, colVar, coeffOldSgn, coeffNewSgn);
+
+ if(coeffNewSgn == 0){
+ removeEntry(id);
+ }
+ }
+ }
+ }
+
+ i = getRow(d_rowInMergeBuffer).begin();
+ i_end = getRow(d_rowInMergeBuffer).end();
+
+ for(; i != i_end; ++i){
+ const Entry& entry = *i;
+ ArithVar colVar = entry.getColVar();
+
+ if(d_mergeBuffer[colVar].second){
+ d_mergeBuffer.get(colVar).second = false;
+ }else{
+ Assert(!(d_mergeBuffer[colVar]).second);
+ T newCoeff = mult * entry.getCoefficient();
+ addEntry(to, colVar, newCoeff);
+
+ cb.update(to, colVar, 0, newCoeff.sgn());
+ }
+ }
+
+ Assert(mergeBufferIsClear());
+
+ if(TraceIsOn("matrix")) { printMatrix(); }
+ }
+
+ bool mergeBufferIsClear() const{
+ RowToPosUsedPairMap::const_iterator i = d_mergeBuffer.begin();
+ RowToPosUsedPairMap::const_iterator i_end = d_mergeBuffer.end();
+ for(; i != i_end; ++i){
+ RowIndex rid = *i;
+ if(d_mergeBuffer[rid].second){
+ return false;
+ }
+ }
+ return true;
+ }
+
+protected:
+
+ EntryID findOnRow(RowIndex rid, ArithVar column) const {
+ RowIterator i = d_rows[rid].begin(), i_end = d_rows[rid].end();
+ for(; i != i_end; ++i){
+ EntryID id = i.getID();
+ const MatrixEntry<T>& entry = *i;
+ ArithVar colVar = entry.getColVar();
+
+ if(colVar == column){
+ return id;
+ }
+ }
+ return ENTRYID_SENTINEL;
+ }
+
+ EntryID findOnCol(RowIndex rid, ArithVar column) const{
+ ColIterator i = d_columns[column].begin(), i_end = d_columns[column].end();
+ for(; i != i_end; ++i){
+ EntryID id = i.getID();
+ const MatrixEntry<T>& entry = *i;
+ RowIndex currRow = entry.getRowIndex();
+
+ if(currRow == rid){
+ return id;
+ }
+ }
+ return ENTRYID_SENTINEL;
+ }
+
+ EntryID findEntryID(RowIndex rid, ArithVar col) const{
+ bool colIsShorter = getColLength(col) < getRowLength(rid);
+ EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
+ return id;
+ }
+ MatrixEntry<T> d_failedFind;
+public:
+
+ /** If the find fails, isUnused is true on the entry. */
+ const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col) const{
+ EntryID id = findEntryID(rid, col);
+ if(id == ENTRYID_SENTINEL){
+ return d_failedFind;
+ }else{
+ return d_entries[id];
+ }
+ }
+
+ /**
+ * Prints the contents of the Matrix to Trace("matrix")
+ */
+ void printMatrix(std::ostream& out) const {
+ out << "Matrix::printMatrix" << std::endl;
+
+ for(RowIndex i = 0, N = d_rows.size(); i < N; ++i){
+ printRow(i, out);
+ }
+ }
+ void printMatrix() const {
+ printMatrix(Trace("matrix"));
+ }
+
+ void printRow(RowIndex rid, std::ostream& out) const {
+ out << "{" << rid << ":";
+ const RowVector<T>& row = getRow(rid);
+ RowIterator i = row.begin();
+ RowIterator i_end = row.end();
+ for(; i != i_end; ++i){
+ printEntry(*i, out);
+ out << ",";
+ }
+ out << "}" << std::endl;
+ }
+ void printRow(RowIndex rid) const {
+ printRow(rid, Trace("matrix"));
+ }
+
+ void printEntry(const MatrixEntry<T>& entry, std::ostream& out) const {
+ out << entry.getColVar() << "*" << entry.getCoefficient();
+ }
+ void printEntry(const MatrixEntry<T>& entry) const {
+ printEntry(entry, Trace("matrix"));
+ }
+public:
+ uint32_t size() const {
+ return d_entriesInUse;
+ }
+ uint32_t getNumEntriesInTableau() const {
+ return d_entries.size();
+ }
+ uint32_t getEntryCapacity() const {
+ return d_entries.capacity();
+ }
+
+ void manipulateRowEntry(RowIndex row, ArithVar col, const T& c, CoefficientChangeCallback& cb){
+ int coeffOldSgn;
+ int coeffNewSgn;
+
+ EntryID id = findEntryID(row, col);
+ if(id == ENTRYID_SENTINEL){
+ coeffOldSgn = 0;
+ addEntry(row, col, c);
+ coeffNewSgn = c.sgn();
+ }else{
+ Entry& e = d_entries.get(id);
+ T& t = e.getCoefficient();
+ coeffOldSgn = t.sgn();
+ t += c;
+ coeffNewSgn = t.sgn();
+ }
+
+ if(coeffOldSgn != coeffNewSgn){
+ cb.update(row, col, coeffOldSgn, coeffNewSgn);
+ }
+ if(coeffNewSgn == 0){
+ removeEntry(id);
+ }
+ }
+
+ void removeRow(RowIndex rid){
+ RowIterator i = getRow(rid).begin();
+ RowIterator i_end = getRow(rid).end();
+ for(; i != i_end; ++i){
+ EntryID id = i.getID();
+ removeEntry(id);
+ }
+ releaseRowIndex(rid);
+ }
+
+ double densityMeasure() const{
+ Assert(numNonZeroEntriesByRow() == numNonZeroEntries());
+ Assert(numNonZeroEntriesByCol() == numNonZeroEntries());
+
+ uint32_t n = getNumRows();
+ if(n == 0){
+ return 1.0;
+ }else {
+ uint32_t s = numNonZeroEntries();
+ uint32_t m = d_columns.size();
+ uint32_t divisor = (n *(m - n + 1));
+
+ Assert(n >= 1);
+ Assert(m >= n);
+ Assert(divisor > 0);
+ Assert(divisor >= s);
+
+ return (double(s)) / divisor;
+ }
+ }
+
+ void loadSignQueries(RowIndex rid, DenseMap<int>& target) const{
+
+ RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
+ for(; i != i_end; ++i){
+ const MatrixEntry<T>& entry = *i;
+ target.set(entry.getColVar(), entry.getCoefficient().sgn());
+ }
+ }
+
+protected:
+ uint32_t numNonZeroEntries() const { return size(); }
+
+ uint32_t numNonZeroEntriesByRow() const {
+ uint32_t rowSum = 0;
+ for(RowIndex rid = 0, N = d_rows.size(); rid < N; ++rid){
+ rowSum += getRowLength(rid);
+ }
+ return rowSum;
+ }
+
+ uint32_t numNonZeroEntriesByCol() const {
+ uint32_t colSum = 0;
+ for(ArithVar v = 0, N = d_columns.size(); v < N; ++v){
+ colSum += getColLength(v);
+ }
+ return colSum;
+ }
+
+
+ bool debugNoZeroCoefficients(RowIndex ridx){
+ for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+ const Entry& entry = *i;
+ if(entry.getCoefficient() == 0){
+ return false;
+ }
+ }
+ return true;
+ }
+ bool debugMatchingCountsForRow(RowIndex ridx){
+ for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+ const Entry& entry = *i;
+ ArithVar colVar = entry.getColVar();
+ uint32_t count = debugCountColLength(colVar);
+ Trace("tableau") << "debugMatchingCountsForRow "
+ << ridx << ":" << colVar << " " << count
+ <<" "<< getColLength(colVar) << std::endl;
+ if( count != getColLength(colVar) ){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ uint32_t debugCountColLength(ArithVar var){
+ Trace("tableau") << var << " ";
+ uint32_t count = 0;
+ for(ColIterator i=getColumn(var).begin(); !i.atEnd(); ++i){
+ const Entry& entry = *i;
+ Trace("tableau") << "(" << entry.getRowIndex() << ", " << i.getID() << ") ";
+ ++count;
+ }
+ Trace("tableau") << std::endl;
+ return count;
+ }
+ uint32_t debugCountRowLength(RowIndex ridx){
+ uint32_t count = 0;
+ for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
+ ++count;
+ }
+ return count;
+ }
+
+};/* class Matrix<T> */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+#include "theory/arith/linear/normal_form.h"
+
+#include <list>
+
+#include "base/output.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/theory.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+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));
+}
+
+VarList::VarList(Node n) : NodeWrapper(n) { Assert(isSorted(begin(), end())); }
+
+bool Variable::isIAndMember(Node n)
+{
+ return n.getKind() == kind::IAND && Polynomial::isMember(n[0])
+ && Polynomial::isMember(n[1]);
+}
+
+bool Variable::isPow2Member(Node n)
+{
+ return n.getKind() == kind::POW2 && Polynomial::isMember(n[0]);
+}
+
+bool Variable::isDivMember(Node n){
+ switch(n.getKind()){
+ case kind::DIVISION:
+ case kind::INTS_DIVISION:
+ case kind::INTS_MODULUS:
+ case kind::DIVISION_TOTAL:
+ case kind::INTS_DIVISION_TOTAL:
+ case kind::INTS_MODULUS_TOTAL:
+ return Polynomial::isMember(n[0]) && Polynomial::isMember(n[1]);
+ default:
+ return false;
+ }
+}
+
+bool Variable::isTranscendentalMember(Node n) {
+ switch(n.getKind()){
+ case kind::EXPONENTIAL:
+ case kind::SINE:
+ case kind::COSINE:
+ case kind::TANGENT:
+ case kind::COSECANT:
+ case kind::SECANT:
+ case kind::COTANGENT:
+ case kind::ARCSINE:
+ case kind::ARCCOSINE:
+ case kind::ARCTANGENT:
+ case kind::ARCCOSECANT:
+ case kind::ARCSECANT:
+ case kind::ARCCOTANGENT:
+ case kind::SQRT: return Polynomial::isMember(n[0]);
+ case kind::PI:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+bool VarList::isSorted(iterator start, iterator end) {
+ return std::is_sorted(start, end);
+}
+
+bool VarList::isMember(Node n) {
+ if(Variable::isMember(n)) {
+ return true;
+ }
+ if(n.getKind() == kind::NONLINEAR_MULT) {
+ Node::iterator curr = n.begin(), end = n.end();
+ Node prev = *curr;
+ if(!Variable::isMember(prev)) return false;
+
+ Variable::VariableNodeCmp cmp;
+
+ while( (++curr) != end) {
+ if(!Variable::isMember(*curr)) return false;
+ // prev <= curr : accept
+ // !(prev <= curr) : reject
+ // !(!(prev > curr)) : reject
+ // curr < prev : reject
+ if((cmp(*curr, prev))) return false;
+ prev = *curr;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int VarList::cmp(const VarList& vl) const {
+ int dif = this->size() - vl.size();
+ if (dif == 0) {
+ if(this->getNode() == vl.getNode()) {
+ return 0;
+ }
+
+ Assert(!empty());
+ Assert(!vl.empty());
+ if(this->size() == 1){
+ return Variable::VariableNodeCmp::cmp(this->getNode(), vl.getNode());
+ }
+
+
+ internal_iterator ii=this->internalBegin(), ie=this->internalEnd();
+ internal_iterator ci=vl.internalBegin(), ce=vl.internalEnd();
+ for(; ii != ie; ++ii, ++ci){
+ Node vi = *ii;
+ Node vc = *ci;
+ int tmp = Variable::VariableNodeCmp::cmp(vi, vc);
+ if(tmp != 0){
+ return tmp;
+ }
+ }
+ Unreachable();
+ } else if(dif < 0) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+VarList VarList::parseVarList(Node n) {
+ return VarList(n);
+ // if(Variable::isMember(n)) {
+ // return VarList(Variable(n));
+ // } else {
+ // Assert(n.getKind() == kind::MULT);
+ // for(Node::iterator i=n.begin(), end = n.end(); i!=end; ++i) {
+ // Assert(Variable::isMember(*i));
+ // }
+ // return VarList(n);
+ // }
+}
+
+VarList VarList::operator*(const VarList& other) const {
+ if(this->empty()) {
+ return other;
+ } else if(other.empty()) {
+ return *this;
+ } else {
+ vector<Node> result;
+
+ internal_iterator
+ thisBegin = this->internalBegin(),
+ thisEnd = this->internalEnd(),
+ otherBegin = other.internalBegin(),
+ otherEnd = other.internalEnd();
+
+ Variable::VariableNodeCmp cmp;
+ std::merge(thisBegin, thisEnd, otherBegin, otherEnd, std::back_inserter(result), cmp);
+
+ Assert(result.size() >= 2);
+ Node mult = NodeManager::currentNM()->mkNode(kind::NONLINEAR_MULT, result);
+ return VarList::parseVarList(mult);
+ }
+}
+
+bool Monomial::isMember(TNode n){
+ if(n.getKind() == kind::CONST_RATIONAL) {
+ return true;
+ } else if(multStructured(n)) {
+ return VarList::isMember(n[1]);
+ } else {
+ return VarList::isMember(n);
+ }
+}
+
+Monomial Monomial::mkMonomial(const Constant& c, const VarList& vl) {
+ if(c.isZero() || vl.empty() ) {
+ return Monomial(c);
+ } else if(c.isOne()) {
+ return Monomial(vl);
+ } else {
+ return Monomial(c, vl);
+ }
+}
+
+Monomial Monomial::mkMonomial(const VarList& vl) {
+ // acts like Monomial::mkMonomial( 1, vl)
+ if( vl.empty() ) {
+ return Monomial::mkOne();
+ } else if(true){
+ return Monomial(vl);
+ }
+}
+
+Monomial Monomial::parseMonomial(Node n) {
+ if(n.getKind() == kind::CONST_RATIONAL) {
+ return Monomial(Constant(n));
+ } else if(multStructured(n)) {
+ return Monomial::mkMonomial(Constant(n[0]),VarList::parseVarList(n[1]));
+ } else {
+ return Monomial(VarList::parseVarList(n));
+ }
+}
+Monomial Monomial::operator*(const Rational& q) const {
+ if(q.isZero()){
+ return mkZero();
+ }else{
+ Constant newConstant = this->getConstant() * q;
+ return Monomial::mkMonomial(newConstant, getVarList());
+ }
+}
+
+Monomial Monomial::operator*(const Constant& c) const {
+ return (*this) * c.getValue();
+ // if(c.isZero()){
+ // return mkZero();
+ // }else{
+ // Constant newConstant = this->getConstant() * c;
+ // return Monomial::mkMonomial(newConstant, getVarList());
+ // }
+}
+
+Monomial Monomial::operator*(const Monomial& mono) const {
+ Constant newConstant = this->getConstant() * mono.getConstant();
+ VarList newVL = this->getVarList() * mono.getVarList();
+
+ return Monomial::mkMonomial(newConstant, newVL);
+}
+
+// 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));
+ 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; }
+ }
+ // 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;
+ }
+ if(writePos > 0 ){
+ Monomial cp = monos[0];
+ Assert(writePos <= N);
+ monos.resize(writePos, cp);
+ }else{
+ monos.clear();
+ }
+ Assert(isStrictlySorted(monos));
+}
+
+void Monomial::print() const {
+ Trace("normal-form") << getNode() << std::endl;
+}
+
+void Monomial::printList(const std::vector<Monomial>& list) {
+ for(vector<Monomial>::const_iterator i = list.begin(), end = list.end(); i != end; ++i) {
+ const Monomial& m =*i;
+ m.print();
+ }
+}
+Polynomial Polynomial::operator+(const Polynomial& vl) const {
+
+ std::vector<Monomial> sortedMonos;
+ std::merge(begin(), end(), vl.begin(), vl.end(), std::back_inserter(sortedMonos));
+
+ Monomial::combineAdjacentMonomials(sortedMonos);
+ //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos);
+
+ Polynomial result = mkPolynomial(sortedMonos);
+ return result;
+}
+
+Polynomial Polynomial::exactDivide(const Integer& z) const {
+ Assert(isIntegral());
+ if(z.isOne()){
+ return (*this);
+ }else {
+ Constant invz = Constant::mkConstant(Rational(1,z));
+ Polynomial prod = (*this) * Monomial::mkMonomial(invz);
+ Assert(prod.isIntegral());
+ return prod;
+ }
+}
+
+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);
+ monos.push_back(Monomial::mkMonomial(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));
+
+ return *this + (vl*negOne);
+}
+
+Polynomial Polynomial::operator*(const Rational& q) const{
+ if(q.isZero()){
+ return Polynomial::mkZero();
+ }else if(q.isOne()){
+ return *this;
+ }else{
+ std::vector<Monomial> newMonos;
+ for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+ newMonos.push_back((*i)*q);
+ }
+
+ Assert(Monomial::isStrictlySorted(newMonos));
+ return Polynomial::mkPolynomial(newMonos);
+ }
+}
+
+Polynomial Polynomial::operator*(const Constant& c) const{
+ return (*this) * c.getValue();
+ // if(c.isZero()){
+ // return Polynomial::mkZero();
+ // }else if(c.isOne()){
+ // return *this;
+ // }else{
+ // std::vector<Monomial> newMonos;
+ // for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+ // newMonos.push_back((*i)*c);
+ // }
+
+ // Assert(Monomial::isStrictlySorted(newMonos));
+ // return Polynomial::mkPolynomial(newMonos);
+ // }
+}
+
+Polynomial Polynomial::operator*(const Monomial& mono) const {
+ if(mono.isZero()) {
+ return Polynomial(mono); //Don't multiply by zero
+ } else {
+ std::vector<Monomial> newMonos;
+ for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+ newMonos.push_back(mono * (*i));
+ }
+
+ // We may need to sort newMonos.
+ // 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.
+ Monomial::sort(newMonos);
+ return Polynomial::mkPolynomial(newMonos);
+ }
+}
+
+Polynomial Polynomial::operator*(const Polynomial& poly) const {
+ Polynomial res = Polynomial::mkZero();
+ for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
+ Monomial curr = *i;
+ Polynomial prod = poly * curr;
+ Polynomial sum = res + prod;
+ res = sum;
+ }
+ return res;
+}
+
+Monomial Polynomial::selectAbsMinimum() const {
+ iterator iter = begin(), myend = end();
+ Assert(iter != myend);
+
+ Monomial min = *iter;
+ ++iter;
+ for(; iter != end(); ++iter){
+ Monomial curr = *iter;
+ if(curr.absCmp(min) < 0){
+ min = curr;
+ }
+ }
+ return min;
+}
+
+bool Polynomial::leadingCoefficientIsAbsOne() const {
+ return getHead().absCoefficientIsOne();
+}
+bool Polynomial::leadingCoefficientIsPositive() const {
+ return getHead().getConstant().isPositive();
+}
+
+bool Polynomial::denominatorLCMIsOne() const {
+ return denominatorLCM().isOne();
+}
+
+bool Polynomial::numeratorGCDIsOne() const {
+ return gcd().isOne();
+}
+
+Integer Polynomial::gcd() const {
+ Assert(isIntegral());
+ return numeratorGCD();
+}
+
+Integer Polynomial::numeratorGCD() const {
+ //We'll use the standardization that gcd(0, 0) = 0
+ //So that the gcd of the zero polynomial is gcd{0} = 0
+ iterator i=begin(), e=end();
+ 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;
+}
+
+Integer Polynomial::denominatorLCM() const {
+ Integer tmp(1);
+ for (iterator i = begin(), e = end(); i != e; ++i) {
+ const Integer denominator = (*i).getConstant().getValue().getDenominator();
+ tmp = tmp.lcm(denominator);
+ }
+ return tmp;
+}
+
+Constant Polynomial::getCoefficient(const VarList& vl) const{
+ //TODO improve to binary search...
+ for(iterator iter=begin(), myend=end(); iter != myend; ++iter){
+ Monomial m = *iter;
+ VarList curr = m.getVarList();
+ if(curr == vl){
+ return m.getConstant();
+ }
+ }
+ return Constant::mkConstant(0);
+}
+
+Node Polynomial::computeQR(const Polynomial& p, const Integer& div){
+ Assert(p.isIntegral());
+ std::vector<Monomial> q_vec, r_vec;
+ Integer tmp_q, tmp_r;
+ for(iterator iter = p.begin(), pend = p.end(); iter != pend; ++iter){
+ Monomial curr = *iter;
+ VarList vl = curr.getVarList();
+ Constant c = curr.getConstant();
+
+ const Integer& a = c.getValue().getNumerator();
+ Integer::floorQR(tmp_q, tmp_r, a, div);
+ Constant q=Constant::mkConstant(tmp_q);
+ Constant r=Constant::mkConstant(tmp_r);
+ if(!q.isZero()){
+ q_vec.push_back(Monomial::mkMonomial(q, vl));
+ }
+ if(!r.isZero()){
+ r_vec.push_back(Monomial::mkMonomial(r, vl));
+ }
+ }
+
+ Polynomial p_q = Polynomial::mkPolynomial(q_vec);
+ Polynomial p_r = Polynomial::mkPolynomial(r_vec);
+
+ return NodeManager::currentNM()->mkNode(
+ kind::ADD, p_q.getNode(), p_r.getNode());
+}
+
+
+Monomial Polynomial::minimumVariableMonomial() const{
+ Assert(!isConstant());
+ if(singleton()){
+ return getHead();
+ }else{
+ iterator i = begin();
+ Monomial first = *i;
+ if( first.isConstant() ){
+ ++i;
+ Assert(i != end());
+ return *i;
+ }else{
+ return first;
+ }
+ }
+}
+
+bool Polynomial::variableMonomialAreStrictlyGreater(const Monomial& m) const{
+ if(isConstant()){
+ return true;
+ }else{
+ Monomial minimum = minimumVariableMonomial();
+ Trace("nf::tmp") << "minimum " << minimum.getNode() << endl;
+ Trace("nf::tmp") << "m " << m.getNode() << endl;
+ return m < minimum;
+ }
+}
+
+bool Polynomial::isMember(TNode n) {
+ if(Monomial::isMember(n)){
+ return true;
+ }
+ else if (n.getKind() == kind::ADD)
+ {
+ Assert(n.getNumChildren() >= 2);
+ Node::iterator currIter = n.begin(), end = n.end();
+ Node prev = *currIter;
+ if(!Monomial::isMember(prev)){
+ return false;
+ }
+
+ Monomial mprev = Monomial::parseMonomial(prev);
+ ++currIter;
+ for(; currIter != end; ++currIter){
+ Node curr = *currIter;
+ if(!Monomial::isMember(curr)){
+ return false;
+ }
+ Monomial mcurr = Monomial::parseMonomial(curr);
+ if(!(mprev < mcurr)){
+ return false;
+ }
+ mprev = mcurr;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+Node SumPair::computeQR(const SumPair& sp, const Integer& div){
+ Assert(sp.isIntegral());
+
+ const Integer& constant = sp.getConstant().getValue().getNumerator();
+
+ Integer constant_q, constant_r;
+ Integer::floorQR(constant_q, constant_r, constant, div);
+
+ Node p_qr = Polynomial::computeQR(sp.getPolynomial(), div);
+ Assert(p_qr.getKind() == kind::ADD);
+ Assert(p_qr.getNumChildren() == 2);
+
+ Polynomial p_q = Polynomial::parsePolynomial(p_qr[0]);
+ Polynomial p_r = Polynomial::parsePolynomial(p_qr[1]);
+
+ SumPair sp_q(p_q, Constant::mkConstant(constant_q));
+ SumPair sp_r(p_r, Constant::mkConstant(constant_r));
+
+ return NodeManager::currentNM()->mkNode(
+ kind::ADD, sp_q.getNode(), sp_r.getNode());
+}
+
+SumPair SumPair::mkSumPair(const Polynomial& p){
+ if(p.isConstant()){
+ Constant leadingConstant = p.getHead().getConstant();
+ return SumPair(Polynomial::mkZero(), leadingConstant);
+ }else if(p.containsConstant()){
+ Assert(!p.singleton());
+ return SumPair(p.getTail(), p.getHead().getConstant());
+ }else{
+ return SumPair(p, Constant::mkZero());
+ }
+}
+
+Comparison::Comparison(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
+
+SumPair Comparison::toSumPair() const {
+ Kind cmpKind = comparisonKind();
+ switch(cmpKind){
+ case kind::LT:
+ case kind::LEQ:
+ case kind::GT:
+ case kind::GEQ:
+ {
+ TNode lit = getNode();
+ TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+ Polynomial p = Polynomial::parsePolynomial(atom[0]);
+ Constant c = Constant::mkConstant(atom[1]);
+ if(p.leadingCoefficientIsPositive()){
+ return SumPair(p, -c);
+ }else{
+ return SumPair(-p, c);
+ }
+ }
+ case kind::EQUAL:
+ case kind::DISTINCT:
+ {
+ Polynomial left = getLeft();
+ Polynomial right = getRight();
+ Trace("nf::tmp") << "left: " << left.getNode() << endl;
+ Trace("nf::tmp") << "right: " << right.getNode() << endl;
+ if(right.isConstant()){
+ return SumPair(left, -right.getHead().getConstant());
+ }else if(right.containsConstant()){
+ Assert(!right.singleton());
+
+ Polynomial noConstant = right.getTail();
+ return SumPair(left - noConstant, -right.getHead().getConstant());
+ }else{
+ return SumPair(left - right, Constant::mkZero());
+ }
+ }
+ default: Unhandled() << cmpKind;
+ }
+}
+
+Polynomial Comparison::normalizedVariablePart() const {
+ Kind cmpKind = comparisonKind();
+ switch(cmpKind){
+ case kind::LT:
+ case kind::LEQ:
+ case kind::GT:
+ case kind::GEQ:
+ {
+ TNode lit = getNode();
+ TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+ Polynomial p = Polynomial::parsePolynomial(atom[0]);
+ if(p.leadingCoefficientIsPositive()){
+ return p;
+ }else{
+ return -p;
+ }
+ }
+ case kind::EQUAL:
+ case kind::DISTINCT:
+ {
+ Polynomial left = getLeft();
+ Polynomial right = getRight();
+ if(right.isConstant()){
+ return left;
+ }else{
+ Polynomial noConstant = right.containsConstant() ? right.getTail() : right;
+ Polynomial diff = left - noConstant;
+ if(diff.leadingCoefficientIsPositive()){
+ return diff;
+ }else{
+ return -diff;
+ }
+ }
+ }
+ default: Unhandled() << cmpKind;
+ }
+}
+
+DeltaRational Comparison::normalizedDeltaRational() const {
+ Kind cmpKind = comparisonKind();
+ int delta = deltaCoeff(cmpKind);
+ switch(cmpKind){
+ case kind::LT:
+ case kind::LEQ:
+ case kind::GT:
+ case kind::GEQ:
+ {
+ Node lit = getNode();
+ Node atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
+ Polynomial left = Polynomial::parsePolynomial(atom[0]);
+ const Rational& q = atom[1].getConst<Rational>();
+ if(left.leadingCoefficientIsPositive()){
+ return DeltaRational(q, delta);
+ }else{
+ return DeltaRational(-q, -delta);
+ }
+ }
+ case kind::EQUAL:
+ case kind::DISTINCT:
+ {
+ Polynomial right = getRight();
+ Monomial firstRight = right.getHead();
+ if(firstRight.isConstant()){
+ DeltaRational c = DeltaRational(firstRight.getConstant().getValue(), 0);
+ Polynomial left = getLeft();
+ if(!left.allIntegralVariables()){
+ return c;
+ //this is a qpolynomial and the sign of the leading
+ //coefficient will not change after the diff below
+ } else{
+ // the polynomial may be a z polynomial in which case
+ // taking the diff is the simplest and obviously correct means
+ Polynomial diff = right.singleton() ? left : left - right.getTail();
+ if(diff.leadingCoefficientIsPositive()){
+ return c;
+ }else{
+ return -c;
+ }
+ }
+ }else{ // The constant is 0 sign cannot change
+ return DeltaRational(0, 0);
+ }
+ }
+ default: Unhandled() << cmpKind;
+ }
+}
+
+std::tuple<Polynomial, Kind, Constant> Comparison::decompose(
+ bool split_constant) const
+{
+ Kind rel = getNode().getKind();
+ if (rel == Kind::NOT)
+ {
+ switch (getNode()[0].getKind())
+ {
+ case kind::LEQ: rel = Kind::GT; break;
+ case kind::LT: rel = Kind::GEQ; break;
+ case kind::EQUAL: rel = Kind::DISTINCT; break;
+ case kind::DISTINCT: rel = Kind::EQUAL; break;
+ case kind::GEQ: rel = Kind::LT; break;
+ case kind::GT: rel = Kind::LEQ; break;
+ default:
+ Assert(false) << "Unsupported relation: " << getNode()[0].getKind();
+ }
+ }
+
+ Polynomial poly = getLeft() - getRight();
+
+ if (!split_constant)
+ {
+ return std::tuple<Polynomial, Kind, Constant>{
+ poly, rel, Constant::mkZero()};
+ }
+
+ Constant right = Constant::mkZero();
+ if (poly.containsConstant())
+ {
+ right = -poly.getHead().getConstant();
+ poly = poly + Polynomial::mkPolynomial(right);
+ }
+
+ Constant lcoeff = poly.getHead().getConstant();
+ if (!lcoeff.isOne())
+ {
+ Constant invlcoeff = lcoeff.inverse();
+ if (lcoeff.isNegative())
+ {
+ switch (rel)
+ {
+ case kind::LEQ: rel = Kind::GEQ; break;
+ case kind::LT: rel = Kind::GT; break;
+ case kind::EQUAL: break;
+ case kind::DISTINCT: break;
+ case kind::GEQ: rel = Kind::LEQ; break;
+ case kind::GT: rel = Kind::LT; break;
+ default: Assert(false) << "Unsupported relation: " << rel;
+ }
+ }
+ poly = poly * invlcoeff;
+ right = right * invlcoeff;
+ }
+
+ return std::tuple<Polynomial, Kind, Constant>{poly, rel, right};
+}
+
+Comparison Comparison::parseNormalForm(TNode n) {
+ Trace("polynomial") << "Comparison::parseNormalForm(" << n << ")";
+ Comparison result(n);
+ Assert(result.isNormalForm());
+ return result;
+}
+
+Node Comparison::toNode(Kind k, const Polynomial& l, const Constant& r) {
+ Assert(isRelationOperator(k));
+ switch(k) {
+ case kind::GEQ:
+ case kind::GT:
+ return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
+ default: Unhandled() << k;
+ }
+}
+
+Node Comparison::toNode(Kind k, const Polynomial& l, const Polynomial& r) {
+ Assert(isRelationOperator(k));
+ switch(k) {
+ case kind::GEQ:
+ case kind::EQUAL:
+ case kind::GT:
+ return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
+ case kind::LEQ:
+ return toNode(kind::GEQ, r, l).notNode();
+ case kind::LT:
+ return toNode(kind::GT, r, l).notNode();
+ case kind::DISTINCT:
+ return toNode(kind::EQUAL, r, l).notNode();
+ default:
+ Unreachable();
+ }
+}
+
+bool Comparison::rightIsConstant() const {
+ if(getNode().getKind() == kind::NOT){
+ return getNode()[0][1].getKind() == kind::CONST_RATIONAL;
+ }else{
+ return getNode()[1].getKind() == kind::CONST_RATIONAL;
+ }
+}
+
+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();
+ switch(k){
+ case kind::LT:
+ case kind::LEQ:
+ case kind::DISTINCT:
+ left = getNode()[0][0];
+ break;
+ case kind::EQUAL:
+ case kind::GT:
+ case kind::GEQ:
+ left = getNode()[0];
+ break;
+ default: Unhandled() << k;
+ }
+ return Polynomial::parsePolynomial(left);
+}
+
+Polynomial Comparison::getRight() const {
+ TNode right;
+ Kind k = comparisonKind();
+ switch(k){
+ case kind::LT:
+ case kind::LEQ:
+ case kind::DISTINCT:
+ right = getNode()[0][1];
+ break;
+ case kind::EQUAL:
+ case kind::GT:
+ case kind::GEQ:
+ right = getNode()[1];
+ break;
+ default: Unhandled() << k;
+ }
+ return Polynomial::parsePolynomial(right);
+}
+
+// Polynomial Comparison::getLeft() const {
+// Node n = getNode();
+// Node left = (n.getKind() == kind::NOT ? n[0]: n)[0];
+// return Polynomial::parsePolynomial(left);
+// }
+
+// Polynomial Comparison::getRight() const {
+// Node n = getNode();
+// Node right = (n.getKind() == kind::NOT ? n[0]: n)[1];
+// return Polynomial::parsePolynomial(right);
+// }
+
+bool Comparison::isNormalForm() const {
+ Node n = getNode();
+ Kind cmpKind = comparisonKind(n);
+ Trace("nf::tmp") << "isNormalForm " << n << " " << cmpKind << endl;
+ switch(cmpKind){
+ case kind::CONST_BOOLEAN:
+ return true;
+ case kind::GT:
+ return isNormalGT();
+ case kind::GEQ:
+ return isNormalGEQ();
+ case kind::EQUAL:
+ return isNormalEquality();
+ case kind::LT:
+ return isNormalLT();
+ case kind::LEQ:
+ return isNormalLEQ();
+ case kind::DISTINCT:
+ return isNormalDistinct();
+ default:
+ return false;
+ }
+}
+
+/** This must be (> qpolynomial constant) */
+bool Comparison::isNormalGT() const {
+ Node n = getNode();
+ Assert(n.getKind() == kind::GT);
+ if(!rightIsConstant()){
+ return false;
+ }else{
+ Polynomial left = getLeft();
+ if(left.containsConstant()){
+ return false;
+ }else if(!left.leadingCoefficientIsAbsOne()){
+ return false;
+ }else{
+ return !left.isIntegral();
+ }
+ }
+}
+
+/** This must be (not (> qpolynomial constant)) */
+bool Comparison::isNormalLEQ() const {
+ Node n = getNode();
+ Trace("nf::tmp") << "isNormalLEQ " << n << endl;
+ Assert(n.getKind() == kind::NOT);
+ Assert(n[0].getKind() == kind::GT);
+ if(!rightIsConstant()){
+ return false;
+ }else{
+ Polynomial left = getLeft();
+ if(left.containsConstant()){
+ return false;
+ }else if(!left.leadingCoefficientIsAbsOne()){
+ return false;
+ }else{
+ return !left.isIntegral();
+ }
+ }
+}
+
+
+/** This must be (>= qpolynomial constant) or (>= zpolynomial constant) */
+bool Comparison::isNormalGEQ() const {
+ Node n = getNode();
+ Assert(n.getKind() == kind::GEQ);
+
+ Trace("nf::tmp") << "isNormalGEQ " << n << " " << rightIsConstant() << endl;
+
+ if(!rightIsConstant()){
+ return false;
+ }else{
+ Polynomial left = getLeft();
+ if(left.containsConstant()){
+ return false;
+ }else{
+ if(left.isIntegral()){
+ return left.signNormalizedReducedSum();
+ }else{
+ return left.leadingCoefficientIsAbsOne();
+ }
+ }
+ }
+}
+
+/** This must be (not (>= qpolynomial constant)) or (not (>= zpolynomial constant)) */
+bool Comparison::isNormalLT() const {
+ Node n = getNode();
+ Assert(n.getKind() == kind::NOT);
+ Assert(n[0].getKind() == kind::GEQ);
+
+ if(!rightIsConstant()){
+ return false;
+ }else{
+ Polynomial left = getLeft();
+ if(left.containsConstant()){
+ return false;
+ }else{
+ if(left.isIntegral()){
+ return left.signNormalizedReducedSum();
+ }else{
+ return left.leadingCoefficientIsAbsOne();
+ }
+ }
+ }
+}
+
+
+bool Comparison::isNormalEqualityOrDisequality() const {
+ Polynomial pleft = getLeft();
+
+ if(pleft.numMonomials() == 1){
+ Monomial mleft = pleft.getHead();
+ if(mleft.isConstant()){
+ return false;
+ }else{
+ Polynomial pright = getRight();
+ if(allIntegralVariables()){
+ const Rational& lcoeff = mleft.getConstant().getValue();
+ if(pright.isConstant()){
+ return pright.isIntegral() && lcoeff.isOne();
+ }
+ Polynomial varRight = pright.containsConstant() ? pright.getTail() : pright;
+ if(lcoeff.sgn() <= 0){
+ return false;
+ }else{
+ Integer lcm = lcoeff.getDenominator().lcm(varRight.denominatorLCM());
+ Integer g = lcoeff.getNumerator().gcd(varRight.numeratorGCD());
+ Trace("nf::tmp") << lcm << " " << g << endl;
+ if(!lcm.isOne()){
+ return false;
+ }else if(!g.isOne()){
+ return false;
+ }else{
+ Monomial absMinRight = varRight.selectAbsMinimum();
+ Trace("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl;
+ if( mleft.absCmp(absMinRight) < 0){
+ return true;
+ }else{
+ return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight;
+ }
+ }
+ }
+ }else{
+ if(mleft.coefficientIsOne()){
+ Trace("nf::tmp")
+ << "dfklj " << mleft.getNode() << endl
+ << pright.getNode() << endl
+ << pright.variableMonomialAreStrictlyGreater(mleft)
+ << endl;
+ return pright.variableMonomialAreStrictlyGreater(mleft);
+ }else{
+ return false;
+ }
+ }
+ }
+ }else{
+ return false;
+ }
+}
+
+/** This must be (= qvarlist qpolynomial) or (= zmonomial zpolynomial)*/
+bool Comparison::isNormalEquality() const {
+ Assert(getNode().getKind() == kind::EQUAL);
+ return Theory::theoryOf(getNode()[0].getType()) == THEORY_ARITH &&
+ isNormalEqualityOrDisequality();
+}
+
+/**
+ * This must be (not (= qvarlist qpolynomial)) or
+ * (not (= zmonomial zpolynomial)).
+ */
+bool Comparison::isNormalDistinct() const {
+ Assert(getNode().getKind() == kind::NOT);
+ Assert(getNode()[0].getKind() == kind::EQUAL);
+
+ return Theory::theoryOf(getNode()[0][0].getType()) == THEORY_ARITH &&
+ isNormalEqualityOrDisequality();
+}
+
+Node Comparison::mkRatEquality(const Polynomial& p){
+ Assert(!p.isConstant());
+ Assert(!p.allIntegralVariables());
+
+ Monomial minimalVList = p.minimumVariableMonomial();
+ Constant coeffInv = -(minimalVList.getConstant().inverse());
+
+ Polynomial newRight = (p - minimalVList) * coeffInv;
+ Polynomial newLeft(Monomial::mkMonomial(minimalVList.getVarList()));
+
+ return toNode(kind::EQUAL, newLeft, newRight);
+}
+
+Node Comparison::mkRatInequality(Kind k, const Polynomial& p){
+ Assert(k == kind::GEQ || k == kind::GT);
+ Assert(!p.isConstant());
+ Assert(!p.allIntegralVariables());
+
+ SumPair sp = SumPair::mkSumPair(p);
+ Polynomial left = sp.getPolynomial();
+ Constant right = - sp.getConstant();
+
+ Monomial minimalVList = left.getHead();
+ Assert(!minimalVList.isConstant());
+
+ Constant coeffInv = minimalVList.getConstant().inverse().abs();
+ Polynomial newLeft = left * coeffInv;
+ Constant newRight = right * (coeffInv);
+
+ return toNode(k, newLeft, newRight);
+}
+
+Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
+ Assert(kind::GT == k || kind::GEQ == k);
+ Assert(!p.isConstant());
+ Assert(p.allIntegralVariables());
+
+ SumPair sp = SumPair::mkSumPair(p);
+ Polynomial left = sp.getPolynomial();
+ Rational right = - (sp.getConstant().getValue());
+
+
+ Monomial m = left.getHead();
+ Assert(!m.isConstant());
+
+ Integer lcm = left.denominatorLCM();
+ Integer g = left.numeratorGCD();
+ Rational mult(lcm,g);
+
+ Polynomial newLeft = left * mult;
+ Rational rightMult = right * mult;
+
+ bool negateResult = false;
+ if(!newLeft.leadingCoefficientIsPositive()){
+ // multiply by -1
+ // a: left >= right or b: left > right
+ // becomes
+ // a: -left <= -right or b: -left < -right
+ // a: not (-left > -right) or b: (not -left >= -right)
+ newLeft = -newLeft;
+ rightMult = -rightMult;
+ k = (kind::GT == k) ? kind::GEQ : kind::GT;
+ negateResult = true;
+ // the later stages handle:
+ // a: not (-left >= -right + 1) or b: (not -left >= -right)
+ }
+
+ Node result = Node::null();
+ if(rightMult.isIntegral()){
+ if(k == kind::GT){
+ // (> p z)
+ // (>= p (+ z 1))
+ Constant rightMultPlusOne = Constant::mkConstant(rightMult + 1);
+ result = toNode(kind::GEQ, newLeft, rightMultPlusOne);
+ }else{
+ Constant newRight = Constant::mkConstant(rightMult);
+ result = toNode(kind::GEQ, newLeft, newRight);
+ }
+ }else{
+ //(>= l (/ n d))
+ //(>= l (ceil (/ n d)))
+ //This also hold for GT as (ceil (/ n d)) > (/ n d)
+ Integer ceilr = rightMult.ceiling();
+ Constant ceilRight = Constant::mkConstant(ceilr);
+ result = toNode(kind::GEQ, newLeft, ceilRight);
+ }
+ Assert(!result.isNull());
+ if(negateResult){
+ return result.notNode();
+ }else{
+ return result;
+ }
+}
+
+Node Comparison::mkIntEquality(const Polynomial& p){
+ Assert(!p.isConstant());
+ Assert(p.allIntegralVariables());
+
+ SumPair sp = SumPair::mkSumPair(p);
+ Polynomial varPart = sp.getPolynomial();
+ Constant constPart = sp.getConstant();
+
+ Integer lcm = varPart.denominatorLCM();
+ Integer g = varPart.numeratorGCD();
+ Constant mult = Constant::mkConstant(Rational(lcm,g));
+
+ Constant constMult = constPart * mult;
+
+ if(constMult.isIntegral()){
+ Polynomial varPartMult = varPart * mult;
+
+ Monomial m = varPartMult.selectAbsMinimum();
+ bool mIsPositive = m.getConstant().isPositive();
+
+ Polynomial noM = (varPartMult + (- m)) + Polynomial::mkPolynomial(constMult);
+
+ // m + noM = 0
+ Polynomial newRight = mIsPositive ? -noM : noM;
+ Polynomial newLeft = mIsPositive ? m : -m;
+
+ Assert(newRight.isIntegral());
+ return toNode(kind::EQUAL, newLeft, newRight);
+ }else{
+ return mkBoolNode(false);
+ }
+}
+
+Comparison Comparison::mkComparison(Kind k, const Polynomial& l, const Polynomial& r){
+
+ //Make this special case fast for sharing!
+ if((k == kind::EQUAL || k == kind::DISTINCT) && l.isVarList() && r.isVarList()){
+ VarList vLeft = l.asVarList();
+ VarList vRight = r.asVarList();
+
+ if(vLeft == vRight){
+ // return true for equalities and false for disequalities
+ return Comparison(k == kind::EQUAL);
+ }else{
+ Node eqNode = vLeft < vRight ? toNode( kind::EQUAL, l, r) : toNode( kind::EQUAL, r, l);
+ Node forK = (k == kind::DISTINCT) ? eqNode.notNode() : eqNode;
+ return Comparison(forK);
+ }
+ }
+
+ //General case
+ Polynomial diff = l - r;
+ if(diff.isConstant()){
+ bool res = evaluateConstantPredicate(k, diff.asConstant(), Rational(0));
+ return Comparison(res);
+ }else{
+ Node result = Node::null();
+ bool isInteger = diff.allIntegralVariables();
+ switch(k){
+ case kind::EQUAL:
+ result = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
+ break;
+ case kind::DISTINCT:
+ {
+ Node eq = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
+ result = eq.notNode();
+ }
+ break;
+ case kind::LEQ:
+ case kind::LT:
+ {
+ Polynomial neg = - diff;
+ Kind negKind = (k == kind::LEQ ? kind::GEQ : kind::GT);
+ result = isInteger ?
+ mkIntInequality(negKind, neg) : mkRatInequality(negKind, neg);
+ }
+ break;
+ case kind::GEQ:
+ case kind::GT:
+ result = isInteger ?
+ mkIntInequality(k, diff) : mkRatInequality(k, diff);
+ break;
+ default: Unhandled() << k;
+ }
+ Assert(!result.isNull());
+ if(result.getKind() == kind::NOT && result[0].getKind() == kind::CONST_BOOLEAN){
+ return Comparison(!(result[0].getConst<bool>()));
+ }else{
+ Comparison cmp(result);
+ Assert(cmp.isNormalForm());
+ return cmp;
+ }
+ }
+}
+
+bool Comparison::isBoolean() const {
+ return getNode().getKind() == kind::CONST_BOOLEAN;
+}
+
+
+bool Comparison::debugIsIntegral() const{
+ return getLeft().isIntegral() && getRight().isIntegral();
+}
+
+Kind Comparison::comparisonKind(TNode literal){
+ switch(literal.getKind()){
+ case kind::CONST_BOOLEAN:
+ case kind::GT:
+ case kind::GEQ:
+ case kind::EQUAL:
+ return literal.getKind();
+ case kind::NOT:
+ {
+ TNode negatedAtom = literal[0];
+ switch(negatedAtom.getKind()){
+ case kind::GT: //(not (GT x c)) <=> (LEQ x c)
+ return kind::LEQ;
+ case kind::GEQ: //(not (GEQ x c)) <=> (LT x c)
+ return kind::LT;
+ case kind::EQUAL:
+ return kind::DISTINCT;
+ default:
+ return kind::UNDEFINED_KIND;
+ }
+ }
+ default:
+ return kind::UNDEFINED_KIND;
+ }
+}
+
+
+Node Polynomial::makeAbsCondition(Variable v, Polynomial p){
+ Polynomial zerop = Polynomial::mkZero();
+
+ Polynomial varp = Polynomial::mkPolynomial(v);
+ Comparison pLeq0 = Comparison::mkComparison(kind::LEQ, p, zerop);
+ Comparison negP = Comparison::mkComparison(kind::EQUAL, varp, -p);
+ Comparison posP = Comparison::mkComparison(kind::EQUAL, varp, p);
+
+ Node absCnd = (pLeq0.getNode()).iteNode(negP.getNode(), posP.getNode());
+ return absCnd;
+}
+
+bool Polynomial::isNonlinear() const {
+
+ for(iterator i=begin(), iend =end(); i != iend; ++i){
+ Monomial m = *i;
+ if(m.isNonlinear()){
+ return true;
+ }
+ }
+ return false;
+}
+
+} //namespace arith
+} //namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__NORMAL_FORM_H
+#define CVC5__THEORY__ARITH__NORMAL_FORM_H
+
+#include <algorithm>
+
+#include "base/output.h"
+#include "expr/node.h"
+#include "expr/node_self_iterator.h"
+#include "theory/arith/delta_rational.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/***********************************************/
+/***************** Normal Form *****************/
+/***********************************************/
+/***********************************************/
+
+/**
+ * Section 1: Languages
+ * The normal form for arithmetic nodes is defined by the language
+ * accepted by the following BNFs with some guard conditions.
+ * (The guard conditions are in Section 3 for completeness.)
+ *
+ * variable := n
+ * where
+ * n.isVar() or is foreign
+ * n.getType() \in {Integer, Real}
+ *
+ * constant := n
+ * where
+ * n.getKind() == kind::CONST_RATIONAL
+ *
+ * var_list := variable | (* [variable])
+ * where
+ * len [variable] >= 2
+ * isSorted varOrder [variable]
+ *
+ * monomial := constant | var_list | (* constant' var_list')
+ * where
+ * \f$ constant' \not\in {0,1} \f$
+ *
+ * polynomial := monomial' | (+ [monomial])
+ * where
+ * len [monomial] >= 2
+ * isStrictlySorted monoOrder [monomial]
+ * forall (\x -> x != 0) [monomial]
+ *
+ * rational_cmp := (|><| qpolynomial constant)
+ * where
+ * |><| is GEQ, or GT
+ * not (exists constantMonomial (monomialList qpolynomial))
+ * (exists realMonomial (monomialList qpolynomial))
+ * abs(monomialCoefficient (head (monomialList qpolynomial))) == 1
+ *
+ * integer_cmp := (>= zpolynomial constant)
+ * where
+ * not (exists constantMonomial (monomialList zpolynomial))
+ * (forall integerMonomial (monomialList zpolynomial))
+ * the gcd of all numerators of coefficients is 1
+ * the denominator of all coefficients and the constant is 1
+ * the leading coefficient is positive
+ *
+ * rational_eq := (= qvarlist qpolynomial)
+ * where
+ * let allMonomials = (cons qvarlist (monomialList zpolynomial))
+ * let variableMonomials = (drop constantMonomial allMonomials)
+ * isStrictlySorted variableMonomials
+ * exists realMonomial variableMonomials
+ * is not empty qvarlist
+ *
+ * integer_eq := (= zmonomial zpolynomial)
+ * where
+ * let allMonomials = (cons zmonomial (monomialList zpolynomial))
+ * let variableMonomials = (drop constantMonomial allMonomials)
+ * not (constantMonomial zmonomial)
+ * (forall integerMonomial allMonomials)
+ * isStrictlySorted variableMonomials
+ * the gcd of all numerators of coefficients is 1
+ * the denominator of all coefficients and the constant is 1
+ * the coefficient of monomial is positive
+ * the value of the coefficient of monomial is minimal in variableMonomials
+ *
+ * comparison := TRUE | FALSE
+ * | rational_cmp | (not rational_cmp)
+ * | rational_eq | (not rational_eq)
+ * | integer_cmp | (not integer_cmp)
+ * | integer_eq | (not integer_eq)
+ *
+ * Normal Form for terms := polynomial
+ * Normal Form for atoms := comparison
+ */
+
+/**
+ * Section 2: Helper Classes
+ * The langauges accepted by each of these defintions
+ * roughly corresponds to one of the following helper classes:
+ * Variable
+ * Constant
+ * VarList
+ * Monomial
+ * Polynomial
+ * Comparison
+ *
+ * Each of the classes obeys the following contracts/design decisions:
+ * -Calling isMember(Node node) on a node returns true iff that node is a
+ * a member of the language. Note: isMember is O(n).
+ * -Calling isNormalForm() on a helper class object returns true iff that
+ * helper class currently represents a normal form object.
+ * -If isNormalForm() is false, then this object must have been made
+ * using a mk*() factory function.
+ * -If isNormalForm() is true, calling getNode() on all of these classes
+ * returns a node that would be accepted by the corresponding language.
+ * And if isNormalForm() is false, returns Node::null().
+ * -Each of the classes is immutable.
+ * -Public facing constuctors have a 1-to-1 correspondence with one of
+ * production rules in the above grammar.
+ * -Public facing constuctors are required to fail in debug mode when the
+ * guards of the production rule are not strictly met.
+ * For example: Monomial(Constant(1),VarList(Variable(x))) must fail.
+ * -When a class has a Class parseClass(Node node) function,
+ * if isMember(node) is true, the function is required to return an instance
+ * of the helper class, instance, s.t. instance.getNode() == node.
+ * And if isMember(node) is false, this throws an assertion failure in debug
+ * mode and has undefined behaviour if not in debug mode.
+ * -Only public facing constructors, parseClass(node), and mk*() functions are
+ * considered privileged functions for the helper class.
+ * -Only privileged functions may use private constructors, and access
+ * private data members.
+ * -All non-privileged functions are considered utility functions and
+ * must use a privileged function in order to create an instance of the class.
+ */
+
+/**
+ * Section 3: Guard Conditions Misc.
+ *
+ *
+ * variable_order x y =
+ * if (meta_kind_variable x) and (meta_kind_variable y)
+ * then node_order x y
+ * else if (meta_kind_variable x)
+ * then false
+ * else if (meta_kind_variable y)
+ * then true
+ * else node_order x y
+ *
+ * var_list_len vl =
+ * match vl with
+ * variable -> 1
+ * | (* [variable]) -> len [variable]
+ *
+ * order res =
+ * match res with
+ * Empty -> (0,Node::null())
+ * | NonEmpty(vl) -> (var_list_len vl, vl)
+ *
+ * var_listOrder a b = tuple_cmp (order a) (order b)
+ *
+ * monomialVarList monomial =
+ * match monomial with
+ * constant -> Empty
+ * | var_list -> NonEmpty(var_list)
+ * | (* constant' var_list') -> NonEmpty(var_list')
+ *
+ * monoOrder m0 m1 = var_listOrder (monomialVarList m0) (monomialVarList m1)
+ *
+ * integerMonomial mono =
+ * forall varHasTypeInteger (monomialVarList mono)
+ *
+ * realMonomial mono = not (integerMonomial mono)
+ *
+ * constantMonomial monomial =
+ * match monomial with
+ * constant -> true
+ * | var_list -> false
+ * | (* constant' var_list') -> false
+ *
+ * monomialCoefficient monomial =
+ * match monomial with
+ * constant -> constant
+ * | var_list -> Constant(1)
+ * | (* constant' var_list') -> constant'
+ *
+ * monomialList polynomial =
+ * match polynomial with
+ * monomial -> monomial::[]
+ * | (+ [monomial]) -> [monomial]
+ */
+
+/**
+ * A NodeWrapper is a class that is a thinly veiled container of a Node object.
+ */
+class NodeWrapper {
+private:
+ Node node;
+public:
+ NodeWrapper(Node n) : node(n) {}
+ const Node& getNode() const { return node; }
+};/* class NodeWrapper */
+
+
+class Variable : public NodeWrapper {
+public:
+ Variable(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
+
+ // TODO: check if it's a theory leaf also
+ static bool isMember(Node n)
+ {
+ Kind k = n.getKind();
+ switch (k)
+ {
+ case kind::CONST_RATIONAL: return false;
+ case kind::INTS_DIVISION:
+ case kind::INTS_MODULUS:
+ case kind::DIVISION:
+ case kind::INTS_DIVISION_TOTAL:
+ case kind::INTS_MODULUS_TOTAL:
+ case kind::DIVISION_TOTAL: return isDivMember(n);
+ case kind::IAND: return isIAndMember(n);
+ case kind::POW2: return isPow2Member(n);
+ case kind::EXPONENTIAL:
+ case kind::SINE:
+ case kind::COSINE:
+ case kind::TANGENT:
+ case kind::COSECANT:
+ case kind::SECANT:
+ case kind::COTANGENT:
+ case kind::ARCSINE:
+ case kind::ARCCOSINE:
+ case kind::ARCTANGENT:
+ case kind::ARCCOSECANT:
+ case kind::ARCSECANT:
+ case kind::ARCCOTANGENT:
+ case kind::SQRT:
+ case kind::PI: return isTranscendentalMember(n);
+ case kind::ABS:
+ case kind::TO_INTEGER:
+ // Treat to_int as a variable; it is replaced in early preprocessing
+ // by a variable.
+ return true;
+ default: return isLeafMember(n);
+ }
+ }
+
+ static bool isLeafMember(Node n);
+ static bool isIAndMember(Node n);
+ static bool isPow2Member(Node n);
+ static bool isDivMember(Node n);
+ bool isDivLike() const{
+ return isDivMember(getNode());
+ }
+ static bool isTranscendentalMember(Node n);
+
+ bool isNormalForm() { return isMember(getNode()); }
+
+ bool isIntegral() const {
+ return getNode().getType().isInteger();
+ }
+
+ bool isMetaKindVariable() const {
+ return getNode().isVar();
+ }
+
+ bool operator<(const Variable& v) const {
+ VariableNodeCmp cmp;
+ return cmp(this->getNode(), v.getNode());
+ }
+
+ struct VariableNodeCmp {
+ static inline int cmp(const Node& n, const Node& m) {
+ if ( n == m ) { return 0; }
+
+ // RAN < real var < int var < non-variable
+
+ bool nIsRAN = n.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
+ bool mIsRAN = m.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
+
+ if (mIsRAN != nIsRAN)
+ {
+ return nIsRAN ? -1 : 1;
+ }
+
+ bool nIsInteger = n.getType().isInteger();
+ bool mIsInteger = m.getType().isInteger();
+
+ if(nIsInteger == mIsInteger){
+ bool nIsVariable = n.isVar();
+ bool mIsVariable = m.isVar();
+
+ if(nIsVariable == mIsVariable){
+ if(n < m){
+ return -1;
+ }else{
+ Assert(n != m);
+ return 1;
+ }
+ }else{
+ if(nIsVariable){
+ return -1; // nIsVariable => !mIsVariable
+ }else{
+ return 1; // !nIsVariable => mIsVariable
+ }
+ }
+ }else{
+ Assert(nIsInteger != mIsInteger);
+ if(nIsInteger){
+ return 1; // nIsInteger => !mIsInteger
+ }else{
+ return -1; // !nIsInteger => mIsInteger
+ }
+ }
+ }
+
+ bool operator()(const Node& n, const Node& m) const {
+ return VariableNodeCmp::cmp(n,m) < 0;
+ }
+ };
+
+ bool operator==(const Variable& v) const { return getNode() == v.getNode();}
+
+ size_t getComplexity() const;
+};/* class Variable */
+
+class Constant : public NodeWrapper {
+public:
+ Constant(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
+
+ static bool isMember(Node n) { return n.getKind() == kind::CONST_RATIONAL; }
+
+ bool isNormalForm() { return isMember(getNode()); }
+
+ static Constant mkConstant(Node n)
+ {
+ Assert(n.getKind() == kind::CONST_RATIONAL);
+ return Constant(n);
+ }
+
+ static Constant mkConstant(const Rational& rat);
+
+ static Constant mkZero() {
+ return mkConstant(Rational(0));
+ }
+
+ static Constant mkOne() {
+ return mkConstant(Rational(1));
+ }
+
+ const Rational& getValue() const {
+ 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(); }
+
+ bool isZero() const { return sgn() == 0; }
+ bool isNegative() const { return sgn() < 0; }
+ bool isPositive() const { return sgn() > 0; }
+
+ bool isOne() const { return getValue() == 1; }
+
+ Constant operator*(const Rational& other) const {
+ return mkConstant(getValue() * other);
+ }
+
+ Constant operator*(const Constant& other) const {
+ return mkConstant(getValue() * other.getValue());
+ }
+ Constant operator+(const Constant& other) const {
+ return mkConstant(getValue() + other.getValue());
+ }
+ Constant operator-() const {
+ return mkConstant(-getValue());
+ }
+
+ Constant inverse() const{
+ Assert(!isZero());
+ return mkConstant(getValue().inverse());
+ }
+
+ bool operator<(const Constant& other) const {
+ return getValue() < other.getValue();
+ }
+
+ bool operator==(const Constant& other) const {
+ //Rely on node uniqueness.
+ return getNode() == other.getNode();
+ }
+
+ Constant abs() const {
+ if(isNegative()){
+ return -(*this);
+ }else{
+ return (*this);
+ }
+ }
+
+ uint32_t length() const{
+ Assert(isIntegral());
+ return getValue().getNumerator().length();
+ }
+
+ size_t getComplexity() const;
+
+};/* class Constant */
+
+
+template <class GetNodeIterator>
+inline Node makeNode(Kind k, GetNodeIterator start, GetNodeIterator end) {
+ NodeBuilder nb(k);
+
+ while(start != end) {
+ nb << (*start).getNode();
+ ++start;
+ }
+
+ return Node(nb);
+}/* makeNode<GetNodeIterator>(Kind, iterator, iterator) */
+
+/**
+ * A VarList is a sorted list of variables representing a product.
+ * If the VarList is empty, it represents an empty product or 1.
+ * If the VarList has size 1, it represents a single variable.
+ *
+ * A non-sorted VarList can never be successfully made in debug mode.
+ */
+class VarList : public NodeWrapper {
+private:
+
+ static Node multList(const std::vector<Variable>& list) {
+ Assert(list.size() >= 2);
+
+ return makeNode(kind::NONLINEAR_MULT, list.begin(), list.end());
+ }
+
+ VarList() : NodeWrapper(Node::null()) {}
+
+ VarList(Node n);
+
+ typedef expr::NodeSelfIterator internal_iterator;
+
+ internal_iterator internalBegin() const {
+ if(singleton()){
+ return expr::NodeSelfIterator::self(getNode());
+ }else{
+ return getNode().begin();
+ }
+ }
+
+ internal_iterator internalEnd() const {
+ if(singleton()){
+ return expr::NodeSelfIterator::selfEnd(getNode());
+ }else{
+ return getNode().end();
+ }
+ }
+
+public:
+
+ class iterator {
+ private:
+ internal_iterator d_iter;
+
+ public:
+ /* The following types are required by trait std::iterator_traits */
+
+ /** Iterator tag */
+ using iterator_category = std::forward_iterator_tag;
+
+ /** The type of the item */
+ using value_type = Variable;
+
+ /** The pointer type of the item */
+ using pointer = Variable*;
+
+ /** The reference type of the item */
+ using reference = Variable&;
+
+ /** The type returned when two iterators are subtracted */
+ using difference_type = std::ptrdiff_t;
+
+ /* End of std::iterator_traits required types */
+
+ explicit iterator(internal_iterator i) : d_iter(i) {}
+
+ inline Variable operator*() {
+ return Variable(*d_iter);
+ }
+
+ bool operator==(const iterator& i) {
+ return d_iter == i.d_iter;
+ }
+
+ bool operator!=(const iterator& i) {
+ return d_iter != i.d_iter;
+ }
+
+ iterator operator++() {
+ ++d_iter;
+ return *this;
+ }
+
+ iterator operator++(int) {
+ return iterator(d_iter++);
+ }
+ };
+
+ iterator begin() const {
+ return iterator(internalBegin());
+ }
+
+ iterator end() const {
+ return iterator(internalEnd());
+ }
+
+ Variable getHead() const {
+ Assert(!empty());
+ return *(begin());
+ }
+
+ VarList(Variable v) : NodeWrapper(v.getNode()) {
+ Assert(isSorted(begin(), end()));
+ }
+
+ VarList(const std::vector<Variable>& l) : NodeWrapper(multList(l)) {
+ Assert(l.size() >= 2);
+ Assert(isSorted(begin(), end()));
+ }
+
+ static bool isMember(Node n);
+
+ bool isNormalForm() const {
+ return !empty();
+ }
+
+ static VarList mkEmptyVarList() {
+ return VarList();
+ }
+
+
+ /** There are no restrictions on the size of l */
+ static VarList mkVarList(const std::vector<Variable>& l) {
+ if(l.size() == 0) {
+ return mkEmptyVarList();
+ } else if(l.size() == 1) {
+ return VarList((*l.begin()).getNode());
+ } else {
+ return VarList(l);
+ }
+ }
+
+ bool empty() const { return getNode().isNull(); }
+ bool singleton() const {
+ return !empty() && getNode().getKind() != kind::NONLINEAR_MULT;
+ }
+
+ int size() const {
+ if(singleton())
+ return 1;
+ else
+ return getNode().getNumChildren();
+ }
+
+ static VarList parseVarList(Node n);
+
+ VarList operator*(const VarList& vl) const;
+
+ int cmp(const VarList& vl) const;
+
+ bool operator<(const VarList& vl) const { return cmp(vl) < 0; }
+
+ bool operator==(const VarList& vl) const { return cmp(vl) == 0; }
+
+ bool isIntegral() const {
+ for(iterator i = begin(), e=end(); i != e; ++i ){
+ Variable var = *i;
+ if(!var.isIntegral()){
+ return false;
+ }
+ }
+ return true;
+ }
+ size_t getComplexity() const;
+
+private:
+ bool isSorted(iterator start, iterator end);
+
+};/* class VarList */
+
+
+/** Constructors have side conditions. Use the static mkMonomial functions instead. */
+class Monomial : public NodeWrapper {
+private:
+ Constant constant;
+ VarList varList;
+ Monomial(Node n, const Constant& c, const VarList& vl):
+ NodeWrapper(n), constant(c), varList(vl)
+ {
+ Assert(!c.isZero() || vl.empty());
+ Assert(c.isZero() || !vl.empty());
+
+ Assert(!c.isOne() || !multStructured(n));
+ }
+
+ static Node makeMultNode(const Constant& c, const VarList& vl) {
+ Assert(!c.isZero());
+ Assert(!c.isOne());
+ Assert(!vl.empty());
+ return NodeManager::currentNM()->mkNode(kind::MULT, c.getNode(), vl.getNode());
+ }
+
+ static bool multStructured(Node n) {
+ return n.getKind() == kind::MULT &&
+ n[0].getKind() == kind::CONST_RATIONAL &&
+ n.getNumChildren() == 2;
+ }
+
+ Monomial(const Constant& c):
+ NodeWrapper(c.getNode()), constant(c), varList(VarList::mkEmptyVarList())
+ { }
+
+ Monomial(const VarList& vl):
+ NodeWrapper(vl.getNode()), constant(Constant::mkConstant(1)), varList(vl)
+ {
+ Assert(!varList.empty());
+ }
+
+ Monomial(const Constant& c, const VarList& vl):
+ NodeWrapper(makeMultNode(c,vl)), constant(c), varList(vl)
+ {
+ Assert(!c.isZero());
+ Assert(!c.isOne());
+ Assert(!varList.empty());
+
+ Assert(multStructured(getNode()));
+ }
+public:
+ static bool isMember(TNode n);
+
+ /** Makes a monomial with no restrictions on c and vl. */
+ static Monomial mkMonomial(const Constant& c, const VarList& vl);
+
+ /** If vl is empty, this make one. */
+ static Monomial mkMonomial(const VarList& vl);
+
+ static Monomial mkMonomial(const Constant& c){
+ return Monomial(c);
+ }
+
+ static Monomial mkMonomial(const Variable& v){
+ return Monomial(VarList(v));
+ }
+
+ static Monomial parseMonomial(Node n);
+
+ static Monomial mkZero() {
+ return Monomial(Constant::mkConstant(0));
+ }
+ static Monomial mkOne() {
+ return Monomial(Constant::mkConstant(1));
+ }
+ const Constant& getConstant() const { return constant; }
+ const VarList& getVarList() const { return varList; }
+
+ bool isConstant() const {
+ return varList.empty();
+ }
+
+ bool isZero() const {
+ return constant.isZero();
+ }
+
+ bool coefficientIsOne() const {
+ return constant.isOne();
+ }
+
+ bool absCoefficientIsOne() const {
+ return coefficientIsOne() || constant.getValue() == -1;
+ }
+
+ bool constantIsPositive() const {
+ return getConstant().isPositive();
+ }
+
+ Monomial operator*(const Rational& q) const;
+ Monomial operator*(const Constant& c) const;
+ Monomial operator*(const Monomial& mono) const;
+
+ Monomial operator-() const{
+ return (*this) * Rational(-1);
+ }
+
+
+ int cmp(const Monomial& mono) const {
+ return getVarList().cmp(mono.getVarList());
+ }
+
+ bool operator<(const Monomial& vl) const {
+ return cmp(vl) < 0;
+ }
+
+ bool operator==(const Monomial& vl) const {
+ return cmp(vl) == 0;
+ }
+
+ static bool isSorted(const std::vector<Monomial>& m) {
+ return std::is_sorted(m.begin(), m.end());
+ }
+
+ static bool isStrictlySorted(const std::vector<Monomial>& m) {
+ 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
+ */
+ bool integralVariables() const {
+ return getVarList().isIntegral();
+ }
+
+ /**
+ * The coefficient of the monomial is integral.
+ */
+ bool integralCoefficient() const {
+ return getConstant().isIntegral();
+ }
+
+ /**
+ * A Monomial is an "integral" monomial if the constant is integral.
+ */
+ bool isIntegral() const {
+ return integralCoefficient() && integralVariables();
+ }
+
+ /** Returns true if the VarList is a product of at least 2 Variables.*/
+ bool isNonlinear() const {
+ return getVarList().size() >= 2;
+ }
+
+ /**
+ * 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);
+
+ 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();
+ }
+
+ void print() const;
+ static void printList(const std::vector<Monomial>& list);
+
+ size_t getComplexity() const;
+};/* class Monomial */
+
+class SumPair;
+class Comparison;;
+
+class Polynomial : public NodeWrapper {
+private:
+ bool d_singleton;
+
+ Polynomial(TNode n) : NodeWrapper(n), d_singleton(Monomial::isMember(n)) {
+ Assert(isMember(getNode()));
+ }
+
+ static Node makePlusNode(const std::vector<Monomial>& m) {
+ Assert(m.size() >= 2);
+
+ return makeNode(kind::ADD, m.begin(), m.end());
+ }
+
+ typedef expr::NodeSelfIterator internal_iterator;
+
+ internal_iterator internalBegin() const {
+ if(singleton()){
+ return expr::NodeSelfIterator::self(getNode());
+ }else{
+ return getNode().begin();
+ }
+ }
+
+ internal_iterator internalEnd() const {
+ if(singleton()){
+ return expr::NodeSelfIterator::selfEnd(getNode());
+ }else{
+ return getNode().end();
+ }
+ }
+
+ bool singleton() const { return d_singleton; }
+
+public:
+ static bool isMember(TNode n);
+
+ class iterator {
+ private:
+ internal_iterator d_iter;
+
+ public:
+ /* The following types are required by trait std::iterator_traits */
+
+ /** Iterator tag */
+ using iterator_category = std::forward_iterator_tag;
+
+ /** The type of the item */
+ using value_type = Monomial;
+
+ /** The pointer type of the item */
+ using pointer = Monomial*;
+
+ /** The reference type of the item */
+ using reference = Monomial&;
+
+ /** The type returned when two iterators are subtracted */
+ using difference_type = std::ptrdiff_t;
+
+ /* End of std::iterator_traits required types */
+
+ explicit iterator(internal_iterator i) : d_iter(i) {}
+
+ inline Monomial operator*() {
+ return Monomial::parseMonomial(*d_iter);
+ }
+
+ bool operator==(const iterator& i) {
+ return d_iter == i.d_iter;
+ }
+
+ bool operator!=(const iterator& i) {
+ return d_iter != i.d_iter;
+ }
+
+ iterator operator++() {
+ ++d_iter;
+ return *this;
+ }
+
+ iterator operator++(int) {
+ return iterator(d_iter++);
+ }
+ };
+
+ iterator begin() const { return iterator(internalBegin()); }
+ iterator end() const { return iterator(internalEnd()); }
+
+ Polynomial(const Monomial& m):
+ NodeWrapper(m.getNode()), d_singleton(true)
+ {}
+
+ Polynomial(const std::vector<Monomial>& m):
+ NodeWrapper(makePlusNode(m)), d_singleton(false)
+ {
+ Assert(m.size() >= 2);
+ Assert(Monomial::isStrictlySorted(m));
+ }
+
+ static Polynomial mkPolynomial(const Constant& c){
+ return Polynomial(Monomial::mkMonomial(c));
+ }
+
+ static Polynomial mkPolynomial(const Variable& v){
+ return Polynomial(Monomial::mkMonomial(v));
+ }
+
+ static Polynomial mkPolynomial(const std::vector<Monomial>& m) {
+ if(m.size() == 0) {
+ return Polynomial(Monomial::mkZero());
+ } else if(m.size() == 1) {
+ return Polynomial((*m.begin()));
+ } else {
+ return Polynomial(m);
+ }
+ }
+
+ static Polynomial parsePolynomial(Node n) {
+ return Polynomial(n);
+ }
+
+ static Polynomial mkZero() {
+ return Polynomial(Monomial::mkZero());
+ }
+ static Polynomial mkOne() {
+ return Polynomial(Monomial::mkOne());
+ }
+ bool isZero() const {
+ return singleton() && (getHead().isZero());
+ }
+
+ bool isConstant() const {
+ return singleton() && (getHead().isConstant());
+ }
+
+ bool containsConstant() const {
+ return getHead().isConstant();
+ }
+
+ uint32_t size() const{
+ if(singleton()){
+ return 1;
+ }else{
+ Assert(getNode().getKind() == kind::ADD);
+ return getNode().getNumChildren();
+ }
+ }
+
+ Monomial getHead() const {
+ return *(begin());
+ }
+
+ Polynomial getTail() const {
+ Assert(!singleton());
+
+ iterator tailStart = begin();
+ ++tailStart;
+ std::vector<Monomial> subrange;
+ std::copy(tailStart, end(), std::back_inserter(subrange));
+ return mkPolynomial(subrange);
+ }
+
+ Monomial minimumVariableMonomial() const;
+ bool variableMonomialAreStrictlyGreater(const Monomial& m) const;
+
+ void printList() const {
+ if(TraceIsOn("normal-form")){
+ Trace("normal-form") << "start list" << std::endl;
+ for(iterator i = begin(), oend = end(); i != oend; ++i) {
+ const Monomial& m =*i;
+ m.print();
+ }
+ Trace("normal-form") << "end list" << std::endl;
+ }
+ }
+
+ /** A Polynomial is an "integral" polynomial if all of the monomials are integral. */
+ bool allIntegralVariables() const {
+ for(iterator i = begin(), e=end(); i!=e; ++i){
+ if(!(*i).integralVariables()){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * A Polynomial is an "integral" polynomial if all of the monomials are integral
+ * and all of the coefficients are Integral. */
+ bool isIntegral() const {
+ for(iterator i = begin(), e=end(); i!=e; ++i){
+ if(!(*i).isIntegral()){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static Polynomial sumPolynomials(const std::vector<Polynomial>& polynomials);
+
+ /** Returns true if the polynomial contains a non-linear monomial.*/
+ bool isNonlinear() const;
+
+ /** Check whether this polynomial is only a single variable. */
+ bool isVariable() const
+ {
+ return singleton() && getHead().getVarList().singleton()
+ && getHead().coefficientIsOne();
+ }
+ /** Return the variable, given that isVariable() holds. */
+ Variable getVariable() const
+ {
+ Assert(isVariable());
+ return getHead().getVarList().getHead();
+ }
+
+ /**
+ * Selects a minimal monomial in the polynomial by the absolute value of
+ * the coefficient.
+ */
+ Monomial selectAbsMinimum() const;
+
+ /** Returns true if the absolute value of the head coefficient is one. */
+ bool leadingCoefficientIsAbsOne() const;
+ bool leadingCoefficientIsPositive() const;
+ bool denominatorLCMIsOne() const;
+ bool numeratorGCDIsOne() const;
+
+ bool signNormalizedReducedSum() const {
+ return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne();
+ }
+
+ /**
+ * Returns the Least Common Multiple of the denominators of the coefficients
+ * of the monomials.
+ */
+ Integer denominatorLCM() const;
+
+ /**
+ * Returns the GCD of the numerators of the monomials.
+ * Requires this to be an isIntegral() polynomial.
+ */
+ Integer numeratorGCD() const;
+
+ /**
+ * Returns the GCD of the coefficients of the monomials.
+ * Requires this to be an isIntegral() polynomial.
+ */
+ Integer gcd() const;
+
+ /** z must divide all of the coefficients of the polynomial. */
+ Polynomial exactDivide(const Integer& z) const;
+
+ Polynomial operator+(const Polynomial& vl) const;
+ Polynomial operator-(const Polynomial& vl) const;
+ Polynomial operator-() const{
+ return (*this) * Rational(-1);
+ }
+
+ Polynomial operator*(const Rational& q) const;
+ Polynomial operator*(const Constant& c) const;
+ Polynomial operator*(const Monomial& mono) const;
+
+ Polynomial operator*(const Polynomial& poly) const;
+
+ /**
+ * Viewing the integer polynomial as a list [(* coeff_i mono_i)]
+ * The quotient and remainder of p divided by the non-zero integer z is:
+ * q := [(* floor(coeff_i/z) mono_i )]
+ * r := [(* rem(coeff_i/z) mono_i)]
+ * computeQR(p,z) returns the node (+ q r).
+ *
+ * q and r are members of the Polynomial class.
+ * For example:
+ * computeQR( p = (+ 5 (* 3 x) (* 8 y)) , z = 2) returns
+ * (+ (+ 2 x (* 4 y)) (+ 1 x))
+ */
+ static Node computeQR(const Polynomial& p, const Integer& z);
+
+ /** Returns the coefficient associated with the VarList in the polynomial. */
+ Constant getCoefficient(const VarList& vl) const;
+
+ uint32_t maxLength() const{
+ iterator i = begin(), e=end();
+ if( i == e){
+ return 1;
+ }else{
+ uint32_t max = (*i).coefficientLength();
+ ++i;
+ for(; i!=e; ++i){
+ uint32_t curr = (*i).coefficientLength();
+ if(curr > max){
+ max = curr;
+ }
+ }
+ return max;
+ }
+ }
+
+ uint32_t numMonomials() const {
+ if (getNode().getKind() == kind::ADD)
+ {
+ return getNode().getNumChildren();
+ }
+ else if (isZero())
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ const Rational& asConstant() const{
+ Assert(isConstant());
+ return getNode().getConst<Rational>();
+ //return getHead().getConstant().getValue();
+ }
+
+ bool isVarList() const {
+ if(singleton()){
+ return VarList::isMember(getNode());
+ }else{
+ return false;
+ }
+ }
+
+ VarList asVarList() const {
+ Assert(isVarList());
+ return getHead().getVarList();
+ }
+
+ size_t getComplexity() const;
+
+ friend class SumPair;
+ friend class Comparison;
+
+ /** Returns a node that if asserted ensures v is the abs of this polynomial.*/
+ Node makeAbsCondition(Variable v){
+ return makeAbsCondition(v, *this);
+ }
+
+ /** Returns a node that if asserted ensures v is the abs of p.*/
+ static Node makeAbsCondition(Variable v, Polynomial p);
+
+};/* class Polynomial */
+
+
+/**
+ * SumPair is a utility class that extends polynomials for use in computations.
+ * A SumPair is always a combination of (+ p c) where
+ * c is a constant and p is a polynomial such that p = 0 or !p.containsConstant().
+ *
+ * These are a useful utility for representing the equation p = c as (+ p -c) where the pair
+ * is known to implicitly be equal to 0.
+ *
+ * SumPairs do not have unique representations due to the potential for p = 0.
+ * This makes them inappropriate for normal forms.
+ */
+class SumPair : public NodeWrapper {
+private:
+ static Node toNode(const Polynomial& p, const Constant& c){
+ return NodeManager::currentNM()->mkNode(
+ kind::ADD, p.getNode(), c.getNode());
+ }
+
+ SumPair(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
+
+ public:
+ SumPair(const Polynomial& p):
+ NodeWrapper(toNode(p, Constant::mkConstant(0)))
+ {
+ Assert(isNormalForm());
+ }
+
+ SumPair(const Polynomial& p, const Constant& c):
+ NodeWrapper(toNode(p, c))
+ {
+ Assert(isNormalForm());
+ }
+
+ static bool isMember(TNode n) {
+ if (n.getKind() == kind::ADD && n.getNumChildren() == 2)
+ {
+ if(Constant::isMember(n[1])){
+ if(Polynomial::isMember(n[0])){
+ Polynomial p = Polynomial::parsePolynomial(n[0]);
+ return p.isZero() || (!p.containsConstant());
+ }else{
+ return false;
+ }
+ }else{
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool isNormalForm() const {
+ return isMember(getNode());
+ }
+
+ Polynomial getPolynomial() const {
+ return Polynomial::parsePolynomial(getNode()[0]);
+ }
+
+ Constant getConstant() const {
+ return Constant::mkConstant((getNode())[1]);
+ }
+
+ SumPair operator+(const SumPair& other) const {
+ return SumPair(getPolynomial() + other.getPolynomial(),
+ getConstant() + other.getConstant());
+ }
+
+ SumPair operator*(const Constant& c) const {
+ return SumPair(getPolynomial() * c, getConstant() * c);
+ }
+
+ SumPair operator-(const SumPair& other) const {
+ return (*this) + (other * Constant::mkConstant(-1));
+ }
+
+ static SumPair mkSumPair(const Polynomial& p);
+
+ static SumPair mkSumPair(const Variable& var){
+ return SumPair(Polynomial::mkPolynomial(var));
+ }
+
+ static SumPair parseSumPair(TNode n){
+ return SumPair(n);
+ }
+
+ bool isIntegral() const{
+ return getConstant().isIntegral() && getPolynomial().isIntegral();
+ }
+
+ bool isConstant() const {
+ return getPolynomial().isZero();
+ }
+
+ bool isZero() const {
+ return getConstant().isZero() && isConstant();
+ }
+
+ uint32_t size() const{
+ return getPolynomial().size();
+ }
+
+ bool isNonlinear() const{
+ return getPolynomial().isNonlinear();
+ }
+
+ /**
+ * Returns the greatest common divisor of gcd(getPolynomial()) and getConstant().
+ * The SumPair must be integral.
+ */
+ Integer gcd() const {
+ Assert(isIntegral());
+ return (getPolynomial().gcd()).gcd(getConstant().getValue().getNumerator());
+ }
+
+ uint32_t maxLength() const {
+ Assert(isIntegral());
+ return std::max(getPolynomial().maxLength(), getConstant().length());
+ }
+
+ static SumPair mkZero() {
+ return SumPair(Polynomial::mkZero(), Constant::mkConstant(0));
+ }
+
+ static Node computeQR(const SumPair& sp, const Integer& div);
+
+};/* class SumPair */
+
+/* class OrderedPolynomialPair { */
+/* private: */
+/* Polynomial d_first; */
+/* Polynomial d_second; */
+/* public: */
+/* OrderedPolynomialPair(const Polynomial& f, const Polynomial& s) */
+/* : d_first(f), */
+/* d_second(s) */
+/* {} */
+
+/* /\** Returns the first part of the pair. *\/ */
+/* const Polynomial& getFirst() const { */
+/* return d_first; */
+/* } */
+
+/* /\** Returns the second part of the pair. *\/ */
+/* const Polynomial& getSecond() const { */
+/* return d_second; */
+/* } */
+
+/* OrderedPolynomialPair operator*(const Constant& c) const; */
+/* OrderedPolynomialPair operator+(const Polynomial& p) const; */
+
+/* /\** Returns true if both of the polynomials are constant. *\/ */
+/* bool isConstant() const; */
+
+/* /\** */
+/* * Evaluates an isConstant() ordered pair as if */
+/* * (k getFirst() getRight()) */
+/* *\/ */
+/* bool evaluateConstant(Kind k) const; */
+
+/* /\** */
+/* * Returns the Least Common Multiple of the monomials */
+/* * on the lefthand side and the constant on the right. */
+/* *\/ */
+/* Integer denominatorLCM() const; */
+
+/* /\** Constructs a SumPair. *\/ */
+/* SumPair toSumPair() const; */
+
+
+/* OrderedPolynomialPair divideByGCD() const; */
+/* OrderedPolynomialPair multiplyConstant(const Constant& c) const; */
+
+/* /\** */
+/* * Returns true if all of the variables are integers, */
+/* * and the coefficients are integers. */
+/* *\/ */
+/* bool isIntegral() const; */
+
+/* /\** Returns true if all of the variables are integers. *\/ */
+/* bool allIntegralVariables() const { */
+/* return getFirst().allIntegralVariables() && getSecond().allIntegralVariables(); */
+/* } */
+/* }; */
+
+class Comparison : public NodeWrapper {
+private:
+
+ static Node toNode(Kind k, const Polynomial& l, const Constant& c);
+ static Node toNode(Kind k, const Polynomial& l, const Polynomial& r);
+
+ Comparison(TNode n);
+
+ /**
+ * Creates a node in normal form equivalent to (= l 0).
+ * All variables in l are integral.
+ */
+ static Node mkIntEquality(const Polynomial& l);
+
+ /**
+ * Creates a comparison equivalent to (k l 0).
+ * k is either GT or GEQ.
+ * All variables in l are integral.
+ */
+ static Node mkIntInequality(Kind k, const Polynomial& l);
+
+ /**
+ * Creates a node equivalent to (= l 0).
+ * It is not the case that all variables in l are integral.
+ */
+ static Node mkRatEquality(const Polynomial& l);
+
+ /**
+ * Creates a comparison equivalent to (k l 0).
+ * k is either GT or GEQ.
+ * It is not the case that all variables in l are integral.
+ */
+ static Node mkRatInequality(Kind k, const Polynomial& l);
+
+public:
+
+ Comparison(bool val) :
+ NodeWrapper(NodeManager::currentNM()->mkConst(val))
+ { }
+
+ /**
+ * Given a literal to TheoryArith return a single kind to
+ * to indicate its underlying structure.
+ * The function returns the following in each case:
+ * - (K left right) -> K where is either EQUAL, GT, or GEQ
+ * - (CONST_BOOLEAN b) -> CONST_BOOLEAN
+ * - (NOT (EQUAL left right)) -> DISTINCT
+ * - (NOT (GT left right)) -> LEQ
+ * - (NOT (GEQ left right)) -> LT
+ * If none of these match, it returns UNDEFINED_KIND.
+ */
+ static Kind comparisonKind(TNode literal);
+
+ Kind comparisonKind() const { return comparisonKind(getNode()); }
+
+ static Comparison mkComparison(Kind k, const Polynomial& l, const Polynomial& r);
+
+ /** Returns true if the comparison is a boolean constant. */
+ bool isBoolean() const;
+
+ /**
+ * Returns true if the comparison is either a boolean term,
+ * in integer normal form or mixed normal form.
+ */
+ bool isNormalForm() const;
+
+private:
+ bool isNormalGT() const;
+ bool isNormalGEQ() const;
+
+ bool isNormalLT() const;
+ bool isNormalLEQ() const;
+
+ bool isNormalEquality() const;
+ bool isNormalDistinct() const;
+ bool isNormalEqualityOrDisequality() const;
+
+ bool allIntegralVariables() const {
+ return getLeft().allIntegralVariables() && getRight().allIntegralVariables();
+ }
+ bool rightIsConstant() const;
+
+public:
+ Polynomial getLeft() const;
+ Polynomial getRight() const;
+
+ /* /\** Normal form check if at least one variable is real. *\/ */
+ /* bool isMixedCompareNormalForm() const; */
+
+ /* /\** Normal form check if at least one variable is real. *\/ */
+ /* bool isMixedEqualsNormalForm() const; */
+
+ /* /\** Normal form check is all variables are integer.*\/ */
+ /* bool isIntegerCompareNormalForm() const; */
+
+ /* /\** Normal form check is all variables are integer.*\/ */
+ /* bool isIntegerEqualsNormalForm() const; */
+
+
+ /**
+ * Returns true if all of the variables are integers, the coefficients are integers,
+ * and the right hand coefficient is an integer.
+ */
+ bool debugIsIntegral() const;
+
+ static Comparison parseNormalForm(TNode n);
+
+ inline static bool isNormalAtom(TNode n){
+ Comparison parse = Comparison::parseNormalForm(n);
+ return parse.isNormalForm();
+ }
+
+ size_t getComplexity() const;
+
+ SumPair toSumPair() const;
+
+ Polynomial normalizedVariablePart() const;
+ DeltaRational normalizedDeltaRational() const;
+
+ /**
+ * Transforms a Comparison object into a stronger normal form:
+ * Polynomial ~Kind~ Constant
+ *
+ * From the comparison, this method resolved a negation (if present) and
+ * moves everything to the left side.
+ * If split_constant is false, the constant is always zero.
+ * If split_constant is true, the polynomial has no constant term and is
+ * normalized to have leading coefficient one.
+ */
+ std::tuple<Polynomial, Kind, Constant> decompose(
+ bool split_constant = false) const;
+
+};/* class Comparison */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__NORMAL_FORM_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+ArithVariables::ArithVariables(context::Context* c,
+ DeltaComputeCallback deltaComputingFunc)
+ : d_vars(),
+ d_safeAssignment(),
+ d_numberOfVariables(0),
+ d_pool(),
+ d_released(),
+ d_nodeToArithVarMap(),
+ d_boundsQueue(),
+ d_enqueueingBoundCounts(true),
+ d_lbRevertHistory(c, true, LowerBoundCleanUp(this)),
+ d_ubRevertHistory(c, true, UpperBoundCleanUp(this)),
+ d_deltaIsSafe(false),
+ d_delta(-1, 1),
+ 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 >= ArithType::Integer;
+}
+
+/** 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),
+ d_lb(NullConstraint),
+ d_ub(NullConstraint),
+ d_cmpAssignmentLB(1),
+ d_cmpAssignmentUB(-1),
+ d_pushCount(0),
+ d_type(ArithType::Unset),
+ d_node(Node::null()),
+ d_auxiliary(false) {}
+
+bool ArithVariables::VarInfo::initialized() const {
+ return d_var != ARITHVAR_SENTINEL;
+}
+
+void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){
+ Assert(!initialized());
+ Assert(d_lb == NullConstraint);
+ Assert(d_ub == NullConstraint);
+ Assert(d_cmpAssignmentLB > 0);
+ Assert(d_cmpAssignmentUB < 0);
+ d_var = v;
+ d_node = n;
+ d_auxiliary = aux;
+
+ 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.
+ Polynomial p = Polynomial::parsePolynomial(n);
+ d_type = p.isIntegral() ? ArithType::Integer : ArithType::Real;
+ }else{
+ d_type = n.getType().isInteger() ? ArithType::Integer : ArithType::Real;
+ }
+
+ Assert(initialized());
+}
+
+void ArithVariables::VarInfo::uninitialize(){
+ d_var = ARITHVAR_SENTINEL;
+ d_node = Node::null();
+}
+
+bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){
+ Assert(initialized());
+ d_assignment = a;
+ int cmpUB = (d_ub == NullConstraint) ? -1 :
+ d_assignment.cmp(d_ub->getValue());
+
+ int cmpLB = (d_lb == NullConstraint) ? 1 :
+ d_assignment.cmp(d_lb->getValue());
+
+ bool lbChanged = cmpLB != d_cmpAssignmentLB &&
+ (cmpLB == 0 || d_cmpAssignmentLB == 0);
+ bool ubChanged = cmpUB != d_cmpAssignmentUB &&
+ (cmpUB == 0 || d_cmpAssignmentUB == 0);
+
+ if(lbChanged || ubChanged){
+ prev = boundsInfo();
+ }
+
+ d_cmpAssignmentUB = cmpUB;
+ d_cmpAssignmentLB = cmpLB;
+ return lbChanged || ubChanged;
+}
+
+void ArithVariables::releaseArithVar(ArithVar v){
+ VarInfo& vi = d_vars.get(v);
+
+ size_t removed CVC5_UNUSED = d_nodeToArithVarMap.erase(vi.d_node);
+ Assert(removed == 1);
+
+ vi.uninitialize();
+
+ if(d_safeAssignment.isKey(v)){
+ d_safeAssignment.remove(v);
+ }
+ if(vi.canBeReclaimed()){
+ d_pool.push_back(v);
+ }else{
+ d_released.push_back(v);
+ }
+}
+
+bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){
+ Assert(initialized());
+ bool wasNull = d_ub == NullConstraint;
+ bool isNull = ub == NullConstraint;
+
+ int cmpUB = isNull ? -1 : d_assignment.cmp(ub->getValue());
+ bool ubChanged = (wasNull != isNull) ||
+ (cmpUB != d_cmpAssignmentUB && (cmpUB == 0 || d_cmpAssignmentUB == 0));
+ if(ubChanged){
+ prev = boundsInfo();
+ }
+ d_ub = ub;
+ d_cmpAssignmentUB = cmpUB;
+ return ubChanged;
+}
+
+bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){
+ Assert(initialized());
+ bool wasNull = d_lb == NullConstraint;
+ bool isNull = lb == NullConstraint;
+
+ int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue());
+
+ bool lbChanged = (wasNull != isNull) ||
+ (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0));
+ if(lbChanged){
+ prev = boundsInfo();
+ }
+ d_lb = lb;
+ d_cmpAssignmentLB = cmpLB;
+ return lbChanged;
+}
+
+BoundCounts ArithVariables::VarInfo::atBoundCounts() const {
+ uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
+ uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
+ return BoundCounts(lbIndc, ubIndc);
+}
+
+BoundCounts ArithVariables::VarInfo::hasBoundCounts() const {
+ uint32_t lbIndc = (d_lb != NullConstraint) ? 1 : 0;
+ uint32_t ubIndc = (d_ub != NullConstraint) ? 1 : 0;
+ return BoundCounts(lbIndc, ubIndc);
+}
+
+BoundsInfo ArithVariables::VarInfo::boundsInfo() const{
+ return BoundsInfo(atBoundCounts(), hasBoundCounts());
+}
+
+bool ArithVariables::VarInfo::canBeReclaimed() const{
+ return d_pushCount == 0;
+}
+
+bool ArithVariables::canBeReleased(ArithVar v) const{
+ return d_vars[v].canBeReclaimed();
+}
+
+void ArithVariables::attemptToReclaimReleased(){
+ 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);
+ }else{
+ d_released[writePos] = v;
+ writePos++;
+ }
+ }
+ d_released.resize(writePos);
+}
+
+ArithVar ArithVariables::allocateVariable(){
+ if(d_pool.empty()){
+ attemptToReclaimReleased();
+ }
+ bool reclaim = !d_pool.empty();
+
+ ArithVar varX;
+ if(reclaim){
+ varX = d_pool.back();
+ d_pool.pop_back();
+ }else{
+ varX = d_numberOfVariables;
+ ++d_numberOfVariables;
+ }
+ d_vars.set(varX, VarInfo());
+ return varX;
+}
+
+
+const Rational& ArithVariables::getDelta(){
+ if(!d_deltaIsSafe){
+ Rational nextDelta = d_deltaComputingFunc();
+ setDelta(nextDelta);
+ }
+ Assert(d_deltaIsSafe);
+ return d_delta;
+}
+
+bool ArithVariables::boundsAreEqual(ArithVar x) const{
+ if(hasLowerBound(x) && hasUpperBound(x)){
+ return getUpperBound(x) == getLowerBound(x);
+ }else{
+ return false;
+ }
+}
+
+
+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){
+ Trace("partial_model") << "pm: updating the assignment to" << x
+ << " now " << r <<endl;
+ VarInfo& vi = d_vars.get(x);
+ if(!d_safeAssignment.isKey(x)){
+ d_safeAssignment.set(x, vi.d_assignment);
+ }
+ invalidateDelta();
+
+ BoundsInfo prev;
+ if(vi.setAssignment(r, prev)){
+ addToBoundQueue(x, prev);
+ }
+}
+
+void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
+ Trace("partial_model") << "pm: updating the assignment to" << x
+ << " now " << r <<endl;
+ if(safe == r){
+ if(d_safeAssignment.isKey(x)){
+ d_safeAssignment.remove(x);
+ }
+ }else{
+ d_safeAssignment.set(x, safe);
+ }
+
+ invalidateDelta();
+ VarInfo& vi = d_vars.get(x);
+ BoundsInfo prev;
+ if(vi.setAssignment(r, prev)){
+ addToBoundQueue(x, prev);
+ }
+}
+
+void ArithVariables::initialize(ArithVar x, Node n, bool aux){
+ VarInfo& vi = d_vars.get(x);
+ vi.initialize(x, n, aux);
+ d_nodeToArithVarMap[n] = x;
+}
+
+ArithVar ArithVariables::allocate(Node n, bool aux){
+ ArithVar v = allocateVariable();
+ initialize(v, n, aux);
+ return v;
+}
+
+// void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
+// Assert(x == d_mapSize);
+// Assert(equalSizes());
+// ++d_mapSize;
+
+// // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
+// // that when d_assignment is set this gets set.
+// invalidateDelta();
+// d_assignment.push_back( r );
+
+// d_boundRel.push_back(BetweenBounds);
+
+// d_ubc.push_back(NullConstraint);
+// d_lbc.push_back(NullConstraint);
+// }
+
+/** Must know that the bound exists both calling this! */
+const DeltaRational& ArithVariables::getUpperBound(ArithVar x) const {
+ Assert(inMaps(x));
+ Assert(hasUpperBound(x));
+
+ return getUpperBoundConstraint(x)->getValue();
+}
+
+const DeltaRational& ArithVariables::getLowerBound(ArithVar x) const {
+ Assert(inMaps(x));
+ Assert(hasLowerBound(x));
+
+ return getLowerBoundConstraint(x)->getValue();
+}
+
+const DeltaRational& ArithVariables::getSafeAssignment(ArithVar x) const{
+ Assert(inMaps(x));
+ if(d_safeAssignment.isKey(x)){
+ return d_safeAssignment[x];
+ }else{
+ return d_vars[x].d_assignment;
+ }
+}
+
+const DeltaRational& ArithVariables::getAssignment(ArithVar x, bool safe) const{
+ Assert(inMaps(x));
+ if(safe && d_safeAssignment.isKey(x)){
+ return d_safeAssignment[x];
+ }else{
+ return d_vars[x].d_assignment;
+ }
+}
+
+const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{
+ Assert(inMaps(x));
+ return d_vars[x].d_assignment;
+}
+
+
+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.");
+ ArithVar x = c->getVariable();
+ Trace("partial_model") << "setLowerBoundConstraint(" << x << ":" << c << ")" << endl;
+ Assert(inMaps(x));
+ Assert(greaterThanLowerBound(x, c->getValue()));
+
+ invalidateDelta();
+ VarInfo& vi = d_vars.get(x);
+ pushLowerBound(vi);
+ BoundsInfo prev;
+ if(vi.setLowerBound(c, prev)){
+ addToBoundQueue(x, prev);
+ }
+}
+
+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.");
+
+ ArithVar x = c->getVariable();
+ Trace("partial_model") << "setUpperBoundConstraint(" << x << ":" << c << ")" << endl;
+ Assert(inMaps(x));
+ Assert(lessThanUpperBound(x, c->getValue()));
+
+ invalidateDelta();
+ VarInfo& vi = d_vars.get(x);
+ pushUpperBound(vi);
+ BoundsInfo prev;
+ if(vi.setUpperBound(c, prev)){
+ addToBoundQueue(x, prev);
+ }
+}
+
+int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
+ if(!hasLowerBound(x)){
+ // l = -\intfy
+ // ? c < -\infty |- _|_
+ return 1;
+ }else{
+ return c.cmp(getLowerBound(x));
+ }
+}
+
+int ArithVariables::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
+ if(!hasUpperBound(x)){
+ //u = \intfy
+ // ? c > \infty |- _|_
+ return -1;
+ }else{
+ return c.cmp(getUpperBound(x));
+ }
+}
+
+bool ArithVariables::equalsLowerBound(ArithVar x, const DeltaRational& c){
+ if(!hasLowerBound(x)){
+ return false;
+ }else{
+ return c == getLowerBound(x);
+ }
+}
+bool ArithVariables::equalsUpperBound(ArithVar x, const DeltaRational& c){
+ if(!hasUpperBound(x)){
+ return false;
+ }else{
+ return c == getUpperBound(x);
+ }
+}
+
+bool ArithVariables::hasEitherBound(ArithVar x) const{
+ return hasLowerBound(x) || hasUpperBound(x);
+}
+
+bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentUB < 0;
+}
+
+bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentLB > 0;
+}
+
+bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
+ return
+ d_vars[x].d_cmpAssignmentLB >= 0 &&
+ d_vars[x].d_cmpAssignmentUB <= 0;
+}
+
+
+void ArithVariables::clearSafeAssignments(bool revert){
+
+ if(revert && !d_safeAssignment.empty()){
+ invalidateDelta();
+ }
+
+ while(!d_safeAssignment.empty()){
+ ArithVar atBack = d_safeAssignment.back();
+ if(revert){
+ VarInfo& vi = d_vars.get(atBack);
+ BoundsInfo prev;
+ if(vi.setAssignment(d_safeAssignment[atBack], prev)){
+ addToBoundQueue(atBack, prev);
+ }
+ }
+ d_safeAssignment.pop_back();
+ }
+}
+
+void ArithVariables::revertAssignmentChanges(){
+ clearSafeAssignments(true);
+}
+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){
+ printModel(*i, out);
+ }
+ out << "---Done Model ---" << std::endl;
+}
+
+void ArithVariables::printModel(ArithVar x, std::ostream& out) const{
+ out << "model" << x << ": "
+ << asNode(x) << " "
+ << getAssignment(x) << " ";
+ if(!hasLowerBound(x)){
+ out << "no lb ";
+ }else{
+ out << getLowerBound(x) << " ";
+ out << getLowerBoundConstraint(x) << " ";
+ }
+ if(!hasUpperBound(x)){
+ out << "no ub ";
+ }else{
+ out << getUpperBound(x) << " ";
+ out << getUpperBoundConstraint(x) << " ";
+ }
+
+ if(isInteger(x) && !integralAssignment(x)){
+ out << "(not an integer)" << endl;
+ }
+ out << endl;
+}
+
+void ArithVariables::printModel(ArithVar x) const{
+ printModel(x, Trace("model"));
+}
+
+void ArithVariables::pushUpperBound(VarInfo& vi){
+ ++vi.d_pushCount;
+ d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
+}
+void ArithVariables::pushLowerBound(VarInfo& vi){
+ ++vi.d_pushCount;
+ d_lbRevertHistory.push_back(make_pair(vi.d_var, vi.d_lb));
+}
+
+void ArithVariables::popUpperBound(AVCPair* c){
+ ArithVar x = c->first;
+ VarInfo& vi = d_vars.get(x);
+ BoundsInfo prev;
+ if(vi.setUpperBound(c->second, prev)){
+ addToBoundQueue(x, prev);
+ }
+ --vi.d_pushCount;
+}
+
+void ArithVariables::popLowerBound(AVCPair* c){
+ ArithVar x = c->first;
+ VarInfo& vi = d_vars.get(x);
+ BoundsInfo prev;
+ if(vi.setLowerBound(c->second, prev)){
+ addToBoundQueue(x, prev);
+ }
+ --vi.d_pushCount;
+}
+
+void ArithVariables::addToBoundQueue(ArithVar v, const BoundsInfo& prev){
+ if(d_enqueueingBoundCounts && !d_boundsQueue.isKey(v)){
+ d_boundsQueue.set(v, prev);
+ }
+}
+
+BoundsInfo ArithVariables::selectBoundsInfo(ArithVar v, bool old) const {
+ if(old && d_boundsQueue.isKey(v)){
+ return d_boundsQueue[v];
+ }else{
+ return boundsInfo(v);
+ }
+}
+
+bool ArithVariables::boundsQueueEmpty() const {
+ return d_boundsQueue.empty();
+}
+
+void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){
+ while(!boundsQueueEmpty()){
+ ArithVar v = d_boundsQueue.back();
+ BoundsInfo prev = d_boundsQueue[v];
+ d_boundsQueue.pop_back();
+ BoundsInfo curr = boundsInfo(v);
+ if(prev != curr){
+ changed(v, prev);
+ }
+ }
+}
+
+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);
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Morgan Deters, Aina Niemetz
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Datastructures that track variable by variable information.
+ *
+ * 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 "cvc5_private.h"
+
+#ifndef CVC5__THEORY__ARITH__PARTIAL_MODEL_H
+#define CVC5__THEORY__ARITH__PARTIAL_MODEL_H
+
+#include <vector>
+
+#include "context/cdlist.h"
+#include "expr/node.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/arithvar_node_map.h"
+#include "theory/arith/linear/bound_counts.h"
+#include "theory/arith/linear/callbacks.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+
+namespace cvc5::context {
+class Context;
+}
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/**
+ * (For the moment) the type hierarchy goes as:
+ * Integer <: Real
+ * The type number of a variable is an integer representing the most specific
+ * type of the variable. The possible values of type number are:
+ */
+enum class ArithType {
+ Unset,
+ Real,
+ Integer,
+};
+
+class ArithVariables {
+private:
+
+ class VarInfo {
+ friend class ArithVariables;
+ ArithVar d_var;
+
+ DeltaRational d_assignment;
+ ConstraintP d_lb;
+ ConstraintP d_ub;
+ int d_cmpAssignmentLB;
+ int d_cmpAssignmentUB;
+
+ unsigned d_pushCount;
+ ArithType d_type;
+ Node d_node;
+ bool d_auxiliary;
+
+ public:
+ VarInfo();
+
+ bool setAssignment(const DeltaRational& r, 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 an auxillary
+ * variable.
+ */
+ 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. */
+ BoundCounts atBoundCounts() const;
+
+ /** Combination of indicator variables for whether it has upper and
+ * lower bounds. */
+ BoundCounts hasBoundCounts() const;
+
+ /** Stores both atBoundCounts() and hasBoundCounts(). */
+ BoundsInfo boundsInfo() const;
+ };
+
+ /**Maps from ArithVar -> VarInfo */
+ typedef DenseMap<VarInfo> VarInfoVec;
+
+ /** This maps an ArithVar to its Variable information.*/
+ VarInfoVec d_vars;
+
+ /** Partial Map from Arithvar -> PreviousAssignment */
+ DenseMap<DeltaRational> d_safeAssignment;
+
+ /** if d_vars.isKey(x), then x < d_numberOfVariables */
+ ArithVar d_numberOfVariables;
+
+ /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */
+ // Everything in the pool is fair game.
+ // There must be NO outstanding assertions
+ std::vector<ArithVar> d_pool;
+ std::vector<ArithVar> d_released;
+ //std::list<ArithVar>::iterator d_releasedIterator;
+
+ // Reverse Map from Node to ArithVar
+ // Inverse of d_vars[x].d_node
+ NodeToArithVarMap d_nodeToArithVarMap;
+
+
+ /** The queue of constraints where the assignment is at the bound.*/
+ DenseMap<BoundsInfo> d_boundsQueue;
+
+ /**
+ * 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.
+ */
+ bool d_enqueueingBoundCounts;
+
+ public:
+
+ /** Returns the number of variables. */
+ ArithVar getNumberOfVariables() const;
+
+ /** Returns true if the node has an associated variables. */
+ bool hasArithVar(TNode x) const;
+
+ /** 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;
+
+ /** Returns the node associated with an ArithVar. */
+ Node asNode(ArithVar a) const;
+
+ /** Allocates a freshly allocated variables. */
+ ArithVar allocateVariable();
+
+ class var_iterator {
+ private:
+ const VarInfoVec* d_vars;
+ VarInfoVec::const_iterator d_wrapped;
+ public:
+ var_iterator();
+ var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci);
+ var_iterator& operator++();
+
+ bool operator==(const var_iterator& other) const;
+ bool operator!=(const var_iterator& other) const;
+ ArithVar operator*() const;
+
+ private:
+ void nextInitialized();
+ };
+
+ var_iterator var_begin() const;
+ var_iterator var_end() const;
+
+
+ bool canBeReleased(ArithVar v) const;
+ void releaseArithVar(ArithVar v);
+ void attemptToReclaimReleased();
+
+ /** Is this variable guaranteed to have an integer assignment?
+ * (Should agree with the type system.) */
+ bool isInteger(ArithVar x) const;
+
+ /** 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, ConstraintP> AVCPair;
+ class LowerBoundCleanUp {
+ private:
+ ArithVariables* d_pm;
+ public:
+ LowerBoundCleanUp(ArithVariables* pm);
+ void operator()(AVCPair* restore);
+ };
+
+ class UpperBoundCleanUp {
+ private:
+ ArithVariables* d_pm;
+ public:
+ UpperBoundCleanUp(ArithVariables* pm);
+ void operator()(AVCPair* restore);
+ };
+
+ typedef context::CDList<AVCPair, LowerBoundCleanUp> LBReverts;
+ LBReverts d_lbRevertHistory;
+
+ typedef context::CDList<AVCPair, UpperBoundCleanUp> UBReverts;
+ UBReverts d_ubRevertHistory;
+
+ void pushUpperBound(VarInfo&);
+ void popUpperBound(AVCPair*);
+ void pushLowerBound(VarInfo&);
+ void popLowerBound(AVCPair*);
+
+ // This is true when setDelta() is called, until invalidateDelta is called
+ bool d_deltaIsSafe;
+ // Cache of a value of delta to ensure a total order.
+ Rational d_delta;
+ // Function to call if the value of delta needs to be recomputed.
+ DeltaComputeCallback d_deltaComputingFunc;
+
+
+public:
+ ArithVariables(context::Context* c, DeltaComputeCallback deltaComputation);
+
+ /**
+ * This sets the lower bound for a variable in the current context.
+ * This must be stronger the previous constraint.
+ */
+ 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(ConstraintP ub);
+
+ /** Returns the constraint for the upper bound of a variable. */
+ inline ConstraintP getUpperBoundConstraint(ArithVar x) const
+ {
+ return d_vars[x].d_ub;
+ }
+ /** Returns the constraint for the lower bound of a variable. */
+ 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 aux);
+
+ 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;
+ const DeltaRational& getAssignment(ArithVar x, bool safe) const;
+
+ /* Reverts all variable assignments to their safe values. */
+ void revertAssignmentChanges();
+
+ /* Commits all variables assignments as safe.*/
+ void commitAssignmentChanges();
+
+
+ bool lowerBoundIsZero(ArithVar x);
+ bool upperBoundIsZero(ArithVar x);
+
+ bool boundsAreEqual(ArithVar x) const;
+
+ /* Sets an unsafe variable assignment */
+ void setAssignment(ArithVar x, const DeltaRational& r);
+ void setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r);
+
+
+ /** Must know that the bound exists before calling this! */
+ const DeltaRational& getUpperBound(ArithVar x) const;
+ const DeltaRational& getLowerBound(ArithVar x) const;
+ const DeltaRational& getAssignment(ArithVar x) const;
+
+
+ bool equalsLowerBound(ArithVar x, const DeltaRational& c);
+ bool equalsUpperBound(ArithVar x, const DeltaRational& c);
+
+ /**
+ * If lowerbound > - \infty:
+ * return getAssignment(x).cmp(getLowerBound(x))
+ * If lowerbound = - \infty:
+ * return 1
+ */
+ int cmpToLowerBound(ArithVar x, const DeltaRational& c) const;
+
+ inline bool strictlyLessThanLowerBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToLowerBound(x, c) < 0;
+ }
+ inline bool lessThanLowerBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToLowerBound(x, c) <= 0;
+ }
+
+ inline bool strictlyGreaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToLowerBound(x, c) > 0;
+ }
+
+ inline bool greaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToLowerBound(x, c) >= 0;
+ }
+ /**
+ * If upperbound < \infty:
+ * return getAssignment(x).cmp(getUpperBound(x))
+ * If upperbound = \infty:
+ * return -1
+ */
+ int cmpToUpperBound(ArithVar x, const DeltaRational& c) const;
+
+ inline bool strictlyLessThanUpperBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToUpperBound(x, c) < 0;
+ }
+
+ inline bool lessThanUpperBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToUpperBound(x, c) <= 0;
+ }
+
+ inline bool strictlyGreaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToUpperBound(x, c) > 0;
+ }
+
+ inline bool greaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
+ return cmpToUpperBound(x, c) >= 0;
+ }
+
+ inline int cmpAssignmentLowerBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentLB;
+ }
+ inline int cmpAssignmentUpperBound(ArithVar x) const{
+ return d_vars[x].d_cmpAssignmentUB;
+ }
+
+ inline BoundCounts atBoundCounts(ArithVar x) const {
+ return d_vars[x].atBoundCounts();
+ }
+ inline BoundCounts hasBoundCounts(ArithVar x) const {
+ return d_vars[x].hasBoundCounts();
+ }
+ inline BoundsInfo boundsInfo(ArithVar x) const{
+ return d_vars[x].boundsInfo();
+ }
+
+ bool strictlyBelowUpperBound(ArithVar x) const;
+ bool strictlyAboveLowerBound(ArithVar x) const;
+ bool assignmentIsConsistent(ArithVar x) const;
+
+ void printModel(ArithVar x, std::ostream& out) const;
+ void printModel(ArithVar x) const;
+
+ /** returns true iff x has both a lower and upper bound. */
+ bool hasEitherBound(ArithVar x) const;
+ inline bool hasLowerBound(ArithVar x) const{
+ return d_vars[x].d_lb != NullConstraint;
+ }
+ inline bool hasUpperBound(ArithVar x) const{
+ return d_vars[x].d_ub != NullConstraint;
+ }
+
+ const Rational& getDelta();
+
+ void invalidateDelta();
+
+ void setDelta(const Rational& d);
+
+ void startQueueingBoundCounts();
+ void stopQueueingBoundCounts();
+ void addToBoundQueue(ArithVar v, const BoundsInfo& prev);
+
+ BoundsInfo selectBoundsInfo(ArithVar v, bool old) const;
+
+ bool boundsQueueEmpty() const;
+ void processBoundsQueue(BoundUpdateCallback& changed);
+
+ 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:
+
+ /**
+ * This function implements the mostly identical:
+ * revertAssignmentChanges() and commitAssignmentChanges().
+ */
+ void clearSafeAssignments(bool revert);
+
+ bool debugEqualSizes();
+
+ bool inMaps(ArithVar x) const;
+
+};/* class ArithVariables */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+
+#endif /* CVC5__THEORY__ARITH__PARTIAL_MODEL_H */
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+
+#include "theory/arith/linear/simplex.h"
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "options/smt_options.h"
+#include "smt/env.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_value.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+SimplexDecisionProcedure::SimplexDecisionProcedure(
+ Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc)
+ : EnvObj(env),
+ d_pivots(0),
+ d_conflictVariables(),
+ d_linEq(linEq),
+ d_variables(d_linEq.getVariables()),
+ d_tableau(d_linEq.getTableau()),
+ d_errorSet(errors),
+ d_numVariables(0),
+ d_conflictChannel(conflictChannel),
+ d_conflictBuilder(NULL),
+ d_arithVarMalloc(tvmalloc),
+ d_errorSize(0),
+ d_zero(0),
+ d_posOne(1),
+ d_negOne(-1)
+{
+ d_heuristicRule = options().arith.arithErrorSelectionRule;
+ d_errorSet.setSelectionRule(d_heuristicRule);
+ d_conflictBuilder = new FarkasConflictBuilder(options().smt.produceProofs);
+}
+
+SimplexDecisionProcedure::~SimplexDecisionProcedure(){
+ delete d_conflictBuilder;
+}
+
+
+bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& conflicts) {
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert(d_conflictVariables.empty());
+
+ while(d_errorSet.moreSignals()){
+ ArithVar curr = d_errorSet.topSignal();
+ if(d_tableau.isBasic(curr) && !d_variables.assignmentIsConsistent(curr)){
+ Assert(d_linEq.basicIsTracked(curr));
+
+ if(!d_conflictVariables.isMember(curr) && checkBasicForConflict(curr)){
+
+ Trace("recentlyViolated")
+ << "It worked? "
+ << conflicts.get()
+ << " " << curr
+ << " " << checkBasicForConflict(curr) << endl;
+ reportConflict(curr);
+ ++conflicts;
+ }
+ }
+ // Pop signal afterwards in case d_linEq.trackVariable(curr);
+ // is needed for for the ErrorSet
+ d_errorSet.popSignal();
+ }
+ d_errorSize = d_errorSet.errorSize();
+
+ Assert(d_errorSet.noSignals());
+ return !d_conflictVariables.empty();
+}
+
+/** Reports a conflict to on the output channel. */
+void SimplexDecisionProcedure::reportConflict(ArithVar basic){
+ Assert(!d_conflictVariables.isMember(basic));
+ Assert(checkBasicForConflict(basic));
+
+ ConstraintCP conflicted = generateConflictForBasic(basic);
+ Assert(conflicted != NullConstraint);
+ d_conflictChannel.raiseConflict(conflicted, InferenceId::ARITH_CONF_SIMPLEX);
+
+ d_conflictVariables.add(basic);
+}
+
+ConstraintCP SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) 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, *d_conflictBuilder);
+ }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+ Assert(d_linEq.nonbasicsAtLowerBounds(basic));
+ return d_linEq.generateConflictAboveUpperBound(basic, *d_conflictBuilder);
+ }else{
+ Unreachable();
+ return NullConstraint;
+ }
+}
+bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const {
+ if(checkBasicForConflict(basic)){
+ ConstraintCP conflicted = generateConflictForBasic(basic);
+ d_conflictChannel.raiseConflict(conflicted, InferenceId::UNKNOWN);
+ return true;
+ }else{
+ return false;
+ }
+}
+
+bool SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic) const {
+ Assert(d_tableau.isBasic(basic));
+ Assert(d_linEq.basicIsTracked(basic));
+
+ if(d_variables.cmpAssignmentLowerBound(basic) < 0){
+ if(d_linEq.nonbasicsAtUpperBounds(basic)){
+ return true;
+ }
+ }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
+ if(d_linEq.nonbasicsAtLowerBounds(basic)){
+ return true;
+ }
+ }
+ return false;
+}
+
+void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, ArithVar tmp){
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert(tmp != ARITHVAR_SENTINEL);
+ Assert(d_tableau.isBasic(tmp));
+
+ RowIndex ri = d_tableau.basicToRowIndex(tmp);
+ d_linEq.stopTrackingRowIndex(ri);
+ d_tableau.removeBasicRow(tmp);
+ releaseVariable(tmp);
+}
+
+void SimplexDecisionProcedure::shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped){
+ TimerStat::CodeTimer codeTimer(timer);
+ for(ArithVarVec::const_iterator i=dropped.begin(), i_end = dropped.end(); i != i_end; ++i){
+ ArithVar back = *i;
+
+ int focusSgn = d_errorSet.focusSgn(back);
+ Rational chg(-focusSgn);
+
+ d_linEq.substitutePlusTimesConstant(inf, back, chg);
+ }
+}
+
+void SimplexDecisionProcedure::adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges){
+ TimerStat::CodeTimer codeTimer(timer);
+ for(AVIntPairVec::const_iterator i=focusChanges.begin(), i_end = focusChanges.end(); i != i_end; ++i){
+ ArithVar v = (*i).first;
+ int focusChange = (*i).second;
+
+ Rational chg(focusChange);
+ if(d_tableau.isBasic(v)){
+ d_linEq.substitutePlusTimesConstant(inf, v, chg);
+ }else{
+ d_linEq.directlyAddToCoefficient(inf, v, chg);
+ }
+ }
+}
+
+void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
+ AVIntPairVec justE;
+ int sgn = d_errorSet.getSgn(e);
+ justE.push_back(make_pair(e, sgn));
+ adjustInfeasFunc(timer, inf, justE);
+}
+
+void SimplexDecisionProcedure::removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
+ AVIntPairVec justE;
+ int opSgn = -d_errorSet.getSgn(e);
+ justE.push_back(make_pair(e, opSgn));
+ adjustInfeasFunc(timer, inf, justE);
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){
+ Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction start" << endl;
+
+ TimerStat::CodeTimer codeTimer(timer);
+ Assert(!d_errorSet.focusEmpty());
+ Assert(debugIsASet(set));
+
+ ArithVar inf = requestVariable();
+ Assert(inf != ARITHVAR_SENTINEL);
+
+ std::vector<Rational> coeffs;
+ std::vector<ArithVar> variables;
+
+ for(ArithVarVec::const_iterator iter = set.begin(), iend = set.end(); iter != iend; ++iter){
+ ArithVar e = *iter;
+
+ Assert(d_tableau.isBasic(e));
+ Assert(!d_variables.assignmentIsConsistent(e));
+
+ int sgn = d_errorSet.getSgn(e);
+ Assert(sgn == -1 || sgn == 1);
+ const Rational& violatedCoeff = sgn < 0 ? d_negOne : d_posOne;
+ coeffs.push_back(violatedCoeff);
+ variables.push_back(e);
+
+ Trace("constructInfeasiblityFunction") << violatedCoeff << " " << e << endl;
+
+ }
+ d_tableau.addRow(inf, coeffs, variables);
+ DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
+ d_variables.setAssignment(inf, newAssignment);
+
+ //d_linEq.trackVariable(inf);
+ d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf));
+
+ Trace("constructInfeasiblityFunction") << inf << " " << newAssignment << endl;
+ Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction done" << endl;
+ return inf;
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer){
+ ArithVarVec inError;
+ d_errorSet.pushFocusInto(inError);
+ return constructInfeasiblityFunction(timer, inError);
+}
+
+ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, ArithVar e){
+ ArithVarVec justE;
+ justE.push_back(e);
+ return constructInfeasiblityFunction(timer, justE);
+}
+
+void SimplexDecisionProcedure::addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ sgns[p].push_back(basic);
+}
+
+void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int norm){
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ int sgn = (entry.getCoefficient().sgn());
+ addSgn(sgns, v, norm * sgn, basic);
+ }
+}
+
+ArithVar SimplexDecisionProcedure::find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ sgn_table::const_iterator i = sgns.find(p);
+
+ if(i != sgns.end()){
+ const ArithVarVec& vec = (*i).second;
+ for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
+ ArithVar curr = *viter;
+ if(inside == m.isMember(curr)){
+ return curr;
+ }
+ }
+ }
+ return ARITHVAR_SENTINEL;
+}
+
+SimplexDecisionProcedure::sgn_table::const_iterator SimplexDecisionProcedure::find_sgns(const sgn_table& sgns, ArithVar col, int sgn){
+ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
+ return sgns.find(p);
+}
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ * by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ * These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <unordered_map>
+
+#include "options/arith_options.h"
+#include "smt/env_obj.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/partial_model.h"
+#include "util/dense_map.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class ErrorSet;
+class LinearEqualityModule;
+class Tableau;
+
+class SimplexDecisionProcedure : protected EnvObj
+{
+ protected:
+ typedef std::vector< std::pair<ArithVar, int> > AVIntPairVec;
+
+ /** Pivot count of the current round of pivoting. */
+ uint32_t d_pivots;
+
+ /** The set of variables that are in conflict in this round. */
+ DenseSet d_conflictVariables;
+
+ /** The rule to use for heuristic selection mode. */
+ options::ErrorSelectionRule d_heuristicRule;
+
+ /** Linear equality module. */
+ LinearEqualityModule& d_linEq;
+
+ /**
+ * Manages information about the assignment and upper and lower bounds on
+ * variables.
+ * Partial model matches that in LinearEqualityModule.
+ */
+ ArithVariables& d_variables;
+
+ /**
+ * Stores the linear equalities used by Simplex.
+ * Tableau from the LinearEquality module.
+ */
+ Tableau& d_tableau;
+
+ /** Contains a superset of the basic variables in violation of their bounds. */
+ ErrorSet& d_errorSet;
+
+ /** Number of variables in the system. This is used for tuning heuristics. */
+ ArithVar d_numVariables;
+
+ /** This is the call back channel for Simplex to report conflicts. */
+ RaiseConflict d_conflictChannel;
+
+ /** This is the call back channel for Simplex to report conflicts. */
+ FarkasConflictBuilder* d_conflictBuilder;
+
+ /** Used for requesting d_opt, bound and error variables for primal.*/
+ TempVarMalloc d_arithVarMalloc;
+
+ /** The size of the error set. */
+ uint32_t d_errorSize;
+
+ /** A local copy of 0. */
+ const Rational d_zero;
+
+ /** A local copy of 1. */
+ const Rational d_posOne;
+
+ /** A local copy of -1. */
+ const Rational d_negOne;
+
+ /**
+ * Locally cached value of arithStandardCheckVarOrderPivots option. It is
+ * cached here to allow for single runs with a different (lower) limit.
+ */
+ int64_t d_varOrderPivotLimit = -1;
+
+ ArithVar constructInfeasiblityFunction(TimerStat& timer);
+ ArithVar constructInfeasiblityFunction(TimerStat& timer, ArithVar e);
+ ArithVar constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set);
+
+ void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf);
+ void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges);
+ void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
+ void removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
+ void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped);
+
+public:
+ SimplexDecisionProcedure(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc);
+ virtual ~SimplexDecisionProcedure();
+
+ /**
+ * Tries to update the assignments of variables such that all of the
+ * assignments are consistent with their bounds.
+ * This is done by a simplex search through the possible bases of the tableau.
+ *
+ * If all of the variables can be made consistent with their bounds
+ * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
+ * was reported on the conflictCallback passed to the Module.
+ *
+ * Tableau pivoting is performed so variables may switch from being basic to
+ * nonbasic and vice versa.
+ *
+ * Corresponds to the "check()" procedure in [Cav06].
+ */
+ virtual Result::Status findModel(bool exactResult) = 0;
+
+ void increaseMax() { d_numVariables++; }
+
+ uint32_t getPivots() const { return d_pivots; }
+
+ /** Set the variable ordering pivot limit */
+ void setVarOrderPivotLimit(int64_t value) { d_varOrderPivotLimit = value; }
+
+protected:
+ /** Reports a conflict to on the output channel. */
+ void reportConflict(ArithVar basic);
+
+ /**
+ * Checks a basic variable, b, to see if it is in conflict.
+ * If a conflict is discovered a node summarizing the conflict is returned.
+ * Otherwise, Node::null() is returned.
+ */
+ 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 on the conflict channel.
+ */
+ ConstraintCP generateConflictForBasic(ArithVar basic) const;
+
+ /** Gets a fresh variable from TheoryArith. */
+ ArithVar requestVariable() { return d_arithVarMalloc.request(); }
+
+ /** Releases a requested variable from TheoryArith.*/
+ void releaseVariable(ArithVar v) { d_arithVarMalloc.release(v); }
+
+ /** Post condition: !d_queue.moreSignals() */
+ bool standardProcessSignals(TimerStat& timer, IntStat& conflictStat);
+
+ struct ArithVarIntPairHashFunc
+ {
+ size_t operator()(const std::pair<ArithVar, int>& p) const
+ {
+ size_t h1 = std::hash<ArithVar>()(p.first);
+ size_t h2 = std::hash<int>()(p.second);
+ return h1 + 3389 * h2;
+ }
+ };
+
+ typedef std::unordered_map< std::pair<ArithVar, int>, ArithVarVec, ArithVarIntPairHashFunc> sgn_table;
+
+ static inline int determinizeSgn(int sgn){
+ return sgn < 0 ? -1 : (sgn == 0 ? 0 : 1);
+ }
+
+ void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
+ void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
+ ArithVar find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside);
+
+ sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn);
+
+}; /* class SimplexDecisionProcedure */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andres Noetzli, Mathias Preiner
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This implements the UpdateInfo.
+ */
+
+#include "theory/arith/linear/simplex_update.h"
+
+#include "theory/arith/linear/constraint.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/*
+ * Generates a string representation of std::optional and inserts it into a
+ * stream.
+ *
+ * Note: We define this function here in the cvc5::internal::theory::arith namespace,
+ * because it would otherwise not be found for std::optional<int>. This is due
+ * to the argument-dependent lookup rules.
+ *
+ * @param out The stream
+ * @param m The value
+ * @return The stream
+ */
+std::ostream& operator<<(std::ostream& out, const std::optional<int>& m)
+{
+ return cvc5::internal::operator<<(out, m);
+}
+
+UpdateInfo::UpdateInfo():
+ d_nonbasic(ARITHVAR_SENTINEL),
+ d_nonbasicDirection(0),
+ d_nonbasicDelta(),
+ d_foundConflict(false),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(),
+ d_limiting(NullConstraint),
+ d_witness(AntiProductive)
+{}
+
+UpdateInfo::UpdateInfo(ArithVar nb, int dir):
+ d_nonbasic(nb),
+ d_nonbasicDirection(dir),
+ d_nonbasicDelta(),
+ d_foundConflict(false),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(),
+ d_limiting(NullConstraint),
+ d_witness(AntiProductive)
+{
+ Assert(dir == 1 || dir == -1);
+}
+
+UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c):
+ d_nonbasic(nb),
+ d_nonbasicDirection(delta.sgn()),
+ d_nonbasicDelta(delta),
+ d_foundConflict(true),
+ d_errorsChange(),
+ d_focusDirection(),
+ d_tableauCoefficient(&r),
+ d_limiting(c),
+ d_witness(ConflictFound)
+{
+ Assert(conflict);
+}
+
+UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){
+ return UpdateInfo(true, nb, delta, r, lim);
+}
+
+void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){
+ d_limiting = NullConstraint;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = f;
+ d_tableauCoefficient.reset();
+ updateWitness();
+ Assert(unbounded());
+ Assert(improvement(d_witness));
+ Assert(!describesPivot());
+ Assert(debugSgnAgreement());
+}
+void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange.reset();
+ d_focusDirection = 1;
+ d_tableauCoefficient.reset();
+ updateWitness();
+ Assert(!describesPivot());
+ Assert(improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange.reset();
+ d_focusDirection.reset();
+ updateWitness();
+ Assert(describesPivot());
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection.reset();
+ d_tableauCoefficient = &r;
+ updateWitness();
+ Assert(describesPivot());
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = fd;
+ d_tableauCoefficient.reset();
+ updateWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){
+ d_limiting = c;
+ d_nonbasicDelta = delta;
+ d_errorsChange = ec;
+ d_focusDirection = fd;
+ d_tableauCoefficient = &r;
+ updateWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ Assert(debugSgnAgreement());
+}
+
+bool UpdateInfo::describesPivot() const {
+ return !unbounded() && d_nonbasic != d_limiting->getVariable();
+}
+
+void UpdateInfo::output(std::ostream& out) const{
+ out << "{UpdateInfo"
+ << ", nb = " << d_nonbasic
+ << ", dir = " << d_nonbasicDirection
+ << ", delta = " << d_nonbasicDelta
+ << ", conflict = " << d_foundConflict
+ << ", errorChange = " << d_errorsChange
+ << ", focusDir = " << d_focusDirection
+ << ", witness = " << d_witness
+ << ", limiting = " << d_limiting
+ << "}";
+}
+
+ArithVar UpdateInfo::leaving() const{
+ Assert(describesPivot());
+
+ return d_limiting->getVariable();
+}
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up){
+ up.output(out);
+ return out;
+}
+
+
+std::ostream& operator<<(std::ostream& out, WitnessImprovement w){
+ switch(w){
+ case ConflictFound:
+ out << "ConflictFound"; break;
+ case ErrorDropped:
+ out << "ErrorDropped"; break;
+ case FocusImproved:
+ out << "FocusImproved"; break;
+ case FocusShrank:
+ out << "FocusShrank"; break;
+ case Degenerate:
+ out << "Degenerate"; break;
+ case BlandsDegenerate:
+ out << "BlandsDegenerate"; break;
+ case HeuristicDegenerate:
+ out << "HeuristicDegenerate"; break;
+ case AntiProductive:
+ out << "AntiProductive"; break;
+ }
+ return out;
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andres Noetzli, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This provides a class for summarizing pivot proposals.
+ *
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This maintains the relationship needed by the SimplexDecisionProcedure.
+ *
+ * In the language of Simplex for DPLL(T), this provides:
+ * - update()
+ * - pivotAndUpdate()
+ *
+ * This class also provides utility functions that require
+ * using both the Tableau and PartialModel.
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <optional>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/constraint_forward.h"
+#include "theory/arith/delta_rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+enum WitnessImprovement {
+ ConflictFound = 0,
+ ErrorDropped = 1,
+ FocusImproved = 2,
+ FocusShrank = 3,
+ Degenerate = 4,
+ BlandsDegenerate = 5,
+ HeuristicDegenerate = 6,
+ AntiProductive = 7
+};
+
+inline bool strongImprovement(WitnessImprovement w){
+ return w <= FocusImproved;
+}
+
+inline bool improvement(WitnessImprovement w){
+ return w <= FocusShrank;
+}
+
+inline bool degenerate(WitnessImprovement w){
+ switch(w){
+ case Degenerate:
+ case BlandsDegenerate:
+ case HeuristicDegenerate:
+ return true;
+ default:
+ return false;
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, WitnessImprovement w);
+
+/**
+ * This class summarizes both potential:
+ * - pivot-and-update operations or
+ * - a pure update operation.
+ * This stores enough information for the various algorithms hat consider these operations.
+ * These require slightly different pieces of information at different points
+ * so they are a bit verbose and paranoid.
+ */
+class UpdateInfo {
+private:
+
+ /**
+ * The nonbasic variables under consideration.
+ * This is either the entering variable on a pivot and update
+ * or the variable being updated.
+ * This can only be set in the constructor or assignment.
+ *
+ * If this uninitialized, then this is ARITHVAR_SENTINEL.
+ */
+ ArithVar d_nonbasic;
+
+ /**
+ * The sgn of the "intended" derivative (delta) of the update to d_nonbasic.
+ * This is either 1, -1, or 0.
+ * It is "intended" as the delta is always allowed to be 0.
+ * (See debugSgnAgreement().)
+ *
+ * If this uninitialized, then this is 0.
+ * If this is initialized, then it is -1 or 1.
+ *
+ * This can only be set in the constructor or assignment.
+ */
+ int d_nonbasicDirection;
+
+ /**
+ * The change in the assignment of d_nonbasic.
+ * This is changed via the updateProposal(...) methods.
+ * The value needs to satisfy debugSgnAgreement() or it is in conflict.
+ */
+ std::optional<DeltaRational> d_nonbasicDelta;
+
+ /**
+ * This is true if the pivot-and-update is *known* to cause a conflict.
+ * This can only be true if it was constructed through the static conflict(...) method.
+ */
+ bool d_foundConflict;
+
+ /** This is the change in the size of the error set. */
+ std::optional<int> d_errorsChange;
+
+ /** This is the sgn of the change in the value of the focus set.*/
+ std::optional<int> d_focusDirection;
+
+ /** This is the sgn of the change in the value of the focus set.*/
+ std::optional<DeltaRational> d_focusChange;
+
+ /** This is the coefficient in the tableau for the entry.*/
+ std::optional<const Rational*> d_tableauCoefficient;
+
+ /**
+ * This is the constraint that nonbasic is basic is updating s.t. its variable is against it.
+ * This has 3 different possibilities:
+ * - Unbounded : then this is NullConstraint and unbounded() is true.
+ * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic.
+ * - Update: then this is not NullConstraint and the variable is d_nonbasic.
+ */
+ ConstraintP d_limiting;
+
+ WitnessImprovement d_witness;
+
+ /**
+ * This returns true if
+ * d_nonbasicDelta is zero() or its sgn() must agree with d_nonbasicDirection.
+ */
+ bool debugSgnAgreement() const {
+ int deltaSgn = d_nonbasicDelta.value().sgn();
+ return deltaSgn == 0 || deltaSgn == d_nonbasicDirection;
+ }
+
+ /** This private constructor allows for setting conflict to true. */
+ UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
+
+public:
+
+ /** This constructs an uninitialized UpdateInfo. */
+ UpdateInfo();
+
+ /**
+ * This constructs an initialized UpdateInfo.
+ * dir must be 1 or -1.
+ */
+ UpdateInfo(ArithVar nb, int dir);
+
+ /**
+ * This updates the nonBasicDelta to d and limiting to NullConstraint.
+ * This describes an unbounded() update.
+ */
+ void updateUnbounded(const DeltaRational& d, int ec, int f);
+
+
+ void updatePureFocus(const DeltaRational& d, ConstraintP c);
+ //void updatePureError(const DeltaRational& d, Constraint c, int e);
+ //void updatePure(const DeltaRational& d, Constraint c, int e, int f);
+
+ /**
+ * This updates the nonBasicDelta to d and limiting to c.
+ * This clears errorChange() and focusDir().
+ */
+ void updatePivot(const DeltaRational& d, const Rational& r, 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, 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, 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, ConstraintP lim);
+
+ inline ArithVar nonbasic() const { return d_nonbasic; }
+ inline bool uninitialized() const {
+ return d_nonbasic == ARITHVAR_SENTINEL;
+ }
+
+ /**
+ * There is no limiting value to the improvement of the focus.
+ * If this is true, this never describes an update.
+ */
+ inline bool unbounded() const {
+ return d_limiting == NullConstraint;
+ }
+
+ /**
+ * The update either describes a pivotAndUpdate operation
+ * or it describes just an update.
+ */
+ bool describesPivot() const;
+
+ /** Returns the . describesPivot() must be true. */
+ ArithVar leaving() const;
+
+ /**
+ * Returns true if this is *known* to find a conflict.
+ * If true, this must have been made through the static conflict(...) function.
+ */
+ bool foundConflict() const { return d_foundConflict; }
+
+ /** Returns the direction nonbasic is supposed to move. */
+ inline int nonbasicDirection() const{ return d_nonbasicDirection; }
+
+ /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+ inline int errorsChange() const { return d_errorsChange.value(); }
+
+ /**
+ * If errorsChange has been set, return errorsChange().
+ * Otherwise, return def.
+ */
+ inline int errorsChangeSafe(int def) const {
+ if (d_errorsChange)
+ {
+ return d_errorsChange.value();
+ }
+ else
+ {
+ return def;
+ }
+ }
+
+ /** Sets the errorChange. */
+ void setErrorsChange(int ec){
+ d_errorsChange = ec;
+ updateWitness();
+ }
+
+
+ /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
+ inline int focusDirection() const { return d_focusDirection.value(); }
+
+ /** Sets the focusDirection. */
+ void setFocusDirection(int fd){
+ Assert(-1 <= fd && fd <= 1);
+ d_focusDirection = fd;
+ updateWitness();
+ }
+
+ /**
+ * nonbasicDirection must be the same as the sign for the focus function's
+ * coefficient for this to be safe.
+ * The burden for this being safe is on the user!
+ */
+ void determineFocusDirection(){
+ const int deltaSgn = d_nonbasicDelta.value().sgn();
+ setFocusDirection(deltaSgn * d_nonbasicDirection);
+ }
+
+ /** Requires nonbasicDelta to be set through updateProposal(...). */
+ const DeltaRational& nonbasicDelta() const { return d_nonbasicDelta.value(); }
+ const Rational& getCoefficient() const {
+ Assert(describesPivot());
+ Assert(d_tableauCoefficient.value() != NULL);
+ return *(d_tableauCoefficient.value());
+ }
+ int basicDirection() const {
+ return nonbasicDirection() * (getCoefficient().sgn());
+ }
+
+ /** Returns the limiting constraint. */
+ inline ConstraintP limiting() const {
+ return d_limiting;
+ }
+
+ WitnessImprovement getWitness(bool useBlands = false) const{
+ Assert(d_witness == computeWitness());
+
+ if(d_witness == Degenerate){
+ if(useBlands){
+ return BlandsDegenerate;
+ }else{
+ return HeuristicDegenerate;
+ }
+ }else{
+ return d_witness;
+ }
+ }
+
+ const DeltaRational& focusChange() const { return d_focusChange.value(); }
+ void setFocusChange(const DeltaRational& fc) {
+ d_focusChange = fc;
+ }
+
+ /** Outputs the UpdateInfo into out. */
+ void output(std::ostream& out) const;
+
+private:
+ void updateWitness() {
+ d_witness = computeWitness();
+ Assert(describesPivot() || improvement(d_witness));
+ }
+
+ /**
+ * Determines the appropriate WitnessImprovement for the update.
+ * useBlands breaks ties for degenerate pivots.
+ *
+ * This is safe if:
+ * - d_foundConflict is true, or
+ * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange < 0, or
+ * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange >= 0 and d_focusDirection has been set.
+ */
+ WitnessImprovement computeWitness() const {
+ if(d_foundConflict){
+ return ConflictFound;
+ }
+ else if (d_errorsChange && d_errorsChange.value() < 0)
+ {
+ return ErrorDropped;
+ }
+ else if (d_errorsChange.value_or(0) == 0)
+ {
+ if (d_focusDirection)
+ {
+ if (*d_focusDirection > 0)
+ {
+ return FocusImproved;
+ }
+ else if (*d_focusDirection == 0)
+ {
+ return Degenerate;
+ }
+ }
+ }
+ return AntiProductive;
+ }
+
+};
+
+std::ostream& operator<<(std::ostream& out, const UpdateInfo& up);
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ */
+#include "theory/arith/linear/soi_simplex.h"
+
+#include <algorithm>
+
+#include "base/output.h"
+#include "options/arith_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/tableau.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
+ d_soiVar(ARITHVAR_SENTINEL),
+ d_pivotBudget(0),
+ d_prevWitnessImprovement(AntiProductive),
+ d_witnessImprovementInARow(0),
+ d_sgnDisagreements(),
+ d_statistics("theory::arith::SOI", d_pivots)
+{ }
+
+SumOfInfeasibilitiesSPD::Statistics::Statistics(const std::string& name,
+ uint32_t& pivots)
+ : d_initialSignalsTime(
+ smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
+ d_initialConflicts(
+ smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
+ d_soiFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
+ d_soiFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
+ d_soiMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
+ d_soiConflicts(
+ smtStatisticsRegistry().registerInt(name + "ConfMin::num")),
+ d_hasToBeMinimal(
+ smtStatisticsRegistry().registerInt(name + "HasToBeMin")),
+ d_maybeNotMinimal(
+ smtStatisticsRegistry().registerInt(name + "MaybeNotMin")),
+ d_soiTimer(smtStatisticsRegistry().registerTimer(name + "Time")),
+ d_soiFocusConstructionTimer(
+ smtStatisticsRegistry().registerTimer(name + "Construction")),
+ d_soiConflictMinimization(smtStatisticsRegistry().registerTimer(
+ name + "Conflict::Minimization")),
+ d_selectUpdateForSOI(
+ smtStatisticsRegistry().registerTimer(name + "selectSOI")),
+ d_finalCheckPivotCounter(
+ smtStatisticsRegistry().registerReference<uint32_t>(
+ name + "lastPivots", pivots))
+{
+}
+
+Result::Status SumOfInfeasibilitiesSPD::findModel(bool exactResult)
+{
+ Assert(d_conflictVariables.empty());
+ Assert(d_sgnDisagreements.empty());
+
+ d_pivots = 0;
+
+ if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
+ Trace("soi::findModel") << "soiFindModel() trivial" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ // We need to reduce this because of
+ d_errorSet.reduceToSignals();
+
+ // We must start tracking NOW
+ d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
+
+ if(initialProcessSignals()){
+ d_conflictVariables.purge();
+ Trace("soi::findModel") << "fcFindModel() early conflict" << endl;
+ Assert(d_conflictVariables.empty());
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Trace("soi::findModel") << "fcFindModel() fixed itself" << endl;
+ Assert(!d_errorSet.moreSignals());
+ Assert(d_conflictVariables.empty());
+ return Result::SAT;
+ }
+
+ Trace("soi::findModel") << "fcFindModel() start non-trivial" << endl;
+
+ exactResult |= d_varOrderPivotLimit < 0;
+
+ d_prevWitnessImprovement = HeuristicDegenerate;
+ d_witnessImprovementInARow = 0;
+
+ Result::Status result = Result::UNKNOWN;
+
+ if (result == Result::UNKNOWN)
+ {
+ if(exactResult){
+ d_pivotBudget = -1;
+ }else{
+ d_pivotBudget = d_varOrderPivotLimit;
+ }
+
+ result = sumOfInfeasibilities();
+
+ if(result == Result::UNSAT){
+ ++(d_statistics.d_soiFoundUnsat);
+ }else if(d_errorSet.errorEmpty()){
+ ++(d_statistics.d_soiFoundSat);
+ }else{
+ ++(d_statistics.d_soiMissed);
+ }
+ }
+
+ Assert(!d_errorSet.moreSignals());
+ if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
+ {
+ result = Result::SAT;
+ }
+
+ // ensure that the conflict variable is still in the queue.
+ d_conflictVariables.purge();
+
+ Trace("soi::findModel") << "end findModel() " << result << endl;
+
+ Assert(d_conflictVariables.empty());
+ return result;
+}
+
+
+void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
+ if(d_pivotBudget > 0) {
+ --d_pivotBudget;
+ }
+ Assert(w != AntiProductive);
+
+ if(w == d_prevWitnessImprovement){
+ ++d_witnessImprovementInARow;
+ if(d_witnessImprovementInARow == 0){
+ --d_witnessImprovementInARow;
+ }
+ }else{
+ if(w != BlandsDegenerate){
+ d_witnessImprovementInARow = 1;
+ }
+ d_prevWitnessImprovement = w;
+ }
+ if(strongImprovement(w)){
+ d_leavingCountSinceImprovement.purge();
+ }
+
+ Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
+}
+
+uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
+ switch(d_prevWitnessImprovement){
+ case ConflictFound:
+ case ErrorDropped:
+ case FocusImproved:
+ return 0;
+ case HeuristicDegenerate:
+ case BlandsDegenerate:
+ return d_witnessImprovementInARow;
+ // Degenerate is unreachable for its own reasons
+ case Degenerate:
+ case FocusShrank:
+ case AntiProductive:
+ Unreachable();
+ return -1;
+ }
+ Unreachable();
+}
+
+void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
+ uint32_t newErrorSize = d_errorSet.errorSize();
+ adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
+ d_errorSize = newErrorSize;
+}
+
+
+UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
+ UpdateInfo selected;
+
+ Trace("soi::selectPrimalUpdate")
+ << "selectPrimalUpdate " << endl
+ << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar) << " "
+ << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
+
+ typedef std::vector<Cand> CandVector;
+ CandVector candidates;
+
+ for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
+ const Tableau::Entry& e = *ri;
+ ArithVar curr = e.getColVar();
+ if(curr == d_soiVar){ continue; }
+
+ int sgn = e.getCoefficient().sgn();
+ bool candidate =
+ (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
+ (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
+
+ Trace("soi::selectPrimalUpdate")
+ << "storing " << d_soiVar
+ << " " << curr
+ << " " << candidate
+ << " " << e.getCoefficient()
+ << " " << sgn << endl;
+
+ if(candidate) {
+ candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
+ }
+ }
+
+ CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
+ CandVector::iterator i = candidates.begin();
+ CandVector::iterator end = candidates.end();
+ std::make_heap(i, end, colCmp);
+
+ // For the first 3 pivots take the best
+ // After that, once an improvement is found on look at a
+ // small number of pivots after finding an improvement
+ // the longer the search to more willing we are to look at more candidates
+ int maxCandidatesAfterImprove =
+ (d_pivots <= 2) ? std::numeric_limits<int>::max() : d_pivots/5;
+
+ int candidatesAfterFocusImprove = 0;
+ while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
+ std::pop_heap(i, end, colCmp);
+ --end;
+ Cand& cand = (*end);
+ ArithVar curr = cand.d_nb;
+ const Rational& coeff = *cand.d_coeff;
+
+ LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
+ UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
+
+ Trace("soi::selectPrimalUpdate")
+ << "selected " << selected << endl
+ << "currProp " << currProposal << endl
+ << "coeff " << coeff << endl;
+
+ Assert(!currProposal.uninitialized());
+
+ if(candidatesAfterFocusImprove > 0){
+ candidatesAfterFocusImprove++;
+ }
+
+ if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
+ selected = currProposal;
+ WitnessImprovement w = selected.getWitness(false);
+ Trace("soi::selectPrimalUpdate") << "selected " << w << endl;
+ //setPenalty(curr, w);
+ if(improvement(w)){
+ bool exitEarly;
+ switch(w){
+ case ConflictFound: exitEarly = true; break;
+ case FocusImproved:
+ candidatesAfterFocusImprove = 1;
+ exitEarly = false;
+ break;
+ default:
+ exitEarly = false; break;
+ }
+ if(exitEarly){ break; }
+ }
+ }else{
+ Trace("soi::selectPrimalUpdate") << "dropped "<< endl;
+ }
+
+ }
+ return selected;
+}
+
+bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
+ if(inf.getWitness(useBlands) == w){
+ switch(w){
+ case ConflictFound: return inf.foundConflict();
+ case ErrorDropped: return inf.errorsChange() < 0;
+ case FocusImproved: return inf.focusDirection() > 0;
+ case FocusShrank: return false; // This is not a valid output
+ case Degenerate: return false; // This is not a valid output
+ case BlandsDegenerate: return useBlands;
+ case HeuristicDegenerate: return !useBlands;
+ case AntiProductive: return false;
+ }
+ }
+ return false;
+}
+
+
+void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
+ Trace("updateAndSignal") << "updated basic " << updated;
+ Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
+ Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
+ int dir = !d_variables.assignmentIsConsistent(updated) ?
+ d_errorSet.getSgn(updated) : 0;
+ Trace("updateAndSignal") << " dir " << dir;
+ Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
+}
+
+
+void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
+ ArithVar nonbasic = selected.nonbasic();
+
+ Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
+
+ if(selected.describesPivot()){
+ ConstraintP limiting = selected.limiting();
+ ArithVar basic = limiting->getVariable();
+ Assert(d_linEq.basicIsTracked(basic));
+ d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
+ }else{
+ Assert(!selected.unbounded() || selected.errorsChange() < 0);
+
+ DeltaRational newAssignment =
+ d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
+
+ d_linEq.updateTracked(nonbasic, newAssignment);
+ }
+ d_pivots++;
+
+ increaseLeavingCount(nonbasic);
+
+ vector< pair<ArithVar, int> > focusChanges;
+ while(d_errorSet.moreSignals()){
+ ArithVar updated = d_errorSet.topSignal();
+ int prevFocusSgn = d_errorSet.popSignal();
+
+ if(d_tableau.isBasic(updated)){
+ Assert(!d_variables.assignmentIsConsistent(updated)
+ == d_errorSet.inError(updated));
+ if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
+ if(!d_variables.assignmentIsConsistent(updated)){
+ if(checkBasicForConflict(updated)){
+ reportConflict(updated);
+ //Assert(debugUpdatedBasic(selected, updated));
+ }
+ }
+ }else{
+ Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
+ }
+ int currFocusSgn = d_errorSet.focusSgn(updated);
+ if(currFocusSgn != prevFocusSgn){
+ int change = currFocusSgn - prevFocusSgn;
+ focusChanges.push_back(make_pair(updated, change));
+ }
+ }
+
+ if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
+
+ //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
+
+ adjustFocusAndError(selected, focusChanges);
+}
+
+void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){
+ Assert(!d_qeInSoi.empty());
+ for(uint32_t i = begin; i != end; ++i){
+ ArithVar v = d_qeConflict[i];
+ addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
+ d_qeInSoi.add(v);
+ }
+}
+
+void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){
+ for(uint32_t i = begin; i != end; ++i){
+ ArithVar v = d_qeConflict[i];
+ removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
+ d_qeInSoi.remove(v);
+ }
+ Assert(!d_qeInSoi.empty());
+}
+
+void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){
+ for(uint32_t i = 0; i < N; ++i){
+ std::swap(d_qeConflict[r+i], d_qeConflict[s+i]);
+ }
+}
+
+/**
+ * Region notation:
+ * A region is either
+ * - A single element X@i with the name X at the position i
+ * - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive
+ * - A concatenation of regions R1 and R2, R1;R2
+ *
+ * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd)
+ * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal
+ * subset of U, Delta, s.t. C \cup Delta is in conflict.
+ *
+ * Pre:
+ * [0, uEnd) is a set and is in conflict.
+ * uEnd <= assumptions.size()
+ * [0, cEnd) is in d_inSoi.
+ *
+ * Invariants: [0,cEnd) is never modified
+ *
+ * Post:
+ * [0, cEnd); [cEnd, deltaEnd) is in conflict
+ * [0, deltaEnd) is a set
+ * [0, deltaEnd) is in d_inSoi
+ */
+uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){
+ Assert(cEnd <= uEnd);
+ Assert(d_qeInUAndNotInSoi.empty());
+ Assert(d_qeGreedyOrder.empty());
+
+ const Tableau::Entry* spoiler = NULL;
+
+ if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){
+ // already in conflict
+ return cEnd;
+ }
+
+ Assert(cEnd < uEnd);
+
+ // Phase 1 : Construct the conflict greedily
+
+ for(uint32_t i = cEnd; i < uEnd; ++i){
+ d_qeInUAndNotInSoi.add(d_qeConflict[i]);
+ }
+ if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty
+ ArithVar first = d_qeConflict[cEnd];
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first);
+ d_qeInSoi.add(first);
+ d_qeInUAndNotInSoi.remove(first);
+ d_qeGreedyOrder.push_back(first);
+ }
+ while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
+ Assert(!d_qeInUAndNotInSoi.empty());
+
+ ArithVar nb = spoiler->getColVar();
+ int oppositeSgn = -(spoiler->getCoefficient().sgn());
+ Assert(oppositeSgn != 0);
+
+ ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true);
+ Assert(basicWithOp != ARITHVAR_SENTINEL);
+
+ addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
+ d_qeInSoi.add(basicWithOp);
+ d_qeInUAndNotInSoi.remove(basicWithOp);
+ d_qeGreedyOrder.push_back(basicWithOp);
+ }
+ Assert(spoiler == NULL);
+
+ // Compact the set u
+ uint32_t newEnd = cEnd + d_qeGreedyOrder.size();
+ std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd);
+
+ d_qeInUAndNotInSoi.purge();
+ d_qeGreedyOrder.clear();
+
+ // Phase 2 : Recursively determine the minimal set of rows
+
+ uint32_t xPos = cEnd;
+ std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]);
+ uint32_t uBegin = xPos + 1;
+ uint32_t split = (newEnd - uBegin)/2 + uBegin;
+
+ //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd)
+ // [0, newEnd) == d_inSoi
+
+ uint32_t compactU2;
+ if(split == newEnd){ // U2 is empty
+ compactU2 = newEnd;
+ }else{
+ // Remove U2 from Soi
+ qeRemoveRange(split, newEnd);
+ // [0, split) == d_inSoi
+
+ // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd)
+ compactU2 = quickExplainRec(split, newEnd);
+ // post:
+ // assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2)
+ // d_inSoi = [0, compactU2)
+ }
+ uint32_t deltaSize = compactU2 - split;
+ qeSwapRange(deltaSize, uBegin, split);
+ uint32_t d2End = uBegin+deltaSize;
+ // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2)
+ // d_inSoi == [0, compactU2)
+
+ uint32_t d1End;
+ if(d2End == compactU2){ // U1 is empty
+ d1End = d2End;
+ }else{
+ qeRemoveRange(d2End, compactU2);
+
+ //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2);
+ d1End = quickExplainRec(d2End, compactU2);
+ //post:
+ // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End);
+ // d_inSoi = [0, d1End)
+ }
+ //After both:
+ // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End);
+
+ Assert(d_qeInUAndNotInSoi.empty());
+ Assert(d_qeGreedyOrder.empty());
+ return d1End;
+}
+
+void SumOfInfeasibilitiesSPD::quickExplain(){
+ Assert(d_qeInSoi.empty());
+ Assert(d_qeInUAndNotInSoi.empty());
+ Assert(d_qeGreedyOrder.empty());
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ Assert(d_qeSgns.empty());
+
+ d_qeConflict.clear();
+ d_errorSet.pushFocusInto(d_qeConflict);
+
+ //cout << d_qeConflict.size() << " ";
+ uint32_t size = d_qeConflict.size();
+
+ if(size > 2){
+ for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+ ArithVar e = *iter;
+ addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e));
+ }
+ uint32_t end = quickExplainRec(0u, size);
+ Assert(end <= d_qeConflict.size());
+ Assert(d_soiVar != ARITHVAR_SENTINEL);
+ Assert(!d_qeInSoi.empty());
+
+ d_qeConflict.resize(end);
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ d_qeInSoi.purge();
+ d_qeSgns.clear();
+ }
+
+ //cout << d_qeConflict.size() << endl;
+
+ Assert(d_qeInSoi.empty());
+ Assert(d_qeInUAndNotInSoi.empty());
+ Assert(d_qeGreedyOrder.empty());
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ Assert(d_qeSgns.empty());
+}
+
+unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ bool success = false;
+ if(set.size() >= 2){
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
+ success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
+
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ }
+ return success ? set.size() : std::numeric_limits<int>::max();
+}
+
+std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
+ Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets start" << endl;
+
+ std::vector< ArithVarVec > subsets;
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+ if(d_errorSize <= 2){
+ ArithVarVec inError;
+ d_errorSet.pushFocusInto(inError);
+
+ Assert(debugIsASet(inError));
+ subsets.push_back(inError);
+ return subsets;
+ }
+ Assert(d_errorSize > 2);
+
+ //sgns_table< <nonbasic,sgn>, [basics] >;
+ // Phase 0: Construct the sgns table
+ sgn_table sgns;
+ DenseSet hasParticipated; //Has participated in a conflict
+ for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+ ArithVar e = *iter;
+ addRowSgns(sgns, e, d_errorSet.getSgn(e));
+
+ Trace("arith::greedyConflictSubsets") << "basic error var: " << e << endl;
+ if(TraceIsOn("arith::greedyConflictSubsets")){
+ d_tableau.debugPrintIsBasic(e);
+ d_tableau.printBasicRow(e, Trace("arith::greedyConflictSubsets"));
+ }
+ }
+
+ // Phase 1: Try to find at least 1 pair for every element
+ ArithVarVec tmp;
+ tmp.push_back(0);
+ tmp.push_back(0);
+ for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
+ ArithVar e = *iter;
+ tmp[0] = e;
+
+ int errSgn = d_errorSet.getSgn(e);
+ bool decreasing = errSgn < 0;
+ const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
+ Assert(spoiler != NULL);
+ ArithVar nb = spoiler->getColVar();
+ int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
+
+ sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
+ Assert(opposites != sgns.end());
+
+ const ArithVarVec& choices = (*opposites).second;
+ for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
+ ArithVar b = *j;
+ if(b < e){ continue; }
+ tmp[0] = e;
+ tmp[1] = b;
+ if(trySet(tmp) == 2){
+ Trace("arith::greedyConflictSubsets") << "found a pair " << b << " " << e << endl;
+ hasParticipated.softAdd(b);
+ hasParticipated.softAdd(e);
+ Assert(debugIsASet(tmp));
+ subsets.push_back(tmp);
+ ++(d_statistics.d_soiConflicts);
+ ++(d_statistics.d_hasToBeMinimal);
+ }
+ }
+ }
+
+
+ // Phase 2: If there is a variable that has not participated attempt to start a conflict
+ ArithVarVec possibleStarts; //List of elements that can be tried for starts.
+ d_errorSet.pushFocusInto(possibleStarts);
+ while(!possibleStarts.empty()){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+ ArithVar v = possibleStarts.back();
+ possibleStarts.pop_back();
+ if(hasParticipated.isMember(v)){ continue; }
+
+ hasParticipated.add(v);
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ //d_soiVar's row = \sumofinfeasibilites underConstruction
+ ArithVarVec underConstruction;
+ underConstruction.push_back(v);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
+
+ Trace("arith::greedyConflictSubsets") << "trying " << v << endl;
+
+ const Tableau::Entry* spoiler = NULL;
+ while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
+ ArithVar nb = spoiler->getColVar();
+ int oppositeSgn = -(spoiler->getCoefficient().sgn());
+ Assert(oppositeSgn != 0);
+
+ Trace("arith::greedyConflictSubsets") << "looking for " << nb << " " << oppositeSgn << endl;
+
+ ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
+
+ if(basicWithOp == ARITHVAR_SENTINEL){
+ Trace("arith::greedyConflictSubsets") << "search did not work for " << nb << endl;
+ // greedy construction has failed
+ break;
+ }else{
+ Trace("arith::greedyConflictSubsets") << "found " << basicWithOp << endl;
+
+ addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
+ hasParticipated.softAdd(basicWithOp);
+ underConstruction.push_back(basicWithOp);
+ }
+ }
+ if(spoiler == NULL){
+ Trace("arith::greedyConflictSubsets") << "success" << endl;
+ //then underConstruction contains a conflicting subset
+ Assert(debugIsASet(underConstruction));
+ subsets.push_back(underConstruction);
+ ++d_statistics.d_soiConflicts;
+ if(underConstruction.size() == 3){
+ ++d_statistics.d_hasToBeMinimal;
+ }else{
+ ++d_statistics.d_maybeNotMinimal;
+ }
+ }else{
+ Trace("arith::greedyConflictSubsets") << "failure" << endl;
+ }
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ // if(false && spoiler == NULL){
+ // ArithVarVec tmp;
+ // int smallest = tryAllSubsets(underConstruction, 0, tmp);
+ // cout << underConstruction.size() << " " << smallest << endl;
+ // Assert(smallest >= underConstruction.size());
+ // if(smallest < underConstruction.size()){
+ // exit(-1);
+ // }
+ // }
+ }
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets done" << endl;
+ return subsets;
+}
+
+bool SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
+ Assert(!subset.empty());
+ Assert(!d_conflictBuilder->underConstruction());
+
+ Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) start" << endl;
+
+ bool success = false;
+
+ for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
+ ArithVar e = *iter;
+ ConstraintP violated = d_errorSet.getViolated(e);
+ Assert(violated != NullConstraint);
+
+ int sgn = d_errorSet.getSgn(e);
+ const Rational& violatedCoeff = sgn > 0 ? d_negOne : d_posOne;
+ Trace("arith::generateSOIConflict") << "basic error var: "
+ << "(" << violatedCoeff << ")"
+ << " " << violated
+ << endl;
+
+
+ d_conflictBuilder->addConstraint(violated, violatedCoeff);
+ Assert(violated->hasProof());
+ if(!success && !violated->negationHasProof()){
+ success = true;
+ d_conflictBuilder->makeLastConsequent();
+ }
+ }
+
+ if(!success){
+ // failure
+ d_conflictBuilder->reset();
+ } else {
+ // pick a violated constraint arbitrarily. any of them may be selected for the conflict
+ Assert(d_conflictBuilder->underConstruction());
+ Assert(d_conflictBuilder->consequentIsSet());
+
+ for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
+ const Tableau::Entry& entry = *i;
+ ArithVar v = entry.getColVar();
+ if(v == d_soiVar){ continue; }
+ const Rational& coeff = entry.getCoefficient();
+
+ ConstraintP c = (coeff.sgn() > 0) ?
+ d_variables.getUpperBoundConstraint(v) :
+ d_variables.getLowerBoundConstraint(v);
+
+ Trace("arith::generateSOIConflict") << "non-basic var: "
+ << "(" << coeff << ")"
+ << " " << c
+ << endl;
+ d_conflictBuilder->addConstraint(c, coeff);
+ }
+ ConstraintCP conflicted = d_conflictBuilder->commitConflict();
+ d_conflictChannel.raiseConflict(conflicted,
+ InferenceId::ARITH_CONF_SOI_SIMPLEX);
+ }
+
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) done" << endl;
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ Assert(!d_conflictBuilder->underConstruction());
+ return success;
+}
+
+
+WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
+ Trace("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() start "
+ << ": |E| = " << d_errorSize << endl;
+ if(TraceIsOn("arith::SOIConflict")){
+ d_errorSet.debugPrint(cout);
+ }
+ Trace("arith::SOIConflict") << endl;
+
+ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+
+ if (options().arith.soiQuickExplain)
+ {
+ quickExplain();
+ generateSOIConflict(d_qeConflict);
+ }
+ else
+ {
+ vector<ArithVarVec> subsets = greedyConflictSubsets();
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ bool anySuccess = false;
+ Assert(!subsets.empty());
+ for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
+ const ArithVarVec& subset = *i;
+ Assert(debugIsASet(subset));
+ anySuccess = generateSOIConflict(subset) || anySuccess;
+ //Node conflict = generateSOIConflict(subset);
+ //cout << conflict << endl;
+
+ //reportConflict(conf); do not do this. We need a custom explanations!
+ //d_conflictChannel(conflict);
+ }
+ Assert(anySuccess);
+ }
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
+
+ //reportConflict(conf); do not do this. We need a custom explanations!
+ d_conflictVariables.add(d_soiVar);
+
+ Trace("arith::SOIConflict")
+ << "SumOfInfeasibilitiesSPD::SOIConflict() end" << endl;
+ return ConflictFound;
+}
+
+WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
+ Assert(d_soiVar != ARITHVAR_SENTINEL);
+
+ bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
+ LinearEqualityModule::UpdatePreferenceFunction upf;
+ if(useBlands) {
+ upf = &LinearEqualityModule::preferWitness<false>;
+ } else {
+ upf = &LinearEqualityModule::preferWitness<true>;
+ }
+
+ LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ &LinearEqualityModule::minVarOrder :
+ &LinearEqualityModule::minRowLength;
+ bpf = &LinearEqualityModule::minVarOrder;
+
+ UpdateInfo selected = selectUpdate(upf, bpf);
+
+ if(selected.uninitialized()){
+ Trace("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
+ return SOIConflict();
+ }else{
+ Assert(!selected.uninitialized());
+ WitnessImprovement w = selected.getWitness(false);
+ Assert(debugCheckWitness(selected, w, false));
+
+ updateAndSignal(selected, w);
+ logPivot(w);
+ return w;
+ }
+}
+
+Result::Status SumOfInfeasibilitiesSPD::sumOfInfeasibilities()
+{
+ TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
+
+ Assert(d_sgnDisagreements.empty());
+ Assert(d_pivotBudget != 0);
+ Assert(d_errorSize == d_errorSet.errorSize());
+ Assert(d_errorSize > 0);
+ Assert(d_conflictVariables.empty());
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+
+ //d_scores.purge();
+ d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
+
+
+ while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
+ Trace("dualLike") << "dualLike" << endl;
+
+ Assert(d_errorSet.noSignals());
+ // Possible outcomes:
+ // - conflict
+ // - budget was exhausted
+ // - focus went down
+ WitnessImprovement w = soiRound();
+ Trace("dualLike") << "selectFocusImproving -> " << w << endl;
+
+ Assert(d_errorSize == d_errorSet.errorSize());
+ }
+
+
+ if(d_soiVar != ARITHVAR_SENTINEL){
+ tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
+ d_soiVar = ARITHVAR_SENTINEL;
+ }
+
+ Assert(d_soiVar == ARITHVAR_SENTINEL);
+ if(!d_conflictVariables.empty()){
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Assert(d_errorSet.noSignals());
+ return Result::SAT;
+ }else{
+ Assert(d_pivotBudget == 0);
+ return Result::UNKNOWN;
+ }
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * This is an implementation of the Simplex Module for the Simplex for
+ * DPLL(T) decision procedure.
+ *
+ * This implements the Simplex module for the Simpelx for DPLL(T) decision
+ * procedure.
+ * See the Simplex for DPLL(T) technical report for more background.(citation?)
+ * This shares with the theory a Tableau, and a PartialModel that:
+ * - satisfies the equalities in the Tableau, and
+ * - the assignment for the non-basic variables satisfies their bounds.
+ * This is required to either produce a conflict or satisifying PartialModel.
+ * Further, we require being told when a basic variable updates its value.
+ *
+ * During the Simplex search we maintain a queue of variables.
+ * The queue is required to contain all of the basic variables that voilate
+ * their bounds.
+ * As elimination from the queue is more efficient to be done lazily,
+ * we do not maintain that the queue of variables needs to be only basic
+ * variables or only variables that satisfy their bounds.
+ *
+ * The simplex procedure roughly follows Alberto's thesis. (citation?)
+ * There is one round of selecting using a heuristic pivoting rule.
+ * (See PreferenceFunction Documentation for the available options.)
+ * The non-basic variable is the one that appears in the fewest pivots.
+ * (Bruno says that Leonardo invented this first.)
+ * After this, Bland's pivot rule is invoked.
+ *
+ * During this proccess, we periodically inspect the queue of variables to
+ * 1) remove now extraneous extries,
+ * 2) detect conflicts that are "waiting" on the queue but may not be detected
+ * by the current queue heuristics, and
+ * 3) detect multiple conflicts.
+ *
+ * Conflicts are greedily slackened to use the weakest bounds that still
+ * produce the conflict.
+ *
+ * Extra things tracked atm: (Subject to change at Tim's whims)
+ * - A superset of all of the newly pivoted variables.
+ * - A queue of additional conflicts that were discovered by Simplex.
+ * These are theory valid and are currently turned into lemmas
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/linear/simplex_update.h"
+#include "util/dense_map.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class SumOfInfeasibilitiesSPD : public SimplexDecisionProcedure {
+public:
+ SumOfInfeasibilitiesSPD(Env& env,
+ LinearEqualityModule& linEq,
+ ErrorSet& errors,
+ RaiseConflict conflictChannel,
+ TempVarMalloc tvmalloc);
+
+ Result::Status findModel(bool exactResult) override;
+
+ // other error variables are dropping
+ WitnessImprovement dualLikeImproveError(ArithVar evar);
+ WitnessImprovement primalImproveError(ArithVar evar);
+
+private:
+ /** The current sum of infeasibilities variable. */
+ ArithVar d_soiVar;
+
+ // dual like
+ // - found conflict
+ // - satisfied error set
+ Result::Status sumOfInfeasibilities();
+
+ int32_t d_pivotBudget;
+
+ WitnessImprovement d_prevWitnessImprovement;
+ uint32_t d_witnessImprovementInARow;
+
+ uint32_t degeneratePivotsInARow() const;
+
+ static constexpr uint32_t s_focusThreshold = 6;
+ static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
+ static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
+
+ DenseMap<uint32_t> d_leavingCountSinceImprovement;
+ void increaseLeavingCount(ArithVar x){
+ if(!d_leavingCountSinceImprovement.isKey(x)){
+ d_leavingCountSinceImprovement.set(x,1);
+ }else{
+ (d_leavingCountSinceImprovement.get(x))++;
+ }
+ }
+ LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
+ bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
+ d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
+ if(useBlands) {
+ return &LinearEqualityModule::preferWitness<false>;
+ } else {
+ return &LinearEqualityModule::preferWitness<true>;
+ }
+ }
+
+ void debugPrintSignal(ArithVar updated) const;
+
+ ArithVarVec d_sgnDisagreements;
+
+ void logPivot(WitnessImprovement w);
+
+ void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
+
+ UpdateInfo selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf,
+ LinearEqualityModule::VarPreferenceFunction bpf);
+
+
+ // UpdateInfo selectUpdateForDualLike(ArithVar basic){
+ // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
+
+ // LinearEqualityModule::UpdatePreferenceFunction upf =
+ // &LinearEqualityModule::preferWitness<true>;
+ // LinearEqualityModule::VarPreferenceFunction bpf =
+ // &LinearEqualityModule::minVarOrder;
+ // return selectPrimalUpdate(basic, upf, bpf);
+ // }
+
+ // UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
+ // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
+
+ // LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
+ // &LinearEqualityModule::preferWitness<false>:
+ // &LinearEqualityModule::preferWitness<true>;
+
+ // LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
+ // &LinearEqualityModule::minVarOrder :
+ // &LinearEqualityModule::minRowLength;
+ // bpf = &LinearEqualityModule::minVarOrder;
+
+ // return selectPrimalUpdate(basic, upf, bpf);
+ // }
+ // WitnessImprovement selectFocusImproving() ;
+ WitnessImprovement soiRound();
+ WitnessImprovement SOIConflict();
+ std::vector< ArithVarVec > greedyConflictSubsets();
+ bool generateSOIConflict(const ArithVarVec& subset);
+
+ // WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
+ // WitnessImprovement focusDownToLastHalf();
+ // WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
+ // WitnessImprovement focusDownToJust(ArithVar v);
+
+
+ void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
+
+ /**
+ * This is the main simplex for DPLL(T) loop.
+ * It runs for at most maxIterations.
+ *
+ * Returns true iff it has found a conflict.
+ * d_conflictVariable will be set and the conflict for this row is reported.
+ */
+ bool searchForFeasibleSolution(uint32_t maxIterations);
+
+ bool initialProcessSignals(){
+ TimerStat &timer = d_statistics.d_initialSignalsTime;
+ IntStat& conflictStat = d_statistics.d_initialConflicts;
+ return standardProcessSignals(timer, conflictStat);
+ }
+
+ void quickExplain();
+ DenseSet d_qeInSoi;
+ DenseSet d_qeInUAndNotInSoi;
+ ArithVarVec d_qeConflict;
+ ArithVarVec d_qeGreedyOrder;
+ sgn_table d_qeSgns;
+
+ uint32_t quickExplainRec(uint32_t cEnd, uint32_t uEnd);
+ void qeAddRange(uint32_t begin, uint32_t end);
+ void qeRemoveRange(uint32_t begin, uint32_t end);
+ void qeSwapRange(uint32_t N, uint32_t r, uint32_t s);
+
+ unsigned trySet(const ArithVarVec& set);
+ unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp);
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ TimerStat d_initialSignalsTime;
+ IntStat d_initialConflicts;
+
+ IntStat d_soiFoundUnsat;
+ IntStat d_soiFoundSat;
+ IntStat d_soiMissed;
+
+ IntStat d_soiConflicts;
+ IntStat d_hasToBeMinimal;
+ IntStat d_maybeNotMinimal;
+
+ TimerStat d_soiTimer;
+ TimerStat d_soiFocusConstructionTimer;
+ TimerStat d_soiConflictMinimization;
+ TimerStat d_selectUpdateForSOI;
+
+ ReferenceStat<uint32_t> d_finalCheckPivotCounter;
+
+ Statistics(const std::string& name, uint32_t& pivots);
+ } d_statistics;
+};/* class FCSimplexDecisionProcedure */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/tableau.h"
+
+using namespace std;
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+
+void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic, CoefficientChangeCallback& cb){
+ Assert(isBasic(oldBasic));
+ Assert(!isBasic(newBasic));
+ Assert(d_mergeBuffer.empty());
+
+ Trace("tableau") << "Tableau::pivot(" << oldBasic <<", " << newBasic <<")" << endl;
+
+ RowIndex ridx = basicToRowIndex(oldBasic);
+
+ rowPivot(oldBasic, newBasic, cb);
+ Assert(ridx == basicToRowIndex(newBasic));
+
+ loadRowIntoBuffer(ridx);
+
+ ColIterator colIter = colIterator(newBasic);
+ while(!colIter.atEnd()){
+ EntryID id = colIter.getID();
+ Entry& entry = d_entries.get(id);
+
+ ++colIter; //needs to be incremented before the variable is removed
+ if(entry.getRowIndex() == ridx){ continue; }
+
+ RowIndex to = entry.getRowIndex();
+ Rational coeff = entry.getCoefficient();
+ if(cb.canUseRow(to)){
+ rowPlusBufferTimesConstant(to, coeff, cb);
+ }else{
+ rowPlusBufferTimesConstant(to, coeff);
+ }
+ }
+ clearBuffer();
+
+ //Clear the column for used for this variable
+
+ Assert(d_mergeBuffer.empty());
+ Assert(!isBasic(oldBasic));
+ Assert(isBasic(newBasic));
+ Assert(getColLength(newBasic) == 1);
+}
+
+/**
+ * Changes basic to newbasic (a variable on the row).
+ */
+void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb){
+ Assert(isBasic(basicOld));
+ Assert(!isBasic(basicNew));
+
+ RowIndex rid = basicToRowIndex(basicOld);
+
+ EntryID newBasicID = findOnRow(rid, basicNew);
+
+ Assert(newBasicID != ENTRYID_SENTINEL);
+
+ Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
+ const Rational& a_rs = newBasicEntry.getCoefficient();
+ int a_rs_sgn = a_rs.sgn();
+ Rational negInverseA_rs = -(a_rs.inverse());
+
+ for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
+ EntryID id = i.getID();
+ Tableau::Entry& entry = d_entries.get(id);
+
+ entry.getCoefficient() *= negInverseA_rs;
+ }
+
+ d_basic2RowIndex.remove(basicOld);
+ d_basic2RowIndex.set(basicNew, rid);
+ d_rowIndex2basic.set(rid, basicNew);
+
+ cb.multiplyRow(rid, -a_rs_sgn);
+}
+
+void Tableau::addRow(ArithVar basic,
+ const std::vector<Rational>& coefficients,
+ const std::vector<ArithVar>& variables)
+{
+ Assert(basic < getNumColumns());
+ Assert(debugIsASet(variables));
+ Assert(coefficients.size() == variables.size());
+ Assert(!isBasic(basic));
+
+ RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
+ addEntry(newRow, basic, Rational(-1));
+
+ Assert(!d_basic2RowIndex.isKey(basic));
+ Assert(!d_rowIndex2basic.isKey(newRow));
+
+ d_basic2RowIndex.set(basic, newRow);
+ d_rowIndex2basic.set(newRow, basic);
+
+
+ if(TraceIsOn("matrix")){ printMatrix(); }
+
+ NoEffectCCCB noeffect;
+ NoEffectCCCB* nep = &noeffect;
+ CoefficientChangeCallback* cccb = static_cast<CoefficientChangeCallback*>(nep);
+
+ vector<Rational>::const_iterator coeffIter = coefficients.begin();
+ vector<ArithVar>::const_iterator varsIter = variables.begin();
+ vector<ArithVar>::const_iterator varsEnd = variables.end();
+ for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
+ ArithVar var = *varsIter;
+
+ if(isBasic(var)){
+ Rational coeff = *coeffIter;
+
+ RowIndex ri = basicToRowIndex(var);
+
+ loadRowIntoBuffer(ri);
+ rowPlusBufferTimesConstant(newRow, coeff, *cccb);
+ clearBuffer();
+ }
+ }
+
+ if(TraceIsOn("matrix")) { printMatrix(); }
+
+ Assert(debugNoZeroCoefficients(newRow));
+ Assert(debugMatchingCountsForRow(newRow));
+ Assert(getColLength(basic) == 1);
+}
+
+void Tableau::removeBasicRow(ArithVar basic){
+ RowIndex rid = basicToRowIndex(basic);
+
+ removeRow(rid);
+ d_basic2RowIndex.remove(basic);
+ d_rowIndex2basic.remove(rid);
+}
+
+void Tableau::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb){
+ if(!mult.isZero()){
+ RowIndex to_idx = basicToRowIndex(to);
+ addEntry(to_idx, from, mult); // Add an entry to be cancelled out
+ RowIndex from_idx = basicToRowIndex(from);
+
+ cb.update(to_idx, from, 0, mult.sgn());
+
+ loadRowIntoBuffer(from_idx);
+ rowPlusBufferTimesConstant(to_idx, mult, cb);
+ clearBuffer();
+ }
+}
+
+uint32_t Tableau::rowComplexity(ArithVar basic) const{
+ uint32_t complexity = 0;
+ for(RowIterator i = basicRowIterator(basic); !i.atEnd(); ++i){
+ const Entry& e = *i;
+ complexity += e.getCoefficient().complexity();
+ }
+ return complexity;
+}
+
+double Tableau::avgRowComplexity() const{
+ double sum = 0;
+ uint32_t rows = 0;
+ for(BasicIterator i = beginBasic(), i_end = endBasic(); i != i_end; ++i){
+ sum += rowComplexity(*i);
+ rows++;
+ }
+ return (rows == 0) ? 0 : (sum/rows);
+}
+
+void Tableau::printBasicRow(ArithVar basic, std::ostream& out){
+ printRow(basicToRowIndex(basic), out);
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Morgan Deters
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include <vector>
+
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/matrix.h"
+#include "util/dense_map.h"
+#include "util/rational.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+/**
+ * A Tableau is a Rational matrix that keeps its rows in solved form.
+ * Each row has a basic variable with coefficient -1 that is solved.
+ * Tableau is optimized for pivoting.
+ * The tableau should only be updated via pivot calls.
+ */
+class Tableau : public Matrix<Rational> {
+public:
+private:
+ typedef DenseMap<RowIndex> BasicToRowMap;
+ // Set of all of the basic variables in the tableau.
+ // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
+ BasicToRowMap d_basic2RowIndex;
+
+ // RowIndex |-> Basic Variable
+ typedef DenseMap<ArithVar> RowIndexToBasicMap;
+ RowIndexToBasicMap d_rowIndex2basic;
+
+public:
+
+ Tableau() : Matrix<Rational>(Rational(0)) {}
+
+ typedef Matrix<Rational>::ColIterator ColIterator;
+ typedef Matrix<Rational>::RowIterator RowIterator;
+ typedef BasicToRowMap::const_iterator BasicIterator;
+
+ typedef MatrixEntry<Rational> Entry;
+
+ bool isBasic(ArithVar v) const{
+ return d_basic2RowIndex.isKey(v);
+ }
+
+ void debugPrintIsBasic(ArithVar v) const {
+ if(isBasic(v)){
+ Trace("model") << v << " is basic." << std::endl;
+ }else{
+ Trace("model") << v << " is non-basic." << std::endl;
+ }
+ }
+
+ BasicIterator beginBasic() const {
+ return d_basic2RowIndex.begin();
+ }
+ BasicIterator endBasic() const {
+ return d_basic2RowIndex.end();
+ }
+
+ RowIndex basicToRowIndex(ArithVar x) const {
+ return d_basic2RowIndex[x];
+ }
+
+ ArithVar rowIndexToBasic(RowIndex rid) const {
+ Assert(d_rowIndex2basic.isKey(rid));
+ return d_rowIndex2basic[rid];
+ }
+
+ ColIterator colIterator(ArithVar x) const {
+ return getColumn(x).begin();
+ }
+
+ RowIterator ridRowIterator(RowIndex rid) const {
+ return getRow(rid).begin();
+ }
+
+ RowIterator basicRowIterator(ArithVar basic) const {
+ return ridRowIterator(basicToRowIndex(basic));
+ }
+
+ const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
+ return findEntry(basicToRowIndex(basic), col);
+ }
+
+ /**
+ * Adds a row to the tableau.
+ * The new row is equivalent to:
+ * basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
+ * preconditions:
+ * basicVar is already declared to be basic
+ * basicVar does not have a row associated with it in the tableau.
+ *
+ * Note: each variables[i] does not have to be non-basic.
+ * Pivoting will be mimicked if it is basic.
+ */
+ void addRow(ArithVar basicVar,
+ const std::vector<Rational>& coeffs,
+ const std::vector<ArithVar>& variables);
+
+ /**
+ * preconditions:
+ * x_r is basic,
+ * x_s is non-basic, and
+ * a_rs != 0.
+ */
+ void pivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+ void removeBasicRow(ArithVar basic);
+
+ uint32_t basicRowLength(ArithVar basic) const{
+ RowIndex ridx = basicToRowIndex(basic);
+ return getRowLength(ridx);
+ }
+
+ /**
+ * to += mult * from
+ * replacing from with its row.
+ */
+ void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb);
+
+ void directlyAddToCoefficient(ArithVar rowVar, ArithVar col, const Rational& mult, CoefficientChangeCallback& cb){
+ RowIndex ridx = basicToRowIndex(rowVar);
+ manipulateRowEntry(ridx, col, mult, cb);
+ }
+
+ /* Returns the complexity of a row in the tableau. */
+ uint32_t rowComplexity(ArithVar basic) const;
+
+ /* Returns the average complexity of the rows in the tableau. */
+ double avgRowComplexity() const;
+
+ void printBasicRow(ArithVar basic, std::ostream& out);
+
+private:
+ /* Changes the basic variable on the row for basicOld to basicNew. */
+ void rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
+
+};/* class Tableau */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "base/output.h"
+#include "theory/arith/linear/tableau_sizes.h"
+#include "theory/arith/linear/tableau.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+uint32_t TableauSizes::getRowLength(ArithVar b) const {
+ return d_tab->basicRowLength(b);
+}
+
+uint32_t TableauSizes::getColumnLength(ArithVar x) const {
+ return d_tab->getColLength(x);
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "cvc5_private.h"
+
+#pragma once
+
+#include "theory/arith/linear/arithvar.h"
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+class Tableau;
+
+class TableauSizes {
+private:
+ const Tableau* d_tab;
+public:
+ TableauSizes(const Tableau* tab): d_tab(tab){}
+
+ uint32_t getRowLength(ArithVar b) const;
+ uint32_t getColumnLength(ArithVar x) const;
+}; /* TableauSizes */
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Gereon Kremer, Alex Ozdemir
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#include "theory/arith/linear/theory_arith_private.h"
+
+#include <map>
+#include <optional>
+#include <queue>
+#include <vector>
+
+#include "base/output.h"
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "context/context.h"
+#include "expr/kind.h"
+#include "expr/metakind.h"
+#include "expr/node.h"
+#include "expr/node_algorithm.h"
+#include "expr/node_builder.h"
+#include "expr/skolem_manager.h"
+#include "options/arith_options.h"
+#include "options/base_options.h"
+#include "options/smt_options.h"
+#include "preprocessing/util/ite_utilities.h"
+#include "proof/proof_generator.h"
+#include "proof/proof_node_manager.h"
+#include "proof/proof_rule.h"
+#include "smt/logic_exception.h"
+#include "smt/smt_statistics_registry.h"
+#include "smt_util/boolean_simplification.h"
+#include "theory/arith/linear/approx_simplex.h"
+#include "theory/arith/arith_rewriter.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/linear/cut_log.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/dio_solver.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/nl/nonlinear_extension.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/linear/simplex.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/ext_theory.h"
+#include "theory/quantifiers/fmf/bounded_integers.h"
+#include "theory/rewriter.h"
+#include "theory/theory_model.h"
+#include "theory/trust_substitutions.h"
+#include "theory/valuation.h"
+#include "util/dense_map.h"
+#include "util/integer.h"
+#include "util/random.h"
+#include "util/rational.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+using namespace std;
+using namespace cvc5::internal::kind;
+
+namespace cvc5::internal {
+namespace theory {
+namespace arith::linear {
+
+static Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum);
+static bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap);
+
+TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing,
+ Env& env,
+ BranchAndBound& bab)
+ : EnvObj(env),
+ d_containing(containing),
+ d_foundNl(false),
+ d_rowTracking(),
+ d_bab(bab),
+ d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
+ : nullptr),
+ d_checker(),
+ d_pfGen(new EagerProofGenerator(d_pnm, userContext())),
+ d_constraintDatabase(d_env,
+ d_partialModel,
+ d_congruenceManager,
+ RaiseConflict(*this),
+ d_pfGen.get()),
+ d_qflraStatus(Result::UNKNOWN),
+ d_unknownsInARow(0),
+ d_hasDoneWorkSinceCut(false),
+ d_learner(userContext()),
+ d_assertionsThatDoNotMatchTheirLiterals(context()),
+ d_nextIntegerCheckVar(0),
+ d_constantIntegerVariables(context()),
+ d_diseqQueue(context(), false),
+ d_currentPropagationList(),
+ d_learnedBounds(context()),
+ d_preregisteredNodes(context()),
+ d_partialModel(context(), DeltaComputeCallback(*this)),
+ d_errorSet(
+ d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)),
+ d_tableau(),
+ d_linEq(d_partialModel,
+ d_tableau,
+ d_rowTracking,
+ BasicVarModelUpdateCallBack(*this)),
+ d_diosolver(env),
+ d_restartsCounter(0),
+ d_tableauSizeHasBeenModified(false),
+ d_tableauResetDensity(1.6),
+ d_tableauResetPeriod(10),
+ d_conflicts(context()),
+ d_blackBoxConflict(context(), Node::null()),
+ d_blackBoxConflictPf(context(), std::shared_ptr<ProofNode>(nullptr)),
+ d_congruenceManager(d_env,
+ d_constraintDatabase,
+ SetupLiteralCallBack(*this),
+ d_partialModel,
+ RaiseEqualityEngineConflict(*this)),
+ d_cmEnabled(context(), options().arith.arithCongMan),
+
+ d_dualSimplex(
+ env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_fcSimplex(
+ env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_soiSimplex(
+ env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_attemptSolSimplex(
+ env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_pass1SDP(NULL),
+ d_otherSDP(NULL),
+ d_lastContextIntegerAttempted(context(), -1),
+
+ d_DELTA_ZERO(0),
+ d_approxCuts(context()),
+ d_fullCheckCounter(0),
+ d_cutCount(context(), 0),
+ d_cutInContext(context()),
+ d_likelyIntegerInfeasible(context(), false),
+ d_guessedCoeffSet(context(), false),
+ d_guessedCoeffs(),
+ d_treeLog(NULL),
+ d_replayVariables(),
+ d_replayConstraints(),
+ d_lhsTmp(),
+ d_approxStats(NULL),
+ d_attemptSolveIntTurnedOff(userContext(), 0),
+ d_dioSolveResources(0),
+ d_solveIntMaybeHelp(0u),
+ d_solveIntAttempts(0u),
+ d_newFacts(false),
+ d_previousStatus(Result::UNKNOWN),
+ d_statistics(statisticsRegistry(), "theory::arith::")
+{
+}
+
+TheoryArithPrivate::~TheoryArithPrivate(){
+ if(d_treeLog != NULL){ delete d_treeLog; }
+ if(d_approxStats != NULL) { delete d_approxStats; }
+}
+
+bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo& esi)
+{
+ if (!d_cmEnabled)
+ {
+ return false;
+ }
+ return d_congruenceManager.needsEqualityEngine(esi);
+}
+void TheoryArithPrivate::finishInit()
+{
+ if (d_cmEnabled)
+ {
+ eq::EqualityEngine* ee = d_containing.getEqualityEngine();
+ Assert(ee != nullptr);
+ d_congruenceManager.finishInit(ee);
+ }
+}
+
+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 CVC5_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 CVC5_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());
+}
+
+TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg)
+{
+ stringstream ss;
+ ss << "Cannot construct a model for " << n << " as " << endl << msg;
+ setMessage(ss.str());
+}
+TheoryArithPrivate::ModelException::~ModelException() {}
+
+TheoryArithPrivate::Statistics::Statistics(StatisticsRegistry& reg,
+ const std::string& name)
+ : d_statAssertUpperConflicts(
+ reg.registerInt(name + "AssertUpperConflicts")),
+ d_statAssertLowerConflicts(
+ reg.registerInt(name + "AssertLowerConflicts")),
+ d_statUserVariables(reg.registerInt(name + "UserVariables")),
+ d_statAuxiliaryVariables(reg.registerInt(name + "AuxiliaryVariables")),
+ d_statDisequalitySplits(reg.registerInt(name + "DisequalitySplits")),
+ d_statDisequalityConflicts(
+ reg.registerInt(name + "DisequalityConflicts")),
+ d_simplifyTimer(reg.registerTimer(name + "simplifyTimer")),
+ d_staticLearningTimer(reg.registerTimer(name + "staticLearningTimer")),
+ d_presolveTime(reg.registerTimer(name + "presolveTime")),
+ d_newPropTime(reg.registerTimer(name + "newPropTimer")),
+ d_externalBranchAndBounds(
+ reg.registerInt(name + "externalBranchAndBounds")),
+ d_initialTableauSize(reg.registerInt(name + "initialTableauSize")),
+ d_currSetToSmaller(reg.registerInt(name + "currSetToSmaller")),
+ d_smallerSetToCurr(reg.registerInt(name + "smallerSetToCurr")),
+ d_restartTimer(reg.registerTimer(name + "restartTimer")),
+ d_boundComputationTime(reg.registerTimer(name + "bound::time")),
+ d_boundComputations(reg.registerInt(name + "bound::boundComputations")),
+ d_boundPropagations(reg.registerInt(name + "bound::boundPropagations")),
+ d_unknownChecks(reg.registerInt(name + "status::unknowns")),
+ d_maxUnknownsInARow(reg.registerInt(name + "status::maxUnknownsInARow")),
+ d_avgUnknownsInARow(
+ reg.registerAverage(name + "status::avgUnknownsInARow")),
+ d_revertsOnConflicts(
+ reg.registerInt(name + "status::revertsOnConflicts")),
+ d_commitsOnConflicts(
+ reg.registerInt(name + "status::commitsOnConflicts")),
+ d_nontrivialSatChecks(
+ reg.registerInt(name + "status::nontrivialSatChecks")),
+ d_replayLogRecCount(reg.registerInt(name + "z::approx::replay::rec")),
+ d_replayLogRecConflictEscalation(
+ reg.registerInt(name + "z::approx::replay::rec::escalation")),
+ d_replayLogRecEarlyExit(
+ reg.registerInt(name + "z::approx::replay::rec::earlyexit")),
+ d_replayBranchCloseFailures(reg.registerInt(
+ name + "z::approx::replay::rec::branch::closefailures")),
+ d_replayLeafCloseFailures(reg.registerInt(
+ name + "z::approx::replay::rec::leaf::closefailures")),
+ d_replayBranchSkips(
+ reg.registerInt(name + "z::approx::replay::rec::branch::skips")),
+ d_mirCutsAttempted(
+ reg.registerInt(name + "z::approx::cuts::mir::attempted")),
+ d_gmiCutsAttempted(
+ reg.registerInt(name + "z::approx::cuts::gmi::attempted")),
+ d_branchCutsAttempted(
+ reg.registerInt(name + "z::approx::cuts::branch::attempted")),
+ d_cutsReconstructed(
+ reg.registerInt(name + "z::approx::cuts::reconstructed")),
+ d_cutsReconstructionFailed(
+ reg.registerInt(name + "z::approx::cuts::reconstructed::failed")),
+ d_cutsProven(reg.registerInt(name + "z::approx::cuts::proofs")),
+ d_cutsProofFailed(
+ reg.registerInt(name + "z::approx::cuts::proofs::failed")),
+ d_mipReplayLemmaCalls(
+ reg.registerInt(name + "z::approx::external::calls")),
+ d_mipExternalCuts(reg.registerInt(name + "z::approx::external::cuts")),
+ d_mipExternalBranch(
+ reg.registerInt(name + "z::approx::external::branches")),
+ d_inSolveInteger(reg.registerInt(name + "z::approx::inSolverInteger")),
+ d_branchesExhausted(
+ reg.registerInt(name + "z::approx::exhausted::branches")),
+ d_execExhausted(reg.registerInt(name + "z::approx::exhausted::exec")),
+ d_pivotsExhausted(reg.registerInt(name + "z::approx::exhausted::pivots")),
+ d_panicBranches(reg.registerInt(name + "z::arith::paniclemmas")),
+ d_relaxCalls(reg.registerInt(name + "z::arith::relax::calls")),
+ d_relaxLinFeas(reg.registerInt(name + "z::arith::relax::feasible::res")),
+ d_relaxLinFeasFailures(
+ reg.registerInt(name + "z::arith::relax::feasible::failures")),
+ d_relaxLinInfeas(reg.registerInt(name + "z::arith::relax::infeasible")),
+ d_relaxLinInfeasFailures(
+ reg.registerInt(name + "z::arith::relax::infeasible::failures")),
+ d_relaxLinExhausted(reg.registerInt(name + "z::arith::relax::exhausted")),
+ d_relaxOthers(reg.registerInt(name + "z::arith::relax::other")),
+ d_applyRowsDeleted(
+ reg.registerInt(name + "z::arith::cuts::applyRowsDeleted")),
+ d_replaySimplexTimer(
+ reg.registerTimer(name + "z::approx::replay::simplex::timer")),
+ d_replayLogTimer(
+ reg.registerTimer(name + "z::approx::replay::log::timer")),
+ d_solveIntTimer(reg.registerTimer(name + "z::solveInt::timer")),
+ d_solveRealRelaxTimer(
+ reg.registerTimer(name + "z::solveRealRelax::timer")),
+ d_solveIntCalls(reg.registerInt(name + "z::solveInt::calls")),
+ d_solveStandardEffort(
+ reg.registerInt(name + "z::solveInt::calls::standardEffort")),
+ d_approxDisabled(reg.registerInt(name + "z::approxDisabled")),
+ d_replayAttemptFailed(reg.registerInt(name + "z::replayAttemptFailed")),
+ d_cutsRejectedDuringReplay(
+ reg.registerInt(name + "z::approx::replay::cuts::rejected")),
+ d_cutsRejectedDuringLemmas(
+ reg.registerInt(name + "z::approx::external::cuts::rejected")),
+ d_satPivots(reg.registerHistogram<uint32_t>(name + "pivots::sat")),
+ d_unsatPivots(reg.registerHistogram<uint32_t>(name + "pivots::unsat")),
+ d_unknownPivots(
+ reg.registerHistogram<uint32_t>(name + "pivots::unknown")),
+ d_solveIntModelsAttempts(
+ reg.registerInt(name + "z::solveInt::models::attempts")),
+ d_solveIntModelsSuccessful(
+ reg.registerInt(name + "zzz::solveInt::models::successful")),
+ d_mipTimer(reg.registerTimer(name + "z::approx::mip::timer")),
+ d_lpTimer(reg.registerTimer(name + "z::approx::lp::timer")),
+ d_mipProofsAttempted(reg.registerInt(name + "z::mip::proofs::attempted")),
+ d_mipProofsSuccessful(
+ reg.registerInt(name + "z::mip::proofs::successful")),
+ d_numBranchesFailed(
+ reg.registerInt(name + "z::mip::branch::proof::failed"))
+{
+}
+
+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;
+}
+
+bool TheoryArithPrivate::isProofEnabled() const
+{
+ return d_pnm != nullptr;
+}
+
+void TheoryArithPrivate::raiseConflict(ConstraintCP a, InferenceId id){
+ Assert(a->inConflict());
+ Assert(id != InferenceId::UNKNOWN)
+ << "Must provide an inference id in TheoryArithPrivate::raiseConflict";
+ d_conflicts.push_back(std::make_pair(a, id));
+}
+
+void TheoryArithPrivate::raiseBlackBoxConflict(Node bb,
+ std::shared_ptr<ProofNode> pf)
+{
+ Trace("arith::bb") << "raiseBlackBoxConflict: " << bb << std::endl;
+ if (d_blackBoxConflict.get().isNull())
+ {
+ if (isProofEnabled())
+ {
+ Trace("arith::bb") << " with proof " << pf << std::endl;
+ d_blackBoxConflictPf.set(pf);
+ }
+ d_blackBoxConflict = bb;
+ }
+}
+
+bool TheoryArithPrivate::anyConflict() const
+{
+ return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull();
+}
+
+void TheoryArithPrivate::revertOutOfConflict(){
+ d_partialModel.revertAssignmentChanges();
+ clearUpdates();
+ d_currentPropagationList.clear();
+}
+
+void TheoryArithPrivate::clearUpdates(){
+ d_updatedBounds.purge();
+}
+
+void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){
+ if(d_cmEnabled){
+ Assert(d_congruenceManager.isWatchedVariable(x));
+ Assert(d_partialModel.upperBoundIsZero(x));
+ Assert(d_partialModel.lowerBoundIsZero(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);
+ }else{
+ d_congruenceManager.watchedVariableIsZero(lb, ub);
+ }
+ }
+}
+
+bool TheoryArithPrivate::getSolveIntegerResource(){
+ if(d_attemptSolveIntTurnedOff > 0){
+ d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1;
+ return false;
+ }else{
+ return true;
+ }
+}
+
+bool TheoryArithPrivate::getDioCuttingResource(){
+ if(d_dioSolveResources > 0){
+ d_dioSolveResources--;
+ if(d_dioSolveResources == 0){
+ d_dioSolveResources = -options().arith.rrTurns;
+ }
+ return true;
+ }else{
+ d_dioSolveResources++;
+ if(d_dioSolveResources >= 0){
+ d_dioSolveResources = options().arith.dioSolverTurns;
+ }
+ return false;
+ }
+}
+
+/* procedure AssertLower( x_i >= c_i ) */
+bool TheoryArithPrivate::AssertLower(ConstraintP constraint){
+ Assert(constraint != NullConstraint);
+ Assert(constraint->isLowerBound());
+ Assert(constraint->isTrue());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Trace("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
+
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ //TODO Relax to less than?
+ if(d_partialModel.lessThanLowerBound(x_i, c_i)){
+ return false; //sat
+ }
+
+ int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+ if(cmpToUB > 0){ // c_i < \lowerbound(x_i)
+ ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i);
+ ConstraintP negation = constraint->getNegation();
+ negation->impliedByUnate(ubc, true);
+
+ raiseConflict(constraint, InferenceId::ARITH_CONF_LOWER);
+
+ ++(d_statistics.d_statAssertLowerConflicts);
+ return true;
+ }else if(cmpToUB == 0){
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Trace("dio::push") << "dio::push " << x_i << endl;
+ }
+ 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.hasEquality()){
+ Assert(vc.hasDisequality());
+ ConstraintP eq = vc.getEquality();
+ ConstraintP diseq = vc.getDisequality();
+ // x <= b, x >= b |= x = b
+ // (x > b or x < b or x = b)
+ Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
+ bool triConflict = diseq->isTrue();
+
+ if(!eq->isTrue()){
+ eq->impliedByTrichotomy(constraint, ub, triConflict);
+ eq->tryToPropagate();
+ }
+ if(triConflict){
+ ++(d_statistics.d_statDisequalityConflicts);
+ raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
+ return true;
+ }
+ }
+ }else{
+ // l <= x <= u and l < u
+ Assert(cmpToUB < 0);
+ const ValueCollection& vc = constraint->getValueCollection();
+
+ if(vc.hasDisequality()){
+ const ConstraintP diseq = vc.getDisequality();
+ if(diseq->isTrue()){
+ const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+ ConstraintP negUb = ub->getNegation();
+
+ // l <= x, l != x |= l < x
+ // |= not (l >= x)
+ bool ubInConflict = ub->hasProof();
+ bool learnNegUb = !(negUb->hasProof());
+ if(learnNegUb){
+ negUb->impliedByTrichotomy(constraint, diseq, ubInConflict);
+ negUb->tryToPropagate();
+ }
+ if(ubInConflict){
+ raiseConflict(ub, InferenceId::ARITH_CONF_TRICHOTOMY);
+ return true;
+ }else if(learnNegUb){
+ d_learnedBounds.push_back(negUb);
+ }
+ }
+ }
+ }
+
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+
+ d_partialModel.setLowerBoundConstraint(constraint);
+
+ if(d_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);
+ }
+ }
+ }
+
+ d_updatedBounds.softAdd(x_i);
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(d_partialModel.getAssignment(x_i) < c_i){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false; //sat
+}
+
+/* procedure AssertUpper( x_i <= c_i) */
+bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){
+ Assert(constraint != NullConstraint);
+ Assert(constraint->isUpperBound());
+ Assert(constraint->isTrue());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+
+
+ //Too strong because of rounding with integers
+ //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
+
+ if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
+ return false; //sat
+ }
+
+ // cmpToLb = \lowerbound(x_i).cmp(c_i)
+ int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+ if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i)
+ // l_i <= x_i and c_i < l_i |= c_i < x_i
+ // or ... |= not (x_i <= c_i)
+ ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i);
+ ConstraintP negConstraint = constraint->getNegation();
+ negConstraint->impliedByUnate(lbc, true);
+ raiseConflict(constraint, InferenceId::ARITH_CONF_UPPER);
+ ++(d_statistics.d_statAssertUpperConflicts);
+ return true;
+ }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Trace("dio::push") << "dio::push " << x_i << endl;
+ }
+
+ const ValueCollection& vc = constraint->getValueCollection();
+ 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);
+ }
+ }
+
+ if(vc.hasDisequality()){
+ Assert(vc.hasDisequality());
+ ConstraintP eq = vc.getEquality();
+ ConstraintP diseq = vc.getDisequality();
+ // x <= b, x >= b |= x = b
+ // (x > b or x < b or x = b)
+ Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
+ bool triConflict = diseq->isTrue();
+ if(!eq->isTrue()){
+ eq->impliedByTrichotomy(constraint, lb, triConflict);
+ eq->tryToPropagate();
+ }
+ if(triConflict){
+ ++(d_statistics.d_statDisequalityConflicts);
+ raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
+ return true;
+ }
+ }
+ }else if(cmpToLB > 0){
+ // l <= x <= u and l < u
+ Assert(cmpToLB > 0);
+ const ValueCollection& vc = constraint->getValueCollection();
+
+ if(vc.hasDisequality()){
+ const ConstraintP diseq = vc.getDisequality();
+ if(diseq->isTrue()){
+ const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+ ConstraintP negLb = lb->getNegation();
+
+ // x <= u, u != x |= u < x
+ // |= not (u >= x)
+ bool lbInConflict = lb->hasProof();
+ bool learnNegLb = !(negLb->hasProof());
+ if(learnNegLb){
+ negLb->impliedByTrichotomy(constraint, diseq, lbInConflict);
+ negLb->tryToPropagate();
+ }
+ if(lbInConflict){
+ raiseConflict(lb, InferenceId::ARITH_CONF_TRICHOTOMY);
+ return true;
+ }else if(learnNegLb){
+ d_learnedBounds.push_back(negLb);
+ }
+ }
+ }
+ }
+
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+ //It is fine if this is NullConstraint
+
+ d_partialModel.setUpperBoundConstraint(constraint);
+
+ if(d_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);
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(d_partialModel.getAssignment(x_i) > c_i){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false; //sat
+}
+
+
+/* procedure AssertEquality( x_i == c_i ) */
+bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){
+ Assert(constraint != NullConstraint);
+ Assert(constraint->isEquality());
+ Assert(constraint->isTrue());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+
+ Trace("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
+
+ //Should be fine in integers
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
+ int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
+
+ // u_i <= c_i <= l_i
+ // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
+ if(cmpToUB >= 0 && cmpToLB <= 0){
+ return false; //sat
+ }
+
+ if(cmpToUB > 0 || cmpToLB < 0){
+ ConstraintP cb = (cmpToUB > 0) ? d_partialModel.getUpperBoundConstraint(x_i) :
+ d_partialModel.getLowerBoundConstraint(x_i);
+ ConstraintP diseq = constraint->getNegation();
+ Assert(!diseq->isTrue());
+ diseq->impliedByUnate(cb, true);
+ raiseConflict(constraint, InferenceId::ARITH_CONF_EQ);
+ return true;
+ }
+
+ Assert(cmpToUB <= 0);
+ Assert(cmpToLB >= 0);
+ Assert(cmpToUB < 0 || cmpToLB > 0);
+
+ if(isInteger(x_i)){
+ d_constantIntegerVariables.push_back(x_i);
+ Trace("dio::push") << "dio::push " << x_i << endl;
+ }
+
+ // Don't bother to check whether x_i != c_i is in d_diseq
+ // The a and (not a) should never be on the fact queue
+ d_currentPropagationList.push_back(constraint);
+ d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
+ d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
+
+ d_partialModel.setUpperBoundConstraint(constraint);
+ d_partialModel.setLowerBoundConstraint(constraint);
+
+ if(d_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.equalsConstant(constraint);
+ }
+ }
+
+ d_updatedBounds.softAdd(x_i);
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "before" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ if(!d_tableau.isBasic(x_i)){
+ if(!(d_partialModel.getAssignment(x_i) == c_i)){
+ d_linEq.update(x_i, c_i);
+ }
+ }else{
+ d_errorSet.signalVariable(x_i);
+ }
+
+ if(TraceIsOn("model")) {
+ Trace("model") << "after" << endl;
+ d_partialModel.printModel(x_i);
+ d_tableau.debugPrintIsBasic(x_i);
+ }
+
+ return false;
+}
+
+
+/* procedure AssertDisequality( x_i != c_i ) */
+bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){
+ Assert(constraint != NullConstraint);
+ Assert(constraint->isDisequality());
+ Assert(constraint->isTrue());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+ const DeltaRational& c_i = constraint->getValue();
+ Trace("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
+
+ //Should be fine in integers
+ Assert(!isInteger(x_i) || c_i.isIntegral());
+
+ 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 ConstraintP lb = vc.getLowerBound();
+ const ConstraintP ub = vc.getUpperBound();
+ if(lb->isTrue() && ub->isTrue()){
+ ConstraintP eq = constraint->getNegation();
+ eq->impliedByTrichotomy(lb, ub, true);
+ raiseConflict(constraint, InferenceId::ARITH_CONF_TRICHOTOMY);
+ //in conflict
+ ++(d_statistics.d_statDisequalityConflicts);
+ return true;
+ }
+ }
+ if(vc.hasLowerBound() ){
+ const ConstraintP lb = vc.getLowerBound();
+ if(lb->isTrue()){
+ const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
+ Assert(!ub->isTrue());
+ Trace("arith::eq") << "propagate UpperBound " << constraint << lb << ub << endl;
+ const ConstraintP negUb = ub->getNegation();
+ if(!negUb->isTrue()){
+ negUb->impliedByTrichotomy(constraint, lb, false);
+ negUb->tryToPropagate();
+ d_learnedBounds.push_back(negUb);
+ }
+ }
+ }
+ if(vc.hasUpperBound()){
+ const ConstraintP ub = vc.getUpperBound();
+ if(ub->isTrue()){
+ const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
+ Assert(!lb->isTrue());
+
+ Trace("arith::eq") << "propagate LowerBound " << constraint << lb << ub << endl;
+ const ConstraintP negLb = lb->getNegation();
+ if(!negLb->isTrue()){
+ negLb->impliedByTrichotomy(constraint, ub, false);
+ negLb->tryToPropagate();
+ d_learnedBounds.push_back(negLb);
+ }
+ }
+ }
+
+ bool split = constraint->isSplit();
+
+ if(!split && c_i == d_partialModel.getAssignment(x_i)){
+ Trace("arith::eq") << "lemma now! " << constraint << endl;
+ outputTrustedLemma(constraint->split(), InferenceId::ARITH_SPLIT_DEQ);
+ return false;
+ }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
+ Trace("arith::eq") << "can drop as less than lb" << constraint << endl;
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
+ Trace("arith::eq") << "can drop as less than ub" << constraint << endl;
+ }else if(!split){
+ Trace("arith::eq") << "push back" << constraint << endl;
+ d_diseqQueue.push(constraint);
+ d_partialModel.invalidateDelta();
+ }else{
+ Trace("arith::eq") << "skipping already split " << constraint << endl;
+ }
+ return false;
+}
+
+void TheoryArithPrivate::notifySharedTerm(TNode n)
+{
+ Trace("arith::notifySharedTerm") << "notifySharedTerm: " << n << endl;
+ if(n.isConst()){
+ d_partialModel.invalidateDelta();
+ }
+ if(!n.isConst() && !isSetup(n)){
+ Polynomial poly = Polynomial::parsePolynomial(n);
+ Polynomial::iterator it = poly.begin();
+ Polynomial::iterator it_end = poly.end();
+ for (; it != it_end; ++ it) {
+ Monomial m = *it;
+ if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
+ setupVariableList(m.getVarList());
+ }
+ }
+ }
+}
+
+Node TheoryArithPrivate::getModelValue(TNode term) {
+ try{
+ const DeltaRational drv = getDeltaValue(term);
+ const Rational& delta = d_partialModel.getDelta();
+ const Rational qmodel = drv.substituteDelta( delta );
+ return NodeManager::currentNM()->mkConstRealOrInt(term.getType(), qmodel);
+ } catch (DeltaRationalException& dr) {
+ return Node::null();
+ } catch (ModelException& me) {
+ return Node::null();
+ }
+}
+
+Theory::PPAssertStatus TheoryArithPrivate::ppAssert(
+ TrustNode tin, TrustSubstitutionMap& outSubstitutions)
+{
+ TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
+ TNode in = tin.getNode();
+ Trace("simplify") << "TheoryArithPrivate::solve(" << in << ")" << endl;
+
+
+ // Solve equalities
+ Rational minConstant = 0;
+ Node minMonomial;
+ Node minVar;
+ if (in.getKind() == kind::EQUAL &&
+ Theory::theoryOf(in[0].getType()) == THEORY_ARITH) {
+ Comparison cmp = Comparison::parseNormalForm(in);
+
+ Polynomial left = cmp.getLeft();
+
+ Monomial m = left.getHead();
+ if (m.getVarList().singleton()){
+ VarList vl = m.getVarList();
+ Node var = vl.getNode();
+ if (var.isVar())
+ {
+ // if vl.isIntegral then m.getConstant().isOne()
+ if(!vl.isIntegral() || m.getConstant().isOne()){
+ minVar = var;
+ }
+ }
+ }
+
+ // Solve for variable
+ if (!minVar.isNull()) {
+ Polynomial right = cmp.getRight();
+ Node elim = right.getNode();
+ // ax + p = c -> (ax + p) -ax - c = -ax
+ // x = (p - ax - c) * -1/a
+ // Add the substitution if not recursive
+ Assert(elim == rewrite(elim));
+
+ if (right.size() > options().arith.ppAssertMaxSubSize)
+ {
+ Trace("simplify")
+ << "TheoryArithPrivate::solve(): did not substitute due to the "
+ "right hand side containing too many terms: "
+ << minVar << ":" << elim << endl;
+ Trace("simplify") << right.size() << endl;
+ }
+ else if (d_containing.isLegalElimination(minVar, elim))
+ {
+ // cannot eliminate integers here unless we know the resulting
+ // substitution is integral
+ Trace("simplify") << "TheoryArithPrivate::solve(): substitution "
+ << minVar << " |-> " << elim << endl;
+
+ outSubstitutions.addSubstitutionSolved(minVar, elim, tin);
+ return Theory::PP_ASSERT_STATUS_SOLVED;
+ }
+ else
+ {
+ Trace("simplify") << "TheoryArithPrivate::solve(): can't substitute "
+ << minVar << ":" << minVar.getType() << " |-> "
+ << elim << ":" << elim.getType() << endl;
+ }
+ }
+ }
+
+ // If a relation, remember the bound
+ switch(in.getKind()) {
+ case kind::LEQ:
+ case kind::LT:
+ case kind::GEQ:
+ case kind::GT:
+ if (in[0].isVar()) {
+ d_learner.addBound(in);
+ }
+ break;
+ default:
+ // Do nothing
+ break;
+ }
+
+ return Theory::PP_ASSERT_STATUS_UNSOLVED;
+}
+
+void TheoryArithPrivate::ppStaticLearn(TNode n, NodeBuilder& learned)
+{
+ TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
+
+ d_learner.staticLearning(n, learned);
+}
+
+ArithVar TheoryArithPrivate::findShortestBasicRow(ArithVar variable){
+ ArithVar bestBasic = ARITHVAR_SENTINEL;
+ uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
+
+ Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ Assert(entry.getColVar() == variable);
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+ uint32_t rowLength = d_tableau.getRowLength(ridx);
+ if((rowLength < bestRowLength) ||
+ (rowLength == bestRowLength && basic < bestBasic)){
+ bestBasic = basic;
+ bestRowLength = rowLength;
+ }
+ }
+ Assert(bestBasic == ARITHVAR_SENTINEL
+ || bestRowLength < std::numeric_limits<uint32_t>::max());
+ return bestBasic;
+}
+
+void TheoryArithPrivate::setupVariable(const Variable& x){
+ Node n = x.getNode();
+
+ Assert(!isSetup(n));
+
+ ++(d_statistics.d_statUserVariables);
+ requestArithVar(n, false, false);
+ //ArithVar varN = requestArithVar(n,false);
+ //setupInitialValue(varN);
+
+ markSetup(n);
+}
+
+void TheoryArithPrivate::setupVariableList(const VarList& vl){
+ Assert(!vl.empty());
+
+ TNode vlNode = vl.getNode();
+ Assert(!isSetup(vlNode));
+ Assert(!d_partialModel.hasArithVar(vlNode));
+
+ for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
+ Variable var = *i;
+
+ if(!isSetup(var.getNode())){
+ setupVariable(var);
+ }
+ }
+
+ if(!vl.singleton()){
+ // vl is the product of at least 2 variables
+ // vl : (* v1 v2 ...)
+ if (logicInfo().isLinear())
+ {
+ throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
+ }
+ d_foundNl = true;
+
+ ++(d_statistics.d_statUserVariables);
+ requestArithVar(vlNode, false, false);
+ //ArithVar av = requestArithVar(vlNode, false);
+ //setupInitialValue(av);
+
+ markSetup(vlNode);
+ }
+ else if (vlNode.getKind() == kind::EXPONENTIAL
+ || vlNode.getKind() == kind::SINE || vlNode.getKind() == kind::COSINE
+ || vlNode.getKind() == kind::TANGENT)
+ {
+ d_foundNl = true;
+ }
+
+ /* Note:
+ * Only call markSetup if the VarList is not a singleton.
+ * See the comment in setupPolynomail for more.
+ */
+}
+
+void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
+ if(p.containsConstant()){
+ if(!p.isConstant()){
+ Polynomial noConstant = p.getTail();
+ if(!isSetup(noConstant.getNode())){
+ setupPolynomial(noConstant);
+ }
+ }
+ }else if(!isSetup(p.getNode())){
+ setupPolynomial(p);
+ }
+}
+
+
+void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
+ Assert(!poly.containsConstant());
+ TNode polyNode = poly.getNode();
+ Assert(!isSetup(polyNode));
+ Assert(!d_partialModel.hasArithVar(polyNode));
+
+ for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
+ Monomial mono = *i;
+ const VarList& vl = mono.getVarList();
+ if(!isSetup(vl.getNode())){
+ setupVariableList(vl);
+ }
+ }
+
+ if (polyNode.getKind() == ADD)
+ {
+ d_tableauSizeHasBeenModified = true;
+
+ vector<ArithVar> variables;
+ vector<Rational> coefficients;
+ asVectors(poly, coefficients, variables);
+
+ ArithVar varSlack = requestArithVar(polyNode, true, false);
+ d_tableau.addRow(varSlack, coefficients, variables);
+ setupBasicValue(varSlack);
+ d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack));
+
+ //Add differences to the difference manager
+ Polynomial::iterator i = poly.begin(), end = poly.end();
+ if(i != end){
+ Monomial first = *i;
+ ++i;
+ if(i != end){
+ Monomial second = *i;
+ ++i;
+ if(i == end){
+ if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
+ VarList vl0 = first.getVarList();
+ VarList vl1 = second.getVarList();
+ if(vl0.singleton() && vl1.singleton()){
+ d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
+ }
+ }
+ }
+ }
+ }
+
+ ++(d_statistics.d_statAuxiliaryVariables);
+ markSetup(polyNode);
+ }
+
+ /* Note:
+ * It is worth documenting that polyNode should only be marked as
+ * being setup by this function if it has kind ADD.
+ * Other kinds will be marked as being setup by lower levels of setup
+ * specifically setupVariableList.
+ */
+}
+
+void TheoryArithPrivate::setupAtom(TNode atom) {
+ Assert(isRelationOperator(atom.getKind())) << atom;
+ Assert(Comparison::isNormalAtom(atom));
+ Assert(!isSetup(atom));
+ Assert(!d_constraintDatabase.hasLiteral(atom));
+
+ Comparison cmp = Comparison::parseNormalForm(atom);
+ Polynomial nvp = cmp.normalizedVariablePart();
+ Assert(!nvp.isZero());
+
+ if(!isSetup(nvp.getNode())){
+ setupPolynomial(nvp);
+ }
+
+ d_constraintDatabase.addLiteral(atom);
+
+ markSetup(atom);
+}
+
+void TheoryArithPrivate::preRegisterTerm(TNode n) {
+ Trace("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
+
+ d_preregisteredNodes.insert(n);
+
+ try {
+ if(isRelationOperator(n.getKind())){
+ if(!isSetup(n)){
+ setupAtom(n);
+ }
+ ConstraintP c = d_constraintDatabase.lookup(n);
+ Assert(c != NullConstraint);
+
+ Trace("arith::preregister") << "setup constraint" << c << endl;
+ Assert(!c->canBePropagated());
+ c->setPreregistered();
+ }
+ } catch(LogicException& le) {
+ std::stringstream ss;
+ ss << le.getMessage() << endl << "The fact in question: " << n << endl;
+ throw LogicException(ss.str());
+ }
+
+ Trace("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
+}
+
+void TheoryArithPrivate::releaseArithVar(ArithVar v){
+ //Assert(d_partialModel.hasNode(v));
+
+ d_constraintDatabase.removeVariable(v);
+ d_partialModel.releaseArithVar(v);
+}
+
+ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){
+ //TODO : The VarList trick is good enough?
+ Kind xk = x.getKind();
+ Assert(isLeaf(x) || VarList::isMember(x) || xk == ADD || internal);
+ if (logicInfo().isLinear()
+ && (Variable::isDivMember(x) || xk == IAND || isTranscendentalKind(xk)))
+ {
+ stringstream ss;
+ ss << "A non-linear fact was asserted to "
+ "arithmetic in a linear logic: "
+ << x << std::endl;
+ throw LogicException(ss.str());
+ }
+ Assert(!d_partialModel.hasArithVar(x));
+ Assert(x.getType().isRealOrInt()); // real or integer
+
+ ArithVar max = d_partialModel.getNumberOfVariables();
+ ArithVar varX = d_partialModel.allocate(x, aux);
+
+ bool reclaim = max >= d_partialModel.getNumberOfVariables();;
+
+ if(!reclaim){
+ d_dualSimplex.increaseMax();
+
+ d_tableau.increaseSize();
+ d_tableauSizeHasBeenModified = true;
+ }
+ d_constraintDatabase.addVariable(varX);
+
+ Trace("arith::arithvar") << "@" << context()->getLevel() << " " << x
+ << " |-> " << varX << "(relaiming " << reclaim << ")"
+ << endl;
+
+ Assert(!d_partialModel.hasUpperBound(varX));
+ Assert(!d_partialModel.hasLowerBound(varX));
+
+ return varX;
+}
+
+void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
+ for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
+ const Monomial& mono = *i;
+ const Constant& constant = mono.getConstant();
+ const VarList& variable = mono.getVarList();
+
+ Node n = variable.getNode();
+
+ Trace("arith::asVectors") << "should be var: " << n << endl;
+
+ // TODO: This VarList::isMember(n) can be stronger
+ Assert(isLeaf(n) || VarList::isMember(n));
+ Assert(theoryOf(n) != THEORY_ARITH || d_partialModel.hasArithVar(n));
+
+ Assert(d_partialModel.hasArithVar(n));
+ ArithVar av = d_partialModel.asArithVar(n);
+
+ coeffs.push_back(constant.getValue());
+ variables.push_back(av);
+ }
+}
+
+/* Requirements:
+ * For basic variables the row must have been added to the tableau.
+ */
+void TheoryArithPrivate::setupBasicValue(ArithVar x){
+ Assert(d_tableau.isBasic(x));
+ //If the variable is basic, assertions may have already happened and updates
+ //may have occured before setting this variable up.
+
+ //This can go away if the tableau creation is done at preregister
+ //time instead of register
+ DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
+ DeltaRational assignment = d_linEq.computeRowValue(x, false);
+ d_partialModel.setAssignment(x,safeAssignment,assignment);
+
+ Trace("arith") << "setupVariable("<<x<<")"<<std::endl;
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(const Polynomial& p) const{
+ Assert(!p.containsConstant());
+ Assert(p.getHead().constantIsPositive());
+ TNode n = p.getNode();
+ Trace("determineArithVar") << "determineArithVar(" << n << ")" << endl;
+ return d_partialModel.asArithVar(n);
+}
+
+ArithVar TheoryArithPrivate::determineArithVar(TNode assertion) const{
+ Trace("determineArithVar") << "determineArithVar " << assertion << endl;
+ Comparison cmp = Comparison::parseNormalForm(assertion);
+ Polynomial variablePart = cmp.normalizedVariablePart();
+ return determineArithVar(variablePart);
+}
+
+
+bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality){
+ Assert(equality.getKind() == EQUAL);
+ return d_partialModel.hasArithVar(equality[0]);
+}
+
+Comparison TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v){
+ const DeltaRational& beta = d_partialModel.getAssignment(v);
+
+ Assert(beta.isIntegral());
+ Polynomial betaAsPolynomial = Polynomial::mkPolynomial( Constant::mkConstant(beta.floor()) );
+
+ TNode var = d_partialModel.asNode(v);
+ Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
+ return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
+}
+
+TrustNode TheoryArithPrivate::dioCutting()
+{
+ context::Context::ScopedPush speculativePush(context());
+ //DO NOT TOUCH THE OUTPUTSTREAM
+
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+ if(isInteger(v)){
+ if(d_partialModel.cmpAssignmentUpperBound(v) == 0 ||
+ d_partialModel.cmpAssignmentLowerBound(v) == 0){
+ if(!d_partialModel.boundsAreEqual(v)){
+ // If the bounds are equal this is already in the dioSolver
+ //Add v = dr as a speculation.
+ Comparison eq = mkIntegerEqualityFromAssignment(v);
+ Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << endl;
+ Assert(!eq.isBoolean());
+ d_diosolver.pushInputConstraint(eq, eq.getNode());
+ // It does not matter what the explanation of eq is.
+ // It cannot be used in a conflict
+ }
+ }
+ }
+ }
+
+ SumPair plane = d_diosolver.processEquationsForCut();
+ if(plane.isZero()){
+ return TrustNode::null();
+ }else{
+ Polynomial p = plane.getPolynomial();
+ Polynomial c = Polynomial::mkPolynomial(plane.getConstant() * Constant::mkConstant(-1));
+ Integer gcd = p.gcd();
+ Assert(p.isIntegral());
+ Assert(c.isIntegral());
+ Assert(gcd > 1);
+ Assert(!gcd.divides(c.asConstant().getNumerator()));
+ Comparison leq = Comparison::mkComparison(LEQ, p, c);
+ Comparison geq = Comparison::mkComparison(GEQ, p, c);
+ Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
+ Node rewrittenLemma = rewrite(lemma);
+ Trace("arith::dio::ex") << "dioCutting found the plane: " << plane.getNode() << endl;
+ Trace("arith::dio::ex") << "resulting in the cut: " << lemma << endl;
+ Trace("arith::dio::ex") << "rewritten " << rewrittenLemma << endl;
+ Trace("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
+ Trace("arith::dio") << "resulting in the cut: " << lemma << endl;
+ Trace("arith::dio") << "rewritten " << rewrittenLemma << endl;
+ if (proofsEnabled())
+ {
+ NodeManager* nm = NodeManager::currentNM();
+ Node gt = nm->mkNode(kind::GT, p.getNode(), c.getNode());
+ Node lt = nm->mkNode(kind::LT, p.getNode(), c.getNode());
+ TypeNode type = gt[0].getType();
+
+ Pf pfNotLeq = d_pnm->mkAssume(leq.getNode().negate());
+ Pf pfGt =
+ d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotLeq}, {gt});
+ Pf pfNotGeq = d_pnm->mkAssume(geq.getNode().negate());
+ Pf pfLt =
+ d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotGeq}, {lt});
+ Pf pfSum = d_pnm->mkNode(
+ PfRule::MACRO_ARITH_SCALE_SUM_UB,
+ {pfGt, pfLt},
+ {nm->mkConstRealOrInt(type, -1), nm->mkConstRealOrInt(type, 1)});
+ Pf pfBot = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pfSum}, {nm->mkConst<bool>(false)});
+ std::vector<Node> assumptions = {leq.getNode().negate(),
+ geq.getNode().negate()};
+ Pf pfNotAndNot = d_pnm->mkScope(pfBot, assumptions);
+ Pf pfOr = d_pnm->mkNode(PfRule::NOT_AND, {pfNotAndNot}, {});
+ Pf pfRewritten = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pfOr}, {rewrittenLemma});
+ return d_pfGen->mkTrustNode(rewrittenLemma, pfRewritten);
+ }
+ else
+ {
+ return TrustNode::mkTrustLemma(rewrittenLemma, nullptr);
+ }
+ }
+}
+
+Node TheoryArithPrivate::callDioSolver(){
+ while(!d_constantIntegerVariables.empty()){
+ ArithVar v = d_constantIntegerVariables.front();
+ d_constantIntegerVariables.pop();
+
+ Trace("arith::dio") << "callDioSolver " << v << endl;
+
+ Assert(isInteger(v));
+ Assert(d_partialModel.boundsAreEqual(v));
+
+ ConstraintP lb = d_partialModel.getLowerBoundConstraint(v);
+ ConstraintP ub = d_partialModel.getUpperBoundConstraint(v);
+
+ Node orig = Node::null();
+ if(lb->isEquality()){
+ orig = Constraint::externalExplainByAssertions({lb});
+ }else if(ub->isEquality()){
+ orig = Constraint::externalExplainByAssertions({ub});
+ }else {
+ orig = Constraint::externalExplainByAssertions(ub, lb);
+ }
+
+ Assert(d_partialModel.assignmentIsConsistent(v));
+
+ Comparison eq = mkIntegerEqualityFromAssignment(v);
+
+ if(eq.isBoolean()){
+ //This can only be a conflict
+ Assert(!eq.getNode().getConst<bool>());
+
+ //This should be handled by the normal form earlier in the case of equality
+ Assert(orig.getKind() != EQUAL);
+ return orig;
+ }else{
+ Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << " with reason " << orig << endl;
+ d_diosolver.pushInputConstraint(eq, orig);
+ }
+ }
+
+ return d_diosolver.processEquationsForConflict();
+}
+
+ConstraintP TheoryArithPrivate::constraintFromFactQueue(TNode assertion)
+{
+ Kind simpleKind = Comparison::comparisonKind(assertion);
+ ConstraintP constraint = d_constraintDatabase.lookup(assertion);
+ if(constraint == NullConstraint){
+ Assert(simpleKind == EQUAL || simpleKind == DISTINCT);
+ bool isDistinct = simpleKind == DISTINCT;
+ Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
+ Assert(!isSetup(eq));
+ Node reEq = rewrite(eq);
+ Trace("arith::distinct::const") << "Assertion: " << assertion << std::endl;
+ Trace("arith::distinct::const") << "Eq : " << eq << std::endl;
+ Trace("arith::distinct::const") << "reEq : " << reEq << std::endl;
+ if(reEq.getKind() == CONST_BOOLEAN){
+ if(reEq.getConst<bool>() == isDistinct){
+ // if is (not true), or false
+ Assert((reEq.getConst<bool>() && isDistinct)
+ || (!reEq.getConst<bool>() && !isDistinct));
+ if (proofsEnabled())
+ {
+ Pf assume = d_pnm->mkAssume(assertion);
+ std::vector<Node> assumptions = {assertion};
+ Pf pf = d_pnm->mkScope(d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkAssume(assertion)},
+ {}),
+ assumptions);
+ raiseBlackBoxConflict(assertion, pf);
+ }
+ else
+ {
+ raiseBlackBoxConflict(assertion);
+ }
+ }
+ return NullConstraint;
+ }
+ Assert(reEq.getKind() != CONST_BOOLEAN);
+ if(!isSetup(reEq)){
+ setupAtom(reEq);
+ }
+ Node reAssertion = isDistinct ? reEq.notNode() : reEq;
+ constraint = d_constraintDatabase.lookup(reAssertion);
+
+ if(assertion != reAssertion){
+ Trace("arith::nf") << "getting non-nf assertion " << assertion << " |-> " << reAssertion << endl;
+ Assert(constraint != NullConstraint);
+ d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
+ }
+ }
+
+ Assert(constraint != NullConstraint);
+
+ if(constraint->assertedToTheTheory()){
+ //Do nothing
+ return NullConstraint;
+ }
+ Assert(!constraint->assertedToTheTheory());
+ bool inConflict = constraint->negationHasProof();
+ constraint->setAssertedToTheTheory(assertion, inConflict);
+
+ if(!constraint->hasProof()){
+ Trace("arith::constraint") << "marking as constraint as self explaining " << endl;
+ constraint->setAssumption(inConflict);
+ } else {
+ Trace("arith::constraint")
+ << "already has proof: "
+ << Constraint::externalExplainByAssertions({constraint});
+ }
+
+ if(TraceIsOn("arith::negatedassumption") && inConflict){
+ ConstraintP negation = constraint->getNegation();
+ if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
+ debugPrintFacts();
+ }
+ Trace("arith::eq") << "negation has proof" << endl;
+ Trace("arith::eq") << constraint << endl;
+ Trace("arith::eq") << negation << endl;
+ }
+
+ if(inConflict){
+ ConstraintP negation = constraint->getNegation();
+ if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
+ debugPrintFacts();
+ }
+ Trace("arith::eq") << "negation has proof" << endl;
+ Trace("arith::eq") << constraint << endl;
+ Trace("arith::eq") << negation << endl;
+ raiseConflict(negation, InferenceId::ARITH_CONF_FACT_QUEUE);
+ return NullConstraint;
+ }else{
+ return constraint;
+ }
+}
+
+bool TheoryArithPrivate::assertionCases(ConstraintP constraint){
+ Assert(constraint->hasProof());
+ Assert(!constraint->negationHasProof());
+
+ ArithVar x_i = constraint->getVariable();
+
+ switch(constraint->getType()){
+ case UpperBound:
+ if(isInteger(x_i) && constraint->isStrictUpperBound()){
+ ConstraintP floorConstraint = constraint->getFloor();
+ if(!floorConstraint->isTrue()){
+ bool inConflict = floorConstraint->negationHasProof();
+ if (TraceIsOn("arith::intbound")) {
+ Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
+ Trace("arith::intbound") << "constraint, after: " << floorConstraint << std::endl;
+ }
+ floorConstraint->impliedByIntTighten(constraint, inConflict);
+ floorConstraint->tryToPropagate();
+ if(inConflict){
+ raiseConflict(floorConstraint, InferenceId::ARITH_TIGHTEN_FLOOR);
+ return true;
+ }
+ }
+ return AssertUpper(floorConstraint);
+ }else{
+ return AssertUpper(constraint);
+ }
+ case LowerBound:
+ if(isInteger(x_i) && constraint->isStrictLowerBound()){
+ ConstraintP ceilingConstraint = constraint->getCeiling();
+ if(!ceilingConstraint->isTrue()){
+ bool inConflict = ceilingConstraint->negationHasProof();
+ if (TraceIsOn("arith::intbound")) {
+ Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
+ Trace("arith::intbound") << "constraint, after: " << ceilingConstraint << std::endl;
+ }
+ ceilingConstraint->impliedByIntTighten(constraint, inConflict);
+ ceilingConstraint->tryToPropagate();
+ if(inConflict){
+ raiseConflict(ceilingConstraint, InferenceId::ARITH_TIGHTEN_CEIL);
+ return true;
+ }
+ }
+ return AssertLower(ceilingConstraint);
+ }else{
+ return AssertLower(constraint);
+ }
+ case Equality:
+ return AssertEquality(constraint);
+ case Disequality:
+ return AssertDisequality(constraint);
+ default:
+ Unreachable();
+ return false;
+ }
+}
+/**
+ * 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 TheoryArithPrivate::nextIntegerViolation(bool assumeBounds) const
+{
+ ArithVar numVars = d_partialModel.getNumberOfVariables();
+ ArithVar v = d_nextIntegerCheckVar;
+ if (numVars > 0)
+ {
+ const ArithVar rrEnd = d_nextIntegerCheckVar;
+ do
+ {
+ if (isIntegerInput(v))
+ {
+ if (!d_partialModel.integralAssignment(v))
+ {
+ if (assumeBounds || d_partialModel.assignmentIsConsistent(v))
+ {
+ return v;
+ }
+ }
+ }
+ 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 = nextIntegerViolation(true);
+ if (next != ARITHVAR_SENTINEL)
+ {
+ d_nextIntegerCheckVar = next;
+ if (TraceIsOn("arith::hasIntegerModel"))
+ {
+ Trace("arith::hasIntegerModel") << "has int model? " << next << endl;
+ d_partialModel.printModel(next, Trace("arith::hasIntegerModel"));
+ }
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+Node flattenAndSort(Node n){
+ Kind k = n.getKind();
+ switch(k){
+ case kind::OR:
+ case kind::AND:
+ case kind::ADD:
+ case kind::MULT:
+ break;
+ default:
+ return n;
+ }
+
+ std::vector<Node> out;
+ std::vector<Node> process;
+ process.push_back(n);
+ while(!process.empty()){
+ Node b = process.back();
+ process.pop_back();
+ if(b.getKind() == k){
+ for(Node::iterator i=b.begin(), end=b.end(); i!=end; ++i){
+ process.push_back(*i);
+ }
+ } else {
+ out.push_back(b);
+ }
+ }
+ Assert(out.size() >= 2);
+ std::sort(out.begin(), out.end());
+ return NodeManager::currentNM()->mkNode(k, out);
+}
+
+
+
+/** Outputs conflicts to the output channel. */
+void TheoryArithPrivate::outputConflicts(){
+ Trace("arith::conflict") << "outputting conflicts" << std::endl;
+ Assert(anyConflict());
+
+ if(!conflictQueueEmpty()){
+ Assert(!d_conflicts.empty());
+ for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
+ const std::pair<ConstraintCP, InferenceId>& conf = d_conflicts[i];
+ const ConstraintCP& confConstraint = conf.first;
+ bool hasProof = confConstraint->hasProof();
+ Assert(confConstraint->inConflict());
+ const ConstraintRule& pf = confConstraint->getConstraintRule();
+ if (TraceIsOn("arith::conflict"))
+ {
+ pf.print(std::cout, options().smt.produceProofs);
+ std::cout << std::endl;
+ }
+ if (TraceIsOn("arith::pf::tree"))
+ {
+ Trace("arith::pf::tree") << "\n\nTree:\n";
+ confConstraint->printProofTree(Trace("arith::pf::tree"));
+ confConstraint->getNegation()->printProofTree(Trace("arith::pf::tree"));
+ }
+
+ TrustNode trustedConflict = confConstraint->externalExplainConflict();
+ Node conflict = trustedConflict.getNode();
+
+ Trace("arith::conflict")
+ << "d_conflicts[" << i << "] " << conflict
+ << " has proof: " << hasProof << ", id = " << conf.second << endl;
+ if(TraceIsOn("arith::normalize::external")){
+ conflict = flattenAndSort(conflict);
+ Trace("arith::conflict") << "(normalized to) " << conflict << endl;
+ }
+
+ if (isProofEnabled())
+ {
+ outputTrustedConflict(trustedConflict, conf.second);
+ }
+ else
+ {
+ outputConflict(conflict, conf.second);
+ }
+ }
+ }
+ if(!d_blackBoxConflict.get().isNull()){
+ Node bb = d_blackBoxConflict.get();
+ Trace("arith::conflict") << "black box conflict" << bb
+ << endl;
+ if(TraceIsOn("arith::normalize::external")){
+ bb = flattenAndSort(bb);
+ Trace("arith::conflict") << "(normalized to) " << bb << endl;
+ }
+ if (isProofEnabled() && d_blackBoxConflictPf.get())
+ {
+ auto confPf = d_blackBoxConflictPf.get();
+ outputTrustedConflict(d_pfGen->mkTrustNode(bb, confPf, true), InferenceId::ARITH_BLACK_BOX);
+ }
+ else
+ {
+ outputConflict(bb, InferenceId::ARITH_BLACK_BOX);
+ }
+ }
+}
+
+bool TheoryArithPrivate::outputTrustedLemma(TrustNode lemma, InferenceId id)
+{
+ Trace("arith::channel") << "Arith trusted lemma: " << lemma << std::endl;
+ return d_containing.d_im.trustedLemma(lemma, id);
+}
+
+bool TheoryArithPrivate::outputLemma(TNode lem, InferenceId id) {
+ Trace("arith::channel") << "Arith lemma: " << lem << std::endl;
+ return d_containing.d_im.lemma(lem, id);
+}
+
+void TheoryArithPrivate::outputTrustedConflict(TrustNode conf, InferenceId id)
+{
+ Trace("arith::channel") << "Arith trusted conflict: " << conf << std::endl;
+ d_containing.d_im.trustedConflict(conf, id);
+}
+
+void TheoryArithPrivate::outputConflict(TNode lit, InferenceId id) {
+ Trace("arith::channel") << "Arith conflict: " << lit << std::endl;
+ d_containing.d_im.conflict(lit, id);
+}
+
+void TheoryArithPrivate::outputPropagate(TNode lit) {
+ Trace("arith::channel") << "Arith propagation: " << lit << std::endl;
+ // call the propagate lit method of the
+ d_containing.d_im.propagateLit(lit);
+}
+
+void TheoryArithPrivate::outputRestart() {
+ Trace("arith::channel") << "Arith restart!" << std::endl;
+ (d_containing.d_out)->demandRestart();
+}
+
+bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){
+ int level = context()->getLevel();
+ Trace("approx")
+ << "attemptSolveInteger " << d_qflraStatus
+ << " " << emmmittedLemmaOrSplit
+ << " " << effortLevel
+ << " " << d_lastContextIntegerAttempted
+ << " " << level
+ << endl;
+
+ if(d_qflraStatus == Result::UNSAT){ return false; }
+ if(emmmittedLemmaOrSplit){ return false; }
+ if (!options().arith.useApprox)
+ {
+ return false;
+ }
+ if(!ApproximateSimplex::enabled()){ return false; }
+
+ if(Theory::fullEffort(effortLevel)){
+ if(hasIntegerModel()){
+ return false;
+ }else{
+ return getSolveIntegerResource();
+ }
+ }
+
+ if(d_lastContextIntegerAttempted <= 0){
+ if(hasIntegerModel()){
+ d_lastContextIntegerAttempted = context()->getLevel();
+ return false;
+ }else{
+ return getSolveIntegerResource();
+ }
+ }
+
+ if (!options().arith.trySolveIntStandardEffort)
+ {
+ return false;
+ }
+
+ if (d_lastContextIntegerAttempted <= (level >> 2))
+ {
+ double d = (double)(d_solveIntMaybeHelp + 1)
+ / (d_solveIntAttempts + 1 + level * level);
+ if (Random::getRandom().pickWithProb(d))
+ {
+ return getSolveIntegerResource();
+ }
+ }
+ return false;
+}
+
+bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer);
+
+ ++d_statistics.d_mipProofsAttempted;
+
+ Assert(d_replayVariables.empty());
+ Assert(d_replayConstraints.empty());
+
+ size_t enteringPropN = d_currentPropagationList.size();
+ Assert(conflictQueueEmpty());
+ TreeLog& tl = getTreeLog();
+ //tl.applySelected(); /* set row ids */
+
+ d_replayedLemmas = false;
+
+ /* use the try block for the purpose of pushing the sat context */
+ context::Context::ScopedPush speculativePush(context());
+ d_cmEnabled = false;
+ std::vector<ConstraintCPVec> res =
+ replayLogRec(approx, tl.getRootId(), NullConstraint, 1);
+
+ if(res.empty()){
+ ++d_statistics.d_replayAttemptFailed;
+ }else{
+ unsigned successes = 0;
+ for(size_t i =0, N = res.size(); i < N; ++i){
+ ConstraintCPVec& vec = res[i];
+ Assert(vec.size() >= 2);
+ for(size_t j=0, M = vec.size(); j < M; ++j){
+ ConstraintCP at_j = vec[j];
+ Assert(at_j->isTrue());
+ if(!at_j->negationHasProof()){
+ successes++;
+ vec[j] = vec.back();
+ vec.pop_back();
+ ConstraintP neg_at_j = at_j->getNegation();
+
+ Trace("approx::replayLog") << "Setting the proof for the replayLog conflict on:" << endl
+ << " (" << neg_at_j->isTrue() <<") " << neg_at_j << endl
+ << " (" << at_j->isTrue() <<") " << at_j << endl;
+ neg_at_j->impliedByIntHole(vec, true);
+ raiseConflict(at_j, InferenceId::ARITH_CONF_REPLAY_LOG);
+ break;
+ }
+ }
+ }
+ if(successes > 0){
+ ++d_statistics.d_mipProofsSuccessful;
+ }
+ }
+
+ if(d_currentPropagationList.size() > enteringPropN){
+ d_currentPropagationList.resize(enteringPropN);
+ }
+
+ /* It is not clear what the d_qflraStatus is at this point */
+ d_qflraStatus = Result::UNKNOWN;
+
+ 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); }
+
+ Trace("approx::constraint") << "replayGetConstraint " << sum
+ << " " << k
+ << " " << rhs
+ << endl;
+
+ Assert(k == kind::LEQ || k == kind::GEQ);
+
+ NodeManager* nm = NodeManager::currentNM();
+ Node comparison =
+ nm->mkNode(k, sum, nm->mkConstRealOrInt(sum.getType(), rhs));
+ Node rewritten = rewrite(comparison);
+ if(!(Comparison::isNormalAtom(rewritten))){
+ return make_pair(NullConstraint, added);
+ }
+
+ Comparison cmp = Comparison::parseNormalForm(rewritten);
+ if(cmp.isBoolean()){ return make_pair(NullConstraint, added); }
+
+ Polynomial nvp = cmp.normalizedVariablePart();
+ if(nvp.isZero()){ return make_pair(NullConstraint, added); }
+
+ Node norm = nvp.getNode();
+
+ ConstraintType t = Constraint::constraintTypeOfComparison(cmp);
+ DeltaRational dr = cmp.normalizedDeltaRational();
+
+ Trace("approx::constraint") << "rewriting " << rewritten << endl
+ << " |-> " << norm << " " << t << " " << dr << endl;
+
+ Assert(!branch || d_partialModel.hasArithVar(norm));
+ ArithVar v = ARITHVAR_SENTINEL;
+ if(d_partialModel.hasArithVar(norm)){
+
+ v = d_partialModel.asArithVar(norm);
+ Trace("approx::constraint")
+ << "replayGetConstraint found " << norm << " |-> " << v << " @ "
+ << context()->getLevel() << endl;
+ Assert(!branch || d_partialModel.isIntegerInput(v));
+ }else{
+ v = requestArithVar(norm, true, true);
+ d_replayVariables.push_back(v);
+
+ added = v;
+
+ Trace("approx::constraint")
+ << "replayGetConstraint adding " << norm << " |-> " << v << " @ "
+ << context()->getLevel() << endl;
+
+ 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)
+{
+ 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();
+ std::optional<Rational> maybe_value =
+ ApproximateSimplex::estimateWithCFE(dval);
+ if (!maybe_value)
+ {
+ return make_pair(NullConstraint, ARITHVAR_SENTINEL);
+ }
+ Rational fl(maybe_value.value().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 toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum){
+ Trace("arith::toSumNode") << "toSumNode() begin" << endl;
+ NodeManager* nm = NodeManager::currentNM();
+ DenseMap<Rational>::const_iterator iter, end;
+ iter = sum.begin(), end = sum.end();
+ std::vector<Node> children;
+ for(; iter != end; ++iter){
+ ArithVar x = *iter;
+ if(!vars.hasNode(x)){ return Node::null(); }
+ Node xNode = vars.asNode(x);
+ const Rational& q = sum[x];
+ Node mult = nm->mkNode(kind::MULT, mkRationalNode(q), xNode);
+ Trace("arith::toSumNode") << "toSumNode() " << x << " " << mult << endl;
+ children.push_back(mult);
+ }
+ Trace("arith::toSumNode") << "toSumNode() end" << endl;
+ if (children.empty())
+ {
+ // NOTE: real type assumed here
+ return nm->mkConstReal(Rational(0));
+ }
+ else if (children.size() == 1)
+ {
+ return children[0];
+ }
+ return nm->mkNode(kind::ADD, children);
+}
+
+ConstraintCP TheoryArithPrivate::vectorToIntHoleConflict(const ConstraintCPVec& conflict){
+ Assert(conflict.size() >= 2);
+ ConstraintCPVec exp(conflict.begin(), conflict.end()-1);
+ ConstraintCP back = conflict.back();
+ Assert(back->hasProof());
+ ConstraintP negBack = back->getNegation();
+ // This can select negBack multiple times so we need to test if negBack has a proof.
+ if(negBack->hasProof()){
+ // back is in conflict already
+ } else {
+ negBack->impliedByIntHole(exp, true);
+ }
+
+ return back;
+}
+
+void TheoryArithPrivate::intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict){
+ ConstraintCP negConflicting = conflicting->getNegation();
+ Assert(conflicting->hasProof());
+ Assert(negConflicting->hasProof());
+
+ conflict.push_back(conflicting);
+ conflict.push_back(negConflicting);
+
+ Constraint::assertionFringe(conflict);
+}
+
+void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){
+ Assert(conflictQueueEmpty());
+ std::vector< ConstraintCPVec > conflicts;
+
+ approx->tryCut(nid, bci);
+ Trace("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(context());
+ 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);
+ // Can change d_qflraStatus
+
+ d_linEq.stopTrackingBoundCounts();
+ d_partialModel.startQueueingBoundCounts();
+ }
+ for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
+
+ conflicts.push_back(ConstraintCPVec());
+ intHoleConflictToVector(d_conflicts[i].first, conflicts.back());
+ Constraint::assertionFringe(conflicts.back());
+
+ // ConstraintCP conflicting = d_conflicts[i];
+ // ConstraintCP negConflicting = conflicting->getNegation();
+ // Assert(conflicting->hasProof());
+ // Assert(negConflicting->hasProof());
+
+ // conflicts.push_back(ConstraintCPVec());
+ // ConstraintCPVec& back = conflicts.back();
+ // back.push_back(conflicting);
+ // back.push_back(negConflicting);
+
+ // // remove the floor/ceiling contraint implied by bcneg
+ // Constraint::assertionFringe(back);
+ }
+
+ if(TraceIsOn("approx::branch")){
+ if(d_conflicts.empty()){
+ entireStateIsConsistent("branchfailure");
+ }
+ }
+ }
+
+ Trace("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)){
+ Trace("approx::branch") << "reraise " << conf << endl;
+ ConstraintCP conflicting = vectorToIntHoleConflict(conf);
+ raiseConflict(conflicting, InferenceId::ARITH_CONF_BRANCH_CUT);
+ }else if(!bci.proven()){
+ drop(conf, bcneg);
+ bci.setExplanation(conf);
+ Trace("approx::branch") << "dropped " << bci << endl;
+ }
+ }
+}
+
+void TheoryArithPrivate::replayAssert(ConstraintP c) {
+ if(!c->assertedToTheTheory()){
+ bool inConflict = c->negationHasProof();
+ if(!c->hasProof()){
+ c->setInternalAssumption(inConflict);
+ Trace("approx::replayAssert") << "replayAssert " << c << " set internal" << endl;
+ }else{
+ Trace("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl;
+ }
+ Trace("approx::replayAssert") << "replayAssertion " << c << endl;
+ if(inConflict){
+ raiseConflict(c, InferenceId::ARITH_CONF_REPLAY_ASSERT);
+ }else{
+ assertionCases(c);
+ }
+ }else{
+ Trace("approx::replayAssert")
+ << "replayAssert " << c << " already asserted" << endl;
+ }
+}
+
+
+void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const {
+ Trace("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);
+ Trace("arith::resolveOutPropagated")
+ << " conf["<<i<<"] " << orig << " to " << conf.size() << endl;
+ }
+ Trace("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 CVC5_UNUSED = 0;
+ int subsumed CVC5_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++) {
+ // i is not subsumed
+ for (size_t j = i + 1; j < confs.size();) {
+ ConstraintCPVec& a = confs[i];
+ 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++;
+ }
+ }
+ }
+ Trace("arith::subsumption") << "subsumed " << subsumed << "/" << checks
+ << endl;
+}
+
+std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){
+ ++(d_statistics.d_replayLogRecCount);
+ Trace("approx::replayLogRec") << "replayLogRec()" << 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(context());
+ 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;
+ if(!(conflictQueueEmpty() || ci->reconstructed())){
+ ++d_statistics.d_numBranchesFailed;
+ }
+ }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().arith.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(TraceIsOn("approx::replayLogRec")){
+ Trace("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl;
+ }
+
+ if(ci->proven()){
+ ++d_statistics.d_cutsProven;
+
+ const ConstraintCPVec& exp = ci->getExplanation();
+ // success
+ if(con->isTrue()){
+ Trace("approx::replayLogRec") << "not asserted?" << endl;
+ }else if(!con->negationHasProof()){
+ con->impliedByIntHole(exp, false);
+ replayAssert(con);
+ Trace("approx::replayLogRec") << "cut prop" << endl;
+ }else {
+ con->impliedByIntHole(exp, true);
+ Trace("approx::replayLogRec") << "cut into conflict " << con << endl;
+ raiseConflict(con, InferenceId::ARITH_CONF_REPLAY_LOG_REC);
+ }
+ }else{
+ ++d_statistics.d_cutsProofFailed;
+ Trace("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().arith.replayEarlyCloseDepths >= 1);
+ if (!nl.isBranch() || depth % options().arith.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);
+ // can change d_qflraStatus
+
+ 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(ConstraintCPVec());
+ intHoleConflictToVector(d_conflicts[i].first, res.back());
+ }
+ ++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{
+ res.push_back(conf);
+ }
+ }
+ }else{
+ Trace("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl;
+ ++d_statistics.d_replayBranchSkips;
+ }
+
+ if(res.empty()){
+ upres = replayLogRec(approx, upid, upc, depth+1);
+
+ 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{
+ Trace("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{
+ Trace("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl;
+ }
+ Trace("approx::replayLogRec") << "found #"<<res.size()<<" conflicts on branch " << nid << endl;
+ if(res.empty()){
+ ++d_statistics.d_replayBranchCloseFailures;
+ }
+
+ }else{
+ Trace("approx::replayLogRec") << "failed to make a branch " << nid << endl;
+ }
+ }else{
+ ++d_statistics.d_replayLeafCloseFailures;
+ Trace("approx::replayLogRec") << "failed on node " << nid << endl;
+ Assert(res.empty());
+ }
+ resolveOutPropagated(res, propagated);
+ Trace("approx::replayLogRec") << "replayLogRec() ending" << std::endl;
+
+ if (options().arith.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()){
+ TrustNode 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()){
+ TrustNode 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. */
+ auto ci = d_tableau.colIterator(v);
+ Assert(!ci.atEnd());
+ ArithVar b = d_tableau.rowIndexToBasic((*ci).getRowIndex());
+ 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);
+ Trace("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
+{
+ 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();
+ std::optional<Rational> maybe_value =
+ ApproximateSimplex::estimateWithCFE(dval);
+ if (!maybe_value)
+ {
+ return Node::null();
+ }
+ Rational fl(maybe_value.value().floor());
+ NodeManager* nm = NodeManager::currentNM();
+ Node leq =
+ nm->mkNode(kind::LEQ, n, nm->mkConstRealOrInt(n.getType(), fl));
+ Node norm = 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()){
+ NodeManager* nm = NodeManager::currentNM();
+ Kind k = ci.getKind();
+ Assert(k == kind::LEQ || k == kind::GEQ);
+ Node rhs = nm->mkConstRealOrInt(sum.getType(), ci.getReconstruction().rhs);
+ Node ineq = nm->mkNode(k, sum, rhs);
+ return rewrite(ineq);
+ }
+ return Node::null();
+}
+
+bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){
+ ++(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().arith.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 = rewrite(cutConstraint);
+ anythingnew = anythingnew || !isSatLiteral(implied);
+
+ Node implication = asLemma.impNode(implied);
+ // DO NOT CALL OUTPUT LEMMA!
+ // TODO (project #37): justify
+ d_approxCuts.push_back(TrustNode::mkTrustLemma(implication, nullptr));
+ Trace("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());
+ if (proofsEnabled())
+ {
+ d_pfGen->mkTrustNode(branch, PfRule::SPLIT, {}, {lit});
+ }
+ else
+ {
+ d_approxCuts.push_back(TrustNode::mkTrustLemma(branch, nullptr));
+ }
+ ++(d_statistics.d_mipExternalBranch);
+ Trace("approx::lemmas") << "branching "<< root <<" as " << branch << endl;
+ }
+ }
+ return anythingnew;
+}
+
+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 codeTimer0(d_statistics.d_solveIntTimer);
+
+ ++(d_statistics.d_solveIntCalls);
+ d_statistics.d_inSolveInteger = 1;
+
+ if(!Theory::fullEffort(effortLevel)){
+ d_solveIntAttempts++;
+ ++(d_statistics.d_solveStandardEffort);
+ }
+
+ // if integers are attempted,
+ Assert(options().arith.useApprox);
+ Assert(ApproximateSimplex::enabled());
+
+ int level = context()->getLevel();
+ d_lastContextIntegerAttempted = level;
+
+ static constexpr int32_t mipLimit = 200000;
+
+ TreeLog& tl = getTreeLog();
+ ApproximateStatistics& stats = getApproxStats();
+ ApproximateSimplex* approx =
+ ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
+
+ approx->setPivotLimit(mipLimit);
+ if(!d_guessedCoeffSet){
+ d_guessedCoeffs = approx->heuristicOptCoeffs();
+ d_guessedCoeffSet = true;
+ }
+ if(!d_guessedCoeffs.empty()){
+ approx->setOptCoeffs(d_guessedCoeffs);
+ }
+ static constexpr int32_t depthForLikelyInfeasible = 10;
+ int maxDepthPass1 = d_likelyIntegerInfeasible
+ ? depthForLikelyInfeasible
+ : options().arith.maxApproxDepth;
+ approx->setBranchingDepth(maxDepthPass1);
+ approx->setBranchOnVariableLimit(100);
+ LinResult relaxRes = approx->solveRelaxation();
+ if( relaxRes == LinFeasible ){
+ MipResult mipRes = MipUnknown;
+ {
+ TimerStat::CodeTimer codeTimer1(d_statistics.d_mipTimer);
+ mipRes = approx->solveMIP(false);
+ }
+
+ Trace("arith::solveInteger") << "mipRes " << mipRes << endl;
+ switch(mipRes) {
+ case MipBingo:
+ // attempt the solution
+ {
+ ++(d_statistics.d_solveIntModelsAttempts);
+
+ d_partialModel.stopQueueingBoundCounts();
+ UpdateTrackingCallback utcb(&d_linEq);
+ d_partialModel.processBoundsQueue(utcb);
+ d_linEq.startTrackingBoundCounts();
+
+ ApproximateSimplex::Solution mipSolution;
+ mipSolution = approx->extractMIP();
+ importSolution(mipSolution);
+ solveRelaxationOrPanic(effortLevel);
+
+ if (d_qflraStatus == Result::SAT)
+ {
+ if (!anyConflict())
+ {
+ if (ARITHVAR_SENTINEL == nextIntegerViolation(false))
+ {
+ ++(d_statistics.d_solveIntModelsSuccessful);
+ }
+ }
+ }
+
+ // shutdown simplex
+ d_linEq.stopTrackingBoundCounts();
+ d_partialModel.startQueueingBoundCounts();
+ }
+ break;
+ case MipClosed:
+ /* All integer branches closed */
+ approx->setPivotLimit(2*mipLimit);
+ {
+ TimerStat::CodeTimer codeTimer2(d_statistics.d_mipTimer);
+ mipRes = approx->solveMIP(true);
+ }
+
+ if(mipRes == MipClosed){
+ d_likelyIntegerInfeasible = true;
+ replayLog(approx);
+ AlwaysAssert(anyConflict() || d_qflraStatus != Result::SAT);
+
+ if (!anyConflict())
+ {
+ solveRealRelaxation(effortLevel);
+ }
+ }
+ if(!(anyConflict() || !d_approxCuts.empty())){
+ turnOffApproxFor(options().arith.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);
+ {
+ TimerStat::CodeTimer codeTimer3(d_statistics.d_mipTimer);
+ mipRes = approx->solveMIP(true);
+ }
+ replayLemmas(approx);
+ break;
+ case MipUnknown:
+ break;
+ }
+ }
+ delete approx;
+
+ if(!Theory::fullEffort(effortLevel)){
+ if(anyConflict() || !d_approxCuts.empty()){
+ d_solveIntMaybeHelp++;
+ }
+ }
+
+ d_statistics.d_inSolveInteger = 0;
+}
+
+SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){
+ if(pass1){
+ if(d_pass1SDP == NULL){
+ if (options().arith.useFC)
+ {
+ d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
+ }
+ else if (options().arith.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().arith.useFC)
+ {
+ d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
+ }
+ else if (options().arith.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(TraceIsOn("arith::importSolution")){
+ Trace("arith::importSolution") << "importSolution before " << d_qflraStatus << endl;
+ d_partialModel.printEntireModel(Trace("arith::importSolution"));
+ }
+
+ d_qflraStatus = d_attemptSolSimplex.attempt(solution);
+
+ if(TraceIsOn("arith::importSolution")){
+ Trace("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl;
+ d_partialModel.printEntireModel(Trace("arith::importSolution"));
+ }
+
+ if(d_qflraStatus != Result::UNSAT){
+ static constexpr int64_t pass2Limit = 20;
+ SimplexDecisionProcedure& simplex = selectSimplex(false);
+ simplex.setVarOrderPivotLimit(pass2Limit);
+ d_qflraStatus = simplex.findModel(false);
+ }
+
+ if(TraceIsOn("arith::importSolution")){
+ Trace("arith::importSolution") << "importSolution after " << d_qflraStatus << endl;
+ d_partialModel.printEntireModel(Trace("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::UNKNOWN)
+ {
+ d_qflraStatus = selectSimplex(true).findModel(false);
+ }
+
+ if (Theory::fullEffort(effortLevel) && d_qflraStatus == Result::UNKNOWN)
+ {
+ ArithVar canBranch = nextIntegerViolation(false);
+ if (canBranch != ARITHVAR_SENTINEL)
+ {
+ ++d_statistics.d_panicBranches;
+ TrustNode branch = branchIntegerVariable(canBranch);
+ Assert(branch.getNode().getKind() == kind::OR);
+ Node rwbranch = rewrite(branch.getNode()[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 codeTimer0(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().arith.restrictedPivots;
+
+ SimplexDecisionProcedure& simplex = selectSimplex(true);
+
+ bool useApprox = options().arith.useApprox && ApproximateSimplex::enabled()
+ && getSolveIntegerResource();
+
+ Trace("TheoryArithPrivate::solveRealRelaxation")
+ << "solveRealRelaxation() approx"
+ << " " << options().arith.useApprox << " "
+ << ApproximateSimplex::enabled() << " " << useApprox << " "
+ << safeToCallApprox() << endl;
+
+ bool noPivotLimitPass1 = noPivotLimit && !useApprox;
+ d_qflraStatus = simplex.findModel(noPivotLimitPass1);
+
+ Trace("TheoryArithPrivate::solveRealRelaxation")
+ << "solveRealRelaxation()" << " pass1 " << d_qflraStatus << endl;
+
+ if (d_qflraStatus == Result::UNKNOWN && useApprox && safeToCallApprox())
+ {
+ // pass2: fancy-final
+ static constexpr 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 = LinUnknown;
+ {
+ TimerStat::CodeTimer codeTimer1(d_statistics.d_lpTimer);
+ relaxRes = approxSolver->solveRelaxation();
+ }
+ Trace("solveRealRelaxation") << "solve relaxation? " << endl;
+ switch(relaxRes){
+ case LinFeasible:
+ Trace("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;
+ Trace("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;
+ Trace("solveRealRelaxation") << "exhuasted " << endl;
+ break;
+ case LinUnknown:
+ default:
+ ++d_statistics.d_relaxOthers;
+ break;
+ }
+ delete approxSolver;
+
+ }
+
+ bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel);
+
+ // 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().isRealOrInt())
+ {
+ 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;
+ }
+}
+
+bool TheoryArithPrivate::preCheck(Theory::Effort level)
+{
+ Assert(d_currentPropagationList.empty());
+ if(TraceIsOn("arith::consistency")){
+ Assert(unenqueuedVariablesAreConsistent());
+ }
+
+ d_newFacts = !done();
+ // If d_previousStatus == SAT, then reverts on conflicts are safe
+ // Otherwise, they are not and must be committed.
+ d_previousStatus = d_qflraStatus;
+ if (d_newFacts)
+ {
+ d_qflraStatus = Result::UNKNOWN;
+ d_hasDoneWorkSinceCut = true;
+ }
+ return false;
+}
+
+void TheoryArithPrivate::preNotifyFact(TNode atom, bool pol, TNode fact)
+{
+ ConstraintP curr = constraintFromFactQueue(fact);
+ if (curr != NullConstraint)
+ {
+ bool res CVC5_UNUSED = assertionCases(curr);
+ Assert(!res || anyConflict());
+ }
+}
+
+bool TheoryArithPrivate::postCheck(Theory::Effort effortLevel)
+{
+ if(!anyConflict()){
+ while(!d_learnedBounds.empty()){
+ // we may attempt some constraints twice. this is okay!
+ ConstraintP curr = d_learnedBounds.front();
+ d_learnedBounds.pop();
+ Trace("arith::learned") << curr << endl;
+
+ bool res CVC5_UNUSED = assertionCases(curr);
+ Assert(!res || anyConflict());
+
+ if(anyConflict()){ break; }
+ }
+ }
+
+ if(anyConflict()){
+ d_qflraStatus = Result::UNSAT;
+ if (options().arith.revertArithModels && d_previousStatus == Result::SAT)
+ {
+ ++d_statistics.d_revertsOnConflicts;
+ Trace("arith::bt") << "clearing here "
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ revertOutOfConflict();
+ d_errorSet.clear();
+ }else{
+ ++d_statistics.d_commitsOnConflicts;
+ Trace("arith::bt") << "committing here "
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ revertOutOfConflict();
+ }
+ outputConflicts();
+ //cout << "unate conflict 1 " << effortLevel << std::endl;
+ return true;
+ }
+
+
+ if(TraceIsOn("arith::print_assertions")) {
+ debugPrintAssertions(Trace("arith::print_assertions"));
+ }
+
+ bool emmittedConflictOrSplit = false;
+ Assert(d_conflicts.empty());
+
+ bool useSimplex = d_qflraStatus != Result::SAT;
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "pre realRelax" << endl;
+
+ if(useSimplex){
+ emmittedConflictOrSplit = solveRealRelaxation(effortLevel);
+ }
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "post realRelax" << endl;
+
+
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "pre solveInteger" << endl;
+
+ if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){
+ solveInteger(effortLevel);
+ if(anyConflict()){
+ ++d_statistics.d_commitsOnConflicts;
+ Trace("arith::bt") << "committing here "
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ revertOutOfConflict();
+ d_errorSet.clear();
+ outputConflicts();
+ return true;
+ }
+ }
+
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "post solveInteger" << endl;
+
+ switch(d_qflraStatus){
+ case Result::SAT:
+ if (d_newFacts)
+ {
+ ++d_statistics.d_nontrivialSatChecks;
+ }
+
+ Trace("arith::bt") << "committing sap inConflit"
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ d_unknownsInARow = 0;
+ if(TraceIsOn("arith::consistency")){
+ Assert(entireStateIsConsistent("sat comit"));
+ }
+ if (useSimplex && options().arith.collectPivots)
+ {
+ if (options().arith.useFC)
+ {
+ d_statistics.d_satPivots << d_fcSimplex.getPivots();
+ }
+ else
+ {
+ d_statistics.d_satPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ case Result::UNKNOWN:
+ ++d_unknownsInARow;
+ ++(d_statistics.d_unknownChecks);
+ Assert(!Theory::fullEffort(effortLevel));
+ Trace("arith::bt") << "committing unknown"
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
+
+ if (useSimplex && options().arith.collectPivots)
+ {
+ if (options().arith.useFC)
+ {
+ d_statistics.d_unknownPivots << d_fcSimplex.getPivots();
+ }
+ else
+ {
+ d_statistics.d_unknownPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ case Result::UNSAT:
+ d_unknownsInARow = 0;
+
+ ++d_statistics.d_commitsOnConflicts;
+
+ Trace("arith::bt") << "committing on conflict"
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+ d_partialModel.commitAssignmentChanges();
+ revertOutOfConflict();
+
+ if(TraceIsOn("arith::consistency::comitonconflict")){
+ entireStateIsConsistent("commit on conflict");
+ }
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+ Trace("arith::conflict") << "simplex conflict" << endl;
+
+ if (useSimplex && options().arith.collectPivots)
+ {
+ if (options().arith.useFC)
+ {
+ d_statistics.d_unsatPivots << d_fcSimplex.getPivots();
+ }
+ else
+ {
+ d_statistics.d_unsatPivots << d_dualSimplex.getPivots();
+ }
+ }
+ break;
+ default:
+ Unimplemented();
+ }
+ d_statistics.d_avgUnknownsInARow << d_unknownsInARow;
+
+ size_t nPivots = options().arith.useFC ? d_fcSimplex.getPivots()
+ : d_dualSimplex.getPivots();
+ for (std::size_t i = 0; i < nPivots; ++i)
+ {
+ d_containing.d_out->spendResource(
+ Resource::ArithPivotStep);
+ }
+
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "pre approx cuts" << endl;
+ if(!d_approxCuts.empty()){
+ bool anyFresh = false;
+ while(!d_approxCuts.empty()){
+ TrustNode lem = d_approxCuts.front();
+ d_approxCuts.pop();
+ Trace("arith::approx::cuts") << "approximate cut:" << lem << endl;
+ anyFresh = anyFresh || hasFreshArithLiteral(lem.getNode());
+ Trace("arith::lemma") << "approximate cut:" << lem << endl;
+ outputTrustedLemma(lem, InferenceId::ARITH_APPROX_CUT);
+ }
+ if(anyFresh){
+ emmittedConflictOrSplit = true;
+ }
+ }
+
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "post approx cuts" << endl;
+
+ // This should be fine if sat or unknown
+ if (!emmittedConflictOrSplit
+ && (options().arith.arithPropagationMode
+ == options::ArithPropagationMode::UNATE_PROP
+ || options().arith.arithPropagationMode
+ == options::ArithPropagationMode::BOTH_PROP))
+ {
+ TimerStat::CodeTimer codeTimer0(d_statistics.d_newPropTime);
+ Assert(d_qflraStatus != Result::UNSAT);
+
+ while(!d_currentPropagationList.empty() && !anyConflict()){
+ ConstraintP curr = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+
+ ConstraintType t = curr->getType();
+ Assert(t != Disequality)
+ << "Disequalities are not allowed in d_currentPropagation";
+
+ switch(t){
+ case LowerBound:
+ {
+ ConstraintP prev = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropLowerBound(curr, prev);
+ break;
+ }
+ case UpperBound:
+ {
+ ConstraintP prev = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropUpperBound(curr, prev);
+ break;
+ }
+ case Equality:
+ {
+ ConstraintP prevLB = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ ConstraintP prevUB = d_currentPropagationList.front();
+ d_currentPropagationList.pop_front();
+ d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
+ break;
+ }
+ default: Unhandled() << curr->getType();
+ }
+ }
+
+ if(anyConflict()){
+ Trace("arith::unate") << "unate conflict" << endl;
+ revertOutOfConflict();
+ d_qflraStatus = Result::UNSAT;
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+ //cout << "unate conflict " << endl;
+ Trace("arith::bt") << "committing on unate conflict"
+ << " " << d_newFacts << " " << d_previousStatus << " "
+ << d_qflraStatus << endl;
+
+ Trace("arith::conflict") << "unate arith conflict" << endl;
+ }
+ }
+ else
+ {
+ TimerStat::CodeTimer codeTimer1(d_statistics.d_newPropTime);
+ d_currentPropagationList.clear();
+ }
+ Assert(d_currentPropagationList.empty());
+
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "post unate" << endl;
+
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+ ++d_fullCheckCounter;
+ }
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
+ emmittedConflictOrSplit = splitDisequalities();
+ }
+ Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
+ << "pos splitting" << endl;
+
+
+ Trace("arith") << "integer? "
+ << " conf/split " << emmittedConflictOrSplit
+ << " fulleffort " << Theory::fullEffort(effortLevel) << endl;
+
+ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
+ Node possibleConflict = Node::null();
+ if (!emmittedConflictOrSplit && options().arith.arithDioSolver)
+ {
+ possibleConflict = callDioSolver();
+ if(possibleConflict != Node::null()){
+ revertOutOfConflict();
+ Trace("arith::conflict") << "dio conflict " << possibleConflict << endl;
+ // TODO (project #37): justify (proofs in the DIO solver)
+ raiseBlackBoxConflict(possibleConflict);
+ outputConflicts();
+ emmittedConflictOrSplit = true;
+ }
+ }
+
+ if (!emmittedConflictOrSplit && d_hasDoneWorkSinceCut
+ && options().arith.arithDioSolver)
+ {
+ if(getDioCuttingResource()){
+ TrustNode possibleLemma = dioCutting();
+ if(!possibleLemma.isNull()){
+ d_hasDoneWorkSinceCut = false;
+ d_cutCount = d_cutCount + 1;
+ Trace("arith::lemma") << "dio cut " << possibleLemma << endl;
+ if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_DIO_CUT))
+ {
+ emmittedConflictOrSplit = true;
+ }
+ }
+ }
+ }
+
+ if(!emmittedConflictOrSplit) {
+ TrustNode possibleLemma = roundRobinBranch();
+ if (!possibleLemma.getNode().isNull())
+ {
+ ++(d_statistics.d_externalBranchAndBounds);
+ d_cutCount = d_cutCount + 1;
+ Trace("arith::lemma") << "rrbranch lemma"
+ << possibleLemma << endl;
+ if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_BB_LEMMA))
+ {
+ emmittedConflictOrSplit = true;
+ }
+ }
+ }
+
+ if (options().arith.maxCutsInContext <= d_cutCount)
+ {
+ if(d_diosolver.hasMoreDecompositionLemmas()){
+ while(d_diosolver.hasMoreDecompositionLemmas()){
+ Node decompositionLemma = d_diosolver.nextDecompositionLemma();
+ Trace("arith::lemma") << "dio decomposition lemma "
+ << decompositionLemma << endl;
+ outputLemma(decompositionLemma, InferenceId::ARITH_DIO_DECOMPOSITION);
+ }
+ }else{
+ Trace("arith::restart") << "arith restart!" << endl;
+ outputRestart();
+ }
+ }
+ }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
+
+ if(Theory::fullEffort(effortLevel)){
+ if(TraceIsOn("arith::consistency::final")){
+ entireStateIsConsistent("arith::consistency::final");
+ }
+ }
+
+ if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+ if(TraceIsOn("arith::print_model")) {
+ debugPrintModel(Trace("arith::print_model"));
+ }
+ Trace("arith") << "TheoryArithPrivate::check end" << std::endl;
+ return emmittedConflictOrSplit;
+}
+
+bool TheoryArithPrivate::foundNonlinear() const { return d_foundNl; }
+
+TrustNode TheoryArithPrivate::branchIntegerVariable(ArithVar x) const
+{
+ const DeltaRational& d = d_partialModel.getAssignment(x);
+ Assert(!d.isIntegral());
+ const Rational& r = d.getNoninfinitesimalPart();
+ const Rational& i = d.getInfinitesimalPart();
+ Trace("integers") << "integers: assignment to [[" << d_partialModel.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
+ Assert(!(r.getDenominator() == 1 && i.getNumerator() == 0));
+ TNode var = d_partialModel.asNode(x);
+ TrustNode lem = d_bab.branchIntegerVariable(var, r);
+ if (TraceIsOn("integers"))
+ {
+ Node l = lem.getNode();
+ if (isSatLiteral(l[0]))
+ {
+ Trace("integers") << " " << l[0] << " == " << getSatValue(l[0])
+ << endl;
+ }
+ else
+ {
+ Trace("integers") << " " << l[0] << " is not assigned a SAT literal"
+ << endl;
+ }
+ if (isSatLiteral(l[1]))
+ {
+ Trace("integers") << " " << l[1] << " == " << getSatValue(l[1])
+ << endl;
+ }
+ else
+ {
+ Trace("integers") << " " << l[1] << " is not assigned a SAT literal"
+ << endl;
+ }
+ }
+ return lem;
+}
+
+std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{
+ vector<ArithVar> lemmas;
+ ArithVar max = d_partialModel.getNumberOfVariables();
+
+ if (options().arith.doCutAllBounded && max > 0)
+ {
+ for(ArithVar iter = 0; iter != max; ++iter){
+ //Do not include slack variables
+ const DeltaRational& d = d_partialModel.getAssignment(iter);
+ if(isIntegerInput(iter) &&
+ !d_cutInContext.contains(iter) &&
+ d_partialModel.hasUpperBound(iter) &&
+ d_partialModel.hasLowerBound(iter) &&
+ !d.isIntegral()){
+ lemmas.push_back(iter);
+ }
+ }
+ }
+ return lemmas;
+}
+
+/** Returns true if the roundRobinBranching() issues a lemma. */
+TrustNode TheoryArithPrivate::roundRobinBranch()
+{
+ if(hasIntegerModel()){
+ return TrustNode::null();
+ }else{
+ ArithVar v = d_nextIntegerCheckVar;
+
+ Assert(isInteger(v));
+ Assert(!isAuxiliaryVariable(v));
+ return branchIntegerVariable(v);
+ }
+}
+
+bool TheoryArithPrivate::splitDisequalities(){
+ bool splitSomething = false;
+
+ vector<ConstraintP> save;
+
+ while(!d_diseqQueue.empty()){
+ ConstraintP front = d_diseqQueue.front();
+ d_diseqQueue.pop();
+
+ if(front->isSplit()){
+ Trace("arith::eq") << "split already" << endl;
+ }else{
+ Trace("arith::eq") << "not split already" << endl;
+
+ ArithVar lhsVar = front->getVariable();
+
+ const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
+ const DeltaRational& rhsValue = front->getValue();
+ if(lhsValue == rhsValue){
+ Trace("arith::lemma") << "Splitting on " << front << endl;
+ Trace("arith::lemma") << "LHS value = " << lhsValue << endl;
+ Trace("arith::lemma") << "RHS value = " << rhsValue << endl;
+ TrustNode lemma = front->split();
+ ++(d_statistics.d_statDisequalitySplits);
+
+ Trace("arith::lemma") << "Now " << lemma.getNode() << endl;
+ outputTrustedLemma(lemma, InferenceId::ARITH_SPLIT_DEQ);
+ splitSomething = true;
+ }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
+ Trace("arith::eq") << "can drop as less than lb" << front << endl;
+ }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
+ Trace("arith::eq") << "can drop as greater than ub" << front << endl;
+ }else{
+ Trace("arith::eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
+ save.push_back(front);
+ }
+ }
+ }
+ vector<ConstraintP>::const_iterator i=save.begin(), i_end = save.end();
+ for(; i != i_end; ++i){
+ d_diseqQueue.push(*i);
+ }
+ return splitSomething;
+}
+
+/**
+ * Should be guarded by at least TraceIsOn("arith::print_assertions").
+ * Prints to Trace("arith::print_assertions")
+ */
+void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const {
+ out << "Assertions:" << endl;
+ for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar i = *vi;
+ if (d_partialModel.hasLowerBound(i)) {
+ ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i);
+ out << lConstr << endl;
+ }
+
+ if (d_partialModel.hasUpperBound(i)) {
+ ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i);
+ out << uConstr << endl;
+ }
+ }
+ 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;
+ }
+}
+
+void TheoryArithPrivate::debugPrintModel(std::ostream& out) const{
+ out << "Model:" << endl;
+ for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar i = *vi;
+ if(d_partialModel.hasNode(i)){
+ out << d_partialModel.asNode(i) << " : " <<
+ d_partialModel.getAssignment(i);
+ if(d_tableau.isBasic(i)){
+ out << " (basic)";
+ }
+ out << endl;
+ }
+ }
+}
+
+TrustNode TheoryArithPrivate::explain(TNode n)
+{
+ Trace("arith::explain") << "explain @" << context()->getLevel() << ": " << n
+ << endl;
+
+ ConstraintP c = d_constraintDatabase.lookup(n);
+ TrustNode exp;
+ if(c != NullConstraint){
+ Assert(!c->isAssumption());
+ exp = c->externalExplainForPropagation(n);
+ Trace("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
+ }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
+ c = d_assertionsThatDoNotMatchTheirLiterals[n];
+ if(!c->isAssumption()){
+ exp = c->externalExplainForPropagation(n);
+ Trace("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
+ }else{
+ Trace("arith::explain") << "this is a strange mismatch" << n << endl;
+ Assert(d_congruenceManager.canExplain(n));
+ exp = d_congruenceManager.explain(n);
+ }
+ }else{
+ Assert(d_congruenceManager.canExplain(n));
+ Trace("arith::explain") << "dm explanation" << n << endl;
+ exp = d_congruenceManager.explain(n);
+ }
+ return exp;
+}
+
+void TheoryArithPrivate::propagate(Theory::Effort e) {
+ // This uses model values for safety. Disable for now.
+ if (d_qflraStatus == Result::SAT
+ && (options().arith.arithPropagationMode
+ == options::ArithPropagationMode::BOUND_INFERENCE_PROP
+ || options().arith.arithPropagationMode
+ == options::ArithPropagationMode::BOTH_PROP)
+ && hasAnyUpdates())
+ {
+ if (options().arith.newProp)
+ {
+ propagateCandidatesNew();
+ }
+ else
+ {
+ propagateCandidates();
+ }
+ }
+ else
+ {
+ clearUpdates();
+ }
+
+ while(d_constraintDatabase.hasMorePropagations()){
+ ConstraintCP c = d_constraintDatabase.nextPropagation();
+ Trace("arith::prop") << "next prop" << context()->getLevel() << ": " << c
+ << endl;
+
+ if(c->negationHasProof()){
+ Trace("arith::prop") << "negation has proof " << c->getNegation() << endl;
+ Trace("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!";
+
+ if(!c->assertedToTheTheory()){
+ Node literal = c->getLiteral();
+ Trace("arith::prop") << "propagating @" << context()->getLevel() << " "
+ << literal << endl;
+
+ outputPropagate(literal);
+ }else{
+ Trace("arith::prop") << "already asserted to the theory " << c->getLiteral() << endl;
+ }
+ }
+
+ NodeManager* nm = NodeManager::currentNM();
+ while(d_congruenceManager.hasMorePropagations()){
+ TNode toProp = d_congruenceManager.getNextPropagation();
+
+ //Currently if the flag is set this came from an equality detected by the
+ //equality engine in the the difference manager.
+ Node normalized = rewrite(toProp);
+
+ ConstraintP constraint = d_constraintDatabase.lookup(normalized);
+ if(constraint == NullConstraint){
+ Trace("arith::prop") << "propagating on non-constraint? " << toProp << endl;
+
+ outputPropagate(toProp);
+ }else if(constraint->negationHasProof()){
+ // The congruence manager can prove: antecedents => toProp,
+ // ergo. antecedents ^ ~toProp is a conflict.
+ TrustNode exp = d_congruenceManager.explain(toProp);
+ Node notNormalized = normalized.negate();
+ std::vector<Node> ants(exp.getNode().begin(), exp.getNode().end());
+ ants.push_back(notNormalized);
+ Node lp = nm->mkAnd(ants);
+ Trace("arith::prop") << "propagate conflict" << lp << endl;
+ if (proofsEnabled())
+ {
+ // Assume all of antecedents and ~toProp (rewritten)
+ std::vector<Pf> pfAntList;
+ for (size_t i = 0; i < ants.size(); ++i)
+ {
+ pfAntList.push_back(d_pnm->mkAssume(ants[i]));
+ }
+ Pf pfAnt = pfAntList.size() > 1
+ ? d_pnm->mkNode(PfRule::AND_INTRO, pfAntList, {})
+ : pfAntList[0];
+ // Use modus ponens to get toProp (un rewritten)
+ Pf pfConc = d_pnm->mkNode(
+ PfRule::MODUS_PONENS,
+ {pfAnt, exp.getGenerator()->getProofFor(exp.getProven())},
+ {});
+ // prove toProp (rewritten)
+ Pf pfConcRewritten = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {pfConc}, {normalized});
+ Pf pfNotNormalized = d_pnm->mkAssume(notNormalized);
+ // prove bottom from toProp and ~toProp
+ Pf pfBot;
+ if (normalized.getKind() == kind::NOT)
+ {
+ pfBot = d_pnm->mkNode(
+ PfRule::CONTRA, {pfNotNormalized, pfConcRewritten}, {});
+ }
+ else
+ {
+ pfBot = d_pnm->mkNode(
+ PfRule::CONTRA, {pfConcRewritten, pfNotNormalized}, {});
+ }
+ // close scope
+ Pf pfNotAnd = d_pnm->mkScope(pfBot, ants);
+ raiseBlackBoxConflict(lp, pfNotAnd);
+ }
+ else
+ {
+ raiseBlackBoxConflict(lp);
+ }
+ outputConflicts();
+ return;
+ }else{
+ Trace("arith::prop") << "propagating still?" << toProp << endl;
+ outputPropagate(toProp);
+ }
+ }
+}
+
+DeltaRational TheoryArithPrivate::getDeltaValue(TNode term) const
+{
+ AlwaysAssert(d_qflraStatus != Result::UNKNOWN);
+ Trace("arith::value") << term << std::endl;
+
+ if (d_partialModel.hasArithVar(term)) {
+ ArithVar var = d_partialModel.asArithVar(term);
+ return d_partialModel.getAssignment(var);
+ }
+
+ switch (Kind kind = term.getKind()) {
+ case kind::CONST_RATIONAL:
+ case kind::CONST_INTEGER: return term.getConst<Rational>();
+
+ case kind::ADD:
+ { // 2+ args
+ DeltaRational value(0);
+ for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
+ ++i) {
+ value = value + getDeltaValue(*i);
+ }
+ return value;
+ }
+
+ case kind::NONLINEAR_MULT:
+ case kind::MULT: { // 2+ args
+ Assert(!isSetup(term));
+ DeltaRational value(1);
+ for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
+ ++i) {
+ value = value * getDeltaValue(*i);
+ }
+ return value;
+ }
+ case kind::SUB:
+ { // 2 args
+ return getDeltaValue(term[0]) - getDeltaValue(term[1]);
+ }
+ case kind::NEG:
+ { // 1 arg
+ return (-getDeltaValue(term[0]));
+ }
+
+ case kind::DIVISION: { // 2 args
+ Assert(!isSetup(term));
+ return getDeltaValue(term[0]) / getDeltaValue(term[1]);
+ }
+ case kind::DIVISION_TOTAL:
+ case kind::INTS_DIVISION_TOTAL:
+ case kind::INTS_MODULUS_TOTAL: { // 2 args
+ Assert(!isSetup(term));
+ DeltaRational denominator = getDeltaValue(term[1]);
+ if (denominator.isZero()) {
+ return DeltaRational(0, 0);
+ }
+ DeltaRational numerator = getDeltaValue(term[0]);
+ if (kind == kind::DIVISION_TOTAL) {
+ return numerator / denominator;
+ } else if (kind == kind::INTS_DIVISION_TOTAL) {
+ return Rational(numerator.euclidianDivideQuotient(denominator));
+ } else {
+ Assert(kind == kind::INTS_MODULUS_TOTAL);
+ return Rational(numerator.euclidianDivideRemainder(denominator));
+ }
+ }
+
+ default:
+ throw ModelException(term, "No model assignment.");
+ }
+}
+
+Rational TheoryArithPrivate::deltaValueForTotalOrder() const{
+ Rational min(2);
+ std::set<DeltaRational> relevantDeltaValues;
+ context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin();
+ context::CDQueue<ConstraintP>::const_iterator qiter_end = d_diseqQueue.end();
+
+ for(; qiter != qiter_end; ++qiter){
+ ConstraintP curr = *qiter;
+
+ const DeltaRational& rhsValue = curr->getValue();
+ relevantDeltaValues.insert(rhsValue);
+ }
+
+ Theory::shared_terms_iterator shared_iter = d_containing.shared_terms_begin();
+ Theory::shared_terms_iterator shared_end = d_containing.shared_terms_end();
+ for(; shared_iter != shared_end; ++shared_iter){
+ Node sharedCurr = *shared_iter;
+
+ // ModelException is fatal as this point. Don't catch!
+ // DeltaRationalException is fatal as this point. Don't catch!
+ DeltaRational val = getDeltaValue(sharedCurr);
+ relevantDeltaValues.insert(val);
+ }
+
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+ const DeltaRational& value = d_partialModel.getAssignment(v);
+ relevantDeltaValues.insert(value);
+ if( d_partialModel.hasLowerBound(v)){
+ const DeltaRational& lb = d_partialModel.getLowerBound(v);
+ relevantDeltaValues.insert(lb);
+ }
+ if( d_partialModel.hasUpperBound(v)){
+ const DeltaRational& ub = d_partialModel.getUpperBound(v);
+ relevantDeltaValues.insert(ub);
+ }
+ }
+
+ if(relevantDeltaValues.size() >= 2){
+ std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
+ std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
+ DeltaRational prev = *iter;
+ ++iter;
+ for(; iter != iter_end; ++iter){
+ const DeltaRational& curr = *iter;
+
+ Assert(prev < curr);
+
+ DeltaRational::seperatingDelta(min, prev, curr);
+ prev = curr;
+ }
+ }
+
+ Assert(min.sgn() > 0);
+ Rational belowMin = min/Rational(2);
+ return belowMin;
+}
+
+void TheoryArithPrivate::collectModelValues(const std::set<Node>& termSet,
+ std::map<Node, Node>& arithModel)
+{
+ AlwaysAssert(d_qflraStatus == Result::SAT);
+
+ if(TraceIsOn("arith::collectModelInfo")){
+ debugPrintFacts();
+ }
+
+ Trace("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
+
+ // Delta lasts at least the duration of the function call
+ const Rational& delta = d_partialModel.getDelta();
+ std::unordered_set<TNode> shared = d_containing.currentlySharedTerms();
+
+ // TODO:
+ // This is not very good for user push/pop....
+ // Revisit when implementing push/pop
+ NodeManager* nm = NodeManager::currentNM();
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar v = *vi;
+
+ if(!isAuxiliaryVariable(v)){
+ Node term = d_partialModel.asNode(v);
+
+ if((theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end())
+ && termSet.find(term) != termSet.end()){
+
+ const DeltaRational& mod = d_partialModel.getAssignment(v);
+ Rational qmodel = mod.substituteDelta(delta);
+
+ Node qNode = nm->mkConstRealOrInt(term.getType(), qmodel);
+ Trace("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
+ // Add to the map
+ arithModel[term] = qNode;
+ }else{
+ Trace("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
+
+ }
+ }
+ }
+
+ // Iterate over equivalence classes in LinearEqualityModule
+ // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
+ // m->assertEqualityEngine(&ee);
+
+ Trace("arith::collectModelInfo") << "collectModelInfo() end " << endl;
+}
+
+bool TheoryArithPrivate::safeToReset() const {
+ Assert(!d_tableauSizeHasBeenModified);
+ Assert(d_errorSet.noSignals());
+
+ ErrorSet::error_iterator error_iter = d_errorSet.errorBegin();
+ ErrorSet::error_iterator error_end = d_errorSet.errorEnd();
+ for(; error_iter != error_end; ++error_iter){
+ ArithVar basic = *error_iter;
+ if(!d_smallTableauCopy.isBasic(basic)){
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TheoryArithPrivate::notifyRestart(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
+
+ if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ ++d_restartsCounter;
+ d_solveIntMaybeHelp = 0;
+ d_solveIntAttempts = 0;
+}
+
+bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
+ bool result = true;
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar var = *vi;
+ //ArithVar var = d_partialModel.asArithVar(*i);
+ if(!d_partialModel.assignmentIsConsistent(var)){
+ d_partialModel.printModel(var);
+ warning() << s << ":" << "Assignment is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ warning() << " (basic)";
+ }
+ warning() << std::endl;
+ result = false;
+ }else if(d_partialModel.isInteger(var) && !d_partialModel.integralAssignment(var)){
+ d_partialModel.printModel(var);
+ warning() << s << ":" << "Assignment is not integer for integer variable " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ warning() << " (basic)";
+ }
+ warning() << std::endl;
+ result = false;
+ }
+ }
+ return result;
+}
+
+bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
+ bool result = true;
+ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
+ ArithVar var = *vi;
+ if(!d_partialModel.assignmentIsConsistent(var)){
+ if(!d_errorSet.inError(var)){
+
+ d_partialModel.printModel(var);
+ warning() << "Unenqueued var is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ warning() << " (basic)";
+ }
+ warning() << std::endl;
+ result = false;
+ } else if(TraceIsOn("arith::consistency::initial")){
+ d_partialModel.printModel(var);
+ warning() << "Initial var is not consistent for " << var << d_partialModel.asNode(var);
+ if(d_tableau.isBasic(var)){
+ warning() << " (basic)";
+ }
+ warning() << std::endl;
+ }
+ }
+ }
+ return result;
+}
+
+void TheoryArithPrivate::presolve(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
+
+ d_statistics.d_initialTableauSize = d_tableau.size();
+
+ if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
+
+ if(TraceIsOn("arith::presolve")) {
+ Trace("arith::presolve") << "TheoryArithPrivate::presolve" << endl;
+ }
+
+ vector<TrustNode> lemmas;
+ if (!options().base.incrementalSolving)
+ {
+ switch (options().arith.arithUnateLemmaMode)
+ {
+ case options::ArithUnateLemmaMode::NO: break;
+ case options::ArithUnateLemmaMode::INEQUALITY:
+ d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+ break;
+ case options::ArithUnateLemmaMode::EQUALITY:
+ d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+ break;
+ case options::ArithUnateLemmaMode::ALL:
+ d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
+ d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
+ break;
+ default: Unhandled() << options().arith.arithUnateLemmaMode;
+ }
+ }
+
+ vector<TrustNode>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
+ for(; i != i_end; ++i){
+ TrustNode lem = *i;
+ Trace("arith::oldprop") << " lemma lemma duck " <<lem << endl;
+ outputTrustedLemma(lem, InferenceId::ARITH_UNATE);
+ }
+}
+
+EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
+ if (d_qflraStatus == Result::UNKNOWN)
+ {
+ return EQUALITY_UNKNOWN;
+ }else{
+ try {
+ if (getDeltaValue(a) == getDeltaValue(b)) {
+ return EQUALITY_TRUE_IN_MODEL;
+ } else {
+ return EQUALITY_FALSE_IN_MODEL;
+ }
+ } catch (DeltaRationalException& dr) {
+ return EQUALITY_UNKNOWN;
+ } catch (ModelException& me) {
+ return EQUALITY_UNKNOWN;
+ }
+ }
+}
+
+bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
+ ++d_statistics.d_boundComputations;
+
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+ DeltaRational bound = d_linEq.computeRowBound(ridx, upperBound, basic);
+
+ if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
+ (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
+
+ // TODO: "Policy point"
+ //We are only going to recreate the functionality for now.
+ //In the future this can be improved to generate a temporary constraint
+ //if none exists.
+ //Experiment with doing this every time or only when the new constraint
+ //implies an unknown fact.
+
+ ConstraintType t = upperBound ? UpperBound : LowerBound;
+ ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
+
+ // Node bestImplied = upperBound ?
+ // d_apm.getBestImpliedUpperBound(basic, bound):
+ // d_apm.getBestImpliedLowerBound(basic, bound);
+
+ if(bestImplied != NullConstraint){
+ //This should be stronger
+ Assert(!upperBound || bound <= bestImplied->getValue());
+ Assert(
+ !upperBound
+ || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
+
+ Assert(upperBound || bound >= bestImplied->getValue());
+ Assert(upperBound
+ || d_partialModel.greaterThanLowerBound(basic,
+ bestImplied->getValue()));
+ //slightly changed
+
+ // ConstraintP c = d_constraintDatabase.lookup(bestImplied);
+ // Assert(c != NullConstraint);
+
+ bool assertedToTheTheory = bestImplied->assertedToTheTheory();
+ bool canBePropagated = bestImplied->canBePropagated();
+ bool hasProof = bestImplied->hasProof();
+
+ Trace("arith::prop") << "arith::prop" << basic
+ << " " << assertedToTheTheory
+ << " " << canBePropagated
+ << " " << hasProof
+ << endl;
+
+ if(bestImplied->negationHasProof()){
+ warning() << "the negation of " << bestImplied << " : " << std::endl
+ << "has proof " << bestImplied->getNegation() << std::endl
+ << bestImplied->getNegation()->externalExplainByAssertions()
+ << std::endl;
+ }
+
+ if(!assertedToTheTheory && canBePropagated && !hasProof ){
+ d_linEq.propagateBasicFromRow(bestImplied, options().smt.produceProofs);
+ // I think this can be skipped if canBePropagated is true
+ //d_learnedBounds.push(bestImplied);
+ if(TraceIsOn("arith::prop")){
+ Trace("arith::prop") << "success " << bestImplied << endl;
+ d_partialModel.printModel(basic, Trace("arith::prop"));
+ }
+ return true;
+ }
+ if(TraceIsOn("arith::prop")){
+ Trace("arith::prop") << "failed " << basic
+ << " " << bound
+ << " " << assertedToTheTheory
+ << " " << canBePropagated
+ << " " << hasProof << endl;
+ d_partialModel.printModel(basic, Trace("arith::prop"));
+ }
+ }
+ }else if(TraceIsOn("arith::prop")){
+ Trace("arith::prop") << "false " << bound << " ";
+ d_partialModel.printModel(basic, Trace("arith::prop"));
+ }
+ return false;
+}
+
+void TheoryArithPrivate::propagateCandidate(ArithVar basic){
+ bool success = false;
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+ bool tryLowerBound =
+ d_partialModel.strictlyAboveLowerBound(basic) &&
+ d_linEq.rowLacksBound(ridx, false, basic) == NULL;
+
+ bool tryUpperBound =
+ d_partialModel.strictlyBelowUpperBound(basic) &&
+ d_linEq.rowLacksBound(ridx, true, basic) == NULL;
+
+ if(tryLowerBound){
+ success |= propagateCandidateLowerBound(basic);
+ }
+ if(tryUpperBound){
+ success |= propagateCandidateUpperBound(basic);
+ }
+ if(success){
+ ++d_statistics.d_boundPropagations;
+ }
+}
+
+void TheoryArithPrivate::propagateCandidates(){
+ TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+
+ Trace("arith::prop") << "propagateCandidates begin" << endl;
+
+ Assert(d_candidateBasics.empty());
+
+ if(d_updatedBounds.empty()){ return; }
+
+ DenseSet::const_iterator i = d_updatedBounds.begin();
+ DenseSet::const_iterator end = d_updatedBounds.end();
+ for(; i != end; ++i){
+ ArithVar var = *i;
+ if (d_tableau.isBasic(var)
+ && d_tableau.basicRowLength(var)
+ <= options().arith.arithPropagateMaxLength)
+ {
+ d_candidateBasics.softAdd(var);
+ }
+ else
+ {
+ Tableau::ColIterator basicIter = d_tableau.colIterator(var);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
+ Assert(entry.getColVar() == var);
+ Assert(d_tableau.isBasic(rowVar));
+ if (d_tableau.getRowLength(ridx)
+ <= options().arith.arithPropagateMaxLength)
+ {
+ d_candidateBasics.softAdd(rowVar);
+ }
+ }
+ }
+ }
+ d_updatedBounds.purge();
+
+ while(!d_candidateBasics.empty()){
+ ArithVar candidate = d_candidateBasics.back();
+ d_candidateBasics.pop_back();
+ Assert(d_tableau.isBasic(candidate));
+ propagateCandidate(candidate);
+ }
+ Trace("arith::prop") << "propagateCandidates end" << endl << endl << endl;
+}
+
+void TheoryArithPrivate::propagateCandidatesNew(){
+ /* Four criteria must be met for progagation on a variable to happen using a row:
+ * 0: A new bound has to have been added to the row.
+ * 1: The hasBoundsCount for the row must be "full" or be full minus one variable
+ * (This is O(1) to check, but requires book keeping.)
+ * 2: The current assignment must be strictly smaller/greater than the current bound.
+ * assign(x) < upper(x)
+ * (This is O(1) to compute.)
+ * 3: There is a bound that is strictly smaller/greater than the current assignment.
+ * assign(x) < c for some x <= c literal
+ * (This is O(log n) to compute.)
+ * 4: The implied bound on x is strictly smaller/greater than the current bound.
+ * (This is O(n) to compute.)
+ */
+
+ TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+ Trace("arith::prop") << "propagateCandidatesNew begin" << endl;
+
+ Assert(d_qflraStatus == Result::SAT);
+ if(d_updatedBounds.empty()){ return; }
+ dumpUpdatedBoundsToRows();
+ Assert(d_updatedBounds.empty());
+
+ if(!d_candidateRows.empty()){
+ UpdateTrackingCallback utcb(&d_linEq);
+ d_partialModel.processBoundsQueue(utcb);
+ }
+
+ while(!d_candidateRows.empty()){
+ RowIndex candidate = d_candidateRows.back();
+ d_candidateRows.pop_back();
+ propagateCandidateRow(candidate);
+ }
+ Trace("arith::prop") << "propagateCandidatesNew end" << endl << endl << endl;
+}
+
+bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{
+ int cmp = ub ? d_partialModel.cmpAssignmentUpperBound(v)
+ : d_partialModel.cmpAssignmentLowerBound(v);
+ bool hasSlack = ub ? cmp < 0 : cmp > 0;
+ if(hasSlack){
+ ConstraintType t = ub ? UpperBound : LowerBound;
+ const DeltaRational& a = d_partialModel.getAssignment(v);
+
+ if(isInteger(v) && !a.isIntegral()){
+ return true;
+ }
+
+ ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a);
+ if(strongestPossible == NullConstraint){
+ return false;
+ }else{
+ bool assertedToTheTheory = strongestPossible->assertedToTheTheory();
+ bool canBePropagated = strongestPossible->canBePropagated();
+ bool hasProof = strongestPossible->hasProof();
+
+ return !assertedToTheTheory && canBePropagated && !hasProof;
+ }
+ }else{
+ return false;
+ }
+}
+
+bool TheoryArithPrivate::attemptSingleton(RowIndex ridx, bool rowUp){
+ Trace("arith::prop") << " attemptSingleton" << ridx;
+
+ const Tableau::Entry* ep;
+ ep = d_linEq.rowLacksBound(ridx, rowUp, ARITHVAR_SENTINEL);
+ Assert(ep != NULL);
+
+ ArithVar v = ep->getColVar();
+ const Rational& coeff = ep->getCoefficient();
+
+ // 0 = c * v + \sum rest
+ // Suppose rowUp
+ // - c * v = \sum rest \leq D
+ // if c > 0, v \geq -D/c so !vUp
+ // if c < 0, v \leq -D/c so vUp
+ // Suppose not rowUp
+ // - c * v = \sum rest \geq D
+ // if c > 0, v \leq -D/c so vUp
+ // if c < 0, v \geq -D/c so !vUp
+ bool vUp = (rowUp == ( coeff.sgn() < 0));
+
+ Trace("arith::prop") << " " << rowUp << " " << v << " " << coeff << " " << vUp << endl;
+ Trace("arith::prop") << " " << propagateMightSucceed(v, vUp) << endl;
+
+ if(propagateMightSucceed(v, vUp)){
+ DeltaRational dr = d_linEq.computeRowBound(ridx, rowUp, v);
+ DeltaRational bound = dr / (- coeff);
+ return tryToPropagate(ridx, rowUp, v, vUp, bound);
+ }
+ return false;
+}
+
+bool TheoryArithPrivate::attemptFull(RowIndex ridx, bool rowUp){
+ Trace("arith::prop") << " attemptFull" << ridx << endl;
+
+ vector<const Tableau::Entry*> candidates;
+
+ for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
+ const Tableau::Entry& e =*i;
+ const Rational& c = e.getCoefficient();
+ ArithVar v = e.getColVar();
+ bool vUp = (rowUp == (c.sgn() < 0));
+ if(propagateMightSucceed(v, vUp)){
+ candidates.push_back(&e);
+ }
+ }
+ if(candidates.empty()){ return false; }
+
+ const DeltaRational slack =
+ d_linEq.computeRowBound(ridx, rowUp, ARITHVAR_SENTINEL);
+ bool any = false;
+ vector<const Tableau::Entry*>::const_iterator i, iend;
+ for(i = candidates.begin(), iend = candidates.end(); i != iend; ++i){
+ const Tableau::Entry* ep = *i;
+ const Rational& c = ep->getCoefficient();
+ ArithVar v = ep->getColVar();
+
+ // See the comment for attemptSingleton()
+ bool activeUp = (rowUp == (c.sgn() > 0));
+ bool vUb = (rowUp == (c.sgn() < 0));
+
+ const DeltaRational& activeBound = activeUp ?
+ d_partialModel.getUpperBound(v):
+ d_partialModel.getLowerBound(v);
+
+ DeltaRational contribution = activeBound * c;
+ DeltaRational impliedBound = (slack - contribution)/(-c);
+
+ bool success = tryToPropagate(ridx, rowUp, v, vUb, impliedBound);
+ any |= success;
+ }
+ return any;
+}
+
+bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUb, const DeltaRational& bound){
+
+ bool weaker = vUb ? d_partialModel.strictlyLessThanUpperBound(v, bound):
+ d_partialModel.strictlyGreaterThanLowerBound(v, bound);
+ if(weaker){
+ ConstraintType t = vUb ? UpperBound : LowerBound;
+
+ ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound);
+ if(implied != NullConstraint){
+ return rowImplicationCanBeApplied(ridx, rowUp, implied);
+ }
+ }
+ return false;
+}
+
+Node flattenImplication(Node imp){
+ NodeBuilder nb(kind::OR);
+ std::unordered_set<Node> included;
+ Node left = imp[0];
+ Node right = imp[1];
+
+ if(left.getKind() == kind::AND){
+ for(Node::iterator i = left.begin(), iend = left.end(); i != iend; ++i) {
+ if (!included.count((*i).negate()))
+ {
+ nb << (*i).negate();
+ included.insert((*i).negate());
+ }
+ }
+ }else{
+ if (!included.count(left.negate()))
+ {
+ nb << left.negate();
+ included.insert(left.negate());
+ }
+ }
+
+ if(right.getKind() == kind::OR){
+ for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) {
+ if (!included.count(*i))
+ {
+ nb << *i;
+ included.insert(*i);
+ }
+ }
+ }else{
+ if (!included.count(right))
+ {
+ nb << right;
+ included.insert(right);
+ }
+ }
+
+ return nb;
+}
+
+bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){
+ Assert(implied != NullConstraint);
+ ArithVar v = implied->getVariable();
+
+ bool assertedToTheTheory = implied->assertedToTheTheory();
+ bool canBePropagated = implied->canBePropagated();
+ bool hasProof = implied->hasProof();
+
+ Trace("arith::prop") << "arith::prop" << v
+ << " " << assertedToTheTheory
+ << " " << canBePropagated
+ << " " << hasProof
+ << endl;
+
+
+ if( !assertedToTheTheory && canBePropagated && !hasProof ){
+ ConstraintCPVec explain;
+ if (options().smt.produceProofs)
+ {
+ d_farkasBuffer.clear();
+ }
+ RationalVectorP coeffs =
+ options().smt.produceProofs ? &d_farkasBuffer : nullptr;
+
+ // After invoking `propegateRow`:
+ // * coeffs[0] is for implied
+ // * coeffs[i+1] is for explain[i]
+ d_linEq.propagateRow(explain, ridx, rowUp, implied, coeffs);
+ if (d_tableau.getRowLength(ridx) <= options().arith.arithPropAsLemmaLength)
+ {
+ if (TraceIsOn("arith::prop::pf")) {
+ for (const auto & constraint : explain) {
+ Assert(constraint->hasProof());
+ constraint->printProofTree(Trace("arith::prop::pf"));
+ }
+ }
+ Node implication = implied->externalImplication(explain);
+ Node clause = flattenImplication(implication);
+ std::shared_ptr<ProofNode> clausePf{nullptr};
+
+ if (isProofEnabled())
+ {
+ // We can prove this lemma from Farkas...
+ std::vector<std::shared_ptr<ProofNode>> conflictPfs;
+ Node pfLit = implied->getNegation()->getProofLiteral();
+ TypeNode type = pfLit[0].getType();
+ // Assume the negated getLiteral version of the implied constaint
+ // then rewrite it into proof normal form.
+ conflictPfs.push_back(
+ d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
+ {d_pnm->mkAssume(implied->getLiteral().negate())},
+ {pfLit}));
+ // Add the explaination proofs.
+ for (const auto constraint : explain)
+ {
+ NodeBuilder nb;
+ conflictPfs.push_back(constraint->externalExplainByAssertions(nb));
+ }
+ // Collect the farkas coefficients, as nodes.
+ std::vector<Node> farkasCoefficients;
+ farkasCoefficients.reserve(coeffs->size());
+ auto nm = NodeManager::currentNM();
+ std::transform(coeffs->begin(),
+ coeffs->end(),
+ std::back_inserter(farkasCoefficients),
+ [nm, type](const Rational& r) {
+ return nm->mkConstRealOrInt(type, r);
+ });
+
+ // Prove bottom.
+ auto sumPf = d_pnm->mkNode(
+ PfRule::MACRO_ARITH_SCALE_SUM_UB, conflictPfs, farkasCoefficients);
+ auto botPf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
+
+ // Prove the conflict
+ std::vector<Node> assumptions;
+ assumptions.reserve(clause.getNumChildren());
+ std::transform(clause.begin(),
+ clause.end(),
+ std::back_inserter(assumptions),
+ [](TNode r) { return r.negate(); });
+ auto notAndNotPf = d_pnm->mkScope(botPf, assumptions);
+
+ // Convert it to a clause
+ auto orNotNotPf = d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
+ clausePf = d_pnm->mkNode(
+ PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {clause});
+
+ // Output it
+ TrustNode trustedClause = d_pfGen->mkTrustNode(clause, clausePf);
+ outputTrustedLemma(trustedClause, InferenceId::ARITH_ROW_IMPL);
+ }
+ else
+ {
+ outputLemma(clause, InferenceId::ARITH_ROW_IMPL);
+ }
+ }
+ else
+ {
+ Assert(!implied->negationHasProof());
+ implied->impliedByFarkas(explain, coeffs, false);
+ implied->tryToPropagate();
+ }
+ return true;
+ }
+
+ if(TraceIsOn("arith::prop")){
+ Trace("arith::prop")
+ << "failed " << v << " " << assertedToTheTheory << " "
+ << canBePropagated << " " << hasProof << " " << implied << endl;
+ d_partialModel.printModel(v, Trace("arith::prop"));
+ }
+ return false;
+}
+
+bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){
+ BoundCounts hasCount = d_linEq.hasBoundCount(ridx);
+ uint32_t rowLength = d_tableau.getRowLength(ridx);
+
+ bool success = false;
+
+ Trace("arith::prop") << "propagateCandidateRow attempt " << rowLength << " "
+ << hasCount << endl;
+
+ if (rowLength >= options().arith.arithPropagateMaxLength
+ && Random::getRandom().pickWithProb(
+ 1.0 - double(options().arith.arithPropagateMaxLength) / rowLength))
+ {
+ return false;
+ }
+
+ if(hasCount.lowerBoundCount() == rowLength){
+ success |= attemptFull(ridx, false);
+ }else if(hasCount.lowerBoundCount() + 1 == rowLength){
+ success |= attemptSingleton(ridx, false);
+ }
+
+ if(hasCount.upperBoundCount() == rowLength){
+ success |= attemptFull(ridx, true);
+ }else if(hasCount.upperBoundCount() + 1 == rowLength){
+ success |= attemptSingleton(ridx, true);
+ }
+ return success;
+}
+
+void TheoryArithPrivate::dumpUpdatedBoundsToRows(){
+ Assert(d_candidateRows.empty());
+ DenseSet::const_iterator i = d_updatedBounds.begin();
+ DenseSet::const_iterator end = d_updatedBounds.end();
+ for(; i != end; ++i){
+ ArithVar var = *i;
+ if(d_tableau.isBasic(var)){
+ RowIndex ridx = d_tableau.basicToRowIndex(var);
+ d_candidateRows.softAdd(ridx);
+ }else{
+ Tableau::ColIterator basicIter = d_tableau.colIterator(var);
+ for(; !basicIter.atEnd(); ++basicIter){
+ const Tableau::Entry& entry = *basicIter;
+ RowIndex ridx = entry.getRowIndex();
+ d_candidateRows.softAdd(ridx);
+ }
+ }
+ }
+ d_updatedBounds.purge();
+}
+
+const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+ return d_rowTracking[ridx];
+}
+
+std::pair<bool, Node> TheoryArithPrivate::entailmentCheck(TNode lit)
+{
+ ArithEntailmentCheckParameters params;
+ params.addLookupRowSumAlgorithms();
+ ArithEntailmentCheckSideEffects out;
+
+ using namespace inferbounds;
+
+ // l k r
+ // diff : (l - r) k 0
+ Trace("arith::entailCheck") << "TheoryArithPrivate::entailmentCheck(" << lit << ")"<< endl;
+ Kind k;
+ int primDir;
+ Rational lm, rm, dm;
+ Node lp, rp, dp;
+ DeltaRational sep;
+ bool successful = decomposeLiteral(lit, k, primDir, lm, lp, rm, rp, dm, dp, sep);
+ if(!successful) { return make_pair(false, Node::null()); }
+
+ if (dp.isConst())
+ {
+ Node eval = rewrite(lit);
+ Assert(eval.getKind() == kind::CONST_BOOLEAN);
+ // if true, true is an acceptable explaination
+ // if false, the node is uninterpreted and eval can be forgotten
+ return make_pair(eval.getConst<bool>(), eval);
+ }
+ Assert(dm != Rational(0));
+ Assert(primDir == 1 || primDir == -1);
+
+ int negPrim = -primDir;
+
+ int secDir = (k == EQUAL || k == DISTINCT) ? negPrim: 0;
+ int negSecDir = (k == EQUAL || k == DISTINCT) ? primDir: 0;
+
+ // primDir*[lm*( lp )] k primDir*[ [rm*( rp )] + sep ]
+ // primDir*[lm*( lp ) - rm*( rp ) ] k primDir*sep
+ // primDir*[dm * dp] k primDir*sep
+
+ std::pair<Node, DeltaRational> bestPrimLeft, bestNegPrimRight, bestPrimDiff, tmp;
+ std::pair<Node, DeltaRational> bestSecLeft, bestNegSecRight, bestSecDiff;
+ bestPrimLeft.first = Node::null(); bestNegPrimRight.first = Node::null(); bestPrimDiff.first = Node::null();
+ bestSecLeft.first = Node::null(); bestNegSecRight.first = Node::null(); bestSecDiff.first = Node::null();
+
+
+
+ ArithEntailmentCheckParameters::const_iterator alg, alg_end;
+ for( alg = params.begin(), alg_end = params.end(); alg != alg_end; ++alg ){
+ const inferbounds::InferBoundAlgorithm& ibalg = *alg;
+
+ Trace("arith::entailCheck") << "entailmentCheck trying " << (inferbounds::Algorithms) ibalg.getAlgorithm() << endl;
+ switch(ibalg.getAlgorithm()){
+ case inferbounds::None:
+ break;
+ case inferbounds::Lookup:
+ case inferbounds::RowSum:
+ {
+ typedef void (TheoryArithPrivate::*EntailmentCheckFunc)(std::pair<Node, DeltaRational>&, int, TNode) const;
+
+ EntailmentCheckFunc ecfunc =
+ (ibalg.getAlgorithm() == inferbounds::Lookup)
+ ? (&TheoryArithPrivate::entailmentCheckBoundLookup)
+ : (&TheoryArithPrivate::entailmentCheckRowSum);
+
+ (*this.*ecfunc)(tmp, primDir * lm.sgn(), lp);
+ setToMin(primDir * lm.sgn(), bestPrimLeft, tmp);
+
+ (*this.*ecfunc)(tmp, negPrim * rm.sgn(), rp);
+ setToMin(negPrim * rm.sgn(), bestNegPrimRight, tmp);
+
+ (*this.*ecfunc)(tmp, secDir * lm.sgn(), lp);
+ setToMin(secDir * lm.sgn(), bestSecLeft, tmp);
+
+ (*this.*ecfunc)(tmp, negSecDir * rm.sgn(), rp);
+ setToMin(negSecDir * rm.sgn(), bestNegSecRight, tmp);
+
+ (*this.*ecfunc)(tmp, primDir * dm.sgn(), dp);
+ setToMin(primDir * dm.sgn(), bestPrimDiff, tmp);
+
+ (*this.*ecfunc)(tmp, secDir * dm.sgn(), dp);
+ setToMin(secDir * dm.sgn(), bestSecDiff, tmp);
+ }
+ break;
+ default:
+ Unhandled();
+ }
+
+ // turn bounds on prim * left and -prim * right into bounds on prim * diff
+ if(!bestPrimLeft.first.isNull() && !bestNegPrimRight.first.isNull()){
+ // primDir*lm* lp <= primDir*lm*L
+ // -primDir*rm* rp <= -primDir*rm*R
+ // primDir*lm* lp -primDir*rm* rp <= primDir*lm*L - primDir*rm*R
+ // primDir [lm* lp -rm* rp] <= primDir[lm*L - *rm*R]
+ // primDir [dm * dp] <= primDir[lm*L - *rm*R]
+ // primDir [dm * dp] <= primDir * dm * ([lm*L - *rm*R]/dm)
+ tmp.second = ((bestPrimLeft.second * lm) - (bestNegPrimRight.second * rm)) / dm;
+ tmp.first = (bestPrimLeft.first).andNode(bestNegPrimRight.first);
+ setToMin(primDir, bestPrimDiff, tmp);
+ }
+
+ // turn bounds on sec * left and sec * right into bounds on sec * diff
+ if(secDir != 0 && !bestSecLeft.first.isNull() && !bestNegSecRight.first.isNull()){
+ // secDir*lm* lp <= secDir*lm*L
+ // -secDir*rm* rp <= -secDir*rm*R
+ // secDir*lm* lp -secDir*rm* rp <= secDir*lm*L - secDir*rm*R
+ // secDir [lm* lp -rm* rp] <= secDir[lm*L - *rm*R]
+ // secDir [dm * dp] <= secDir[lm*L - *rm*R]
+ // secDir [dm * dp] <= secDir * dm * ([lm*L - *rm*R]/dm)
+ tmp.second = ((bestSecLeft.second * lm) - (bestNegSecRight.second * rm)) / dm;
+ tmp.first = (bestSecLeft.first).andNode(bestNegSecRight.first);
+ setToMin(secDir, bestSecDiff, tmp);
+ }
+
+ switch(k){
+ case LEQ:
+ if(!bestPrimDiff.first.isNull()){
+ DeltaRational d = (bestPrimDiff.second * dm);
+ if((primDir > 0 && d <= sep) || (primDir < 0 && d >= sep) ){
+ Trace("arith::entailCheck") << "entailmentCheck found "
+ << primDir << "*" << dm << "*(" << dp<<")"
+ << " <= " << primDir << "*" << dm << "*" << bestPrimDiff.second
+ << " <= " << primDir << "*" << sep << endl
+ << " by " << bestPrimDiff.first << endl;
+ Assert(bestPrimDiff.second * (Rational(primDir) * dm)
+ <= (sep * Rational(primDir)));
+ return make_pair(true, bestPrimDiff.first);
+ }
+ }
+ break;
+ case EQUAL:
+ if(!bestPrimDiff.first.isNull() && !bestSecDiff.first.isNull()){
+ // Is primDir [dm * dp] == primDir * sep entailed?
+ // Iff [dm * dp] == sep entailed?
+ // Iff dp == sep / dm entailed?
+ // Iff dp <= sep / dm and dp >= sep / dm entailed?
+
+ // primDir [dm * dp] <= primDir * dm * U
+ // secDir [dm * dp] <= secDir * dm * L
+
+ // Suppose primDir * dm > 0
+ // then secDir * dm < 0
+ // dp >= (secDir * L) / secDir * dm
+ // dp >= (primDir * L) / primDir * dm
+ //
+ // dp <= U / dm
+ // dp >= L / dm
+ // dp == sep / dm entailed iff U == L == sep
+ // Suppose primDir * dm < 0
+ // then secDir * dm > 0
+ // dp >= U / dm
+ // dp <= L / dm
+ // dp == sep / dm entailed iff U == L == sep
+ if(bestPrimDiff.second == bestSecDiff.second){
+ if(bestPrimDiff.second == sep){
+ return make_pair(true, (bestPrimDiff.first).andNode(bestSecDiff.first));
+ }
+ }
+ }
+ // intentionally fall through to DISTINCT case!
+ // entailments of negations are eager exit cases for EQUAL
+ CVC5_FALLTHROUGH;
+ case DISTINCT:
+ if(!bestPrimDiff.first.isNull()){
+ // primDir [dm * dp] <= primDir * dm * U < primDir * sep
+ if((primDir > 0 && (bestPrimDiff.second * dm < sep)) ||
+ (primDir < 0 && (bestPrimDiff.second * dm > sep))){
+ // entailment of negation
+ if(k == DISTINCT){
+ return make_pair(true, bestPrimDiff.first);
+ }else{
+ Assert(k == EQUAL);
+ return make_pair(false, Node::null());
+ }
+ }
+ }
+ if(!bestSecDiff.first.isNull()){
+ // If primDir [dm * dp] > primDir * sep, then this is not entailed.
+ // If primDir [dm * dp] >= primDir * dm * L > primDir * sep
+ // -primDir * dm * L < -primDir * sep
+ // secDir * dm * L < secDir * sep
+ if((secDir > 0 && (bestSecDiff.second * dm < sep)) ||
+ (secDir < 0 && (bestSecDiff.second * dm > sep))){
+ if(k == DISTINCT){
+ return make_pair(true, bestSecDiff.first);
+ }else{
+ Assert(k == EQUAL);
+ return make_pair(false, Node::null());
+ }
+ }
+ }
+
+ break;
+ default:
+ Unreachable();
+ break;
+ }
+ }
+ return make_pair(false, Node::null());
+}
+
+bool TheoryArithPrivate::decomposeTerm(Node t,
+ Rational& m,
+ Node& p,
+ Rational& c)
+{
+ if(!Polynomial::isMember(t)){
+ return false;
+ }
+
+ // TODO Speed up
+ preprocessing::util::ContainsTermITEVisitor ctv;
+ if(ctv.containsTermITE(t)){
+ return false;
+ }
+
+ Polynomial poly = Polynomial::parsePolynomial(t);
+ if(poly.isConstant()){
+ c = poly.getHead().getConstant().getValue();
+ p = mkRationalNode(Rational(0));
+ m = Rational(1);
+ return true;
+ }else if(poly.containsConstant()){
+ c = poly.getHead().getConstant().getValue();
+ poly = poly.getTail();
+ }else{
+ c = Rational(0);
+ }
+ Assert(!poly.isConstant());
+ Assert(!poly.containsConstant());
+
+ const bool intVars = poly.allIntegralVariables();
+
+ if(intVars){
+ m = Rational(1);
+ if(!poly.isIntegral()){
+ Integer denom = poly.denominatorLCM();
+ m /= denom;
+ poly = poly * denom;
+ }
+ Integer g = poly.gcd();
+ m *= g;
+ poly = poly * Rational(1,g);
+ Assert(poly.isIntegral());
+ }else{
+ Assert(!intVars);
+ m = poly.getHead().getConstant().getValue();
+ poly = poly * m.inverse();
+ Assert(poly.leadingCoefficientIsAbsOne());
+ }
+ p = poly.getNode();
+ return true;
+}
+
+void TheoryArithPrivate::setToMin(int sgn, std::pair<Node, DeltaRational>& min, const std::pair<Node, DeltaRational>& e){
+ if(sgn != 0){
+ if(min.first.isNull() && !e.first.isNull()){
+ min = e;
+ }else if(!min.first.isNull() && !e.first.isNull()){
+ if(sgn > 0 && min.second > e.second){
+ min = e;
+ }else if(sgn < 0 && min.second < e.second){
+ min = e;
+ }
+ }
+ }
+}
+
+// std::pair<bool, Node> TheoryArithPrivate::entailmentUpperCheck(const Rational& lm, Node lp, const Rational& rm, Node rp, const DeltaRational& sep, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
+
+// Rational negRM = -rm;
+// Node diff = NodeManager::currentNM()->mkNode(MULT, mkRationalConstan(lm), lp) + (negRM * rp);
+
+// Rational diffm;
+// Node diffp;
+// decompose(diff, diffm, diffNode);
+
+
+// std::pair<Node, DeltaRational> bestUbLeft, bestLbRight, bestUbDiff, tmp;
+// bestUbLeft = bestLbRight = bestUbDiff = make_pair(Node::Null(), DeltaRational());
+
+// return make_pair(false, Node::null());
+// }
+
+/**
+ * Decomposes a literal into the form:
+ * dir*[lm*( lp )] k dir*[ [rm*( rp )] + sep ]
+ * dir*[dm* dp] k dir *sep
+ * dir is either 1 or -1
+ */
+bool TheoryArithPrivate::decomposeLiteral(Node lit, Kind& k, int& dir, Rational& lm, Node& lp, Rational& rm, Node& rp, Rational& dm, Node& dp, DeltaRational& sep){
+ bool negated = (lit.getKind() == kind::NOT);
+ TNode atom = negated ? lit[0] : lit;
+
+ TNode left = atom[0];
+ TNode right = atom[1];
+
+ // left : lm*( lp ) + lc
+ // right: rm*( rp ) + rc
+ Rational lc, rc;
+ bool success = decomposeTerm(rewrite(left), lm, lp, lc);
+ if(!success){ return false; }
+ success = decomposeTerm(rewrite(right), rm, rp, rc);
+ if(!success){ return false; }
+
+ Node diff = rewrite(NodeManager::currentNM()->mkNode(kind::SUB, left, right));
+ Rational dc;
+ success = decomposeTerm(diff, dm, dp, dc);
+ Assert(success);
+
+ // reduce the kind of the to not include literals
+ // GT, NOT LEQ
+ // GEQ, NOT LT
+ // LT, NOT GEQ
+ // LEQ, NOT LT
+ Kind atomKind = atom.getKind();
+ Kind normKind = negated ? negateKind(atomKind) : atomKind;
+
+ if(normKind == GEQ || normKind == GT){
+ dir = -1;
+ normKind = (normKind == GEQ) ? LEQ : LT;
+ }else{
+ dir = 1;
+ }
+
+ Trace("arith::decomp") << "arith::decomp "
+ << lit << "(" << normKind << "*" << dir << ")"<< endl
+ << " left:" << lc << " + " << lm << "*(" << lp << ") : " <<left << endl
+ << " right:" << rc << " + " << rm << "*(" << rp << ") : " << right << endl
+ << " diff: " << dc << " + " << dm << "*("<< dp <<"): " << diff << endl
+ << " sep: " << sep << endl;
+
+
+ // k in LT, LEQ, EQUAL, DISEQUAL
+ // [dir*lm*( lp ) + dir*lc] k [dir*rm*( rp ) + dir*rc]
+ Rational change = rc - lc;
+ Assert(change == (-dc));
+ // [dir*lm*( lp )] k [dir*rm*( rp ) + dir*(rc - lc)]
+ if(normKind == LT){
+ sep = DeltaRational(change, Rational(-1));
+ k = LEQ;
+ }else{
+ sep = DeltaRational(change);
+ k = normKind;
+ }
+ // k in LEQ, EQUAL, DISEQUAL
+ // dir*lm*( lp ) k [dir*rm*( rp )] + dir*(sep + d * delta)
+ return true;
+}
+
+/**
+ * Precondition:
+ * tp is a polynomial not containing an ite.
+ * either tp is constant or contains no constants.
+ * Post:
+ * if tmp.first is not null, then
+ * sgn * tp <= sgn * tmp.second
+ */
+void TheoryArithPrivate::entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
+ tmp.first = Node::null();
+ if(sgn == 0){ return; }
+
+ Assert(Polynomial::isMember(tp));
+ if (tp.isConst())
+ {
+ tmp.first = mkBoolNode(true);
+ tmp.second = DeltaRational(tp.getConst<Rational>());
+ }
+ else if (d_partialModel.hasArithVar(tp))
+ {
+ Assert(!tp.isConst());
+ ArithVar v = d_partialModel.asArithVar(tp);
+ Assert(v != ARITHVAR_SENTINEL);
+ ConstraintP c = (sgn > 0)
+ ? d_partialModel.getUpperBoundConstraint(v)
+ : d_partialModel.getLowerBoundConstraint(v);
+ if(c != NullConstraint){
+ tmp.first = Constraint::externalExplainByAssertions({c});
+ tmp.second = c->getValue();
+ }
+ }
+}
+
+void TheoryArithPrivate::entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
+ tmp.first = Node::null();
+ if(sgn == 0){ return; }
+ if (tp.getKind() != ADD)
+ {
+ return;
+ }
+ Assert(Polynomial::isMember(tp));
+
+ tmp.second = DeltaRational(0);
+ NodeBuilder nb(kind::AND);
+
+ Polynomial p = Polynomial::parsePolynomial(tp);
+ for(Polynomial::iterator i = p.begin(), iend = p.end(); i != iend; ++i) {
+ Monomial m = *i;
+ Node x = m.getVarList().getNode();
+ if(d_partialModel.hasArithVar(x)){
+ ArithVar v = d_partialModel.asArithVar(x);
+ const Rational& coeff = m.getConstant().getValue();
+ int dir = sgn * coeff.sgn();
+ ConstraintP c = (dir > 0)
+ ? d_partialModel.getUpperBoundConstraint(v)
+ : d_partialModel.getLowerBoundConstraint(v);
+ if(c != NullConstraint){
+ tmp.second += c->getValue() * coeff;
+ c->externalExplainByAssertions(nb);
+ }else{
+ //failed
+ return;
+ }
+ }else{
+ // failed
+ return;
+ }
+ }
+ // success
+ tmp.first = nb;
+}
+
+ArithProofRuleChecker* TheoryArithPrivate::getProofChecker()
+{
+ return &d_checker;
+}
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
--- /dev/null
+/******************************************************************************
+ * Top contributors (to current version):
+ * Tim King, Andrew Reynolds, Gereon Kremer
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved. See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * [[ Add one-line brief description here ]]
+ *
+ * [[ Add lengthier description here ]]
+ * \todo document this file
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include "context/cdhashset.h"
+#include "context/cdinsert_hashmap.h"
+#include "context/cdlist.h"
+#include "context/cdqueue.h"
+#include "expr/kind.h"
+#include "expr/node.h"
+#include "expr/node_builder.h"
+#include "proof/trust_node.h"
+#include "theory/arith/linear/arith_static_learner.h"
+#include "theory/arith/arith_utilities.h"
+#include "theory/arith/linear/arithvar.h"
+#include "theory/arith/linear/attempt_solution_simplex.h"
+#include "theory/arith/branch_and_bound.h"
+#include "theory/arith/linear/congruence_manager.h"
+#include "theory/arith/linear/constraint.h"
+#include "theory/arith/delta_rational.h"
+#include "theory/arith/linear/dio_solver.h"
+#include "theory/arith/linear/dual_simplex.h"
+#include "theory/arith/linear/error_set.h"
+#include "theory/arith/linear/fc_simplex.h"
+#include "theory/arith/linear/infer_bounds.h"
+#include "theory/arith/linear/linear_equality.h"
+#include "theory/arith/linear/matrix.h"
+#include "theory/arith/linear/normal_form.h"
+#include "theory/arith/linear/partial_model.h"
+#include "theory/arith/proof_checker.h"
+#include "theory/arith/linear/soi_simplex.h"
+#include "theory/arith/theory_arith.h"
+#include "theory/valuation.h"
+#include "util/dense_map.h"
+#include "util/integer.h"
+#include "util/rational.h"
+#include "util/result.h"
+#include "util/statistics_stats.h"
+
+namespace cvc5::internal {
+
+class EagerProofGenerator;
+
+namespace theory {
+
+class TheoryModel;
+
+namespace arith::linear {
+
+class BranchCutInfo;
+class TreeLog;
+class ApproximateStatistics;
+
+class ArithEntailmentCheckParameters;
+class ArithEntailmentCheckSideEffects;
+namespace inferbounds {
+ class InferBoundAlgorithm;
+}
+class InferBoundsResult;
+
+/**
+ * Implementation of QF_LRA.
+ * Based upon:
+ * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
+ */
+class TheoryArithPrivate : protected EnvObj
+{
+ private:
+ static constexpr uint32_t RESET_START = 2;
+
+ TheoryArith& d_containing;
+
+ /**
+ * Whether we encountered non-linear arithmetic at any time during solving.
+ */
+ bool d_foundNl;
+
+ BoundInfoMap d_rowTracking;
+ /** Branch and bound utility */
+ BranchAndBound& d_bab;
+ // For proofs
+ /** Manages the proof nodes of this theory. */
+ ProofNodeManager* d_pnm;
+ /** Checks the proof rules of this theory. */
+ ArithProofRuleChecker d_checker;
+ /** Stores proposition(node)/proof pairs. */
+ std::unique_ptr<EagerProofGenerator> d_pfGen;
+
+ /**
+ * The constraint database associated with the theory.
+ * This must be declared before ArithPartialModel.
+ */
+ ConstraintDatabase d_constraintDatabase;
+
+ enum Result::Status d_qflraStatus;
+ // check()
+ // !done() -> d_qflraStatus = Unknown
+ // fullEffort(e) -> simplex returns either sat or unsat
+ // !fullEffort(e) -> simplex returns either sat, unsat or unknown
+ // if unknown, save the assignment
+ // if unknown, the simplex priority queue cannot be emptied
+ int d_unknownsInARow;
+
+ bool d_replayedLemmas;
+
+ /**
+ * This counter is false if nothing has been done since the last cut.
+ * This is used to break an infinite loop.
+ */
+ bool d_hasDoneWorkSinceCut;
+
+ /** Static learner. */
+ ArithStaticLearner d_learner;
+
+ //std::vector<ArithVar> d_pool;
+public:
+ void releaseArithVar(ArithVar v);
+ void signal(ArithVar v){ d_errorSet.signalVariable(v); }
+
+
+private:
+ // t does not contain constants
+ void entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
+ void entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
+
+ /**
+ * Infers either a new upper/lower bound on term in the real relaxation.
+ * Either:
+ * - term is malformed (see below)
+ * - a maximum/minimum is found with the result being a pair
+ * -- <dr, exp> where
+ * -- term <?> dr is implies by exp
+ * -- <?> is <= if inferring an upper bound, >= otherwise
+ * -- exp is in terms of the assertions to the theory.
+ * - No upper or lower bound is inferrable in the real relaxation.
+ * -- Returns <0, Null()>
+ * - the maximum number of rounds was exhausted:
+ * -- Returns <v, term> where v is the current feasible value of term
+ * - Threshold reached:
+ * -- If theshold != NULL, and a feasible value is found to exceed threshold
+ * -- Simplex stops and returns <threshold, term>
+ */
+ //std::pair<DeltaRational, Node> inferBound(TNode term, bool lb, int maxRounds = -1, const DeltaRational* threshold = NULL);
+
+private:
+ static bool decomposeTerm(Node t, Rational& m, Node& p, Rational& c);
+ bool decomposeLiteral(Node lit,
+ Kind& k,
+ int& dir,
+ Rational& lm,
+ Node& lp,
+ Rational& rm,
+ Node& rp,
+ Rational& dm,
+ Node& dp,
+ DeltaRational& sep);
+ static void setToMin(int sgn,
+ std::pair<Node, DeltaRational>& min,
+ const std::pair<Node, DeltaRational>& e);
+
+ typedef ArithVariables::var_iterator var_iterator;
+ var_iterator var_begin() const { return d_partialModel.var_begin(); }
+ var_iterator var_end() const { return d_partialModel.var_end(); }
+
+ NodeSet d_setupNodes;
+public:
+ bool isSetup(Node n) const {
+ return d_setupNodes.find(n) != d_setupNodes.end();
+ }
+ void markSetup(Node n){
+ Assert(!isSetup(n));
+ d_setupNodes.insert(n);
+ }
+private:
+ void setupVariable(const Variable& x);
+ void setupVariableList(const VarList& vl);
+ void setupPolynomial(const Polynomial& poly);
+public:
+ void setupAtom(TNode atom);
+private:
+ void cautiousSetupPolynomial(const Polynomial& p);
+
+ /**
+ * A superset of all of the assertions that currently are not the literal for
+ * their constraint do not match constraint literals. Not just the witnesses.
+ */
+ context::CDInsertHashMap<Node, ConstraintP>
+ d_assertionsThatDoNotMatchTheirLiterals;
+
+ /** Returns true if x is of type Integer. */
+ inline bool isInteger(ArithVar x) const {
+ return d_partialModel.isInteger(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)
+ && d_preregisteredNodes.contains(d_partialModel.asNode(x));
+ }
+
+ /**
+ * On full effort checks (after determining LA(Q) satisfiability), we
+ * consider integer vars, but we make sure to do so fairly to avoid
+ * nontermination (although this isn't a guarantee). To do it fairly,
+ * we consider variables in round-robin fashion. This is the
+ * round-robin index.
+ */
+ ArithVar d_nextIntegerCheckVar;
+
+ /**
+ * Queue of Integer variables that are known to be equal to a constant.
+ */
+ context::CDQueue<ArithVar> d_constantIntegerVariables;
+
+ Node callDioSolver();
+ /**
+ * Produces lemmas of the form (or (>= f 0) (<= f 0)),
+ * where f is a plane that the diophantine solver is interested in.
+ *
+ * More precisely, produces lemmas of the form (or (>= lc -c) (<= lc -c))
+ * where lc is a linear combination of variables, c is a constant, and lc + c
+ * is the plane.
+ */
+ TrustNode dioCutting();
+
+ Comparison mkIntegerEqualityFromAssignment(ArithVar v);
+
+ /**
+ * List of all of the disequalities asserted in the current context that are not known
+ * to be satisfied.
+ */
+ context::CDQueue<ConstraintP> d_diseqQueue;
+
+ /**
+ * Constraints that have yet to be processed by proagation work list.
+ * All of the elements have type of LowerBound, UpperBound, or
+ * Equality.
+ *
+ * This is empty at the beginning of every check call.
+ *
+ * If head()->getType() == LowerBound or UpperBound,
+ * then d_cPL[1] is the previous constraint in d_partialModel for the
+ * corresponding bound.
+ * If head()->getType() == Equality,
+ * then d_cPL[1] is the previous lowerBound in d_partialModel,
+ * and d_cPL[2] is the previous upperBound in d_partialModel.
+ */
+ std::deque<ConstraintP> d_currentPropagationList;
+
+ context::CDQueue<ConstraintP> d_learnedBounds;
+
+ /**
+ * Contains all nodes that have been preregistered
+ */
+ context::CDHashSet<Node> d_preregisteredNodes;
+
+ /**
+ * Manages information about the assignment and upper and lower bounds on
+ * variables.
+ */
+ ArithVariables d_partialModel;
+
+ /** The set of variables in error in the partial model. */
+ ErrorSet d_errorSet;
+
+ /**
+ * The tableau for all of the constraints seen thus far in the system.
+ */
+ Tableau d_tableau;
+
+ /**
+ * Maintains the relationship between the PartialModel and the Tableau.
+ */
+ LinearEqualityModule d_linEq;
+
+ /**
+ * A Diophantine equation solver. Accesses the tableau and partial
+ * model (each in a read-only fashion).
+ */
+ DioSolver d_diosolver;
+
+ /** Counts the number of notifyRestart() calls to the theory. */
+ uint32_t d_restartsCounter;
+
+ /**
+ * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
+ * the density of the tableau, d, is computed.
+ * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
+ * is set to d_initialTableau.
+ */
+ bool d_tableauSizeHasBeenModified;
+ double d_tableauResetDensity;
+ uint32_t d_tableauResetPeriod;
+ static constexpr uint32_t s_TABLEAU_RESET_INCREMENT = 5;
+
+ /** This is only used by simplex at the moment. */
+ context::CDList<std::pair<ConstraintCP, InferenceId>> d_conflicts;
+
+ /** This is only used by simplex at the moment. */
+ context::CDO<Node> d_blackBoxConflict;
+ /** For holding the proof of the above conflict node. */
+ context::CDO<std::shared_ptr<ProofNode>> d_blackBoxConflictPf;
+
+ bool isProofEnabled() const;
+
+ public:
+ /**
+ * This adds the constraint a to the queue of conflicts in d_conflicts.
+ * Both a and ~a must have a proof.
+ */
+ void raiseConflict(ConstraintCP a, InferenceId id);
+
+ // 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);
+
+ /** This is a conflict that is magically known to hold. */
+ void raiseBlackBoxConflict(Node bb, std::shared_ptr<ProofNode> pf = nullptr);
+ /**
+ * Returns true iff a conflict has been raised. This method is public since
+ * it is needed by the ArithState class to know whether we are in conflict.
+ */
+ bool anyConflict() const;
+
+ private:
+ inline bool conflictQueueEmpty() const {
+ return d_conflicts.empty();
+ }
+
+ /**
+ * Outputs the contents of d_conflicts onto d_out.
+ * The conditions of anyConflict() must hold.
+ */
+ void outputConflicts();
+
+ /**
+ * A copy of the tableau.
+ * This is equivalent to the original tableau if d_tableauSizeHasBeenModified
+ * is false.
+ * The set of basic and non-basic variables may differ from d_tableau.
+ */
+ Tableau d_smallTableauCopy;
+
+ /**
+ * Returns true if all of the basic variables in the simplex queue of
+ * basic variables that violate their bounds in the current tableau
+ * are basic in d_smallTableauCopy.
+ *
+ * d_tableauSizeHasBeenModified must be false when calling this.
+ * Simplex's priority queue must be in collection mode.
+ */
+ bool safeToReset() const;
+
+ /** This keeps track of difference equalities. Mostly for sharing. */
+ ArithCongruenceManager d_congruenceManager;
+ context::CDO<bool> d_cmEnabled;
+
+ /** This implements the Simplex decision procedure. */
+ DualSimplexDecisionProcedure d_dualSimplex;
+ FCSimplexDecisionProcedure d_fcSimplex;
+ SumOfInfeasibilitiesSPD d_soiSimplex;
+ AttemptSolutionSDP d_attemptSolSimplex;
+
+ 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);
+ ~ModelException() override;
+ };
+
+ /**
+ * Computes the delta rational value of a term from the current partial
+ * model. This returns the delta value assignment to the term if it is in the
+ * partial model. Otherwise, this is computed recursively for arithmetic terms
+ * from each subterm.
+ *
+ * This throws a DeltaRationalException if the value cannot be represented as
+ * a DeltaRational. This throws a ModelException if there is a term is not in
+ * the partial model and is not a theory of arithmetic term.
+ *
+ * precondition: The linear abstraction of the nodes must be satisfiable.
+ */
+ DeltaRational getDeltaValue(TNode term) const
+ /* throw(DeltaRationalException, ModelException) */;
+ public:
+ TheoryArithPrivate(TheoryArith& containing, Env& env, BranchAndBound& bab);
+ ~TheoryArithPrivate();
+
+ //--------------------------------- initialization
+ /**
+ * Returns true if we need an equality engine, see
+ * Theory::needsEqualityEngine.
+ */
+ bool needsEqualityEngine(EeSetupInfo& esi);
+ /** finish initialize */
+ void finishInit();
+ //--------------------------------- end initialization
+
+ /**
+ * Does non-context dependent setup for a node connected to a theory.
+ */
+ void preRegisterTerm(TNode n);
+
+ void propagate(Theory::Effort e);
+ TrustNode explain(TNode n);
+
+ Rational deltaValueForTotalOrder() const;
+
+ bool collectModelInfo(TheoryModel* m);
+ /**
+ * Collect model values. This is the main method for extracting information
+ * about how to construct the model. This method relies on the caller for
+ * processing the map, which is done so that other modules (e.g. the
+ * non-linear extension) can modify arithModel before it is sent to the model.
+ *
+ * @param termSet The set of relevant terms
+ * @param arithModel Mapping from terms (of real type) to their values. The
+ * caller should assert equalities to the model for each entry in this map.
+ */
+ void collectModelValues(const std::set<Node>& termSet,
+ std::map<Node, Node>& arithModel);
+
+ void shutdown(){ }
+
+ void presolve();
+ void notifyRestart();
+ Theory::PPAssertStatus ppAssert(TrustNode tin,
+ TrustSubstitutionMap& outSubstitutions);
+ void ppStaticLearn(TNode in, NodeBuilder& learned);
+
+ std::string identify() const { return std::string("TheoryArith"); }
+
+ EqualityStatus getEqualityStatus(TNode a, TNode b);
+
+ /** Called when n is notified as being a shared term with TheoryArith. */
+ void notifySharedTerm(TNode n);
+
+ Node getModelValue(TNode var);
+
+
+ std::pair<bool, Node> entailmentCheck(TNode lit);
+
+ //--------------------------------- standard check
+ /** Pre-check, called before the fact queue of the theory is processed. */
+ bool preCheck(Theory::Effort level);
+ /** Pre-notify fact. */
+ void preNotifyFact(TNode atom, bool pol, TNode fact);
+ /**
+ * Post-check, called after the fact queue of the theory is processed. Returns
+ * true if a conflict or lemma was emitted.
+ */
+ bool postCheck(Theory::Effort level);
+ //--------------------------------- end standard check
+ /**
+ * Found non-linear? This returns true if this solver ever encountered
+ * any non-linear terms that were unhandled. Note that this class is not
+ * responsible for handling non-linear arithmetic. If the owner of this
+ * class does not handle non-linear arithmetic in another way, then
+ * setIncomplete should be called on the output channel of TheoryArith.
+ */
+ bool foundNonlinear() const;
+
+ /** get the proof checker of this theory */
+ ArithProofRuleChecker* getProofChecker();
+
+ private:
+ /** The constant zero. */
+ DeltaRational d_DELTA_ZERO;
+
+ /** propagates an arithvar */
+ void propagateArithVar(bool upperbound, ArithVar var );
+
+ /**
+ * Using the simpleKind return the ArithVar associated with the assertion.
+ */
+ ArithVar determineArithVar(const Polynomial& p) const;
+ ArithVar determineArithVar(TNode assertion) const;
+
+ /**
+ * Splits the disequalities in d_diseq that are violated using lemmas on demand.
+ * returns true if any lemmas were issued.
+ * returns false if all disequalities are satisfied in the current model.
+ */
+ bool splitDisequalities();
+
+ /** A Difference variable is known to be 0.*/
+ void zeroDifferenceDetected(ArithVar x);
+
+
+ /**
+ * Looks for the next integer variable without an integer assignment in a
+ * round-robin fashion. Changes the value of d_nextIntegerCheckVar.
+ *
+ * This returns true if all integer variables have integer assignments.
+ * If this returns false, d_nextIntegerCheckVar does not have an integer
+ * assignment.
+ */
+ bool hasIntegerModel();
+
+ /**
+ * 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 nextIntegerViolation(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().
+ */
+ TrustNode roundRobinBranch();
+
+ bool proofsEnabled() const { return d_pnm; }
+
+ 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 aux, bool internal);
+
+public:
+ const BoundsInfo& boundsInfo(ArithVar basic) const;
+
+
+private:
+ /** Initial (not context dependent) sets up for a variable.*/
+ void setupBasicValue(ArithVar x);
+
+ /** Initial (not context dependent) sets up for a new auxiliary variable.*/
+ void setupAuxiliary(TNode left);
+
+
+ /**
+ * Assert*(n, orig) takes an bound n that is implied by orig.
+ * and asserts that as a new bound if it is tighter than the current bound
+ * and updates the value of a basic variable if needed.
+ *
+ * orig must be a literal in the SAT solver so that it can be used for
+ * conflict analysis.
+ *
+ * x is the variable getting the new bound,
+ * c is the value of the new bound.
+ *
+ * If this new bound is in conflict with the other bound,
+ * a node describing this conflict is returned.
+ * If this new bound is not in conflict, Node::null() is returned.
+ */
+ bool AssertLower(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;
+
+ /** Tracks the basic variables where propagation might be possible. */
+ DenseSet d_candidateBasics;
+ DenseSet d_candidateRows;
+
+ bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
+ void clearUpdates();
+
+ void revertOutOfConflict();
+
+ void propagateCandidatesNew();
+ void dumpUpdatedBoundsToRows();
+ bool propagateCandidateRow(RowIndex rid);
+ bool propagateMightSucceed(ArithVar v, bool ub) const;
+ /** Attempt to perform a row propagation where there is at most 1 possible variable.*/
+ bool attemptSingleton(RowIndex ridx, bool rowUp);
+ /** 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, 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;
+
+ void propagateCandidates();
+ void propagateCandidate(ArithVar basic);
+ bool propagateCandidateBound(ArithVar basic, bool upperBound);
+
+ inline bool propagateCandidateLowerBound(ArithVar basic){
+ return propagateCandidateBound(basic, false);
+ }
+ inline bool propagateCandidateUpperBound(ArithVar basic){
+ return propagateCandidateBound(basic, true);
+ }
+
+ /**
+ * Performs a check to see if it is definitely true that setup can be avoided.
+ */
+ bool canSafelyAvoidEqualitySetup(TNode equality);
+
+ /**
+ * Handles the case splitting for check() for a new assertion.
+ * Returns a conflict if one was found.
+ * Returns Node::null if no conflict was found.
+ *
+ * @param assertion The assertion that was just popped from the fact queue
+ * of TheoryArith and given to this class via preNotifyFact.
+ */
+ ConstraintP constraintFromFactQueue(TNode assertion);
+ bool assertionCases(ConstraintP c);
+
+ /**
+ * Returns the basic variable with the shorted row containing a non-basic variable.
+ * If no such row exists, return ARITHVAR_SENTINEL.
+ */
+ ArithVar findShortestBasicRow(ArithVar variable);
+
+ /**
+ * Debugging only routine!
+ * Returns true iff every variable is consistent in the partial model.
+ */
+ bool entireStateIsConsistent(const std::string& locationHint);
+ bool unenqueuedVariablesAreConsistent();
+
+ bool isImpliedUpperBound(ArithVar var, Node exp);
+ bool isImpliedLowerBound(ArithVar var, Node exp);
+
+ void internalExplain(TNode n, NodeBuilder& explainBuilder);
+
+ void asVectors(const Polynomial& p,
+ std::vector<Rational>& coeffs,
+ std::vector<ArithVar>& variables);
+
+ /** Routine for debugging. Print the assertions the theory is aware of. */
+ void debugPrintAssertions(std::ostream& out) const;
+ /** Debugging only routine. Prints the model. */
+ void debugPrintModel(std::ostream& out) const;
+
+ bool done() const { return d_containing.done(); }
+ bool isLeaf(TNode x) const { return d_containing.isLeaf(x); }
+ TheoryId theoryOf(TNode x) const { return d_containing.theoryOf(x); }
+ void debugPrintFacts() const { d_containing.debugPrintFacts(); }
+ bool outputTrustedLemma(TrustNode lem, InferenceId id);
+ bool outputLemma(TNode lem, InferenceId id);
+ void outputTrustedConflict(TrustNode conf, InferenceId id);
+ void outputConflict(TNode lit, InferenceId id);
+ void outputPropagate(TNode lit);
+ void outputRestart();
+
+ inline bool isSatLiteral(TNode l) const {
+ return (d_containing.d_valuation).isSatLiteral(l);
+ }
+ inline Node getSatValue(TNode n) const {
+ return (d_containing.d_valuation).getSatValue(n);
+ }
+
+ /** Used for replaying approximate simplex */
+ context::CDQueue<TrustNode> d_approxCuts;
+ /** Also used for replaying approximate simplex. "approximate cuts temporary storage" */
+ std::vector<TrustNode> d_acTmp;
+
+ /** Counts the number of fullCheck calls to arithmetic. */
+ uint32_t d_fullCheckCounter;
+ std::vector<ArithVar> cutAllBounded() const;
+ TrustNode branchIntegerVariable(ArithVar x) const;
+ void branchVector(const std::vector<ArithVar>& lemmas);
+
+ context::CDO<unsigned> d_cutCount;
+ context::CDHashSet<ArithVar, std::hash<ArithVar>> d_cutInContext;
+
+ context::CDO<bool> d_likelyIntegerInfeasible;
+
+ 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);
+ std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch);
+
+ void replayAssert(ConstraintP c);
+
+ static ConstraintCP vectorToIntHoleConflict(const ConstraintCPVec& conflict);
+ static void intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict);
+
+ // 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;
+
+ RationalVector d_farkasBuffer;
+
+ //---------------- during check
+ /** Whether there were new facts during preCheck */
+ bool d_newFacts;
+ /** The previous status, computed during preCheck */
+ Result::Status d_previousStatus;
+ //---------------- end during check
+
+ /** These fields are designed to be accessible to TheoryArith methods. */
+ class Statistics {
+ public:
+ IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
+
+ IntStat d_statUserVariables, d_statAuxiliaryVariables;
+ IntStat d_statDisequalitySplits;
+ IntStat d_statDisequalityConflicts;
+ TimerStat d_simplifyTimer;
+ TimerStat d_staticLearningTimer;
+
+ TimerStat d_presolveTime;
+
+ TimerStat d_newPropTime;
+
+ IntStat d_externalBranchAndBounds;
+
+ IntStat d_initialTableauSize;
+ IntStat d_currSetToSmaller;
+ IntStat d_smallerSetToCurr;
+ TimerStat d_restartTimer;
+
+ TimerStat d_boundComputationTime;
+ IntStat d_boundComputations, d_boundPropagations;
+
+ IntStat d_unknownChecks;
+ IntStat d_maxUnknownsInARow;
+ AverageStat d_avgUnknownsInARow;
+
+ IntStat d_revertsOnConflicts;
+ IntStat d_commitsOnConflicts;
+ IntStat d_nontrivialSatChecks;
+
+ 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;
+ HistogramStat<uint32_t> d_unknownPivots;
+
+ IntStat d_solveIntModelsAttempts;
+ IntStat d_solveIntModelsSuccessful;
+ TimerStat d_mipTimer;
+ TimerStat d_lpTimer;
+
+ IntStat d_mipProofsAttempted;
+ IntStat d_mipProofsSuccessful;
+
+ IntStat d_numBranchesFailed;
+
+ Statistics(StatisticsRegistry& reg, const std::string& name);
+ };
+
+
+ Statistics d_statistics;
+}; /* class TheoryArithPrivate */
+
+
+} // namespace arith
+} // namespace theory
+} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andres Noetzli
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This implements the LinearEqualityModule.
- */
-#include "theory/arith/linear_equality.h"
-
-#include "base/output.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/* Explicitly instatiate these functions. */
-
-template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
-template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
-
-template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
-template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
-
-
-void Border::output(std::ostream& out) const{
- out << "{Border"
- << ", " << d_bound->getVariable()
- << ", " << d_bound->getValue()
- << ", " << d_diff
- << ", " << d_areFixing
- << ", " << d_upperbound;
- if(ownBorder()){
- out << ", ownBorder";
- }else{
- out << ", " << d_entry->getCoefficient();
- }
- out << ", " << d_bound
- << "}";
-}
-
-LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundsTracking, BasicVarModelUpdateCallBack f):
- d_variables(vars),
- d_tableau(t),
- d_basicVariableUpdates(f),
- d_increasing(1),
- d_decreasing(-1),
- d_upperBoundDifference(),
- d_lowerBoundDifference(),
- d_one(1),
- d_negOne(-1),
- d_btracking(boundsTracking),
- d_areTracking(false),
- d_trackCallback(this)
-{}
-
-LinearEqualityModule::Statistics::Statistics()
- : d_statPivots(
- smtStatisticsRegistry().registerInt("theory::arith::pivots")),
- d_statUpdates(
- smtStatisticsRegistry().registerInt("theory::arith::updates")),
- d_pivotTime(
- smtStatisticsRegistry().registerTimer("theory::arith::pivotTime")),
- d_adjTime(
- smtStatisticsRegistry().registerTimer("theory::arith::adjTime")),
- d_weakeningAttempts(smtStatisticsRegistry().registerInt(
- "theory::arith::weakening::attempts")),
- d_weakeningSuccesses(smtStatisticsRegistry().registerInt(
- "theory::arith::weakening::success")),
- d_weakenings(smtStatisticsRegistry().registerInt(
- "theory::arith::weakening::total")),
- d_weakenTime(smtStatisticsRegistry().registerTimer(
- "theory::arith::weakening::time")),
- d_forceTime(
- smtStatisticsRegistry().registerTimer("theory::arith::forcing::time"))
-{
-}
-
-void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){
- Assert(!d_areTracking);
-
- BoundsInfo curr = d_variables.boundsInfo(v);
-
- Assert(prev != curr);
- Tableau::ColIterator basicIter = d_tableau.colIterator(v);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- Assert(entry.getColVar() == v);
- int a_ijSgn = entry.getCoefficient().sgn();
-
- RowIndex ridx = entry.getRowIndex();
- BoundsInfo& counts = d_btracking.get(ridx);
- Trace("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ;
- counts.addInChange(a_ijSgn, prev, curr);
- Trace("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl;
- }
-}
-
-void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
- for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
- ArithVar nb = *i;
- if(!d_tableau.isBasic(nb)){
- Assert(!d_tableau.isBasic(nb));
- const DeltaRational& newValue = many[nb];
- if(newValue != d_variables.getAssignment(nb)){
- Trace("arith::updateMany")
- << "updateMany:" << nb << " "
- << d_variables.getAssignment(nb) << " to "<< newValue << endl;
- update(nb, newValue);
- }
- }
- }
-}
-
-
-
-
-void LinearEqualityModule::applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues){
- forceNewBasis(newBasis);
- updateMany(newValues);
-}
-
-void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
- TimerStat::CodeTimer codeTimer(d_statistics.d_forceTime);
- cout << "force begin" << endl;
- DenseSet needsToBeAdded;
- for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){
- ArithVar b = *i;
- if(!d_tableau.isBasic(b)){
- needsToBeAdded.add(b);
- }
- }
-
- while(!needsToBeAdded.empty()){
- ArithVar toRemove = ARITHVAR_SENTINEL;
- ArithVar toAdd = ARITHVAR_SENTINEL;
- DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end();
- for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){
- ArithVar v = *i;
-
- Tableau::ColIterator colIter = d_tableau.colIterator(v);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == v);
- ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex());
- if(!newBasis.isMember(b)){
- toAdd = v;
- if(toRemove == ARITHVAR_SENTINEL ||
- d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)){
- toRemove = b;
- }
- }
- }
- }
- Assert(toRemove != ARITHVAR_SENTINEL);
- Assert(toAdd != ARITHVAR_SENTINEL);
-
- Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
- d_tableau.pivot(toRemove, toAdd, d_trackCallback);
- d_basicVariableUpdates(toAdd);
-
- Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
- needsToBeAdded.remove(toAdd);
- }
-}
-
-void LinearEqualityModule::updateUntracked(ArithVar x_i, const DeltaRational& v){
- Assert(!d_tableau.isBasic(x_i));
- Assert(!d_areTracking);
- const DeltaRational& assignment_x_i = d_variables.getAssignment(x_i);
- ++(d_statistics.d_statUpdates);
-
-
- Trace("arith") <<"update " << x_i << ": "
- << assignment_x_i << "|-> " << v << endl;
- DeltaRational diff = v - assignment_x_i;
-
- Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == x_i);
-
- ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
- const Rational& a_ji = entry.getCoefficient();
-
- const DeltaRational& assignment = d_variables.getAssignment(x_j);
- DeltaRational nAssignment = assignment+(diff * a_ji);
- d_variables.setAssignment(x_j, nAssignment);
-
- d_basicVariableUpdates(x_j);
- }
-
- d_variables.setAssignment(x_i, v);
-
- if(TraceIsOn("paranoid:check_tableau")){ debugCheckTableau(); }
-}
-
-void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
- TimerStat::CodeTimer codeTimer(d_statistics.d_adjTime);
-
- Assert(!d_tableau.isBasic(x_i));
- Assert(d_areTracking);
-
- ++(d_statistics.d_statUpdates);
-
- DeltaRational diff = v - d_variables.getAssignment(x_i);
- Trace("arith") <<"update " << x_i << ": "
- << d_variables.getAssignment(x_i) << "|-> " << v << endl;
-
-
- BoundCounts before = d_variables.atBoundCounts(x_i);
- d_variables.setAssignment(x_i, v);
- BoundCounts after = d_variables.atBoundCounts(x_i);
-
- bool anyChange = before != after;
-
- Tableau::ColIterator colIter = d_tableau.colIterator(x_i);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == x_i);
-
- RowIndex ridx = entry.getRowIndex();
- ArithVar x_j = d_tableau.rowIndexToBasic(ridx);
- const Rational& a_ji = entry.getCoefficient();
-
- const DeltaRational& assignment = d_variables.getAssignment(x_j);
- DeltaRational nAssignment = assignment+(diff * a_ji);
- Trace("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl;
- BoundCounts xjBefore = d_variables.atBoundCounts(x_j);
- d_variables.setAssignment(x_j, nAssignment);
- BoundCounts xjAfter = d_variables.atBoundCounts(x_j);
-
- Assert(rowIndexIsTracked(ridx));
- BoundsInfo& next_bc_k = d_btracking.get(ridx);
- if(anyChange){
- next_bc_k.addInAtBoundChange(a_ji.sgn(), before, after);
- }
- if(xjBefore != xjAfter){
- next_bc_k.addInAtBoundChange(-1, xjBefore, xjAfter);
- }
-
- d_basicVariableUpdates(x_j);
- }
-
- if(TraceIsOn("paranoid:check_tableau")){ debugCheckTableau(); }
-}
-
-void LinearEqualityModule::pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& x_i_value){
- Assert(x_i != x_j);
-
- TimerStat::CodeTimer codeTimer(d_statistics.d_pivotTime);
-
- if(TraceIsOn("arith::tracking::pre")){
- Trace("arith::tracking") << "pre update" << endl;
- debugCheckTracking();
- }
-
- if(TraceIsOn("arith::simplex:row")){ debugPivot(x_i, x_j); }
-
- RowIndex ridx = d_tableau.basicToRowIndex(x_i);
- const Tableau::Entry& entry_ij = d_tableau.findEntry(ridx, x_j);
- Assert(!entry_ij.blank());
-
- const Rational& a_ij = entry_ij.getCoefficient();
- const DeltaRational& betaX_i = d_variables.getAssignment(x_i);
- DeltaRational theta = (x_i_value - betaX_i)/a_ij;
- DeltaRational x_j_value = d_variables.getAssignment(x_j) + theta;
-
- updateTracked(x_j, x_j_value);
-
- if(TraceIsOn("arith::tracking::mid")){
- Trace("arith::tracking") << "postupdate prepivot" << endl;
- debugCheckTracking();
- }
-
- // Pivots
- ++(d_statistics.d_statPivots);
-
- d_tableau.pivot(x_i, x_j, d_trackCallback);
-
- if(TraceIsOn("arith::tracking::post")){
- Trace("arith::tracking") << "postpivot" << endl;
- debugCheckTracking();
- }
-
- d_basicVariableUpdates(x_j);
-
- if(TraceIsOn("matrix")){
- d_tableau.printMatrix();
- }
-}
-
-uint32_t LinearEqualityModule::updateProduct(const UpdateInfo& inf) const {
- uint32_t colLen = d_tableau.getColLength(inf.nonbasic());
- if(inf.describesPivot()){
- Assert(inf.leaving() != inf.nonbasic());
- return colLen + d_tableau.basicRowLength(inf.leaving());
- }else{
- return colLen;
- }
-}
-
-void LinearEqualityModule::debugCheckTracking(){
- Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
- endIter = d_tableau.endBasic();
- for(; basicIter != endIter; ++basicIter){
- ArithVar basic = *basicIter;
- Trace("arith::tracking") << "arith::tracking row basic: " << basic << endl;
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd() && TraceIsOn("arith::tracking"); ++iter){
- const Tableau::Entry& entry = *iter;
-
- ArithVar var = entry.getColVar();
- const Rational& coeff = entry.getCoefficient();
- DeltaRational beta = d_variables.getAssignment(var);
- Trace("arith::tracking") << var << " " << d_variables.boundsInfo(var)
- << " " << beta << coeff;
- if(d_variables.hasLowerBound(var)){
- Trace("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
- }
- if(d_variables.hasUpperBound(var)){
- Trace("arith::tracking") << "(up " << d_variables.getUpperBound(var) << ")";
- }
- Trace("arith::tracking") << endl;
- }
- Trace("arith::tracking") << "end row"<< endl;
-
- if(basicIsTracked(basic)){
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
- BoundsInfo computed = computeRowBoundInfo(ridx, false);
- Trace("arith::tracking")
- << "computed " << computed
- << " tracking " << d_btracking[ridx] << endl;
- Assert(computed == d_btracking[ridx]);
- }
- }
-}
-
-void LinearEqualityModule::debugPivot(ArithVar x_i, ArithVar x_j){
- Trace("arith::pivot") << "debugPivot("<< x_i <<"|->"<< x_j << ")" << endl;
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
-
- ArithVar var = entry.getColVar();
- const Rational& coeff = entry.getCoefficient();
- DeltaRational beta = d_variables.getAssignment(var);
- Trace("arith::pivot") << var << beta << coeff;
- if(d_variables.hasLowerBound(var)){
- Trace("arith::pivot") << "(lb " << d_variables.getLowerBound(var) << ")";
- }
- if(d_variables.hasUpperBound(var)){
- Trace("arith::pivot") << "(up " << d_variables.getUpperBound(var) << ")";
- }
- Trace("arith::pivot") << endl;
- }
- Trace("arith::pivot") << "end row"<< endl;
-}
-
-/**
- * This check is quite expensive.
- * It should be wrapped in a TraceIsOn() guard.
- * if(TraceIsOn("paranoid:check_tableau")){
- * checkTableau();
- * }
- */
-void LinearEqualityModule::debugCheckTableau(){
- Tableau::BasicIterator basicIter = d_tableau.beginBasic(),
- endIter = d_tableau.endBasic();
- for(; basicIter != endIter; ++basicIter){
- ArithVar basic = *basicIter;
- DeltaRational sum;
- Trace("paranoid:check_tableau") << "starting row" << basic << endl;
- Tableau::RowIterator nonbasicIter = d_tableau.basicRowIterator(basic);
- for(; !nonbasicIter.atEnd(); ++nonbasicIter){
- const Tableau::Entry& entry = *nonbasicIter;
- ArithVar nonbasic = entry.getColVar();
- if(basic == nonbasic) continue;
-
- const Rational& coeff = entry.getCoefficient();
- DeltaRational beta = d_variables.getAssignment(nonbasic);
- Trace("paranoid:check_tableau") << nonbasic << beta << coeff<<endl;
- sum = sum + (beta*coeff);
- }
- DeltaRational shouldBe = d_variables.getAssignment(basic);
- Trace("paranoid:check_tableau") << "ending row" << sum
- << "," << shouldBe << endl;
-
- Assert(sum == shouldBe);
- }
-}
-
-DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const {
- DeltaRational sum(0,0);
- for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
- const Tableau::Entry& entry = (*i);
- ArithVar v = entry.getColVar();
- if(v == skip){ continue; }
-
- const Rational& coeff = entry.getCoefficient();
- bool vUb = (rowUb == (coeff.sgn() > 0));
-
- const DeltaRational& bound = vUb ?
- d_variables.getUpperBound(v):
- d_variables.getLowerBound(v);
-
- DeltaRational diff = bound * coeff;
- sum = sum + diff;
- }
- return sum;
-}
-
-/**
- * Computes the value of a basic variable using the current assignment.
- */
-DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{
- Assert(d_tableau.isBasic(x));
- DeltaRational sum(0);
-
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(x); !i.atEnd(); ++i){
- const Tableau::Entry& entry = (*i);
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x) continue;
- const Rational& coeff = entry.getCoefficient();
-
- const DeltaRational& assignment = d_variables.getAssignment(nonbasic, useSafe);
- sum = sum + (assignment * coeff);
- }
- return sum;
-}
-
-const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool rowUb, ArithVar skip){
- Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
- for(; !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
-
- ArithVar var = entry.getColVar();
- if(var == skip) { continue; }
-
- int sgn = entry.getCoefficient().sgn();
- bool selectUb = (rowUb == (sgn > 0));
- bool hasBound = selectUb ?
- d_variables.hasUpperBound(var):
- d_variables.hasLowerBound(var);
- if(!hasBound){
- return &entry;
- }
- }
- return NULL;
-}
-
-void LinearEqualityModule::propagateBasicFromRow(ConstraintP c,
- bool produceProofs)
-{
- Assert(c != NullConstraint);
- Assert(c->isUpperBound() || c->isLowerBound());
- Assert(!c->assertedToTheTheory());
- Assert(!c->hasProof());
-
- bool upperBound = c->isUpperBound();
- ArithVar basic = c->getVariable();
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
- ConstraintCPVec bounds;
- RationalVectorP coeffs = produceProofs ? new RationalVector() : nullptr;
- propagateRow(bounds, ridx, upperBound, c, coeffs);
- c->impliedByFarkas(bounds, coeffs, false);
- c->tryToPropagate();
-
- if(coeffs != RationalVectorPSentinel) { delete coeffs; }
-}
-
-/* An explanation of the farkas coefficients.
- *
- * We are proving c using the other variables on the row.
- * The proof is in terms of the other constraints and the negation of c, ~c.
- *
- * A row has the form:
- * sum a_i * x_i = 0
- * or
- * sx + sum r y + sum q z = 0
- * where r > 0 and q < 0.
- *
- * If rowUp, we are proving c
- * g = sum r u_y + sum q l_z
- * and c is entailed by -sx <= g
- * If !rowUp, we are proving c
- * g = sum r l_y + sum q u_z
- * and c is entailed by -sx >= g
- *
- * | s | c | ~c | u_i | l_i
- * if rowUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
- * if rowUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
- * if !rowUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
- * if !rowUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
- *
- *
- * Thus we treat !rowUp as multiplying the row by -1 and rowUp as 1
- * for the entire row.
- */
-void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c, RationalVectorP farkas){
- Assert(!c->assertedToTheTheory());
- Assert(c->canBePropagated());
- Assert(!c->hasProof());
-
- if(farkas != RationalVectorPSentinel){
- Assert(farkas->empty());
- farkas->push_back(Rational(0));
- }
-
- ArithVar v = c->getVariable();
- Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
- << ridx << ", " << rowUp << ", " << v << ") start" << endl;
-
- const Rational& multiple = rowUp ? d_one : d_negOne;
-
- Trace("arith::propagateRow") << "multiple: " << multiple << endl;
-
- Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
- for(; !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- const Rational& a_ij = entry.getCoefficient();
- int sgn = a_ij.sgn();
- Assert(sgn != 0);
- bool selectUb = rowUp ? (sgn > 0) : (sgn < 0);
-
- Assert(nonbasic != v || (rowUp && a_ij.sgn() > 0 && c->isLowerBound())
- || (rowUp && a_ij.sgn() < 0 && c->isUpperBound())
- || (!rowUp && a_ij.sgn() > 0 && c->isUpperBound())
- || (!rowUp && a_ij.sgn() < 0 && c->isLowerBound()));
-
- if(TraceIsOn("arith::propagateRow")){
- if(nonbasic == v){
- Trace("arith::propagateRow") << "(target) "
- << rowUp << " "
- << a_ij.sgn() << " "
- << c->isLowerBound() << " "
- << c->isUpperBound() << endl;
-
- Trace("arith::propagateRow") << "(target) ";
- }
- Trace("arith::propagateRow") << "propagateRow " << a_ij << " * " << nonbasic ;
- }
-
- if(nonbasic == v){
- if(farkas != RationalVectorPSentinel){
- Assert(farkas->front().isZero());
- Rational multAij = multiple * a_ij;
- Trace("arith::propagateRow") << "(" << multAij << ") ";
- farkas->front() = multAij;
- }
-
- Trace("arith::propagateRow") << c << endl;
- }else{
-
- ConstraintCP bound = selectUb
- ? d_variables.getUpperBoundConstraint(nonbasic)
- : d_variables.getLowerBoundConstraint(nonbasic);
-
- if(farkas != RationalVectorPSentinel){
- Rational multAij = multiple * a_ij;
- Trace("arith::propagateRow") << "(" << multAij << ") ";
- farkas->push_back(multAij);
- }
- Assert(bound != NullConstraint);
- Trace("arith::propagateRow") << bound << endl;
- into.push_back(bound);
- }
- }
- Trace("arith::propagateRow") << "LinearEqualityModule::propagateRow("
- << ridx << ", " << rowUp << ", " << v << ") done" << endl;
-
-}
-
-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);
-
- ConstraintP c = ub ?
- d_variables.getUpperBoundConstraint(v) :
- d_variables.getLowerBoundConstraint(v);
-
- bool weakened;
- do{
- const DeltaRational& bound = c->getValue();
-
- weakened = false;
-
- ConstraintP weaker = ub?
- c->getStrictlyWeakerUpperBound(true, true):
- c->getStrictlyWeakerLowerBound(true, true);
-
- if(weaker != NullConstraint){
- const DeltaRational& weakerBound = weaker->getValue();
-
- DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
- //if var == basic,
- // if aboveUpper, weakerBound > bound, multiply by -1
- // if !aboveUpper, weakerBound < bound, multiply by -1
- diff = diff * coeff;
- if(surplus > diff){
- ++d_statistics.d_weakenings;
- weakened = true;
- anyWeakening = true;
- surplus = surplus - diff;
-
- Trace("arith::weak") << "found:" << endl;
- if(v == basic){
- Trace("arith::weak") << " basic: ";
- }
- Trace("arith::weak") << " " << surplus << " "<< diff << endl
- << " " << bound << c << endl
- << " " << weakerBound << weaker << endl;
-
- Assert(diff.sgn() > 0);
- c = weaker;
- }
- }
- }while(weakened);
-
- return c;
-}
-
-/* An explanation of the farkas coefficients.
- *
- * We are proving a conflict on the basic variable x_b.
- * If aboveUpper, then the conflict is with the constraint c : x_b <= u_b.
- * If !aboveUpper, then the conflict is with the constraint c : x_b >= l_b.
- *
- * A row has the form:
- * -x_b sum a_i * x_i = 0
- * or
- * -x_b + sum r y + sum q z = 0,
- * x_b = sum r y + sum q z
- * where r > 0 and q < 0.
- *
- *
- * If !aboveUp, we are proving ~c: x_b < l_b
- * g = sum r u_y + sum q l_z
- * x_b <= g < l_b
- * and ~c is entailed by x_b <= g
- *
- * If aboveUp, we are proving ~c : x_b > u_b
- * g = sum r l_y + sum q u_z
- * x_b >= g > u_b
- * and ~c is entailed by x_b >= g
- *
- *
- * | s | c | ~c | u_i | l_i
- * if !aboveUp | s > 0 | x >= -g/s | x < -g/s | a_i > 0 | a_i < 0
- * if !aboveUp | s < 0 | x <= -g/s | x > -g/s | a_i > 0 | a_i < 0
- * if aboveUp | s > 0 | x <= -g/s | x > -g/s | a_i < 0 | a_i > 0
- * if aboveUp | s < 0 | x >= -g/s | x < -g/s | a_i < 0 | a_i > 0
- *
- * Thus we treat aboveUp as multiplying the row by -1 and !aboveUp as 1
- * for the entire row.
- */
-ConstraintCP LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& fcs) const {
- Assert(!fcs.underConstruction());
- TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
-
- Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
- << aboveUpper <<", "<< basicVar << ", ...) start" << endl;
-
- const Rational& adjustSgn = aboveUpper ? d_negOne : d_one;
- const DeltaRational& assignment = d_variables.getAssignment(basicVar);
- DeltaRational surplus;
- if(aboveUpper){
- Assert(d_variables.hasUpperBound(basicVar));
- Assert(assignment > d_variables.getUpperBound(basicVar));
- surplus = assignment - d_variables.getUpperBound(basicVar);
- }else{
- Assert(d_variables.hasLowerBound(basicVar));
- Assert(assignment < d_variables.getLowerBound(basicVar));
- surplus = d_variables.getLowerBound(basicVar) - assignment;
- }
-
- 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;
- ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
- Trace("arith::weak") << "weak : " << weakening << " "
- << c->assertedToTheTheory() << " "
- << d_variables.getAssignment(v) << " "
- << c << endl;
- anyWeakenings = anyWeakenings || weakening;
-
- fcs.addConstraint(c, coeff, adjustSgn);
- if(basicVar == v){
- Assert(!c->negationHasProof());
- fcs.makeLastConsequent();
- }
- }
- Assert(fcs.consequentIsSet());
-
- ConstraintCP conflicted = fcs.commitConflict();
-
- ++d_statistics.d_weakeningAttempts;
- if(anyWeakenings){
- ++d_statistics.d_weakeningSuccesses;
- }
- Trace("arith::weak") << "LinearEqualityModule::minimallyWeakConflict("
- << aboveUpper <<", "<< basicVar << ", ...) done" << endl;
- return conflicted;
-}
-
-ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const {
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- if(x <= y){
- return x;
- } else {
- return y;
- }
-}
-
-ArithVar LinearEqualityModule::minColLength(ArithVar x, ArithVar y) const {
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!d_tableau.isBasic(x));
- Assert(!d_tableau.isBasic(y));
- uint32_t xLen = d_tableau.getColLength(x);
- uint32_t yLen = d_tableau.getColLength(y);
- if( xLen > yLen){
- return y;
- } else if( xLen== yLen ){
- return minVarOrder(x,y);
- }else{
- return x;
- }
-}
-
-ArithVar LinearEqualityModule::minRowLength(ArithVar x, ArithVar y) const {
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(d_tableau.isBasic(x));
- Assert(d_tableau.isBasic(y));
- uint32_t xLen = d_tableau.basicRowLength(x);
- uint32_t yLen = d_tableau.basicRowLength(y);
- if( xLen > yLen){
- return y;
- } else if( xLen== yLen ){
- return minVarOrder(x,y);
- }else{
- return x;
- }
-}
-
-ArithVar LinearEqualityModule::minBoundAndColLength(ArithVar x, ArithVar y) const{
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!d_tableau.isBasic(x));
- Assert(!d_tableau.isBasic(y));
- if(d_variables.hasEitherBound(x) && !d_variables.hasEitherBound(y)){
- return y;
- }else if(!d_variables.hasEitherBound(x) && d_variables.hasEitherBound(y)){
- return x;
- }else {
- return minColLength(x, y);
- }
-}
-
-template <bool above>
-ArithVar LinearEqualityModule::selectSlack(ArithVar x_i, VarPreferenceFunction pref) const{
- ArithVar slack = ARITHVAR_SENTINEL;
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
- const Rational& a_ij = entry.getCoefficient();
- int sgn = a_ij.sgn();
- if(isAcceptableSlack<above>(sgn, nonbasic)){
- //If one of the above conditions is met, we have found an acceptable
- //nonbasic variable to pivot x_i with. We can now choose which one we
- //prefer the most.
- slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : (this->*pref)(slack, nonbasic);
- }
- }
-
- return slack;
-}
-
-const Tableau::Entry* LinearEqualityModule::selectSlackEntry(ArithVar x_i, bool above) const{
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
- const Rational& a_ij = entry.getCoefficient();
- int sgn = a_ij.sgn();
- if(above && isAcceptableSlack<true>(sgn, nonbasic)){
- //If one of the above conditions is met, we have found an acceptable
- //nonbasic variable to pivot x_i with. We can now choose which one we
- //prefer the most.
- return &entry;
- }else if(!above && isAcceptableSlack<false>(sgn, nonbasic)){
- return &entry;
- }
- }
-
- return NULL;
-}
-
-void LinearEqualityModule::startTrackingBoundCounts(){
- Assert(!d_areTracking);
- d_areTracking = true;
- if(TraceIsOn("arith::tracking")){
- debugCheckTracking();
- }
- Assert(d_areTracking);
-}
-
-void LinearEqualityModule::stopTrackingBoundCounts(){
- Assert(d_areTracking);
- d_areTracking = false;
- if(TraceIsOn("arith::tracking")){
- debugCheckTracking();
- }
- Assert(!d_areTracking);
-}
-
-
-void LinearEqualityModule::trackRowIndex(RowIndex ridx){
- Assert(!rowIndexIsTracked(ridx));
- BoundsInfo bi = computeRowBoundInfo(ridx, true);
- d_btracking.set(ridx, bi);
-}
-
-BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{
- BoundsInfo bi;
-
- Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
- for(; !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar v = entry.getColVar();
- const Rational& a_ij = entry.getCoefficient();
- bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn());
- }
- return bi;
-}
-
-BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const {
- return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds();
-}
-
-/**
- * If the pivot described in u were performed,
- * then the row would qualify as being either at the minimum/maximum
- * to the non-basics being at their bounds.
- * The minimum/maximum is determined by the direction the non-basic is changing.
- */
-bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
- Assert(u.describesPivot());
-
- ArithVar nonbasic = u.nonbasic();
- ArithVar basic = u.leaving();
- Assert(basicIsTracked(basic));
- int coeffSgn = u.getCoefficient().sgn();
- int nbdir = u.nonbasicDirection();
-
- ConstraintP c = u.limiting();
- int toUB = (c->getType() == UpperBound ||
- c->getType() == Equality) ? 1 : 0;
- int toLB = (c->getType() == LowerBound ||
- c->getType() == Equality) ? 1 : 0;
-
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
- BoundCounts bcs = d_btracking[ridx].atBounds();
- // x = c*n + \sum d*m
- // 0 = -x + c*n + \sum d*m
- // n = 1/c * x + -1/c * (\sum d*m)
- BoundCounts nonb = bcs - d_variables.atBoundCounts(nonbasic).multiplyBySgn(coeffSgn);
- nonb.addInChange(-1, d_variables.atBoundCounts(basic), BoundCounts(toLB, toUB));
- nonb = nonb.multiplyBySgn(-coeffSgn);
-
- uint32_t length = d_tableau.basicRowLength(basic);
- Trace("basicsAtBounds")
- << "bcs " << bcs
- << "nonb " << nonb
- << "length " << length << endl;
- // nonb has nb excluded.
- if(nbdir < 0){
- return nonb.lowerBoundCount() + 1 == length;
- }else{
- Assert(nbdir > 0);
- return nonb.upperBoundCount() + 1 == length;
- }
-}
-
-bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
- Assert(basicIsTracked(basic));
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
- BoundCounts bcs = d_btracking[ridx].atBounds();
- uint32_t length = d_tableau.basicRowLength(basic);
-
- // return true if excluding the basic is every element is at its "lowerbound"
- // The psuedo code is:
- // bcs -= basic.count(basic, basic's sgn)
- // return bcs.lowerBoundCount() + 1 == length
- // As basic's sign is always -1, we can pull out the pieces of the count:
- // bcs.lowerBoundCount() - basic.atUpperBoundInd() + 1 == length
- // basic.atUpperBoundInd() is either 0 or 1
- uint32_t lbc = bcs.lowerBoundCount();
- return (lbc == length) ||
- (lbc + 1 == length && d_variables.cmpAssignmentUpperBound(basic) != 0);
-}
-
-bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const {
- Assert(basicIsTracked(basic));
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
- BoundCounts bcs = d_btracking[ridx].atBounds();
- uint32_t length = d_tableau.basicRowLength(basic);
- uint32_t ubc = bcs.upperBoundCount();
- // See the comment for nonbasicsAtLowerBounds()
-
- return (ubc == length) ||
- (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0);
-}
-
-void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) {
- Assert(rowIndexIsTracked(ridx));
- Assert(sgn != 0);
- if(sgn < 0){
- BoundsInfo& bi = d_btracking.get(ridx);
- bi = bi.multiplyBySgn(sgn);
- }
-}
-
-void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
- Assert(oldSgn != currSgn);
- BoundsInfo nb_inf = d_variables.boundsInfo(nb);
-
- Assert(rowIndexIsTracked(ridx));
-
- BoundsInfo& row_bi = d_btracking.get(ridx);
- row_bi.addInSgn(nb_inf, oldSgn, currSgn);
-}
-
-ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
- if(vec.empty()) {
- return ARITHVAR_SENTINEL;
- }else {
- ArithVar sel = vec.front();
- ArithVarVec::const_iterator i = vec.begin() + 1;
- ArithVarVec::const_iterator i_end = vec.end();
- for(; i != i_end; ++i){
- sel = (this->*pf)(sel, *i);
- }
- return sel;
- }
-}
-
-bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub){
- ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
-
- Assert(basicIsTracked(currBasic));
-
- ConstraintP bound = ub ?
- d_variables.getUpperBoundConstraint(currBasic):
- d_variables.getLowerBoundConstraint(currBasic);
-
- if(bound == NullConstraint){ return false; }
- Assert(bound != NullConstraint);
-
- const Rational& coeff = entry.getCoefficient();
-
- const DeltaRational& assignment = d_variables.getAssignment(currBasic);
- DeltaRational toBound = bound->getValue() - assignment;
- DeltaRational nbDiff = toBound/coeff;
-
- // if ub
- // if toUB >= 0
- // then ub >= currBasic
- // if sgn > 0,
- // then diff >= 0, so nb must increase for G
- // else diff <= 0, so nb must decrease for G
- // else ub < currBasic
- // if sgn > 0,
- // then diff < 0, so nb must decrease for G
- // else diff > 0, so nb must increase for G
-
- int diffSgn = nbDiff.sgn();
-
- if(diffSgn != 0 && willBeInConflictAfterPivot(entry, nbDiff, ub)){
- return true;
- }else{
- bool areFixing = ub ? (toBound.sgn() < 0 ) : (toBound.sgn() > 0);
- Border border(bound, nbDiff, areFixing, &entry, ub);
- bool increasing =
- (diffSgn > 0) ||
- (diffSgn == 0 && ((coeff.sgn() > 0) == ub));
-
- // assume diffSgn == 0
- // if coeff > 0,
- // if ub, inc
- // else, dec
- // else coeff < 0
- // if ub, dec
- // else, inc
-
- if(increasing){
- Trace("handleBorders") << "push back increasing " << border << endl;
- d_increasing.push_back(border);
- }else{
- Trace("handleBorders") << "push back decreasing " << border << endl;
- d_decreasing.push_back(border);
- }
- return false;
- }
-}
-
-bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const{
- int nbSgn = nbDiff.sgn();
- Assert(nbSgn != 0);
-
- if(nbSgn > 0){
- if (!d_upperBoundDifference || nbDiff <= *d_upperBoundDifference)
- {
- return false;
- }
- }else{
- if (!d_lowerBoundDifference || nbDiff >= *d_lowerBoundDifference)
- {
- return false;
- }
- }
-
- // Assume past this point, nb will be in error if this pivot is done
- ArithVar nb = entry.getColVar();
- RowIndex ridx = entry.getRowIndex();
- ArithVar basic = d_tableau.rowIndexToBasic(ridx);
- Assert(rowIndexIsTracked(ridx));
- int coeffSgn = entry.getCoefficient().sgn();
-
-
- // if bToUB, then basic is going to be set to its upperbound
- // if not bToUB, then basic is going to be set to its lowerbound
-
- // Different steps of solving for this:
- // 1) y = a * x + \sum b * z
- // 2) -a * x = -y + \sum b * z
- // 3) x = (-1/a) * ( -y + \sum b * z)
-
- BoundCounts bc = d_btracking[ridx].atBounds();
-
- // 1) y = a * x + \sum b * z
- // Get bc(\sum b * z)
- BoundCounts sumOnly = bc - d_variables.atBoundCounts(nb).multiplyBySgn(coeffSgn);
-
- // y's bounds in the proposed model
- int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
- int yWillBeAtLb = (!bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0;
- BoundCounts ysBounds(yWillBeAtLb, yWillBeAtUb);
-
- // 2) -a * x = -y + \sum b * z
- // Get bc(-y + \sum b * z)
- sumOnly.addInChange(-1, d_variables.atBoundCounts(basic), ysBounds);
-
- // 3) x = (-1/a) * ( -y + \sum b * z)
- // Get bc((-1/a) * ( -y + \sum b * z))
- BoundCounts xsBoundsAfterPivot = sumOnly.multiplyBySgn(-coeffSgn);
-
- uint32_t length = d_tableau.basicRowLength(basic);
- if(nbSgn > 0){
- // Only check for the upper bound being violated
- return xsBoundsAfterPivot.lowerBoundCount() + 1 == length;
- }else{
- // Only check for the lower bound being violated
- return xsBoundsAfterPivot.upperBoundCount() + 1 == length;
- }
-}
-
-UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, bool ub) const{
- ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex());
- ArithVar nb = entry.getColVar();
-
- ConstraintP bound = ub ?
- d_variables.getUpperBoundConstraint(currBasic):
- d_variables.getLowerBoundConstraint(currBasic);
-
-
- const Rational& coeff = entry.getCoefficient();
- const DeltaRational& assignment = d_variables.getAssignment(currBasic);
- DeltaRational toBound = bound->getValue() - assignment;
- DeltaRational nbDiff = toBound/coeff;
-
- return UpdateInfo::conflict(nb, nbDiff, coeff, bound);
-}
-
-UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref){
- Assert(d_increasing.empty());
- Assert(d_decreasing.empty());
- Assert(!d_lowerBoundDifference);
- Assert(!d_upperBoundDifference);
-
- int focusCoeffSgn = focusCoeff.sgn();
-
- Trace("speculativeUpdate") << "speculativeUpdate" << endl;
- Trace("speculativeUpdate") << "nb " << nb << endl;
- Trace("speculativeUpdate") << "focusCoeff " << focusCoeff << endl;
-
- if(d_variables.hasUpperBound(nb)){
- ConstraintP ub = d_variables.getUpperBoundConstraint(nb);
- d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb);
- Border border(ub, *d_upperBoundDifference, false, NULL, true);
- Trace("handleBorders") << "push back increasing " << border << endl;
- d_increasing.push_back(border);
- }
- if(d_variables.hasLowerBound(nb)){
- ConstraintP lb = d_variables.getLowerBoundConstraint(nb);
- d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb);
- Border border(lb, *d_lowerBoundDifference, false, NULL, false);
- Trace("handleBorders") << "push back decreasing " << border << endl;
- d_decreasing.push_back(border);
- }
-
- Tableau::ColIterator colIter = d_tableau.colIterator(nb);
- for(; !colIter.atEnd(); ++colIter){
- const Tableau::Entry& entry = *colIter;
- Assert(entry.getColVar() == nb);
-
- if(accumulateBorder(entry, true)){
- clearSpeculative();
- return mkConflictUpdate(entry, true);
- }
- if(accumulateBorder(entry, false)){
- clearSpeculative();
- return mkConflictUpdate(entry, false);
- }
- }
-
- UpdateInfo selected;
- BorderHeap& withSgn = focusCoeffSgn > 0 ? d_increasing : d_decreasing;
- BorderHeap& againstSgn = focusCoeffSgn > 0 ? d_decreasing : d_increasing;
-
- handleBorders(selected, nb, focusCoeff, withSgn, 0, pref);
- int m = 1 - selected.errorsChangeSafe(0);
- handleBorders(selected, nb, focusCoeff, againstSgn, m, pref);
-
- clearSpeculative();
- return selected;
-}
-
-void LinearEqualityModule::clearSpeculative(){
- // clear everything away
- d_increasing.clear();
- d_decreasing.clear();
- d_lowerBoundDifference.reset();
- d_upperBoundDifference.reset();
-}
-
-void LinearEqualityModule::handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref){
- Assert(minimumFixes >= 0);
-
- // The values popped off of the heap
- // should be popped with the values closest to 0
- // being first and larger in absolute value last
-
-
- int fixesRemaining = heap.possibleFixes();
-
- Trace("handleBorders")
- << "handleBorders "
- << "nb " << nb
- << "fc " << focusCoeff
- << "h.e " << heap.empty()
- << "h.dir " << heap.direction()
- << "h.rem " << fixesRemaining
- << "h.0s " << heap.numZeroes()
- << "min " << minimumFixes
- << endl;
-
- if(heap.empty()){
- // if the heap is empty, return
- return;
- }
-
- bool zeroesWillDominate = fixesRemaining - heap.numZeroes() < minimumFixes;
-
- // can the number of fixes ever exceed the minimum?
- // no more than the number of possible fixes can be fixed in total
- // nothing can be fixed before the zeroes are taken care of
- if(minimumFixes > 0 && zeroesWillDominate){
- return;
- }
-
-
- int negErrorChange = 0;
- int nbDir = heap.direction();
-
- // points at the beginning of the heap
- if(zeroesWillDominate){
- heap.dropNonZeroes();
- }
- heap.make_heap();
-
-
- // pretend like the previous block had a value of zero.
- // The block that actually has a value of 0 must handle this.
- const DeltaRational zero(0);
- const DeltaRational* prevBlockValue = &zero;
-
- /** The coefficient changes as the value crosses border. */
- Rational effectiveCoefficient = focusCoeff;
-
- /* Keeps track of the change to the value of the focus function.*/
- DeltaRational totalFocusChange(0);
-
- const int focusCoeffSgn = focusCoeff.sgn();
-
- while(heap.more() &&
- (fixesRemaining + negErrorChange > minimumFixes ||
- (fixesRemaining + negErrorChange == minimumFixes &&
- effectiveCoefficient.sgn() == focusCoeffSgn))){
- // There are more elements &&
- // we can either fix at least 1 more variable in the error function
- // or we can improve the error function
-
-
- int brokenInBlock = 0;
- BorderVec::const_iterator endBlock = heap.end();
-
- pop_block(heap, brokenInBlock, fixesRemaining, negErrorChange);
-
- // if endVec == beginVec, block starts there
- // other wise, block starts at endVec
- BorderVec::const_iterator startBlock
- = heap.more() ? heap.end() : heap.begin();
-
- const DeltaRational& blockValue = (*startBlock).d_diff;
-
- // if decreasing
- // blockValue < prevBlockValue
- // diff.sgn() = -1
- DeltaRational diff = blockValue - (*prevBlockValue);
- DeltaRational blockChangeToFocus = diff * effectiveCoefficient;
- totalFocusChange += blockChangeToFocus;
-
- Trace("handleBorders")
- << "blockValue " << (blockValue)
- << "diff " << diff
- << "blockChangeToFocus " << totalFocusChange
- << "blockChangeToFocus " << totalFocusChange
- << "negErrorChange " << negErrorChange
- << "brokenInBlock " << brokenInBlock
- << "fixesRemaining " << fixesRemaining
- << endl;
-
- int currFocusChangeSgn = totalFocusChange.sgn();
- for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
- const Border& b = *i;
-
- Trace("handleBorders") << b << endl;
-
- bool makesImprovement = negErrorChange > 0 ||
- (negErrorChange == 0 && currFocusChangeSgn > 0);
-
- if(!makesImprovement){
- if(b.ownBorder() || minimumFixes > 0){
- continue;
- }
- }
-
- UpdateInfo proposal(nb, nbDir);
- if(b.ownBorder()){
- proposal.witnessedUpdate(b.d_diff, b.d_bound, -negErrorChange, currFocusChangeSgn);
- }else{
- proposal.update(b.d_diff, b.getCoefficient(), b.d_bound, -negErrorChange, currFocusChangeSgn);
- }
-
- if(selected.unbounded() || (this->*pref)(selected, proposal)){
- selected = proposal;
- }
- }
-
- effectiveCoefficient += updateCoefficient(startBlock, endBlock);
- prevBlockValue = &blockValue;
- negErrorChange -= brokenInBlock;
- }
-}
-
-Rational LinearEqualityModule::updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock){
- //update coefficient
- Rational changeToCoefficient(0);
- for(BorderVec::const_iterator i = startBlock; i != endBlock; ++i){
- const Border& curr = *i;
- if(curr.ownBorder()){// breaking its own bound
- if(curr.d_upperbound){
- changeToCoefficient -= 1;
- }else{
- changeToCoefficient += 1;
- }
- }else{
- const Rational& coeff = curr.d_entry->getCoefficient();
- if(curr.d_areFixing){
- if(curr.d_upperbound){// fixing an upper bound
- changeToCoefficient += coeff;
- }else{// fixing a lower bound
- changeToCoefficient -= coeff;
- }
- }else{
- if(curr.d_upperbound){// breaking an upper bound
- changeToCoefficient -= coeff;
- }else{
- // breaking a lower bound
- changeToCoefficient += coeff;
- }
- }
- }
- }
- return changeToCoefficient;
-}
-
-void LinearEqualityModule::pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange){
- Assert(heap.more());
-
- if(heap.top().d_areFixing){
- fixesRemaining--;
- negErrorChange++;
- }else{
- brokenInBlock++;
- }
- heap.pop_heap();
- const DeltaRational& blockValue = (*heap.end()).d_diff;
-
- while(heap.more()){
- const Border& top = heap.top();
- if(blockValue == top.d_diff){
- // belongs to the block
- if(top.d_areFixing){
- fixesRemaining--;
- negErrorChange++;
- }else{
- brokenInBlock++;
- }
- heap.pop_heap();
- }else{
- // does not belong to the block
- Assert((heap.direction() > 0) ? (blockValue < top.d_diff)
- : (blockValue > top.d_diff));
- break;
- }
- }
-}
-
-void LinearEqualityModule::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult){
- d_tableau.substitutePlusTimesConstant(to, from, mult, d_trackCallback);
-}
-void LinearEqualityModule::directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult){
- d_tableau.directlyAddToCoefficient(row, col, mult, d_trackCallback);
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This module maintains the relationship between a Tableau and
- * PartialModel.
- *
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This maintains the relationship needed by the SimplexDecisionProcedure.
- *
- * In the language of Simplex for DPLL(T), this provides:
- * - update()
- * - pivotAndUpdate()
- *
- * This class also provides utility functions that require
- * using both the Tableau and PartialModel.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "options/arith_options.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/simplex_update.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-struct Border{
- // The constraint for the border
- ConstraintP d_bound;
-
- // The change to the nonbasic to reach the border
- DeltaRational d_diff;
-
- // Is reach this value fixing the constraint
- // or is going past this value hurting the constraint
- bool d_areFixing;
-
- // Entry into the tableau
- const Tableau::Entry* d_entry;
-
- // Was this an upper bound or a lower bound?
- bool d_upperbound;
-
- Border():
- d_bound(NullConstraint) // ignore the other values
- {}
-
- Border(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(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{
- return d_diff < other.d_diff;
- }
-
- /** d_lim is the nonbasic variable's own bound. */
- bool ownBorder() const { return d_entry == NULL; }
-
- bool isZero() const { return d_diff.sgn() == 0; }
- static bool nonZero(const Border& b) { return !b.isZero(); }
-
- const Rational& getCoefficient() const {
- Assert(!ownBorder());
- return d_entry->getCoefficient();
- }
- void output(std::ostream& out) const;
-};
-
-inline std::ostream& operator<<(std::ostream& out, const Border& b){
- b.output(out);
- return out;
-}
-
-typedef std::vector<Border> BorderVec;
-
-class BorderHeap {
- const int d_dir;
-
- class BorderHeapCmp {
- private:
- int d_nbDirection;
- public:
- BorderHeapCmp(int dir): d_nbDirection(dir){}
- bool operator()(const Border& a, const Border& b) const{
- if(d_nbDirection > 0){
- // if nb is increasing,
- // this needs to act like a max
- // in order to have a min heap
- return b < a;
- }else{
- // if nb is decreasing,
- // this needs to act like a min
- // in order to have a max heap
- return a < b;
- }
- }
- };
- const BorderHeapCmp d_cmp;
-
- BorderVec d_vec;
-
- BorderVec::iterator d_begin;
-
- /**
- * Once this is initialized the top of the heap will always
- * be at d_end - 1
- */
- BorderVec::iterator d_end;
-
- int d_possibleFixes;
- int d_numZeroes;
-
-public:
- BorderHeap(int dir)
- : d_dir(dir), d_cmp(dir), d_possibleFixes(0), d_numZeroes(0)
- {}
-
- void push_back(const Border& b){
- d_vec.push_back(b);
- if(b.d_areFixing){
- d_possibleFixes++;
- }
- if(b.d_diff.sgn() == 0){
- d_numZeroes++;
- }
- }
-
- int numZeroes() const { return d_numZeroes; }
- int possibleFixes() const { return d_possibleFixes; }
- int direction() const { return d_dir; }
-
- void make_heap(){
- d_begin = d_vec.begin();
- d_end = d_vec.end();
- std::make_heap(d_begin, d_end, d_cmp);
- }
-
- void dropNonZeroes(){
- d_vec.erase(std::remove_if(d_vec.begin(), d_vec.end(), &Border::nonZero),
- d_vec.end());
- }
-
- const Border& top() const {
- Assert(more());
- return *d_begin;
- }
- void pop_heap(){
- Assert(more());
-
- std::pop_heap(d_begin, d_end, d_cmp);
- --d_end;
- }
-
- BorderVec::const_iterator end() const{
- return BorderVec::const_iterator(d_end);
- }
- BorderVec::const_iterator begin() const{
- return BorderVec::const_iterator(d_begin);
- }
-
- inline bool more() const{ return d_begin != d_end; }
-
- inline bool empty() const{ return d_vec.empty(); }
-
- void clear(){
- d_possibleFixes = 0;
- d_numZeroes = 0;
- d_vec.clear();
- }
-};
-
-
-class LinearEqualityModule {
-public:
- typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
-
-
- typedef bool (LinearEqualityModule::*UpdatePreferenceFunction)(const UpdateInfo&, const UpdateInfo&) const;
-
-
-private:
- /**
- * Manages information about the assignment and upper and lower bounds on the
- * variables.
- */
- ArithVariables& d_variables;
-
- /** Reference to the Tableau to operate upon. */
- Tableau& d_tableau;
-
- /** Called whenever the value of a basic variable is updated. */
- BasicVarModelUpdateCallBack d_basicVariableUpdates;
-
- BorderHeap d_increasing;
- BorderHeap d_decreasing;
- std::optional<DeltaRational> d_upperBoundDifference;
- std::optional<DeltaRational> d_lowerBoundDifference;
-
- Rational d_one;
- Rational d_negOne;
-public:
-
- /**
- * Initializes a LinearEqualityModule with a partial model, a tableau,
- * and a callback function for when basic variables update their values.
- */
- LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundTracking, BasicVarModelUpdateCallBack f);
-
- /**
- * Updates the assignment of a nonbasic variable x_i to v.
- * Also updates the assignment of basic variables accordingly.
- */
- void update(ArithVar x_i, const DeltaRational& v){
- if(d_areTracking){
- updateTracked(x_i,v);
- }else{
- updateUntracked(x_i,v);
- }
- }
-
- /** Specialization of update if the module is not tracking yet (for Assert*). */
- void updateUntracked(ArithVar x_i, const DeltaRational& v);
-
- /** Specialization of update if the module is not tracking yet (for Simplex). */
- void updateTracked(ArithVar x_i, const DeltaRational& v);
-
-
- /**
- * Updates the value of a basic variable x_i to v,
- * and then pivots x_i with the nonbasic variable in its row x_j.
- * Updates the assignment of the other basic variables accordingly.
- */
- void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
-
- ArithVariables& getVariables() const{ return d_variables; }
- Tableau& getTableau() const{ return d_tableau; }
-
- /**
- * Updates every non-basic to reflect the assignment in many.
- * For use with ApproximateSimplex.
- */
- void updateMany(const DenseMap<DeltaRational>& many);
- void forceNewBasis(const DenseSet& newBasis);
- void applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues);
-
-
- /**
- * Returns a pointer to the first Tableau entry on the row ridx that does not
- * have an either a lower bound/upper bound for proving a bound on skip.
- * The variable skip is always excluded. Returns NULL if there is no such element.
- *
- * If skip == ARITHVAR_SENTINEL, this is equivalent to considering the whole row.
- */
- const Tableau::Entry* rowLacksBound(RowIndex ridx, bool upperBound, ArithVar skip);
-
-
- void startTrackingBoundCounts();
- void stopTrackingBoundCounts();
-
-
- void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev);
-
-
- uint32_t updateProduct(const UpdateInfo& inf) const;
-
- inline bool minNonBasicVarOrder(const UpdateInfo& a, const UpdateInfo& b) const{
- return a.nonbasic() >= b.nonbasic();
- }
-
- /**
- * Prefer the update that touch the fewest entries in the matrix.
- *
- * The intuition is that this operation will be cheaper.
- * This strongly biases the system towards updates instead of pivots.
- */
- inline bool minProduct(const UpdateInfo& a, const UpdateInfo& b) const{
- uint32_t aprod = updateProduct(a);
- uint32_t bprod = updateProduct(b);
-
- if(aprod == bprod){
- return minNonBasicVarOrder(a,b);
- }else{
- return aprod > bprod;
- }
- }
- inline bool constrainedMin(const UpdateInfo& a, const UpdateInfo& b) const{
- if(a.describesPivot() && b.describesPivot()){
- bool aAtBounds = basicsAtBounds(a);
- bool bAtBounds = basicsAtBounds(b);
- if(aAtBounds != bAtBounds){
- return bAtBounds;
- }
- }
- return minProduct(a,b);
- }
-
- /**
- * If both a and b are pivots, prefer the pivot with the leaving variables that has equal bounds.
- * The intuition is that such variables will be less likely to lead to future problems.
- */
- inline bool preferFrozen(const UpdateInfo& a, const UpdateInfo& b) const {
- if(a.describesPivot() && b.describesPivot()){
- bool aFrozen = d_variables.boundsAreEqual(a.leaving());
- bool bFrozen = d_variables.boundsAreEqual(b.leaving());
-
- if(aFrozen != bFrozen){
- return bFrozen;
- }
- }
- return constrainedMin(a,b);
- }
-
- /**
- * Prefer pivots with entering variables that do not have bounds.
- * The intuition is that such variables will be less likely to lead to future problems.
- */
- bool preferNeitherBound(const UpdateInfo& a, const UpdateInfo& b) const {
- if(d_variables.hasEitherBound(a.nonbasic()) == d_variables.hasEitherBound(b.nonbasic())){
- return preferFrozen(a,b);
- }else{
- return d_variables.hasEitherBound(a.nonbasic());
- }
- }
-
- bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
- Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
- Assert(a.describesPivot());
- Assert(b.describesPivot());
- if(a.nonbasic() == b.nonbasic()){
- bool aIsZero = a.nonbasicDelta().sgn() == 0;
- bool bIsZero = b.nonbasicDelta().sgn() == 0;
-
- if((aIsZero || bIsZero) && (!aIsZero || !bIsZero)){
- return bIsZero;
- }else{
- return a.leaving() >= b.leaving();
- }
- }else{
- return a.nonbasic() > b.nonbasic();
- }
- }
-
- template <bool heuristic>
- bool preferWitness(const UpdateInfo& a, const UpdateInfo& b) const{
- WitnessImprovement aImp = a.getWitness(!heuristic);
- WitnessImprovement bImp = b.getWitness(!heuristic);
-
- if(aImp == bImp){
- switch(aImp){
- case ConflictFound:
- return preferNeitherBound(a,b);
- case ErrorDropped:
- if(a.errorsChange() == b.errorsChange()){
- return preferNeitherBound(a,b);
- }else{
- return a.errorsChange() > b.errorsChange();
- }
- case FocusImproved:
- return preferNeitherBound(a,b);
- case BlandsDegenerate:
- Assert(a.describesPivot());
- Assert(b.describesPivot());
- Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
- return modifiedBlands(a,b);
- case HeuristicDegenerate:
- Assert(a.describesPivot());
- Assert(b.describesPivot());
- Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
- return preferNeitherBound(a,b);
- case AntiProductive:
- return minNonBasicVarOrder(a, b);
- // Not valid responses
- case Degenerate:
- case FocusShrank:
- Unreachable();
- }
- Unreachable();
- }else{
- return aImp > bImp;
- }
- }
-
-private:
-
- /**
- * This maps each row index to its relevant bounds info.
- * This tracks the count for how many variables on a row have bounds
- * and how many are assigned at their bounds.
- */
- BoundInfoMap& d_btracking;
- bool d_areTracking;
-
-public:
- /**
- * The constraint on a basic variable b is implied by the constraints
- * on its row. This is a wrapper for propagateRow().
- */
- void propagateBasicFromRow(ConstraintP c, bool produceProofs);
-
- /**
- * Let v be the variable for the constraint c.
- * Exports either the explanation of an upperbound or a lower bound
- * of v using the other variables in the row.
- *
- * If farkas != RationalVectorPSentinel, this function additionally
- * stores the farkas coefficients of the constraints stored in into.
- * Position 0 is the coefficient of v.
- * Position i > 0, corresponds to the order of the other constraints.
- */
- void propagateRow(ConstraintCPVec& into,
- RowIndex ridx,
- bool rowUp,
- ConstraintP c,
- RationalVectorP farkas);
-
- /**
- * Computes the value of a basic variable using the assignments
- * of the values of the variables in the basic variable's row tableau.
- * This can compute the value using either:
- * - the the current assignment (useSafe=false) or
- * - the safe assignment (useSafe = true).
- */
- DeltaRational computeRowValue(ArithVar x, bool useSafe) const;
-
- /**
- * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
- * and 2 ArithVar variables and returns one of the ArithVar variables
- * potentially using the internals of the SimplexDecisionProcedure.
- */
-
- ArithVar noPreference(ArithVar x, ArithVar y) const { return x; }
-
- /**
- * minVarOrder is a PreferenceFunction for selecting the smaller of the 2
- * ArithVars. This PreferenceFunction is used during the VarOrder stage of
- * findModel.
- */
- ArithVar minVarOrder(ArithVar x, ArithVar y) const;
-
- /**
- * minColLength is a PreferenceFunction for selecting the variable with the
- * smaller row count in the tableau.
- *
- * This is a heuristic rule and should not be used during the VarOrder
- * stage of findModel.
- */
- ArithVar minColLength(ArithVar x, ArithVar y) const;
-
- /**
- * minRowLength is a PreferenceFunction for selecting the variable with the
- * smaller row count in the tableau.
- *
- * This is a heuristic rule and should not be used during the VarOrder
- * stage of findModel.
- */
- ArithVar minRowLength(ArithVar x, ArithVar y) const;
-
- /**
- * minBoundAndRowCount is a PreferenceFunction for preferring a variable
- * without an asserted bound over variables with an asserted bound.
- * If both have bounds or both do not have bounds,
- * the rule falls back to minRowCount(...).
- *
- * This is a heuristic rule and should not be used during the VarOrder
- * stage of findModel.
- */
- ArithVar minBoundAndColLength(ArithVar x, ArithVar y) const;
-
- template <bool above>
- inline bool isAcceptableSlack(int sgn, ArithVar nonbasic) const
- {
- return (above && sgn < 0 && d_variables.strictlyBelowUpperBound(nonbasic))
- || (above && sgn > 0 && d_variables.strictlyAboveLowerBound(nonbasic))
- || (!above && sgn > 0
- && d_variables.strictlyBelowUpperBound(nonbasic))
- || (!above && sgn < 0
- && d_variables.strictlyAboveLowerBound(nonbasic));
- }
-
- /**
- * Given the basic variable x_i,
- * this function finds the smallest nonbasic variable x_j in the row of x_i
- * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
- * This returns ARITHVAR_SENTINEL if none exists.
- *
- * More formally one of the following conditions must be satisfied:
- * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
- * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
- * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
- * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
- *
- */
- template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, VarPreferenceFunction pf) const;
- ArithVar selectSlackLowerBound(ArithVar x_i, VarPreferenceFunction pf) const {
- return selectSlack<true>(x_i, pf);
- }
- ArithVar selectSlackUpperBound(ArithVar x_i, VarPreferenceFunction pf) const {
- return selectSlack<false>(x_i, pf);
- }
-
- const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const;
-
- inline bool rowIndexIsTracked(RowIndex ridx) const {
- return d_btracking.isKey(ridx);
- }
- inline bool basicIsTracked(ArithVar v) const {
- return rowIndexIsTracked(d_tableau.basicToRowIndex(v));
- }
- void trackRowIndex(RowIndex ridx);
- void stopTrackingRowIndex(RowIndex ridx){
- Assert(rowIndexIsTracked(ridx));
- d_btracking.remove(ridx);
- }
-
- /**
- * If the pivot described in u were performed,
- * then the row would qualify as being either at the minimum/maximum
- * to the non-basics being at their bounds.
- * The minimum/maximum is determined by the direction the non-basic is changing.
- */
- bool basicsAtBounds(const UpdateInfo& u) const;
-
-private:
-
- /**
- * Recomputes the bound info for a row using either the information
- * in the bounds queue or the current information.
- * O(row length of ridx)
- */
- BoundsInfo computeRowBoundInfo(RowIndex ridx, bool inQueue) const;
-
-public:
- /** Debug only routine. */
- BoundCounts debugBasicAtBoundCount(ArithVar x_i) const;
-
- /** Track the effect of the change of coefficient for bound counting. */
- void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn);
-
- /** Track the effect of multiplying a row by a sign for bound counting. */
- void trackingMultiplyRow(RowIndex ridx, int sgn);
-
- /** Count for how many on a row have *an* upper/lower bounds. */
- BoundCounts hasBoundCount(RowIndex ri) const {
- Assert(d_variables.boundsQueueEmpty());
- return d_btracking[ri].hasBounds();
- }
-
- /**
- * Are there any non-basics on x_i's row that are not at
- * their respective lower bounds (mod sgns).
- * O(1) time due to the atBound() count.
- */
- bool nonbasicsAtLowerBounds(ArithVar x_i) const;
-
- /**
- * Are there any non-basics on x_i's row that are not at
- * their respective upper bounds (mod sgns).
- * O(1) time due to the atBound() count.
- */
- bool nonbasicsAtUpperBounds(ArithVar x_i) const;
-
-private:
- class TrackingCallback : public CoefficientChangeCallback {
- private:
- LinearEqualityModule* d_linEq;
- public:
- TrackingCallback(LinearEqualityModule* le) : d_linEq(le) {}
- void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override
- {
- d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
- }
- void multiplyRow(RowIndex ridx, int sgn) override
- {
- d_linEq->trackingMultiplyRow(ridx, sgn);
- }
- bool canUseRow(RowIndex ridx) const override
- {
- ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
- return d_linEq->basicIsTracked(basic);
- }
- } d_trackCallback;
-
- /**
- * Selects the constraint for the variable v on the row for basic
- * with the weakest possible constraint that is consistent with the surplus
- * surplus.
- */
- 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.
- *
- * Returns a constraint that is now in conflict.
- */
- ConstraintCP minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, FarkasConflictBuilder& rc) const;
-
- /**
- * Given a 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 ConstraintCP generateConflictAboveUpperBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
- return minimallyWeakConflict(true, conflictVar, rc);
- }
-
- inline ConstraintCP generateConflictBelowLowerBound(ArithVar conflictVar, FarkasConflictBuilder& rc) const {
- return minimallyWeakConflict(false, conflictVar, rc);
- }
-
- /**
- * Computes the sum of the upper/lower bound of row.
- * The variable skip is not included in the sum.
- */
- DeltaRational computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const;
-
-public:
- void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult);
- void directlyAddToCoefficient(ArithVar row, ArithVar col, const Rational& mult);
-
-
- /**
- * Checks to make sure the assignment is consistent with the tableau.
- * This code is for debugging.
- */
- void debugCheckTableau();
-
- void debugCheckTracking();
-
- /** Debugging information for a pivot. */
- void debugPivot(ArithVar x_i, ArithVar x_j);
-
- ArithVar minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const;
-
- /**
- * Returns true if there would be a conflict on this row after a pivot
- * and update using its basic variable and one of the non-basic variables on
- * the row.
- */
- bool willBeInConflictAfterPivot(const Tableau::Entry& entry, const DeltaRational& nbDiff, bool bToUB) const;
- UpdateInfo mkConflictUpdate(const Tableau::Entry& entry, bool ub) const;
-
- /**
- * Looks more an update for fcSimplex on the nonbasic variable nb with the focus coefficient.
- */
- UpdateInfo speculativeUpdate(ArithVar nb, const Rational& focusCoeff, UpdatePreferenceFunction pref);
-
-private:
-
- /**
- * Examines the effects of pivoting the entries column variable
- * with the row's basic variable and setting the variable s.t.
- * the basic variable is equal to one of its bounds.
- *
- * If ub, then the basic variable will be equal its upper bound.
- * If not ub,then the basic variable will be equal its lower bound.
- *
- * Returns iff this row will be in conflict after the pivot.
- *
- * If this is false, add the bound to the relevant heap.
- * If the bound is +/-infinity, this is ignored.
-
- *
- * Returns true if this would be a conflict.
- * If it returns false, this
- */
- bool accumulateBorder(const Tableau::Entry& entry, bool ub);
-
- void handleBorders(UpdateInfo& selected, ArithVar nb, const Rational& focusCoeff, BorderHeap& heap, int minimumFixes, UpdatePreferenceFunction pref);
- void pop_block(BorderHeap& heap, int& brokenInBlock, int& fixesRemaining, int& negErrorChange);
- void clearSpeculative();
- Rational updateCoefficient(BorderVec::const_iterator startBlock, BorderVec::const_iterator endBlock);
-
-private:
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statPivots, d_statUpdates;
- TimerStat d_pivotTime;
- TimerStat d_adjTime;
-
- IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
- TimerStat d_weakenTime;
- TimerStat d_forceTime;
-
- Statistics();
- };
- mutable Statistics d_statistics;
-
-};/* class LinearEqualityModule */
-
-struct Cand {
- ArithVar d_nb;
- uint32_t d_penalty;
- int d_sgn;
- const Rational* d_coeff;
-
- Cand(ArithVar nb, uint32_t penalty, int s, const Rational* c) :
- d_nb(nb), d_penalty(penalty), d_sgn(s), d_coeff(c){}
-};
-
-
-class CompPenaltyColLength {
-private:
- LinearEqualityModule* d_mod;
- const bool d_havePenalties;
-
- public:
- CompPenaltyColLength(LinearEqualityModule* mod, bool havePenalties)
- : d_mod(mod), d_havePenalties(havePenalties)
- {
- }
-
- bool operator()(const Cand& x, const Cand& y) const {
- if (x.d_penalty == y.d_penalty || !d_havePenalties)
- {
- return x.d_nb == d_mod->minBoundAndColLength(x.d_nb,y.d_nb);
- }
- else
- {
- return x.d_penalty < y.d_penalty;
- }
- }
-};
-
-class UpdateTrackingCallback : public BoundUpdateCallback {
-private:
- LinearEqualityModule* d_mod;
-public:
- UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
- void operator()(ArithVar v, const BoundsInfo& bi) override
- {
- d_mod->includeBoundUpdate(v, bi);
- }
-};
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Sparse matrix implementations for different types.
- */
-
-#include "theory/arith/matrix.h"
-
-using namespace std;
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
-void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){}
-bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- * Sparse matrix implementations for different types.
- *
- * Sparse matrix implementations for different types.
- * This defines Matrix<T>, IntegerEqualityTables and Tableau.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <queue>
-#include <utility>
-#include <vector>
-
-#include "base/output.h"
-#include "theory/arith/arithvar.h"
-#include "util/dense_map.h"
-#include "util/index.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-typedef Index EntryID;
-const EntryID ENTRYID_SENTINEL = std::numeric_limits<EntryID>::max();
-
-typedef Index RowIndex;
-const RowIndex ROW_INDEX_SENTINEL = std::numeric_limits<RowIndex>::max();
-
-class CoefficientChangeCallback {
-public:
- virtual ~CoefficientChangeCallback() {}
- virtual void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) = 0;
- virtual void multiplyRow(RowIndex ridx, int Sgn) = 0;
- virtual bool canUseRow(RowIndex ridx) const = 0;
-};
-
-class NoEffectCCCB : public CoefficientChangeCallback {
-public:
- void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) override;
- void multiplyRow(RowIndex ridx, int Sgn) override;
- bool canUseRow(RowIndex ridx) const override;
-};
-
-template<class T>
-class MatrixEntry {
-private:
- RowIndex d_rowIndex;
- ArithVar d_colVar;
-
- EntryID d_nextRow;
- EntryID d_nextCol;
-
- EntryID d_prevRow;
- EntryID d_prevCol;
-
- T d_coefficient;
-
-public:
- MatrixEntry():
- d_rowIndex(ROW_INDEX_SENTINEL),
- d_colVar(ARITHVAR_SENTINEL),
- d_nextRow(ENTRYID_SENTINEL),
- d_nextCol(ENTRYID_SENTINEL),
- d_prevRow(ENTRYID_SENTINEL),
- d_prevCol(ENTRYID_SENTINEL),
- d_coefficient()
- {}
-
- MatrixEntry(RowIndex row, ArithVar col, const T& coeff):
- d_rowIndex(row),
- d_colVar(col),
- d_nextRow(ENTRYID_SENTINEL),
- d_nextCol(ENTRYID_SENTINEL),
- d_prevRow(ENTRYID_SENTINEL),
- d_prevCol(ENTRYID_SENTINEL),
- d_coefficient(coeff)
- {}
-
-private:
- bool unusedConsistent() const {
- return
- (d_rowIndex == ROW_INDEX_SENTINEL && d_colVar == ARITHVAR_SENTINEL) ||
- (d_rowIndex != ROW_INDEX_SENTINEL && d_colVar != ARITHVAR_SENTINEL);
- }
-
-public:
-
- EntryID getNextRowEntryID() const {
- return d_nextRow;
- }
-
- EntryID getNextColEntryID() const {
- return d_nextCol;
- }
- EntryID getPrevRowEntryID() const {
- return d_prevRow;
- }
-
- EntryID getPrevColEntryID() const {
- return d_prevCol;
- }
-
- void setNextRowEntryID(EntryID id) {
- d_nextRow = id;
- }
- void setNextColEntryID(EntryID id) {
- d_nextCol = id;
- }
- void setPrevRowEntryID(EntryID id) {
- d_prevRow = id;
- }
- void setPrevColEntryID(EntryID id) {
- d_prevCol = id;
- }
-
- RowIndex getRowIndex() const{
- return d_rowIndex;
- }
-
- ArithVar getColVar() const{
- return d_colVar;
- }
-
- const T& getCoefficient() const {
- return d_coefficient;
- }
-
- T& getCoefficient(){
- return d_coefficient;
- }
-
- void setCoefficient(const T& t){
- d_coefficient = t;
- }
-
- void markBlank() {
- d_rowIndex = ROW_INDEX_SENTINEL;
- d_colVar = ARITHVAR_SENTINEL;
- }
-
- bool blank() const{
- Assert(unusedConsistent());
-
- return d_rowIndex == ROW_INDEX_SENTINEL;
- }
-}; /* class MatrixEntry<T> */
-
-template<class T>
-class MatrixEntryVector {
-private:
- typedef MatrixEntry<T> EntryType;
- typedef std::vector<EntryType> EntryArray;
-
- EntryArray d_entries;
- std::queue<EntryID> d_freedEntries;
-
- uint32_t d_size;
-
-public:
- MatrixEntryVector():
- d_entries(), d_freedEntries(), d_size(0)
- {}
-
- const EntryType& operator[](EntryID id) const{
- Assert(inBounds(id));
- return d_entries[id];
- }
-
- EntryType& get(EntryID id){
- Assert(inBounds(id));
- return d_entries[id];
- }
-
- void freeEntry(EntryID id){
- Assert(get(id).blank());
- Assert(d_size > 0);
-
- d_freedEntries.push(id);
- --d_size;
- }
-
- EntryID newEntry(){
- EntryID newId;
- if(d_freedEntries.empty()){
- newId = d_entries.size();
- d_entries.push_back(MatrixEntry<T>());
- }else{
- newId = d_freedEntries.front();
- d_freedEntries.pop();
- }
- ++d_size;
- return newId;
- }
-
- uint32_t size() const{ return d_size; }
- uint32_t capacity() const{ return d_entries.capacity(); }
-
-
-private:
- bool inBounds(EntryID id) const{
- return id < d_entries.size();
- }
-}; /* class MatrixEntryVector<T> */
-
-template <class T, bool isRow>
-class MatrixVector {
-private:
- EntryID d_head;
- uint32_t d_size;
-
- MatrixEntryVector<T>* d_entries;
-
- class Iterator {
- private:
- EntryID d_curr;
- const MatrixEntryVector<T>* d_entries;
-
- public:
- Iterator(EntryID start, const MatrixEntryVector<T>* entries) :
- d_curr(start), d_entries(entries)
- {}
-
- public:
-
- EntryID getID() const {
- return d_curr;
- }
-
- const MatrixEntry<T>& operator*() const{
- Assert(!atEnd());
- return (*d_entries)[d_curr];
- }
-
- Iterator& operator++(){
- Assert(!atEnd());
- const MatrixEntry<T>& entry = (*d_entries)[d_curr];
- d_curr = isRow ? entry.getNextRowEntryID() : entry.getNextColEntryID();
- return *this;
- }
-
- bool atEnd() const {
- return d_curr == ENTRYID_SENTINEL;
- }
-
- bool operator==(const Iterator& i) const{
- return d_curr == i.d_curr && d_entries == i.d_entries;
- }
-
- bool operator!=(const Iterator& i) const{
- return !(d_curr == i.d_curr && d_entries == i.d_entries);
- }
- }; /* class MatrixVector<T, isRow>::Iterator */
-
-public:
- MatrixVector(MatrixEntryVector<T>* mev)
- : d_head(ENTRYID_SENTINEL), d_size(0), d_entries(mev)
- {}
-
- MatrixVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
- : d_head(head), d_size(size), d_entries(mev)
- {}
-
- typedef Iterator const_iterator;
- const_iterator begin() const {
- return Iterator(d_head, d_entries);
- }
- const_iterator end() const {
- return Iterator(ENTRYID_SENTINEL, d_entries);
- }
-
- EntryID getHead() const { return d_head; }
-
- uint32_t getSize() const { return d_size; }
-
- void insert(EntryID newId){
- if(isRow){
- d_entries->get(newId).setNextRowEntryID(d_head);
-
- if(d_head != ENTRYID_SENTINEL){
- d_entries->get(d_head).setPrevRowEntryID(newId);
- }
- }else{
- d_entries->get(newId).setNextColEntryID(d_head);
-
- if(d_head != ENTRYID_SENTINEL){
- d_entries->get(d_head).setPrevColEntryID(newId);
- }
- }
-
- d_head = newId;
- ++d_size;
- }
- void remove(EntryID id){
- Assert(d_size > 0);
- --d_size;
- if(isRow){
- EntryID prevRow = d_entries->get(id).getPrevRowEntryID();
- EntryID nextRow = d_entries->get(id).getNextRowEntryID();
-
- if(d_head == id){
- d_head = nextRow;
- }
- if(prevRow != ENTRYID_SENTINEL){
- d_entries->get(prevRow).setNextRowEntryID(nextRow);
- }
- if(nextRow != ENTRYID_SENTINEL){
- d_entries->get(nextRow).setPrevRowEntryID(prevRow);
- }
- }else{
- EntryID prevCol = d_entries->get(id).getPrevColEntryID();
- EntryID nextCol = d_entries->get(id).getNextColEntryID();
-
- if(d_head == id){
- d_head = nextCol;
- }
-
- if(prevCol != ENTRYID_SENTINEL){
- d_entries->get(prevCol).setNextColEntryID(nextCol);
- }
- if(nextCol != ENTRYID_SENTINEL){
- d_entries->get(nextCol).setPrevColEntryID(prevCol);
- }
- }
- }
-}; /* class MatrixVector<T, isRow> */
-
-template <class T>
- class RowVector : public MatrixVector<T, true>
-{
-private:
- typedef MatrixVector<T, true> SuperT;
-public:
- typedef typename SuperT::const_iterator const_iterator;
-
- RowVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
- RowVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
- : SuperT(head, size, mev){}
-};/* class RowVector<T> */
-
-template <class T>
- class ColumnVector : public MatrixVector<T, false>
-{
-private:
- typedef MatrixVector<T, false> SuperT;
-public:
- typedef typename SuperT::const_iterator const_iterator;
-
- ColumnVector(MatrixEntryVector<T>* mev) : SuperT(mev){}
- ColumnVector(EntryID head, uint32_t size, MatrixEntryVector<T>* mev)
- : SuperT(head, size, mev){}
-};/* class ColumnVector<T> */
-
-template <class T>
-class Matrix {
-public:
- typedef MatrixEntry<T> Entry;
-
-protected:
- typedef cvc5::internal::theory::arith::RowVector<T> RowVectorT;
- typedef cvc5::internal::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;
-
- // ColumnTable : ArithVar |-> ColumnVector
- typedef std::vector< ColumnVectorT > ColumnTable;
- ColumnTable d_columns;
-
- /* The merge buffer is used to store a row in order to optimize row addition. */
- typedef std::pair<EntryID, bool> PosUsedPair;
- typedef DenseMap< PosUsedPair > RowToPosUsedPairMap;
- RowToPosUsedPairMap d_mergeBuffer;
-
- /* The row that is in the merge buffer. */
- RowIndex d_rowInMergeBuffer;
-
- uint32_t d_entriesInUse;
- MatrixEntryVector<T> d_entries;
-
- std::vector<RowIndex> d_pool;
-
- T d_zero;
-
-public:
- /**
- * Constructs an empty Matrix.
- */
- Matrix()
- : d_rows(),
- d_columns(),
- d_mergeBuffer(),
- d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
- d_entriesInUse(0),
- d_entries(),
- d_zero(0)
- {}
-
- Matrix(const T& zero)
- : d_rows(),
- d_columns(),
- d_mergeBuffer(),
- d_rowInMergeBuffer(ROW_INDEX_SENTINEL),
- d_entriesInUse(0),
- d_entries(),
- d_zero(zero)
- {}
-
- Matrix(const Matrix& m)
- : d_rows(),
- d_columns(),
- d_mergeBuffer(m.d_mergeBuffer),
- d_rowInMergeBuffer(m.d_rowInMergeBuffer),
- d_entriesInUse(m.d_entriesInUse),
- d_entries(m.d_entries),
- d_zero(m.d_zero)
- {
- d_columns.clear();
- for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
- const ColumnVectorT& col = *c;
- d_columns.push_back(ColumnVectorT(col.getHead(),col.getSize(),&d_entries));
- }
- d_rows.clear();
- for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
- const RowVectorT& row = *r;
- d_rows.push_back(RowVectorT(row.getHead(),row.getSize(),&d_entries));
- }
- }
-
- Matrix& operator=(const Matrix& m){
- d_mergeBuffer = (m.d_mergeBuffer);
- d_rowInMergeBuffer = (m.d_rowInMergeBuffer);
- d_entriesInUse = (m.d_entriesInUse);
- d_entries = (m.d_entries);
- d_zero = (m.d_zero);
- d_columns.clear();
- for(typename ColumnTable::const_iterator c=m.d_columns.begin(), cend = m.d_columns.end(); c!=cend; ++c){
- const ColumnVector<T>& col = *c;
- d_columns.push_back(ColumnVector<T>(col.getHead(), col.getSize(), &d_entries));
- }
- d_rows.clear();
- for(typename RowTable::const_iterator r=m.d_rows.begin(), rend = m.d_rows.end(); r!=rend; ++r){
- const RowVector<T>& row = *r;
- d_rows.push_back(RowVector<T>(row.getHead(), row.getSize(), &d_entries));
- }
- return *this;
- }
-
-protected:
-
- void addEntry(RowIndex row, ArithVar col, const T& coeff){
- Trace("tableau") << "addEntry(" << row << "," << col <<"," << coeff << ")" << std::endl;
-
- Assert(coeff != 0);
- Assert(row < d_rows.size());
- Assert(col < d_columns.size());
-
- EntryID newId = d_entries.newEntry();
- Entry& newEntry = d_entries.get(newId);
- newEntry = Entry(row, col, coeff);
-
- Assert(newEntry.getCoefficient() != 0);
-
- ++d_entriesInUse;
-
- d_rows[row].insert(newId);
- d_columns[col].insert(newId);
- }
-
- void removeEntry(EntryID id){
- Assert(d_entriesInUse > 0);
- --d_entriesInUse;
-
- Entry& entry = d_entries.get(id);
-
- RowIndex ridx = entry.getRowIndex();
- ArithVar col = entry.getColVar();
-
- Assert(d_rows[ridx].getSize() > 0);
- Assert(d_columns[col].getSize() > 0);
-
- d_rows[ridx].remove(id);
- d_columns[col].remove(id);
-
- entry.markBlank();
-
- d_entries.freeEntry(id);
- }
-
- private:
- RowIndex requestRowIndex(){
- if(d_pool.empty()){
- RowIndex ridx = d_rows.size();
- d_rows.push_back(RowVectorT(&d_entries));
- return ridx;
- }else{
- RowIndex rid = d_pool.back();
- d_pool.pop_back();
- return rid;
- }
- }
-
- void releaseRowIndex(RowIndex rid){
- d_pool.push_back(rid);
- }
-
-public:
-
- size_t getNumRows() const {
- return d_rows.size();
- }
-
- size_t getNumColumns() const {
- return d_columns.size();
- }
-
- void increaseSize(){
- 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];
- }
-
- const ColumnVector<T>& getColumn(ArithVar v) const {
- Assert(v < d_columns.size());
- return d_columns[v];
- }
-
- uint32_t getRowLength(RowIndex r) const{
- return getRow(r).getSize();
- }
-
- uint32_t getColLength(ArithVar x) const{
- return getColumn(x).getSize();
- }
-
- /**
- * Adds a row to the matrix.
- * The new row is equivalent to:
- * \f$\sum_i\f$ coeffs[i] * variables[i]
- */
- RowIndex addRow(const std::vector<T>& coeffs,
- const std::vector<ArithVar>& variables){
-
- RowIndex ridx = requestRowIndex();
-
- //RowIndex ridx = d_rows.size();
- //d_rows.push_back(RowVectorT(&d_entries));
-
- typename std::vector<T>::const_iterator coeffIter = coeffs.begin();
- std::vector<ArithVar>::const_iterator varsIter = variables.begin();
- std::vector<ArithVar>::const_iterator varsEnd = variables.end();
-
- for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
- const T& coeff = *coeffIter;
- ArithVar var_i = *varsIter;
- Assert(var_i < getNumColumns());
- addEntry(ridx, var_i, coeff);
- }
-
- return ridx;
- }
-
-
- void loadRowIntoBuffer(RowIndex rid){
- Assert(d_mergeBuffer.empty());
- Assert(d_rowInMergeBuffer == ROW_INDEX_SENTINEL);
-
- RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
- for(; i != i_end; ++i){
- EntryID id = i.getID();
- const MatrixEntry<T>& entry = *i;
- ArithVar colVar = entry.getColVar();
- d_mergeBuffer.set(colVar, std::make_pair(id, false));
- }
-
- d_rowInMergeBuffer = rid;
- }
-
- void clearBuffer() {
- Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
-
- d_rowInMergeBuffer = ROW_INDEX_SENTINEL;
- d_mergeBuffer.purge();
- }
-
- /* 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);
-
- Trace("tableau") << "rowPlusRowTimesConstant("
- << to << "," << mult << "," << d_rowInMergeBuffer << ")"
- << std::endl;
-
- Assert(debugNoZeroCoefficients(to));
- Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
-
- Assert(mult != 0);
-
- RowIterator i = getRow(to).begin();
- RowIterator i_end = getRow(to).end();
- while(i != i_end){
- EntryID id = i.getID();
- Entry& entry = d_entries.get(id);
- ArithVar colVar = entry.getColVar();
-
- ++i;
-
- if(d_mergeBuffer.isKey(colVar)){
- EntryID bufferEntry = d_mergeBuffer[colVar].first;
- Assert(!d_mergeBuffer[colVar].second);
- d_mergeBuffer.get(colVar).second = true;
-
- const Entry& other = d_entries.get(bufferEntry);
- T& coeff = entry.getCoefficient();
- coeff += mult * other.getCoefficient();
-
- if(coeff.sgn() == 0){
- removeEntry(id);
- }
- }
- }
-
- i = getRow(d_rowInMergeBuffer).begin();
- i_end = getRow(d_rowInMergeBuffer).end();
-
- for(; i != i_end; ++i){
- const Entry& entry = *i;
- ArithVar colVar = entry.getColVar();
-
- if(d_mergeBuffer[colVar].second){
- d_mergeBuffer.get(colVar).second = false;
- }else{
- Assert(!(d_mergeBuffer[colVar]).second);
- T newCoeff = mult * entry.getCoefficient();
- addEntry(to, colVar, newCoeff);
- }
- }
-
- Assert(mergeBufferIsClear());
-
- if(TraceIsOn("matrix")) { printMatrix(); }
- }
-
- /** to += mult * buffer. */
- void rowPlusBufferTimesConstant(RowIndex to, const T& mult, CoefficientChangeCallback& cb){
- Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL);
- Assert(to != ROW_INDEX_SENTINEL);
-
- Trace("tableau") << "rowPlusRowTimesConstant("
- << to << "," << mult << "," << d_rowInMergeBuffer << ")"
- << std::endl;
-
- Assert(debugNoZeroCoefficients(to));
- Assert(debugNoZeroCoefficients(d_rowInMergeBuffer));
-
- Assert(mult != 0);
-
- RowIterator i = getRow(to).begin();
- RowIterator i_end = getRow(to).end();
- while(i != i_end){
- EntryID id = i.getID();
- Entry& entry = d_entries.get(id);
- ArithVar colVar = entry.getColVar();
-
- ++i;
-
- if(d_mergeBuffer.isKey(colVar)){
- EntryID bufferEntry = d_mergeBuffer[colVar].first;
- Assert(!d_mergeBuffer[colVar].second);
- d_mergeBuffer.get(colVar).second = true;
-
- const Entry& other = d_entries.get(bufferEntry);
- T& coeff = entry.getCoefficient();
- int coeffOldSgn = coeff.sgn();
- coeff += mult * other.getCoefficient();
- int coeffNewSgn = coeff.sgn();
-
- if(coeffOldSgn != coeffNewSgn){
- cb.update(to, colVar, coeffOldSgn, coeffNewSgn);
-
- if(coeffNewSgn == 0){
- removeEntry(id);
- }
- }
- }
- }
-
- i = getRow(d_rowInMergeBuffer).begin();
- i_end = getRow(d_rowInMergeBuffer).end();
-
- for(; i != i_end; ++i){
- const Entry& entry = *i;
- ArithVar colVar = entry.getColVar();
-
- if(d_mergeBuffer[colVar].second){
- d_mergeBuffer.get(colVar).second = false;
- }else{
- Assert(!(d_mergeBuffer[colVar]).second);
- T newCoeff = mult * entry.getCoefficient();
- addEntry(to, colVar, newCoeff);
-
- cb.update(to, colVar, 0, newCoeff.sgn());
- }
- }
-
- Assert(mergeBufferIsClear());
-
- if(TraceIsOn("matrix")) { printMatrix(); }
- }
-
- bool mergeBufferIsClear() const{
- RowToPosUsedPairMap::const_iterator i = d_mergeBuffer.begin();
- RowToPosUsedPairMap::const_iterator i_end = d_mergeBuffer.end();
- for(; i != i_end; ++i){
- RowIndex rid = *i;
- if(d_mergeBuffer[rid].second){
- return false;
- }
- }
- return true;
- }
-
-protected:
-
- EntryID findOnRow(RowIndex rid, ArithVar column) const {
- RowIterator i = d_rows[rid].begin(), i_end = d_rows[rid].end();
- for(; i != i_end; ++i){
- EntryID id = i.getID();
- const MatrixEntry<T>& entry = *i;
- ArithVar colVar = entry.getColVar();
-
- if(colVar == column){
- return id;
- }
- }
- return ENTRYID_SENTINEL;
- }
-
- EntryID findOnCol(RowIndex rid, ArithVar column) const{
- ColIterator i = d_columns[column].begin(), i_end = d_columns[column].end();
- for(; i != i_end; ++i){
- EntryID id = i.getID();
- const MatrixEntry<T>& entry = *i;
- RowIndex currRow = entry.getRowIndex();
-
- if(currRow == rid){
- return id;
- }
- }
- return ENTRYID_SENTINEL;
- }
-
- EntryID findEntryID(RowIndex rid, ArithVar col) const{
- bool colIsShorter = getColLength(col) < getRowLength(rid);
- EntryID id = colIsShorter ? findOnCol(rid, col) : findOnRow(rid,col);
- return id;
- }
- MatrixEntry<T> d_failedFind;
-public:
-
- /** If the find fails, isUnused is true on the entry. */
- const MatrixEntry<T>& findEntry(RowIndex rid, ArithVar col) const{
- EntryID id = findEntryID(rid, col);
- if(id == ENTRYID_SENTINEL){
- return d_failedFind;
- }else{
- return d_entries[id];
- }
- }
-
- /**
- * Prints the contents of the Matrix to Trace("matrix")
- */
- void printMatrix(std::ostream& out) const {
- out << "Matrix::printMatrix" << std::endl;
-
- for(RowIndex i = 0, N = d_rows.size(); i < N; ++i){
- printRow(i, out);
- }
- }
- void printMatrix() const {
- printMatrix(Trace("matrix"));
- }
-
- void printRow(RowIndex rid, std::ostream& out) const {
- out << "{" << rid << ":";
- const RowVector<T>& row = getRow(rid);
- RowIterator i = row.begin();
- RowIterator i_end = row.end();
- for(; i != i_end; ++i){
- printEntry(*i, out);
- out << ",";
- }
- out << "}" << std::endl;
- }
- void printRow(RowIndex rid) const {
- printRow(rid, Trace("matrix"));
- }
-
- void printEntry(const MatrixEntry<T>& entry, std::ostream& out) const {
- out << entry.getColVar() << "*" << entry.getCoefficient();
- }
- void printEntry(const MatrixEntry<T>& entry) const {
- printEntry(entry, Trace("matrix"));
- }
-public:
- uint32_t size() const {
- return d_entriesInUse;
- }
- uint32_t getNumEntriesInTableau() const {
- return d_entries.size();
- }
- uint32_t getEntryCapacity() const {
- return d_entries.capacity();
- }
-
- void manipulateRowEntry(RowIndex row, ArithVar col, const T& c, CoefficientChangeCallback& cb){
- int coeffOldSgn;
- int coeffNewSgn;
-
- EntryID id = findEntryID(row, col);
- if(id == ENTRYID_SENTINEL){
- coeffOldSgn = 0;
- addEntry(row, col, c);
- coeffNewSgn = c.sgn();
- }else{
- Entry& e = d_entries.get(id);
- T& t = e.getCoefficient();
- coeffOldSgn = t.sgn();
- t += c;
- coeffNewSgn = t.sgn();
- }
-
- if(coeffOldSgn != coeffNewSgn){
- cb.update(row, col, coeffOldSgn, coeffNewSgn);
- }
- if(coeffNewSgn == 0){
- removeEntry(id);
- }
- }
-
- void removeRow(RowIndex rid){
- RowIterator i = getRow(rid).begin();
- RowIterator i_end = getRow(rid).end();
- for(; i != i_end; ++i){
- EntryID id = i.getID();
- removeEntry(id);
- }
- releaseRowIndex(rid);
- }
-
- double densityMeasure() const{
- Assert(numNonZeroEntriesByRow() == numNonZeroEntries());
- Assert(numNonZeroEntriesByCol() == numNonZeroEntries());
-
- uint32_t n = getNumRows();
- if(n == 0){
- return 1.0;
- }else {
- uint32_t s = numNonZeroEntries();
- uint32_t m = d_columns.size();
- uint32_t divisor = (n *(m - n + 1));
-
- Assert(n >= 1);
- Assert(m >= n);
- Assert(divisor > 0);
- Assert(divisor >= s);
-
- return (double(s)) / divisor;
- }
- }
-
- void loadSignQueries(RowIndex rid, DenseMap<int>& target) const{
-
- RowIterator i = getRow(rid).begin(), i_end = getRow(rid).end();
- for(; i != i_end; ++i){
- const MatrixEntry<T>& entry = *i;
- target.set(entry.getColVar(), entry.getCoefficient().sgn());
- }
- }
-
-protected:
- uint32_t numNonZeroEntries() const { return size(); }
-
- uint32_t numNonZeroEntriesByRow() const {
- uint32_t rowSum = 0;
- for(RowIndex rid = 0, N = d_rows.size(); rid < N; ++rid){
- rowSum += getRowLength(rid);
- }
- return rowSum;
- }
-
- uint32_t numNonZeroEntriesByCol() const {
- uint32_t colSum = 0;
- for(ArithVar v = 0, N = d_columns.size(); v < N; ++v){
- colSum += getColLength(v);
- }
- return colSum;
- }
-
-
- bool debugNoZeroCoefficients(RowIndex ridx){
- for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
- const Entry& entry = *i;
- if(entry.getCoefficient() == 0){
- return false;
- }
- }
- return true;
- }
- bool debugMatchingCountsForRow(RowIndex ridx){
- for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
- const Entry& entry = *i;
- ArithVar colVar = entry.getColVar();
- uint32_t count = debugCountColLength(colVar);
- Trace("tableau") << "debugMatchingCountsForRow "
- << ridx << ":" << colVar << " " << count
- <<" "<< getColLength(colVar) << std::endl;
- if( count != getColLength(colVar) ){
- return false;
- }
- }
- return true;
- }
-
- uint32_t debugCountColLength(ArithVar var){
- Trace("tableau") << var << " ";
- uint32_t count = 0;
- for(ColIterator i=getColumn(var).begin(); !i.atEnd(); ++i){
- const Entry& entry = *i;
- Trace("tableau") << "(" << entry.getRowIndex() << ", " << i.getID() << ") ";
- ++count;
- }
- Trace("tableau") << std::endl;
- return count;
- }
- uint32_t debugCountRowLength(RowIndex ridx){
- uint32_t count = 0;
- for(RowIterator i=getRow(ridx).begin(); !i.atEnd(); ++i){
- ++count;
- }
- return count;
- }
-
-};/* class Matrix<T> */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
#include "theory/arith/arith_msum.h"
#include "theory/arith/inference_manager.h"
#include "theory/arith/nl/poly_conversion.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/normal_form.h"
#include "theory/rewriter.h"
#include "util/poly_util.h"
{
return {};
}
- auto comp = Comparison::parseNormalForm(tmp).decompose(false);
+ auto comp = linear::Comparison::parseNormalForm(tmp).decompose(false);
Kind k = std::get<1>(comp);
if (k == Kind::DISTINCT)
{
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-#include "theory/arith/normal_form.h"
-
-#include <list>
-
-#include "base/output.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/theory.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-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));
-}
-
-VarList::VarList(Node n) : NodeWrapper(n) { Assert(isSorted(begin(), end())); }
-
-bool Variable::isIAndMember(Node n)
-{
- return n.getKind() == kind::IAND && Polynomial::isMember(n[0])
- && Polynomial::isMember(n[1]);
-}
-
-bool Variable::isPow2Member(Node n)
-{
- return n.getKind() == kind::POW2 && Polynomial::isMember(n[0]);
-}
-
-bool Variable::isDivMember(Node n){
- switch(n.getKind()){
- case kind::DIVISION:
- case kind::INTS_DIVISION:
- case kind::INTS_MODULUS:
- case kind::DIVISION_TOTAL:
- case kind::INTS_DIVISION_TOTAL:
- case kind::INTS_MODULUS_TOTAL:
- return Polynomial::isMember(n[0]) && Polynomial::isMember(n[1]);
- default:
- return false;
- }
-}
-
-bool Variable::isTranscendentalMember(Node n) {
- switch(n.getKind()){
- case kind::EXPONENTIAL:
- case kind::SINE:
- case kind::COSINE:
- case kind::TANGENT:
- case kind::COSECANT:
- case kind::SECANT:
- case kind::COTANGENT:
- case kind::ARCSINE:
- case kind::ARCCOSINE:
- case kind::ARCTANGENT:
- case kind::ARCCOSECANT:
- case kind::ARCSECANT:
- case kind::ARCCOTANGENT:
- case kind::SQRT: return Polynomial::isMember(n[0]);
- case kind::PI:
- return true;
- default:
- return false;
- }
-}
-
-
-bool VarList::isSorted(iterator start, iterator end) {
- return std::is_sorted(start, end);
-}
-
-bool VarList::isMember(Node n) {
- if(Variable::isMember(n)) {
- return true;
- }
- if(n.getKind() == kind::NONLINEAR_MULT) {
- Node::iterator curr = n.begin(), end = n.end();
- Node prev = *curr;
- if(!Variable::isMember(prev)) return false;
-
- Variable::VariableNodeCmp cmp;
-
- while( (++curr) != end) {
- if(!Variable::isMember(*curr)) return false;
- // prev <= curr : accept
- // !(prev <= curr) : reject
- // !(!(prev > curr)) : reject
- // curr < prev : reject
- if((cmp(*curr, prev))) return false;
- prev = *curr;
- }
- return true;
- } else {
- return false;
- }
-}
-
-int VarList::cmp(const VarList& vl) const {
- int dif = this->size() - vl.size();
- if (dif == 0) {
- if(this->getNode() == vl.getNode()) {
- return 0;
- }
-
- Assert(!empty());
- Assert(!vl.empty());
- if(this->size() == 1){
- return Variable::VariableNodeCmp::cmp(this->getNode(), vl.getNode());
- }
-
-
- internal_iterator ii=this->internalBegin(), ie=this->internalEnd();
- internal_iterator ci=vl.internalBegin(), ce=vl.internalEnd();
- for(; ii != ie; ++ii, ++ci){
- Node vi = *ii;
- Node vc = *ci;
- int tmp = Variable::VariableNodeCmp::cmp(vi, vc);
- if(tmp != 0){
- return tmp;
- }
- }
- Unreachable();
- } else if(dif < 0) {
- return -1;
- } else {
- return 1;
- }
-}
-
-VarList VarList::parseVarList(Node n) {
- return VarList(n);
- // if(Variable::isMember(n)) {
- // return VarList(Variable(n));
- // } else {
- // Assert(n.getKind() == kind::MULT);
- // for(Node::iterator i=n.begin(), end = n.end(); i!=end; ++i) {
- // Assert(Variable::isMember(*i));
- // }
- // return VarList(n);
- // }
-}
-
-VarList VarList::operator*(const VarList& other) const {
- if(this->empty()) {
- return other;
- } else if(other.empty()) {
- return *this;
- } else {
- vector<Node> result;
-
- internal_iterator
- thisBegin = this->internalBegin(),
- thisEnd = this->internalEnd(),
- otherBegin = other.internalBegin(),
- otherEnd = other.internalEnd();
-
- Variable::VariableNodeCmp cmp;
- std::merge(thisBegin, thisEnd, otherBegin, otherEnd, std::back_inserter(result), cmp);
-
- Assert(result.size() >= 2);
- Node mult = NodeManager::currentNM()->mkNode(kind::NONLINEAR_MULT, result);
- return VarList::parseVarList(mult);
- }
-}
-
-bool Monomial::isMember(TNode n){
- if(n.getKind() == kind::CONST_RATIONAL) {
- return true;
- } else if(multStructured(n)) {
- return VarList::isMember(n[1]);
- } else {
- return VarList::isMember(n);
- }
-}
-
-Monomial Monomial::mkMonomial(const Constant& c, const VarList& vl) {
- if(c.isZero() || vl.empty() ) {
- return Monomial(c);
- } else if(c.isOne()) {
- return Monomial(vl);
- } else {
- return Monomial(c, vl);
- }
-}
-
-Monomial Monomial::mkMonomial(const VarList& vl) {
- // acts like Monomial::mkMonomial( 1, vl)
- if( vl.empty() ) {
- return Monomial::mkOne();
- } else if(true){
- return Monomial(vl);
- }
-}
-
-Monomial Monomial::parseMonomial(Node n) {
- if(n.getKind() == kind::CONST_RATIONAL) {
- return Monomial(Constant(n));
- } else if(multStructured(n)) {
- return Monomial::mkMonomial(Constant(n[0]),VarList::parseVarList(n[1]));
- } else {
- return Monomial(VarList::parseVarList(n));
- }
-}
-Monomial Monomial::operator*(const Rational& q) const {
- if(q.isZero()){
- return mkZero();
- }else{
- Constant newConstant = this->getConstant() * q;
- return Monomial::mkMonomial(newConstant, getVarList());
- }
-}
-
-Monomial Monomial::operator*(const Constant& c) const {
- return (*this) * c.getValue();
- // if(c.isZero()){
- // return mkZero();
- // }else{
- // Constant newConstant = this->getConstant() * c;
- // return Monomial::mkMonomial(newConstant, getVarList());
- // }
-}
-
-Monomial Monomial::operator*(const Monomial& mono) const {
- Constant newConstant = this->getConstant() * mono.getConstant();
- VarList newVL = this->getVarList() * mono.getVarList();
-
- return Monomial::mkMonomial(newConstant, newVL);
-}
-
-// 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));
- 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; }
- }
- // 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;
- }
- if(writePos > 0 ){
- Monomial cp = monos[0];
- Assert(writePos <= N);
- monos.resize(writePos, cp);
- }else{
- monos.clear();
- }
- Assert(isStrictlySorted(monos));
-}
-
-void Monomial::print() const {
- Trace("normal-form") << getNode() << std::endl;
-}
-
-void Monomial::printList(const std::vector<Monomial>& list) {
- for(vector<Monomial>::const_iterator i = list.begin(), end = list.end(); i != end; ++i) {
- const Monomial& m =*i;
- m.print();
- }
-}
-Polynomial Polynomial::operator+(const Polynomial& vl) const {
-
- std::vector<Monomial> sortedMonos;
- std::merge(begin(), end(), vl.begin(), vl.end(), std::back_inserter(sortedMonos));
-
- Monomial::combineAdjacentMonomials(sortedMonos);
- //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos);
-
- Polynomial result = mkPolynomial(sortedMonos);
- return result;
-}
-
-Polynomial Polynomial::exactDivide(const Integer& z) const {
- Assert(isIntegral());
- if(z.isOne()){
- return (*this);
- }else {
- Constant invz = Constant::mkConstant(Rational(1,z));
- Polynomial prod = (*this) * Monomial::mkMonomial(invz);
- Assert(prod.isIntegral());
- return prod;
- }
-}
-
-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);
- monos.push_back(Monomial::mkMonomial(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));
-
- return *this + (vl*negOne);
-}
-
-Polynomial Polynomial::operator*(const Rational& q) const{
- if(q.isZero()){
- return Polynomial::mkZero();
- }else if(q.isOne()){
- return *this;
- }else{
- std::vector<Monomial> newMonos;
- for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
- newMonos.push_back((*i)*q);
- }
-
- Assert(Monomial::isStrictlySorted(newMonos));
- return Polynomial::mkPolynomial(newMonos);
- }
-}
-
-Polynomial Polynomial::operator*(const Constant& c) const{
- return (*this) * c.getValue();
- // if(c.isZero()){
- // return Polynomial::mkZero();
- // }else if(c.isOne()){
- // return *this;
- // }else{
- // std::vector<Monomial> newMonos;
- // for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
- // newMonos.push_back((*i)*c);
- // }
-
- // Assert(Monomial::isStrictlySorted(newMonos));
- // return Polynomial::mkPolynomial(newMonos);
- // }
-}
-
-Polynomial Polynomial::operator*(const Monomial& mono) const {
- if(mono.isZero()) {
- return Polynomial(mono); //Don't multiply by zero
- } else {
- std::vector<Monomial> newMonos;
- for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
- newMonos.push_back(mono * (*i));
- }
-
- // We may need to sort newMonos.
- // 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.
- Monomial::sort(newMonos);
- return Polynomial::mkPolynomial(newMonos);
- }
-}
-
-Polynomial Polynomial::operator*(const Polynomial& poly) const {
- Polynomial res = Polynomial::mkZero();
- for(iterator i = this->begin(), end = this->end(); i != end; ++i) {
- Monomial curr = *i;
- Polynomial prod = poly * curr;
- Polynomial sum = res + prod;
- res = sum;
- }
- return res;
-}
-
-Monomial Polynomial::selectAbsMinimum() const {
- iterator iter = begin(), myend = end();
- Assert(iter != myend);
-
- Monomial min = *iter;
- ++iter;
- for(; iter != end(); ++iter){
- Monomial curr = *iter;
- if(curr.absCmp(min) < 0){
- min = curr;
- }
- }
- return min;
-}
-
-bool Polynomial::leadingCoefficientIsAbsOne() const {
- return getHead().absCoefficientIsOne();
-}
-bool Polynomial::leadingCoefficientIsPositive() const {
- return getHead().getConstant().isPositive();
-}
-
-bool Polynomial::denominatorLCMIsOne() const {
- return denominatorLCM().isOne();
-}
-
-bool Polynomial::numeratorGCDIsOne() const {
- return gcd().isOne();
-}
-
-Integer Polynomial::gcd() const {
- Assert(isIntegral());
- return numeratorGCD();
-}
-
-Integer Polynomial::numeratorGCD() const {
- //We'll use the standardization that gcd(0, 0) = 0
- //So that the gcd of the zero polynomial is gcd{0} = 0
- iterator i=begin(), e=end();
- 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;
-}
-
-Integer Polynomial::denominatorLCM() const {
- Integer tmp(1);
- for (iterator i = begin(), e = end(); i != e; ++i) {
- const Integer denominator = (*i).getConstant().getValue().getDenominator();
- tmp = tmp.lcm(denominator);
- }
- return tmp;
-}
-
-Constant Polynomial::getCoefficient(const VarList& vl) const{
- //TODO improve to binary search...
- for(iterator iter=begin(), myend=end(); iter != myend; ++iter){
- Monomial m = *iter;
- VarList curr = m.getVarList();
- if(curr == vl){
- return m.getConstant();
- }
- }
- return Constant::mkConstant(0);
-}
-
-Node Polynomial::computeQR(const Polynomial& p, const Integer& div){
- Assert(p.isIntegral());
- std::vector<Monomial> q_vec, r_vec;
- Integer tmp_q, tmp_r;
- for(iterator iter = p.begin(), pend = p.end(); iter != pend; ++iter){
- Monomial curr = *iter;
- VarList vl = curr.getVarList();
- Constant c = curr.getConstant();
-
- const Integer& a = c.getValue().getNumerator();
- Integer::floorQR(tmp_q, tmp_r, a, div);
- Constant q=Constant::mkConstant(tmp_q);
- Constant r=Constant::mkConstant(tmp_r);
- if(!q.isZero()){
- q_vec.push_back(Monomial::mkMonomial(q, vl));
- }
- if(!r.isZero()){
- r_vec.push_back(Monomial::mkMonomial(r, vl));
- }
- }
-
- Polynomial p_q = Polynomial::mkPolynomial(q_vec);
- Polynomial p_r = Polynomial::mkPolynomial(r_vec);
-
- return NodeManager::currentNM()->mkNode(
- kind::ADD, p_q.getNode(), p_r.getNode());
-}
-
-
-Monomial Polynomial::minimumVariableMonomial() const{
- Assert(!isConstant());
- if(singleton()){
- return getHead();
- }else{
- iterator i = begin();
- Monomial first = *i;
- if( first.isConstant() ){
- ++i;
- Assert(i != end());
- return *i;
- }else{
- return first;
- }
- }
-}
-
-bool Polynomial::variableMonomialAreStrictlyGreater(const Monomial& m) const{
- if(isConstant()){
- return true;
- }else{
- Monomial minimum = minimumVariableMonomial();
- Trace("nf::tmp") << "minimum " << minimum.getNode() << endl;
- Trace("nf::tmp") << "m " << m.getNode() << endl;
- return m < minimum;
- }
-}
-
-bool Polynomial::isMember(TNode n) {
- if(Monomial::isMember(n)){
- return true;
- }
- else if (n.getKind() == kind::ADD)
- {
- Assert(n.getNumChildren() >= 2);
- Node::iterator currIter = n.begin(), end = n.end();
- Node prev = *currIter;
- if(!Monomial::isMember(prev)){
- return false;
- }
-
- Monomial mprev = Monomial::parseMonomial(prev);
- ++currIter;
- for(; currIter != end; ++currIter){
- Node curr = *currIter;
- if(!Monomial::isMember(curr)){
- return false;
- }
- Monomial mcurr = Monomial::parseMonomial(curr);
- if(!(mprev < mcurr)){
- return false;
- }
- mprev = mcurr;
- }
- return true;
- }
- else
- {
- return false;
- }
-}
-
-Node SumPair::computeQR(const SumPair& sp, const Integer& div){
- Assert(sp.isIntegral());
-
- const Integer& constant = sp.getConstant().getValue().getNumerator();
-
- Integer constant_q, constant_r;
- Integer::floorQR(constant_q, constant_r, constant, div);
-
- Node p_qr = Polynomial::computeQR(sp.getPolynomial(), div);
- Assert(p_qr.getKind() == kind::ADD);
- Assert(p_qr.getNumChildren() == 2);
-
- Polynomial p_q = Polynomial::parsePolynomial(p_qr[0]);
- Polynomial p_r = Polynomial::parsePolynomial(p_qr[1]);
-
- SumPair sp_q(p_q, Constant::mkConstant(constant_q));
- SumPair sp_r(p_r, Constant::mkConstant(constant_r));
-
- return NodeManager::currentNM()->mkNode(
- kind::ADD, sp_q.getNode(), sp_r.getNode());
-}
-
-SumPair SumPair::mkSumPair(const Polynomial& p){
- if(p.isConstant()){
- Constant leadingConstant = p.getHead().getConstant();
- return SumPair(Polynomial::mkZero(), leadingConstant);
- }else if(p.containsConstant()){
- Assert(!p.singleton());
- return SumPair(p.getTail(), p.getHead().getConstant());
- }else{
- return SumPair(p, Constant::mkZero());
- }
-}
-
-Comparison::Comparison(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
-
-SumPair Comparison::toSumPair() const {
- Kind cmpKind = comparisonKind();
- switch(cmpKind){
- case kind::LT:
- case kind::LEQ:
- case kind::GT:
- case kind::GEQ:
- {
- TNode lit = getNode();
- TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
- Polynomial p = Polynomial::parsePolynomial(atom[0]);
- Constant c = Constant::mkConstant(atom[1]);
- if(p.leadingCoefficientIsPositive()){
- return SumPair(p, -c);
- }else{
- return SumPair(-p, c);
- }
- }
- case kind::EQUAL:
- case kind::DISTINCT:
- {
- Polynomial left = getLeft();
- Polynomial right = getRight();
- Trace("nf::tmp") << "left: " << left.getNode() << endl;
- Trace("nf::tmp") << "right: " << right.getNode() << endl;
- if(right.isConstant()){
- return SumPair(left, -right.getHead().getConstant());
- }else if(right.containsConstant()){
- Assert(!right.singleton());
-
- Polynomial noConstant = right.getTail();
- return SumPair(left - noConstant, -right.getHead().getConstant());
- }else{
- return SumPair(left - right, Constant::mkZero());
- }
- }
- default: Unhandled() << cmpKind;
- }
-}
-
-Polynomial Comparison::normalizedVariablePart() const {
- Kind cmpKind = comparisonKind();
- switch(cmpKind){
- case kind::LT:
- case kind::LEQ:
- case kind::GT:
- case kind::GEQ:
- {
- TNode lit = getNode();
- TNode atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
- Polynomial p = Polynomial::parsePolynomial(atom[0]);
- if(p.leadingCoefficientIsPositive()){
- return p;
- }else{
- return -p;
- }
- }
- case kind::EQUAL:
- case kind::DISTINCT:
- {
- Polynomial left = getLeft();
- Polynomial right = getRight();
- if(right.isConstant()){
- return left;
- }else{
- Polynomial noConstant = right.containsConstant() ? right.getTail() : right;
- Polynomial diff = left - noConstant;
- if(diff.leadingCoefficientIsPositive()){
- return diff;
- }else{
- return -diff;
- }
- }
- }
- default: Unhandled() << cmpKind;
- }
-}
-
-DeltaRational Comparison::normalizedDeltaRational() const {
- Kind cmpKind = comparisonKind();
- int delta = deltaCoeff(cmpKind);
- switch(cmpKind){
- case kind::LT:
- case kind::LEQ:
- case kind::GT:
- case kind::GEQ:
- {
- Node lit = getNode();
- Node atom = (cmpKind == kind::LT || cmpKind == kind::LEQ) ? lit[0] : lit;
- Polynomial left = Polynomial::parsePolynomial(atom[0]);
- const Rational& q = atom[1].getConst<Rational>();
- if(left.leadingCoefficientIsPositive()){
- return DeltaRational(q, delta);
- }else{
- return DeltaRational(-q, -delta);
- }
- }
- case kind::EQUAL:
- case kind::DISTINCT:
- {
- Polynomial right = getRight();
- Monomial firstRight = right.getHead();
- if(firstRight.isConstant()){
- DeltaRational c = DeltaRational(firstRight.getConstant().getValue(), 0);
- Polynomial left = getLeft();
- if(!left.allIntegralVariables()){
- return c;
- //this is a qpolynomial and the sign of the leading
- //coefficient will not change after the diff below
- } else{
- // the polynomial may be a z polynomial in which case
- // taking the diff is the simplest and obviously correct means
- Polynomial diff = right.singleton() ? left : left - right.getTail();
- if(diff.leadingCoefficientIsPositive()){
- return c;
- }else{
- return -c;
- }
- }
- }else{ // The constant is 0 sign cannot change
- return DeltaRational(0, 0);
- }
- }
- default: Unhandled() << cmpKind;
- }
-}
-
-std::tuple<Polynomial, Kind, Constant> Comparison::decompose(
- bool split_constant) const
-{
- Kind rel = getNode().getKind();
- if (rel == Kind::NOT)
- {
- switch (getNode()[0].getKind())
- {
- case kind::LEQ: rel = Kind::GT; break;
- case kind::LT: rel = Kind::GEQ; break;
- case kind::EQUAL: rel = Kind::DISTINCT; break;
- case kind::DISTINCT: rel = Kind::EQUAL; break;
- case kind::GEQ: rel = Kind::LT; break;
- case kind::GT: rel = Kind::LEQ; break;
- default:
- Assert(false) << "Unsupported relation: " << getNode()[0].getKind();
- }
- }
-
- Polynomial poly = getLeft() - getRight();
-
- if (!split_constant)
- {
- return std::tuple<Polynomial, Kind, Constant>{
- poly, rel, Constant::mkZero()};
- }
-
- Constant right = Constant::mkZero();
- if (poly.containsConstant())
- {
- right = -poly.getHead().getConstant();
- poly = poly + Polynomial::mkPolynomial(right);
- }
-
- Constant lcoeff = poly.getHead().getConstant();
- if (!lcoeff.isOne())
- {
- Constant invlcoeff = lcoeff.inverse();
- if (lcoeff.isNegative())
- {
- switch (rel)
- {
- case kind::LEQ: rel = Kind::GEQ; break;
- case kind::LT: rel = Kind::GT; break;
- case kind::EQUAL: break;
- case kind::DISTINCT: break;
- case kind::GEQ: rel = Kind::LEQ; break;
- case kind::GT: rel = Kind::LT; break;
- default: Assert(false) << "Unsupported relation: " << rel;
- }
- }
- poly = poly * invlcoeff;
- right = right * invlcoeff;
- }
-
- return std::tuple<Polynomial, Kind, Constant>{poly, rel, right};
-}
-
-Comparison Comparison::parseNormalForm(TNode n) {
- Trace("polynomial") << "Comparison::parseNormalForm(" << n << ")";
- Comparison result(n);
- Assert(result.isNormalForm());
- return result;
-}
-
-Node Comparison::toNode(Kind k, const Polynomial& l, const Constant& r) {
- Assert(isRelationOperator(k));
- switch(k) {
- case kind::GEQ:
- case kind::GT:
- return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
- default: Unhandled() << k;
- }
-}
-
-Node Comparison::toNode(Kind k, const Polynomial& l, const Polynomial& r) {
- Assert(isRelationOperator(k));
- switch(k) {
- case kind::GEQ:
- case kind::EQUAL:
- case kind::GT:
- return NodeManager::currentNM()->mkNode(k, l.getNode(), r.getNode());
- case kind::LEQ:
- return toNode(kind::GEQ, r, l).notNode();
- case kind::LT:
- return toNode(kind::GT, r, l).notNode();
- case kind::DISTINCT:
- return toNode(kind::EQUAL, r, l).notNode();
- default:
- Unreachable();
- }
-}
-
-bool Comparison::rightIsConstant() const {
- if(getNode().getKind() == kind::NOT){
- return getNode()[0][1].getKind() == kind::CONST_RATIONAL;
- }else{
- return getNode()[1].getKind() == kind::CONST_RATIONAL;
- }
-}
-
-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();
- switch(k){
- case kind::LT:
- case kind::LEQ:
- case kind::DISTINCT:
- left = getNode()[0][0];
- break;
- case kind::EQUAL:
- case kind::GT:
- case kind::GEQ:
- left = getNode()[0];
- break;
- default: Unhandled() << k;
- }
- return Polynomial::parsePolynomial(left);
-}
-
-Polynomial Comparison::getRight() const {
- TNode right;
- Kind k = comparisonKind();
- switch(k){
- case kind::LT:
- case kind::LEQ:
- case kind::DISTINCT:
- right = getNode()[0][1];
- break;
- case kind::EQUAL:
- case kind::GT:
- case kind::GEQ:
- right = getNode()[1];
- break;
- default: Unhandled() << k;
- }
- return Polynomial::parsePolynomial(right);
-}
-
-// Polynomial Comparison::getLeft() const {
-// Node n = getNode();
-// Node left = (n.getKind() == kind::NOT ? n[0]: n)[0];
-// return Polynomial::parsePolynomial(left);
-// }
-
-// Polynomial Comparison::getRight() const {
-// Node n = getNode();
-// Node right = (n.getKind() == kind::NOT ? n[0]: n)[1];
-// return Polynomial::parsePolynomial(right);
-// }
-
-bool Comparison::isNormalForm() const {
- Node n = getNode();
- Kind cmpKind = comparisonKind(n);
- Trace("nf::tmp") << "isNormalForm " << n << " " << cmpKind << endl;
- switch(cmpKind){
- case kind::CONST_BOOLEAN:
- return true;
- case kind::GT:
- return isNormalGT();
- case kind::GEQ:
- return isNormalGEQ();
- case kind::EQUAL:
- return isNormalEquality();
- case kind::LT:
- return isNormalLT();
- case kind::LEQ:
- return isNormalLEQ();
- case kind::DISTINCT:
- return isNormalDistinct();
- default:
- return false;
- }
-}
-
-/** This must be (> qpolynomial constant) */
-bool Comparison::isNormalGT() const {
- Node n = getNode();
- Assert(n.getKind() == kind::GT);
- if(!rightIsConstant()){
- return false;
- }else{
- Polynomial left = getLeft();
- if(left.containsConstant()){
- return false;
- }else if(!left.leadingCoefficientIsAbsOne()){
- return false;
- }else{
- return !left.isIntegral();
- }
- }
-}
-
-/** This must be (not (> qpolynomial constant)) */
-bool Comparison::isNormalLEQ() const {
- Node n = getNode();
- Trace("nf::tmp") << "isNormalLEQ " << n << endl;
- Assert(n.getKind() == kind::NOT);
- Assert(n[0].getKind() == kind::GT);
- if(!rightIsConstant()){
- return false;
- }else{
- Polynomial left = getLeft();
- if(left.containsConstant()){
- return false;
- }else if(!left.leadingCoefficientIsAbsOne()){
- return false;
- }else{
- return !left.isIntegral();
- }
- }
-}
-
-
-/** This must be (>= qpolynomial constant) or (>= zpolynomial constant) */
-bool Comparison::isNormalGEQ() const {
- Node n = getNode();
- Assert(n.getKind() == kind::GEQ);
-
- Trace("nf::tmp") << "isNormalGEQ " << n << " " << rightIsConstant() << endl;
-
- if(!rightIsConstant()){
- return false;
- }else{
- Polynomial left = getLeft();
- if(left.containsConstant()){
- return false;
- }else{
- if(left.isIntegral()){
- return left.signNormalizedReducedSum();
- }else{
- return left.leadingCoefficientIsAbsOne();
- }
- }
- }
-}
-
-/** This must be (not (>= qpolynomial constant)) or (not (>= zpolynomial constant)) */
-bool Comparison::isNormalLT() const {
- Node n = getNode();
- Assert(n.getKind() == kind::NOT);
- Assert(n[0].getKind() == kind::GEQ);
-
- if(!rightIsConstant()){
- return false;
- }else{
- Polynomial left = getLeft();
- if(left.containsConstant()){
- return false;
- }else{
- if(left.isIntegral()){
- return left.signNormalizedReducedSum();
- }else{
- return left.leadingCoefficientIsAbsOne();
- }
- }
- }
-}
-
-
-bool Comparison::isNormalEqualityOrDisequality() const {
- Polynomial pleft = getLeft();
-
- if(pleft.numMonomials() == 1){
- Monomial mleft = pleft.getHead();
- if(mleft.isConstant()){
- return false;
- }else{
- Polynomial pright = getRight();
- if(allIntegralVariables()){
- const Rational& lcoeff = mleft.getConstant().getValue();
- if(pright.isConstant()){
- return pright.isIntegral() && lcoeff.isOne();
- }
- Polynomial varRight = pright.containsConstant() ? pright.getTail() : pright;
- if(lcoeff.sgn() <= 0){
- return false;
- }else{
- Integer lcm = lcoeff.getDenominator().lcm(varRight.denominatorLCM());
- Integer g = lcoeff.getNumerator().gcd(varRight.numeratorGCD());
- Trace("nf::tmp") << lcm << " " << g << endl;
- if(!lcm.isOne()){
- return false;
- }else if(!g.isOne()){
- return false;
- }else{
- Monomial absMinRight = varRight.selectAbsMinimum();
- Trace("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl;
- if( mleft.absCmp(absMinRight) < 0){
- return true;
- }else{
- return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight;
- }
- }
- }
- }else{
- if(mleft.coefficientIsOne()){
- Trace("nf::tmp")
- << "dfklj " << mleft.getNode() << endl
- << pright.getNode() << endl
- << pright.variableMonomialAreStrictlyGreater(mleft)
- << endl;
- return pright.variableMonomialAreStrictlyGreater(mleft);
- }else{
- return false;
- }
- }
- }
- }else{
- return false;
- }
-}
-
-/** This must be (= qvarlist qpolynomial) or (= zmonomial zpolynomial)*/
-bool Comparison::isNormalEquality() const {
- Assert(getNode().getKind() == kind::EQUAL);
- return Theory::theoryOf(getNode()[0].getType()) == THEORY_ARITH &&
- isNormalEqualityOrDisequality();
-}
-
-/**
- * This must be (not (= qvarlist qpolynomial)) or
- * (not (= zmonomial zpolynomial)).
- */
-bool Comparison::isNormalDistinct() const {
- Assert(getNode().getKind() == kind::NOT);
- Assert(getNode()[0].getKind() == kind::EQUAL);
-
- return Theory::theoryOf(getNode()[0][0].getType()) == THEORY_ARITH &&
- isNormalEqualityOrDisequality();
-}
-
-Node Comparison::mkRatEquality(const Polynomial& p){
- Assert(!p.isConstant());
- Assert(!p.allIntegralVariables());
-
- Monomial minimalVList = p.minimumVariableMonomial();
- Constant coeffInv = -(minimalVList.getConstant().inverse());
-
- Polynomial newRight = (p - minimalVList) * coeffInv;
- Polynomial newLeft(Monomial::mkMonomial(minimalVList.getVarList()));
-
- return toNode(kind::EQUAL, newLeft, newRight);
-}
-
-Node Comparison::mkRatInequality(Kind k, const Polynomial& p){
- Assert(k == kind::GEQ || k == kind::GT);
- Assert(!p.isConstant());
- Assert(!p.allIntegralVariables());
-
- SumPair sp = SumPair::mkSumPair(p);
- Polynomial left = sp.getPolynomial();
- Constant right = - sp.getConstant();
-
- Monomial minimalVList = left.getHead();
- Assert(!minimalVList.isConstant());
-
- Constant coeffInv = minimalVList.getConstant().inverse().abs();
- Polynomial newLeft = left * coeffInv;
- Constant newRight = right * (coeffInv);
-
- return toNode(k, newLeft, newRight);
-}
-
-Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
- Assert(kind::GT == k || kind::GEQ == k);
- Assert(!p.isConstant());
- Assert(p.allIntegralVariables());
-
- SumPair sp = SumPair::mkSumPair(p);
- Polynomial left = sp.getPolynomial();
- Rational right = - (sp.getConstant().getValue());
-
-
- Monomial m = left.getHead();
- Assert(!m.isConstant());
-
- Integer lcm = left.denominatorLCM();
- Integer g = left.numeratorGCD();
- Rational mult(lcm,g);
-
- Polynomial newLeft = left * mult;
- Rational rightMult = right * mult;
-
- bool negateResult = false;
- if(!newLeft.leadingCoefficientIsPositive()){
- // multiply by -1
- // a: left >= right or b: left > right
- // becomes
- // a: -left <= -right or b: -left < -right
- // a: not (-left > -right) or b: (not -left >= -right)
- newLeft = -newLeft;
- rightMult = -rightMult;
- k = (kind::GT == k) ? kind::GEQ : kind::GT;
- negateResult = true;
- // the later stages handle:
- // a: not (-left >= -right + 1) or b: (not -left >= -right)
- }
-
- Node result = Node::null();
- if(rightMult.isIntegral()){
- if(k == kind::GT){
- // (> p z)
- // (>= p (+ z 1))
- Constant rightMultPlusOne = Constant::mkConstant(rightMult + 1);
- result = toNode(kind::GEQ, newLeft, rightMultPlusOne);
- }else{
- Constant newRight = Constant::mkConstant(rightMult);
- result = toNode(kind::GEQ, newLeft, newRight);
- }
- }else{
- //(>= l (/ n d))
- //(>= l (ceil (/ n d)))
- //This also hold for GT as (ceil (/ n d)) > (/ n d)
- Integer ceilr = rightMult.ceiling();
- Constant ceilRight = Constant::mkConstant(ceilr);
- result = toNode(kind::GEQ, newLeft, ceilRight);
- }
- Assert(!result.isNull());
- if(negateResult){
- return result.notNode();
- }else{
- return result;
- }
-}
-
-Node Comparison::mkIntEquality(const Polynomial& p){
- Assert(!p.isConstant());
- Assert(p.allIntegralVariables());
-
- SumPair sp = SumPair::mkSumPair(p);
- Polynomial varPart = sp.getPolynomial();
- Constant constPart = sp.getConstant();
-
- Integer lcm = varPart.denominatorLCM();
- Integer g = varPart.numeratorGCD();
- Constant mult = Constant::mkConstant(Rational(lcm,g));
-
- Constant constMult = constPart * mult;
-
- if(constMult.isIntegral()){
- Polynomial varPartMult = varPart * mult;
-
- Monomial m = varPartMult.selectAbsMinimum();
- bool mIsPositive = m.getConstant().isPositive();
-
- Polynomial noM = (varPartMult + (- m)) + Polynomial::mkPolynomial(constMult);
-
- // m + noM = 0
- Polynomial newRight = mIsPositive ? -noM : noM;
- Polynomial newLeft = mIsPositive ? m : -m;
-
- Assert(newRight.isIntegral());
- return toNode(kind::EQUAL, newLeft, newRight);
- }else{
- return mkBoolNode(false);
- }
-}
-
-Comparison Comparison::mkComparison(Kind k, const Polynomial& l, const Polynomial& r){
-
- //Make this special case fast for sharing!
- if((k == kind::EQUAL || k == kind::DISTINCT) && l.isVarList() && r.isVarList()){
- VarList vLeft = l.asVarList();
- VarList vRight = r.asVarList();
-
- if(vLeft == vRight){
- // return true for equalities and false for disequalities
- return Comparison(k == kind::EQUAL);
- }else{
- Node eqNode = vLeft < vRight ? toNode( kind::EQUAL, l, r) : toNode( kind::EQUAL, r, l);
- Node forK = (k == kind::DISTINCT) ? eqNode.notNode() : eqNode;
- return Comparison(forK);
- }
- }
-
- //General case
- Polynomial diff = l - r;
- if(diff.isConstant()){
- bool res = evaluateConstantPredicate(k, diff.asConstant(), Rational(0));
- return Comparison(res);
- }else{
- Node result = Node::null();
- bool isInteger = diff.allIntegralVariables();
- switch(k){
- case kind::EQUAL:
- result = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
- break;
- case kind::DISTINCT:
- {
- Node eq = isInteger ? mkIntEquality(diff) : mkRatEquality(diff);
- result = eq.notNode();
- }
- break;
- case kind::LEQ:
- case kind::LT:
- {
- Polynomial neg = - diff;
- Kind negKind = (k == kind::LEQ ? kind::GEQ : kind::GT);
- result = isInteger ?
- mkIntInequality(negKind, neg) : mkRatInequality(negKind, neg);
- }
- break;
- case kind::GEQ:
- case kind::GT:
- result = isInteger ?
- mkIntInequality(k, diff) : mkRatInequality(k, diff);
- break;
- default: Unhandled() << k;
- }
- Assert(!result.isNull());
- if(result.getKind() == kind::NOT && result[0].getKind() == kind::CONST_BOOLEAN){
- return Comparison(!(result[0].getConst<bool>()));
- }else{
- Comparison cmp(result);
- Assert(cmp.isNormalForm());
- return cmp;
- }
- }
-}
-
-bool Comparison::isBoolean() const {
- return getNode().getKind() == kind::CONST_BOOLEAN;
-}
-
-
-bool Comparison::debugIsIntegral() const{
- return getLeft().isIntegral() && getRight().isIntegral();
-}
-
-Kind Comparison::comparisonKind(TNode literal){
- switch(literal.getKind()){
- case kind::CONST_BOOLEAN:
- case kind::GT:
- case kind::GEQ:
- case kind::EQUAL:
- return literal.getKind();
- case kind::NOT:
- {
- TNode negatedAtom = literal[0];
- switch(negatedAtom.getKind()){
- case kind::GT: //(not (GT x c)) <=> (LEQ x c)
- return kind::LEQ;
- case kind::GEQ: //(not (GEQ x c)) <=> (LT x c)
- return kind::LT;
- case kind::EQUAL:
- return kind::DISTINCT;
- default:
- return kind::UNDEFINED_KIND;
- }
- }
- default:
- return kind::UNDEFINED_KIND;
- }
-}
-
-
-Node Polynomial::makeAbsCondition(Variable v, Polynomial p){
- Polynomial zerop = Polynomial::mkZero();
-
- Polynomial varp = Polynomial::mkPolynomial(v);
- Comparison pLeq0 = Comparison::mkComparison(kind::LEQ, p, zerop);
- Comparison negP = Comparison::mkComparison(kind::EQUAL, varp, -p);
- Comparison posP = Comparison::mkComparison(kind::EQUAL, varp, p);
-
- Node absCnd = (pLeq0.getNode()).iteNode(negP.getNode(), posP.getNode());
- return absCnd;
-}
-
-bool Polynomial::isNonlinear() const {
-
- for(iterator i=begin(), iend =end(); i != iend; ++i){
- Monomial m = *i;
- if(m.isNonlinear()){
- return true;
- }
- }
- return false;
-}
-
-} //namespace arith
-} //namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__NORMAL_FORM_H
-#define CVC5__THEORY__ARITH__NORMAL_FORM_H
-
-#include <algorithm>
-
-#include "base/output.h"
-#include "expr/node.h"
-#include "expr/node_self_iterator.h"
-#include "theory/arith/delta_rational.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/***********************************************/
-/***************** Normal Form *****************/
-/***********************************************/
-/***********************************************/
-
-/**
- * Section 1: Languages
- * The normal form for arithmetic nodes is defined by the language
- * accepted by the following BNFs with some guard conditions.
- * (The guard conditions are in Section 3 for completeness.)
- *
- * variable := n
- * where
- * n.isVar() or is foreign
- * n.getType() \in {Integer, Real}
- *
- * constant := n
- * where
- * n.getKind() == kind::CONST_RATIONAL
- *
- * var_list := variable | (* [variable])
- * where
- * len [variable] >= 2
- * isSorted varOrder [variable]
- *
- * monomial := constant | var_list | (* constant' var_list')
- * where
- * \f$ constant' \not\in {0,1} \f$
- *
- * polynomial := monomial' | (+ [monomial])
- * where
- * len [monomial] >= 2
- * isStrictlySorted monoOrder [monomial]
- * forall (\x -> x != 0) [monomial]
- *
- * rational_cmp := (|><| qpolynomial constant)
- * where
- * |><| is GEQ, or GT
- * not (exists constantMonomial (monomialList qpolynomial))
- * (exists realMonomial (monomialList qpolynomial))
- * abs(monomialCoefficient (head (monomialList qpolynomial))) == 1
- *
- * integer_cmp := (>= zpolynomial constant)
- * where
- * not (exists constantMonomial (monomialList zpolynomial))
- * (forall integerMonomial (monomialList zpolynomial))
- * the gcd of all numerators of coefficients is 1
- * the denominator of all coefficients and the constant is 1
- * the leading coefficient is positive
- *
- * rational_eq := (= qvarlist qpolynomial)
- * where
- * let allMonomials = (cons qvarlist (monomialList zpolynomial))
- * let variableMonomials = (drop constantMonomial allMonomials)
- * isStrictlySorted variableMonomials
- * exists realMonomial variableMonomials
- * is not empty qvarlist
- *
- * integer_eq := (= zmonomial zpolynomial)
- * where
- * let allMonomials = (cons zmonomial (monomialList zpolynomial))
- * let variableMonomials = (drop constantMonomial allMonomials)
- * not (constantMonomial zmonomial)
- * (forall integerMonomial allMonomials)
- * isStrictlySorted variableMonomials
- * the gcd of all numerators of coefficients is 1
- * the denominator of all coefficients and the constant is 1
- * the coefficient of monomial is positive
- * the value of the coefficient of monomial is minimal in variableMonomials
- *
- * comparison := TRUE | FALSE
- * | rational_cmp | (not rational_cmp)
- * | rational_eq | (not rational_eq)
- * | integer_cmp | (not integer_cmp)
- * | integer_eq | (not integer_eq)
- *
- * Normal Form for terms := polynomial
- * Normal Form for atoms := comparison
- */
-
-/**
- * Section 2: Helper Classes
- * The langauges accepted by each of these defintions
- * roughly corresponds to one of the following helper classes:
- * Variable
- * Constant
- * VarList
- * Monomial
- * Polynomial
- * Comparison
- *
- * Each of the classes obeys the following contracts/design decisions:
- * -Calling isMember(Node node) on a node returns true iff that node is a
- * a member of the language. Note: isMember is O(n).
- * -Calling isNormalForm() on a helper class object returns true iff that
- * helper class currently represents a normal form object.
- * -If isNormalForm() is false, then this object must have been made
- * using a mk*() factory function.
- * -If isNormalForm() is true, calling getNode() on all of these classes
- * returns a node that would be accepted by the corresponding language.
- * And if isNormalForm() is false, returns Node::null().
- * -Each of the classes is immutable.
- * -Public facing constuctors have a 1-to-1 correspondence with one of
- * production rules in the above grammar.
- * -Public facing constuctors are required to fail in debug mode when the
- * guards of the production rule are not strictly met.
- * For example: Monomial(Constant(1),VarList(Variable(x))) must fail.
- * -When a class has a Class parseClass(Node node) function,
- * if isMember(node) is true, the function is required to return an instance
- * of the helper class, instance, s.t. instance.getNode() == node.
- * And if isMember(node) is false, this throws an assertion failure in debug
- * mode and has undefined behaviour if not in debug mode.
- * -Only public facing constructors, parseClass(node), and mk*() functions are
- * considered privileged functions for the helper class.
- * -Only privileged functions may use private constructors, and access
- * private data members.
- * -All non-privileged functions are considered utility functions and
- * must use a privileged function in order to create an instance of the class.
- */
-
-/**
- * Section 3: Guard Conditions Misc.
- *
- *
- * variable_order x y =
- * if (meta_kind_variable x) and (meta_kind_variable y)
- * then node_order x y
- * else if (meta_kind_variable x)
- * then false
- * else if (meta_kind_variable y)
- * then true
- * else node_order x y
- *
- * var_list_len vl =
- * match vl with
- * variable -> 1
- * | (* [variable]) -> len [variable]
- *
- * order res =
- * match res with
- * Empty -> (0,Node::null())
- * | NonEmpty(vl) -> (var_list_len vl, vl)
- *
- * var_listOrder a b = tuple_cmp (order a) (order b)
- *
- * monomialVarList monomial =
- * match monomial with
- * constant -> Empty
- * | var_list -> NonEmpty(var_list)
- * | (* constant' var_list') -> NonEmpty(var_list')
- *
- * monoOrder m0 m1 = var_listOrder (monomialVarList m0) (monomialVarList m1)
- *
- * integerMonomial mono =
- * forall varHasTypeInteger (monomialVarList mono)
- *
- * realMonomial mono = not (integerMonomial mono)
- *
- * constantMonomial monomial =
- * match monomial with
- * constant -> true
- * | var_list -> false
- * | (* constant' var_list') -> false
- *
- * monomialCoefficient monomial =
- * match monomial with
- * constant -> constant
- * | var_list -> Constant(1)
- * | (* constant' var_list') -> constant'
- *
- * monomialList polynomial =
- * match polynomial with
- * monomial -> monomial::[]
- * | (+ [monomial]) -> [monomial]
- */
-
-/**
- * A NodeWrapper is a class that is a thinly veiled container of a Node object.
- */
-class NodeWrapper {
-private:
- Node node;
-public:
- NodeWrapper(Node n) : node(n) {}
- const Node& getNode() const { return node; }
-};/* class NodeWrapper */
-
-
-class Variable : public NodeWrapper {
-public:
- Variable(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
-
- // TODO: check if it's a theory leaf also
- static bool isMember(Node n)
- {
- Kind k = n.getKind();
- switch (k)
- {
- case kind::CONST_RATIONAL: return false;
- case kind::INTS_DIVISION:
- case kind::INTS_MODULUS:
- case kind::DIVISION:
- case kind::INTS_DIVISION_TOTAL:
- case kind::INTS_MODULUS_TOTAL:
- case kind::DIVISION_TOTAL: return isDivMember(n);
- case kind::IAND: return isIAndMember(n);
- case kind::POW2: return isPow2Member(n);
- case kind::EXPONENTIAL:
- case kind::SINE:
- case kind::COSINE:
- case kind::TANGENT:
- case kind::COSECANT:
- case kind::SECANT:
- case kind::COTANGENT:
- case kind::ARCSINE:
- case kind::ARCCOSINE:
- case kind::ARCTANGENT:
- case kind::ARCCOSECANT:
- case kind::ARCSECANT:
- case kind::ARCCOTANGENT:
- case kind::SQRT:
- case kind::PI: return isTranscendentalMember(n);
- case kind::ABS:
- case kind::TO_INTEGER:
- // Treat to_int as a variable; it is replaced in early preprocessing
- // by a variable.
- return true;
- default: return isLeafMember(n);
- }
- }
-
- static bool isLeafMember(Node n);
- static bool isIAndMember(Node n);
- static bool isPow2Member(Node n);
- static bool isDivMember(Node n);
- bool isDivLike() const{
- return isDivMember(getNode());
- }
- static bool isTranscendentalMember(Node n);
-
- bool isNormalForm() { return isMember(getNode()); }
-
- bool isIntegral() const {
- return getNode().getType().isInteger();
- }
-
- bool isMetaKindVariable() const {
- return getNode().isVar();
- }
-
- bool operator<(const Variable& v) const {
- VariableNodeCmp cmp;
- return cmp(this->getNode(), v.getNode());
- }
-
- struct VariableNodeCmp {
- static inline int cmp(const Node& n, const Node& m) {
- if ( n == m ) { return 0; }
-
- // RAN < real var < int var < non-variable
-
- bool nIsRAN = n.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
- bool mIsRAN = m.getKind() == Kind::REAL_ALGEBRAIC_NUMBER;
-
- if (mIsRAN != nIsRAN)
- {
- return nIsRAN ? -1 : 1;
- }
-
- bool nIsInteger = n.getType().isInteger();
- bool mIsInteger = m.getType().isInteger();
-
- if(nIsInteger == mIsInteger){
- bool nIsVariable = n.isVar();
- bool mIsVariable = m.isVar();
-
- if(nIsVariable == mIsVariable){
- if(n < m){
- return -1;
- }else{
- Assert(n != m);
- return 1;
- }
- }else{
- if(nIsVariable){
- return -1; // nIsVariable => !mIsVariable
- }else{
- return 1; // !nIsVariable => mIsVariable
- }
- }
- }else{
- Assert(nIsInteger != mIsInteger);
- if(nIsInteger){
- return 1; // nIsInteger => !mIsInteger
- }else{
- return -1; // !nIsInteger => mIsInteger
- }
- }
- }
-
- bool operator()(const Node& n, const Node& m) const {
- return VariableNodeCmp::cmp(n,m) < 0;
- }
- };
-
- bool operator==(const Variable& v) const { return getNode() == v.getNode();}
-
- size_t getComplexity() const;
-};/* class Variable */
-
-class Constant : public NodeWrapper {
-public:
- Constant(Node n) : NodeWrapper(n) { Assert(isMember(getNode())); }
-
- static bool isMember(Node n) { return n.getKind() == kind::CONST_RATIONAL; }
-
- bool isNormalForm() { return isMember(getNode()); }
-
- static Constant mkConstant(Node n)
- {
- Assert(n.getKind() == kind::CONST_RATIONAL);
- return Constant(n);
- }
-
- static Constant mkConstant(const Rational& rat);
-
- static Constant mkZero() {
- return mkConstant(Rational(0));
- }
-
- static Constant mkOne() {
- return mkConstant(Rational(1));
- }
-
- const Rational& getValue() const {
- 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(); }
-
- bool isZero() const { return sgn() == 0; }
- bool isNegative() const { return sgn() < 0; }
- bool isPositive() const { return sgn() > 0; }
-
- bool isOne() const { return getValue() == 1; }
-
- Constant operator*(const Rational& other) const {
- return mkConstant(getValue() * other);
- }
-
- Constant operator*(const Constant& other) const {
- return mkConstant(getValue() * other.getValue());
- }
- Constant operator+(const Constant& other) const {
- return mkConstant(getValue() + other.getValue());
- }
- Constant operator-() const {
- return mkConstant(-getValue());
- }
-
- Constant inverse() const{
- Assert(!isZero());
- return mkConstant(getValue().inverse());
- }
-
- bool operator<(const Constant& other) const {
- return getValue() < other.getValue();
- }
-
- bool operator==(const Constant& other) const {
- //Rely on node uniqueness.
- return getNode() == other.getNode();
- }
-
- Constant abs() const {
- if(isNegative()){
- return -(*this);
- }else{
- return (*this);
- }
- }
-
- uint32_t length() const{
- Assert(isIntegral());
- return getValue().getNumerator().length();
- }
-
- size_t getComplexity() const;
-
-};/* class Constant */
-
-
-template <class GetNodeIterator>
-inline Node makeNode(Kind k, GetNodeIterator start, GetNodeIterator end) {
- NodeBuilder nb(k);
-
- while(start != end) {
- nb << (*start).getNode();
- ++start;
- }
-
- return Node(nb);
-}/* makeNode<GetNodeIterator>(Kind, iterator, iterator) */
-
-/**
- * A VarList is a sorted list of variables representing a product.
- * If the VarList is empty, it represents an empty product or 1.
- * If the VarList has size 1, it represents a single variable.
- *
- * A non-sorted VarList can never be successfully made in debug mode.
- */
-class VarList : public NodeWrapper {
-private:
-
- static Node multList(const std::vector<Variable>& list) {
- Assert(list.size() >= 2);
-
- return makeNode(kind::NONLINEAR_MULT, list.begin(), list.end());
- }
-
- VarList() : NodeWrapper(Node::null()) {}
-
- VarList(Node n);
-
- typedef expr::NodeSelfIterator internal_iterator;
-
- internal_iterator internalBegin() const {
- if(singleton()){
- return expr::NodeSelfIterator::self(getNode());
- }else{
- return getNode().begin();
- }
- }
-
- internal_iterator internalEnd() const {
- if(singleton()){
- return expr::NodeSelfIterator::selfEnd(getNode());
- }else{
- return getNode().end();
- }
- }
-
-public:
-
- class iterator {
- private:
- internal_iterator d_iter;
-
- public:
- /* The following types are required by trait std::iterator_traits */
-
- /** Iterator tag */
- using iterator_category = std::forward_iterator_tag;
-
- /** The type of the item */
- using value_type = Variable;
-
- /** The pointer type of the item */
- using pointer = Variable*;
-
- /** The reference type of the item */
- using reference = Variable&;
-
- /** The type returned when two iterators are subtracted */
- using difference_type = std::ptrdiff_t;
-
- /* End of std::iterator_traits required types */
-
- explicit iterator(internal_iterator i) : d_iter(i) {}
-
- inline Variable operator*() {
- return Variable(*d_iter);
- }
-
- bool operator==(const iterator& i) {
- return d_iter == i.d_iter;
- }
-
- bool operator!=(const iterator& i) {
- return d_iter != i.d_iter;
- }
-
- iterator operator++() {
- ++d_iter;
- return *this;
- }
-
- iterator operator++(int) {
- return iterator(d_iter++);
- }
- };
-
- iterator begin() const {
- return iterator(internalBegin());
- }
-
- iterator end() const {
- return iterator(internalEnd());
- }
-
- Variable getHead() const {
- Assert(!empty());
- return *(begin());
- }
-
- VarList(Variable v) : NodeWrapper(v.getNode()) {
- Assert(isSorted(begin(), end()));
- }
-
- VarList(const std::vector<Variable>& l) : NodeWrapper(multList(l)) {
- Assert(l.size() >= 2);
- Assert(isSorted(begin(), end()));
- }
-
- static bool isMember(Node n);
-
- bool isNormalForm() const {
- return !empty();
- }
-
- static VarList mkEmptyVarList() {
- return VarList();
- }
-
-
- /** There are no restrictions on the size of l */
- static VarList mkVarList(const std::vector<Variable>& l) {
- if(l.size() == 0) {
- return mkEmptyVarList();
- } else if(l.size() == 1) {
- return VarList((*l.begin()).getNode());
- } else {
- return VarList(l);
- }
- }
-
- bool empty() const { return getNode().isNull(); }
- bool singleton() const {
- return !empty() && getNode().getKind() != kind::NONLINEAR_MULT;
- }
-
- int size() const {
- if(singleton())
- return 1;
- else
- return getNode().getNumChildren();
- }
-
- static VarList parseVarList(Node n);
-
- VarList operator*(const VarList& vl) const;
-
- int cmp(const VarList& vl) const;
-
- bool operator<(const VarList& vl) const { return cmp(vl) < 0; }
-
- bool operator==(const VarList& vl) const { return cmp(vl) == 0; }
-
- bool isIntegral() const {
- for(iterator i = begin(), e=end(); i != e; ++i ){
- Variable var = *i;
- if(!var.isIntegral()){
- return false;
- }
- }
- return true;
- }
- size_t getComplexity() const;
-
-private:
- bool isSorted(iterator start, iterator end);
-
-};/* class VarList */
-
-
-/** Constructors have side conditions. Use the static mkMonomial functions instead. */
-class Monomial : public NodeWrapper {
-private:
- Constant constant;
- VarList varList;
- Monomial(Node n, const Constant& c, const VarList& vl):
- NodeWrapper(n), constant(c), varList(vl)
- {
- Assert(!c.isZero() || vl.empty());
- Assert(c.isZero() || !vl.empty());
-
- Assert(!c.isOne() || !multStructured(n));
- }
-
- static Node makeMultNode(const Constant& c, const VarList& vl) {
- Assert(!c.isZero());
- Assert(!c.isOne());
- Assert(!vl.empty());
- return NodeManager::currentNM()->mkNode(kind::MULT, c.getNode(), vl.getNode());
- }
-
- static bool multStructured(Node n) {
- return n.getKind() == kind::MULT &&
- n[0].getKind() == kind::CONST_RATIONAL &&
- n.getNumChildren() == 2;
- }
-
- Monomial(const Constant& c):
- NodeWrapper(c.getNode()), constant(c), varList(VarList::mkEmptyVarList())
- { }
-
- Monomial(const VarList& vl):
- NodeWrapper(vl.getNode()), constant(Constant::mkConstant(1)), varList(vl)
- {
- Assert(!varList.empty());
- }
-
- Monomial(const Constant& c, const VarList& vl):
- NodeWrapper(makeMultNode(c,vl)), constant(c), varList(vl)
- {
- Assert(!c.isZero());
- Assert(!c.isOne());
- Assert(!varList.empty());
-
- Assert(multStructured(getNode()));
- }
-public:
- static bool isMember(TNode n);
-
- /** Makes a monomial with no restrictions on c and vl. */
- static Monomial mkMonomial(const Constant& c, const VarList& vl);
-
- /** If vl is empty, this make one. */
- static Monomial mkMonomial(const VarList& vl);
-
- static Monomial mkMonomial(const Constant& c){
- return Monomial(c);
- }
-
- static Monomial mkMonomial(const Variable& v){
- return Monomial(VarList(v));
- }
-
- static Monomial parseMonomial(Node n);
-
- static Monomial mkZero() {
- return Monomial(Constant::mkConstant(0));
- }
- static Monomial mkOne() {
- return Monomial(Constant::mkConstant(1));
- }
- const Constant& getConstant() const { return constant; }
- const VarList& getVarList() const { return varList; }
-
- bool isConstant() const {
- return varList.empty();
- }
-
- bool isZero() const {
- return constant.isZero();
- }
-
- bool coefficientIsOne() const {
- return constant.isOne();
- }
-
- bool absCoefficientIsOne() const {
- return coefficientIsOne() || constant.getValue() == -1;
- }
-
- bool constantIsPositive() const {
- return getConstant().isPositive();
- }
-
- Monomial operator*(const Rational& q) const;
- Monomial operator*(const Constant& c) const;
- Monomial operator*(const Monomial& mono) const;
-
- Monomial operator-() const{
- return (*this) * Rational(-1);
- }
-
-
- int cmp(const Monomial& mono) const {
- return getVarList().cmp(mono.getVarList());
- }
-
- bool operator<(const Monomial& vl) const {
- return cmp(vl) < 0;
- }
-
- bool operator==(const Monomial& vl) const {
- return cmp(vl) == 0;
- }
-
- static bool isSorted(const std::vector<Monomial>& m) {
- return std::is_sorted(m.begin(), m.end());
- }
-
- static bool isStrictlySorted(const std::vector<Monomial>& m) {
- 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
- */
- bool integralVariables() const {
- return getVarList().isIntegral();
- }
-
- /**
- * The coefficient of the monomial is integral.
- */
- bool integralCoefficient() const {
- return getConstant().isIntegral();
- }
-
- /**
- * A Monomial is an "integral" monomial if the constant is integral.
- */
- bool isIntegral() const {
- return integralCoefficient() && integralVariables();
- }
-
- /** Returns true if the VarList is a product of at least 2 Variables.*/
- bool isNonlinear() const {
- return getVarList().size() >= 2;
- }
-
- /**
- * 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);
-
- 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();
- }
-
- void print() const;
- static void printList(const std::vector<Monomial>& list);
-
- size_t getComplexity() const;
-};/* class Monomial */
-
-class SumPair;
-class Comparison;;
-
-class Polynomial : public NodeWrapper {
-private:
- bool d_singleton;
-
- Polynomial(TNode n) : NodeWrapper(n), d_singleton(Monomial::isMember(n)) {
- Assert(isMember(getNode()));
- }
-
- static Node makePlusNode(const std::vector<Monomial>& m) {
- Assert(m.size() >= 2);
-
- return makeNode(kind::ADD, m.begin(), m.end());
- }
-
- typedef expr::NodeSelfIterator internal_iterator;
-
- internal_iterator internalBegin() const {
- if(singleton()){
- return expr::NodeSelfIterator::self(getNode());
- }else{
- return getNode().begin();
- }
- }
-
- internal_iterator internalEnd() const {
- if(singleton()){
- return expr::NodeSelfIterator::selfEnd(getNode());
- }else{
- return getNode().end();
- }
- }
-
- bool singleton() const { return d_singleton; }
-
-public:
- static bool isMember(TNode n);
-
- class iterator {
- private:
- internal_iterator d_iter;
-
- public:
- /* The following types are required by trait std::iterator_traits */
-
- /** Iterator tag */
- using iterator_category = std::forward_iterator_tag;
-
- /** The type of the item */
- using value_type = Monomial;
-
- /** The pointer type of the item */
- using pointer = Monomial*;
-
- /** The reference type of the item */
- using reference = Monomial&;
-
- /** The type returned when two iterators are subtracted */
- using difference_type = std::ptrdiff_t;
-
- /* End of std::iterator_traits required types */
-
- explicit iterator(internal_iterator i) : d_iter(i) {}
-
- inline Monomial operator*() {
- return Monomial::parseMonomial(*d_iter);
- }
-
- bool operator==(const iterator& i) {
- return d_iter == i.d_iter;
- }
-
- bool operator!=(const iterator& i) {
- return d_iter != i.d_iter;
- }
-
- iterator operator++() {
- ++d_iter;
- return *this;
- }
-
- iterator operator++(int) {
- return iterator(d_iter++);
- }
- };
-
- iterator begin() const { return iterator(internalBegin()); }
- iterator end() const { return iterator(internalEnd()); }
-
- Polynomial(const Monomial& m):
- NodeWrapper(m.getNode()), d_singleton(true)
- {}
-
- Polynomial(const std::vector<Monomial>& m):
- NodeWrapper(makePlusNode(m)), d_singleton(false)
- {
- Assert(m.size() >= 2);
- Assert(Monomial::isStrictlySorted(m));
- }
-
- static Polynomial mkPolynomial(const Constant& c){
- return Polynomial(Monomial::mkMonomial(c));
- }
-
- static Polynomial mkPolynomial(const Variable& v){
- return Polynomial(Monomial::mkMonomial(v));
- }
-
- static Polynomial mkPolynomial(const std::vector<Monomial>& m) {
- if(m.size() == 0) {
- return Polynomial(Monomial::mkZero());
- } else if(m.size() == 1) {
- return Polynomial((*m.begin()));
- } else {
- return Polynomial(m);
- }
- }
-
- static Polynomial parsePolynomial(Node n) {
- return Polynomial(n);
- }
-
- static Polynomial mkZero() {
- return Polynomial(Monomial::mkZero());
- }
- static Polynomial mkOne() {
- return Polynomial(Monomial::mkOne());
- }
- bool isZero() const {
- return singleton() && (getHead().isZero());
- }
-
- bool isConstant() const {
- return singleton() && (getHead().isConstant());
- }
-
- bool containsConstant() const {
- return getHead().isConstant();
- }
-
- uint32_t size() const{
- if(singleton()){
- return 1;
- }else{
- Assert(getNode().getKind() == kind::ADD);
- return getNode().getNumChildren();
- }
- }
-
- Monomial getHead() const {
- return *(begin());
- }
-
- Polynomial getTail() const {
- Assert(!singleton());
-
- iterator tailStart = begin();
- ++tailStart;
- std::vector<Monomial> subrange;
- std::copy(tailStart, end(), std::back_inserter(subrange));
- return mkPolynomial(subrange);
- }
-
- Monomial minimumVariableMonomial() const;
- bool variableMonomialAreStrictlyGreater(const Monomial& m) const;
-
- void printList() const {
- if(TraceIsOn("normal-form")){
- Trace("normal-form") << "start list" << std::endl;
- for(iterator i = begin(), oend = end(); i != oend; ++i) {
- const Monomial& m =*i;
- m.print();
- }
- Trace("normal-form") << "end list" << std::endl;
- }
- }
-
- /** A Polynomial is an "integral" polynomial if all of the monomials are integral. */
- bool allIntegralVariables() const {
- for(iterator i = begin(), e=end(); i!=e; ++i){
- if(!(*i).integralVariables()){
- return false;
- }
- }
- return true;
- }
-
- /**
- * A Polynomial is an "integral" polynomial if all of the monomials are integral
- * and all of the coefficients are Integral. */
- bool isIntegral() const {
- for(iterator i = begin(), e=end(); i!=e; ++i){
- if(!(*i).isIntegral()){
- return false;
- }
- }
- return true;
- }
-
- static Polynomial sumPolynomials(const std::vector<Polynomial>& polynomials);
-
- /** Returns true if the polynomial contains a non-linear monomial.*/
- bool isNonlinear() const;
-
- /** Check whether this polynomial is only a single variable. */
- bool isVariable() const
- {
- return singleton() && getHead().getVarList().singleton()
- && getHead().coefficientIsOne();
- }
- /** Return the variable, given that isVariable() holds. */
- Variable getVariable() const
- {
- Assert(isVariable());
- return getHead().getVarList().getHead();
- }
-
- /**
- * Selects a minimal monomial in the polynomial by the absolute value of
- * the coefficient.
- */
- Monomial selectAbsMinimum() const;
-
- /** Returns true if the absolute value of the head coefficient is one. */
- bool leadingCoefficientIsAbsOne() const;
- bool leadingCoefficientIsPositive() const;
- bool denominatorLCMIsOne() const;
- bool numeratorGCDIsOne() const;
-
- bool signNormalizedReducedSum() const {
- return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne();
- }
-
- /**
- * Returns the Least Common Multiple of the denominators of the coefficients
- * of the monomials.
- */
- Integer denominatorLCM() const;
-
- /**
- * Returns the GCD of the numerators of the monomials.
- * Requires this to be an isIntegral() polynomial.
- */
- Integer numeratorGCD() const;
-
- /**
- * Returns the GCD of the coefficients of the monomials.
- * Requires this to be an isIntegral() polynomial.
- */
- Integer gcd() const;
-
- /** z must divide all of the coefficients of the polynomial. */
- Polynomial exactDivide(const Integer& z) const;
-
- Polynomial operator+(const Polynomial& vl) const;
- Polynomial operator-(const Polynomial& vl) const;
- Polynomial operator-() const{
- return (*this) * Rational(-1);
- }
-
- Polynomial operator*(const Rational& q) const;
- Polynomial operator*(const Constant& c) const;
- Polynomial operator*(const Monomial& mono) const;
-
- Polynomial operator*(const Polynomial& poly) const;
-
- /**
- * Viewing the integer polynomial as a list [(* coeff_i mono_i)]
- * The quotient and remainder of p divided by the non-zero integer z is:
- * q := [(* floor(coeff_i/z) mono_i )]
- * r := [(* rem(coeff_i/z) mono_i)]
- * computeQR(p,z) returns the node (+ q r).
- *
- * q and r are members of the Polynomial class.
- * For example:
- * computeQR( p = (+ 5 (* 3 x) (* 8 y)) , z = 2) returns
- * (+ (+ 2 x (* 4 y)) (+ 1 x))
- */
- static Node computeQR(const Polynomial& p, const Integer& z);
-
- /** Returns the coefficient associated with the VarList in the polynomial. */
- Constant getCoefficient(const VarList& vl) const;
-
- uint32_t maxLength() const{
- iterator i = begin(), e=end();
- if( i == e){
- return 1;
- }else{
- uint32_t max = (*i).coefficientLength();
- ++i;
- for(; i!=e; ++i){
- uint32_t curr = (*i).coefficientLength();
- if(curr > max){
- max = curr;
- }
- }
- return max;
- }
- }
-
- uint32_t numMonomials() const {
- if (getNode().getKind() == kind::ADD)
- {
- return getNode().getNumChildren();
- }
- else if (isZero())
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
-
- const Rational& asConstant() const{
- Assert(isConstant());
- return getNode().getConst<Rational>();
- //return getHead().getConstant().getValue();
- }
-
- bool isVarList() const {
- if(singleton()){
- return VarList::isMember(getNode());
- }else{
- return false;
- }
- }
-
- VarList asVarList() const {
- Assert(isVarList());
- return getHead().getVarList();
- }
-
- size_t getComplexity() const;
-
- friend class SumPair;
- friend class Comparison;
-
- /** Returns a node that if asserted ensures v is the abs of this polynomial.*/
- Node makeAbsCondition(Variable v){
- return makeAbsCondition(v, *this);
- }
-
- /** Returns a node that if asserted ensures v is the abs of p.*/
- static Node makeAbsCondition(Variable v, Polynomial p);
-
-};/* class Polynomial */
-
-
-/**
- * SumPair is a utility class that extends polynomials for use in computations.
- * A SumPair is always a combination of (+ p c) where
- * c is a constant and p is a polynomial such that p = 0 or !p.containsConstant().
- *
- * These are a useful utility for representing the equation p = c as (+ p -c) where the pair
- * is known to implicitly be equal to 0.
- *
- * SumPairs do not have unique representations due to the potential for p = 0.
- * This makes them inappropriate for normal forms.
- */
-class SumPair : public NodeWrapper {
-private:
- static Node toNode(const Polynomial& p, const Constant& c){
- return NodeManager::currentNM()->mkNode(
- kind::ADD, p.getNode(), c.getNode());
- }
-
- SumPair(TNode n) : NodeWrapper(n) { Assert(isNormalForm()); }
-
- public:
- SumPair(const Polynomial& p):
- NodeWrapper(toNode(p, Constant::mkConstant(0)))
- {
- Assert(isNormalForm());
- }
-
- SumPair(const Polynomial& p, const Constant& c):
- NodeWrapper(toNode(p, c))
- {
- Assert(isNormalForm());
- }
-
- static bool isMember(TNode n) {
- if (n.getKind() == kind::ADD && n.getNumChildren() == 2)
- {
- if(Constant::isMember(n[1])){
- if(Polynomial::isMember(n[0])){
- Polynomial p = Polynomial::parsePolynomial(n[0]);
- return p.isZero() || (!p.containsConstant());
- }else{
- return false;
- }
- }else{
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- bool isNormalForm() const {
- return isMember(getNode());
- }
-
- Polynomial getPolynomial() const {
- return Polynomial::parsePolynomial(getNode()[0]);
- }
-
- Constant getConstant() const {
- return Constant::mkConstant((getNode())[1]);
- }
-
- SumPair operator+(const SumPair& other) const {
- return SumPair(getPolynomial() + other.getPolynomial(),
- getConstant() + other.getConstant());
- }
-
- SumPair operator*(const Constant& c) const {
- return SumPair(getPolynomial() * c, getConstant() * c);
- }
-
- SumPair operator-(const SumPair& other) const {
- return (*this) + (other * Constant::mkConstant(-1));
- }
-
- static SumPair mkSumPair(const Polynomial& p);
-
- static SumPair mkSumPair(const Variable& var){
- return SumPair(Polynomial::mkPolynomial(var));
- }
-
- static SumPair parseSumPair(TNode n){
- return SumPair(n);
- }
-
- bool isIntegral() const{
- return getConstant().isIntegral() && getPolynomial().isIntegral();
- }
-
- bool isConstant() const {
- return getPolynomial().isZero();
- }
-
- bool isZero() const {
- return getConstant().isZero() && isConstant();
- }
-
- uint32_t size() const{
- return getPolynomial().size();
- }
-
- bool isNonlinear() const{
- return getPolynomial().isNonlinear();
- }
-
- /**
- * Returns the greatest common divisor of gcd(getPolynomial()) and getConstant().
- * The SumPair must be integral.
- */
- Integer gcd() const {
- Assert(isIntegral());
- return (getPolynomial().gcd()).gcd(getConstant().getValue().getNumerator());
- }
-
- uint32_t maxLength() const {
- Assert(isIntegral());
- return std::max(getPolynomial().maxLength(), getConstant().length());
- }
-
- static SumPair mkZero() {
- return SumPair(Polynomial::mkZero(), Constant::mkConstant(0));
- }
-
- static Node computeQR(const SumPair& sp, const Integer& div);
-
-};/* class SumPair */
-
-/* class OrderedPolynomialPair { */
-/* private: */
-/* Polynomial d_first; */
-/* Polynomial d_second; */
-/* public: */
-/* OrderedPolynomialPair(const Polynomial& f, const Polynomial& s) */
-/* : d_first(f), */
-/* d_second(s) */
-/* {} */
-
-/* /\** Returns the first part of the pair. *\/ */
-/* const Polynomial& getFirst() const { */
-/* return d_first; */
-/* } */
-
-/* /\** Returns the second part of the pair. *\/ */
-/* const Polynomial& getSecond() const { */
-/* return d_second; */
-/* } */
-
-/* OrderedPolynomialPair operator*(const Constant& c) const; */
-/* OrderedPolynomialPair operator+(const Polynomial& p) const; */
-
-/* /\** Returns true if both of the polynomials are constant. *\/ */
-/* bool isConstant() const; */
-
-/* /\** */
-/* * Evaluates an isConstant() ordered pair as if */
-/* * (k getFirst() getRight()) */
-/* *\/ */
-/* bool evaluateConstant(Kind k) const; */
-
-/* /\** */
-/* * Returns the Least Common Multiple of the monomials */
-/* * on the lefthand side and the constant on the right. */
-/* *\/ */
-/* Integer denominatorLCM() const; */
-
-/* /\** Constructs a SumPair. *\/ */
-/* SumPair toSumPair() const; */
-
-
-/* OrderedPolynomialPair divideByGCD() const; */
-/* OrderedPolynomialPair multiplyConstant(const Constant& c) const; */
-
-/* /\** */
-/* * Returns true if all of the variables are integers, */
-/* * and the coefficients are integers. */
-/* *\/ */
-/* bool isIntegral() const; */
-
-/* /\** Returns true if all of the variables are integers. *\/ */
-/* bool allIntegralVariables() const { */
-/* return getFirst().allIntegralVariables() && getSecond().allIntegralVariables(); */
-/* } */
-/* }; */
-
-class Comparison : public NodeWrapper {
-private:
-
- static Node toNode(Kind k, const Polynomial& l, const Constant& c);
- static Node toNode(Kind k, const Polynomial& l, const Polynomial& r);
-
- Comparison(TNode n);
-
- /**
- * Creates a node in normal form equivalent to (= l 0).
- * All variables in l are integral.
- */
- static Node mkIntEquality(const Polynomial& l);
-
- /**
- * Creates a comparison equivalent to (k l 0).
- * k is either GT or GEQ.
- * All variables in l are integral.
- */
- static Node mkIntInequality(Kind k, const Polynomial& l);
-
- /**
- * Creates a node equivalent to (= l 0).
- * It is not the case that all variables in l are integral.
- */
- static Node mkRatEquality(const Polynomial& l);
-
- /**
- * Creates a comparison equivalent to (k l 0).
- * k is either GT or GEQ.
- * It is not the case that all variables in l are integral.
- */
- static Node mkRatInequality(Kind k, const Polynomial& l);
-
-public:
-
- Comparison(bool val) :
- NodeWrapper(NodeManager::currentNM()->mkConst(val))
- { }
-
- /**
- * Given a literal to TheoryArith return a single kind to
- * to indicate its underlying structure.
- * The function returns the following in each case:
- * - (K left right) -> K where is either EQUAL, GT, or GEQ
- * - (CONST_BOOLEAN b) -> CONST_BOOLEAN
- * - (NOT (EQUAL left right)) -> DISTINCT
- * - (NOT (GT left right)) -> LEQ
- * - (NOT (GEQ left right)) -> LT
- * If none of these match, it returns UNDEFINED_KIND.
- */
- static Kind comparisonKind(TNode literal);
-
- Kind comparisonKind() const { return comparisonKind(getNode()); }
-
- static Comparison mkComparison(Kind k, const Polynomial& l, const Polynomial& r);
-
- /** Returns true if the comparison is a boolean constant. */
- bool isBoolean() const;
-
- /**
- * Returns true if the comparison is either a boolean term,
- * in integer normal form or mixed normal form.
- */
- bool isNormalForm() const;
-
-private:
- bool isNormalGT() const;
- bool isNormalGEQ() const;
-
- bool isNormalLT() const;
- bool isNormalLEQ() const;
-
- bool isNormalEquality() const;
- bool isNormalDistinct() const;
- bool isNormalEqualityOrDisequality() const;
-
- bool allIntegralVariables() const {
- return getLeft().allIntegralVariables() && getRight().allIntegralVariables();
- }
- bool rightIsConstant() const;
-
-public:
- Polynomial getLeft() const;
- Polynomial getRight() const;
-
- /* /\** Normal form check if at least one variable is real. *\/ */
- /* bool isMixedCompareNormalForm() const; */
-
- /* /\** Normal form check if at least one variable is real. *\/ */
- /* bool isMixedEqualsNormalForm() const; */
-
- /* /\** Normal form check is all variables are integer.*\/ */
- /* bool isIntegerCompareNormalForm() const; */
-
- /* /\** Normal form check is all variables are integer.*\/ */
- /* bool isIntegerEqualsNormalForm() const; */
-
-
- /**
- * Returns true if all of the variables are integers, the coefficients are integers,
- * and the right hand coefficient is an integer.
- */
- bool debugIsIntegral() const;
-
- static Comparison parseNormalForm(TNode n);
-
- inline static bool isNormalAtom(TNode n){
- Comparison parse = Comparison::parseNormalForm(n);
- return parse.isNormalForm();
- }
-
- size_t getComplexity() const;
-
- SumPair toSumPair() const;
-
- Polynomial normalizedVariablePart() const;
- DeltaRational normalizedDeltaRational() const;
-
- /**
- * Transforms a Comparison object into a stronger normal form:
- * Polynomial ~Kind~ Constant
- *
- * From the comparison, this method resolved a negation (if present) and
- * moves everything to the left side.
- * If split_constant is false, the constant is always zero.
- * If split_constant is true, the polynomial has no constant term and is
- * normalized to have leading coefficient one.
- */
- std::tuple<Polynomial, Kind, Constant> decompose(
- bool split_constant = false) const;
-
-};/* class Comparison */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__NORMAL_FORM_H */
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-ArithVariables::ArithVariables(context::Context* c,
- DeltaComputeCallback deltaComputingFunc)
- : d_vars(),
- d_safeAssignment(),
- d_numberOfVariables(0),
- d_pool(),
- d_released(),
- d_nodeToArithVarMap(),
- d_boundsQueue(),
- d_enqueueingBoundCounts(true),
- d_lbRevertHistory(c, true, LowerBoundCleanUp(this)),
- d_ubRevertHistory(c, true, UpperBoundCleanUp(this)),
- d_deltaIsSafe(false),
- d_delta(-1, 1),
- 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 >= ArithType::Integer;
-}
-
-/** 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),
- d_lb(NullConstraint),
- d_ub(NullConstraint),
- d_cmpAssignmentLB(1),
- d_cmpAssignmentUB(-1),
- d_pushCount(0),
- d_type(ArithType::Unset),
- d_node(Node::null()),
- d_auxiliary(false) {}
-
-bool ArithVariables::VarInfo::initialized() const {
- return d_var != ARITHVAR_SENTINEL;
-}
-
-void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){
- Assert(!initialized());
- Assert(d_lb == NullConstraint);
- Assert(d_ub == NullConstraint);
- Assert(d_cmpAssignmentLB > 0);
- Assert(d_cmpAssignmentUB < 0);
- d_var = v;
- d_node = n;
- d_auxiliary = aux;
-
- 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.
- Polynomial p = Polynomial::parsePolynomial(n);
- d_type = p.isIntegral() ? ArithType::Integer : ArithType::Real;
- }else{
- d_type = n.getType().isInteger() ? ArithType::Integer : ArithType::Real;
- }
-
- Assert(initialized());
-}
-
-void ArithVariables::VarInfo::uninitialize(){
- d_var = ARITHVAR_SENTINEL;
- d_node = Node::null();
-}
-
-bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){
- Assert(initialized());
- d_assignment = a;
- int cmpUB = (d_ub == NullConstraint) ? -1 :
- d_assignment.cmp(d_ub->getValue());
-
- int cmpLB = (d_lb == NullConstraint) ? 1 :
- d_assignment.cmp(d_lb->getValue());
-
- bool lbChanged = cmpLB != d_cmpAssignmentLB &&
- (cmpLB == 0 || d_cmpAssignmentLB == 0);
- bool ubChanged = cmpUB != d_cmpAssignmentUB &&
- (cmpUB == 0 || d_cmpAssignmentUB == 0);
-
- if(lbChanged || ubChanged){
- prev = boundsInfo();
- }
-
- d_cmpAssignmentUB = cmpUB;
- d_cmpAssignmentLB = cmpLB;
- return lbChanged || ubChanged;
-}
-
-void ArithVariables::releaseArithVar(ArithVar v){
- VarInfo& vi = d_vars.get(v);
-
- size_t removed CVC5_UNUSED = d_nodeToArithVarMap.erase(vi.d_node);
- Assert(removed == 1);
-
- vi.uninitialize();
-
- if(d_safeAssignment.isKey(v)){
- d_safeAssignment.remove(v);
- }
- if(vi.canBeReclaimed()){
- d_pool.push_back(v);
- }else{
- d_released.push_back(v);
- }
-}
-
-bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){
- Assert(initialized());
- bool wasNull = d_ub == NullConstraint;
- bool isNull = ub == NullConstraint;
-
- int cmpUB = isNull ? -1 : d_assignment.cmp(ub->getValue());
- bool ubChanged = (wasNull != isNull) ||
- (cmpUB != d_cmpAssignmentUB && (cmpUB == 0 || d_cmpAssignmentUB == 0));
- if(ubChanged){
- prev = boundsInfo();
- }
- d_ub = ub;
- d_cmpAssignmentUB = cmpUB;
- return ubChanged;
-}
-
-bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){
- Assert(initialized());
- bool wasNull = d_lb == NullConstraint;
- bool isNull = lb == NullConstraint;
-
- int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue());
-
- bool lbChanged = (wasNull != isNull) ||
- (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0));
- if(lbChanged){
- prev = boundsInfo();
- }
- d_lb = lb;
- d_cmpAssignmentLB = cmpLB;
- return lbChanged;
-}
-
-BoundCounts ArithVariables::VarInfo::atBoundCounts() const {
- uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
- uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
- return BoundCounts(lbIndc, ubIndc);
-}
-
-BoundCounts ArithVariables::VarInfo::hasBoundCounts() const {
- uint32_t lbIndc = (d_lb != NullConstraint) ? 1 : 0;
- uint32_t ubIndc = (d_ub != NullConstraint) ? 1 : 0;
- return BoundCounts(lbIndc, ubIndc);
-}
-
-BoundsInfo ArithVariables::VarInfo::boundsInfo() const{
- return BoundsInfo(atBoundCounts(), hasBoundCounts());
-}
-
-bool ArithVariables::VarInfo::canBeReclaimed() const{
- return d_pushCount == 0;
-}
-
-bool ArithVariables::canBeReleased(ArithVar v) const{
- return d_vars[v].canBeReclaimed();
-}
-
-void ArithVariables::attemptToReclaimReleased(){
- 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);
- }else{
- d_released[writePos] = v;
- writePos++;
- }
- }
- d_released.resize(writePos);
-}
-
-ArithVar ArithVariables::allocateVariable(){
- if(d_pool.empty()){
- attemptToReclaimReleased();
- }
- bool reclaim = !d_pool.empty();
-
- ArithVar varX;
- if(reclaim){
- varX = d_pool.back();
- d_pool.pop_back();
- }else{
- varX = d_numberOfVariables;
- ++d_numberOfVariables;
- }
- d_vars.set(varX, VarInfo());
- return varX;
-}
-
-
-const Rational& ArithVariables::getDelta(){
- if(!d_deltaIsSafe){
- Rational nextDelta = d_deltaComputingFunc();
- setDelta(nextDelta);
- }
- Assert(d_deltaIsSafe);
- return d_delta;
-}
-
-bool ArithVariables::boundsAreEqual(ArithVar x) const{
- if(hasLowerBound(x) && hasUpperBound(x)){
- return getUpperBound(x) == getLowerBound(x);
- }else{
- return false;
- }
-}
-
-
-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){
- Trace("partial_model") << "pm: updating the assignment to" << x
- << " now " << r <<endl;
- VarInfo& vi = d_vars.get(x);
- if(!d_safeAssignment.isKey(x)){
- d_safeAssignment.set(x, vi.d_assignment);
- }
- invalidateDelta();
-
- BoundsInfo prev;
- if(vi.setAssignment(r, prev)){
- addToBoundQueue(x, prev);
- }
-}
-
-void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r){
- Trace("partial_model") << "pm: updating the assignment to" << x
- << " now " << r <<endl;
- if(safe == r){
- if(d_safeAssignment.isKey(x)){
- d_safeAssignment.remove(x);
- }
- }else{
- d_safeAssignment.set(x, safe);
- }
-
- invalidateDelta();
- VarInfo& vi = d_vars.get(x);
- BoundsInfo prev;
- if(vi.setAssignment(r, prev)){
- addToBoundQueue(x, prev);
- }
-}
-
-void ArithVariables::initialize(ArithVar x, Node n, bool aux){
- VarInfo& vi = d_vars.get(x);
- vi.initialize(x, n, aux);
- d_nodeToArithVarMap[n] = x;
-}
-
-ArithVar ArithVariables::allocate(Node n, bool aux){
- ArithVar v = allocateVariable();
- initialize(v, n, aux);
- return v;
-}
-
-// void ArithVariables::initialize(ArithVar x, const DeltaRational& r){
-// Assert(x == d_mapSize);
-// Assert(equalSizes());
-// ++d_mapSize;
-
-// // Is worth mentioning that this is not strictly necessary, but this maintains the internal invariant
-// // that when d_assignment is set this gets set.
-// invalidateDelta();
-// d_assignment.push_back( r );
-
-// d_boundRel.push_back(BetweenBounds);
-
-// d_ubc.push_back(NullConstraint);
-// d_lbc.push_back(NullConstraint);
-// }
-
-/** Must know that the bound exists both calling this! */
-const DeltaRational& ArithVariables::getUpperBound(ArithVar x) const {
- Assert(inMaps(x));
- Assert(hasUpperBound(x));
-
- return getUpperBoundConstraint(x)->getValue();
-}
-
-const DeltaRational& ArithVariables::getLowerBound(ArithVar x) const {
- Assert(inMaps(x));
- Assert(hasLowerBound(x));
-
- return getLowerBoundConstraint(x)->getValue();
-}
-
-const DeltaRational& ArithVariables::getSafeAssignment(ArithVar x) const{
- Assert(inMaps(x));
- if(d_safeAssignment.isKey(x)){
- return d_safeAssignment[x];
- }else{
- return d_vars[x].d_assignment;
- }
-}
-
-const DeltaRational& ArithVariables::getAssignment(ArithVar x, bool safe) const{
- Assert(inMaps(x));
- if(safe && d_safeAssignment.isKey(x)){
- return d_safeAssignment[x];
- }else{
- return d_vars[x].d_assignment;
- }
-}
-
-const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{
- Assert(inMaps(x));
- return d_vars[x].d_assignment;
-}
-
-
-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.");
- ArithVar x = c->getVariable();
- Trace("partial_model") << "setLowerBoundConstraint(" << x << ":" << c << ")" << endl;
- Assert(inMaps(x));
- Assert(greaterThanLowerBound(x, c->getValue()));
-
- invalidateDelta();
- VarInfo& vi = d_vars.get(x);
- pushLowerBound(vi);
- BoundsInfo prev;
- if(vi.setLowerBound(c, prev)){
- addToBoundQueue(x, prev);
- }
-}
-
-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.");
-
- ArithVar x = c->getVariable();
- Trace("partial_model") << "setUpperBoundConstraint(" << x << ":" << c << ")" << endl;
- Assert(inMaps(x));
- Assert(lessThanUpperBound(x, c->getValue()));
-
- invalidateDelta();
- VarInfo& vi = d_vars.get(x);
- pushUpperBound(vi);
- BoundsInfo prev;
- if(vi.setUpperBound(c, prev)){
- addToBoundQueue(x, prev);
- }
-}
-
-int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
- if(!hasLowerBound(x)){
- // l = -\intfy
- // ? c < -\infty |- _|_
- return 1;
- }else{
- return c.cmp(getLowerBound(x));
- }
-}
-
-int ArithVariables::cmpToUpperBound(ArithVar x, const DeltaRational& c) const{
- if(!hasUpperBound(x)){
- //u = \intfy
- // ? c > \infty |- _|_
- return -1;
- }else{
- return c.cmp(getUpperBound(x));
- }
-}
-
-bool ArithVariables::equalsLowerBound(ArithVar x, const DeltaRational& c){
- if(!hasLowerBound(x)){
- return false;
- }else{
- return c == getLowerBound(x);
- }
-}
-bool ArithVariables::equalsUpperBound(ArithVar x, const DeltaRational& c){
- if(!hasUpperBound(x)){
- return false;
- }else{
- return c == getUpperBound(x);
- }
-}
-
-bool ArithVariables::hasEitherBound(ArithVar x) const{
- return hasLowerBound(x) || hasUpperBound(x);
-}
-
-bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
- return d_vars[x].d_cmpAssignmentUB < 0;
-}
-
-bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
- return d_vars[x].d_cmpAssignmentLB > 0;
-}
-
-bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
- return
- d_vars[x].d_cmpAssignmentLB >= 0 &&
- d_vars[x].d_cmpAssignmentUB <= 0;
-}
-
-
-void ArithVariables::clearSafeAssignments(bool revert){
-
- if(revert && !d_safeAssignment.empty()){
- invalidateDelta();
- }
-
- while(!d_safeAssignment.empty()){
- ArithVar atBack = d_safeAssignment.back();
- if(revert){
- VarInfo& vi = d_vars.get(atBack);
- BoundsInfo prev;
- if(vi.setAssignment(d_safeAssignment[atBack], prev)){
- addToBoundQueue(atBack, prev);
- }
- }
- d_safeAssignment.pop_back();
- }
-}
-
-void ArithVariables::revertAssignmentChanges(){
- clearSafeAssignments(true);
-}
-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){
- printModel(*i, out);
- }
- out << "---Done Model ---" << std::endl;
-}
-
-void ArithVariables::printModel(ArithVar x, std::ostream& out) const{
- out << "model" << x << ": "
- << asNode(x) << " "
- << getAssignment(x) << " ";
- if(!hasLowerBound(x)){
- out << "no lb ";
- }else{
- out << getLowerBound(x) << " ";
- out << getLowerBoundConstraint(x) << " ";
- }
- if(!hasUpperBound(x)){
- out << "no ub ";
- }else{
- out << getUpperBound(x) << " ";
- out << getUpperBoundConstraint(x) << " ";
- }
-
- if(isInteger(x) && !integralAssignment(x)){
- out << "(not an integer)" << endl;
- }
- out << endl;
-}
-
-void ArithVariables::printModel(ArithVar x) const{
- printModel(x, Trace("model"));
-}
-
-void ArithVariables::pushUpperBound(VarInfo& vi){
- ++vi.d_pushCount;
- d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
-}
-void ArithVariables::pushLowerBound(VarInfo& vi){
- ++vi.d_pushCount;
- d_lbRevertHistory.push_back(make_pair(vi.d_var, vi.d_lb));
-}
-
-void ArithVariables::popUpperBound(AVCPair* c){
- ArithVar x = c->first;
- VarInfo& vi = d_vars.get(x);
- BoundsInfo prev;
- if(vi.setUpperBound(c->second, prev)){
- addToBoundQueue(x, prev);
- }
- --vi.d_pushCount;
-}
-
-void ArithVariables::popLowerBound(AVCPair* c){
- ArithVar x = c->first;
- VarInfo& vi = d_vars.get(x);
- BoundsInfo prev;
- if(vi.setLowerBound(c->second, prev)){
- addToBoundQueue(x, prev);
- }
- --vi.d_pushCount;
-}
-
-void ArithVariables::addToBoundQueue(ArithVar v, const BoundsInfo& prev){
- if(d_enqueueingBoundCounts && !d_boundsQueue.isKey(v)){
- d_boundsQueue.set(v, prev);
- }
-}
-
-BoundsInfo ArithVariables::selectBoundsInfo(ArithVar v, bool old) const {
- if(old && d_boundsQueue.isKey(v)){
- return d_boundsQueue[v];
- }else{
- return boundsInfo(v);
- }
-}
-
-bool ArithVariables::boundsQueueEmpty() const {
- return d_boundsQueue.empty();
-}
-
-void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){
- while(!boundsQueueEmpty()){
- ArithVar v = d_boundsQueue.back();
- BoundsInfo prev = d_boundsQueue[v];
- d_boundsQueue.pop_back();
- BoundsInfo curr = boundsInfo(v);
- if(prev != curr){
- changed(v, prev);
- }
- }
-}
-
-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);
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Morgan Deters, Aina Niemetz
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * Datastructures that track variable by variable information.
- *
- * 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 "cvc5_private.h"
-
-#ifndef CVC5__THEORY__ARITH__PARTIAL_MODEL_H
-#define CVC5__THEORY__ARITH__PARTIAL_MODEL_H
-
-#include <vector>
-
-#include "context/cdlist.h"
-#include "expr/node.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/bound_counts.h"
-#include "theory/arith/callbacks.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-
-namespace cvc5::context {
-class Context;
-}
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/**
- * (For the moment) the type hierarchy goes as:
- * Integer <: Real
- * The type number of a variable is an integer representing the most specific
- * type of the variable. The possible values of type number are:
- */
-enum class ArithType {
- Unset,
- Real,
- Integer,
-};
-
-class ArithVariables {
-private:
-
- class VarInfo {
- friend class ArithVariables;
- ArithVar d_var;
-
- DeltaRational d_assignment;
- ConstraintP d_lb;
- ConstraintP d_ub;
- int d_cmpAssignmentLB;
- int d_cmpAssignmentUB;
-
- unsigned d_pushCount;
- ArithType d_type;
- Node d_node;
- bool d_auxiliary;
-
- public:
- VarInfo();
-
- bool setAssignment(const DeltaRational& r, 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 an auxillary
- * variable.
- */
- 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. */
- BoundCounts atBoundCounts() const;
-
- /** Combination of indicator variables for whether it has upper and
- * lower bounds. */
- BoundCounts hasBoundCounts() const;
-
- /** Stores both atBoundCounts() and hasBoundCounts(). */
- BoundsInfo boundsInfo() const;
- };
-
- /**Maps from ArithVar -> VarInfo */
- typedef DenseMap<VarInfo> VarInfoVec;
-
- /** This maps an ArithVar to its Variable information.*/
- VarInfoVec d_vars;
-
- /** Partial Map from Arithvar -> PreviousAssignment */
- DenseMap<DeltaRational> d_safeAssignment;
-
- /** if d_vars.isKey(x), then x < d_numberOfVariables */
- ArithVar d_numberOfVariables;
-
- /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */
- // Everything in the pool is fair game.
- // There must be NO outstanding assertions
- std::vector<ArithVar> d_pool;
- std::vector<ArithVar> d_released;
- //std::list<ArithVar>::iterator d_releasedIterator;
-
- // Reverse Map from Node to ArithVar
- // Inverse of d_vars[x].d_node
- NodeToArithVarMap d_nodeToArithVarMap;
-
-
- /** The queue of constraints where the assignment is at the bound.*/
- DenseMap<BoundsInfo> d_boundsQueue;
-
- /**
- * 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.
- */
- bool d_enqueueingBoundCounts;
-
- public:
-
- /** Returns the number of variables. */
- ArithVar getNumberOfVariables() const;
-
- /** Returns true if the node has an associated variables. */
- bool hasArithVar(TNode x) const;
-
- /** 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;
-
- /** Returns the node associated with an ArithVar. */
- Node asNode(ArithVar a) const;
-
- /** Allocates a freshly allocated variables. */
- ArithVar allocateVariable();
-
- class var_iterator {
- private:
- const VarInfoVec* d_vars;
- VarInfoVec::const_iterator d_wrapped;
- public:
- var_iterator();
- var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci);
- var_iterator& operator++();
-
- bool operator==(const var_iterator& other) const;
- bool operator!=(const var_iterator& other) const;
- ArithVar operator*() const;
-
- private:
- void nextInitialized();
- };
-
- var_iterator var_begin() const;
- var_iterator var_end() const;
-
-
- bool canBeReleased(ArithVar v) const;
- void releaseArithVar(ArithVar v);
- void attemptToReclaimReleased();
-
- /** Is this variable guaranteed to have an integer assignment?
- * (Should agree with the type system.) */
- bool isInteger(ArithVar x) const;
-
- /** 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, ConstraintP> AVCPair;
- class LowerBoundCleanUp {
- private:
- ArithVariables* d_pm;
- public:
- LowerBoundCleanUp(ArithVariables* pm);
- void operator()(AVCPair* restore);
- };
-
- class UpperBoundCleanUp {
- private:
- ArithVariables* d_pm;
- public:
- UpperBoundCleanUp(ArithVariables* pm);
- void operator()(AVCPair* restore);
- };
-
- typedef context::CDList<AVCPair, LowerBoundCleanUp> LBReverts;
- LBReverts d_lbRevertHistory;
-
- typedef context::CDList<AVCPair, UpperBoundCleanUp> UBReverts;
- UBReverts d_ubRevertHistory;
-
- void pushUpperBound(VarInfo&);
- void popUpperBound(AVCPair*);
- void pushLowerBound(VarInfo&);
- void popLowerBound(AVCPair*);
-
- // This is true when setDelta() is called, until invalidateDelta is called
- bool d_deltaIsSafe;
- // Cache of a value of delta to ensure a total order.
- Rational d_delta;
- // Function to call if the value of delta needs to be recomputed.
- DeltaComputeCallback d_deltaComputingFunc;
-
-
-public:
- ArithVariables(context::Context* c, DeltaComputeCallback deltaComputation);
-
- /**
- * This sets the lower bound for a variable in the current context.
- * This must be stronger the previous constraint.
- */
- 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(ConstraintP ub);
-
- /** Returns the constraint for the upper bound of a variable. */
- inline ConstraintP getUpperBoundConstraint(ArithVar x) const
- {
- return d_vars[x].d_ub;
- }
- /** Returns the constraint for the lower bound of a variable. */
- 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 aux);
-
- 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;
- const DeltaRational& getAssignment(ArithVar x, bool safe) const;
-
- /* Reverts all variable assignments to their safe values. */
- void revertAssignmentChanges();
-
- /* Commits all variables assignments as safe.*/
- void commitAssignmentChanges();
-
-
- bool lowerBoundIsZero(ArithVar x);
- bool upperBoundIsZero(ArithVar x);
-
- bool boundsAreEqual(ArithVar x) const;
-
- /* Sets an unsafe variable assignment */
- void setAssignment(ArithVar x, const DeltaRational& r);
- void setAssignment(ArithVar x, const DeltaRational& safe, const DeltaRational& r);
-
-
- /** Must know that the bound exists before calling this! */
- const DeltaRational& getUpperBound(ArithVar x) const;
- const DeltaRational& getLowerBound(ArithVar x) const;
- const DeltaRational& getAssignment(ArithVar x) const;
-
-
- bool equalsLowerBound(ArithVar x, const DeltaRational& c);
- bool equalsUpperBound(ArithVar x, const DeltaRational& c);
-
- /**
- * If lowerbound > - \infty:
- * return getAssignment(x).cmp(getLowerBound(x))
- * If lowerbound = - \infty:
- * return 1
- */
- int cmpToLowerBound(ArithVar x, const DeltaRational& c) const;
-
- inline bool strictlyLessThanLowerBound(ArithVar x, const DeltaRational& c) const{
- return cmpToLowerBound(x, c) < 0;
- }
- inline bool lessThanLowerBound(ArithVar x, const DeltaRational& c) const{
- return cmpToLowerBound(x, c) <= 0;
- }
-
- inline bool strictlyGreaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
- return cmpToLowerBound(x, c) > 0;
- }
-
- inline bool greaterThanLowerBound(ArithVar x, const DeltaRational& c) const{
- return cmpToLowerBound(x, c) >= 0;
- }
- /**
- * If upperbound < \infty:
- * return getAssignment(x).cmp(getUpperBound(x))
- * If upperbound = \infty:
- * return -1
- */
- int cmpToUpperBound(ArithVar x, const DeltaRational& c) const;
-
- inline bool strictlyLessThanUpperBound(ArithVar x, const DeltaRational& c) const{
- return cmpToUpperBound(x, c) < 0;
- }
-
- inline bool lessThanUpperBound(ArithVar x, const DeltaRational& c) const{
- return cmpToUpperBound(x, c) <= 0;
- }
-
- inline bool strictlyGreaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
- return cmpToUpperBound(x, c) > 0;
- }
-
- inline bool greaterThanUpperBound(ArithVar x, const DeltaRational& c) const{
- return cmpToUpperBound(x, c) >= 0;
- }
-
- inline int cmpAssignmentLowerBound(ArithVar x) const{
- return d_vars[x].d_cmpAssignmentLB;
- }
- inline int cmpAssignmentUpperBound(ArithVar x) const{
- return d_vars[x].d_cmpAssignmentUB;
- }
-
- inline BoundCounts atBoundCounts(ArithVar x) const {
- return d_vars[x].atBoundCounts();
- }
- inline BoundCounts hasBoundCounts(ArithVar x) const {
- return d_vars[x].hasBoundCounts();
- }
- inline BoundsInfo boundsInfo(ArithVar x) const{
- return d_vars[x].boundsInfo();
- }
-
- bool strictlyBelowUpperBound(ArithVar x) const;
- bool strictlyAboveLowerBound(ArithVar x) const;
- bool assignmentIsConsistent(ArithVar x) const;
-
- void printModel(ArithVar x, std::ostream& out) const;
- void printModel(ArithVar x) const;
-
- /** returns true iff x has both a lower and upper bound. */
- bool hasEitherBound(ArithVar x) const;
- inline bool hasLowerBound(ArithVar x) const{
- return d_vars[x].d_lb != NullConstraint;
- }
- inline bool hasUpperBound(ArithVar x) const{
- return d_vars[x].d_ub != NullConstraint;
- }
-
- const Rational& getDelta();
-
- void invalidateDelta();
-
- void setDelta(const Rational& d);
-
- void startQueueingBoundCounts();
- void stopQueueingBoundCounts();
- void addToBoundQueue(ArithVar v, const BoundsInfo& prev);
-
- BoundsInfo selectBoundsInfo(ArithVar v, bool old) const;
-
- bool boundsQueueEmpty() const;
- void processBoundsQueue(BoundUpdateCallback& changed);
-
- 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:
-
- /**
- * This function implements the mostly identical:
- * revertAssignmentChanges() and commitAssignmentChanges().
- */
- void clearSafeAssignments(bool revert);
-
- bool debugEqualSizes();
-
- bool inMaps(ArithVar x) const;
-
-};/* class ArithVariables */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
-
-#endif /* CVC5__THEORY__ARITH__PARTIAL_MODEL_H */
#include "expr/skolem_manager.h"
#include "theory/arith/arith_poly_norm.h"
#include "theory/arith/arith_utilities.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/normal_form.h"
+#include "theory/arith/linear/constraint.h"
#include "theory/arith/operator_elim.h"
using namespace cvc5::internal::kind;
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-
-#include "theory/arith/simplex.h"
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "options/smt_options.h"
-#include "smt/env.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_value.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-SimplexDecisionProcedure::SimplexDecisionProcedure(
- Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc)
- : EnvObj(env),
- d_pivots(0),
- d_conflictVariables(),
- d_linEq(linEq),
- d_variables(d_linEq.getVariables()),
- d_tableau(d_linEq.getTableau()),
- d_errorSet(errors),
- d_numVariables(0),
- d_conflictChannel(conflictChannel),
- d_conflictBuilder(NULL),
- d_arithVarMalloc(tvmalloc),
- d_errorSize(0),
- d_zero(0),
- d_posOne(1),
- d_negOne(-1)
-{
- d_heuristicRule = options().arith.arithErrorSelectionRule;
- d_errorSet.setSelectionRule(d_heuristicRule);
- d_conflictBuilder = new FarkasConflictBuilder(options().smt.produceProofs);
-}
-
-SimplexDecisionProcedure::~SimplexDecisionProcedure(){
- delete d_conflictBuilder;
-}
-
-
-bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& conflicts) {
- TimerStat::CodeTimer codeTimer(timer);
- Assert(d_conflictVariables.empty());
-
- while(d_errorSet.moreSignals()){
- ArithVar curr = d_errorSet.topSignal();
- if(d_tableau.isBasic(curr) && !d_variables.assignmentIsConsistent(curr)){
- Assert(d_linEq.basicIsTracked(curr));
-
- if(!d_conflictVariables.isMember(curr) && checkBasicForConflict(curr)){
-
- Trace("recentlyViolated")
- << "It worked? "
- << conflicts.get()
- << " " << curr
- << " " << checkBasicForConflict(curr) << endl;
- reportConflict(curr);
- ++conflicts;
- }
- }
- // Pop signal afterwards in case d_linEq.trackVariable(curr);
- // is needed for for the ErrorSet
- d_errorSet.popSignal();
- }
- d_errorSize = d_errorSet.errorSize();
-
- Assert(d_errorSet.noSignals());
- return !d_conflictVariables.empty();
-}
-
-/** Reports a conflict to on the output channel. */
-void SimplexDecisionProcedure::reportConflict(ArithVar basic){
- Assert(!d_conflictVariables.isMember(basic));
- Assert(checkBasicForConflict(basic));
-
- ConstraintCP conflicted = generateConflictForBasic(basic);
- Assert(conflicted != NullConstraint);
- d_conflictChannel.raiseConflict(conflicted, InferenceId::ARITH_CONF_SIMPLEX);
-
- d_conflictVariables.add(basic);
-}
-
-ConstraintCP SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) 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, *d_conflictBuilder);
- }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
- Assert(d_linEq.nonbasicsAtLowerBounds(basic));
- return d_linEq.generateConflictAboveUpperBound(basic, *d_conflictBuilder);
- }else{
- Unreachable();
- return NullConstraint;
- }
-}
-bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const {
- if(checkBasicForConflict(basic)){
- ConstraintCP conflicted = generateConflictForBasic(basic);
- d_conflictChannel.raiseConflict(conflicted, InferenceId::UNKNOWN);
- return true;
- }else{
- return false;
- }
-}
-
-bool SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic) const {
- Assert(d_tableau.isBasic(basic));
- Assert(d_linEq.basicIsTracked(basic));
-
- if(d_variables.cmpAssignmentLowerBound(basic) < 0){
- if(d_linEq.nonbasicsAtUpperBounds(basic)){
- return true;
- }
- }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){
- if(d_linEq.nonbasicsAtLowerBounds(basic)){
- return true;
- }
- }
- return false;
-}
-
-void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, ArithVar tmp){
- TimerStat::CodeTimer codeTimer(timer);
- Assert(tmp != ARITHVAR_SENTINEL);
- Assert(d_tableau.isBasic(tmp));
-
- RowIndex ri = d_tableau.basicToRowIndex(tmp);
- d_linEq.stopTrackingRowIndex(ri);
- d_tableau.removeBasicRow(tmp);
- releaseVariable(tmp);
-}
-
-void SimplexDecisionProcedure::shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped){
- TimerStat::CodeTimer codeTimer(timer);
- for(ArithVarVec::const_iterator i=dropped.begin(), i_end = dropped.end(); i != i_end; ++i){
- ArithVar back = *i;
-
- int focusSgn = d_errorSet.focusSgn(back);
- Rational chg(-focusSgn);
-
- d_linEq.substitutePlusTimesConstant(inf, back, chg);
- }
-}
-
-void SimplexDecisionProcedure::adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges){
- TimerStat::CodeTimer codeTimer(timer);
- for(AVIntPairVec::const_iterator i=focusChanges.begin(), i_end = focusChanges.end(); i != i_end; ++i){
- ArithVar v = (*i).first;
- int focusChange = (*i).second;
-
- Rational chg(focusChange);
- if(d_tableau.isBasic(v)){
- d_linEq.substitutePlusTimesConstant(inf, v, chg);
- }else{
- d_linEq.directlyAddToCoefficient(inf, v, chg);
- }
- }
-}
-
-void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
- AVIntPairVec justE;
- int sgn = d_errorSet.getSgn(e);
- justE.push_back(make_pair(e, sgn));
- adjustInfeasFunc(timer, inf, justE);
-}
-
-void SimplexDecisionProcedure::removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){
- AVIntPairVec justE;
- int opSgn = -d_errorSet.getSgn(e);
- justE.push_back(make_pair(e, opSgn));
- adjustInfeasFunc(timer, inf, justE);
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){
- Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction start" << endl;
-
- TimerStat::CodeTimer codeTimer(timer);
- Assert(!d_errorSet.focusEmpty());
- Assert(debugIsASet(set));
-
- ArithVar inf = requestVariable();
- Assert(inf != ARITHVAR_SENTINEL);
-
- std::vector<Rational> coeffs;
- std::vector<ArithVar> variables;
-
- for(ArithVarVec::const_iterator iter = set.begin(), iend = set.end(); iter != iend; ++iter){
- ArithVar e = *iter;
-
- Assert(d_tableau.isBasic(e));
- Assert(!d_variables.assignmentIsConsistent(e));
-
- int sgn = d_errorSet.getSgn(e);
- Assert(sgn == -1 || sgn == 1);
- const Rational& violatedCoeff = sgn < 0 ? d_negOne : d_posOne;
- coeffs.push_back(violatedCoeff);
- variables.push_back(e);
-
- Trace("constructInfeasiblityFunction") << violatedCoeff << " " << e << endl;
-
- }
- d_tableau.addRow(inf, coeffs, variables);
- DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
- d_variables.setAssignment(inf, newAssignment);
-
- //d_linEq.trackVariable(inf);
- d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf));
-
- Trace("constructInfeasiblityFunction") << inf << " " << newAssignment << endl;
- Trace("constructInfeasiblityFunction") << "constructInfeasiblityFunction done" << endl;
- return inf;
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer){
- ArithVarVec inError;
- d_errorSet.pushFocusInto(inError);
- return constructInfeasiblityFunction(timer, inError);
-}
-
-ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, ArithVar e){
- ArithVarVec justE;
- justE.push_back(e);
- return constructInfeasiblityFunction(timer, justE);
-}
-
-void SimplexDecisionProcedure::addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic){
- pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
- sgns[p].push_back(basic);
-}
-
-void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int norm){
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
- const Tableau::Entry& entry = *i;
- ArithVar v = entry.getColVar();
- int sgn = (entry.getCoefficient().sgn());
- addSgn(sgns, v, norm * sgn, basic);
- }
-}
-
-ArithVar SimplexDecisionProcedure::find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside){
- pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
- sgn_table::const_iterator i = sgns.find(p);
-
- if(i != sgns.end()){
- const ArithVarVec& vec = (*i).second;
- for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
- ArithVar curr = *viter;
- if(inside == m.isMember(curr)){
- return curr;
- }
- }
- }
- return ARITHVAR_SENTINEL;
-}
-
-SimplexDecisionProcedure::sgn_table::const_iterator SimplexDecisionProcedure::find_sgns(const sgn_table& sgns, ArithVar col, int sgn){
- pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn));
- return sgns.find(p);
-}
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- * by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- * These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <unordered_map>
-
-#include "options/arith_options.h"
-#include "smt/env_obj.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/partial_model.h"
-#include "util/dense_map.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class ErrorSet;
-class LinearEqualityModule;
-class Tableau;
-
-class SimplexDecisionProcedure : protected EnvObj
-{
- protected:
- typedef std::vector< std::pair<ArithVar, int> > AVIntPairVec;
-
- /** Pivot count of the current round of pivoting. */
- uint32_t d_pivots;
-
- /** The set of variables that are in conflict in this round. */
- DenseSet d_conflictVariables;
-
- /** The rule to use for heuristic selection mode. */
- options::ErrorSelectionRule d_heuristicRule;
-
- /** Linear equality module. */
- LinearEqualityModule& d_linEq;
-
- /**
- * Manages information about the assignment and upper and lower bounds on
- * variables.
- * Partial model matches that in LinearEqualityModule.
- */
- ArithVariables& d_variables;
-
- /**
- * Stores the linear equalities used by Simplex.
- * Tableau from the LinearEquality module.
- */
- Tableau& d_tableau;
-
- /** Contains a superset of the basic variables in violation of their bounds. */
- ErrorSet& d_errorSet;
-
- /** Number of variables in the system. This is used for tuning heuristics. */
- ArithVar d_numVariables;
-
- /** This is the call back channel for Simplex to report conflicts. */
- RaiseConflict d_conflictChannel;
-
- /** This is the call back channel for Simplex to report conflicts. */
- FarkasConflictBuilder* d_conflictBuilder;
-
- /** Used for requesting d_opt, bound and error variables for primal.*/
- TempVarMalloc d_arithVarMalloc;
-
- /** The size of the error set. */
- uint32_t d_errorSize;
-
- /** A local copy of 0. */
- const Rational d_zero;
-
- /** A local copy of 1. */
- const Rational d_posOne;
-
- /** A local copy of -1. */
- const Rational d_negOne;
-
- /**
- * Locally cached value of arithStandardCheckVarOrderPivots option. It is
- * cached here to allow for single runs with a different (lower) limit.
- */
- int64_t d_varOrderPivotLimit = -1;
-
- ArithVar constructInfeasiblityFunction(TimerStat& timer);
- ArithVar constructInfeasiblityFunction(TimerStat& timer, ArithVar e);
- ArithVar constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set);
-
- void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf);
- void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges);
- void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
- void removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e);
- void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped);
-
-public:
- SimplexDecisionProcedure(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc);
- virtual ~SimplexDecisionProcedure();
-
- /**
- * Tries to update the assignments of variables such that all of the
- * assignments are consistent with their bounds.
- * This is done by a simplex search through the possible bases of the tableau.
- *
- * If all of the variables can be made consistent with their bounds
- * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
- * was reported on the conflictCallback passed to the Module.
- *
- * Tableau pivoting is performed so variables may switch from being basic to
- * nonbasic and vice versa.
- *
- * Corresponds to the "check()" procedure in [Cav06].
- */
- virtual Result::Status findModel(bool exactResult) = 0;
-
- void increaseMax() { d_numVariables++; }
-
- uint32_t getPivots() const { return d_pivots; }
-
- /** Set the variable ordering pivot limit */
- void setVarOrderPivotLimit(int64_t value) { d_varOrderPivotLimit = value; }
-
-protected:
- /** Reports a conflict to on the output channel. */
- void reportConflict(ArithVar basic);
-
- /**
- * Checks a basic variable, b, to see if it is in conflict.
- * If a conflict is discovered a node summarizing the conflict is returned.
- * Otherwise, Node::null() is returned.
- */
- 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 on the conflict channel.
- */
- ConstraintCP generateConflictForBasic(ArithVar basic) const;
-
- /** Gets a fresh variable from TheoryArith. */
- ArithVar requestVariable() { return d_arithVarMalloc.request(); }
-
- /** Releases a requested variable from TheoryArith.*/
- void releaseVariable(ArithVar v) { d_arithVarMalloc.release(v); }
-
- /** Post condition: !d_queue.moreSignals() */
- bool standardProcessSignals(TimerStat& timer, IntStat& conflictStat);
-
- struct ArithVarIntPairHashFunc
- {
- size_t operator()(const std::pair<ArithVar, int>& p) const
- {
- size_t h1 = std::hash<ArithVar>()(p.first);
- size_t h2 = std::hash<int>()(p.second);
- return h1 + 3389 * h2;
- }
- };
-
- typedef std::unordered_map< std::pair<ArithVar, int>, ArithVarVec, ArithVarIntPairHashFunc> sgn_table;
-
- static inline int determinizeSgn(int sgn){
- return sgn < 0 ? -1 : (sgn == 0 ? 0 : 1);
- }
-
- void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
- void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
- ArithVar find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside);
-
- sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn);
-
-}; /* class SimplexDecisionProcedure */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andres Noetzli, Mathias Preiner
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This implements the UpdateInfo.
- */
-
-#include "theory/arith/simplex_update.h"
-
-#include "theory/arith/constraint.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/*
- * Generates a string representation of std::optional and inserts it into a
- * stream.
- *
- * Note: We define this function here in the cvc5::internal::theory::arith namespace,
- * because it would otherwise not be found for std::optional<int>. This is due
- * to the argument-dependent lookup rules.
- *
- * @param out The stream
- * @param m The value
- * @return The stream
- */
-std::ostream& operator<<(std::ostream& out, const std::optional<int>& m)
-{
- return cvc5::internal::operator<<(out, m);
-}
-
-UpdateInfo::UpdateInfo():
- d_nonbasic(ARITHVAR_SENTINEL),
- d_nonbasicDirection(0),
- d_nonbasicDelta(),
- d_foundConflict(false),
- d_errorsChange(),
- d_focusDirection(),
- d_tableauCoefficient(),
- d_limiting(NullConstraint),
- d_witness(AntiProductive)
-{}
-
-UpdateInfo::UpdateInfo(ArithVar nb, int dir):
- d_nonbasic(nb),
- d_nonbasicDirection(dir),
- d_nonbasicDelta(),
- d_foundConflict(false),
- d_errorsChange(),
- d_focusDirection(),
- d_tableauCoefficient(),
- d_limiting(NullConstraint),
- d_witness(AntiProductive)
-{
- Assert(dir == 1 || dir == -1);
-}
-
-UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c):
- d_nonbasic(nb),
- d_nonbasicDirection(delta.sgn()),
- d_nonbasicDelta(delta),
- d_foundConflict(true),
- d_errorsChange(),
- d_focusDirection(),
- d_tableauCoefficient(&r),
- d_limiting(c),
- d_witness(ConflictFound)
-{
- Assert(conflict);
-}
-
-UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){
- return UpdateInfo(true, nb, delta, r, lim);
-}
-
-void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){
- d_limiting = NullConstraint;
- d_nonbasicDelta = delta;
- d_errorsChange = ec;
- d_focusDirection = f;
- d_tableauCoefficient.reset();
- updateWitness();
- Assert(unbounded());
- Assert(improvement(d_witness));
- Assert(!describesPivot());
- Assert(debugSgnAgreement());
-}
-void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){
- d_limiting = c;
- d_nonbasicDelta = delta;
- d_errorsChange.reset();
- d_focusDirection = 1;
- d_tableauCoefficient.reset();
- updateWitness();
- Assert(!describesPivot());
- Assert(improvement(d_witness));
- Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){
- d_limiting = c;
- d_nonbasicDelta = delta;
- d_errorsChange.reset();
- d_focusDirection.reset();
- updateWitness();
- Assert(describesPivot());
- Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){
- d_limiting = c;
- d_nonbasicDelta = delta;
- d_errorsChange = ec;
- d_focusDirection.reset();
- d_tableauCoefficient = &r;
- updateWitness();
- Assert(describesPivot());
- Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){
- d_limiting = c;
- d_nonbasicDelta = delta;
- d_errorsChange = ec;
- d_focusDirection = fd;
- d_tableauCoefficient.reset();
- updateWitness();
- Assert(describesPivot() || improvement(d_witness));
- Assert(debugSgnAgreement());
-}
-
-void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){
- d_limiting = c;
- d_nonbasicDelta = delta;
- d_errorsChange = ec;
- d_focusDirection = fd;
- d_tableauCoefficient = &r;
- updateWitness();
- Assert(describesPivot() || improvement(d_witness));
- Assert(debugSgnAgreement());
-}
-
-bool UpdateInfo::describesPivot() const {
- return !unbounded() && d_nonbasic != d_limiting->getVariable();
-}
-
-void UpdateInfo::output(std::ostream& out) const{
- out << "{UpdateInfo"
- << ", nb = " << d_nonbasic
- << ", dir = " << d_nonbasicDirection
- << ", delta = " << d_nonbasicDelta
- << ", conflict = " << d_foundConflict
- << ", errorChange = " << d_errorsChange
- << ", focusDir = " << d_focusDirection
- << ", witness = " << d_witness
- << ", limiting = " << d_limiting
- << "}";
-}
-
-ArithVar UpdateInfo::leaving() const{
- Assert(describesPivot());
-
- return d_limiting->getVariable();
-}
-
-std::ostream& operator<<(std::ostream& out, const UpdateInfo& up){
- up.output(out);
- return out;
-}
-
-
-std::ostream& operator<<(std::ostream& out, WitnessImprovement w){
- switch(w){
- case ConflictFound:
- out << "ConflictFound"; break;
- case ErrorDropped:
- out << "ErrorDropped"; break;
- case FocusImproved:
- out << "FocusImproved"; break;
- case FocusShrank:
- out << "FocusShrank"; break;
- case Degenerate:
- out << "Degenerate"; break;
- case BlandsDegenerate:
- out << "BlandsDegenerate"; break;
- case HeuristicDegenerate:
- out << "HeuristicDegenerate"; break;
- case AntiProductive:
- out << "AntiProductive"; break;
- }
- return out;
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andres Noetzli, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This provides a class for summarizing pivot proposals.
- *
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This maintains the relationship needed by the SimplexDecisionProcedure.
- *
- * In the language of Simplex for DPLL(T), this provides:
- * - update()
- * - pivotAndUpdate()
- *
- * This class also provides utility functions that require
- * using both the Tableau and PartialModel.
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <optional>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/constraint_forward.h"
-#include "theory/arith/delta_rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-enum WitnessImprovement {
- ConflictFound = 0,
- ErrorDropped = 1,
- FocusImproved = 2,
- FocusShrank = 3,
- Degenerate = 4,
- BlandsDegenerate = 5,
- HeuristicDegenerate = 6,
- AntiProductive = 7
-};
-
-inline bool strongImprovement(WitnessImprovement w){
- return w <= FocusImproved;
-}
-
-inline bool improvement(WitnessImprovement w){
- return w <= FocusShrank;
-}
-
-inline bool degenerate(WitnessImprovement w){
- switch(w){
- case Degenerate:
- case BlandsDegenerate:
- case HeuristicDegenerate:
- return true;
- default:
- return false;
- }
-}
-
-std::ostream& operator<<(std::ostream& out, WitnessImprovement w);
-
-/**
- * This class summarizes both potential:
- * - pivot-and-update operations or
- * - a pure update operation.
- * This stores enough information for the various algorithms hat consider these operations.
- * These require slightly different pieces of information at different points
- * so they are a bit verbose and paranoid.
- */
-class UpdateInfo {
-private:
-
- /**
- * The nonbasic variables under consideration.
- * This is either the entering variable on a pivot and update
- * or the variable being updated.
- * This can only be set in the constructor or assignment.
- *
- * If this uninitialized, then this is ARITHVAR_SENTINEL.
- */
- ArithVar d_nonbasic;
-
- /**
- * The sgn of the "intended" derivative (delta) of the update to d_nonbasic.
- * This is either 1, -1, or 0.
- * It is "intended" as the delta is always allowed to be 0.
- * (See debugSgnAgreement().)
- *
- * If this uninitialized, then this is 0.
- * If this is initialized, then it is -1 or 1.
- *
- * This can only be set in the constructor or assignment.
- */
- int d_nonbasicDirection;
-
- /**
- * The change in the assignment of d_nonbasic.
- * This is changed via the updateProposal(...) methods.
- * The value needs to satisfy debugSgnAgreement() or it is in conflict.
- */
- std::optional<DeltaRational> d_nonbasicDelta;
-
- /**
- * This is true if the pivot-and-update is *known* to cause a conflict.
- * This can only be true if it was constructed through the static conflict(...) method.
- */
- bool d_foundConflict;
-
- /** This is the change in the size of the error set. */
- std::optional<int> d_errorsChange;
-
- /** This is the sgn of the change in the value of the focus set.*/
- std::optional<int> d_focusDirection;
-
- /** This is the sgn of the change in the value of the focus set.*/
- std::optional<DeltaRational> d_focusChange;
-
- /** This is the coefficient in the tableau for the entry.*/
- std::optional<const Rational*> d_tableauCoefficient;
-
- /**
- * This is the constraint that nonbasic is basic is updating s.t. its variable is against it.
- * This has 3 different possibilities:
- * - Unbounded : then this is NullConstraint and unbounded() is true.
- * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic.
- * - Update: then this is not NullConstraint and the variable is d_nonbasic.
- */
- ConstraintP d_limiting;
-
- WitnessImprovement d_witness;
-
- /**
- * This returns true if
- * d_nonbasicDelta is zero() or its sgn() must agree with d_nonbasicDirection.
- */
- bool debugSgnAgreement() const {
- int deltaSgn = d_nonbasicDelta.value().sgn();
- return deltaSgn == 0 || deltaSgn == d_nonbasicDirection;
- }
-
- /** This private constructor allows for setting conflict to true. */
- UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim);
-
-public:
-
- /** This constructs an uninitialized UpdateInfo. */
- UpdateInfo();
-
- /**
- * This constructs an initialized UpdateInfo.
- * dir must be 1 or -1.
- */
- UpdateInfo(ArithVar nb, int dir);
-
- /**
- * This updates the nonBasicDelta to d and limiting to NullConstraint.
- * This describes an unbounded() update.
- */
- void updateUnbounded(const DeltaRational& d, int ec, int f);
-
-
- void updatePureFocus(const DeltaRational& d, ConstraintP c);
- //void updatePureError(const DeltaRational& d, Constraint c, int e);
- //void updatePure(const DeltaRational& d, Constraint c, int e, int f);
-
- /**
- * This updates the nonBasicDelta to d and limiting to c.
- * This clears errorChange() and focusDir().
- */
- void updatePivot(const DeltaRational& d, const Rational& r, 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, 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, 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, ConstraintP lim);
-
- inline ArithVar nonbasic() const { return d_nonbasic; }
- inline bool uninitialized() const {
- return d_nonbasic == ARITHVAR_SENTINEL;
- }
-
- /**
- * There is no limiting value to the improvement of the focus.
- * If this is true, this never describes an update.
- */
- inline bool unbounded() const {
- return d_limiting == NullConstraint;
- }
-
- /**
- * The update either describes a pivotAndUpdate operation
- * or it describes just an update.
- */
- bool describesPivot() const;
-
- /** Returns the . describesPivot() must be true. */
- ArithVar leaving() const;
-
- /**
- * Returns true if this is *known* to find a conflict.
- * If true, this must have been made through the static conflict(...) function.
- */
- bool foundConflict() const { return d_foundConflict; }
-
- /** Returns the direction nonbasic is supposed to move. */
- inline int nonbasicDirection() const{ return d_nonbasicDirection; }
-
- /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
- inline int errorsChange() const { return d_errorsChange.value(); }
-
- /**
- * If errorsChange has been set, return errorsChange().
- * Otherwise, return def.
- */
- inline int errorsChangeSafe(int def) const {
- if (d_errorsChange)
- {
- return d_errorsChange.value();
- }
- else
- {
- return def;
- }
- }
-
- /** Sets the errorChange. */
- void setErrorsChange(int ec){
- d_errorsChange = ec;
- updateWitness();
- }
-
-
- /** Requires errorsChange to be set through setErrorsChange or updateProposal. */
- inline int focusDirection() const { return d_focusDirection.value(); }
-
- /** Sets the focusDirection. */
- void setFocusDirection(int fd){
- Assert(-1 <= fd && fd <= 1);
- d_focusDirection = fd;
- updateWitness();
- }
-
- /**
- * nonbasicDirection must be the same as the sign for the focus function's
- * coefficient for this to be safe.
- * The burden for this being safe is on the user!
- */
- void determineFocusDirection(){
- const int deltaSgn = d_nonbasicDelta.value().sgn();
- setFocusDirection(deltaSgn * d_nonbasicDirection);
- }
-
- /** Requires nonbasicDelta to be set through updateProposal(...). */
- const DeltaRational& nonbasicDelta() const { return d_nonbasicDelta.value(); }
- const Rational& getCoefficient() const {
- Assert(describesPivot());
- Assert(d_tableauCoefficient.value() != NULL);
- return *(d_tableauCoefficient.value());
- }
- int basicDirection() const {
- return nonbasicDirection() * (getCoefficient().sgn());
- }
-
- /** Returns the limiting constraint. */
- inline ConstraintP limiting() const {
- return d_limiting;
- }
-
- WitnessImprovement getWitness(bool useBlands = false) const{
- Assert(d_witness == computeWitness());
-
- if(d_witness == Degenerate){
- if(useBlands){
- return BlandsDegenerate;
- }else{
- return HeuristicDegenerate;
- }
- }else{
- return d_witness;
- }
- }
-
- const DeltaRational& focusChange() const { return d_focusChange.value(); }
- void setFocusChange(const DeltaRational& fc) {
- d_focusChange = fc;
- }
-
- /** Outputs the UpdateInfo into out. */
- void output(std::ostream& out) const;
-
-private:
- void updateWitness() {
- d_witness = computeWitness();
- Assert(describesPivot() || improvement(d_witness));
- }
-
- /**
- * Determines the appropriate WitnessImprovement for the update.
- * useBlands breaks ties for degenerate pivots.
- *
- * This is safe if:
- * - d_foundConflict is true, or
- * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange < 0, or
- * - d_foundConflict is false and d_errorsChange has been set and d_errorsChange >= 0 and d_focusDirection has been set.
- */
- WitnessImprovement computeWitness() const {
- if(d_foundConflict){
- return ConflictFound;
- }
- else if (d_errorsChange && d_errorsChange.value() < 0)
- {
- return ErrorDropped;
- }
- else if (d_errorsChange.value_or(0) == 0)
- {
- if (d_focusDirection)
- {
- if (*d_focusDirection > 0)
- {
- return FocusImproved;
- }
- else if (*d_focusDirection == 0)
- {
- return Degenerate;
- }
- }
- }
- return AntiProductive;
- }
-
-};
-
-std::ostream& operator<<(std::ostream& out, const UpdateInfo& up);
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Andrew Reynolds
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- */
-#include "theory/arith/soi_simplex.h"
-
-#include <algorithm>
-
-#include "base/output.h"
-#include "options/arith_options.h"
-#include "smt/smt_statistics_registry.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/tableau.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc)
- : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
- d_soiVar(ARITHVAR_SENTINEL),
- d_pivotBudget(0),
- d_prevWitnessImprovement(AntiProductive),
- d_witnessImprovementInARow(0),
- d_sgnDisagreements(),
- d_statistics("theory::arith::SOI", d_pivots)
-{ }
-
-SumOfInfeasibilitiesSPD::Statistics::Statistics(const std::string& name,
- uint32_t& pivots)
- : d_initialSignalsTime(
- smtStatisticsRegistry().registerTimer(name + "initialProcessTime")),
- d_initialConflicts(
- smtStatisticsRegistry().registerInt(name + "UpdateConflicts")),
- d_soiFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
- d_soiFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
- d_soiMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
- d_soiConflicts(
- smtStatisticsRegistry().registerInt(name + "ConfMin::num")),
- d_hasToBeMinimal(
- smtStatisticsRegistry().registerInt(name + "HasToBeMin")),
- d_maybeNotMinimal(
- smtStatisticsRegistry().registerInt(name + "MaybeNotMin")),
- d_soiTimer(smtStatisticsRegistry().registerTimer(name + "Time")),
- d_soiFocusConstructionTimer(
- smtStatisticsRegistry().registerTimer(name + "Construction")),
- d_soiConflictMinimization(smtStatisticsRegistry().registerTimer(
- name + "Conflict::Minimization")),
- d_selectUpdateForSOI(
- smtStatisticsRegistry().registerTimer(name + "selectSOI")),
- d_finalCheckPivotCounter(
- smtStatisticsRegistry().registerReference<uint32_t>(
- name + "lastPivots", pivots))
-{
-}
-
-Result::Status SumOfInfeasibilitiesSPD::findModel(bool exactResult)
-{
- Assert(d_conflictVariables.empty());
- Assert(d_sgnDisagreements.empty());
-
- d_pivots = 0;
-
- if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
- Trace("soi::findModel") << "soiFindModel() trivial" << endl;
- Assert(d_conflictVariables.empty());
- return Result::SAT;
- }
-
- // We need to reduce this because of
- d_errorSet.reduceToSignals();
-
- // We must start tracking NOW
- d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
-
- if(initialProcessSignals()){
- d_conflictVariables.purge();
- Trace("soi::findModel") << "fcFindModel() early conflict" << endl;
- Assert(d_conflictVariables.empty());
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Trace("soi::findModel") << "fcFindModel() fixed itself" << endl;
- Assert(!d_errorSet.moreSignals());
- Assert(d_conflictVariables.empty());
- return Result::SAT;
- }
-
- Trace("soi::findModel") << "fcFindModel() start non-trivial" << endl;
-
- exactResult |= d_varOrderPivotLimit < 0;
-
- d_prevWitnessImprovement = HeuristicDegenerate;
- d_witnessImprovementInARow = 0;
-
- Result::Status result = Result::UNKNOWN;
-
- if (result == Result::UNKNOWN)
- {
- if(exactResult){
- d_pivotBudget = -1;
- }else{
- d_pivotBudget = d_varOrderPivotLimit;
- }
-
- result = sumOfInfeasibilities();
-
- if(result == Result::UNSAT){
- ++(d_statistics.d_soiFoundUnsat);
- }else if(d_errorSet.errorEmpty()){
- ++(d_statistics.d_soiFoundSat);
- }else{
- ++(d_statistics.d_soiMissed);
- }
- }
-
- Assert(!d_errorSet.moreSignals());
- if (result == Result::UNKNOWN && d_errorSet.errorEmpty())
- {
- result = Result::SAT;
- }
-
- // ensure that the conflict variable is still in the queue.
- d_conflictVariables.purge();
-
- Trace("soi::findModel") << "end findModel() " << result << endl;
-
- Assert(d_conflictVariables.empty());
- return result;
-}
-
-
-void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
- if(d_pivotBudget > 0) {
- --d_pivotBudget;
- }
- Assert(w != AntiProductive);
-
- if(w == d_prevWitnessImprovement){
- ++d_witnessImprovementInARow;
- if(d_witnessImprovementInARow == 0){
- --d_witnessImprovementInARow;
- }
- }else{
- if(w != BlandsDegenerate){
- d_witnessImprovementInARow = 1;
- }
- d_prevWitnessImprovement = w;
- }
- if(strongImprovement(w)){
- d_leavingCountSinceImprovement.purge();
- }
-
- Trace("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl;
-}
-
-uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
- switch(d_prevWitnessImprovement){
- case ConflictFound:
- case ErrorDropped:
- case FocusImproved:
- return 0;
- case HeuristicDegenerate:
- case BlandsDegenerate:
- return d_witnessImprovementInARow;
- // Degenerate is unreachable for its own reasons
- case Degenerate:
- case FocusShrank:
- case AntiProductive:
- Unreachable();
- return -1;
- }
- Unreachable();
-}
-
-void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
- uint32_t newErrorSize = d_errorSet.errorSize();
- adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
- d_errorSize = newErrorSize;
-}
-
-
-UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
- UpdateInfo selected;
-
- Trace("soi::selectPrimalUpdate")
- << "selectPrimalUpdate " << endl
- << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar) << " "
- << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
-
- typedef std::vector<Cand> CandVector;
- CandVector candidates;
-
- for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
- const Tableau::Entry& e = *ri;
- ArithVar curr = e.getColVar();
- if(curr == d_soiVar){ continue; }
-
- int sgn = e.getCoefficient().sgn();
- bool candidate =
- (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
- (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
-
- Trace("soi::selectPrimalUpdate")
- << "storing " << d_soiVar
- << " " << curr
- << " " << candidate
- << " " << e.getCoefficient()
- << " " << sgn << endl;
-
- if(candidate) {
- candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
- }
- }
-
- CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
- CandVector::iterator i = candidates.begin();
- CandVector::iterator end = candidates.end();
- std::make_heap(i, end, colCmp);
-
- // For the first 3 pivots take the best
- // After that, once an improvement is found on look at a
- // small number of pivots after finding an improvement
- // the longer the search to more willing we are to look at more candidates
- int maxCandidatesAfterImprove =
- (d_pivots <= 2) ? std::numeric_limits<int>::max() : d_pivots/5;
-
- int candidatesAfterFocusImprove = 0;
- while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
- std::pop_heap(i, end, colCmp);
- --end;
- Cand& cand = (*end);
- ArithVar curr = cand.d_nb;
- const Rational& coeff = *cand.d_coeff;
-
- LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
- UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
-
- Trace("soi::selectPrimalUpdate")
- << "selected " << selected << endl
- << "currProp " << currProposal << endl
- << "coeff " << coeff << endl;
-
- Assert(!currProposal.uninitialized());
-
- if(candidatesAfterFocusImprove > 0){
- candidatesAfterFocusImprove++;
- }
-
- if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
- selected = currProposal;
- WitnessImprovement w = selected.getWitness(false);
- Trace("soi::selectPrimalUpdate") << "selected " << w << endl;
- //setPenalty(curr, w);
- if(improvement(w)){
- bool exitEarly;
- switch(w){
- case ConflictFound: exitEarly = true; break;
- case FocusImproved:
- candidatesAfterFocusImprove = 1;
- exitEarly = false;
- break;
- default:
- exitEarly = false; break;
- }
- if(exitEarly){ break; }
- }
- }else{
- Trace("soi::selectPrimalUpdate") << "dropped "<< endl;
- }
-
- }
- return selected;
-}
-
-bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
- if(inf.getWitness(useBlands) == w){
- switch(w){
- case ConflictFound: return inf.foundConflict();
- case ErrorDropped: return inf.errorsChange() < 0;
- case FocusImproved: return inf.focusDirection() > 0;
- case FocusShrank: return false; // This is not a valid output
- case Degenerate: return false; // This is not a valid output
- case BlandsDegenerate: return useBlands;
- case HeuristicDegenerate: return !useBlands;
- case AntiProductive: return false;
- }
- }
- return false;
-}
-
-
-void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
- Trace("updateAndSignal") << "updated basic " << updated;
- Trace("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
- Trace("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
- int dir = !d_variables.assignmentIsConsistent(updated) ?
- d_errorSet.getSgn(updated) : 0;
- Trace("updateAndSignal") << " dir " << dir;
- Trace("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
-}
-
-
-void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
- ArithVar nonbasic = selected.nonbasic();
-
- Trace("updateAndSignal") << "updateAndSignal " << selected << endl;
-
- if(selected.describesPivot()){
- ConstraintP limiting = selected.limiting();
- ArithVar basic = limiting->getVariable();
- Assert(d_linEq.basicIsTracked(basic));
- d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
- }else{
- Assert(!selected.unbounded() || selected.errorsChange() < 0);
-
- DeltaRational newAssignment =
- d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
-
- d_linEq.updateTracked(nonbasic, newAssignment);
- }
- d_pivots++;
-
- increaseLeavingCount(nonbasic);
-
- vector< pair<ArithVar, int> > focusChanges;
- while(d_errorSet.moreSignals()){
- ArithVar updated = d_errorSet.topSignal();
- int prevFocusSgn = d_errorSet.popSignal();
-
- if(d_tableau.isBasic(updated)){
- Assert(!d_variables.assignmentIsConsistent(updated)
- == d_errorSet.inError(updated));
- if(TraceIsOn("updateAndSignal")){debugPrintSignal(updated);}
- if(!d_variables.assignmentIsConsistent(updated)){
- if(checkBasicForConflict(updated)){
- reportConflict(updated);
- //Assert(debugUpdatedBasic(selected, updated));
- }
- }
- }else{
- Trace("updateAndSignal") << "updated nonbasic " << updated << endl;
- }
- int currFocusSgn = d_errorSet.focusSgn(updated);
- if(currFocusSgn != prevFocusSgn){
- int change = currFocusSgn - prevFocusSgn;
- focusChanges.push_back(make_pair(updated, change));
- }
- }
-
- if(TraceIsOn("error")){ d_errorSet.debugPrint(Trace("error")); }
-
- //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
-
- adjustFocusAndError(selected, focusChanges);
-}
-
-void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){
- Assert(!d_qeInSoi.empty());
- for(uint32_t i = begin; i != end; ++i){
- ArithVar v = d_qeConflict[i];
- addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
- d_qeInSoi.add(v);
- }
-}
-
-void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){
- for(uint32_t i = begin; i != end; ++i){
- ArithVar v = d_qeConflict[i];
- removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
- d_qeInSoi.remove(v);
- }
- Assert(!d_qeInSoi.empty());
-}
-
-void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){
- for(uint32_t i = 0; i < N; ++i){
- std::swap(d_qeConflict[r+i], d_qeConflict[s+i]);
- }
-}
-
-/**
- * Region notation:
- * A region is either
- * - A single element X@i with the name X at the position i
- * - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive
- * - A concatenation of regions R1 and R2, R1;R2
- *
- * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd)
- * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal
- * subset of U, Delta, s.t. C \cup Delta is in conflict.
- *
- * Pre:
- * [0, uEnd) is a set and is in conflict.
- * uEnd <= assumptions.size()
- * [0, cEnd) is in d_inSoi.
- *
- * Invariants: [0,cEnd) is never modified
- *
- * Post:
- * [0, cEnd); [cEnd, deltaEnd) is in conflict
- * [0, deltaEnd) is a set
- * [0, deltaEnd) is in d_inSoi
- */
-uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){
- Assert(cEnd <= uEnd);
- Assert(d_qeInUAndNotInSoi.empty());
- Assert(d_qeGreedyOrder.empty());
-
- const Tableau::Entry* spoiler = NULL;
-
- if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){
- // already in conflict
- return cEnd;
- }
-
- Assert(cEnd < uEnd);
-
- // Phase 1 : Construct the conflict greedily
-
- for(uint32_t i = cEnd; i < uEnd; ++i){
- d_qeInUAndNotInSoi.add(d_qeConflict[i]);
- }
- if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty
- ArithVar first = d_qeConflict[cEnd];
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first);
- d_qeInSoi.add(first);
- d_qeInUAndNotInSoi.remove(first);
- d_qeGreedyOrder.push_back(first);
- }
- while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
- Assert(!d_qeInUAndNotInSoi.empty());
-
- ArithVar nb = spoiler->getColVar();
- int oppositeSgn = -(spoiler->getCoefficient().sgn());
- Assert(oppositeSgn != 0);
-
- ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true);
- Assert(basicWithOp != ARITHVAR_SENTINEL);
-
- addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
- d_qeInSoi.add(basicWithOp);
- d_qeInUAndNotInSoi.remove(basicWithOp);
- d_qeGreedyOrder.push_back(basicWithOp);
- }
- Assert(spoiler == NULL);
-
- // Compact the set u
- uint32_t newEnd = cEnd + d_qeGreedyOrder.size();
- std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd);
-
- d_qeInUAndNotInSoi.purge();
- d_qeGreedyOrder.clear();
-
- // Phase 2 : Recursively determine the minimal set of rows
-
- uint32_t xPos = cEnd;
- std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]);
- uint32_t uBegin = xPos + 1;
- uint32_t split = (newEnd - uBegin)/2 + uBegin;
-
- //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd)
- // [0, newEnd) == d_inSoi
-
- uint32_t compactU2;
- if(split == newEnd){ // U2 is empty
- compactU2 = newEnd;
- }else{
- // Remove U2 from Soi
- qeRemoveRange(split, newEnd);
- // [0, split) == d_inSoi
-
- // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd)
- compactU2 = quickExplainRec(split, newEnd);
- // post:
- // assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2)
- // d_inSoi = [0, compactU2)
- }
- uint32_t deltaSize = compactU2 - split;
- qeSwapRange(deltaSize, uBegin, split);
- uint32_t d2End = uBegin+deltaSize;
- // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2)
- // d_inSoi == [0, compactU2)
-
- uint32_t d1End;
- if(d2End == compactU2){ // U1 is empty
- d1End = d2End;
- }else{
- qeRemoveRange(d2End, compactU2);
-
- //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2);
- d1End = quickExplainRec(d2End, compactU2);
- //post:
- // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End);
- // d_inSoi = [0, d1End)
- }
- //After both:
- // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End);
-
- Assert(d_qeInUAndNotInSoi.empty());
- Assert(d_qeGreedyOrder.empty());
- return d1End;
-}
-
-void SumOfInfeasibilitiesSPD::quickExplain(){
- Assert(d_qeInSoi.empty());
- Assert(d_qeInUAndNotInSoi.empty());
- Assert(d_qeGreedyOrder.empty());
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- Assert(d_qeSgns.empty());
-
- d_qeConflict.clear();
- d_errorSet.pushFocusInto(d_qeConflict);
-
- //cout << d_qeConflict.size() << " ";
- uint32_t size = d_qeConflict.size();
-
- if(size > 2){
- for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
- ArithVar e = *iter;
- addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e));
- }
- uint32_t end = quickExplainRec(0u, size);
- Assert(end <= d_qeConflict.size());
- Assert(d_soiVar != ARITHVAR_SENTINEL);
- Assert(!d_qeInSoi.empty());
-
- d_qeConflict.resize(end);
- tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
- d_qeInSoi.purge();
- d_qeSgns.clear();
- }
-
- //cout << d_qeConflict.size() << endl;
-
- Assert(d_qeInSoi.empty());
- Assert(d_qeInUAndNotInSoi.empty());
- Assert(d_qeGreedyOrder.empty());
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- Assert(d_qeSgns.empty());
-}
-
-unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- bool success = false;
- if(set.size() >= 2){
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
- success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
-
- tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
- }
- return success ? set.size() : std::numeric_limits<int>::max();
-}
-
-std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
- Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets start" << endl;
-
- std::vector< ArithVarVec > subsets;
- Assert(d_soiVar == ARITHVAR_SENTINEL);
-
- if(d_errorSize <= 2){
- ArithVarVec inError;
- d_errorSet.pushFocusInto(inError);
-
- Assert(debugIsASet(inError));
- subsets.push_back(inError);
- return subsets;
- }
- Assert(d_errorSize > 2);
-
- //sgns_table< <nonbasic,sgn>, [basics] >;
- // Phase 0: Construct the sgns table
- sgn_table sgns;
- DenseSet hasParticipated; //Has participated in a conflict
- for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
- ArithVar e = *iter;
- addRowSgns(sgns, e, d_errorSet.getSgn(e));
-
- Trace("arith::greedyConflictSubsets") << "basic error var: " << e << endl;
- if(TraceIsOn("arith::greedyConflictSubsets")){
- d_tableau.debugPrintIsBasic(e);
- d_tableau.printBasicRow(e, Trace("arith::greedyConflictSubsets"));
- }
- }
-
- // Phase 1: Try to find at least 1 pair for every element
- ArithVarVec tmp;
- tmp.push_back(0);
- tmp.push_back(0);
- for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
- ArithVar e = *iter;
- tmp[0] = e;
-
- int errSgn = d_errorSet.getSgn(e);
- bool decreasing = errSgn < 0;
- const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
- Assert(spoiler != NULL);
- ArithVar nb = spoiler->getColVar();
- int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
-
- sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
- Assert(opposites != sgns.end());
-
- const ArithVarVec& choices = (*opposites).second;
- for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
- ArithVar b = *j;
- if(b < e){ continue; }
- tmp[0] = e;
- tmp[1] = b;
- if(trySet(tmp) == 2){
- Trace("arith::greedyConflictSubsets") << "found a pair " << b << " " << e << endl;
- hasParticipated.softAdd(b);
- hasParticipated.softAdd(e);
- Assert(debugIsASet(tmp));
- subsets.push_back(tmp);
- ++(d_statistics.d_soiConflicts);
- ++(d_statistics.d_hasToBeMinimal);
- }
- }
- }
-
-
- // Phase 2: If there is a variable that has not participated attempt to start a conflict
- ArithVarVec possibleStarts; //List of elements that can be tried for starts.
- d_errorSet.pushFocusInto(possibleStarts);
- while(!possibleStarts.empty()){
- Assert(d_soiVar == ARITHVAR_SENTINEL);
-
- ArithVar v = possibleStarts.back();
- possibleStarts.pop_back();
- if(hasParticipated.isMember(v)){ continue; }
-
- hasParticipated.add(v);
-
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- //d_soiVar's row = \sumofinfeasibilites underConstruction
- ArithVarVec underConstruction;
- underConstruction.push_back(v);
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
-
- Trace("arith::greedyConflictSubsets") << "trying " << v << endl;
-
- const Tableau::Entry* spoiler = NULL;
- while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
- ArithVar nb = spoiler->getColVar();
- int oppositeSgn = -(spoiler->getCoefficient().sgn());
- Assert(oppositeSgn != 0);
-
- Trace("arith::greedyConflictSubsets") << "looking for " << nb << " " << oppositeSgn << endl;
-
- ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
-
- if(basicWithOp == ARITHVAR_SENTINEL){
- Trace("arith::greedyConflictSubsets") << "search did not work for " << nb << endl;
- // greedy construction has failed
- break;
- }else{
- Trace("arith::greedyConflictSubsets") << "found " << basicWithOp << endl;
-
- addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
- hasParticipated.softAdd(basicWithOp);
- underConstruction.push_back(basicWithOp);
- }
- }
- if(spoiler == NULL){
- Trace("arith::greedyConflictSubsets") << "success" << endl;
- //then underConstruction contains a conflicting subset
- Assert(debugIsASet(underConstruction));
- subsets.push_back(underConstruction);
- ++d_statistics.d_soiConflicts;
- if(underConstruction.size() == 3){
- ++d_statistics.d_hasToBeMinimal;
- }else{
- ++d_statistics.d_maybeNotMinimal;
- }
- }else{
- Trace("arith::greedyConflictSubsets") << "failure" << endl;
- }
- tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
- // if(false && spoiler == NULL){
- // ArithVarVec tmp;
- // int smallest = tryAllSubsets(underConstruction, 0, tmp);
- // cout << underConstruction.size() << " " << smallest << endl;
- // Assert(smallest >= underConstruction.size());
- // if(smallest < underConstruction.size()){
- // exit(-1);
- // }
- // }
- }
-
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- Trace("arith::greedyConflictSubsets") << "greedyConflictSubsets done" << endl;
- return subsets;
-}
-
-bool SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
- Assert(!subset.empty());
- Assert(!d_conflictBuilder->underConstruction());
-
- Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) start" << endl;
-
- bool success = false;
-
- for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
- ArithVar e = *iter;
- ConstraintP violated = d_errorSet.getViolated(e);
- Assert(violated != NullConstraint);
-
- int sgn = d_errorSet.getSgn(e);
- const Rational& violatedCoeff = sgn > 0 ? d_negOne : d_posOne;
- Trace("arith::generateSOIConflict") << "basic error var: "
- << "(" << violatedCoeff << ")"
- << " " << violated
- << endl;
-
-
- d_conflictBuilder->addConstraint(violated, violatedCoeff);
- Assert(violated->hasProof());
- if(!success && !violated->negationHasProof()){
- success = true;
- d_conflictBuilder->makeLastConsequent();
- }
- }
-
- if(!success){
- // failure
- d_conflictBuilder->reset();
- } else {
- // pick a violated constraint arbitrarily. any of them may be selected for the conflict
- Assert(d_conflictBuilder->underConstruction());
- Assert(d_conflictBuilder->consequentIsSet());
-
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
- const Tableau::Entry& entry = *i;
- ArithVar v = entry.getColVar();
- if(v == d_soiVar){ continue; }
- const Rational& coeff = entry.getCoefficient();
-
- ConstraintP c = (coeff.sgn() > 0) ?
- d_variables.getUpperBoundConstraint(v) :
- d_variables.getLowerBoundConstraint(v);
-
- Trace("arith::generateSOIConflict") << "non-basic var: "
- << "(" << coeff << ")"
- << " " << c
- << endl;
- d_conflictBuilder->addConstraint(c, coeff);
- }
- ConstraintCP conflicted = d_conflictBuilder->commitConflict();
- d_conflictChannel.raiseConflict(conflicted,
- InferenceId::ARITH_CONF_SOI_SIMPLEX);
- }
-
- tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
- Trace("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) done" << endl;
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- Assert(!d_conflictBuilder->underConstruction());
- return success;
-}
-
-
-WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
- Trace("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() start "
- << ": |E| = " << d_errorSize << endl;
- if(TraceIsOn("arith::SOIConflict")){
- d_errorSet.debugPrint(cout);
- }
- Trace("arith::SOIConflict") << endl;
-
- tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
-
- if (options().arith.soiQuickExplain)
- {
- quickExplain();
- generateSOIConflict(d_qeConflict);
- }
- else
- {
- vector<ArithVarVec> subsets = greedyConflictSubsets();
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- bool anySuccess = false;
- Assert(!subsets.empty());
- for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
- const ArithVarVec& subset = *i;
- Assert(debugIsASet(subset));
- anySuccess = generateSOIConflict(subset) || anySuccess;
- //Node conflict = generateSOIConflict(subset);
- //cout << conflict << endl;
-
- //reportConflict(conf); do not do this. We need a custom explanations!
- //d_conflictChannel(conflict);
- }
- Assert(anySuccess);
- }
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
-
- //reportConflict(conf); do not do this. We need a custom explanations!
- d_conflictVariables.add(d_soiVar);
-
- Trace("arith::SOIConflict")
- << "SumOfInfeasibilitiesSPD::SOIConflict() end" << endl;
- return ConflictFound;
-}
-
-WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
- Assert(d_soiVar != ARITHVAR_SENTINEL);
-
- bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
- LinearEqualityModule::UpdatePreferenceFunction upf;
- if(useBlands) {
- upf = &LinearEqualityModule::preferWitness<false>;
- } else {
- upf = &LinearEqualityModule::preferWitness<true>;
- }
-
- LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
- &LinearEqualityModule::minVarOrder :
- &LinearEqualityModule::minRowLength;
- bpf = &LinearEqualityModule::minVarOrder;
-
- UpdateInfo selected = selectUpdate(upf, bpf);
-
- if(selected.uninitialized()){
- Trace("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
- return SOIConflict();
- }else{
- Assert(!selected.uninitialized());
- WitnessImprovement w = selected.getWitness(false);
- Assert(debugCheckWitness(selected, w, false));
-
- updateAndSignal(selected, w);
- logPivot(w);
- return w;
- }
-}
-
-Result::Status SumOfInfeasibilitiesSPD::sumOfInfeasibilities()
-{
- TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
-
- Assert(d_sgnDisagreements.empty());
- Assert(d_pivotBudget != 0);
- Assert(d_errorSize == d_errorSet.errorSize());
- Assert(d_errorSize > 0);
- Assert(d_conflictVariables.empty());
- Assert(d_soiVar == ARITHVAR_SENTINEL);
-
- //d_scores.purge();
- d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
-
-
- while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){
- Trace("dualLike") << "dualLike" << endl;
-
- Assert(d_errorSet.noSignals());
- // Possible outcomes:
- // - conflict
- // - budget was exhausted
- // - focus went down
- WitnessImprovement w = soiRound();
- Trace("dualLike") << "selectFocusImproving -> " << w << endl;
-
- Assert(d_errorSize == d_errorSet.errorSize());
- }
-
-
- if(d_soiVar != ARITHVAR_SENTINEL){
- tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
- d_soiVar = ARITHVAR_SENTINEL;
- }
-
- Assert(d_soiVar == ARITHVAR_SENTINEL);
- if(!d_conflictVariables.empty()){
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Assert(d_errorSet.noSignals());
- return Result::SAT;
- }else{
- Assert(d_pivotBudget == 0);
- return Result::UNKNOWN;
- }
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * This is an implementation of the Simplex Module for the Simplex for
- * DPLL(T) decision procedure.
- *
- * This implements the Simplex module for the Simpelx for DPLL(T) decision
- * procedure.
- * See the Simplex for DPLL(T) technical report for more background.(citation?)
- * This shares with the theory a Tableau, and a PartialModel that:
- * - satisfies the equalities in the Tableau, and
- * - the assignment for the non-basic variables satisfies their bounds.
- * This is required to either produce a conflict or satisifying PartialModel.
- * Further, we require being told when a basic variable updates its value.
- *
- * During the Simplex search we maintain a queue of variables.
- * The queue is required to contain all of the basic variables that voilate
- * their bounds.
- * As elimination from the queue is more efficient to be done lazily,
- * we do not maintain that the queue of variables needs to be only basic
- * variables or only variables that satisfy their bounds.
- *
- * The simplex procedure roughly follows Alberto's thesis. (citation?)
- * There is one round of selecting using a heuristic pivoting rule.
- * (See PreferenceFunction Documentation for the available options.)
- * The non-basic variable is the one that appears in the fewest pivots.
- * (Bruno says that Leonardo invented this first.)
- * After this, Bland's pivot rule is invoked.
- *
- * During this proccess, we periodically inspect the queue of variables to
- * 1) remove now extraneous extries,
- * 2) detect conflicts that are "waiting" on the queue but may not be detected
- * by the current queue heuristics, and
- * 3) detect multiple conflicts.
- *
- * Conflicts are greedily slackened to use the weakest bounds that still
- * produce the conflict.
- *
- * Extra things tracked atm: (Subject to change at Tim's whims)
- * - A superset of all of the newly pivoted variables.
- * - A queue of additional conflicts that were discovered by Simplex.
- * These are theory valid and are currently turned into lemmas
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/simplex_update.h"
-#include "util/dense_map.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class SumOfInfeasibilitiesSPD : public SimplexDecisionProcedure {
-public:
- SumOfInfeasibilitiesSPD(Env& env,
- LinearEqualityModule& linEq,
- ErrorSet& errors,
- RaiseConflict conflictChannel,
- TempVarMalloc tvmalloc);
-
- Result::Status findModel(bool exactResult) override;
-
- // other error variables are dropping
- WitnessImprovement dualLikeImproveError(ArithVar evar);
- WitnessImprovement primalImproveError(ArithVar evar);
-
-private:
- /** The current sum of infeasibilities variable. */
- ArithVar d_soiVar;
-
- // dual like
- // - found conflict
- // - satisfied error set
- Result::Status sumOfInfeasibilities();
-
- int32_t d_pivotBudget;
-
- WitnessImprovement d_prevWitnessImprovement;
- uint32_t d_witnessImprovementInARow;
-
- uint32_t degeneratePivotsInARow() const;
-
- static constexpr uint32_t s_focusThreshold = 6;
- static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnLeaving = 100;
- static constexpr uint32_t s_maxDegeneratePivotsBeforeBlandsOnEntering = 10;
-
- DenseMap<uint32_t> d_leavingCountSinceImprovement;
- void increaseLeavingCount(ArithVar x){
- if(!d_leavingCountSinceImprovement.isKey(x)){
- d_leavingCountSinceImprovement.set(x,1);
- }else{
- (d_leavingCountSinceImprovement.get(x))++;
- }
- }
- LinearEqualityModule::UpdatePreferenceFunction selectLeavingFunction(ArithVar x){
- bool useBlands = d_leavingCountSinceImprovement.isKey(x) &&
- d_leavingCountSinceImprovement[x] >= s_maxDegeneratePivotsBeforeBlandsOnEntering;
- if(useBlands) {
- return &LinearEqualityModule::preferWitness<false>;
- } else {
- return &LinearEqualityModule::preferWitness<true>;
- }
- }
-
- void debugPrintSignal(ArithVar updated) const;
-
- ArithVarVec d_sgnDisagreements;
-
- void logPivot(WitnessImprovement w);
-
- void updateAndSignal(const UpdateInfo& selected, WitnessImprovement w);
-
- UpdateInfo selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf,
- LinearEqualityModule::VarPreferenceFunction bpf);
-
-
- // UpdateInfo selectUpdateForDualLike(ArithVar basic){
- // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForDualLike);
-
- // LinearEqualityModule::UpdatePreferenceFunction upf =
- // &LinearEqualityModule::preferWitness<true>;
- // LinearEqualityModule::VarPreferenceFunction bpf =
- // &LinearEqualityModule::minVarOrder;
- // return selectPrimalUpdate(basic, upf, bpf);
- // }
-
- // UpdateInfo selectUpdateForPrimal(ArithVar basic, bool useBlands){
- // TimerStat::CodeTimer codeTimer(d_statistics.d_selectUpdateForPrimal);
-
- // LinearEqualityModule::UpdatePreferenceFunction upf = useBlands ?
- // &LinearEqualityModule::preferWitness<false>:
- // &LinearEqualityModule::preferWitness<true>;
-
- // LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
- // &LinearEqualityModule::minVarOrder :
- // &LinearEqualityModule::minRowLength;
- // bpf = &LinearEqualityModule::minVarOrder;
-
- // return selectPrimalUpdate(basic, upf, bpf);
- // }
- // WitnessImprovement selectFocusImproving() ;
- WitnessImprovement soiRound();
- WitnessImprovement SOIConflict();
- std::vector< ArithVarVec > greedyConflictSubsets();
- bool generateSOIConflict(const ArithVarVec& subset);
-
- // WitnessImprovement focusUsingSignDisagreements(ArithVar basic);
- // WitnessImprovement focusDownToLastHalf();
- // WitnessImprovement adjustFocusShrank(const ArithVarVec& drop);
- // WitnessImprovement focusDownToJust(ArithVar v);
-
-
- void adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges);
-
- /**
- * This is the main simplex for DPLL(T) loop.
- * It runs for at most maxIterations.
- *
- * Returns true iff it has found a conflict.
- * d_conflictVariable will be set and the conflict for this row is reported.
- */
- bool searchForFeasibleSolution(uint32_t maxIterations);
-
- bool initialProcessSignals(){
- TimerStat &timer = d_statistics.d_initialSignalsTime;
- IntStat& conflictStat = d_statistics.d_initialConflicts;
- return standardProcessSignals(timer, conflictStat);
- }
-
- void quickExplain();
- DenseSet d_qeInSoi;
- DenseSet d_qeInUAndNotInSoi;
- ArithVarVec d_qeConflict;
- ArithVarVec d_qeGreedyOrder;
- sgn_table d_qeSgns;
-
- uint32_t quickExplainRec(uint32_t cEnd, uint32_t uEnd);
- void qeAddRange(uint32_t begin, uint32_t end);
- void qeRemoveRange(uint32_t begin, uint32_t end);
- void qeSwapRange(uint32_t N, uint32_t r, uint32_t s);
-
- unsigned trySet(const ArithVarVec& set);
- unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp);
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- TimerStat d_initialSignalsTime;
- IntStat d_initialConflicts;
-
- IntStat d_soiFoundUnsat;
- IntStat d_soiFoundSat;
- IntStat d_soiMissed;
-
- IntStat d_soiConflicts;
- IntStat d_hasToBeMinimal;
- IntStat d_maybeNotMinimal;
-
- TimerStat d_soiTimer;
- TimerStat d_soiFocusConstructionTimer;
- TimerStat d_soiConflictMinimization;
- TimerStat d_selectUpdateForSOI;
-
- ReferenceStat<uint32_t> d_finalCheckPivotCounter;
-
- Statistics(const std::string& name, uint32_t& pivots);
- } d_statistics;
-};/* class FCSimplexDecisionProcedure */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/tableau.h"
-
-using namespace std;
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-
-void Tableau::pivot(ArithVar oldBasic, ArithVar newBasic, CoefficientChangeCallback& cb){
- Assert(isBasic(oldBasic));
- Assert(!isBasic(newBasic));
- Assert(d_mergeBuffer.empty());
-
- Trace("tableau") << "Tableau::pivot(" << oldBasic <<", " << newBasic <<")" << endl;
-
- RowIndex ridx = basicToRowIndex(oldBasic);
-
- rowPivot(oldBasic, newBasic, cb);
- Assert(ridx == basicToRowIndex(newBasic));
-
- loadRowIntoBuffer(ridx);
-
- ColIterator colIter = colIterator(newBasic);
- while(!colIter.atEnd()){
- EntryID id = colIter.getID();
- Entry& entry = d_entries.get(id);
-
- ++colIter; //needs to be incremented before the variable is removed
- if(entry.getRowIndex() == ridx){ continue; }
-
- RowIndex to = entry.getRowIndex();
- Rational coeff = entry.getCoefficient();
- if(cb.canUseRow(to)){
- rowPlusBufferTimesConstant(to, coeff, cb);
- }else{
- rowPlusBufferTimesConstant(to, coeff);
- }
- }
- clearBuffer();
-
- //Clear the column for used for this variable
-
- Assert(d_mergeBuffer.empty());
- Assert(!isBasic(oldBasic));
- Assert(isBasic(newBasic));
- Assert(getColLength(newBasic) == 1);
-}
-
-/**
- * Changes basic to newbasic (a variable on the row).
- */
-void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb){
- Assert(isBasic(basicOld));
- Assert(!isBasic(basicNew));
-
- RowIndex rid = basicToRowIndex(basicOld);
-
- EntryID newBasicID = findOnRow(rid, basicNew);
-
- Assert(newBasicID != ENTRYID_SENTINEL);
-
- Tableau::Entry& newBasicEntry = d_entries.get(newBasicID);
- const Rational& a_rs = newBasicEntry.getCoefficient();
- int a_rs_sgn = a_rs.sgn();
- Rational negInverseA_rs = -(a_rs.inverse());
-
- for(RowIterator i = basicRowIterator(basicOld); !i.atEnd(); ++i){
- EntryID id = i.getID();
- Tableau::Entry& entry = d_entries.get(id);
-
- entry.getCoefficient() *= negInverseA_rs;
- }
-
- d_basic2RowIndex.remove(basicOld);
- d_basic2RowIndex.set(basicNew, rid);
- d_rowIndex2basic.set(rid, basicNew);
-
- cb.multiplyRow(rid, -a_rs_sgn);
-}
-
-void Tableau::addRow(ArithVar basic,
- const std::vector<Rational>& coefficients,
- const std::vector<ArithVar>& variables)
-{
- Assert(basic < getNumColumns());
- Assert(debugIsASet(variables));
- Assert(coefficients.size() == variables.size());
- Assert(!isBasic(basic));
-
- RowIndex newRow = Matrix<Rational>::addRow(coefficients, variables);
- addEntry(newRow, basic, Rational(-1));
-
- Assert(!d_basic2RowIndex.isKey(basic));
- Assert(!d_rowIndex2basic.isKey(newRow));
-
- d_basic2RowIndex.set(basic, newRow);
- d_rowIndex2basic.set(newRow, basic);
-
-
- if(TraceIsOn("matrix")){ printMatrix(); }
-
- NoEffectCCCB noeffect;
- NoEffectCCCB* nep = &noeffect;
- CoefficientChangeCallback* cccb = static_cast<CoefficientChangeCallback*>(nep);
-
- vector<Rational>::const_iterator coeffIter = coefficients.begin();
- vector<ArithVar>::const_iterator varsIter = variables.begin();
- vector<ArithVar>::const_iterator varsEnd = variables.end();
- for(; varsIter != varsEnd; ++coeffIter, ++varsIter){
- ArithVar var = *varsIter;
-
- if(isBasic(var)){
- Rational coeff = *coeffIter;
-
- RowIndex ri = basicToRowIndex(var);
-
- loadRowIntoBuffer(ri);
- rowPlusBufferTimesConstant(newRow, coeff, *cccb);
- clearBuffer();
- }
- }
-
- if(TraceIsOn("matrix")) { printMatrix(); }
-
- Assert(debugNoZeroCoefficients(newRow));
- Assert(debugMatchingCountsForRow(newRow));
- Assert(getColLength(basic) == 1);
-}
-
-void Tableau::removeBasicRow(ArithVar basic){
- RowIndex rid = basicToRowIndex(basic);
-
- removeRow(rid);
- d_basic2RowIndex.remove(basic);
- d_rowIndex2basic.remove(rid);
-}
-
-void Tableau::substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb){
- if(!mult.isZero()){
- RowIndex to_idx = basicToRowIndex(to);
- addEntry(to_idx, from, mult); // Add an entry to be cancelled out
- RowIndex from_idx = basicToRowIndex(from);
-
- cb.update(to_idx, from, 0, mult.sgn());
-
- loadRowIntoBuffer(from_idx);
- rowPlusBufferTimesConstant(to_idx, mult, cb);
- clearBuffer();
- }
-}
-
-uint32_t Tableau::rowComplexity(ArithVar basic) const{
- uint32_t complexity = 0;
- for(RowIterator i = basicRowIterator(basic); !i.atEnd(); ++i){
- const Entry& e = *i;
- complexity += e.getCoefficient().complexity();
- }
- return complexity;
-}
-
-double Tableau::avgRowComplexity() const{
- double sum = 0;
- uint32_t rows = 0;
- for(BasicIterator i = beginBasic(), i_end = endBasic(); i != i_end; ++i){
- sum += rowComplexity(*i);
- rows++;
- }
- return (rows == 0) ? 0 : (sum/rows);
-}
-
-void Tableau::printBasicRow(ArithVar basic, std::ostream& out){
- printRow(basicToRowIndex(basic), out);
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Morgan Deters
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include <vector>
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/matrix.h"
-#include "util/dense_map.h"
-#include "util/rational.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-/**
- * A Tableau is a Rational matrix that keeps its rows in solved form.
- * Each row has a basic variable with coefficient -1 that is solved.
- * Tableau is optimized for pivoting.
- * The tableau should only be updated via pivot calls.
- */
-class Tableau : public Matrix<Rational> {
-public:
-private:
- typedef DenseMap<RowIndex> BasicToRowMap;
- // Set of all of the basic variables in the tableau.
- // ArithVarMap<RowIndex> : ArithVar |-> RowIndex
- BasicToRowMap d_basic2RowIndex;
-
- // RowIndex |-> Basic Variable
- typedef DenseMap<ArithVar> RowIndexToBasicMap;
- RowIndexToBasicMap d_rowIndex2basic;
-
-public:
-
- Tableau() : Matrix<Rational>(Rational(0)) {}
-
- typedef Matrix<Rational>::ColIterator ColIterator;
- typedef Matrix<Rational>::RowIterator RowIterator;
- typedef BasicToRowMap::const_iterator BasicIterator;
-
- typedef MatrixEntry<Rational> Entry;
-
- bool isBasic(ArithVar v) const{
- return d_basic2RowIndex.isKey(v);
- }
-
- void debugPrintIsBasic(ArithVar v) const {
- if(isBasic(v)){
- Trace("model") << v << " is basic." << std::endl;
- }else{
- Trace("model") << v << " is non-basic." << std::endl;
- }
- }
-
- BasicIterator beginBasic() const {
- return d_basic2RowIndex.begin();
- }
- BasicIterator endBasic() const {
- return d_basic2RowIndex.end();
- }
-
- RowIndex basicToRowIndex(ArithVar x) const {
- return d_basic2RowIndex[x];
- }
-
- ArithVar rowIndexToBasic(RowIndex rid) const {
- Assert(d_rowIndex2basic.isKey(rid));
- return d_rowIndex2basic[rid];
- }
-
- ColIterator colIterator(ArithVar x) const {
- return getColumn(x).begin();
- }
-
- RowIterator ridRowIterator(RowIndex rid) const {
- return getRow(rid).begin();
- }
-
- RowIterator basicRowIterator(ArithVar basic) const {
- return ridRowIterator(basicToRowIndex(basic));
- }
-
- const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
- return findEntry(basicToRowIndex(basic), col);
- }
-
- /**
- * Adds a row to the tableau.
- * The new row is equivalent to:
- * basicVar = \f$\sum_i\f$ coeffs[i] * variables[i]
- * preconditions:
- * basicVar is already declared to be basic
- * basicVar does not have a row associated with it in the tableau.
- *
- * Note: each variables[i] does not have to be non-basic.
- * Pivoting will be mimicked if it is basic.
- */
- void addRow(ArithVar basicVar,
- const std::vector<Rational>& coeffs,
- const std::vector<ArithVar>& variables);
-
- /**
- * preconditions:
- * x_r is basic,
- * x_s is non-basic, and
- * a_rs != 0.
- */
- void pivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
-
- void removeBasicRow(ArithVar basic);
-
- uint32_t basicRowLength(ArithVar basic) const{
- RowIndex ridx = basicToRowIndex(basic);
- return getRowLength(ridx);
- }
-
- /**
- * to += mult * from
- * replacing from with its row.
- */
- void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult, CoefficientChangeCallback& cb);
-
- void directlyAddToCoefficient(ArithVar rowVar, ArithVar col, const Rational& mult, CoefficientChangeCallback& cb){
- RowIndex ridx = basicToRowIndex(rowVar);
- manipulateRowEntry(ridx, col, mult, cb);
- }
-
- /* Returns the complexity of a row in the tableau. */
- uint32_t rowComplexity(ArithVar basic) const;
-
- /* Returns the average complexity of the rows in the tableau. */
- double avgRowComplexity() const;
-
- void printBasicRow(ArithVar basic, std::ostream& out);
-
-private:
- /* Changes the basic variable on the row for basicOld to basicNew. */
- void rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCallback& cb);
-
-};/* class Tableau */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "base/output.h"
-#include "theory/arith/tableau_sizes.h"
-#include "theory/arith/tableau.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-uint32_t TableauSizes::getRowLength(ArithVar b) const {
- return d_tab->basicRowLength(b);
-}
-
-uint32_t TableauSizes::getColumnLength(ArithVar x) const {
- return d_tab->getColLength(x);
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "cvc5_private.h"
-
-#pragma once
-
-#include "theory/arith/arithvar.h"
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-class Tableau;
-
-class TableauSizes {
-private:
- const Tableau* d_tab;
-public:
- TableauSizes(const Tableau* tab): d_tab(tab){}
-
- uint32_t getRowLength(ArithVar b) const;
- uint32_t getColumnLength(ArithVar x) const;
-}; /* TableauSizes */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
#include "theory/arith/arith_evaluator.h"
#include "theory/arith/arith_rewriter.h"
#include "theory/arith/equality_solver.h"
-#include "theory/arith/infer_bounds.h"
#include "theory/arith/nl/nonlinear_extension.h"
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
#include "theory/ext_theory.h"
#include "theory/rewriter.h"
#include "theory/theory_model.h"
d_ppre(d_env),
d_bab(env, d_astate, d_im, d_ppre, d_pnm),
d_eqSolver(nullptr),
- d_internal(new TheoryArithPrivate(*this, env, d_bab)),
+ d_internal(new linear::TheoryArithPrivate(*this, env, d_bab)),
d_nonlinearExtension(nullptr),
d_opElim(d_env),
d_arithPreproc(env, d_astate, d_im, d_pnm, d_opElim),
std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
{
- ArithEntailmentCheckParameters def;
- def.addLookupRowSumAlgorithms();
- ArithEntailmentCheckSideEffects ase;
- std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
- return res;
+ return d_internal->entailmentCheck(lit);
}
+
eq::ProofEqEngine* TheoryArith::getProofEqEngine()
{
return d_im.getProofEqEngine();
}
class EqualitySolver;
+
+namespace linear {
class TheoryArithPrivate;
+}
/**
* Implementation of linear and non-linear integer and real arithmetic.
* http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
*/
class TheoryArith : public Theory {
- friend class TheoryArithPrivate;
+ friend class linear::TheoryArithPrivate;
public:
TheoryArith(Env& env, OutputChannel& out, Valuation valuation);
virtual ~TheoryArith();
/** The equality solver */
std::unique_ptr<EqualitySolver> d_eqSolver;
/** The (old) linear arithmetic solver */
- TheoryArithPrivate* d_internal;
+ linear::TheoryArithPrivate* d_internal;
/**
* The non-linear extension, responsible for all approaches for non-linear
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Gereon Kremer, Alex Ozdemir
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#include "theory/arith/theory_arith_private.h"
-
-#include <map>
-#include <optional>
-#include <queue>
-#include <vector>
-
-#include "base/output.h"
-#include "context/cdhashset.h"
-#include "context/cdinsert_hashmap.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "context/context.h"
-#include "expr/kind.h"
-#include "expr/metakind.h"
-#include "expr/node.h"
-#include "expr/node_algorithm.h"
-#include "expr/node_builder.h"
-#include "expr/skolem_manager.h"
-#include "options/arith_options.h"
-#include "options/base_options.h"
-#include "options/smt_options.h"
-#include "preprocessing/util/ite_utilities.h"
-#include "proof/proof_generator.h"
-#include "proof/proof_node_manager.h"
-#include "proof/proof_rule.h"
-#include "smt/logic_exception.h"
-#include "smt/smt_statistics_registry.h"
-#include "smt_util/boolean_simplification.h"
-#include "theory/arith/approx_simplex.h"
-#include "theory/arith/arith_rewriter.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/cut_log.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/dio_solver.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/nl/nonlinear_extension.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/simplex.h"
-#include "theory/arith/theory_arith.h"
-#include "theory/ext_theory.h"
-#include "theory/quantifiers/fmf/bounded_integers.h"
-#include "theory/rewriter.h"
-#include "theory/theory_model.h"
-#include "theory/trust_substitutions.h"
-#include "theory/valuation.h"
-#include "util/dense_map.h"
-#include "util/integer.h"
-#include "util/random.h"
-#include "util/rational.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-using namespace std;
-using namespace cvc5::internal::kind;
-
-namespace cvc5::internal {
-namespace theory {
-namespace arith {
-
-static Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum);
-static bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap);
-
-TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing,
- Env& env,
- BranchAndBound& bab)
- : EnvObj(env),
- d_containing(containing),
- d_foundNl(false),
- d_rowTracking(),
- d_bab(bab),
- d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
- : nullptr),
- d_checker(),
- d_pfGen(new EagerProofGenerator(d_pnm, userContext())),
- d_constraintDatabase(d_env,
- d_partialModel,
- d_congruenceManager,
- RaiseConflict(*this),
- d_pfGen.get()),
- d_qflraStatus(Result::UNKNOWN),
- d_unknownsInARow(0),
- d_hasDoneWorkSinceCut(false),
- d_learner(userContext()),
- d_assertionsThatDoNotMatchTheirLiterals(context()),
- d_nextIntegerCheckVar(0),
- d_constantIntegerVariables(context()),
- d_diseqQueue(context(), false),
- d_currentPropagationList(),
- d_learnedBounds(context()),
- d_preregisteredNodes(context()),
- d_partialModel(context(), DeltaComputeCallback(*this)),
- d_errorSet(
- d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)),
- d_tableau(),
- d_linEq(d_partialModel,
- d_tableau,
- d_rowTracking,
- BasicVarModelUpdateCallBack(*this)),
- d_diosolver(env),
- d_restartsCounter(0),
- d_tableauSizeHasBeenModified(false),
- d_tableauResetDensity(1.6),
- d_tableauResetPeriod(10),
- d_conflicts(context()),
- d_blackBoxConflict(context(), Node::null()),
- d_blackBoxConflictPf(context(), std::shared_ptr<ProofNode>(nullptr)),
- d_congruenceManager(d_env,
- d_constraintDatabase,
- SetupLiteralCallBack(*this),
- d_partialModel,
- RaiseEqualityEngineConflict(*this)),
- d_cmEnabled(context(), options().arith.arithCongMan),
-
- d_dualSimplex(
- env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
- d_fcSimplex(
- env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
- d_soiSimplex(
- env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
- d_attemptSolSimplex(
- env, d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
- d_pass1SDP(NULL),
- d_otherSDP(NULL),
- d_lastContextIntegerAttempted(context(), -1),
-
- d_DELTA_ZERO(0),
- d_approxCuts(context()),
- d_fullCheckCounter(0),
- d_cutCount(context(), 0),
- d_cutInContext(context()),
- d_likelyIntegerInfeasible(context(), false),
- d_guessedCoeffSet(context(), false),
- d_guessedCoeffs(),
- d_treeLog(NULL),
- d_replayVariables(),
- d_replayConstraints(),
- d_lhsTmp(),
- d_approxStats(NULL),
- d_attemptSolveIntTurnedOff(userContext(), 0),
- d_dioSolveResources(0),
- d_solveIntMaybeHelp(0u),
- d_solveIntAttempts(0u),
- d_newFacts(false),
- d_previousStatus(Result::UNKNOWN),
- d_statistics(statisticsRegistry(), "theory::arith::")
-{
-}
-
-TheoryArithPrivate::~TheoryArithPrivate(){
- if(d_treeLog != NULL){ delete d_treeLog; }
- if(d_approxStats != NULL) { delete d_approxStats; }
-}
-
-bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo& esi)
-{
- if (!d_cmEnabled)
- {
- return false;
- }
- return d_congruenceManager.needsEqualityEngine(esi);
-}
-void TheoryArithPrivate::finishInit()
-{
- if (d_cmEnabled)
- {
- eq::EqualityEngine* ee = d_containing.getEqualityEngine();
- Assert(ee != nullptr);
- d_congruenceManager.finishInit(ee);
- }
-}
-
-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 CVC5_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 CVC5_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());
-}
-
-TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg)
-{
- stringstream ss;
- ss << "Cannot construct a model for " << n << " as " << endl << msg;
- setMessage(ss.str());
-}
-TheoryArithPrivate::ModelException::~ModelException() {}
-
-TheoryArithPrivate::Statistics::Statistics(StatisticsRegistry& reg,
- const std::string& name)
- : d_statAssertUpperConflicts(
- reg.registerInt(name + "AssertUpperConflicts")),
- d_statAssertLowerConflicts(
- reg.registerInt(name + "AssertLowerConflicts")),
- d_statUserVariables(reg.registerInt(name + "UserVariables")),
- d_statAuxiliaryVariables(reg.registerInt(name + "AuxiliaryVariables")),
- d_statDisequalitySplits(reg.registerInt(name + "DisequalitySplits")),
- d_statDisequalityConflicts(
- reg.registerInt(name + "DisequalityConflicts")),
- d_simplifyTimer(reg.registerTimer(name + "simplifyTimer")),
- d_staticLearningTimer(reg.registerTimer(name + "staticLearningTimer")),
- d_presolveTime(reg.registerTimer(name + "presolveTime")),
- d_newPropTime(reg.registerTimer(name + "newPropTimer")),
- d_externalBranchAndBounds(
- reg.registerInt(name + "externalBranchAndBounds")),
- d_initialTableauSize(reg.registerInt(name + "initialTableauSize")),
- d_currSetToSmaller(reg.registerInt(name + "currSetToSmaller")),
- d_smallerSetToCurr(reg.registerInt(name + "smallerSetToCurr")),
- d_restartTimer(reg.registerTimer(name + "restartTimer")),
- d_boundComputationTime(reg.registerTimer(name + "bound::time")),
- d_boundComputations(reg.registerInt(name + "bound::boundComputations")),
- d_boundPropagations(reg.registerInt(name + "bound::boundPropagations")),
- d_unknownChecks(reg.registerInt(name + "status::unknowns")),
- d_maxUnknownsInARow(reg.registerInt(name + "status::maxUnknownsInARow")),
- d_avgUnknownsInARow(
- reg.registerAverage(name + "status::avgUnknownsInARow")),
- d_revertsOnConflicts(
- reg.registerInt(name + "status::revertsOnConflicts")),
- d_commitsOnConflicts(
- reg.registerInt(name + "status::commitsOnConflicts")),
- d_nontrivialSatChecks(
- reg.registerInt(name + "status::nontrivialSatChecks")),
- d_replayLogRecCount(reg.registerInt(name + "z::approx::replay::rec")),
- d_replayLogRecConflictEscalation(
- reg.registerInt(name + "z::approx::replay::rec::escalation")),
- d_replayLogRecEarlyExit(
- reg.registerInt(name + "z::approx::replay::rec::earlyexit")),
- d_replayBranchCloseFailures(reg.registerInt(
- name + "z::approx::replay::rec::branch::closefailures")),
- d_replayLeafCloseFailures(reg.registerInt(
- name + "z::approx::replay::rec::leaf::closefailures")),
- d_replayBranchSkips(
- reg.registerInt(name + "z::approx::replay::rec::branch::skips")),
- d_mirCutsAttempted(
- reg.registerInt(name + "z::approx::cuts::mir::attempted")),
- d_gmiCutsAttempted(
- reg.registerInt(name + "z::approx::cuts::gmi::attempted")),
- d_branchCutsAttempted(
- reg.registerInt(name + "z::approx::cuts::branch::attempted")),
- d_cutsReconstructed(
- reg.registerInt(name + "z::approx::cuts::reconstructed")),
- d_cutsReconstructionFailed(
- reg.registerInt(name + "z::approx::cuts::reconstructed::failed")),
- d_cutsProven(reg.registerInt(name + "z::approx::cuts::proofs")),
- d_cutsProofFailed(
- reg.registerInt(name + "z::approx::cuts::proofs::failed")),
- d_mipReplayLemmaCalls(
- reg.registerInt(name + "z::approx::external::calls")),
- d_mipExternalCuts(reg.registerInt(name + "z::approx::external::cuts")),
- d_mipExternalBranch(
- reg.registerInt(name + "z::approx::external::branches")),
- d_inSolveInteger(reg.registerInt(name + "z::approx::inSolverInteger")),
- d_branchesExhausted(
- reg.registerInt(name + "z::approx::exhausted::branches")),
- d_execExhausted(reg.registerInt(name + "z::approx::exhausted::exec")),
- d_pivotsExhausted(reg.registerInt(name + "z::approx::exhausted::pivots")),
- d_panicBranches(reg.registerInt(name + "z::arith::paniclemmas")),
- d_relaxCalls(reg.registerInt(name + "z::arith::relax::calls")),
- d_relaxLinFeas(reg.registerInt(name + "z::arith::relax::feasible::res")),
- d_relaxLinFeasFailures(
- reg.registerInt(name + "z::arith::relax::feasible::failures")),
- d_relaxLinInfeas(reg.registerInt(name + "z::arith::relax::infeasible")),
- d_relaxLinInfeasFailures(
- reg.registerInt(name + "z::arith::relax::infeasible::failures")),
- d_relaxLinExhausted(reg.registerInt(name + "z::arith::relax::exhausted")),
- d_relaxOthers(reg.registerInt(name + "z::arith::relax::other")),
- d_applyRowsDeleted(
- reg.registerInt(name + "z::arith::cuts::applyRowsDeleted")),
- d_replaySimplexTimer(
- reg.registerTimer(name + "z::approx::replay::simplex::timer")),
- d_replayLogTimer(
- reg.registerTimer(name + "z::approx::replay::log::timer")),
- d_solveIntTimer(reg.registerTimer(name + "z::solveInt::timer")),
- d_solveRealRelaxTimer(
- reg.registerTimer(name + "z::solveRealRelax::timer")),
- d_solveIntCalls(reg.registerInt(name + "z::solveInt::calls")),
- d_solveStandardEffort(
- reg.registerInt(name + "z::solveInt::calls::standardEffort")),
- d_approxDisabled(reg.registerInt(name + "z::approxDisabled")),
- d_replayAttemptFailed(reg.registerInt(name + "z::replayAttemptFailed")),
- d_cutsRejectedDuringReplay(
- reg.registerInt(name + "z::approx::replay::cuts::rejected")),
- d_cutsRejectedDuringLemmas(
- reg.registerInt(name + "z::approx::external::cuts::rejected")),
- d_satPivots(reg.registerHistogram<uint32_t>(name + "pivots::sat")),
- d_unsatPivots(reg.registerHistogram<uint32_t>(name + "pivots::unsat")),
- d_unknownPivots(
- reg.registerHistogram<uint32_t>(name + "pivots::unknown")),
- d_solveIntModelsAttempts(
- reg.registerInt(name + "z::solveInt::models::attempts")),
- d_solveIntModelsSuccessful(
- reg.registerInt(name + "zzz::solveInt::models::successful")),
- d_mipTimer(reg.registerTimer(name + "z::approx::mip::timer")),
- d_lpTimer(reg.registerTimer(name + "z::approx::lp::timer")),
- d_mipProofsAttempted(reg.registerInt(name + "z::mip::proofs::attempted")),
- d_mipProofsSuccessful(
- reg.registerInt(name + "z::mip::proofs::successful")),
- d_numBranchesFailed(
- reg.registerInt(name + "z::mip::branch::proof::failed"))
-{
-}
-
-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;
-}
-
-bool TheoryArithPrivate::isProofEnabled() const
-{
- return d_pnm != nullptr;
-}
-
-void TheoryArithPrivate::raiseConflict(ConstraintCP a, InferenceId id){
- Assert(a->inConflict());
- Assert(id != InferenceId::UNKNOWN)
- << "Must provide an inference id in TheoryArithPrivate::raiseConflict";
- d_conflicts.push_back(std::make_pair(a, id));
-}
-
-void TheoryArithPrivate::raiseBlackBoxConflict(Node bb,
- std::shared_ptr<ProofNode> pf)
-{
- Trace("arith::bb") << "raiseBlackBoxConflict: " << bb << std::endl;
- if (d_blackBoxConflict.get().isNull())
- {
- if (isProofEnabled())
- {
- Trace("arith::bb") << " with proof " << pf << std::endl;
- d_blackBoxConflictPf.set(pf);
- }
- d_blackBoxConflict = bb;
- }
-}
-
-bool TheoryArithPrivate::anyConflict() const
-{
- return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull();
-}
-
-void TheoryArithPrivate::revertOutOfConflict(){
- d_partialModel.revertAssignmentChanges();
- clearUpdates();
- d_currentPropagationList.clear();
-}
-
-void TheoryArithPrivate::clearUpdates(){
- d_updatedBounds.purge();
-}
-
-void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){
- if(d_cmEnabled){
- Assert(d_congruenceManager.isWatchedVariable(x));
- Assert(d_partialModel.upperBoundIsZero(x));
- Assert(d_partialModel.lowerBoundIsZero(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);
- }else{
- d_congruenceManager.watchedVariableIsZero(lb, ub);
- }
- }
-}
-
-bool TheoryArithPrivate::getSolveIntegerResource(){
- if(d_attemptSolveIntTurnedOff > 0){
- d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1;
- return false;
- }else{
- return true;
- }
-}
-
-bool TheoryArithPrivate::getDioCuttingResource(){
- if(d_dioSolveResources > 0){
- d_dioSolveResources--;
- if(d_dioSolveResources == 0){
- d_dioSolveResources = -options().arith.rrTurns;
- }
- return true;
- }else{
- d_dioSolveResources++;
- if(d_dioSolveResources >= 0){
- d_dioSolveResources = options().arith.dioSolverTurns;
- }
- return false;
- }
-}
-
-/* procedure AssertLower( x_i >= c_i ) */
-bool TheoryArithPrivate::AssertLower(ConstraintP constraint){
- Assert(constraint != NullConstraint);
- Assert(constraint->isLowerBound());
- Assert(constraint->isTrue());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Trace("arith") << "AssertLower(" << x_i << " " << c_i << ")"<< std::endl;
-
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- //TODO Relax to less than?
- if(d_partialModel.lessThanLowerBound(x_i, c_i)){
- return false; //sat
- }
-
- int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
- if(cmpToUB > 0){ // c_i < \lowerbound(x_i)
- ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i);
- ConstraintP negation = constraint->getNegation();
- negation->impliedByUnate(ubc, true);
-
- raiseConflict(constraint, InferenceId::ARITH_CONF_LOWER);
-
- ++(d_statistics.d_statAssertLowerConflicts);
- return true;
- }else if(cmpToUB == 0){
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Trace("dio::push") << "dio::push " << x_i << endl;
- }
- 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.hasEquality()){
- Assert(vc.hasDisequality());
- ConstraintP eq = vc.getEquality();
- ConstraintP diseq = vc.getDisequality();
- // x <= b, x >= b |= x = b
- // (x > b or x < b or x = b)
- Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
- bool triConflict = diseq->isTrue();
-
- if(!eq->isTrue()){
- eq->impliedByTrichotomy(constraint, ub, triConflict);
- eq->tryToPropagate();
- }
- if(triConflict){
- ++(d_statistics.d_statDisequalityConflicts);
- raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
- return true;
- }
- }
- }else{
- // l <= x <= u and l < u
- Assert(cmpToUB < 0);
- const ValueCollection& vc = constraint->getValueCollection();
-
- if(vc.hasDisequality()){
- const ConstraintP diseq = vc.getDisequality();
- if(diseq->isTrue()){
- const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
- ConstraintP negUb = ub->getNegation();
-
- // l <= x, l != x |= l < x
- // |= not (l >= x)
- bool ubInConflict = ub->hasProof();
- bool learnNegUb = !(negUb->hasProof());
- if(learnNegUb){
- negUb->impliedByTrichotomy(constraint, diseq, ubInConflict);
- negUb->tryToPropagate();
- }
- if(ubInConflict){
- raiseConflict(ub, InferenceId::ARITH_CONF_TRICHOTOMY);
- return true;
- }else if(learnNegUb){
- d_learnedBounds.push_back(negUb);
- }
- }
- }
- }
-
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
-
- d_partialModel.setLowerBoundConstraint(constraint);
-
- if(d_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);
- }
- }
- }
-
- d_updatedBounds.softAdd(x_i);
-
- if(TraceIsOn("model")) {
- Trace("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(d_partialModel.getAssignment(x_i) < c_i){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_errorSet.signalVariable(x_i);
- }
-
- if(TraceIsOn("model")) {
- Trace("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false; //sat
-}
-
-/* procedure AssertUpper( x_i <= c_i) */
-bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){
- Assert(constraint != NullConstraint);
- Assert(constraint->isUpperBound());
- Assert(constraint->isTrue());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
-
-
- //Too strong because of rounding with integers
- //Assert(!constraint->hasLiteral() || original == constraint->getLiteral());
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- Trace("arith") << "AssertUpper(" << x_i << " " << c_i << ")"<< std::endl;
-
- if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i
- return false; //sat
- }
-
- // cmpToLb = \lowerbound(x_i).cmp(c_i)
- int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
- if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i)
- // l_i <= x_i and c_i < l_i |= c_i < x_i
- // or ... |= not (x_i <= c_i)
- ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i);
- ConstraintP negConstraint = constraint->getNegation();
- negConstraint->impliedByUnate(lbc, true);
- raiseConflict(constraint, InferenceId::ARITH_CONF_UPPER);
- ++(d_statistics.d_statAssertUpperConflicts);
- return true;
- }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i)
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Trace("dio::push") << "dio::push " << x_i << endl;
- }
-
- const ValueCollection& vc = constraint->getValueCollection();
- 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);
- }
- }
-
- if(vc.hasDisequality()){
- Assert(vc.hasDisequality());
- ConstraintP eq = vc.getEquality();
- ConstraintP diseq = vc.getDisequality();
- // x <= b, x >= b |= x = b
- // (x > b or x < b or x = b)
- Trace("arith::eq") << "lb == ub, propagate eq" << eq << endl;
- bool triConflict = diseq->isTrue();
- if(!eq->isTrue()){
- eq->impliedByTrichotomy(constraint, lb, triConflict);
- eq->tryToPropagate();
- }
- if(triConflict){
- ++(d_statistics.d_statDisequalityConflicts);
- raiseConflict(eq, InferenceId::ARITH_CONF_TRICHOTOMY);
- return true;
- }
- }
- }else if(cmpToLB > 0){
- // l <= x <= u and l < u
- Assert(cmpToLB > 0);
- const ValueCollection& vc = constraint->getValueCollection();
-
- if(vc.hasDisequality()){
- const ConstraintP diseq = vc.getDisequality();
- if(diseq->isTrue()){
- const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
- ConstraintP negLb = lb->getNegation();
-
- // x <= u, u != x |= u < x
- // |= not (u >= x)
- bool lbInConflict = lb->hasProof();
- bool learnNegLb = !(negLb->hasProof());
- if(learnNegLb){
- negLb->impliedByTrichotomy(constraint, diseq, lbInConflict);
- negLb->tryToPropagate();
- }
- if(lbInConflict){
- raiseConflict(lb, InferenceId::ARITH_CONF_TRICHOTOMY);
- return true;
- }else if(learnNegLb){
- d_learnedBounds.push_back(negLb);
- }
- }
- }
- }
-
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
- //It is fine if this is NullConstraint
-
- d_partialModel.setUpperBoundConstraint(constraint);
-
- if(d_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);
-
- if(TraceIsOn("model")) {
- Trace("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(d_partialModel.getAssignment(x_i) > c_i){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_errorSet.signalVariable(x_i);
- }
-
- if(TraceIsOn("model")) {
- Trace("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false; //sat
-}
-
-
-/* procedure AssertEquality( x_i == c_i ) */
-bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){
- Assert(constraint != NullConstraint);
- Assert(constraint->isEquality());
- Assert(constraint->isTrue());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
-
- Trace("arith") << "AssertEquality(" << x_i << " " << c_i << ")"<< std::endl;
-
- //Should be fine in integers
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i);
- int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i);
-
- // u_i <= c_i <= l_i
- // This can happen if both c_i <= x_i and x_i <= c_i are in the system.
- if(cmpToUB >= 0 && cmpToLB <= 0){
- return false; //sat
- }
-
- if(cmpToUB > 0 || cmpToLB < 0){
- ConstraintP cb = (cmpToUB > 0) ? d_partialModel.getUpperBoundConstraint(x_i) :
- d_partialModel.getLowerBoundConstraint(x_i);
- ConstraintP diseq = constraint->getNegation();
- Assert(!diseq->isTrue());
- diseq->impliedByUnate(cb, true);
- raiseConflict(constraint, InferenceId::ARITH_CONF_EQ);
- return true;
- }
-
- Assert(cmpToUB <= 0);
- Assert(cmpToLB >= 0);
- Assert(cmpToUB < 0 || cmpToLB > 0);
-
- if(isInteger(x_i)){
- d_constantIntegerVariables.push_back(x_i);
- Trace("dio::push") << "dio::push " << x_i << endl;
- }
-
- // Don't bother to check whether x_i != c_i is in d_diseq
- // The a and (not a) should never be on the fact queue
- d_currentPropagationList.push_back(constraint);
- d_currentPropagationList.push_back(d_partialModel.getLowerBoundConstraint(x_i));
- d_currentPropagationList.push_back(d_partialModel.getUpperBoundConstraint(x_i));
-
- d_partialModel.setUpperBoundConstraint(constraint);
- d_partialModel.setLowerBoundConstraint(constraint);
-
- if(d_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.equalsConstant(constraint);
- }
- }
-
- d_updatedBounds.softAdd(x_i);
-
- if(TraceIsOn("model")) {
- Trace("model") << "before" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- if(!d_tableau.isBasic(x_i)){
- if(!(d_partialModel.getAssignment(x_i) == c_i)){
- d_linEq.update(x_i, c_i);
- }
- }else{
- d_errorSet.signalVariable(x_i);
- }
-
- if(TraceIsOn("model")) {
- Trace("model") << "after" << endl;
- d_partialModel.printModel(x_i);
- d_tableau.debugPrintIsBasic(x_i);
- }
-
- return false;
-}
-
-
-/* procedure AssertDisequality( x_i != c_i ) */
-bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){
- Assert(constraint != NullConstraint);
- Assert(constraint->isDisequality());
- Assert(constraint->isTrue());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
- const DeltaRational& c_i = constraint->getValue();
- Trace("arith") << "AssertDisequality(" << x_i << " " << c_i << ")"<< std::endl;
-
- //Should be fine in integers
- Assert(!isInteger(x_i) || c_i.isIntegral());
-
- 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 ConstraintP lb = vc.getLowerBound();
- const ConstraintP ub = vc.getUpperBound();
- if(lb->isTrue() && ub->isTrue()){
- ConstraintP eq = constraint->getNegation();
- eq->impliedByTrichotomy(lb, ub, true);
- raiseConflict(constraint, InferenceId::ARITH_CONF_TRICHOTOMY);
- //in conflict
- ++(d_statistics.d_statDisequalityConflicts);
- return true;
- }
- }
- if(vc.hasLowerBound() ){
- const ConstraintP lb = vc.getLowerBound();
- if(lb->isTrue()){
- const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound);
- Assert(!ub->isTrue());
- Trace("arith::eq") << "propagate UpperBound " << constraint << lb << ub << endl;
- const ConstraintP negUb = ub->getNegation();
- if(!negUb->isTrue()){
- negUb->impliedByTrichotomy(constraint, lb, false);
- negUb->tryToPropagate();
- d_learnedBounds.push_back(negUb);
- }
- }
- }
- if(vc.hasUpperBound()){
- const ConstraintP ub = vc.getUpperBound();
- if(ub->isTrue()){
- const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound);
- Assert(!lb->isTrue());
-
- Trace("arith::eq") << "propagate LowerBound " << constraint << lb << ub << endl;
- const ConstraintP negLb = lb->getNegation();
- if(!negLb->isTrue()){
- negLb->impliedByTrichotomy(constraint, ub, false);
- negLb->tryToPropagate();
- d_learnedBounds.push_back(negLb);
- }
- }
- }
-
- bool split = constraint->isSplit();
-
- if(!split && c_i == d_partialModel.getAssignment(x_i)){
- Trace("arith::eq") << "lemma now! " << constraint << endl;
- outputTrustedLemma(constraint->split(), InferenceId::ARITH_SPLIT_DEQ);
- return false;
- }else if(d_partialModel.strictlyLessThanLowerBound(x_i, c_i)){
- Trace("arith::eq") << "can drop as less than lb" << constraint << endl;
- }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, c_i)){
- Trace("arith::eq") << "can drop as less than ub" << constraint << endl;
- }else if(!split){
- Trace("arith::eq") << "push back" << constraint << endl;
- d_diseqQueue.push(constraint);
- d_partialModel.invalidateDelta();
- }else{
- Trace("arith::eq") << "skipping already split " << constraint << endl;
- }
- return false;
-}
-
-void TheoryArithPrivate::notifySharedTerm(TNode n)
-{
- Trace("arith::notifySharedTerm") << "notifySharedTerm: " << n << endl;
- if(n.isConst()){
- d_partialModel.invalidateDelta();
- }
- if(!n.isConst() && !isSetup(n)){
- Polynomial poly = Polynomial::parsePolynomial(n);
- Polynomial::iterator it = poly.begin();
- Polynomial::iterator it_end = poly.end();
- for (; it != it_end; ++ it) {
- Monomial m = *it;
- if (!m.isConstant() && !isSetup(m.getVarList().getNode())) {
- setupVariableList(m.getVarList());
- }
- }
- }
-}
-
-Node TheoryArithPrivate::getModelValue(TNode term) {
- try{
- const DeltaRational drv = getDeltaValue(term);
- const Rational& delta = d_partialModel.getDelta();
- const Rational qmodel = drv.substituteDelta( delta );
- return NodeManager::currentNM()->mkConstRealOrInt(term.getType(), qmodel);
- } catch (DeltaRationalException& dr) {
- return Node::null();
- } catch (ModelException& me) {
- return Node::null();
- }
-}
-
-Theory::PPAssertStatus TheoryArithPrivate::ppAssert(
- TrustNode tin, TrustSubstitutionMap& outSubstitutions)
-{
- TimerStat::CodeTimer codeTimer(d_statistics.d_simplifyTimer);
- TNode in = tin.getNode();
- Trace("simplify") << "TheoryArithPrivate::solve(" << in << ")" << endl;
-
-
- // Solve equalities
- Rational minConstant = 0;
- Node minMonomial;
- Node minVar;
- if (in.getKind() == kind::EQUAL &&
- Theory::theoryOf(in[0].getType()) == THEORY_ARITH) {
- Comparison cmp = Comparison::parseNormalForm(in);
-
- Polynomial left = cmp.getLeft();
-
- Monomial m = left.getHead();
- if (m.getVarList().singleton()){
- VarList vl = m.getVarList();
- Node var = vl.getNode();
- if (var.isVar())
- {
- // if vl.isIntegral then m.getConstant().isOne()
- if(!vl.isIntegral() || m.getConstant().isOne()){
- minVar = var;
- }
- }
- }
-
- // Solve for variable
- if (!minVar.isNull()) {
- Polynomial right = cmp.getRight();
- Node elim = right.getNode();
- // ax + p = c -> (ax + p) -ax - c = -ax
- // x = (p - ax - c) * -1/a
- // Add the substitution if not recursive
- Assert(elim == rewrite(elim));
-
- if (right.size() > options().arith.ppAssertMaxSubSize)
- {
- Trace("simplify")
- << "TheoryArithPrivate::solve(): did not substitute due to the "
- "right hand side containing too many terms: "
- << minVar << ":" << elim << endl;
- Trace("simplify") << right.size() << endl;
- }
- else if (d_containing.isLegalElimination(minVar, elim))
- {
- // cannot eliminate integers here unless we know the resulting
- // substitution is integral
- Trace("simplify") << "TheoryArithPrivate::solve(): substitution "
- << minVar << " |-> " << elim << endl;
-
- outSubstitutions.addSubstitutionSolved(minVar, elim, tin);
- return Theory::PP_ASSERT_STATUS_SOLVED;
- }
- else
- {
- Trace("simplify") << "TheoryArithPrivate::solve(): can't substitute "
- << minVar << ":" << minVar.getType() << " |-> "
- << elim << ":" << elim.getType() << endl;
- }
- }
- }
-
- // If a relation, remember the bound
- switch(in.getKind()) {
- case kind::LEQ:
- case kind::LT:
- case kind::GEQ:
- case kind::GT:
- if (in[0].isVar()) {
- d_learner.addBound(in);
- }
- break;
- default:
- // Do nothing
- break;
- }
-
- return Theory::PP_ASSERT_STATUS_UNSOLVED;
-}
-
-void TheoryArithPrivate::ppStaticLearn(TNode n, NodeBuilder& learned)
-{
- TimerStat::CodeTimer codeTimer(d_statistics.d_staticLearningTimer);
-
- d_learner.staticLearning(n, learned);
-}
-
-ArithVar TheoryArithPrivate::findShortestBasicRow(ArithVar variable){
- ArithVar bestBasic = ARITHVAR_SENTINEL;
- uint64_t bestRowLength = std::numeric_limits<uint64_t>::max();
-
- Tableau::ColIterator basicIter = d_tableau.colIterator(variable);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- Assert(entry.getColVar() == variable);
- RowIndex ridx = entry.getRowIndex();
- ArithVar basic = d_tableau.rowIndexToBasic(ridx);
- uint32_t rowLength = d_tableau.getRowLength(ridx);
- if((rowLength < bestRowLength) ||
- (rowLength == bestRowLength && basic < bestBasic)){
- bestBasic = basic;
- bestRowLength = rowLength;
- }
- }
- Assert(bestBasic == ARITHVAR_SENTINEL
- || bestRowLength < std::numeric_limits<uint32_t>::max());
- return bestBasic;
-}
-
-void TheoryArithPrivate::setupVariable(const Variable& x){
- Node n = x.getNode();
-
- Assert(!isSetup(n));
-
- ++(d_statistics.d_statUserVariables);
- requestArithVar(n, false, false);
- //ArithVar varN = requestArithVar(n,false);
- //setupInitialValue(varN);
-
- markSetup(n);
-}
-
-void TheoryArithPrivate::setupVariableList(const VarList& vl){
- Assert(!vl.empty());
-
- TNode vlNode = vl.getNode();
- Assert(!isSetup(vlNode));
- Assert(!d_partialModel.hasArithVar(vlNode));
-
- for(VarList::iterator i = vl.begin(), end = vl.end(); i != end; ++i){
- Variable var = *i;
-
- if(!isSetup(var.getNode())){
- setupVariable(var);
- }
- }
-
- if(!vl.singleton()){
- // vl is the product of at least 2 variables
- // vl : (* v1 v2 ...)
- if (logicInfo().isLinear())
- {
- throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
- }
- d_foundNl = true;
-
- ++(d_statistics.d_statUserVariables);
- requestArithVar(vlNode, false, false);
- //ArithVar av = requestArithVar(vlNode, false);
- //setupInitialValue(av);
-
- markSetup(vlNode);
- }
- else if (vlNode.getKind() == kind::EXPONENTIAL
- || vlNode.getKind() == kind::SINE || vlNode.getKind() == kind::COSINE
- || vlNode.getKind() == kind::TANGENT)
- {
- d_foundNl = true;
- }
-
- /* Note:
- * Only call markSetup if the VarList is not a singleton.
- * See the comment in setupPolynomail for more.
- */
-}
-
-void TheoryArithPrivate::cautiousSetupPolynomial(const Polynomial& p){
- if(p.containsConstant()){
- if(!p.isConstant()){
- Polynomial noConstant = p.getTail();
- if(!isSetup(noConstant.getNode())){
- setupPolynomial(noConstant);
- }
- }
- }else if(!isSetup(p.getNode())){
- setupPolynomial(p);
- }
-}
-
-
-void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
- Assert(!poly.containsConstant());
- TNode polyNode = poly.getNode();
- Assert(!isSetup(polyNode));
- Assert(!d_partialModel.hasArithVar(polyNode));
-
- for(Polynomial::iterator i = poly.begin(), end = poly.end(); i != end; ++i){
- Monomial mono = *i;
- const VarList& vl = mono.getVarList();
- if(!isSetup(vl.getNode())){
- setupVariableList(vl);
- }
- }
-
- if (polyNode.getKind() == ADD)
- {
- d_tableauSizeHasBeenModified = true;
-
- vector<ArithVar> variables;
- vector<Rational> coefficients;
- asVectors(poly, coefficients, variables);
-
- ArithVar varSlack = requestArithVar(polyNode, true, false);
- d_tableau.addRow(varSlack, coefficients, variables);
- setupBasicValue(varSlack);
- d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack));
-
- //Add differences to the difference manager
- Polynomial::iterator i = poly.begin(), end = poly.end();
- if(i != end){
- Monomial first = *i;
- ++i;
- if(i != end){
- Monomial second = *i;
- ++i;
- if(i == end){
- if(first.getConstant().isOne() && second.getConstant().getValue() == -1){
- VarList vl0 = first.getVarList();
- VarList vl1 = second.getVarList();
- if(vl0.singleton() && vl1.singleton()){
- d_congruenceManager.addWatchedPair(varSlack, vl0.getNode(), vl1.getNode());
- }
- }
- }
- }
- }
-
- ++(d_statistics.d_statAuxiliaryVariables);
- markSetup(polyNode);
- }
-
- /* Note:
- * It is worth documenting that polyNode should only be marked as
- * being setup by this function if it has kind ADD.
- * Other kinds will be marked as being setup by lower levels of setup
- * specifically setupVariableList.
- */
-}
-
-void TheoryArithPrivate::setupAtom(TNode atom) {
- Assert(isRelationOperator(atom.getKind())) << atom;
- Assert(Comparison::isNormalAtom(atom));
- Assert(!isSetup(atom));
- Assert(!d_constraintDatabase.hasLiteral(atom));
-
- Comparison cmp = Comparison::parseNormalForm(atom);
- Polynomial nvp = cmp.normalizedVariablePart();
- Assert(!nvp.isZero());
-
- if(!isSetup(nvp.getNode())){
- setupPolynomial(nvp);
- }
-
- d_constraintDatabase.addLiteral(atom);
-
- markSetup(atom);
-}
-
-void TheoryArithPrivate::preRegisterTerm(TNode n) {
- Trace("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
-
- d_preregisteredNodes.insert(n);
-
- try {
- if(isRelationOperator(n.getKind())){
- if(!isSetup(n)){
- setupAtom(n);
- }
- ConstraintP c = d_constraintDatabase.lookup(n);
- Assert(c != NullConstraint);
-
- Trace("arith::preregister") << "setup constraint" << c << endl;
- Assert(!c->canBePropagated());
- c->setPreregistered();
- }
- } catch(LogicException& le) {
- std::stringstream ss;
- ss << le.getMessage() << endl << "The fact in question: " << n << endl;
- throw LogicException(ss.str());
- }
-
- Trace("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
-}
-
-void TheoryArithPrivate::releaseArithVar(ArithVar v){
- //Assert(d_partialModel.hasNode(v));
-
- d_constraintDatabase.removeVariable(v);
- d_partialModel.releaseArithVar(v);
-}
-
-ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){
- //TODO : The VarList trick is good enough?
- Kind xk = x.getKind();
- Assert(isLeaf(x) || VarList::isMember(x) || xk == ADD || internal);
- if (logicInfo().isLinear()
- && (Variable::isDivMember(x) || xk == IAND || isTranscendentalKind(xk)))
- {
- stringstream ss;
- ss << "A non-linear fact was asserted to "
- "arithmetic in a linear logic: "
- << x << std::endl;
- throw LogicException(ss.str());
- }
- Assert(!d_partialModel.hasArithVar(x));
- Assert(x.getType().isRealOrInt()); // real or integer
-
- ArithVar max = d_partialModel.getNumberOfVariables();
- ArithVar varX = d_partialModel.allocate(x, aux);
-
- bool reclaim = max >= d_partialModel.getNumberOfVariables();;
-
- if(!reclaim){
- d_dualSimplex.increaseMax();
-
- d_tableau.increaseSize();
- d_tableauSizeHasBeenModified = true;
- }
- d_constraintDatabase.addVariable(varX);
-
- Trace("arith::arithvar") << "@" << context()->getLevel() << " " << x
- << " |-> " << varX << "(relaiming " << reclaim << ")"
- << endl;
-
- Assert(!d_partialModel.hasUpperBound(varX));
- Assert(!d_partialModel.hasLowerBound(varX));
-
- return varX;
-}
-
-void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& coeffs, std::vector<ArithVar>& variables) {
- for(Polynomial::iterator i = p.begin(), end = p.end(); i != end; ++i){
- const Monomial& mono = *i;
- const Constant& constant = mono.getConstant();
- const VarList& variable = mono.getVarList();
-
- Node n = variable.getNode();
-
- Trace("arith::asVectors") << "should be var: " << n << endl;
-
- // TODO: This VarList::isMember(n) can be stronger
- Assert(isLeaf(n) || VarList::isMember(n));
- Assert(theoryOf(n) != THEORY_ARITH || d_partialModel.hasArithVar(n));
-
- Assert(d_partialModel.hasArithVar(n));
- ArithVar av = d_partialModel.asArithVar(n);
-
- coeffs.push_back(constant.getValue());
- variables.push_back(av);
- }
-}
-
-/* Requirements:
- * For basic variables the row must have been added to the tableau.
- */
-void TheoryArithPrivate::setupBasicValue(ArithVar x){
- Assert(d_tableau.isBasic(x));
- //If the variable is basic, assertions may have already happened and updates
- //may have occured before setting this variable up.
-
- //This can go away if the tableau creation is done at preregister
- //time instead of register
- DeltaRational safeAssignment = d_linEq.computeRowValue(x, true);
- DeltaRational assignment = d_linEq.computeRowValue(x, false);
- d_partialModel.setAssignment(x,safeAssignment,assignment);
-
- Trace("arith") << "setupVariable("<<x<<")"<<std::endl;
-}
-
-ArithVar TheoryArithPrivate::determineArithVar(const Polynomial& p) const{
- Assert(!p.containsConstant());
- Assert(p.getHead().constantIsPositive());
- TNode n = p.getNode();
- Trace("determineArithVar") << "determineArithVar(" << n << ")" << endl;
- return d_partialModel.asArithVar(n);
-}
-
-ArithVar TheoryArithPrivate::determineArithVar(TNode assertion) const{
- Trace("determineArithVar") << "determineArithVar " << assertion << endl;
- Comparison cmp = Comparison::parseNormalForm(assertion);
- Polynomial variablePart = cmp.normalizedVariablePart();
- return determineArithVar(variablePart);
-}
-
-
-bool TheoryArithPrivate::canSafelyAvoidEqualitySetup(TNode equality){
- Assert(equality.getKind() == EQUAL);
- return d_partialModel.hasArithVar(equality[0]);
-}
-
-Comparison TheoryArithPrivate::mkIntegerEqualityFromAssignment(ArithVar v){
- const DeltaRational& beta = d_partialModel.getAssignment(v);
-
- Assert(beta.isIntegral());
- Polynomial betaAsPolynomial = Polynomial::mkPolynomial( Constant::mkConstant(beta.floor()) );
-
- TNode var = d_partialModel.asNode(v);
- Polynomial varAsPolynomial = Polynomial::parsePolynomial(var);
- return Comparison::mkComparison(EQUAL, varAsPolynomial, betaAsPolynomial);
-}
-
-TrustNode TheoryArithPrivate::dioCutting()
-{
- context::Context::ScopedPush speculativePush(context());
- //DO NOT TOUCH THE OUTPUTSTREAM
-
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
- if(isInteger(v)){
- if(d_partialModel.cmpAssignmentUpperBound(v) == 0 ||
- d_partialModel.cmpAssignmentLowerBound(v) == 0){
- if(!d_partialModel.boundsAreEqual(v)){
- // If the bounds are equal this is already in the dioSolver
- //Add v = dr as a speculation.
- Comparison eq = mkIntegerEqualityFromAssignment(v);
- Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << endl;
- Assert(!eq.isBoolean());
- d_diosolver.pushInputConstraint(eq, eq.getNode());
- // It does not matter what the explanation of eq is.
- // It cannot be used in a conflict
- }
- }
- }
- }
-
- SumPair plane = d_diosolver.processEquationsForCut();
- if(plane.isZero()){
- return TrustNode::null();
- }else{
- Polynomial p = plane.getPolynomial();
- Polynomial c = Polynomial::mkPolynomial(plane.getConstant() * Constant::mkConstant(-1));
- Integer gcd = p.gcd();
- Assert(p.isIntegral());
- Assert(c.isIntegral());
- Assert(gcd > 1);
- Assert(!gcd.divides(c.asConstant().getNumerator()));
- Comparison leq = Comparison::mkComparison(LEQ, p, c);
- Comparison geq = Comparison::mkComparison(GEQ, p, c);
- Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode());
- Node rewrittenLemma = rewrite(lemma);
- Trace("arith::dio::ex") << "dioCutting found the plane: " << plane.getNode() << endl;
- Trace("arith::dio::ex") << "resulting in the cut: " << lemma << endl;
- Trace("arith::dio::ex") << "rewritten " << rewrittenLemma << endl;
- Trace("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl;
- Trace("arith::dio") << "resulting in the cut: " << lemma << endl;
- Trace("arith::dio") << "rewritten " << rewrittenLemma << endl;
- if (proofsEnabled())
- {
- NodeManager* nm = NodeManager::currentNM();
- Node gt = nm->mkNode(kind::GT, p.getNode(), c.getNode());
- Node lt = nm->mkNode(kind::LT, p.getNode(), c.getNode());
- TypeNode type = gt[0].getType();
-
- Pf pfNotLeq = d_pnm->mkAssume(leq.getNode().negate());
- Pf pfGt =
- d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotLeq}, {gt});
- Pf pfNotGeq = d_pnm->mkAssume(geq.getNode().negate());
- Pf pfLt =
- d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pfNotGeq}, {lt});
- Pf pfSum = d_pnm->mkNode(
- PfRule::MACRO_ARITH_SCALE_SUM_UB,
- {pfGt, pfLt},
- {nm->mkConstRealOrInt(type, -1), nm->mkConstRealOrInt(type, 1)});
- Pf pfBot = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pfSum}, {nm->mkConst<bool>(false)});
- std::vector<Node> assumptions = {leq.getNode().negate(),
- geq.getNode().negate()};
- Pf pfNotAndNot = d_pnm->mkScope(pfBot, assumptions);
- Pf pfOr = d_pnm->mkNode(PfRule::NOT_AND, {pfNotAndNot}, {});
- Pf pfRewritten = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pfOr}, {rewrittenLemma});
- return d_pfGen->mkTrustNode(rewrittenLemma, pfRewritten);
- }
- else
- {
- return TrustNode::mkTrustLemma(rewrittenLemma, nullptr);
- }
- }
-}
-
-Node TheoryArithPrivate::callDioSolver(){
- while(!d_constantIntegerVariables.empty()){
- ArithVar v = d_constantIntegerVariables.front();
- d_constantIntegerVariables.pop();
-
- Trace("arith::dio") << "callDioSolver " << v << endl;
-
- Assert(isInteger(v));
- Assert(d_partialModel.boundsAreEqual(v));
-
- ConstraintP lb = d_partialModel.getLowerBoundConstraint(v);
- ConstraintP ub = d_partialModel.getUpperBoundConstraint(v);
-
- Node orig = Node::null();
- if(lb->isEquality()){
- orig = Constraint::externalExplainByAssertions({lb});
- }else if(ub->isEquality()){
- orig = Constraint::externalExplainByAssertions({ub});
- }else {
- orig = Constraint::externalExplainByAssertions(ub, lb);
- }
-
- Assert(d_partialModel.assignmentIsConsistent(v));
-
- Comparison eq = mkIntegerEqualityFromAssignment(v);
-
- if(eq.isBoolean()){
- //This can only be a conflict
- Assert(!eq.getNode().getConst<bool>());
-
- //This should be handled by the normal form earlier in the case of equality
- Assert(orig.getKind() != EQUAL);
- return orig;
- }else{
- Trace("dio::push") << "dio::push " << v << " " << eq.getNode() << " with reason " << orig << endl;
- d_diosolver.pushInputConstraint(eq, orig);
- }
- }
-
- return d_diosolver.processEquationsForConflict();
-}
-
-ConstraintP TheoryArithPrivate::constraintFromFactQueue(TNode assertion)
-{
- Kind simpleKind = Comparison::comparisonKind(assertion);
- ConstraintP constraint = d_constraintDatabase.lookup(assertion);
- if(constraint == NullConstraint){
- Assert(simpleKind == EQUAL || simpleKind == DISTINCT);
- bool isDistinct = simpleKind == DISTINCT;
- Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
- Assert(!isSetup(eq));
- Node reEq = rewrite(eq);
- Trace("arith::distinct::const") << "Assertion: " << assertion << std::endl;
- Trace("arith::distinct::const") << "Eq : " << eq << std::endl;
- Trace("arith::distinct::const") << "reEq : " << reEq << std::endl;
- if(reEq.getKind() == CONST_BOOLEAN){
- if(reEq.getConst<bool>() == isDistinct){
- // if is (not true), or false
- Assert((reEq.getConst<bool>() && isDistinct)
- || (!reEq.getConst<bool>() && !isDistinct));
- if (proofsEnabled())
- {
- Pf assume = d_pnm->mkAssume(assertion);
- std::vector<Node> assumptions = {assertion};
- Pf pf = d_pnm->mkScope(d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkAssume(assertion)},
- {}),
- assumptions);
- raiseBlackBoxConflict(assertion, pf);
- }
- else
- {
- raiseBlackBoxConflict(assertion);
- }
- }
- return NullConstraint;
- }
- Assert(reEq.getKind() != CONST_BOOLEAN);
- if(!isSetup(reEq)){
- setupAtom(reEq);
- }
- Node reAssertion = isDistinct ? reEq.notNode() : reEq;
- constraint = d_constraintDatabase.lookup(reAssertion);
-
- if(assertion != reAssertion){
- Trace("arith::nf") << "getting non-nf assertion " << assertion << " |-> " << reAssertion << endl;
- Assert(constraint != NullConstraint);
- d_assertionsThatDoNotMatchTheirLiterals.insert(assertion, constraint);
- }
- }
-
- Assert(constraint != NullConstraint);
-
- if(constraint->assertedToTheTheory()){
- //Do nothing
- return NullConstraint;
- }
- Assert(!constraint->assertedToTheTheory());
- bool inConflict = constraint->negationHasProof();
- constraint->setAssertedToTheTheory(assertion, inConflict);
-
- if(!constraint->hasProof()){
- Trace("arith::constraint") << "marking as constraint as self explaining " << endl;
- constraint->setAssumption(inConflict);
- } else {
- Trace("arith::constraint")
- << "already has proof: "
- << Constraint::externalExplainByAssertions({constraint});
- }
-
- if(TraceIsOn("arith::negatedassumption") && inConflict){
- ConstraintP negation = constraint->getNegation();
- if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
- debugPrintFacts();
- }
- Trace("arith::eq") << "negation has proof" << endl;
- Trace("arith::eq") << constraint << endl;
- Trace("arith::eq") << negation << endl;
- }
-
- if(inConflict){
- ConstraintP negation = constraint->getNegation();
- if(TraceIsOn("arith::negatedassumption") && negation->isAssumption()){
- debugPrintFacts();
- }
- Trace("arith::eq") << "negation has proof" << endl;
- Trace("arith::eq") << constraint << endl;
- Trace("arith::eq") << negation << endl;
- raiseConflict(negation, InferenceId::ARITH_CONF_FACT_QUEUE);
- return NullConstraint;
- }else{
- return constraint;
- }
-}
-
-bool TheoryArithPrivate::assertionCases(ConstraintP constraint){
- Assert(constraint->hasProof());
- Assert(!constraint->negationHasProof());
-
- ArithVar x_i = constraint->getVariable();
-
- switch(constraint->getType()){
- case UpperBound:
- if(isInteger(x_i) && constraint->isStrictUpperBound()){
- ConstraintP floorConstraint = constraint->getFloor();
- if(!floorConstraint->isTrue()){
- bool inConflict = floorConstraint->negationHasProof();
- if (TraceIsOn("arith::intbound")) {
- Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
- Trace("arith::intbound") << "constraint, after: " << floorConstraint << std::endl;
- }
- floorConstraint->impliedByIntTighten(constraint, inConflict);
- floorConstraint->tryToPropagate();
- if(inConflict){
- raiseConflict(floorConstraint, InferenceId::ARITH_TIGHTEN_FLOOR);
- return true;
- }
- }
- return AssertUpper(floorConstraint);
- }else{
- return AssertUpper(constraint);
- }
- case LowerBound:
- if(isInteger(x_i) && constraint->isStrictLowerBound()){
- ConstraintP ceilingConstraint = constraint->getCeiling();
- if(!ceilingConstraint->isTrue()){
- bool inConflict = ceilingConstraint->negationHasProof();
- if (TraceIsOn("arith::intbound")) {
- Trace("arith::intbound") << "literal, before: " << constraint->getLiteral() << std::endl;
- Trace("arith::intbound") << "constraint, after: " << ceilingConstraint << std::endl;
- }
- ceilingConstraint->impliedByIntTighten(constraint, inConflict);
- ceilingConstraint->tryToPropagate();
- if(inConflict){
- raiseConflict(ceilingConstraint, InferenceId::ARITH_TIGHTEN_CEIL);
- return true;
- }
- }
- return AssertLower(ceilingConstraint);
- }else{
- return AssertLower(constraint);
- }
- case Equality:
- return AssertEquality(constraint);
- case Disequality:
- return AssertDisequality(constraint);
- default:
- Unreachable();
- return false;
- }
-}
-/**
- * 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 TheoryArithPrivate::nextIntegerViolation(bool assumeBounds) const
-{
- ArithVar numVars = d_partialModel.getNumberOfVariables();
- ArithVar v = d_nextIntegerCheckVar;
- if (numVars > 0)
- {
- const ArithVar rrEnd = d_nextIntegerCheckVar;
- do
- {
- if (isIntegerInput(v))
- {
- if (!d_partialModel.integralAssignment(v))
- {
- if (assumeBounds || d_partialModel.assignmentIsConsistent(v))
- {
- return v;
- }
- }
- }
- 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 = nextIntegerViolation(true);
- if (next != ARITHVAR_SENTINEL)
- {
- d_nextIntegerCheckVar = next;
- if (TraceIsOn("arith::hasIntegerModel"))
- {
- Trace("arith::hasIntegerModel") << "has int model? " << next << endl;
- d_partialModel.printModel(next, Trace("arith::hasIntegerModel"));
- }
- return false;
- }
- else
- {
- return true;
- }
-}
-
-Node flattenAndSort(Node n){
- Kind k = n.getKind();
- switch(k){
- case kind::OR:
- case kind::AND:
- case kind::ADD:
- case kind::MULT:
- break;
- default:
- return n;
- }
-
- std::vector<Node> out;
- std::vector<Node> process;
- process.push_back(n);
- while(!process.empty()){
- Node b = process.back();
- process.pop_back();
- if(b.getKind() == k){
- for(Node::iterator i=b.begin(), end=b.end(); i!=end; ++i){
- process.push_back(*i);
- }
- } else {
- out.push_back(b);
- }
- }
- Assert(out.size() >= 2);
- std::sort(out.begin(), out.end());
- return NodeManager::currentNM()->mkNode(k, out);
-}
-
-
-
-/** Outputs conflicts to the output channel. */
-void TheoryArithPrivate::outputConflicts(){
- Trace("arith::conflict") << "outputting conflicts" << std::endl;
- Assert(anyConflict());
-
- if(!conflictQueueEmpty()){
- Assert(!d_conflicts.empty());
- for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){
- const std::pair<ConstraintCP, InferenceId>& conf = d_conflicts[i];
- const ConstraintCP& confConstraint = conf.first;
- bool hasProof = confConstraint->hasProof();
- Assert(confConstraint->inConflict());
- const ConstraintRule& pf = confConstraint->getConstraintRule();
- if (TraceIsOn("arith::conflict"))
- {
- pf.print(std::cout, options().smt.produceProofs);
- std::cout << std::endl;
- }
- if (TraceIsOn("arith::pf::tree"))
- {
- Trace("arith::pf::tree") << "\n\nTree:\n";
- confConstraint->printProofTree(Trace("arith::pf::tree"));
- confConstraint->getNegation()->printProofTree(Trace("arith::pf::tree"));
- }
-
- TrustNode trustedConflict = confConstraint->externalExplainConflict();
- Node conflict = trustedConflict.getNode();
-
- Trace("arith::conflict")
- << "d_conflicts[" << i << "] " << conflict
- << " has proof: " << hasProof << ", id = " << conf.second << endl;
- if(TraceIsOn("arith::normalize::external")){
- conflict = flattenAndSort(conflict);
- Trace("arith::conflict") << "(normalized to) " << conflict << endl;
- }
-
- if (isProofEnabled())
- {
- outputTrustedConflict(trustedConflict, conf.second);
- }
- else
- {
- outputConflict(conflict, conf.second);
- }
- }
- }
- if(!d_blackBoxConflict.get().isNull()){
- Node bb = d_blackBoxConflict.get();
- Trace("arith::conflict") << "black box conflict" << bb
- << endl;
- if(TraceIsOn("arith::normalize::external")){
- bb = flattenAndSort(bb);
- Trace("arith::conflict") << "(normalized to) " << bb << endl;
- }
- if (isProofEnabled() && d_blackBoxConflictPf.get())
- {
- auto confPf = d_blackBoxConflictPf.get();
- outputTrustedConflict(d_pfGen->mkTrustNode(bb, confPf, true), InferenceId::ARITH_BLACK_BOX);
- }
- else
- {
- outputConflict(bb, InferenceId::ARITH_BLACK_BOX);
- }
- }
-}
-
-bool TheoryArithPrivate::outputTrustedLemma(TrustNode lemma, InferenceId id)
-{
- Trace("arith::channel") << "Arith trusted lemma: " << lemma << std::endl;
- return d_containing.d_im.trustedLemma(lemma, id);
-}
-
-bool TheoryArithPrivate::outputLemma(TNode lem, InferenceId id) {
- Trace("arith::channel") << "Arith lemma: " << lem << std::endl;
- return d_containing.d_im.lemma(lem, id);
-}
-
-void TheoryArithPrivate::outputTrustedConflict(TrustNode conf, InferenceId id)
-{
- Trace("arith::channel") << "Arith trusted conflict: " << conf << std::endl;
- d_containing.d_im.trustedConflict(conf, id);
-}
-
-void TheoryArithPrivate::outputConflict(TNode lit, InferenceId id) {
- Trace("arith::channel") << "Arith conflict: " << lit << std::endl;
- d_containing.d_im.conflict(lit, id);
-}
-
-void TheoryArithPrivate::outputPropagate(TNode lit) {
- Trace("arith::channel") << "Arith propagation: " << lit << std::endl;
- // call the propagate lit method of the
- d_containing.d_im.propagateLit(lit);
-}
-
-void TheoryArithPrivate::outputRestart() {
- Trace("arith::channel") << "Arith restart!" << std::endl;
- (d_containing.d_out)->demandRestart();
-}
-
-bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){
- int level = context()->getLevel();
- Trace("approx")
- << "attemptSolveInteger " << d_qflraStatus
- << " " << emmmittedLemmaOrSplit
- << " " << effortLevel
- << " " << d_lastContextIntegerAttempted
- << " " << level
- << endl;
-
- if(d_qflraStatus == Result::UNSAT){ return false; }
- if(emmmittedLemmaOrSplit){ return false; }
- if (!options().arith.useApprox)
- {
- return false;
- }
- if(!ApproximateSimplex::enabled()){ return false; }
-
- if(Theory::fullEffort(effortLevel)){
- if(hasIntegerModel()){
- return false;
- }else{
- return getSolveIntegerResource();
- }
- }
-
- if(d_lastContextIntegerAttempted <= 0){
- if(hasIntegerModel()){
- d_lastContextIntegerAttempted = context()->getLevel();
- return false;
- }else{
- return getSolveIntegerResource();
- }
- }
-
- if (!options().arith.trySolveIntStandardEffort)
- {
- return false;
- }
-
- if (d_lastContextIntegerAttempted <= (level >> 2))
- {
- double d = (double)(d_solveIntMaybeHelp + 1)
- / (d_solveIntAttempts + 1 + level * level);
- if (Random::getRandom().pickWithProb(d))
- {
- return getSolveIntegerResource();
- }
- }
- return false;
-}
-
-bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){
- TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer);
-
- ++d_statistics.d_mipProofsAttempted;
-
- Assert(d_replayVariables.empty());
- Assert(d_replayConstraints.empty());
-
- size_t enteringPropN = d_currentPropagationList.size();
- Assert(conflictQueueEmpty());
- TreeLog& tl = getTreeLog();
- //tl.applySelected(); /* set row ids */
-
- d_replayedLemmas = false;
-
- /* use the try block for the purpose of pushing the sat context */
- context::Context::ScopedPush speculativePush(context());
- d_cmEnabled = false;
- std::vector<ConstraintCPVec> res =
- replayLogRec(approx, tl.getRootId(), NullConstraint, 1);
-
- if(res.empty()){
- ++d_statistics.d_replayAttemptFailed;
- }else{
- unsigned successes = 0;
- for(size_t i =0, N = res.size(); i < N; ++i){
- ConstraintCPVec& vec = res[i];
- Assert(vec.size() >= 2);
- for(size_t j=0, M = vec.size(); j < M; ++j){
- ConstraintCP at_j = vec[j];
- Assert(at_j->isTrue());
- if(!at_j->negationHasProof()){
- successes++;
- vec[j] = vec.back();
- vec.pop_back();
- ConstraintP neg_at_j = at_j->getNegation();
-
- Trace("approx::replayLog") << "Setting the proof for the replayLog conflict on:" << endl
- << " (" << neg_at_j->isTrue() <<") " << neg_at_j << endl
- << " (" << at_j->isTrue() <<") " << at_j << endl;
- neg_at_j->impliedByIntHole(vec, true);
- raiseConflict(at_j, InferenceId::ARITH_CONF_REPLAY_LOG);
- break;
- }
- }
- }
- if(successes > 0){
- ++d_statistics.d_mipProofsSuccessful;
- }
- }
-
- if(d_currentPropagationList.size() > enteringPropN){
- d_currentPropagationList.resize(enteringPropN);
- }
-
- /* It is not clear what the d_qflraStatus is at this point */
- d_qflraStatus = Result::UNKNOWN;
-
- 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); }
-
- Trace("approx::constraint") << "replayGetConstraint " << sum
- << " " << k
- << " " << rhs
- << endl;
-
- Assert(k == kind::LEQ || k == kind::GEQ);
-
- NodeManager* nm = NodeManager::currentNM();
- Node comparison =
- nm->mkNode(k, sum, nm->mkConstRealOrInt(sum.getType(), rhs));
- Node rewritten = rewrite(comparison);
- if(!(Comparison::isNormalAtom(rewritten))){
- return make_pair(NullConstraint, added);
- }
-
- Comparison cmp = Comparison::parseNormalForm(rewritten);
- if(cmp.isBoolean()){ return make_pair(NullConstraint, added); }
-
- Polynomial nvp = cmp.normalizedVariablePart();
- if(nvp.isZero()){ return make_pair(NullConstraint, added); }
-
- Node norm = nvp.getNode();
-
- ConstraintType t = Constraint::constraintTypeOfComparison(cmp);
- DeltaRational dr = cmp.normalizedDeltaRational();
-
- Trace("approx::constraint") << "rewriting " << rewritten << endl
- << " |-> " << norm << " " << t << " " << dr << endl;
-
- Assert(!branch || d_partialModel.hasArithVar(norm));
- ArithVar v = ARITHVAR_SENTINEL;
- if(d_partialModel.hasArithVar(norm)){
-
- v = d_partialModel.asArithVar(norm);
- Trace("approx::constraint")
- << "replayGetConstraint found " << norm << " |-> " << v << " @ "
- << context()->getLevel() << endl;
- Assert(!branch || d_partialModel.isIntegerInput(v));
- }else{
- v = requestArithVar(norm, true, true);
- d_replayVariables.push_back(v);
-
- added = v;
-
- Trace("approx::constraint")
- << "replayGetConstraint adding " << norm << " |-> " << v << " @ "
- << context()->getLevel() << endl;
-
- 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)
-{
- 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();
- std::optional<Rational> maybe_value =
- ApproximateSimplex::estimateWithCFE(dval);
- if (!maybe_value)
- {
- return make_pair(NullConstraint, ARITHVAR_SENTINEL);
- }
- Rational fl(maybe_value.value().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 toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum){
- Trace("arith::toSumNode") << "toSumNode() begin" << endl;
- NodeManager* nm = NodeManager::currentNM();
- DenseMap<Rational>::const_iterator iter, end;
- iter = sum.begin(), end = sum.end();
- std::vector<Node> children;
- for(; iter != end; ++iter){
- ArithVar x = *iter;
- if(!vars.hasNode(x)){ return Node::null(); }
- Node xNode = vars.asNode(x);
- const Rational& q = sum[x];
- Node mult = nm->mkNode(kind::MULT, mkRationalNode(q), xNode);
- Trace("arith::toSumNode") << "toSumNode() " << x << " " << mult << endl;
- children.push_back(mult);
- }
- Trace("arith::toSumNode") << "toSumNode() end" << endl;
- if (children.empty())
- {
- // NOTE: real type assumed here
- return nm->mkConstReal(Rational(0));
- }
- else if (children.size() == 1)
- {
- return children[0];
- }
- return nm->mkNode(kind::ADD, children);
-}
-
-ConstraintCP TheoryArithPrivate::vectorToIntHoleConflict(const ConstraintCPVec& conflict){
- Assert(conflict.size() >= 2);
- ConstraintCPVec exp(conflict.begin(), conflict.end()-1);
- ConstraintCP back = conflict.back();
- Assert(back->hasProof());
- ConstraintP negBack = back->getNegation();
- // This can select negBack multiple times so we need to test if negBack has a proof.
- if(negBack->hasProof()){
- // back is in conflict already
- } else {
- negBack->impliedByIntHole(exp, true);
- }
-
- return back;
-}
-
-void TheoryArithPrivate::intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict){
- ConstraintCP negConflicting = conflicting->getNegation();
- Assert(conflicting->hasProof());
- Assert(negConflicting->hasProof());
-
- conflict.push_back(conflicting);
- conflict.push_back(negConflicting);
-
- Constraint::assertionFringe(conflict);
-}
-
-void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){
- Assert(conflictQueueEmpty());
- std::vector< ConstraintCPVec > conflicts;
-
- approx->tryCut(nid, bci);
- Trace("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(context());
- 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);
- // Can change d_qflraStatus
-
- d_linEq.stopTrackingBoundCounts();
- d_partialModel.startQueueingBoundCounts();
- }
- for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){
-
- conflicts.push_back(ConstraintCPVec());
- intHoleConflictToVector(d_conflicts[i].first, conflicts.back());
- Constraint::assertionFringe(conflicts.back());
-
- // ConstraintCP conflicting = d_conflicts[i];
- // ConstraintCP negConflicting = conflicting->getNegation();
- // Assert(conflicting->hasProof());
- // Assert(negConflicting->hasProof());
-
- // conflicts.push_back(ConstraintCPVec());
- // ConstraintCPVec& back = conflicts.back();
- // back.push_back(conflicting);
- // back.push_back(negConflicting);
-
- // // remove the floor/ceiling contraint implied by bcneg
- // Constraint::assertionFringe(back);
- }
-
- if(TraceIsOn("approx::branch")){
- if(d_conflicts.empty()){
- entireStateIsConsistent("branchfailure");
- }
- }
- }
-
- Trace("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)){
- Trace("approx::branch") << "reraise " << conf << endl;
- ConstraintCP conflicting = vectorToIntHoleConflict(conf);
- raiseConflict(conflicting, InferenceId::ARITH_CONF_BRANCH_CUT);
- }else if(!bci.proven()){
- drop(conf, bcneg);
- bci.setExplanation(conf);
- Trace("approx::branch") << "dropped " << bci << endl;
- }
- }
-}
-
-void TheoryArithPrivate::replayAssert(ConstraintP c) {
- if(!c->assertedToTheTheory()){
- bool inConflict = c->negationHasProof();
- if(!c->hasProof()){
- c->setInternalAssumption(inConflict);
- Trace("approx::replayAssert") << "replayAssert " << c << " set internal" << endl;
- }else{
- Trace("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl;
- }
- Trace("approx::replayAssert") << "replayAssertion " << c << endl;
- if(inConflict){
- raiseConflict(c, InferenceId::ARITH_CONF_REPLAY_ASSERT);
- }else{
- assertionCases(c);
- }
- }else{
- Trace("approx::replayAssert")
- << "replayAssert " << c << " already asserted" << endl;
- }
-}
-
-
-void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const {
- Trace("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);
- Trace("arith::resolveOutPropagated")
- << " conf["<<i<<"] " << orig << " to " << conf.size() << endl;
- }
- Trace("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 CVC5_UNUSED = 0;
- int subsumed CVC5_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++) {
- // i is not subsumed
- for (size_t j = i + 1; j < confs.size();) {
- ConstraintCPVec& a = confs[i];
- 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++;
- }
- }
- }
- Trace("arith::subsumption") << "subsumed " << subsumed << "/" << checks
- << endl;
-}
-
-std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){
- ++(d_statistics.d_replayLogRecCount);
- Trace("approx::replayLogRec") << "replayLogRec()" << 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(context());
- 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;
- if(!(conflictQueueEmpty() || ci->reconstructed())){
- ++d_statistics.d_numBranchesFailed;
- }
- }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().arith.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(TraceIsOn("approx::replayLogRec")){
- Trace("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl;
- }
-
- if(ci->proven()){
- ++d_statistics.d_cutsProven;
-
- const ConstraintCPVec& exp = ci->getExplanation();
- // success
- if(con->isTrue()){
- Trace("approx::replayLogRec") << "not asserted?" << endl;
- }else if(!con->negationHasProof()){
- con->impliedByIntHole(exp, false);
- replayAssert(con);
- Trace("approx::replayLogRec") << "cut prop" << endl;
- }else {
- con->impliedByIntHole(exp, true);
- Trace("approx::replayLogRec") << "cut into conflict " << con << endl;
- raiseConflict(con, InferenceId::ARITH_CONF_REPLAY_LOG_REC);
- }
- }else{
- ++d_statistics.d_cutsProofFailed;
- Trace("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().arith.replayEarlyCloseDepths >= 1);
- if (!nl.isBranch() || depth % options().arith.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);
- // can change d_qflraStatus
-
- 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(ConstraintCPVec());
- intHoleConflictToVector(d_conflicts[i].first, res.back());
- }
- ++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{
- res.push_back(conf);
- }
- }
- }else{
- Trace("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl;
- ++d_statistics.d_replayBranchSkips;
- }
-
- if(res.empty()){
- upres = replayLogRec(approx, upid, upc, depth+1);
-
- 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{
- Trace("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{
- Trace("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl;
- }
- Trace("approx::replayLogRec") << "found #"<<res.size()<<" conflicts on branch " << nid << endl;
- if(res.empty()){
- ++d_statistics.d_replayBranchCloseFailures;
- }
-
- }else{
- Trace("approx::replayLogRec") << "failed to make a branch " << nid << endl;
- }
- }else{
- ++d_statistics.d_replayLeafCloseFailures;
- Trace("approx::replayLogRec") << "failed on node " << nid << endl;
- Assert(res.empty());
- }
- resolveOutPropagated(res, propagated);
- Trace("approx::replayLogRec") << "replayLogRec() ending" << std::endl;
-
- if (options().arith.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()){
- TrustNode 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()){
- TrustNode 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. */
- auto ci = d_tableau.colIterator(v);
- Assert(!ci.atEnd());
- ArithVar b = d_tableau.rowIndexToBasic((*ci).getRowIndex());
- 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);
- Trace("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
-{
- 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();
- std::optional<Rational> maybe_value =
- ApproximateSimplex::estimateWithCFE(dval);
- if (!maybe_value)
- {
- return Node::null();
- }
- Rational fl(maybe_value.value().floor());
- NodeManager* nm = NodeManager::currentNM();
- Node leq =
- nm->mkNode(kind::LEQ, n, nm->mkConstRealOrInt(n.getType(), fl));
- Node norm = 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()){
- NodeManager* nm = NodeManager::currentNM();
- Kind k = ci.getKind();
- Assert(k == kind::LEQ || k == kind::GEQ);
- Node rhs = nm->mkConstRealOrInt(sum.getType(), ci.getReconstruction().rhs);
- Node ineq = nm->mkNode(k, sum, rhs);
- return rewrite(ineq);
- }
- return Node::null();
-}
-
-bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){
- ++(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().arith.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 = rewrite(cutConstraint);
- anythingnew = anythingnew || !isSatLiteral(implied);
-
- Node implication = asLemma.impNode(implied);
- // DO NOT CALL OUTPUT LEMMA!
- // TODO (project #37): justify
- d_approxCuts.push_back(TrustNode::mkTrustLemma(implication, nullptr));
- Trace("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());
- if (proofsEnabled())
- {
- d_pfGen->mkTrustNode(branch, PfRule::SPLIT, {}, {lit});
- }
- else
- {
- d_approxCuts.push_back(TrustNode::mkTrustLemma(branch, nullptr));
- }
- ++(d_statistics.d_mipExternalBranch);
- Trace("approx::lemmas") << "branching "<< root <<" as " << branch << endl;
- }
- }
- return anythingnew;
-}
-
-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 codeTimer0(d_statistics.d_solveIntTimer);
-
- ++(d_statistics.d_solveIntCalls);
- d_statistics.d_inSolveInteger = 1;
-
- if(!Theory::fullEffort(effortLevel)){
- d_solveIntAttempts++;
- ++(d_statistics.d_solveStandardEffort);
- }
-
- // if integers are attempted,
- Assert(options().arith.useApprox);
- Assert(ApproximateSimplex::enabled());
-
- int level = context()->getLevel();
- d_lastContextIntegerAttempted = level;
-
- static constexpr int32_t mipLimit = 200000;
-
- TreeLog& tl = getTreeLog();
- ApproximateStatistics& stats = getApproxStats();
- ApproximateSimplex* approx =
- ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats);
-
- approx->setPivotLimit(mipLimit);
- if(!d_guessedCoeffSet){
- d_guessedCoeffs = approx->heuristicOptCoeffs();
- d_guessedCoeffSet = true;
- }
- if(!d_guessedCoeffs.empty()){
- approx->setOptCoeffs(d_guessedCoeffs);
- }
- static constexpr int32_t depthForLikelyInfeasible = 10;
- int maxDepthPass1 = d_likelyIntegerInfeasible
- ? depthForLikelyInfeasible
- : options().arith.maxApproxDepth;
- approx->setBranchingDepth(maxDepthPass1);
- approx->setBranchOnVariableLimit(100);
- LinResult relaxRes = approx->solveRelaxation();
- if( relaxRes == LinFeasible ){
- MipResult mipRes = MipUnknown;
- {
- TimerStat::CodeTimer codeTimer1(d_statistics.d_mipTimer);
- mipRes = approx->solveMIP(false);
- }
-
- Trace("arith::solveInteger") << "mipRes " << mipRes << endl;
- switch(mipRes) {
- case MipBingo:
- // attempt the solution
- {
- ++(d_statistics.d_solveIntModelsAttempts);
-
- d_partialModel.stopQueueingBoundCounts();
- UpdateTrackingCallback utcb(&d_linEq);
- d_partialModel.processBoundsQueue(utcb);
- d_linEq.startTrackingBoundCounts();
-
- ApproximateSimplex::Solution mipSolution;
- mipSolution = approx->extractMIP();
- importSolution(mipSolution);
- solveRelaxationOrPanic(effortLevel);
-
- if (d_qflraStatus == Result::SAT)
- {
- if (!anyConflict())
- {
- if (ARITHVAR_SENTINEL == nextIntegerViolation(false))
- {
- ++(d_statistics.d_solveIntModelsSuccessful);
- }
- }
- }
-
- // shutdown simplex
- d_linEq.stopTrackingBoundCounts();
- d_partialModel.startQueueingBoundCounts();
- }
- break;
- case MipClosed:
- /* All integer branches closed */
- approx->setPivotLimit(2*mipLimit);
- {
- TimerStat::CodeTimer codeTimer2(d_statistics.d_mipTimer);
- mipRes = approx->solveMIP(true);
- }
-
- if(mipRes == MipClosed){
- d_likelyIntegerInfeasible = true;
- replayLog(approx);
- AlwaysAssert(anyConflict() || d_qflraStatus != Result::SAT);
-
- if (!anyConflict())
- {
- solveRealRelaxation(effortLevel);
- }
- }
- if(!(anyConflict() || !d_approxCuts.empty())){
- turnOffApproxFor(options().arith.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);
- {
- TimerStat::CodeTimer codeTimer3(d_statistics.d_mipTimer);
- mipRes = approx->solveMIP(true);
- }
- replayLemmas(approx);
- break;
- case MipUnknown:
- break;
- }
- }
- delete approx;
-
- if(!Theory::fullEffort(effortLevel)){
- if(anyConflict() || !d_approxCuts.empty()){
- d_solveIntMaybeHelp++;
- }
- }
-
- d_statistics.d_inSolveInteger = 0;
-}
-
-SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){
- if(pass1){
- if(d_pass1SDP == NULL){
- if (options().arith.useFC)
- {
- d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
- }
- else if (options().arith.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().arith.useFC)
- {
- d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex);
- }
- else if (options().arith.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(TraceIsOn("arith::importSolution")){
- Trace("arith::importSolution") << "importSolution before " << d_qflraStatus << endl;
- d_partialModel.printEntireModel(Trace("arith::importSolution"));
- }
-
- d_qflraStatus = d_attemptSolSimplex.attempt(solution);
-
- if(TraceIsOn("arith::importSolution")){
- Trace("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl;
- d_partialModel.printEntireModel(Trace("arith::importSolution"));
- }
-
- if(d_qflraStatus != Result::UNSAT){
- static constexpr int64_t pass2Limit = 20;
- SimplexDecisionProcedure& simplex = selectSimplex(false);
- simplex.setVarOrderPivotLimit(pass2Limit);
- d_qflraStatus = simplex.findModel(false);
- }
-
- if(TraceIsOn("arith::importSolution")){
- Trace("arith::importSolution") << "importSolution after " << d_qflraStatus << endl;
- d_partialModel.printEntireModel(Trace("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::UNKNOWN)
- {
- d_qflraStatus = selectSimplex(true).findModel(false);
- }
-
- if (Theory::fullEffort(effortLevel) && d_qflraStatus == Result::UNKNOWN)
- {
- ArithVar canBranch = nextIntegerViolation(false);
- if (canBranch != ARITHVAR_SENTINEL)
- {
- ++d_statistics.d_panicBranches;
- TrustNode branch = branchIntegerVariable(canBranch);
- Assert(branch.getNode().getKind() == kind::OR);
- Node rwbranch = rewrite(branch.getNode()[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 codeTimer0(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().arith.restrictedPivots;
-
- SimplexDecisionProcedure& simplex = selectSimplex(true);
-
- bool useApprox = options().arith.useApprox && ApproximateSimplex::enabled()
- && getSolveIntegerResource();
-
- Trace("TheoryArithPrivate::solveRealRelaxation")
- << "solveRealRelaxation() approx"
- << " " << options().arith.useApprox << " "
- << ApproximateSimplex::enabled() << " " << useApprox << " "
- << safeToCallApprox() << endl;
-
- bool noPivotLimitPass1 = noPivotLimit && !useApprox;
- d_qflraStatus = simplex.findModel(noPivotLimitPass1);
-
- Trace("TheoryArithPrivate::solveRealRelaxation")
- << "solveRealRelaxation()" << " pass1 " << d_qflraStatus << endl;
-
- if (d_qflraStatus == Result::UNKNOWN && useApprox && safeToCallApprox())
- {
- // pass2: fancy-final
- static constexpr 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 = LinUnknown;
- {
- TimerStat::CodeTimer codeTimer1(d_statistics.d_lpTimer);
- relaxRes = approxSolver->solveRelaxation();
- }
- Trace("solveRealRelaxation") << "solve relaxation? " << endl;
- switch(relaxRes){
- case LinFeasible:
- Trace("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;
- Trace("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;
- Trace("solveRealRelaxation") << "exhuasted " << endl;
- break;
- case LinUnknown:
- default:
- ++d_statistics.d_relaxOthers;
- break;
- }
- delete approxSolver;
-
- }
-
- bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel);
-
- // 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().isRealOrInt())
- {
- 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;
- }
-}
-
-bool TheoryArithPrivate::preCheck(Theory::Effort level)
-{
- Assert(d_currentPropagationList.empty());
- if(TraceIsOn("arith::consistency")){
- Assert(unenqueuedVariablesAreConsistent());
- }
-
- d_newFacts = !done();
- // If d_previousStatus == SAT, then reverts on conflicts are safe
- // Otherwise, they are not and must be committed.
- d_previousStatus = d_qflraStatus;
- if (d_newFacts)
- {
- d_qflraStatus = Result::UNKNOWN;
- d_hasDoneWorkSinceCut = true;
- }
- return false;
-}
-
-void TheoryArithPrivate::preNotifyFact(TNode atom, bool pol, TNode fact)
-{
- ConstraintP curr = constraintFromFactQueue(fact);
- if (curr != NullConstraint)
- {
- bool res CVC5_UNUSED = assertionCases(curr);
- Assert(!res || anyConflict());
- }
-}
-
-bool TheoryArithPrivate::postCheck(Theory::Effort effortLevel)
-{
- if(!anyConflict()){
- while(!d_learnedBounds.empty()){
- // we may attempt some constraints twice. this is okay!
- ConstraintP curr = d_learnedBounds.front();
- d_learnedBounds.pop();
- Trace("arith::learned") << curr << endl;
-
- bool res CVC5_UNUSED = assertionCases(curr);
- Assert(!res || anyConflict());
-
- if(anyConflict()){ break; }
- }
- }
-
- if(anyConflict()){
- d_qflraStatus = Result::UNSAT;
- if (options().arith.revertArithModels && d_previousStatus == Result::SAT)
- {
- ++d_statistics.d_revertsOnConflicts;
- Trace("arith::bt") << "clearing here "
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- revertOutOfConflict();
- d_errorSet.clear();
- }else{
- ++d_statistics.d_commitsOnConflicts;
- Trace("arith::bt") << "committing here "
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- revertOutOfConflict();
- }
- outputConflicts();
- //cout << "unate conflict 1 " << effortLevel << std::endl;
- return true;
- }
-
-
- if(TraceIsOn("arith::print_assertions")) {
- debugPrintAssertions(Trace("arith::print_assertions"));
- }
-
- bool emmittedConflictOrSplit = false;
- Assert(d_conflicts.empty());
-
- bool useSimplex = d_qflraStatus != Result::SAT;
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "pre realRelax" << endl;
-
- if(useSimplex){
- emmittedConflictOrSplit = solveRealRelaxation(effortLevel);
- }
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "post realRelax" << endl;
-
-
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "pre solveInteger" << endl;
-
- if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){
- solveInteger(effortLevel);
- if(anyConflict()){
- ++d_statistics.d_commitsOnConflicts;
- Trace("arith::bt") << "committing here "
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- revertOutOfConflict();
- d_errorSet.clear();
- outputConflicts();
- return true;
- }
- }
-
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "post solveInteger" << endl;
-
- switch(d_qflraStatus){
- case Result::SAT:
- if (d_newFacts)
- {
- ++d_statistics.d_nontrivialSatChecks;
- }
-
- Trace("arith::bt") << "committing sap inConflit"
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- d_unknownsInARow = 0;
- if(TraceIsOn("arith::consistency")){
- Assert(entireStateIsConsistent("sat comit"));
- }
- if (useSimplex && options().arith.collectPivots)
- {
- if (options().arith.useFC)
- {
- d_statistics.d_satPivots << d_fcSimplex.getPivots();
- }
- else
- {
- d_statistics.d_satPivots << d_dualSimplex.getPivots();
- }
- }
- break;
- case Result::UNKNOWN:
- ++d_unknownsInARow;
- ++(d_statistics.d_unknownChecks);
- Assert(!Theory::fullEffort(effortLevel));
- Trace("arith::bt") << "committing unknown"
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- d_statistics.d_maxUnknownsInARow.maxAssign(d_unknownsInARow);
-
- if (useSimplex && options().arith.collectPivots)
- {
- if (options().arith.useFC)
- {
- d_statistics.d_unknownPivots << d_fcSimplex.getPivots();
- }
- else
- {
- d_statistics.d_unknownPivots << d_dualSimplex.getPivots();
- }
- }
- break;
- case Result::UNSAT:
- d_unknownsInARow = 0;
-
- ++d_statistics.d_commitsOnConflicts;
-
- Trace("arith::bt") << "committing on conflict"
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
- d_partialModel.commitAssignmentChanges();
- revertOutOfConflict();
-
- if(TraceIsOn("arith::consistency::comitonconflict")){
- entireStateIsConsistent("commit on conflict");
- }
- outputConflicts();
- emmittedConflictOrSplit = true;
- Trace("arith::conflict") << "simplex conflict" << endl;
-
- if (useSimplex && options().arith.collectPivots)
- {
- if (options().arith.useFC)
- {
- d_statistics.d_unsatPivots << d_fcSimplex.getPivots();
- }
- else
- {
- d_statistics.d_unsatPivots << d_dualSimplex.getPivots();
- }
- }
- break;
- default:
- Unimplemented();
- }
- d_statistics.d_avgUnknownsInARow << d_unknownsInARow;
-
- size_t nPivots = options().arith.useFC ? d_fcSimplex.getPivots()
- : d_dualSimplex.getPivots();
- for (std::size_t i = 0; i < nPivots; ++i)
- {
- d_containing.d_out->spendResource(
- Resource::ArithPivotStep);
- }
-
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "pre approx cuts" << endl;
- if(!d_approxCuts.empty()){
- bool anyFresh = false;
- while(!d_approxCuts.empty()){
- TrustNode lem = d_approxCuts.front();
- d_approxCuts.pop();
- Trace("arith::approx::cuts") << "approximate cut:" << lem << endl;
- anyFresh = anyFresh || hasFreshArithLiteral(lem.getNode());
- Trace("arith::lemma") << "approximate cut:" << lem << endl;
- outputTrustedLemma(lem, InferenceId::ARITH_APPROX_CUT);
- }
- if(anyFresh){
- emmittedConflictOrSplit = true;
- }
- }
-
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "post approx cuts" << endl;
-
- // This should be fine if sat or unknown
- if (!emmittedConflictOrSplit
- && (options().arith.arithPropagationMode
- == options::ArithPropagationMode::UNATE_PROP
- || options().arith.arithPropagationMode
- == options::ArithPropagationMode::BOTH_PROP))
- {
- TimerStat::CodeTimer codeTimer0(d_statistics.d_newPropTime);
- Assert(d_qflraStatus != Result::UNSAT);
-
- while(!d_currentPropagationList.empty() && !anyConflict()){
- ConstraintP curr = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
-
- ConstraintType t = curr->getType();
- Assert(t != Disequality)
- << "Disequalities are not allowed in d_currentPropagation";
-
- switch(t){
- case LowerBound:
- {
- ConstraintP prev = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropLowerBound(curr, prev);
- break;
- }
- case UpperBound:
- {
- ConstraintP prev = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropUpperBound(curr, prev);
- break;
- }
- case Equality:
- {
- ConstraintP prevLB = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- ConstraintP prevUB = d_currentPropagationList.front();
- d_currentPropagationList.pop_front();
- d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB);
- break;
- }
- default: Unhandled() << curr->getType();
- }
- }
-
- if(anyConflict()){
- Trace("arith::unate") << "unate conflict" << endl;
- revertOutOfConflict();
- d_qflraStatus = Result::UNSAT;
- outputConflicts();
- emmittedConflictOrSplit = true;
- //cout << "unate conflict " << endl;
- Trace("arith::bt") << "committing on unate conflict"
- << " " << d_newFacts << " " << d_previousStatus << " "
- << d_qflraStatus << endl;
-
- Trace("arith::conflict") << "unate arith conflict" << endl;
- }
- }
- else
- {
- TimerStat::CodeTimer codeTimer1(d_statistics.d_newPropTime);
- d_currentPropagationList.clear();
- }
- Assert(d_currentPropagationList.empty());
-
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "post unate" << endl;
-
- if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
- ++d_fullCheckCounter;
- }
- if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
- emmittedConflictOrSplit = splitDisequalities();
- }
- Trace("arith::ems") << "ems: " << emmittedConflictOrSplit
- << "pos splitting" << endl;
-
-
- Trace("arith") << "integer? "
- << " conf/split " << emmittedConflictOrSplit
- << " fulleffort " << Theory::fullEffort(effortLevel) << endl;
-
- if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
- Node possibleConflict = Node::null();
- if (!emmittedConflictOrSplit && options().arith.arithDioSolver)
- {
- possibleConflict = callDioSolver();
- if(possibleConflict != Node::null()){
- revertOutOfConflict();
- Trace("arith::conflict") << "dio conflict " << possibleConflict << endl;
- // TODO (project #37): justify (proofs in the DIO solver)
- raiseBlackBoxConflict(possibleConflict);
- outputConflicts();
- emmittedConflictOrSplit = true;
- }
- }
-
- if (!emmittedConflictOrSplit && d_hasDoneWorkSinceCut
- && options().arith.arithDioSolver)
- {
- if(getDioCuttingResource()){
- TrustNode possibleLemma = dioCutting();
- if(!possibleLemma.isNull()){
- d_hasDoneWorkSinceCut = false;
- d_cutCount = d_cutCount + 1;
- Trace("arith::lemma") << "dio cut " << possibleLemma << endl;
- if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_DIO_CUT))
- {
- emmittedConflictOrSplit = true;
- }
- }
- }
- }
-
- if(!emmittedConflictOrSplit) {
- TrustNode possibleLemma = roundRobinBranch();
- if (!possibleLemma.getNode().isNull())
- {
- ++(d_statistics.d_externalBranchAndBounds);
- d_cutCount = d_cutCount + 1;
- Trace("arith::lemma") << "rrbranch lemma"
- << possibleLemma << endl;
- if (outputTrustedLemma(possibleLemma, InferenceId::ARITH_BB_LEMMA))
- {
- emmittedConflictOrSplit = true;
- }
- }
- }
-
- if (options().arith.maxCutsInContext <= d_cutCount)
- {
- if(d_diosolver.hasMoreDecompositionLemmas()){
- while(d_diosolver.hasMoreDecompositionLemmas()){
- Node decompositionLemma = d_diosolver.nextDecompositionLemma();
- Trace("arith::lemma") << "dio decomposition lemma "
- << decompositionLemma << endl;
- outputLemma(decompositionLemma, InferenceId::ARITH_DIO_DECOMPOSITION);
- }
- }else{
- Trace("arith::restart") << "arith restart!" << endl;
- outputRestart();
- }
- }
- }//if !emmittedConflictOrSplit && fullEffort(effortLevel) && !hasIntegerModel()
-
- if(Theory::fullEffort(effortLevel)){
- if(TraceIsOn("arith::consistency::final")){
- entireStateIsConsistent("arith::consistency::final");
- }
- }
-
- if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
- if(TraceIsOn("arith::print_model")) {
- debugPrintModel(Trace("arith::print_model"));
- }
- Trace("arith") << "TheoryArithPrivate::check end" << std::endl;
- return emmittedConflictOrSplit;
-}
-
-bool TheoryArithPrivate::foundNonlinear() const { return d_foundNl; }
-
-TrustNode TheoryArithPrivate::branchIntegerVariable(ArithVar x) const
-{
- const DeltaRational& d = d_partialModel.getAssignment(x);
- Assert(!d.isIntegral());
- const Rational& r = d.getNoninfinitesimalPart();
- const Rational& i = d.getInfinitesimalPart();
- Trace("integers") << "integers: assignment to [[" << d_partialModel.asNode(x) << "]] is " << r << "[" << i << "]" << endl;
- Assert(!(r.getDenominator() == 1 && i.getNumerator() == 0));
- TNode var = d_partialModel.asNode(x);
- TrustNode lem = d_bab.branchIntegerVariable(var, r);
- if (TraceIsOn("integers"))
- {
- Node l = lem.getNode();
- if (isSatLiteral(l[0]))
- {
- Trace("integers") << " " << l[0] << " == " << getSatValue(l[0])
- << endl;
- }
- else
- {
- Trace("integers") << " " << l[0] << " is not assigned a SAT literal"
- << endl;
- }
- if (isSatLiteral(l[1]))
- {
- Trace("integers") << " " << l[1] << " == " << getSatValue(l[1])
- << endl;
- }
- else
- {
- Trace("integers") << " " << l[1] << " is not assigned a SAT literal"
- << endl;
- }
- }
- return lem;
-}
-
-std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{
- vector<ArithVar> lemmas;
- ArithVar max = d_partialModel.getNumberOfVariables();
-
- if (options().arith.doCutAllBounded && max > 0)
- {
- for(ArithVar iter = 0; iter != max; ++iter){
- //Do not include slack variables
- const DeltaRational& d = d_partialModel.getAssignment(iter);
- if(isIntegerInput(iter) &&
- !d_cutInContext.contains(iter) &&
- d_partialModel.hasUpperBound(iter) &&
- d_partialModel.hasLowerBound(iter) &&
- !d.isIntegral()){
- lemmas.push_back(iter);
- }
- }
- }
- return lemmas;
-}
-
-/** Returns true if the roundRobinBranching() issues a lemma. */
-TrustNode TheoryArithPrivate::roundRobinBranch()
-{
- if(hasIntegerModel()){
- return TrustNode::null();
- }else{
- ArithVar v = d_nextIntegerCheckVar;
-
- Assert(isInteger(v));
- Assert(!isAuxiliaryVariable(v));
- return branchIntegerVariable(v);
- }
-}
-
-bool TheoryArithPrivate::splitDisequalities(){
- bool splitSomething = false;
-
- vector<ConstraintP> save;
-
- while(!d_diseqQueue.empty()){
- ConstraintP front = d_diseqQueue.front();
- d_diseqQueue.pop();
-
- if(front->isSplit()){
- Trace("arith::eq") << "split already" << endl;
- }else{
- Trace("arith::eq") << "not split already" << endl;
-
- ArithVar lhsVar = front->getVariable();
-
- const DeltaRational& lhsValue = d_partialModel.getAssignment(lhsVar);
- const DeltaRational& rhsValue = front->getValue();
- if(lhsValue == rhsValue){
- Trace("arith::lemma") << "Splitting on " << front << endl;
- Trace("arith::lemma") << "LHS value = " << lhsValue << endl;
- Trace("arith::lemma") << "RHS value = " << rhsValue << endl;
- TrustNode lemma = front->split();
- ++(d_statistics.d_statDisequalitySplits);
-
- Trace("arith::lemma") << "Now " << rewrite(lemma.getNode()) << endl;
- outputTrustedLemma(lemma, InferenceId::ARITH_SPLIT_DEQ);
- // cout << "Now " << rewrite(lemma) << endl;
- splitSomething = true;
- }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){
- Trace("arith::eq") << "can drop as less than lb" << front << endl;
- }else if(d_partialModel.strictlyGreaterThanUpperBound(lhsVar, rhsValue)){
- Trace("arith::eq") << "can drop as greater than ub" << front << endl;
- }else{
- Trace("arith::eq") << "save" << front << ": " <<lhsValue << " != " << rhsValue << endl;
- save.push_back(front);
- }
- }
- }
- vector<ConstraintP>::const_iterator i=save.begin(), i_end = save.end();
- for(; i != i_end; ++i){
- d_diseqQueue.push(*i);
- }
- return splitSomething;
-}
-
-/**
- * Should be guarded by at least TraceIsOn("arith::print_assertions").
- * Prints to Trace("arith::print_assertions")
- */
-void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const {
- out << "Assertions:" << endl;
- for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar i = *vi;
- if (d_partialModel.hasLowerBound(i)) {
- ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i);
- out << lConstr << endl;
- }
-
- if (d_partialModel.hasUpperBound(i)) {
- ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i);
- out << uConstr << endl;
- }
- }
- 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;
- }
-}
-
-void TheoryArithPrivate::debugPrintModel(std::ostream& out) const{
- out << "Model:" << endl;
- for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar i = *vi;
- if(d_partialModel.hasNode(i)){
- out << d_partialModel.asNode(i) << " : " <<
- d_partialModel.getAssignment(i);
- if(d_tableau.isBasic(i)){
- out << " (basic)";
- }
- out << endl;
- }
- }
-}
-
-TrustNode TheoryArithPrivate::explain(TNode n)
-{
- Trace("arith::explain") << "explain @" << context()->getLevel() << ": " << n
- << endl;
-
- ConstraintP c = d_constraintDatabase.lookup(n);
- TrustNode exp;
- if(c != NullConstraint){
- Assert(!c->isAssumption());
- exp = c->externalExplainForPropagation(n);
- Trace("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
- }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
- c = d_assertionsThatDoNotMatchTheirLiterals[n];
- if(!c->isAssumption()){
- exp = c->externalExplainForPropagation(n);
- Trace("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
- }else{
- Trace("arith::explain") << "this is a strange mismatch" << n << endl;
- Assert(d_congruenceManager.canExplain(n));
- exp = d_congruenceManager.explain(n);
- }
- }else{
- Assert(d_congruenceManager.canExplain(n));
- Trace("arith::explain") << "dm explanation" << n << endl;
- exp = d_congruenceManager.explain(n);
- }
- return exp;
-}
-
-void TheoryArithPrivate::propagate(Theory::Effort e) {
- // This uses model values for safety. Disable for now.
- if (d_qflraStatus == Result::SAT
- && (options().arith.arithPropagationMode
- == options::ArithPropagationMode::BOUND_INFERENCE_PROP
- || options().arith.arithPropagationMode
- == options::ArithPropagationMode::BOTH_PROP)
- && hasAnyUpdates())
- {
- if (options().arith.newProp)
- {
- propagateCandidatesNew();
- }
- else
- {
- propagateCandidates();
- }
- }
- else
- {
- clearUpdates();
- }
-
- while(d_constraintDatabase.hasMorePropagations()){
- ConstraintCP c = d_constraintDatabase.nextPropagation();
- Trace("arith::prop") << "next prop" << context()->getLevel() << ": " << c
- << endl;
-
- if(c->negationHasProof()){
- Trace("arith::prop") << "negation has proof " << c->getNegation() << endl;
- Trace("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!";
-
- if(!c->assertedToTheTheory()){
- Node literal = c->getLiteral();
- Trace("arith::prop") << "propagating @" << context()->getLevel() << " "
- << literal << endl;
-
- outputPropagate(literal);
- }else{
- Trace("arith::prop") << "already asserted to the theory " << c->getLiteral() << endl;
- }
- }
-
- NodeManager* nm = NodeManager::currentNM();
- while(d_congruenceManager.hasMorePropagations()){
- TNode toProp = d_congruenceManager.getNextPropagation();
-
- //Currently if the flag is set this came from an equality detected by the
- //equality engine in the the difference manager.
- Node normalized = rewrite(toProp);
-
- ConstraintP constraint = d_constraintDatabase.lookup(normalized);
- if(constraint == NullConstraint){
- Trace("arith::prop") << "propagating on non-constraint? " << toProp << endl;
-
- outputPropagate(toProp);
- }else if(constraint->negationHasProof()){
- // The congruence manager can prove: antecedents => toProp,
- // ergo. antecedents ^ ~toProp is a conflict.
- TrustNode exp = d_congruenceManager.explain(toProp);
- Node notNormalized = normalized.negate();
- std::vector<Node> ants(exp.getNode().begin(), exp.getNode().end());
- ants.push_back(notNormalized);
- Node lp = nm->mkAnd(ants);
- Trace("arith::prop") << "propagate conflict" << lp << endl;
- if (proofsEnabled())
- {
- // Assume all of antecedents and ~toProp (rewritten)
- std::vector<Pf> pfAntList;
- for (size_t i = 0; i < ants.size(); ++i)
- {
- pfAntList.push_back(d_pnm->mkAssume(ants[i]));
- }
- Pf pfAnt = pfAntList.size() > 1
- ? d_pnm->mkNode(PfRule::AND_INTRO, pfAntList, {})
- : pfAntList[0];
- // Use modus ponens to get toProp (un rewritten)
- Pf pfConc = d_pnm->mkNode(
- PfRule::MODUS_PONENS,
- {pfAnt, exp.getGenerator()->getProofFor(exp.getProven())},
- {});
- // prove toProp (rewritten)
- Pf pfConcRewritten = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {pfConc}, {normalized});
- Pf pfNotNormalized = d_pnm->mkAssume(notNormalized);
- // prove bottom from toProp and ~toProp
- Pf pfBot;
- if (normalized.getKind() == kind::NOT)
- {
- pfBot = d_pnm->mkNode(
- PfRule::CONTRA, {pfNotNormalized, pfConcRewritten}, {});
- }
- else
- {
- pfBot = d_pnm->mkNode(
- PfRule::CONTRA, {pfConcRewritten, pfNotNormalized}, {});
- }
- // close scope
- Pf pfNotAnd = d_pnm->mkScope(pfBot, ants);
- raiseBlackBoxConflict(lp, pfNotAnd);
- }
- else
- {
- raiseBlackBoxConflict(lp);
- }
- outputConflicts();
- return;
- }else{
- Trace("arith::prop") << "propagating still?" << toProp << endl;
- outputPropagate(toProp);
- }
- }
-}
-
-DeltaRational TheoryArithPrivate::getDeltaValue(TNode term) const
-{
- AlwaysAssert(d_qflraStatus != Result::UNKNOWN);
- Trace("arith::value") << term << std::endl;
-
- if (d_partialModel.hasArithVar(term)) {
- ArithVar var = d_partialModel.asArithVar(term);
- return d_partialModel.getAssignment(var);
- }
-
- switch (Kind kind = term.getKind()) {
- case kind::CONST_RATIONAL:
- case kind::CONST_INTEGER: return term.getConst<Rational>();
-
- case kind::ADD:
- { // 2+ args
- DeltaRational value(0);
- for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
- ++i) {
- value = value + getDeltaValue(*i);
- }
- return value;
- }
-
- case kind::NONLINEAR_MULT:
- case kind::MULT: { // 2+ args
- Assert(!isSetup(term));
- DeltaRational value(1);
- for (TNode::iterator i = term.begin(), iend = term.end(); i != iend;
- ++i) {
- value = value * getDeltaValue(*i);
- }
- return value;
- }
- case kind::SUB:
- { // 2 args
- return getDeltaValue(term[0]) - getDeltaValue(term[1]);
- }
- case kind::NEG:
- { // 1 arg
- return (-getDeltaValue(term[0]));
- }
-
- case kind::DIVISION: { // 2 args
- Assert(!isSetup(term));
- return getDeltaValue(term[0]) / getDeltaValue(term[1]);
- }
- case kind::DIVISION_TOTAL:
- case kind::INTS_DIVISION_TOTAL:
- case kind::INTS_MODULUS_TOTAL: { // 2 args
- Assert(!isSetup(term));
- DeltaRational denominator = getDeltaValue(term[1]);
- if (denominator.isZero()) {
- return DeltaRational(0, 0);
- }
- DeltaRational numerator = getDeltaValue(term[0]);
- if (kind == kind::DIVISION_TOTAL) {
- return numerator / denominator;
- } else if (kind == kind::INTS_DIVISION_TOTAL) {
- return Rational(numerator.euclidianDivideQuotient(denominator));
- } else {
- Assert(kind == kind::INTS_MODULUS_TOTAL);
- return Rational(numerator.euclidianDivideRemainder(denominator));
- }
- }
-
- default:
- throw ModelException(term, "No model assignment.");
- }
-}
-
-Rational TheoryArithPrivate::deltaValueForTotalOrder() const{
- Rational min(2);
- std::set<DeltaRational> relevantDeltaValues;
- context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin();
- context::CDQueue<ConstraintP>::const_iterator qiter_end = d_diseqQueue.end();
-
- for(; qiter != qiter_end; ++qiter){
- ConstraintP curr = *qiter;
-
- const DeltaRational& rhsValue = curr->getValue();
- relevantDeltaValues.insert(rhsValue);
- }
-
- Theory::shared_terms_iterator shared_iter = d_containing.shared_terms_begin();
- Theory::shared_terms_iterator shared_end = d_containing.shared_terms_end();
- for(; shared_iter != shared_end; ++shared_iter){
- Node sharedCurr = *shared_iter;
-
- // ModelException is fatal as this point. Don't catch!
- // DeltaRationalException is fatal as this point. Don't catch!
- DeltaRational val = getDeltaValue(sharedCurr);
- relevantDeltaValues.insert(val);
- }
-
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
- const DeltaRational& value = d_partialModel.getAssignment(v);
- relevantDeltaValues.insert(value);
- if( d_partialModel.hasLowerBound(v)){
- const DeltaRational& lb = d_partialModel.getLowerBound(v);
- relevantDeltaValues.insert(lb);
- }
- if( d_partialModel.hasUpperBound(v)){
- const DeltaRational& ub = d_partialModel.getUpperBound(v);
- relevantDeltaValues.insert(ub);
- }
- }
-
- if(relevantDeltaValues.size() >= 2){
- std::set<DeltaRational>::const_iterator iter = relevantDeltaValues.begin();
- std::set<DeltaRational>::const_iterator iter_end = relevantDeltaValues.end();
- DeltaRational prev = *iter;
- ++iter;
- for(; iter != iter_end; ++iter){
- const DeltaRational& curr = *iter;
-
- Assert(prev < curr);
-
- DeltaRational::seperatingDelta(min, prev, curr);
- prev = curr;
- }
- }
-
- Assert(min.sgn() > 0);
- Rational belowMin = min/Rational(2);
- return belowMin;
-}
-
-void TheoryArithPrivate::collectModelValues(const std::set<Node>& termSet,
- std::map<Node, Node>& arithModel)
-{
- AlwaysAssert(d_qflraStatus == Result::SAT);
-
- if(TraceIsOn("arith::collectModelInfo")){
- debugPrintFacts();
- }
-
- Trace("arith::collectModelInfo") << "collectModelInfo() begin " << endl;
-
- // Delta lasts at least the duration of the function call
- const Rational& delta = d_partialModel.getDelta();
- std::unordered_set<TNode> shared = d_containing.currentlySharedTerms();
-
- // TODO:
- // This is not very good for user push/pop....
- // Revisit when implementing push/pop
- NodeManager* nm = NodeManager::currentNM();
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar v = *vi;
-
- if(!isAuxiliaryVariable(v)){
- Node term = d_partialModel.asNode(v);
-
- if((theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end())
- && termSet.find(term) != termSet.end()){
-
- const DeltaRational& mod = d_partialModel.getAssignment(v);
- Rational qmodel = mod.substituteDelta(delta);
-
- Node qNode = nm->mkConstRealOrInt(term.getType(), qmodel);
- Trace("arith::collectModelInfo") << "m->assertEquality(" << term << ", " << qmodel << ", true)" << endl;
- // Add to the map
- arithModel[term] = qNode;
- }else{
- Trace("arith::collectModelInfo") << "Skipping m->assertEquality(" << term << ", true)" << endl;
-
- }
- }
- }
-
- // Iterate over equivalence classes in LinearEqualityModule
- // const eq::EqualityEngine& ee = d_congruenceManager.getEqualityEngine();
- // m->assertEqualityEngine(&ee);
-
- Trace("arith::collectModelInfo") << "collectModelInfo() end " << endl;
-}
-
-bool TheoryArithPrivate::safeToReset() const {
- Assert(!d_tableauSizeHasBeenModified);
- Assert(d_errorSet.noSignals());
-
- ErrorSet::error_iterator error_iter = d_errorSet.errorBegin();
- ErrorSet::error_iterator error_end = d_errorSet.errorEnd();
- for(; error_iter != error_end; ++error_iter){
- ArithVar basic = *error_iter;
- if(!d_smallTableauCopy.isBasic(basic)){
- return false;
- }
- }
-
- return true;
-}
-
-void TheoryArithPrivate::notifyRestart(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_restartTimer);
-
- if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
- ++d_restartsCounter;
- d_solveIntMaybeHelp = 0;
- d_solveIntAttempts = 0;
-}
-
-bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
- bool result = true;
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar var = *vi;
- //ArithVar var = d_partialModel.asArithVar(*i);
- if(!d_partialModel.assignmentIsConsistent(var)){
- d_partialModel.printModel(var);
- warning() << s << ":" << "Assignment is not consistent for " << var << d_partialModel.asNode(var);
- if(d_tableau.isBasic(var)){
- warning() << " (basic)";
- }
- warning() << std::endl;
- result = false;
- }else if(d_partialModel.isInteger(var) && !d_partialModel.integralAssignment(var)){
- d_partialModel.printModel(var);
- warning() << s << ":" << "Assignment is not integer for integer variable " << var << d_partialModel.asNode(var);
- if(d_tableau.isBasic(var)){
- warning() << " (basic)";
- }
- warning() << std::endl;
- result = false;
- }
- }
- return result;
-}
-
-bool TheoryArithPrivate::unenqueuedVariablesAreConsistent(){
- bool result = true;
- for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){
- ArithVar var = *vi;
- if(!d_partialModel.assignmentIsConsistent(var)){
- if(!d_errorSet.inError(var)){
-
- d_partialModel.printModel(var);
- warning() << "Unenqueued var is not consistent for " << var << d_partialModel.asNode(var);
- if(d_tableau.isBasic(var)){
- warning() << " (basic)";
- }
- warning() << std::endl;
- result = false;
- } else if(TraceIsOn("arith::consistency::initial")){
- d_partialModel.printModel(var);
- warning() << "Initial var is not consistent for " << var << d_partialModel.asNode(var);
- if(d_tableau.isBasic(var)){
- warning() << " (basic)";
- }
- warning() << std::endl;
- }
- }
- }
- return result;
-}
-
-void TheoryArithPrivate::presolve(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_presolveTime);
-
- d_statistics.d_initialTableauSize = d_tableau.size();
-
- if(TraceIsOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
- if(TraceIsOn("arith::presolve")) {
- Trace("arith::presolve") << "TheoryArithPrivate::presolve" << endl;
- }
-
- vector<TrustNode> lemmas;
- if (!options().base.incrementalSolving)
- {
- switch (options().arith.arithUnateLemmaMode)
- {
- case options::ArithUnateLemmaMode::NO: break;
- case options::ArithUnateLemmaMode::INEQUALITY:
- d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
- break;
- case options::ArithUnateLemmaMode::EQUALITY:
- d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
- break;
- case options::ArithUnateLemmaMode::ALL:
- d_constraintDatabase.outputUnateInequalityLemmas(lemmas);
- d_constraintDatabase.outputUnateEqualityLemmas(lemmas);
- break;
- default: Unhandled() << options().arith.arithUnateLemmaMode;
- }
- }
-
- vector<TrustNode>::const_iterator i = lemmas.begin(), i_end = lemmas.end();
- for(; i != i_end; ++i){
- TrustNode lem = *i;
- Trace("arith::oldprop") << " lemma lemma duck " <<lem << endl;
- outputTrustedLemma(lem, InferenceId::ARITH_UNATE);
- }
-}
-
-EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
- if (d_qflraStatus == Result::UNKNOWN)
- {
- return EQUALITY_UNKNOWN;
- }else{
- try {
- if (getDeltaValue(a) == getDeltaValue(b)) {
- return EQUALITY_TRUE_IN_MODEL;
- } else {
- return EQUALITY_FALSE_IN_MODEL;
- }
- } catch (DeltaRationalException& dr) {
- return EQUALITY_UNKNOWN;
- } catch (ModelException& me) {
- return EQUALITY_UNKNOWN;
- }
- }
-}
-
-bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
- ++d_statistics.d_boundComputations;
-
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
- DeltaRational bound = d_linEq.computeRowBound(ridx, upperBound, basic);
-
- if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) ||
- (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){
-
- // TODO: "Policy point"
- //We are only going to recreate the functionality for now.
- //In the future this can be improved to generate a temporary constraint
- //if none exists.
- //Experiment with doing this every time or only when the new constraint
- //implies an unknown fact.
-
- ConstraintType t = upperBound ? UpperBound : LowerBound;
- ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound);
-
- // Node bestImplied = upperBound ?
- // d_apm.getBestImpliedUpperBound(basic, bound):
- // d_apm.getBestImpliedLowerBound(basic, bound);
-
- if(bestImplied != NullConstraint){
- //This should be stronger
- Assert(!upperBound || bound <= bestImplied->getValue());
- Assert(
- !upperBound
- || d_partialModel.lessThanUpperBound(basic, bestImplied->getValue()));
-
- Assert(upperBound || bound >= bestImplied->getValue());
- Assert(upperBound
- || d_partialModel.greaterThanLowerBound(basic,
- bestImplied->getValue()));
- //slightly changed
-
- // ConstraintP c = d_constraintDatabase.lookup(bestImplied);
- // Assert(c != NullConstraint);
-
- bool assertedToTheTheory = bestImplied->assertedToTheTheory();
- bool canBePropagated = bestImplied->canBePropagated();
- bool hasProof = bestImplied->hasProof();
-
- Trace("arith::prop") << "arith::prop" << basic
- << " " << assertedToTheTheory
- << " " << canBePropagated
- << " " << hasProof
- << endl;
-
- if(bestImplied->negationHasProof()){
- warning() << "the negation of " << bestImplied << " : " << std::endl
- << "has proof " << bestImplied->getNegation() << std::endl
- << bestImplied->getNegation()->externalExplainByAssertions()
- << std::endl;
- }
-
- if(!assertedToTheTheory && canBePropagated && !hasProof ){
- d_linEq.propagateBasicFromRow(bestImplied, options().smt.produceProofs);
- // I think this can be skipped if canBePropagated is true
- //d_learnedBounds.push(bestImplied);
- if(TraceIsOn("arith::prop")){
- Trace("arith::prop") << "success " << bestImplied << endl;
- d_partialModel.printModel(basic, Trace("arith::prop"));
- }
- return true;
- }
- if(TraceIsOn("arith::prop")){
- Trace("arith::prop") << "failed " << basic
- << " " << bound
- << " " << assertedToTheTheory
- << " " << canBePropagated
- << " " << hasProof << endl;
- d_partialModel.printModel(basic, Trace("arith::prop"));
- }
- }
- }else if(TraceIsOn("arith::prop")){
- Trace("arith::prop") << "false " << bound << " ";
- d_partialModel.printModel(basic, Trace("arith::prop"));
- }
- return false;
-}
-
-void TheoryArithPrivate::propagateCandidate(ArithVar basic){
- bool success = false;
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
-
- bool tryLowerBound =
- d_partialModel.strictlyAboveLowerBound(basic) &&
- d_linEq.rowLacksBound(ridx, false, basic) == NULL;
-
- bool tryUpperBound =
- d_partialModel.strictlyBelowUpperBound(basic) &&
- d_linEq.rowLacksBound(ridx, true, basic) == NULL;
-
- if(tryLowerBound){
- success |= propagateCandidateLowerBound(basic);
- }
- if(tryUpperBound){
- success |= propagateCandidateUpperBound(basic);
- }
- if(success){
- ++d_statistics.d_boundPropagations;
- }
-}
-
-void TheoryArithPrivate::propagateCandidates(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
-
- Trace("arith::prop") << "propagateCandidates begin" << endl;
-
- Assert(d_candidateBasics.empty());
-
- if(d_updatedBounds.empty()){ return; }
-
- DenseSet::const_iterator i = d_updatedBounds.begin();
- DenseSet::const_iterator end = d_updatedBounds.end();
- for(; i != end; ++i){
- ArithVar var = *i;
- if (d_tableau.isBasic(var)
- && d_tableau.basicRowLength(var)
- <= options().arith.arithPropagateMaxLength)
- {
- d_candidateBasics.softAdd(var);
- }
- else
- {
- Tableau::ColIterator basicIter = d_tableau.colIterator(var);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- RowIndex ridx = entry.getRowIndex();
- ArithVar rowVar = d_tableau.rowIndexToBasic(ridx);
- Assert(entry.getColVar() == var);
- Assert(d_tableau.isBasic(rowVar));
- if (d_tableau.getRowLength(ridx)
- <= options().arith.arithPropagateMaxLength)
- {
- d_candidateBasics.softAdd(rowVar);
- }
- }
- }
- }
- d_updatedBounds.purge();
-
- while(!d_candidateBasics.empty()){
- ArithVar candidate = d_candidateBasics.back();
- d_candidateBasics.pop_back();
- Assert(d_tableau.isBasic(candidate));
- propagateCandidate(candidate);
- }
- Trace("arith::prop") << "propagateCandidates end" << endl << endl << endl;
-}
-
-void TheoryArithPrivate::propagateCandidatesNew(){
- /* Four criteria must be met for progagation on a variable to happen using a row:
- * 0: A new bound has to have been added to the row.
- * 1: The hasBoundsCount for the row must be "full" or be full minus one variable
- * (This is O(1) to check, but requires book keeping.)
- * 2: The current assignment must be strictly smaller/greater than the current bound.
- * assign(x) < upper(x)
- * (This is O(1) to compute.)
- * 3: There is a bound that is strictly smaller/greater than the current assignment.
- * assign(x) < c for some x <= c literal
- * (This is O(log n) to compute.)
- * 4: The implied bound on x is strictly smaller/greater than the current bound.
- * (This is O(n) to compute.)
- */
-
- TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
- Trace("arith::prop") << "propagateCandidatesNew begin" << endl;
-
- Assert(d_qflraStatus == Result::SAT);
- if(d_updatedBounds.empty()){ return; }
- dumpUpdatedBoundsToRows();
- Assert(d_updatedBounds.empty());
-
- if(!d_candidateRows.empty()){
- UpdateTrackingCallback utcb(&d_linEq);
- d_partialModel.processBoundsQueue(utcb);
- }
-
- while(!d_candidateRows.empty()){
- RowIndex candidate = d_candidateRows.back();
- d_candidateRows.pop_back();
- propagateCandidateRow(candidate);
- }
- Trace("arith::prop") << "propagateCandidatesNew end" << endl << endl << endl;
-}
-
-bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{
- int cmp = ub ? d_partialModel.cmpAssignmentUpperBound(v)
- : d_partialModel.cmpAssignmentLowerBound(v);
- bool hasSlack = ub ? cmp < 0 : cmp > 0;
- if(hasSlack){
- ConstraintType t = ub ? UpperBound : LowerBound;
- const DeltaRational& a = d_partialModel.getAssignment(v);
-
- if(isInteger(v) && !a.isIntegral()){
- return true;
- }
-
- ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a);
- if(strongestPossible == NullConstraint){
- return false;
- }else{
- bool assertedToTheTheory = strongestPossible->assertedToTheTheory();
- bool canBePropagated = strongestPossible->canBePropagated();
- bool hasProof = strongestPossible->hasProof();
-
- return !assertedToTheTheory && canBePropagated && !hasProof;
- }
- }else{
- return false;
- }
-}
-
-bool TheoryArithPrivate::attemptSingleton(RowIndex ridx, bool rowUp){
- Trace("arith::prop") << " attemptSingleton" << ridx;
-
- const Tableau::Entry* ep;
- ep = d_linEq.rowLacksBound(ridx, rowUp, ARITHVAR_SENTINEL);
- Assert(ep != NULL);
-
- ArithVar v = ep->getColVar();
- const Rational& coeff = ep->getCoefficient();
-
- // 0 = c * v + \sum rest
- // Suppose rowUp
- // - c * v = \sum rest \leq D
- // if c > 0, v \geq -D/c so !vUp
- // if c < 0, v \leq -D/c so vUp
- // Suppose not rowUp
- // - c * v = \sum rest \geq D
- // if c > 0, v \leq -D/c so vUp
- // if c < 0, v \geq -D/c so !vUp
- bool vUp = (rowUp == ( coeff.sgn() < 0));
-
- Trace("arith::prop") << " " << rowUp << " " << v << " " << coeff << " " << vUp << endl;
- Trace("arith::prop") << " " << propagateMightSucceed(v, vUp) << endl;
-
- if(propagateMightSucceed(v, vUp)){
- DeltaRational dr = d_linEq.computeRowBound(ridx, rowUp, v);
- DeltaRational bound = dr / (- coeff);
- return tryToPropagate(ridx, rowUp, v, vUp, bound);
- }
- return false;
-}
-
-bool TheoryArithPrivate::attemptFull(RowIndex ridx, bool rowUp){
- Trace("arith::prop") << " attemptFull" << ridx << endl;
-
- vector<const Tableau::Entry*> candidates;
-
- for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
- const Tableau::Entry& e =*i;
- const Rational& c = e.getCoefficient();
- ArithVar v = e.getColVar();
- bool vUp = (rowUp == (c.sgn() < 0));
- if(propagateMightSucceed(v, vUp)){
- candidates.push_back(&e);
- }
- }
- if(candidates.empty()){ return false; }
-
- const DeltaRational slack =
- d_linEq.computeRowBound(ridx, rowUp, ARITHVAR_SENTINEL);
- bool any = false;
- vector<const Tableau::Entry*>::const_iterator i, iend;
- for(i = candidates.begin(), iend = candidates.end(); i != iend; ++i){
- const Tableau::Entry* ep = *i;
- const Rational& c = ep->getCoefficient();
- ArithVar v = ep->getColVar();
-
- // See the comment for attemptSingleton()
- bool activeUp = (rowUp == (c.sgn() > 0));
- bool vUb = (rowUp == (c.sgn() < 0));
-
- const DeltaRational& activeBound = activeUp ?
- d_partialModel.getUpperBound(v):
- d_partialModel.getLowerBound(v);
-
- DeltaRational contribution = activeBound * c;
- DeltaRational impliedBound = (slack - contribution)/(-c);
-
- bool success = tryToPropagate(ridx, rowUp, v, vUb, impliedBound);
- any |= success;
- }
- return any;
-}
-
-bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUb, const DeltaRational& bound){
-
- bool weaker = vUb ? d_partialModel.strictlyLessThanUpperBound(v, bound):
- d_partialModel.strictlyGreaterThanLowerBound(v, bound);
- if(weaker){
- ConstraintType t = vUb ? UpperBound : LowerBound;
-
- ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound);
- if(implied != NullConstraint){
- return rowImplicationCanBeApplied(ridx, rowUp, implied);
- }
- }
- return false;
-}
-
-Node flattenImplication(Node imp){
- NodeBuilder nb(kind::OR);
- std::unordered_set<Node> included;
- Node left = imp[0];
- Node right = imp[1];
-
- if(left.getKind() == kind::AND){
- for(Node::iterator i = left.begin(), iend = left.end(); i != iend; ++i) {
- if (!included.count((*i).negate()))
- {
- nb << (*i).negate();
- included.insert((*i).negate());
- }
- }
- }else{
- if (!included.count(left.negate()))
- {
- nb << left.negate();
- included.insert(left.negate());
- }
- }
-
- if(right.getKind() == kind::OR){
- for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) {
- if (!included.count(*i))
- {
- nb << *i;
- included.insert(*i);
- }
- }
- }else{
- if (!included.count(right))
- {
- nb << right;
- included.insert(right);
- }
- }
-
- return nb;
-}
-
-bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){
- Assert(implied != NullConstraint);
- ArithVar v = implied->getVariable();
-
- bool assertedToTheTheory = implied->assertedToTheTheory();
- bool canBePropagated = implied->canBePropagated();
- bool hasProof = implied->hasProof();
-
- Trace("arith::prop") << "arith::prop" << v
- << " " << assertedToTheTheory
- << " " << canBePropagated
- << " " << hasProof
- << endl;
-
-
- if( !assertedToTheTheory && canBePropagated && !hasProof ){
- ConstraintCPVec explain;
- if (options().smt.produceProofs)
- {
- d_farkasBuffer.clear();
- }
- RationalVectorP coeffs =
- options().smt.produceProofs ? &d_farkasBuffer : nullptr;
-
- // After invoking `propegateRow`:
- // * coeffs[0] is for implied
- // * coeffs[i+1] is for explain[i]
- d_linEq.propagateRow(explain, ridx, rowUp, implied, coeffs);
- if (d_tableau.getRowLength(ridx) <= options().arith.arithPropAsLemmaLength)
- {
- if (TraceIsOn("arith::prop::pf")) {
- for (const auto & constraint : explain) {
- Assert(constraint->hasProof());
- constraint->printProofTree(Trace("arith::prop::pf"));
- }
- }
- Node implication = implied->externalImplication(explain);
- Node clause = flattenImplication(implication);
- std::shared_ptr<ProofNode> clausePf{nullptr};
-
- if (isProofEnabled())
- {
- // We can prove this lemma from Farkas...
- std::vector<std::shared_ptr<ProofNode>> conflictPfs;
- Node pfLit = implied->getNegation()->getProofLiteral();
- TypeNode type = pfLit[0].getType();
- // Assume the negated getLiteral version of the implied constaint
- // then rewrite it into proof normal form.
- conflictPfs.push_back(
- d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM,
- {d_pnm->mkAssume(implied->getLiteral().negate())},
- {pfLit}));
- // Add the explaination proofs.
- for (const auto constraint : explain)
- {
- NodeBuilder nb;
- conflictPfs.push_back(constraint->externalExplainByAssertions(nb));
- }
- // Collect the farkas coefficients, as nodes.
- std::vector<Node> farkasCoefficients;
- farkasCoefficients.reserve(coeffs->size());
- auto nm = NodeManager::currentNM();
- std::transform(coeffs->begin(),
- coeffs->end(),
- std::back_inserter(farkasCoefficients),
- [nm, type](const Rational& r) {
- return nm->mkConstRealOrInt(type, r);
- });
-
- // Prove bottom.
- auto sumPf = d_pnm->mkNode(
- PfRule::MACRO_ARITH_SCALE_SUM_UB, conflictPfs, farkasCoefficients);
- auto botPf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
-
- // Prove the conflict
- std::vector<Node> assumptions;
- assumptions.reserve(clause.getNumChildren());
- std::transform(clause.begin(),
- clause.end(),
- std::back_inserter(assumptions),
- [](TNode r) { return r.negate(); });
- auto notAndNotPf = d_pnm->mkScope(botPf, assumptions);
-
- // Convert it to a clause
- auto orNotNotPf = d_pnm->mkNode(PfRule::NOT_AND, {notAndNotPf}, {});
- clausePf = d_pnm->mkNode(
- PfRule::MACRO_SR_PRED_TRANSFORM, {orNotNotPf}, {clause});
-
- // Output it
- TrustNode trustedClause = d_pfGen->mkTrustNode(clause, clausePf);
- outputTrustedLemma(trustedClause, InferenceId::ARITH_ROW_IMPL);
- }
- else
- {
- outputLemma(clause, InferenceId::ARITH_ROW_IMPL);
- }
- }
- else
- {
- Assert(!implied->negationHasProof());
- implied->impliedByFarkas(explain, coeffs, false);
- implied->tryToPropagate();
- }
- return true;
- }
-
- if(TraceIsOn("arith::prop")){
- Trace("arith::prop")
- << "failed " << v << " " << assertedToTheTheory << " "
- << canBePropagated << " " << hasProof << " " << implied << endl;
- d_partialModel.printModel(v, Trace("arith::prop"));
- }
- return false;
-}
-
-bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){
- BoundCounts hasCount = d_linEq.hasBoundCount(ridx);
- uint32_t rowLength = d_tableau.getRowLength(ridx);
-
- bool success = false;
-
- Trace("arith::prop") << "propagateCandidateRow attempt " << rowLength << " "
- << hasCount << endl;
-
- if (rowLength >= options().arith.arithPropagateMaxLength
- && Random::getRandom().pickWithProb(
- 1.0 - double(options().arith.arithPropagateMaxLength) / rowLength))
- {
- return false;
- }
-
- if(hasCount.lowerBoundCount() == rowLength){
- success |= attemptFull(ridx, false);
- }else if(hasCount.lowerBoundCount() + 1 == rowLength){
- success |= attemptSingleton(ridx, false);
- }
-
- if(hasCount.upperBoundCount() == rowLength){
- success |= attemptFull(ridx, true);
- }else if(hasCount.upperBoundCount() + 1 == rowLength){
- success |= attemptSingleton(ridx, true);
- }
- return success;
-}
-
-void TheoryArithPrivate::dumpUpdatedBoundsToRows(){
- Assert(d_candidateRows.empty());
- DenseSet::const_iterator i = d_updatedBounds.begin();
- DenseSet::const_iterator end = d_updatedBounds.end();
- for(; i != end; ++i){
- ArithVar var = *i;
- if(d_tableau.isBasic(var)){
- RowIndex ridx = d_tableau.basicToRowIndex(var);
- d_candidateRows.softAdd(ridx);
- }else{
- Tableau::ColIterator basicIter = d_tableau.colIterator(var);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- RowIndex ridx = entry.getRowIndex();
- d_candidateRows.softAdd(ridx);
- }
- }
- }
- d_updatedBounds.purge();
-}
-
-const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{
- RowIndex ridx = d_tableau.basicToRowIndex(basic);
- return d_rowTracking[ridx];
-}
-
-std::pair<bool, Node> TheoryArithPrivate::entailmentCheck(TNode lit, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
- using namespace inferbounds;
-
- // l k r
- // diff : (l - r) k 0
- Trace("arith::entailCheck") << "TheoryArithPrivate::entailmentCheck(" << lit << ")"<< endl;
- Kind k;
- int primDir;
- Rational lm, rm, dm;
- Node lp, rp, dp;
- DeltaRational sep;
- bool successful = decomposeLiteral(lit, k, primDir, lm, lp, rm, rp, dm, dp, sep);
- if(!successful) { return make_pair(false, Node::null()); }
-
- if (dp.isConst())
- {
- Node eval = rewrite(lit);
- Assert(eval.getKind() == kind::CONST_BOOLEAN);
- // if true, true is an acceptable explaination
- // if false, the node is uninterpreted and eval can be forgotten
- return make_pair(eval.getConst<bool>(), eval);
- }
- Assert(dm != Rational(0));
- Assert(primDir == 1 || primDir == -1);
-
- int negPrim = -primDir;
-
- int secDir = (k == EQUAL || k == DISTINCT) ? negPrim: 0;
- int negSecDir = (k == EQUAL || k == DISTINCT) ? primDir: 0;
-
- // primDir*[lm*( lp )] k primDir*[ [rm*( rp )] + sep ]
- // primDir*[lm*( lp ) - rm*( rp ) ] k primDir*sep
- // primDir*[dm * dp] k primDir*sep
-
- std::pair<Node, DeltaRational> bestPrimLeft, bestNegPrimRight, bestPrimDiff, tmp;
- std::pair<Node, DeltaRational> bestSecLeft, bestNegSecRight, bestSecDiff;
- bestPrimLeft.first = Node::null(); bestNegPrimRight.first = Node::null(); bestPrimDiff.first = Node::null();
- bestSecLeft.first = Node::null(); bestNegSecRight.first = Node::null(); bestSecDiff.first = Node::null();
-
-
-
- ArithEntailmentCheckParameters::const_iterator alg, alg_end;
- for( alg = params.begin(), alg_end = params.end(); alg != alg_end; ++alg ){
- const inferbounds::InferBoundAlgorithm& ibalg = *alg;
-
- Trace("arith::entailCheck") << "entailmentCheck trying " << (inferbounds::Algorithms) ibalg.getAlgorithm() << endl;
- switch(ibalg.getAlgorithm()){
- case inferbounds::None:
- break;
- case inferbounds::Lookup:
- case inferbounds::RowSum:
- {
- typedef void (TheoryArithPrivate::*EntailmentCheckFunc)(std::pair<Node, DeltaRational>&, int, TNode) const;
-
- EntailmentCheckFunc ecfunc =
- (ibalg.getAlgorithm() == inferbounds::Lookup)
- ? (&TheoryArithPrivate::entailmentCheckBoundLookup)
- : (&TheoryArithPrivate::entailmentCheckRowSum);
-
- (*this.*ecfunc)(tmp, primDir * lm.sgn(), lp);
- setToMin(primDir * lm.sgn(), bestPrimLeft, tmp);
-
- (*this.*ecfunc)(tmp, negPrim * rm.sgn(), rp);
- setToMin(negPrim * rm.sgn(), bestNegPrimRight, tmp);
-
- (*this.*ecfunc)(tmp, secDir * lm.sgn(), lp);
- setToMin(secDir * lm.sgn(), bestSecLeft, tmp);
-
- (*this.*ecfunc)(tmp, negSecDir * rm.sgn(), rp);
- setToMin(negSecDir * rm.sgn(), bestNegSecRight, tmp);
-
- (*this.*ecfunc)(tmp, primDir * dm.sgn(), dp);
- setToMin(primDir * dm.sgn(), bestPrimDiff, tmp);
-
- (*this.*ecfunc)(tmp, secDir * dm.sgn(), dp);
- setToMin(secDir * dm.sgn(), bestSecDiff, tmp);
- }
- break;
- default:
- Unhandled();
- }
-
- // turn bounds on prim * left and -prim * right into bounds on prim * diff
- if(!bestPrimLeft.first.isNull() && !bestNegPrimRight.first.isNull()){
- // primDir*lm* lp <= primDir*lm*L
- // -primDir*rm* rp <= -primDir*rm*R
- // primDir*lm* lp -primDir*rm* rp <= primDir*lm*L - primDir*rm*R
- // primDir [lm* lp -rm* rp] <= primDir[lm*L - *rm*R]
- // primDir [dm * dp] <= primDir[lm*L - *rm*R]
- // primDir [dm * dp] <= primDir * dm * ([lm*L - *rm*R]/dm)
- tmp.second = ((bestPrimLeft.second * lm) - (bestNegPrimRight.second * rm)) / dm;
- tmp.first = (bestPrimLeft.first).andNode(bestNegPrimRight.first);
- setToMin(primDir, bestPrimDiff, tmp);
- }
-
- // turn bounds on sec * left and sec * right into bounds on sec * diff
- if(secDir != 0 && !bestSecLeft.first.isNull() && !bestNegSecRight.first.isNull()){
- // secDir*lm* lp <= secDir*lm*L
- // -secDir*rm* rp <= -secDir*rm*R
- // secDir*lm* lp -secDir*rm* rp <= secDir*lm*L - secDir*rm*R
- // secDir [lm* lp -rm* rp] <= secDir[lm*L - *rm*R]
- // secDir [dm * dp] <= secDir[lm*L - *rm*R]
- // secDir [dm * dp] <= secDir * dm * ([lm*L - *rm*R]/dm)
- tmp.second = ((bestSecLeft.second * lm) - (bestNegSecRight.second * rm)) / dm;
- tmp.first = (bestSecLeft.first).andNode(bestNegSecRight.first);
- setToMin(secDir, bestSecDiff, tmp);
- }
-
- switch(k){
- case LEQ:
- if(!bestPrimDiff.first.isNull()){
- DeltaRational d = (bestPrimDiff.second * dm);
- if((primDir > 0 && d <= sep) || (primDir < 0 && d >= sep) ){
- Trace("arith::entailCheck") << "entailmentCheck found "
- << primDir << "*" << dm << "*(" << dp<<")"
- << " <= " << primDir << "*" << dm << "*" << bestPrimDiff.second
- << " <= " << primDir << "*" << sep << endl
- << " by " << bestPrimDiff.first << endl;
- Assert(bestPrimDiff.second * (Rational(primDir) * dm)
- <= (sep * Rational(primDir)));
- return make_pair(true, bestPrimDiff.first);
- }
- }
- break;
- case EQUAL:
- if(!bestPrimDiff.first.isNull() && !bestSecDiff.first.isNull()){
- // Is primDir [dm * dp] == primDir * sep entailed?
- // Iff [dm * dp] == sep entailed?
- // Iff dp == sep / dm entailed?
- // Iff dp <= sep / dm and dp >= sep / dm entailed?
-
- // primDir [dm * dp] <= primDir * dm * U
- // secDir [dm * dp] <= secDir * dm * L
-
- // Suppose primDir * dm > 0
- // then secDir * dm < 0
- // dp >= (secDir * L) / secDir * dm
- // dp >= (primDir * L) / primDir * dm
- //
- // dp <= U / dm
- // dp >= L / dm
- // dp == sep / dm entailed iff U == L == sep
- // Suppose primDir * dm < 0
- // then secDir * dm > 0
- // dp >= U / dm
- // dp <= L / dm
- // dp == sep / dm entailed iff U == L == sep
- if(bestPrimDiff.second == bestSecDiff.second){
- if(bestPrimDiff.second == sep){
- return make_pair(true, (bestPrimDiff.first).andNode(bestSecDiff.first));
- }
- }
- }
- // intentionally fall through to DISTINCT case!
- // entailments of negations are eager exit cases for EQUAL
- CVC5_FALLTHROUGH;
- case DISTINCT:
- if(!bestPrimDiff.first.isNull()){
- // primDir [dm * dp] <= primDir * dm * U < primDir * sep
- if((primDir > 0 && (bestPrimDiff.second * dm < sep)) ||
- (primDir < 0 && (bestPrimDiff.second * dm > sep))){
- // entailment of negation
- if(k == DISTINCT){
- return make_pair(true, bestPrimDiff.first);
- }else{
- Assert(k == EQUAL);
- return make_pair(false, Node::null());
- }
- }
- }
- if(!bestSecDiff.first.isNull()){
- // If primDir [dm * dp] > primDir * sep, then this is not entailed.
- // If primDir [dm * dp] >= primDir * dm * L > primDir * sep
- // -primDir * dm * L < -primDir * sep
- // secDir * dm * L < secDir * sep
- if((secDir > 0 && (bestSecDiff.second * dm < sep)) ||
- (secDir < 0 && (bestSecDiff.second * dm > sep))){
- if(k == DISTINCT){
- return make_pair(true, bestSecDiff.first);
- }else{
- Assert(k == EQUAL);
- return make_pair(false, Node::null());
- }
- }
- }
-
- break;
- default:
- Unreachable();
- break;
- }
- }
- return make_pair(false, Node::null());
-}
-
-bool TheoryArithPrivate::decomposeTerm(Node t,
- Rational& m,
- Node& p,
- Rational& c)
-{
- if(!Polynomial::isMember(t)){
- return false;
- }
-
- // TODO Speed up
- preprocessing::util::ContainsTermITEVisitor ctv;
- if(ctv.containsTermITE(t)){
- return false;
- }
-
- Polynomial poly = Polynomial::parsePolynomial(t);
- if(poly.isConstant()){
- c = poly.getHead().getConstant().getValue();
- p = mkRationalNode(Rational(0));
- m = Rational(1);
- return true;
- }else if(poly.containsConstant()){
- c = poly.getHead().getConstant().getValue();
- poly = poly.getTail();
- }else{
- c = Rational(0);
- }
- Assert(!poly.isConstant());
- Assert(!poly.containsConstant());
-
- const bool intVars = poly.allIntegralVariables();
-
- if(intVars){
- m = Rational(1);
- if(!poly.isIntegral()){
- Integer denom = poly.denominatorLCM();
- m /= denom;
- poly = poly * denom;
- }
- Integer g = poly.gcd();
- m *= g;
- poly = poly * Rational(1,g);
- Assert(poly.isIntegral());
- }else{
- Assert(!intVars);
- m = poly.getHead().getConstant().getValue();
- poly = poly * m.inverse();
- Assert(poly.leadingCoefficientIsAbsOne());
- }
- p = poly.getNode();
- return true;
-}
-
-void TheoryArithPrivate::setToMin(int sgn, std::pair<Node, DeltaRational>& min, const std::pair<Node, DeltaRational>& e){
- if(sgn != 0){
- if(min.first.isNull() && !e.first.isNull()){
- min = e;
- }else if(!min.first.isNull() && !e.first.isNull()){
- if(sgn > 0 && min.second > e.second){
- min = e;
- }else if(sgn < 0 && min.second < e.second){
- min = e;
- }
- }
- }
-}
-
-// std::pair<bool, Node> TheoryArithPrivate::entailmentUpperCheck(const Rational& lm, Node lp, const Rational& rm, Node rp, const DeltaRational& sep, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out){
-
-// Rational negRM = -rm;
-// Node diff = NodeManager::currentNM()->mkNode(MULT, mkRationalConstan(lm), lp) + (negRM * rp);
-
-// Rational diffm;
-// Node diffp;
-// decompose(diff, diffm, diffNode);
-
-
-// std::pair<Node, DeltaRational> bestUbLeft, bestLbRight, bestUbDiff, tmp;
-// bestUbLeft = bestLbRight = bestUbDiff = make_pair(Node::Null(), DeltaRational());
-
-// return make_pair(false, Node::null());
-// }
-
-/**
- * Decomposes a literal into the form:
- * dir*[lm*( lp )] k dir*[ [rm*( rp )] + sep ]
- * dir*[dm* dp] k dir *sep
- * dir is either 1 or -1
- */
-bool TheoryArithPrivate::decomposeLiteral(Node lit, Kind& k, int& dir, Rational& lm, Node& lp, Rational& rm, Node& rp, Rational& dm, Node& dp, DeltaRational& sep){
- bool negated = (lit.getKind() == kind::NOT);
- TNode atom = negated ? lit[0] : lit;
-
- TNode left = atom[0];
- TNode right = atom[1];
-
- // left : lm*( lp ) + lc
- // right: rm*( rp ) + rc
- Rational lc, rc;
- bool success = decomposeTerm(rewrite(left), lm, lp, lc);
- if(!success){ return false; }
- success = decomposeTerm(rewrite(right), rm, rp, rc);
- if(!success){ return false; }
-
- Node diff = rewrite(NodeManager::currentNM()->mkNode(kind::SUB, left, right));
- Rational dc;
- success = decomposeTerm(diff, dm, dp, dc);
- Assert(success);
-
- // reduce the kind of the to not include literals
- // GT, NOT LEQ
- // GEQ, NOT LT
- // LT, NOT GEQ
- // LEQ, NOT LT
- Kind atomKind = atom.getKind();
- Kind normKind = negated ? negateKind(atomKind) : atomKind;
-
- if(normKind == GEQ || normKind == GT){
- dir = -1;
- normKind = (normKind == GEQ) ? LEQ : LT;
- }else{
- dir = 1;
- }
-
- Trace("arith::decomp") << "arith::decomp "
- << lit << "(" << normKind << "*" << dir << ")"<< endl
- << " left:" << lc << " + " << lm << "*(" << lp << ") : " <<left << endl
- << " right:" << rc << " + " << rm << "*(" << rp << ") : " << right << endl
- << " diff: " << dc << " + " << dm << "*("<< dp <<"): " << diff << endl
- << " sep: " << sep << endl;
-
-
- // k in LT, LEQ, EQUAL, DISEQUAL
- // [dir*lm*( lp ) + dir*lc] k [dir*rm*( rp ) + dir*rc]
- Rational change = rc - lc;
- Assert(change == (-dc));
- // [dir*lm*( lp )] k [dir*rm*( rp ) + dir*(rc - lc)]
- if(normKind == LT){
- sep = DeltaRational(change, Rational(-1));
- k = LEQ;
- }else{
- sep = DeltaRational(change);
- k = normKind;
- }
- // k in LEQ, EQUAL, DISEQUAL
- // dir*lm*( lp ) k [dir*rm*( rp )] + dir*(sep + d * delta)
- return true;
-}
-
-/**
- * Precondition:
- * tp is a polynomial not containing an ite.
- * either tp is constant or contains no constants.
- * Post:
- * if tmp.first is not null, then
- * sgn * tp <= sgn * tmp.second
- */
-void TheoryArithPrivate::entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
- tmp.first = Node::null();
- if(sgn == 0){ return; }
-
- Assert(Polynomial::isMember(tp));
- if (tp.isConst())
- {
- tmp.first = mkBoolNode(true);
- tmp.second = DeltaRational(tp.getConst<Rational>());
- }
- else if (d_partialModel.hasArithVar(tp))
- {
- Assert(!tp.isConst());
- ArithVar v = d_partialModel.asArithVar(tp);
- Assert(v != ARITHVAR_SENTINEL);
- ConstraintP c = (sgn > 0)
- ? d_partialModel.getUpperBoundConstraint(v)
- : d_partialModel.getLowerBoundConstraint(v);
- if(c != NullConstraint){
- tmp.first = Constraint::externalExplainByAssertions({c});
- tmp.second = c->getValue();
- }
- }
-}
-
-void TheoryArithPrivate::entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const {
- tmp.first = Node::null();
- if(sgn == 0){ return; }
- if (tp.getKind() != ADD)
- {
- return;
- }
- Assert(Polynomial::isMember(tp));
-
- tmp.second = DeltaRational(0);
- NodeBuilder nb(kind::AND);
-
- Polynomial p = Polynomial::parsePolynomial(tp);
- for(Polynomial::iterator i = p.begin(), iend = p.end(); i != iend; ++i) {
- Monomial m = *i;
- Node x = m.getVarList().getNode();
- if(d_partialModel.hasArithVar(x)){
- ArithVar v = d_partialModel.asArithVar(x);
- const Rational& coeff = m.getConstant().getValue();
- int dir = sgn * coeff.sgn();
- ConstraintP c = (dir > 0)
- ? d_partialModel.getUpperBoundConstraint(v)
- : d_partialModel.getLowerBoundConstraint(v);
- if(c != NullConstraint){
- tmp.second += c->getValue() * coeff;
- c->externalExplainByAssertions(nb);
- }else{
- //failed
- return;
- }
- }else{
- // failed
- return;
- }
- }
- // success
- tmp.first = nb;
-}
-
-ArithProofRuleChecker* TheoryArithPrivate::getProofChecker()
-{
- return &d_checker;
-}
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
+++ /dev/null
-/******************************************************************************
- * Top contributors (to current version):
- * Tim King, Andrew Reynolds, Gereon Kremer
- *
- * This file is part of the cvc5 project.
- *
- * Copyright (c) 2009-2022 by the authors listed in the file AUTHORS
- * in the top-level source directory and their institutional affiliations.
- * All rights reserved. See the file COPYING in the top-level source
- * directory for licensing information.
- * ****************************************************************************
- *
- * [[ Add one-line brief description here ]]
- *
- * [[ Add lengthier description here ]]
- * \todo document this file
- */
-
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include "context/cdhashset.h"
-#include "context/cdinsert_hashmap.h"
-#include "context/cdlist.h"
-#include "context/cdqueue.h"
-#include "expr/kind.h"
-#include "expr/node.h"
-#include "expr/node_builder.h"
-#include "proof/trust_node.h"
-#include "theory/arith/arith_static_learner.h"
-#include "theory/arith/arith_utilities.h"
-#include "theory/arith/arithvar.h"
-#include "theory/arith/attempt_solution_simplex.h"
-#include "theory/arith/branch_and_bound.h"
-#include "theory/arith/congruence_manager.h"
-#include "theory/arith/constraint.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/dio_solver.h"
-#include "theory/arith/dual_simplex.h"
-#include "theory/arith/error_set.h"
-#include "theory/arith/fc_simplex.h"
-#include "theory/arith/infer_bounds.h"
-#include "theory/arith/linear_equality.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/normal_form.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/proof_checker.h"
-#include "theory/arith/soi_simplex.h"
-#include "theory/arith/theory_arith.h"
-#include "theory/valuation.h"
-#include "util/dense_map.h"
-#include "util/integer.h"
-#include "util/rational.h"
-#include "util/result.h"
-#include "util/statistics_stats.h"
-
-namespace cvc5::internal {
-
-class EagerProofGenerator;
-
-namespace theory {
-
-class TheoryModel;
-
-namespace arith {
-
-class BranchCutInfo;
-class TreeLog;
-class ApproximateStatistics;
-
-class ArithEntailmentCheckParameters;
-class ArithEntailmentCheckSideEffects;
-namespace inferbounds {
- class InferBoundAlgorithm;
-}
-class InferBoundsResult;
-
-/**
- * Implementation of QF_LRA.
- * Based upon:
- * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf
- */
-class TheoryArithPrivate : protected EnvObj
-{
- private:
- static constexpr uint32_t RESET_START = 2;
-
- TheoryArith& d_containing;
-
- /**
- * Whether we encountered non-linear arithmetic at any time during solving.
- */
- bool d_foundNl;
-
- BoundInfoMap d_rowTracking;
- /** Branch and bound utility */
- BranchAndBound& d_bab;
- // For proofs
- /** Manages the proof nodes of this theory. */
- ProofNodeManager* d_pnm;
- /** Checks the proof rules of this theory. */
- ArithProofRuleChecker d_checker;
- /** Stores proposition(node)/proof pairs. */
- std::unique_ptr<EagerProofGenerator> d_pfGen;
-
- /**
- * The constraint database associated with the theory.
- * This must be declared before ArithPartialModel.
- */
- ConstraintDatabase d_constraintDatabase;
-
- enum Result::Status d_qflraStatus;
- // check()
- // !done() -> d_qflraStatus = Unknown
- // fullEffort(e) -> simplex returns either sat or unsat
- // !fullEffort(e) -> simplex returns either sat, unsat or unknown
- // if unknown, save the assignment
- // if unknown, the simplex priority queue cannot be emptied
- int d_unknownsInARow;
-
- bool d_replayedLemmas;
-
- /**
- * This counter is false if nothing has been done since the last cut.
- * This is used to break an infinite loop.
- */
- bool d_hasDoneWorkSinceCut;
-
- /** Static learner. */
- ArithStaticLearner d_learner;
-
- //std::vector<ArithVar> d_pool;
-public:
- void releaseArithVar(ArithVar v);
- void signal(ArithVar v){ d_errorSet.signalVariable(v); }
-
-
-private:
- // t does not contain constants
- void entailmentCheckBoundLookup(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
- void entailmentCheckRowSum(std::pair<Node, DeltaRational>& tmp, int sgn, TNode tp) const;
-
- /**
- * Infers either a new upper/lower bound on term in the real relaxation.
- * Either:
- * - term is malformed (see below)
- * - a maximum/minimum is found with the result being a pair
- * -- <dr, exp> where
- * -- term <?> dr is implies by exp
- * -- <?> is <= if inferring an upper bound, >= otherwise
- * -- exp is in terms of the assertions to the theory.
- * - No upper or lower bound is inferrable in the real relaxation.
- * -- Returns <0, Null()>
- * - the maximum number of rounds was exhausted:
- * -- Returns <v, term> where v is the current feasible value of term
- * - Threshold reached:
- * -- If theshold != NULL, and a feasible value is found to exceed threshold
- * -- Simplex stops and returns <threshold, term>
- */
- //std::pair<DeltaRational, Node> inferBound(TNode term, bool lb, int maxRounds = -1, const DeltaRational* threshold = NULL);
-
-private:
- static bool decomposeTerm(Node t, Rational& m, Node& p, Rational& c);
- bool decomposeLiteral(Node lit,
- Kind& k,
- int& dir,
- Rational& lm,
- Node& lp,
- Rational& rm,
- Node& rp,
- Rational& dm,
- Node& dp,
- DeltaRational& sep);
- static void setToMin(int sgn,
- std::pair<Node, DeltaRational>& min,
- const std::pair<Node, DeltaRational>& e);
-
- typedef ArithVariables::var_iterator var_iterator;
- var_iterator var_begin() const { return d_partialModel.var_begin(); }
- var_iterator var_end() const { return d_partialModel.var_end(); }
-
- NodeSet d_setupNodes;
-public:
- bool isSetup(Node n) const {
- return d_setupNodes.find(n) != d_setupNodes.end();
- }
- void markSetup(Node n){
- Assert(!isSetup(n));
- d_setupNodes.insert(n);
- }
-private:
- void setupVariable(const Variable& x);
- void setupVariableList(const VarList& vl);
- void setupPolynomial(const Polynomial& poly);
-public:
- void setupAtom(TNode atom);
-private:
- void cautiousSetupPolynomial(const Polynomial& p);
-
- /**
- * A superset of all of the assertions that currently are not the literal for
- * their constraint do not match constraint literals. Not just the witnesses.
- */
- context::CDInsertHashMap<Node, ConstraintP>
- d_assertionsThatDoNotMatchTheirLiterals;
-
- /** Returns true if x is of type Integer. */
- inline bool isInteger(ArithVar x) const {
- return d_partialModel.isInteger(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)
- && d_preregisteredNodes.contains(d_partialModel.asNode(x));
- }
-
- /**
- * On full effort checks (after determining LA(Q) satisfiability), we
- * consider integer vars, but we make sure to do so fairly to avoid
- * nontermination (although this isn't a guarantee). To do it fairly,
- * we consider variables in round-robin fashion. This is the
- * round-robin index.
- */
- ArithVar d_nextIntegerCheckVar;
-
- /**
- * Queue of Integer variables that are known to be equal to a constant.
- */
- context::CDQueue<ArithVar> d_constantIntegerVariables;
-
- Node callDioSolver();
- /**
- * Produces lemmas of the form (or (>= f 0) (<= f 0)),
- * where f is a plane that the diophantine solver is interested in.
- *
- * More precisely, produces lemmas of the form (or (>= lc -c) (<= lc -c))
- * where lc is a linear combination of variables, c is a constant, and lc + c
- * is the plane.
- */
- TrustNode dioCutting();
-
- Comparison mkIntegerEqualityFromAssignment(ArithVar v);
-
- /**
- * List of all of the disequalities asserted in the current context that are not known
- * to be satisfied.
- */
- context::CDQueue<ConstraintP> d_diseqQueue;
-
- /**
- * Constraints that have yet to be processed by proagation work list.
- * All of the elements have type of LowerBound, UpperBound, or
- * Equality.
- *
- * This is empty at the beginning of every check call.
- *
- * If head()->getType() == LowerBound or UpperBound,
- * then d_cPL[1] is the previous constraint in d_partialModel for the
- * corresponding bound.
- * If head()->getType() == Equality,
- * then d_cPL[1] is the previous lowerBound in d_partialModel,
- * and d_cPL[2] is the previous upperBound in d_partialModel.
- */
- std::deque<ConstraintP> d_currentPropagationList;
-
- context::CDQueue<ConstraintP> d_learnedBounds;
-
- /**
- * Contains all nodes that have been preregistered
- */
- context::CDHashSet<Node> d_preregisteredNodes;
-
- /**
- * Manages information about the assignment and upper and lower bounds on
- * variables.
- */
- ArithVariables d_partialModel;
-
- /** The set of variables in error in the partial model. */
- ErrorSet d_errorSet;
-
- /**
- * The tableau for all of the constraints seen thus far in the system.
- */
- Tableau d_tableau;
-
- /**
- * Maintains the relationship between the PartialModel and the Tableau.
- */
- LinearEqualityModule d_linEq;
-
- /**
- * A Diophantine equation solver. Accesses the tableau and partial
- * model (each in a read-only fashion).
- */
- DioSolver d_diosolver;
-
- /** Counts the number of notifyRestart() calls to the theory. */
- uint32_t d_restartsCounter;
-
- /**
- * Every number of restarts equal to s_TABLEAU_RESET_PERIOD,
- * the density of the tableau, d, is computed.
- * If d >= s_TABLEAU_RESET_DENSITY * d_initialDensity, the tableau
- * is set to d_initialTableau.
- */
- bool d_tableauSizeHasBeenModified;
- double d_tableauResetDensity;
- uint32_t d_tableauResetPeriod;
- static constexpr uint32_t s_TABLEAU_RESET_INCREMENT = 5;
-
- /** This is only used by simplex at the moment. */
- context::CDList<std::pair<ConstraintCP, InferenceId>> d_conflicts;
-
- /** This is only used by simplex at the moment. */
- context::CDO<Node> d_blackBoxConflict;
- /** For holding the proof of the above conflict node. */
- context::CDO<std::shared_ptr<ProofNode>> d_blackBoxConflictPf;
-
- bool isProofEnabled() const;
-
- public:
- /**
- * This adds the constraint a to the queue of conflicts in d_conflicts.
- * Both a and ~a must have a proof.
- */
- void raiseConflict(ConstraintCP a, InferenceId id);
-
- // 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);
-
- /** This is a conflict that is magically known to hold. */
- void raiseBlackBoxConflict(Node bb, std::shared_ptr<ProofNode> pf = nullptr);
- /**
- * Returns true iff a conflict has been raised. This method is public since
- * it is needed by the ArithState class to know whether we are in conflict.
- */
- bool anyConflict() const;
-
- private:
- inline bool conflictQueueEmpty() const {
- return d_conflicts.empty();
- }
-
- /**
- * Outputs the contents of d_conflicts onto d_out.
- * The conditions of anyConflict() must hold.
- */
- void outputConflicts();
-
- /**
- * A copy of the tableau.
- * This is equivalent to the original tableau if d_tableauSizeHasBeenModified
- * is false.
- * The set of basic and non-basic variables may differ from d_tableau.
- */
- Tableau d_smallTableauCopy;
-
- /**
- * Returns true if all of the basic variables in the simplex queue of
- * basic variables that violate their bounds in the current tableau
- * are basic in d_smallTableauCopy.
- *
- * d_tableauSizeHasBeenModified must be false when calling this.
- * Simplex's priority queue must be in collection mode.
- */
- bool safeToReset() const;
-
- /** This keeps track of difference equalities. Mostly for sharing. */
- ArithCongruenceManager d_congruenceManager;
- context::CDO<bool> d_cmEnabled;
-
- /** This implements the Simplex decision procedure. */
- DualSimplexDecisionProcedure d_dualSimplex;
- FCSimplexDecisionProcedure d_fcSimplex;
- SumOfInfeasibilitiesSPD d_soiSimplex;
- AttemptSolutionSDP d_attemptSolSimplex;
-
- 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);
- ~ModelException() override;
- };
-
- /**
- * Computes the delta rational value of a term from the current partial
- * model. This returns the delta value assignment to the term if it is in the
- * partial model. Otherwise, this is computed recursively for arithmetic terms
- * from each subterm.
- *
- * This throws a DeltaRationalException if the value cannot be represented as
- * a DeltaRational. This throws a ModelException if there is a term is not in
- * the partial model and is not a theory of arithmetic term.
- *
- * precondition: The linear abstraction of the nodes must be satisfiable.
- */
- DeltaRational getDeltaValue(TNode term) const
- /* throw(DeltaRationalException, ModelException) */;
- public:
- TheoryArithPrivate(TheoryArith& containing, Env& env, BranchAndBound& bab);
- ~TheoryArithPrivate();
-
- //--------------------------------- initialization
- /**
- * Returns true if we need an equality engine, see
- * Theory::needsEqualityEngine.
- */
- bool needsEqualityEngine(EeSetupInfo& esi);
- /** finish initialize */
- void finishInit();
- //--------------------------------- end initialization
-
- /**
- * Does non-context dependent setup for a node connected to a theory.
- */
- void preRegisterTerm(TNode n);
-
- void propagate(Theory::Effort e);
- TrustNode explain(TNode n);
-
- Rational deltaValueForTotalOrder() const;
-
- bool collectModelInfo(TheoryModel* m);
- /**
- * Collect model values. This is the main method for extracting information
- * about how to construct the model. This method relies on the caller for
- * processing the map, which is done so that other modules (e.g. the
- * non-linear extension) can modify arithModel before it is sent to the model.
- *
- * @param termSet The set of relevant terms
- * @param arithModel Mapping from terms (of real type) to their values. The
- * caller should assert equalities to the model for each entry in this map.
- */
- void collectModelValues(const std::set<Node>& termSet,
- std::map<Node, Node>& arithModel);
-
- void shutdown(){ }
-
- void presolve();
- void notifyRestart();
- Theory::PPAssertStatus ppAssert(TrustNode tin,
- TrustSubstitutionMap& outSubstitutions);
- void ppStaticLearn(TNode in, NodeBuilder& learned);
-
- std::string identify() const { return std::string("TheoryArith"); }
-
- EqualityStatus getEqualityStatus(TNode a, TNode b);
-
- /** Called when n is notified as being a shared term with TheoryArith. */
- void notifySharedTerm(TNode n);
-
- Node getModelValue(TNode var);
-
-
- std::pair<bool, Node> entailmentCheck(TNode lit, const ArithEntailmentCheckParameters& params, ArithEntailmentCheckSideEffects& out);
-
- //--------------------------------- standard check
- /** Pre-check, called before the fact queue of the theory is processed. */
- bool preCheck(Theory::Effort level);
- /** Pre-notify fact. */
- void preNotifyFact(TNode atom, bool pol, TNode fact);
- /**
- * Post-check, called after the fact queue of the theory is processed. Returns
- * true if a conflict or lemma was emitted.
- */
- bool postCheck(Theory::Effort level);
- //--------------------------------- end standard check
- /**
- * Found non-linear? This returns true if this solver ever encountered
- * any non-linear terms that were unhandled. Note that this class is not
- * responsible for handling non-linear arithmetic. If the owner of this
- * class does not handle non-linear arithmetic in another way, then
- * setIncomplete should be called on the output channel of TheoryArith.
- */
- bool foundNonlinear() const;
-
- /** get the proof checker of this theory */
- ArithProofRuleChecker* getProofChecker();
-
- private:
- /** The constant zero. */
- DeltaRational d_DELTA_ZERO;
-
- /** propagates an arithvar */
- void propagateArithVar(bool upperbound, ArithVar var );
-
- /**
- * Using the simpleKind return the ArithVar associated with the assertion.
- */
- ArithVar determineArithVar(const Polynomial& p) const;
- ArithVar determineArithVar(TNode assertion) const;
-
- /**
- * Splits the disequalities in d_diseq that are violated using lemmas on demand.
- * returns true if any lemmas were issued.
- * returns false if all disequalities are satisfied in the current model.
- */
- bool splitDisequalities();
-
- /** A Difference variable is known to be 0.*/
- void zeroDifferenceDetected(ArithVar x);
-
-
- /**
- * Looks for the next integer variable without an integer assignment in a
- * round-robin fashion. Changes the value of d_nextIntegerCheckVar.
- *
- * This returns true if all integer variables have integer assignments.
- * If this returns false, d_nextIntegerCheckVar does not have an integer
- * assignment.
- */
- bool hasIntegerModel();
-
- /**
- * 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 nextIntegerViolation(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().
- */
- TrustNode roundRobinBranch();
-
- bool proofsEnabled() const { return d_pnm; }
-
- 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 aux, bool internal);
-
-public:
- const BoundsInfo& boundsInfo(ArithVar basic) const;
-
-
-private:
- /** Initial (not context dependent) sets up for a variable.*/
- void setupBasicValue(ArithVar x);
-
- /** Initial (not context dependent) sets up for a new auxiliary variable.*/
- void setupAuxiliary(TNode left);
-
-
- /**
- * Assert*(n, orig) takes an bound n that is implied by orig.
- * and asserts that as a new bound if it is tighter than the current bound
- * and updates the value of a basic variable if needed.
- *
- * orig must be a literal in the SAT solver so that it can be used for
- * conflict analysis.
- *
- * x is the variable getting the new bound,
- * c is the value of the new bound.
- *
- * If this new bound is in conflict with the other bound,
- * a node describing this conflict is returned.
- * If this new bound is not in conflict, Node::null() is returned.
- */
- bool AssertLower(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;
-
- /** Tracks the basic variables where propagation might be possible. */
- DenseSet d_candidateBasics;
- DenseSet d_candidateRows;
-
- bool hasAnyUpdates() { return !d_updatedBounds.empty(); }
- void clearUpdates();
-
- void revertOutOfConflict();
-
- void propagateCandidatesNew();
- void dumpUpdatedBoundsToRows();
- bool propagateCandidateRow(RowIndex rid);
- bool propagateMightSucceed(ArithVar v, bool ub) const;
- /** Attempt to perform a row propagation where there is at most 1 possible variable.*/
- bool attemptSingleton(RowIndex ridx, bool rowUp);
- /** 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, 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;
-
- void propagateCandidates();
- void propagateCandidate(ArithVar basic);
- bool propagateCandidateBound(ArithVar basic, bool upperBound);
-
- inline bool propagateCandidateLowerBound(ArithVar basic){
- return propagateCandidateBound(basic, false);
- }
- inline bool propagateCandidateUpperBound(ArithVar basic){
- return propagateCandidateBound(basic, true);
- }
-
- /**
- * Performs a check to see if it is definitely true that setup can be avoided.
- */
- bool canSafelyAvoidEqualitySetup(TNode equality);
-
- /**
- * Handles the case splitting for check() for a new assertion.
- * Returns a conflict if one was found.
- * Returns Node::null if no conflict was found.
- *
- * @param assertion The assertion that was just popped from the fact queue
- * of TheoryArith and given to this class via preNotifyFact.
- */
- ConstraintP constraintFromFactQueue(TNode assertion);
- bool assertionCases(ConstraintP c);
-
- /**
- * Returns the basic variable with the shorted row containing a non-basic variable.
- * If no such row exists, return ARITHVAR_SENTINEL.
- */
- ArithVar findShortestBasicRow(ArithVar variable);
-
- /**
- * Debugging only routine!
- * Returns true iff every variable is consistent in the partial model.
- */
- bool entireStateIsConsistent(const std::string& locationHint);
- bool unenqueuedVariablesAreConsistent();
-
- bool isImpliedUpperBound(ArithVar var, Node exp);
- bool isImpliedLowerBound(ArithVar var, Node exp);
-
- void internalExplain(TNode n, NodeBuilder& explainBuilder);
-
- void asVectors(const Polynomial& p,
- std::vector<Rational>& coeffs,
- std::vector<ArithVar>& variables);
-
- /** Routine for debugging. Print the assertions the theory is aware of. */
- void debugPrintAssertions(std::ostream& out) const;
- /** Debugging only routine. Prints the model. */
- void debugPrintModel(std::ostream& out) const;
-
- bool done() const { return d_containing.done(); }
- bool isLeaf(TNode x) const { return d_containing.isLeaf(x); }
- TheoryId theoryOf(TNode x) const { return d_containing.theoryOf(x); }
- void debugPrintFacts() const { d_containing.debugPrintFacts(); }
- bool outputTrustedLemma(TrustNode lem, InferenceId id);
- bool outputLemma(TNode lem, InferenceId id);
- void outputTrustedConflict(TrustNode conf, InferenceId id);
- void outputConflict(TNode lit, InferenceId id);
- void outputPropagate(TNode lit);
- void outputRestart();
-
- inline bool isSatLiteral(TNode l) const {
- return (d_containing.d_valuation).isSatLiteral(l);
- }
- inline Node getSatValue(TNode n) const {
- return (d_containing.d_valuation).getSatValue(n);
- }
-
- /** Used for replaying approximate simplex */
- context::CDQueue<TrustNode> d_approxCuts;
- /** Also used for replaying approximate simplex. "approximate cuts temporary storage" */
- std::vector<TrustNode> d_acTmp;
-
- /** Counts the number of fullCheck calls to arithmetic. */
- uint32_t d_fullCheckCounter;
- std::vector<ArithVar> cutAllBounded() const;
- TrustNode branchIntegerVariable(ArithVar x) const;
- void branchVector(const std::vector<ArithVar>& lemmas);
-
- context::CDO<unsigned> d_cutCount;
- context::CDHashSet<ArithVar, std::hash<ArithVar>> d_cutInContext;
-
- context::CDO<bool> d_likelyIntegerInfeasible;
-
- 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);
- std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch);
-
- void replayAssert(ConstraintP c);
-
- static ConstraintCP vectorToIntHoleConflict(const ConstraintCPVec& conflict);
- static void intHoleConflictToVector(ConstraintCP conflicting, ConstraintCPVec& conflict);
-
- // 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;
-
- RationalVector d_farkasBuffer;
-
- //---------------- during check
- /** Whether there were new facts during preCheck */
- bool d_newFacts;
- /** The previous status, computed during preCheck */
- Result::Status d_previousStatus;
- //---------------- end during check
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts;
-
- IntStat d_statUserVariables, d_statAuxiliaryVariables;
- IntStat d_statDisequalitySplits;
- IntStat d_statDisequalityConflicts;
- TimerStat d_simplifyTimer;
- TimerStat d_staticLearningTimer;
-
- TimerStat d_presolveTime;
-
- TimerStat d_newPropTime;
-
- IntStat d_externalBranchAndBounds;
-
- IntStat d_initialTableauSize;
- IntStat d_currSetToSmaller;
- IntStat d_smallerSetToCurr;
- TimerStat d_restartTimer;
-
- TimerStat d_boundComputationTime;
- IntStat d_boundComputations, d_boundPropagations;
-
- IntStat d_unknownChecks;
- IntStat d_maxUnknownsInARow;
- AverageStat d_avgUnknownsInARow;
-
- IntStat d_revertsOnConflicts;
- IntStat d_commitsOnConflicts;
- IntStat d_nontrivialSatChecks;
-
- 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;
- HistogramStat<uint32_t> d_unknownPivots;
-
- IntStat d_solveIntModelsAttempts;
- IntStat d_solveIntModelsSuccessful;
- TimerStat d_mipTimer;
- TimerStat d_lpTimer;
-
- IntStat d_mipProofsAttempted;
- IntStat d_mipProofsSuccessful;
-
- IntStat d_numBranchesFailed;
-
- Statistics(StatisticsRegistry& reg, const std::string& name);
- };
-
-
- Statistics d_statistics;
-}; /* class TheoryArithPrivate */
-
-} // namespace arith
-} // namespace theory
-} // namespace cvc5::internal
#include "expr/node_algorithm.h"
#include "options/quantifiers_options.h"
#include "theory/arith/arith_msum.h"
-#include "theory/arith/partial_model.h"
+#include "theory/arith/linear/partial_model.h"
#include "theory/arith/theory_arith.h"
-#include "theory/arith/theory_arith_private.h"
+#include "theory/arith/linear/theory_arith_private.h"
#include "theory/quantifiers/term_util.h"
#include "theory/rewriter.h"
#include "util/random.h"