summaryrefslogtreecommitdiff
path: root/src/theory/arith/nonlinear_extension.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/arith/nonlinear_extension.cpp')
-rw-r--r--src/theory/arith/nonlinear_extension.cpp411
1 files changed, 307 insertions, 104 deletions
diff --git a/src/theory/arith/nonlinear_extension.cpp b/src/theory/arith/nonlinear_extension.cpp
index 20f19db9d..65a7597f1 100644
--- a/src/theory/arith/nonlinear_extension.cpp
+++ b/src/theory/arith/nonlinear_extension.cpp
@@ -236,6 +236,7 @@ NonlinearExtension::NonlinearExtension(TheoryArith& containing,
"a", NodeManager::currentNM()->realType());
d_taylor_real_fv_base_rem = NodeManager::currentNM()->mkBoundVar(
"b", NodeManager::currentNM()->realType());
+ d_taylor_degree = options::nlExtTfTaylorDegree();
}
NonlinearExtension::~NonlinearExtension() {}
@@ -1107,9 +1108,10 @@ int NonlinearExtension::flushLemmas(std::vector<Node>& lemmas) {
return sum;
}
-std::set<Node> NonlinearExtension::getFalseInModel(
- const std::vector<Node>& assertions) {
- std::set<Node> false_asserts;
+std::vector<Node> NonlinearExtension::checkModel(
+ const std::vector<Node>& assertions)
+{
+ std::vector<Node> false_asserts;
for (size_t i = 0; i < assertions.size(); ++i) {
Node lit = assertions[i];
Node atom = lit.getKind()==NOT ? lit[0] : lit;
@@ -1119,7 +1121,7 @@ std::set<Node> NonlinearExtension::getFalseInModel(
if (litv != d_true) {
Trace("nl-ext-mv") << " [model-false]" << std::endl;
//Assert(litv == d_false);
- false_asserts.insert(lit);
+ false_asserts.push_back(lit);
} else {
Trace("nl-ext-mv") << std::endl;
}
@@ -1128,6 +1130,132 @@ std::set<Node> NonlinearExtension::getFalseInModel(
return false_asserts;
}
+bool NonlinearExtension::checkModelTf(const std::vector<Node>& assertions)
+{
+ Trace("nl-ext-tf-check-model") << "check-model : Run" << std::endl;
+ // add bounds for PI
+ d_tf_check_model_bounds[d_pi] =
+ std::pair<Node, Node>(d_pi_bound[0], d_pi_bound[1]);
+ for (const std::pair<const Node, std::pair<Node, Node> >& tfb :
+ d_tf_check_model_bounds)
+ {
+ Node tf = tfb.first;
+ Trace("nl-ext-tf-check-model")
+ << "check-model : satisfied approximate bound : ";
+ Trace("nl-ext-tf-check-model") << tfb.second.first << " <= " << tf
+ << " <= " << tfb.second.second << std::endl;
+ }
+
+ std::vector<Node> check_assertions;
+ for (const Node& a : assertions)
+ {
+ Node av = computeModelValue(a);
+ // simple check
+ if (!simpleCheckModelTfLit(av))
+ {
+ check_assertions.push_back(av);
+ Trace("nl-ext-tf-check-model") << "check-model : assertion : " << av
+ << " (from " << a << ")" << std::endl;
+ }
+ }
+
+ if (check_assertions.empty())
+ {
+ return true;
+ }
+ else
+ {
+ // TODO (#1450) check model for general case
+ return false;
+ }
+}
+
+bool NonlinearExtension::simpleCheckModelTfLit(Node lit)
+{
+ Trace("nl-ext-tf-check-model-simple") << "simple check-model for " << lit
+ << "..." << std::endl;
+ NodeManager* nm = NodeManager::currentNM();
+ bool pol = lit.getKind() != kind::NOT;
+ Node atom = lit.getKind() == kind::NOT ? lit[0] : lit;
+
+ if (atom.getKind() == kind::GEQ)
+ {
+ std::map<Node, Node> msum;
+ if (ArithMSum::getMonomialSumLit(atom, msum))
+ {
+ // map from transcendental functions to whether they were set to lower
+ // bound
+ std::map<Node, bool> set_bound;
+ std::vector<Node> sum_bound;
+ for (std::pair<const Node, Node>& m : msum)
+ {
+ Node v = m.first;
+ if (v.isNull())
+ {
+ sum_bound.push_back(m.second.isNull() ? d_one : m.second);
+ }
+ else
+ {
+ std::map<Node, std::pair<Node, Node> >::iterator bit =
+ d_tf_check_model_bounds.find(v);
+ if (bit != d_tf_check_model_bounds.end())
+ {
+ bool set_lower =
+ (m.second.isNull() || m.second.getConst<Rational>().sgn() == 1)
+ == pol;
+ std::map<Node, bool>::iterator itsb = set_bound.find(v);
+ if (itsb != set_bound.end() && itsb->second != set_lower)
+ {
+ Trace("nl-ext-tf-check-model-simple")
+ << " failed due to conflicting bound for " << v << std::endl;
+ return false;
+ }
+ set_bound[v] = set_lower;
+ // must over/under approximate
+ Node vbound = set_lower ? bit->second.first : bit->second.second;
+ sum_bound.push_back(ArithMSum::mkCoeffTerm(m.second, vbound));
+ }
+ else
+ {
+ Trace("nl-ext-tf-check-model-simple")
+ << " failed due to unknown bound for " << v << std::endl;
+ return false;
+ }
+ }
+ }
+ Node bound;
+ if (sum_bound.size() > 1)
+ {
+ bound = nm->mkNode(kind::PLUS, sum_bound);
+ }
+ else if (sum_bound.size() == 1)
+ {
+ bound = sum_bound[0];
+ }
+ else
+ {
+ bound = d_zero;
+ }
+ Node comp = nm->mkNode(kind::GEQ, bound, d_zero);
+ if (!pol)
+ {
+ comp = comp.negate();
+ }
+ Trace("nl-ext-tf-check-model-simple") << " comparison is : " << comp
+ << std::endl;
+ comp = Rewriter::rewrite(comp);
+ Assert(comp.isConst());
+ Trace("nl-ext-tf-check-model-simple") << " returned : " << comp
+ << std::endl;
+ return comp == d_true;
+ }
+ }
+
+ Trace("nl-ext-tf-check-model-simple") << " failed due to unknown literal."
+ << std::endl;
+ return false;
+}
+
std::vector<Node> NonlinearExtension::checkSplitZero() {
std::vector<Node> lemmas;
for (unsigned i = 0; i < d_ms_vars.size(); i++) {
@@ -1143,8 +1271,9 @@ std::vector<Node> NonlinearExtension::checkSplitZero() {
}
int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions,
- const std::set<Node>& false_asserts,
- const std::vector<Node>& xts) {
+ const std::vector<Node>& false_asserts,
+ const std::vector<Node>& xts)
+{
d_ms_vars.clear();
d_ms_proc.clear();
d_ms.clear();
@@ -1156,6 +1285,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions,
d_ci_max.clear();
d_tf_rep_map.clear();
d_tf_region.clear();
+ d_tf_check_model_bounds.clear();
int lemmas_proc = 0;
std::vector<Node> lemmas;
@@ -1454,101 +1584,161 @@ void NonlinearExtension::check(Theory::Effort e) {
}
}
} else {
- Assert(e == Theory::EFFORT_LAST_CALL);
- Trace("nl-ext-mv") << "Getting model values... check for [model-false]"
- << std::endl;
- // reset cached information
- d_mv[0].clear();
- d_mv[1].clear();
-
- // get the assertions
- std::vector<Node> assertions;
- for (Theory::assertions_iterator it = d_containing.facts_begin();
- it != d_containing.facts_end(); ++it) {
- const Assertion& assertion = *it;
- assertions.push_back(assertion.assertion);
- }
- // get the assertions that are false in the model
- const std::set<Node> false_asserts = getFalseInModel(assertions);
-
- // get the extended terms belonging to this theory
- std::vector<Node> xts;
- d_containing.getExtTheory()->getTerms(xts);
-
- if( Trace.isOn("nl-ext-debug") ){
- Trace("nl-ext-debug") << " processing NonlinearExtension::check : " << std::endl;
- Trace("nl-ext-debug") << " " << false_asserts.size() << " false assertions" << std::endl;
- Trace("nl-ext-debug") << " " << xts.size() << " extended terms: " << std::endl;
- Trace("nl-ext-debug") << " ";
- for( unsigned j=0; j<xts.size(); j++ ){
- Trace("nl-ext-debug") << xts[j] << " ";
- }
- Trace("nl-ext-debug") << std::endl;
- }
-
- // compute whether shared terms have correct values
- unsigned num_shared_wrong_value = 0;
- std::vector< Node > shared_term_value_splits;
- //must ensure that shared terms are equal to their concrete value
- for (context::CDList<TNode>::const_iterator its = d_containing.shared_terms_begin();
- its != d_containing.shared_terms_end(); ++its) {
- TNode shared_term = *its;
- // compute its value in the model, and its evaluation in the model
- Node stv0 = computeModelValue( shared_term, 0 );
- Node stv1 = computeModelValue( shared_term, 1 );
- if( stv0!=stv1 ){
- num_shared_wrong_value++;
- Trace("nl-ext-mv") << "Bad shared term value : " << shared_term << " : " << stv1 << ", actual is " << stv0 << std::endl;
- if( shared_term!=stv0 ){
- //split on the value, this is non-terminating in general, TODO : improve this
- Node eq = shared_term.eqNode(stv0);
- shared_term_value_splits.push_back( eq );
- }else{
- // this can happen for transcendental functions
- // the problem is that we cannot evaluate transcendental functions (they don't have a rewriter that returns constants)
- // thus, the actual value in their model can be themselves, hence we have no reference
- // point to rule out the current model. In this case, we may set incomplete below.
+ bool needsRecheck;
+ do
+ {
+ needsRecheck = false;
+ Assert(e == Theory::EFFORT_LAST_CALL);
+ Trace("nl-ext-mv") << "Getting model values... check for [model-false]"
+ << std::endl;
+ // reset cached information
+ d_mv[0].clear();
+ d_mv[1].clear();
+
+ // get the assertions
+ std::vector<Node> assertions;
+ for (Theory::assertions_iterator it = d_containing.facts_begin();
+ it != d_containing.facts_end();
+ ++it)
+ {
+ const Assertion& assertion = *it;
+ assertions.push_back(assertion.assertion);
+ }
+ // get the assertions that are false in the model
+ const std::vector<Node> false_asserts = checkModel(assertions);
+
+ // get the extended terms belonging to this theory
+ std::vector<Node> xts;
+ d_containing.getExtTheory()->getTerms(xts);
+
+ if (Trace.isOn("nl-ext-debug"))
+ {
+ Trace("nl-ext-debug") << " processing NonlinearExtension::check : "
+ << std::endl;
+ Trace("nl-ext-debug") << " " << false_asserts.size()
+ << " false assertions" << std::endl;
+ Trace("nl-ext-debug") << " " << xts.size()
+ << " extended terms: " << std::endl;
+ Trace("nl-ext-debug") << " ";
+ for (unsigned j = 0; j < xts.size(); j++)
+ {
+ Trace("nl-ext-debug") << xts[j] << " ";
}
+ Trace("nl-ext-debug") << std::endl;
}
- }
- Trace("nl-ext-debug") << " " << num_shared_wrong_value << " shared terms with wrong model value." << std::endl;
-
- // we require a check either if an assertion is false or a shared term has a wrong value
- bool isIncomplete = false;
- int num_added_lemmas = 0;
- if(!false_asserts.empty() || num_shared_wrong_value>0 ){
- isIncomplete = true;
- num_added_lemmas = checkLastCall(assertions, false_asserts, xts);
- }
-
- //if we did not add a lemma during check
- if(num_added_lemmas==0) {
- if(num_shared_wrong_value>0) {
- // resort to splitting on shared terms with their model value
- isIncomplete = true;
- if( !shared_term_value_splits.empty() ){
- std::vector< Node > shared_term_value_lemmas;
- for( unsigned i=0; i<shared_term_value_splits.size(); i++ ){
- Node eq = shared_term_value_splits[i];
- Node literal = d_containing.getValuation().ensureLiteral(eq);
- d_containing.getOutputChannel().requirePhase(literal, true);
- shared_term_value_lemmas.push_back(literal.orNode(literal.negate()));
+
+ // compute whether shared terms have correct values
+ unsigned num_shared_wrong_value = 0;
+ std::vector<Node> shared_term_value_splits;
+ // must ensure that shared terms are equal to their concrete value
+ for (context::CDList<TNode>::const_iterator its =
+ d_containing.shared_terms_begin();
+ its != d_containing.shared_terms_end();
+ ++its)
+ {
+ TNode shared_term = *its;
+ // compute its value in the model, and its evaluation in the model
+ Node stv0 = computeModelValue(shared_term, 0);
+ Node stv1 = computeModelValue(shared_term, 1);
+ if (stv0 != stv1)
+ {
+ num_shared_wrong_value++;
+ Trace("nl-ext-mv") << "Bad shared term value : " << shared_term
+ << " : " << stv1 << ", actual is " << stv0
+ << std::endl;
+ if (shared_term != stv0)
+ {
+ // split on the value, this is non-terminating in general, TODO :
+ // improve this
+ Node eq = shared_term.eqNode(stv0);
+ shared_term_value_splits.push_back(eq);
+ }
+ else
+ {
+ // this can happen for transcendental functions
+ // the problem is that we cannot evaluate transcendental functions
+ // (they don't have a rewriter that returns constants)
+ // thus, the actual value in their model can be themselves, hence we
+ // have no reference
+ // point to rule out the current model. In this case, we may set
+ // incomplete below.
}
- num_added_lemmas = flushLemmas(shared_term_value_lemmas);
- Trace("nl-ext") << "...added " << num_added_lemmas << " shared term value split lemmas." << std::endl;
- }else{
- // this can happen if we are trying to do theory combination with trancendental functions
- // since their model value cannot even be computed exactly
}
}
-
+ Trace("nl-ext-debug") << " " << num_shared_wrong_value
+ << " shared terms with wrong model value."
+ << std::endl;
+
+ // we require a check either if an assertion is false or a shared term has
+ // a wrong value
+ bool isIncomplete = false;
+ int num_added_lemmas = 0;
+ if (!false_asserts.empty() || num_shared_wrong_value > 0)
+ {
+ isIncomplete = true;
+ num_added_lemmas = checkLastCall(assertions, false_asserts, xts);
+ }
+
+ // if we did not add a lemma during check
if(num_added_lemmas==0) {
- if(isIncomplete) {
- Trace("nl-ext") << "...failed to send lemma in NonLinearExtension, set incomplete" << std::endl;
- d_containing.getOutputChannel().setIncomplete();
+ if (num_shared_wrong_value > 0)
+ {
+ // resort to splitting on shared terms with their model value
+ isIncomplete = true;
+ if (!shared_term_value_splits.empty())
+ {
+ std::vector<Node> shared_term_value_lemmas;
+ for (const Node& eq : shared_term_value_splits)
+ {
+ Node literal = d_containing.getValuation().ensureLiteral(eq);
+ d_containing.getOutputChannel().requirePhase(literal, true);
+ shared_term_value_lemmas.push_back(
+ literal.orNode(literal.negate()));
+ }
+ num_added_lemmas = flushLemmas(shared_term_value_lemmas);
+ Trace("nl-ext") << "...added " << num_added_lemmas
+ << " shared term value split lemmas." << std::endl;
+ }
+ else
+ {
+ // this can happen if we are trying to do theory combination with
+ // trancendental functions
+ // since their model value cannot even be computed exactly
+ }
+ }
+ if (num_added_lemmas == 0)
+ {
+ if (isIncomplete)
+ {
+ // check the model using error bounds on the Taylor approximation
+ if (!d_tf_rep_map.empty() && checkModelTf(false_asserts))
+ {
+ isIncomplete = false;
+ }
+ }
+ if (isIncomplete)
+ {
+ if (options::nlExtTfIncPrecision() && !d_tf_rep_map.empty())
+ {
+ d_taylor_degree++;
+ d_secant_points.clear();
+ needsRecheck = true;
+ // increase precision for PI?
+ // Difficult since Taylor series is very slow to converge
+ Trace("nl-ext") << "...increment Taylor degree to "
+ << d_taylor_degree << std::endl;
+ }
+ else
+ {
+ Trace("nl-ext") << "...failed to send lemma in "
+ "NonLinearExtension, set incomplete"
+ << std::endl;
+ d_containing.getOutputChannel().setIncomplete();
+ }
+ }
}
}
- }
+ } while (needsRecheck);
}
}
@@ -1666,8 +1856,8 @@ void NonlinearExtension::mkPi(){
void NonlinearExtension::getCurrentPiBounds( std::vector< Node >& lemmas ) {
Node pi_lem = NodeManager::currentNM()->mkNode(
AND,
- NodeManager::currentNM()->mkNode(GT, d_pi, d_pi_bound[0]),
- NodeManager::currentNM()->mkNode(LT, d_pi, d_pi_bound[1]));
+ NodeManager::currentNM()->mkNode(GEQ, d_pi, d_pi_bound[0]),
+ NodeManager::currentNM()->mkNode(LEQ, d_pi, d_pi_bound[1]));
lemmas.push_back( pi_lem );
}
@@ -2239,12 +2429,11 @@ std::vector<Node> NonlinearExtension::checkTangentPlanes() {
Trace("nl-ext") << "...trying " << lemmas.size()
<< " tangent plane lemmas..." << std::endl;
return lemmas;
-}
-
+}
-
-std::vector<Node> NonlinearExtension::checkMonomialInferBounds( std::vector<Node>& nt_lemmas,
- const std::set<Node>& false_asserts ) {
+std::vector<Node> NonlinearExtension::checkMonomialInferBounds(
+ std::vector<Node>& nt_lemmas, const std::vector<Node>& false_asserts)
+{
std::vector< Node > lemmas;
// register constraints
Trace("nl-ext-debug") << "Register bound constraints..." << std::endl;
@@ -2255,7 +2444,9 @@ std::vector<Node> NonlinearExtension::checkMonomialInferBounds( std::vector<Node
bool polarity = lit.getKind() != NOT;
Node atom = lit.getKind() == NOT ? lit[0] : lit;
registerConstraint(atom);
- bool is_false_lit = false_asserts.find(lit) != false_asserts.end();
+ bool is_false_lit =
+ std::find(false_asserts.begin(), false_asserts.end(), lit)
+ != false_asserts.end();
// add information about bounds to variables
std::map<Node, std::map<Node, ConstraintInfo> >::iterator itc =
d_c_info.find(atom);
@@ -2505,7 +2696,9 @@ std::vector<Node> NonlinearExtension::checkMonomialInferBounds( std::vector<Node
return lemmas;
}
-std::vector<Node> NonlinearExtension::checkFactoring( const std::set<Node>& false_asserts ) {
+std::vector<Node> NonlinearExtension::checkFactoring(
+ const std::vector<Node>& false_asserts)
+{
std::vector< Node > lemmas;
Trace("nl-ext") << "Get factoring lemmas..." << std::endl;
for (context::CDList<Assertion>::const_iterator it =
@@ -2514,7 +2707,10 @@ std::vector<Node> NonlinearExtension::checkFactoring( const std::set<Node>& fals
Node lit = (*it).assertion;
bool polarity = lit.getKind() != NOT;
Node atom = lit.getKind() == NOT ? lit[0] : lit;
- if( false_asserts.find(lit) != false_asserts.end() || d_skolem_atoms.find(atom)!=d_skolem_atoms.end() ){
+ if (std::find(false_asserts.begin(), false_asserts.end(), lit)
+ != false_asserts.end()
+ || d_skolem_atoms.find(atom) != d_skolem_atoms.end())
+ {
std::map<Node, Node> msum;
if (ArithMSum::getMonomialSumLit(atom, msum))
{
@@ -3044,7 +3240,7 @@ std::vector<Node> NonlinearExtension::checkTranscendentalTangentPlanes()
std::map<int, Node>
poly_approx_bounds_neg[2]; // the negative case is different for exp
// n is the Taylor degree we are currently considering
- unsigned n = 8;
+ unsigned n = 2 * d_taylor_degree;
// n must be even
std::pair<Node, Node> taylor = getTaylor(tft, n);
Trace("nl-ext-tf-tplanes-debug") << "Taylor for " << k
@@ -3241,6 +3437,13 @@ std::vector<Node> NonlinearExtension::checkTranscendentalTangentPlanes()
Trace("nl-ext-tf-tplanes-debug") << "...poly appoximation at c is "
<< poly_approx_c << std::endl;
}
+ else
+ {
+ // store for check model bounds
+ Node atf = computeModelValue(tf);
+ d_tf_check_model_bounds[atf] =
+ std::pair<Node, Node>(model_values[0], model_values[1]);
+ }
if (is_tangent)
{
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback