summaryrefslogtreecommitdiff
path: root/src/theory
diff options
context:
space:
mode:
authorLiana Hadarean <lianahady@gmail.com>2013-09-30 13:56:51 -0400
committerLiana Hadarean <lianahady@gmail.com>2013-09-30 13:56:51 -0400
commit7d2265eb2b5dc96ddff04211959e208b1cb8a7f0 (patch)
tree26fb270349580c90efe163ca7767bccce6607902 /src/theory
parentdb6df44574927f9b75db664e1e490f757725d13a (diff)
parent0c2eafec69b694a507ac914bf285fe0574be085f (diff)
merged golden
Diffstat (limited to 'src/theory')
-rw-r--r--src/theory/Makefile.am10
-rw-r--r--src/theory/arith/Makefile.am4
-rw-r--r--src/theory/arith/approx_simplex.cpp336
-rw-r--r--src/theory/arith/approx_simplex.h11
-rw-r--r--src/theory/arith/arith_rewriter.cpp95
-rw-r--r--src/theory/arith/arith_static_learner.h1
-rw-r--r--src/theory/arith/attempt_solution_simplex.cpp135
-rw-r--r--src/theory/arith/attempt_solution_simplex.h (renamed from src/theory/arith/pure_update_simplex.h)53
-rw-r--r--src/theory/arith/bound_counts.h242
-rw-r--r--src/theory/arith/callbacks.cpp4
-rw-r--r--src/theory/arith/callbacks.h16
-rw-r--r--src/theory/arith/constraint.cpp22
-rw-r--r--src/theory/arith/constraint.h2
-rw-r--r--src/theory/arith/dual_simplex.cpp37
-rw-r--r--src/theory/arith/error_set.cpp12
-rw-r--r--src/theory/arith/error_set.h16
-rw-r--r--src/theory/arith/fc_simplex.cpp17
-rw-r--r--src/theory/arith/kinds26
-rw-r--r--src/theory/arith/linear_equality.cpp572
-rw-r--r--src/theory/arith/linear_equality.h205
-rw-r--r--src/theory/arith/matrix.cpp2
-rw-r--r--src/theory/arith/matrix.h6
-rw-r--r--src/theory/arith/normal_form.cpp35
-rw-r--r--src/theory/arith/normal_form.h14
-rw-r--r--src/theory/arith/options24
-rw-r--r--src/theory/arith/partial_model.cpp217
-rw-r--r--src/theory/arith/partial_model.h115
-rw-r--r--src/theory/arith/pure_update_simplex.cpp261
-rw-r--r--src/theory/arith/simplex-converge.cpp1674
-rw-r--r--src/theory/arith/simplex-converge.h531
-rw-r--r--src/theory/arith/simplex.cpp17
-rw-r--r--src/theory/arith/simplex.h4
-rw-r--r--src/theory/arith/simplex_update.h2
-rw-r--r--src/theory/arith/soi_simplex.cpp252
-rw-r--r--src/theory/arith/soi_simplex.h13
-rw-r--r--src/theory/arith/tableau.cpp2
-rw-r--r--src/theory/arith/tableau.h6
-rw-r--r--src/theory/arith/theory_arith.cpp5
-rw-r--r--src/theory/arith/theory_arith.h3
-rw-r--r--src/theory/arith/theory_arith_private.cpp724
-rw-r--r--src/theory/arith/theory_arith_private.h41
-rw-r--r--src/theory/arith/theory_arith_type_rules.h59
-rw-r--r--src/theory/arrays/Makefile.am4
-rw-r--r--src/theory/arrays/theory_arrays.cpp79
-rw-r--r--src/theory/arrays/theory_arrays_model.cpp65
-rw-r--r--src/theory/arrays/theory_arrays_model.h58
-rw-r--r--src/theory/arrays/theory_arrays_rewriter.h14
-rw-r--r--src/theory/atom_requests.cpp62
-rw-r--r--src/theory/atom_requests.h107
-rw-r--r--src/theory/booleans/theory_bool.cpp1
-rw-r--r--src/theory/builtin/kinds25
-rw-r--r--src/theory/builtin/theory_builtin_rewriter.cpp3
-rw-r--r--src/theory/builtin/theory_builtin_type_rules.h16
-rw-r--r--src/theory/bv/bitblaster.cpp196
-rw-r--r--src/theory/bv/bitblaster.h93
-rw-r--r--src/theory/bv/bv_subtheory.h29
-rw-r--r--src/theory/bv/bv_subtheory_bitblast.cpp17
-rw-r--r--src/theory/bv/bv_subtheory_bitblast.h10
-rw-r--r--src/theory/bv/bv_subtheory_core.cpp203
-rw-r--r--src/theory/bv/bv_subtheory_core.h28
-rw-r--r--src/theory/bv/bv_subtheory_inequality.cpp87
-rw-r--r--src/theory/bv/bv_subtheory_inequality.h30
-rw-r--r--src/theory/bv/kinds11
-rw-r--r--src/theory/bv/options6
-rw-r--r--src/theory/bv/theory_bv.cpp72
-rw-r--r--src/theory/bv/theory_bv_rewrite_rules.h20
-rw-r--r--src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h10
-rw-r--r--src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h51
-rw-r--r--src/theory/bv/theory_bv_rewrite_rules_simplification.h17
-rw-r--r--src/theory/bv/theory_bv_rewriter.cpp196
-rw-r--r--src/theory/bv/theory_bv_rewriter.h3
-rw-r--r--src/theory/bv/theory_bv_type_rules.h28
-rw-r--r--src/theory/bv/theory_bv_utils.h2
-rw-r--r--src/theory/datatypes/datatypes_rewriter.h11
-rw-r--r--src/theory/datatypes/kinds4
-rw-r--r--src/theory/datatypes/theory_datatypes.cpp156
-rw-r--r--src/theory/idl/Makefile4
-rw-r--r--src/theory/idl/Makefile.am19
-rw-r--r--src/theory/idl/idl_assertion.cpp213
-rw-r--r--src/theory/idl/idl_assertion.h91
-rw-r--r--src/theory/idl/idl_assertion_db.cpp59
-rw-r--r--src/theory/idl/idl_assertion_db.h86
-rw-r--r--src/theory/idl/idl_model.cpp64
-rw-r--r--src/theory/idl/idl_model.h84
-rw-r--r--src/theory/idl/kinds8
-rw-r--r--src/theory/idl/options12
-rw-r--r--src/theory/idl/theory_idl.cpp143
-rw-r--r--src/theory/idl/theory_idl.h63
-rw-r--r--src/theory/ite_simplifier.cpp4
-rw-r--r--src/theory/ite_simplifier.h6
-rw-r--r--src/theory/logic_info.cpp28
-rw-r--r--src/theory/logic_info.h4
-rw-r--r--src/theory/model.cpp29
-rw-r--r--src/theory/options4
-rw-r--r--src/theory/output_channel.h7
-rw-r--r--src/theory/quantifiers/Makefile.am17
-rw-r--r--src/theory/quantifiers/bounded_integers.cpp372
-rw-r--r--src/theory/quantifiers/bounded_integers.h127
-rw-r--r--src/theory/quantifiers/candidate_generator.cpp30
-rw-r--r--src/theory/quantifiers/candidate_generator.h42
-rw-r--r--src/theory/quantifiers/first_order_model.cpp254
-rw-r--r--src/theory/quantifiers/first_order_model.h106
-rw-r--r--src/theory/quantifiers/first_order_reasoning.cpp171
-rw-r--r--src/theory/quantifiers/first_order_reasoning.h45
-rw-r--r--src/theory/quantifiers/full_model_check.cpp1409
-rw-r--r--src/theory/quantifiers/full_model_check.h160
-rw-r--r--src/theory/quantifiers/inst_gen.cpp10
-rw-r--r--src/theory/quantifiers/inst_match.cpp21
-rw-r--r--src/theory/quantifiers/inst_match.h3
-rw-r--r--src/theory/quantifiers/inst_match_generator.cpp224
-rw-r--r--src/theory/quantifiers/inst_match_generator.h9
-rw-r--r--src/theory/quantifiers/inst_strategy_cbqi.cpp285
-rw-r--r--src/theory/quantifiers/inst_strategy_cbqi.h14
-rw-r--r--src/theory/quantifiers/inst_strategy_e_matching.cpp4
-rw-r--r--src/theory/quantifiers/instantiation_engine.cpp17
-rw-r--r--src/theory/quantifiers/model_builder.cpp368
-rw-r--r--src/theory/quantifiers/model_builder.h90
-rw-r--r--src/theory/quantifiers/model_engine.cpp232
-rw-r--r--src/theory/quantifiers/model_engine.h34
-rw-r--r--src/theory/quantifiers/options31
-rw-r--r--src/theory/quantifiers/quant_util.cpp102
-rw-r--r--src/theory/quantifiers/quant_util.h12
-rw-r--r--src/theory/quantifiers/quantifiers_attributes.cpp4
-rw-r--r--src/theory/quantifiers/quantifiers_attributes.h4
-rw-r--r--src/theory/quantifiers/quantifiers_rewriter.cpp8
-rwxr-xr-x[-rw-r--r--]src/theory/quantifiers/relevant_domain.cpp268
-rwxr-xr-x[-rw-r--r--]src/theory/quantifiers/relevant_domain.h32
-rwxr-xr-xsrc/theory/quantifiers/rewrite_engine.cpp184
-rwxr-xr-xsrc/theory/quantifiers/rewrite_engine.h54
-rwxr-xr-xsrc/theory/quantifiers/symmetry_breaking.cpp314
-rwxr-xr-xsrc/theory/quantifiers/symmetry_breaking.h118
-rw-r--r--src/theory/quantifiers/term_database.cpp122
-rw-r--r--src/theory/quantifiers/term_database.h28
-rw-r--r--src/theory/quantifiers/theory_quantifiers.cpp8
-rw-r--r--src/theory/quantifiers/trigger.cpp145
-rw-r--r--src/theory/quantifiers/trigger.h3
-rwxr-xr-x[-rw-r--r--]src/theory/quantifiers_engine.cpp89
-rw-r--r--src/theory/quantifiers_engine.h24
-rw-r--r--src/theory/rep_set.cpp198
-rw-r--r--src/theory/rep_set.h32
-rw-r--r--src/theory/rewriterules/rr_inst_match.cpp23
-rw-r--r--src/theory/rewriterules/rr_inst_match.h2
-rw-r--r--src/theory/rewriterules/rr_trigger.cpp20
-rw-r--r--src/theory/rewriterules/rr_trigger.h3
-rw-r--r--src/theory/rewriterules/theory_rewriterules.h2
-rw-r--r--src/theory/rewriterules/theory_rewriterules_rules.cpp15
-rw-r--r--src/theory/rewriterules/theory_rewriterules_type_rules.h2
-rw-r--r--src/theory/shared_terms_database.cpp4
-rw-r--r--src/theory/strings/Makefile4
-rw-r--r--src/theory/strings/Makefile.am19
-rw-r--r--src/theory/strings/kinds105
-rw-r--r--src/theory/strings/options11
-rw-r--r--src/theory/strings/theory_strings.cpp1711
-rw-r--r--src/theory/strings/theory_strings.h244
-rw-r--r--src/theory/strings/theory_strings_preprocess.cpp135
-rw-r--r--src/theory/strings/theory_strings_preprocess.h44
-rw-r--r--src/theory/strings/theory_strings_rewriter.cpp156
-rw-r--r--src/theory/strings/theory_strings_rewriter.h49
-rw-r--r--src/theory/strings/theory_strings_type_rules.h223
-rw-r--r--src/theory/strings/type_enumerator.h130
-rw-r--r--src/theory/substitutions.cpp75
-rw-r--r--src/theory/substitutions.h20
-rw-r--r--src/theory/term_registration_visitor.cpp9
-rw-r--r--src/theory/term_registration_visitor.h15
-rw-r--r--src/theory/theory.cpp28
-rw-r--r--src/theory/theory.h37
-rw-r--r--src/theory/theory_engine.cpp349
-rw-r--r--src/theory/theory_engine.h40
-rw-r--r--src/theory/theory_test_utils.h5
-rw-r--r--src/theory/theoryof_mode.h16
-rw-r--r--src/theory/type_enumerator.h21
-rw-r--r--src/theory/uf/equality_engine.cpp2
-rw-r--r--src/theory/uf/equality_engine.h14
-rw-r--r--src/theory/uf/options12
-rw-r--r--src/theory/uf/symmetry_breaker.cpp37
-rw-r--r--src/theory/uf/symmetry_breaker.h28
-rw-r--r--src/theory/uf/theory_uf.cpp3
-rw-r--r--src/theory/uf/theory_uf_model.cpp96
-rw-r--r--src/theory/uf/theory_uf_model.h16
-rw-r--r--src/theory/uf/theory_uf_rewriter.h1
-rw-r--r--src/theory/uf/theory_uf_strong_solver.cpp265
-rw-r--r--src/theory/uf/theory_uf_strong_solver.h45
182 files changed, 12525 insertions, 5800 deletions
diff --git a/src/theory/Makefile.am b/src/theory/Makefile.am
index bd7b881e1..860075aa8 100644
--- a/src/theory/Makefile.am
+++ b/src/theory/Makefile.am
@@ -3,7 +3,7 @@ AM_CPPFLAGS = \
-I@builddir@/.. -I@srcdir@/../include -I@srcdir@/..
AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN)
-SUBDIRS = builtin booleans uf arith bv arrays datatypes quantifiers rewriterules
+SUBDIRS = builtin booleans uf arith bv arrays datatypes quantifiers rewriterules idl strings
DIST_SUBDIRS = $(SUBDIRS) example
noinst_LTLIBRARIES = libtheory.la
@@ -42,7 +42,9 @@ libtheory_la_SOURCES = \
model.h \
model.cpp \
rep_set.h \
- rep_set.cpp
+ rep_set.cpp \
+ atom_requests.h \
+ atom_requests.cpp
nodist_libtheory_la_SOURCES = \
rewriter_tables.h \
@@ -58,7 +60,9 @@ libtheory_la_LIBADD = \
@builddir@/bv/libbv.la \
@builddir@/datatypes/libdatatypes.la \
@builddir@/quantifiers/libquantifiers.la \
- @builddir@/rewriterules/librewriterules.la
+ @builddir@/rewriterules/librewriterules.la \
+ @builddir@/idl/libidl.la \
+ @builddir@/strings/libstrings.la
EXTRA_DIST = \
logic_info.i \
diff --git a/src/theory/arith/Makefile.am b/src/theory/arith/Makefile.am
index 3c664d806..620b8a121 100644
--- a/src/theory/arith/Makefile.am
+++ b/src/theory/arith/Makefile.am
@@ -51,8 +51,8 @@ libarith_la_SOURCES = \
soi_simplex.cpp \
approx_simplex.h \
approx_simplex.cpp \
- pure_update_simplex.h \
- pure_update_simplex.cpp \
+ attempt_solution_simplex.h \
+ attempt_solution_simplex.cpp \
theory_arith.h \
theory_arith.cpp \
theory_arith_private_forward.h \
diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp
index d6be9f657..0f5a0fd4e 100644
--- a/src/theory/arith/approx_simplex.cpp
+++ b/src/theory/arith/approx_simplex.cpp
@@ -2,6 +2,7 @@
#include "theory/arith/approx_simplex.h"
#include "theory/arith/normal_form.h"
+#include "theory/arith/constraint.h"
#include <math.h>
#include <cmath>
@@ -94,6 +95,10 @@ public:
return Solution();
}
+ virtual ArithRatPairVec heuristicOptCoeffs() const{
+ return ArithRatPairVec();
+ }
+
virtual ApproxResult solveMIP(){
return ApproxError;
}
@@ -111,8 +116,14 @@ public:
/* Begin the declaration of GLPK specific code. */
#ifdef CVC4_USE_GLPK
extern "C" {
-#include <glpk.h>
-}
+/* Sometimes the header is in a subdirectory glpk/, sometimes not.
+ * The configure script figures it out. */
+#ifdef HAVE_GLPK_GLPK_H
+# include <glpk/glpk.h>
+#else /* HAVE_GLPK_GLPK_H */
+# include <glpk.h>
+#endif /* HAVE_GLPK_GLPK_H */
+}/* extern "C" */
namespace CVC4 {
namespace theory {
@@ -132,6 +143,8 @@ private:
bool d_solvedRelaxation;
bool d_solvedMIP;
+ static int s_verbosity;
+
public:
ApproxGLPK(const ArithVariables& vars);
~ApproxGLPK();
@@ -141,16 +154,22 @@ public:
return extractSolution(false);
}
+ virtual ArithRatPairVec heuristicOptCoeffs() const;
+
virtual ApproxResult solveMIP();
virtual Solution extractMIP() const{
return extractSolution(true);
}
virtual void setOptCoeffs(const ArithRatPairVec& ref);
+ static void printGLPKStatus(int status, std::ostream& out);
private:
Solution extractSolution(bool mip) const;
+ int guessDir(ArithVar v) const;
};
+int ApproxGLPK::s_verbosity = 0;
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
@@ -220,8 +239,10 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) :
for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
ArithVar v = *vi;
- //cout << v << " ";
- //d_vars.printModel(v, cout);
+ if(s_verbosity >= 2){
+ Message() << v << " ";
+ d_vars.printModel(v, Message());
+ }
int type;
double lb = 0.0;
@@ -301,6 +322,189 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) :
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;
+
+ if(s_verbosity >= 2){
+ Message() << v << " ";
+ d_vars.printModel(v, Message());
+ }
+
+ 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.isSlack(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){
+ Constraint 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(DenseMap<uint32_t>::const_iterator i = d_rowCandidates.begin(), iend =d_rowCandidates.end(); i != iend; ++i ){
+ ArithVar r = *i;
+ uint32_t len = d_rowCandidates[r];
+
+ int dir = guessDir(r);
+ if(len >= rowLengthReq){
+ if(s_verbosity >= 1){
+ Message() << "high row " << r << " " << len << " " << avgRowLength << " " << dir<< endl;
+ d_vars.printModel(r, Message());
+ }
+ ret.push_back(ArithRatPair(r, Rational(dir)));
+ }
+ }
+ }
+
+ // Attempt columns
+ bool guessAColCandidate = maxCount >= 4;
+ if(guessAColCandidate){
+ for(DenseMap<BoundCounts>::const_iterator i = d_colCandidates.begin(), iend = d_colCandidates.end(); i != iend; ++i ){
+ ArithVar c = *i;
+ 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){
+ if(s_verbosity >= 1){
+ Message() << "high col " << c << " " << bc << " " << ubScore << " " << lbScore << " " << dir << endl;
+ d_vars.printModel(c, Message());
+ }
+ ret.push_back(ArithRatPair(c, Rational(c)));
+ }
+ }
+ }
+
+
+ return ret;
+}
void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
DenseMap<double> nbCoeffs;
@@ -346,6 +550,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
}
}
+
/*
* rough strategy:
* real relaxation
@@ -361,7 +566,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
* check with FCSimplex
*/
-static void printGLPKStatus(int status, std::ostream& out){
+void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
switch(status){
case GLP_OPT:
out << "GLP_OPT" << endl;
@@ -406,77 +611,90 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{
int glpk_index = isSlack ? d_rowIndices[vi] : d_colIndices[vi];
int status = isSlack ? glp_get_row_stat(d_prob, glpk_index) : glp_get_col_stat(d_prob, glpk_index);
- //cout << "assignment " << vi << endl;
+ if(s_verbosity >= 2){
+ Message() << "assignment " << vi << endl;
+ }
+
+ bool useDefaultAssignment = false;
switch(status){
case GLP_BS:
- //cout << "basic" << endl;
+ //Message() << "basic" << endl;
newBasis.add(vi);
+ useDefaultAssignment = true;
break;
case GLP_NL:
case GLP_NS:
if(!mip){
- //cout << "non-basic lb" << endl;
+ if(s_verbosity >= 2){ Message() << "non-basic lb" << endl; }
newValues.set(vi, d_vars.getLowerBound(vi));
- break;
- }// intentionally fall through otherwise
+ }else{// intentionally fall through otherwise
+ useDefaultAssignment = true;
+ }
+ break;
case GLP_NU:
if(!mip){
- // cout << "non-basic ub" << endl;
+ if(s_verbosity >= 2){ Message() << "non-basic ub" << endl; }
newValues.set(vi, d_vars.getUpperBound(vi));
- break;
- }// intentionally fall through otherwise
+ }else {// intentionally fall through otherwise
+ useDefaultAssignment = true;
+ }
+ break;
default:
{
- // cout << "non-basic other" << endl;
+ useDefaultAssignment = true;
+ }
+ break;
+ }
+
+ if(useDefaultAssignment){
+ if(s_verbosity >= 2){ Message() << "non-basic other" << endl; }
+
+ double newAssign =
+ mip ?
+ (isSlack ? glp_mip_row_val(d_prob, glpk_index) : glp_mip_col_val(d_prob, glpk_index))
+ : (isSlack ? glp_get_row_prim(d_prob, glpk_index) : glp_get_col_prim(d_prob, glpk_index));
+ const DeltaRational& oldAssign = d_vars.getAssignment(vi);
- double newAssign =
- mip ?
- (isSlack ? glp_mip_row_val(d_prob, glpk_index) : glp_mip_col_val(d_prob, glpk_index))
- : (isSlack ? glp_get_row_prim(d_prob, glpk_index) : glp_get_col_prim(d_prob, glpk_index));
- const DeltaRational& oldAssign = d_vars.getAssignment(vi);
+ if(d_vars.hasLowerBound(vi) &&
+ roughlyEqual(newAssign, d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA))){
+ //Message() << " to lb" << endl;
- if(d_vars.hasLowerBound(vi) &&
- roughlyEqual(newAssign, d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA))){
- //cout << " to lb" << endl;
+ newValues.set(vi, d_vars.getLowerBound(vi));
+ }else if(d_vars.hasUpperBound(vi) &&
+ roughlyEqual(newAssign, d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA))){
+ newValues.set(vi, d_vars.getUpperBound(vi));
+ // Message() << " to ub" << endl;
+ }else{
- newValues.set(vi, d_vars.getLowerBound(vi));
- }else if(d_vars.hasUpperBound(vi) &&
- roughlyEqual(newAssign, d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA))){
- newValues.set(vi, d_vars.getUpperBound(vi));
- // cout << " to ub" << endl;
+ double rounded = round(newAssign);
+ if(roughlyEqual(newAssign, rounded)){
+ // Message() << "roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
+ newAssign = rounded;
}else{
+ // Message() << "not roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
+ }
- double rounded = round(newAssign);
- if(roughlyEqual(newAssign, rounded)){
- // cout << "roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
- newAssign = rounded;
- }else{
- // cout << "not roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl;
- }
-
- DeltaRational proposal = estimateWithCFE(newAssign);
-
-
- if(roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA))){
- // cout << " to prev value" << newAssign << " " << oldAssign << endl;
- proposal = d_vars.getAssignment(vi);
- }
-
-
- if(d_vars.strictlyLessThanLowerBound(vi, proposal)){
- //cout << " round to lb " << d_vars.getLowerBound(vi) << endl;
- proposal = d_vars.getLowerBound(vi);
- }else if(d_vars.strictlyGreaterThanUpperBound(vi, proposal)){
- //cout << " round to ub " << d_vars.getUpperBound(vi) << endl;
- proposal = d_vars.getUpperBound(vi);
- }else{
- //cout << " use proposal" << proposal << " " << oldAssign << endl;
- }
- newValues.set(vi, proposal);
+ DeltaRational proposal = estimateWithCFE(newAssign);
+
+
+ if(roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA))){
+ // Message() << " to prev value" << newAssign << " " << oldAssign << endl;
+ proposal = d_vars.getAssignment(vi);
}
- break;
+
+
+ if(d_vars.strictlyLessThanLowerBound(vi, proposal)){
+ //Message() << " round to lb " << d_vars.getLowerBound(vi) << endl;
+ proposal = d_vars.getLowerBound(vi);
+ }else if(d_vars.strictlyGreaterThanUpperBound(vi, proposal)){
+ //Message() << " round to ub " << d_vars.getUpperBound(vi) << endl;
+ proposal = d_vars.getUpperBound(vi);
+ }else{
+ //Message() << " use proposal" << proposal << " " << oldAssign << endl;
+ }
+ newValues.set(vi, proposal);
}
}
}
@@ -492,8 +710,10 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){
parm.meth = GLP_PRIMAL;
parm.pricing = GLP_PT_PSE;
parm.it_lim = d_pivotLimit;
- //parm.msg_lev = GLP_MSG_ALL;
parm.msg_lev = GLP_MSG_OFF;
+ if(s_verbosity >= 1){
+ parm.msg_lev = GLP_MSG_ALL;
+ }
int res = glp_simplex(d_prob, &parm);
switch(res){
@@ -550,8 +770,10 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){
parm.cov_cuts = GLP_ON;
parm.cb_func = stopAtBingoOrPivotLimit;
parm.cb_info = &d_pivotLimit;
- //parm.msg_lev = GLP_MSG_ALL;
parm.msg_lev = GLP_MSG_OFF;
+ if(s_verbosity >= 1){
+ parm.msg_lev = GLP_MSG_ALL;
+ }
int res = glp_intopt(d_prob, &parm);
switch(res){
diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h
index a2f3cde24..a34c8981d 100644
--- a/src/theory/arith/approx_simplex.h
+++ b/src/theory/arith/approx_simplex.h
@@ -24,7 +24,7 @@ public:
/**
* If glpk is enabled, return a subclass that can do something.
- * If glpk is disabled, return a sublass that does nothing.
+ * If glpk is disabled, return a subclass that does nothing.
*/
static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars);
ApproximateSimplex();
@@ -44,18 +44,15 @@ public:
/** Sets a maximization criteria for the approximate solver.*/
virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0;
+ virtual ArithRatPairVec heuristicOptCoeffs() const = 0;
+
virtual ApproxResult solveRelaxation() = 0;
virtual Solution extractRelaxation() const = 0;
virtual ApproxResult solveMIP() = 0;
virtual Solution extractMIP() const = 0;
- static void applySolution(LinearEqualityModule& linEq, const Solution& sol){
- linEq.forceNewBasis(sol.newBasis);
- linEq.updateMany(sol.newValues);
- }
-
- /** UTILIES FOR DEALING WITH ESTIMATES */
+ /** UTILITIES FOR DEALING WITH ESTIMATES */
static const double SMALL_FIXED_DELTA;
static const double TOLERENCE;
diff --git a/src/theory/arith/arith_rewriter.cpp b/src/theory/arith/arith_rewriter.cpp
index aa5049ed4..247c09294 100644
--- a/src/theory/arith/arith_rewriter.cpp
+++ b/src/theory/arith/arith_rewriter.cpp
@@ -29,7 +29,8 @@ namespace theory {
namespace arith {
bool ArithRewriter::isAtom(TNode n) {
- return arith::isRelationOperator(n.getKind());
+ Kind k = n.getKind();
+ return arith::isRelationOperator(k) || k == kind::IS_INTEGER || k == kind::DIVISIBLE;
}
RewriteResponse ArithRewriter::rewriteConstant(TNode t){
@@ -98,11 +99,28 @@ RewriteResponse ArithRewriter::preRewriteTerm(TNode t){
return preRewritePlus(t);
case kind::MULT:
return preRewriteMult(t);
- //case kind::INTS_DIVISION:
- //case kind::INTS_MODULUS:
+ case kind::INTS_DIVISION:
+ case kind::INTS_MODULUS:
+ return RewriteResponse(REWRITE_DONE, t);
case kind::INTS_DIVISION_TOTAL:
case kind::INTS_MODULUS_TOTAL:
return rewriteIntsDivModTotal(t,true);
+ case kind::ABS:
+ if(t[0].isConst()) {
+ const Rational& rat = t[0].getConst<Rational>();
+ if(rat >= 0) {
+ return RewriteResponse(REWRITE_DONE, t[0]);
+ } else {
+ return RewriteResponse(REWRITE_DONE,
+ NodeManager::currentNM()->mkConst(-rat));
+ }
+ }
+ return RewriteResponse(REWRITE_DONE, t);
+ case kind::IS_INTEGER:
+ case kind::TO_INTEGER:
+ return RewriteResponse(REWRITE_DONE, t);
+ case kind::TO_REAL:
+ return RewriteResponse(REWRITE_DONE, t[0]);
default:
Unhandled(k);
}
@@ -126,11 +144,44 @@ RewriteResponse ArithRewriter::postRewriteTerm(TNode t){
return postRewritePlus(t);
case kind::MULT:
return postRewriteMult(t);
- //case kind::INTS_DIVISION:
- //case kind::INTS_MODULUS:
+ case kind::INTS_DIVISION:
+ case kind::INTS_MODULUS:
+ return RewriteResponse(REWRITE_DONE, t);
case kind::INTS_DIVISION_TOTAL:
case kind::INTS_MODULUS_TOTAL:
return rewriteIntsDivModTotal(t, false);
+ case kind::ABS:
+ if(t[0].isConst()) {
+ const Rational& rat = t[0].getConst<Rational>();
+ if(rat >= 0) {
+ return RewriteResponse(REWRITE_DONE, t[0]);
+ } else {
+ return RewriteResponse(REWRITE_DONE,
+ NodeManager::currentNM()->mkConst(-rat));
+ }
+ }
+ case kind::TO_REAL:
+ return RewriteResponse(REWRITE_DONE, t[0]);
+ case kind::TO_INTEGER:
+ if(t[0].isConst()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(Rational(t[0].getConst<Rational>().floor())));
+ }
+ if(t[0].getType().isInteger()) {
+ return RewriteResponse(REWRITE_DONE, t[0]);
+ }
+ //Unimplemented("TO_INTEGER, nonconstant");
+ //return rewriteToInteger(t);
+ return RewriteResponse(REWRITE_DONE, t);
+ case kind::IS_INTEGER:
+ if(t[0].isConst()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(t[0].getConst<Rational>().getDenominator() == 1));
+ }
+ if(t[0].getType().isInteger()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true));
+ }
+ //Unimplemented("IS_INTEGER, nonconstant");
+ //return rewriteIsInteger(t);
+ return RewriteResponse(REWRITE_DONE, t);
default:
Unreachable();
}
@@ -190,6 +241,25 @@ RewriteResponse ArithRewriter::postRewriteMult(TNode t){
}
RewriteResponse ArithRewriter::postRewriteAtom(TNode atom){
+ if(atom.getKind() == kind::IS_INTEGER) {
+ if(atom[0].isConst()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(atom[0].getConst<Rational>().isIntegral()));
+ }
+ if(atom[0].getType().isInteger()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true));
+ }
+ // not supported, but this isn't the right place to complain
+ return RewriteResponse(REWRITE_DONE, atom);
+ } else if(atom.getKind() == kind::DIVISIBLE) {
+ if(atom[0].isConst()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(bool((atom[0].getConst<Rational>() / atom.getOperator().getConst<Divisible>().k).isIntegral())));
+ }
+ if(atom.getOperator().getConst<Divisible>().k.isOne()) {
+ return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true));
+ }
+ return RewriteResponse(REWRITE_AGAIN, NodeManager::currentNM()->mkNode(kind::EQUAL, NodeManager::currentNM()->mkNode(kind::INTS_MODULUS_TOTAL, atom[0], NodeManager::currentNM()->mkConst(Rational(atom.getOperator().getConst<Divisible>().k))), NodeManager::currentNM()->mkConst(Rational(0))));
+ }
+
// left |><| right
TNode left = atom[0];
TNode right = atom[1];
@@ -217,6 +287,14 @@ RewriteResponse ArithRewriter::preRewriteAtom(TNode atom){
}else if(atom.getKind() == kind::LT){
Node geq = currNM->mkNode(kind::GEQ, atom[0], atom[1]);
return RewriteResponse(REWRITE_DONE, currNM->mkNode(kind::NOT, geq));
+ }else if(atom.getKind() == kind::IS_INTEGER){
+ if(atom[0].getType().isInteger()){
+ return RewriteResponse(REWRITE_DONE, currNM->mkConst(true));
+ }
+ }else if(atom.getKind() == kind::DIVISIBLE){
+ if(atom.getOperator().getConst<Divisible>().k.isOne()){
+ return RewriteResponse(REWRITE_DONE, currNM->mkConst(true));
+ }
}
return RewriteResponse(REWRITE_DONE, atom);
@@ -329,6 +407,13 @@ RewriteResponse ArithRewriter::rewriteIntsDivModTotal(TNode t, bool pre){
Assert(k == kind::INTS_DIVISION || k == kind::INTS_DIVISION_TOTAL);
return RewriteResponse(REWRITE_AGAIN, n);
}
+ }else if(dIsConstant && d.getConst<Rational>().isNegativeOne()){
+ if(k == kind::INTS_MODULUS || k == kind::INTS_MODULUS_TOTAL){
+ return RewriteResponse(REWRITE_DONE, mkRationalNode(0));
+ }else{
+ Assert(k == kind::INTS_DIVISION || k == kind::INTS_DIVISION_TOTAL);
+ return RewriteResponse(REWRITE_AGAIN, NodeManager::currentNM()->mkNode(kind::UMINUS, n));
+ }
}else if(dIsConstant && n.getKind() == kind::CONST_RATIONAL){
Assert(d.getConst<Rational>().isIntegral());
Assert(n.getConst<Rational>().isIntegral());
diff --git a/src/theory/arith/arith_static_learner.h b/src/theory/arith/arith_static_learner.h
index c4bf92a16..d8407eeba 100644
--- a/src/theory/arith/arith_static_learner.h
+++ b/src/theory/arith/arith_static_learner.h
@@ -23,7 +23,6 @@
#include "util/statistics_registry.h"
#include "theory/arith/arith_utilities.h"
-#include "theory/substitutions.h"
#include "context/context.h"
#include "context/cdlist.h"
diff --git a/src/theory/arith/attempt_solution_simplex.cpp b/src/theory/arith/attempt_solution_simplex.cpp
new file mode 100644
index 000000000..f0cecc24b
--- /dev/null
+++ b/src/theory/arith/attempt_solution_simplex.cpp
@@ -0,0 +1,135 @@
+
+#include "theory/arith/attempt_solution_simplex.h"
+#include "theory/arith/options.h"
+#include "theory/arith/constraint.h"
+
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace arith {
+
+AttemptSolutionSDP::AttemptSolutionSDP(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
+ : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
+ , d_statistics()
+{ }
+
+AttemptSolutionSDP::Statistics::Statistics():
+ d_searchTime("theory::arith::attempt::searchTime"),
+ d_queueTime("theory::arith::attempt::queueTime"),
+ d_conflicts("theory::arith::attempt::conflicts", 0)
+{
+ StatisticsRegistry::registerStat(&d_searchTime);
+ StatisticsRegistry::registerStat(&d_queueTime);
+ StatisticsRegistry::registerStat(&d_conflicts);
+}
+
+AttemptSolutionSDP::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_searchTime);
+ StatisticsRegistry::unregisterStat(&d_queueTime);
+ StatisticsRegistry::unregisterStat(&d_conflicts);
+}
+
+bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{
+ return nv[v] == d_variables.getAssignment(v);
+}
+
+Result::Sat 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(VAR_ORDER);
+
+ static int instance = 0;
+ ++instance;
+
+ if(processSignals()){
+ Debug("arith::findModel") << "attemptSolution("<< instance <<") early conflict" << endl;
+ d_conflictVariables.purge();
+ return Result::UNSAT;
+ }else if(d_errorSet.errorEmpty()){
+ Debug("arith::findModel") << "attemptSolution("<< instance <<") 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;
+ //Message() << toRemove << " " << toAdd << endl;
+
+ d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]);
+
+ Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+ //Message() << 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::SAT_UNKNOWN;
+ }
+}
+
+}/* CVC4::theory::arith namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/arith/pure_update_simplex.h b/src/theory/arith/attempt_solution_simplex.h
index 50b751d7b..5bcdc6aab 100644
--- a/src/theory/arith/pure_update_simplex.h
+++ b/src/theory/arith/attempt_solution_simplex.h
@@ -53,64 +53,43 @@
#pragma once
-#include "theory/arith/simplex.h"
-#include "util/dense_map.h"
#include "util/statistics_registry.h"
-#include <stdint.h>
-#include "theory/arith/arithvar.h"
-#include "theory/arith/delta_rational.h"
+#include "theory/arith/simplex.h"
+#include "theory/arith/approx_simplex.h"
namespace CVC4 {
namespace theory {
namespace arith {
-class PureUpdateSimplexDecisionProcedure : public SimplexDecisionProcedure{
+class AttemptSolutionSDP : public SimplexDecisionProcedure {
public:
- PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
+ AttemptSolutionSDP(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc);
- Result::Sat findModel(bool exactResult);
+ Result::Sat attempt(const ApproximateSimplex::Solution& sol);
-private:
- ArithVar d_focusErrorVar;
-
- bool attemptPureUpdates();
+ Result::Sat findModel(bool exactResult){
+ Unreachable();
+ }
- /**
- * 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);
+private:
+ bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const;
bool processSignals(){
- TimerStat &timer = d_statistics.d_processSignalsTime;
- IntStat& conflictStat = d_statistics.d_foundConflicts;
+ 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:
- IntStat d_pureUpdateFoundUnsat;
- IntStat d_pureUpdateFoundSat;
- IntStat d_pureUpdateMissed;
- IntStat d_pureUpdates;
- IntStat d_pureUpdateDropped;
- IntStat d_pureUpdateConflicts;
-
- IntStat d_foundConflicts;
-
- TimerStat d_attemptPureUpdatesTimer;
- TimerStat d_processSignalsTime;
-
- TimerStat d_constructionTimer;
+ TimerStat d_searchTime;
+ TimerStat d_queueTime;
+ IntStat d_conflicts;
Statistics();
~Statistics();
} d_statistics;
-};/* class PureUpdateSimplexDecisionProcedure */
+};/* class AttemptSolutionSDP */
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
diff --git a/src/theory/arith/bound_counts.h b/src/theory/arith/bound_counts.h
index 954cc056a..49c1a94ce 100644
--- a/src/theory/arith/bound_counts.h
+++ b/src/theory/arith/bound_counts.h
@@ -1,3 +1,20 @@
+/********************* */
+/*! \file bound_counts.h
+ ** \verbatim
+ ** Original author: Tim King
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
#include "cvc4_private.h"
#pragma once
@@ -10,62 +27,92 @@ namespace CVC4 {
namespace theory {
namespace arith {
-/**
- * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j
- *
- * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)}
- * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)}
- */
class BoundCounts {
private:
- uint32_t d_atLowerBounds;
- uint32_t d_atUpperBounds;
+ uint32_t d_lowerBoundCount;
+ uint32_t d_upperBoundCount;
public:
- BoundCounts() : d_atLowerBounds(0), d_atUpperBounds(0) {}
+ BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {}
BoundCounts(uint32_t lbs, uint32_t ubs)
- : d_atLowerBounds(lbs), d_atUpperBounds(ubs) {}
+ : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {}
bool operator==(BoundCounts bc) const {
- return d_atLowerBounds == bc.d_atLowerBounds
- && d_atUpperBounds == bc.d_atUpperBounds;
+ return d_lowerBoundCount == bc.d_lowerBoundCount
+ && d_upperBoundCount == bc.d_upperBoundCount;
}
bool operator!=(BoundCounts bc) const {
- return d_atLowerBounds != bc.d_atLowerBounds
- || d_atUpperBounds != bc.d_atUpperBounds;
+ 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_atLowerBounds == 0 && d_atUpperBounds == 0; }
- inline uint32_t atLowerBounds() const{
- return d_atLowerBounds;
+
+ inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; }
+ inline uint32_t lowerBoundCount() const{
+ return d_lowerBoundCount;
}
- inline uint32_t atUpperBounds() const{
- return d_atUpperBounds;
+ inline uint32_t upperBoundCount() const{
+ return d_upperBoundCount;
}
inline BoundCounts operator+(BoundCounts bc) const{
- return BoundCounts(d_atLowerBounds + bc.d_atLowerBounds,
- d_atUpperBounds + bc.d_atUpperBounds);
+ return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount,
+ d_upperBoundCount + bc.d_upperBoundCount);
}
inline BoundCounts operator-(BoundCounts bc) const {
- Assert(d_atLowerBounds >= bc.d_atLowerBounds);
- Assert(d_atUpperBounds >= bc.d_atUpperBounds);
- return BoundCounts(d_atLowerBounds - bc.d_atLowerBounds,
- d_atUpperBounds - bc.d_atUpperBounds);
+ 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){
- Assert(before != after);
- if(sgn < 0){
- Assert(d_atUpperBounds >= before.d_atLowerBounds);
- Assert(d_atLowerBounds >= before.d_atUpperBounds);
- d_atUpperBounds += after.d_atLowerBounds - before.d_atLowerBounds;
- d_atLowerBounds += after.d_atUpperBounds - before.d_atUpperBounds;
+ 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_atUpperBounds >= before.d_atUpperBounds);
- Assert(d_atLowerBounds >= before.d_atLowerBounds);
- d_atUpperBounds += after.d_atUpperBounds - before.d_atUpperBounds;
- d_atLowerBounds += after.d_atLowerBounds - before.d_atLowerBounds;
+ 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;
}
}
@@ -74,69 +121,112 @@ public:
Assert(!bc.isZero());
if(before < 0){
- d_atUpperBounds -= bc.d_atLowerBounds;
- d_atLowerBounds -= bc.d_atUpperBounds;
+ d_upperBoundCount -= bc.d_lowerBoundCount;
+ d_lowerBoundCount -= bc.d_upperBoundCount;
}else if(before > 0){
- d_atUpperBounds -= bc.d_atUpperBounds;
- d_atLowerBounds -= bc.d_atLowerBounds;
+ d_upperBoundCount -= bc.d_upperBoundCount;
+ d_lowerBoundCount -= bc.d_lowerBoundCount;
}
if(after < 0){
- d_atUpperBounds += bc.d_atLowerBounds;
- d_atLowerBounds += bc.d_atUpperBounds;
+ d_upperBoundCount += bc.d_lowerBoundCount;
+ d_lowerBoundCount += bc.d_upperBoundCount;
}else if(after > 0){
- d_atUpperBounds += bc.d_atUpperBounds;
- d_atLowerBounds += bc.d_atLowerBounds;
+ d_upperBoundCount += bc.d_upperBoundCount;
+ d_lowerBoundCount += bc.d_lowerBoundCount;
}
}
+};
- inline BoundCounts& operator+=(BoundCounts bc) {
- d_atUpperBounds += bc.d_atUpperBounds;
- d_atLowerBounds += bc.d_atLowerBounds;
- return *this;
- }
+class BoundsInfo {
+private:
- inline BoundCounts& operator-=(BoundCounts bc) {
- Assert(d_atLowerBounds >= bc.d_atLowerBounds);
- Assert(d_atUpperBounds >= bc.d_atUpperBounds);
- d_atUpperBounds -= bc.d_atUpperBounds;
- d_atLowerBounds -= bc.d_atLowerBounds;
+ /**
+ * 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;
- return *this;
+ /** 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 BoundCounts multiplyBySgn(int sgn) const{
- if(sgn > 0){
- return *this;
- }else if(sgn == 0){
- return BoundCounts(0,0);
- }else{
- return BoundCounts(d_atUpperBounds, d_atLowerBounds);
- }
+ inline BoundsInfo& operator+=(const BoundsInfo& bc) {
+ d_atBounds += bc.d_atBounds;
+ d_hasBounds += bc.d_hasBounds;
+ return (*this);
}
-};
-typedef DenseMap<BoundCounts> BoundCountingVector;
+ /** 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));
+ }
-class BoundCountingLookup {
-private:
- BoundCountingVector* d_bc;
-public:
- BoundCountingLookup(BoundCountingVector* bc) : d_bc(bc) {}
- BoundCounts boundCounts(ArithVar v) const {
- Assert(d_bc->isKey(v));
- return (*d_bc)[v];
+ 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.atLowerBounds() << ", "
- << bc.atUpperBounds() << "]";
+ os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]";
return os;
}
-class BoundCountsCallback {
+inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){
+ os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]";
+ return os;
+}
+class BoundUpdateCallback {
public:
- virtual void operator()(ArithVar v, BoundCounts bc) = 0;
+ virtual void operator()(ArithVar v, const BoundsInfo& up) = 0;
};
}/* CVC4::theory::arith namespace */
diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp
index 6b6170b20..1e827d316 100644
--- a/src/theory/arith/callbacks.cpp
+++ b/src/theory/arith/callbacks.cpp
@@ -32,6 +32,10 @@ void RaiseConflict::operator()(Node n){
d_ta.raiseConflict(n);
}
+const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{
+ return d_ta.boundsInfo(basic);
+}
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h
index 0d754d159..718799e9f 100644
--- a/src/theory/arith/callbacks.h
+++ b/src/theory/arith/callbacks.h
@@ -3,10 +3,10 @@
#include "expr/node.h"
#include "util/rational.h"
-#include "context/cdlist.h"
#include "theory/arith/theory_arith_private_forward.h"
#include "theory/arith/arithvar.h"
+#include "theory/arith/bound_counts.h"
namespace CVC4 {
namespace theory {
@@ -87,6 +87,20 @@ public:
void operator()(Node n);
};
+class BoundCountingLookup {
+private:
+ TheoryArithPrivate& d_ta;
+public:
+ BoundCountingLookup(TheoryArithPrivate& ta) : d_ta(ta) {}
+ const BoundsInfo& boundsInfo(ArithVar basic) const;
+ BoundCounts atBounds(ArithVar basic) const{
+ return boundsInfo(basic).atBounds();
+ }
+ BoundCounts hasBounds(ArithVar basic) const {
+ return boundsInfo(basic).hasBounds();
+ }
+};
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp
index e26687bf1..78b9d3494 100644
--- a/src/theory/arith/constraint.cpp
+++ b/src/theory/arith/constraint.cpp
@@ -922,6 +922,28 @@ bool ConstraintValue::proofIsEmpty() const{
return result;
}
+Node ConstraintValue::makeImplication(const std::vector<Constraint>& b) const{
+ Node antecedent = makeConjunction(b);
+ Node implied = getLiteral();
+ return antecedent.impNode(implied);
+}
+
+
+Node ConstraintValue::makeConjunction(const std::vector<Constraint>& b){
+ NodeBuilder<> nb(kind::AND);
+ for(vector<Constraint>::const_iterator i = b.begin(), end = b.end(); i != end; ++i){
+ Constraint b_i = *i;
+ b_i->explainBefore(nb, AssertionOrderSentinel);
+ }
+ if(nb.getNumChildren() >= 2){
+ return nb;
+ }else if(nb.getNumChildren() == 1){
+ return nb[0];
+ }else{
+ return mkBoolNode(true);
+ }
+}
+
void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) const{
Assert(hasProof());
Assert(!isSelfExplaining() || assertedToTheTheory());
diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h
index a5d64a652..4966115d2 100644
--- a/src/theory/arith/constraint.h
+++ b/src/theory/arith/constraint.h
@@ -598,6 +598,8 @@ public:
void impliedBy(Constraint a, Constraint b);
void impliedBy(const std::vector<Constraint>& b);
+ Node makeImplication(const std::vector<Constraint>& b) const;
+ static Node makeConjunction(const std::vector<Constraint>& b);
/** The node must have a proof already and be eligible for propagation! */
void propagate();
diff --git a/src/theory/arith/dual_simplex.cpp b/src/theory/arith/dual_simplex.cpp
index 7caee6708..a9304ae76 100644
--- a/src/theory/arith/dual_simplex.cpp
+++ b/src/theory/arith/dual_simplex.cpp
@@ -196,19 +196,19 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI
//DeltaRational beta_i = d_variables.getAssignment(x_i);
ArithVar x_j = ARITHVAR_SENTINEL;
- int32_t prevErrorSize = d_errorSet.errorSize();
+ int32_t prevErrorSize CVC4_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);
+ // ++(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;
+ // return true;
}else{
const DeltaRational& l_i = d_variables.getLowerBound(x_i);
d_linEq.pivotAndUpdate(x_i, x_j, l_i);
@@ -217,13 +217,13 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI
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);
+ // ++(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;
+ // return true;
}else{
const DeltaRational& u_i = d_variables.getUpperBound(x_i);
d_linEq.pivotAndUpdate(x_i, x_j, u_i);
@@ -232,16 +232,19 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI
Assert(x_j != ARITHVAR_SENTINEL);
bool conflict = processSignals();
- int32_t currErrorSize = d_errorSet.errorSize();
+ int32_t currErrorSize CVC4_UNUSED = d_errorSet.errorSize();
d_pivots++;
- // cout << "#" << d_pivots
- // << " c" << conflict
- // << " d" << (prevErrorSize - currErrorSize)
- // << " f" << d_errorSet.inError(x_j)
- // << " h" << d_conflictVariables.isMember(x_j)
- // << " " << x_i << "->" << x_j
- // << endl;
+ if(Debug.isOn("arith::dual")){
+ Debug("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;
diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp
index ee72d1949..dea78acf7 100644
--- a/src/theory/arith/error_set.cpp
+++ b/src/theory/arith/error_set.cpp
@@ -1,11 +1,11 @@
/********************* */
-/*! \file arith_priority_queue.cpp
+/*! \file error_set.cpp
** \verbatim
- ** Original author: taking
- ** Major contributors: mdeters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** Original author: Tim King
+ ** Major contributors: none
+ ** Minor contributors (to current version): Morgan Deters
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h
index 91d7e49ea..d1b692cb4 100644
--- a/src/theory/arith/error_set.h
+++ b/src/theory/arith/error_set.h
@@ -1,11 +1,11 @@
/********************* */
-/*! \file arith_priority_queue.h
+/*! \file error_set.h
** \verbatim
- ** Original author: taking
+ ** Original author: Tim King
** Major contributors: none
- ** Minor contributors (to current version): mdeters
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** Minor contributors (to current version): Morgan Deters
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
@@ -26,9 +26,9 @@
#include "theory/arith/partial_model.h"
#include "theory/arith/arith_heuristic_pivot_rule.h"
#include "theory/arith/tableau_sizes.h"
+#include "theory/arith/callbacks.h"
#include "util/statistics_registry.h"
-//#include <boost/heap/d_ary_heap.hpp>
#if CVC4_GCC_HAS_PB_DS_BUG
// Unfortunate bug in some older GCCs (e.g., v4.2):
@@ -377,8 +377,8 @@ public:
uint32_t sumMetric(ArithVar a) const{
Assert(inError(a));
- BoundCounts bcs = d_boundLookup.boundCounts(a);
- uint32_t count = getSgn(a) > 0 ? bcs.atUpperBounds() : bcs.atLowerBounds();
+ BoundCounts bcs = d_boundLookup.atBounds(a);
+ uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount();
uint32_t length = d_tableauSizes.getRowLength(a);
diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp
index ac4625ba3..e99e62505 100644
--- a/src/theory/arith/fc_simplex.cpp
+++ b/src/theory/arith/fc_simplex.cpp
@@ -289,7 +289,7 @@ UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, Linear
Debug("arith::selectPrimalUpdate")
<< "selectPrimalUpdate " << instance << endl
<< basic << " " << d_tableau.basicRowLength(basic)
- << " " << d_linEq._countBounds(basic) << endl;
+ << " " << d_linEq.debugBasicAtBoundCount(basic) << endl;
static const int s_maxCandidatesAfterImprove = 3;
bool isFocus = basic == d_focusErrorVar;
@@ -358,18 +358,9 @@ UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, Linear
ArithVar curr = cand.d_nb;
const Rational& coeff = *cand.d_coeff;
-#warning "Who is using computeSafeUpdate?"
LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
- //int curr_movement = cand.d_sgn;
- // if(isFocus){
- // currProposal = d_linEq.speculativeUpdate(curr, coeff, upf);
- // }else{
- // currProposal = UpdateInfo(curr, curr_movement);
- // d_linEq.computeSafeUpdate(currProposal, bpf);
- // }
-
Debug("arith::selectPrimalUpdate")
<< "selected " << selected << endl
<< "currProp " << currProposal << endl
@@ -505,7 +496,7 @@ void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{
int dir = !d_variables.assignmentIsConsistent(updated) ?
d_errorSet.getSgn(updated) : 0;
Debug("updateAndSignal") << " dir " << dir;
- Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl;
+ Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
}
bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){
@@ -530,7 +521,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit
ArithVar leaving = selected.leaving();
ss << "leaving " << leaving
<< " " << d_tableau.basicRowLength(leaving)
- << " " << d_linEq._countBounds(leaving)
+ << " " << d_linEq.debugBasicAtBoundCount(leaving)
<< endl;
}
if(degenerate(w) && selected.describesPivot()){
@@ -539,7 +530,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit
<< "degenerate " << leaving
<< ", atBounds " << d_linEq.basicsAtBounds(selected)
<< ", len " << d_tableau.basicRowLength(leaving)
- << ", bc " << d_linEq._countBounds(leaving)
+ << ", bc " << d_linEq.debugBasicAtBoundCount(leaving)
<< endl;
}
}
diff --git a/src/theory/arith/kinds b/src/theory/arith/kinds
index 07cfcc9e5..a8a4047ca 100644
--- a/src/theory/arith/kinds
+++ b/src/theory/arith/kinds
@@ -23,8 +23,16 @@ operator INTS_DIVISION 2 "ints division (user symbol)"
operator INTS_DIVISION_TOTAL 2 "ints division with interpreted division by 0 (internal symbol)"
operator INTS_MODULUS 2 "ints modulus (user symbol)"
operator INTS_MODULUS_TOTAL 2 "ints modulus with interpreted division by 0 (internal symbol)"
+operator ABS 1 "absolute value"
+parameterized DIVISIBLE DIVISIBLE_OP 1 "divisibility-by-k predicate"
operator POW 2 "arithmetic power"
+constant DIVISIBLE_OP \
+ ::CVC4::Divisible \
+ ::CVC4::DivisibleHashFunction \
+ "util/divisible.h" \
+ "operator for the divisibility-by-k predicate"
+
sort REAL_TYPE \
Cardinality::REALS \
well-founded \
@@ -72,6 +80,10 @@ operator LEQ 2 "less than or equal, x <= y"
operator GT 2 "greater than, x > y"
operator GEQ 2 "greater than or equal, x >= y"
+operator IS_INTEGER 1 "term is integer"
+operator TO_INTEGER 1 "cast term to integer"
+operator TO_REAL 1 "cast term to real"
+
typerule PLUS ::CVC4::theory::arith::ArithOperatorTypeRule
typerule MULT ::CVC4::theory::arith::ArithOperatorTypeRule
typerule MINUS ::CVC4::theory::arith::ArithOperatorTypeRule
@@ -86,11 +98,17 @@ typerule LEQ ::CVC4::theory::arith::ArithPredicateTypeRule
typerule GT ::CVC4::theory::arith::ArithPredicateTypeRule
typerule GEQ ::CVC4::theory::arith::ArithPredicateTypeRule
-typerule INTS_DIVISION ::CVC4::theory::arith::ArithOperatorTypeRule
-typerule INTS_MODULUS ::CVC4::theory::arith::ArithOperatorTypeRule
+typerule TO_REAL ::CVC4::theory::arith::ArithOperatorTypeRule
+typerule TO_INTEGER ::CVC4::theory::arith::ArithOperatorTypeRule
+typerule IS_INTEGER ::CVC4::theory::arith::ArithUnaryPredicateTypeRule
+
+typerule ABS ::CVC4::theory::arith::IntOperatorTypeRule
+typerule INTS_DIVISION ::CVC4::theory::arith::IntOperatorTypeRule
+typerule INTS_MODULUS ::CVC4::theory::arith::IntOperatorTypeRule
+typerule DIVISIBLE ::CVC4::theory::arith::IntUnaryPredicateTypeRule
typerule DIVISION_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule
-typerule INTS_DIVISION_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule
-typerule INTS_MODULUS_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule
+typerule INTS_DIVISION_TOTAL ::CVC4::theory::arith::IntOperatorTypeRule
+typerule INTS_MODULUS_TOTAL ::CVC4::theory::arith::IntOperatorTypeRule
endtheory
diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp
index 42d8b41f8..5817a3629 100644
--- a/src/theory/arith/linear_equality.cpp
+++ b/src/theory/arith/linear_equality.cpp
@@ -25,18 +25,10 @@ namespace theory {
namespace arith {
/* Explicitly instatiate these functions. */
-template void LinearEqualityModule::propagateNonbasics<true>(ArithVar basic, Constraint c);
-template void LinearEqualityModule::propagateNonbasics<false>(ArithVar basic, Constraint c);
template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const;
template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const;
-// template bool LinearEqualityModule::preferNonDegenerate<true>(const UpdateInfo& a, const UpdateInfo& b) const;
-// template bool LinearEqualityModule::preferNonDegenerate<false>(const UpdateInfo& a, const UpdateInfo& b) const;
-
-// template bool LinearEqualityModule::preferErrorsFixed<true>(const UpdateInfo& a, const UpdateInfo& b) const;
-// template bool LinearEqualityModule::preferErrorsFixed<false>(const UpdateInfo& a, const UpdateInfo& b) const;
-
template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const;
template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const;
@@ -57,14 +49,13 @@ void Border::output(std::ostream& out) const{
<< "}";
}
-LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f):
+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_relevantErrorBuffer(),
- d_boundTracking(boundTracking),
+ d_btracking(boundsTracking),
d_areTracking(false),
d_trackCallback(this)
{}
@@ -77,7 +68,8 @@ LinearEqualityModule::Statistics::Statistics():
d_weakeningAttempts("theory::arith::weakening::attempts",0),
d_weakeningSuccesses("theory::arith::weakening::success",0),
d_weakenings("theory::arith::weakening::total",0),
- d_weakenTime("theory::arith::weakening::time")
+ d_weakenTime("theory::arith::weakening::time"),
+ d_forceTime("theory::arith::forcing::time")
{
StatisticsRegistry::registerStat(&d_statPivots);
StatisticsRegistry::registerStat(&d_statUpdates);
@@ -89,6 +81,7 @@ LinearEqualityModule::Statistics::Statistics():
StatisticsRegistry::registerStat(&d_weakeningSuccesses);
StatisticsRegistry::registerStat(&d_weakenings);
StatisticsRegistry::registerStat(&d_weakenTime);
+ StatisticsRegistry::registerStat(&d_forceTime);
}
LinearEqualityModule::Statistics::~Statistics(){
@@ -102,50 +95,56 @@ LinearEqualityModule::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
StatisticsRegistry::unregisterStat(&d_weakenings);
StatisticsRegistry::unregisterStat(&d_weakenTime);
+ StatisticsRegistry::unregisterStat(&d_forceTime);
}
-void LinearEqualityModule::includeBoundCountChange(ArithVar nb, BoundCounts prev){
- if(d_tableau.isBasic(nb)){
- return;
- }
- Assert(!d_tableau.isBasic(nb));
+
+void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){
Assert(!d_areTracking);
- BoundCounts curr = d_variables.boundCounts(nb);
+ BoundsInfo curr = d_variables.boundsInfo(v);
Assert(prev != curr);
- Tableau::ColIterator basicIter = d_tableau.colIterator(nb);
+ Tableau::ColIterator basicIter = d_tableau.colIterator(v);
for(; !basicIter.atEnd(); ++basicIter){
const Tableau::Entry& entry = *basicIter;
- Assert(entry.getColVar() == nb);
+ Assert(entry.getColVar() == v);
int a_ijSgn = entry.getCoefficient().sgn();
- ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
-
- BoundCounts& counts = d_boundTracking.get(basic);
- Debug("includeBoundCountChange") << basic << " " << counts << " to " ;
- counts -= prev.multiplyBySgn(a_ijSgn);
- counts += curr.multiplyBySgn(a_ijSgn);
- Debug("includeBoundCountChange") << counts << " " << a_ijSgn << std::endl;
+ RowIndex ridx = entry.getRowIndex();
+ BoundsInfo& counts = d_btracking.get(ridx);
+ Debug("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ;
+ counts.addInChange(a_ijSgn, prev, curr);
+ Debug("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl;
}
- d_boundTracking.set(nb, curr);
}
void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){
for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){
ArithVar nb = *i;
- Assert(!d_tableau.isBasic(nb));
- const DeltaRational& newValue = many[nb];
- if(newValue != d_variables.getAssignment(nb)){
- Trace("arith::updateMany")
- << "updateMany:" << nb << " "
- << d_variables.getAssignment(nb) << " to "<< newValue << endl;
- update(nb, newValue);
+ 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;
@@ -179,10 +178,12 @@ void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){
Assert(toAdd != ARITHVAR_SENTINEL);
Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl;
+ Message() << toRemove << " " << toAdd << endl;
d_tableau.pivot(toRemove, toAdd, d_trackCallback);
d_basicVariableUpdates(toAdd);
Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl;
+ Message() << needsToBeAdded.size() << "to go" << endl;
needsToBeAdded.remove(toAdd);
}
}
@@ -231,9 +232,9 @@ void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
<< d_variables.getAssignment(x_i) << "|-> " << v << endl;
- BoundCounts before = d_variables.boundCounts(x_i);
+ BoundCounts before = d_variables.atBoundCounts(x_i);
d_variables.setAssignment(x_i, v);
- BoundCounts after = d_variables.boundCounts(x_i);
+ BoundCounts after = d_variables.atBoundCounts(x_i);
bool anyChange = before != after;
@@ -242,17 +243,24 @@ void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){
const Tableau::Entry& entry = *colIter;
Assert(entry.getColVar() == x_i);
- ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex());
+ 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);
Debug("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);
- if(anyChange && basicIsTracked(x_j)){
- BoundCounts& next_bc_k = d_boundTracking.get(x_j);
- next_bc_k.addInChange(a_ji.sgn(), before, after);
+ 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);
@@ -332,7 +340,7 @@ void LinearEqualityModule::debugCheckTracking(){
ArithVar var = entry.getColVar();
const Rational& coeff = entry.getCoefficient();
DeltaRational beta = d_variables.getAssignment(var);
- Debug("arith::tracking") << var << " " << d_variables.boundCounts(var)
+ Debug("arith::tracking") << var << " " << d_variables.boundsInfo(var)
<< " " << beta << coeff;
if(d_variables.hasLowerBound(var)){
Debug("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")";
@@ -345,11 +353,12 @@ void LinearEqualityModule::debugCheckTracking(){
Debug("arith::tracking") << "end row"<< endl;
if(basicIsTracked(basic)){
- BoundCounts computed = computeBoundCounts(basic);
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+ BoundsInfo computed = computeRowBoundInfo(ridx, false);
Debug("arith::tracking")
<< "computed " << computed
- << " tracking " << d_boundTracking[basic] << endl;
- Assert(computed == d_boundTracking[basic]);
+ << " tracking " << d_btracking[ridx] << endl;
+ Assert(computed == d_btracking[ridx]);
}
}
@@ -426,19 +435,19 @@ bool LinearEqualityModule::debugEntireLinEqIsConsistent(const string& s){
return result;
}
-DeltaRational LinearEqualityModule::computeBound(ArithVar basic, bool upperBound){
+DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const {
DeltaRational sum(0,0);
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){
+ for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){
const Tableau::Entry& entry = (*i);
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == basic) continue;
+ ArithVar v = entry.getColVar();
+ if(v == skip){ continue; }
+
const Rational& coeff = entry.getCoefficient();
- int sgn = coeff.sgn();
- bool ub = upperBound ? (sgn > 0) : (sgn < 0);
+ bool vUb = (rowUb == (coeff.sgn() > 0));
- const DeltaRational& bound = ub ?
- d_variables.getUpperBound(nonbasic):
- d_variables.getLowerBound(nonbasic);
+ const DeltaRational& bound = vUb ?
+ d_variables.getUpperBound(v):
+ d_variables.getLowerBound(v);
DeltaRational diff = bound * coeff;
sum = sum + diff;
@@ -449,7 +458,7 @@ DeltaRational LinearEqualityModule::computeBound(ArithVar basic, bool upperBound
/**
* Computes the value of a basic variable using the current assignment.
*/
-DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe){
+DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{
Assert(d_tableau.isBasic(x));
DeltaRational sum(0);
@@ -465,76 +474,71 @@ DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe){
return sum;
}
-bool LinearEqualityModule::hasBounds(ArithVar basic, bool upperBound){
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd(); ++iter){
+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 == basic) continue;
+ if(var == skip) { continue; }
+
int sgn = entry.getCoefficient().sgn();
- if(upperBound){
- if( (sgn < 0 && !d_variables.hasLowerBound(var)) ||
- (sgn > 0 && !d_variables.hasUpperBound(var))){
- return false;
- }
- }else{
- if( (sgn < 0 && !d_variables.hasUpperBound(var)) ||
- (sgn > 0 && !d_variables.hasLowerBound(var))){
- return false;
- }
+ bool selectUb = (rowUb == (sgn > 0));
+ bool hasBound = selectUb ?
+ d_variables.hasUpperBound(var):
+ d_variables.hasLowerBound(var);
+ if(!hasBound){
+ return &entry;
}
}
- return true;
+ return NULL;
}
-template <bool upperBound>
-void LinearEqualityModule::propagateNonbasics(ArithVar basic, Constraint c){
- Assert(d_tableau.isBasic(basic));
- Assert(c->getVariable() == basic);
+void LinearEqualityModule::propagateBasicFromRow(Constraint c){
+ Assert(c != NullConstraint);
+ Assert(c->isUpperBound() || c->isLowerBound());
Assert(!c->assertedToTheTheory());
- Assert(!upperBound || c->isUpperBound()); // upperbound implies c is an upperbound
- Assert(upperBound || c->isLowerBound()); // !upperbound implies c is a lowerbound
- //Assert(c->canBePropagated());
Assert(!c->hasProof());
- Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics("
- << basic <<") start" << endl;
+ bool upperBound = c->isUpperBound();
+ ArithVar basic = c->getVariable();
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
vector<Constraint> bounds;
+ propagateRow(bounds, ridx, upperBound, c);
+ c->impliedBy(bounds);
+}
+
+void LinearEqualityModule::propagateRow(vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c){
+ Assert(!c->assertedToTheTheory());
+ Assert(c->canBePropagated());
+ Assert(!c->hasProof());
+
+ ArithVar v = c->getVariable();
+ Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics("
+ << v <<") start" << endl;
- Tableau::RowIterator iter = d_tableau.basicRowIterator(basic);
+ Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
for(; !iter.atEnd(); ++iter){
const Tableau::Entry& entry = *iter;
ArithVar nonbasic = entry.getColVar();
- if(nonbasic == basic) continue;
+ if(nonbasic == v){ continue; }
const Rational& a_ij = entry.getCoefficient();
int sgn = a_ij.sgn();
Assert(sgn != 0);
- Constraint bound = NullConstraint;
- if(upperBound){
- if(sgn < 0){
- bound = d_variables.getLowerBoundConstraint(nonbasic);
- }else{
- Assert(sgn > 0);
- bound = d_variables.getUpperBoundConstraint(nonbasic);
- }
- }else{
- if(sgn < 0){
- bound = d_variables.getUpperBoundConstraint(nonbasic);
- }else{
- Assert(sgn > 0);
- bound = d_variables.getLowerBoundConstraint(nonbasic);
- }
- }
+ bool selectUb = rowUp ? (sgn > 0) : (sgn < 0);
+ Constraint bound = selectUb
+ ? d_variables.getUpperBoundConstraint(nonbasic)
+ : d_variables.getLowerBoundConstraint(nonbasic);
+
Assert(bound != NullConstraint);
Debug("arith::explainNonbasics") << "explainNonbasics" << bound << " for " << c << endl;
- bounds.push_back(bound);
+ into.push_back(bound);
}
- c->impliedBy(bounds);
Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics("
- << basic << ") done" << endl;
+ << v << ") done" << endl;
}
Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const {
@@ -745,60 +749,35 @@ void LinearEqualityModule::stopTrackingBoundCounts(){
}
-void LinearEqualityModule::trackVariable(ArithVar x_i){
- Assert(!basicIsTracked(x_i));
- BoundCounts counts(0,0);
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
- const Rational& a_ij = entry.getCoefficient();
- counts += (d_variables.oldBoundCounts(nonbasic)).multiplyBySgn(a_ij.sgn());
- }
- d_boundTracking.set(x_i, counts);
+void LinearEqualityModule::trackRowIndex(RowIndex ridx){
+ Assert(!rowIndexIsTracked(ridx));
+ BoundsInfo bi = computeRowBoundInfo(ridx, true);
+ d_btracking.set(ridx, bi);
}
-BoundCounts LinearEqualityModule::computeBoundCounts(ArithVar x_i) const{
- BoundCounts counts(0,0);
+BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{
+ BoundsInfo bi;
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
+ Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx);
+ for(; !iter.atEnd(); ++iter){
const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
+ ArithVar v = entry.getColVar();
const Rational& a_ij = entry.getCoefficient();
- counts += (d_variables.boundCounts(nonbasic)).multiplyBySgn(a_ij.sgn());
+ bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn());
}
-
- return counts;
+ return bi;
}
-// BoundCounts LinearEqualityModule::cachingCountBounds(ArithVar x_i) const{
-// if(d_boundTracking.isKey(x_i)){
-// return d_boundTracking[x_i];
-// }else{
-// return computeBoundCounts(x_i);
-// }
-// }
-BoundCounts LinearEqualityModule::_countBounds(ArithVar x_i) const {
- Assert(d_boundTracking.isKey(x_i));
- return d_boundTracking[x_i];
+BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const {
+ return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds();
}
-// BoundCounts LinearEqualityModule::countBounds(ArithVar x_i){
-// if(d_boundTracking.isKey(x_i)){
-// return d_boundTracking[x_i];
-// }else{
-// BoundCounts bc = computeBoundCounts(x_i);
-// if(d_areTracking){
-// d_boundTracking.set(x_i,bc);
-// }
-// return bc;
-// }
-// }
-
+/**
+ * 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());
@@ -814,289 +793,78 @@ bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const {
int toLB = (c->getType() == LowerBound ||
c->getType() == Equality) ? 1 : 0;
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
- BoundCounts bcs = d_boundTracking[basic];
+ BoundCounts bcs = d_btracking[ridx].atBounds();
// x = c*n + \sum d*m
- // n = 1/c * x + -1/c * (\sum d*m)
- BoundCounts nonb = bcs - d_variables.boundCounts(nonbasic).multiplyBySgn(coeffSgn);
+ // 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);
- nonb += BoundCounts(toLB, toUB).multiplyBySgn(coeffSgn);
uint32_t length = d_tableau.basicRowLength(basic);
Debug("basicsAtBounds")
<< "bcs " << bcs
<< "nonb " << nonb
<< "length " << length << endl;
-
+ // nonb has nb excluded.
if(nbdir < 0){
- return bcs.atLowerBounds() + 1 == length;
+ return nonb.lowerBoundCount() + 1 == length;
}else{
Assert(nbdir > 0);
- return bcs.atUpperBounds() + 1 == length;
+ return nonb.upperBoundCount() + 1 == length;
}
}
bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const {
Assert(basicIsTracked(basic));
- BoundCounts bcs = d_boundTracking[basic];
+ RowIndex ridx = d_tableau.basicToRowIndex(basic);
+
+ BoundCounts bcs = d_btracking[ridx].atBounds();
uint32_t length = d_tableau.basicRowLength(basic);
- return bcs.atLowerBounds() + 1 == length;
+ // 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));
- BoundCounts bcs = d_boundTracking[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 bcs.atUpperBounds() + 1 == length;
-}
-
-void LinearEqualityModule::trackingSwap(ArithVar basic, ArithVar nb, int nbSgn) {
- Assert(basicIsTracked(basic));
-
- // z = a*x + \sum b*y
- // x = (1/a) z + \sum (-1/a)*b*y
- // basicCount(z) = bc(a*x) + bc(\sum b y)
- // basicCount(x) = bc(z/a) + bc(\sum -b/a * y)
-
- // sgn(1/a) = sgn(a)
- // bc(a*x) = bc(x).multiply(sgn(a))
- // bc(z/a) = bc(z).multiply(sgn(a))
- // bc(\sum -b/a * y) = bc(\sum b y).multiplyBySgn(-sgn(a))
- // bc(\sum b y) = basicCount(z) - bc(a*x)
- // basicCount(x) =
- // = bc(z).multiply(sgn(a)) + (basicCount(z) - bc(a*x)).multiplyBySgn(-sgn(a))
-
- BoundCounts bc = d_boundTracking[basic];
- bc -= (d_variables.boundCounts(nb)).multiplyBySgn(nbSgn);
- bc = bc.multiplyBySgn(-nbSgn);
- bc += d_variables.boundCounts(basic).multiplyBySgn(nbSgn);
- d_boundTracking.set(nb, bc);
- d_boundTracking.remove(basic);
-}
-
-void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
- Assert(oldSgn != currSgn);
- BoundCounts nb_bc = d_variables.boundCounts(nb);
-
- if(!nb_bc.isZero()){
- ArithVar basic = d_tableau.rowIndexToBasic(ridx);
- Assert(basicIsTracked(basic));
-
- BoundCounts& basic_bc = d_boundTracking.get(basic);
- basic_bc.addInSgn(nb_bc, oldSgn, currSgn);
- }
+ return (ubc == length) ||
+ (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0);
}
-void LinearEqualityModule::computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction pref){
- ArithVar nb = inf.nonbasic();
- int sgn = inf.nonbasicDirection();
+void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) {
+ Assert(rowIndexIsTracked(ridx));
Assert(sgn != 0);
- Assert(!d_tableau.isBasic(nb));
-
- //inf.setErrorsChange(0);
- //inf.setlimiting = NullConstraint;
-
-
- // Error variables moving in the correct direction
- Assert(d_relevantErrorBuffer.empty());
-
- // phases :
- enum ComputeSafePhase {
- NoBoundSelected,
- NbsBoundSelected,
- BasicBoundSelected,
- DegenerateBoundSelected
- } phase;
-
- phase = NoBoundSelected;
-
- static int instance = 0;
- Debug("computeSafeUpdate") << "computeSafeUpdate " << (++instance) << endl;
-
- if(sgn > 0 && d_variables.hasUpperBound(nb)){
- Constraint ub = d_variables.getUpperBoundConstraint(nb);
- inf.updatePureFocus(ub->getValue() - d_variables.getAssignment(nb), ub);
-
- Assert(inf.nonbasicDelta().sgn() == sgn);
- Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl;
- phase = NbsBoundSelected;
- }else if(sgn < 0 && d_variables.hasLowerBound(nb)){
- Constraint lb = d_variables.getLowerBoundConstraint(nb);
- inf.updatePureFocus(lb->getValue() - d_variables.getAssignment(nb), lb);
-
- Assert(inf.nonbasicDelta().sgn() == sgn);
-
- Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl;
- phase = NbsBoundSelected;
- }
-
- Tableau::ColIterator basicIter = d_tableau.colIterator(nb);
- for(; !basicIter.atEnd(); ++basicIter){
- const Tableau::Entry& entry = *basicIter;
- Assert(entry.getColVar() == nb);
-
- ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
- const Rational& a_ji = entry.getCoefficient();
- int basic_movement = sgn * a_ji.sgn();
-
- Debug("computeSafeUpdate")
- << "computeSafeUpdate: "
- << basic << ", "
- << basic_movement << ", "
- << d_variables.cmpAssignmentUpperBound(basic) << ", "
- << d_variables.cmpAssignmentLowerBound(basic) << ", "
- << a_ji << ", "
- << d_variables.getAssignment(basic) << endl;
-
- Constraint proposal = NullConstraint;
-
- if(basic_movement > 0){
- if(d_variables.cmpAssignmentLowerBound(basic) < 0){
- d_relevantErrorBuffer.push_back(&entry);
- }
- if(d_variables.hasUpperBound(basic) &&
- d_variables.cmpAssignmentUpperBound(basic) <= 0){
- proposal = d_variables.getUpperBoundConstraint(basic);
- }
- }else if(basic_movement < 0){
- if(d_variables.cmpAssignmentUpperBound(basic) > 0){
- d_relevantErrorBuffer.push_back(&entry);
- }
- if(d_variables.hasLowerBound(basic) &&
- d_variables.cmpAssignmentLowerBound(basic) >= 0){
- proposal = d_variables.getLowerBoundConstraint(basic);
- }
- }
- if(proposal != NullConstraint){
- const Rational& coeff = entry.getCoefficient();
- DeltaRational diff = proposal->getValue() - d_variables.getAssignment(basic);
- diff /= coeff;
- int cmp = phase == NoBoundSelected ? 0 : diff.cmp(inf.nonbasicDelta());
- Assert(diff.sgn() == sgn || diff.sgn() == 0);
- bool prefer = false;
- switch(phase){
- case NoBoundSelected:
- prefer = true;
- break;
- case NbsBoundSelected:
- prefer = (sgn > 0 && cmp < 0 ) || (sgn < 0 && cmp > 0);
- break;
- case BasicBoundSelected:
- prefer =
- (sgn > 0 && cmp < 0 ) ||
- (sgn < 0 && cmp > 0) ||
- (cmp == 0 && basic == (this->*pref)(basic, inf.leaving()));
- break;
- case DegenerateBoundSelected:
- prefer = cmp == 0 && basic == (this->*pref)(basic, inf.leaving());
- break;
- }
- if(prefer){
- inf.updatePivot(diff, coeff, proposal);
-
- phase = (diff.sgn() != 0) ? BasicBoundSelected : DegenerateBoundSelected;
- }
- }
+ if(sgn < 0){
+ BoundsInfo& bi = d_btracking.get(ridx);
+ bi = bi.multiplyBySgn(sgn);
}
-
- if(phase == DegenerateBoundSelected){
- inf.setErrorsChange(0);
- }else{
- computedFixed(inf);
- }
- inf.determineFocusDirection();
-
- d_relevantErrorBuffer.clear();
}
-void LinearEqualityModule::computedFixed(UpdateInfo& proposal){
- Assert(proposal.nonbasicDirection() != 0);
- Assert(!d_tableau.isBasic(proposal.nonbasic()));
-
- //bool unconstrained = (proposal.d_limiting == NullConstraint);
-
- Assert(!proposal.unbounded() || !d_relevantErrorBuffer.empty());
-
- Assert(proposal.unbounded() ||
- proposal.nonbasicDelta().sgn() == proposal.nonbasicDirection());
-
- // proposal.d_value is the max
-
- UpdateInfo max;
- int dropped = 0;
- //Constraint maxFix = NullConstraint;
- //DeltaRational maxAmount;
-
- EntryPointerVector::const_iterator i = d_relevantErrorBuffer.begin();
- EntryPointerVector::const_iterator i_end = d_relevantErrorBuffer.end();
- for(; i != i_end; ++i){
- const Tableau::Entry& entry = *(*i);
- Assert(entry.getColVar() == proposal.nonbasic());
-
- ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
- const Rational& a_ji = entry.getCoefficient();
- int basic_movement = proposal.nonbasicDirection() * a_ji.sgn();
-
- DeltaRational theta;
- DeltaRational proposedValue;
- if(!proposal.unbounded()){
- theta = proposal.nonbasicDelta() * a_ji;
- proposedValue = theta + d_variables.getAssignment(basic);
- }
-
- Constraint fixed = NullConstraint;
-
- if(basic_movement < 0){
- Assert(d_variables.cmpAssignmentUpperBound(basic) > 0);
-
- if(proposal.unbounded() || d_variables.cmpToUpperBound(basic, proposedValue) <= 0){
- --dropped;
- fixed = d_variables.getUpperBoundConstraint(basic);
- }
- }else if(basic_movement > 0){
- Assert(d_variables.cmpAssignmentLowerBound(basic) < 0);
+void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
+ Assert(oldSgn != currSgn);
+ BoundsInfo nb_inf = d_variables.boundsInfo(nb);
- if(proposal.unbounded() || d_variables.cmpToLowerBound(basic, proposedValue) >= 0){
- --dropped;
- fixed = d_variables.getLowerBoundConstraint(basic);
- }
- }
- if(fixed != NullConstraint){
- DeltaRational amount = fixed->getValue() - d_variables.getAssignment(basic);
- amount /= a_ji;
- Assert(amount.sgn() == proposal.nonbasicDirection());
-
- if(max.uninitialized()){
- max = UpdateInfo(proposal.nonbasic(), proposal.nonbasicDirection());
- max.updatePivot(amount, a_ji, fixed, dropped);
- }else{
- int cmp = amount.cmp(max.nonbasicDelta());
- bool prefer =
- (proposal.nonbasicDirection() < 0 && cmp < 0) ||
- (proposal.nonbasicDirection() > 0 && cmp > 0) ||
- (cmp == 0 && fixed->getVariable() < max.limiting()->getVariable());
-
- if(prefer){
- max.updatePivot(amount, a_ji, fixed, dropped);
- }else{
- max.setErrorsChange(dropped);
- }
- }
- }
- }
- Assert(dropped < 0 || !proposal.unbounded());
+ Assert(rowIndexIsTracked(ridx));
- if(dropped < 0){
- proposal = max;
- }else{
- Assert(dropped == 0);
- Assert(proposal.nonbasicDelta().sgn() != 0);
- Assert(proposal.nonbasicDirection() != 0);
- proposal.setErrorsChange(0);
- }
- Assert(proposal.errorsChange() == dropped);
+ BoundsInfo& row_bi = d_btracking.get(ridx);
+ row_bi.addInSgn(nb_inf, oldSgn, currSgn);
}
ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{
@@ -1188,8 +956,9 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr
// Assume past this point, nb will be in error if this pivot is done
ArithVar nb = entry.getColVar();
- ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex());
- Assert(basicIsTracked(basic));
+ RowIndex ridx = entry.getRowIndex();
+ ArithVar basic = d_tableau.rowIndexToBasic(ridx);
+ Assert(rowIndexIsTracked(ridx));
int coeffSgn = entry.getCoefficient().sgn();
@@ -1201,12 +970,11 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr
// 2) -a * x = -y + \sum b * z
// 3) x = (-1/a) * ( -y + \sum b * z)
- Assert(basicIsTracked(basic));
- BoundCounts bc = d_boundTracking[basic];
+ BoundCounts bc = d_btracking[ridx].atBounds();
// 1) y = a * x + \sum b * z
// Get bc(\sum b * z)
- BoundCounts sumOnly = bc - d_variables.boundCounts(nb).multiplyBySgn(coeffSgn);
+ 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;
@@ -1215,19 +983,19 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr
// 2) -a * x = -y + \sum b * z
// Get bc(-y + \sum b * z)
- BoundCounts withNegY = sumOnly + ysBounds.multiplyBySgn(-1);
+ 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 = withNegY.multiplyBySgn(-coeffSgn);
+ 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.atLowerBounds() + 1 == length;
+ return xsBoundsAfterPivot.lowerBoundCount() + 1 == length;
}else{
// Only check for the lower bound being violated
- return xsBoundsAfterPivot.atUpperBounds() + 1 == length;
+ return xsBoundsAfterPivot.upperBoundCount() + 1 == length;
}
}
diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h
index 8b9b888f2..293a0ddad 100644
--- a/src/theory/arith/linear_equality.h
+++ b/src/theory/arith/linear_equality.h
@@ -192,9 +192,6 @@ public:
};
-
-
-
class LinearEqualityModule {
public:
typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const;
@@ -226,14 +223,12 @@ 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, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f);
+ 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 updateUntracked(ArithVar x_i, const DeltaRational& v);
- void updateTracked(ArithVar x_i, const DeltaRational& v);
void update(ArithVar x_i, const DeltaRational& v){
if(d_areTracking){
updateTracked(x_i,v);
@@ -241,7 +236,13 @@ public:
updateUntracked(x_i,v);
}
}
- void updateMany(const DenseMap<DeltaRational>& many);
+
+ /** 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,
@@ -249,28 +250,34 @@ public:
* Updates the assignment of the other basic variables accordingly.
*/
void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
- //void pivotAndUpdateAdj(ArithVar x_i, ArithVar x_j, const DeltaRational& v);
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);
- bool hasBounds(ArithVar basic, bool upperBound);
- bool hasLowerBounds(ArithVar basic){
- return hasBounds(basic, false);
- }
- bool hasUpperBounds(ArithVar basic){
- return hasBounds(basic, true);
- }
void startTrackingBoundCounts();
void stopTrackingBoundCounts();
- void includeBoundCountChange(ArithVar nb, BoundCounts prev);
-
- void computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction basic);
+ void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev);
uint32_t updateProduct(const UpdateInfo& inf) const;
@@ -334,40 +341,6 @@ public:
}
}
- // template<bool heuristic>
- // bool preferNonDegenerate(const UpdateInfo& a, const UpdateInfo& b) const{
- // if(a.focusDirection() == b.focusDirection()){
- // if(heuristic){
- // return preferNeitherBound(a,b);
- // }else{
- // return minNonBasicVarOrder(a,b);
- // }
- // }else{
- // return a.focusDirection() < b.focusDirection();
- // }
- // }
-
- // template <bool heuristic>
- // bool preferErrorsFixed(const UpdateInfo& a, const UpdateInfo& b) const{
- // if( a.errorsChange() == b.errorsChange() ){
- // return preferNonDegenerate<heuristic>(a,b);
- // }else{
- // return a.errorsChange() > b.errorsChange();
- // }
- // }
-
- // template <bool heuristic>
- // bool preferConflictFound(const UpdateInfo& a, const UpdateInfo& b) const{
- // if(a.d_foundConflict && b.d_foundConflict){
- // // if both are true, determinize the preference
- // return minNonBasicVarOrder(a,b);
- // }else if( a.d_foundConflict || b.d_foundConflict ){
- // return b.d_foundConflict;
- // }else{
- // return preferErrorsFixed<heuristic>(a,b);
- // }
- // }
-
bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const {
Assert(a.focusDirection() == 0 && b.focusDirection() == 0);
Assert(a.describesPivot());
@@ -427,31 +400,27 @@ public:
}
private:
- typedef std::vector<const Tableau::Entry*> EntryPointerVector;
- EntryPointerVector d_relevantErrorBuffer;
-
- //uint32_t computeUnconstrainedUpdate(ArithVar nb, int sgn, DeltaRational& am);
- //uint32_t computedFixed(ArithVar nb, int sgn, const DeltaRational& am);
- void computedFixed(UpdateInfo&);
- // RowIndex -> BoundCount
- BoundCountingVector& d_boundTracking;
+ /**
+ * 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(Constraint c);
+
/**
* Exports either the explanation of an upperbound or a lower bound
* of the basic variable basic, using the non-basic variables in the row.
*/
- template <bool upperBound>
- void propagateNonbasics(ArithVar basic, Constraint c);
-
-public:
- void propagateNonbasicsLowerBound(ArithVar basic, Constraint c){
- propagateNonbasics<false>(basic, c);
- }
- void propagateNonbasicsUpperBound(ArithVar basic, Constraint c){
- propagateNonbasics<true>(basic, c);
- }
+ void propagateRow(std::vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c);
/**
* Computes the value of a basic variable using the assignments
@@ -460,14 +429,7 @@ public:
* - the the current assignment (useSafe=false) or
* - the safe assignment (useSafe = true).
*/
- DeltaRational computeRowValue(ArithVar x, bool useSafe);
-
- inline DeltaRational computeLowerBound(ArithVar basic){
- return computeBound(basic, false);
- }
- inline DeltaRational computeUpperBound(ArithVar basic){
- return computeBound(basic, true);
- }
+ DeltaRational computeRowValue(ArithVar x, bool useSafe) const;
/**
* A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
@@ -548,41 +510,64 @@ public:
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 d_boundTracking.isKey(v);
+ return rowIndexIsTracked(d_tableau.basicToRowIndex(v));
}
- void trackVariable(ArithVar x_i);
-
- void maybeRemoveTracking(ArithVar v){
- Assert(!d_tableau.isBasic(v));
- if(d_boundTracking.isKey(v)){
- d_boundTracking.remove(v);
- }
+ void trackRowIndex(RowIndex ridx);
+ void stopTrackingRowIndex(RowIndex ridx){
+ Assert(rowIndexIsTracked(ridx));
+ d_btracking.remove(ridx);
}
- // void trackVariable(ArithVar x_i){
- // Assert(!basicIsTracked(x_i));
- // d_boundTracking.set(x_i,computeBoundCounts(x_i));
- // }
+ /**
+ * 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:
- BoundCounts computeBoundCounts(ArithVar x_i) const;
+
+ /**
+ * 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:
- //BoundCounts cachingCountBounds(ArithVar x_i) const;
- BoundCounts _countBounds(ArithVar x_i) const;
+ /** 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);
- void trackingSwap(ArithVar basic, ArithVar nb, int sgn);
+ /** 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;
- bool nonbasicsAtUpperBounds(ArithVar x_i) const;
- ArithVar _anySlackLowerBound(ArithVar x_i) const {
- return selectSlack<true>(x_i, &LinearEqualityModule::noPreference);
- }
- ArithVar _anySlackUpperBound(ArithVar x_i) const {
- return selectSlack<false>(x_i, &LinearEqualityModule::noPreference);
- }
+ /**
+ * 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 {
@@ -593,8 +578,8 @@ private:
void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){
d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn);
}
- void swap(ArithVar basic, ArithVar nb, int oldNbSgn){
- d_linEq->trackingSwap(basic, nb, oldNbSgn);
+ void multiplyRow(RowIndex ridx, int sgn){
+ d_linEq->trackingMultiplyRow(ridx, sgn);
}
bool canUseRow(RowIndex ridx) const {
ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx);
@@ -629,8 +614,11 @@ public:
return minimallyWeakConflict(false, conflictVar);
}
-private:
- DeltaRational computeBound(ArithVar basic, bool upperBound);
+ /**
+ * 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);
@@ -703,6 +691,7 @@ private:
IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
TimerStat d_weakenTime;
+ TimerStat d_forceTime;
Statistics();
~Statistics();
@@ -737,13 +726,13 @@ public:
}
};
-class UpdateTrackingCallback : public BoundCountsCallback {
+class UpdateTrackingCallback : public BoundUpdateCallback {
private:
LinearEqualityModule* d_mod;
public:
UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){}
- void operator()(ArithVar v, BoundCounts bc){
- d_mod->includeBoundCountChange(v, bc);
+ void operator()(ArithVar v, const BoundsInfo& bi){
+ d_mod->includeBoundUpdate(v, bi);
}
};
diff --git a/src/theory/arith/matrix.cpp b/src/theory/arith/matrix.cpp
index 7136c3fa8..b8bd68488 100644
--- a/src/theory/arith/matrix.cpp
+++ b/src/theory/arith/matrix.cpp
@@ -24,7 +24,7 @@ namespace theory {
namespace arith {
void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {}
-void NoEffectCCCB::swap(ArithVar basic, ArithVar nb, int nbSgn){}
+void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){}
bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; }
}/* CVC4::theory::arith namespace */
diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h
index 100f999e0..d93b6986e 100644
--- a/src/theory/arith/matrix.h
+++ b/src/theory/arith/matrix.h
@@ -39,15 +39,15 @@ const RowIndex ROW_INDEX_SENTINEL = std::numeric_limits<RowIndex>::max();
class CoefficientChangeCallback {
public:
- virtual void update(RowIndex basic, ArithVar nb, int oldSgn, int currSgn) = 0;
- virtual void swap(ArithVar basic, ArithVar nb, int nbSgn) = 0;
+ virtual 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);
- void swap(ArithVar basic, ArithVar nb, int nbSgn);
+ void multiplyRow(RowIndex ridx, int Sgn);
bool canUseRow(RowIndex ridx) const;
};
diff --git a/src/theory/arith/normal_form.cpp b/src/theory/arith/normal_form.cpp
index 8454ca210..7cd202e53 100644
--- a/src/theory/arith/normal_form.cpp
+++ b/src/theory/arith/normal_form.cpp
@@ -22,7 +22,7 @@
using namespace std;
namespace CVC4 {
-namespace theory{
+namespace theory {
namespace arith {
bool Variable::isDivMember(Node n){
@@ -745,9 +745,8 @@ bool Comparison::isNormalGEQ() const {
return false;
}else{
if(left.isIntegral()){
- return left.denominatorLCMIsOne() && left.numeratorGCDIsOne();
+ return left.signNormalizedReducedSum();
}else{
- Debug("nf::tmp") << "imme sdfhkdjfh "<< left.leadingCoefficientIsAbsOne() << endl;
return left.leadingCoefficientIsAbsOne();
}
}
@@ -768,7 +767,7 @@ bool Comparison::isNormalLT() const {
return false;
}else{
if(left.isIntegral()){
- return left.denominatorLCMIsOne() && left.numeratorGCDIsOne();
+ return left.signNormalizedReducedSum();
}else{
return left.leadingCoefficientIsAbsOne();
}
@@ -889,6 +888,7 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
Polynomial left = sp.getPolynomial();
Rational right = - (sp.getConstant().getValue());
+
Monomial m = left.getHead();
Assert(!m.isConstant());
@@ -899,16 +899,31 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
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);
- return toNode(kind::GEQ, newLeft, rightMultPlusOne);
+ result = toNode(kind::GEQ, newLeft, rightMultPlusOne);
}else{
Constant newRight = Constant::mkConstant(rightMult);
- return toNode(kind::GEQ, newLeft, newRight);
+ result = toNode(kind::GEQ, newLeft, newRight);
}
}else{
//(>= l (/ n d))
@@ -916,7 +931,13 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){
//This also hold for GT as (ceil (/ n d)) > (/ n d)
Integer ceilr = rightMult.ceiling();
Constant ceilRight = Constant::mkConstant(ceilr);
- return toNode(kind::GEQ, newLeft, ceilRight);
+ result = toNode(kind::GEQ, newLeft, ceilRight);
+ }
+ Assert(!result.isNull());
+ if(negateResult){
+ return result.notNode();
+ }else{
+ return result;
}
}
diff --git a/src/theory/arith/normal_form.h b/src/theory/arith/normal_form.h
index bcf9cbfa4..1dddb5a5b 100644
--- a/src/theory/arith/normal_form.h
+++ b/src/theory/arith/normal_form.h
@@ -76,12 +76,13 @@ namespace arith {
* (exists realMonomial (monomialList qpolynomial))
* abs(monomialCoefficient (head (monomialList qpolynomial))) == 1
*
- * integer_cmp := (<= zpolynomial constant)
+ * 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
@@ -240,6 +241,11 @@ public:
case kind::INTS_MODULUS_TOTAL:
case kind::DIVISION_TOTAL:
return isDivMember(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 (!isRelationOperator(k)) &&
(Theory::isLeafOf(n, theory::THEORY_ARITH));
@@ -939,6 +945,10 @@ public:
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.
@@ -1265,7 +1275,7 @@ private:
* 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:
diff --git a/src/theory/arith/options b/src/theory/arith/options
index 977d6cb32..43b582bc8 100644
--- a/src/theory/arith/options
+++ b/src/theory/arith/options
@@ -56,7 +56,7 @@ option arithMLTrick miplib-trick --enable-miplib-trick/--disable-miplib-trick bo
turns on the preprocessing step of attempting to infer bounds on miplib problems
/turns off the preprocessing step of attempting to infer bounds on miplib problems
-option arithMLTrickSubstitutions miplib-trick-subs --miplib-trick-subs unsigned :default 1
+option arithMLTrickSubstitutions miplib-trick-subs --miplib-trick-subs=N unsigned :default 1
do substitution for miplib 'tmp' vars if defined in <= N eliminated vars
option doCutAllBounded --cut-all-bounded bool :default false :read-write
@@ -67,11 +67,11 @@ option maxCutsInContext --maxCutsInContext unsigned :default 65535
maximum cuts in a given context before signalling a restart
option revertArithModels --revert-arith-models-on-unsat bool :default false
- Revert the arithmetic model to a known safe model on unsat if one is cached
+ revert the arithmetic model to a known safe model on unsat if one is cached
option havePenalties --fc-penalties bool :default false :read-write
turns on degenerate pivot penalties
-/ turns off degenerate pivot penalties
+/turns off degenerate pivot penalties
option useFC --use-fcsimplex bool :default false :read-write
use focusing and converging simplex (FMCAD 2013 submission)
@@ -80,15 +80,27 @@ option useSOI --use-soi bool :default false :read-write
use sum of infeasibility simplex (FMCAD 2013 submission)
option restrictedPivots --restrict-pivots bool :default true :read-write
- have a pivot cap for simplex at effort levels below fullEffort.
+ have a pivot cap for simplex at effort levels below fullEffort
option collectPivots --collect-pivot-stats bool :default false :read-write
collect the pivot history
option fancyFinal --fancy-final bool :default false :read-write
- Tuning how final check works for really hard problems.
+ tuning how final check works for really hard problems
option exportDioDecompositions --dio-decomps bool :default false :read-write
- Let skolem variables for integer divisibility constraints leak from the dio solver.
+ let skolem variables for integer divisibility constraints leak from the dio solver
+
+option newProp --new-prop bool :default false :read-write
+ use the new row propagation system
+
+option arithPropAsLemmaLength --arith-prop-clauses uint16_t :default 8 :read-write
+ rows shorter than this are propagated as clauses
+
+option soiQuickExplain --soi-qe bool :default false :read-write
+ use quick explain to minimize the sum of infeasibility conflicts
+
+option rewriteDivk rewrite-divk --rewrite-divk bool :default false
+ rewrite division and mod when by a constant into linear terms
endmodule
diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp
index 695d9df25..3fae3751c 100644
--- a/src/theory/arith/partial_model.cpp
+++ b/src/theory/arith/partial_model.cpp
@@ -35,12 +35,13 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo
d_released(),
d_releasedIterator(d_released.begin()),
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),
- d_enqueueingBoundCounts(true)
+ d_deltaComputingFunc(deltaComputingFunc)
{ }
ArithVariables::VarInfo::VarInfo()
@@ -55,6 +56,10 @@ ArithVariables::VarInfo::VarInfo()
d_slack(false)
{ }
+bool ArithVariables::VarInfo::initialized() const {
+ return d_var != ARITHVAR_SENTINEL;
+}
+
void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){
Assert(!initialized());
Assert(d_lb == NullConstraint);
@@ -82,7 +87,7 @@ void ArithVariables::VarInfo::uninitialize(){
d_node = Node::null();
}
-bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundCounts & prev){
+bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){
Assert(initialized());
d_assignment = a;
int cmpUB = (d_ub == NullConstraint) ? -1 :
@@ -97,7 +102,7 @@ bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundCounts
(cmpUB == 0 || d_cmpAssignmentUB == 0);
if(lbChanged || ubChanged){
- prev = boundCounts();
+ prev = boundsInfo();
}
d_cmpAssignmentUB = cmpUB;
@@ -119,33 +124,59 @@ void ArithVariables::releaseArithVar(ArithVar v){
}
}
-bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundCounts& prev){
+bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){
Assert(initialized());
- d_ub = ub;
- int cmpUB = (d_ub == NullConstraint) ? -1 : d_assignment.cmp(d_ub->getValue());
- bool ubChanged = cmpUB != d_cmpAssignmentUB &&
- (cmpUB == 0 || d_cmpAssignmentUB == 0);
+ 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 = boundCounts();
+ prev = boundsInfo();
}
+ d_ub = ub;
d_cmpAssignmentUB = cmpUB;
return ubChanged;
}
-bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundCounts& prev){
+bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundsInfo& prev){
Assert(initialized());
- d_lb = lb;
- int cmpLB = (d_lb == NullConstraint) ? 1 : d_assignment.cmp(d_lb->getValue());
+ bool wasNull = d_lb == NullConstraint;
+ bool isNull = lb == NullConstraint;
- bool lbChanged = cmpLB != d_cmpAssignmentLB &&
- (cmpLB == 0 || d_cmpAssignmentLB == 0);
+ int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue());
+
+ bool lbChanged = (wasNull != isNull) ||
+ (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0));
if(lbChanged){
- prev = boundCounts();
+ 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;
+}
+
void ArithVariables::attemptToReclaimReleased(){
std::list<ArithVar>::iterator i_end = d_released.end();
for(int iter = 0; iter < 20 && d_releasedIterator != i_end; ++d_releasedIterator){
@@ -170,7 +201,7 @@ ArithVar ArithVariables::allocateVariable(){
attemptToReclaimReleased();
}
bool reclaim = !d_pool.empty();
-
+
ArithVar varX;
if(reclaim){
varX = d_pool.back();
@@ -210,7 +241,7 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){
}
invalidateDelta();
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setAssignment(r, prev)){
addToBoundQueue(x, prev);
}
@@ -229,7 +260,7 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const
invalidateDelta();
VarInfo& vi = d_vars.get(x);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setAssignment(r, prev)){
addToBoundQueue(x, prev);
}
@@ -314,7 +345,7 @@ void ArithVariables::setLowerBoundConstraint(Constraint c){
invalidateDelta();
VarInfo& vi = d_vars.get(x);
pushLowerBound(vi);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setLowerBound(c, prev)){
addToBoundQueue(x, prev);
}
@@ -333,37 +364,12 @@ void ArithVariables::setUpperBoundConstraint(Constraint c){
invalidateDelta();
VarInfo& vi = d_vars.get(x);
pushUpperBound(vi);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setUpperBound(c, prev)){
addToBoundQueue(x, prev);
}
}
-// void ArithVariables::forceRelaxLowerBound(ArithVar v){
-// AssertArgument(inMaps(v), "Calling forceRelaxLowerBound on a variable that is not properly setup.");
-// AssertArgument(hasLowerBound(v), "Calling forceRelaxLowerBound on a variable without a lowerbound.");
-
-// Debug("partial_model") << "forceRelaxLowerBound(" << v << ") dropping :" << getLowerBoundConstraint(v) << endl;
-
-// invalidateDelta();
-// VarInfo& vi = d_vars.get(v);
-// pushLowerBound(vi);
-// vi.setLowerBound(NullConstraint);
-// }
-
-
-// void ArithVariables::forceRelaxUpperBound(ArithVar v){
-// AssertArgument(inMaps(v), "Calling forceRelaxUpperBound on a variable that is not properly setup.");
-// AssertArgument(hasUpperBound(v), "Calling forceRelaxUpperBound on a variable without an upper bound.");
-
-// Debug("partial_model") << "forceRelaxUpperBound(" << v << ") dropping :" << getUpperBoundConstraint(v) << endl;
-
-// invalidateDelta();
-// VarInfo& vi = d_vars.get(v);
-// pushUpperBound(vi);
-// vi.setUpperBound(NullConstraint);
-// }
-
int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{
if(!hasLowerBound(x)){
// l = -\intfy
@@ -405,30 +411,16 @@ bool ArithVariables::hasEitherBound(ArithVar x) const{
bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{
return d_vars[x].d_cmpAssignmentUB < 0;
- // if(!hasUpperBound(x)){ // u = \infty
- // return true;
- // }else{
- // return d_assignment[x] < getUpperBound(x);
- // }
}
bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{
return d_vars[x].d_cmpAssignmentLB > 0;
- // if(!hasLowerBound(x)){ // l = -\infty
- // return true;
- // }else{
- // return getLowerBound(x) < d_assignment[x];
- // }
}
bool ArithVariables::assignmentIsConsistent(ArithVar x) const{
return
d_vars[x].d_cmpAssignmentLB >= 0 &&
d_vars[x].d_cmpAssignmentUB <= 0;
- // const DeltaRational& beta = getAssignment(x);
-
- // //l_i <= beta(x_i) <= u_i
- // return greaterThanLowerBound(x,beta) && lessThanUpperBound(x,beta);
}
@@ -442,7 +434,7 @@ void ArithVariables::clearSafeAssignments(bool revert){
ArithVar atBack = d_safeAssignment.back();
if(revert){
VarInfo& vi = d_vars.get(atBack);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setAssignment(d_safeAssignment[atBack], prev)){
addToBoundQueue(atBack, prev);
}
@@ -489,33 +481,6 @@ void ArithVariables::printModel(ArithVar x) const{
printModel(x, Debug("model"));
}
-// BoundRelationship ArithVariables::boundRelationship(Constraint lb, const DeltaRational& d, Constraint ub){
-// if(lb == NullConstraint && ub == NullConstraint){
-// return BetweenBounds;
-// }else if(lb == NullConstraint){
-// int cmp = d.cmp(ub->getValue());
-// return (cmp < 0) ? BetweenBounds :
-// (cmp == 0 ? AtUpperBound : AboveUpperBound);
-// }else if(ub == NullConstraint){
-// int cmp = d.cmp(lb->getValue());
-// return (cmp > 0) ? BetweenBounds :
-// (cmp == 0 ? AtLowerBound : BelowLowerBound);
-// }else{
-// Assert(lb->getValue() <= ub->getValue());
-// int cmpToLB = d.cmp(lb->getValue());
-// if(cmpToLB < 0){
-// return BelowLowerBound;
-// }else if(cmpToLB == 0){
-// return (d == ub->getValue()) ? AtBothBounds : AtLowerBound;
-// }else{
-// // d > 0
-// int cmpToUB = d.cmp(ub->getValue());
-// return (cmpToUB > 0) ? BetweenBounds :
-// (cmpToUB == 0 ? AtLowerBound : BelowLowerBound);
-// }
-// }
-// }
-
void ArithVariables::pushUpperBound(VarInfo& vi){
++vi.d_pushCount;
d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub));
@@ -528,7 +493,7 @@ void ArithVariables::pushLowerBound(VarInfo& vi){
void ArithVariables::popUpperBound(AVCPair* c){
ArithVar x = c->first;
VarInfo& vi = d_vars.get(x);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setUpperBound(c->second, prev)){
addToBoundQueue(x, prev);
}
@@ -538,62 +503,42 @@ void ArithVariables::popUpperBound(AVCPair* c){
void ArithVariables::popLowerBound(AVCPair* c){
ArithVar x = c->first;
VarInfo& vi = d_vars.get(x);
- BoundCounts prev;
+ BoundsInfo prev;
if(vi.setLowerBound(c->second, prev)){
addToBoundQueue(x, prev);
}
--vi.d_pushCount;
}
-/* To ensure that the final deallocation stuff works,
- * we need to ensure that we need to not reference any of the other vectors
- */
-// void ArithVariables::relaxUpperBound(Constraint curr, Constraint afterPop){
-// BoundRelation next = Undefined;
-// switch(d_boundRel[x]){
-// case BelowLowerBound:
-// case BetweenBounds:
-// case AtLowerBound:
-// return; // do nothing
-// case AtUpperBound:
-// if(afterPop != NullConstraint
-// && curr->getValue() == afterPop->getValue()){
-// next = AtUpperBound;
-// }else{
-// next = BetweenBounds;
-// }
-// break;
-// case AtBothBounds:
-// if(afterPop != NullConstraint
-// && curr->getValue() == afterPop->getValue()){
-// next = AtUpperBound;
-// }else{
-// next = AtLowerBound;
-// }
-// break;
-// case AboveUpperBound:
-// if(afterPop == NullConstraint){
-// next = BetweenBounds;
-// }else{
-// int cmp = d_assignment[x].cmp(afterPop->getValue());
-// next = (cmp < 0) ? BetweenBounds :
-// (cmp == 0) ? AtUpperBound : AboveUpperBound;
-// }
-// break;
-// default:
-// Unreachable();
-// }
-// d_boundRel[x] = next;
-// }
+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::relaxLowerBound(Constraint curr, Constraint afterPop){
-// // TODO this can be optimized using the automata induced by d_boundRel and
-// // the knowledge that lb <= ub
-// ArithVar x = curr->getVariable();
-// d_boundRel[x] = boundRelationship(afterPop, d_assignment[x], d_ubc[x]);
-// }
+void ArithVariables::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::LowerBoundCleanUp::operator()(AVCPair* p){
d_pm->popLowerBound(p);
diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h
index dcfe97079..deafb559a 100644
--- a/src/theory/arith/partial_model.h
+++ b/src/theory/arith/partial_model.h
@@ -44,6 +44,7 @@ namespace arith {
class ArithVariables {
private:
+
class VarInfo {
friend class ArithVariables;
ArithVar d_var;
@@ -62,29 +63,36 @@ private:
public:
VarInfo();
- bool setAssignment(const DeltaRational& r, BoundCounts& prev);
- bool setLowerBound(Constraint c, BoundCounts& prev);
- bool setUpperBound(Constraint c, BoundCounts& prev);
+ bool setAssignment(const DeltaRational& r, BoundsInfo& prev);
+ bool setLowerBound(Constraint c, BoundsInfo& prev);
+ bool setUpperBound(Constraint c, BoundsInfo& prev);
- bool initialized() const {
- return d_var != ARITHVAR_SENTINEL;
- }
+ /** Returns true if this VarInfo has been initialized. */
+ bool initialized() const;
+
+ /**
+ * Initializes the VarInfo with the ArithVar index it is associated with,
+ * the node that the variable represents, and whether it is a slack variable.
+ */
void initialize(ArithVar v, Node n, bool slack);
+ /** Uninitializes the VarInfo. */
void uninitialize();
- bool canBeReclaimed() const{
- return d_pushCount == 0;
- }
+ bool canBeReclaimed() const;
- BoundCounts boundCounts() const {
- uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0;
- uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0;
- return BoundCounts(lbIndc, ubIndc);
- }
+ /** 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
+ /**Maps from ArithVar -> VarInfo */
typedef DenseMap<VarInfo> VarInfoVec;
+ /** This maps an ArithVar to its Variable information.*/
VarInfoVec d_vars;
// Partial Map from Arithvar -> PreviousAssignment
@@ -104,7 +112,15 @@ private:
// Inverse of d_vars[x].d_node
NodeToArithVarMap d_nodeToArithVarMap;
- DenseMap<BoundCounts> d_atBoundsQueue;
+
+ /** 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:
@@ -188,6 +204,11 @@ private:
bool isSlack(ArithVar x) const {
return d_vars[x].d_slack;
}
+
+ bool integralAssignment(ArithVar x) const {
+ return getAssignment(x).isIntegral();
+ }
+
private:
typedef std::pair<ArithVar, Constraint> AVCPair;
@@ -225,7 +246,6 @@ private:
// Function to call if the value of delta needs to be recomputed.
DeltaComputeCallback d_deltaComputingFunc;
- bool d_enqueueingBoundCounts;
public:
@@ -252,22 +272,7 @@ public:
return d_vars[x].d_lb;
}
- /**
- * This forces the lower bound for a variable to be relaxed in the current context.
- * This is done by forcing the lower bound to be NullConstraint.
- * This is an expert only operation! (See primal simplex for an example.)
- */
- //void forceRelaxLowerBound(ArithVar x);
-
- /**
- * This forces the upper bound for a variable to be relaxed in the current context.
- * This is done by forcing the upper bound to be NullConstraint.
- * This is an expert only operation! (See primal simplex for an example.)
- */
- //void forceRelaxUpperBound(ArithVar x);
-
/* Initializes a variable to a safe value.*/
- //void initialize(ArithVar x, const DeltaRational& r);
void initialize(ArithVar x, Node n, bool slack);
ArithVar allocate(Node n, bool slack = false);
@@ -360,8 +365,14 @@ public:
return d_vars[x].d_cmpAssignmentUB;
}
- inline BoundCounts boundCounts(ArithVar x) const {
- return d_vars[x].boundCounts();
+ 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;
@@ -391,38 +402,14 @@ public:
d_deltaIsSafe = true;
}
- // inline bool initialized(ArithVar x) const {
- // return d_vars[x].initialized();
- // }
-
- void addToBoundQueue(ArithVar v, BoundCounts prev){
- if(d_enqueueingBoundCounts && !d_atBoundsQueue.isKey(v)){
- d_atBoundsQueue.set(v, prev);
- }
- }
-
- BoundCounts oldBoundCounts(ArithVar v) const {
- if(d_atBoundsQueue.isKey(v)){
- return d_atBoundsQueue[v];
- }else{
- return boundCounts(v);
- }
- }
+ void startQueueingBoundCounts(){ d_enqueueingBoundCounts = true; }
+ void stopQueueingBoundCounts(){ d_enqueueingBoundCounts = false; }
+ void addToBoundQueue(ArithVar v, const BoundsInfo& prev);
- void startQueueingAtBoundQueue(){ d_enqueueingBoundCounts = true; }
- void stopQueueingAtBoundQueue(){ d_enqueueingBoundCounts = false; }
+ BoundsInfo selectBoundsInfo(ArithVar v, bool old) const;
- void processAtBoundQueue(BoundCountsCallback& changed){
- while(!d_atBoundsQueue.empty()){
- ArithVar v = d_atBoundsQueue.back();
- BoundCounts prev = d_atBoundsQueue[v];
- d_atBoundsQueue.pop_back();
- BoundCounts curr = boundCounts(v);
- if(prev != curr){
- changed(v, prev);
- }
- }
- }
+ bool boundsQueueEmpty() const;
+ void processBoundsQueue(BoundUpdateCallback& changed);
void printEntireModel(std::ostream& out) const;
diff --git a/src/theory/arith/pure_update_simplex.cpp b/src/theory/arith/pure_update_simplex.cpp
deleted file mode 100644
index 9b3edfa6f..000000000
--- a/src/theory/arith/pure_update_simplex.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-/********************* */
-/*! \file simplex.cpp
- ** \verbatim
- ** Original author: taking
- ** Major contributors: none
- ** Minor contributors (to current version): mdeters
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009-2012 New York University and The University of Iowa
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief [[ Add one-line brief description here ]]
- **
- ** [[ Add lengthier description here ]]
- ** \todo document this file
- **/
-
-
-#include "theory/arith/pure_update_simplex.h"
-#include "theory/arith/options.h"
-#include "theory/arith/constraint.h"
-
-using namespace std;
-
-namespace CVC4 {
-namespace theory {
-namespace arith {
-
-PureUpdateSimplexDecisionProcedure::PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc)
- : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc)
-{ }
-
-Result::Sat PureUpdateSimplexDecisionProcedure::findModel(bool exactResult){
- Assert(d_conflictVariables.empty());
-
- static CVC4_THREADLOCAL(unsigned int) instance = 0;
- instance = instance + 1;
-
- if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
- Debug("arith::findModel") << "puFindModel("<< instance <<") "
- << "trivial" << endl;
- return Result::SAT;
- }
-
- // We need to reduce this because of
- d_errorSet.reduceToSignals();
- d_errorSet.setSelectionRule(VAR_ORDER);
-
- if(processSignals()){
- d_conflictVariables.purge();
-
- Debug("arith::findModel") << "puFindModel("<< instance <<") "
- << "early conflict" << endl;
- return Result::UNSAT;
- }else if(d_errorSet.errorEmpty()){
- Debug("arith::findModel") << "puFindModel("<< instance <<") "
- << "fixed itself" << endl;
- Assert(!d_errorSet.moreSignals());
- return Result::SAT;
- }
-
- Debug("arith::findModel") << "puFindModel(" << instance <<") "
- << "start non-trivial" << endl;
-
- static const bool verbose = false;
- Result::Sat result = Result::SAT_UNKNOWN;
-
- if(result == Result::SAT_UNKNOWN){
- if(attemptPureUpdates()){
- result = Result::UNSAT;
- }
- if(result == Result::UNSAT){
- ++(d_statistics.d_pureUpdateFoundUnsat);
- if(verbose){ Message() << "pure updates found unsat" << endl; }
- }else if(d_errorSet.errorEmpty()){
- ++(d_statistics.d_pureUpdateFoundSat);
- if(verbose){ Message() << "pure updates found model" << endl; }
- }else{
- ++(d_statistics.d_pureUpdateMissed);
- if(verbose){ Message() << "pure updates missed" << endl; }
- }
- }
-
- Assert(!d_errorSet.moreSignals());
- if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
- result = Result::SAT;
- }
-
- // ensure that the conflict variable is still in the queue.
- d_conflictVariables.purge();
-
- Assert(d_focusErrorVar == ARITHVAR_SENTINEL);
- Debug("arith::findModel") << "end findModel() " << instance << " "
- << result << endl;
-
- return result;
-}
-
-
-
-PureUpdateSimplexDecisionProcedure::Statistics::Statistics():
- d_pureUpdateFoundUnsat("theory::arith::PureUpdate::FoundUnsat", 0),
- d_pureUpdateFoundSat("theory::arith::PureUpdate::FoundSat", 0),
- d_pureUpdateMissed("theory::arith::PureUpdate::Missed", 0),
- d_pureUpdates("theory::arith::PureUpdate::updates", 0),
- d_pureUpdateDropped("theory::arith::PureUpdate::dropped", 0),
- d_pureUpdateConflicts("theory::arith::PureUpdate::conflicts", 0),
- d_foundConflicts("theory::arith::PureUpdate::foundConflicts", 0),
- d_attemptPureUpdatesTimer("theory::arith::PureUpdate::timer"),
- d_processSignalsTime("theory::arith::PureUpdate::process::timer"),
- d_constructionTimer("theory::arith::PureUpdate::construction::timer")
-{
- StatisticsRegistry::registerStat(&d_pureUpdateFoundUnsat);
- StatisticsRegistry::registerStat(&d_pureUpdateFoundSat);
- StatisticsRegistry::registerStat(&d_pureUpdateMissed);
- StatisticsRegistry::registerStat(&d_pureUpdates);
- StatisticsRegistry::registerStat(&d_pureUpdateDropped);
- StatisticsRegistry::registerStat(&d_pureUpdateConflicts);
-
- StatisticsRegistry::registerStat(&d_foundConflicts);
-
- StatisticsRegistry::registerStat(&d_attemptPureUpdatesTimer);
- StatisticsRegistry::registerStat(&d_processSignalsTime);
- StatisticsRegistry::registerStat(&d_constructionTimer);
-}
-
-PureUpdateSimplexDecisionProcedure::Statistics::~Statistics(){
- StatisticsRegistry::unregisterStat(&d_pureUpdateFoundUnsat);
- StatisticsRegistry::unregisterStat(&d_pureUpdateFoundSat);
- StatisticsRegistry::unregisterStat(&d_pureUpdateMissed);
- StatisticsRegistry::unregisterStat(&d_pureUpdates);
- StatisticsRegistry::unregisterStat(&d_pureUpdateDropped);
- StatisticsRegistry::unregisterStat(&d_pureUpdateConflicts);
-
- StatisticsRegistry::unregisterStat(&d_foundConflicts);
-
- StatisticsRegistry::unregisterStat(&d_attemptPureUpdatesTimer);
- StatisticsRegistry::unregisterStat(&d_processSignalsTime);
- StatisticsRegistry::unregisterStat(&d_constructionTimer);
-}
-
-bool PureUpdateSimplexDecisionProcedure::attemptPureUpdates(){
- TimerStat::CodeTimer codeTimer(d_statistics.d_attemptPureUpdatesTimer);
-
- Assert(!d_errorSet.focusEmpty());
- Assert(d_errorSet.noSignals());
-
- d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_constructionTimer);
-
- UpdateInfo proposal;
- int boundImprovements = 0;
- int dropped = 0;
- int computations = 0;
-
- for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){
- const Tableau::Entry& e = *ri;
-
- ArithVar curr = e.getColVar();
- if(curr == d_focusErrorVar){ continue; }
-
- int dir = e.getCoefficient().sgn();
- Assert(dir != 0);
-
- bool worthwhile = false;
-
- if( (dir > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
- (dir < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0) ){
-
- ++computations;
- proposal = UpdateInfo(curr, dir);
- d_linEq.computeSafeUpdate(proposal, &LinearEqualityModule::noPreference);
-
- Assert(proposal.errorsChange() <= 0);
- Assert(proposal.focusDirection() >= 0);
-
- worthwhile = proposal.errorsChange() < 0 ||
- (proposal.focusDirection() > 0 &&
- d_variables.boundCounts(curr).isZero() &&
- !proposal.describesPivot());
-
- Debug("pu::refined")
- << "pure update proposal "
- << curr << " "
- << worthwhile << " "
- << proposal
- << endl;
- }
- if(worthwhile){
- Debug("pu") << d_variables.getAssignment(d_focusErrorVar) << endl;
-
- BoundCounts before = d_variables.boundCounts(curr);
- DeltaRational newAssignment =
- d_variables.getAssignment(curr) + proposal.nonbasicDelta();
- d_linEq.updateTracked(curr, newAssignment);
- BoundCounts after = d_variables.boundCounts(curr);
-
- ++d_statistics.d_pureUpdates;
- ++boundImprovements;
- Debug("pu") << boundImprovements << ": " << curr
- << " before: " << before
- << " after: " << after
- << e.getCoefficient()
- << d_variables.getAssignment(d_focusErrorVar) << endl;
-
- uint32_t prevSize = d_errorSet.errorSize();
- Assert(d_errorSet.moreSignals());
- if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); }
- while(d_errorSet.moreSignals()){
- ArithVar updated = d_errorSet.topSignal();
- bool wasInError = d_errorSet.inError(updated);
- d_errorSet.popSignal();
- if(updated == curr){ continue; }
- Assert(d_tableau.isBasic(updated));
- if(!d_linEq.basicIsTracked(updated)){continue;}
-
-
- Assert(d_linEq.basicIsTracked(updated));
- Assert(wasInError || d_variables.assignmentIsConsistent(updated));
-
- if(!d_variables.assignmentIsConsistent(updated)
- && checkBasicForConflict(updated)){
- Assert(!d_conflictVariables.isMember(updated) );
- Debug("pu")
- << "It worked? "
- << d_statistics.d_pureUpdateConflicts.getData()
- << " " << curr
- << " " << checkBasicForConflict(updated) << endl;
- reportConflict(updated);
- ++(d_statistics.d_foundConflicts);
- ++(d_statistics.d_pureUpdateConflicts);
- }
- }
- if(d_conflictVariables.empty()){
- if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); }
- uint32_t currSize = d_errorSet.errorSize();
- Assert(currSize <= prevSize);
- if(currSize < prevSize){
- dropped+= prevSize - currSize;
- if(currSize == 0){
- break;
- }
- }
- }else{
- break;
- }
- }
- }
-
- tearDownInfeasiblityFunction(d_statistics.d_constructionTimer, d_focusErrorVar);
- d_focusErrorVar = ARITHVAR_SENTINEL;
-
- (d_statistics.d_pureUpdateDropped) += dropped;
-
- Assert(d_errorSet.noSignals());
- return !d_conflictVariables.empty();
-}
-
-
-}/* CVC4::theory::arith namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
diff --git a/src/theory/arith/simplex-converge.cpp b/src/theory/arith/simplex-converge.cpp
deleted file mode 100644
index decce3882..000000000
--- a/src/theory/arith/simplex-converge.cpp
+++ /dev/null
@@ -1,1674 +0,0 @@
-/********************* */
-/*! \file simplex.cpp
- ** \verbatim
- ** Original author: taking
- ** Major contributors: none
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief [[ Add one-line brief description here ]]
- **
- ** [[ Add lengthier description here ]]
- ** \todo document this file
- **/
-
-
-#include "theory/arith/simplex.h"
-#include "theory/arith/options.h"
-
-using namespace std;
-
-using namespace CVC4;
-using namespace CVC4::kind;
-
-using namespace CVC4::theory;
-using namespace CVC4::theory::arith;
-
-static const bool CHECK_AFTER_PIVOT = true;
-
-SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel, ArithVarMalloc& malloc, ConstraintDatabase& cd) :
- d_conflictVariable(ARITHVAR_SENTINEL),
- d_linEq(linEq),
- d_partialModel(d_linEq.getPartialModel()),
- d_tableau(d_linEq.getTableau()),
- d_queue(d_partialModel, d_tableau),
- d_numVariables(0),
- d_conflictChannel(conflictChannel),
- d_pivotsInRound(),
- d_DELTA_ZERO(0,0),
- d_arithVarMalloc(malloc),
- d_constraintDatabase(cd),
- d_optRow(ARITHVAR_SENTINEL),
- d_negOptConstant(d_DELTA_ZERO)
-{
- switch(ArithHeuristicPivotRule rule = options::arithHeuristicPivotRule()) {
- case MINIMUM:
- d_queue.setPivotRule(ArithPriorityQueue::MINIMUM);
- break;
- case BREAK_TIES:
- d_queue.setPivotRule(ArithPriorityQueue::BREAK_TIES);
- break;
- case MAXIMUM:
- d_queue.setPivotRule(ArithPriorityQueue::MAXIMUM);
- break;
- default:
- Unhandled(rule);
- }
-
- srand(62047);
-}
-
-SimplexDecisionProcedure::Statistics::Statistics():
- d_statUpdateConflicts("theory::arith::UpdateConflicts", 0),
- d_findConflictOnTheQueueTime("theory::arith::findConflictOnTheQueueTime"),
- d_attemptBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::attempt",0),
- d_successBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::success",0),
- d_attemptAfterDiffSearch("theory::arith::qi::AfterDiffSearch::attempt",0),
- d_successAfterDiffSearch("theory::arith::qi::AfterDiffSearch::success",0),
- d_attemptDuringDiffSearch("theory::arith::qi::DuringDiffSearch::attempt",0),
- d_successDuringDiffSearch("theory::arith::qi::DuringDiffSearch::success",0),
- d_attemptDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::attempt",0),
- d_successDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::success",0),
- d_attemptAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::attempt",0),
- d_successAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::success",0),
- d_weakeningAttempts("theory::arith::weakening::attempts",0),
- d_weakeningSuccesses("theory::arith::weakening::success",0),
- d_weakenings("theory::arith::weakening::total",0),
- d_weakenTime("theory::arith::weakening::time"),
- d_simplexConflicts("theory::arith::simplexConflicts",0),
- // primal
- d_primalTimer("theory::arith::primal::overall::timer"),
- d_internalTimer("theory::arith::primal::internal::timer"),
- d_primalCalls("theory::arith::primal::calls",0),
- d_primalSatCalls("theory::arith::primal::calls::sat",0),
- d_primalUnsatCalls("theory::arith::primal::calls::unsat",0),
- d_primalPivots("theory::arith::primal::pivots",0),
- d_primalImprovingPivots("theory::arith::primal::pivots::improving",0),
- d_primalThresholdReachedPivot("theory::arith::primal::thresholds",0),
- d_primalThresholdReachedPivot_dropped("theory::arith::primal::thresholds::dropped",0),
- d_primalReachedMaxPivots("theory::arith::primal::maxpivots",0),
- d_primalReachedMaxPivots_contractMadeProgress("theory::arith::primal::maxpivots::contract",0),
- d_primalReachedMaxPivots_checkForConflictWorked("theory::arith::primal::maxpivots::checkworked",0),
- d_primalGlobalMinimum("theory::arith::primal::minimum",0),
- d_primalGlobalMinimum_rowConflictWorked("theory::arith::primal::minimum::checkworked",0),
- d_primalGlobalMinimum_firstHalfWasSat("theory::arith::primal::minimum::firsthalf::sat",0),
- d_primalGlobalMinimum_firstHalfWasUnsat("theory::arith::primal::minimum::firsthalf::unsat",0),
- d_primalGlobalMinimum_contractMadeProgress("theory::arith::primal::minimum::progress",0),
- d_unboundedFound("theory::arith::primal::unbounded",0),
- d_unboundedFound_drive("theory::arith::primal::unbounded::drive",0),
- d_unboundedFound_dropped("theory::arith::primal::unbounded::dropped",0)
-{
- StatisticsRegistry::registerStat(&d_statUpdateConflicts);
-
- StatisticsRegistry::registerStat(&d_findConflictOnTheQueueTime);
-
- StatisticsRegistry::registerStat(&d_attemptBeforeDiffSearch);
- StatisticsRegistry::registerStat(&d_successBeforeDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptAfterDiffSearch);
- StatisticsRegistry::registerStat(&d_successAfterDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptDuringDiffSearch);
- StatisticsRegistry::registerStat(&d_successDuringDiffSearch);
- StatisticsRegistry::registerStat(&d_attemptDuringVarOrderSearch);
- StatisticsRegistry::registerStat(&d_successDuringVarOrderSearch);
- StatisticsRegistry::registerStat(&d_attemptAfterVarOrderSearch);
- StatisticsRegistry::registerStat(&d_successAfterVarOrderSearch);
-
- StatisticsRegistry::registerStat(&d_weakeningAttempts);
- StatisticsRegistry::registerStat(&d_weakeningSuccesses);
- StatisticsRegistry::registerStat(&d_weakenings);
- StatisticsRegistry::registerStat(&d_weakenTime);
-
- StatisticsRegistry::registerStat(&d_simplexConflicts);
-
- //primal
- StatisticsRegistry::registerStat(&d_primalTimer);
- StatisticsRegistry::registerStat(&d_internalTimer);
-
- StatisticsRegistry::registerStat(&d_primalCalls);
- StatisticsRegistry::registerStat(&d_primalSatCalls);
- StatisticsRegistry::registerStat(&d_primalUnsatCalls);
-
- StatisticsRegistry::registerStat(&d_primalPivots);
- StatisticsRegistry::registerStat(&d_primalImprovingPivots);
-
- StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot);
- StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot_dropped);
-
- StatisticsRegistry::registerStat(&d_primalReachedMaxPivots);
- StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_contractMadeProgress);
- StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_checkForConflictWorked);
-
-
- StatisticsRegistry::registerStat(&d_primalGlobalMinimum);
- StatisticsRegistry::registerStat(&d_primalGlobalMinimum_rowConflictWorked);
- StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasSat);
- StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
- StatisticsRegistry::registerStat(&d_primalGlobalMinimum_contractMadeProgress);
-
-
- StatisticsRegistry::registerStat(&d_unboundedFound);
- StatisticsRegistry::registerStat(&d_unboundedFound_drive);
- StatisticsRegistry::registerStat(&d_unboundedFound_dropped);
-
-}
-
-SimplexDecisionProcedure::Statistics::~Statistics(){
- StatisticsRegistry::unregisterStat(&d_statUpdateConflicts);
-
- StatisticsRegistry::unregisterStat(&d_findConflictOnTheQueueTime);
-
- StatisticsRegistry::unregisterStat(&d_attemptBeforeDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successBeforeDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptAfterDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successAfterDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptDuringDiffSearch);
- StatisticsRegistry::unregisterStat(&d_successDuringDiffSearch);
- StatisticsRegistry::unregisterStat(&d_attemptDuringVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_successDuringVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_attemptAfterVarOrderSearch);
- StatisticsRegistry::unregisterStat(&d_successAfterVarOrderSearch);
-
- StatisticsRegistry::unregisterStat(&d_weakeningAttempts);
- StatisticsRegistry::unregisterStat(&d_weakeningSuccesses);
- StatisticsRegistry::unregisterStat(&d_weakenings);
- StatisticsRegistry::unregisterStat(&d_weakenTime);
-
- StatisticsRegistry::unregisterStat(&d_simplexConflicts);
-
- //primal
- StatisticsRegistry::unregisterStat(&d_primalTimer);
- StatisticsRegistry::unregisterStat(&d_internalTimer);
-
- StatisticsRegistry::unregisterStat(&d_primalCalls);
- StatisticsRegistry::unregisterStat(&d_primalSatCalls);
- StatisticsRegistry::unregisterStat(&d_primalUnsatCalls);
-
- StatisticsRegistry::unregisterStat(&d_primalPivots);
- StatisticsRegistry::unregisterStat(&d_primalImprovingPivots);
-
- StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot);
- StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot_dropped);
-
- StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots);
- StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_contractMadeProgress);
- StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_checkForConflictWorked);
-
-
- StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum);
- StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_rowConflictWorked);
- StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasSat);
- StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasUnsat);
- StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_contractMadeProgress);
-
- StatisticsRegistry::unregisterStat(&d_unboundedFound);
- StatisticsRegistry::unregisterStat(&d_unboundedFound_drive);
- StatisticsRegistry::unregisterStat(&d_unboundedFound_dropped);
-}
-
-
-
-
-ArithVar SimplexDecisionProcedure::minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- // Assert(!simp.d_tableau.isBasic(x));
- // Assert(!simp.d_tableau.isBasic(y));
- if(x <= y){
- return x;
- } else {
- return y;
- }
-}
-
-ArithVar SimplexDecisionProcedure::minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!simp.d_tableau.isBasic(x));
- Assert(!simp.d_tableau.isBasic(y));
- uint32_t xLen = simp.d_tableau.getColLength(x);
- uint32_t yLen = simp.d_tableau.getColLength(y);
- if( xLen > yLen){
- return y;
- } else if( xLen== yLen ){
- return minVarOrder(simp,x,y);
- }else{
- return x;
- }
-}
-
-ArithVar SimplexDecisionProcedure::minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(simp.d_tableau.isBasic(x));
- Assert(simp.d_tableau.isBasic(y));
- uint32_t xLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(x));
- uint32_t yLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(y));
- if( xLen > yLen){
- return y;
- } else if( xLen == yLen ){
- return minVarOrder(simp,x,y);
- }else{
- return x;
- }
-}
-ArithVar SimplexDecisionProcedure::minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){
- Assert(x != ARITHVAR_SENTINEL);
- Assert(y != ARITHVAR_SENTINEL);
- Assert(!simp.d_tableau.isBasic(x));
- Assert(!simp.d_tableau.isBasic(y));
- if(simp.d_partialModel.hasEitherBound(x) && !simp.d_partialModel.hasEitherBound(y)){
- return y;
- }else if(!simp.d_partialModel.hasEitherBound(x) && simp.d_partialModel.hasEitherBound(y)){
- return x;
- }else {
- return minColLength(simp, x, y);
- }
-}
-
-template <bool above>
-ArithVar SimplexDecisionProcedure::selectSlack(ArithVar x_i, SimplexDecisionProcedure::PreferenceFunction pref){
- ArithVar slack = ARITHVAR_SENTINEL;
-
- for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){
- const Tableau::Entry& entry = *iter;
- ArithVar nonbasic = entry.getColVar();
- if(nonbasic == x_i) continue;
-
- const Rational& a_ij = entry.getCoefficient();
- int sgn = a_ij.sgn();
- if(isAcceptableSlack<above>(sgn, nonbasic)){
- //If one of the above conditions is met, we have found an acceptable
- //nonbasic variable to pivot x_i with. We can now choose which one we
- //prefer the most.
- slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : pref(*this, slack, nonbasic);
- }
- }
-
- return slack;
-}
-
-Node betterConflict(TNode x, TNode y){
- if(x.isNull()) return y;
- else if(y.isNull()) return x;
- else if(x.getNumChildren() <= y.getNumChildren()) return x;
- else return y;
-}
-
-bool SimplexDecisionProcedure::findConflictOnTheQueue(SearchPeriod type) {
- TimerStat::CodeTimer codeTimer(d_statistics.d_findConflictOnTheQueueTime);
- Assert(d_successes.empty());
-
- switch(type){
- case BeforeDiffSearch: ++(d_statistics.d_attemptBeforeDiffSearch); break;
- case DuringDiffSearch: ++(d_statistics.d_attemptDuringDiffSearch); break;
- case AfterDiffSearch: ++(d_statistics.d_attemptAfterDiffSearch); break;
- case DuringVarOrderSearch: ++(d_statistics.d_attemptDuringVarOrderSearch); break;
- case AfterVarOrderSearch: ++(d_statistics.d_attemptAfterVarOrderSearch); break;
- }
-
- ArithPriorityQueue::const_iterator i = d_queue.begin();
- ArithPriorityQueue::const_iterator end = d_queue.end();
- for(; i != end; ++i){
- ArithVar x_i = *i;
-
- if(x_i != d_conflictVariable && d_tableau.isBasic(x_i) && !d_successes.isMember(x_i)){
- Node possibleConflict = checkBasicForConflict(x_i);
- if(!possibleConflict.isNull()){
- d_successes.add(x_i);
- reportConflict(possibleConflict);
- }
- }
- }
- if(!d_successes.empty()){
- switch(type){
- case BeforeDiffSearch: ++(d_statistics.d_successBeforeDiffSearch); break;
- case DuringDiffSearch: ++(d_statistics.d_successDuringDiffSearch); break;
- case AfterDiffSearch: ++(d_statistics.d_successAfterDiffSearch); break;
- case DuringVarOrderSearch: ++(d_statistics.d_successDuringVarOrderSearch); break;
- case AfterVarOrderSearch: ++(d_statistics.d_successAfterVarOrderSearch); break;
- }
- d_successes.purge();
- return true;
- }else{
- return false;
- }
-}
-
-Result::Sat SimplexDecisionProcedure::dualFindModel(bool exactResult){
- Assert(d_conflictVariable == ARITHVAR_SENTINEL);
- Assert(d_queue.inCollectionMode());
-
- if(d_queue.empty()){
- return Result::SAT;
- }
- static CVC4_THREADLOCAL(unsigned int) instance = 0;
- instance = instance + 1;
- Debug("arith::findModel") << "begin findModel()" << instance << endl;
-
- d_queue.transitionToDifferenceMode();
-
- Result::Sat result = Result::SAT_UNKNOWN;
-
- if(d_queue.empty()){
- result = Result::SAT;
- }else if(d_queue.size() > 1){
- if(findConflictOnTheQueue(BeforeDiffSearch)){
- result = Result::UNSAT;
- }
- }
- static const bool verbose = false;
- exactResult |= options::arithStandardCheckVarOrderPivots() < 0;
- const uint32_t inexactResultsVarOrderPivots = exactResult ? 0 : options::arithStandardCheckVarOrderPivots();
-
- uint32_t checkPeriod = options::arithSimplexCheckPeriod();
- if(result == Result::SAT_UNKNOWN){
- uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ?
- d_numVariables + 1 : options::arithHeuristicPivots();
- // The signed to unsigned conversion is safe.
- uint32_t pivotsRemaining = numDifferencePivots;
- while(!d_queue.empty() &&
- result != Result::UNSAT &&
- pivotsRemaining > 0){
- uint32_t pivotsToDo = min(checkPeriod, pivotsRemaining);
- pivotsRemaining -= pivotsToDo;
- if(searchForFeasibleSolution(pivotsToDo)){
- result = Result::UNSAT;
- }//Once every CHECK_PERIOD examine the entire queue for conflicts
- if(result != Result::UNSAT){
- if(findConflictOnTheQueue(DuringDiffSearch)) { result = Result::UNSAT; }
- }else{
- findConflictOnTheQueue(AfterDiffSearch); // already unsat
- }
- }
-
- if(verbose && numDifferencePivots > 0){
- if(result == Result::UNSAT){
- Message() << "diff order found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "diff order found model" << endl;
- }else{
- Message() << "diff order missed" << endl;
- }
- }
- }
-
- if(!d_queue.empty() && result != Result::UNSAT){
- if(exactResult){
- d_queue.transitionToVariableOrderMode();
-
- while(!d_queue.empty() && result != Result::UNSAT){
- if(searchForFeasibleSolution(checkPeriod)){
- result = Result::UNSAT;
- }
-
- //Once every CHECK_PERIOD examine the entire queue for conflicts
- if(result != Result::UNSAT){
- if(findConflictOnTheQueue(DuringVarOrderSearch)){
- result = Result::UNSAT;
- }
- } else{
- findConflictOnTheQueue(AfterVarOrderSearch);
- }
- }
- if(verbose){
- if(result == Result::UNSAT){
- Message() << "bland found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "bland found model" << endl;
- }else{
- Message() << "bland order missed" << endl;
- }
- }
- }else{
- d_queue.transitionToVariableOrderMode();
-
- if(searchForFeasibleSolution(inexactResultsVarOrderPivots)){
- result = Result::UNSAT;
- findConflictOnTheQueue(AfterVarOrderSearch); // already unsat
- }else{
- if(findConflictOnTheQueue(AfterVarOrderSearch)) { result = Result::UNSAT; }
- }
-
- if(verbose){
- if(result == Result::UNSAT){
- Message() << "restricted var order found unsat" << endl;
- }else if(d_queue.empty()){
- Message() << "restricted var order found model" << endl;
- }else{
- Message() << "restricted var order missed" << endl;
- }
- }
- }
- }
-
- if(result == Result::SAT_UNKNOWN && d_queue.empty()){
- result = Result::SAT;
- }
-
-
-
- d_pivotsInRound.purge();
- // ensure that the conflict variable is still in the queue.
- if(d_conflictVariable != ARITHVAR_SENTINEL){
- d_queue.enqueueIfInconsistent(d_conflictVariable);
- }
- d_conflictVariable = ARITHVAR_SENTINEL;
-
- d_queue.transitionToCollectionMode();
- Assert(d_queue.inCollectionMode());
- Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl;
- return result;
-
-
- // Assert(foundConflict || d_queue.empty());
-
- // // Curiously the invariant that we always do a full check
- // // means that the assignment we can always empty these queues.
- // d_queue.clear();
- // d_pivotsInRound.purge();
- // d_conflictVariable = ARITHVAR_SENTINEL;
-
- // Assert(!d_queue.inCollectionMode());
- // d_queue.transitionToCollectionMode();
-
-
- // Assert(d_queue.inCollectionMode());
-
- // Debug("arith::findModel") << "end findModel() " << instance << endl;
-
- // return foundConflict;
-}
-
-
-
-Node SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic){
-
- Assert(d_tableau.isBasic(basic));
- const DeltaRational& beta = d_partialModel.getAssignment(basic);
-
- if(d_partialModel.strictlyLessThanLowerBound(basic, beta)){
- ArithVar x_j = selectSlackUpperBound(basic);
- if(x_j == ARITHVAR_SENTINEL ){
- return generateConflictBelowLowerBound(basic);
- }
- }else if(d_partialModel.strictlyGreaterThanUpperBound(basic, beta)){
- ArithVar x_j = selectSlackLowerBound(basic);
- if(x_j == ARITHVAR_SENTINEL ){
- return generateConflictAboveUpperBound(basic);
- }
- }
- return Node::null();
-}
-
-//corresponds to Check() in dM06
-//template <SimplexDecisionProcedure::PreferenceFunction pf>
-bool SimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){
- Debug("arith") << "searchForFeasibleSolution" << endl;
- Assert(remainingIterations > 0);
-
- while(remainingIterations > 0){
- if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
-
- ArithVar x_i = d_queue.dequeueInconsistentBasicVariable();
- Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl;
- if(x_i == ARITHVAR_SENTINEL){
- Debug("arith_update") << "No inconsistent variables" << endl;
- return false; //sat
- }
-
- --remainingIterations;
-
- bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold();
- if(!useVarOrderPivot){
- d_pivotsInRound.add(x_i);
- }
-
-
- Debug("playground") << "pivots in rounds: " << d_pivotsInRound.count(x_i)
- << " use " << useVarOrderPivot
- << " threshold " << options::arithPivotThreshold()
- << endl;
-
- PreferenceFunction pf = useVarOrderPivot ? minVarOrder : minBoundAndRowCount;
-
- DeltaRational beta_i = d_partialModel.getAssignment(x_i);
- ArithVar x_j = ARITHVAR_SENTINEL;
-
- if(d_partialModel.strictlyLessThanLowerBound(x_i, beta_i)){
- x_j = selectSlackUpperBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- ++(d_statistics.d_statUpdateConflicts);
- Node conflict = generateConflictBelowLowerBound(x_i); //unsat
- d_conflictVariable = x_i;
- reportConflict(conflict);
- return true;
- }
- DeltaRational l_i = d_partialModel.getLowerBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, l_i);
-
- }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, beta_i)){
- x_j = selectSlackLowerBound(x_i, pf);
- if(x_j == ARITHVAR_SENTINEL ){
- ++(d_statistics.d_statUpdateConflicts);
- Node conflict = generateConflictAboveUpperBound(x_i); //unsat
-
- d_conflictVariable = x_i;
- reportConflict(conflict);
- return true;
- }
- DeltaRational u_i = d_partialModel.getUpperBound(x_i);
- d_linEq.pivotAndUpdate(x_i, x_j, u_i);
- }
- Assert(x_j != ARITHVAR_SENTINEL);
-
- //Check to see if we already have a conflict with x_j to prevent wasteful work
- if(CHECK_AFTER_PIVOT){
- Node possibleConflict = checkBasicForConflict(x_j);
- if(!possibleConflict.isNull()){
- d_conflictVariable = x_j;
- reportConflict(possibleConflict);
- return true; // unsat
- }
- }
- }
- Assert(remainingIterations == 0);
-
- return false;
-}
-
-
-
-Constraint SimplexDecisionProcedure::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic){
-
- int sgn = coeff.sgn();
- bool ub = aboveUpper?(sgn < 0) : (sgn > 0);
-
- Constraint c = ub ?
- d_partialModel.getUpperBoundConstraint(v) :
- d_partialModel.getLowerBoundConstraint(v);
-
-// #warning "revisit"
-// Node exp = ub ?
-// d_partialModel.explainUpperBound(v) :
-// d_partialModel.explainLowerBound(v);
-
- bool weakened;
- do{
- const DeltaRational& bound = c->getValue();
-
- weakened = false;
-
- Constraint weaker = ub?
- c->getStrictlyWeakerUpperBound(true, true):
- c->getStrictlyWeakerLowerBound(true, true);
-
- // Node weaker = ub?
- // d_propManager.strictlyWeakerAssertedUpperBound(v, bound):
- // d_propManager.strictlyWeakerAssertedLowerBound(v, bound);
-
- if(weaker != NullConstraint){
- //if(!weaker.isNull()){
- const DeltaRational& weakerBound = weaker->getValue();
- //DeltaRational weakerBound = asDeltaRational(weaker);
-
- DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound;
- //if var == basic,
- // if aboveUpper, weakerBound > bound, multiply by -1
- // if !aboveUpper, weakerBound < bound, multiply by -1
- diff = diff * coeff;
- if(surplus > diff){
- ++d_statistics.d_weakenings;
- weakened = true;
- anyWeakening = true;
- surplus = surplus - diff;
-
- Debug("weak") << "found:" << endl;
- if(v == basic){
- Debug("weak") << " basic: ";
- }
- Debug("weak") << " " << surplus << " "<< diff << endl
- << " " << bound << c << endl
- << " " << weakerBound << weaker << endl;
-
- Assert(diff > d_DELTA_ZERO);
- c = weaker;
- }
- }
- }while(weakened);
-
- return c;
-}
-
-Node SimplexDecisionProcedure::weakenConflict(bool aboveUpper, ArithVar basicVar){
- TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime);
-
- const DeltaRational& assignment = d_partialModel.getAssignment(basicVar);
- DeltaRational surplus;
- if(aboveUpper){
- Assert(d_partialModel.hasUpperBound(basicVar));
- Assert(assignment > d_partialModel.getUpperBound(basicVar));
- surplus = assignment - d_partialModel.getUpperBound(basicVar);
- }else{
- Assert(d_partialModel.hasLowerBound(basicVar));
- Assert(assignment < d_partialModel.getLowerBound(basicVar));
- surplus = d_partialModel.getLowerBound(basicVar) - assignment;
- }
-
- NodeBuilder<> conflict(kind::AND);
- bool anyWeakenings = false;
- for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){
- const Tableau::Entry& entry = *i;
- ArithVar v = entry.getColVar();
- const Rational& coeff = entry.getCoefficient();
- bool weakening = false;
- Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar);
- Debug("weak") << "weak : " << weakening << " "
- << c->assertedToTheTheory() << " "
- << d_partialModel.getAssignment(v) << " "
- << c << endl
- << c->explainForConflict() << endl;
- anyWeakenings = anyWeakenings || weakening;
-
- Debug("weak") << "weak : " << c->explainForConflict() << endl;
- c->explainForConflict(conflict);
- }
- ++d_statistics.d_weakeningAttempts;
- if(anyWeakenings){
- ++d_statistics.d_weakeningSuccesses;
- }
- return conflict;
-}
-
-
-Node SimplexDecisionProcedure::generateConflictAboveUpperBound(ArithVar conflictVar){
- return weakenConflict(true, conflictVar);
-}
-
-Node SimplexDecisionProcedure::generateConflictBelowLowerBound(ArithVar conflictVar){
- return weakenConflict(false, conflictVar);
-}
-
-
-// responses
-// unbounded below(arithvar)
-// reached threshold
-// reached maxpivots
-// reached GlobalMinimumd
-//
-SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primal(uint32_t maxIterations)
-{
- Assert(d_optRow != ARITHVAR_SENTINEL);
-
- for(uint32_t iteration = 0; iteration < maxIterations; iteration++){
- if(belowThreshold()){
- return ReachedThresholdValue;
- }
-
- PrimalResponse res = primalCheck();
- switch(res){
- case GlobalMinimum:
- case FoundUnboundedVariable:
- return res;
- case NoProgressOnLeaving:
- ++d_statistics.d_primalPivots;
- ++d_pivotsSinceOptProgress;
- ++d_pivotsSinceLastCheck;
- ++d_pivotsSinceErrorProgress;
-
- d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_partialModel.getAssignment(d_primalCarry.d_entering));
-
- if(Debug.isOn("primal::tableau")){
- d_linEq.debugCheckTableau();
- }
- if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
-
- break;
- case MakeProgressOnLeaving:
- {
- ++d_statistics.d_primalPivots;
- ++d_statistics.d_primalImprovingPivots;
-
- d_pivotsSinceOptProgress = 0;
- ++d_pivotsSinceErrorProgress;
- ++d_pivotsSinceLastCheck;
-
- d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_primalCarry.d_nextEnteringValue);
-
- static int helpful = 0;
- static int hurtful = 0;
- static int penguin = 0;
- if(d_currentErrorVariables.isKey(d_primalCarry.d_entering)){
- cout << "entering is error " << d_primalCarry.d_entering;
- if(d_currentErrorVariables[d_primalCarry.d_entering].errorIsLeqZero(d_partialModel)){
- cout << " now below threshold (" << (++helpful) << ") " << d_pivotsSinceErrorProgress << endl;
- }else{
- cout << "ouch (" << (++hurtful) << ")"<< d_pivotsSinceErrorProgress << endl;
- }
- }else if(d_currentErrorVariables.isKey(d_primalCarry.d_leaving)){
- cout << "leaving is error " << d_primalCarry.d_leaving;
- if(d_currentErrorVariables[d_primalCarry.d_leaving].errorIsLeqZero(d_partialModel)){
- cout << " now below threshold(" << (++helpful) << ")" << d_pivotsSinceErrorProgress << endl;
- }else{
- cout << "ouch (" << (++hurtful) << ")" << d_pivotsSinceErrorProgress<< endl;
- }
- }else{
- cout << " penguin (" << (++penguin) << ")" << d_pivotsSinceErrorProgress<< endl;
- }
-
- if(Debug.isOn("primal::tableau")){
- d_linEq.debugCheckTableau();
- }
- if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); }
- }
- break;
- default:
- Unreachable();
- }
- }
- return UsedMaxPivots;
-}
-
-
-/**
- * Error set
- * ErrorVariable |-> {ErrorVariable, InputVariable, InputConstraint}
- */
-
-/**
- * Returns SAT if it was able to satisfy all of the constraints in the error set
- * Returns UNSAT if it was able to able to find an error
- */
-Result::Sat SimplexDecisionProcedure::primalConverge(int depth){
- d_pivotsSinceLastCheck = 0;
-
- while(!d_currentErrorVariables.empty()){
- PrimalResponse res = primal(MAX_ITERATIONS - d_pivotsSinceLastCheck);
-
- switch(res){
- case FoundUnboundedVariable:
-
- // Drive the variable to at least 0
- // TODO This variable should be driven to a value that makes all of the error functions including it 0
- // It'll or another unbounded will be selected in the next round anyways so ignore for now.
- ++d_statistics.d_unboundedFound;
- if( !belowThreshold() ){
- driveOptToZero(d_primalCarry.d_unbounded);
- d_linEq.debugCheckTableau();
- if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("primalConverge")); }
-
- ++d_statistics.d_unboundedFound_drive;
- }
- Assert(belowThreshold());
- {
- uint32_t dropped = contractErrorVariables(true);
- Debug("primal::converge") << "primalConverge -> FoundUnboundedVariable -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
- d_statistics.d_unboundedFound_dropped += dropped;
- }
- break;
- case ReachedThresholdValue:
- ++d_statistics.d_primalThresholdReachedPivot;
-
- Assert(belowThreshold());
- {
- uint32_t dropped = contractErrorVariables(true);
- Debug("primal::converge") << "primalConverge -> ReachedThresholdValue -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
- d_statistics.d_primalThresholdReachedPivot_dropped += dropped;
- }
- break;
- case UsedMaxPivots:
- {
- d_pivotsSinceLastCheck = 0;
- ++d_statistics.d_primalReachedMaxPivots;
-
- // periodically attempt to do the following :
- // contract the error variable
- // check for a conflict on an error variable
- uint32_t dropped = contractErrorVariables(false);
-
- if( checkForRowConflicts() ){ // try to periodically look for a row
- Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> unsat " << endl;
-
- ++d_statistics.d_primalReachedMaxPivots_checkForConflictWorked;
- return Result::UNSAT; // row conflicts are minimal so stop.
- }
-
- if(dropped > 0){
- Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
- ++d_statistics.d_primalReachedMaxPivots_contractMadeProgress;
- }else{
- Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> nothing " << endl;
- }
- }
- break;
- case GlobalMinimum:
- ++d_statistics.d_primalGlobalMinimum;
-
- // If the minimum is positive, this is unsat.
- // However, the optimization row is not necessarily a minimal conflict
- if(!belowThreshold()){
-
- if(d_currentErrorVariables.size() == 1){
- // The optimization function is exactly the same as the last remaining variable
- // The conflict for the row is the same as the conflict for the optimization function.
- bool foundConflict = checkForRowConflicts();
- Assert(foundConflict);
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> one variable" << endl;
-
- return Result::UNSAT;
- }else{
- // There are at least 2 error variables.
- // Look for a minimal conflict
-
-
- if(checkForRowConflicts() ){
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> rowconflict " << endl;
-
- ++d_statistics.d_primalGlobalMinimum_rowConflictWorked;
- return Result::UNSAT;
- }
-
- uint32_t dropped = contractErrorVariables(false);
-
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl;
- if(dropped > 0){
- ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
- }
-
- ErrorMap half;
- d_currentErrorVariables.splitInto(half);
-
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << endl;
-
-
- reconstructOptimizationFunction();
- Result::Sat resultOnRemaining = primalConverge(depth + 1);
-
- if(resultOnRemaining == Result::UNSAT){
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was unsat " << endl;
- ++d_statistics.d_primalGlobalMinimum_firstHalfWasUnsat;
- restoreErrorVariables(half);
- return Result::UNSAT;
- }else{
- ++d_statistics.d_primalGlobalMinimum_firstHalfWasSat;
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was sat " << endl;
-
- Assert(resultOnRemaining == Result::SAT);
- Assert(d_currentErrorVariables.empty());
- d_currentErrorVariables.addAll(half);
- reconstructOptimizationFunction();
- return primalConverge(depth + 1);
- }
- }
-
- }else{
-
- // if the optimum is <= 0
- // drop all of the satisfied variables and continue;
- uint32_t dropped = contractErrorVariables(true);
- Debug("primal::converge") << "primalConverge -> GlobalMinimum -> negative -> dropped "<< dropped << " to " << d_currentErrorVariables.size() << endl;
-
- ++d_statistics.d_primalGlobalMinimum_contractMadeProgress;
- }
- break;
- default:
- Unreachable();
- }
- }
-
- return Result::SAT;
-}
-
-
-Result::Sat SimplexDecisionProcedure::primalFindModel(){
- Assert(d_primalCarry.isClear());
-
- // Reduce the queue to only contain violations
- reduceQueue();
-
- if(d_queue.empty()){
- return Result::SAT;
- }
- TimerStat::CodeTimer codeTimer(d_statistics.d_primalTimer);
-
- ++d_statistics.d_primalCalls;
-
- Debug("primalFindModel") << "primalFindModel() begin" << endl;
-
- const int PAUSE_RATE = 100;
- if(Debug.isOn("primal::pause") && d_statistics.d_primalCalls.getData() % PAUSE_RATE == 0){
- Debug("primal::pause") << "waiting for input: ";
- std::string dummy;
- std::getline(std::cin, dummy);
- }
-
- // TODO restore the tableau by ejecting variables
- // Tableau copy(d_tableau);
-
- Result::Sat answer;
- {
- TimerStat::CodeTimer codeTimer(d_statistics.d_internalTimer);
-
- // This is needed because of the fiddling with the partial model
- //context::Context::ScopedPush speculativePush(satContext);
-
- constructErrorVariables();
- constructOptimizationFunction();
- if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
- if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel 1"); }
- answer = primalConverge(0);
- }
- removeOptimizationFunction();
-
-
- // exit
- uint32_t nc = d_tableau.getNumColumns();
- //d_tableau = copy;
- while(d_tableau.getNumColumns() < nc){
- d_tableau.increaseSize();
- }
- restoreErrorVariables(d_currentErrorVariables);
-
- reduceQueue();
-
- if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
-
- if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel2"); }
- Debug("primalFindModel") << "primalFindModel() end " << answer << endl;
-
- // The set of variables in conflict with their bounds will still be a subset of the
- // variables that are in conflict with their bounds in the beginning.
- // The basic variables are the same because of the copy.
- // Thus it is safe to not try to not recompute the queue of violating variables
-
- if(answer == Result::UNSAT){
- // This needs to be done in a different context level than the push
- ++d_statistics.d_primalUnsatCalls;
- }else{
- ++d_statistics.d_primalSatCalls;
- }
-
- d_primalCarry.clear();
-
- return answer;
-}
-
-/** Clears the ErrorMap and relase the resources associated with it.
- * There are a couple of error maps around
- */
-void SimplexDecisionProcedure::restoreErrorVariables(SimplexDecisionProcedure::ErrorMap& es){
- while(!es.empty()){
- ArithVar e = es.back();
-
- reassertErrorConstraint(e, es, false, false);
- es.pop_back();
- }
-}
-
-void SimplexDecisionProcedure::constructErrorVariables(){
- Assert(d_currentErrorVariables.empty());
- Assert(!d_queue.empty());
-
- for(ArithPriorityQueue::const_iterator iter = d_queue.begin(), end = d_queue.end(); iter != end; ++iter){
- ArithVar input = *iter;
-
- Assert(d_tableau.isBasic(input));
- Assert(!d_partialModel.assignmentIsConsistent(input));
-
- Assert(!d_currentErrorVariables.isKey(input));
-
- bool ub = d_partialModel.strictlyGreaterThanUpperBound(input, d_partialModel.getAssignment(input));
-
- Constraint original = ub ? d_partialModel.getUpperBoundConstraint(input)
- : d_partialModel.getLowerBoundConstraint(input);
-
- d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
-
- if(ub){
- d_partialModel.forceRelaxUpperBound(input);
- }else{
- d_partialModel.forceRelaxLowerBound(input);
- }
- Debug("primal") << "adding error variable (" << input << ", " << ", " << original <<") ";
- Debug("primal") << "ub "<< ub << " " << d_partialModel.getAssignment(input) << " " << original->getValue() <<")" << endl;
- d_currentErrorVariables.set(input, ErrorInfo(input, ub, original));
-
- // Constraint boundIsValue = d_constraintDatabase.getConstraint(bound, Equality, original->getValue());
- // boundIsValue->setPsuedoConstraint();
-
- // d_partialModel.setAssignment(bound, boundIsValue->getValue());
- // d_partialModel.setUpperBoundConstraint(boundIsValue);
- // d_partialModel.setLowerBoundConstraint(boundIsValue);
-
- // // if ub
- // // then error = x - boundIsValue
- // // else error = boundIsValue - x
-
- // ArithVar error = requestVariable();
-
- // DeltaRational diff = ub ?
- // d_partialModel.getAssignment(input) - boundIsValue->getValue() :
- // boundIsValue->getValue() - d_partialModel.getAssignment(input);
-
- // d_partialModel.setAssignment(error, diff);
-
- // vector<Rational> coeffs;
- // vector<ArithVar> variables;
- // variables.push_back(input);
- // coeffs.push_back(ub ? Rational(1) : Rational(-1));
- // variables.push_back(bound);
- // coeffs.push_back(ub ? Rational(-1) : Rational(1));
-
- // d_tableau.addRow(error, coeffs, variables);
-
-
- }
-
- if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
- if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("constructErrorVariables");}
- Assert(!d_currentErrorVariables.empty());
-}
-
-
-
-/** Returns true if it has found a row conflict for any of the error variables. */
-bool SimplexDecisionProcedure::checkForRowConflicts(){
- vector<ArithVar> inConflict;
- for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
- ArithVar error = *iter;
- const ErrorInfo& info = d_currentErrorVariables[error];
- if(d_tableau.isBasic(error) && !info.errorIsLeqZero(d_partialModel)){
-
- ArithVar x_j = info.isUpperbound() ?
- selectSlackLowerBound(error) :
- selectSlackUpperBound(error);
-
- if(x_j == ARITHVAR_SENTINEL ){
- inConflict.push_back(error);
- }
- }
- }
-
- if(!inConflict.empty()){
- while(!inConflict.empty()){
- ArithVar error = inConflict.back();
- inConflict.pop_back();
-
- reassertErrorConstraint(error, d_currentErrorVariables, false, true);
-
- Node conflict = d_currentErrorVariables[error].isUpperbound() ?
- generateConflictAboveUpperBound(error) :
- generateConflictBelowLowerBound(error);
- Assert(!conflict.isNull());
-
- d_currentErrorVariables.remove(error);
-
- reportConflict(conflict);
- }
- reconstructOptimizationFunction();
- return true;
- }else{
- return false;
- }
-}
-
-void SimplexDecisionProcedure::reassertErrorConstraint(ArithVar e, SimplexDecisionProcedure::ErrorMap& es, bool definitelyTrue, bool definitelyFalse){
- Assert(es.isKey(e));
- const ErrorInfo& info = es.get(e);
- Constraint original = info.getConstraint();
-
- if(info.isUpperbound()){
- d_partialModel.setUpperBoundConstraint(original);
- }else if(original->isLowerBound()){
- d_partialModel.setLowerBoundConstraint(original);
- }
-
- Assert(!definitelyTrue || d_partialModel.assignmentIsConsistent(e));
- Assert(!definitelyFalse || !d_partialModel.assignmentIsConsistent(e));
-}
-
-uint32_t SimplexDecisionProcedure::contractErrorVariables(bool guaranteedSuccess){
- uint32_t entrySize = d_currentErrorVariables.size();
- Debug("primal::contract") << "contractErrorVariables() begin : " << d_currentErrorVariables.size() << endl;
-
- std::vector<ArithVar> toRemove;
- for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
- ArithVar e = *iter;
- if(d_currentErrorVariables[e].errorIsLeqZero(d_partialModel)){
- toRemove.push_back(e);
- }
- }
-
- Assert(!guaranteedSuccess || !toRemove.empty());
-
- if(!toRemove.empty()){
- while(!toRemove.empty()){
- ArithVar e = toRemove.back();
- toRemove.pop_back();
- reassertErrorConstraint(e, d_currentErrorVariables, true, false);
- d_currentErrorVariables.remove(e);
- }
-
- reconstructOptimizationFunction();
- }
-
- Debug("primal::contract") << "contractErrorVariables() end : " << d_currentErrorVariables.size() << endl;
-
- uint32_t exitSize = d_currentErrorVariables.size();
-
- Assert(exitSize <= entrySize);
- Assert(!guaranteedSuccess|| exitSize < entrySize);
- return entrySize - exitSize;
-}
-
-void SimplexDecisionProcedure::removeOptimizationFunction(){
- Assert(d_optRow != ARITHVAR_SENTINEL);
- Assert(d_tableau.isBasic(d_optRow));
-
- d_tableau.removeBasicRow(d_optRow);
- releaseVariable(d_optRow);
-
- d_optRow = ARITHVAR_SENTINEL;
- d_negOptConstant = d_DELTA_ZERO;
-
- Assert(d_optRow == ARITHVAR_SENTINEL);
-}
-
-void SimplexDecisionProcedure::constructOptimizationFunction(){
- Assert(d_optRow == ARITHVAR_SENTINEL);
- Assert(d_negOptConstant == d_DELTA_ZERO);
-
- d_optRow = requestVariable();
-
-
- std::vector<Rational> coeffs;
- std::vector<ArithVar> variables;
- for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){
- ArithVar e = *iter;
-
- if(d_currentErrorVariables[e].isUpperbound()){
- coeffs.push_back(Rational(1));
- variables.push_back(e);
- d_negOptConstant = d_negOptConstant + (d_currentErrorVariables[e].getConstraint()->getValue());
- }else{
- coeffs.push_back(Rational(-1));
- variables.push_back(e);
- d_negOptConstant = d_negOptConstant - (d_currentErrorVariables[e].getConstraint()->getValue());
- }
- }
- d_tableau.addRow(d_optRow, coeffs, variables);
-
- DeltaRational newAssignment = d_linEq.computeRowValue(d_optRow, false);
- d_partialModel.setAssignment(d_optRow, newAssignment);
-
- if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); }
-
-
- if(Debug.isOn("primal::consistent")){
- d_linEq.debugEntireLinEqIsConsistent("constructOptimizationFunction");
- }
-
- d_pivotsSinceOptProgress = 0;
- d_pivotsSinceErrorProgress = 0;
-
- Assert(d_optRow != ARITHVAR_SENTINEL);
-}
-
-void SimplexDecisionProcedure::reconstructOptimizationFunction(){
- removeOptimizationFunction();
- constructOptimizationFunction();
-}
-
-
-
-/* TODO:
- * Very naive implementation. Recomputes everything every time.
- * Currently looks for the variable that can decrease the optimization function the most.
- *
- */
-SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primalCheck()
-{
- Debug("primal") << "primalCheck() begin" << endl;
-
- ArithVar leaving = ARITHVAR_SENTINEL;
- ArithVar entering = ARITHVAR_SENTINEL;
- DeltaRational leavingShift = d_DELTA_ZERO; // The amount the leaving variable can change by without making the tableau inconsistent
- DeltaRational leavingDelta = d_DELTA_ZERO; // The amount the optimization function changes by selecting leaving
-
- Assert(d_improvementCandidates.empty());
-
- for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_optRow); !ri.atEnd(); ++ri){
- const Tableau::Entry& e = *ri;
-
- ArithVar curr = e.getColVar();
- if(curr == d_optRow){ continue; }
-
-
- int sgn = e.getCoefficient().sgn();
- Assert(sgn != 0);
- if( (sgn < 0 && d_partialModel.strictlyBelowUpperBound(curr)) ||
- (sgn > 0 && d_partialModel.strictlyAboveLowerBound(curr)) ){
-
- d_improvementCandidates.push_back(&e);
- }
- }
-
- if(d_improvementCandidates.empty()){
- Debug("primal") << "primalCheck() end : global" << endl;
- return GlobalMinimum; // No variable in the optimization function can be improved
- }
-
- DeltaRational minimumShift;
- DeltaRational currShift;
- for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
- const Tableau::Entry& e = *(*ci);
- ArithVar curr = e.getColVar();
-
- ArithVar currEntering;
- bool progress;
-
- minimumShift = (leaving == ARITHVAR_SENTINEL ) ? leavingDelta/(e.getCoefficient().abs()) : d_DELTA_ZERO;
- int sgn = e.getCoefficient().sgn();
- computeShift(curr, sgn < 0, progress, currEntering, currShift, minimumShift);
-
- if(currEntering == ARITHVAR_SENTINEL){
- d_improvementCandidates.clear();
-
- Debug("primal") << "primalCheck() end : unbounded" << endl;
- d_primalCarry.d_unbounded = curr;
- return FoundUnboundedVariable;
- }else if(progress) {
- leaving = curr;
- leavingShift = currShift;
- leavingDelta = currShift * e.getCoefficient();
- entering = currEntering;
-
- Assert(leavingDelta < d_DELTA_ZERO);
-
- const int RECHECK_PERIOD = 10;
- if(d_pivotsSinceErrorProgress % RECHECK_PERIOD != 0){
- // we can make progress, stop
- break;
- }
- }
- }
-
- if(leaving == ARITHVAR_SENTINEL){
- cout << "Nothing can make progress " << endl;
-
- const uint32_t THRESHOLD = 20;
- if(d_pivotsSinceOptProgress <= THRESHOLD){
-
- int index = rand() % d_improvementCandidates.size();
- leaving = (*d_improvementCandidates[index]).getColVar();
- entering = selectFirstValid(leaving, (*d_improvementCandidates[index]).getCoefficient().sgn() < 0);
- }else{ // Bland's rule
- bool increasing;
- for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){
- const Tableau::Entry& e = *(*ci);
- ArithVar curr = e.getColVar();
- leaving = (leaving == ARITHVAR_SENTINEL) ? curr : minVarOrder(*this, curr, leaving);
- if(leaving == curr){
- increasing = (e.getCoefficient().sgn() < 0);
- }
- }
-
- entering = selectMinimumValid(leaving, increasing);
- }
- Assert(leaving != ARITHVAR_SENTINEL);
- Assert(entering != ARITHVAR_SENTINEL);
-
- d_primalCarry.d_leaving = leaving;
- d_primalCarry.d_entering = entering;
-
- d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering);
-
- Debug("primal") << "primalCheck() end : no progress made " << leaving << " to " << entering << " (" << d_pivotsSinceOptProgress << ")"<< endl;
- d_improvementCandidates.clear();
- return NoProgressOnLeaving;
- }else{
- const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
- Assert(!enterLeavingEntry.blank());
-
- d_primalCarry.d_leaving = leaving;
- d_primalCarry.d_entering = entering;
- d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
- + leavingShift * enterLeavingEntry.getCoefficient();
-
- Debug("primal") << "primalCheck() end : progress" << endl
- << leaving << " to " << entering << " ~ "
- << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
- << " ~ " << enterLeavingEntry.getCoefficient()
- << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
-
- d_improvementCandidates.clear();
- return MakeProgressOnLeaving;
- }
-
- // anyProgress = true;
-
- // DeltaRational currDelta = currShift * e.getCoefficient();
-
- // int cmp = currDelta.cmp(leavingDelta);
-
- // // Cases:
- // // 1) No candidate yet,
- // // 2) there is a candidate with a strictly better update, or
- // // 3) there is a candidate with the same update value that has a smaller value in the variable ordering.
- // //
- // // Case 3 covers Bland's rule.
- // if(entering == ARITHVAR_SENTINEL || cmp < 0){
- // leaving = curr;
- // }else if( cmp == 0 ){
- // leaving = minVarOrder(*this, curr, leaving);
- // }
-
- // if(leaving == curr){
- // leavingShift = currShift;
- // leavingDelta = currDelta;
- // entering = currEntering;
- // }
- // }
- // }
-
- // if(leaving == ARITHVAR_SENTINEL){
- // Debug("primal") << "primalCheck() end : global" << endl;
- // return GlobalMinimum; // No variable in the optimization function can be improved
- // }else{
- // const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving);
- // Assert(!enterLeavingEntry.blank());
-
- // d_primalCarry.d_leaving = leaving;
- // d_primalCarry.d_entering = entering;
- // d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering)
- // + leavingShift * enterLeavingEntry.getCoefficient();
-
- // Debug("primal") << "primalCheck() end : progress" << endl
- // << leaving << " to " << entering << " ~ "
- // << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift
- // << " ~ " << enterLeavingEntry.getCoefficient()
- // << " ~ " << d_primalCarry.d_nextEnteringValue << endl;
- // return MakeProgressOnLeaving;
- // }
-}
-
-ArithVar SimplexDecisionProcedure::selectMinimumValid(ArithVar v, bool increasing){
- ArithVar minimum = ARITHVAR_SENTINEL;
- for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
- const Tableau::Entry& e = *colIter;
- ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
- if(basic == d_optRow) continue;
-
-
- int esgn = e.getCoefficient().sgn();
- bool basicInc = (increasing == (esgn > 0));
-
- if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
- d_partialModel.strictlyAboveLowerBound(basic))){
- if(minimum == ARITHVAR_SENTINEL){
- minimum = basic;
- }else{
- minimum = minVarOrder(*this, basic, minimum);
- }
- }
- }
- return minimum;
-}
-
-ArithVar SimplexDecisionProcedure::selectFirstValid(ArithVar v, bool increasing){
- ArithVar minimum = ARITHVAR_SENTINEL;
-
- for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){
- const Tableau::Entry& e = *colIter;
- ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
- if(basic == d_optRow) continue;
-
- int esgn = e.getCoefficient().sgn();
- bool basicInc = (increasing == (esgn > 0));
-
- if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
- d_partialModel.strictlyAboveLowerBound(basic))){
- if(minimum == ARITHVAR_SENTINEL){
- minimum = basic;
- }else{
- minimum = minRowLength(*this, basic, minimum);
- }
- }
- }
- return minimum;
-}
-
-
-
-void SimplexDecisionProcedure::computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift){
- Assert(increasing ? (minimumShift >= d_DELTA_ZERO) : (minimumShift <= d_DELTA_ZERO) );
-
- static int instance = 0;
- Debug("primal") << "computeshift " << ++instance << " " << leaving << endl;
-
- // The selection for the leaving variable
- entering = ARITHVAR_SENTINEL;
-
- // no progress is initially made
- progress = false;
-
- bool bounded = false;
-
- if(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving)){
- const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
-
- bounded = true;
-
- DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
- Assert(increasing ? diff.sgn() >=0 : diff.sgn() <= 0);
- if((increasing) ? (diff < minimumShift) : ( diff > minimumShift)){
- Assert(!progress);
- entering = leaving; // My my my, what an ugly hack
- return; // no progress is possible stop
- }
- }
-
- // shift has a meaningful value once entering has a meaningful value
- // if increasing,
- // then shift > minimumShift >= 0
- // else shift < minimumShift <= 0
- //
- // Maintain the following invariant:
- //
- // if increasing,
- // if e_ij > 0, diff >= shift > minimumShift >= 0
- // if e_ij < 0, diff >= shift > minimumShift >= 0
- // if !increasing,
- // if e_ij > 0, diff <= shift < minimumShift <= 0
- // if e_ij < 0, diff <= shift < minimumShift <= 0
- // if increasing == (e_ij > 0), diff = (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
- // if increasing != (e_ij > 0), diff = (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient()
-
- for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
- const Tableau::Entry& e = *colIter;
-
- ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
- if(basic == d_optRow) continue;
-
- int esgn = e.getCoefficient().sgn();
- bool basicInc = (increasing == (esgn > 0));
- // If both are true, increasing the variable entering increases the basic variable
- // If both are false, the entering variable is decreasing, but the coefficient is negative and the basic variable is increasing
- // If exactly one is false, the basic variable is decreasing.
-
- Debug("primal::shift") << basic << " " << d_partialModel.hasUpperBound(basic) << " "
- << d_partialModel.hasLowerBound(basic) << " "
- << e.getCoefficient() << endl;
-
- if( (basicInc && d_partialModel.hasUpperBound(basic))||
- (!basicInc && d_partialModel.hasLowerBound(basic))){
-
- if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) :
- d_partialModel.strictlyAboveLowerBound(basic))){
- // diff == 0, as diff > minimumShift >= 0 or diff < minimumShift <= 0
- Assert(!progress);
- entering = basic;
- return;
- }else{
- DeltaRational diff = basicInc ?
- (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
- (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
-
- if( entering == ARITHVAR_SENTINEL ){
- if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
- Assert(!progress);
- entering = basic;
- return;
- }else{
- Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
- shift = diff;
- entering = basic;
- bounded = true;
- }
- }else{
- if( increasing ? (diff < shift) : diff > shift){
- // a new min for increasing
- // a new max for decreasing
-
- if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){
- Assert(!progress);
- entering = basic;
- return;
- }else{
- Assert(increasing ? (diff > minimumShift) : (diff < minimumShift));
- shift = diff;
- entering = basic;
- }
- }
- }
- }
- }
- }
-
- if(!bounded){
- // A totally unbounded variable
- Assert(entering == ARITHVAR_SENTINEL);
- progress = true;
- return;
- }else if(entering == ARITHVAR_SENTINEL){
- // We have a variable that is bounded only by its maximum
- for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){
- const Tableau::Entry& e = *colIter;
-
- ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex());
- if(basic == d_optRow){
- continue;
- } else{
- entering = basic;
- break;
- }
- }
- Assert(entering != ARITHVAR_SENTINEL);
-
- Assert(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving));
-
- const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
- DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
-
- shift = diff;
-
- Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
- Assert(increasing ? shift > minimumShift : shift < minimumShift);
-
- progress = true;
- return;
- }else{
- Assert(bounded);
- progress = true;
-
- if((increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
- Assert(entering != ARITHVAR_SENTINEL);
- const DeltaRational& assignment = d_partialModel.getAssignment(leaving);
- DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
- if((increasing) ? (diff < shift) : ( diff > shift)){
- shift = diff;
- }
- }
-
- Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
- Assert(increasing ? shift > minimumShift : shift < minimumShift);
- return;
- }
-
-
- // if(! bounded ||
- // (increasing && diff < shift) || // a new min for increasing
- // (!increasing && diff > shift)){ // a new max for decreasing
- // bounded = true;
- // shift = diff;
- // entering = basic;
- // }
- // }
-
- // if(notAtTheBound && !blandMode){
- // DeltaRational diff = basicInc ?
- // (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() :
- // (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient();
-
- // if(! bounded ||
- // (increasing && diff < shift) || // a new min for increasing
- // (!increasing && diff > shift)){ // a new max for decreasing
- // bounded = true;
- // shift = diff;
- // entering = basic;
- // }
- // }else if (!notAtTheBound) { // basic is already exactly at the bound
- // if(!blandMode){ // Enter into using Bland's rule
- // blandMode = true;
- // bounded = true;
- // shift = d_DELTA_ZERO;
- // entering = basic;
- // }else{
- // entering = minVarOrder(*this, entering, basic); // Bland's rule.
- // }
- // }
- // else this basic variable cannot be violated by increasing/decreasing entering
-
-
-
-
- // if(!blandMode && (increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){
- // Assert(entering != ARITHVAR_SENTINEL);
- // bounded = true;
- // DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment;
- // if((increasing) ? (diff < shift) : ( diff > shift)){
- // shift = diff;
- // }
- // }
-
- // Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0);
-
- // return shift;
-}
-
-
-/**
- * Given an variable on the optimization row that can be used to decrease the value of the optimization function
- * arbitrarily and an optimization function that is strictly positive in the current model,
- * driveOptToZero updates the value of unbounded s.t. the value of d_opt is exactly 0.
- */
-void SimplexDecisionProcedure::driveOptToZero(ArithVar unbounded){
- Assert(!belowThreshold());
-
- const Tableau::Entry& e = d_tableau.findEntry(d_tableau.basicToRowIndex(d_optRow), unbounded);
- Assert(!e.blank());
-
- DeltaRational theta = (d_negOptConstant-d_partialModel.getAssignment(d_optRow))/ (e.getCoefficient());
- Assert((e.getCoefficient().sgn() > 0) ? (theta.sgn() < 0) : (theta.sgn() > 0));
-
- DeltaRational newAssignment = d_partialModel.getAssignment(unbounded) + theta;
- d_linEq.update(unbounded, newAssignment);
-
- if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("driveOptToZero")); }
-
- Assert(belowThreshold());
-}
diff --git a/src/theory/arith/simplex-converge.h b/src/theory/arith/simplex-converge.h
deleted file mode 100644
index dac3a9b49..000000000
--- a/src/theory/arith/simplex-converge.h
+++ /dev/null
@@ -1,531 +0,0 @@
-/********************* */
-/*! \file simplex.h
- ** \verbatim
- ** Original author: taking
- ** Major contributors: none
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 prototype.
- ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
- ** Courant Institute of Mathematical Sciences
- ** New York University
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure.
- **
- ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
- ** See the Simplex for DPLL(T) technical report for more background.(citation?)
- ** This shares with the theory a Tableau, and a PartialModel that:
- ** - satisfies the equalities in the Tableau, and
- ** - the assignment for the non-basic variables satisfies their bounds.
- ** This is required to either produce a conflict or satisifying PartialModel.
- ** Further, we require being told when a basic variable updates its value.
- **
- ** During the Simplex search we maintain a queue of variables.
- ** The queue is required to contain all of the basic variables that voilate their bounds.
- ** As elimination from the queue is more efficient to be done lazily,
- ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds.
- **
- ** The simplex procedure roughly follows Alberto's thesis. (citation?)
- ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.)
- ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.)
- ** After this, Bland's pivot rule is invoked.
- **
- ** During this proccess, we periodically inspect the queue of variables to
- ** 1) remove now extraneous extries,
- ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and
- ** 3) detect multiple conflicts.
- **
- ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict.
- **
- ** Extra things tracked atm: (Subject to change at Tim's whims)
- ** - A superset of all of the newly pivoted variables.
- ** - A queue of additional conflicts that were discovered by Simplex.
- ** These are theory valid and are currently turned into lemmas
- **/
-
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H
-#define __CVC4__THEORY__ARITH__SIMPLEX_H
-
-#include "theory/arith/arithvar.h"
-#include "theory/arith/arith_priority_queue.h"
-#include "theory/arith/delta_rational.h"
-#include "theory/arith/matrix.h"
-#include "theory/arith/partial_model.h"
-#include "theory/arith/linear_equality.h"
-
-#include "context/cdlist.h"
-
-#include "util/dense_map.h"
-#include "options/options.h"
-#include "util/stats.h"
-#include "util/result.h"
-
-#include <queue>
-
-namespace CVC4 {
-namespace theory {
-namespace arith {
-
-class SimplexDecisionProcedure {
-private:
- ArithVar d_conflictVariable;
- DenseSet d_successes;
-
- /** Linear equality module. */
- LinearEqualityModule& d_linEq;
-
- /**
- * Manages information about the assignment and upper and lower bounds on
- * variables.
- * Partial model matches that in LinearEqualityModule.
- */
- ArithPartialModel& d_partialModel;
-
- /**
- * Stores the linear equalities used by Simplex.
- * Tableau from the LinearEquality module.
- */
- Tableau& d_tableau;
-
- /** Contains a superset of the basic variables in violation of their bounds. */
- ArithPriorityQueue d_queue;
-
- /** Number of variables in the system. This is used for tuning heuristics. */
- ArithVar d_numVariables;
-
- /** This is the call back channel for Simplex to report conflicts. */
- NodeCallBack& d_conflictChannel;
-
- /** Maps a variable to how many times they have been used as a pivot in the simplex search. */
- DenseMultiset d_pivotsInRound;
-
- /** Stores to the DeltaRational constant 0. */
- DeltaRational d_DELTA_ZERO;
-
- //TODO make an option
- const static uint32_t MAX_ITERATIONS = 20;
-
-
- /** Used for requesting d_opt, bound and error variables for primal.*/
- ArithVarMalloc& d_arithVarMalloc;
-
- /** Used for constructing temporary variables, bound and error variables for primal.*/
- ConstraintDatabase& d_constraintDatabase;
-
-public:
- SimplexDecisionProcedure(LinearEqualityModule& linEq,
- NodeCallBack& conflictChannel,
- ArithVarMalloc& variables,
- ConstraintDatabase& constraintDatabase);
-
- /**
- * This must be called when the value of a basic variable may now voilate one
- * of its bounds.
- */
- void updateBasic(ArithVar x){
- d_queue.enqueueIfInconsistent(x);
- }
-
- /**
- * Tries to update the assignments of variables such that all of the
- * assignments are consistent with their bounds.
- * This is done by a simplex search through the possible bases of the tableau.
- *
- * If all of the variables can be made consistent with their bounds
- * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
- * was reported on the conflictCallback passed to the Module.
- *
- * Tableau pivoting is performed so variables may switch from being basic to
- * nonbasic and vice versa.
- *
- * Corresponds to the "check()" procedure in [Cav06].
- */
- Result::Sat dualFindModel(bool exactResult);
-
-
- /**
- * Tries to update the assignments of the variables s.t. all of the assignments
- * are consistent with their bounds.
- *
- * This is a REALLY heavy hammer consider calling dualFindModel(false) first.
- *
- * !!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!
- * This procedure needs to temporarily relax contraints to contruct a satisfiable system.
- * To do this, it is going to do a sat push.
- */
- Result::Sat primalFindModel();
-
-private:
-
-
- /**
- * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
- * and 2 ArithVar variables and returns one of the ArithVar variables potentially
- * using the internals of the SimplexDecisionProcedure.
- *
- * Both ArithVar must be non-basic in d_tableau.
- */
- typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar);
-
- /**
- * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars.
- * This PreferenceFunction is used during the VarOrder stage of
- * findModel.
- */
- static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
-
- /**
- * minRowCount is a PreferenceFunction for selecting the variable with the smaller
- * row count in the tableau.
- *
- * This is a heuristic rule and should not be used
- * during the VarOrder stage of findModel.
- */
- static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
- static ArithVar minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
-
- /**
- * minBoundAndRowCount is a PreferenceFunction for preferring a variable
- * without an asserted bound over variables with an asserted bound.
- * If both have bounds or both do not have bounds,
- * the rule falls back to minRowCount(...).
- *
- * This is a heuristic rule and should not be used
- * during the VarOrder stage of findModel.
- */
- static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
-
-
- /**
- * This is the main simplex for DPLL(T) loop.
- * It runs for at most maxIterations.
- *
- * Returns true iff it has found a conflict.
- * d_conflictVariable will be set and the conflict for this row is reported.
- */
- bool searchForFeasibleSolution(uint32_t maxIterations);
-
- enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch};
-
- bool findConflictOnTheQueue(SearchPeriod period);
-
-
- /**
- * Given the basic variable x_i,
- * this function finds the smallest nonbasic variable x_j in the row of x_i
- * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
- * This returns ARITHVAR_SENTINEL if none exists.
- *
- * More formally one of the following conditions must be satisfied:
- * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
- * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
- * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
- * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
- *
- */
- template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf);
- ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
- return selectSlack<true>(x_i, pf);
- }
- ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
- return selectSlack<false>(x_i, pf);
- }
- /**
- * Returns the smallest basic variable whose assignment is not consistent
- * with its upper and lower bounds.
- */
- ArithVar selectSmallestInconsistentVar();
-
- /**
- * Given a non-basic variable that is know to not be updatable
- * to a consistent value, construct and return a conflict.
- * Follows section 4.2 in the CAV06 paper.
- */
- Node generateConflictAboveUpperBound(ArithVar conflictVar);
- Node generateConflictBelowLowerBound(ArithVar conflictVar);
-
-public:
- void increaseMax() {d_numVariables++;}
-
-
- void clearQueue() {
- d_queue.clear();
- }
-
-
- bool debugIsInCollectionQueue(ArithVar var) const{
- Assert(d_queue.inCollectionMode());
- return d_queue.collectionModeContains(var);
- }
-
- void reduceQueue(){
- d_queue.reduce();
- }
-
- ArithPriorityQueue::const_iterator queueBegin() const{
- return d_queue.begin();
- }
-
- ArithPriorityQueue::const_iterator queueEnd() const{
- return d_queue.end();
- }
-
-private:
-
- /** Reports a conflict to on the output channel. */
- void reportConflict(Node conflict){
- d_conflictChannel(conflict);
- ++(d_statistics.d_simplexConflicts);
- }
-
- template <bool above>
- inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){
- return
- ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
- ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) ||
- (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
- (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic));
- }
-
- /**
- * Checks a basic variable, b, to see if it is in conflict.
- * If a conflict is discovered a node summarizing the conflict is returned.
- * Otherwise, Node::null() is returned.
- */
- Node checkBasicForConflict(ArithVar b);
-
- Node weakenConflict(bool aboveUpper, ArithVar basicVar);
- Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic);
-
- /** Gets a fresh variable from TheoryArith. */
- ArithVar requestVariable(){
- return d_arithVarMalloc.request();
- }
-
- /** Releases a requested variable from TheoryArith.*/
- void releaseVariable(ArithVar v){
- d_arithVarMalloc.release(v);
- }
-
-
- /** An error info keeps the information associated with the construction of an ErrorVariable. */
- class ErrorInfo {
- private:
- /** The variable for which the error variable was constructed.*/
- ArithVar d_variable;
-
- // If false -> lowerbound
- bool d_upperbound;
-
- /** The violated constraint this was constructed to try to satisfy.*/
- Constraint d_violated;
-
- public:
- ErrorInfo(ArithVar error, bool ub, const Constraint original)
- : d_variable(error), d_upperbound(ub), d_violated(original) {}
-
- ErrorInfo() :
- d_variable(ARITHVAR_SENTINEL), d_upperbound(false), d_violated(NullConstraint){}
-
- inline ArithVar getVariable() const {
- return d_variable;
- }
-
- inline bool isUpperbound() const {
- return d_upperbound;
- }
-
- inline bool errorIsLeqZero(const ArithPartialModel& d_pm) const{
- return isUpperbound() ?
- (d_pm.getAssignment(d_variable) <= d_violated->getValue()) :
- (d_pm.getAssignment(d_variable) >= d_violated->getValue());
- }
-
- inline Constraint getConstraint() const {
- return d_violated;
- }
-
- inline DeltaRational getErrorAmount(const ArithPartialModel& d_pm) const {
- return isUpperbound() ?
- (d_pm.getAssignment(d_variable) - d_violated->getValue()) :
- (d_violated->getValue() - d_pm.getAssignment(d_variable));
- }
- };
-
- typedef DenseMap<ErrorInfo> ErrorMap;
-
- /** A map from the error variables to the associated ErrorInfo.*/
- ErrorMap d_currentErrorVariables;
-
- /** The optimization function is implicitly defined as
- * f_i = d_optRow - d_negOptConstant
- *
- * d_optRow is a basic variable in the tableau.
- * The tableau maintains that it is equal to the sum of -1^{!ub} * the error variables in
- * d_currentErrorVariables.
- *
- * d_negOptConstant is explicitly the negation of the sum of the bounds that were violated
- *
- * assignment(f_i) <= 0 iff assignment(d_optRow) <= d_negOptConstant
- */
- /** The variable for the variable part of the optimization function.*/
- ArithVar d_optRow;
-
- /** The constant part of the optimization function.*/
- DeltaRational d_negOptConstant;
-
- inline bool belowThreshold() const {
- return d_partialModel.getAssignment(d_optRow) <= d_negOptConstant;
- }
-
- /**
- * A PrimalResponse represents the state that the primal simplex solver is in.
- */
- enum PrimalResponse {
- // The optimization can decrease arbitrarily on some variable in the function
- FoundUnboundedVariable,
-
- // The optimization function has reached a threshold value and is checking back in
- ReachedThresholdValue,
-
- // Simplex has used up its pivot bound and is checking back in with its caller
- UsedMaxPivots,
-
- //Simplex can make progress on the pair of entering and leaving variables
- MakeProgressOnLeaving,
-
- //Simplex is not at a minimum but no leaving variable can be changed to help
- NoProgressOnLeaving,
-
- // Simplex has reached a minimum for its optimization function
- GlobalMinimum
- };
-
- /**
- * These fields are for sticking information for passing information back with the PrimalRespones.
- * These fields should be ignored as unsafe/unknown unless you have a PrimalResponse that states
- * the field makes sense.
- */
- struct PrimalPassBack {
- public:
- /**
- * A variable s.t. its value can be increased/decreased arbitrarily to change the optimization function
- * arbitrarily low.
- */
- ArithVar d_unbounded;
-
- /** The leaving variable selection for primal simplex. */
- ArithVar d_leaving;
-
- /** The entering variable selection for primal simplex. */
- ArithVar d_entering;
-
- /** The new value for the leaving variable value for primal simplex.*/
- DeltaRational d_nextEnteringValue;
-
- PrimalPassBack() { clear(); }
- void clear(){
- d_unbounded = (d_leaving = (d_entering = ARITHVAR_SENTINEL));
- d_nextEnteringValue = DeltaRational();
- }
-
- bool isClear() const {
- return d_unbounded == ARITHVAR_SENTINEL &&
- d_leaving == ARITHVAR_SENTINEL &&
- d_entering == ARITHVAR_SENTINEL &&
- d_nextEnteringValue.sgn() == 0;
- }
- } d_primalCarry;
-
- uint32_t d_pivotsSinceErrorProgress;
- uint32_t d_pivotsSinceOptProgress;
- uint32_t d_pivotsSinceLastCheck;
-
- typedef std::vector< const Tableau::Entry* > EntryVector;
- EntryVector d_improvementCandidates;
-
- PrimalResponse primal(uint32_t maxIterations);
- PrimalResponse primalCheck();
- Result::Sat primalConverge(int depth);
- void driveOptToZero(ArithVar unbounded);
- uint32_t contractErrorVariables(bool guaranteedSuccess);
- bool checkForRowConflicts();
- void restoreErrorVariables(ErrorMap& es);
- void constructErrorVariables();
- void constructOptimizationFunction();
- void removeOptimizationFunction();
- void reconstructOptimizationFunction();
- ArithVar selectMinimumValid(ArithVar v, bool increasing);
- ArithVar selectFirstValid(ArithVar v, bool increasing);
-
- void reassertErrorConstraint(ArithVar e, ErrorMap& es, bool definitelyTrue, bool definitelyFalse);
- void computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift);
-
- /** These fields are designed to be accessible to TheoryArith methods. */
- class Statistics {
- public:
- IntStat d_statUpdateConflicts;
-
- TimerStat d_findConflictOnTheQueueTime;
-
- IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch;
- IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch;
- IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch;
- IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch;
- IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch;
-
- IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
- TimerStat d_weakenTime;
-
-
- IntStat d_simplexConflicts;
-
- // Primal stuffs
- TimerStat d_primalTimer;
- TimerStat d_internalTimer;
-
- IntStat d_primalCalls;
- IntStat d_primalSatCalls;
- IntStat d_primalUnsatCalls;
-
- IntStat d_primalPivots;
- IntStat d_primalImprovingPivots;
-
- IntStat d_primalThresholdReachedPivot;
- IntStat d_primalThresholdReachedPivot_dropped;
-
- IntStat d_primalReachedMaxPivots;
- IntStat d_primalReachedMaxPivots_contractMadeProgress;
- IntStat d_primalReachedMaxPivots_checkForConflictWorked;
-
-
- IntStat d_primalGlobalMinimum;
- IntStat d_primalGlobalMinimum_rowConflictWorked;
- IntStat d_primalGlobalMinimum_firstHalfWasSat;
- IntStat d_primalGlobalMinimum_firstHalfWasUnsat;
- IntStat d_primalGlobalMinimum_contractMadeProgress;
-
-
- IntStat d_unboundedFound;
- IntStat d_unboundedFound_drive;
- IntStat d_unboundedFound_dropped;
-
-
- Statistics();
- ~Statistics();
- };
-
- Statistics d_statistics;
-
-};/* class SimplexDecisionProcedure */
-
-}/* CVC4::theory::arith namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */
-
diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp
index 02e49258c..a160f4fe2 100644
--- a/src/theory/arith/simplex.cpp
+++ b/src/theory/arith/simplex.cpp
@@ -130,6 +130,8 @@ void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, Ar
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);
}
@@ -168,6 +170,12 @@ void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, A
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){
TimerStat::CodeTimer codeTimer(timer);
@@ -193,9 +201,10 @@ ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& time
DeltaRational newAssignment = d_linEq.computeRowValue(inf, false);
d_variables.setAssignment(inf, newAssignment);
- d_linEq.trackVariable(inf);
+ //d_linEq.trackVariable(inf);
+ d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf));
- Debug("Inf") << inf << " " << newAssignment << endl;
+ Debug("constructInfeasiblityFunction") << inf << " " << newAssignment << endl;
return inf;
}
@@ -226,7 +235,7 @@ void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int n
}
}
-ArithVar SimplexDecisionProcedure::find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m){
+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);
@@ -234,7 +243,7 @@ ArithVar SimplexDecisionProcedure::find_basic_outside(const sgn_table& sgns, Ari
const ArithVarVec& vec = (*i).second;
for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){
ArithVar curr = *viter;
- if(!m.isMember(curr)){
+ if(inside == m.isMember(curr)){
return curr;
}
}
diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h
index bc47a128a..d646ca889 100644
--- a/src/theory/arith/simplex.h
+++ b/src/theory/arith/simplex.h
@@ -121,6 +121,7 @@ protected:
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:
@@ -197,7 +198,8 @@ protected:
void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic);
void addRowSgns(sgn_table& sgns, ArithVar basic, int norm);
- ArithVar find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m);
+ 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 */
diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h
index 516586568..64aa193dd 100644
--- a/src/theory/arith/simplex_update.h
+++ b/src/theory/arith/simplex_update.h
@@ -318,7 +318,7 @@ private:
}
/**
- * Determines the appropraite WitnessImprovement for the update.
+ * Determines the appropriate WitnessImprovement for the update.
* useBlands breaks ties for degenerate pivots.
*
* This is safe if:
diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp
index 7255d92ef..c0ee7ad20 100644
--- a/src/theory/arith/soi_simplex.cpp
+++ b/src/theory/arith/soi_simplex.cpp
@@ -22,6 +22,8 @@
#include "util/statistics_registry.h"
+#include <algorithm>
+
using namespace std;
namespace CVC4 {
@@ -237,7 +239,7 @@ UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePre
Debug("soi::selectPrimalUpdate")
<< "selectPrimalUpdate " << instance << endl
<< d_soiVar << " " << d_tableau.basicRowLength(d_soiVar)
- << " " << d_linEq._countBounds(d_soiVar) << endl;
+ << " " << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
typedef std::vector<Cand> CandVector;
CandVector candidates;
@@ -284,7 +286,6 @@ UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePre
ArithVar curr = cand.d_nb;
const Rational& coeff = *cand.d_coeff;
-#warning "Who is using computeSafeUpdate?"
LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
@@ -349,7 +350,7 @@ void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
int dir = !d_variables.assignmentIsConsistent(updated) ?
d_errorSet.getSgn(updated) : 0;
Debug("updateAndSignal") << " dir " << dir;
- Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl;
+ Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
}
@@ -367,7 +368,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes
ArithVar leaving = selected.leaving();
ss << "leaving " << leaving
<< " " << d_tableau.basicRowLength(leaving)
- << " " << d_linEq._countBounds(leaving)
+ << " " << d_linEq.debugBasicAtBoundCount(leaving)
<< endl;
}
if(degenerate(w) && selected.describesPivot()){
@@ -376,7 +377,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes
<< "degenerate " << leaving
<< ", atBounds " << d_linEq.basicsAtBounds(selected)
<< ", len " << d_tableau.basicRowLength(leaving)
- << ", bc " << d_linEq._countBounds(leaving)
+ << ", bc " << d_linEq.debugBasicAtBoundCount(leaving)
<< endl;
}
}
@@ -433,6 +434,192 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes
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;
@@ -446,30 +633,6 @@ unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
return success ? set.size() : std::numeric_limits<int>::max();
}
-unsigned SumOfInfeasibilitiesSPD::tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp) {
- if(depth < set.size()){
- unsigned resWithout = tryAllSubsets(set, depth+1, tmp);
- if(resWithout == tmp.size() && resWithout < set.size()){
- for(unsigned i = 0; i < tmp.size(); ++i){
- cout << tmp[i] << " ";
- }
- cout << endl;
- }
- tmp.push_back(set[depth]);
- unsigned resWith = tryAllSubsets(set, depth+1, tmp);
- if(resWith == tmp.size() && resWith < set.size()){
- for(unsigned i = 0; i < tmp.size(); ++i){
- cout << tmp[i] << " ";
- }
- cout << endl;
- }
- tmp.pop_back();
- return std::min(resWith, resWithout);
- }else{
- return trySet(tmp);
- }
-}
-
std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
std::vector< ArithVarVec > subsets;
Assert(d_soiVar == ARITHVAR_SENTINEL);
@@ -547,8 +710,6 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
underConstruction.push_back(v);
d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
- bool uniqueChoices = true;
-
//cout << "trying " << v << endl;
const Tableau::Entry* spoiler = NULL;
@@ -559,7 +720,7 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
//cout << "looking for " << nb << " " << oppositeSgn << endl;
- ArithVar basicWithOp = find_basic_outside(sgns, nb, oppositeSgn, hasParticipated);
+ ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
if(basicWithOp == ARITHVAR_SENTINEL){
//cout << "search did not work for " << nb << endl;
@@ -648,17 +809,26 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
d_soiVar = ARITHVAR_SENTINEL;
- vector<ArithVarVec> subsets = greedyConflictSubsets();
- Assert( d_soiVar == ARITHVAR_SENTINEL);
- Assert(!subsets.empty());
- for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
- const ArithVarVec& subset = *i;
- Node conflict = generateSOIConflict(subset);
+ if(options::soiQuickExplain()){
+ quickExplain();
+ Node conflict = generateSOIConflict(d_qeConflict);
//cout << conflict << endl;
-
- //reportConflict(conf); do not do this. We need a custom explanations!
d_conflictChannel(conflict);
+ }else{
+
+ vector<ArithVarVec> subsets = greedyConflictSubsets();
+ Assert( d_soiVar == ARITHVAR_SENTINEL);
+
+ Assert(!subsets.empty());
+ for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
+ const ArithVarVec& subset = *i;
+ Node conflict = generateSOIConflict(subset);
+ //cout << conflict << endl;
+
+ //reportConflict(conf); do not do this. We need a custom explanations!
+ d_conflictChannel(conflict);
+ }
}
Assert( d_soiVar == ARITHVAR_SENTINEL);
d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
@@ -703,7 +873,7 @@ WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
}
bool SumOfInfeasibilitiesSPD::debugSOI(WitnessImprovement w, ostream& out, int instance) const{
-#warning "Redo SOI"
+//#warning "Redo SOI"
return true;
// out << "DLV("<<instance<<") ";
// switch(w){
diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h
index 006839a55..de565df64 100644
--- a/src/theory/arith/soi_simplex.h
+++ b/src/theory/arith/soi_simplex.h
@@ -195,6 +195,19 @@ private:
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);
diff --git a/src/theory/arith/tableau.cpp b/src/theory/arith/tableau.cpp
index c54b0857a..9d06fadc4 100644
--- a/src/theory/arith/tableau.cpp
+++ b/src/theory/arith/tableau.cpp
@@ -75,7 +75,7 @@ void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCa
d_basic2RowIndex.set(basicNew, rid);
d_rowIndex2basic.set(rid, basicNew);
- cb.swap(basicOld, basicNew, a_rs_sgn);
+ cb.multiplyRow(rid, -a_rs_sgn);
}
diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h
index 8b6ef1df6..deed7e7be 100644
--- a/src/theory/arith/tableau.h
+++ b/src/theory/arith/tableau.h
@@ -72,8 +72,12 @@ public:
return getColumn(x).begin();
}
+ RowIterator ridRowIterator(RowIndex rid) const {
+ return getRow(rid).begin();
+ }
+
RowIterator basicRowIterator(ArithVar basic) const {
- return getRow(basicToRowIndex(basic)).begin();
+ return ridRowIterator(basicToRowIndex(basic));
}
const Entry& basicFindEntry(ArithVar basic, ArithVar col) const {
diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp
index c0442da90..6c7f622ec 100644
--- a/src/theory/arith/theory_arith.cpp
+++ b/src/theory/arith/theory_arith.cpp
@@ -47,6 +47,7 @@ void TheoryArith::addSharedTerm(TNode n){
}
Node TheoryArith::ppRewrite(TNode atom) {
+ CodeTimer timer(d_ppRewriteTimer);
return d_internal->ppRewrite(atom);
}
@@ -86,6 +87,10 @@ EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
return d_internal->getEqualityStatus(a,b);
}
+Node TheoryArith::getModelValue(TNode var) {
+ return d_internal->getModelValue( var );
+}
+
}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h
index 10c79b293..451f1e8ff 100644
--- a/src/theory/arith/theory_arith.h
+++ b/src/theory/arith/theory_arith.h
@@ -42,6 +42,7 @@ private:
TheoryArithPrivate* d_internal;
+ KEEP_STATISTIC(TimerStat, d_ppRewriteTimer, "theory::arith::ppRewriteTimer");
public:
TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
@@ -73,6 +74,8 @@ public:
EqualityStatus getEqualityStatus(TNode a, TNode b);
void addSharedTerm(TNode n);
+
+ Node getModelValue(TNode var);
};/* class TheoryArith */
}/* CVC4::theory::arith namespace */
diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp
index 263f9536b..9d13dccb7 100644
--- a/src/theory/arith/theory_arith_private.cpp
+++ b/src/theory/arith/theory_arith_private.cpp
@@ -67,6 +67,8 @@
#include "theory/arith/options.h"
+#include "theory/quantifiers/bounded_integers.h"
+
#include <stdint.h>
#include <vector>
@@ -83,12 +85,13 @@ namespace arith {
TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) :
d_containing(containing),
d_nlIncomplete( false),
- d_boundTracking(),
+ d_rowTracking(),
d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this)),
d_qflraStatus(Result::SAT_UNKNOWN),
d_unknownsInARow(0),
d_hasDoneWorkSinceCut(false),
d_learner(u),
+ d_quantEngine(qe),
d_assertionsThatDoNotMatchTheirLiterals(c),
d_nextIntegerCheckVar(0),
d_constantIntegerVariables(c),
@@ -96,9 +99,9 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context
d_currentPropagationList(),
d_learnedBounds(c),
d_partialModel(c, DeltaComputeCallback(*this)),
- d_errorSet(d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(&d_boundTracking)),
+ d_errorSet(d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)),
d_tableau(),
- d_linEq(d_partialModel, d_tableau, d_boundTracking, BasicVarModelUpdateCallBack(*this)),
+ d_linEq(d_partialModel, d_tableau, d_rowTracking, BasicVarModelUpdateCallBack(*this)),
d_diosolver(c),
d_restartsCounter(0),
d_tableauSizeHasBeenModified(false),
@@ -107,15 +110,19 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context
d_conflicts(c),
d_congruenceManager(c, d_constraintDatabase, SetupLiteralCallBack(*this), d_partialModel, RaiseConflict(*this)),
d_dualSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
- d_pureUpdate(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
d_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
+ d_attemptSolSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)),
d_DELTA_ZERO(0),
d_fullCheckCounter(0),
d_cutCount(c, 0),
d_cutInContext(c),
+ d_likelyIntegerInfeasible(c, false),
+ d_guessedCoeffSet(c, false),
+ d_guessedCoeffs(),
d_statistics()
{
+ srand(79);
}
TheoryArithPrivate::~TheoryArithPrivate(){ }
@@ -489,9 +496,7 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){
}else if(!lb->negationHasProof()){
Constraint negLb = lb->getNegation();
negLb->impliedBy(constraint, diseq);
- //if(!negLb->canBePropagated()){
d_learnedBounds.push_back(negLb);
- //}//otherwise let this be propagated/asserted later
}
}
}
@@ -732,19 +737,155 @@ void TheoryArithPrivate::addSharedTerm(TNode n){
}
}
+Node TheoryArithPrivate::getModelValue(TNode term) {
+ try{
+ DeltaRational drv = getDeltaValue(term);
+ const Rational& delta = d_partialModel.getDelta();
+ Rational qmodel = drv.substituteDelta( delta );
+ return mkRationalNode( qmodel );
+ } catch (DeltaRationalException& dr) {
+ return Node::null();
+ } catch (ModelException& me) {
+ return Node::null();
+ }
+}
+
+namespace attr {
+ struct ToIntegerTag { };
+ struct LinearIntDivTag { };
+}/* CVC4::theory::arith::attr namespace */
+
+/**
+ * This attribute maps the child of a to_int / is_int to the
+ * corresponding integer skolem.
+ */
+typedef expr::Attribute<attr::ToIntegerTag, Node> ToIntegerAttr;
+
+/**
+ * This attribute maps division-by-constant-k terms to a variable
+ * used to eliminate them.
+ */
+typedef expr::Attribute<attr::LinearIntDivTag, Node> LinearIntDivAttr;
+
+Node TheoryArithPrivate::ppRewriteTerms(TNode n) {
+ if(Theory::theoryOf(n) != THEORY_ARITH) {
+ return n;
+ }
+
+ NodeManager* nm = NodeManager::currentNM();
+
+ switch(Kind k = n.getKind()) {
+
+ case kind::TO_INTEGER:
+ case kind::IS_INTEGER: {
+ Node intVar;
+ if(!n[0].getAttribute(ToIntegerAttr(), intVar)) {
+ intVar = nm->mkSkolem("toInt", nm->integerType(), "a conversion of a Real term to its Integer part");
+ n[0].setAttribute(ToIntegerAttr(), intVar);
+ d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LT, nm->mkNode(kind::MINUS, n[0], nm->mkConst(Rational(1))), intVar), nm->mkNode(kind::LEQ, intVar, n[0])));
+ }
+ if(n.getKind() == kind::TO_INTEGER) {
+ Node node = intVar;
+ return node;
+ } else {
+ Node node = nm->mkNode(kind::EQUAL, n[0], intVar);
+ return node;
+ }
+ Unreachable();
+ }
+
+ case kind::INTS_DIVISION:
+ case kind::INTS_DIVISION_TOTAL: {
+ if(!options::rewriteDivk()) {
+ return n;
+ }
+ Node num = Rewriter::rewrite(n[0]);
+ Node den = Rewriter::rewrite(n[1]);
+ if(den.isConst()) {
+ const Rational& rat = den.getConst<Rational>();
+ Assert(!num.isConst());
+ if(rat != 0) {
+ Node intVar;
+ Node rw = nm->mkNode(k, num, den);
+ if(!rw.getAttribute(LinearIntDivAttr(), intVar)) {
+ intVar = nm->mkSkolem("linearIntDiv", nm->integerType(), "the result of an intdiv-by-k term");
+ rw.setAttribute(LinearIntDivAttr(), intVar);
+ if(rat > 0) {
+ d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1)))))));
+ } else {
+ d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1)))))));
+ }
+ }
+ return intVar;
+ }
+ }
+ break;
+ }
+
+ case kind::INTS_MODULUS:
+ case kind::INTS_MODULUS_TOTAL: {
+ if(!options::rewriteDivk()) {
+ return n;
+ }
+ Node num = Rewriter::rewrite(n[0]);
+ Node den = Rewriter::rewrite(n[1]);
+ if(den.isConst()) {
+ const Rational& rat = den.getConst<Rational>();
+ Assert(!num.isConst());
+ if(rat != 0) {
+ Node intVar;
+ Node rw = nm->mkNode(k, num, den);
+ if(!rw.getAttribute(LinearIntDivAttr(), intVar)) {
+ intVar = nm->mkSkolem("linearIntDiv", nm->integerType(), "the result of an intdiv-by-k term");
+ rw.setAttribute(LinearIntDivAttr(), intVar);
+ if(rat > 0) {
+ d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1)))))));
+ } else {
+ d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1)))))));
+ }
+ }
+ Node node = nm->mkNode(kind::MINUS, num, nm->mkNode(kind::MULT, den, intVar));
+ return node;
+ }
+ }
+ break;
+ }
+
+ default:
+ ;
+ }
+
+ for(TNode::const_iterator i = n.begin(); i != n.end(); ++i) {
+ Node rewritten = ppRewriteTerms(*i);
+ if(rewritten != *i) {
+ NodeBuilder<> b(n.getKind());
+ b.append(n.begin(), i);
+ b << rewritten;
+ for(++i; i != n.end(); ++i) {
+ b << ppRewriteTerms(*i);
+ }
+ rewritten = b;
+ return rewritten;
+ }
+ }
+
+ return n;
+}
+
Node TheoryArithPrivate::ppRewrite(TNode atom) {
Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
-
if (atom.getKind() == kind::EQUAL && options::arithRewriteEq()) {
Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1];
Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1];
+ leq = ppRewriteTerms(leq);
+ geq = ppRewriteTerms(geq);
Node rewritten = Rewriter::rewrite(leq.andNode(geq));
Debug("arith::preprocess") << "arith::preprocess() : returning "
<< rewritten << endl;
return rewritten;
} else {
- return atom;
+ return ppRewriteTerms(atom);
}
}
@@ -897,7 +1038,7 @@ void TheoryArithPrivate::setupVariableList(const VarList& vl){
// vl is the product of at least 2 variables
// vl : (* v1 v2 ...)
if(getLogicInfo().isLinear()){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic.");
}
setIncomplete();
@@ -934,7 +1075,7 @@ void TheoryArithPrivate::setupDivLike(const Variable& v){
Assert(v.isDivLike());
if(getLogicInfo().isLinear()){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ throw LogicException("A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic;\nif you only use division (or modulus) by a constant value, or if you only use the divisibility-by-k predicate, try using the --rewrite-divk option.");
}
Node vnode = v.getNode();
@@ -1100,7 +1241,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) {
ArithVar varSlack = requestArithVar(polyNode, true);
d_tableau.addRow(varSlack, coefficients, variables);
setupBasicValue(varSlack);
- d_linEq.trackVariable(varSlack);
+ d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack));
//Add differences to the difference manager
Polynomial::iterator i = poly.begin(), end = poly.end();
@@ -1156,16 +1297,22 @@ void TheoryArithPrivate::setupAtom(TNode atom) {
void TheoryArithPrivate::preRegisterTerm(TNode n) {
Debug("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl;
- if(isRelationOperator(n.getKind())){
- if(!isSetup(n)){
- setupAtom(n);
+ try {
+ if(isRelationOperator(n.getKind())){
+ if(!isSetup(n)){
+ setupAtom(n);
+ }
+ Constraint c = d_constraintDatabase.lookup(n);
+ Assert(c != NullConstraint);
+
+ Debug("arith::preregister") << "setup constraint" << c << endl;
+ Assert(!c->canBePropagated());
+ c->setPreregistered();
}
- Constraint c = d_constraintDatabase.lookup(n);
- Assert(c != NullConstraint);
-
- Debug("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());
}
Debug("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl;
@@ -1176,82 +1323,33 @@ void TheoryArithPrivate::releaseArithVar(ArithVar v){
d_constraintDatabase.removeVariable(v);
d_partialModel.releaseArithVar(v);
- d_linEq.maybeRemoveTracking(v);
}
ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){
//TODO : The VarList trick is good enough?
Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS);
if(getLogicInfo().isLinear() && Variable::isDivMember(x)){
- throw LogicException("Non-linear term was asserted to arithmetic in a linear logic.");
+ stringstream ss;
+ ss << "A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic: " << x << endl
+ << "if you only use division (or modulus) by a constant value, or if you only use the divisibility-by-k predicate, try using the --rewrite-divk option.";
+ throw LogicException(ss.str());
}
Assert(!d_partialModel.hasArithVar(x));
Assert(x.getType().isReal()); // real or integer
- // ArithVar varX = d_variables.size();
- // d_variables.push_back(Node(x));
-
ArithVar max = d_partialModel.getNumberOfVariables();
ArithVar varX = d_partialModel.allocate(x, slack);
bool reclaim = max >= d_partialModel.getNumberOfVariables();;
- if(reclaim){
- // varX = d_pool.back();
- // d_pool.pop_back();
-
- // d_partialModel.setAssignment(varX, d_DELTA_ZERO, d_DELTA_ZERO);
- }else{
- // varX = d_numberOfVariables;
- // ++d_numberOfVariables;
-
- // d_slackVars.push_back(true);
- // d_variableTypes.push_back(ATReal);
-
+ if(!reclaim){
d_dualSimplex.increaseMax();
d_tableau.increaseSize();
d_tableauSizeHasBeenModified = true;
-
- //d_partialModel.initialize(varX, d_DELTA_ZERO);
}
-
- // ArithType type;
- // if(slack){
- // //The type computation is not quite accurate for Rationals that are integral.
- // //We'll use the isIntegral check from the polynomial package instead.
- // Polynomial p = Polynomial::parsePolynomial(x);
- // type = p.isIntegral() ? ATInteger : ATReal;
- // }else{
- // type = nodeToArithType(x);
- // }
- // d_variableTypes[varX] = type;
- // d_slackVars[varX] = slack;
-
d_constraintDatabase.addVariable(varX);
- //d_partialModel.setArithVar(x,varX);
-
- // Debug("integers") << "isInteger[[" << x << "]]: " << x.getType().isInteger() << endl;
-
- // if(slack){
- // //The type computation is not quite accurate for Rationals that are integral.
- // //We'll use the isIntegral check from the polynomial package instead.
- // Polynomial p = Polynomial::parsePolynomial(x);
- // d_variableTypes.push_back(p.isIntegral() ? ATInteger : ATReal);
- // }else{
- // d_variableTypes.push_back(nodeToArithType(x));
- // }
-
- // d_slackVars.push_back(slack);
-
- // d_simplex.increaseMax();
-
- // d_tableau.increaseSize();
- // d_tableauSizeHasBeenModified = true;
-
- // d_constraintDatabase.addVariable(varX);
-
Debug("arith::arithvar") << x << " |-> " << varX << endl;
Assert(!d_partialModel.hasUpperBound(varX));
@@ -1423,6 +1521,10 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){
Assert(!done());
TNode assertion = get();
+ if( options::finiteModelFind() && d_quantEngine && d_quantEngine->getBoundedIntegers() ){
+ d_quantEngine->getBoundedIntegers()->assertNode(assertion);
+ }
+
Kind simpleKind = Comparison::comparisonKind(assertion);
Constraint constraint = d_constraintDatabase.lookup(assertion);
if(constraint == NullConstraint){
@@ -1454,20 +1556,6 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){
}
}
- // Kind simpleKind = Comparison::comparisonKind(assertion);
- // Assert(simpleKind != UNDEFINED_KIND);
- // Assert(constraint != NullConstraint ||
- // simpleKind == EQUAL ||
- // simpleKind == DISTINCT );
- // if(simpleKind == EQUAL || simpleKind == DISTINCT){
- // Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion;
-
- // if(!isSetup(eq)){
- // //The previous code was equivalent to:
- // setupAtom(eq);
- // constraint = d_constraintDatabase.lookup(assertion);
- // }
- // }
Assert(constraint != NullConstraint);
if(constraint->negationHasProof()){
@@ -1609,9 +1697,9 @@ void TheoryArithPrivate::branchVector(const std::vector<ArithVar>& lemmas){
bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
Assert(d_qflraStatus != Result::SAT);
- d_partialModel.stopQueueingAtBoundQueue();
+ d_partialModel.stopQueueingBoundCounts();
UpdateTrackingCallback utcb(&d_linEq);
- d_partialModel.processAtBoundQueue(utcb);
+ d_partialModel.processBoundsQueue(utcb);
d_linEq.startTrackingBoundCounts();
bool noPivotLimit = Theory::fullEffort(effortLevel) ||
@@ -1641,13 +1729,23 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
static const int32_t relaxationLimit = 10000;
static const int32_t mipLimit = 200000;
+ //cout << "start" << endl;
d_qflraStatus = simplex.findModel(false);
+ //cout << "end" << endl;
if(d_qflraStatus == Result::SAT_UNKNOWN ||
- (d_qflraStatus == Result::SAT && !hasIntegerModel())){
+ (d_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){
ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel);
approxSolver->setPivotLimit(relaxationLimit);
+ if(!d_guessedCoeffSet){
+ d_guessedCoeffs = approxSolver->heuristicOptCoeffs();
+ d_guessedCoeffSet = true;
+ }
+ if(!d_guessedCoeffs.empty()){
+ approxSolver->setOptCoeffs(d_guessedCoeffs);
+ }
+
ApproximateSimplex::ApproxResult relaxRes, mipRes;
ApproximateSimplex::Solution relaxSolution, mipSolution;
relaxRes = approxSolver->solveRelaxation();
@@ -1655,30 +1753,37 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
case ApproximateSimplex::ApproxSat:
{
relaxSolution = approxSolver->extractRelaxation();
- approxSolver->setPivotLimit(mipLimit);
- mipRes = approxSolver->solveMIP();
- d_errorSet.reduceToSignals();
- if(mipRes == ApproximateSimplex::ApproxSat){
- mipSolution = approxSolver->extractMIP();
- ApproximateSimplex::applySolution(d_linEq, mipSolution);
+
+ if(d_likelyIntegerInfeasible){
+ d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution);
}else{
- ApproximateSimplex::applySolution(d_linEq, relaxSolution);
- // if(d_qflraStatus != UNSAT){
- // d_likelyIntegerUnsat = true;
- // }
+ approxSolver->setPivotLimit(mipLimit);
+ mipRes = approxSolver->solveMIP();
+ d_errorSet.reduceToSignals();
+ //Message() << "here" << endl;
+ if(mipRes == ApproximateSimplex::ApproxSat){
+ mipSolution = approxSolver->extractMIP();
+ d_qflraStatus = d_attemptSolSimplex.attempt(mipSolution);
+ }else{
+ if(mipRes == ApproximateSimplex::ApproxUnsat){
+ d_likelyIntegerInfeasible = true;
+ }
+ d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution);
+ }
}
options::arithStandardCheckVarOrderPivots.set(pass2Limit);
- d_qflraStatus = simplex.findModel(false);
+ if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); }
+ //Message() << "done" << endl;
}
break;
case ApproximateSimplex::ApproxUnsat:
{
ApproximateSimplex::Solution sol = approxSolver->extractRelaxation();
- d_errorSet.reduceToSignals();
- ApproximateSimplex::applySolution(d_linEq, sol);
- options::arithStandardCheckVarOrderPivots.set(100);
- d_qflraStatus = simplex.findModel(false);
+ d_qflraStatus = d_attemptSolSimplex.attempt(sol);
+ options::arithStandardCheckVarOrderPivots.set(pass2Limit);
+
+ if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); }
}
break;
default:
@@ -1688,11 +1793,14 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
}
if(d_qflraStatus == Result::SAT_UNKNOWN){
+ //Message() << "got sat unknown" << endl;
vector<ArithVar> toCut = cutAllBounded();
if(toCut.size() > 0){
branchVector(toCut);
emmittedConflictOrSplit = true;
}else{
+ //Message() << "splitting" << endl;
+
d_qflraStatus = simplex.findModel(noPivotLimit);
}
}
@@ -1701,7 +1809,7 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){
// TODO Save zeroes with no conflicts
d_linEq.stopTrackingBoundCounts();
- d_partialModel.startQueueingAtBoundQueue();
+ d_partialModel.startQueueingBoundCounts();
return emmittedConflictOrSplit;
}
@@ -1765,7 +1873,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){
if(Debug.isOn("arith::print_assertions")) {
- debugPrintAssertions();
+ debugPrintAssertions(Debug("arith::print_assertions"));
}
bool emmittedConflictOrSplit = false;
@@ -1911,7 +2019,6 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){
if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){
emmittedConflictOrSplit = splitDisequalities();
}
- emmittedConflictOrSplit = false;
if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){
Node possibleConflict = Node::null();
@@ -1967,7 +2074,9 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){
}
if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
- if(Debug.isOn("arith::print_model")) { debugPrintModel(); }
+ if(Debug.isOn("arith::print_model")) {
+ debugPrintModel(Debug("arith::print_model"));
+ }
Debug("arith") << "TheoryArithPrivate::check end" << std::endl;
}
@@ -2088,37 +2197,38 @@ bool TheoryArithPrivate::splitDisequalities(){
* Should be guarded by at least Debug.isOn("arith::print_assertions").
* Prints to Debug("arith::print_assertions")
*/
-void TheoryArithPrivate::debugPrintAssertions() {
- Debug("arith::print_assertions") << "Assertions:" << endl;
+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)) {
Constraint lConstr = d_partialModel.getLowerBoundConstraint(i);
- Debug("arith::print_assertions") << lConstr << endl;
+ out << lConstr << endl;
}
if (d_partialModel.hasUpperBound(i)) {
Constraint uConstr = d_partialModel.getUpperBoundConstraint(i);
- Debug("arith::print_assertions") << uConstr << endl;
+ out << uConstr << endl;
}
}
context::CDQueue<Constraint>::const_iterator it = d_diseqQueue.begin();
context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end();
for(; it != it_end; ++ it) {
- Debug("arith::print_assertions") << *it << endl;
+ out << *it << endl;
}
}
-void TheoryArithPrivate::debugPrintModel(){
- Debug("arith::print_model") << "Model:" << 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)){
- Debug("arith::print_model") << d_partialModel.asNode(i) << " : " <<
+ out << d_partialModel.asNode(i) << " : " <<
d_partialModel.getAssignment(i);
- if(d_tableau.isBasic(i))
- Debug("arith::print_model") << " (basic)";
- Debug("arith::print_model") << endl;
+ if(d_tableau.isBasic(i)){
+ out << " (basic)";
+ }
+ out << endl;
}
}
}
@@ -2161,7 +2271,11 @@ void TheoryArithPrivate::propagate(Theory::Effort e) {
(options::arithPropagationMode() == BOUND_INFERENCE_PROP ||
options::arithPropagationMode() == BOTH_PROP)
&& hasAnyUpdates()){
- propagateCandidates();
+ if(options::newProp()){
+ propagateCandidatesNew();
+ }else{
+ propagateCandidates();
+ }
}else{
clearUpdates();
}
@@ -2432,37 +2546,6 @@ void TheoryArithPrivate::notifyRestart(){
if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); }
++d_restartsCounter;
-#warning "removing restart"
- // return;
-
- // uint32_t currSize = d_tableau.size();
- // uint32_t copySize = d_smallTableauCopy.size();
-
- // Debug("arith::reset") << "resetting" << d_restartsCounter << endl;
- // Debug("arith::reset") << "curr " << currSize << " copy " << copySize << endl;
- // Debug("arith::reset") << "tableauSizeHasBeenModified " << d_tableauSizeHasBeenModified << endl;
-
- // if(d_tableauSizeHasBeenModified){
- // Debug("arith::reset") << "row has been added must copy " << d_restartsCounter << endl;
- // d_smallTableauCopy = d_tableau;
- // d_tableauSizeHasBeenModified = false;
- // }else if( d_restartsCounter >= RESET_START){
- // if(copySize >= currSize * 1.1 ){
- // Debug("arith::reset") << "size has shrunk " << d_restartsCounter << endl;
- // ++d_statistics.d_smallerSetToCurr;
- // d_smallTableauCopy = d_tableau;
- // }else if(d_tableauResetDensity * copySize <= currSize){
- // d_errorSet.popAllSignals();
- // if(safeToReset()){
- // Debug("arith::reset") << "resetting " << d_restartsCounter << endl;
- // ++d_statistics.d_currSetToSmaller;
- // d_tableau = d_smallTableauCopy;
- // }else{
- // Debug("arith::reset") << "not safe to reset at the moment " << d_restartsCounter << endl;
- // }
- // }
- // }
- // Assert(unenqueuedVariablesAreConsistent());
}
bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
@@ -2478,6 +2561,14 @@ bool TheoryArithPrivate::entireStateIsConsistent(const string& s){
}
Warning() << 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() << endl;
+ result = false;
}
}
return result;
@@ -2570,9 +2661,8 @@ EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) {
bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){
++d_statistics.d_boundComputations;
- DeltaRational bound = upperBound ?
- d_linEq.computeUpperBound(basic):
- d_linEq.computeLowerBound(basic);
+ 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))){
@@ -2620,28 +2710,44 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound
}
if(!assertedToTheTheory && canBePropagated && !hasProof ){
- if(upperBound){
- Assert(bestImplied != d_partialModel.getUpperBoundConstraint(basic));
- d_linEq.propagateNonbasicsUpperBound(basic, bestImplied);
- }else{
- Assert(bestImplied != d_partialModel.getLowerBoundConstraint(basic));
- d_linEq.propagateNonbasicsLowerBound(basic, bestImplied);
- }
+ d_linEq.propagateBasicFromRow(bestImplied);
// I think this can be skipped if canBePropagated is true
//d_learnedBounds.push(bestImplied);
+ if(Debug.isOn("arith::prop")){
+ Debug("arith::prop") << "success " << bestImplied << endl;
+ d_partialModel.printModel(basic, Debug("arith::prop"));
+ }
return true;
}
+ if(Debug.isOn("arith::prop")){
+ Debug("arith::prop") << "failed " << basic << " " << bound << assertedToTheTheory << " " <<
+ canBePropagated << " " << hasProof << endl;
+ d_partialModel.printModel(basic, Debug("arith::prop"));
+ }
}
+ }else if(Debug.isOn("arith::prop")){
+ Debug("arith::prop") << "false " << bound << " ";
+ d_partialModel.printModel(basic, Debug("arith::prop"));
}
return false;
}
void TheoryArithPrivate::propagateCandidate(ArithVar basic){
bool success = false;
- if(d_partialModel.strictlyAboveLowerBound(basic) && d_linEq.hasLowerBounds(basic)){
+ 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(d_partialModel.strictlyBelowUpperBound(basic) && d_linEq.hasUpperBounds(basic)){
+ if(tryUpperBound){
success |= propagateCandidateUpperBound(basic);
}
if(success){
@@ -2652,6 +2758,8 @@ void TheoryArithPrivate::propagateCandidate(ArithVar basic){
void TheoryArithPrivate::propagateCandidates(){
TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime);
+ Debug("arith::prop") << "propagateCandidates begin" << endl;
+
Assert(d_candidateBasics.empty());
if(d_updatedBounds.empty()){ return; }
@@ -2685,6 +2793,292 @@ void TheoryArithPrivate::propagateCandidates(){
Assert(d_tableau.isBasic(candidate));
propagateCandidate(candidate);
}
+ Debug("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);
+ Debug("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);
+ }
+ Debug("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;
+ }
+
+ Constraint 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){
+ Debug("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));
+
+ Debug("arith::prop") << " " << rowUp << " " << v << " " << coeff << " " << vUp << endl;
+ Debug("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){
+ Debug("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;
+
+ if(isInteger(v)){
+ //cout << "maybe" << endl;
+ //cout << bound << endl;
+ }
+ Constraint 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);
+ 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) {
+ nb << (*i).negate();
+ }
+ }else{
+ nb << left.negate();
+ }
+
+ if(right.getKind() == kind::OR){
+ for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) {
+ nb << *i;
+ }
+ }else{
+ nb << right;
+ }
+
+ return nb;
+}
+
+bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint implied){
+ Assert(implied != NullConstraint);
+ ArithVar v = implied->getVariable();
+
+ bool assertedToTheTheory = implied->assertedToTheTheory();
+ bool canBePropagated = implied->canBePropagated();
+ bool hasProof = implied->hasProof();
+
+ Debug("arith::prop") << "arith::prop" << v
+ << " " << assertedToTheTheory
+ << " " << canBePropagated
+ << " " << hasProof
+ << endl;
+
+ if(implied->negationHasProof()){
+ Warning() << "the negation of " << implied << " : " << endl
+ << "has proof " << implied->getNegation() << endl
+ << implied->getNegation()->explainForConflict() << endl;
+ }
+
+ if(!assertedToTheTheory && canBePropagated && !hasProof ){
+ vector<Constraint> explain;
+ d_linEq.propagateRow(explain, ridx, rowUp, implied);
+ if(d_tableau.getRowLength(ridx) <= options::arithPropAsLemmaLength()){
+ Node implication = implied->makeImplication(explain);
+ Node clause = flattenImplication(implication);
+ outputLemma(clause);
+ }else{
+ implied->impliedBy(explain);
+ }
+ return true;
+ }
+
+ if(Debug.isOn("arith::prop")){
+ Debug("arith::prop")
+ << "failed " << v << " " << assertedToTheTheory << " "
+ << canBePropagated << " " << hasProof << " " << implied << endl;
+ d_partialModel.printModel(v, Debug("arith::prop"));
+ }
+ return false;
+}
+
+double fRand(double fMin, double fMax)
+{
+ double f = (double)rand() / RAND_MAX;
+ return fMin + f * (fMax - fMin);
+}
+
+bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){
+ BoundCounts hasCount = d_linEq.hasBoundCount(ridx);
+ uint32_t rowLength = d_tableau.getRowLength(ridx);
+
+ bool success = false;
+ static int instance = 0;
+ ++instance;
+
+ Debug("arith::prop")
+ << "propagateCandidateRow " << instance << " attempt " << rowLength << " " << hasCount << endl;
+
+ if(rowLength >= options::arithPropagateMaxLength()){
+ if(fRand(0.0,1.0) >= double(options::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];
}
}/* CVC4::theory::arith namespace */
diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h
index 7b37a813f..22fc8d4a7 100644
--- a/src/theory/arith/theory_arith_private.h
+++ b/src/theory/arith/theory_arith_private.h
@@ -58,7 +58,7 @@
#include "theory/arith/dual_simplex.h"
#include "theory/arith/fc_simplex.h"
#include "theory/arith/soi_simplex.h"
-#include "theory/arith/pure_update_simplex.h"
+#include "theory/arith/attempt_solution_simplex.h"
#include "theory/arith/constraint.h"
@@ -105,7 +105,7 @@ private:
// TODO A better would be:
//context::CDO<bool> d_nlIncomplete;
- BoundCountingVector d_boundTracking;
+ BoundInfoMap d_rowTracking;
/**
* The constraint database associated with the theory.
@@ -132,7 +132,8 @@ private:
/** Static learner. */
ArithStaticLearner d_learner;
-
+ /** quantifiers engine */
+ QuantifiersEngine * d_quantEngine;
//std::vector<ArithVar> d_pool;
public:
void releaseArithVar(ArithVar v);
@@ -316,9 +317,9 @@ private:
/** This implements the Simplex decision procedure. */
DualSimplexDecisionProcedure d_dualSimplex;
- PureUpdateSimplexDecisionProcedure d_pureUpdate;
FCSimplexDecisionProcedure d_fcSimplex;
SumOfInfeasibilitiesSPD d_soiSimplex;
+ AttemptSolutionSDP d_attemptSolSimplex;
bool solveRealRelaxation(Theory::Effort effortLevel);
@@ -345,7 +346,8 @@ private:
Node axiomIteForTotalDivision(Node div_tot);
Node axiomIteForTotalIntDivision(Node int_div_like);
-
+ // handle linear /, div, mod, and also is_int, to_int
+ Node ppRewriteTerms(TNode atom);
public:
TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
@@ -381,6 +383,8 @@ public:
void addSharedTerm(TNode n);
+ Node getModelValue(TNode var);
+
private:
/** The constant zero. */
@@ -430,6 +434,10 @@ public:
*/
ArithVar requestArithVar(TNode x, bool slack);
+public:
+ const BoundsInfo& boundsInfo(ArithVar basic) const;
+
+
private:
/** Initial (not context dependent) sets up for a variable.*/
void setupBasicValue(ArithVar x);
@@ -463,12 +471,25 @@ private:
/** 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, Constraint bestImplied);
+
+
void propagateCandidates();
void propagateCandidate(ArithVar basic);
bool propagateCandidateBound(ArithVar basic, bool upperBound);
@@ -517,9 +538,9 @@ private:
std::vector<ArithVar>& variables);
/** Routine for debugging. Print the assertions the theory is aware of. */
- void debugPrintAssertions();
+ void debugPrintAssertions(std::ostream& out) const;
/** Debugging only routine. Prints the model. */
- void debugPrintModel();
+ void debugPrintModel(std::ostream& out) const;
inline LogicInfo getLogicInfo() const { return d_containing.getLogicInfo(); }
inline bool done() const { return d_containing.done(); }
@@ -553,6 +574,12 @@ private:
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;
+
/** These fields are designed to be accessible to TheoryArith methods. */
class Statistics {
public:
diff --git a/src/theory/arith/theory_arith_type_rules.h b/src/theory/arith/theory_arith_type_rules.h
index cc8451f8b..45e18fe0d 100644
--- a/src/theory/arith/theory_arith_type_rules.h
+++ b/src/theory/arith/theory_arith_type_rules.h
@@ -62,12 +62,37 @@ public:
}
}
}
- Kind k = n.getKind();
- bool isDivision = k == kind::DIVISION || k == kind::DIVISION_TOTAL;
- return (isInteger && !isDivision ? integerType : realType);
+ switch(Kind k = n.getKind()) {
+ case kind::TO_REAL:
+ return realType;
+ case kind::TO_INTEGER:
+ return integerType;
+ default: {
+ bool isDivision = k == kind::DIVISION || k == kind::DIVISION_TOTAL;
+ return (isInteger && !isDivision ? integerType : realType);
+ }
+ }
}
};/* class ArithOperatorTypeRule */
+class IntOperatorTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator child_it = n.begin();
+ TNode::iterator child_it_end = n.end();
+ if(check) {
+ for(; child_it != child_it_end; ++child_it) {
+ TypeNode childType = (*child_it).getType(check);
+ if (!childType.isInteger()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting an integer subterm");
+ }
+ }
+ }
+ return nodeManager->integerType();
+ }
+};/* class IntOperatorTypeRule */
+
class ArithPredicateTypeRule {
public:
inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
@@ -87,6 +112,34 @@ public:
}
};/* class ArithPredicateTypeRule */
+class ArithUnaryPredicateTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ if( check ) {
+ TypeNode t = n[0].getType(check);
+ if (!t.isReal()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting an arithmetic term");
+ }
+ }
+ return nodeManager->booleanType();
+ }
+};/* class ArithUnaryPredicateTypeRule */
+
+class IntUnaryPredicateTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ if( check ) {
+ TypeNode t = n[0].getType(check);
+ if (!t.isInteger()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting an integer term");
+ }
+ }
+ return nodeManager->booleanType();
+ }
+};/* class IntUnaryPredicateTypeRule */
+
class SubrangeProperties {
public:
inline static Cardinality computeCardinality(TypeNode type) {
diff --git a/src/theory/arrays/Makefile.am b/src/theory/arrays/Makefile.am
index ec834522f..77f102cf8 100644
--- a/src/theory/arrays/Makefile.am
+++ b/src/theory/arrays/Makefile.am
@@ -16,9 +16,7 @@ libarrays_la_SOURCES = \
array_info.h \
array_info.cpp \
static_fact_manager.h \
- static_fact_manager.cpp \
- theory_arrays_model.h \
- theory_arrays_model.cpp
+ static_fact_manager.cpp
EXTRA_DIST = \
kinds
diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp
index 801893107..98346d0e3 100644
--- a/src/theory/arrays/theory_arrays.cpp
+++ b/src/theory/arrays/theory_arrays.cpp
@@ -21,7 +21,6 @@
#include <map>
#include "theory/rewriter.h"
#include "expr/command.h"
-#include "theory/arrays/theory_arrays_model.h"
#include "theory/model.h"
#include "theory/arrays/options.h"
#include "smt/logic_exception.h"
@@ -464,7 +463,9 @@ void TheoryArrays::preRegisterTermInternal(TNode node)
}
case kind::STORE: {
// Invariant: array terms should be preregistered before being added to the equality engine
- Assert(!d_equalityEngine.hasTerm(node));
+ if (d_equalityEngine.hasTerm(node)) {
+ break;
+ }
d_equalityEngine.addTriggerTerm(node, THEORY_ARRAY);
TNode a = d_equalityEngine.getRepresentative(node[0]);
@@ -493,7 +494,7 @@ void TheoryArrays::preRegisterTermInternal(TNode node)
}
case kind::STORE_ALL: {
throw LogicException("Array theory solver does not yet support assertions using constant array value");
- }
+ }
default:
// Variables etc
if (node.getType().isArray()) {
@@ -588,7 +589,10 @@ void TheoryArrays::computeCareGraph()
}
}
}
- if (options::arraysModelBased()) return;
+ if (options::arraysModelBased()) {
+ checkModel(EFFORT_COMBINATION);
+ return;
+ }
if (d_sharedTerms) {
vector< pair<TNode, TNode> > currentPairs;
@@ -1009,7 +1013,7 @@ void TheoryArrays::checkModel(Effort e)
Assert(d_skolemAssertions.empty());
Assert(d_lemmas.empty());
- if (fullEffort(e)) {
+ if (combination(e)) {
// Add constraints for shared terms
context::CDList<TNode>::const_iterator shared_it = shared_terms_begin(), shared_it_end = shared_terms_end(), shared_it2;
Node modelVal, modelVal2, d;
@@ -1061,9 +1065,10 @@ void TheoryArrays::checkModel(Effort e)
unsigned constraintIdx;
Node assertion, assertionToCheck;
vector<TNode> assumptions;
- // int numrestarts = 0;
- while (true) {
- // ++numrestarts;
+ int numrestarts = 0;
+ while (true || numrestarts < 1 || fullEffort(e) || combination(e)) {
+ ++numrestarts;
+ d_out->safePoint();
int level = getSatContext()->getLevel();
d_getModelValCache.clear();
for (constraintIdx = 0; constraintIdx < d_modelConstraints.size(); ++constraintIdx) {
@@ -1076,7 +1081,7 @@ void TheoryArrays::checkModel(Effort e)
if (constraintIdx == d_modelConstraints.size()) {
break;
}
-
+
if (assertion.getKind() == kind::EQUAL && assertion[0].getType().isArray()) {
assertionToCheck = solveWrite(expandStores(assertion[0], assumptions).eqNode(expandStores(assertion[1], assumptions)), true, true, false);
if (assertionToCheck.getKind() == kind::AND &&
@@ -1202,20 +1207,20 @@ void TheoryArrays::checkModel(Effort e)
}
{
// generate lemma
- // if (all.size() == 0) {
- // d_lemmas.push_back(decision.negate());
- // }
- // else {
- // NodeBuilder<> disjunction(kind::OR);
- // std::set<TNode>::const_iterator it = all.begin();
- // std::set<TNode>::const_iterator it_end = all.end();
- // while (it != it_end) {
- // disjunction << (*it).negate();
- // ++it;
- // }
- // disjunction << decision.negate();
- // d_lemmas.push_back(disjunction);
- // }
+ if (all.size() == 0) {
+ d_lemmas.push_back(decision.negate());
+ }
+ else {
+ NodeBuilder<> disjunction(kind::OR);
+ std::set<TNode>::const_iterator it = all.begin();
+ std::set<TNode>::const_iterator it_end = all.end();
+ while (it != it_end) {
+ disjunction << (*it).negate();
+ ++it;
+ }
+ disjunction << decision.negate();
+ d_lemmas.push_back(disjunction);
+ }
}
d_equalityEngine.assertEquality(decision, eq, explanation);
if (!eq) decision = decision.notNode();
@@ -1268,6 +1273,13 @@ void TheoryArrays::checkModel(Effort e)
}
d_skolemIndex = d_skolemIndex + 1;
}
+ // Reregister stores
+ if (assertionToCheck != assertion &&
+ assertionToCheck.getKind() == kind::AND &&
+ assertionToCheck[assertionToCheck.getNumChildren()-1].getKind() == kind::EQUAL) {
+ TNode s = assertionToCheck[assertionToCheck.getNumChildren()-1][0];
+ preRegisterStores(s);
+ }
}
if (d_conflict) {
break;
@@ -1294,10 +1306,10 @@ void TheoryArrays::checkModel(Effort e)
d_skolemIndex = 0;
while (!d_lemmas.empty()) {
Debug("arrays-model-based") << "Sending lemma: " << d_lemmas.back() << endl;
- d_out->lemma(d_lemmas.back());
+ d_out->splitLemma(d_lemmas.back());
#ifdef CVC4_ASSERTIONS
- Assert(d_lemmasSaved.find(d_lemmas.back()) == d_lemmasSaved.end());
- d_lemmasSaved.insert(d_lemmas.back());
+ // Assert(d_lemmasSaved.find(d_lemmas.back()) == d_lemmasSaved.end());
+ // d_lemmasSaved.insert(d_lemmas.back());
#endif
d_lemmas.pop_back();
}
@@ -1416,7 +1428,7 @@ Node TheoryArrays::getModelValRec(TNode node)
}
++d_numGetModelValConflicts;
getSatContext()->pop();
- }
+ }
++te;
if (te.isFinished()) {
Assert(false);
@@ -1453,7 +1465,7 @@ bool TheoryArrays::hasLoop(TNode node, TNode target)
return true;
}
}
-
+
return false;
}
@@ -1633,9 +1645,14 @@ bool TheoryArrays::setModelVal(TNode node, TNode val, bool invert, bool explain,
return true;
}
}
- getSatContext()->push();
Node d = node.eqNode(val);
- d_decisions.push_back(invert ? d.notNode() : d);
+ Node r = Rewriter::rewrite(d);
+ if (r.isConst()) {
+ d_equalityEngine.assertEquality(d, r == d_true, d_true);
+ return ((r == d_true) == (!invert));
+ }
+ getSatContext()->push();
+ d_decisions.push_back(invert ? d.negate() : d);
d_equalityEngine.assertEquality(d, !invert, d_decisions.back());
Debug("arrays-model-based") << "Asserting " << d_decisions.back() << " with explanation " << d_decisions.back() << endl;
++d_numSetModelValSplits;
@@ -1667,7 +1684,7 @@ bool TheoryArrays::setModelVal(TNode node, TNode val, bool invert, bool explain,
d_decisions.pop_back();
d_permRef.push_back(explanation);
d = d.negate();
- Debug("arrays-model-based") << "Asserting learned literal " << d << " with explanation " << explanation << endl;
+ Debug("arrays-model-based") << "Asserting learned literal2 " << d << " with explanation " << explanation << endl;
bool eq = true;
if (d.getKind() == kind::NOT) {
d = d[0];
diff --git a/src/theory/arrays/theory_arrays_model.cpp b/src/theory/arrays/theory_arrays_model.cpp
deleted file mode 100644
index b5c81ef69..000000000
--- a/src/theory/arrays/theory_arrays_model.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/********************* */
-/*! \file theory_arrays_model.cpp
- ** \verbatim
- ** Original author: Andrew Reynolds
- ** Major contributors: Morgan Deters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2013 New York University and The University of Iowa
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief Implementation of theory_arrays_model class
- **/
-
-#include "theory/theory_engine.h"
-#include "theory/arrays/theory_arrays_model.h"
-#include "theory/model.h"
-
-using namespace std;
-using namespace CVC4;
-using namespace CVC4::kind;
-using namespace CVC4::context;
-using namespace CVC4::theory;
-using namespace CVC4::theory::arrays;
-
-ArrayModel::ArrayModel( Node arr, TheoryModel* m ) : d_arr( arr ){
- d_base_arr = arr;
- while( d_base_arr.getKind()==STORE ){
- Node ri = m->getRepresentative( d_base_arr[1] );
- if( d_values.find( ri )==d_values.end() ){
- d_values[ ri ] = m->getRepresentative( d_base_arr[2] );
- }
- d_base_arr = d_base_arr[0];
- }
-}
-
-Node ArrayModel::getValue( TheoryModel* m, Node i ){
- i = m->getRepresentative( i );
- std::map< Node, Node >::iterator it = d_values.find( i );
- if( it!=d_values.end() ){
- return it->second;
- }else{
- return NodeManager::currentNM()->mkNode( SELECT, getArrayValue(), i );
- //return d_default_value; //TODO: guarantee I can return this here
- }
-}
-
-void ArrayModel::setValue( TheoryModel* m, Node i, Node e ){
- Node ri = m->getRepresentative( i );
- if( d_values.find( ri )==d_values.end() ){
- d_values[ ri ] = m->getRepresentative( e );
- }
-}
-
-void ArrayModel::setDefaultArray( Node arr ){
- d_base_arr = arr;
-}
-
-Node ArrayModel::getArrayValue(){
- Node curr = d_base_arr;
- for( std::map< Node, Node >::iterator it = d_values.begin(); it != d_values.end(); ++it ){
- curr = NodeManager::currentNM()->mkNode( STORE, curr, it->first, it->second );
- }
- return curr;
-}
diff --git a/src/theory/arrays/theory_arrays_model.h b/src/theory/arrays/theory_arrays_model.h
deleted file mode 100644
index 66dc80568..000000000
--- a/src/theory/arrays/theory_arrays_model.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/********************* */
-/*! \file theory_arrays_model.h
- ** \verbatim
- ** Original author: Andrew Reynolds
- ** Major contributors: Morgan Deters
- ** Minor contributors (to current version): none
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2013 New York University and The University of Iowa
- ** See the file COPYING in the top-level source directory for licensing
- ** information.\endverbatim
- **
- ** \brief MODEL for theory of arrays
- **/
-
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY_ARRAYS_MODEL_H
-#define __CVC4__THEORY_ARRAYS_MODEL_H
-
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-
-class TheoryModel;
-
-namespace arrays {
-
-class ArrayModel{
-protected:
- /** the array this model is for */
- Node d_arr;
-public:
- ArrayModel(){}
- ArrayModel( Node arr, TheoryModel* m );
- ~ArrayModel() {}
-public:
- /** pre-defined values */
- std::map< Node, Node > d_values;
- /** base array */
- Node d_base_arr;
- /** get value, return arguments that the value depends on */
- Node getValue( TheoryModel* m, Node i );
- /** set value */
- void setValue( TheoryModel* m, Node i, Node e );
- /** set default */
- void setDefaultArray( Node arr );
-public:
- /** get array value */
- Node getArrayValue();
-};/* class ArrayModel */
-
-}
-}
-}
-
-#endif \ No newline at end of file
diff --git a/src/theory/arrays/theory_arrays_rewriter.h b/src/theory/arrays/theory_arrays_rewriter.h
index 18bbef8cf..5df06bda8 100644
--- a/src/theory/arrays/theory_arrays_rewriter.h
+++ b/src/theory/arrays/theory_arrays_rewriter.h
@@ -36,6 +36,10 @@ namespace attr {
typedef expr::Attribute<attr::ArrayConstantMostFrequentValueCountTag, uint64_t> ArrayConstantMostFrequentValueCountAttr;
typedef expr::Attribute<attr::ArrayConstantMostFrequentValueTag, Node> ArrayConstantMostFrequentValueAttr;
+static inline Node mkEqNode(Node a, Node b) {
+ return a.getType().isBoolean() ? a.iffNode(b) : a.eqNode(b);
+}
+
class TheoryArraysRewriter {
static Node normalizeConstant(TNode node) {
return normalizeConstant(node, node[1].getType().getCardinality());
@@ -244,7 +248,7 @@ public:
val = false;
}
else {
- n = Rewriter::rewrite(store[1].eqNode(index));
+ n = Rewriter::rewrite(mkEqNode(store[1], index));
if (n.getKind() != kind::CONST_BOOLEAN) {
break;
}
@@ -301,7 +305,7 @@ public:
val = false;
}
else {
- Node eqRewritten = Rewriter::rewrite(store[1].eqNode(index));
+ Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index));
if (eqRewritten.getKind() != kind::CONST_BOOLEAN) {
Trace("arrays-postrewrite") << "Arrays::postRewrite returning " << node << std::endl;
return RewriteResponse(REWRITE_DONE, node);
@@ -340,7 +344,7 @@ public:
val = false;
}
else {
- n = Rewriter::rewrite(store[1].eqNode(index));
+ n = Rewriter::rewrite(mkEqNode(store[1], index));
if (n.getKind() != kind::CONST_BOOLEAN) {
break;
}
@@ -416,7 +420,7 @@ public:
val = false;
}
else {
- n = Rewriter::rewrite(store[1].eqNode(index));
+ n = Rewriter::rewrite(mkEqNode(store[1], index));
if (n.getKind() != kind::CONST_BOOLEAN) {
break;
}
@@ -466,7 +470,7 @@ public:
val = false;
}
else {
- Node eqRewritten = Rewriter::rewrite(store[1].eqNode(index));
+ Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index));
if (eqRewritten.getKind() != kind::CONST_BOOLEAN) {
break;
}
diff --git a/src/theory/atom_requests.cpp b/src/theory/atom_requests.cpp
new file mode 100644
index 000000000..3d111f9f8
--- /dev/null
+++ b/src/theory/atom_requests.cpp
@@ -0,0 +1,62 @@
+#include "theory/atom_requests.h"
+
+using namespace CVC4;
+
+AtomRequests::AtomRequests(context::Context* context)
+: d_allRequests(context)
+, d_requests(context)
+, d_triggerToRequestMap(context)
+{}
+
+AtomRequests::element_index AtomRequests::getList(TNode trigger) const {
+ trigger_to_list_map::const_iterator find = d_triggerToRequestMap.find(trigger);
+ if (find == d_triggerToRequestMap.end()) {
+ return null_index;
+ } else {
+ return (*find).second;
+ }
+}
+
+bool AtomRequests::isTrigger(TNode atom) const {
+ return getList(atom) != null_index;
+}
+
+AtomRequests::atom_iterator AtomRequests::getAtomIterator(TNode trigger) const {
+ return atom_iterator(*this, getList(trigger));
+}
+
+void AtomRequests::add(TNode triggerAtom, TNode atomToSend, theory::TheoryId toTheory) {
+
+ Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << ")" << std::endl;
+
+ Request request(atomToSend, toTheory);
+
+ if (d_allRequests.find(request) != d_allRequests.end()) {
+ // Have it already
+ Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << "): already there" << std::endl;
+ return;
+ }
+ Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << "): adding" << std::endl;
+
+ /// Mark the new request
+ d_allRequests.insert(request);
+
+ // Index of the new request in the list of trigger
+ element_index index = d_requests.size();
+ element_index previous = getList(triggerAtom);
+ d_requests.push_back(Element(request, previous));
+ d_triggerToRequestMap[triggerAtom] = index;
+}
+
+bool AtomRequests::atom_iterator::done() const {
+ return index == null_index;
+}
+
+void AtomRequests::atom_iterator::next() {
+ index = requests.d_requests[index].previous;
+}
+
+const AtomRequests::Request& AtomRequests::atom_iterator::get() const {
+ return requests.d_requests[index].request;
+}
+
diff --git a/src/theory/atom_requests.h b/src/theory/atom_requests.h
new file mode 100644
index 000000000..99878125a
--- /dev/null
+++ b/src/theory/atom_requests.h
@@ -0,0 +1,107 @@
+#pragma once
+
+#include "expr/node.h"
+#include "theory/theory.h"
+#include "context/cdlist.h"
+#include "context/cdhashset.h"
+#include "context/cdhashmap.h"
+
+namespace CVC4 {
+
+class AtomRequests {
+
+public:
+
+ /** Which atom and where to send it */
+ struct Request {
+ /** Atom */
+ Node atom;
+ /** Where to send it */
+ theory::TheoryId toTheory;
+
+ Request(TNode atom, theory::TheoryId toTheory)
+ : atom(atom), toTheory(toTheory) {}
+ Request()
+ : toTheory(theory::THEORY_LAST)
+ {}
+
+ bool operator == (const Request& other) const {
+ return atom == other.atom && toTheory == other.toTheory;
+ }
+
+ size_t hash() const {
+ return atom.getId();
+ }
+
+ };
+
+ AtomRequests(context::Context* context);
+
+ /** Mark the atom to be sent to a theory, when the trigger atom gets assigned */
+ void add(TNode triggerAtom, TNode atomToSend, theory::TheoryId toTheory);
+
+ /** Returns true if the node is a trigger and has a list of atoms to send */
+ bool isTrigger(TNode atom) const;
+
+ /** Indices in lists */
+ typedef size_t element_index;
+
+ class atom_iterator {
+ const AtomRequests& requests;
+ element_index index;
+ friend class AtomRequests;
+ atom_iterator(const AtomRequests& requests, element_index start)
+ : requests(requests), index(start) {}
+ public:
+ /** Is this iterator done */
+ bool done() const;
+ /** Go to the next element */
+ void next();
+ /** Get the actual request */
+ const Request& get() const;
+ };
+
+ atom_iterator getAtomIterator(TNode trigger) const;
+
+private:
+
+ struct RequestHashFunction {
+ size_t operator () (const Request& r) const {
+ return r.hash();
+ }
+ };
+
+ /** Set of all requests so we don't add twice */
+ context::CDHashSet<Request, RequestHashFunction> d_allRequests;
+
+ static const element_index null_index = -1;
+
+ struct Element {
+ /** Current request */
+ Request request;
+ /** Previous request */
+ element_index previous;
+
+ Element(const Request& request, element_index previous)
+ : request(request), previous(previous)
+ {}
+ };
+
+ /** We index the requests in this vector, it's a list */
+ context::CDList<Element> d_requests;
+
+ typedef context::CDHashMap<Node, element_index, NodeHashFunction> trigger_to_list_map;
+
+ /** Map from triggers, to the list of elements they trigger */
+ trigger_to_list_map d_triggerToRequestMap;
+
+ /** Get the list index of the trigger */
+ element_index getList(TNode trigger) const;
+
+};
+
+}
+
+
+
+
diff --git a/src/theory/booleans/theory_bool.cpp b/src/theory/booleans/theory_bool.cpp
index 3e75dd258..895f4a279 100644
--- a/src/theory/booleans/theory_bool.cpp
+++ b/src/theory/booleans/theory_bool.cpp
@@ -19,6 +19,7 @@
#include "theory/booleans/circuit_propagator.h"
#include "theory/valuation.h"
#include "util/boolean_simplification.h"
+#include "theory/substitutions.h"
#include <vector>
#include <stack>
diff --git a/src/theory/builtin/kinds b/src/theory/builtin/kinds
index fca79aff0..b51feea6d 100644
--- a/src/theory/builtin/kinds
+++ b/src/theory/builtin/kinds
@@ -300,7 +300,12 @@ operator SEXPR 0: "a symbolic expression"
operator LAMBDA 2 "lambda"
-parameterized CHAIN BUILTIN 2: "chain operator"
+parameterized CHAIN CHAIN_OP 2: "chained operator"
+constant CHAIN_OP \
+ ::CVC4::Chain \
+ ::CVC4::ChainHashFunction \
+ "util/chain.h" \
+ "the chained operator"
constant TYPE_CONSTANT \
::CVC4::TypeConstant \
@@ -321,29 +326,13 @@ well-founded SEXPR_TYPE \
"::CVC4::theory::builtin::SExprProperties::mkGroundTerm(%TYPE%)" \
"theory/builtin/theory_builtin_type_rules.h"
-# These will eventually move to a theory of strings.
-#
-# For now these are unbounded strings over a fixed, finite alphabet
-# (this may change).
-sort STRING_TYPE \
- Cardinality::INTEGERS \
- well-founded \
- "NodeManager::currentNM()->mkConst(::std::string())" \
- "string" \
- "String type"
-constant CONST_STRING \
- ::std::string \
- ::CVC4::StringHashFunction \
- "util/hash.h" \
- "a string of characters"
-typerule CONST_STRING ::CVC4::theory::builtin::StringConstantTypeRule
-
typerule APPLY ::CVC4::theory::builtin::ApplyTypeRule
typerule EQUAL ::CVC4::theory::builtin::EqualityTypeRule
typerule DISTINCT ::CVC4::theory::builtin::DistinctTypeRule
typerule SEXPR ::CVC4::theory::builtin::SExprTypeRule
typerule LAMBDA ::CVC4::theory::builtin::LambdaTypeRule
typerule CHAIN ::CVC4::theory::builtin::ChainTypeRule
+typerule CHAIN_OP ::CVC4::theory::builtin::ChainedOperatorTypeRule
constant SUBTYPE_TYPE \
::CVC4::Predicate \
diff --git a/src/theory/builtin/theory_builtin_rewriter.cpp b/src/theory/builtin/theory_builtin_rewriter.cpp
index 4d62ce511..392e146ba 100644
--- a/src/theory/builtin/theory_builtin_rewriter.cpp
+++ b/src/theory/builtin/theory_builtin_rewriter.cpp
@@ -16,6 +16,7 @@
**/
#include "theory/builtin/theory_builtin_rewriter.h"
+#include "util/chain.h"
using namespace std;
@@ -53,7 +54,7 @@ Node TheoryBuiltinRewriter::blastChain(TNode in) {
Assert(in.getKind() == kind::CHAIN);
- Kind chainedOp = in.getOperator().getConst<Kind>();
+ Kind chainedOp = in.getOperator().getConst<Chain>().getOperator();
if(in.getNumChildren() == 2) {
// if this is the case exactly 1 pair will be generated so the
diff --git a/src/theory/builtin/theory_builtin_type_rules.h b/src/theory/builtin/theory_builtin_type_rules.h
index 2a4e07528..c7143bdeb 100644
--- a/src/theory/builtin/theory_builtin_type_rules.h
+++ b/src/theory/builtin/theory_builtin_type_rules.h
@@ -146,14 +146,6 @@ public:
}
};/* class AbstractValueTypeRule */
-class StringConstantTypeRule {
-public:
- inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) {
- Assert(n.getKind() == kind::CONST_STRING);
- return nodeManager->stringType();
- }
-};/* class StringConstantTypeRule */
-
class LambdaTypeRule {
public:
inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) {
@@ -220,6 +212,14 @@ public:
}
};/* class ChainTypeRule */
+class ChainedOperatorTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) {
+ Assert(n.getKind() == kind::CHAIN_OP);
+ return nodeManager->getType(nodeManager->operatorOf(n.getConst<Chain>().getOperator()), check);
+ }
+};/* class ChainedOperatorTypeRule */
+
class SortProperties {
public:
inline static bool isWellFounded(TypeNode type) {
diff --git a/src/theory/bv/bitblaster.cpp b/src/theory/bv/bitblaster.cpp
index 8579012ab..d17dd588f 100644
--- a/src/theory/bv/bitblaster.cpp
+++ b/src/theory/bv/bitblaster.cpp
@@ -12,7 +12,7 @@
** \brief [[ Add one-line brief description here ]]
**
** [[ Add lengthier description here ]]
- **
+ **
**/
#include "bitblaster.h"
@@ -29,7 +29,7 @@
using namespace std;
using namespace CVC4::theory::bv::utils;
-using namespace CVC4::context;
+using namespace CVC4::context;
using namespace CVC4::prop;
namespace CVC4 {
@@ -37,20 +37,20 @@ namespace theory {
namespace bv{
std::string toString(Bits& bits) {
- ostringstream os;
+ ostringstream os;
for (int i = bits.size() - 1; i >= 0; --i) {
TNode bit = bits[i];
if (bit.getKind() == kind::CONST_BOOLEAN) {
os << (bit.getConst<bool>() ? "1" : "0");
} else {
- os << bit<< " ";
+ os << bit<< " ";
}
}
os <<"\n";
-
- return os.str();
+
+ return os.str();
}
-/////// Bitblaster
+/////// Bitblaster
Bitblaster::Bitblaster(context::Context* c, bv::TheoryBV* bv) :
d_bv(bv),
@@ -64,38 +64,41 @@ Bitblaster::Bitblaster(context::Context* c, bv::TheoryBV* bv) :
d_cnfStream = new TseitinCnfStream(d_satSolver, new NullRegistrar(), new Context());
MinisatNotify* notify = new MinisatNotify(d_cnfStream, bv);
- d_satSolver->setNotify(notify);
+ d_satSolver->setNotify(notify);
// initializing the bit-blasting strategies
- initAtomBBStrategies();
- initTermBBStrategies();
+ initAtomBBStrategies();
+ initTermBBStrategies();
}
Bitblaster::~Bitblaster() {
delete d_cnfStream;
- delete d_satSolver;
+ delete d_satSolver;
}
-/**
+/**
* Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver
* NOTE: duplicate clauses are not detected because of marker literal
* @param node the atom to be bitblasted
- *
+ *
*/
void Bitblaster::bbAtom(TNode node) {
node = node.getKind() == kind::NOT? node[0] : node;
-
+
if (hasBBAtom(node)) {
- return;
+ return;
}
// make sure it is marked as an atom
- addAtom(node);
+ addAtom(node);
- Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n";
+ Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n";
++d_statistics.d_numAtoms;
// the bitblasted definition of the atom
- Node atom_bb = Rewriter::rewrite(d_atomBBStrategies[node.getKind()](node, this));
+ Node normalized = Rewriter::rewrite(node);
+ Node atom_bb = normalized.getKind() != kind::CONST_BOOLEAN ?
+ Rewriter::rewrite(d_atomBBStrategies[normalized.getKind()](normalized, this)) :
+ normalized;
// asserting that the atom is true iff the definition holds
Node atom_definition = mkNode(kind::IFF, node, atom_bb);
@@ -123,14 +126,14 @@ void Bitblaster::bbTerm(TNode node, Bits& bits) {
return;
}
- Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n";
+ Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n";
++d_statistics.d_numTerms;
d_termBBStrategies[node.getKind()] (node, bits,this);
-
+
Assert (bits.size() == utils::getSize(node));
- cacheTermDef(node, bits);
+ cacheTermDef(node, bits);
}
Node Bitblaster::bbOptimize(TNode node) {
@@ -139,21 +142,21 @@ Node Bitblaster::bbOptimize(TNode node) {
if (node.getKind() == kind::BITVECTOR_PLUS) {
if (RewriteRule<BBPlusNeg>::applies(node)) {
Node res = RewriteRule<BBPlusNeg>::run<false>(node);
- return res;
+ return res;
}
// if (RewriteRule<BBFactorOut>::applies(node)) {
// Node res = RewriteRule<BBFactorOut>::run<false>(node);
- // return res;
- // }
+ // return res;
+ // }
} else if (node.getKind() == kind::BITVECTOR_MULT) {
if (RewriteRule<MultPow2>::applies(node)) {
Node res = RewriteRule<MultPow2>::run<false>(node);
- return res;
+ return res;
}
}
-
- return node;
+
+ return node;
}
/// Public methods
@@ -170,31 +173,31 @@ void Bitblaster::explain(TNode atom, std::vector<TNode>& explanation) {
std::vector<SatLiteral> literal_explanation;
d_satSolver->explain(d_cnfStream->getLiteral(atom), literal_explanation);
for (unsigned i = 0; i < literal_explanation.size(); ++i) {
- explanation.push_back(d_cnfStream->getNode(literal_explanation[i]));
+ explanation.push_back(d_cnfStream->getNode(literal_explanation[i]));
}
}
-/**
+/*
* Asserts the clauses corresponding to the atom to the Sat Solver
* by turning on the marker literal (i.e. setting it to false)
* @param node the atom to be asserted
- *
+ *
*/
-
+
bool Bitblaster::propagate() {
return d_satSolver->propagate() == prop::SAT_VALUE_TRUE;
}
bool Bitblaster::assertToSat(TNode lit, bool propagate) {
// strip the not
- TNode atom;
+ TNode atom;
if (lit.getKind() == kind::NOT) {
- atom = lit[0];
+ atom = lit[0];
} else {
- atom = lit;
+ atom = lit;
}
-
+
Assert (hasBBAtom(atom));
SatLiteral markerLit = d_cnfStream->getLiteral(atom);
@@ -202,9 +205,9 @@ bool Bitblaster::assertToSat(TNode lit, bool propagate) {
if(lit.getKind() == kind::NOT) {
markerLit = ~markerLit;
}
-
+
Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat asserting node: " << atom <<"\n";
- Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat with literal: " << markerLit << "\n";
+ Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat with literal: " << markerLit << "\n";
SatValue ret = d_satSolver->assertAssumption(markerLit, propagate);
@@ -214,13 +217,13 @@ bool Bitblaster::assertToSat(TNode lit, bool propagate) {
return ret == prop::SAT_VALUE_TRUE;
}
-/**
- * Calls the solve method for the Sat Solver.
+/**
+ * Calls the solve method for the Sat Solver.
* passing it the marker literals to be asserted
- *
+ *
* @return true for sat, and false for unsat
*/
-
+
bool Bitblaster::solve(bool quick_solve) {
if (Trace.isOn("bitvector")) {
Trace("bitvector") << "Bitblaster::solve() asserted atoms ";
@@ -229,24 +232,24 @@ bool Bitblaster::solve(bool quick_solve) {
Trace("bitvector") << " " << d_cnfStream->getNode(*it) << "\n";
}
}
- Debug("bitvector") << "Bitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n";
- return SAT_VALUE_TRUE == d_satSolver->solve();
+ Debug("bitvector") << "Bitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n";
+ return SAT_VALUE_TRUE == d_satSolver->solve();
}
void Bitblaster::getConflict(std::vector<TNode>& conflict) {
SatClause conflictClause;
d_satSolver->getUnsatCore(conflictClause);
-
+
for (unsigned i = 0; i < conflictClause.size(); i++) {
- SatLiteral lit = conflictClause[i];
+ SatLiteral lit = conflictClause[i];
TNode atom = d_cnfStream->getNode(lit);
- Node not_atom;
+ Node not_atom;
if (atom.getKind() == kind::NOT) {
not_atom = atom[0];
} else {
- not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom);
+ not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom);
}
- conflict.push_back(not_atom);
+ conflict.push_back(not_atom);
}
}
@@ -256,9 +259,9 @@ void Bitblaster::getConflict(std::vector<TNode>& conflict) {
void Bitblaster::initAtomBBStrategies() {
for (int i = 0 ; i < kind::LAST_KIND; ++i ) {
- d_atomBBStrategies[i] = UndefinedAtomBBStrategy;
+ d_atomBBStrategies[i] = UndefinedAtomBBStrategy;
}
-
+
/// setting default bb strategies for atoms
d_atomBBStrategies [ kind::EQUAL ] = DefaultEqBB;
d_atomBBStrategies [ kind::BITVECTOR_ULT ] = DefaultUltBB;
@@ -269,7 +272,7 @@ void Bitblaster::initAtomBBStrategies() {
d_atomBBStrategies [ kind::BITVECTOR_SLE ] = DefaultSleBB;
d_atomBBStrategies [ kind::BITVECTOR_SGT ] = DefaultSgtBB;
d_atomBBStrategies [ kind::BITVECTOR_SGE ] = DefaultSgeBB;
-
+
}
void Bitblaster::initTermBBStrategies() {
@@ -278,7 +281,7 @@ void Bitblaster::initTermBBStrategies() {
for (int i = 0 ; i < kind::LAST_KIND; ++i ) {
d_termBBStrategies[i] = DefaultVarBB;
}
-
+
/// setting default bb strategies for terms:
// d_termBBStrategies [ kind::VARIABLE ] = DefaultVarBB;
d_termBBStrategies [ kind::CONST_BITVECTOR ] = DefaultConstBB;
@@ -295,13 +298,13 @@ void Bitblaster::initTermBBStrategies() {
d_termBBStrategies [ kind::BITVECTOR_PLUS ] = DefaultPlusBB;
d_termBBStrategies [ kind::BITVECTOR_SUB ] = DefaultSubBB;
d_termBBStrategies [ kind::BITVECTOR_NEG ] = DefaultNegBB;
- d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy;
- d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy;
+ d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy;
+ d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy;
d_termBBStrategies [ kind::BITVECTOR_UDIV_TOTAL ] = DefaultUdivBB;
d_termBBStrategies [ kind::BITVECTOR_UREM_TOTAL ] = DefaultUremBB;
- d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy;
- d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy;
- d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy;
+ d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy;
+ d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy;
+ d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy;
d_termBBStrategies [ kind::BITVECTOR_SHL ] = DefaultShlBB;
d_termBBStrategies [ kind::BITVECTOR_LSHR ] = DefaultLshrBB;
d_termBBStrategies [ kind::BITVECTOR_ASHR ] = DefaultAshrBB;
@@ -313,22 +316,22 @@ void Bitblaster::initTermBBStrategies() {
d_termBBStrategies [ kind::BITVECTOR_ROTATE_LEFT ] = DefaultRotateLeftBB;
}
-
+
bool Bitblaster::hasBBAtom(TNode atom) const {
return d_bitblastedAtoms.find(atom) != d_bitblastedAtoms.end();
}
void Bitblaster::cacheTermDef(TNode term, Bits def) {
Assert (d_termCache.find(term) == d_termCache.end());
- d_termCache[term] = def;
+ d_termCache[term] = def;
}
bool Bitblaster::hasBBTerm(TNode node) const {
- return d_termCache.find(node) != d_termCache.end();
+ return d_termCache.find(node) != d_termCache.end();
}
void Bitblaster::getBBTerm(TNode node, Bits& bits) const {
- Assert (hasBBTerm(node));
+ Assert (hasBBTerm(node));
// copy?
bits = d_termCache.find(node)->second;
}
@@ -337,7 +340,7 @@ Bitblaster::Statistics::Statistics() :
d_numTermClauses("theory::bv::NumberOfTermSatClauses", 0),
d_numAtomClauses("theory::bv::NumberOfAtomSatClauses", 0),
d_numTerms("theory::bv::NumberOfBitblastedTerms", 0),
- d_numAtoms("theory::bv::NumberOfBitblastedAtoms", 0),
+ d_numAtoms("theory::bv::NumberOfBitblastedAtoms", 0),
d_bitblastTimer("theory::bv::BitblastTimer")
{
StatisticsRegistry::registerStat(&d_numTermClauses);
@@ -374,7 +377,7 @@ void Bitblaster::MinisatNotify::notify(prop::SatClause& clause) {
};
void Bitblaster::MinisatNotify::safePoint() {
- d_bv->d_out->safePoint();
+ d_bv->d_out->safePoint();
}
EqualityStatus Bitblaster::getEqualityStatus(TNode a, TNode b) {
@@ -417,70 +420,77 @@ EqualityStatus Bitblaster::getEqualityStatus(TNode a, TNode b) {
bool Bitblaster::isSharedTerm(TNode node) {
- return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end();
+ return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end();
}
bool Bitblaster::hasValue(TNode a) {
- Assert (d_termCache.find(a) != d_termCache.end());
+ Assert (d_termCache.find(a) != d_termCache.end());
Bits bits = d_termCache[a];
for (int i = bits.size() -1; i >= 0; --i) {
- SatValue bit_value;
- if (d_cnfStream->hasLiteral(bits[i])) {
+ SatValue bit_value;
+ if (d_cnfStream->hasLiteral(bits[i])) {
SatLiteral bit = d_cnfStream->getLiteral(bits[i]);
bit_value = d_satSolver->value(bit);
if (bit_value == SAT_VALUE_UNKNOWN)
- return false;
+ return false;
} else {
- return false;
+ return false;
}
}
- return true;
+ return true;
}
-/**
+/**
* Returns the value a is currently assigned to in the SAT solver
- * or null if the value is completely unassigned.
- *
- * @param a
- *
- * @return
+ * or null if the value is completely unassigned.
+ *
+ * @param a
+ * @param fullModel whether to create a "full model," i.e., add
+ * constants to equivalence classes that don't already have them
+ *
+ * @return
*/
-Node Bitblaster::getVarValue(TNode a) {
+Node Bitblaster::getVarValue(TNode a, bool fullModel) {
if (d_termCache.find(a) == d_termCache.end()) {
Assert(isSharedTerm(a));
- return Node();
+ return Node();
}
Bits bits = d_termCache[a];
- Integer value(0);
+ Integer value(0);
for (int i = bits.size() -1; i >= 0; --i) {
SatValue bit_value;
- if (d_cnfStream->hasLiteral(bits[i])) {
+ if (d_cnfStream->hasLiteral(bits[i])) {
SatLiteral bit = d_cnfStream->getLiteral(bits[i]);
bit_value = d_satSolver->value(bit);
- Assert (bit_value != SAT_VALUE_UNKNOWN);
+ Assert (bit_value != SAT_VALUE_UNKNOWN);
} else {
- // the bit is unconstrainted so we can give it an arbitrary value
+ //TODO: return Node() if fullModel=false?
+ // the bit is unconstrainted so we can give it an arbitrary value
bit_value = SAT_VALUE_FALSE;
}
- Integer bit_int = bit_value == SAT_VALUE_TRUE ? Integer(1) : Integer(0);
- value = value * 2 + bit_int;
+ Integer bit_int = bit_value == SAT_VALUE_TRUE ? Integer(1) : Integer(0);
+ value = value * 2 + bit_int;
}
- return utils::mkConst(BitVector(bits.size(), value));
+ return utils::mkConst(BitVector(bits.size(), value));
}
-void Bitblaster::collectModelInfo(TheoryModel* m) {
+void Bitblaster::collectModelInfo(TheoryModel* m, bool fullModel) {
__gnu_cxx::hash_set<TNode, TNodeHashFunction>::iterator it = d_variables.begin();
for (; it!= d_variables.end(); ++it) {
TNode var = *it;
if (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)) {
- Node const_value = getVarValue(var);
+ Node const_value = getVarValue(var, fullModel);
if(const_value == Node()) {
- // if the value is unassigned just set it to zero
- const_value = utils::mkConst(BitVector(utils::getSize(var), 0u));
+ if( fullModel ){
+ // if the value is unassigned just set it to zero
+ const_value = utils::mkConst(BitVector(utils::getSize(var), 0u));
+ }
+ }
+ if(const_value != Node()) {
+ Debug("bitvector-model") << "Bitblaster::collectModelInfo (assert (= "
+ << var << " "
+ << const_value << "))\n";
+ m->assertEquality(var, const_value, true);
}
- Debug("bitvector-model") << "Bitblaster::collectModelInfo (assert (= "
- << var << " "
- << const_value << "))\n";
- m->assertEquality(var, const_value, true);
}
}
}
diff --git a/src/theory/bv/bitblaster.h b/src/theory/bv/bitblaster.h
index c122c407d..6fab0369c 100644
--- a/src/theory/bv/bitblaster.h
+++ b/src/theory/bv/bitblaster.h
@@ -55,14 +55,14 @@ namespace bv {
typedef std::vector<Node> Bits;
-std::string toString (Bits& bits);
+std::string toString (Bits& bits);
class TheoryBV;
-/**
- * The Bitblaster that manages the mapping between Nodes
- * and their bitwise definition
- *
+/**
+ * The Bitblaster that manages the mapping between Nodes
+ * and their bitwise definition
+ *
*/
class Bitblaster {
@@ -79,26 +79,26 @@ class Bitblaster {
void notify(prop::SatClause& clause);
void safePoint();
};
-
-
+
+
typedef __gnu_cxx::hash_map <Node, Bits, TNodeHashFunction > TermDefMap;
typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> AtomSet;
- typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> VarSet;
-
- typedef void (*TermBBStrategy) (TNode, Bits&, Bitblaster*);
- typedef Node (*AtomBBStrategy) (TNode, Bitblaster*);
+ typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> VarSet;
+
+ typedef void (*TermBBStrategy) (TNode, Bits&, Bitblaster*);
+ typedef Node (*AtomBBStrategy) (TNode, Bitblaster*);
TheoryBV *d_bv;
-
+
// sat solver used for bitblasting and associated CnfStream
theory::OutputChannel* d_bvOutput;
- prop::BVSatSolverInterface* d_satSolver;
+ prop::BVSatSolverInterface* d_satSolver;
prop::CnfStream* d_cnfStream;
// caches and mappings
TermDefMap d_termCache;
AtomSet d_bitblastedAtoms;
- VarSet d_variables;
+ VarSet d_variables;
context::CDList<prop::SatLiteral> d_assertedAtoms; /**< context dependent list storing the atoms
currently asserted by the DPLL SAT solver. */
@@ -111,79 +111,80 @@ class Bitblaster {
/// function tables for the various bitblasting strategies indexed by node kind
TermBBStrategy d_termBBStrategies[kind::LAST_KIND];
- AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND];
+ AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND];
// helper methods to initialize function tables
void initAtomBBStrategies();
- void initTermBBStrategies();
+ void initTermBBStrategies();
// returns a node that might be easier to bitblast
- Node bbOptimize(TNode node);
-
- void addAtom(TNode atom);
+ Node bbOptimize(TNode node);
+
+ void addAtom(TNode atom);
// division is bitblasted in terms of constraints
// so it needs to use private bitblaster interface
void bbUdiv(TNode node, Bits& bits);
void bbUrem(TNode node, Bits& bits);
- bool hasValue(TNode a);
+ bool hasValue(TNode a);
public:
void cacheTermDef(TNode node, Bits def); // public so we can cache remainder for division
void bbTerm(TNode node, Bits& bits);
void bbAtom(TNode node);
-
- Bitblaster(context::Context* c, bv::TheoryBV* bv);
+
+ Bitblaster(context::Context* c, bv::TheoryBV* bv);
~Bitblaster();
bool assertToSat(TNode node, bool propagate = true);
bool propagate();
bool solve(bool quick_solve = false);
- void getConflict(std::vector<TNode>& conflict);
+ void getConflict(std::vector<TNode>& conflict);
void explain(TNode atom, std::vector<TNode>& explanation);
EqualityStatus getEqualityStatus(TNode a, TNode b);
- /**
+ /**
* Return a constant Node representing the value of a variable
- * in the current model.
- * @param a
- *
- * @return
+ * in the current model.
+ * @param a
+ *
+ * @return
*/
- Node getVarValue(TNode a);
- /**
- * Adds a constant value for each bit-blasted variable in the model.
- *
- * @param m the model
+ Node getVarValue(TNode a, bool fullModel=true);
+ /**
+ * Adds a constant value for each bit-blasted variable in the model.
+ *
+ * @param m the model
+ * @param fullModel whether to create a "full model," i.e., add
+ * constants to equivalence classes that don't already have them
*/
- void collectModelInfo(TheoryModel* m);
- /**
- * Stores the variable (or non-bv term) and its corresponding bits.
- *
- * @param var
- * @param bits
+ void collectModelInfo(TheoryModel* m, bool fullModel);
+ /**
+ * Stores the variable (or non-bv term) and its corresponding bits.
+ *
+ * @param var
*/
void storeVariable(TNode var) {
- d_variables.insert(var);
+ d_variables.insert(var);
}
bool isSharedTerm(TNode node);
uint64_t computeAtomWeight(TNode node);
private:
-
+
class Statistics {
public:
IntStat d_numTermClauses, d_numAtomClauses;
- IntStat d_numTerms, d_numAtoms;
+ IntStat d_numTerms, d_numAtoms;
TimerStat d_bitblastTimer;
Statistics();
- ~Statistics();
- };
-
+ ~Statistics();
+ };
+
Statistics d_statistics;
};
-} /* bv namespace */
+} /* bv namespace */
} /* theory namespace */
diff --git a/src/theory/bv/bv_subtheory.h b/src/theory/bv/bv_subtheory.h
index 8374a3f75..0b0551283 100644
--- a/src/theory/bv/bv_subtheory.h
+++ b/src/theory/bv/bv_subtheory.h
@@ -9,7 +9,7 @@
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
- ** \brief Algebraic solver.
+ ** \brief Algebraic solver.
**
** Algebraic solver.
**/
@@ -46,7 +46,7 @@ inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) {
out << "BV_CORE_SUBTHEORY";
break;
case SUB_INEQUALITY:
- out << "BV_INEQUALITY_SUBTHEORY";
+ out << "BV_INEQUALITY_SUBTHEORY";
default:
Unreachable();
break;
@@ -55,13 +55,10 @@ inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) {
}
-const bool d_useEqualityEngine = true;
-const bool d_useSatPropagation = true;
+// forward declaration
+class TheoryBV;
-// forward declaration
-class TheoryBV;
-
-typedef context::CDQueue<Node> AssertionQueue;
+typedef context::CDQueue<Node> AssertionQueue;
/**
* Abstract base class for bit-vector subtheory solvers
*
@@ -78,7 +75,7 @@ protected:
AssertionQueue d_assertionQueue;
context::CDO<uint32_t> d_assertionIndex;
public:
-
+
SubtheorySolver(context::Context* c, TheoryBV* bv) :
d_context(c),
d_bv(bv),
@@ -86,24 +83,24 @@ public:
d_assertionIndex(c, 0)
{}
virtual ~SubtheorySolver() {}
- virtual bool check(Theory::Effort e) = 0;
+ virtual bool check(Theory::Effort e) = 0;
virtual void explain(TNode literal, std::vector<TNode>& assumptions) = 0;
virtual void preRegister(TNode node) {}
virtual void propagate(Theory::Effort e) {}
- virtual void collectModelInfo(TheoryModel* m) = 0;
- virtual Node getModelValue(TNode var) = 0;
+ virtual void collectModelInfo(TheoryModel* m, bool fullModel) = 0;
+ virtual Node getModelValue(TNode var) = 0;
virtual bool isComplete() = 0;
virtual EqualityStatus getEqualityStatus(TNode a, TNode b) = 0;
- virtual void addSharedTerm(TNode node) {}
+ virtual void addSharedTerm(TNode node) {}
bool done() { return d_assertionQueue.size() == d_assertionIndex; }
TNode get() {
- Assert (!done());
+ Assert (!done());
TNode res = d_assertionQueue[d_assertionIndex];
d_assertionIndex = d_assertionIndex + 1;
- return res;
+ return res;
}
virtual void assertFact(TNode fact) { d_assertionQueue.push_back(fact); }
-};
+};
}
}
diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp
index 2308f36a3..5a0c17134 100644
--- a/src/theory/bv/bv_subtheory_bitblast.cpp
+++ b/src/theory/bv/bv_subtheory_bitblast.cpp
@@ -34,7 +34,8 @@ BitblastSolver::BitblastSolver(context::Context* c, TheoryBV* bv)
d_bitblaster(new Bitblaster(c, bv)),
d_bitblastQueue(c),
d_statistics(),
- d_validModelCache(c, true)
+ d_validModelCache(c, true),
+ d_useSatPropagation(options::bvPropagate())
{}
BitblastSolver::~BitblastSolver() {
@@ -83,20 +84,20 @@ void BitblastSolver::bitblastQueue() {
}
bool BitblastSolver::check(Theory::Effort e) {
- Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n";
+ Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n";
Assert(!options::bitvectorEagerBitblast());
- ++(d_statistics.d_numCallstoCheck);
+ ++(d_statistics.d_numCallstoCheck);
//// Lazy bit-blasting
// bit-blast enqueued nodes
bitblastQueue();
- // Processing assertions
+ // Processing assertions
while (!done()) {
TNode fact = get();
d_validModelCache = false;
- Debug("bv-bitblast") << " fact " << fact << ")\n";
+ Debug("bv-bitblast") << " fact " << fact << ")\n";
if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) {
// Some atoms have not been bit-blasted yet
d_bitblaster->bbAtom(fact);
@@ -143,8 +144,8 @@ EqualityStatus BitblastSolver::getEqualityStatus(TNode a, TNode b) {
return d_bitblaster->getEqualityStatus(a, b);
}
-void BitblastSolver::collectModelInfo(TheoryModel* m) {
- return d_bitblaster->collectModelInfo(m);
+void BitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) {
+ return d_bitblaster->collectModelInfo(m, fullModel);
}
Node BitblastSolver::getModelValue(TNode node)
@@ -188,5 +189,5 @@ Node BitblastSolver::getModelValueRec(TNode node)
Assert(val.isConst());
d_modelCache[node] = val;
Debug("bitvector-model") << node << " => " << val <<"\n";
- return val;
+ return val;
}
diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h
index 819b3d62c..f1204dbdf 100644
--- a/src/theory/bv/bv_subtheory_bitblast.h
+++ b/src/theory/bv/bv_subtheory_bitblast.h
@@ -19,7 +19,6 @@
#pragma once
#include "theory/bv/bv_subtheory.h"
-#include "theory/substitutions.h"
namespace CVC4 {
namespace theory {
namespace bv {
@@ -33,20 +32,21 @@ class BitblastSolver : public SubtheorySolver {
struct Statistics {
IntStat d_numCallstoCheck;
Statistics();
- ~Statistics();
- };
+ ~Statistics();
+ };
/** Bitblaster */
Bitblaster* d_bitblaster;
/** Nodes that still need to be bit-blasted */
context::CDQueue<TNode> d_bitblastQueue;
- Statistics d_statistics;
+ Statistics d_statistics;
typedef std::hash_map<Node, Node, NodeHashFunction> NodeMap;
NodeMap d_modelCache;
context::CDO<bool> d_validModelCache;
Node getModelValueRec(TNode node);
+ bool d_useSatPropagation;
public:
BitblastSolver(context::Context* c, TheoryBV* bv);
~BitblastSolver();
@@ -55,7 +55,7 @@ public:
bool check(Theory::Effort e);
void explain(TNode literal, std::vector<TNode>& assumptions);
EqualityStatus getEqualityStatus(TNode a, TNode b);
- void collectModelInfo(TheoryModel* m);
+ void collectModelInfo(TheoryModel* m, bool fullModel);
Node getModelValue(TNode node);
bool isComplete() { return true; }
void bitblastQueue();
diff --git a/src/theory/bv/bv_subtheory_core.cpp b/src/theory/bv/bv_subtheory_core.cpp
index c0546f892..45946b8c8 100644
--- a/src/theory/bv/bv_subtheory_core.cpp
+++ b/src/theory/bv/bv_subtheory_core.cpp
@@ -37,53 +37,48 @@ CoreSolver::CoreSolver(context::Context* c, TheoryBV* bv)
d_isCoreTheory(c, true),
d_reasons(c)
{
- if (d_useEqualityEngine) {
-
- // The kinds we are treating as function application in congruence
- d_equalityEngine.addFunctionKind(kind::BITVECTOR_CONCAT, true);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_AND);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_OR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XOR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOT);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NAND);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XNOR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_COMP);
- d_equalityEngine.addFunctionKind(kind::BITVECTOR_MULT, true);
- d_equalityEngine.addFunctionKind(kind::BITVECTOR_PLUS, true);
- d_equalityEngine.addFunctionKind(kind::BITVECTOR_EXTRACT, true);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SUB);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NEG);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UDIV);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UREM);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SDIV);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SREM);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SMOD);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SHL);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_LSHR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ASHR);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULT);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULE);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGT);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGE);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLT);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLE);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGT);
- // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGE);
- }
+
+ // The kinds we are treating as function application in congruence
+ d_equalityEngine.addFunctionKind(kind::BITVECTOR_CONCAT, true);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_AND);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_OR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XOR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOT);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NAND);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XNOR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_COMP);
+ d_equalityEngine.addFunctionKind(kind::BITVECTOR_MULT, true);
+ d_equalityEngine.addFunctionKind(kind::BITVECTOR_PLUS, true);
+ d_equalityEngine.addFunctionKind(kind::BITVECTOR_EXTRACT, true);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SUB);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NEG);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UDIV);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UREM);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SDIV);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SREM);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SMOD);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SHL);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_LSHR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ASHR);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULT);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULE);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGT);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGE);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLT);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLE);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGT);
+ // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGE);
}
CoreSolver::~CoreSolver() {
- delete d_slicer;
+ delete d_slicer;
}
void CoreSolver::setMasterEqualityEngine(eq::EqualityEngine* eq) {
d_equalityEngine.setMasterEqualityEngine(eq);
}
void CoreSolver::preRegister(TNode node) {
- if (!d_useEqualityEngine)
- return;
-
if (node.getKind() == kind::EQUAL) {
d_equalityEngine.addTriggerEquality(node);
if (options::bitvectorCoreSolver()) {
@@ -109,51 +104,51 @@ Node CoreSolver::getBaseDecomposition(TNode a) {
std::vector<Node> a_decomp;
d_slicer->getBaseDecomposition(a, a_decomp);
Node new_a = utils::mkConcat(a_decomp);
- Debug("bv-slicer") << "CoreSolver::getBaseDecomposition " << a <<" => " << new_a << "\n";
- return new_a;
+ Debug("bv-slicer") << "CoreSolver::getBaseDecomposition " << a <<" => " << new_a << "\n";
+ return new_a;
}
bool CoreSolver::decomposeFact(TNode fact) {
- Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl;
+ Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl;
// assert decompositions since the equality engine does not know the semantics of
// concat:
// a == a_1 concat ... concat a_k
// b == b_1 concat ... concat b_k
- Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl;
- // FIXME: are this the right things to assert?
+ Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl;
+ // FIXME: are this the right things to assert?
// assert decompositions since the equality engine does not know the semantics of
// concat:
// a == a_1 concat ... concat a_k
// b == b_1 concat ... concat b_k
- TNode eq = fact.getKind() == kind::NOT? fact[0] : fact;
+ TNode eq = fact.getKind() == kind::NOT? fact[0] : fact;
TNode a = eq[0];
TNode b = eq[1];
Node new_a = getBaseDecomposition(a);
- Node new_b = getBaseDecomposition(b);
-
+ Node new_b = getBaseDecomposition(b);
+
Assert (utils::getSize(new_a) == utils::getSize(new_b) &&
- utils::getSize(new_a) == utils::getSize(a));
-
+ utils::getSize(new_a) == utils::getSize(a));
+
NodeManager* nm = NodeManager::currentNM();
Node a_eq_new_a = nm->mkNode(kind::EQUAL, a, new_a);
Node b_eq_new_b = nm->mkNode(kind::EQUAL, b, new_b);
bool ok = true;
ok = assertFactToEqualityEngine(a_eq_new_a, utils::mkTrue());
- if (!ok) return false;
+ if (!ok) return false;
ok = assertFactToEqualityEngine(b_eq_new_b, utils::mkTrue());
- if (!ok) return false;
+ if (!ok) return false;
ok = assertFactToEqualityEngine(fact, fact);
if (!ok) return false;
-
+
if (fact.getKind() == kind::EQUAL) {
// assert the individual equalities as well
// a_i == b_i
if (new_a.getKind() == kind::BITVECTOR_CONCAT &&
new_b.getKind() == kind::BITVECTOR_CONCAT) {
-
- Assert (new_a.getNumChildren() == new_b.getNumChildren());
+
+ Assert (new_a.getNumChildren() == new_b.getNumChildren());
for (unsigned i = 0; i < new_a.getNumChildren(); ++i) {
Node eq_i = nm->mkNode(kind::EQUAL, new_a[i], new_b[i]);
ok = assertFactToEqualityEngine(eq_i, fact);
@@ -161,23 +156,23 @@ bool CoreSolver::decomposeFact(TNode fact) {
}
}
}
- return true;
+ return true;
}
bool CoreSolver::check(Theory::Effort e) {
Trace("bitvector::core") << "CoreSolver::check \n";
Assert (!d_bv->inConflict());
- ++(d_statistics.d_numCallstoCheck);
- bool ok = true;
+ ++(d_statistics.d_numCallstoCheck);
+ bool ok = true;
std::vector<Node> core_eqs;
while (! done()) {
- TNode fact = get();
-
+ TNode fact = get();
+
// update whether we are in the core fragment
if (d_isCoreTheory && !d_slicer->isCoreTerm(fact)) {
- d_isCoreTheory = false;
+ d_isCoreTheory = false;
}
-
+
// only reason about equalities
if (fact.getKind() == kind::EQUAL || (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL)) {
if (options::bitvectorCoreSolver()) {
@@ -186,31 +181,31 @@ bool CoreSolver::check(Theory::Effort e) {
ok = assertFactToEqualityEngine(fact, fact);
}
} else {
- ok = assertFactToEqualityEngine(fact, fact);
+ ok = assertFactToEqualityEngine(fact, fact);
}
if (!ok)
- return false;
+ return false;
}
-
+
if (Theory::fullEffort(e) && isComplete()) {
buildModel();
}
-
+
return true;
}
void CoreSolver::buildModel() {
if (options::bitvectorCoreSolver()) {
// FIXME
- Unreachable();
- return;
+ Unreachable();
+ return;
}
- Debug("bv-core") << "CoreSolver::buildModel() \n";
- d_modelValues.clear();
+ Debug("bv-core") << "CoreSolver::buildModel() \n";
+ d_modelValues.clear();
TNodeSet constants;
- TNodeSet constants_in_eq_engine;
+ TNodeSet constants_in_eq_engine;
// collect constants in equality engine
- eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine);
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine);
while (!eqcs_i.isFinished()) {
TNode repr = *eqcs_i;
if (repr.getKind() == kind::CONST_BITVECTOR) {
@@ -218,39 +213,39 @@ void CoreSolver::buildModel() {
eq::EqClassIterator it(repr, &d_equalityEngine);
if (!(++it).isFinished() || true) {
constants.insert(repr);
- constants_in_eq_engine.insert(repr);
+ constants_in_eq_engine.insert(repr);
}
}
- ++eqcs_i;
+ ++eqcs_i;
}
// build repr to value map
-
+
eqcs_i = eq::EqClassesIterator(&d_equalityEngine);
while (!eqcs_i.isFinished()) {
TNode repr = *eqcs_i;
++eqcs_i;
-
+
if (repr.getKind() != kind::VARIABLE &&
repr.getKind() != kind::SKOLEM &&
repr.getKind() != kind::CONST_BITVECTOR &&
!d_bv->isSharedTerm(repr)) {
- continue;
+ continue;
}
-
- TypeNode type = repr.getType();
+
+ TypeNode type = repr.getType();
if (type.isBitVector() && repr.getKind()!= kind::CONST_BITVECTOR) {
- Debug("bv-core-model") << " processing " << repr <<"\n";
+ Debug("bv-core-model") << " processing " << repr <<"\n";
// we need to assign a value for it
TypeEnumerator te(type);
- Node val;
+ Node val;
do {
- val = *te;
+ val = *te;
++te;
// Debug("bv-core-model") << " trying value " << val << "\n";
// Debug("bv-core-model") << " is in set? " << constants.count(val) << "\n";
- // Debug("bv-core-model") << " enumerator done? " << te.isFinished() << "\n";
+ // Debug("bv-core-model") << " enumerator done? " << te.isFinished() << "\n";
} while (constants.count(val) != 0 && !(te.isFinished()));
-
+
if (te.isFinished() && constants.count(val) != 0) {
// if we cannot enumerate anymore values we just return the lemma stating that
// at least two of the representatives are equal.
@@ -259,15 +254,15 @@ void CoreSolver::buildModel() {
for (TNodeSet::const_iterator it = constants_in_eq_engine.begin();
it != constants_in_eq_engine.end(); ++it) {
- TNode constant = *it;
+ TNode constant = *it;
if (utils::getSize(constant) == utils::getSize(repr)) {
- representatives.push_back(constant);
+ representatives.push_back(constant);
}
}
for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) {
representatives.push_back(it->first);
}
- std::vector<Node> equalities;
+ std::vector<Node> equalities;
for (unsigned i = 0; i < representatives.size(); ++i) {
for (unsigned j = i + 1; j < representatives.size(); ++j) {
TNode a = representatives[i];
@@ -279,19 +274,19 @@ void CoreSolver::buildModel() {
}
Node lemma = utils::mkOr(equalities);
d_bv->lemma(lemma);
- Debug("bv-core") << " lemma: " << lemma << "\n";
- return;
+ Debug("bv-core") << " lemma: " << lemma << "\n";
+ return;
}
Debug("bv-core-model") << " " << repr << " => " << val <<"\n" ;
constants.insert(val);
- d_modelValues[repr] = val;
+ d_modelValues[repr] = val;
}
}
}
bool CoreSolver::assertFactToEqualityEngine(TNode fact, TNode reason) {
- // Notify the equality engine
- if (d_useEqualityEngine && !d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || !d_bv->getPropagatingSubtheory(fact) == SUB_CORE)) {
+ // Notify the equality engine
+ if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || !d_bv->getPropagatingSubtheory(fact) == SUB_CORE)) {
Debug("bv-slicer-eq") << "CoreSolver::assertFactToEqualityEngine fact=" << fact << endl;
// Debug("bv-slicer-eq") << " reason=" << reason << endl;
bool negated = fact.getKind() == kind::NOT;
@@ -315,8 +310,8 @@ bool CoreSolver::assertFactToEqualityEngine(TNode fact, TNode reason) {
// checking for a conflict
if (d_bv->inConflict()) {
return false;
- }
- return true;
+ }
+ return true;
}
bool CoreSolver::NotifyClass::eqNotifyTriggerEquality(TNode equality, bool value) {
@@ -361,10 +356,10 @@ void CoreSolver::conflict(TNode a, TNode b) {
d_bv->setConflict(conflict);
}
-void CoreSolver::collectModelInfo(TheoryModel* m) {
+void CoreSolver::collectModelInfo(TheoryModel* m, bool fullModel) {
if (options::bitvectorCoreSolver()) {
Unreachable();
- return;
+ return;
}
if (Debug.isOn("bitvector-model")) {
context::CDQueue<Node>::const_iterator it = d_assertionQueue.begin();
@@ -377,11 +372,11 @@ void CoreSolver::collectModelInfo(TheoryModel* m) {
d_bv->computeRelevantTerms(termSet);
m->assertEqualityEngine(&d_equalityEngine, &termSet);
if (isComplete()) {
- Debug("bitvector-model") << "CoreSolver::collectModelInfo complete.";
+ Debug("bitvector-model") << "CoreSolver::collectModelInfo complete.";
for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) {
Node a = it->first;
Node b = it->second;
- m->assertEquality(a, b, true);
+ m->assertEquality(a, b, true);
}
}
}
@@ -390,23 +385,23 @@ Node CoreSolver::getModelValue(TNode var) {
// we don't need to evaluate bv expressions and only look at variable values
// because this only gets called when the core theory is complete (i.e. no other bv
// function symbols are currently asserted)
- Assert (d_slicer->isCoreTerm(var));
-
- Debug("bitvector-model") << "CoreSolver::getModelValue (" << var <<")";
+ Assert (d_slicer->isCoreTerm(var));
+
+ Debug("bitvector-model") << "CoreSolver::getModelValue (" << var <<")";
Assert (isComplete());
TNode repr = d_equalityEngine.getRepresentative(var);
- Node result = Node();
+ Node result = Node();
if (repr.getKind() == kind::CONST_BITVECTOR) {
- result = repr;
+ result = repr;
} else if (d_modelValues.find(repr) == d_modelValues.end()) {
// it may be a shared term that never gets asserted
// result is just Null
Assert(d_bv->isSharedTerm(var));
} else {
- result = d_modelValues[repr];
+ result = d_modelValues[repr];
}
- Debug("bitvector-model") << " => " << result <<"\n";
- return result;
+ Debug("bitvector-model") << " => " << result <<"\n";
+ return result;
}
CoreSolver::Statistics::Statistics()
diff --git a/src/theory/bv/bv_subtheory_core.h b/src/theory/bv/bv_subtheory_core.h
index 423408a7c..b886bbdd5 100644
--- a/src/theory/bv/bv_subtheory_core.h
+++ b/src/theory/bv/bv_subtheory_core.h
@@ -25,21 +25,21 @@ namespace CVC4 {
namespace theory {
namespace bv {
-class Slicer;
-class Base;
+class Slicer;
+class Base;
/**
* Bitvector equality solver
*/
class CoreSolver : public SubtheorySolver {
typedef __gnu_cxx::hash_map<TNode, Node, TNodeHashFunction> ModelValue;
- typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet;
+ typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet;
struct Statistics {
IntStat d_numCallstoCheck;
Statistics();
- ~Statistics();
- };
-
+ ~Statistics();
+ };
+
// NotifyClass: handles call-back from congruence closure module
class NotifyClass : public eq::EqualityEngineNotify {
CoreSolver& d_solver;
@@ -59,13 +59,13 @@ class CoreSolver : public SubtheorySolver {
/** The notify class for d_equalityEngine */
NotifyClass d_notify;
-
+
/** Equality engine */
eq::EqualityEngine d_equalityEngine;
/** Store a propagation to the bv solver */
bool storePropagation(TNode literal);
-
+
/** Store a conflict from merging two constants */
void conflict(TNode a, TNode b);
@@ -74,12 +74,12 @@ class CoreSolver : public SubtheorySolver {
/** To make sure we keep the explanations */
context::CDHashSet<Node, NodeHashFunction> d_reasons;
ModelValue d_modelValues;
- void buildModel();
- bool assertFactToEqualityEngine(TNode fact, TNode reason);
+ void buildModel();
+ bool assertFactToEqualityEngine(TNode fact, TNode reason);
bool decomposeFact(TNode fact);
Node getBaseDecomposition(TNode a);
- Statistics d_statistics;
-public:
+ Statistics d_statistics;
+public:
CoreSolver(context::Context* c, TheoryBV* bv);
~CoreSolver();
bool isComplete() { return d_isCoreTheory; }
@@ -87,8 +87,8 @@ public:
void preRegister(TNode node);
bool check(Theory::Effort e);
void explain(TNode literal, std::vector<TNode>& assumptions);
- void collectModelInfo(TheoryModel* m);
- Node getModelValue(TNode var);
+ void collectModelInfo(TheoryModel* m, bool fullModel);
+ Node getModelValue(TNode var);
void addSharedTerm(TNode t) {
d_equalityEngine.addTriggerTerm(t, THEORY_BV);
}
diff --git a/src/theory/bv/bv_subtheory_inequality.cpp b/src/theory/bv/bv_subtheory_inequality.cpp
index f45250f5b..a3970c9e3 100644
--- a/src/theory/bv/bv_subtheory_inequality.cpp
+++ b/src/theory/bv/bv_subtheory_inequality.cpp
@@ -29,17 +29,17 @@ using namespace CVC4::theory::bv::utils;
bool InequalitySolver::check(Theory::Effort e) {
Debug("bv-subtheory-inequality") << "InequalitySolveR::check("<< e <<")\n";
++(d_statistics.d_numCallstoCheck);
-
- bool ok = true;
+
+ bool ok = true;
while (!done() && ok) {
TNode fact = get();
- Debug("bv-subtheory-inequality") << " "<< fact <<"\n";
+ Debug("bv-subtheory-inequality") << " "<< fact <<"\n";
if (fact.getKind() == kind::EQUAL) {
TNode a = fact[0];
TNode b = fact[1];
ok = d_inequalityGraph.addInequality(a, b, false, fact);
if (ok)
- ok = d_inequalityGraph.addInequality(b, a, false, fact);
+ ok = d_inequalityGraph.addInequality(b, a, false, fact);
} else if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL) {
TNode a = fact[0][0];
TNode b = fact[0][1];
@@ -47,40 +47,40 @@ bool InequalitySolver::check(Theory::Effort e) {
}
if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::BITVECTOR_ULE) {
TNode a = fact[0][1];
- TNode b = fact[0][0];
+ TNode b = fact[0][0];
ok = d_inequalityGraph.addInequality(a, b, true, fact);
// propagate
// if (d_bv->isSharedTerm(a) && d_bv->isSharedTerm(b)) {
- // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b));
+ // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b));
// d_bv->storePropagation(neq, SUB_INEQUALITY);
// d_explanations[neq] = fact;
// }
} else if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::BITVECTOR_ULT) {
TNode a = fact[0][1];
- TNode b = fact[0][0];
+ TNode b = fact[0][0];
ok = d_inequalityGraph.addInequality(a, b, false, fact);
} else if (fact.getKind() == kind::BITVECTOR_ULT) {
TNode a = fact[0];
- TNode b = fact[1];
+ TNode b = fact[1];
ok = d_inequalityGraph.addInequality(a, b, true, fact);
// propagate
// if (d_bv->isSharedTerm(a) && d_bv->isSharedTerm(b)) {
- // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b));
+ // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b));
// d_bv->storePropagation(neq, SUB_INEQUALITY);
// d_explanations[neq] = fact;
// }
} else if (fact.getKind() == kind::BITVECTOR_ULE) {
TNode a = fact[0];
- TNode b = fact[1];
+ TNode b = fact[1];
ok = d_inequalityGraph.addInequality(a, b, false, fact);
}
}
-
+
if (!ok) {
std::vector<TNode> conflict;
- d_inequalityGraph.getConflict(conflict);
+ d_inequalityGraph.getConflict(conflict);
d_bv->setConflict(utils::flattenAnd(conflict));
- return false;
+ return false;
}
if (isComplete() && Theory::fullEffort(e)) {
@@ -89,36 +89,39 @@ bool InequalitySolver::check(Theory::Effort e) {
std::vector<Node> lemmas;
d_inequalityGraph.checkDisequalities(lemmas);
for(unsigned i = 0; i < lemmas.size(); ++i) {
- d_bv->lemma(lemmas[i]);
+ d_bv->lemma(lemmas[i]);
}
}
- return true;
+ return true;
}
EqualityStatus InequalitySolver::getEqualityStatus(TNode a, TNode b) {
+ if (!isComplete())
+ return EQUALITY_UNKNOWN;
+
Node a_lt_b = utils::mkNode(kind::BITVECTOR_ULT, a, b);
Node b_lt_a = utils::mkNode(kind::BITVECTOR_ULT, b, a);
// if an inequality containing the terms has been asserted then we know
// the equality is false
if (d_assertionSet.contains(a_lt_b) || d_assertionSet.contains(b_lt_a)) {
- return EQUALITY_FALSE;
+ return EQUALITY_FALSE;
}
-
+
if (!d_inequalityGraph.hasValueInModel(a) ||
!d_inequalityGraph.hasValueInModel(b)) {
- return EQUALITY_UNKNOWN;
+ return EQUALITY_UNKNOWN;
}
// TODO: check if this disequality is entailed by inequalities via transitivity
-
+
BitVector a_val = d_inequalityGraph.getValueInModel(a);
BitVector b_val = d_inequalityGraph.getValueInModel(b);
-
+
if (a_val == b_val) {
- return EQUALITY_TRUE_IN_MODEL;
+ return EQUALITY_TRUE_IN_MODEL;
} else {
- return EQUALITY_FALSE_IN_MODEL;
+ return EQUALITY_FALSE_IN_MODEL;
}
}
@@ -126,19 +129,19 @@ void InequalitySolver::assertFact(TNode fact) {
d_assertionQueue.push_back(fact);
d_assertionSet.insert(fact);
if (!isInequalityOnly(fact)) {
- d_isComplete = false;
+ d_isComplete = false;
}
}
bool InequalitySolver::isInequalityOnly(TNode node) {
if (d_ineqTermCache.find(node) != d_ineqTermCache.end()) {
- return d_ineqTermCache[node];
+ return d_ineqTermCache[node];
}
-
+
if (node.getKind() == kind::NOT) {
- node = node[0];
+ node = node[0];
}
-
+
if (node.getKind() != kind::EQUAL &&
node.getKind() != kind::BITVECTOR_ULT &&
node.getKind() != kind::BITVECTOR_ULE &&
@@ -146,50 +149,50 @@ bool InequalitySolver::isInequalityOnly(TNode node) {
node.getKind() != kind::SELECT &&
node.getKind() != kind::STORE &&
node.getMetaKind() != kind::metakind::VARIABLE) {
- return false;
+ return false;
}
- bool res = true;
+ bool res = true;
for (unsigned i = 0; i < node.getNumChildren(); ++i) {
res = res && isInequalityOnly(node[i]);
}
- d_ineqTermCache[node] = res;
- return res;
+ d_ineqTermCache[node] = res;
+ return res;
}
void InequalitySolver::explain(TNode literal, std::vector<TNode>& assumptions) {
Assert (d_explanations.find(literal) != d_explanations.end());
TNode explanation = d_explanations[literal];
assumptions.push_back(explanation);
- Debug("bv-inequality-explain") << "InequalitySolver::explain " << literal << " with " << explanation <<"\n";
+ Debug("bv-inequality-explain") << "InequalitySolver::explain " << literal << " with " << explanation <<"\n";
}
void InequalitySolver::propagate(Theory::Effort e) {
- Assert (false);
+ Assert (false);
}
-void InequalitySolver::collectModelInfo(TheoryModel* m) {
- Debug("bitvector-model") << "InequalitySolver::collectModelInfo \n";
+void InequalitySolver::collectModelInfo(TheoryModel* m, bool fullModel) {
+ Debug("bitvector-model") << "InequalitySolver::collectModelInfo \n";
std::vector<Node> model;
d_inequalityGraph.getAllValuesInModel(model);
for (unsigned i = 0; i < model.size(); ++i) {
- Assert (model[i].getKind() == kind::EQUAL);
- m->assertEquality(model[i][0], model[i][1], true);
+ Assert (model[i].getKind() == kind::EQUAL);
+ m->assertEquality(model[i][0], model[i][1], true);
}
}
Node InequalitySolver::getModelValue(TNode var) {
- Assert (isInequalityOnly(var));
- Debug("bitvector-model") << "InequalitySolver::getModelValue (" << var <<")";
+ Assert (isInequalityOnly(var));
+ Debug("bitvector-model") << "InequalitySolver::getModelValue (" << var <<")";
Assert (isComplete());
- Node result = Node();
+ Node result = Node();
if (!d_inequalityGraph.hasValueInModel(var)) {
Assert (d_bv->isSharedTerm(var));
} else {
BitVector val = d_inequalityGraph.getValueInModel(var);
result = utils::mkConst(val);
}
- Debug("bitvector-model") << " => " << result <<"\n";
- return result;
+ Debug("bitvector-model") << " => " << result <<"\n";
+ return result;
}
InequalitySolver::Statistics::Statistics()
diff --git a/src/theory/bv/bv_subtheory_inequality.h b/src/theory/bv/bv_subtheory_inequality.h
index 390a329ff..6e0139e09 100644
--- a/src/theory/bv/bv_subtheory_inequality.h
+++ b/src/theory/bv/bv_subtheory_inequality.h
@@ -9,7 +9,7 @@
** See the file COPYING in the top-level source directory for licensing
** information.\endverbatim
**
- ** \brief Algebraic solver.
+ ** \brief Algebraic solver.
**
** Algebraic solver.
**/
@@ -31,16 +31,16 @@ class InequalitySolver: public SubtheorySolver {
struct Statistics {
IntStat d_numCallstoCheck;
Statistics();
- ~Statistics();
- };
+ ~Statistics();
+ };
- context::CDHashSet<Node, NodeHashFunction> d_assertionSet;
+ context::CDHashSet<Node, NodeHashFunction> d_assertionSet;
InequalityGraph d_inequalityGraph;
- context::CDHashMap<Node, TNode, NodeHashFunction> d_explanations;
+ context::CDHashMap<Node, TNode, NodeHashFunction> d_explanations;
context::CDO<bool> d_isComplete;
- __gnu_cxx::hash_map<TNode, bool, TNodeHashFunction> d_ineqTermCache;
- bool isInequalityOnly(TNode node);
- Statistics d_statistics;
+ __gnu_cxx::hash_map<TNode, bool, TNodeHashFunction> d_ineqTermCache;
+ bool isInequalityOnly(TNode node);
+ Statistics d_statistics;
public:
InequalitySolver(context::Context* c, TheoryBV* bv)
: SubtheorySolver(c, bv),
@@ -51,19 +51,19 @@ public:
d_ineqTermCache(),
d_statistics()
{}
-
+
bool check(Theory::Effort e);
- void propagate(Theory::Effort e);
+ void propagate(Theory::Effort e);
void explain(TNode literal, std::vector<TNode>& assumptions);
bool isComplete() { return d_isComplete; }
- void collectModelInfo(TheoryModel* m);
- Node getModelValue(TNode var);
+ void collectModelInfo(TheoryModel* m, bool fullModel);
+ Node getModelValue(TNode var);
EqualityStatus getEqualityStatus(TNode a, TNode b);
- void assertFact(TNode fact);
-};
+ void assertFact(TNode fact);
+};
}
}
}
-#endif /* __CVC4__THEORY__BV__BV_SUBTHEORY__INEQUALITY_H */
+#endif /* __CVC4__THEORY__BV__BV_SUBTHEORY__INEQUALITY_H */
diff --git a/src/theory/bv/kinds b/src/theory/bv/kinds
index 052e477ea..aeae1073e 100644
--- a/src/theory/bv/kinds
+++ b/src/theory/bv/kinds
@@ -120,6 +120,14 @@ parameterized BITVECTOR_SIGN_EXTEND BITVECTOR_SIGN_EXTEND_OP 1 "bit-vector sign-
parameterized BITVECTOR_ROTATE_LEFT BITVECTOR_ROTATE_LEFT_OP 1 "bit-vector rotate left"
parameterized BITVECTOR_ROTATE_RIGHT BITVECTOR_ROTATE_RIGHT_OP 1 "bit-vector rotate right"
+constant INT_TO_BITVECTOR_OP \
+ ::CVC4::IntToBitVector \
+ "::CVC4::UnsignedHashFunction< ::CVC4::IntToBitVector >" \
+ "util/bitvector.h" \
+ "operator for the integer conversion to bit-vector"
+parameterized INT_TO_BITVECTOR INT_TO_BITVECTOR_OP 1 "integer conversion to bit-vector"
+operator BITVECTOR_TO_NAT 1 "bit-vector conversion to (nonnegative) integer"
+
typerule CONST_BITVECTOR ::CVC4::theory::bv::BitVectorConstantTypeRule
typerule BITVECTOR_AND ::CVC4::theory::bv::BitVectorFixedWidthTypeRule
@@ -166,4 +174,7 @@ typerule BITVECTOR_REPEAT ::CVC4::theory::bv::BitVectorRepeatTypeRule
typerule BITVECTOR_ZERO_EXTEND ::CVC4::theory::bv::BitVectorExtendTypeRule
typerule BITVECTOR_SIGN_EXTEND ::CVC4::theory::bv::BitVectorExtendTypeRule
+typerule BITVECTOR_TO_NAT ::CVC4::theory::bv::BitVectorConversionTypeRule
+typerule INT_TO_BITVECTOR ::CVC4::theory::bv::BitVectorConversionTypeRule
+
endtheory
diff --git a/src/theory/bv/options b/src/theory/bv/options
index 7b87baa21..077299d1f 100644
--- a/src/theory/bv/options
+++ b/src/theory/bv/options
@@ -22,5 +22,11 @@ option bitvectorCoreSolver --bv-core-solver bool
option bvToBool --bv-to-bool bool
lift bit-vectors of size 1 to booleans when possible
+
+option bvPropagate --bv-propagate bool :default true
+ use bit-vector propagation in the bit-blaster
+
+option bvEquality --bv-eq bool :default true
+ use the equality engine for the bit-vector theory
endmodule
diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp
index cb68a0f65..a2de951aa 100644
--- a/src/theory/bv/theory_bv.cpp
+++ b/src/theory/bv/theory_bv.cpp
@@ -49,29 +49,32 @@ TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel&
d_literalsToPropagateIndex(c, 0),
d_propagatedBy(c)
{
- SubtheorySolver* core_solver = new CoreSolver(c, this);
- d_subtheories.push_back(core_solver);
- d_subtheoryMap[SUB_CORE] = core_solver;
-
+ if (options::bvEquality()) {
+ SubtheorySolver* core_solver = new CoreSolver(c, this);
+ d_subtheories.push_back(core_solver);
+ d_subtheoryMap[SUB_CORE] = core_solver;
+ }
if (options::bitvectorInequalitySolver()) {
- SubtheorySolver* ineq_solver = new InequalitySolver(c, this);
+ SubtheorySolver* ineq_solver = new InequalitySolver(c, this);
d_subtheories.push_back(ineq_solver);
d_subtheoryMap[SUB_INEQUALITY] = ineq_solver;
}
-
- SubtheorySolver* bb_solver = new BitblastSolver(c, this);
+
+ SubtheorySolver* bb_solver = new BitblastSolver(c, this);
d_subtheories.push_back(bb_solver);
d_subtheoryMap[SUB_BITBLAST] = bb_solver;
}
TheoryBV::~TheoryBV() {
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
- delete d_subtheories[i];
+ delete d_subtheories[i];
}
}
void TheoryBV::setMasterEqualityEngine(eq::EqualityEngine* eq) {
- dynamic_cast<CoreSolver*>(d_subtheoryMap[SUB_CORE])->setMasterEqualityEngine(eq);
+ if (options::bvEquality()) {
+ dynamic_cast<CoreSolver*>(d_subtheoryMap[SUB_CORE])->setMasterEqualityEngine(eq);
+ }
}
TheoryBV::Statistics::Statistics():
@@ -110,7 +113,7 @@ void TheoryBV::preRegisterTerm(TNode node) {
return;
}
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
- d_subtheories[i]->preRegister(node);
+ d_subtheories[i]->preRegister(node);
}
}
@@ -131,22 +134,22 @@ void TheoryBV::checkForLemma(TNode fact) {
if (fact[0].getKind() == kind::BITVECTOR_UREM_TOTAL) {
TNode urem = fact[0];
TNode result = fact[1];
- TNode divisor = urem[1];
+ TNode divisor = urem[1];
Node result_ult_div = mkNode(kind::BITVECTOR_ULT, result, divisor);
Node divisor_eq_0 = mkNode(kind::EQUAL,
divisor,
- mkConst(BitVector(getSize(divisor), 0u)));
+ mkConst(BitVector(getSize(divisor), 0u)));
Node split = utils::mkNode(kind::OR, divisor_eq_0, mkNode(kind::NOT, fact), result_ult_div);
lemma(split);
}
if (fact[1].getKind() == kind::BITVECTOR_UREM_TOTAL) {
TNode urem = fact[1];
TNode result = fact[0];
- TNode divisor = urem[1];
+ TNode divisor = urem[1];
Node result_ult_div = mkNode(kind::BITVECTOR_ULT, result, divisor);
Node divisor_eq_0 = mkNode(kind::EQUAL,
divisor,
- mkConst(BitVector(getSize(divisor), 0u)));
+ mkConst(BitVector(getSize(divisor), 0u)));
Node split = utils::mkNode(kind::OR, divisor_eq_0, mkNode(kind::NOT, fact), result_ult_div);
lemma(split);
}
@@ -162,9 +165,9 @@ void TheoryBV::check(Effort e)
}
if (Theory::fullEffort(e)) {
- ++(d_statistics.d_numCallsToCheckFullEffort);
+ ++(d_statistics.d_numCallsToCheckFullEffort);
} else {
- ++(d_statistics.d_numCallsToCheckStandardEffort);
+ ++(d_statistics.d_numCallsToCheckStandardEffort);
}
// if we are already in conflict just return the conflict
if (inConflict()) {
@@ -174,28 +177,29 @@ void TheoryBV::check(Effort e)
while (!done()) {
TNode fact = get().assertion;
- checkForLemma(fact);
+ checkForLemma(fact);
+
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
- d_subtheories[i]->assertFact(fact);
+ d_subtheories[i]->assertFact(fact);
}
}
bool ok = true;
bool complete = false;
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
- Assert (!inConflict());
+ Assert (!inConflict());
ok = d_subtheories[i]->check(e);
- complete = d_subtheories[i]->isComplete();
+ complete = d_subtheories[i]->isComplete();
if (!ok) {
// if we are in a conflict no need to check with other theories
Assert (inConflict());
sendConflict();
- return;
+ return;
}
if (complete) {
// if the last subtheory was complete we stop
- return;
+ return;
}
}
}
@@ -205,8 +209,8 @@ void TheoryBV::collectModelInfo( TheoryModel* m, bool fullModel ){
// Assert (fullModel); // can only query full model
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
if (d_subtheories[i]->isComplete()) {
- d_subtheories[i]->collectModelInfo(m);
- return;
+ d_subtheories[i]->collectModelInfo(m, fullModel);
+ return;
}
}
}
@@ -215,10 +219,10 @@ Node TheoryBV::getModelValue(TNode var) {
Assert(!inConflict());
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
if (d_subtheories[i]->isComplete()) {
- return d_subtheories[i]->getModelValue(var);
+ return d_subtheories[i]->getModelValue(var);
}
}
- Unreachable();
+ Unreachable();
}
void TheoryBV::propagate(Effort e) {
@@ -236,7 +240,7 @@ void TheoryBV::propagate(Effort e) {
bool ok = true;
for (; d_literalsToPropagateIndex < d_literalsToPropagate.size() && ok; d_literalsToPropagateIndex = d_literalsToPropagateIndex + 1) {
TNode literal = d_literalsToPropagate[d_literalsToPropagateIndex];
- // temporary fix for incremental bit-blasting
+ // temporary fix for incremental bit-blasting
if (d_valuation.isSatLiteral(literal)) {
Debug("bitvector::propagate") << "TheoryBV:: propagating " << literal <<"\n";
ok = d_out->propagate(literal);
@@ -286,14 +290,14 @@ Node TheoryBV::ppRewrite(TNode t)
if (options::bitvectorCoreSolver() && t.getKind() == kind::EQUAL) {
std::vector<Node> equalities;
Slicer::splitEqualities(t, equalities);
- return utils::mkAnd(equalities);
+ return utils::mkAnd(equalities);
}
-
+
return t;
}
void TheoryBV::presolve() {
- Debug("bitvector") << "TheoryBV::presolve" << endl;
+ Debug("bitvector") << "TheoryBV::presolve" << endl;
}
bool TheoryBV::storePropagation(TNode literal, SubTheory subtheory)
@@ -318,7 +322,7 @@ bool TheoryBV::storePropagation(TNode literal, SubTheory subtheory)
// Safe to ignore this one, subtheory should produce a conflict
return true;
}
-
+
d_propagatedBy[literal] = subtheory;
}
@@ -366,7 +370,7 @@ Node TheoryBV::explain(TNode node) {
void TheoryBV::addSharedTerm(TNode t) {
Debug("bitvector::sharing") << indent() << "TheoryBV::addSharedTerm(" << t << ")" << std::endl;
d_sharedTermsSet.insert(t);
- if (!options::bitvectorEagerBitblast() && d_useEqualityEngine) {
+ if (!options::bitvectorEagerBitblast() && options::bvEquality()) {
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
d_subtheories[i]->addSharedTerm(t);
}
@@ -379,11 +383,11 @@ EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b)
if (options::bitvectorEagerBitblast()) {
return EQUALITY_UNKNOWN;
}
-
+
for (unsigned i = 0; i < d_subtheories.size(); ++i) {
EqualityStatus status = d_subtheories[i]->getEqualityStatus(a, b);
if (status != EQUALITY_UNKNOWN) {
- return status;
+ return status;
}
}
return EQUALITY_UNKNOWN; ;
diff --git a/src/theory/bv/theory_bv_rewrite_rules.h b/src/theory/bv/theory_bv_rewrite_rules.h
index baaf7e133..8426afb59 100644
--- a/src/theory/bv/theory_bv_rewrite_rules.h
+++ b/src/theory/bv/theory_bv_rewrite_rules.h
@@ -66,6 +66,9 @@ enum RewriteRuleId {
SremEliminate,
ZeroExtendEliminate,
SignExtendEliminate,
+ BVToNatEliminate,
+ IntToBVEliminate,
+
/// ground term evaluation
EvalEquals,
EvalConcat,
@@ -173,13 +176,15 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) {
case FailEq: out << "FailEq"; return out;
case SimplifyEq: out << "SimplifyEq"; return out;
case ReflexivityEq: out << "ReflexivityEq"; return out;
- case UgtEliminate: out << "UgtEliminate"; return out;
- case SgtEliminate: out << "SgtEliminate"; return out;
- case UgeEliminate: out << "UgeEliminate"; return out;
- case SgeEliminate: out << "SgeEliminate"; return out;
+ case UgtEliminate: out << "UgtEliminate"; return out;
+ case SgtEliminate: out << "SgtEliminate"; return out;
+ case UgeEliminate: out << "UgeEliminate"; return out;
+ case SgeEliminate: out << "SgeEliminate"; return out;
case RepeatEliminate: out << "RepeatEliminate"; return out;
case RotateLeftEliminate: out << "RotateLeftEliminate"; return out;
case RotateRightEliminate:out << "RotateRightEliminate";return out;
+ case BVToNatEliminate: out << "BVToNatEliminate"; return out;
+ case IntToBVEliminate: out << "IntToBVEliminate"; return out;
case NandEliminate: out << "NandEliminate"; return out;
case NorEliminate : out << "NorEliminate"; return out;
case SdivEliminate : out << "SdivEliminate"; return out;
@@ -214,7 +219,7 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) {
case ExtractBitwise : out << "ExtractBitwise"; return out;
case ExtractNot : out << "ExtractNot"; return out;
case ExtractArith : out << "ExtractArith"; return out;
- case ExtractArith2 : out << "ExtractArith2"; return out;
+ case ExtractArith2 : out << "ExtractArith2"; return out;
case DoubleNeg : out << "DoubleNeg"; return out;
case NotConcat : out << "NotConcat"; return out;
case NotAnd : out << "NotAnd"; return out;
@@ -226,7 +231,7 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) {
case BitwiseNotOr : out << "BitwiseNotOr"; return out;
case XorNot : out << "XorNot"; return out;
case LtSelf : out << "LtSelf"; return out;
- case LteSelf : out << "LteSelf"; return out;
+ case LteSelf : out << "LteSelf"; return out;
case UltZero : out << "UltZero"; return out;
case UleZero : out << "UleZero"; return out;
case ZeroUle : out << "ZeroUle"; return out;
@@ -491,7 +496,8 @@ struct AllRewriteRules {
RewriteRule<BitwiseEq> rule113;
RewriteRule<UltOne> rule114;
RewriteRule<SltZero> rule115;
-
+ RewriteRule<BVToNatEliminate> rule116;
+ RewriteRule<IntToBVEliminate> rule117;
};
template<> inline
diff --git a/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h b/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h
index a5368d2c9..9f3d12415 100644
--- a/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h
+++ b/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h
@@ -198,8 +198,9 @@ Node RewriteRule<EvalNeg>::apply(TNode node) {
}
template<> inline
bool RewriteRule<EvalUdiv>::applies(TNode node) {
- return (node.getKind() == kind::BITVECTOR_UDIV_TOTAL &&
- utils::isBVGroundTerm(node));
+ return (utils::isBVGroundTerm(node) &&
+ (node.getKind() == kind::BITVECTOR_UDIV_TOTAL ||
+ (node.getKind() == kind::BITVECTOR_UDIV && node[1].isConst())));
}
template<> inline
@@ -213,8 +214,9 @@ Node RewriteRule<EvalUdiv>::apply(TNode node) {
}
template<> inline
bool RewriteRule<EvalUrem>::applies(TNode node) {
- return (node.getKind() == kind::BITVECTOR_UREM_TOTAL &&
- utils::isBVGroundTerm(node));
+ return (utils::isBVGroundTerm(node) &&
+ (node.getKind() == kind::BITVECTOR_UREM_TOTAL ||
+ (node.getKind() == kind::BITVECTOR_UREM && node[1].isConst())));
}
template<> inline
diff --git a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h
index cf36633fa..b513dbf90 100644
--- a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h
+++ b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h
@@ -231,6 +231,57 @@ Node RewriteRule<RotateRightEliminate>::apply(TNode node) {
}
template<>
+bool RewriteRule<BVToNatEliminate>::applies(TNode node) {
+ return (node.getKind() == kind::BITVECTOR_TO_NAT);
+}
+
+template<>
+Node RewriteRule<BVToNatEliminate>::apply(TNode node) {
+ Debug("bv-rewrite") << "RewriteRule<BVToNatEliminate>(" << node << ")" << std::endl;
+
+ const unsigned size = utils::getSize(node[0]);
+ NodeManager* const nm = NodeManager::currentNM();
+ const Node z = nm->mkConst(Rational(0));
+ const Node bvone = nm->mkConst(BitVector(1u, 1u));
+
+ NodeBuilder<> result(kind::PLUS);
+ Integer i = 1;
+ for(unsigned bit = 0; bit < size; ++bit, i *= 2) {
+ Node cond = nm->mkNode(kind::EQUAL, nm->mkNode(nm->mkConst(BitVectorExtract(bit, bit)), node[0]), bvone);
+ result << nm->mkNode(kind::ITE, cond, nm->mkConst(Rational(i)), z);
+ }
+
+ return Node(result);
+}
+
+template<>
+bool RewriteRule<IntToBVEliminate>::applies(TNode node) {
+ return (node.getKind() == kind::INT_TO_BITVECTOR);
+}
+
+template<>
+Node RewriteRule<IntToBVEliminate>::apply(TNode node) {
+ Debug("bv-rewrite") << "RewriteRule<IntToBVEliminate>(" << node << ")" << std::endl;
+
+ const unsigned size = node.getOperator().getConst<IntToBitVector>().size;
+ NodeManager* const nm = NodeManager::currentNM();
+ const Node bvzero = nm->mkConst(BitVector(1u, 0u));
+ const Node bvone = nm->mkConst(BitVector(1u, 1u));
+
+ std::vector<Node> v;
+ Integer i = 2;
+ while(v.size() < size) {
+ Node cond = nm->mkNode(kind::GEQ, nm->mkNode(kind::INTS_MODULUS_TOTAL, node[0], nm->mkConst(Rational(i))), nm->mkConst(Rational(i, 2)));
+ v.push_back(nm->mkNode(kind::ITE, cond, bvone, bvzero));
+ i *= 2;
+ }
+
+ NodeBuilder<> result(kind::BITVECTOR_CONCAT);
+ result.append(v.rbegin(), v.rend());
+ return Node(result);
+}
+
+template<>
bool RewriteRule<NandEliminate>::applies(TNode node) {
return (node.getKind() == kind::BITVECTOR_NAND &&
node.getNumChildren() == 2);
diff --git a/src/theory/bv/theory_bv_rewrite_rules_simplification.h b/src/theory/bv/theory_bv_rewrite_rules_simplification.h
index d660dde29..ff7d67cb0 100644
--- a/src/theory/bv/theory_bv_rewrite_rules_simplification.h
+++ b/src/theory/bv/theory_bv_rewrite_rules_simplification.h
@@ -45,7 +45,9 @@ template<> inline
Node RewriteRule<ShlByConst>::apply(TNode node) {
Debug("bv-rewrite") << "RewriteRule<ShlByConst>(" << node << ")" << std::endl;
Integer amount = node[1].getConst<BitVector>().toInteger();
-
+ if (amount == 0) {
+ return node[0];
+ }
Node a = node[0];
uint32_t size = utils::getSize(a);
@@ -59,6 +61,7 @@ Node RewriteRule<ShlByConst>::apply(TNode node) {
Assert(amount < Integer(1).multiplyByPow2(32));
uint32_t uint32_amount = amount.toUnsignedInt();
+
Node left = utils::mkExtract(a, size - 1 - uint32_amount, 0);
Node right = utils::mkConst(BitVector(uint32_amount, Integer(0)));
return utils::mkConcat(left, right);
@@ -81,6 +84,9 @@ template<> inline
Node RewriteRule<LshrByConst>::apply(TNode node) {
Debug("bv-rewrite") << "RewriteRule<LshrByConst>(" << node << ")" << std::endl;
Integer amount = node[1].getConst<BitVector>().toInteger();
+ if (amount == 0) {
+ return node[0];
+ }
Node a = node[0];
uint32_t size = utils::getSize(a);
@@ -117,7 +123,10 @@ template<> inline
Node RewriteRule<AshrByConst>::apply(TNode node) {
Debug("bv-rewrite") << "RewriteRule<AshrByConst>(" << node << ")" << std::endl;
Integer amount = node[1].getConst<BitVector>().toInteger();
-
+ if (amount == 0) {
+ return node[0];
+ }
+
Node a = node[0];
uint32_t size = utils::getSize(a);
Node sign_bit = utils::mkExtract(a, size-1, size-1);
@@ -812,7 +821,9 @@ Node RewriteRule<UdivPow2>::apply(TNode node) {
Debug("bv-rewrite") << "RewriteRule<UdivPow2>(" << node << ")" << std::endl;
Node a = node[0];
unsigned power = utils::isPow2Const(node[1]) -1;
-
+ if (power == 0) {
+ return a;
+ }
Node extract = utils::mkExtract(a, utils::getSize(node) - 1, power);
Node zeros = utils::mkConst(power, 0);
diff --git a/src/theory/bv/theory_bv_rewriter.cpp b/src/theory/bv/theory_bv_rewriter.cpp
index 7844e5b92..2467ae3f1 100644
--- a/src/theory/bv/theory_bv_rewriter.cpp
+++ b/src/theory/bv/theory_bv_rewriter.cpp
@@ -70,7 +70,7 @@ RewriteResponse TheoryBVRewriter::postRewrite(TNode node) {
return res;
}
-RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool prerewrite) {
// reduce common subexpressions on both sides
Node resultNode = LinearRewriteStrategy
< RewriteRule<EvalUlt>,
@@ -82,7 +82,7 @@ RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool preregister) {
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule < EvalSlt >
>::apply(node);
@@ -97,7 +97,7 @@ RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool preregister){
// return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<EvalUle>,
RewriteRule<UleMax>,
@@ -109,7 +109,7 @@ RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool preregister){
return RewriteResponse(resultNode == node ? REWRITE_DONE : REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule <EvalSle>,
RewriteRule <SleEliminate>
@@ -117,7 +117,7 @@ RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool preregister){
return RewriteResponse(resultNode == node? REWRITE_DONE : REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<UgtEliminate>
>::apply(node);
@@ -125,7 +125,7 @@ RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool preregister){
return RewriteResponse(REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<SgtEliminate>
//RewriteRule<SltEliminate>
@@ -134,7 +134,7 @@ RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool preregister){
return RewriteResponse(REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<UgeEliminate>
>::apply(node);
@@ -142,7 +142,7 @@ RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool preregister){
return RewriteResponse(REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<SgeEliminate>
// RewriteRule<SleEliminate>
@@ -151,7 +151,7 @@ RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool preregister){
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool prerewrite){
Node resultNode = node;
if(RewriteRule<NotXor>::applies(node)) {
@@ -166,7 +166,7 @@ RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool preregister){
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool prerewrite) {
Node resultNode = node;
if (RewriteRule<ExtractConcat>::applies(node)) {
@@ -206,7 +206,7 @@ RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool preregister) {
}
-RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<ConcatFlatten>,
@@ -220,63 +220,70 @@ RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool preregister) {
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteAnd(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteAnd(TNode node, bool prerewrite) {
Node resultNode = node;
-
resultNode = LinearRewriteStrategy
< RewriteRule<FlattenAssocCommut>,
- RewriteRule<AndSimplify>,
- RewriteRule<BitwiseSlicing>
+ RewriteRule<AndSimplify>
>::apply(node);
- if (resultNode.getKind() != node.getKind()) {
- return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ if (!prerewrite) {
+ resultNode = LinearRewriteStrategy
+ < RewriteRule<BitwiseSlicing>
+ >::apply(resultNode);
+
+ if (resultNode.getKind() != node.getKind()) {
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ }
}
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteOr(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteOr(TNode node, bool prerewrite){
Node resultNode = node;
-
resultNode = LinearRewriteStrategy
< RewriteRule<FlattenAssocCommut>,
- RewriteRule<OrSimplify>,
- RewriteRule<BitwiseSlicing>
- >::apply(node);
+ RewriteRule<OrSimplify>
+ >::apply(node);
- if (resultNode.getKind() != node.getKind()) {
- return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ if (!prerewrite) {
+ resultNode = LinearRewriteStrategy
+ < RewriteRule<BitwiseSlicing>
+ >::apply(resultNode);
+
+ if (resultNode.getKind() != node.getKind()) {
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ }
}
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteXor(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteXor(TNode node, bool prerewrite) {
Node resultNode = node;
-
resultNode = LinearRewriteStrategy
< RewriteRule<FlattenAssocCommut>, // flatten the expression
RewriteRule<XorSimplify>, // simplify duplicates and constants
RewriteRule<XorZero>, // checks if the constant part is zero and eliminates it
RewriteRule<BitwiseSlicing>
- >::apply(node);
-
- // this simplification introduces new terms and might require further
- // rewriting
- if (RewriteRule<XorOne>::applies(resultNode)) {
- resultNode = RewriteRule<XorOne>::run<false> (resultNode);
- return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
- }
+ >::apply(node);
- if (resultNode.getKind() != node.getKind()) {
- return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ if (!prerewrite) {
+ resultNode = LinearRewriteStrategy
+ < RewriteRule<XorOne>,
+ RewriteRule <BitwiseSlicing>
+ >::apply(resultNode);
+
+ if (resultNode.getKind() != node.getKind()) {
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+ }
}
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<XnorEliminate>
>::apply(node);
@@ -284,7 +291,7 @@ RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<NandEliminate>
>::apply(node);
@@ -292,7 +299,7 @@ RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<NorEliminate>
>::apply(node);
@@ -300,7 +307,7 @@ RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<CompEliminate>
>::apply(node);
@@ -308,7 +315,7 @@ RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool prerewrite) {
Node resultNode = node;
resultNode = LinearRewriteStrategy
@@ -318,7 +325,7 @@ RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) {
>::apply(node);
// only apply if every subterm was already rewritten
- if (!preregister) {
+ if (!prerewrite) {
// distributes multiplication by constant over +, - and unary -
if(RewriteRule<MultDistribConst>::applies(resultNode)) {
resultNode = RewriteRule<MultDistribConst>::run<false>(resultNode);
@@ -333,23 +340,28 @@ RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewritePlus(TNode node, bool preregister) {
- if (preregister) {
- return RewriteResponse(REWRITE_DONE, node);
+RewriteResponse TheoryBVRewriter::RewritePlus(TNode node, bool prerewrite) {
+ Node resultNode = node;
+ if (prerewrite) {
+ resultNode = LinearRewriteStrategy
+ < RewriteRule<FlattenAssocCommut>
+ >::apply(node);
+ return RewriteResponse(REWRITE_DONE, resultNode);
}
- Node resultNode = LinearRewriteStrategy
- < RewriteRule<FlattenAssocCommut>,
+
+ resultNode = LinearRewriteStrategy
+ < RewriteRule<FlattenAssocCommut>,
RewriteRule<PlusCombineLikeTerms>
- // RewriteRule<PlusLiftConcat>
>::apply(node);
- if (resultNode == node) {
- return RewriteResponse(REWRITE_DONE, resultNode);
- } else {
- return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+
+ if (node != resultNode) {
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
+
+ return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool prerewrite){
// return RewriteResponse(REWRITE_DONE, node);
Node resultNode = LinearRewriteStrategy
< RewriteRule<SubEliminate>
@@ -358,7 +370,7 @@ RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool preregister){
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool prerewrite) {
Node resultNode = node;
resultNode = LinearRewriteStrategy
@@ -372,7 +384,7 @@ RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
- if(!preregister) {
+ if(!prerewrite) {
if (RewriteRule<NegMult>::applies(node)) {
resultNode = RewriteRule<NegMult>::run<false>(node);
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
@@ -382,8 +394,27 @@ RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) {
return RewriteResponse(REWRITE_DONE, resultNode);
}
+RewriteResponse TheoryBVRewriter::RewriteUdiv(TNode node, bool prerewrite){
+ Node resultNode = node;
+
+ if(node[1].isConst() && node[1].getConst<BitVector>().getValue() != 0) {
+ return RewriteUdivTotal(node, prerewrite);
+ }
-RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool preregister){
+ return RewriteResponse(REWRITE_DONE, resultNode);
+}
+
+RewriteResponse TheoryBVRewriter::RewriteUrem(TNode node, bool prerewrite){
+ Node resultNode = node;
+
+ if(node[1].isConst() && node[1].getConst<BitVector>().getValue() != 0) {
+ return RewriteUremTotal(node, prerewrite);
+ }
+
+ return RewriteResponse(REWRITE_DONE, resultNode);
+}
+
+RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool prerewrite){
Node resultNode = node;
if(RewriteRule<UdivPow2>::applies(node)) {
@@ -400,7 +431,7 @@ RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool preregister)
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool prerewrite) {
Node resultNode = node;
if(RewriteRule<UremPow2>::applies(node)) {
@@ -416,7 +447,7 @@ RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool preregister)
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<SmodEliminate>
>::apply(node);
@@ -424,7 +455,7 @@ RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<SdivEliminate>
>::apply(node);
@@ -432,14 +463,14 @@ RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSrem(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteSrem(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<SremEliminate>
>::apply(node);
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool prerewrite) {
Node resultNode = node;
if(RewriteRule<ShlByConst>::applies(node)) {
resultNode = RewriteRule<ShlByConst>::run <false> (node);
@@ -454,7 +485,7 @@ RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool preregister) {
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool prerewrite) {
Node resultNode = node;
if(RewriteRule<LshrByConst>::applies(node)) {
resultNode = RewriteRule<LshrByConst>::run <false> (node);
@@ -469,7 +500,7 @@ RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool preregister) {
return RewriteResponse(REWRITE_DONE, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool prerewrite) {
Node resultNode = node;
if(RewriteRule<AshrByConst>::applies(node)) {
resultNode = RewriteRule<AshrByConst>::run <false> (node);
@@ -485,7 +516,7 @@ RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool preregister) {
}
-RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<RepeatEliminate >
>::apply(node);
@@ -493,7 +524,7 @@ RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool preregister) {
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<ZeroExtendEliminate >
>::apply(node);
@@ -501,7 +532,7 @@ RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool preregister
return RewriteResponse(REWRITE_AGAIN, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<EvalSignExtend>
>::apply(node);
@@ -511,7 +542,7 @@ RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool preregister
}
-RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool preregister) {
+RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<RotateRightEliminate >
>::apply(node);
@@ -519,16 +550,32 @@ RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool preregiste
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteRotateLeft(TNode node, bool preregister){
+RewriteResponse TheoryBVRewriter::RewriteRotateLeft(TNode node, bool prerewrite){
Node resultNode = LinearRewriteStrategy
< RewriteRule<RotateLeftEliminate >
- >::apply(node);
+ >::apply(node);
return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
}
-RewriteResponse TheoryBVRewriter::RewriteEqual(TNode node, bool preregister) {
- if (preregister) {
+RewriteResponse TheoryBVRewriter::RewriteBVToNat(TNode node, bool prerewrite) {
+ Node resultNode = LinearRewriteStrategy
+ < RewriteRule<BVToNatEliminate>
+ >::apply(node);
+
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+}
+
+RewriteResponse TheoryBVRewriter::RewriteIntToBV(TNode node, bool prerewrite) {
+ Node resultNode = LinearRewriteStrategy
+ < RewriteRule<IntToBVEliminate>
+ >::apply(node);
+
+ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode);
+}
+
+RewriteResponse TheoryBVRewriter::RewriteEqual(TNode node, bool prerewrite) {
+ if (prerewrite) {
Node resultNode = LinearRewriteStrategy
< RewriteRule<FailEq>,
RewriteRule<SimplifyEq>,
@@ -593,8 +640,8 @@ void TheoryBVRewriter::initializeRewrites() {
d_rewriteTable [ kind::BITVECTOR_PLUS ] = RewritePlus;
d_rewriteTable [ kind::BITVECTOR_SUB ] = RewriteSub;
d_rewriteTable [ kind::BITVECTOR_NEG ] = RewriteNeg;
- // d_rewriteTable [ kind::BITVECTOR_UDIV ] = RewriteUdiv;
- // d_rewriteTable [ kind::BITVECTOR_UREM ] = RewriteUrem;
+ d_rewriteTable [ kind::BITVECTOR_UDIV ] = RewriteUdiv;
+ d_rewriteTable [ kind::BITVECTOR_UREM ] = RewriteUrem;
d_rewriteTable [ kind::BITVECTOR_UDIV_TOTAL ] = RewriteUdivTotal;
d_rewriteTable [ kind::BITVECTOR_UREM_TOTAL ] = RewriteUremTotal;
d_rewriteTable [ kind::BITVECTOR_SMOD ] = RewriteSmod;
@@ -609,6 +656,9 @@ void TheoryBVRewriter::initializeRewrites() {
d_rewriteTable [ kind::BITVECTOR_SIGN_EXTEND ] = RewriteSignExtend;
d_rewriteTable [ kind::BITVECTOR_ROTATE_RIGHT ] = RewriteRotateRight;
d_rewriteTable [ kind::BITVECTOR_ROTATE_LEFT ] = RewriteRotateLeft;
+
+ d_rewriteTable [ kind::BITVECTOR_TO_NAT ] = RewriteBVToNat;
+ d_rewriteTable [ kind::INT_TO_BITVECTOR ] = RewriteIntToBV;
}
Node TheoryBVRewriter::eliminateBVSDiv(TNode node) {
diff --git a/src/theory/bv/theory_bv_rewriter.h b/src/theory/bv/theory_bv_rewriter.h
index 183b5e8d5..def8e24fe 100644
--- a/src/theory/bv/theory_bv_rewriter.h
+++ b/src/theory/bv/theory_bv_rewriter.h
@@ -78,6 +78,9 @@ class TheoryBVRewriter {
static RewriteResponse RewriteRotateRight(TNode node, bool prerewrite = false);
static RewriteResponse RewriteRotateLeft(TNode node, bool prerewrite = false);
+ static RewriteResponse RewriteBVToNat(TNode node, bool prerewrite = false);
+ static RewriteResponse RewriteIntToBV(TNode node, bool prerewrite = false);
+
public:
static RewriteResponse postRewrite(TNode node);
diff --git a/src/theory/bv/theory_bv_type_rules.h b/src/theory/bv/theory_bv_type_rules.h
index 00284e7ae..67dae0cfa 100644
--- a/src/theory/bv/theory_bv_type_rules.h
+++ b/src/theory/bv/theory_bv_type_rules.h
@@ -29,6 +29,11 @@ class BitVectorConstantTypeRule {
public:
inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
throw (TypeCheckingExceptionPrivate, AssertionException) {
+ if (check) {
+ if (n.getConst<BitVector>().getSize() == 0) {
+ throw TypeCheckingExceptionPrivate(n, "constant of size 0");
+ }
+ }
return nodeManager->mkBitVectorType(n.getConst<BitVector>().getSize());
}
};
@@ -190,6 +195,29 @@ public:
}
};
+class BitVectorConversionTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ if(n.getKind() == kind::BITVECTOR_TO_NAT) {
+ if(check && !n[0].getType(check).isBitVector()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting bit-vector term");
+ }
+ return nodeManager->integerType();
+ }
+
+ if(n.getKind() == kind::INT_TO_BITVECTOR) {
+ size_t bvSize = n.getOperator().getConst<IntToBitVector>();
+ if(check && !n[0].getType(check).isInteger()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting integer term");
+ }
+ return nodeManager->mkBitVectorType(bvSize);
+ }
+
+ InternalError("bv-conversion typerule invoked for non-bv-conversion kind");
+ }
+};
+
class CardinalityComputer {
public:
inline static Cardinality computeCardinality(TypeNode type) {
diff --git a/src/theory/bv/theory_bv_utils.h b/src/theory/bv/theory_bv_utils.h
index 5847bac3e..ab6a615a2 100644
--- a/src/theory/bv/theory_bv_utils.h
+++ b/src/theory/bv/theory_bv_utils.h
@@ -357,7 +357,7 @@ inline Node flattenAnd(std::vector<TNode>& queue) {
}
-// neeed a better name, this is not technically a ground term
+// need a better name, this is not technically a ground term
inline bool isBVGroundTerm(TNode node) {
if (node.getNumChildren() == 0) {
return node.isConst();
diff --git a/src/theory/datatypes/datatypes_rewriter.h b/src/theory/datatypes/datatypes_rewriter.h
index bc6d1f839..186444e0a 100644
--- a/src/theory/datatypes/datatypes_rewriter.h
+++ b/src/theory/datatypes/datatypes_rewriter.h
@@ -21,6 +21,7 @@
#include "theory/rewriter.h"
#include "theory/datatypes/options.h"
+#include "theory/type_enumerator.h"
namespace CVC4 {
namespace theory {
@@ -137,10 +138,16 @@ public:
return RewriteResponse(REWRITE_DONE, in[0][selectorIndex]);
}else{
if( options::dtRewriteErrorSel() ){
- Node gt = in.getType().mkGroundTerm();
+ Node gt;
+ if( in.getType().isSort() ){
+ TypeEnumerator te(in.getType());
+ gt = *te;
+ }else{
+ gt = in.getType().mkGroundTerm();
+ }
TypeNode gtt = gt.getType();
//Assert( gtt.isDatatype() || gtt.isParametricDatatype() );
- if( !gtt.isInstantiatedDatatype() ){
+ if( gtt.isDatatype() && !gtt.isInstantiatedDatatype() ){
gt = NodeManager::currentNM()->mkNode(kind::APPLY_TYPE_ASCRIPTION,
NodeManager::currentNM()->mkConst(AscriptionType(in.getType().toType())), gt);
}
diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds
index 2e58677df..81ef32b32 100644
--- a/src/theory/datatypes/kinds
+++ b/src/theory/datatypes/kinds
@@ -88,7 +88,7 @@ typerule APPLY_TYPE_ASCRIPTION ::CVC4::theory::datatypes::DatatypeAscriptionType
# constructor applications are constant if they are applied only to constants
construle APPLY_CONSTRUCTOR ::CVC4::theory::datatypes::DatatypeConstructorTypeRule
-operator TUPLE_TYPE 1: "tuple type"
+operator TUPLE_TYPE 0: "tuple type"
cardinality TUPLE_TYPE \
"::CVC4::theory::datatypes::TupleProperties::computeCardinality(%TYPE%)" \
"theory/datatypes/theory_datatypes_type_rules.h"
@@ -100,7 +100,7 @@ enumerator TUPLE_TYPE \
"::CVC4::theory::datatypes::TupleEnumerator" \
"theory/datatypes/type_enumerator.h"
-operator TUPLE 1: "a tuple"
+operator TUPLE 0: "a tuple"
typerule TUPLE ::CVC4::theory::datatypes::TupleTypeRule
construle TUPLE ::CVC4::theory::datatypes::TupleProperties
diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp
index 7f96232d6..a0651efb4 100644
--- a/src/theory/datatypes/theory_datatypes.cpp
+++ b/src/theory/datatypes/theory_datatypes.cpp
@@ -91,7 +91,7 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( Node n, bool doMake
}
void TheoryDatatypes::check(Effort e) {
-
+ Trace("datatypes-debug") << "Check effort " << e << std::endl;
while(!done() && !d_conflict) {
// Get all the assertions
Assertion assertion = get();
@@ -117,80 +117,95 @@ void TheoryDatatypes::check(Effort e) {
if( e == EFFORT_FULL ) {
Debug("datatypes-split") << "Check for splits " << e << endl;
- eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine );
- while( !eqcs_i.isFinished() ){
- Node n = (*eqcs_i);
- if( DatatypesRewriter::isTermDatatype( n ) ){
- EqcInfo* eqc = getOrMakeEqcInfo( n, true );
- //if there are more than 1 possible constructors for eqc
- if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) {
- const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype();
- //if only one constructor, then this term must be this constructor
- if( dt.getNumConstructors()==1 ){
- Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n );
- d_pending.push_back( t );
- d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true );
- Trace("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl;
- d_infer.push_back( t );
- }else{
- std::vector< bool > pcons;
- getPossibleCons( eqc, n, pcons );
- //std::cout << "pcons " << n << " = ";
- //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; }
- //std::cout << std::endl;
- //check if we do not need to resolve the constructor type for this equivalence class.
- // this is if there are no selectors for this equivalence class, its possible values are infinite,
- // and we are not producing a model, then do not split.
- int consIndex = -1;
- bool needSplit = true;
- for( unsigned int j=0; j<pcons.size(); j++ ) {
- if( pcons[j] ) {
- if( consIndex==-1 ){
- consIndex = j;
+ bool addedFact = false;
+ do {
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine );
+ while( !eqcs_i.isFinished() ){
+ Node n = (*eqcs_i);
+ if( DatatypesRewriter::isTermDatatype( n ) ){
+ Trace("datatypes-debug") << "Process equivalence class " << n << std::endl;
+ EqcInfo* eqc = getOrMakeEqcInfo( n, true );
+ //if there are more than 1 possible constructors for eqc
+ if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) {
+ Trace("datatypes-debug") << "No constructor..." << std::endl;
+ const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype();
+ //if only one constructor, then this term must be this constructor
+ if( dt.getNumConstructors()==1 ){
+ Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n );
+ d_pending.push_back( t );
+ d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true );
+ Trace("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl;
+ d_infer.push_back( t );
+ }else{
+ std::vector< bool > pcons;
+ getPossibleCons( eqc, n, pcons );
+ //std::cout << "pcons " << n << " = ";
+ //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; }
+ //std::cout << std::endl;
+ //check if we do not need to resolve the constructor type for this equivalence class.
+ // this is if there are no selectors for this equivalence class, its possible values are infinite,
+ // and we are not producing a model, then do not split.
+ int consIndex = -1;
+ bool needSplit = true;
+ for( unsigned int j=0; j<pcons.size(); j++ ) {
+ if( pcons[j] ) {
+ if( consIndex==-1 ){
+ consIndex = j;
+ }
+ if( !dt[ j ].isFinite() && !eqc->d_selectors ) {
+ needSplit = false;
+ }
}
- if( !dt[ j ].isFinite() && !eqc->d_selectors ) {
- needSplit = false;
+ }
+ if( !needSplit && mustSpecifyAssignment() ){
+ //for the sake of termination, we must choose the constructor of a ground term
+ //NEED GUARENTEE: groundTerm should not contain any subterms of the same type
+ //** TODO: this is probably not good enough, actually need fair enumeration strategy
+ Node groundTerm = n.getType().mkGroundTerm();
+ int index = Datatype::indexOf( groundTerm.getOperator().toExpr() );
+ if( pcons[index] ){
+ consIndex = index;
}
+ needSplit = true;
}
- }
- if( !needSplit && mustSpecifyAssignment() ){
- //for the sake of termination, we must choose the constructor of a ground term
- //NEED GUARENTEE: groundTerm should not contain any subterms of the same type
- //** TODO: this is probably not good enough, actually need fair enumeration strategy
- Node groundTerm = n.getType().mkGroundTerm();
- int index = Datatype::indexOf( groundTerm.getOperator().toExpr() );
- if( pcons[index] ){
- consIndex = index;
+ if( needSplit && consIndex!=-1 ) {
+ Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n );
+ Trace("dt-split") << "*************Split for possible constructor " << dt[consIndex] << " for " << n << endl;
+ test = Rewriter::rewrite( test );
+ NodeBuilder<> nb(kind::OR);
+ nb << test << test.notNode();
+ Node lemma = nb;
+ d_out->lemma( lemma );
+ d_out->requirePhase( test, true );
+ return;
+ }else{
+ Trace("dt-split") << "Do not split constructor for " << n << std::endl;
}
- needSplit = true;
- }
- if( needSplit && consIndex!=-1 ) {
- Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n );
- Trace("dt-split") << "*************Split for possible constructor " << dt[consIndex] << " for " << n << endl;
- test = Rewriter::rewrite( test );
- NodeBuilder<> nb(kind::OR);
- nb << test << test.notNode();
- Node lemma = nb;
- d_out->lemma( lemma );
- d_out->requirePhase( test, true );
- return;
- }else{
- Trace("dt-split") << "Do not split constructor for " << n << std::endl;
}
+ }else{
+ Trace("datatypes-debug") << "Has constructor " << eqc->d_constructor.get() << std::endl;
}
}
+ ++eqcs_i;
}
- ++eqcs_i;
- }
- flushPendingFacts();
- if( !d_conflict ){
- if( options::dtRewriteErrorSel() ){
- collapseSelectors();
- flushPendingFacts();
+ Trace("datatypes-debug") << "Flush pending facts..." << std::endl;
+ addedFact = !d_pending.empty() || !d_pending_merge.empty();
+ flushPendingFacts();
+ if( !d_conflict ){
+ if( options::dtRewriteErrorSel() ){
+ bool innerAddedFact = false;
+ do {
+ collapseSelectors();
+ innerAddedFact = !d_pending.empty() || !d_pending_merge.empty();
+ flushPendingFacts();
+ }while( !d_conflict && innerAddedFact );
+ }
}
- }
+ }while( !d_conflict && addedFact );
+ Trace("datatypes-debug") << "Finished. " << d_conflict << std::endl;
if( !d_conflict ){
- // printModelDebug();
+ Trace("dt-model-test") << std::endl;
+ printModelDebug("dt-model-test");
}
}
@@ -1003,8 +1018,8 @@ bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){
// (3) Instantiate : is_C( t ) => t = C( sel_1( t ) ... sel_n( t ) )
//We may need to communicate (3) outwards if the conclusions involve other theories
Trace("dt-lemma-debug") << "Compute for " << exp << " => " << n << std::endl;
+ bool addLemma = false;
if( ( n.getKind()==EQUAL || n.getKind()==IFF) && n[1].getKind()==APPLY_CONSTRUCTOR && exp.getKind()!=EQUAL ){
- bool addLemma = false;
#if 1
const Datatype& dt = ((DatatypeType)(n[1].getType()).toType()).getDatatype();
addLemma = dt.involvesExternalType();
@@ -1028,6 +1043,11 @@ bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){
}
}
}
+ //else if( exp.getKind()==APPLY_TESTER ){
+ //if( n.getKind()==EQUAL && !DatatypesRewriter::isTermDatatype( n[0] ) ){
+ // return true;
+ //}
+ //}
Trace("dt-lemma-debug") << "Do not need to communicate " << n << std::endl;
return false;
}
@@ -1066,6 +1086,10 @@ Node TheoryDatatypes::getRepresentative( Node a ){
void TheoryDatatypes::printModelDebug( const char* c ){
+ if(! (Trace.isOn(c))) {
+ return;
+ }
+
Trace( c ) << "Datatypes model : " << std::endl;
eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine );
while( !eqcs_i.isFinished() ){
diff --git a/src/theory/idl/Makefile b/src/theory/idl/Makefile
new file mode 100644
index 000000000..75ae33c7e
--- /dev/null
+++ b/src/theory/idl/Makefile
@@ -0,0 +1,4 @@
+topdir = ../../..
+srcdir = src/theory/idl
+
+include $(topdir)/Makefile.subdir
diff --git a/src/theory/idl/Makefile.am b/src/theory/idl/Makefile.am
new file mode 100644
index 000000000..4297e3bdb
--- /dev/null
+++ b/src/theory/idl/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = \
+ -D__BUILDING_CVC4LIB \
+ -I@builddir@/../.. -I@srcdir@/../../include -I@srcdir@/../..
+AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN)
+
+noinst_LTLIBRARIES = libidl.la
+
+libidl_la_SOURCES = \
+ idl_model.h \
+ idl_model.cpp \
+ idl_assertion.h \
+ idl_assertion.cpp \
+ idl_assertion_db.h \
+ idl_assertion_db.cpp \
+ theory_idl.h \
+ theory_idl.cpp
+
+EXTRA_DIST = \
+ kinds
diff --git a/src/theory/idl/idl_assertion.cpp b/src/theory/idl/idl_assertion.cpp
new file mode 100644
index 000000000..1e725932b
--- /dev/null
+++ b/src/theory/idl/idl_assertion.cpp
@@ -0,0 +1,213 @@
+/********************* */
+/*! \file idl_assertion.cpp
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "theory/idl/idl_assertion.h"
+
+using namespace CVC4;
+using namespace theory;
+using namespace idl;
+
+IDLAssertion::IDLAssertion()
+: d_op(kind::LAST_KIND)
+{}
+
+IDLAssertion::IDLAssertion(TNode node) {
+ bool ok = parse(node, 1, false);
+ if (!ok) {
+ d_x = d_y = TNode::null();
+ } else {
+ if (d_op == kind::GT) {
+ // Turn GT into LT x - y > c is the same as y - x < -c
+ std::swap(d_x, d_y);
+ d_c = -d_c;
+ d_op = kind::LT;
+ }
+ if (d_op == kind::GEQ) {
+ // Turn GT into LT x - y >= c is the same as y - x <= -c
+ std::swap(d_x, d_y);
+ d_c = -d_c;
+ d_op = kind::LEQ;
+ }
+ if (d_op == kind::LT) {
+ // Turn strict into non-strict x - y < c is the same as x - y <= c-1
+ d_c = d_c - 1;
+ d_op = kind::LEQ;
+ }
+ }
+ d_original = node;
+}
+
+IDLAssertion::IDLAssertion(const IDLAssertion& other)
+: d_x(other.d_x)
+, d_y(other.d_y)
+, d_op(other.d_op)
+, d_c(other.d_c)
+, d_original(other.d_original)
+{}
+
+bool IDLAssertion::propagate(IDLModel& model) const {
+ Debug("theory::idl::model") << model << std::endl;
+ Assert(ok());
+ // Should be d_x - d_y <= d_c, or d_x - d_c <= d_y
+ Integer x_value = model.getValue(d_x);
+ Integer y_value = model.getValue(d_y);
+ if (x_value - y_value > d_c) {
+ model.setValue(d_y, x_value - d_c, IDLReason(d_x, d_original));
+ Debug("theory::idl::model") << model << std::endl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void IDLAssertion::toStream(std::ostream& out) const {
+ out << "IDL[" << d_x << " - " << d_y << " " << d_op << " " << d_c << "]";
+}
+
+/** Negates the given arithmetic kind */
+static Kind negateOp(Kind op) {
+ switch (op) {
+ case kind::LT:
+ // not (a < b) = (a >= b)
+ return kind::GEQ;
+ case kind::LEQ:
+ // not (a <= b) = (a > b)
+ return kind::GT;
+ case kind::GT:
+ // not (a > b) = (a <= b)
+ return kind::LEQ;
+ case kind::GEQ:
+ // not (a >= b) = (a < b)
+ return kind::LT;
+ case kind::EQUAL:
+ // not (a = b) = (a != b)
+ return kind::DISTINCT;
+ case kind::DISTINCT:
+ // not (a != b) = (a = b)
+ return kind::EQUAL;
+ default:
+ Unreachable();
+ break;
+ }
+ return kind::LAST_KIND;
+}
+
+bool IDLAssertion::parse(TNode node, int c, bool negated) {
+
+ // Only unit coefficients allowed
+ if (c != 1 && c != -1) {
+ return false;
+ }
+
+ // Assume we're ok
+ bool ok = true;
+
+ // The kind of the node
+ switch(node.getKind()) {
+
+ case kind::NOT:
+ // We parse the negation
+ ok = parse(node[0], c, true);
+ // Setup the kind
+ if (ok) {
+ d_op = negateOp(d_op);
+ }
+ break;
+
+ case kind::EQUAL:
+ case kind::LT:
+ case kind::LEQ:
+ case kind::GT:
+ case kind::GEQ: {
+ // All relation operators are parsed on both sides
+ d_op = node.getKind();
+ ok = parse(node[0], c, negated);
+ if (ok) {
+ ok = parse(node[1],-c, negated);
+ }
+ break;
+ }
+
+ case kind::CONST_RATIONAL: {
+ // Constants
+ Rational m = node.getConst<Rational>();
+ if (m.isIntegral()) {
+ d_c += m.getNumerator() * (-c);
+ } else {
+ ok = false;
+ }
+ break;
+ }
+ case kind::MULT: {
+ // Only unit multiplication of variables
+ if (node.getNumChildren() == 2 && node[0].isConst()) {
+ Rational a = node[0].getConst<Rational>();
+ if (a == 1 || a == -1) {
+ ok = parse(node[1], c * a.sgn(), negated);
+ } else {
+ ok = false;
+ }
+ } else {
+ ok = false;
+ }
+ break;
+ }
+
+ case kind::PLUS: {
+ for(unsigned i = 0; i < node.getNumChildren(); ++i) {
+ ok = parse(node[i], c, negated);
+ if(!ok) {
+ break;
+ }
+ }
+ break;
+ }
+
+ case kind::MINUS: {
+ ok = parse(node[0], c, negated);
+ if (ok) {
+ ok = parse(node[1], -c, negated);
+ }
+ break;
+ }
+
+ case kind::UMINUS: {
+ ok = parse(node[0], -c, negated);
+ break;
+ }
+
+ default: {
+ if (c > 0) {
+ if (d_x.isNull()) {
+ d_x = node;
+ } else {
+ ok = false;
+ }
+ } else {
+ if (d_y.isNull()) {
+ d_y = node;
+ } else {
+ ok = false;
+ }
+ }
+ break;
+ }
+ } // End case
+
+ // Difference logic OK
+ return ok;
+}
diff --git a/src/theory/idl/idl_assertion.h b/src/theory/idl/idl_assertion.h
new file mode 100644
index 000000000..8ce0e93b2
--- /dev/null
+++ b/src/theory/idl/idl_assertion.h
@@ -0,0 +1,91 @@
+/********************* */
+/*! \file idl_assertion.h
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#pragma once
+
+#include "theory/idl/idl_model.h"
+
+namespace CVC4 {
+namespace theory {
+namespace idl {
+
+/**
+ * An internal representation of the IDL assertions. Each IDL assertions is
+ * of the form (x - y op c) where op is one of (<=, =, !=). IDL assertion
+ * can be constructed from an expression.
+ */
+class IDLAssertion {
+
+ /** The positive variable */
+ TNode d_x;
+ /** The negative variable */
+ TNode d_y;
+ /** The relation */
+ Kind d_op;
+ /** The RHS constant */
+ Integer d_c;
+
+ /** Original assertion we got this one from */
+ TNode d_original;
+
+ /** Parses the given node into an assertion, and return true if OK. */
+ bool parse(TNode node, int c = 1, bool negated = false);
+
+public:
+
+ /** Null assertion */
+ IDLAssertion();
+ /** Create the assertion from given node */
+ IDLAssertion(TNode node);
+ /** Copy constructor */
+ IDLAssertion(const IDLAssertion& other);
+
+ TNode getX() const { return d_x; }
+ TNode getY() const { return d_y; }
+ Kind getOp() const { return d_op;}
+ Integer getC() const { return d_c; }
+
+ /**
+ * Propagate the constraint using the model. For example, if the constraint
+ * is of the form x - y <= -1, and the value of x in the model is 0, then
+ *
+ * (x - y <= -1) and (x = 0) implies y >= x + 1 = 1
+ *
+ * If the value of y is less then 1, is is set to 1 and true is returned. If
+ * the value of y is 1 or more, than false is return.
+ *
+ * @return true if value of y was updated
+ */
+ bool propagate(IDLModel& model) const;
+
+ /** Is this constraint proper */
+ bool ok() const {
+ return !d_x.isNull() || !d_y.isNull();
+ }
+
+ /** Output to the stream */
+ void toStream(std::ostream& out) const;
+};
+
+inline std::ostream& operator << (std::ostream& out, const IDLAssertion& assertion) {
+ assertion.toStream(out);
+ return out;
+}
+
+}
+}
+}
diff --git a/src/theory/idl/idl_assertion_db.cpp b/src/theory/idl/idl_assertion_db.cpp
new file mode 100644
index 000000000..697c70c02
--- /dev/null
+++ b/src/theory/idl/idl_assertion_db.cpp
@@ -0,0 +1,59 @@
+/********************* */
+/*! \file idl_assertion_db.cpp
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "theory/idl/idl_assertion_db.h"
+
+using namespace CVC4;
+using namespace theory;
+using namespace idl;
+
+IDLAssertionDB::IDLAssertionDB(context::Context* c)
+: d_assertions(c)
+, d_variableLists(c)
+{}
+
+void IDLAssertionDB::add(const IDLAssertion& assertion, TNode var) {
+ // Is there a list for the variable already?
+ unsigned previous = -1;
+ var_to_unsigned_map::iterator find = d_variableLists.find(var);
+ if (find != d_variableLists.end()) {
+ previous = (*find).second;
+ }
+ // Add to the DB
+ d_variableLists[var] = d_assertions.size();
+ d_assertions.push_back(IDLAssertionListElement(assertion, previous));
+}
+
+IDLAssertionDB::iterator::iterator(IDLAssertionDB& db, TNode var)
+: d_db(db)
+, d_current(-1)
+{
+ var_to_unsigned_map::const_iterator find = d_db.d_variableLists.find(var);
+ if (find != d_db.d_variableLists.end()) {
+ d_current = (*find).second;
+ }
+}
+
+void IDLAssertionDB::iterator::next() {
+ if (d_current != (unsigned)(-1)) {
+ d_current = d_db.d_assertions[d_current].d_previous;
+ }
+}
+
+IDLAssertion IDLAssertionDB::iterator::get() const {
+ return d_db.d_assertions[d_current].d_assertion;
+}
diff --git a/src/theory/idl/idl_assertion_db.h b/src/theory/idl/idl_assertion_db.h
new file mode 100644
index 000000000..0501bc6bf
--- /dev/null
+++ b/src/theory/idl/idl_assertion_db.h
@@ -0,0 +1,86 @@
+/********************* */
+/*! \file idl_assertion_db.h
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#pragma once
+
+#include "theory/idl/idl_assertion.h"
+#include "context/cdlist.h"
+
+namespace CVC4 {
+namespace theory {
+namespace idl {
+
+/**
+ * Context-dependent database assertions, organized by variable. Each variable
+ * can be associated a list of IDL assertions. The list of assertions can
+ * be iterated over using the provided iterator class.
+ */
+class IDLAssertionDB {
+
+ /** Elements of the assertion lists */
+ struct IDLAssertionListElement {
+ /** The assertion itself */
+ IDLAssertion d_assertion;
+ /** The inndex of the previous element (-1 for null) */
+ unsigned d_previous;
+
+ IDLAssertionListElement(const IDLAssertion& assertion, unsigned previous)
+ : d_assertion(assertion), d_previous(previous)
+ {}
+ };
+
+ /** All assertions in a context dependent stack */
+ context::CDList<IDLAssertionListElement> d_assertions;
+
+ typedef context::CDHashMap<TNode, unsigned, TNodeHashFunction> var_to_unsigned_map;
+
+ /** Map from variables to the first element of their list */
+ var_to_unsigned_map d_variableLists;
+
+public:
+
+ /** Create a new assertion database */
+ IDLAssertionDB(context::Context* c);
+
+ /** Add a new assertion, attach to the list of the given variable */
+ void add(const IDLAssertion& assertion, TNode var);
+
+ /** Iteration over the constraints of a variable */
+ class iterator {
+ /** The database */
+ const IDLAssertionDB& d_db;
+ /** Index of the current constraint */
+ unsigned d_current;
+ public:
+ /** Construct the iterator for the variable */
+ iterator(IDLAssertionDB& db, TNode var);
+ /** Is this iterator done */
+ bool done() const { return d_current == (unsigned)(-1); }
+ /** Next element */
+ void next();
+ /** Get the assertion */
+ IDLAssertion get() const;
+ };
+};
+
+}
+}
+}
+
+
+
+
diff --git a/src/theory/idl/idl_model.cpp b/src/theory/idl/idl_model.cpp
new file mode 100644
index 000000000..75f4834ea
--- /dev/null
+++ b/src/theory/idl/idl_model.cpp
@@ -0,0 +1,64 @@
+/********************* */
+/*! \file idl_model.cpp
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "theory/idl/idl_model.h"
+
+using namespace CVC4;
+using namespace theory;
+using namespace idl;
+
+IDLModel::IDLModel(context::Context* context)
+: d_model(context)
+, d_reason(context)
+{}
+
+Integer IDLModel::getValue(TNode var) const {
+ model_value_map::const_iterator find = d_model.find(var);
+ if (find != d_model.end()) {
+ return (*find).second;
+ } else {
+ return 0;
+ }
+}
+
+void IDLModel::setValue(TNode var, Integer value, IDLReason reason) {
+ Assert(!reason.constraint.isNull());
+ d_model[var] = value;
+ d_reason[var] = reason;
+}
+
+void IDLModel::getReasonCycle(TNode var, std::vector<TNode>& reasons) {
+ TNode current = var;
+ do {
+ Debug("theory::idl::model") << "processing: " << var << std::endl;
+ Assert(d_reason.find(current) != d_reason.end());
+ IDLReason reason = d_reason[current];
+ Debug("theory::idl::model") << "adding reason: " << reason.constraint << std::endl;
+ reasons.push_back(reason.constraint);
+ current = reason.x;
+ } while (current != var);
+}
+
+void IDLModel::toStream(std::ostream& out) const {
+ model_value_map::const_iterator it = d_model.begin();
+ model_value_map::const_iterator it_end = d_model.end();
+ out << "Model[" << std::endl;
+ for (; it != it_end; ++ it) {
+ out << (*it).first << " -> " << (*it).second << std::endl;
+ }
+ out << "]";
+}
diff --git a/src/theory/idl/idl_model.h b/src/theory/idl/idl_model.h
new file mode 100644
index 000000000..64407684b
--- /dev/null
+++ b/src/theory/idl/idl_model.h
@@ -0,0 +1,84 @@
+/********************* */
+/*! \file idl_model.h
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#pragma once
+
+#include "expr/node.h"
+#include "context/cdhashmap.h"
+
+namespace CVC4 {
+namespace theory {
+namespace idl {
+
+/**
+ * A reason for a value of a variable in the model is a constraint that implies
+ * this value by means of the value of another variable. For example, if the
+ * value of x is 0, then the variable x and the constraint (y > 0) are a reason
+ * for the y taking the value 1.
+ */
+struct IDLReason {
+ /** The variable of the reason */
+ TNode x;
+ /** The constraint of the reaason */
+ TNode constraint;
+
+ IDLReason(TNode x, TNode constraint)
+ : x(x), constraint(constraint) {}
+ IDLReason() {}
+};
+
+/**
+ * A model maps variables to integer values and backs them up with reasons.
+ * Default values (if not set with setValue) for all variables are 0.
+ */
+class IDLModel {
+
+ typedef context::CDHashMap<TNode, Integer, TNodeHashFunction> model_value_map;
+ typedef context::CDHashMap<TNode, IDLReason, TNodeHashFunction> model_reason_map;
+
+ /** Values assigned to individual variables */
+ model_value_map d_model;
+
+ /** Reasons constraining the individual variables */
+ model_reason_map d_reason;
+
+public:
+
+ IDLModel(context::Context* context);
+
+ /** Get the model value of the variable */
+ Integer getValue(TNode var) const;
+
+ /** Set the value of the variable */
+ void setValue(TNode var, Integer value, IDLReason reason);
+
+ /** Get the cycle of reasons behind the variable var */
+ void getReasonCycle(TNode var, std::vector<TNode>& reasons);
+
+ /** Output to the given stream */
+ void toStream(std::ostream& out) const;
+
+};
+
+inline std::ostream& operator << (std::ostream& out, const IDLModel& model) {
+ model.toStream(out);
+ return out;
+}
+
+}
+}
+}
diff --git a/src/theory/idl/kinds b/src/theory/idl/kinds
new file mode 100644
index 000000000..6bf0218b0
--- /dev/null
+++ b/src/theory/idl/kinds
@@ -0,0 +1,8 @@
+# kinds -*- sh -*-
+#
+# For documentation on this file format, please refer to
+# src/theory/builtin/kinds.
+#
+
+alternate THEORY_ARITH "idl" ::CVC4::theory::idl::TheoryIdl "theory/idl/theory_idl.h"
+
diff --git a/src/theory/idl/options b/src/theory/idl/options
new file mode 100644
index 000000000..c1c9edcef
--- /dev/null
+++ b/src/theory/idl/options
@@ -0,0 +1,12 @@
+#
+# Option specification file for CVC4
+# See src/options/base_options for a description of this file format
+#
+
+module IDL "theory/idl/options.h" Idl
+
+option idlRewriteEq --enable-idl-rewrite-equalities/--disable-idl-rewrite-equalities bool :default false :read-write
+ enable rewriting equalities into two inequalities in IDL solver (default is disabled)
+/disable rewriting equalities into two inequalities in IDL solver (default is disabled)
+
+endmodule
diff --git a/src/theory/idl/theory_idl.cpp b/src/theory/idl/theory_idl.cpp
new file mode 100644
index 000000000..e5100fc71
--- /dev/null
+++ b/src/theory/idl/theory_idl.cpp
@@ -0,0 +1,143 @@
+/********************* */
+/*! \file theory_idl.cpp
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "theory/idl/theory_idl.h"
+#include "theory/idl/options.h"
+#include "theory/rewriter.h"
+
+#include <set>
+#include <queue>
+
+using namespace std;
+
+using namespace CVC4;
+using namespace theory;
+using namespace idl;
+
+TheoryIdl::TheoryIdl(context::Context* c, context::UserContext* u, OutputChannel& out,
+ Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe)
+: Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe)
+, d_model(c)
+, d_assertionsDB(c)
+{}
+
+Node TheoryIdl::ppRewrite(TNode atom) {
+ if (atom.getKind() == kind::EQUAL && options::idlRewriteEq()) {
+ // If the option is turned on, each equality into two inequalities. This in
+ // effect removes equalities, and theorefore dis-equalities too.
+ Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1];
+ Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1];
+ Node rewritten = Rewriter::rewrite(leq.andNode(geq));
+ return rewritten;
+ } else {
+ return atom;
+ }
+}
+
+void TheoryIdl::check(Effort level) {
+
+ while(!done()) {
+
+ // Get the next assertion
+ Assertion assertion = get();
+ Debug("theory::idl") << "TheoryIdl::check(): processing " << assertion.assertion << std::endl;
+
+ // Convert the assertion into the internal representation
+ IDLAssertion idlAssertion(assertion.assertion);
+ Debug("theory::idl") << "TheoryIdl::check(): got " << idlAssertion << std::endl;
+
+ if (idlAssertion.ok()) {
+ if (idlAssertion.getOp() == kind::DISTINCT) {
+ // We don't handle dis-equalities
+ d_out->setIncomplete();
+ } else {
+ // Process the convex assertions immediately
+ bool ok = processAssertion(idlAssertion);
+ if (!ok) {
+ // In conflict, we're done
+ return;
+ }
+ }
+ } else {
+ // Not an IDL assertion, set incomplete
+ d_out->setIncomplete();
+ }
+ }
+
+}
+
+bool TheoryIdl::processAssertion(const IDLAssertion& assertion) {
+
+ Debug("theory::idl") << "TheoryIdl::processAssertion(" << assertion << ")" << std::endl;
+
+ // Add the constraint (x - y op c) to the list assertions of x
+ d_assertionsDB.add(assertion, assertion.getX());
+
+ // Update the model, if forced by the assertion
+ bool y_updated = assertion.propagate(d_model);
+
+ // If the value of y was updated, we might need to update further
+ if (y_updated) {
+
+ std::queue<TNode> queue; // Queue of variables to consider
+ std::set<TNode> inQueue; // Current elements of the queue
+
+ // Add the first updated variable to the queue
+ queue.push(assertion.getY());
+ inQueue.insert(assertion.getY());
+
+ while (!queue.empty()) {
+ // Pop a new variable x off the queue
+ TNode x = queue.front();
+ queue.pop();
+ inQueue.erase(x);
+
+ // Go through the constraint (x - y op c), and update values of y
+ IDLAssertionDB::iterator it(d_assertionsDB, x);
+ while (!it.done()) {
+ // Get the assertion and update y
+ IDLAssertion x_y_assertion = it.get();
+ y_updated = x_y_assertion.propagate(d_model);
+ // If updated add to the queue
+ if (y_updated) {
+ // If the variable that we updated is the same as the first
+ // variable that we updated, it's a cycle of updates => conflict
+ if (x_y_assertion.getY() == assertion.getX()) {
+ std::vector<TNode> reasons;
+ d_model.getReasonCycle(x_y_assertion.getY(), reasons);
+ // Construct the reason of the conflict
+ Node conflict = NodeManager::currentNM()->mkNode(kind::AND, reasons);
+ d_out->conflict(conflict);
+ return false;
+ } else {
+ // No cycle, just a model update, so we add to the queue
+ TNode y = x_y_assertion.getY();
+ if (inQueue.count(y) == 0) {
+ queue.push(y);
+ inQueue.insert(x_y_assertion.getY());
+ }
+ }
+ }
+ // Go to the next constraint
+ it.next();
+ }
+ }
+ }
+
+ // Everything fine, no conflict
+ return true;
+}
diff --git a/src/theory/idl/theory_idl.h b/src/theory/idl/theory_idl.h
new file mode 100644
index 000000000..c629ad2b0
--- /dev/null
+++ b/src/theory/idl/theory_idl.h
@@ -0,0 +1,63 @@
+/********************* */
+/*! \file theory_idl.h
+ ** \verbatim
+ ** Original author: Dejan Jovanovic
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#pragma once
+
+#include "cvc4_private.h"
+
+#include "theory/theory.h"
+#include "theory/idl/idl_model.h"
+#include "theory/idl/idl_assertion_db.h"
+
+namespace CVC4 {
+namespace theory {
+namespace idl {
+
+/**
+ * Handles integer difference logic (IDL) constraints.
+ */
+class TheoryIdl : public Theory {
+
+ /** The current model */
+ IDLModel d_model;
+
+ /** The asserted constraints, organized by variable */
+ IDLAssertionDB d_assertionsDB;
+
+ /** Process a new assertion, returns false if in conflict */
+ bool processAssertion(const IDLAssertion& assertion);
+
+public:
+
+ /** Theory constructor. */
+ TheoryIdl(context::Context* c, context::UserContext* u, OutputChannel& out,
+ Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
+
+ /** Pre-processing of input atoms */
+ Node ppRewrite(TNode atom);
+
+ /** Check the assertions for satisfiability */
+ void check(Effort effort);
+
+ /** Identity string */
+ std::string identify() const { return "THEORY_IDL"; }
+
+};/* class TheoryIdl */
+
+}/* CVC4::theory::idl namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/ite_simplifier.cpp b/src/theory/ite_simplifier.cpp
index ec9eb27d4..463a9c41a 100644
--- a/src/theory/ite_simplifier.cpp
+++ b/src/theory/ite_simplifier.cpp
@@ -33,7 +33,7 @@ bool ITESimplifier::containsTermITE(TNode e)
}
hash_map<Node, bool, NodeHashFunction>::iterator it;
- it = d_containsTermITECache.find(e);
+ it = d_containsTermITECache.find(e);
if (it != d_containsTermITECache.end()) {
return (*it).second;
}
@@ -60,7 +60,7 @@ bool ITESimplifier::leavesAreConst(TNode e, TheoryId tid)
}
hash_map<Node, bool, NodeHashFunction>::iterator it;
- it = d_leavesConstCache.find(e);
+ it = d_leavesConstCache.find(e);
if (it != d_leavesConstCache.end()) {
return (*it).second;
}
diff --git a/src/theory/ite_simplifier.h b/src/theory/ite_simplifier.h
index 0f648f91d..07fa0dedb 100644
--- a/src/theory/ite_simplifier.h
+++ b/src/theory/ite_simplifier.h
@@ -31,9 +31,7 @@
#include "prop/prop_engine.h"
#include "context/cdhashset.h"
#include "theory/theory.h"
-#include "theory/substitutions.h"
#include "theory/rewriter.h"
-#include "theory/substitutions.h"
#include "theory/shared_terms_database.h"
#include "theory/term_registration_visitor.h"
#include "theory/valuation.h"
@@ -43,6 +41,7 @@
#include "util/ite_removal.h"
namespace CVC4 {
+namespace theory {
class ITESimplifier {
Node d_true;
@@ -160,6 +159,7 @@ public:
};
-}
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
#endif
diff --git a/src/theory/logic_info.cpp b/src/theory/logic_info.cpp
index dc9de8662..d74f36069 100644
--- a/src/theory/logic_info.cpp
+++ b/src/theory/logic_info.cpp
@@ -3,7 +3,7 @@
** \verbatim
** Original author: Morgan Deters
** Major contributors: none
- ** Minor contributors (to current version): Dejan Jovanovic
+ ** Minor contributors (to current version): Dejan Jovanovic, Tianyi Liang
** This file is part of the CVC4 project.
** Copyright (c) 2009-2013 New York University and The University of Iowa
** See the file COPYING in the top-level source directory for licensing
@@ -105,6 +105,10 @@ std::string LogicInfo::getLogicString() const {
ss << "DT";
++seen;
}
+ if(d_theories[THEORY_STRINGS]) {
+ ss << "S";
+ ++seen;
+ }
if(d_theories[THEORY_ARITH]) {
if(isDifferenceLogic()) {
ss << (areIntegersUsed() ? "I" : "");
@@ -177,10 +181,21 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc
enableTheory(THEORY_ARRAY);
++p;
}
+ if(*p == 'S') {
+ // Strings requires arith for length constraints,
+ // and UF for equality (?)
+ enableTheory(THEORY_STRINGS);
+ enableTheory(THEORY_UF);
+ enableTheory(THEORY_ARITH);
+ enableIntegers();
+ arithOnlyLinear();
+ ++p;
+ }
if(!strncmp(p, "UF", 2)) {
enableTheory(THEORY_UF);
p += 2;
}
+ // allow BV or DT in either order
if(!strncmp(p, "BV", 2)) {
enableTheory(THEORY_BV);
p += 2;
@@ -189,6 +204,10 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc
enableTheory(THEORY_DATATYPES);
p += 2;
}
+ if(!d_theories[THEORY_BV] && !strncmp(p, "BV", 2)) {
+ enableTheory(THEORY_BV);
+ p += 2;
+ }
if(!strncmp(p, "IDL", 3)) {
enableIntegers();
disableReals();
@@ -241,7 +260,12 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc
}
if(*p != '\0') {
stringstream err;
- err << "LogicInfo::setLogicString(): junk (\"" << p << "\") at end of logic string: " << logicString;
+ err << "LogicInfo::setLogicString(): ";
+ if(p == logicString) {
+ err << "cannot parse logic string: " << logicString;
+ } else {
+ err << "junk (\"" << p << "\") at end of logic string: " << logicString;
+ }
IllegalArgument(logicString, err.str().c_str());
}
diff --git a/src/theory/logic_info.h b/src/theory/logic_info.h
index c7b5c58f9..2448898c0 100644
--- a/src/theory/logic_info.h
+++ b/src/theory/logic_info.h
@@ -155,21 +155,25 @@ public:
/** Are integers in this logic? */
bool areIntegersUsed() const {
CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried");
+ CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether integers are used");
return d_integers;
}
/** Are reals in this logic? */
bool areRealsUsed() const {
CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried");
+ CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether reals are used");
return d_reals;
}
/** Does this logic only linear arithmetic? */
bool isLinear() const {
CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried");
+ CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether it's linear");
return d_linear || d_differenceLogic;
}
/** Does this logic only permit difference reasoning? (implies linear) */
bool isDifferenceLogic() const {
CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried");
+ CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether it's difference logic");
return d_differenceLogic;
}
diff --git a/src/theory/model.cpp b/src/theory/model.cpp
index 1c511be30..840c8bc3a 100644
--- a/src/theory/model.cpp
+++ b/src/theory/model.cpp
@@ -19,6 +19,7 @@
#include "smt/options.h"
#include "smt/smt_engine.h"
#include "theory/uf/theory_uf_model.h"
+#include "theory/uf/options.h"
using namespace std;
using namespace CVC4;
@@ -27,7 +28,7 @@ using namespace CVC4::context;
using namespace CVC4::theory;
TheoryModel::TheoryModel( context::Context* c, std::string name, bool enableFuncModels) :
- d_substitutions(c), d_equalityEngine(c, name), d_modelBuilt(c, false), d_enableFuncModels(enableFuncModels)
+ d_substitutions(c, false), d_equalityEngine(c, name), d_modelBuilt(c, false), d_enableFuncModels(enableFuncModels)
{
d_true = NodeManager::currentNM()->mkConst( true );
d_false = NodeManager::currentNM()->mkConst( false );
@@ -47,12 +48,15 @@ void TheoryModel::reset(){
d_uf_models.clear();
}
-Node TheoryModel::getValue( TNode n ) const{
+Node TheoryModel::getValue(TNode n) const {
//apply substitutions
- Node nn = d_substitutions.apply( n );
+ Node nn = d_substitutions.apply(n);
//get value in model
- nn = getModelValue( nn );
- Assert(nn.isConst() || nn.getKind() == kind::LAMBDA);
+ nn = getModelValue(nn);
+ if(options::condenseFunctionValues() || nn.getKind() != kind::LAMBDA) {
+ //normalize
+ nn = Rewriter::rewrite(nn);
+ }
return nn;
}
@@ -94,16 +98,15 @@ Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const
// no good. Instead, return the quantifier itself. If we're in
// checkModel(), and the quantifier actually matters, we'll get an
// assert-fail since the quantifier isn't a constant.
- if(!d_equalityEngine.hasTerm(n)) {
+ if(!d_equalityEngine.hasTerm(Rewriter::rewrite(n))) {
return n;
+ } else {
+ n = Rewriter::rewrite(n);
}
} else {
if(n.getKind() == kind::LAMBDA) {
NodeManager* nm = NodeManager::currentNM();
Node body = getModelValue(n[1], true);
- // This is a bit ugly, but cache inside simplifier can change, so can't be const
- // The ite simplifier is needed to get rid of artifacts created by Boolean terms
- body = const_cast<ITESimplifier*>(&d_iteSimp)->simpITE(body);
body = Rewriter::rewrite(body);
return nm->mkNode(kind::LAMBDA, n[0], body);
}
@@ -430,6 +433,7 @@ void TheoryEngineModelBuilder::checkTerms(TNode n, TheoryModel* tm, NodeSet& cac
void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel)
{
+ Trace("model-builder") << "TheoryEngineModelBuilder: buildModel, fullModel = " << fullModel << std::endl;
TheoryModel* tm = (TheoryModel*)m;
// buildModel with fullModel = true should only be called once in any context
@@ -718,6 +722,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel)
}
if (!fullModel) {
+ Trace("model-builder") << "Make sure ECs have reps..." << std::endl;
// Make sure every EC has a rep
for (itMap = assertedReps.begin(); itMap != assertedReps.end(); ++itMap ) {
tm->d_reps[itMap->first] = itMap->second;
@@ -851,8 +856,10 @@ void TheoryEngineModelBuilder::processBuildModel(TheoryModel* m, bool fullModel)
default_v = (*te);
}
ufmt.setDefaultValue( m, default_v );
- ufmt.simplify();
- Node val = ufmt.getFunctionValue( "_ufmt_" );
+ if(options::condenseFunctionValues()) {
+ ufmt.simplify();
+ }
+ Node val = ufmt.getFunctionValue( "_ufmt_", options::condenseFunctionValues() );
Trace("model-builder") << " Assigning (" << n << ") to (" << val << ")" << endl;
m->d_uf_models[n] = val;
//ufmt.debugPrint( std::cout, m );
diff --git a/src/theory/options b/src/theory/options
index 5d752fca1..9944264c8 100644
--- a/src/theory/options
+++ b/src/theory/options
@@ -5,8 +5,8 @@
module THEORY "theory/options.h" Theory layer
-expert-option theoryOfMode --theoryof-mode=MODE CVC4::theory::TheoryOfMode :handler CVC4::theory::stringToTheoryOfMode :handler-include "theory/options_handlers.h" :default CVC4::theory::THEORY_OF_TYPE_BASED :include "theory/theoryof_mode.h"
- mode for theoryof
+expert-option theoryOfMode theoryof-mode --theoryof-mode=MODE CVC4::theory::TheoryOfMode :handler CVC4::theory::stringToTheoryOfMode :handler-include "theory/options_handlers.h" :default CVC4::theory::THEORY_OF_TYPE_BASED :include "theory/theoryof_mode.h" :read-write
+ mode for Theory::theoryof()
option - use-theory --use-theory=NAME argument :handler CVC4::theory::useTheory :handler-include "theory/options_handlers.h"
use alternate theory implementation NAME (--use-theory=help for a list)
diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h
index af3065404..44b89e8cb 100644
--- a/src/theory/output_channel.h
+++ b/src/theory/output_channel.h
@@ -127,9 +127,12 @@ public:
*/
LemmaStatus split(TNode n)
throw(TypeCheckingExceptionPrivate, AssertionException) {
- return lemma(n.orNode(n.notNode()));
+ return splitLemma(n.orNode(n.notNode()));
}
+ virtual LemmaStatus splitLemma(TNode n, bool removable = false)
+ throw(TypeCheckingExceptionPrivate, AssertionException) = 0;
+
/**
* If a decision is made on n, it must be in the phase specified.
* Note that this is enforced *globally*, i.e., it is completely
@@ -219,7 +222,7 @@ public:
/** Demands that the search restart from sat search level 0.
* Using this leads to non-termination issues.
- * It is appropraite for prototyping for theories.
+ * It is appropriate for prototyping for theories.
*/
virtual void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException) {}
diff --git a/src/theory/quantifiers/Makefile.am b/src/theory/quantifiers/Makefile.am
index 7fea8cf3a..be24d6c67 100644
--- a/src/theory/quantifiers/Makefile.am
+++ b/src/theory/quantifiers/Makefile.am
@@ -23,8 +23,6 @@ libquantifiers_la_SOURCES = \
model_engine.cpp \
modes.cpp \
modes.h \
- relevant_domain.h \
- relevant_domain.cpp \
term_database.h \
term_database.cpp \
first_order_model.h \
@@ -44,7 +42,20 @@ libquantifiers_la_SOURCES = \
inst_strategy_e_matching.h \
inst_strategy_e_matching.cpp \
inst_strategy_cbqi.h \
- inst_strategy_cbqi.cpp
+ inst_strategy_cbqi.cpp \
+ full_model_check.h \
+ full_model_check.cpp \
+ bounded_integers.h \
+ bounded_integers.cpp \
+ first_order_reasoning.h \
+ first_order_reasoning.cpp \
+ rewrite_engine.h \
+ rewrite_engine.cpp \
+ relevant_domain.h \
+ relevant_domain.cpp \
+ symmetry_breaking.h \
+ symmetry_breaking.cpp
+
EXTRA_DIST = \
kinds \
diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp
new file mode 100644
index 000000000..30ff5242b
--- /dev/null
+++ b/src/theory/quantifiers/bounded_integers.cpp
@@ -0,0 +1,372 @@
+/********************* */
+/*! \file bounded_integers.cpp
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Bounded integers module
+ **
+ ** This class manages integer bounds for quantifiers
+ **/
+
+#include "theory/quantifiers/bounded_integers.h"
+#include "theory/quantifiers/quant_util.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/model_engine.h"
+
+using namespace CVC4;
+using namespace std;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace CVC4::kind;
+
+void BoundedIntegers::RangeModel::initialize() {
+ //add initial split lemma
+ Node ltr = NodeManager::currentNM()->mkNode( LT, d_range, NodeManager::currentNM()->mkConst( Rational(0) ) );
+ ltr = Rewriter::rewrite( ltr );
+ Trace("bound-int-lemma") << " *** bound int: initial split on " << ltr << std::endl;
+ d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr );
+ Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr;
+ d_range_literal[-1] = ltr_lit;
+ d_lit_to_range[ltr_lit] = -1;
+ d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT;
+ //register with bounded integers
+ Trace("bound-int-debug") << "Literal " << ltr_lit << " is literal for " << d_range << std::endl;
+ d_bi->addLiteralFromRange(ltr_lit, d_range);
+}
+
+void BoundedIntegers::RangeModel::assertNode(Node n) {
+ bool pol = n.getKind()!=NOT;
+ Node nlit = n.getKind()==NOT ? n[0] : n;
+ if( d_lit_to_range.find( nlit )!=d_lit_to_range.end() ){
+ Trace("bound-int-assert") << "With polarity = " << pol << " (req "<< d_lit_to_pol[nlit] << ")";
+ Trace("bound-int-assert") << ", found literal " << nlit;
+ Trace("bound-int-assert") << ", it is bound literal " << d_lit_to_range[nlit] << " for " << d_range << std::endl;
+ d_range_assertions[nlit] = (pol==d_lit_to_pol[nlit]);
+ if( pol!=d_lit_to_pol[nlit] ){
+ //check if we need a new split?
+ if( !d_has_range ){
+ bool needsRange = true;
+ for( std::map< Node, int >::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){
+ if( d_range_assertions.find( it->first )==d_range_assertions.end() ){
+ needsRange = false;
+ break;
+ }
+ }
+ if( needsRange ){
+ allocateRange();
+ }
+ }
+ }else{
+ if (!d_has_range || d_lit_to_range[nlit]<d_curr_range ){
+ Trace("bound-int-bound") << "Successfully bound " << d_range << " to " << d_lit_to_range[nlit] << std::endl;
+ d_curr_range = d_lit_to_range[nlit];
+ }
+ //set the range
+ d_has_range = true;
+ }
+ }else{
+ Message() << "Could not find literal " << nlit << " for range " << d_range << std::endl;
+ exit(0);
+ }
+}
+
+void BoundedIntegers::RangeModel::allocateRange() {
+ d_curr_max++;
+ int newBound = d_curr_max;
+ Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl;
+ //TODO: newBound should be chosen in a smarter way
+ Node ltr = NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) );
+ ltr = Rewriter::rewrite( ltr );
+ Trace("bound-int-lemma") << " *** bound int: split on " << ltr << std::endl;
+ d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr );
+ Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr;
+ d_range_literal[newBound] = ltr_lit;
+ d_lit_to_range[ltr_lit] = newBound;
+ d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT;
+ //register with bounded integers
+ d_bi->addLiteralFromRange(ltr_lit, d_range);
+}
+
+Node BoundedIntegers::RangeModel::getNextDecisionRequest() {
+ //request the current cardinality as a decision literal, if not already asserted
+ for( std::map< Node, int >::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){
+ int i = it->second;
+ if( !d_has_range || i<d_curr_range ){
+ Node rn = it->first;
+ Assert( !rn.isNull() );
+ if( d_range_assertions.find( rn )==d_range_assertions.end() ){
+ if (!d_lit_to_pol[it->first]) {
+ rn = rn.negate();
+ }
+ Trace("bound-int-dec") << "For " << d_range << ", make decision " << rn << " to make range " << i << std::endl;
+ return rn;
+ }
+ }
+ }
+ return Node::null();
+}
+
+
+BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) :
+QuantifiersModule(qe), d_assertions(c){
+
+}
+
+bool BoundedIntegers::isBound( Node f, Node v ) {
+ return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end();
+}
+
+bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) {
+ if( b.getKind()==BOUND_VARIABLE ){
+ if( !isBound( f, b ) ){
+ return true;
+ }
+ }else{
+ for( unsigned i=0; i<b.getNumChildren(); i++ ){
+ if( hasNonBoundVar( f, b[i] ) ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void BoundedIntegers::processLiteral( Node f, Node lit, bool pol,
+ std::map< int, std::map< Node, Node > >& bound_lit_map,
+ std::map< int, std::map< Node, bool > >& bound_lit_pol_map ) {
+ if( lit.getKind()==GEQ && lit[0].getType().isInteger() ){
+ std::map< Node, Node > msum;
+ if (QuantArith::getMonomialSumLit( lit, msum )){
+ Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is monomial sum : " << std::endl;
+ for(std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){
+ Trace("bound-int-debug") << " ";
+ if( !it->second.isNull() ){
+ Trace("bound-int-debug") << it->second;
+ if( !it->first.isNull() ){
+ Trace("bound-int-debug") << " * ";
+ }
+ }
+ if( !it->first.isNull() ){
+ Trace("bound-int-debug") << it->first;
+ }
+ Trace("bound-int-debug") << std::endl;
+ }
+ Trace("bound-int-debug") << std::endl;
+ for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){
+ if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( f, it->first ) ){
+ Node veq;
+ if( QuantArith::isolate( it->first, msum, veq, GEQ ) ){
+ Node n1 = veq[0];
+ Node n2 = veq[1];
+ if(pol){
+ //flip
+ n1 = veq[1];
+ n2 = veq[0];
+ if( n1.getKind()==BOUND_VARIABLE ){
+ n2 = QuantArith::offset( n2, 1 );
+ }else{
+ n1 = QuantArith::offset( n1, -1 );
+ }
+ veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 );
+ }
+ Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl;
+ Node bv = n1.getKind()==BOUND_VARIABLE ? n1 : n2;
+ if( !isBound( f, bv ) ){
+ if( !hasNonBoundVar( f, n1.getKind()==BOUND_VARIABLE ? n2 : n1 ) ) {
+ Trace("bound-int-debug") << "The bound is relevant." << std::endl;
+ int loru = n1.getKind()==BOUND_VARIABLE ? 0 : 1;
+ d_bounds[loru][f][bv] = (n1.getKind()==BOUND_VARIABLE ? n2 : n1);
+ bound_lit_map[loru][bv] = lit;
+ bound_lit_pol_map[loru][bv] = pol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }else if( lit.getKind()==LEQ || lit.getKind()==LT || lit.getKind()==GT ) {
+ Message() << "BoundedIntegers : Bad kind for literal : " << lit << std::endl;
+ exit(0);
+ }
+}
+
+void BoundedIntegers::process( Node f, Node n, bool pol,
+ std::map< int, std::map< Node, Node > >& bound_lit_map,
+ std::map< int, std::map< Node, bool > >& bound_lit_pol_map ){
+ if( (( n.getKind()==IMPLIES || n.getKind()==OR) && pol) || (n.getKind()==AND && !pol) ){
+ for( unsigned i=0; i<n.getNumChildren(); i++) {
+ bool newPol = n.getKind()==IMPLIES && i==0 ? !pol : pol;
+ process( f, n[i], newPol, bound_lit_map, bound_lit_pol_map );
+ }
+ }else if( n.getKind()==NOT ){
+ process( f, n[0], !pol, bound_lit_map, bound_lit_pol_map );
+ }else {
+ processLiteral( f, n, pol, bound_lit_map, bound_lit_pol_map );
+ }
+}
+
+void BoundedIntegers::check( Theory::Effort e ) {
+
+}
+
+
+void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) {
+ d_lit_to_ranges[lit].push_back(r);
+ //check if it is already asserted?
+ if(d_assertions.find(lit)!=d_assertions.end()){
+ d_rms[r]->assertNode( d_assertions[lit] ? lit : lit.negate() );
+ }
+}
+
+void BoundedIntegers::registerQuantifier( Node f ) {
+ Trace("bound-int") << "Register quantifier " << f << std::endl;
+ bool hasIntType = false;
+ int finiteTypes = 0;
+ std::map< Node, int > numMap;
+ for( unsigned i=0; i<f[0].getNumChildren(); i++) {
+ numMap[f[0][i]] = i;
+ if( f[0][i].getType().isInteger() ){
+ hasIntType = true;
+ }
+ else if( f[0][i].getType().isSort() ){
+ finiteTypes++;
+ }
+ }
+ if( hasIntType ){
+ bool success;
+ do{
+ std::map< int, std::map< Node, Node > > bound_lit_map;
+ std::map< int, std::map< Node, bool > > bound_lit_pol_map;
+ success = false;
+ process( f, f[1], true, bound_lit_map, bound_lit_pol_map );
+ for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){
+ Node v = it->first;
+ if( !isBound(f,v) ){
+ if( d_bounds[1][f].find(v)!=d_bounds[1][f].end() ){
+ d_set[f].push_back(v);
+ d_set_nums[f].push_back(numMap[v]);
+ success = true;
+ //set Attributes on literals
+ for( unsigned b=0; b<2; b++ ){
+ Assert( bound_lit_map[b].find( v )!=bound_lit_map[b].end() );
+ Assert( bound_lit_pol_map[b].find( v )!=bound_lit_pol_map[b].end() );
+ BoundIntLitAttribute bila;
+ bound_lit_map[b][v].setAttribute( bila, bound_lit_pol_map[b][v] ? 1 : 0 );
+ }
+ Trace("bound-int") << "Variable " << v << " is bound because of literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl;
+ }
+ }
+ }
+ }while( success );
+ Trace("bound-int") << "Bounds are : " << std::endl;
+ for( unsigned i=0; i<d_set[f].size(); i++) {
+ Node v = d_set[f][i];
+ Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] );
+ d_range[f][v] = Rewriter::rewrite( r );
+ Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl;
+ }
+ if( d_set[f].size()==(f[0].getNumChildren()-finiteTypes) ){
+ d_bound_quants.push_back( f );
+ for( unsigned i=0; i<d_set[f].size(); i++) {
+ Node v = d_set[f][i];
+ Node r = d_range[f][v];
+ if( quantifiers::TermDb::hasBoundVarAttr(r) ){
+ //introduce a new bound
+ Node new_range = NodeManager::currentNM()->mkSkolem( "bir_$$", r.getType(), "bound for term" );
+ d_nground_range[f][v] = d_range[f][v];
+ d_range[f][v] = new_range;
+ r = new_range;
+ }
+ if( r.getKind()!=CONST_RATIONAL ){
+ if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){
+ Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << " " << r.getKind() << std::endl;
+ d_ranges.push_back( r );
+ d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext() );
+ d_rms[r]->initialize();
+ }
+ }
+ }
+ }else{
+ Trace("bound-int-warn") << "Warning : Bounded Integers : Could not find bounds for " << f << std::endl;
+ }
+ }
+}
+
+void BoundedIntegers::assertNode( Node n ) {
+ Trace("bound-int-assert") << "Assert " << n << std::endl;
+ Node nlit = n.getKind()==NOT ? n[0] : n;
+ if( d_lit_to_ranges.find(nlit)!=d_lit_to_ranges.end() ){
+ Trace("bound-int-assert") << "This is the bounding literal for " << d_lit_to_ranges[nlit].size() << " ranges." << std::endl;
+ for( unsigned i=0; i<d_lit_to_ranges[nlit].size(); i++) {
+ Node r = d_lit_to_ranges[nlit][i];
+ Trace("bound-int-assert") << " ...this is a bounding literal for " << r << std::endl;
+ d_rms[r]->assertNode( n );
+ }
+ }
+ d_assertions[nlit] = n.getKind()!=NOT;
+}
+
+Node BoundedIntegers::getNextDecisionRequest() {
+ Trace("bound-int-dec") << "bi: Get next decision request?" << std::endl;
+ for( unsigned i=0; i<d_ranges.size(); i++) {
+ Node d = d_rms[d_ranges[i]]->getNextDecisionRequest();
+ if (!d.isNull()) {
+ return d;
+ }
+ }
+ return Node::null();
+}
+
+void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) {
+ l = d_bounds[0][f][v];
+ u = d_bounds[1][f][v];
+ if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){
+ //must create substitution
+ std::vector< Node > vars;
+ std::vector< Node > subs;
+ Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl;
+ for( unsigned i=0; i<d_set[f].size(); i++) {
+ if( d_set[f][i]!=v ){
+ Trace("bound-int-rsi") << "Look up the value for " << d_set[f][i] << " " << rsi->d_var_order[d_set_nums[f][i]] << std::endl;
+ Trace("bound-int-rsi") << "term : " << rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]]) << std::endl;
+ vars.push_back(d_set[f][i]);
+ subs.push_back(rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]]));
+ }else{
+ break;
+ }
+ }
+ Trace("bound-int-rsi") << "Do substitution..." << std::endl;
+ //check if it has been instantiated
+ if (!vars.empty() && !d_bnd_it[f][v].hasInstantiated(subs)){
+ //must add the lemma
+ Node nn = d_nground_range[f][v];
+ nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
+ Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[f][v] );
+ Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl;
+ d_quantEngine->getOutputChannel().lemma(lem);
+ l = Node::null();
+ u = Node::null();
+ return;
+ }else{
+ u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
+ l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
+ }
+ }
+}
+
+void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) {
+ getBounds( f, v, rsi, l, u );
+ Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl;
+ l = d_quantEngine->getModel()->getCurrentModelValue( l );
+ u = d_quantEngine->getModel()->getCurrentModelValue( u );
+ Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl;
+ return;
+}
+
+bool BoundedIntegers::isGroundRange(Node f, Node v) {
+ return isBoundVar(f,v) && !quantifiers::TermDb::hasBoundVarAttr(getLowerBound(f,v)) && !quantifiers::TermDb::hasBoundVarAttr(getUpperBound(f,v));
+}
diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h
new file mode 100644
index 000000000..3da938d31
--- /dev/null
+++ b/src/theory/quantifiers/bounded_integers.h
@@ -0,0 +1,127 @@
+/********************* */
+/*! \file bounded_integers.h
+** \verbatim
+** Original author: Andrew Reynolds
+** This file is part of the CVC4 project.
+** Copyright (c) 2009-2013 New York University and The University of Iowa
+** See the file COPYING in the top-level source directory for licensing
+** information.\endverbatim
+**
+** \brief This class manages integer bounds for quantifiers
+**/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__BOUNDED_INTEGERS_H
+#define __CVC4__BOUNDED_INTEGERS_H
+
+
+#include "theory/quantifiers_engine.h"
+
+#include "context/context.h"
+#include "context/context_mm.h"
+#include "context/cdchunk_list.h"
+
+namespace CVC4 {
+namespace theory {
+
+class RepSetIterator;
+
+namespace quantifiers {
+
+
+class BoundedIntegers : public QuantifiersModule
+{
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+ typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap;
+ typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
+private:
+ //for determining bounds
+ bool isBound( Node f, Node v );
+ bool hasNonBoundVar( Node f, Node b );
+ std::map< Node, std::map< Node, Node > > d_bounds[2];
+ std::map< Node, std::vector< Node > > d_set;
+ std::map< Node, std::vector< int > > d_set_nums;
+ std::map< Node, std::map< Node, Node > > d_range;
+ std::map< Node, std::map< Node, Node > > d_nground_range;
+ void hasFreeVar( Node f, Node n );
+ void process( Node f, Node n, bool pol,
+ std::map< int, std::map< Node, Node > >& bound_lit_map,
+ std::map< int, std::map< Node, bool > >& bound_lit_pol_map );
+ void processLiteral( Node f, Node lit, bool pol,
+ std::map< int, std::map< Node, Node > >& bound_lit_map,
+ std::map< int, std::map< Node, bool > >& bound_lit_pol_map );
+ std::vector< Node > d_bound_quants;
+private:
+ class RangeModel {
+ private:
+ BoundedIntegers * d_bi;
+ void allocateRange();
+ public:
+ RangeModel(BoundedIntegers * bi, Node r, context::Context* c) : d_bi(bi),
+ d_range(r), d_curr_max(-1), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1) {}
+ Node d_range;
+ int d_curr_max;
+ std::map< int, Node > d_range_literal;
+ std::map< Node, bool > d_lit_to_pol;
+ std::map< Node, int > d_lit_to_range;
+ NodeBoolMap d_range_assertions;
+ context::CDO< bool > d_has_range;
+ context::CDO< int > d_curr_range;
+ void initialize();
+ void assertNode(Node n);
+ Node getNextDecisionRequest();
+ };
+private:
+ //information for minimizing ranges
+ std::vector< Node > d_ranges;
+ //map to range model objects
+ std::map< Node, RangeModel * > d_rms;
+ //literal to range
+ std::map< Node, std::vector< Node > > d_lit_to_ranges;
+ //list of currently asserted arithmetic literals
+ NodeBoolMap d_assertions;
+private:
+ //class to store whether bounding lemmas have been added
+ class BoundInstTrie
+ {
+ public:
+ std::map< Node, BoundInstTrie > d_children;
+ bool hasInstantiated( std::vector< Node > & vals, int index = 0, bool madeNew = false ){
+ if( index>=(int)vals.size() ){
+ return !madeNew;
+ }else{
+ Node n = vals[index];
+ if( d_children.find(n)==d_children.end() ){
+ madeNew = true;
+ }
+ return d_children[n].hasInstantiated(vals,index+1,madeNew);
+ }
+ }
+ };
+ std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it;
+private:
+ void addLiteralFromRange( Node lit, Node r );
+public:
+ BoundedIntegers( context::Context* c, QuantifiersEngine* qe );
+
+ void check( Theory::Effort e );
+ void registerQuantifier( Node f );
+ void assertNode( Node n );
+ Node getNextDecisionRequest();
+ bool isBoundVar( Node f, Node v ) { return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); }
+ unsigned getNumBoundVars( Node f ) { return d_set[f].size(); }
+ Node getBoundVar( Node f, int i ) { return d_set[f][i]; }
+ int getBoundVarNum( Node f, int i ) { return d_set_nums[f][i]; }
+ Node getLowerBound( Node f, Node v ){ return d_bounds[0][f][v]; }
+ Node getUpperBound( Node f, Node v ){ return d_bounds[1][f][v]; }
+ void getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u );
+ void getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u );
+ bool isGroundRange(Node f, Node v);
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/candidate_generator.cpp b/src/theory/quantifiers/candidate_generator.cpp
index 0c423de19..42b49cf01 100644
--- a/src/theory/quantifiers/candidate_generator.cpp
+++ b/src/theory/quantifiers/candidate_generator.cpp
@@ -27,7 +27,7 @@ using namespace CVC4::theory;
using namespace CVC4::theory::inst;
bool CandidateGenerator::isLegalCandidate( Node n ){
- return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !n.hasAttribute(InstConstantAttribute()) ) );
+ return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ) );
}
void CandidateGeneratorQueue::addCandidate( Node n ) {
@@ -149,7 +149,7 @@ void CandidateGeneratorQELitEq::reset( Node eqc ){
d_eq = eq::EqClassesIterator( d_qe->getEqualityQuery()->getEngine() );
}
Node CandidateGeneratorQELitEq::getNextCandidate(){
- while( d_eq.isFinished() ){
+ while( !d_eq.isFinished() ){
Node n = (*d_eq);
++d_eq;
if( n.getType()==d_match_pattern[0].getType() ){
@@ -186,3 +186,29 @@ Node CandidateGeneratorQELitDeq::getNextCandidate(){
}
return Node::null();
}
+
+
+CandidateGeneratorQEAll::CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat ) :
+ d_match_pattern( mpat ), d_qe( qe ){
+
+}
+
+void CandidateGeneratorQEAll::resetInstantiationRound() {
+
+}
+
+void CandidateGeneratorQEAll::reset( Node eqc ) {
+ d_eq = eq::EqClassesIterator( d_qe->getEqualityQuery()->getEngine() );
+}
+
+Node CandidateGeneratorQEAll::getNextCandidate() {
+ while( !d_eq.isFinished() ){
+ Node n = (*d_eq);
+ ++d_eq;
+ if( n.getType()==d_match_pattern.getType() ){
+ //an equivalence class with the same type as the pattern, return it
+ return n;
+ }
+ }
+ return Node::null();
+}
diff --git a/src/theory/quantifiers/candidate_generator.h b/src/theory/quantifiers/candidate_generator.h
index 81b98ce0a..402a29848 100644
--- a/src/theory/quantifiers/candidate_generator.h
+++ b/src/theory/quantifiers/candidate_generator.h
@@ -69,8 +69,7 @@ public:
Node getNextCandidate();
};/* class CandidateGeneratorQueue */
-class CandidateGeneratorQEDisequal;
-
+//the default generator
class CandidateGeneratorQE : public CandidateGenerator
{
friend class CandidateGeneratorQEDisequal;
@@ -93,27 +92,6 @@ public:
Node getNextCandidate();
};
-
-//class CandidateGeneratorQEDisequal : public CandidateGenerator
-//{
-//private:
-// //equivalence class
-// Node d_eq_class;
-// //equivalence class info
-// EqClassInfo* d_eci;
-// //equivalence class iterator
-// EqClassInfo::BoolMap::const_iterator d_eqci_iter;
-// //instantiator pointer
-// QuantifiersEngine* d_qe;
-//public:
-// CandidateGeneratorQEDisequal( QuantifiersEngine* qe, Node eqc );
-// ~CandidateGeneratorQEDisequal(){}
-//
-// void resetInstantiationRound();
-// void reset( Node eqc ); //should be what you want to be disequal from
-// Node getNextCandidate();
-//};
-
class CandidateGeneratorQELitEq : public CandidateGenerator
{
private:
@@ -150,6 +128,24 @@ public:
Node getNextCandidate();
};
+class CandidateGeneratorQEAll : public CandidateGenerator
+{
+private:
+ //the equality classes iterator
+ eq::EqClassesIterator d_eq;
+ //equality you are trying to match equalities for
+ Node d_match_pattern;
+ //einstantiator pointer
+ QuantifiersEngine* d_qe;
+public:
+ CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat );
+ ~CandidateGeneratorQEAll(){}
+
+ void resetInstantiationRound();
+ void reset( Node eqc );
+ Node getNextCandidate();
+};
+
}/* CVC4::theory::inst namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp
index bba9c0163..63cac9c15 100644
--- a/src/theory/quantifiers/first_order_model.cpp
+++ b/src/theory/quantifiers/first_order_model.cpp
@@ -25,6 +25,7 @@ using namespace CVC4::kind;
using namespace CVC4::context;
using namespace CVC4::theory;
using namespace CVC4::theory::quantifiers;
+using namespace CVC4::theory::quantifiers::fmcheck;
FirstOrderModel::FirstOrderModel( context::Context* c, std::string name ) : TheoryModel( c, name, true ),
d_axiom_asserted( c, false ), d_forall_asserts( c ), d_isModelSet( c, false ){
@@ -38,15 +39,34 @@ void FirstOrderModel::assertQuantifier( Node n ){
}
}
-void FirstOrderModel::reset(){
- TheoryModel::reset();
+Node FirstOrderModel::getCurrentModelValue( Node n, bool partial ) {
+ std::vector< Node > children;
+ if( n.getNumChildren()>0 ){
+ if( n.getKind()!=APPLY_UF && n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( n.getOperator() );
+ }
+ for (unsigned i=0; i<n.getNumChildren(); i++) {
+ Node nc = getCurrentModelValue( n[i], partial );
+ if (nc.isNull()) {
+ return Node::null();
+ }else{
+ children.push_back( nc );
+ }
+ }
+ if( n.getKind()==APPLY_UF ){
+ return getCurrentUfModelValue( n, children, partial );
+ }else{
+ Node nn = NodeManager::currentNM()->mkNode( n.getKind(), children );
+ nn = Rewriter::rewrite( nn );
+ return nn;
+ }
+ }else{
+ return getRepresentative(n);
+ }
}
-void FirstOrderModel::initialize( bool considerAxioms ){
- //rebuild models
- d_uf_model_tree.clear();
- d_uf_model_gen.clear();
- d_array_model.clear();
+void FirstOrderModel::initialize( bool considerAxioms ) {
+ processInitialize();
//this is called after representatives have been chosen and the equality engine has been built
//for each quantifier, collect all operators we care about
for( int i=0; i<getNumAssertedQuantifiers(); i++ ){
@@ -59,6 +79,23 @@ void FirstOrderModel::initialize( bool considerAxioms ){
}
void FirstOrderModel::initializeModelForTerm( Node n ){
+ processInitializeModelForTerm( n );
+ for( int i=0; i<(int)n.getNumChildren(); i++ ){
+ initializeModelForTerm( n[i] );
+ }
+}
+
+FirstOrderModelIG::FirstOrderModelIG(context::Context* c, std::string name) : FirstOrderModel(c,name) {
+
+}
+
+void FirstOrderModelIG::processInitialize(){
+ //rebuild models
+ d_uf_model_tree.clear();
+ d_uf_model_gen.clear();
+}
+
+void FirstOrderModelIG::processInitializeModelForTerm( Node n ){
if( n.getKind()==APPLY_UF ){
Node op = n.getOperator();
if( d_uf_model_tree.find( op )==d_uf_model_tree.end() ){
@@ -82,14 +119,11 @@ void FirstOrderModel::initializeModelForTerm( Node n ){
}
}
*/
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- initializeModelForTerm( n[i] );
- }
}
//for evaluation of quantifier bodies
-void FirstOrderModel::resetEvaluate(){
+void FirstOrderModelIG::resetEvaluate(){
d_eval_uf_use_default.clear();
d_eval_uf_model.clear();
d_eval_term_index_order.clear();
@@ -107,7 +141,7 @@ void FirstOrderModel::resetEvaluate(){
// if eVal = 0, then n' cannot be proven to be equal to phaseReq
// if eVal is not 0, then
// each n{ri->d_index[0]/x_0...ri->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model
-int FirstOrderModel::evaluate( Node n, int& depIndex, RepSetIterator* ri ){
+int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){
++d_eval_formulas;
//Debug("fmf-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl;
//Notice() << "Eval " << n << std::endl;
@@ -226,7 +260,7 @@ int FirstOrderModel::evaluate( Node n, int& depIndex, RepSetIterator* ri ){
}
}
-Node FirstOrderModel::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){
+Node FirstOrderModelIG::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){
//Message() << "Eval term " << n << std::endl;
Node val;
depIndex = ri->getNumTerms()-1;
@@ -342,7 +376,7 @@ Node FirstOrderModel::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){
return val;
}
-Node FirstOrderModel::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){
+Node FirstOrderModelIG::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){
depIndex = -1;
if( n.getNumChildren()==0 ){
return n;
@@ -372,14 +406,14 @@ Node FirstOrderModel::evaluateTermDefault( Node n, int& depIndex, std::vector< i
}
}
-void FirstOrderModel::clearEvalFailed( int index ){
+void FirstOrderModelIG::clearEvalFailed( int index ){
for( int i=0; i<(int)d_eval_failed_lits[index].size(); i++ ){
d_eval_failed[ d_eval_failed_lits[index][i] ] = false;
}
d_eval_failed_lits[index].clear();
}
-void FirstOrderModel::makeEvalUfModel( Node n ){
+void FirstOrderModelIG::makeEvalUfModel( Node n ){
if( d_eval_uf_model.find( n )==d_eval_uf_model.end() ){
makeEvalUfIndexOrder( n );
if( !d_eval_uf_use_default[n] ){
@@ -397,7 +431,7 @@ struct sortGetMaxVariableNum {
int computeMaxVariableNum( Node n ){
if( n.getKind()==INST_CONSTANT ){
return n.getAttribute(InstVarNumAttribute());
- }else if( n.hasAttribute(InstConstantAttribute()) ){
+ }else if( TermDb::hasInstConstAttr(n) ){
int maxVal = -1;
for( int i=0; i<(int)n.getNumChildren(); i++ ){
int val = getMaxVariableNum( n[i] );
@@ -423,7 +457,7 @@ struct sortGetMaxVariableNum {
bool operator() (Node i,Node j) { return (getMaxVariableNum(i)<getMaxVariableNum(j));}
};
-void FirstOrderModel::makeEvalUfIndexOrder( Node n ){
+void FirstOrderModelIG::makeEvalUfIndexOrder( Node n ){
if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){
#ifdef USE_INDEX_ORDERING
//sort arguments in order of least significant vs. most significant variable in default ordering
@@ -460,3 +494,187 @@ void FirstOrderModel::makeEvalUfIndexOrder( Node n ){
#endif
}
}
+
+Node FirstOrderModelIG::getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) {
+ std::vector< Node > children;
+ children.push_back(n.getOperator());
+ children.insert(children.end(), args.begin(), args.end());
+ Node nv = NodeManager::currentNM()->mkNode(APPLY_UF, children);
+ //make the term model specifically for nv
+ makeEvalUfModel( nv );
+ int argDepIndex;
+ if( d_eval_uf_use_default[nv] ){
+ return d_uf_model_tree[ n.getOperator() ].getValue( this, nv, argDepIndex );
+ }else{
+ return d_eval_uf_model[ nv ].getValue( this, nv, argDepIndex );
+ }
+}
+
+
+
+
+
+
+FirstOrderModelFmc::FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name) :
+FirstOrderModel(c, name), d_qe(qe){
+
+}
+
+Node FirstOrderModelFmc::getUsedRepresentative(Node n, bool strict) {
+ //Assert( fm->hasTerm(n) );
+ TypeNode tn = n.getType();
+ if( tn.isBoolean() ){
+ return areEqual(n, d_true) ? d_true : d_false;
+ }else{
+ if( !hasTerm(n) ){
+ if( strict ){
+ return Node::null();
+ }else{
+ Trace("fmc-warn") << "WARNING : no representative for " << n << std::endl;
+ }
+ }
+ Node r = getRepresentative(n);
+ if( d_model_basis_rep.find(tn)!=d_model_basis_rep.end() ){
+ if (r==d_model_basis_rep[tn]) {
+ r = d_qe->getTermDatabase()->getModelBasisTerm(tn);
+ }
+ }
+ return r;
+ }
+}
+
+Node FirstOrderModelFmc::getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) {
+ Trace("fmc-uf-model") << "Get model value for " << n << " " << n.getKind() << std::endl;
+ for(unsigned i=0; i<args.size(); i++) {
+ args[i] = getUsedRepresentative(args[i]);
+ }
+ Assert( n.getKind()==APPLY_UF );
+ return d_models[n.getOperator()]->evaluate(this, args);
+}
+
+void FirstOrderModelFmc::processInitialize() {
+ if( options::fmfFmcInterval() && intervalOp.isNull() ){
+ std::vector< TypeNode > types;
+ for(unsigned i=0; i<2; i++){
+ types.push_back(NodeManager::currentNM()->integerType());
+ }
+ TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->integerType() );
+ intervalOp = NodeManager::currentNM()->mkSkolem( "interval_$$", typ, "op representing interval" );
+ }
+ for( std::map<Node, Def * >::iterator it = d_models.begin(); it != d_models.end(); ++it ){
+ it->second->reset();
+ }
+ d_model_basis_rep.clear();
+}
+
+void FirstOrderModelFmc::processInitializeModelForTerm(Node n) {
+ if( n.getKind()==APPLY_UF ){
+ if( d_models.find(n.getOperator())==d_models.end()) {
+ d_models[n.getOperator()] = new Def;
+ }
+ }
+}
+
+Node FirstOrderModelFmc::getSomeDomainElement(TypeNode tn){
+ //check if there is even any domain elements at all
+ if (!d_rep_set.hasType(tn)) {
+ Trace("fmc-model-debug") << "Must create domain element for " << tn << "..." << std::endl;
+ Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(tn);
+ d_rep_set.d_type_reps[tn].push_back(mbt);
+ }else if( d_rep_set.d_type_reps[tn].size()==0 ){
+ Message() << "empty reps" << std::endl;
+ exit(0);
+ }
+ return d_rep_set.d_type_reps[tn][0];
+}
+
+
+bool FirstOrderModelFmc::isStar(Node n) {
+ return n==getStar(n.getType());
+}
+
+Node FirstOrderModelFmc::getStar(TypeNode tn) {
+ if( d_type_star.find(tn)==d_type_star.end() ){
+ Node st = NodeManager::currentNM()->mkSkolem( "star_$$", tn, "skolem created for full-model checking" );
+ d_type_star[tn] = st;
+ }
+ return d_type_star[tn];
+}
+
+Node FirstOrderModelFmc::getStarElement(TypeNode tn) {
+ Node st = getStar(tn);
+ if( options::fmfFmcInterval() && tn.isInteger() ){
+ st = getInterval( st, st );
+ }
+ return st;
+}
+
+bool FirstOrderModelFmc::isModelBasisTerm(Node n) {
+ return n==getModelBasisTerm(n.getType());
+}
+
+Node FirstOrderModelFmc::getModelBasisTerm(TypeNode tn) {
+ return d_qe->getTermDatabase()->getModelBasisTerm(tn);
+}
+
+Node FirstOrderModelFmc::getFunctionValue(Node op, const char* argPrefix ) {
+ Trace("fmc-model") << "Get function value for " << op << std::endl;
+ TypeNode type = op.getType();
+ std::vector< Node > vars;
+ for( size_t i=0; i<type.getNumChildren()-1; i++ ){
+ std::stringstream ss;
+ ss << argPrefix << (i+1);
+ Node b = NodeManager::currentNM()->mkBoundVar( ss.str(), type[i] );
+ vars.push_back( b );
+ }
+ Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars);
+ Node curr;
+ for( int i=(d_models[op]->d_cond.size()-1); i>=0; i--) {
+ Node v = getRepresentative( d_models[op]->d_value[i] );
+ if( curr.isNull() ){
+ curr = v;
+ }else{
+ //make the condition
+ Node cond = d_models[op]->d_cond[i];
+ std::vector< Node > children;
+ for( unsigned j=0; j<cond.getNumChildren(); j++) {
+ if (isInterval(cond[j])){
+ if( !isStar(cond[j][0]) ){
+ children.push_back( NodeManager::currentNM()->mkNode( GEQ, vars[j], cond[j][0] ) );
+ }
+ if( !isStar(cond[j][1]) ){
+ children.push_back( NodeManager::currentNM()->mkNode( LT, vars[j], cond[j][1] ) );
+ }
+ }else if (!isStar(cond[j])){
+ Node c = getUsedRepresentative( cond[j] );
+ children.push_back( NodeManager::currentNM()->mkNode( EQUAL, vars[j], c ) );
+ }
+ }
+ Assert( !children.empty() );
+ Node cc = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( AND, children );
+ curr = NodeManager::currentNM()->mkNode( ITE, cc, v, curr );
+ }
+ }
+ curr = Rewriter::rewrite( curr );
+ return NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, curr);
+}
+
+bool FirstOrderModelFmc::isInterval(Node n) {
+ return n.getKind()==APPLY_UF && n.getOperator()==intervalOp;
+}
+
+Node FirstOrderModelFmc::getInterval( Node lb, Node ub ){
+ return NodeManager::currentNM()->mkNode( APPLY_UF, intervalOp, lb, ub );
+}
+
+bool FirstOrderModelFmc::isInRange( Node v, Node i ) {
+ for( unsigned b=0; b<2; b++ ){
+ if( !isStar( i[b] ) ){
+ if( ( b==0 && i[b].getConst<Rational>() > v.getConst<Rational>() ) ||
+ ( b==1 && i[b].getConst<Rational>() <= v.getConst<Rational>() ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h
index 76f21e19c..f6e012660 100644
--- a/src/theory/quantifiers/first_order_model.h
+++ b/src/theory/quantifiers/first_order_model.h
@@ -19,7 +19,6 @@
#include "theory/model.h"
#include "theory/uf/theory_uf_model.h"
-#include "theory/arrays/theory_arrays_model.h"
namespace CVC4 {
namespace theory {
@@ -30,33 +29,22 @@ namespace quantifiers{
class TermDb;
+class FirstOrderModelIG;
+namespace fmcheck {
+ class FirstOrderModelFmc;
+}
+
class FirstOrderModel : public TheoryModel
{
private:
- //for initialize model
- void initializeModelForTerm( Node n );
/** whether an axiom is asserted */
context::CDO< bool > d_axiom_asserted;
/** list of quantifiers asserted in the current context */
context::CDList<Node> d_forall_asserts;
/** is model set */
context::CDO< bool > d_isModelSet;
-public: //for Theory UF:
- //models for each UF operator
- std::map< Node, uf::UfModelTree > d_uf_model_tree;
- //model generators
- std::map< Node, uf::UfModelTreeGenerator > d_uf_model_gen;
-private:
- //map from terms to the models used to calculate their value
- std::map< Node, bool > d_eval_uf_use_default;
- std::map< Node, uf::UfModelTree > d_eval_uf_model;
- void makeEvalUfModel( Node n );
- //index ordering to use for each term
- std::map< Node, std::vector< int > > d_eval_term_index_order;
- void makeEvalUfIndexOrder( Node n );
-public: //for Theory Arrays:
- //default value for each non-store array
- std::map< Node, arrays::ArrayModel > d_array_model;
+ /** get current model value */
+ virtual Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) = 0;
public: //for Theory Quantifiers:
/** assert quantifier */
void assertQuantifier( Node n );
@@ -66,19 +54,51 @@ public: //for Theory Quantifiers:
Node getAssertedQuantifier( int i ) { return d_forall_asserts[i]; }
/** bool axiom asserted */
bool isAxiomAsserted() { return d_axiom_asserted; }
+ /** initialize model for term */
+ void initializeModelForTerm( Node n );
+ virtual void processInitializeModelForTerm( Node n ) = 0;
public:
FirstOrderModel( context::Context* c, std::string name );
virtual ~FirstOrderModel(){}
- // reset the model
- void reset();
+ virtual FirstOrderModelIG * asFirstOrderModelIG() { return NULL; }
+ virtual fmcheck::FirstOrderModelFmc * asFirstOrderModelFmc() { return NULL; }
// initialize the model
void initialize( bool considerAxioms = true );
+ virtual void processInitialize() = 0;
/** mark model set */
void markModelSet() { d_isModelSet = true; }
/** is model set */
bool isModelSet() { return d_isModelSet; }
+ /** get current model value */
+ Node getCurrentModelValue( Node n, bool partial = false );
+};/* class FirstOrderModel */
+
+
+class FirstOrderModelIG : public FirstOrderModel
+{
+public: //for Theory UF:
+ //models for each UF operator
+ std::map< Node, uf::UfModelTree > d_uf_model_tree;
+ //model generators
+ std::map< Node, uf::UfModelTreeGenerator > d_uf_model_gen;
+private:
+ //map from terms to the models used to calculate their value
+ std::map< Node, bool > d_eval_uf_use_default;
+ std::map< Node, uf::UfModelTree > d_eval_uf_model;
+ void makeEvalUfModel( Node n );
+ //index ordering to use for each term
+ std::map< Node, std::vector< int > > d_eval_term_index_order;
+ void makeEvalUfIndexOrder( Node n );
+ /** get current model value */
+ Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial );
//the following functions are for evaluating quantifier bodies
public:
+ FirstOrderModelIG(context::Context* c, std::string name);
+ FirstOrderModelIG * asFirstOrderModelIG() { return this; }
+ // initialize the model
+ void processInitialize();
+ //for initialize model
+ void processInitializeModelForTerm( Node n );
/** reset evaluation */
void resetEvaluate();
/** evaluate functions */
@@ -97,7 +117,49 @@ private:
void clearEvalFailed( int index );
std::map< Node, bool > d_eval_failed;
std::map< int, std::vector< Node > > d_eval_failed_lits;
-};/* class FirstOrderModel */
+};
+
+
+namespace fmcheck {
+
+class Def;
+
+class FirstOrderModelFmc : public FirstOrderModel
+{
+ friend class FullModelChecker;
+private:
+ /** quant engine */
+ QuantifiersEngine * d_qe;
+ /** models for UF */
+ std::map<Node, Def * > d_models;
+ std::map<TypeNode, Node > d_model_basis_rep;
+ std::map<TypeNode, Node > d_type_star;
+ Node intervalOp;
+ Node getUsedRepresentative(Node n, bool strict = false);
+ /** get current model value */
+ Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial );
+ void processInitializeModelForTerm(Node n);
+public:
+ FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name);
+ FirstOrderModelFmc * asFirstOrderModelFmc() { return this; }
+ // initialize the model
+ void processInitialize();
+
+ Node getFunctionValue(Node op, const char* argPrefix );
+
+ bool isStar(Node n);
+ Node getStar(TypeNode tn);
+ Node getStarElement(TypeNode tn);
+ bool isModelBasisTerm(Node n);
+ Node getModelBasisTerm(TypeNode tn);
+ Node getSomeDomainElement(TypeNode tn);
+ bool isInterval(Node n);
+ Node getInterval( Node lb, Node ub );
+ bool isInRange( Node v, Node i );
+};
+
+}
+
}/* CVC4::theory::quantifiers namespace */
}/* CVC4::theory namespace */
diff --git a/src/theory/quantifiers/first_order_reasoning.cpp b/src/theory/quantifiers/first_order_reasoning.cpp
new file mode 100644
index 000000000..ebfb55f08
--- /dev/null
+++ b/src/theory/quantifiers/first_order_reasoning.cpp
@@ -0,0 +1,171 @@
+/********************* */
+/*! \file first_order_reasoning.cpp
+ ** \verbatim
+ ** Original author: ajreynol
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief first order reasoning module
+ **
+ **/
+
+#include <vector>
+
+#include "theory/quantifiers/first_order_reasoning.h"
+#include "theory/rewriter.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+
+
+void FirstOrderPropagation::collectLits( Node n, std::vector<Node> & lits ){
+ if( n.getKind()==FORALL ){
+ collectLits( n[1], lits );
+ }else if( n.getKind()==OR ){
+ for(unsigned j=0; j<n.getNumChildren(); j++) {
+ collectLits(n[j], lits );
+ }
+ }else{
+ lits.push_back( n );
+ }
+}
+
+void FirstOrderPropagation::simplify( std::vector< Node >& assertions ){
+ for( unsigned i=0; i<assertions.size(); i++) {
+ Trace("fo-rsn") << "Assert : " << assertions[i] << std::endl;
+ }
+
+ //process all assertions
+ int num_processed;
+ int num_true = 0;
+ int num_rounds = 0;
+ do {
+ num_processed = 0;
+ for( unsigned i=0; i<assertions.size(); i++ ){
+ if( d_assertion_true.find(assertions[i])==d_assertion_true.end() ){
+ std::vector< Node > fo_lits;
+ collectLits( assertions[i], fo_lits );
+ Node unitLit = process( assertions[i], fo_lits );
+ if( !unitLit.isNull() ){
+ Trace("fo-rsn-debug") << "...possible unit literal : " << unitLit << " from " << assertions[i] << std::endl;
+ bool pol = unitLit.getKind()!=NOT;
+ unitLit = unitLit.getKind()==NOT ? unitLit[0] : unitLit;
+ if( unitLit.getKind()==EQUAL ){
+
+ }else if( unitLit.getKind()==APPLY_UF ){
+ //make sure all are unique vars;
+ bool success = true;
+ std::vector< Node > unique_vars;
+ for( unsigned j=0; j<unitLit.getNumChildren(); j++) {
+ if( unitLit[j].getKind()==BOUND_VARIABLE ){
+ if( std::find(unique_vars.begin(), unique_vars.end(), unitLit[j])==unique_vars.end() ){
+ unique_vars.push_back( unitLit[j] );
+ }else{
+ success = false;
+ break;
+ }
+ }else{
+ success = false;
+ break;
+ }
+ }
+ if( success ){
+ d_const_def[unitLit.getOperator()] = NodeManager::currentNM()->mkConst(pol);
+ Trace("fo-rsn") << "Propagate : " << unitLit.getOperator() << " == " << pol << std::endl;
+ Trace("fo-rsn") << " from : " << assertions[i] << std::endl;
+ d_assertion_true[assertions[i]] = true;
+ num_processed++;
+ }
+ }else if( unitLit.getKind()==VARIABLE ){
+ d_const_def[unitLit] = NodeManager::currentNM()->mkConst(pol);
+ Trace("fo-rsn") << "Propagate variable : " << unitLit << " == " << pol << std::endl;
+ Trace("fo-rsn") << " from : " << assertions[i] << std::endl;
+ d_assertion_true[assertions[i]] = true;
+ num_processed++;
+ }
+ }
+ if( d_assertion_true.find(assertions[i])!=d_assertion_true.end() ){
+ num_true++;
+ }
+ }
+ }
+ num_rounds++;
+ }while( num_processed>0 );
+ Trace("fo-rsn-sum") << "Simplified " << num_true << " / " << assertions.size() << " in " << num_rounds << " rounds." << std::endl;
+ for( unsigned i=0; i<assertions.size(); i++ ){
+ assertions[i] = theory::Rewriter::rewrite( simplify( assertions[i] ) );
+ }
+}
+
+Node FirstOrderPropagation::process(Node a, std::vector< Node > & lits) {
+ int index = -1;
+ for( unsigned i=0; i<lits.size(); i++) {
+ bool pol = lits[i].getKind()!=NOT;
+ Node n = lits[i].getKind()==NOT ? lits[i][0] : lits[i];
+ Node litDef;
+ if( n.getKind()==APPLY_UF ){
+ if( d_const_def.find(n.getOperator())!=d_const_def.end() ){
+ litDef = d_const_def[n.getOperator()];
+ }
+ }else if( n.getKind()==VARIABLE ){
+ if( d_const_def.find(n)!=d_const_def.end() ){
+ litDef = d_const_def[n];
+ }
+ }
+ if( !litDef.isNull() ){
+ Node poln = NodeManager::currentNM()->mkConst( pol );
+ if( litDef==poln ){
+ Trace("fo-rsn-debug") << "Assertion " << a << " is true because of " << lits[i] << std::endl;
+ d_assertion_true[a] = true;
+ return Node::null();
+ }
+ }
+ if( litDef.isNull() ){
+ if( index==-1 ){
+ //store undefined index
+ index = i;
+ }else{
+ //two undefined, return null
+ return Node::null();
+ }
+ }
+ }
+ if( index!=-1 ){
+ return lits[index];
+ }else{
+ return Node::null();
+ }
+}
+
+Node FirstOrderPropagation::simplify( Node n ) {
+ if( n.getKind()==VARIABLE ){
+ if( d_const_def.find(n)!=d_const_def.end() ){
+ return d_const_def[n];
+ }
+ }else if( n.getKind()==APPLY_UF ){
+ if( d_const_def.find(n.getOperator())!=d_const_def.end() ){
+ return d_const_def[n.getOperator()];
+ }
+ }
+ if( n.getNumChildren()==0 ){
+ return n;
+ }else{
+ std::vector< Node > children;
+ if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( n.getOperator() );
+ }
+ for(unsigned i=0; i<n.getNumChildren(); i++) {
+ children.push_back( simplify(n[i]) );
+ }
+ return NodeManager::currentNM()->mkNode( n.getKind(), children );
+ }
+}
+
+}
diff --git a/src/theory/quantifiers/first_order_reasoning.h b/src/theory/quantifiers/first_order_reasoning.h
new file mode 100644
index 000000000..5f217050c
--- /dev/null
+++ b/src/theory/quantifiers/first_order_reasoning.h
@@ -0,0 +1,45 @@
+/********************* */
+/*! \file first_order_reasoning.h
+ ** \verbatim
+ ** Original author: ajreynol
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Pre-process step for first-order reasoning
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__FIRST_ORDER_REASONING_H
+#define __CVC4__FIRST_ORDER_REASONING_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include "expr/node.h"
+#include "expr/type_node.h"
+
+namespace CVC4 {
+
+class FirstOrderPropagation {
+private:
+ std::map< Node, Node > d_const_def;
+ std::map< Node, bool > d_assertion_true;
+ Node process(Node a, std::vector< Node > & lits);
+ void collectLits( Node n, std::vector<Node> & lits );
+ Node simplify( Node n );
+public:
+ FirstOrderPropagation(){}
+ ~FirstOrderPropagation(){}
+
+ void simplify( std::vector< Node >& assertions );
+};
+
+}
+
+#endif
diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp
new file mode 100644
index 000000000..bf10369e6
--- /dev/null
+++ b/src/theory/quantifiers/full_model_check.cpp
@@ -0,0 +1,1409 @@
+/********************* */
+/*! \file full_model_check.cpp
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of full model check class
+ **/
+
+#include "theory/quantifiers/full_model_check.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/options.h"
+
+using namespace std;
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace CVC4::theory::inst;
+using namespace CVC4::theory::quantifiers::fmcheck;
+
+struct ModelBasisArgSort
+{
+ std::vector< Node > d_terms;
+ bool operator() (int i,int j) {
+ return (d_terms[i].getAttribute(ModelBasisArgAttribute()) <
+ d_terms[j].getAttribute(ModelBasisArgAttribute()) );
+ }
+};
+
+
+struct ConstRationalSort
+{
+ std::vector< Node > d_terms;
+ bool operator() (int i, int j) {
+ return (d_terms[i].getConst<Rational>() < d_terms[j].getConst<Rational>() );
+ }
+};
+
+
+bool EntryTrie::hasGeneralization( FirstOrderModelFmc * m, Node c, int index ) {
+ if (index==(int)c.getNumChildren()) {
+ return d_data!=-1;
+ }else{
+ TypeNode tn = c[index].getType();
+ Node st = m->getStar(tn);
+ if(d_child.find(st)!=d_child.end()) {
+ if( d_child[st].hasGeneralization(m, c, index+1) ){
+ return true;
+ }
+ }
+ if( c[index]!=st && d_child.find( c[index] )!=d_child.end() ){
+ if( d_child[ c[index] ].hasGeneralization(m, c, index+1) ){
+ return true;
+ }
+ }
+ if( !options::fmfFmcInterval() || !c[index].getType().isInteger() ){
+ //for star: check if all children are defined and have generalizations
+ if( options::fmfFmcCoverSimplify() && c[index]==st ){
+ //check if all children exist and are complete
+ int num_child_def = d_child.size() - (d_child.find(st)!=d_child.end() ? 1 : 0);
+ if( num_child_def==m->d_rep_set.getNumRepresentatives(tn) ){
+ bool complete = true;
+ for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){
+ if( !m->isStar(it->first) ){
+ if( !it->second.hasGeneralization(m, c, index+1) ){
+ complete = false;
+ break;
+ }
+ }
+ }
+ if( complete ){
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+int EntryTrie::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node> & inst, int index ) {
+ Debug("fmc-entry-trie") << "Get generalization index " << inst.size() << " " << index << std::endl;
+ if (index==(int)inst.size()) {
+ return d_data;
+ }else{
+ int minIndex = -1;
+ if( options::fmfFmcInterval() && inst[index].getType().isInteger() ){
+ for( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){
+ if( !m->isInterval( it->first ) ){
+ std::cout << "Not an interval during getGenIndex " << it->first << std::endl;
+ exit( 11 );
+ }
+ //check if it is in the range
+ if( m->isInRange(inst[index], it->first ) ){
+ int gindex = it->second.getGeneralizationIndex(m, inst, index+1);
+ if( minIndex==-1 || (gindex!=-1 && gindex<minIndex) ){
+ minIndex = gindex;
+ }
+ }
+ }
+ }else{
+ Node st = m->getStar(inst[index].getType());
+ if(d_child.find(st)!=d_child.end()) {
+ minIndex = d_child[st].getGeneralizationIndex(m, inst, index+1);
+ }
+ Node cc = inst[index];
+ if( cc!=st && d_child.find( cc )!=d_child.end() ){
+ int gindex = d_child[ cc ].getGeneralizationIndex(m, inst, index+1);
+ if (minIndex==-1 || (gindex!=-1 && gindex<minIndex) ){
+ minIndex = gindex;
+ }
+ }
+ }
+ return minIndex;
+ }
+}
+
+void EntryTrie::addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index ) {
+ if (index==(int)c.getNumChildren()) {
+ if(d_data==-1) {
+ d_data = data;
+ }
+ }
+ else {
+ d_child[ c[index] ].addEntry(m,c,v,data,index+1);
+ if( d_complete==0 ){
+ d_complete = -1;
+ }
+ }
+}
+
+void EntryTrie::getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & gen, int index, bool is_gen ) {
+ if (index==(int)c.getNumChildren()) {
+ if( d_data!=-1) {
+ if( is_gen ){
+ gen.push_back(d_data);
+ }
+ compat.push_back(d_data);
+ }
+ }else{
+ if (m->isStar(c[index])) {
+ for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){
+ it->second.getEntries(m, c, compat, gen, index+1, is_gen );
+ }
+ }else{
+ Node st = m->getStar(c[index].getType());
+ if(d_child.find(st)!=d_child.end()) {
+ d_child[st].getEntries(m, c, compat, gen, index+1, false);
+ }
+ if( d_child.find( c[index] )!=d_child.end() ){
+ d_child[ c[index] ].getEntries(m, c, compat, gen, index+1, is_gen);
+ }
+ }
+
+ }
+}
+
+void EntryTrie::collectIndices(Node c, int index, std::vector< int >& indices ) {
+ if (index==(int)c.getNumChildren()) {
+ if( d_data!=-1 ){
+ indices.push_back( d_data );
+ }
+ }else{
+ for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){
+ it->second.collectIndices(c, index+1, indices );
+ }
+ }
+}
+
+bool EntryTrie::isComplete(FirstOrderModelFmc * m, Node c, int index) {
+ if( d_complete==-1 ){
+ d_complete = 1;
+ if (index<(int)c.getNumChildren()) {
+ Node st = m->getStar(c[index].getType());
+ if(d_child.find(st)!=d_child.end()) {
+ if (!d_child[st].isComplete(m, c, index+1)) {
+ d_complete = 0;
+ }
+ }else{
+ d_complete = 0;
+ }
+ }
+ }
+ return d_complete==1;
+}
+
+bool Def::addEntry( FirstOrderModelFmc * m, Node c, Node v) {
+ if (d_et.hasGeneralization(m, c)) {
+ Trace("fmc-debug") << "Already has generalization, skip." << std::endl;
+ return false;
+ }
+ int newIndex = (int)d_cond.size();
+ if (!d_has_simplified) {
+ std::vector<int> compat;
+ std::vector<int> gen;
+ d_et.getEntries(m, c, compat, gen);
+ for( unsigned i=0; i<compat.size(); i++) {
+ if( d_status[compat[i]]==status_unk ){
+ if( d_value[compat[i]]!=v ){
+ d_status[compat[i]] = status_non_redundant;
+ }
+ }
+ }
+ for( unsigned i=0; i<gen.size(); i++) {
+ if( d_status[gen[i]]==status_unk ){
+ if( d_value[gen[i]]==v ){
+ d_status[gen[i]] = status_redundant;
+ }
+ }
+ }
+ d_status.push_back( status_unk );
+ }
+ d_et.addEntry(m, c, v, newIndex);
+ d_cond.push_back(c);
+ d_value.push_back(v);
+ return true;
+}
+
+Node Def::evaluate( FirstOrderModelFmc * m, std::vector<Node>& inst ) {
+ int gindex = d_et.getGeneralizationIndex(m, inst);
+ if (gindex!=-1) {
+ return d_value[gindex];
+ }else{
+ Trace("fmc-warn") << "Warning : evaluation came up null!" << std::endl;
+ return Node::null();
+ }
+}
+
+int Def::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& inst ) {
+ return d_et.getGeneralizationIndex(m, inst);
+}
+
+void Def::basic_simplify( FirstOrderModelFmc * m ) {
+ d_has_simplified = true;
+ std::vector< Node > cond;
+ cond.insert( cond.end(), d_cond.begin(), d_cond.end() );
+ d_cond.clear();
+ std::vector< Node > value;
+ value.insert( value.end(), d_value.begin(), d_value.end() );
+ d_value.clear();
+ d_et.reset();
+ for (unsigned i=0; i<d_status.size(); i++) {
+ if( d_status[i]!=status_redundant ){
+ addEntry(m, cond[i], value[i]);
+ }
+ }
+ d_status.clear();
+}
+
+void Def::simplify(FullModelChecker * mc, FirstOrderModelFmc * m) {
+ basic_simplify( m );
+
+ //check if the last entry is not all star, if so, we can make the last entry all stars
+ if( !d_cond.empty() ){
+ bool last_all_stars = true;
+ Node cc = d_cond[d_cond.size()-1];
+ for( unsigned i=0; i<cc.getNumChildren(); i++ ){
+ if( !m->isInterval(cc[i]) && !m->isStar(cc[i]) ){
+ last_all_stars = false;
+ break;
+ }
+ }
+ if( !last_all_stars ){
+ Trace("fmc-cover-simplify") << "Need to modify last entry to be all stars." << std::endl;
+ Trace("fmc-cover-simplify") << "Before: " << std::endl;
+ debugPrint("fmc-cover-simplify",Node::null(), mc);
+ Trace("fmc-cover-simplify") << std::endl;
+ std::vector< Node > cond;
+ cond.insert( cond.end(), d_cond.begin(), d_cond.end() );
+ d_cond.clear();
+ std::vector< Node > value;
+ value.insert( value.end(), d_value.begin(), d_value.end() );
+ d_value.clear();
+ d_et.reset();
+ d_has_simplified = false;
+ //change the last to all star
+ std::vector< Node > nc;
+ nc.push_back( cc.getOperator() );
+ for( unsigned j=0; j< cc.getNumChildren(); j++){
+ nc.push_back(m->getStarElement(cc[j].getType()));
+ }
+ cond[cond.size()-1] = NodeManager::currentNM()->mkNode( APPLY_UF, nc );
+ //re-add the entries
+ for (unsigned i=0; i<cond.size(); i++) {
+ addEntry(m, cond[i], value[i]);
+ }
+ Trace("fmc-cover-simplify") << "Finished re-adding entries." << std::endl;
+ basic_simplify( m );
+ Trace("fmc-cover-simplify") << "After: " << std::endl;
+ debugPrint("fmc-cover-simplify",Node::null(), mc);
+ Trace("fmc-cover-simplify") << std::endl;
+ }
+ }
+}
+
+void Def::debugPrint(const char * tr, Node op, FullModelChecker * m) {
+ if (!op.isNull()) {
+ Trace(tr) << "Model for " << op << " : " << std::endl;
+ }
+ for( unsigned i=0; i<d_cond.size(); i++) {
+ //print the condition
+ if (!op.isNull()) {
+ Trace(tr) << op;
+ }
+ m->debugPrintCond(tr, d_cond[i], true);
+ Trace(tr) << " -> ";
+ m->debugPrint(tr, d_value[i]);
+ Trace(tr) << std::endl;
+ }
+}
+
+
+FullModelChecker::FullModelChecker(context::Context* c, QuantifiersEngine* qe) :
+QModelBuilder( c, qe ){
+ d_true = NodeManager::currentNM()->mkConst(true);
+ d_false = NodeManager::currentNM()->mkConst(false);
+}
+
+bool FullModelChecker::optBuildAtFullModel() {
+ //need to build after full model has taken effect if we are constructing interval models
+ // this is because we need to have a constant in all integer equivalence classes
+ return options::fmfFmcInterval();
+}
+
+void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){
+ FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc();
+ if( fullModel==optBuildAtFullModel() ){
+ Trace("fmc") << "---Full Model Check reset() " << std::endl;
+ fm->initialize( d_considerAxioms );
+ d_quant_models.clear();
+ d_rep_ids.clear();
+ d_star_insts.clear();
+ //process representatives
+ for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin();
+ it != fm->d_rep_set.d_type_reps.end(); ++it ){
+ if( it->first.isSort() ){
+ Trace("fmc") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl;
+ Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(it->first);
+ Node rmbt = fm->getUsedRepresentative( mbt);
+ int mbt_index = -1;
+ Trace("fmc") << " Model basis term : " << mbt << std::endl;
+ for( size_t a=0; a<it->second.size(); a++ ){
+ Node r = fm->getUsedRepresentative( it->second[a] );
+ std::vector< Node > eqc;
+ ((EqualityQueryQuantifiersEngine*)d_qe->getEqualityQuery())->getEquivalenceClass( r, eqc );
+ Trace("fmc-model-debug") << " " << (it->second[a]==r) << (r==mbt);
+ Trace("fmc-model-debug") << " : " << it->second[a] << " : " << r << " : ";
+ //Trace("fmc-model-debug") << r2 << " : " << ir << " : ";
+ Trace("fmc-model-debug") << " {";
+ //find best selection for representative
+ for( size_t i=0; i<eqc.size(); i++ ){
+ Trace("fmc-model-debug") << eqc[i] << ", ";
+ }
+ Trace("fmc-model-debug") << "}" << std::endl;
+
+ //if this is the model basis eqc, replace with actual model basis term
+ if (r==rmbt || (mbt_index==-1 && a==(it->second.size()-1))) {
+ fm->d_model_basis_rep[it->first] = r;
+ r = mbt;
+ mbt_index = a;
+ }
+ d_rep_ids[it->first][r] = (int)a;
+ }
+ Trace("fmc-model-debug") << std::endl;
+
+ if (mbt_index==-1) {
+ std::cout << " WARNING: model basis term is not a representative!" << std::endl;
+ exit(0);
+ }else{
+ Trace("fmc") << "Star index for " << it->first << " is " << mbt_index << std::endl;
+ }
+ }
+ }
+ //also process non-rep set sorts
+ for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
+ Node op = it->first;
+ TypeNode tno = op.getType();
+ for( unsigned i=0; i<tno.getNumChildren(); i++) {
+ initializeType( fm, tno[i] );
+ }
+ }
+ //now, make models
+ for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
+ Node op = it->first;
+ //reset the model
+ fm->d_models[op]->reset();
+
+ Trace("fmc-model-debug") << fm->d_uf_terms[op].size() << " model values for " << op << " ... " << std::endl;
+ for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){
+ Node r = fm->getUsedRepresentative(fm->d_uf_terms[op][i]);
+ Trace("fmc-model-debug") << fm->d_uf_terms[op][i] << " -> " << r << std::endl;
+ }
+ Trace("fmc-model-debug") << std::endl;
+
+
+ std::vector< Node > add_conds;
+ std::vector< Node > add_values;
+ bool needsDefault = true;
+ for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){
+ Node n = fm->d_uf_terms[op][i];
+ if( !n.getAttribute(NoMatchAttribute()) ){
+ add_conds.push_back( n );
+ add_values.push_back( n );
+ }
+ }
+ //possibly get default
+ if( needsDefault ){
+ Node nmb = d_qe->getTermDatabase()->getModelBasisOpTerm(op);
+ //add default value if necessary
+ if( fm->hasTerm( nmb ) ){
+ Trace("fmc-model-debug") << "Add default " << nmb << std::endl;
+ add_conds.push_back( nmb );
+ add_values.push_back( nmb );
+ }else{
+ Node vmb = getSomeDomainElement(fm, nmb.getType());
+ Trace("fmc-model-debug") << "Add default to default representative " << nmb << " ";
+ Trace("fmc-model-debug") << fm->d_rep_set.d_type_reps[nmb.getType()].size() << std::endl;
+ add_conds.push_back( nmb );
+ add_values.push_back( vmb );
+ }
+ }
+
+ std::vector< Node > conds;
+ std::vector< Node > values;
+ std::vector< Node > entry_conds;
+ //get the entries for the mdoel
+ for( size_t i=0; i<add_conds.size(); i++ ){
+ Node c = add_conds[i];
+ Node v = add_values[i];
+ std::vector< Node > children;
+ std::vector< Node > entry_children;
+ children.push_back(op);
+ entry_children.push_back(op);
+ bool hasNonStar = false;
+ for( unsigned i=0; i<c.getNumChildren(); i++) {
+ Node ri = fm->getUsedRepresentative( c[i]);
+ if( !ri.getType().isSort() && !ri.isConst() ){
+ Trace("fmc-warn") << "Warning : model has non-constant argument in model " << ri << std::endl;
+ }
+ children.push_back(ri);
+ if( !options::fmfFmcInterval() || !ri.getType().isInteger() ){
+ if (fm->isModelBasisTerm(ri) ) {
+ ri = fm->getStar( ri.getType() );
+ }else{
+ hasNonStar = true;
+ }
+ }
+ entry_children.push_back(ri);
+ }
+ Node n = NodeManager::currentNM()->mkNode( APPLY_UF, children );
+ Node nv = fm->getUsedRepresentative( v );
+ if( !nv.getType().isSort() && !nv.isConst() ){
+ Trace("fmc-warn") << "Warning : model has non-constant value in model " << nv << std::endl;
+ }
+ Node en = (useSimpleModels() && hasNonStar) ? n : NodeManager::currentNM()->mkNode( APPLY_UF, entry_children );
+ if( std::find(conds.begin(), conds.end(), n )==conds.end() ){
+ Trace("fmc-model-debug") << "- add " << n << " -> " << nv << " (entry is " << en << ")" << std::endl;
+ conds.push_back(n);
+ values.push_back(nv);
+ entry_conds.push_back(en);
+ }
+ else {
+ Trace("fmc-model-debug") << "Already have entry for : " << n << " -> " << nv << " (entry is " << en << ")" << std::endl;
+ }
+ }
+
+
+ //sort based on # default arguments
+ std::vector< int > indices;
+ ModelBasisArgSort mbas;
+ for (int i=0; i<(int)conds.size(); i++) {
+ d_qe->getTermDatabase()->computeModelBasisArgAttribute( conds[i] );
+ mbas.d_terms.push_back(conds[i]);
+ indices.push_back(i);
+ }
+ std::sort( indices.begin(), indices.end(), mbas );
+
+ for (int i=0; i<(int)indices.size(); i++) {
+ fm->d_models[op]->addEntry(fm, entry_conds[indices[i]], values[indices[i]]);
+ }
+
+
+ if( options::fmfFmcInterval() ){
+ Trace("fmc-interval-model") << "Changing to interval model, Before : " << std::endl;
+ fm->d_models[op]->debugPrint("fmc-interval-model", op, this);
+ Trace("fmc-interval-model") << std::endl;
+ std::vector< int > indices;
+ for( int i=0; i<(int)fm->d_models[op]->d_cond.size(); i++ ){
+ indices.push_back( i );
+ }
+ std::map< int, std::map< int, Node > > changed_vals;
+ makeIntervalModel( fm, op, indices, 0, changed_vals );
+
+ std::vector< Node > conds;
+ std::vector< Node > values;
+ for( unsigned i=0; i<fm->d_models[op]->d_cond.size(); i++ ){
+ if( changed_vals.find(i)==changed_vals.end() ){
+ conds.push_back( fm->d_models[op]->d_cond[i] );
+ }else{
+ std::vector< Node > children;
+ children.push_back( op );
+ for( unsigned j=0; j<fm->d_models[op]->d_cond[i].getNumChildren(); j++ ){
+ if( changed_vals[i].find(j)==changed_vals[i].end() ){
+ children.push_back( fm->d_models[op]->d_cond[i][j] );
+ }else{
+ children.push_back( changed_vals[i][j] );
+ }
+ }
+ Node nc = NodeManager::currentNM()->mkNode( APPLY_UF, children );
+ conds.push_back( nc );
+ Trace("fmc-interval-model") << "Interval : Entry #" << i << " changed to ";
+ debugPrintCond("fmc-interval-model", nc, true );
+ Trace("fmc-interval-model") << std::endl;
+ }
+ values.push_back( fm->d_models[op]->d_value[i] );
+ }
+ fm->d_models[op]->reset();
+ for( unsigned i=0; i<conds.size(); i++ ){
+ fm->d_models[op]->addEntry(fm, conds[i], values[i] );
+ }
+ }
+
+ Trace("fmc-model-simplify") << "Before simplification : " << std::endl;
+ fm->d_models[op]->debugPrint("fmc-model-simplify", op, this);
+ Trace("fmc-model-simplify") << std::endl;
+
+ Trace("fmc-model-simplify") << "Simplifying " << op << "..." << std::endl;
+ fm->d_models[op]->simplify( this, fm );
+
+ fm->d_models[op]->debugPrint("fmc-model", op, this);
+ Trace("fmc-model") << std::endl;
+ }
+ }
+ if( fullModel ){
+ //make function values
+ for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ){
+ m->d_uf_models[ it->first ] = getFunctionValue( fm, it->first, "$x" );
+ }
+ TheoryEngineModelBuilder::processBuildModel( m, fullModel );
+ //mark that the model has been set
+ fm->markModelSet();
+ //debug the model
+ debugModel( fm );
+ }
+}
+
+void FullModelChecker::initializeType( FirstOrderModelFmc * fm, TypeNode tn ){
+ if( fm->d_model_basis_rep.find( tn )==fm->d_model_basis_rep.end() ){
+ Trace("fmc") << "Initialize type " << tn << " hasType = " << fm->d_rep_set.hasType(tn) << std::endl;
+ Node mbn;
+ if (!fm->d_rep_set.hasType(tn)) {
+ mbn = fm->getSomeDomainElement(tn);
+ }else{
+ mbn = d_qe->getTermDatabase()->getModelBasisTerm(tn);
+ }
+ Node mbnr = fm->getUsedRepresentative( mbn );
+ fm->d_model_basis_rep[tn] = mbnr;
+ Trace("fmc") << "Add model basis for type " << tn << " : " << mbn << " " << mbnr << std::endl;
+ }
+}
+
+void FullModelChecker::debugPrintCond(const char * tr, Node n, bool dispStar) {
+ Trace(tr) << "(";
+ for( unsigned j=0; j<n.getNumChildren(); j++) {
+ if( j>0 ) Trace(tr) << ", ";
+ debugPrint(tr, n[j], dispStar);
+ }
+ Trace(tr) << ")";
+}
+
+void FullModelChecker::debugPrint(const char * tr, Node n, bool dispStar) {
+ FirstOrderModelFmc * fm = (FirstOrderModelFmc *)d_qe->getModel();
+ if( n.isNull() ){
+ Trace(tr) << "null";
+ }
+ else if(fm->isStar(n) && dispStar) {
+ Trace(tr) << "*";
+ }
+ else if(fm->isInterval(n)) {
+ debugPrint(tr, n[0], dispStar );
+ Trace(tr) << "...";
+ debugPrint(tr, n[1], dispStar );
+ }else{
+ TypeNode tn = n.getType();
+ if( d_rep_ids.find(tn)!=d_rep_ids.end() ){
+ if (d_rep_ids[tn].find(n)!=d_rep_ids[tn].end()) {
+ Trace(tr) << d_rep_ids[tn][n];
+ }else{
+ Trace(tr) << n;
+ }
+ }else{
+ Trace(tr) << n;
+ }
+ }
+}
+
+
+bool FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) {
+ Trace("fmc") << "Full model check " << f << ", effort = " << effort << "..." << std::endl;
+ if( optUseModel() ){
+ FirstOrderModelFmc * fmfmc = fm->asFirstOrderModelFmc();
+ if (effort==0) {
+ //register the quantifier
+ if (d_quant_cond.find(f)==d_quant_cond.end()) {
+ std::vector< TypeNode > types;
+ for(unsigned i=0; i<f[0].getNumChildren(); i++){
+ types.push_back(f[0][i].getType());
+ d_quant_var_id[f][f[0][i]] = i;
+ }
+ TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->booleanType() );
+ Node op = NodeManager::currentNM()->mkSkolem( "fmc_$$", typ, "op created for full-model checking" );
+ d_quant_cond[f] = op;
+ }
+ //make sure all types are set
+ for( unsigned i=0; i<f[0].getNumChildren(); i++ ){
+ initializeType( fmfmc, f[0][i].getType() );
+ }
+
+ //model check the quantifier
+ doCheck(fmfmc, f, d_quant_models[f], f[1]);
+ Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl;
+ d_quant_models[f].debugPrint("fmc", Node::null(), this);
+ Trace("fmc") << std::endl;
+
+ //consider all entries going to non-true
+ for (unsigned i=0; i<d_quant_models[f].d_cond.size(); i++) {
+ if( d_quant_models[f].d_value[i]!=d_true) {
+ Trace("fmc-inst") << "Instantiate based on " << d_quant_models[f].d_cond[i] << "..." << std::endl;
+ bool hasStar = false;
+ std::vector< Node > inst;
+ for (unsigned j=0; j<d_quant_models[f].d_cond[i].getNumChildren(); j++) {
+ if (fmfmc->isStar(d_quant_models[f].d_cond[i][j])) {
+ hasStar = true;
+ inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j].getType()));
+ }else if( fmfmc->isInterval(d_quant_models[f].d_cond[i][j])){
+ hasStar = true;
+ //if interval, find a sample point
+ if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][0]) ){
+ if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][1]) ){
+ inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j][1].getType()));
+ }else{
+ Node nn = NodeManager::currentNM()->mkNode( MINUS, d_quant_models[f].d_cond[i][j][1],
+ NodeManager::currentNM()->mkConst( Rational(1) ) );
+ nn = Rewriter::rewrite( nn );
+ inst.push_back( nn );
+ }
+ }else{
+ inst.push_back(d_quant_models[f].d_cond[i][j][0]);
+ }
+ }else{
+ inst.push_back(d_quant_models[f].d_cond[i][j]);
+ }
+ }
+ bool addInst = true;
+ if( hasStar ){
+ //try obvious (specified by inst)
+ Node ev = d_quant_models[f].evaluate(fmfmc, inst);
+ if (ev==d_true) {
+ addInst = false;
+ }
+ }else{
+ //for debugging
+ if (Trace.isOn("fmc-test-inst")) {
+ Node ev = d_quant_models[f].evaluate(fmfmc, inst);
+ if( ev==d_true ){
+ std::cout << "WARNING: instantiation was true! " << f << " " << d_quant_models[f].d_cond[i] << std::endl;
+ exit(0);
+ }else{
+ Trace("fmc-test-inst") << "...instantiation evaluated to false." << std::endl;
+ }
+ }
+ }
+ if( addInst ){
+ InstMatch m;
+ for( unsigned j=0; j<inst.size(); j++) {
+ m.set( d_qe, f, j, inst[j] );
+ }
+ d_triedLemmas++;
+ if( d_qe->addInstantiation( f, m ) ){
+ Trace("fmc-debug-inst") << "** Added instantiation." << std::endl;
+ d_addedLemmas++;
+ }else{
+ Trace("fmc-debug-inst") << "** Instantiation was duplicate." << std::endl;
+ //this can happen if evaluation is unknown
+ //might try it next effort level
+ d_star_insts[f].push_back(i);
+ }
+ }else{
+ Trace("fmc-debug-inst") << "** Instantiation was already true." << std::endl;
+ //might try it next effort level
+ d_star_insts[f].push_back(i);
+ }
+ }
+ }
+ }else{
+ if (!d_star_insts[f].empty()) {
+ Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl;
+ Trace("fmc-exh") << "Definition was : " << std::endl;
+ d_quant_models[f].debugPrint("fmc-exh", Node::null(), this);
+ Trace("fmc-exh") << std::endl;
+ Def temp;
+ //simplify the exceptions?
+ for( int i=(d_star_insts[f].size()-1); i>=0; i--) {
+ //get witness for d_star_insts[f][i]
+ int j = d_star_insts[f][i];
+ if( temp.addEntry(fmfmc, d_quant_models[f].d_cond[j], d_quant_models[f].d_value[j] ) ){
+ if( !exhaustiveInstantiate(fmfmc, f, d_quant_models[f].d_cond[j], j ) ){
+ //something went wrong, resort to exhaustive instantiation
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index) {
+ RepSetIterator riter( d_qe, &(fm->d_rep_set) );
+ Trace("fmc-exh") << "Exhaustive instantiate based on index " << c_index << " : " << c << " ";
+ debugPrintCond("fmc-exh", c, true);
+ Trace("fmc-exh")<< std::endl;
+ Trace("fmc-exh-debug") << "Set interval domains..." << std::endl;
+ //set the bounds on the iterator based on intervals
+ for( unsigned i=0; i<c.getNumChildren(); i++ ){
+ if( c[i].getType().isInteger() ){
+ if( fm->isInterval(c[i]) ){
+ for( unsigned b=0; b<2; b++ ){
+ if( !fm->isStar(c[i][b]) ){
+ riter.d_bounds[b][i] = c[i][b];
+ }
+ }
+ }else if( !fm->isStar(c[i]) ){
+ riter.d_bounds[0][i] = c[i];
+ riter.d_bounds[1][i] = QuantArith::offset( c[i], 1 );
+ }
+ }
+ }
+ Trace("fmc-exh-debug") << "Set quantifier..." << std::endl;
+ //initialize
+ if( riter.setQuantifier( f ) ){
+ Trace("fmc-exh-debug") << "Set element domains..." << std::endl;
+ //set the domains based on the entry
+ for (unsigned i=0; i<c.getNumChildren(); i++) {
+ if (riter.d_enum_type[i]==RepSetIterator::ENUM_DOMAIN_ELEMENTS) {
+ TypeNode tn = c[i].getType();
+ if( d_rep_ids.find(tn)!=d_rep_ids.end() ){
+ if( fm->isInterval(c[i]) || fm->isStar(c[i]) ){
+ //add the full range
+ }else{
+ if (d_rep_ids[tn].find(c[i])!=d_rep_ids[tn].end()) {
+ riter.d_domain[i].clear();
+ riter.d_domain[i].push_back(d_rep_ids[tn][c[i]]);
+ }else{
+ return false;
+ }
+ }
+ }else{
+ return false;
+ }
+ }
+ }
+ int addedLemmas = 0;
+ //now do full iteration
+ while( !riter.isFinished() ){
+ d_triedLemmas++;
+ Trace("fmc-exh-debug") << "Inst : ";
+ std::vector< Node > inst;
+ for( int i=0; i<riter.getNumTerms(); i++ ){
+ //m.set( d_quantEngine, f, riter.d_index_order[i], riter.getTerm( i ) );
+ Node r = fm->getUsedRepresentative( riter.getTerm( i ) );
+ debugPrint("fmc-exh-debug", r);
+ Trace("fmc-exh-debug") << " ";
+ inst.push_back(r);
+ }
+ int ev_index = d_quant_models[f].getGeneralizationIndex(fm, inst);
+ Trace("fmc-exh-debug") << ", index = " << ev_index << " / " << d_quant_models[f].d_value.size();
+ Node ev = ev_index==-1 ? Node::null() : d_quant_models[f].d_value[ev_index];
+ if (ev!=d_true) {
+ InstMatch m;
+ for( int i=0; i<riter.getNumTerms(); i++ ){
+ m.set( d_qe, f, i, riter.getTerm( i ) );
+ }
+ Trace("fmc-exh-debug") << ", add!";
+ //add as instantiation
+ if( d_qe->addInstantiation( f, m ) ){
+ Trace("fmc-exh-debug") << " ...success.";
+ addedLemmas++;
+ }
+ }else{
+ Trace("fmc-exh-debug") << ", already true";
+ }
+ Trace("fmc-exh-debug") << std::endl;
+ int index = riter.increment();
+ Trace("fmc-exh-debug") << "Incremented index " << index << std::endl;
+ if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_RANGE) {
+ Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl;
+ riter.increment2( index-1 );
+ }
+ }
+ d_addedLemmas += addedLemmas;
+ return true;
+ }else{
+ return false;
+ }
+}
+
+void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ) {
+ Trace("fmc-debug") << "Check " << n << " " << n.getKind() << std::endl;
+ //first check if it is a bounding literal
+ if( n.hasAttribute(BoundIntLitAttribute()) ){
+ Trace("fmc-debug") << "It is a bounding literal, polarity = " << n.getAttribute(BoundIntLitAttribute()) << std::endl;
+ d.addEntry(fm, mkCondDefault(fm, f), n.getAttribute(BoundIntLitAttribute())==1 ? d_false : d_true );
+ }else if( n.getKind() == kind::BOUND_VARIABLE ){
+ Trace("fmc-debug") << "Add default entry..." << std::endl;
+ d.addEntry(fm, mkCondDefault(fm, f), n);
+ }
+ else if( n.getKind() == kind::NOT ){
+ //just do directly
+ doCheck( fm, f, d, n[0] );
+ doNegate( d );
+ }
+ else if( n.getKind() == kind::FORALL ){
+ d.addEntry(fm, mkCondDefault(fm, f), Node::null());
+ }
+ else if( n.getType().isArray() ){
+ //make the definition
+ Node r = fm->getRepresentative(n);
+ Trace("fmc-debug") << "Representative for array is " << r << std::endl;
+ while( r.getKind() == kind::STORE ){
+ Node i = fm->getUsedRepresentative( r[1] );
+ Node e = fm->getUsedRepresentative( r[2] );
+ d.addEntry(fm, mkArrayCond(i), e );
+ r = fm->getRepresentative( r[0] );
+ }
+ Node defC = mkArrayCond(fm->getStar(n.getType().getArrayIndexType()));
+ bool success = false;
+ Node odefaultValue;
+ if( r.getKind() == kind::STORE_ALL ){
+ ArrayStoreAll storeAll = r.getConst<ArrayStoreAll>();
+ odefaultValue = Node::fromExpr(storeAll.getExpr());
+ Node defaultValue = fm->getUsedRepresentative( odefaultValue, true );
+ if( !defaultValue.isNull() ){
+ d.addEntry(fm, defC, defaultValue);
+ success = true;
+ }
+ }
+ if( !success ){
+ Trace("fmc-warn") << "WARNING : ARRAYS : Can't process base array " << r << std::endl;
+ Trace("fmc-warn") << " Default value was : " << odefaultValue << std::endl;
+ Trace("fmc-debug") << "Can't process base array " << r << std::endl;
+ //can't process this array
+ d.reset();
+ d.addEntry(fm, defC, Node::null());
+ }
+ }
+ else if( n.getNumChildren()==0 ){
+ Node r = n;
+ if( !n.isConst() ){
+ if( !fm->hasTerm(n) ){
+ r = getSomeDomainElement(fm, n.getType() );
+ }
+ r = fm->getUsedRepresentative( r );
+ }
+ Trace("fmc-debug") << "Add constant entry..." << std::endl;
+ d.addEntry(fm, mkCondDefault(fm, f), r);
+ }
+ else{
+ std::vector< int > var_ch;
+ std::vector< Def > children;
+ for( int i=0; i<(int)n.getNumChildren(); i++) {
+ Def dc;
+ doCheck(fm, f, dc, n[i]);
+ children.push_back(dc);
+ if( n[i].getKind() == kind::BOUND_VARIABLE ){
+ var_ch.push_back(i);
+ }
+ }
+
+ if( n.getKind()==APPLY_UF ){
+ Trace("fmc-debug") << "Do uninterpreted compose " << n << std::endl;
+ //uninterpreted compose
+ doUninterpretedCompose( fm, f, d, n.getOperator(), children );
+ } else if( n.getKind()==SELECT ){
+ Trace("fmc-debug") << "Do select compose " << n << std::endl;
+ std::vector< Def > children2;
+ children2.push_back( children[1] );
+ std::vector< Node > cond;
+ mkCondDefaultVec(fm, f, cond);
+ std::vector< Node > val;
+ doUninterpretedCompose(fm, f, d, children[0], children2, 0, cond, val );
+ } else {
+ if( !var_ch.empty() ){
+ if( n.getKind()==EQUAL ){
+ if( var_ch.size()==2 ){
+ Trace("fmc-debug") << "Do variable equality " << n << std::endl;
+ doVariableEquality( fm, f, d, n );
+ }else{
+ Trace("fmc-debug") << "Do variable relation " << n << std::endl;
+ doVariableRelation( fm, f, d, var_ch[0]==0 ? children[1] : children[0], var_ch[0]==0 ? n[0] : n[1] );
+ }
+ }else{
+ Trace("fmc-warn") << "Don't know how to check " << n << std::endl;
+ d.addEntry(fm, mkCondDefault(fm, f), Node::null());
+ }
+ }else{
+ Trace("fmc-debug") << "Do interpreted compose " << n << std::endl;
+ std::vector< Node > cond;
+ mkCondDefaultVec(fm, f, cond);
+ std::vector< Node > val;
+ //interpreted compose
+ doInterpretedCompose( fm, f, d, n, children, 0, cond, val );
+ }
+ }
+ Trace("fmc-debug") << "Simplify the definition..." << std::endl;
+ d.debugPrint("fmc-debug", Node::null(), this);
+ d.simplify(this, fm);
+ Trace("fmc-debug") << "Done simplifying" << std::endl;
+ }
+ Trace("fmc-debug") << "Definition for " << n << " is : " << std::endl;
+ d.debugPrint("fmc-debug", Node::null(), this);
+ Trace("fmc-debug") << std::endl;
+}
+
+void FullModelChecker::doNegate( Def & dc ) {
+ for (unsigned i=0; i<dc.d_cond.size(); i++) {
+ if (!dc.d_value[i].isNull()) {
+ dc.d_value[i] = dc.d_value[i]==d_true ? d_false : d_true;
+ }
+ }
+}
+
+void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ) {
+ std::vector<Node> cond;
+ mkCondDefaultVec(fm, f, cond);
+ if (eq[0]==eq[1]){
+ d.addEntry(fm, mkCond(cond), d_true);
+ }else{
+ TypeNode tn = eq[0].getType();
+ if( tn.isSort() ){
+ int j = getVariableId(f, eq[0]);
+ int k = getVariableId(f, eq[1]);
+ if( !fm->d_rep_set.hasType( tn ) ){
+ getSomeDomainElement( fm, tn ); //to verify the type is initialized
+ }
+ for (unsigned i=0; i<fm->d_rep_set.d_type_reps[tn].size(); i++) {
+ Node r = fm->getUsedRepresentative( fm->d_rep_set.d_type_reps[tn][i] );
+ cond[j+1] = r;
+ cond[k+1] = r;
+ d.addEntry( fm, mkCond(cond), d_true);
+ }
+ d.addEntry( fm, mkCondDefault(fm, f), d_false);
+ }else{
+ d.addEntry( fm, mkCondDefault(fm, f), Node::null());
+ }
+ }
+}
+
+void FullModelChecker::doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v) {
+ int j = getVariableId(f, v);
+ for (unsigned i=0; i<dc.d_cond.size(); i++) {
+ Node val = dc.d_value[i];
+ if( val.isNull() ){
+ d.addEntry( fm, dc.d_cond[i], val);
+ }else{
+ if( dc.d_cond[i][j]!=val ){
+ if (fm->isStar(dc.d_cond[i][j])) {
+ std::vector<Node> cond;
+ mkCondVec(dc.d_cond[i],cond);
+ cond[j+1] = val;
+ d.addEntry(fm, mkCond(cond), d_true);
+ cond[j+1] = fm->getStar(val.getType());
+ d.addEntry(fm, mkCond(cond), d_false);
+ }else{
+ d.addEntry( fm, dc.d_cond[i], d_false);
+ }
+ }else{
+ d.addEntry( fm, dc.d_cond[i], d_true);
+ }
+ }
+ }
+}
+
+void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node op, std::vector< Def > & dc ) {
+ Trace("fmc-uf-debug") << "Definition : " << std::endl;
+ fm->d_models[op]->debugPrint("fmc-uf-debug", op, this);
+ Trace("fmc-uf-debug") << std::endl;
+
+ std::vector< Node > cond;
+ mkCondDefaultVec(fm, f, cond);
+ std::vector< Node > val;
+ doUninterpretedCompose( fm, f, d, *fm->d_models[op], dc, 0, cond, val);
+}
+
+void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d,
+ Def & df, std::vector< Def > & dc, int index,
+ std::vector< Node > & cond, std::vector<Node> & val ) {
+ Trace("fmc-uf-process") << "process at " << index << std::endl;
+ for( unsigned i=1; i<cond.size(); i++) {
+ debugPrint("fmc-uf-process", cond[i], true);
+ Trace("fmc-uf-process") << " ";
+ }
+ Trace("fmc-uf-process") << std::endl;
+ if (index==(int)dc.size()) {
+ //we have an entry, now do actual compose
+ std::map< int, Node > entries;
+ doUninterpretedCompose2( fm, f, entries, 0, cond, val, df.d_et);
+ if( entries.empty() ){
+ d.addEntry(fm, mkCond(cond), Node::null());
+ }else{
+ //add them to the definition
+ for( unsigned e=0; e<df.d_cond.size(); e++ ){
+ if ( entries.find(e)!=entries.end() ){
+ Trace("fmf-uf-process-debug") << "Add entry..." << std::endl;
+ d.addEntry(fm, entries[e], df.d_value[e] );
+ Trace("fmf-uf-process-debug") << "Done add entry." << std::endl;
+ }
+ }
+ }
+ }else{
+ for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
+ if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
+ std::vector< Node > new_cond;
+ new_cond.insert(new_cond.end(), cond.begin(), cond.end());
+ if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){
+ Trace("fmc-uf-process") << "index " << i << " succeeded meet." << std::endl;
+ val.push_back(dc[index].d_value[i]);
+ doUninterpretedCompose(fm, f, d, df, dc, index+1, new_cond, val);
+ val.pop_back();
+ }else{
+ Trace("fmc-uf-process") << "index " << i << " failed meet." << std::endl;
+ }
+ }
+ }
+ }
+}
+
+void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f,
+ std::map< int, Node > & entries, int index,
+ std::vector< Node > & cond, std::vector< Node > & val,
+ EntryTrie & curr ) {
+ Trace("fmc-uf-process") << "compose " << index << std::endl;
+ for( unsigned i=1; i<cond.size(); i++) {
+ debugPrint("fmc-uf-process", cond[i], true);
+ Trace("fmc-uf-process") << " ";
+ }
+ Trace("fmc-uf-process") << std::endl;
+ if (index==(int)val.size()) {
+ Node c = mkCond(cond);
+ Trace("fmc-uf-entry") << "Entry : " << c << " -> index[" << curr.d_data << "]" << std::endl;
+ entries[curr.d_data] = c;
+ }else{
+ Node v = val[index];
+ bool bind_var = false;
+ if( !v.isNull() && v.getKind()==kind::BOUND_VARIABLE ){
+ int j = getVariableId(f, v);
+ Trace("fmc-uf-process") << v << " is variable #" << j << std::endl;
+ if (!fm->isStar(cond[j+1]) && !fm->isInterval(cond[j+1])) {
+ v = cond[j+1];
+ }else{
+ bind_var = true;
+ }
+ }
+ if (bind_var) {
+ Trace("fmc-uf-process") << "bind variable..." << std::endl;
+ int j = getVariableId(f, v);
+ if( fm->isStar(cond[j+1]) ){
+ for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) {
+ cond[j+1] = it->first;
+ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second);
+ }
+ cond[j+1] = fm->getStar(v.getType());
+ }else{
+ Node orig = cond[j+1];
+ for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) {
+ Node nw = doIntervalMeet( fm, it->first, orig );
+ if( !nw.isNull() ){
+ cond[j+1] = nw;
+ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second);
+ }
+ }
+ cond[j+1] = orig;
+ }
+ }else{
+ if( !v.isNull() ){
+ if( options::fmfFmcInterval() && v.getType().isInteger() ){
+ for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) {
+ if( fm->isInRange( v, it->first ) ){
+ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second);
+ }
+ }
+ }else{
+ if (curr.d_child.find(v)!=curr.d_child.end()) {
+ Trace("fmc-uf-process") << "follow value..." << std::endl;
+ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[v]);
+ }
+ Node st = fm->getStarElement(v.getType());
+ if (curr.d_child.find(st)!=curr.d_child.end()) {
+ Trace("fmc-uf-process") << "follow star..." << std::endl;
+ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[st]);
+ }
+ }
+ }
+ }
+ }
+}
+
+void FullModelChecker::doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n,
+ std::vector< Def > & dc, int index,
+ std::vector< Node > & cond, std::vector<Node> & val ) {
+ if ( index==(int)dc.size() ){
+ Node c = mkCond(cond);
+ Node v = evaluateInterpreted(n, val);
+ d.addEntry(fm, c, v);
+ }
+ else {
+ TypeNode vtn = n.getType();
+ for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
+ if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
+ std::vector< Node > new_cond;
+ new_cond.insert(new_cond.end(), cond.begin(), cond.end());
+ if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){
+ bool process = true;
+ if (vtn.isBoolean()) {
+ //short circuit
+ if( (n.getKind()==OR && dc[index].d_value[i]==d_true) ||
+ (n.getKind()==AND && dc[index].d_value[i]==d_false) ){
+ Node c = mkCond(new_cond);
+ d.addEntry(fm, c, dc[index].d_value[i]);
+ process = false;
+ }
+ }
+ if (process) {
+ val.push_back(dc[index].d_value[i]);
+ doInterpretedCompose(fm, f, d, n, dc, index+1, new_cond, val);
+ val.pop_back();
+ }
+ }
+ }
+ }
+ }
+}
+
+int FullModelChecker::isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) {
+ Trace("fmc-debug3") << "isCompat " << c << std::endl;
+ Assert(cond.size()==c.getNumChildren()+1);
+ for (unsigned i=1; i<cond.size(); i++) {
+ if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){
+ Node iv = doIntervalMeet( fm, cond[i], c[i-1], false );
+ if( iv.isNull() ){
+ return 0;
+ }
+ }else{
+ if( cond[i]!=c[i-1] && !fm->isStar(cond[i]) && !fm->isStar(c[i-1]) ) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+bool FullModelChecker::doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) {
+ Trace("fmc-debug3") << "doMeet " << c << std::endl;
+ Assert(cond.size()==c.getNumChildren()+1);
+ for (unsigned i=1; i<cond.size(); i++) {
+ if( cond[i]!=c[i-1] ) {
+ if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){
+ Node iv = doIntervalMeet( fm, cond[i], c[i-1] );
+ if( !iv.isNull() ){
+ cond[i] = iv;
+ }else{
+ return false;
+ }
+ }else{
+ if( fm->isStar(cond[i]) ){
+ cond[i] = c[i-1];
+ }else if( !fm->isStar(c[i-1]) ){
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+Node FullModelChecker::doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk ) {
+ if( fm->isStar( i1 ) ){
+ return i2;
+ }else if( fm->isStar( i2 ) ){
+ return i1;
+ }else{
+ if( !fm->isInterval( i1 ) || !fm->isInterval( i2 ) ){
+ std::cout << "Not interval during meet! " << i1 << " " << i2 << std::endl;
+ exit( 0 );
+ }
+ Node b[2];
+ for( unsigned j=0; j<2; j++ ){
+ Node b1 = i1[j];
+ Node b2 = i2[j];
+ if( fm->isStar( b1 ) ){
+ b[j] = b2;
+ }else if( fm->isStar( b2 ) ){
+ b[j] = b1;
+ }else if( b1.getConst<Rational>() < b2.getConst<Rational>() ){
+ b[j] = j==0 ? b2 : b1;
+ }else{
+ b[j] = j==0 ? b1 : b2;
+ }
+ }
+ if( fm->isStar( b[0] ) || fm->isStar( b[1] ) || b[0].getConst<Rational>() < b[1].getConst<Rational>() ){
+ return mk ? fm->getInterval( b[0], b[1] ) : i1;
+ }else{
+ return Node::null();
+ }
+ }
+}
+
+Node FullModelChecker::mkCond( std::vector< Node > & cond ) {
+ return NodeManager::currentNM()->mkNode(APPLY_UF, cond);
+}
+
+Node FullModelChecker::mkCondDefault( FirstOrderModelFmc * fm, Node f) {
+ std::vector< Node > cond;
+ mkCondDefaultVec(fm, f, cond);
+ return mkCond(cond);
+}
+
+void FullModelChecker::mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ) {
+ Trace("fmc-debug") << "Make default vec, intervals = " << options::fmfFmcInterval() << std::endl;
+ //get function symbol for f
+ cond.push_back(d_quant_cond[f]);
+ for (unsigned i=0; i<f[0].getNumChildren(); i++) {
+ Node ts = fm->getStarElement( f[0][i].getType() );
+ cond.push_back(ts);
+ }
+}
+
+void FullModelChecker::mkCondVec( Node n, std::vector< Node > & cond ) {
+ cond.push_back(n.getOperator());
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ cond.push_back( n[i] );
+ }
+}
+
+Node FullModelChecker::mkArrayCond( Node a ) {
+ if( d_array_term_cond.find(a)==d_array_term_cond.end() ){
+ if( d_array_cond.find(a.getType())==d_array_cond.end() ){
+ TypeNode typ = NodeManager::currentNM()->mkFunctionType( a.getType(), NodeManager::currentNM()->booleanType() );
+ Node op = NodeManager::currentNM()->mkSkolem( "fmc_$$", typ, "op created for full-model checking" );
+ d_array_cond[a.getType()] = op;
+ }
+ std::vector< Node > cond;
+ cond.push_back(d_array_cond[a.getType()]);
+ cond.push_back(a);
+ d_array_term_cond[a] = NodeManager::currentNM()->mkNode(APPLY_UF, cond );
+ }
+ return d_array_term_cond[a];
+}
+
+Node FullModelChecker::evaluateInterpreted( Node n, std::vector< Node > & vals ) {
+ if( n.getKind()==EQUAL ){
+ if (!vals[0].isNull() && !vals[1].isNull()) {
+ return vals[0]==vals[1] ? d_true : d_false;
+ }else{
+ return Node::null();
+ }
+ }else if( n.getKind()==ITE ){
+ if( vals[0]==d_true ){
+ return vals[1];
+ }else if( vals[0]==d_false ){
+ return vals[2];
+ }else{
+ return vals[1]==vals[2] ? vals[1] : Node::null();
+ }
+ }else if( n.getKind()==AND || n.getKind()==OR ){
+ bool isNull = false;
+ for (unsigned i=0; i<vals.size(); i++) {
+ if((vals[i]==d_true && n.getKind()==OR) || (vals[i]==d_false && n.getKind()==AND)) {
+ return vals[i];
+ }else if( vals[i].isNull() ){
+ isNull = true;
+ }
+ }
+ return isNull ? Node::null() : vals[0];
+ }else{
+ std::vector<Node> children;
+ if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( n.getOperator() );
+ }
+ for (unsigned i=0; i<vals.size(); i++) {
+ if( vals[i].isNull() ){
+ return Node::null();
+ }else{
+ children.push_back( vals[i] );
+ }
+ }
+ Node nc = NodeManager::currentNM()->mkNode(n.getKind(), children);
+ Trace("fmc-eval") << "Evaluate " << nc << " to ";
+ nc = Rewriter::rewrite(nc);
+ Trace("fmc-eval") << nc << std::endl;
+ return nc;
+ }
+}
+
+Node FullModelChecker::getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ) {
+ bool addRepId = !fm->d_rep_set.hasType( tn );
+ Node de = fm->getSomeDomainElement(tn);
+ if( addRepId ){
+ d_rep_ids[tn][de] = 0;
+ }
+ return de;
+}
+
+Node FullModelChecker::getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ) {
+ return fm->getFunctionValue(op, argPrefix);
+}
+
+
+bool FullModelChecker::useSimpleModels() {
+ return options::fmfFmcSimple();
+}
+
+void FullModelChecker::makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index,
+ std::map< int, std::map< int, Node > >& changed_vals ) {
+ if( index==(int)fm->d_models[op]->d_cond[0].getNumChildren() ){
+ makeIntervalModel2( fm, op, indices, 0, changed_vals );
+ }else{
+ TypeNode tn = fm->d_models[op]->d_cond[0][index].getType();
+ if( tn.isInteger() ){
+ makeIntervalModel(fm,op,indices,index+1, changed_vals );
+ }else{
+ std::map< Node, std::vector< int > > new_indices;
+ for( unsigned i=0; i<indices.size(); i++ ){
+ Node v = fm->d_models[op]->d_cond[indices[i]][index];
+ new_indices[v].push_back( indices[i] );
+ }
+
+ for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){
+ makeIntervalModel( fm, op, it->second, index+1, changed_vals );
+ }
+ }
+ }
+}
+
+void FullModelChecker::makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index,
+ std::map< int, std::map< int, Node > >& changed_vals ) {
+ Debug("fmc-interval-model-debug") << "Process " << index << " with indicies : ";
+ for( unsigned i=0; i<indices.size(); i++ ){
+ Debug("fmc-interval-model-debug") << indices[i] << " ";
+ }
+ Debug("fmc-interval-model-debug") << std::endl;
+
+ if( index<(int)fm->d_models[op]->d_cond[0].getNumChildren() ){
+ TypeNode tn = fm->d_models[op]->d_cond[0][index].getType();
+ if( tn.isInteger() ){
+ std::map< Node, std::vector< int > > new_indices;
+ for( unsigned i=0; i<indices.size(); i++ ){
+ Node v = fm->d_models[op]->d_cond[indices[i]][index];
+ new_indices[v].push_back( indices[i] );
+ if( !v.isConst() ){
+ Trace("fmc-warn") << "WARNING: for interval, model has non-constant : " << v << std::endl;
+ Trace("fmc-warn") << "From condition : " << fm->d_models[op]->d_cond[indices[i]] << std::endl;
+ }
+ }
+
+ std::vector< Node > values;
+ for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){
+ makeIntervalModel2( fm, op, it->second, index+1, changed_vals );
+ values.push_back( it->first );
+ }
+
+ if( tn.isInteger() ){
+ //sort values by size
+ ConstRationalSort crs;
+ std::vector< int > sindices;
+ for( unsigned i=0; i<values.size(); i++ ){
+ sindices.push_back( (int)i );
+ crs.d_terms.push_back( values[i] );
+ }
+ std::sort( sindices.begin(), sindices.end(), crs );
+
+ Node ub = fm->getStar( tn );
+ for( int i=(int)(sindices.size()-1); i>=0; i-- ){
+ Node lb = fm->getStar( tn );
+ if( i>0 ){
+ lb = values[sindices[i]];
+ }
+ Node interval = fm->getInterval( lb, ub );
+ for( unsigned j=0; j<new_indices[values[sindices[i]]].size(); j++ ){
+ Debug("fmc-interval-model-debug") << "Change " << new_indices[values[sindices[i]]][j] << ", " << index << " to " << interval << std::endl;
+ changed_vals[new_indices[values[sindices[i]]][j]][index] = interval;
+ }
+ ub = lb;
+ }
+ }
+ }else{
+ makeIntervalModel2( fm, op, indices, index+1, changed_vals );
+ }
+ }
+}
diff --git a/src/theory/quantifiers/full_model_check.h b/src/theory/quantifiers/full_model_check.h
new file mode 100644
index 000000000..6bb375c34
--- /dev/null
+++ b/src/theory/quantifiers/full_model_check.h
@@ -0,0 +1,160 @@
+/********************* */
+/*! \file full_model_check.h
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Full model check class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef FULL_MODEL_CHECK
+#define FULL_MODEL_CHECK
+
+#include "theory/quantifiers/model_builder.h"
+#include "theory/quantifiers/first_order_model.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+namespace fmcheck {
+
+
+class FirstOrderModelFmc;
+class FullModelChecker;
+
+class EntryTrie
+{
+private:
+ int d_complete;
+public:
+ EntryTrie() : d_complete(-1), d_data(-1){}
+ std::map<Node,EntryTrie> d_child;
+ int d_data;
+ void reset() { d_data = -1; d_child.clear(); d_complete = -1; }
+ void addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index = 0 );
+ bool hasGeneralization( FirstOrderModelFmc * m, Node c, int index = 0 );
+ int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node> & inst, int index = 0 );
+ void getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & gen, int index = 0, bool is_gen = true );
+
+ void collectIndices(Node c, int index, std::vector< int >& indices );
+ bool isComplete(FirstOrderModelFmc * m, Node c, int index);
+};
+
+
+class Def
+{
+public:
+ EntryTrie d_et;
+ //cond is APPLY_UF whose arguments are returned by FullModelChecker::getRepresentative
+ std::vector< Node > d_cond;
+ //value is returned by FullModelChecker::getRepresentative
+ std::vector< Node > d_value;
+ void basic_simplify( FirstOrderModelFmc * m );
+private:
+ enum {
+ status_unk,
+ status_redundant,
+ status_non_redundant
+ };
+ std::vector< int > d_status;
+ bool d_has_simplified;
+public:
+ Def() : d_has_simplified(false){}
+ void reset() {
+ d_et.reset();
+ d_cond.clear();
+ d_value.clear();
+ d_status.clear();
+ d_has_simplified = false;
+ }
+ bool addEntry( FirstOrderModelFmc * m, Node c, Node v);
+ Node evaluate( FirstOrderModelFmc * m, std::vector<Node>& inst );
+ int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& inst );
+ void simplify( FullModelChecker * mc, FirstOrderModelFmc * m );
+ void debugPrint(const char * tr, Node op, FullModelChecker * m);
+};
+
+
+class FullModelChecker : public QModelBuilder
+{
+protected:
+ Node d_true;
+ Node d_false;
+ std::map<TypeNode, std::map< Node, int > > d_rep_ids;
+ std::map<Node, Def > d_quant_models;
+ std::map<Node, Node > d_quant_cond;
+ std::map< TypeNode, Node > d_array_cond;
+ std::map< Node, Node > d_array_term_cond;
+ std::map<Node, std::map< Node, int > > d_quant_var_id;
+ std::map<Node, std::vector< int > > d_star_insts;
+ void initializeType( FirstOrderModelFmc * fm, TypeNode tn );
+ Node normalizeArgReps(FirstOrderModelFmc * fm, Node op, Node n);
+ bool exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index);
+protected:
+ void makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index,
+ std::map< int, std::map< int, Node > >& changed_vals );
+ void makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index,
+ std::map< int, std::map< int, Node > >& changed_vals );
+private:
+ void doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n );
+
+ void doNegate( Def & dc );
+ void doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq );
+ void doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v);
+ void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, std::vector< Def > & dc );
+
+ void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d,
+ Def & df, std::vector< Def > & dc, int index,
+ std::vector< Node > & cond, std::vector<Node> & val );
+ void doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f,
+ std::map< int, Node > & entries, int index,
+ std::vector< Node > & cond, std::vector< Node > & val,
+ EntryTrie & curr);
+
+ void doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n,
+ std::vector< Def > & dc, int index,
+ std::vector< Node > & cond, std::vector<Node> & val );
+ int isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c );
+ Node doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk = true );
+ bool doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c );
+ Node mkCond( std::vector< Node > & cond );
+ Node mkCondDefault( FirstOrderModelFmc * fm, Node f );
+ void mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond );
+ void mkCondVec( Node n, std::vector< Node > & cond );
+ Node mkArrayCond( Node a );
+ Node evaluateInterpreted( Node n, std::vector< Node > & vals );
+ Node getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn );
+public:
+ FullModelChecker( context::Context* c, QuantifiersEngine* qe );
+ ~FullModelChecker(){}
+
+ bool optBuildAtFullModel();
+
+ int getVariableId(Node f, Node n) { return d_quant_var_id[f][n]; }
+
+ void debugPrintCond(const char * tr, Node n, bool dispStar = false);
+ void debugPrint(const char * tr, Node n, bool dispStar = false);
+
+ bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort );
+
+ Node getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix );
+
+ /** process build model */
+ void processBuildModel(TheoryModel* m, bool fullModel);
+ /** get current model value */
+ Node getCurrentUfModelValue( FirstOrderModelFmc* fm, Node n, std::vector< Node > & args, bool partial );
+
+ bool useSimpleModels();
+};
+
+}
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/inst_gen.cpp b/src/theory/quantifiers/inst_gen.cpp
index e495b39c0..157861670 100644
--- a/src/theory/quantifiers/inst_gen.cpp
+++ b/src/theory/quantifiers/inst_gen.cpp
@@ -29,10 +29,10 @@ using namespace CVC4::theory::quantifiers;
InstGenProcess::InstGenProcess( Node n ) : d_node( n ){
- Assert( n.hasAttribute(InstConstantAttribute()) );
+ Assert( TermDb::hasInstConstAttr(n) );
int count = 0;
for( size_t i=0; i<n.getNumChildren(); i++ ){
- if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){
+ if( n[i].getKind()!=INST_CONSTANT && TermDb::hasInstConstAttr(n[i]) ){
d_children.push_back( InstGenProcess( n[i] ) );
d_children_index.push_back( i );
d_children_map[ i ] = count;
@@ -47,7 +47,7 @@ void InstGenProcess::addMatchValue( QuantifiersEngine* qe, Node f, Node val, Ins
if( d_inst_trie[val].addInstMatch( qe, f, m, true ) ){
d_match_values.push_back( val );
d_matches.push_back( InstMatch( &m ) );
- qe->getModelEngine()->getModelBuilder()->d_instGenMatches++;
+ ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->d_instGenMatches++;
}
}
}
@@ -92,7 +92,7 @@ void InstGenProcess::calculateMatches( QuantifiersEngine* qe, Node f, std::vecto
//for each term we consider, calculate a current match
for( size_t i=0; i<considerTerms.size(); i++ ){
Node n = considerTerms[i];
- bool isSelected = qe->getModelEngine()->getModelBuilder()->isTermSelected( n );
+ bool isSelected = ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->isTermSelected( n );
bool hadSuccess CVC4_UNUSED = false;
for( int t=(isSelected ? 0 : 1); t<2; t++ ){
if( t==0 || !n.getAttribute(NoMatchAttribute()) ){
@@ -193,7 +193,7 @@ void InstGenProcess::calculateMatches( QuantifiersEngine* qe, Node f, std::vecto
//process all values
for( size_t i=0; i<considerTerms.size(); i++ ){
Node n = considerTerms[i];
- bool isSelected = qe->getModelEngine()->getModelBuilder()->isTermSelected( n );
+ bool isSelected = ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->isTermSelected( n );
for( int t=(isSelected ? 0 : 1); t<2; t++ ){
//do not consider ground case if it is already congruent to another ground term
if( t==0 || !n.getAttribute(NoMatchAttribute()) ){
diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp
index f6a0dad11..d55f72a88 100644
--- a/src/theory/quantifiers/inst_match.cpp
+++ b/src/theory/quantifiers/inst_match.cpp
@@ -134,18 +134,27 @@ Node InstMatch::getValue( Node var ) const{
}
}
+Node InstMatch::get( QuantifiersEngine* qe, Node f, int i ) {
+ return get( qe->getTermDatabase()->getInstantiationConstant( f, i ) );
+}
+
void InstMatch::set(TNode var, TNode n){
Assert( !var.isNull() );
- if( !n.isNull() &&// For a strange use in inst_match.cpp InstMatchGeneratorSimple::addInstantiations
- //var.getType() == n.getType()
- !n.getType().isSubtypeOf( var.getType() ) ){
- Trace("inst-match-warn") << var.getAttribute(InstConstantAttribute()) << std::endl;
- Trace("inst-match-warn") << var << " " << var.getType() << " " << n << " " << n.getType() << std::endl ;
- Assert(false);
+ if (Trace.isOn("inst-match-warn")) {
+ // For a strange use in inst_match.cpp InstMatchGeneratorSimple::addInstantiations
+ if( !n.isNull() && !n.getType().isSubtypeOf( var.getType() ) ){
+ Trace("inst-match-warn") << quantifiers::TermDb::getInstConstAttr(var) << std::endl;
+ Trace("inst-match-warn") << var << " " << var.getType() << " " << n << " " << n.getType() << std::endl ;
+ }
}
+ Assert( n.isNull() || n.getType().isSubtypeOf( var.getType() ) );
d_map[var] = n;
}
+void InstMatch::set( QuantifiersEngine* qe, Node f, int i, TNode n ) {
+ set( qe->getTermDatabase()->getInstantiationConstant( f, i ), n );
+}
+
/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */
void InstMatchTrie::addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ){
if( long(index)<long(f[0].getNumChildren()) && ( !imtio || long(index)<long(imtio->d_order.size()) ) ){
diff --git a/src/theory/quantifiers/inst_match.h b/src/theory/quantifiers/inst_match.h
index 127f83c60..72447fd66 100644
--- a/src/theory/quantifiers/inst_match.h
+++ b/src/theory/quantifiers/inst_match.h
@@ -92,8 +92,11 @@ public:
void erase(Node node){ d_map.erase(node); }
/** get */
Node get( TNode var ) { return d_map[var]; }
+ Node get( QuantifiersEngine* qe, Node f, int i );
/** set */
void set(TNode var, TNode n);
+ void set( QuantifiersEngine* qe, Node f, int i, TNode n );
+ /** size */
size_t size(){ return d_map.size(); }
/* iterator */
std::map< Node, Node >::iterator begin(){ return d_map.begin(); };
diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp
index de7f2f373..bf4bb15a6 100644
--- a/src/theory/quantifiers/inst_match_generator.cpp
+++ b/src/theory/quantifiers/inst_match_generator.cpp
@@ -32,16 +32,16 @@ namespace inst {
InstMatchGenerator::InstMatchGenerator( Node pat, int matchPolicy ) : d_matchPolicy( matchPolicy ){
d_active_add = false;
- Assert( pat.hasAttribute(InstConstantAttribute()) );
+ Assert( quantifiers::TermDb::hasInstConstAttr(pat) );
d_pattern = pat;
d_match_pattern = pat;
d_next = NULL;
}
-void InstMatchGenerator::setActiveAdd(){
- d_active_add = true;
+void InstMatchGenerator::setActiveAdd(bool val){
+ d_active_add = val;
if( d_next!=NULL ){
- d_next->setActiveAdd();
+ d_next->setActiveAdd(val);
}
}
@@ -52,28 +52,43 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat
//we want to add the children of the NOT
d_match_pattern = d_pattern[0];
}
- if( d_match_pattern.getKind()==IFF || d_match_pattern.getKind()==EQUAL ){
- if( !d_match_pattern[0].hasAttribute(InstConstantAttribute()) ){
- Assert( d_match_pattern[1].hasAttribute(InstConstantAttribute()) );
- //swap sides
- Node pat = d_pattern;
- d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] );
- d_pattern = pat.getKind()==NOT ? d_pattern.notNode() : d_pattern;
- if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching
- d_match_pattern = d_match_pattern[1];
- }else{
- d_match_pattern = d_pattern[0][0];
+ if( d_match_pattern.getKind()==IFF || d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==GEQ ){
+ //make sure the matching portion of the equality is on the LHS of d_pattern
+ // and record what d_match_pattern is
+ if( !quantifiers::TermDb::hasInstConstAttr(d_match_pattern[0]) ||
+ d_match_pattern[0].getKind()==INST_CONSTANT ){
+ if( d_match_pattern[1].getKind()!=INST_CONSTANT ){
+ Assert( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[1]) );
+ Node mp = d_match_pattern[1];
+ //swap sides
+ Node pat = d_pattern;
+ if(d_match_pattern.getKind()==GEQ){
+ d_pattern = NodeManager::currentNM()->mkNode( kind::GT, d_match_pattern[1], d_match_pattern[0] );
+ d_pattern = d_pattern.negate();
+ }else{
+ d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] );
+ }
+ d_pattern = pat.getKind()==NOT ? d_pattern.negate() : d_pattern;
+ d_match_pattern = mp;
}
- }else if( !d_match_pattern[1].hasAttribute(InstConstantAttribute()) ){
- Assert( d_match_pattern[0].hasAttribute(InstConstantAttribute()) );
- if( d_pattern.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching
- d_match_pattern = d_match_pattern[0];
+ }else if( !quantifiers::TermDb::hasInstConstAttr(d_match_pattern[1]) ||
+ d_match_pattern[1].getKind()==INST_CONSTANT ){
+ if( d_match_pattern[0].getKind()!=INST_CONSTANT ){
+ Assert( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[0]) );
+ if( d_pattern.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching
+ d_match_pattern = d_match_pattern[0];
+ }else if( d_match_pattern[1].getKind()==INST_CONSTANT ){
+ d_match_pattern = d_match_pattern[0];
+ }
}
}
}
+ Trace("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl;
+
+ //now, collect children of d_match_pattern
int childMatchPolicy = MATCH_GEN_DEFAULT;
for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){
- if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[i]) ){
if( d_match_pattern[i].getKind()!=INST_CONSTANT && !Trigger::isBooleanTermTrigger( d_match_pattern[i] ) ){
InstMatchGenerator * cimg = new InstMatchGenerator( d_match_pattern[i], childMatchPolicy );
d_children.push_back( cimg );
@@ -83,10 +98,11 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat
}
}
- Debug("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl;
-
//create candidate generator
- if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){
+ if( d_match_pattern.getKind()==INST_CONSTANT ){
+ d_cg = new CandidateGeneratorQEAll( qe, d_match_pattern );
+ }
+ else if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){
Assert( d_matchPolicy==MATCH_GEN_DEFAULT );
//we will be producing candidates via literal matching heuristics
if( d_pattern.getKind()!=NOT ){
@@ -96,50 +112,40 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat
//candidates will be all disequalities
d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern );
}
- }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF || d_pattern.getKind()==NOT ){
+ }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF ||
+ d_pattern.getKind()==GEQ || d_pattern.getKind()==GT || d_pattern.getKind()==NOT ){
Assert( d_matchPolicy==MATCH_GEN_DEFAULT );
if( d_pattern.getKind()==NOT ){
- Unimplemented("Disequal generator unimplemented");
+ if (d_pattern[0][1].getKind()!=INST_CONSTANT) {
+ Unimplemented("Disequal generator unimplemented");
+ }else{
+ d_eq_class = d_pattern[0][1];
+ }
}else{
- Assert( Trigger::isAtomicTrigger( d_match_pattern ) );
- //we are matching only in a particular equivalence class
- d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() );
//store the equivalence class that we will call d_cg->reset( ... ) on
d_eq_class = d_pattern[1];
}
+ Assert( Trigger::isAtomicTrigger( d_match_pattern ) );
+ //we are matching only in a particular equivalence class
+ d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() );
}else if( Trigger::isAtomicTrigger( d_match_pattern ) ){
- //if( d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ){
- //Warning() << "Currently efficient e matching is not taken into account for quantifiers: " << d_pattern << std::endl;
- //}
//we will be scanning lists trying to find d_match_pattern.getOperator()
d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() );
}else{
d_cg = new CandidateGeneratorQueue;
- if( !Trigger::isArithmeticTrigger( d_match_pattern.getAttribute(InstConstantAttribute()), d_match_pattern, d_arith_coeffs ) ){
- Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl;
- //Warning() << "(?) Unknown matching pattern is " << d_match_pattern << std::endl;
- d_matchPolicy = MATCH_GEN_INTERNAL_ERROR;
- }else{
- Debug("matching-arith") << "Generated arithmetic pattern for " << d_match_pattern << ": " << std::endl;
- for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){
- Debug("matching-arith") << " " << it->first << " -> " << it->second << std::endl;
- }
- //we will treat this as match gen internal arithmetic
- d_matchPolicy = MATCH_GEN_INTERNAL_ARITHMETIC;
- }
+ Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl;
+ d_matchPolicy = MATCH_GEN_INTERNAL_ERROR;
}
}
}
/** get match (not modulo equality) */
bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngine* qe ){
- Debug("matching") << "Matching " << t << " against pattern " << d_match_pattern << " ("
- << m << ")" << ", " << d_children.size() << std::endl;
+ Trace("matching") << "Matching " << t << " against pattern " << d_match_pattern << " ("
+ << m << ")" << ", " << d_children.size() << ", pattern is " << d_pattern << std::endl;
Assert( !d_match_pattern.isNull() );
if( qe->d_optMatchIgnoreModelBasis && t.getAttribute(ModelBasisAttribute()) ){
return true;
- }else if( d_matchPolicy==MATCH_GEN_INTERNAL_ARITHMETIC ){
- return getMatchArithmetic( t, m, qe );
}else if( d_matchPolicy==MATCH_GEN_INTERNAL_ERROR ){
return false;
}else{
@@ -149,12 +155,12 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi
InstMatch prev( &m );
//if t is null
Assert( !t.isNull() );
- Assert( !t.hasAttribute(InstConstantAttribute()) );
+ Assert( !quantifiers::TermDb::hasInstConstAttr(t) );
Assert( t.getKind()==d_match_pattern.getKind() );
Assert( !Trigger::isAtomicTrigger( d_match_pattern ) || t.getOperator()==d_match_pattern.getOperator() );
//first, check if ground arguments are not equal, or a match is in conflict
for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){
- if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[i]) ){
if( d_match_pattern[i].getKind()==INST_CONSTANT || Trigger::isBooleanTermTrigger( d_match_pattern[i] ) ){
Node vv = d_match_pattern[i];
Node tt = t[i];
@@ -164,24 +170,54 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi
}
if( !m.setMatch( q, vv, tt ) ){
//match is in conflict
- Debug("matching-debug") << "Match in conflict " << tt << " and "
+ Trace("matching-debug") << "Match in conflict " << tt << " and "
<< vv << " because "
<< m.get(vv)
<< std::endl;
- Debug("matching-fail") << "Match fail: " << m.get(vv) << " and " << tt << std::endl;
+ Trace("matching-fail") << "Match fail: " << m.get(vv) << " and " << tt << std::endl;
success = false;
break;
}
}
}else{
if( !q->areEqual( d_match_pattern[i], t[i] ) ){
- Debug("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl;
+ Trace("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl;
//ground arguments are not equal
success = false;
break;
}
}
}
+ //for relational matching
+ if( !d_eq_class.isNull() && d_eq_class.getKind()==INST_CONSTANT ){
+ //also must fit match to equivalence class
+ bool pol = d_pattern.getKind()!=NOT;
+ Node pat = d_pattern.getKind()==NOT ? d_pattern[0] : d_pattern;
+ Node t_match;
+ if( pol ){
+ if (pat.getKind()==GT) {
+ Node r = NodeManager::currentNM()->mkConst( Rational(-1) );
+ t_match = NodeManager::currentNM()->mkNode(PLUS, t, r);
+ }else{
+ t_match = t;
+ }
+ }else{
+ if(pat.getKind()==EQUAL) {
+ Node r = NodeManager::currentNM()->mkConst( Rational(1) );
+ t_match = NodeManager::currentNM()->mkNode(PLUS, t, r);
+ }else if( pat.getKind()==IFF ){
+ t_match = NodeManager::currentNM()->mkConst( !q->areEqual( NodeManager::currentNM()->mkConst(true), t ) );
+ }else if( pat.getKind()==GEQ ){
+ Node r = NodeManager::currentNM()->mkConst( Rational(1) );
+ t_match = NodeManager::currentNM()->mkNode(PLUS, t, r);
+ }else if( pat.getKind()==GT ){
+ t_match = t;
+ }
+ }
+ if( !t_match.isNull() && !m.setMatch( q, d_eq_class, t_match ) ){
+ success = false;
+ }
+ }
if( success ){
//now, fit children into match
//we will be requesting candidates for matching terms for each child
@@ -208,95 +244,45 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi
}
}
-bool InstMatchGenerator::getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ){
- Debug("matching-arith") << "Matching " << t << " " << d_match_pattern << std::endl;
- if( !d_arith_coeffs.empty() ){
- NodeBuilder<> tb(kind::PLUS);
- Node ic = Node::null();
- for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){
- Debug("matching-arith") << it->first << " -> " << it->second << std::endl;
- if( !it->first.isNull() ){
- if( m.find( it->first )==m.end() ){
- //see if we can choose this to set
- if( ic.isNull() && ( it->second.isNull() || !it->first.getType().isInteger() ) ){
- ic = it->first;
- }
- }else{
- Debug("matching-arith") << "already set " << m.get( it->first ) << std::endl;
- Node tm = m.get( it->first );
- if( !it->second.isNull() ){
- tm = NodeManager::currentNM()->mkNode( MULT, it->second, tm );
- }
- tb << tm;
- }
- }else{
- tb << it->second;
- }
- }
- if( !ic.isNull() ){
- Node tm;
- if( tb.getNumChildren()==0 ){
- tm = t;
- }else{
- tm = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb;
- tm = NodeManager::currentNM()->mkNode( MINUS, t, tm );
- }
- if( !d_arith_coeffs[ ic ].isNull() ){
- Assert( !ic.getType().isInteger() );
- Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_arith_coeffs[ ic ].getConst<Rational>() );
- tm = NodeManager::currentNM()->mkNode( MULT, coeff, tm );
- }
- m.set( ic, Rewriter::rewrite( tm ));
- //set the rest to zeros
- for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){
- if( !it->first.isNull() ){
- if( m.find( it->first )==m.end() ){
- m.set( it->first, NodeManager::currentNM()->mkConst( Rational(0) ) );
- }
- }
- }
- Debug("matching-arith") << "Setting " << ic << " to " << tm << std::endl;
- return true;
- }else{
- return false;
- }
- }else{
- return false;
- }
-}
-
-
/** reset instantiation round */
void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){
- if( d_match_pattern.isNull() ){
- for( int i=0; i<(int)d_children.size(); i++ ){
- d_children[i]->resetInstantiationRound( qe );
- }
- }else{
+ if( !d_match_pattern.isNull() ){
+ Trace("matching-debug2") << this << " reset instantiation round." << std::endl;
+ d_needsReset = true;
if( d_cg ){
d_cg->resetInstantiationRound();
}
}
+ if( d_next ){
+ d_next->resetInstantiationRound( qe );
+ }
}
void InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){
+ Trace("matching-debug2") << this << " reset " << eqc << "." << std::endl;
if( !eqc.isNull() ){
d_eq_class = eqc;
}
//we have a specific equivalence class in mind
//we are producing matches for f(E) ~ t, where E is a non-ground vector of terms, and t is a ground term
//just look in equivalence class of the RHS
- d_cg->reset( d_eq_class );
+ d_cg->reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class );
+ d_needsReset = false;
}
bool InstMatchGenerator::getNextMatch( Node f, InstMatch& m, QuantifiersEngine* qe ){
+ if( d_needsReset ){
+ Trace("matching") << "Reset not done yet, must do the reset..." << std::endl;
+ reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class, qe );
+ }
m.d_matched = Node::null();
- //Debug("matching") << this << " " << d_pattern << " get next match 2 " << m << " in eq class " << d_eq_class << std::endl;
+ Trace("matching") << this << " " << d_match_pattern << " get next match " << m << " in eq class " << d_eq_class << std::endl;
bool success = false;
Node t;
do{
//get the next candidate term t
t = d_cg->getNextCandidate();
+ Trace("matching-debug2") << "Matching candidate : " << t << std::endl;
//if t not null, try to fit it into match m
if( !t.isNull() && t.getType()==d_match_pattern.getType() ){
success = getMatch( f, t, m, qe );
@@ -304,9 +290,9 @@ bool InstMatchGenerator::getNextMatch( Node f, InstMatch& m, QuantifiersEngine*
}while( !success && !t.isNull() );
m.d_matched = t;
if( !success ){
- //Debug("matching") << this << " failed, reset " << d_eq_class << std::endl;
+ Trace("matching") << this << " failed, reset " << d_eq_class << std::endl;
//we failed, must reset
- reset( d_eq_class, qe );
+ reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class, qe );
}
return success;
}
diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h
index 4c954fa81..5d2128922 100644
--- a/src/theory/quantifiers/inst_match_generator.h
+++ b/src/theory/quantifiers/inst_match_generator.h
@@ -44,13 +44,14 @@ public:
/** add ground term t, called when t is added to term db */
virtual int addTerm( Node f, Node t, QuantifiersEngine* qe ) = 0;
/** set active add */
- virtual void setActiveAdd() {}
+ virtual void setActiveAdd( bool val ) {}
};/* class IMGenerator */
class CandidateGenerator;
class InstMatchGenerator : public IMGenerator {
private:
+ bool d_needsReset;
/** candidate generator */
CandidateGenerator* d_cg;
/** policy to use for matching */
@@ -72,12 +73,8 @@ public:
MATCH_GEN_DEFAULT = 0,
MATCH_GEN_EFFICIENT_E_MATCH, //generate matches via Efficient E-matching for SMT solvers
//others (internally used)
- MATCH_GEN_INTERNAL_ARITHMETIC,
MATCH_GEN_INTERNAL_ERROR,
};
-private:
- /** for arithmetic */
- bool getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe );
public:
/** get the match against ground term or formula t.
d_match_pattern and t should have the same shape.
@@ -108,7 +105,7 @@ public:
int addTerm( Node f, Node t, QuantifiersEngine* qe );
bool d_active_add;
- void setActiveAdd();
+ void setActiveAdd( bool val );
static InstMatchGenerator* mkInstMatchGenerator( Node pat, QuantifiersEngine* qe );
static InstMatchGenerator* mkInstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe );
diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp
index dbdf95613..4fe4072a3 100644
--- a/src/theory/quantifiers/inst_strategy_cbqi.cpp
+++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp
@@ -41,6 +41,19 @@ bool InstStrategySimplex::calculateShouldProcess( Node f ){
return false;
}
+void getInstantiationConstants( Node n, std::vector< Node >& ics ){
+ if( n.getKind()==INST_CONSTANT ){
+ if( std::find( ics.begin(), ics.end(), n )==ics.end() ){
+ ics.push_back( n );
+ }
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ getInstantiationConstants( n[i], ics );
+ }
+ }
+}
+
+
void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort ){
Debug("quant-arith") << "Setting up simplex for instantiator... " << std::endl;
d_instRows.clear();
@@ -54,37 +67,69 @@ void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort
ArithVar x = *vi;
if( d_th->d_internal->d_partialModel.hasEitherBound( x ) ){
Node n = avnm.asNode(x);
- Node f;
- NodeBuilder<> t(kind::PLUS);
- if( n.getKind()==PLUS ){
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- addTermToRow( x, n[i], f, t );
+
+ //collect instantiation constants
+ std::vector< Node > ics;
+ getInstantiationConstants( n, ics );
+ for( unsigned i=0; i<ics.size(); i++ ){
+
+ NodeBuilder<> t(kind::PLUS);
+ if( n.getKind()==PLUS ){
+ for( int j=0; j<(int)n.getNumChildren(); j++ ){
+ addTermToRow( ics[i], x, n[j], t );
+ }
+ }else{
+ addTermToRow( ics[i], x, n, t );
}
- }else{
- addTermToRow( x, n, f, t );
- }
- if( f!=Node::null() ){
- d_instRows[f].push_back( x );
+ d_instRows[ics[i]].push_back( x );
//this theory has constraints from f
+ Node f = TermDb::getInstConstAttr(ics[i]);
Debug("quant-arith") << "Has constraints from " << f << std::endl;
//set that we should process it
d_quantActive[ f ] = true;
//set tableaux term
if( t.getNumChildren()==0 ){
- d_tableaux_term[x] = NodeManager::currentNM()->mkConst( Rational(0) );
+ d_tableaux_term[ics[i]][x] = NodeManager::currentNM()->mkConst( Rational(0) );
}else if( t.getNumChildren()==1 ){
- d_tableaux_term[x] = t.getChild( 0 );
+ d_tableaux_term[ics[i]][x] = t.getChild( 0 );
}else{
- d_tableaux_term[x] = t;
+ d_tableaux_term[ics[i]][x] = t;
}
}
}
}
//print debug
+ Debug("quant-arith-debug") << std::endl;
debugPrint( "quant-arith-debug" );
d_counter++;
}
+void InstStrategySimplex::addTermToRow( Node i, ArithVar x, Node n, NodeBuilder<>& t ){
+ if( n.getKind()==MULT ){
+ if( TermDb::hasInstConstAttr(n[1]) ){
+ if( n[1]==i ){
+ d_ceTableaux[i][x][ n[1] ] = n[0];
+ }else{
+ d_tableaux_ce_term[i][x][ n[1] ] = n[0];
+ }
+ }else{
+ d_tableaux[i][x][ n[1] ] = n[0];
+ t << n;
+ }
+ }else{
+ if( TermDb::hasInstConstAttr(n) ){
+ if( n==i ){
+ d_ceTableaux[i][x][ n ] = Node::null();
+ }else{
+ d_tableaux_ce_term[i][x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) );
+ }
+ }else{
+ d_tableaux[i][x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) );
+ t << n;
+ }
+ }
+}
+
int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){
if( e<2 ){
return STATUS_UNFINISHED;
@@ -92,48 +137,51 @@ int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){
//Notice() << f << std::endl;
//Notice() << "Num inst rows = " << d_th->d_instRows[f].size() << std::endl;
//Notice() << "Num inst constants = " << d_quantEngine->getNumInstantiationConstants( f ) << std::endl;
- Debug("quant-arith-simplex") << "InstStrategySimplex check " << f << ", rows = " << d_instRows[f].size() << std::endl;
- for( int j=0; j<(int)d_instRows[f].size(); j++ ){
- ArithVar x = d_instRows[f][j];
- if( !d_ceTableaux[x].empty() ){
- Debug("quant-arith-simplex") << "Check row " << x << std::endl;
- //instantiation row will be A*e + B*t = beta,
- // where e is a vector of terms , and t is vector of ground terms.
- // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant
- // We will construct the term ( beta - B*t)/coeff to use for e_i.
- InstMatch m;
- //By default, choose the first instantiation constant to be e_i.
- Node var = d_ceTableaux[x].begin()->first;
- if( var.getType().isInteger() ){
- std::map< Node, Node >::iterator it = d_ceTableaux[x].begin();
- //try to find coefficent that is +/- 1
- while( !var.isNull() && !d_ceTableaux[x][var].isNull() && d_ceTableaux[x][var]!=d_negOne ){
- ++it;
- if( it==d_ceTableaux[x].end() ){
- var = Node::null();
- }else{
- var = it->first;
+ for( int i=0; i<d_quantEngine->getTermDatabase()->getNumInstantiationConstants( f ); i++ ){
+ Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i );
+ Debug("quant-arith-simplex") << "InstStrategySimplex check " << ic << ", rows = " << d_instRows[ic].size() << std::endl;
+ for( int j=0; j<(int)d_instRows[ic].size(); j++ ){
+ ArithVar x = d_instRows[ic][j];
+ if( !d_ceTableaux[ic][x].empty() ){
+ Debug("quant-arith-simplex") << "Check row " << ic << " " << x << std::endl;
+ //instantiation row will be A*e + B*t = beta,
+ // where e is a vector of terms , and t is vector of ground terms.
+ // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant
+ // We will construct the term ( beta - B*t)/coeff to use for e_i.
+ InstMatch m;
+ //By default, choose the first instantiation constant to be e_i.
+ Node var = d_ceTableaux[ic][x].begin()->first;
+ if( var.getType().isInteger() ){
+ std::map< Node, Node >::iterator it = d_ceTableaux[ic][x].begin();
+ //try to find coefficent that is +/- 1
+ while( !var.isNull() && !d_ceTableaux[ic][x][var].isNull() && d_ceTableaux[ic][x][var]!=d_negOne ){
+ ++it;
+ if( it==d_ceTableaux[ic][x].end() ){
+ var = Node::null();
+ }else{
+ var = it->first;
+ }
}
+ //otherwise, try one that divides all ground term coefficients? DO_THIS
}
- //otherwise, try one that divides all ground term coefficients? DO_THIS
- }
- if( !var.isNull() ){
- Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl;
- doInstantiation( f, d_tableaux_term[x], x, m, var );
- }else{
- Debug("quant-arith-simplex") << "Could not find var." << std::endl;
+ if( !var.isNull() ){
+ Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl;
+ doInstantiation( f, ic, d_tableaux_term[ic][x], x, m, var );
+ }else{
+ Debug("quant-arith-simplex") << "Could not find var." << std::endl;
+ }
+ ////choose a new variable based on alternation strategy
+ //int index = d_counter%(int)d_th->d_ceTableaux[x].size();
+ //Node var;
+ //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){
+ // if( index==0 ){
+ // var = it->first;
+ // break;
+ // }
+ // index--;
+ //}
+ //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var );
}
- ////choose a new variable based on alternation strategy
- //int index = d_counter%(int)d_th->d_ceTableaux[x].size();
- //Node var;
- //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){
- // if( index==0 ){
- // var = it->first;
- // break;
- // }
- // index--;
- //}
- //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var );
}
}
}
@@ -141,34 +189,6 @@ int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){
}
-void InstStrategySimplex::addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ){
- if( n.getKind()==MULT ){
- if( n[1].hasAttribute(InstConstantAttribute()) ){
- f = n[1].getAttribute(InstConstantAttribute());
- if( n[1].getKind()==INST_CONSTANT ){
- d_ceTableaux[x][ n[1] ] = n[0];
- }else{
- d_tableaux_ce_term[x][ n[1] ] = n[0];
- }
- }else{
- d_tableaux[x][ n[1] ] = n[0];
- t << n;
- }
- }else{
- if( n.hasAttribute(InstConstantAttribute()) ){
- f = n.getAttribute(InstConstantAttribute());
- if( n.getKind()==INST_CONSTANT ){
- d_ceTableaux[x][ n ] = Node::null();
- }else{
- d_tableaux_ce_term[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) );
- }
- }else{
- d_tableaux[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) );
- t << n;
- }
- }
-}
-
void InstStrategySimplex::debugPrint( const char* c ){
ArithVariables& avnm = d_th->d_internal->d_partialModel;
ArithVariables::var_iterator vi, vend;
@@ -218,14 +238,17 @@ void InstStrategySimplex::debugPrint( const char* c ){
Debug( c ) << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i );
}
Debug(c) << std::endl;
- Debug(c) << " Instantiation rows: ";
- for( int i=0; i<(int)d_instRows[f].size(); i++ ){
- if( i>0 ){
- Debug(c) << ", ";
+ for( int j=0; j<d_quantEngine->getTermDatabase()->getNumInstantiationConstants( f ); j++ ){
+ Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, j );
+ Debug(c) << " Instantiation rows for " << ic << " : ";
+ for( int i=0; i<(int)d_instRows[ic].size(); i++ ){
+ if( i>0 ){
+ Debug(c) << ", ";
+ }
+ Debug(c) << d_instRows[ic][i];
}
- Debug(c) << d_instRows[f][i];
+ Debug(c) << std::endl;
}
- Debug(c) << std::endl;
}
}
@@ -234,15 +257,15 @@ void InstStrategySimplex::debugPrint( const char* c ){
// t[e] is a vector of terms containing instantiation constants from f,
// and term is a ground term (c1*t1 + ... + cn*tn).
// We construct the term ( beta - term )/coeff to use as an instantiation for var.
-bool InstStrategySimplex::doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ){
+bool InstStrategySimplex::doInstantiation( Node f, Node ic, Node term, ArithVar x, InstMatch& m, Node var ){
//first try +delta
- if( doInstantiation2( f, term, x, m, var ) ){
+ if( doInstantiation2( f, ic, term, x, m, var ) ){
++(d_quantEngine->getInstantiationEngine()->d_statistics.d_instantiations_cbqi_arith);
return true;
}else{
#ifdef ARITH_INSTANTIATOR_USE_MINUS_DELTA
//otherwise try -delta
- if( doInstantiation2( f, term, x, m, var, true ) ){
+ if( doInstantiation2( f, ic, term, x, m, var, true ) ){
++(d_quantEngine->getInstantiationEngine()->d_statistics.d_instantiations_cbqi_arith_minus);
return true;
}else{
@@ -254,16 +277,16 @@ bool InstStrategySimplex::doInstantiation( Node f, Node term, ArithVar x, InstMa
}
}
-bool InstStrategySimplex::doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){
+bool InstStrategySimplex::doInstantiation2( Node f, Node ic, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){
// make term ( beta - term )/coeff
Node beta = getTableauxValue( x, minus_delta );
Node instVal = NodeManager::currentNM()->mkNode( MINUS, beta, term );
- if( !d_ceTableaux[x][var].isNull() ){
+ if( !d_ceTableaux[ic][x][var].isNull() ){
if( var.getType().isInteger() ){
- Assert( d_ceTableaux[x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) );
- instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[x][var], instVal );
+ Assert( d_ceTableaux[ic][x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) );
+ instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[ic][x][var], instVal );
}else{
- Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[x][var].getConst<Rational>() );
+ Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[ic][x][var].getConst<Rational>() );
instVal = NodeManager::currentNM()->mkNode( MULT, coeff, instVal );
}
}
@@ -327,81 +350,9 @@ int InstStrategyDatatypesValue::process( Node f, Theory::Effort effort, int e ){
Node InstStrategyDatatypesValue::getValueFor( Node n ){
//simply get the ground value for n in the current model, if it exists,
// or return an arbitrary ground term otherwise
- if( !n.hasAttribute(InstConstantAttribute()) ){
+ if( !TermDb::hasInstConstAttr(n) ){
return n;
}else{
return n;
}
- /* FIXME
-
- Debug("quant-datatypes-debug") << "get value for " << n << std::endl;
- if( !n.hasAttribute(InstConstantAttribute()) ){
- return n;
- }else{
- Assert( n.getType().isDatatype() );
- //check if in equivalence class with ground term
- Node rep = getRepresentative( n );
- Debug("quant-datatypes-debug") << "Rep is " << rep << std::endl;
- if( !rep.hasAttribute(InstConstantAttribute()) ){
- return rep;
- }else{
- if( !n.getType().isDatatype() ){
- return n.getType().mkGroundTerm();
- }else{
- if( n.getKind()==APPLY_CONSTRUCTOR ){
- std::vector< Node > children;
- children.push_back( n.getOperator() );
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- children.push_back( getValueFor( n[i] ) );
- }
- return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
- }else{
- const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype();
- TheoryDatatypes::EqLists* labels = &((TheoryDatatypes*)d_th)->d_labels;
- //otherwise, use which constructor the inst constant is current chosen to be
- if( labels->find( n )!=labels->end() ){
- TheoryDatatypes::EqList* lbl = (*labels->find( n )).second;
- int tIndex = -1;
- if( !lbl->empty() && (*lbl)[ lbl->size()-1 ].getKind()==APPLY_TESTER ){
- Debug("quant-datatypes-debug") << n << " tester is " << (*lbl)[ lbl->size()-1 ] << std::endl;
- tIndex = Datatype::indexOf((*lbl)[ lbl->size()-1 ].getOperator().toExpr());
- }else{
- Debug("quant-datatypes-debug") << "find possible tester choice" << std::endl;
- //must find a possible choice
- vector< bool > possibleCons;
- possibleCons.resize( dt.getNumConstructors(), true );
- for( TheoryDatatypes::EqList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ) {
- Node leqn = (*j);
- possibleCons[ Datatype::indexOf( leqn[0].getOperator().toExpr() ) ] = false;
- }
- for( unsigned int j=0; j<possibleCons.size(); j++ ) {
- if( possibleCons[j] ){
- tIndex = j;
- break;
- }
- }
- }
- Assert( tIndex!=-1 );
- Node cons = Node::fromExpr( dt[ tIndex ].getConstructor() );
- Debug("quant-datatypes-debug") << n << " cons is " << cons << std::endl;
- std::vector< Node > children;
- children.push_back( cons );
- for( int i=0; i<(int)dt[ tIndex ].getNumArgs(); i++ ) {
- Node sn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[tIndex][i].getSelector() ), n );
- if( n.hasAttribute(InstConstantAttribute()) ){
- InstConstantAttribute ica;
- sn.setAttribute(ica,n.getAttribute(InstConstantAttribute()) );
- }
- Node snn = getValueFor( sn );
- children.push_back( snn );
- }
- return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
- }else{
- return n.getType().mkGroundTerm();
- }
- }
- }
- }
- }
- */
}
diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h
index a45318489..821beeae0 100644
--- a/src/theory/quantifiers/inst_strategy_cbqi.h
+++ b/src/theory/quantifiers/inst_strategy_cbqi.h
@@ -49,19 +49,19 @@ private:
/** for each quantifier, simplex rows */
std::map< Node, std::vector< arith::ArithVar > > d_instRows;
/** tableaux */
- std::map< arith::ArithVar, Node > d_tableaux_term;
- std::map< arith::ArithVar, std::map< Node, Node > > d_tableaux_ce_term;
- std::map< arith::ArithVar, std::map< Node, Node > > d_tableaux;
+ std::map< Node, std::map< arith::ArithVar, Node > > d_tableaux_term;
+ std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_tableaux_ce_term;
+ std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_tableaux;
/** ce tableaux */
- std::map< arith::ArithVar, std::map< Node, Node > > d_ceTableaux;
+ std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_ceTableaux;
/** get value */
Node getTableauxValue( Node n, bool minus_delta = false );
Node getTableauxValue( arith::ArithVar v, bool minus_delta = false );
/** do instantiation */
- bool doInstantiation( Node f, Node term, arith::ArithVar x, InstMatch& m, Node var );
- bool doInstantiation2( Node f, Node term, arith::ArithVar x, InstMatch& m, Node var, bool minus_delta = false );
+ bool doInstantiation( Node f, Node ic, Node term, arith::ArithVar x, InstMatch& m, Node var );
+ bool doInstantiation2( Node f, Node ic, Node term, arith::ArithVar x, InstMatch& m, Node var, bool minus_delta = false );
/** add term to row */
- void addTermToRow( arith::ArithVar x, Node n, Node& f, NodeBuilder<>& t );
+ void addTermToRow( Node ic, arith::ArithVar x, Node n, NodeBuilder<>& t );
/** print debug */
void debugPrint( const char* c );
private:
diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp
index 0e1266e0d..ef81d55a1 100644
--- a/src/theory/quantifiers/inst_strategy_e_matching.cpp
+++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp
@@ -144,7 +144,11 @@ int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e )
}
if( gen ){
generateTriggers( f, effort, e, status );
+ if( d_auto_gen_trigger[f].empty() && f.getNumChildren()==2 ){
+ Trace("no-trigger") << "Could not find trigger for " << f << std::endl;
+ }
}
+
//if( e==4 ){
// d_processed_trigger.clear();
// d_quantEngine->getEqualityQuery()->setLiberal( true );
diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp
index 77df69456..628f8b14a 100644
--- a/src/theory/quantifiers/instantiation_engine.cpp
+++ b/src/theory/quantifiers/instantiation_engine.cpp
@@ -92,7 +92,7 @@ bool InstantiationEngine::doInstantiationRound( Theory::Effort effort ){
NodeBuilder<> nb(kind::OR);
nb << f << ceLit;
Node lem = nb;
- Debug("cbqi-debug") << "Counterexample lemma : " << lem << std::endl;
+ Trace("cbqi") << "Counterexample lemma : " << lem << std::endl;
d_quantEngine->getOutputChannel().lemma( lem );
addedLemma = true;
}
@@ -197,7 +197,10 @@ void InstantiationEngine::check( Theory::Effort e ){
<< d_quantEngine->getModel()->getNumAssertedQuantifiers() << std::endl;
for( int i=0; i<(int)d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){
Node n = d_quantEngine->getModel()->getAssertedQuantifier( i );
- if( options::cbqi() && hasAddedCbqiLemma( n ) ){
+ //it is not active if we have found the skolemized negation is unsat
+ if( n.hasAttribute(QRewriteRuleAttribute()) ){
+ d_quant_active[n] = false;
+ }else if( options::cbqi() && hasAddedCbqiLemma( n ) ){
Node cel = d_quantEngine->getTermDatabase()->getCounterexampleLiteral( n );
bool active, value;
bool ceValue = false;
@@ -210,7 +213,9 @@ void InstantiationEngine::check( Theory::Effort e ){
d_quant_active[n] = active;
if( active ){
Debug("quantifiers") << " Active : " << n;
- quantActive = true;
+ if( !TermDb::hasInstConstAttr(n) ){
+ quantActive = true;
+ }
}else{
Debug("quantifiers") << " NOT active : " << n;
if( d_quantEngine->getValuation().isDecision( cel ) ){
@@ -226,14 +231,18 @@ void InstantiationEngine::check( Theory::Effort e ){
Debug("quantifiers") << ", ce is asserted";
}
Debug("quantifiers") << std::endl;
+ //it is not active if it corresponds to a rewrite rule: we will process in rewrite engine
}else{
d_quant_active[n] = true;
- quantActive = true;
+ if( !TermDb::hasInstConstAttr(n) ){
+ quantActive = true;
+ }
Debug("quantifiers") << " Active : " << n << ", no ce assigned." << std::endl;
}
Debug("quantifiers-relevance") << "Quantifier : " << n << std::endl;
Debug("quantifiers-relevance") << " Relevance : " << d_quantEngine->getQuantifierRelevance()->getRelevance( n ) << std::endl;
Debug("quantifiers") << " Relevance : " << d_quantEngine->getQuantifierRelevance()->getRelevance( n ) << std::endl;
+ Trace("inst-engine-debug") << "Process : " << n << " " << d_quant_active[n] << std::endl;
}
if( quantActive ){
bool addedLemmas = doInstantiationRound( e );
diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp
index 0b74cfc5e..5edf2de96 100644
--- a/src/theory/quantifiers/model_builder.cpp
+++ b/src/theory/quantifiers/model_builder.cpp
@@ -18,7 +18,6 @@
#include "theory/uf/theory_uf.h"
#include "theory/uf/theory_uf_model.h"
#include "theory/uf/theory_uf_strong_solver.h"
-#include "theory/arrays/theory_arrays_model.h"
#include "theory/quantifiers/first_order_model.h"
#include "theory/quantifiers/term_database.h"
#include "theory/quantifiers/model_builder.h"
@@ -33,6 +32,65 @@ using namespace CVC4::context;
using namespace CVC4::theory;
using namespace CVC4::theory::quantifiers;
+
+QModelBuilder::QModelBuilder( context::Context* c, QuantifiersEngine* qe ) :
+TheoryEngineModelBuilder( qe->getTheoryEngine() ), d_curr_model( c, NULL ), d_qe( qe ){
+ d_considerAxioms = true;
+}
+
+bool QModelBuilder::isQuantifierActive( Node f ) {
+ return !f.hasAttribute(QRewriteRuleAttribute());
+}
+
+
+bool QModelBuilder::optUseModel() {
+ return options::fmfModelBasedInst();
+}
+
+void QModelBuilder::debugModel( FirstOrderModel* fm ){
+ //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true
+ if( Trace.isOn("quant-model-warn") ){
+ Trace("quant-model-warn") << "Testing quantifier instantiations..." << std::endl;
+ int tests = 0;
+ int bad = 0;
+ for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
+ Node f = fm->getAssertedQuantifier( i );
+ std::vector< Node > vars;
+ for( int j=0; j<(int)f[0].getNumChildren(); j++ ){
+ vars.push_back( f[0][j] );
+ }
+ RepSetIterator riter( d_qe, &(fm->d_rep_set) );
+ if( riter.setQuantifier( f ) ){
+ while( !riter.isFinished() ){
+ tests++;
+ std::vector< Node > terms;
+ for( int i=0; i<riter.getNumTerms(); i++ ){
+ terms.push_back( riter.getTerm( i ) );
+ }
+ Node n = d_qe->getInstantiation( f, vars, terms );
+ Node val = fm->getValue( n );
+ if( val!=fm->d_true ){
+ Trace("quant-model-warn") << "******* Instantiation " << n << " for " << std::endl;
+ Trace("quant-model-warn") << " " << f << std::endl;
+ Trace("quant-model-warn") << " Evaluates to " << val << std::endl;
+ bad++;
+ }
+ riter.increment();
+ }
+ Trace("quant-model-warn") << "Tested " << tests << " instantiations";
+ if( bad>0 ){
+ Trace("quant-model-warn") << ", " << bad << " failed" << std::endl;
+ }
+ Trace("quant-model-warn") << "." << std::endl;
+ }else{
+ Trace("quant-model-warn") << "Warning: Could not test quantifier " << f << std::endl;
+ }
+ }
+ }
+}
+
+
+
bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){
if( argIndex<(int)n.getNumChildren() ){
Node r;
@@ -53,49 +111,25 @@ bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){
}
}
-ModelEngineBuilder::ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe ) :
-TheoryEngineModelBuilder( qe->getTheoryEngine() ),
-d_qe( qe ), d_curr_model( c, NULL ){
- d_considerAxioms = true;
+
+QModelBuilderIG::QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ) :
+QModelBuilder( c, qe ) {
+
}
-void ModelEngineBuilder::debugModel( FirstOrderModel* fm ){
- //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true
- if( Trace.isOn("quant-model-warn") ){
- for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
- Node f = fm->getAssertedQuantifier( i );
- std::vector< Node > vars;
- for( int j=0; j<(int)f[0].getNumChildren(); j++ ){
- vars.push_back( f[0][j] );
- }
- RepSetIterator riter( &(fm->d_rep_set) );
- riter.setQuantifier( f );
- while( !riter.isFinished() ){
- std::vector< Node > terms;
- for( int i=0; i<riter.getNumTerms(); i++ ){
- terms.push_back( riter.getTerm( i ) );
- }
- Node n = d_qe->getInstantiation( f, vars, terms );
- Node val = fm->getValue( n );
- if( val!=fm->d_true ){
- Trace("quant-model-warn") << "******* Instantiation " << n << " for " << std::endl;
- Trace("quant-model-warn") << " " << f << std::endl;
- Trace("quant-model-warn") << " Evaluates to " << val << std::endl;
- }
- riter.increment();
- }
- }
- }
+Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ) {
+ return n;
}
-void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) {
- FirstOrderModel* fm = (FirstOrderModel*)m;
+void QModelBuilderIG::processBuildModel( TheoryModel* m, bool fullModel ) {
+ FirstOrderModel* f = (FirstOrderModel*)m;
+ FirstOrderModelIG* fm = f->asFirstOrderModelIG();
if( fullModel ){
Assert( d_curr_model==fm );
//update models
for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){
it->second.update( fm );
- Trace("model-func") << "ModelEngineBuilder: Make function value from tree " << it->first << std::endl;
+ Trace("model-func") << "QModelBuilder: Make function value from tree " << it->first << std::endl;
//construct function values
fm->d_uf_models[ it->first ] = it->second.getFunctionValue( "$x" );
}
@@ -106,7 +140,6 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) {
debugModel( fm );
}else{
d_curr_model = fm;
- d_addedLemmas = 0;
d_didInstGen = false;
//reset the internal information
reset( fm );
@@ -186,12 +219,13 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) {
}
}
//construct the model if necessary
- if( d_addedLemmas==0 || optExhInstNonInstGenQuant() ){
+ if( d_addedLemmas==0 ){
//if no immediate exceptions, build the model
// this model will be an approximation that will need to be tested via exhaustive instantiation
Trace("model-engine-debug") << "Building model..." << std::endl;
//build model for UF
for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){
+ Trace("model-engine-debug-uf") << "Building model for " << it->first << "..." << std::endl;
constructModelUf( fm, it->first );
}
/*
@@ -211,7 +245,7 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) {
}
}
-int ModelEngineBuilder::initializeQuantifier( Node f, Node fp ){
+int QModelBuilderIG::initializeQuantifier( Node f, Node fp ){
if( d_quant_basis_match_added.find( f )==d_quant_basis_match_added.end() ){
//create the basis match if necessary
if( d_quant_basis_match.find( f )==d_quant_basis_match.end() ){
@@ -254,17 +288,18 @@ int ModelEngineBuilder::initializeQuantifier( Node f, Node fp ){
return 0;
}
-void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){
+void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){
+ FirstOrderModelIG* fmig = fm->asFirstOrderModelIG();
d_uf_model_constructed.clear();
//determine if any functions are constant
- for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){
+ for( std::map< Node, uf::UfModelTree >::iterator it = fmig->d_uf_model_tree.begin(); it != fmig->d_uf_model_tree.end(); ++it ){
Node op = it->first;
TermArgBasisTrie tabt;
- for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){
- Node n = fm->d_uf_terms[op][i];
+ for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){
+ Node n = fmig->d_uf_terms[op][i];
//for calculating if op is constant
if( !n.getAttribute(NoMatchAttribute()) ){
- Node v = fm->getRepresentative( n );
+ Node v = fmig->getRepresentative( n );
if( i==0 ){
d_uf_prefs[op].d_const_val = v;
}else if( v!=d_uf_prefs[op].d_const_val ){
@@ -273,10 +308,10 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){
}
}
//for calculating terms that we don't need to consider
- if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())==1 ){
+ if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())!=0 ){
if( !n.getAttribute(BasisNoMatchAttribute()) ){
//need to consider if it is not congruent modulo model basis
- if( !tabt.addTerm( fm, n ) ){
+ if( !tabt.addTerm( fmig, n ) ){
BasisNoMatchAttribute bnma;
n.setAttribute(bnma,true);
}
@@ -284,10 +319,10 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){
}
}
if( !d_uf_prefs[op].d_const_val.isNull() ){
- fm->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val );
- fm->d_uf_model_gen[op].makeModel( fm, it->second );
+ fmig->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val );
+ fmig->d_uf_model_gen[op].makeModel( fmig, it->second );
Debug("fmf-model-cons") << "Function " << op << " is the constant function ";
- fm->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val );
+ fmig->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val );
Debug("fmf-model-cons") << std::endl;
d_uf_model_constructed[op] = true;
}else{
@@ -296,7 +331,7 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){
}
}
-bool ModelEngineBuilder::hasConstantDefinition( Node n ){
+bool QModelBuilderIG::hasConstantDefinition( Node n ){
Node lit = n.getKind()==NOT ? n[0] : n;
if( lit.getKind()==APPLY_UF ){
Node op = lit.getOperator();
@@ -307,60 +342,141 @@ bool ModelEngineBuilder::hasConstantDefinition( Node n ){
return false;
}
-bool ModelEngineBuilder::optUseModel() {
- return options::fmfModelBasedInst();
-}
-
-bool ModelEngineBuilder::optInstGen(){
+bool QModelBuilderIG::optInstGen(){
return options::fmfInstGen();
}
-bool ModelEngineBuilder::optOneQuantPerRoundInstGen(){
+bool QModelBuilderIG::optOneQuantPerRoundInstGen(){
return options::fmfInstGenOneQuantPerRound();
}
-bool ModelEngineBuilder::optExhInstNonInstGenQuant(){
- return options::fmfNewInstGen();
-}
-
-void ModelEngineBuilder::setEffort( int effort ){
- d_considerAxioms = effort>=1;
-}
-
-ModelEngineBuilder::Statistics::Statistics():
- d_num_quants_init("ModelEngineBuilder::Number_Quantifiers", 0),
- d_num_partial_quants_init("ModelEngineBuilder::Number_Partial_Quantifiers", 0),
- d_init_inst_gen_lemmas("ModelEngineBuilder::Initialize_Inst_Gen_Lemmas", 0 ),
- d_inst_gen_lemmas("ModelEngineBuilder::Inst_Gen_Lemmas", 0 )
+QModelBuilderIG::Statistics::Statistics():
+ d_num_quants_init("QModelBuilderIG::Number_Quantifiers", 0),
+ d_num_partial_quants_init("QModelBuilderIG::Number_Partial_Quantifiers", 0),
+ d_init_inst_gen_lemmas("QModelBuilderIG::Initialize_Inst_Gen_Lemmas", 0 ),
+ d_inst_gen_lemmas("QModelBuilderIG::Inst_Gen_Lemmas", 0 ),
+ d_eval_formulas("QModelBuilderIG::Eval_Formulas", 0 ),
+ d_eval_uf_terms("QModelBuilderIG::Eval_Uf_Terms", 0 ),
+ d_eval_lits("QModelBuilderIG::Eval_Lits", 0 ),
+ d_eval_lits_unknown("QModelBuilderIG::Eval_Lits_Unknown", 0 )
{
StatisticsRegistry::registerStat(&d_num_quants_init);
StatisticsRegistry::registerStat(&d_num_partial_quants_init);
StatisticsRegistry::registerStat(&d_init_inst_gen_lemmas);
StatisticsRegistry::registerStat(&d_inst_gen_lemmas);
+ StatisticsRegistry::registerStat(&d_eval_formulas);
+ StatisticsRegistry::registerStat(&d_eval_uf_terms);
+ StatisticsRegistry::registerStat(&d_eval_lits);
+ StatisticsRegistry::registerStat(&d_eval_lits_unknown);
}
-ModelEngineBuilder::Statistics::~Statistics(){
+QModelBuilderIG::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_num_quants_init);
StatisticsRegistry::unregisterStat(&d_num_partial_quants_init);
StatisticsRegistry::unregisterStat(&d_init_inst_gen_lemmas);
StatisticsRegistry::unregisterStat(&d_inst_gen_lemmas);
+ StatisticsRegistry::unregisterStat(&d_eval_formulas);
+ StatisticsRegistry::unregisterStat(&d_eval_uf_terms);
+ StatisticsRegistry::unregisterStat(&d_eval_lits);
+ StatisticsRegistry::unregisterStat(&d_eval_lits_unknown);
}
-bool ModelEngineBuilder::isQuantifierActive( Node f ){
- return ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end();
+bool QModelBuilderIG::isQuantifierActive( Node f ){
+ return !f.hasAttribute(QRewriteRuleAttribute()) &&
+ ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end();
}
-bool ModelEngineBuilder::isTermActive( Node n ){
+bool QModelBuilderIG::isTermActive( Node n ){
return !n.getAttribute(NoMatchAttribute()) || //it is not congruent to another active term
- ( n.getAttribute(ModelBasisArgAttribute())==1 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments
+ ( n.getAttribute(ModelBasisArgAttribute())!=0 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments
//and is not congruent modulo model basis
//to another active term
}
+//do exhaustive instantiation
+bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) {
+ if( optUseModel() ){
+
+ RepSetIterator riter( d_qe, &(d_qe->getModel()->d_rep_set) );
+ if( riter.setQuantifier( f ) ){
+ FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel();
+ Debug("inst-fmf-ei") << "Reset evaluate..." << std::endl;
+ fmig->resetEvaluate();
+ Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl;
+ while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){
+ d_triedLemmas++;
+ for( int i=0; i<(int)riter.d_index.size(); i++ ){
+ Trace("try") << i << " : " << riter.d_index[i] << " : " << riter.getTerm( i ) << std::endl;
+ }
+ int eval = 0;
+ int depIndex;
+ //see if instantiation is already true in current model
+ Debug("fmf-model-eval") << "Evaluating ";
+ riter.debugPrintSmall("fmf-model-eval");
+ Debug("fmf-model-eval") << "Done calculating terms." << std::endl;
+ //if evaluate(...)==1, then the instantiation is already true in the model
+ // depIndex is the index of the least significant variable that this evaluation relies upon
+ depIndex = riter.getNumTerms()-1;
+ eval = fmig->evaluate( d_qe->getTermDatabase()->getInstConstantBody( f ), depIndex, &riter );
+ if( eval==1 ){
+ Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl;
+ }else{
+ Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl;
+ }
+ if( eval==1 ){
+ //instantiation is already true -> skip
+ riter.increment2( depIndex );
+ }else{
+ //instantiation was not shown to be true, construct the match
+ InstMatch m;
+ for( int i=0; i<riter.getNumTerms(); i++ ){
+ m.set( d_qe, f, riter.d_index_order[i], riter.getTerm( i ) );
+ }
+ Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl;
+ //add as instantiation
+ if( d_qe->addInstantiation( f, m ) ){
+ d_addedLemmas++;
+ //if the instantiation is show to be false, and we wish to skip multiple instantiations at once
+ if( eval==-1 ){
+ riter.increment2( depIndex );
+ }else{
+ riter.increment();
+ }
+ }else{
+ Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl;
+ riter.increment();
+ }
+ }
+ }
+ //print debugging information
+ if( fmig ){
+ d_statistics.d_eval_formulas += fmig->d_eval_formulas;
+ d_statistics.d_eval_uf_terms += fmig->d_eval_uf_terms;
+ d_statistics.d_eval_lits += fmig->d_eval_lits;
+ d_statistics.d_eval_lits_unknown += fmig->d_eval_lits_unknown;
+ }
+ Trace("inst-fmf-ei") << "Finished: " << std::endl;
+ Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl;
+ Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl;
+ if( d_addedLemmas>1000 ){
+ Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl;
+ Trace("model-engine-warn") << " Inst Tried: " << d_triedLemmas << std::endl;
+ Trace("model-engine-warn") << " Inst Added: " << d_addedLemmas << std::endl;
+ Trace("model-engine-warn") << std::endl;
+ }
+ }
+ //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round
+ d_incomplete_check = riter.d_incomplete;
+ return true;
+ }else{
+ return false;
+ }
+}
+
-void ModelEngineBuilderDefault::reset( FirstOrderModel* fm ){
+void QModelBuilderDefault::reset( FirstOrderModel* fm ){
d_quant_selection_lit.clear();
d_quant_selection_lit_candidates.clear();
d_quant_selection_lit_terms.clear();
@@ -369,7 +485,7 @@ void ModelEngineBuilderDefault::reset( FirstOrderModel* fm ){
}
-int ModelEngineBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) {
+int QModelBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) {
/*
size_t maxChildren = 0;
for( size_t i=0; i<uf_terms.size(); i++ ){
@@ -383,7 +499,8 @@ int ModelEngineBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms
return 0;
}
-void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){
+void QModelBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){
+ FirstOrderModelIG* fmig = fm->asFirstOrderModelIG();
Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl;
//the pro/con preferences for this quantifier
std::vector< Node > pro_con[2];
@@ -408,7 +525,7 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f )
// constant definitions.
bool isConst = true;
std::vector< Node > uf_terms;
- if( n.hasAttribute(InstConstantAttribute()) ){
+ if( TermDb::hasInstConstAttr(n) ){
isConst = false;
if( gn.getKind()==APPLY_UF ){
uf_terms.push_back( gn );
@@ -416,9 +533,9 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f )
}else if( gn.getKind()==EQUAL ){
isConst = true;
for( int j=0; j<2; j++ ){
- if( n[j].hasAttribute(InstConstantAttribute()) ){
+ if( TermDb::hasInstConstAttr(n[j]) ){
if( n[j].getKind()==APPLY_UF &&
- fm->d_uf_model_tree.find( gn[j].getOperator() )!=fm->d_uf_model_tree.end() ){
+ fmig->d_uf_model_tree.find( gn[j].getOperator() )!=fmig->d_uf_model_tree.end() ){
uf_terms.push_back( gn[j] );
isConst = isConst && hasConstantDefinition( gn[j] );
}else{
@@ -506,14 +623,14 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f )
for( int k=0; k<2; k++ ){
for( int j=0; j<(int)pro_con[k].size(); j++ ){
Node op = pro_con[k][j].getOperator();
- Node r = fm->getRepresentative( pro_con[k][j] );
+ Node r = fmig->getRepresentative( pro_con[k][j] );
d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 );
}
}
}
}
-int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){
+int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){
int addedLemmas = 0;
//we wish to add all known exceptions to our selection literal for f. this will help to refine our current model.
//This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms,
@@ -523,17 +640,16 @@ int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){
for( size_t i=0; i<d_quant_selection_lit_candidates[f].size(); i++ ){
bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT;
Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i];
- Assert( lit.hasAttribute(InstConstantAttribute()) );
+ Assert( TermDb::hasInstConstAttr(lit) );
std::vector< Node > tr_terms;
if( lit.getKind()==APPLY_UF ){
//only match predicates that are contrary to this one, use literal matching
Node eq = NodeManager::currentNM()->mkNode( IFF, lit, !phase ? fm->d_true : fm->d_false );
- d_qe->getTermDatabase()->setInstantiationConstantAttr( eq, f );
tr_terms.push_back( eq );
}else if( lit.getKind()==EQUAL ){
//collect trigger terms
for( int j=0; j<2; j++ ){
- if( lit[j].hasAttribute(InstConstantAttribute()) ){
+ if( TermDb::hasInstConstAttr(lit[j]) ){
if( lit[j].getKind()==APPLY_UF ){
tr_terms.push_back( lit[j] );
}else{
@@ -564,7 +680,8 @@ int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){
return addedLemmas;
}
-void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){
+void QModelBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){
+ FirstOrderModelIG* fmig = fm->asFirstOrderModelIG();
if( optReconsiderFuncConstants() ){
//reconsider constant functions that weren't necessary
if( d_uf_model_constructed[op] ){
@@ -573,8 +690,8 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op )
Node v = d_uf_prefs[op].d_const_val;
if( d_uf_prefs[op].d_value_pro_con[0][v].empty() ){
Debug("fmf-model-cons-debug") << "Consider changing the default value for " << op << std::endl;
- fm->d_uf_model_tree[op].clear();
- fm->d_uf_model_gen[op].clear();
+ fmig->d_uf_model_tree[op].clear();
+ fmig->d_uf_model_gen[op].clear();
d_uf_model_constructed[op] = false;
}
}
@@ -586,20 +703,20 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op )
Node defaultTerm = d_qe->getTermDatabase()->getModelBasisOpTerm( op );
Trace("fmf-model-cons") << "Construct model for " << op << "..." << std::endl;
//set the values in the model
- for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){
- Node n = fm->d_uf_terms[op][i];
+ for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){
+ Node n = fmig->d_uf_terms[op][i];
if( isTermActive( n ) ){
- Node v = fm->getRepresentative( n );
- Trace("fmf-model-cons") << "Set term " << n << " : " << fm->d_rep_set.getIndexFor( v ) << " " << v << std::endl;
+ Node v = fmig->getRepresentative( n );
+ Trace("fmf-model-cons") << "Set term " << n << " : " << fmig->d_rep_set.getIndexFor( v ) << " " << v << std::endl;
//if this assertion did not help the model, just consider it ground
//set n = v in the model tree
//set it as ground value
- fm->d_uf_model_gen[op].setValue( fm, n, v );
- if( fm->d_uf_model_gen[op].optUsePartialDefaults() ){
+ fmig->d_uf_model_gen[op].setValue( fm, n, v );
+ if( fmig->d_uf_model_gen[op].optUsePartialDefaults() ){
//also set as default value if necessary
- if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())==1 ){
+ if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())!=0 ){
Trace("fmf-model-cons") << " Set as default." << std::endl;
- fm->d_uf_model_gen[op].setValue( fm, n, v, false );
+ fmig->d_uf_model_gen[op].setValue( fm, n, v, false );
if( n==defaultTerm ){
//incidentally already set, we will not need to find a default value
setDefaultVal = false;
@@ -607,7 +724,7 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op )
}
}else{
if( n==defaultTerm ){
- fm->d_uf_model_gen[op].setValue( fm, n, v, false );
+ fmig->d_uf_model_gen[op].setValue( fm, n, v, false );
//incidentally already set, we will not need to find a default value
setDefaultVal = false;
}
@@ -619,12 +736,19 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op )
Trace("fmf-model-cons") << " Choose default value..." << std::endl;
//chose defaultVal based on heuristic, currently the best ratio of "pro" responses
Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm );
+ if( defaultVal.isNull() ){
+ if (!fmig->d_rep_set.hasType(defaultTerm.getType())) {
+ Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(defaultTerm.getType());
+ fmig->d_rep_set.d_type_reps[defaultTerm.getType()].push_back(mbt);
+ }
+ defaultVal = fmig->d_rep_set.d_type_reps[defaultTerm.getType()][0];
+ }
Assert( !defaultVal.isNull() );
- Trace("fmf-model-cons") << "Set default term : " << fm->d_rep_set.getIndexFor( defaultVal ) << std::endl;
- fm->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false );
+ Trace("fmf-model-cons") << "Set default term : " << fmig->d_rep_set.getIndexFor( defaultVal ) << std::endl;
+ fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false );
}
Debug("fmf-model-cons") << " Making model...";
- fm->d_uf_model_gen[op].makeModel( fm, fm->d_uf_model_tree[op] );
+ fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] );
d_uf_model_constructed[op] = true;
Debug("fmf-model-cons") << " Finished constructing model for " << op << "." << std::endl;
}
@@ -635,7 +759,7 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op )
////////////////////// Inst-Gen style Model Builder ///////////
-void ModelEngineBuilderInstGen::reset( FirstOrderModel* fm ){
+void QModelBuilderInstGen::reset( FirstOrderModel* fm ){
//for new inst gen
d_quant_selection_formula.clear();
d_term_selected.clear();
@@ -643,15 +767,15 @@ void ModelEngineBuilderInstGen::reset( FirstOrderModel* fm ){
//d_sub_quant_inst_trie.clear();//*
}
-int ModelEngineBuilderInstGen::initializeQuantifier( Node f, Node fp ){
- int addedLemmas = ModelEngineBuilder::initializeQuantifier( f, fp );
+int QModelBuilderInstGen::initializeQuantifier( Node f, Node fp ){
+ int addedLemmas = QModelBuilderIG::initializeQuantifier( f, fp );
for( size_t i=0; i<d_sub_quants[f].size(); i++ ){
addedLemmas += initializeQuantifier( d_sub_quants[f][i], fp );
}
return addedLemmas;
}
-void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ){
+void QModelBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ){
//Node fp = getParentQuantifier( f );//*
//bool quantRedundant = ( f!=fp && d_sub_quant_inst_trie[fp].addInstMatch( d_qe, fp, d_sub_quant_inst[ f ], true ) );
//if( f==fp || d_sub_quant_inst_trie[fp].addInstMatch( d_qe, fp, d_sub_quant_inst[ f ], true ) ){//*
@@ -662,7 +786,6 @@ void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f )
//if( !s.isNull() ){
// s = Rewriter::rewrite( s );
//}
- d_qe->getTermDatabase()->setInstantiationConstantAttr( s, f );
Trace("sel-form-debug") << "Selection formula " << f << std::endl;
Trace("sel-form-debug") << " " << s << std::endl;
if( !s.isNull() ){
@@ -685,7 +808,7 @@ void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f )
}
-int ModelEngineBuilderInstGen::doInstGen( FirstOrderModel* fm, Node f ){
+int QModelBuilderInstGen::doInstGen( FirstOrderModel* fm, Node f ){
int addedLemmas = 0;
if( d_quant_sat.find( f )==d_quant_sat.end() ){
Node fp = d_sub_quant_parent.find( f )==d_sub_quant_parent.end() ? f : d_sub_quant_parent[f];
@@ -802,7 +925,7 @@ Node mkAndSelectionFormula( Node n1, Node n2 ){
//if possible, returns a formula n' such that n' => ( n <=> polarity ), and n' is true in the current context,
// and NULL otherwise
-Node ModelEngineBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, int useOption ){
+Node QModelBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, int useOption ){
Trace("sel-form-debug") << "Looking for selection formula " << n << " " << polarity << std::endl;
Node ret;
if( n.getKind()==NOT ){
@@ -911,7 +1034,7 @@ Node ModelEngineBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polar
return ret;
}
-int ModelEngineBuilderInstGen::getSelectionFormulaScore( Node fn ){
+int QModelBuilderInstGen::getSelectionFormulaScore( Node fn ){
if( fn.getType().isBoolean() ){
if( fn.getKind()==APPLY_UF ){
Node op = fn.getOperator();
@@ -929,13 +1052,13 @@ int ModelEngineBuilderInstGen::getSelectionFormulaScore( Node fn ){
}
}
-void ModelEngineBuilderInstGen::setSelectedTerms( Node s ){
+void QModelBuilderInstGen::setSelectedTerms( Node s ){
//if it is apply uf and has model basis arguments, then mark term as being "selected"
if( s.getKind()==APPLY_UF ){
Assert( s.hasAttribute(ModelBasisArgAttribute()) );
if( !s.hasAttribute(ModelBasisArgAttribute()) ) std::cout << "no mba!! " << s << std::endl;
- if( s.getAttribute(ModelBasisArgAttribute())==1 ){
+ if( s.getAttribute(ModelBasisArgAttribute())!=0 ){
d_term_selected[ s ] = true;
Trace("sel-form-term") << " " << s << " is a selected term." << std::endl;
}
@@ -945,7 +1068,7 @@ void ModelEngineBuilderInstGen::setSelectedTerms( Node s ){
}
}
-bool ModelEngineBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption ){
+bool QModelBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption ){
if( n.getKind()==FORALL ){
return false;
}else if( n.getKind()!=APPLY_UF ){
@@ -964,7 +1087,7 @@ bool ModelEngineBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption
return true;
}
-void ModelEngineBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp, InstMatch& m, Node f ){
+void QModelBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp, InstMatch& m, Node f ){
if( f!=fp ){
//std::cout << "gpqm " << fp << " " << f << " " << m << std::endl;
//std::cout << " " << fp[0].getNumChildren() << " " << f[0].getNumChildren() << std::endl;
@@ -988,20 +1111,21 @@ void ModelEngineBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp
}
}
-void ModelEngineBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op ){
+void QModelBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op ){
+ FirstOrderModelIG* fmig = fm->asFirstOrderModelIG();
bool setDefaultVal = true;
Node defaultTerm = d_qe->getTermDatabase()->getModelBasisOpTerm( op );
//set the values in the model
- for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){
- Node n = fm->d_uf_terms[op][i];
+ for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){
+ Node n = fmig->d_uf_terms[op][i];
if( isTermActive( n ) ){
- Node v = fm->getRepresentative( n );
- fm->d_uf_model_gen[op].setValue( fm, n, v );
+ Node v = fmig->getRepresentative( n );
+ fmig->d_uf_model_gen[op].setValue( fm, n, v );
}
//also possible set as default
if( d_term_selected.find( n )!=d_term_selected.end() || n==defaultTerm ){
- Node v = fm->getRepresentative( n );
- fm->d_uf_model_gen[op].setValue( fm, n, v, false );
+ Node v = fmig->getRepresentative( n );
+ fmig->d_uf_model_gen[op].setValue( fm, n, v, false );
if( n==defaultTerm ){
setDefaultVal = false;
}
@@ -1010,12 +1134,12 @@ void ModelEngineBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op )
//set the overall default value if not set already (is this necessary??)
if( setDefaultVal ){
Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm );
- fm->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false );
+ fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false );
}
- fm->d_uf_model_gen[op].makeModel( fm, fm->d_uf_model_tree[op] );
+ fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] );
d_uf_model_constructed[op] = true;
}
-bool ModelEngineBuilderInstGen::existsInstantiation( Node f, InstMatch& m, bool modEq, bool modInst ){
+bool QModelBuilderInstGen::existsInstantiation( Node f, InstMatch& m, bool modEq, bool modInst ){
return d_child_sub_quant_inst_trie[f].existsInstMatch( d_qe, f, m, modEq, true );
-} \ No newline at end of file
+}
diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h
index 31448acee..b96c58767 100644
--- a/src/theory/quantifiers/model_builder.h
+++ b/src/theory/quantifiers/model_builder.h
@@ -25,6 +25,42 @@ namespace CVC4 {
namespace theory {
namespace quantifiers {
+
+class QModelBuilder : public TheoryEngineModelBuilder
+{
+protected:
+ //the model we are working with
+ context::CDO< FirstOrderModel* > d_curr_model;
+ //quantifiers engine
+ QuantifiersEngine* d_qe;
+public:
+ QModelBuilder( context::Context* c, QuantifiersEngine* qe );
+ virtual ~QModelBuilder(){}
+ // is quantifier active?
+ virtual bool isQuantifierActive( Node f );
+ //do exhaustive instantiation
+ virtual bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { return false; }
+ //whether to construct model
+ virtual bool optUseModel();
+ //whether to construct model at fullModel = true
+ virtual bool optBuildAtFullModel() { return false; }
+ //consider axioms
+ bool d_considerAxioms;
+ /** number of lemmas generated while building model */
+ //is the exhaustive instantiation incomplete?
+ bool d_incomplete_check;
+ int d_addedLemmas;
+ int d_triedLemmas;
+ /** exist instantiation ? */
+ virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; }
+ //debug model
+ void debugModel( FirstOrderModel* fm );
+};
+
+
+
+
+
/** Attribute true for nodes that should not be used when considered for inst-gen basis */
struct BasisNoMatchAttributeId {};
/** use the special for boolean flag */
@@ -47,17 +83,13 @@ public:
/** model builder class
* This class is capable of building candidate models based on the current quantified formulas
* that are asserted. Use:
- * (1) call ModelEngineBuilder::buildModel( m, false );, where m is a FirstOrderModel
+ * (1) call QModelBuilder::buildModel( m, false );, where m is a FirstOrderModel
* (2) if candidate model is determined to be a real model,
- then call ModelEngineBuilder::buildModel( m, true );
+ then call QModelBuilder::buildModel( m, true );
*/
-class ModelEngineBuilder : public TheoryEngineModelBuilder
+class QModelBuilderIG : public QModelBuilder
{
protected:
- //quantifiers engine
- QuantifiersEngine* d_qe;
- //the model we are working with
- context::CDO< FirstOrderModel* > d_curr_model;
//map from operators to model preference data
std::map< Node, uf::UfModelPreferenceData > d_uf_prefs;
//built model uf
@@ -66,6 +98,8 @@ protected:
bool d_didInstGen;
/** process build model */
virtual void processBuildModel( TheoryModel* m, bool fullModel );
+ /** get current model value */
+ Node getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial );
protected:
//reset
virtual void reset( FirstOrderModel* fm ) = 0;
@@ -90,25 +124,13 @@ protected: //helper functions
/** term has constant definition */
bool hasConstantDefinition( Node n );
public:
- ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe );
- virtual ~ModelEngineBuilder(){}
- /** number of lemmas generated while building model */
- int d_addedLemmas;
- //consider axioms
- bool d_considerAxioms;
- // set effort
- void setEffort( int effort );
- //debug model
- void debugModel( FirstOrderModel* fm );
+ QModelBuilderIG( context::Context* c, QuantifiersEngine* qe );
+ virtual ~QModelBuilderIG(){}
public:
- //whether to construct model
- virtual bool optUseModel();
//whether to add inst-gen lemmas
virtual bool optInstGen();
//whether to only consider only quantifier per round of inst-gen
virtual bool optOneQuantPerRoundInstGen();
- //whether we should exhaustively instantiate quantifiers where inst-gen is not working
- virtual bool optExhInstNonInstGenQuant();
/** statistics class */
class Statistics {
public:
@@ -116,22 +138,26 @@ public:
IntStat d_num_partial_quants_init;
IntStat d_init_inst_gen_lemmas;
IntStat d_inst_gen_lemmas;
+ IntStat d_eval_formulas;
+ IntStat d_eval_uf_terms;
+ IntStat d_eval_lits;
+ IntStat d_eval_lits_unknown;
Statistics();
~Statistics();
};
Statistics d_statistics;
- // is quantifier active?
- bool isQuantifierActive( Node f );
// is term active
bool isTermActive( Node n );
// is term selected
virtual bool isTermSelected( Node n ) { return false; }
- /** exist instantiation ? */
- virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; }
/** quantifier has inst-gen definition */
virtual bool hasInstGen( Node f ) = 0;
/** did inst gen this round? */
bool didInstGen() { return d_didInstGen; }
+ // is quantifier active?
+ bool isQuantifierActive( Node f );
+ //do exhaustive instantiation
+ bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort );
//temporary stats
int d_numQuantSat;
@@ -140,10 +166,10 @@ public:
int d_numQuantNoSelForm;
//temporary stat
int d_instGenMatches;
-};/* class ModelEngineBuilder */
+};/* class QModelBuilder */
-class ModelEngineBuilderDefault : public ModelEngineBuilder
+class QModelBuilderDefault : public QModelBuilderIG
{
private: ///information for (old) InstGen
//map from quantifiers to their selection literals
@@ -167,15 +193,15 @@ protected:
//theory-specific build models
void constructModelUf( FirstOrderModel* fm, Node op );
public:
- ModelEngineBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : ModelEngineBuilder( c, qe ){}
- ~ModelEngineBuilderDefault(){}
+ QModelBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){}
+ ~QModelBuilderDefault(){}
//options
bool optReconsiderFuncConstants() { return true; }
//has inst gen
bool hasInstGen( Node f ) { return !d_quant_selection_lit[f].isNull(); }
};
-class ModelEngineBuilderInstGen : public ModelEngineBuilder
+class QModelBuilderInstGen : public QModelBuilderIG
{
private: ///information for (new) InstGen
//map from quantifiers to their selection formulas
@@ -217,8 +243,8 @@ private:
//get parent quantifier
Node getParentQuantifier( Node f ) { return d_sub_quant_parent.find( f )==d_sub_quant_parent.end() ? f : d_sub_quant_parent[f]; }
public:
- ModelEngineBuilderInstGen( context::Context* c, QuantifiersEngine* qe ) : ModelEngineBuilder( c, qe ){}
- ~ModelEngineBuilderInstGen(){}
+ QModelBuilderInstGen( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){}
+ ~QModelBuilderInstGen(){}
// is term selected
bool isTermSelected( Node n ) { return d_term_selected.find( n )!=d_term_selected.end(); }
/** exist instantiation ? */
diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp
index a69b278c0..cb8cb8154 100644
--- a/src/theory/quantifiers/model_engine.cpp
+++ b/src/theory/quantifiers/model_engine.cpp
@@ -18,13 +18,10 @@
#include "theory/uf/theory_uf.h"
#include "theory/uf/theory_uf_strong_solver.h"
#include "theory/quantifiers/options.h"
-#include "theory/arrays/theory_arrays_model.h"
#include "theory/quantifiers/first_order_model.h"
#include "theory/quantifiers/term_database.h"
#include "theory/quantifiers/quantifiers_attributes.h"
-#define EVAL_FAIL_SKIP_MULTIPLE
-
using namespace std;
using namespace CVC4;
using namespace CVC4::kind;
@@ -35,15 +32,21 @@ using namespace CVC4::theory::inst;
//Model Engine constructor
ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) :
-QuantifiersModule( qe ),
-d_rel_domain( qe, qe->getModel() ){
+QuantifiersModule( qe ){
- if( options::fmfNewInstGen() ){
- d_builder = new ModelEngineBuilderInstGen( c, qe );
+ if( options::fmfFullModelCheck() ){
+ d_builder = new fmcheck::FullModelChecker( c, qe );
+ }else if( options::fmfNewInstGen() ){
+ d_builder = new QModelBuilderInstGen( c, qe );
}else{
- d_builder = new ModelEngineBuilderDefault( c, qe );
+ d_builder = new QModelBuilderDefault( c, qe );
}
+ if( options::fmfRelevantDomain() ){
+ d_rel_dom = new RelevantDomain( qe, qe->getModel() );
+ }else{
+ d_rel_dom = NULL;
+ }
}
void ModelEngine::check( Theory::Effort e ){
@@ -57,6 +60,7 @@ void ModelEngine::check( Theory::Effort e ){
clSet = double(clock())/double(CLOCKS_PER_SEC);
}
++(d_statistics.d_inst_rounds);
+ bool buildAtFullModel = d_builder->optBuildAtFullModel();
//two effort levels: first try exhaustive instantiation without axioms, then with.
int startEffort = ( !fm->isAxiomAsserted() || options::axiomInstMode()==AXIOM_INST_MODE_DEFAULT ) ? 1 : 0;
for( int effort=startEffort; effort<2; effort++ ){
@@ -66,8 +70,9 @@ void ModelEngine::check( Theory::Effort e ){
Trace("model-engine") << "---Model Engine Round---" << std::endl;
//initialize the model
Trace("model-engine-debug") << "Build model..." << std::endl;
- d_builder->setEffort( effort );
- d_builder->buildModel( fm, false );
+ d_builder->d_considerAxioms = effort>=1;
+ d_builder->d_addedLemmas = 0;
+ d_builder->buildModel( fm, buildAtFullModel );
addedLemmas += (int)d_builder->d_addedLemmas;
//if builder has lemmas, add and return
if( addedLemmas==0 ){
@@ -81,11 +86,7 @@ void ModelEngine::check( Theory::Effort e ){
Debug("fmf-model-complete") << std::endl;
debugPrint("fmf-model-complete");
//successfully built an acceptable model, now check it
- addedLemmas += checkModel( check_model_full );
- }else if( d_builder->didInstGen() && d_builder->optExhInstNonInstGenQuant() ){
- Trace("model-engine-debug") << "Check model for non-inst gen quantifiers..." << std::endl;
- //check quantifiers that inst-gen didn't apply to
- addedLemmas += checkModel( check_model_no_inst_gen );
+ addedLemmas += checkModel();
}
}
if( addedLemmas==0 ){
@@ -108,7 +109,7 @@ void ModelEngine::check( Theory::Effort e ){
//CVC4 will answer SAT or unknown
Trace("fmf-consistent") << std::endl;
debugPrint("fmf-consistent");
- if( options::produceModels() ){
+ if( options::produceModels() && !buildAtFullModel ){
// finish building the model
d_builder->buildModel( fm, true );
}
@@ -131,28 +132,12 @@ void ModelEngine::assertNode( Node f ){
}
-bool ModelEngine::optOneInstPerQuantRound(){
- return options::fmfOneInstPerRound();
-}
-
-bool ModelEngine::optUseRelevantDomain(){
- return options::fmfRelevantDomain();
-}
-
bool ModelEngine::optOneQuantPerRound(){
return options::fmfOneQuantPerRound();
}
-bool ModelEngine::optExhInstEvalSkipMultiple(){
-#ifdef EVAL_FAIL_SKIP_MULTIPLE
- return true;
-#else
- return false;
-#endif
-}
-int ModelEngine::checkModel( int checkOption ){
- int addedLemmas = 0;
+int ModelEngine::checkModel(){
FirstOrderModel* fm = d_quantEngine->getModel();
//for debugging
if( Trace.isOn("model-engine") || Trace.isOn("model-engine-debug") ){
@@ -161,27 +146,28 @@ int ModelEngine::checkModel( int checkOption ){
if( it->first.isSort() ){
Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl;
Trace("model-engine-debug") << " ";
+ Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first);
for( size_t i=0; i<it->second.size(); i++ ){
//Trace("model-engine-debug") << it->second[i] << " ";
Node r = ((EqualityQueryQuantifiersEngine*)d_quantEngine->getEqualityQuery())->getRepresentative( it->second[i] );
Trace("model-engine-debug") << r << " ";
}
Trace("model-engine-debug") << std::endl;
+ Trace("model-engine-debug") << " Model basis term : " << mbt << std::endl;
}
}
}
- //compute the relevant domain if necessary
- if( optUseRelevantDomain() ){
- d_rel_domain.compute();
+ //relevant domain?
+ if( d_rel_dom ){
+ d_rel_dom->compute();
}
+
d_triedLemmas = 0;
- d_testLemmas = 0;
- d_relevantLemmas = 0;
+ d_addedLemmas = 0;
d_totalLemmas = 0;
- Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl;
+ //for statistics
for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
Node f = fm->getAssertedQuantifier( i );
- //keep track of total instantiations for statistics
int totalInst = 1;
for( size_t i=0; i<f[0].getNumChildren(); i++ ){
TypeNode tn = f[0][i].getType();
@@ -190,133 +176,85 @@ int ModelEngine::checkModel( int checkOption ){
}
}
d_totalLemmas += totalInst;
- //determine if we should check this quantifiers
- bool checkQuant = false;
- if( checkOption==check_model_full ){
- checkQuant = d_builder->isQuantifierActive( f );
- }else if( checkOption==check_model_no_inst_gen ){
- checkQuant = !d_builder->hasInstGen( f );
- }
- //if we need to consider this quantifier on this iteration
- if( checkQuant ){
- addedLemmas += exhaustiveInstantiate( f, optUseRelevantDomain() );
- if( Trace.isOn("model-engine-warn") ){
- if( addedLemmas>10000 ){
- Debug("fmf-exit") << std::endl;
- debugPrint("fmf-exit");
- exit( 0 );
+ }
+
+ Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl;
+ int e_max = options::fmfFullModelCheck() && options::fmfModelBasedInst() ? 2 : 1;
+ for( int e=0; e<e_max; e++) {
+ if (d_addedLemmas==0) {
+ for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
+ Node f = fm->getAssertedQuantifier( i );
+ //determine if we should check this quantifier
+ if( d_builder->isQuantifierActive( f ) ){
+ exhaustiveInstantiate( f, e );
+ if( Trace.isOn("model-engine-warn") ){
+ if( d_addedLemmas>10000 ){
+ Debug("fmf-exit") << std::endl;
+ debugPrint("fmf-exit");
+ exit( 0 );
+ }
+ }
+ if( optOneQuantPerRound() && d_addedLemmas>0 ){
+ break;
+ }
}
}
- if( optOneQuantPerRound() && addedLemmas>0 ){
- break;
- }
}
}
//print debug information
if( Trace.isOn("model-engine") ){
Trace("model-engine") << "Instantiate axioms : " << ( d_builder->d_considerAxioms ? "yes" : "no" ) << std::endl;
- Trace("model-engine") << "Added Lemmas = " << addedLemmas << " / " << d_triedLemmas << " / ";
- Trace("model-engine") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl;
+ Trace("model-engine") << "Added Lemmas = " << d_addedLemmas << " / " << d_triedLemmas << " / ";
+ Trace("model-engine") << d_totalLemmas << std::endl;
}
- d_statistics.d_exh_inst_lemmas += addedLemmas;
- return addedLemmas;
+ d_statistics.d_exh_inst_lemmas += d_addedLemmas;
+ return d_addedLemmas;
}
-int ModelEngine::exhaustiveInstantiate( Node f, bool useRelInstDomain ){
- int addedLemmas = 0;
- Trace("inst-fmf-ei") << "Exhaustive instantiate " << f << "..." << std::endl;
- Debug("inst-fmf-ei") << " Instantiation Constants: ";
- for( size_t i=0; i<f[0].getNumChildren(); i++ ){
- Debug("inst-fmf-ei") << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ) << " ";
- }
- Debug("inst-fmf-ei") << std::endl;
-
- //create a rep set iterator and iterate over the (relevant) domain of the quantifier
- RepSetIterator riter( &(d_quantEngine->getModel()->d_rep_set) );
- if( riter.setQuantifier( f ) ){
- //set the domain for the iterator (the sufficient set of instantiations to try)
- if( useRelInstDomain ){
- riter.setDomain( d_rel_domain.d_quant_inst_domain[f] );
+void ModelEngine::exhaustiveInstantiate( Node f, int effort ){
+ //first check if the builder can do the exhaustive instantiation
+ d_builder->d_triedLemmas = 0;
+ d_builder->d_addedLemmas = 0;
+ d_builder->d_incomplete_check = false;
+ if( d_builder->doExhaustiveInstantiation( d_quantEngine->getModel(), f, effort ) ){
+ d_triedLemmas += d_builder->d_triedLemmas;
+ d_addedLemmas += d_builder->d_addedLemmas;
+ d_incomplete_check = d_incomplete_check || d_builder->d_incomplete_check;
+ }else{
+ Trace("inst-fmf-ei") << "Exhaustive instantiate " << f << ", effort = " << effort << "..." << std::endl;
+ Debug("inst-fmf-ei") << " Instantiation Constants: ";
+ for( size_t i=0; i<f[0].getNumChildren(); i++ ){
+ Debug("inst-fmf-ei") << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ) << " ";
}
- d_quantEngine->getModel()->resetEvaluate();
- int tests = 0;
- int triedLemmas = 0;
- while( !riter.isFinished() && ( addedLemmas==0 || !optOneInstPerQuantRound() ) ){
- d_testLemmas++;
- int eval = 0;
- int depIndex;
- if( d_builder->optUseModel() ){
- //see if instantiation is already true in current model
- Debug("fmf-model-eval") << "Evaluating ";
- riter.debugPrintSmall("fmf-model-eval");
- Debug("fmf-model-eval") << "Done calculating terms." << std::endl;
- tests++;
- //if evaluate(...)==1, then the instantiation is already true in the model
- // depIndex is the index of the least significant variable that this evaluation relies upon
- depIndex = riter.getNumTerms()-1;
- eval = d_quantEngine->getModel()->evaluate( d_quantEngine->getTermDatabase()->getInstConstantBody( f ), depIndex, &riter );
- if( eval==1 ){
- Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl;
- }else{
- Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl;
- }
- }
- if( eval==1 ){
- //instantiation is already true -> skip
- riter.increment2( depIndex );
- }else{
+ Debug("inst-fmf-ei") << std::endl;
+ //create a rep set iterator and iterate over the (relevant) domain of the quantifier
+ RepSetIterator riter( d_quantEngine, &(d_quantEngine->getModel()->d_rep_set) );
+ if( riter.setQuantifier( f ) ){
+ Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl;
+ int triedLemmas = 0;
+ int addedLemmas = 0;
+ while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){
//instantiation was not shown to be true, construct the match
InstMatch m;
for( int i=0; i<riter.getNumTerms(); i++ ){
- m.set( d_quantEngine->getTermDatabase()->getInstantiationConstant( f, riter.d_index_order[i] ), riter.getTerm( i ) );
+ m.set( d_quantEngine, f, riter.d_index_order[i], riter.getTerm( i ) );
}
Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl;
triedLemmas++;
- d_triedLemmas++;
//add as instantiation
if( d_quantEngine->addInstantiation( f, m ) ){
addedLemmas++;
- //if the instantiation is show to be false, and we wish to skip multiple instantiations at once
- if( eval==-1 && optExhInstEvalSkipMultiple() ){
- riter.increment2( depIndex );
- }else{
- riter.increment();
- }
}else{
Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl;
- riter.increment();
}
+ riter.increment();
}
+ d_addedLemmas += addedLemmas;
+ d_triedLemmas += triedLemmas;
}
- //print debugging information
- d_statistics.d_eval_formulas += d_quantEngine->getModel()->d_eval_formulas;
- d_statistics.d_eval_uf_terms += d_quantEngine->getModel()->d_eval_uf_terms;
- d_statistics.d_eval_lits += d_quantEngine->getModel()->d_eval_lits;
- d_statistics.d_eval_lits_unknown += d_quantEngine->getModel()->d_eval_lits_unknown;
- int relevantInst = 1;
- for( size_t i=0; i<f[0].getNumChildren(); i++ ){
- relevantInst = relevantInst * (int)riter.d_domain[i].size();
- }
- d_relevantLemmas += relevantInst;
- Trace("inst-fmf-ei") << "Finished: " << std::endl;
- //Debug("inst-fmf-ei") << " Inst Total: " << totalInst << std::endl;
- Trace("inst-fmf-ei") << " Inst Relevant: " << relevantInst << std::endl;
- Trace("inst-fmf-ei") << " Inst Tried: " << triedLemmas << std::endl;
- Trace("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl;
- Trace("inst-fmf-ei") << " # Tests: " << tests << std::endl;
- if( addedLemmas>1000 ){
- Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl;
- //Trace("model-engine-warn") << " Inst Total: " << totalInst << std::endl;
- Trace("model-engine-warn") << " Inst Relevant: " << relevantInst << std::endl;
- Trace("model-engine-warn") << " Inst Tried: " << triedLemmas << std::endl;
- Trace("model-engine-warn") << " Inst Added: " << addedLemmas << std::endl;
- Trace("model-engine-warn") << " # Tests: " << tests << std::endl;
- Trace("model-engine-warn") << std::endl;
- }
+ //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round
+ d_incomplete_check = d_incomplete_check || riter.d_incomplete;
}
- //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round
- d_incomplete_check = d_incomplete_check || riter.d_incomplete;
- return addedLemmas;
}
void ModelEngine::debugPrint( const char* c ){
@@ -336,26 +274,14 @@ void ModelEngine::debugPrint( const char* c ){
ModelEngine::Statistics::Statistics():
d_inst_rounds("ModelEngine::Inst_Rounds", 0),
- d_eval_formulas("ModelEngine::Eval_Formulas", 0 ),
- d_eval_uf_terms("ModelEngine::Eval_Uf_Terms", 0 ),
- d_eval_lits("ModelEngine::Eval_Lits", 0 ),
- d_eval_lits_unknown("ModelEngine::Eval_Lits_Unknown", 0 ),
d_exh_inst_lemmas("ModelEngine::Exhaustive_Instantiation_Lemmas", 0 )
{
StatisticsRegistry::registerStat(&d_inst_rounds);
- StatisticsRegistry::registerStat(&d_eval_formulas);
- StatisticsRegistry::registerStat(&d_eval_uf_terms);
- StatisticsRegistry::registerStat(&d_eval_lits);
- StatisticsRegistry::registerStat(&d_eval_lits_unknown);
StatisticsRegistry::registerStat(&d_exh_inst_lemmas);
}
ModelEngine::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_inst_rounds);
- StatisticsRegistry::unregisterStat(&d_eval_formulas);
- StatisticsRegistry::unregisterStat(&d_eval_uf_terms);
- StatisticsRegistry::unregisterStat(&d_eval_lits);
- StatisticsRegistry::unregisterStat(&d_eval_lits_unknown);
StatisticsRegistry::unregisterStat(&d_exh_inst_lemmas);
}
diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h
index 386864164..1c2c38c51 100644
--- a/src/theory/quantifiers/model_engine.h
+++ b/src/theory/quantifiers/model_engine.h
@@ -20,6 +20,7 @@
#include "theory/quantifiers_engine.h"
#include "theory/quantifiers/model_builder.h"
#include "theory/model.h"
+#include "theory/quantifiers/full_model_check.h"
#include "theory/quantifiers/relevant_domain.h"
namespace CVC4 {
@@ -31,38 +32,31 @@ class ModelEngine : public QuantifiersModule
friend class RepSetIterator;
private:
/** builder class */
- ModelEngineBuilder* d_builder;
+ QModelBuilder* d_builder;
private: //analysis of current model:
- //relevant domain
- RelevantDomain d_rel_domain;
- //is the exhaustive instantiation incomplete?
- bool d_incomplete_check;
+ RelevantDomain* d_rel_dom;
private:
//options
- bool optOneInstPerQuantRound();
- bool optUseRelevantDomain();
bool optOneQuantPerRound();
- bool optExhInstEvalSkipMultiple();
private:
- enum{
- check_model_full,
- check_model_no_inst_gen,
- };
//check model
- int checkModel( int checkOption );
- //exhaustively instantiate quantifier (possibly using mbqi), return number of lemmas produced
- int exhaustiveInstantiate( Node f, bool useRelInstDomain = false );
+ int checkModel();
+ //exhaustively instantiate quantifier (possibly using mbqi)
+ void exhaustiveInstantiate( Node f, int effort = 0 );
private:
//temporary statistics
+ //is the exhaustive instantiation incomplete?
+ bool d_incomplete_check;
+ int d_addedLemmas;
int d_triedLemmas;
- int d_testLemmas;
int d_totalLemmas;
- int d_relevantLemmas;
public:
ModelEngine( context::Context* c, QuantifiersEngine* qe );
~ModelEngine(){}
+ //get relevant domain
+ RelevantDomain * getRelevantDomain() { return d_rel_dom; }
//get the builder
- ModelEngineBuilder* getModelBuilder() { return d_builder; }
+ QModelBuilder* getModelBuilder() { return d_builder; }
public:
void check( Theory::Effort e );
void registerQuantifier( Node f );
@@ -74,10 +68,6 @@ public:
class Statistics {
public:
IntStat d_inst_rounds;
- IntStat d_eval_formulas;
- IntStat d_eval_uf_terms;
- IntStat d_eval_lits;
- IntStat d_eval_lits_unknown;
IntStat d_exh_inst_lemmas;
Statistics();
~Statistics();
diff --git a/src/theory/quantifiers/options b/src/theory/quantifiers/options
index 60f5a171d..57211ade7 100644
--- a/src/theory/quantifiers/options
+++ b/src/theory/quantifiers/options
@@ -24,8 +24,11 @@ option prenexQuant /--disable-prenex-quant bool :default true
# Whether to variable-eliminate quantifiers.
# For example, forall x y. ( P( x, y ) V x != c ) will be rewritten to
# forall y. P( c, y )
-option varElimQuant --var-elim-quant bool :default false
- enable variable elimination of quantified formulas
+option varElimQuant /--disable-var-elim-quant bool :default true
+ disable simple variable elimination for quantified formulas
+
+option simpleIteLiftQuant /--disable-ite-lift-quant bool :default true
+ disable simple ite lifting for quantified formulas
# Whether to CNF quantifier bodies
option cnfQuant --cnf-quant bool :default false
@@ -47,6 +50,9 @@ option aggressiveMiniscopeQuant --ag-miniscope-quant bool :default false
# Whether to perform quantifier macro expansion
option macrosQuant --macros-quant bool :default false
perform quantifiers macro expansions
+# Whether to perform first-order propagation
+option foPropQuant --fo-prop-quant bool :default false
+ perform first-order propagation on quantifiers
# Whether to use smart triggers
option smartTriggers /--disable-smart-triggers bool :default true
@@ -54,6 +60,8 @@ option smartTriggers /--disable-smart-triggers bool :default true
# Whether to use relevent triggers
option relevantTriggers /--disable-relevant-triggers bool :default true
prefer triggers that are more relevant based on SInE style analysis
+option relationalTriggers --relational-triggers bool :default false
+ choose relational triggers such as x = f(y), x >= f(y)
# Whether to consider terms in the bodies of quantifiers for matching
option registerQuantBodyTerms --register-quant-body-terms bool :default false
@@ -72,6 +80,8 @@ option cbqi --enable-cbqi/--disable-cbqi bool :default false
turns on counterexample-based quantifier instantiation [off by default]
/turns off counterexample-based quantifier instantiation
+option recurseCbqi --cbqi-recurse bool :default false
+ turns on recursive counterexample-based quantifier instantiation
option userPatternsQuant /--ignore-user-patterns bool :default true
ignore user-provided patterns for quantifier instantiation
@@ -88,6 +98,15 @@ option finiteModelFind --finite-model-find bool :default false
option fmfModelBasedInst /--disable-fmf-mbqi bool :default true
disable model-based quantifier instantiation for finite model finding
+option fmfFullModelCheck --fmf-fmc bool :default false
+ enable full model check for finite model finding
+option fmfFmcSimple /--disable-fmf-fmc-simple bool :default true
+ disable simple models in full model check for finite model finding
+option fmfFmcCoverSimplify /--disable-fmf-fmc-cover-simplify bool :default true
+ disable covering simplification of fmc models
+option fmfFmcInterval --fmf-fmc-interval bool :default false
+ construct interval models for fmc models
+
option fmfOneInstPerRound --fmf-one-inst-per-round bool :default false
only add one instantiation per quantifier per round for fmf
option fmfOneQuantPerRound --fmf-one-quant-per-round bool :default false
@@ -98,13 +117,17 @@ option fmfRelevantDomain --fmf-relevant-domain bool :default false
use relevant domain computation, similar to complete instantiation (Ge, deMoura 09)
option fmfNewInstGen --fmf-new-inst-gen bool :default false
use new inst gen technique for answering sat without exhaustive instantiation
-option fmfInstGen /--disable-fmf-inst-gen bool :default true
- disable Inst-Gen instantiation techniques for finite model finding
+option fmfInstGen --fmf-inst-gen/--disable-fmf-inst-gen bool :read-write :default true
+ enable Inst-Gen instantiation techniques for finite model finding (default)
+/disable Inst-Gen instantiation techniques for finite model finding
option fmfInstGenOneQuantPerRound --fmf-inst-gen-one-quant-per-round bool :default false
only perform Inst-Gen instantiation techniques on one quantifier per round
option fmfFreshDistConst --fmf-fresh-dc bool :default false
use fresh distinguished representative when applying Inst-Gen techniques
+option fmfBoundInt --fmf-bound-int bool :default false
+ finite model finding on bounded integer quantification
+
option axiomInstMode --axiom-inst=MODE CVC4::theory::quantifiers::AxiomInstMode :default CVC4::theory::quantifiers::AXIOM_INST_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToAxiomInstMode :handler-include "theory/quantifiers/options_handlers.h"
policy for instantiating axioms
diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp
index 36db56d0d..59995a510 100644
--- a/src/theory/quantifiers/quant_util.cpp
+++ b/src/theory/quantifiers/quant_util.cpp
@@ -22,6 +22,102 @@ using namespace CVC4::kind;
using namespace CVC4::context;
using namespace CVC4::theory;
+
+bool QuantArith::getMonomial( Node n, std::map< Node, Node >& msum ) {
+ if ( n.getKind()==MULT ){
+ if( n.getNumChildren()==2 && msum.find(n[1])==msum.end() && n[0].isConst() ){
+ msum[n[1]] = n[0];
+ return true;
+ }
+ }else{
+ if( msum.find(n)==msum.end() ){
+ msum[n] = Node::null();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuantArith::getMonomialSum( Node n, std::map< Node, Node >& msum ) {
+ if ( n.getKind()==PLUS ){
+ for( unsigned i=0; i<n.getNumChildren(); i++) {
+ if (!getMonomial( n[i], msum )){
+ return false;
+ }
+ }
+ return true;
+ }else{
+ return getMonomial( n, msum );
+ }
+}
+
+bool QuantArith::getMonomialSumLit( Node lit, std::map< Node, Node >& msum ) {
+ if( lit.getKind()==GEQ || lit.getKind()==EQUAL ){
+ if( getMonomialSum( lit[0], msum ) ){
+ if( lit[1].isConst() ){
+ if( !lit[1].getConst<Rational>().isZero() ){
+ msum[Node::null()] = negate( lit[1] );
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool QuantArith::isolate( Node v, std::map< Node, Node >& msum, Node & veq, Kind k ) {
+ if( msum.find(v)!=msum.end() ){
+ std::vector< Node > children;
+ Rational r = msum[v].isNull() ? Rational(1) : msum[v].getConst<Rational>();
+ if ( r.sgn()!=0 ){
+ for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){
+ if( it->first.isNull() || it->first!=v ){
+ Node m;
+ if( !it->first.isNull() ){
+ if ( !it->second.isNull() ){
+ m = NodeManager::currentNM()->mkNode( MULT, it->second, it->first );
+ }else{
+ m = it->first;
+ }
+ }else{
+ m = it->second;
+ }
+ children.push_back(m);
+ }
+ }
+ veq = children.size()>1 ? NodeManager::currentNM()->mkNode( PLUS, children ) :
+ (children.size()==1 ? children[0] : NodeManager::currentNM()->mkConst( Rational(0) ));
+ if( !r.isNegativeOne() ){
+ if( r.isOne() ){
+ veq = negate(veq);
+ }else{
+ //TODO
+ return false;
+ }
+ }
+ veq = Rewriter::rewrite( veq );
+ veq = NodeManager::currentNM()->mkNode( k, r.sgn()==1 ? v : veq, r.sgn()==1 ? veq : v );
+ return true;
+ }
+ return false;
+ }else{
+ return false;
+ }
+}
+
+Node QuantArith::negate( Node t ) {
+ Node tt = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(-1) ), t );
+ tt = Rewriter::rewrite( tt );
+ return tt;
+}
+
+Node QuantArith::offset( Node t, int i ) {
+ Node tt = NodeManager::currentNM()->mkNode( PLUS, NodeManager::currentNM()->mkConst( Rational(i) ), t );
+ tt = Rewriter::rewrite( tt );
+ return tt;
+}
+
+
void QuantRelevance::registerQuantifier( Node f ){
//compute symbols in f
std::vector< Node > syms;
@@ -93,13 +189,13 @@ QuantPhaseReq::QuantPhaseReq( Node n, bool computeEq ){
for( std::map< Node, bool >::iterator it = d_phase_reqs.begin(); it != d_phase_reqs.end(); ++it ){
Debug("inst-engine-phase-req") << " " << it->first << " -> " << it->second << std::endl;
if( it->first.getKind()==EQUAL ){
- if( it->first[0].hasAttribute(InstConstantAttribute()) ){
- if( !it->first[1].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(it->first[0]) ){
+ if( !quantifiers::TermDb::hasInstConstAttr(it->first[1]) ){
d_phase_reqs_equality_term[ it->first[0] ] = it->first[1];
d_phase_reqs_equality[ it->first[0] ] = it->second;
Debug("inst-engine-phase-req") << " " << it->first[0] << ( it->second ? " == " : " != " ) << it->first[1] << std::endl;
}
- }else if( it->first[1].hasAttribute(InstConstantAttribute()) ){
+ }else if( quantifiers::TermDb::hasInstConstAttr(it->first[1]) ){
d_phase_reqs_equality_term[ it->first[1] ] = it->first[0];
d_phase_reqs_equality[ it->first[1] ] = it->second;
Debug("inst-engine-phase-req") << " " << it->first[1] << ( it->second ? " == " : " != " ) << it->first[0] << std::endl;
diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h
index 6a5726cc7..86c7bc3a0 100644
--- a/src/theory/quantifiers/quant_util.h
+++ b/src/theory/quantifiers/quant_util.h
@@ -28,6 +28,18 @@ namespace CVC4 {
namespace theory {
+class QuantArith
+{
+public:
+ static bool getMonomial( Node n, std::map< Node, Node >& msum );
+ static bool getMonomialSum( Node n, std::map< Node, Node >& msum );
+ static bool getMonomialSumLit( Node lit, std::map< Node, Node >& msum );
+ static bool isolate( Node v, std::map< Node, Node >& msum, Node & veq, Kind k );
+ static Node negate( Node t );
+ static Node offset( Node t, int i );
+};
+
+
class QuantRelevance
{
private:
diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp
index 5bdce5fac..909c9c388 100644
--- a/src/theory/quantifiers/quantifiers_attributes.cpp
+++ b/src/theory/quantifiers/quantifiers_attributes.cpp
@@ -32,6 +32,10 @@ void QuantifiersAttributes::setUserAttribute( const std::string& attr, Node n ){
Trace("quant-attr") << "Set conjecture " << n << std::endl;
ConjectureAttribute ca;
n.setAttribute( ca, true );
+ }else if( attr=="rr_priority" ){
+ //Trace("quant-attr") << "Set rr priority " << n << std::endl;
+ //RrPriorityAttribute rra;
+
}
}else{
for( size_t i=0; i<n.getNumChildren(); i++ ){
diff --git a/src/theory/quantifiers/quantifiers_attributes.h b/src/theory/quantifiers/quantifiers_attributes.h
index 878d3ac50..1aebbfcc5 100644
--- a/src/theory/quantifiers/quantifiers_attributes.h
+++ b/src/theory/quantifiers/quantifiers_attributes.h
@@ -34,6 +34,10 @@ typedef expr::Attribute< AxiomAttributeId, bool > AxiomAttribute;
struct ConjectureAttributeId {};
typedef expr::Attribute< ConjectureAttributeId, bool > ConjectureAttribute;
+/** Attribute priority for rewrite rules */
+//struct RrPriorityAttributeId {};
+//typedef expr::Attribute< RrPriorityAttributeId, uint64_t > RrPriorityAttribute;
+
struct QuantifiersAttributes
{
/** set user attribute
diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp
index 5c71f6e6f..e27897a96 100644
--- a/src/theory/quantifiers/quantifiers_rewriter.cpp
+++ b/src/theory/quantifiers/quantifiers_rewriter.cpp
@@ -211,10 +211,6 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) {
if( in.hasAttribute(NestedQuantAttribute()) ){
setNestedQuantifiers( ret, in.getAttribute(NestedQuantAttribute()) );
}
- if( in.hasAttribute(InstConstantAttribute()) ){
- InstConstantAttribute ica;
- ret.setAttribute(ica,in.getAttribute(InstConstantAttribute()) );
- }
Trace("quantifiers-rewrite") << "*** rewrite " << in << std::endl;
Trace("quantifiers-rewrite") << " to " << std::endl;
Trace("quantifiers-rewrite") << ret << std::endl;
@@ -311,7 +307,7 @@ Node QuantifiersRewriter::computeSimpleIteLift( Node body ) {
}
Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ){
- Trace("var-elim-quant") << "Compute var elimination for " << body << std::endl;
+ Trace("var-elim-quant-debug") << "Compute var elimination for " << body << std::endl;
QuantPhaseReq qpr( body );
std::vector< Node > vars;
std::vector< Node > subs;
@@ -918,7 +914,7 @@ bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption
}else if( computeOption==COMPUTE_NNF ){
return false;//TODO: compute NNF (current bad idea since arithmetic rewrites equalities)
}else if( computeOption==COMPUTE_SIMPLE_ITE_LIFT ){
- return !options::finiteModelFind();
+ return options::simpleIteLiftQuant();//!options::finiteModelFind();
}else if( computeOption==COMPUTE_PRENEX ){
return options::prenexQuant() && !options::aggressiveMiniscopeQuant();
}else if( computeOption==COMPUTE_VAR_ELIMINATION ){
diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp
index 2b011552c..b079ae75c 100644..100755
--- a/src/theory/quantifiers/relevant_domain.cpp
+++ b/src/theory/quantifiers/relevant_domain.cpp
@@ -24,175 +24,159 @@ using namespace CVC4::context;
using namespace CVC4::theory;
using namespace CVC4::theory::quantifiers;
+void RelevantDomain::RDomain::merge( RDomain * r ) {
+ Assert( !d_parent );
+ Assert( !r->d_parent );
+ d_parent = r;
+ for( unsigned i=0; i<d_terms.size(); i++ ){
+ r->addTerm( d_terms[i] );
+ }
+ d_terms.clear();
+}
+
+void RelevantDomain::RDomain::addTerm( Node t ) {
+ if( std::find( d_terms.begin(), d_terms.end(), t )==d_terms.end() ){
+ d_terms.push_back( t );
+ }
+}
+
+RelevantDomain::RDomain * RelevantDomain::RDomain::getParent() {
+ if( !d_parent ){
+ return this;
+ }else{
+ RDomain * p = d_parent->getParent();
+ d_parent = p;
+ return p;
+ }
+}
+
+void RelevantDomain::RDomain::removeRedundantTerms( FirstOrderModel * fm ) {
+ std::map< Node, Node > rterms;
+ for( unsigned i=0; i<d_terms.size(); i++ ){
+ Node r = d_terms[i];
+ if( !TermDb::hasInstConstAttr( d_terms[i] ) ){
+ r = fm->getRepresentative( d_terms[i] );
+ }
+ if( rterms.find( r )==rterms.end() ){
+ rterms[r] = d_terms[i];
+ }
+ }
+ d_terms.clear();
+ for( std::map< Node, Node >::iterator it = rterms.begin(); it != rterms.end(); ++it ){
+ d_terms.push_back( it->second );
+ }
+}
+
+
+
RelevantDomain::RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m ) : d_qe( qe ), d_model( m ){
}
+RelevantDomain::RDomain * RelevantDomain::getRDomain( Node n, int i ) {
+ if( d_rel_doms.find( n )==d_rel_doms.end() || d_rel_doms[n].find( i )==d_rel_doms[n].end() ){
+ d_rel_doms[n][i] = new RDomain;
+ d_rn_map[d_rel_doms[n][i]] = n;
+ d_ri_map[d_rel_doms[n][i]] = i;
+ }
+ return d_rel_doms[n][i]->getParent();
+}
+
void RelevantDomain::compute(){
- Trace("rel-dom") << "compute relevant domain" << std::endl;
- d_quant_inst_domain.clear();
+ for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){
+ for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ it2->second->reset();
+ }
+ }
for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){
Node f = d_model->getAssertedQuantifier( i );
- d_quant_inst_domain[f].resize( f[0].getNumChildren() );
+ Node icf = d_qe->getTermDatabase()->getInstConstantBody( f );
+ Trace("rel-dom-debug") << "compute relevant domain for " << icf << std::endl;
+ computeRelevantDomain( icf, true, true );
}
- Trace("rel-dom") << "account for ground terms" << std::endl;
- //add ground terms to domain (rule 1 of complete instantiation essentially uf fragment)
- for( std::map< Node, uf::UfModelTree >::iterator it = d_model->d_uf_model_tree.begin();
- it != d_model->d_uf_model_tree.end(); ++it ){
+
+ Trace("rel-dom-debug") << "account for ground terms" << std::endl;
+ for( std::map< Node, std::vector< Node > >::iterator it = d_model->d_uf_terms.begin(); it != d_model->d_uf_terms.end(); ++it ){
Node op = it->first;
- for( size_t i=0; i<d_model->d_uf_terms[op].size(); i++ ){
- Node n = d_model->d_uf_terms[op][i];
- //add arguments to domain
- for( int j=0; j<(int)n.getNumChildren(); j++ ){
- if( d_model->d_rep_set.hasType( n[j].getType() ) ){
- Node ra = d_model->getRepresentative( n[j] );
- int raIndex = d_model->d_rep_set.getIndexFor( ra );
- if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground domain: rep set does not contain : " << ra << std::endl;
- Assert( raIndex!=-1 );
- if( std::find( d_active_domain[op][j].begin(), d_active_domain[op][j].end(), raIndex )==d_active_domain[op][j].end() ){
- d_active_domain[op][j].push_back( raIndex );
- }
+ for( unsigned i=0; i<it->second.size(); i++ ){
+ Node n = it->second[i];
+ //if it is a non-redundant term
+ if( !n.getAttribute(NoMatchAttribute()) ){
+ for( unsigned j=0; j<n.getNumChildren(); j++ ){
+ RDomain * rf = getRDomain( op, j );
+ rf->addTerm( n[j] );
}
}
- //add to range
- Node r = d_model->getRepresentative( n );
- int raIndex = d_model->d_rep_set.getIndexFor( r );
- if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground range: rep set does not contain : " << r << std::endl;
- Assert( raIndex!=-1 );
- if( std::find( d_active_range[op].begin(), d_active_range[op].end(), raIndex )==d_active_range[op].end() ){
- d_active_range[op].push_back( raIndex );
- }
}
}
- Trace("rel-dom") << "do quantifiers" << std::endl;
- //find fixed point for relevant domain computation
- bool success;
- do{
- success = true;
- for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){
- Node f = d_model->getAssertedQuantifier( i );
- //compute the domain of relevant instantiations (rule 3 of complete instantiation, essentially uf fragment)
- if( computeRelevantInstantiationDomain( d_qe->getTermDatabase()->getInstConstantBody( f ), Node::null(), -1, f ) ){
- success = false;
- }
- //extend the possible domain for functions (rule 2 of complete instantiation, essentially uf fragment)
- RepDomain range;
- if( extendFunctionDomains( d_qe->getTermDatabase()->getInstConstantBody( f ), range ) ){
- success = false;
- }
- }
- }while( !success );
- Trace("rel-dom") << "done compute relevant domain" << std::endl;
- /*
- //debug printing
- Trace("rel-dom") << "Exhaustive instantiate " << f << std::endl;
- if( useRelInstDomain ){
- Trace("rel-dom") << "Relevant domain : " << std::endl;
- for( size_t i=0; i<d_rel_domain.d_quant_inst_domain[f].size(); i++ ){
- Trace("rel-dom") << " " << i << " : ";
- for( size_t j=0; j<d_rel_domain.d_quant_inst_domain[f][i].size(); j++ ){
- Trace("rel-dom") << d_rel_domain.d_quant_inst_domain[f][i][j] << " ";
+ //print debug
+ for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){
+ Trace("rel-dom") << "Relevant domain for " << it->first << " : " << std::endl;
+ for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ Trace("rel-dom") << " " << it2->first << " : ";
+ RDomain * r = it2->second;
+ RDomain * rp = r->getParent();
+ if( r==rp ){
+ r->removeRedundantTerms( d_model );
+ for( unsigned i=0; i<r->d_terms.size(); i++ ){
+ Trace("rel-dom") << r->d_terms[i] << " ";
+ }
+ }else{
+ Trace("rel-dom") << "Dom( " << d_rn_map[rp] << ", " << d_ri_map[rp] << " ) ";
}
Trace("rel-dom") << std::endl;
}
}
- */
+
}
-bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f ){
- bool domainChanged = false;
- if( n.getKind()==INST_CONSTANT ){
- bool domainSet = false;
- int vi = n.getAttribute(InstVarNumAttribute());
- Assert( !parent.isNull() );
- if( parent.getKind()==APPLY_UF ){
- //if the child of APPLY_UF term f( ... ), only consider the active domain of f at given argument
- Node op = parent.getOperator();
- if( d_active_domain.find( op )!=d_active_domain.end() ){
- for( size_t i=0; i<d_active_domain[op][arg].size(); i++ ){
- int d = d_active_domain[op][arg][i];
- if( std::find( d_quant_inst_domain[f][vi].begin(), d_quant_inst_domain[f][vi].end(), d )==
- d_quant_inst_domain[f][vi].end() ){
- d_quant_inst_domain[f][vi].push_back( d );
- domainChanged = true;
- }
- }
- domainSet = true;
- }
- }
- if( !domainSet ){
- //otherwise, we must consider the entire domain
- TypeNode tn = n.getType();
- if( d_quant_inst_domain_complete[f].find( vi )==d_quant_inst_domain_complete[f].end() ){
- if( d_model->d_rep_set.hasType( tn ) ){
- //it is the complete domain
- d_quant_inst_domain[f][vi].clear();
- for( size_t i=0; i<d_model->d_rep_set.d_type_reps[tn].size(); i++ ){
- d_quant_inst_domain[f][vi].push_back( i );
- }
- domainChanged = true;
+void RelevantDomain::computeRelevantDomain( Node n, bool hasPol, bool pol ) {
+
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( n.getKind()==APPLY_UF ){
+ RDomain * rf = getRDomain( n.getOperator(), i );
+ if( n[i].getKind()==INST_CONSTANT ){
+ Node q = d_qe->getTermDatabase()->getInstConstAttr( n[i] );
+ //merge the RDomains
+ unsigned id = n[i].getAttribute(InstVarNumAttribute());
+ Trace("rel-dom-debug") << n[i] << " is variable # " << id << " for " << q;
+ Trace("rel-dom-debug") << " with body : " << d_qe->getTermDatabase()->getInstConstantBody( q ) << std::endl;
+ RDomain * rq = getRDomain( q, id );
+ if( rf!=rq ){
+ rq->merge( rf );
}
- d_quant_inst_domain_complete[f][vi] = true;
+ }else{
+ //term to add
+ rf->addTerm( n[i] );
}
}
- }else{
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( computeRelevantInstantiationDomain( n[i], n, i, f ) ){
- domainChanged = true;
- }
+
+ //TODO
+ if( n[i].getKind()!=FORALL ){
+ bool newHasPol = hasPol;
+ bool newPol = pol;
+ computeRelevantDomain( n[i], newHasPol, newPol );
}
}
- return domainChanged;
}
-bool RelevantDomain::extendFunctionDomains( Node n, RepDomain& range ){
- if( n.getKind()==INST_CONSTANT ){
- Node f = n.getAttribute(InstConstantAttribute());
- int var = n.getAttribute(InstVarNumAttribute());
- range.insert( range.begin(), d_quant_inst_domain[f][var].begin(), d_quant_inst_domain[f][var].end() );
- return false;
+Node RelevantDomain::getRelevantTerm( Node f, int i, Node r ) {
+ RDomain * rf = getRDomain( f, i );
+ Trace("rel-dom-debug") << "Get relevant domain " << rf << " " << r << std::endl;
+ if( !d_qe->getEqualityQuery()->getEngine()->hasTerm( r ) || rf->hasTerm( r ) ){
+ return r;
}else{
- Node op;
- if( n.getKind()==APPLY_UF ){
- op = n.getOperator();
- }
- bool domainChanged = false;
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- RepDomain childRange;
- if( extendFunctionDomains( n[i], childRange ) ){
- domainChanged = true;
- }
- if( n.getKind()==APPLY_UF ){
- if( d_active_domain.find( op )!=d_active_domain.end() ){
- for( int j=0; j<(int)childRange.size(); j++ ){
- int v = childRange[j];
- if( std::find( d_active_domain[op][i].begin(), d_active_domain[op][i].end(), v )==d_active_domain[op][i].end() ){
- d_active_domain[op][i].push_back( v );
- domainChanged = true;
- }
- }
- }else{
- //do this?
- }
- }
- }
- //get the range
- if( n.hasAttribute(InstConstantAttribute()) ){
- if( n.getKind()==APPLY_UF && d_active_range.find( op )!=d_active_range.end() ){
- range.insert( range.end(), d_active_range[op].begin(), d_active_range[op].end() );
- }else{
- //infinite range?
- }
- }else{
- Node r = d_model->getRepresentative( n );
- int index = d_model->d_rep_set.getIndexFor( r );
- if( index==-1 ){
- //we consider all ground terms in bodies of quantifiers to be the first ground representative
- range.push_back( 0 );
- }else{
- range.push_back( index );
+ Node rr = d_model->getRepresentative( r );
+ eq::EqClassIterator eqc( rr, d_qe->getEqualityQuery()->getEngine() );
+ while( !eqc.isFinished() ){
+ Node en = (*eqc);
+ if( rf->hasTerm( en ) ){
+ return en;
}
+ ++eqc;
}
- return domainChanged;
+ //otherwise, may be equal to some non-ground term
+
+ return r;
}
-} \ No newline at end of file
+}
diff --git a/src/theory/quantifiers/relevant_domain.h b/src/theory/quantifiers/relevant_domain.h
index 6fc035e8a..c4345f828 100644..100755
--- a/src/theory/quantifiers/relevant_domain.h
+++ b/src/theory/quantifiers/relevant_domain.h
@@ -26,25 +26,33 @@ namespace quantifiers {
class RelevantDomain
{
private:
+ class RDomain
+ {
+ public:
+ RDomain() : d_parent( NULL ) {}
+ void reset() { d_parent = NULL; d_terms.clear(); }
+ RDomain * d_parent;
+ std::vector< Node > d_terms;
+ void merge( RDomain * r );
+ void addTerm( Node t );
+ RDomain * getParent();
+ void removeRedundantTerms( FirstOrderModel * fm );
+ bool hasTerm( Node n ) { return std::find( d_terms.begin(), d_terms.end(), n )!=d_terms.end(); }
+ };
+ std::map< Node, std::map< int, RDomain * > > d_rel_doms;
+ std::map< RDomain *, Node > d_rn_map;
+ std::map< RDomain *, int > d_ri_map;
+ RDomain * getRDomain( Node n, int i );
QuantifiersEngine* d_qe;
FirstOrderModel* d_model;
-
- //the domain of the arguments for each operator
- std::map< Node, std::map< int, RepDomain > > d_active_domain;
- //the range for each operator
- std::map< Node, RepDomain > d_active_range;
- //for computing relevant instantiation domain, return true if changed
- bool computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f );
- //for computing extended
- bool extendFunctionDomains( Node n, RepDomain& range );
+ void computeRelevantDomain( Node n, bool hasPol, bool pol );
public:
RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m );
virtual ~RelevantDomain(){}
//compute the relevant domain
void compute();
- //relevant instantiation domain for each quantifier
- std::map< Node, std::vector< RepDomain > > d_quant_inst_domain;
- std::map< Node, std::map< int, bool > > d_quant_inst_domain_complete;
+
+ Node getRelevantTerm( Node f, int i, Node r );
};/* class RelevantDomain */
}/* CVC4::theory::quantifiers namespace */
diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp
new file mode 100755
index 000000000..107a99014
--- /dev/null
+++ b/src/theory/quantifiers/rewrite_engine.cpp
@@ -0,0 +1,184 @@
+/********************* */
+/*! \file bounded_integers.cpp
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Rewrite engine module
+ **
+ ** This class manages rewrite rules for quantifiers
+ **/
+
+#include "theory/quantifiers/rewrite_engine.h"
+#include "theory/quantifiers/quant_util.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/model_engine.h"
+#include "theory/quantifiers/options.h"
+#include "theory/quantifiers/inst_match_generator.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4;
+using namespace std;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace CVC4::kind;
+
+struct PrioritySort {
+ std::vector< double > d_priority;
+ bool operator() (int i,int j) {
+ return d_priority[i] < d_priority[j];
+ }
+};
+
+
+RewriteEngine::RewriteEngine( context::Context* c, QuantifiersEngine* qe ) : QuantifiersModule(qe) {
+
+}
+
+double RewriteEngine::getPriority( Node f ) {
+ Node rr = f.getAttribute(QRewriteRuleAttribute());
+ Node rrr = rr[2];
+ Trace("rr-priority") << "Get priority : " << rrr << " " << rrr.getKind() << std::endl;
+ bool deterministic = rrr[1].getKind()!=OR;
+ if( rrr.getKind()==RR_REWRITE ){
+ return deterministic ? 0.0 : 3.0;
+ }else if( rrr.getKind()==RR_DEDUCTION ){
+ return deterministic ? 1.0 : 4.0;
+ }else if( rrr.getKind()==RR_REDUCTION ){
+ return deterministic ? 2.0 : 5.0;
+ }else{
+ return 6.0;
+ }
+}
+
+void RewriteEngine::check( Theory::Effort e ) {
+ if( e==Theory::EFFORT_LAST_CALL ){
+ if( !d_quantEngine->getModel()->isModelSet() ){
+ d_quantEngine->getTheoryEngine()->getModelBuilder()->buildModel( d_quantEngine->getModel(), true );
+ }
+ if( d_true.isNull() ){
+ d_true = NodeManager::currentNM()->mkConst( true );
+ }
+ std::vector< Node > priority_order;
+ PrioritySort ps;
+ std::vector< int > indicies;
+ for( int i=0; i<(int)d_rr_quant.size(); i++ ){
+ ps.d_priority.push_back( getPriority( d_rr_quant[i] ) );
+ indicies.push_back( i );
+ }
+ std::sort( indicies.begin(), indicies.end(), ps );
+ for( unsigned i=0; i<indicies.size(); i++ ){
+ priority_order.push_back( d_rr_quant[indicies[i]] );
+ }
+
+ //apply rewrite rules
+ int addedLemmas = 0;
+ //per priority level
+ int index = 0;
+ bool success = true;
+ while( success && index<(int)priority_order.size() ) {
+ addedLemmas += checkRewriteRule( priority_order[index] );
+ index++;
+ if( index<(int)priority_order.size() ){
+ success = addedLemmas==0 || getPriority( priority_order[index] )==getPriority( priority_order[index-1] );
+ }
+ }
+
+ Trace("inst-engine") << "Rewrite engine added " << addedLemmas << " lemmas." << std::endl;
+ if (addedLemmas==0) {
+ }else{
+ //otherwise, the search will continue
+ d_quantEngine->flushLemmas( &d_quantEngine->getOutputChannel() );
+ }
+ }
+}
+
+int RewriteEngine::checkRewriteRule( Node f ) {
+ Trace("rewrite-engine-inst") << "Check " << f << ", priority = " << getPriority( f ) << "..." << std::endl;
+ int addedLemmas = 0;
+ //reset triggers
+ Node rr = f.getAttribute(QRewriteRuleAttribute());
+ if( d_rr_triggers.find(f)==d_rr_triggers.end() ){
+ std::vector< inst::Trigger * > triggers;
+ if( f.getNumChildren()==3 ){
+ for(unsigned i=0; i<f[2].getNumChildren(); i++ ){
+ Node pat = f[2][i];
+ std::vector< Node > nodes;
+ Trace("rewrite-engine-trigger") << "Trigger is : ";
+ for( int j=0; j<(int)pat.getNumChildren(); j++ ){
+ Node p = d_quantEngine->getTermDatabase()->getInstConstantNode( pat[j], f );
+ nodes.push_back( p );
+ Trace("rewrite-engine-trigger") << p << " " << p.getKind() << " ";
+ }
+ Trace("rewrite-engine-trigger") << std::endl;
+ Assert( inst::Trigger::isUsableTrigger( nodes, f ) );
+ inst::Trigger * tr = inst::Trigger::mkTrigger( d_quantEngine, f, nodes, 0, true, inst::Trigger::TR_MAKE_NEW, false );
+ tr->getGenerator()->setActiveAdd(false);
+ triggers.push_back( tr );
+ }
+ }
+ d_rr_triggers[f].insert( d_rr_triggers[f].end(), triggers.begin(), triggers.end() );
+ }
+ for( unsigned i=0; i<d_rr_triggers[f].size(); i++ ){
+ Trace("rewrite-engine-inst") << "Try trigger " << *d_rr_triggers[f][i] << std::endl;
+ d_rr_triggers[f][i]->resetInstantiationRound();
+ d_rr_triggers[f][i]->reset( Node::null() );
+ bool success;
+ do
+ {
+ InstMatch m;
+ success = d_rr_triggers[f][i]->getNextMatch( f, m );
+ if( success ){
+ //see if instantiation is true in the model
+ Node rr = f.getAttribute(QRewriteRuleAttribute());
+ Node rrg = rr[1];
+ std::vector< Node > vars;
+ std::vector< Node > terms;
+ d_quantEngine->computeTermVector( f, m, vars, terms );
+ Trace("rewrite-engine-inst-debug") << "Instantiation : " << m << std::endl;
+ Node inst = d_rr_guard[f];
+ inst = inst.substitute( vars.begin(), vars.end(), terms.begin(), terms.end() );
+ Trace("rewrite-engine-inst-debug") << "Try instantiation, guard : " << inst << std::endl;
+ FirstOrderModel * fm = d_quantEngine->getModel();
+ Node v = fm->getValue( inst );
+ Trace("rewrite-engine-inst-debug") << "Evaluated to " << v << std::endl;
+ if( v==d_true ){
+ Trace("rewrite-engine-inst-debug") << "Add instantiation : " << m << std::endl;
+ if( d_quantEngine->addInstantiation( f, m ) ){
+ addedLemmas++;
+ Trace("rewrite-engine-inst-debug") << "success" << std::endl;
+ //set the no-match attribute (the term is rewritten and can be ignored)
+ //Trace("rewrite-engine-inst-debug") << "We matched on : " << m.d_matched << std::endl;
+ //if( !m.d_matched.isNull() ){
+ // NoMatchAttribute nma;
+ // m.d_matched.setAttribute(nma,true);
+ //}
+ }else{
+ Trace("rewrite-engine-inst-debug") << "failure." << std::endl;
+ }
+ }
+ }
+ }while(success);
+ }
+ Trace("rewrite-engine-inst") << "Rule " << f << " generated " << addedLemmas << " lemmas." << std::endl;
+ return addedLemmas;
+}
+
+void RewriteEngine::registerQuantifier( Node f ) {
+ if( f.hasAttribute(QRewriteRuleAttribute()) ){
+ Trace("rewrite-engine") << "Register quantifier " << f << std::endl;
+ Node rr = f.getAttribute(QRewriteRuleAttribute());
+ Trace("rewrite-engine") << " rewrite rule is : " << rr << std::endl;
+ d_rr_quant.push_back( f );
+ d_rr_guard[f] = rr[1];
+ Trace("rewrite-engine") << " guard is : " << d_rr_guard[f] << std::endl;
+ }
+}
+
+void RewriteEngine::assertNode( Node n ) {
+
+}
+
diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h
new file mode 100755
index 000000000..2d9d751c2
--- /dev/null
+++ b/src/theory/quantifiers/rewrite_engine.h
@@ -0,0 +1,54 @@
+/********************* */
+/*! \file bounded_integers.h
+** \verbatim
+** Original author: Andrew Reynolds
+** This file is part of the CVC4 project.
+** Copyright (c) 2009-2013 New York University and The University of Iowa
+** See the file COPYING in the top-level source directory for licensing
+** information.\endverbatim
+**
+** \brief This class manages integer bounds for quantifiers
+**/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__REWRITE_ENGINE_H
+#define __CVC4__REWRITE_ENGINE_H
+
+#include "theory/quantifiers_engine.h"
+#include "theory/quantifiers/trigger.h"
+
+#include "context/context.h"
+#include "context/context_mm.h"
+#include "context/cdchunk_list.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class RewriteEngine : public QuantifiersModule
+{
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+ typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap;
+ typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
+ std::vector< Node > d_rr_quant;
+ std::map< Node, Node > d_rr_guard;
+ Node d_true;
+ /** explicitly provided patterns */
+ std::map< Node, std::vector< inst::Trigger* > > d_rr_triggers;
+ double getPriority( Node f );
+private:
+ int checkRewriteRule( Node f );
+public:
+ RewriteEngine( context::Context* c, QuantifiersEngine* qe );
+
+ void check( Theory::Effort e );
+ void registerQuantifier( Node f );
+ void assertNode( Node n );
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/symmetry_breaking.cpp b/src/theory/quantifiers/symmetry_breaking.cpp
new file mode 100755
index 000000000..507a50838
--- /dev/null
+++ b/src/theory/quantifiers/symmetry_breaking.cpp
@@ -0,0 +1,314 @@
+/********************* */
+/*! \file symmetry_breaking.cpp
+ ** \verbatim
+ ** Original author: ajreynol
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief symmetry breaking module
+ **
+ **/
+
+#include <vector>
+
+#include "theory/quantifiers/symmetry_breaking.h"
+#include "theory/rewriter.h"
+#include "theory/quantifiers_engine.h"
+#include "theory/theory_engine.h"
+#include "util/sort_inference.h"
+#include "theory/uf/theory_uf_strong_solver.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::theory;
+using namespace std;
+
+namespace CVC4 {
+
+
+SubsortSymmetryBreaker::SubsortSymmetryBreaker(QuantifiersEngine* qe, context::Context* c) :
+d_qe(qe), d_conflict(c,false) {
+ d_true = NodeManager::currentNM()->mkConst( true );
+}
+
+eq::EqualityEngine * SubsortSymmetryBreaker::getEqualityEngine() {
+ return ((uf::TheoryUF*)d_qe->getTheoryEngine()->theoryOf( theory::THEORY_UF ))->getEqualityEngine();
+}
+
+bool SubsortSymmetryBreaker::areEqual( Node n1, Node n2 ) {
+ return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areEqual( n1,n2 );
+}
+
+bool SubsortSymmetryBreaker::areDisequal( Node n1, Node n2 ) {
+ return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areDisequal( n1,n2, false );
+}
+
+
+Node SubsortSymmetryBreaker::getRepresentative( Node n ) {
+ return getEqualityEngine()->getRepresentative( n );
+}
+
+uf::StrongSolverTheoryUF * SubsortSymmetryBreaker::getStrongSolver() {
+ return ((uf::TheoryUF*)d_qe->getTheoryEngine()->theoryOf( theory::THEORY_UF ))->getStrongSolver();
+}
+
+SubsortSymmetryBreaker::TypeInfo::TypeInfo( context::Context * c ) :
+d_max_dom_const_sort(c,0), d_has_dom_const_sort(c,false) {
+}
+
+SubsortSymmetryBreaker::SubSortInfo::SubSortInfo( context::Context * c ) :
+d_dom_constants( c ), d_first_active( c, 0 ){
+ d_dc_nodes = 0;
+}
+
+unsigned SubsortSymmetryBreaker::SubSortInfo::getNumDomainConstants() {
+ if( d_nodes.empty() ){
+ return 0;
+ }else{
+ return 1 + d_dom_constants.size();
+ }
+}
+
+Node SubsortSymmetryBreaker::SubSortInfo::getDomainConstant( int i ) {
+ if( i==0 ){
+ return d_nodes[0];
+ }else{
+ Assert( i<=(int)d_dom_constants.size() );
+ return d_dom_constants[i-1];
+ }
+}
+
+Node SubsortSymmetryBreaker::SubSortInfo::getFirstActive(eq::EqualityEngine * ee) {
+ if( d_first_active.get()<(int)d_nodes.size() ){
+ Node fa = d_nodes[d_first_active.get()];
+ return ee->hasTerm( fa ) ? fa : Node::null();
+ }else{
+ return Node::null();
+ }
+}
+
+SubsortSymmetryBreaker::TypeInfo * SubsortSymmetryBreaker::getTypeInfo( TypeNode tn ) {
+ if( d_t_info.find( tn )==d_t_info.end() ){
+ d_t_info[tn] = new TypeInfo( d_qe->getSatContext() );
+ }
+ return d_t_info[tn];
+}
+
+SubsortSymmetryBreaker::SubSortInfo * SubsortSymmetryBreaker::getSubSortInfo( TypeNode tn, int sid ) {
+ if( d_type_info.find( sid )==d_type_info.end() ){
+ d_type_info[sid] = new SubSortInfo( d_qe->getSatContext() );
+ d_sub_sorts[tn].push_back( sid );
+ d_sid_to_type[sid] = tn;
+ }
+ return d_type_info[sid];
+}
+
+void SubsortSymmetryBreaker::newEqClass( Node n ) {
+ Trace("sym-break-temp") << "New eq class " << n << std::endl;
+ if( !d_conflict ){
+ TypeNode tn = n.getType();
+ SortInference * si = d_qe->getTheoryEngine()->getSortInference();
+ if( si->isWellSorted( n ) ){
+ int sid = si->getSortId( n );
+ Trace("sym-break-debug") << "SSB: New eq class " << n << " : " << n.getType() << " : " << sid << std::endl;
+ SubSortInfo * ti = getSubSortInfo( tn, sid );
+ if( std::find( ti->d_nodes.begin(), ti->d_nodes.end(), n )==ti->d_nodes.end() ){
+ if( ti->d_nodes.empty() ){
+ //for first subsort, we add unit equality
+ if( d_sub_sorts[tn][0]!=sid ){
+ Trace("sym-break-temp") << "Do sym break unit with " << d_type_info[d_sub_sorts[tn][0]]->getBaseConstant() << std::endl;
+ //add unit symmetry breaking lemma
+ Node eq = n.eqNode( d_type_info[d_sub_sorts[tn][0]]->getBaseConstant() );
+ eq = Rewriter::rewrite( eq );
+ d_unit_lemmas.push_back( eq );
+ Trace("sym-break-lemma") << "*** SymBreak : Unit lemma (" << sid << "==" << d_sub_sorts[tn][0] << ") : " << eq << std::endl;
+ d_pending_lemmas.push_back( eq );
+ }
+ Trace("sym-break-dc") << "* Set first domain constant : " << n << " for " << tn << " : " << sid << std::endl;
+ ti->d_dc_nodes++;
+ }
+ ti->d_node_to_id[n] = ti->d_nodes.size();
+ ti->d_nodes.push_back( n );
+ }
+ TypeInfo * tti = getTypeInfo( tn );
+ if( !tti->d_has_dom_const_sort.get() ){
+ tti->d_has_dom_const_sort.set( true );
+ tti->d_max_dom_const_sort.set( sid );
+ }
+ }
+ }
+ Trace("sym-break-temp") << "Done new eq class" << std::endl;
+}
+
+
+
+void SubsortSymmetryBreaker::merge( Node a, Node b ) {
+ if( Trace.isOn("sym-break-debug") ){
+ SortInference * si = d_qe->getTheoryEngine()->getSortInference();
+ int as = si->getSortId( a );
+ int bs = si->getSortId( b );
+ Trace("sym-break-debug") << "SSB: New merge " << a.getType() << " :: " << a << " : " << as;
+ Trace("sym-break-debug") << " == " << b << " : " << bs << std::endl;
+ }
+}
+
+void SubsortSymmetryBreaker::assertDisequal( Node a, Node b ) {
+ if( Trace.isOn("sym-break-debug") ){
+ SortInference * si = d_qe->getTheoryEngine()->getSortInference();
+ int as = si->getSortId( a );
+ int bs = si->getSortId( b );
+ Trace("sym-break-debug") << "SSB: New assert disequal " << a.getType() << " :: " << a << " : " << as;
+ Trace("sym-break-debug") << " != " << b << " : " << bs << std::endl;
+ }
+}
+
+void SubsortSymmetryBreaker::processFirstActive( TypeNode tn, int sid, int curr_card ){
+ SubSortInfo * ti = getSubSortInfo( tn, sid );
+ if( (int)ti->getNumDomainConstants()<curr_card ){
+ //static int checkCount = 0;
+ //checkCount++;
+ //if( checkCount%1000==0 ){
+ // std::cout << "Check count = " << checkCount << std::endl;
+ //}
+
+ Trace("sym-break-dc-debug") << "Check for domain constants " << tn << " : " << sid << ", curr_card = " << curr_card << ", ";
+ Trace("sym-break-dc-debug") << "#domain constants = " << ti->getNumDomainConstants() << std::endl;
+ Node fa = ti->getFirstActive(getEqualityEngine());
+ bool invalid = true;
+ while( invalid && !fa.isNull() && (int)ti->getNumDomainConstants()<curr_card ){
+ invalid = false;
+ unsigned deq = 0;
+ for( unsigned i=0; i<ti->getNumDomainConstants(); i++ ){
+ Node dc = ti->getDomainConstant( i );
+ if( areEqual( fa, dc ) ){
+ invalid = true;
+ break;
+ }else if( areDisequal( fa, dc ) ){
+ deq++;
+ }
+ }
+ if( deq==ti->getNumDomainConstants() ){
+ Trace("sym-break-dc") << "* Can infer domain constant #" << ti->getNumDomainConstants()+1;
+ Trace("sym-break-dc") << " : " << fa << " for " << tn << " : " << sid << std::endl;
+ //add to domain constants
+ ti->d_dom_constants.push_back( fa );
+ if( ti->d_node_to_id[fa]>ti->d_dc_nodes ){
+ Trace("sym-break-dc-debug") << "Swap nodes... " << ti->d_dc_nodes << " " << ti->d_node_to_id[fa] << " " << ti->d_nodes.size() << std::endl;
+ //swap
+ Node on = ti->d_nodes[ti->d_dc_nodes];
+ int id = ti->d_node_to_id[fa];
+
+ ti->d_nodes[ti->d_dc_nodes] = fa;
+ ti->d_nodes[id] = on;
+ ti->d_node_to_id[fa] = ti->d_dc_nodes;
+ ti->d_node_to_id[on] = id;
+ }
+ ti->d_dc_nodes++;
+ Trace("sym-break-dc-debug") << "Get max type info..." << std::endl;
+ TypeInfo * tti = getTypeInfo( tn );
+ Assert( tti->d_has_dom_const_sort.get() );
+ int msid = tti->d_max_dom_const_sort.get();
+ SubSortInfo * max_ti = getSubSortInfo( d_sid_to_type[msid], msid );
+ Trace("sym-break-dc-debug") << "Swap nodes..." << std::endl;
+ //now, check if we can apply symmetry breaking to another sort
+ if( ti->getNumDomainConstants()>max_ti->getNumDomainConstants() ){
+ Trace("sym-break-dc") << "Max domain constant subsort for " << tn << " becomes " << sid << std::endl;
+ tti->d_max_dom_const_sort.set( sid );
+ }else if( ti!=max_ti ){
+ //construct symmetry breaking lemma
+ //current domain constant must be disequal from all current ones
+ Trace("sym-break-dc") << "Get domain constant " << ti->getNumDomainConstants()-1;
+ Trace("sym-break-dc") << " from max_ti, " << max_ti->getNumDomainConstants() << std::endl;
+ //apply a symmetry breaking lemma
+ Node m = max_ti->getDomainConstant(ti->getNumDomainConstants()-1);
+ //if fa and m are disequal from all previous domain constants in the other sort
+ std::vector< Node > cc;
+ for( unsigned r=0; r<2; r++ ){
+ Node n = ((r==0)==(msid>sid)) ? fa : m;
+ Node on = ((r==0)==(msid>sid)) ? m : fa;
+ SubSortInfo * t = ((r==0)==(msid>sid)) ? max_ti : ti;
+ for( unsigned i=0; i<t->d_node_to_id[on]; i++ ){
+ cc.push_back( n.eqNode( t->d_nodes[i] ) );
+ }
+ }
+ //then, we can assume fa = m
+ cc.push_back( fa.eqNode( m ) );
+ Node lem = NodeManager::currentNM()->mkNode( kind::OR, cc );
+ lem = Rewriter::rewrite( lem );
+ if( std::find( d_lemmas.begin(), d_lemmas.end(), lem )==d_lemmas.end() ){
+ d_lemmas.push_back( lem );
+ Trace("sym-break-lemma") << "*** Symmetry break lemma for " << tn << " (" << sid << "==" << tti->d_max_dom_const_sort.get() << ") : ";
+ Trace("sym-break-lemma") << lem << std::endl;
+ d_pending_lemmas.push_back( lem );
+ }
+ }
+ invalid = true;
+ }
+ if( invalid ){
+ ti->d_first_active.set( ti->d_first_active + 1 );
+ fa = ti->getFirstActive(getEqualityEngine());
+ }
+ }
+ }
+}
+
+void SubsortSymmetryBreaker::printDebugSubSortInfo( const char * c, TypeNode tn, int sid ) {
+ Trace(c) << "SubSortInfo( " << tn << ", " << sid << " ) = " << std::endl;
+ Trace(c) << " Domain constants : ";
+ SubSortInfo * ti = getSubSortInfo( tn, sid );
+ for( NodeList::const_iterator it = ti->d_dom_constants.begin(); it != ti->d_dom_constants.end(); ++it ){
+ Node dc = *it;
+ Trace(c) << dc << " ";
+ }
+ Trace(c) << std::endl;
+ Trace(c) << " First active node : " << ti->getFirstActive(getEqualityEngine()) << std::endl;
+}
+
+bool SubsortSymmetryBreaker::check( Theory::Effort level ) {
+
+ Trace("sym-break-debug") << "SymBreak : check " << level << std::endl;
+ /*
+ while( d_fact_index.get()<d_fact_list.size() ){
+ Node f = d_fact_list[d_fact_index.get()];
+ d_fact_index.set( d_fact_index.get() + 1 );
+ if( f.getKind()==EQUAL ){
+ merge( f[0], f[1] );
+ }else if( f.getKind()==NOT && f[0].getKind()==EQUAL ){
+ assertDisequal( f[0][0], f[0][1] );
+ }else{
+ newEqClass( f );
+ }
+ }
+ */
+ Trace("sym-break-debug") << "SymBreak : update first actives" << std::endl;
+ for( std::map< TypeNode, std::vector< int > >::iterator it = d_sub_sorts.begin(); it != d_sub_sorts.end(); ++it ){
+ int card = getStrongSolver()->getCardinality( it->first );
+ for( unsigned i=0; i<it->second.size(); i++ ){
+ //check if the first active is disequal from all domain constants
+ processFirstActive( it->first, it->second[i], card );
+ }
+ }
+
+
+ Trace("sym-break-debug") << "SymBreak : finished check, now flush lemmas... (#lemmas = " << d_pending_lemmas.size() << ")" << std::endl;
+ //flush pending lemmas
+ if( !d_pending_lemmas.empty() ){
+ for( unsigned i=0; i<d_pending_lemmas.size(); i++ ){
+ getStrongSolver()->getOutputChannel().lemma( d_pending_lemmas[i] );
+ ++( getStrongSolver()->d_statistics.d_sym_break_lemmas );
+ }
+ d_pending_lemmas.clear();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+
+
+}
+
diff --git a/src/theory/quantifiers/symmetry_breaking.h b/src/theory/quantifiers/symmetry_breaking.h
new file mode 100755
index 000000000..05461d378
--- /dev/null
+++ b/src/theory/quantifiers/symmetry_breaking.h
@@ -0,0 +1,118 @@
+/********************* */
+/*! \file symmetry_breaking.h
+ ** \verbatim
+ ** Original author: ajreynol
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009-2012 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Pre-process step for first-order reasoning
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__QUANT_SYMMETRY_BREAKING_H
+#define __CVC4__QUANT_SYMMETRY_BREAKING_H
+
+#include "theory/theory.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include "expr/node.h"
+#include "expr/type_node.h"
+
+#include "util/sort_inference.h"
+#include "context/context.h"
+#include "context/context_mm.h"
+#include "context/cdchunk_list.h"
+
+namespace CVC4 {
+namespace theory {
+
+namespace uf {
+ class StrongSolverTheoryUF;
+}
+
+class SubsortSymmetryBreaker {
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+ typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap;
+ typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
+ //typedef context::CDChunkList<int> IntList;
+ typedef context::CDList<Node> NodeList;
+ typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap;
+private:
+ /** quantifiers engine */
+ QuantifiersEngine* d_qe;
+ eq::EqualityEngine * getEqualityEngine();
+ bool areDisequal( Node n1, Node n2 );
+ bool areEqual( Node n1, Node n2 );
+ Node getRepresentative( Node n );
+ uf::StrongSolverTheoryUF * getStrongSolver();
+ std::vector< Node > d_unit_lemmas;
+ Node d_true;
+ context::CDO< bool > d_conflict;
+public:
+ SubsortSymmetryBreaker( QuantifiersEngine* qe, context::Context* c );
+ ~SubsortSymmetryBreaker(){}
+
+private:
+ class TypeInfo {
+ public:
+ TypeInfo( context::Context* c );
+ context::CDO< int > d_max_dom_const_sort;
+ context::CDO< bool > d_has_dom_const_sort;
+ };
+ class SubSortInfo {
+ public:
+ SubSortInfo( context::Context* c );
+ //list of all nodes from this (sub)type
+ std::vector< Node > d_nodes;
+ //the current domain constants for this (sub)type
+ NodeList d_dom_constants;
+ //# nodes in d_nodes that have been domain constants, size of this distinct # of domain constants seen
+ unsigned d_dc_nodes;
+ //the node we are currently watching to become a domain constant
+ context::CDO< int > d_first_active;
+ //node to id
+ std::map< Node, unsigned > d_node_to_id;
+ Node getBaseConstant() { return d_nodes.empty() ? Node::null() : d_nodes[0]; }
+ bool hasDomainConstant( Node n );
+ unsigned getNumDomainConstants();
+ Node getDomainConstant( int i );
+ Node getFirstActive(eq::EqualityEngine * ee);
+ };
+ std::map< TypeNode, std::vector< int > > d_sub_sorts;
+ std::map< int, TypeNode > d_sid_to_type;
+ std::map< TypeNode, TypeInfo * > d_t_info;
+ std::map< int, SubSortInfo * > d_type_info;
+
+ TypeInfo * getTypeInfo( TypeNode tn );
+ SubSortInfo * getSubSortInfo( TypeNode tn, int sid );
+
+ void processFirstActive( TypeNode tn, int sid, int curr_card );
+private:
+ //void printDebugNodeInfo( const char * c, Node n );
+ void printDebugSubSortInfo( const char * c, TypeNode tn, int sid );
+ /** fact list */
+ std::vector< Node > d_pending_lemmas;
+ std::vector< Node > d_lemmas;
+public:
+ /** new node */
+ void newEqClass( Node n );
+ /** merge */
+ void merge( Node a, Node b );
+ /** assert disequal */
+ void assertDisequal( Node a, Node b );
+ /** check */
+ bool check( Theory::Effort level );
+};
+
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp
index 3153a3c64..e18a4e0dc 100644
--- a/src/theory/quantifiers/term_database.cpp
+++ b/src/theory/quantifiers/term_database.cpp
@@ -74,7 +74,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){
//if this is an atomic trigger, consider adding it
//Call the children?
if( inst::Trigger::isAtomicTrigger( n ) ){
- if( !n.hasAttribute(InstConstantAttribute()) ){
+ if( !TermDb::hasInstConstAttr(n) ){
Trace("term-db") << "register term in db " << n << std::endl;
//std::cout << "register trigger term " << n << std::endl;
Node op = n.getOperator();
@@ -117,7 +117,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){
}
}
}else{
- if( options::efficientEMatching() && !n.hasAttribute(InstConstantAttribute())){
+ if( options::efficientEMatching() && !TermDb::hasInstConstAttr(n)){
//Efficient e-matching must be notified
//The term in triggers are not important here
Debug("term-db") << "New in this branch term " << n << std::endl;
@@ -167,7 +167,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){
while( !eqc.isFinished() ){
Node en = (*eqc);
computeModelBasisArgAttribute( en );
- if( en.getKind()==APPLY_UF && !en.hasAttribute(InstConstantAttribute()) ){
+ if( en.getKind()==APPLY_UF && !TermDb::hasInstConstAttr(en) ){
if( !en.getAttribute(NoMatchAttribute()) ){
Node op = en.getOperator();
if( !d_pred_map_trie[i][op].addTerm( d_quantEngine, en ) ){
@@ -194,14 +194,20 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){
Node TermDb::getModelBasisTerm( TypeNode tn, int i ){
if( d_model_basis_term.find( tn )==d_model_basis_term.end() ){
Node mbt;
- if( options::fmfFreshDistConst() || d_type_map[ tn ].empty() ){
- std::stringstream ss;
- ss << Expr::setlanguage(options::outputLanguage());
- ss << "e_" << tn;
- mbt = NodeManager::currentNM()->mkSkolem( ss.str(), tn, "is a model basis term" );
- Trace("mkVar") << "ModelBasis:: Make variable " << mbt << " : " << tn << std::endl;
+ if( tn.isInteger() || tn.isReal() ){
+ mbt = NodeManager::currentNM()->mkConst( Rational( 0 ) );
+ }else if( !tn.isSort() ){
+ mbt = tn.mkGroundTerm();
}else{
- mbt = d_type_map[ tn ][ 0 ];
+ if( options::fmfFreshDistConst() || d_type_map[ tn ].empty() ){
+ std::stringstream ss;
+ ss << Expr::setlanguage(options::outputLanguage());
+ ss << "e_" << tn;
+ mbt = NodeManager::currentNM()->mkSkolem( ss.str(), tn, "is a model basis term" );
+ Trace("mkVar") << "ModelBasis:: Make variable " << mbt << " : " << tn << std::endl;
+ }else{
+ mbt = d_type_map[ tn ][ 0 ];
+ }
}
ModelBasisAttribute mba;
mbt.setAttribute(mba,true);
@@ -254,8 +260,7 @@ void TermDb::computeModelBasisArgAttribute( Node n ){
//determine if it has model basis attribute
for( int j=0; j<(int)n.getNumChildren(); j++ ){
if( n[j].getAttribute(ModelBasisAttribute()) ){
- val = 1;
- break;
+ val++;
}
}
ModelBasisArgAttribute mbaa;
@@ -276,33 +281,75 @@ void TermDb::makeInstantiationConstantsFor( Node f ){
//set the var number attribute
InstVarNumAttribute ivna;
ic.setAttribute(ivna,i);
+ InstConstantAttribute ica;
+ ic.setAttribute(ica,f);
+ //also set the no-match attribute
+ NoMatchAttribute nma;
+ ic.setAttribute(nma,true);
}
}
}
-void TermDb::setInstantiationConstantAttr( Node n, Node f ){
- if( !n.hasAttribute(InstConstantAttribute()) ){
- bool setAttr = false;
- if( n.getKind()==INST_CONSTANT ){
- setAttr = true;
- }else{
- for( int i=0; i<(int)n.getNumChildren(); i++ ){
- setInstantiationConstantAttr( n[i], f );
- if( n[i].hasAttribute(InstConstantAttribute()) ){
- setAttr = true;
- }
+
+Node TermDb::getInstConstAttr( Node n ) {
+ if (!n.hasAttribute(InstConstantAttribute()) ){
+ Node f;
+ for( int i=0; i<(int)n.getNumChildren(); i++ ){
+ f = getInstConstAttr(n[i]);
+ if( !f.isNull() ){
+ break;
}
}
- if( setAttr ){
- InstConstantAttribute ica;
- n.setAttribute(ica,f);
+ InstConstantAttribute ica;
+ n.setAttribute(ica,f);
+ if( !f.isNull() ){
//also set the no-match attribute
NoMatchAttribute nma;
n.setAttribute(nma,true);
}
}
+ return n.getAttribute(InstConstantAttribute());
+}
+
+bool TermDb::hasInstConstAttr( Node n ) {
+ return !getInstConstAttr(n).isNull();
+}
+
+bool TermDb::hasBoundVarAttr( Node n ) {
+ if( !n.getAttribute(HasBoundVarComputedAttribute()) ){
+ bool hasBv = false;
+ if( n.getKind()==BOUND_VARIABLE ){
+ hasBv = true;
+ }else{
+ for (unsigned i=0; i<n.getNumChildren(); i++) {
+ if( hasBoundVarAttr(n[i]) ){
+ hasBv = true;
+ break;
+ }
+ }
+ }
+ HasBoundVarAttribute hbva;
+ n.setAttribute(hbva, hasBv);
+ HasBoundVarComputedAttribute hbvca;
+ n.setAttribute(hbvca, true);
+ Trace("bva") << n << " has bva : " << n.getAttribute(HasBoundVarAttribute()) << std::endl;
+ }
+ return n.getAttribute(HasBoundVarAttribute());
+}
+
+void TermDb::getBoundVars( Node n, std::vector< Node >& bvs) {
+ if (n.getKind()==BOUND_VARIABLE ){
+ if ( std::find( bvs.begin(), bvs.end(), n)==bvs.end() ){
+ bvs.push_back( n );
+ }
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++) {
+ getBoundVars(n[i], bvs);
+ }
+ }
}
+
/** get the i^th instantiation constant of f */
Node TermDb::getInstantiationConstant( Node f, int i ) const {
std::map< Node, std::vector< Node > >::const_iterator it = d_inst_constants.find( f );
@@ -348,7 +395,6 @@ Node TermDb::getCounterexampleLiteral( Node f ){
//otherwise, ensure literal
Node ceLit = d_quantEngine->getValuation().ensureLiteral( ceBody.notNode() );
d_ce_lit[ f ] = ceLit;
- setInstantiationConstantAttr( ceLit, f );
}
return d_ce_lit[ f ];
}
@@ -362,7 +408,6 @@ Node TermDb::convertNodeToPattern( Node n, Node f, const std::vector<Node> & var
Node n2 = n.substitute( vars.begin(), vars.end(),
inst_constants.begin(),
inst_constants.end() );
- setInstantiationConstantAttr( n2, f );
return n2;
}
@@ -390,16 +435,19 @@ Node TermDb::getSkolemizedBody( Node f ){
Node TermDb::getFreeVariableForInstConstant( Node n ){
TypeNode tn = n.getType();
if( d_free_vars.find( tn )==d_free_vars.end() ){
- //if integer or real, make zero
- if( tn.isInteger() || tn.isReal() ){
- Rational z(0);
- d_free_vars[tn] = NodeManager::currentNM()->mkConst( z );
- }else{
- if( d_type_map[ tn ].empty() ){
- d_free_vars[tn] = NodeManager::currentNM()->mkSkolem( "freevar_$$", tn, "is a free variable created by termdb" );
- Trace("mkVar") << "FreeVar:: Make variable " << d_free_vars[tn] << " : " << tn << std::endl;
+ for( unsigned i=0; i<d_type_map[ tn ].size(); i++ ){
+ if( !quantifiers::TermDb::hasInstConstAttr(d_type_map[ tn ][ i ]) ){
+ d_free_vars[tn] = d_type_map[ tn ][ i ];
+ }
+ }
+ if( d_free_vars.find( tn )==d_free_vars.end() ){
+ //if integer or real, make zero
+ if( tn.isInteger() || tn.isReal() ){
+ Rational z(0);
+ d_free_vars[tn] = NodeManager::currentNM()->mkConst( z );
}else{
- d_free_vars[tn] = d_type_map[ tn ][ 0 ];
+ d_free_vars[tn] = NodeManager::currentNM()->mkSkolem( "freevar_$$", tn, "is a free variable created by termdb" );
+ Trace("mkVar") << "FreeVar:: Make variable " << d_free_vars[tn] << " : " << tn << std::endl;
}
}
}
diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h
index 231d0ee9e..1688479f3 100644
--- a/src/theory/quantifiers/term_database.h
+++ b/src/theory/quantifiers/term_database.h
@@ -59,6 +59,19 @@ typedef expr::Attribute<ModelBasisAttributeId, bool> ModelBasisAttribute;
struct ModelBasisArgAttributeId {};
typedef expr::Attribute<ModelBasisArgAttributeId, uint64_t> ModelBasisArgAttribute;
+struct HasBoundVarAttributeId {};
+typedef expr::Attribute<HasBoundVarAttributeId, bool> HasBoundVarAttribute;
+struct HasBoundVarComputedAttributeId {};
+typedef expr::Attribute<HasBoundVarComputedAttributeId, bool> HasBoundVarComputedAttribute;
+
+//for rewrite rules
+struct QRewriteRuleAttributeId {};
+typedef expr::Attribute<QRewriteRuleAttributeId, Node> QRewriteRuleAttribute;
+
+//for bounded integers
+struct BoundIntLitAttributeId {};
+typedef expr::Attribute<BoundIntLitAttributeId, uint64_t> BoundIntLitAttribute;
+
class QuantifiersEngine;
@@ -83,10 +96,15 @@ public:
};/* class TermArgTrie */
+namespace fmcheck {
+ class FullModelChecker;
+}
+
class TermDb {
friend class ::CVC4::theory::QuantifiersEngine;
friend class ::CVC4::theory::inst::Trigger;
friend class ::CVC4::theory::rrinst::Trigger;
+ friend class ::CVC4::theory::quantifiers::fmcheck::FullModelChecker;
private:
/** reference to the quantifiers engine */
QuantifiersEngine* d_quantEngine;
@@ -181,9 +199,15 @@ public:
Node convertNodeToPattern( Node n, Node f,
const std::vector<Node> & vars,
const std::vector<Node> & nvars);
- /** set instantiation constant attr */
- void setInstantiationConstantAttr( Node n, Node f );
+ static Node getInstConstAttr( Node n );
+ static bool hasInstConstAttr( Node n );
+//for bound variables
+public:
+ //does n have bound variables?
+ static bool hasBoundVarAttr( Node n );
+ //get bound variables in n
+ static void getBoundVars( Node n, std::vector< Node >& bvs);
//for skolem
private:
/** map from universal quantifiers to the list of skolem constants */
diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp
index 9843cd09e..066282c2c 100644
--- a/src/theory/quantifiers/theory_quantifiers.cpp
+++ b/src/theory/quantifiers/theory_quantifiers.cpp
@@ -65,7 +65,7 @@ void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) {
void TheoryQuantifiers::preRegisterTerm(TNode n) {
Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl;
- if( n.getKind()==FORALL && !n.hasAttribute(InstConstantAttribute()) ){
+ if( n.getKind()==FORALL && !TermDb::hasInstConstAttr(n) ){
getQuantifiersEngine()->registerQuantifier( n );
}
}
@@ -149,7 +149,7 @@ Node TheoryQuantifiers::getNextDecisionRequest(){
void TheoryQuantifiers::assertUniversal( Node n ){
Assert( n.getKind()==FORALL );
- if( !n.hasAttribute(InstConstantAttribute()) ){
+ if( options::recurseCbqi() || !TermDb::hasInstConstAttr(n) ){
getQuantifiersEngine()->registerQuantifier( n );
getQuantifiersEngine()->assertNode( n );
}
@@ -157,13 +157,13 @@ void TheoryQuantifiers::assertUniversal( Node n ){
void TheoryQuantifiers::assertExistential( Node n ){
Assert( n.getKind()== NOT && n[0].getKind()==FORALL );
- if( !n[0].hasAttribute(InstConstantAttribute()) ){
+ if( options::recurseCbqi() || !TermDb::hasInstConstAttr(n[0]) ){
if( d_skolemized.find( n )==d_skolemized.end() ){
Node body = getQuantifiersEngine()->getTermDatabase()->getSkolemizedBody( n[0] );
NodeBuilder<> nb(kind::OR);
nb << n[0] << body.notNode();
Node lem = nb;
- Debug("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl;
+ Trace("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl;
d_out->lemma( lem );
d_skolemized[n] = true;
}
diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp
index cab94fb5c..39063942d 100644
--- a/src/theory/quantifiers/trigger.cpp
+++ b/src/theory/quantifiers/trigger.cpp
@@ -28,8 +28,6 @@ using namespace CVC4::context;
using namespace CVC4::theory;
using namespace CVC4::theory::inst;
-//#define NESTED_PATTERN_SELECTION
-
/** trigger class constructor */
Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool smartTriggers ) :
d_quantEngine( qe ), d_f( f ){
@@ -46,7 +44,7 @@ d_quantEngine( qe ), d_f( f ){
d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] );
}else{
d_mg = InstMatchGenerator::mkInstMatchGenerator( d_nodes[0], qe );
- d_mg->setActiveAdd();
+ d_mg->setActiveAdd(true);
}
}else{
d_mg = new InstMatchGeneratorMulti( f, d_nodes, qe, matchOption );
@@ -55,7 +53,7 @@ d_quantEngine( qe ), d_f( f ){
}
}else{
d_mg = InstMatchGenerator::mkInstMatchGenerator( d_nodes, qe );
- d_mg->setActiveAdd();
+ d_mg->setActiveAdd(true);
}
if( d_nodes.size()==1 ){
if( isSimpleTrigger( d_nodes[0] ) ){
@@ -75,6 +73,7 @@ d_quantEngine( qe ), d_f( f ){
qe->getTermDatabase()->registerTrigger( this, d_nodes[i].getOperator() );
}
}
+ Trace("trigger-debug") << "Finished making trigger." << std::endl;
}
void Trigger::resetInstantiationRound(){
@@ -126,7 +125,7 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >&
qe->getTermDatabase()->computeVarContains( temp[i] );
for( int j=0; j<(int)qe->getTermDatabase()->d_var_contains[ temp[i] ].size(); j++ ){
Node v = qe->getTermDatabase()->d_var_contains[ temp[i] ][j];
- if( v.getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(v)==f ){
if( vars.find( v )==vars.end() ){
varCount++;
vars[ v ] = true;
@@ -146,6 +145,12 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >&
}
}
if( varCount<f[0].getNumChildren() ){
+ Trace("trigger-debug") << "Don't consider trigger since it does not contain all variables in " << f << std::endl;
+ for( unsigned i=0; i<nodes.size(); i++) {
+ Trace("trigger-debug") << nodes[i] << " ";
+ }
+ Trace("trigger-debug") << std::endl;
+
//do not generate multi-trigger if it does not contain all variables
return NULL;
}else{
@@ -225,7 +230,7 @@ bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){
}
bool Trigger::isUsable( Node n, Node f ){
- if( n.getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(n)==f ){
if( isAtomicTrigger( n ) ){
for( int i=0; i<(int)n.getNumChildren(); i++ ){
if( !isUsable( n[i], f ) ){
@@ -249,11 +254,71 @@ bool Trigger::isUsable( Node n, Node f ){
}
}
+bool nodeContainsVar( Node n, Node v ){
+ if( n==v) {
+ return true;
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++) {
+ if( nodeContainsVar(n[i], v) ){
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+Node Trigger::getIsUsableTrigger( Node n, Node f, bool pol, bool hasPol ) {
+ if( options::relationalTriggers() ){
+ if( n.getKind()==EQUAL || n.getKind()==IFF || n.getKind()==GEQ ){
+ Node rtr;
+ for( unsigned i=0; i<2; i++) {
+ unsigned j = (i==0) ? 1 : 0;
+ if( n[j].getKind()==INST_CONSTANT && isUsableTrigger(n[i], f) && !nodeContainsVar( n[i], n[j] ) ) {
+ rtr = n;
+ break;
+ }
+ }
+ if( n[0].getType().isInteger() ){
+ //try to rearrange?
+ std::map< Node, Node > m;
+ if (QuantArith::getMonomialSumLit(n, m) ){
+ for( std::map< Node, Node >::iterator it = m.begin(); it!=m.end(); ++it ){
+ if( !it->first.isNull() && it->first.getKind()==INST_CONSTANT ){
+ Node veq;
+ if( QuantArith::isolate( it->first, m, veq, n.getKind() ) ){
+ int vti = veq[0]==it->first ? 1 : 0;
+ if( isUsableTrigger(veq[vti], f) && !nodeContainsVar( veq[vti], veq[vti==0 ? 1 : 0]) ){
+ rtr = veq;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !rtr.isNull() ){
+ Trace("relational-trigger") << "Relational trigger : " << std::endl;
+ Trace("relational-trigger") << " " << rtr << " (from " << n << ")" << std::endl;
+ Trace("relational-trigger") << " in quantifier " << f << std::endl;
+ if( hasPol ){
+ Trace("relational-trigger") << " polarity : " << pol << std::endl;
+ }
+ Node rtr2 = (hasPol && pol) ? rtr.negate() : rtr;
+ return rtr2;
+ }
+ }
+ }
+ bool usable = quantifiers::TermDb::getInstConstAttr(n)==f && isAtomicTrigger( n ) && isUsable( n, f );
+ Trace("usable") << n << " usable : " << (quantifiers::TermDb::getInstConstAttr(n)==f) << " " << isAtomicTrigger( n ) << " " << isUsable( n, f ) << std::endl;
+ if( usable ){
+ return n;
+ }else{
+ return Node::null();
+ }
+}
+
bool Trigger::isUsableTrigger( Node n, Node f ){
- //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF;
- bool usable = n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f );
- Trace("usable") << n << " usable : " << usable << std::endl;
- return usable;
+ Node nu = getIsUsableTrigger(n,f);
+ return !nu.isNull();
}
bool Trigger::isAtomicTrigger( Node n ){
@@ -263,7 +328,7 @@ bool Trigger::isAtomicTrigger( Node n ){
bool Trigger::isSimpleTrigger( Node n ){
if( isAtomicTrigger( n ) ){
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){
+ if( n[i].getKind()!=INST_CONSTANT && quantifiers::TermDb::hasInstConstAttr(n[i]) ){
return false;
}
}
@@ -274,55 +339,51 @@ bool Trigger::isSimpleTrigger( Node n ){
}
-bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ){
+bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt, bool pol, bool hasPol ){
if( patMap.find( n )==patMap.end() ){
patMap[ n ] = false;
+ bool newHasPol = (n.getKind()==IFF || n.getKind()==XOR) ? false : hasPol;
+ bool newPol = n.getKind()==NOT ? !pol : pol;
if( tstrt==TS_MIN_TRIGGER ){
if( n.getKind()==FORALL ){
-#ifdef NESTED_PATTERN_SELECTION
- //return collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt );
- return collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt );
-#else
return false;
-#endif
}else{
bool retVal = false;
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){
+ bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol;
+ bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol;
+ if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){
retVal = true;
}
}
if( retVal ){
return true;
- }else if( isUsableTrigger( n, f ) ){
- patMap[ n ] = true;
- return true;
}else{
- return false;
+ Node nu = getIsUsableTrigger( n, f, pol, hasPol );
+ if( !nu.isNull() ){
+ patMap[ nu ] = true;
+ return true;
+ }else{
+ return false;
+ }
}
}
}else{
bool retVal = false;
- if( isUsableTrigger( n, f ) ){
- patMap[ n ] = true;
+ Node nu = getIsUsableTrigger( n, f, pol, hasPol );
+ if( !nu.isNull() ){
+ patMap[ nu ] = true;
if( tstrt==TS_MAX_TRIGGER ){
return true;
}else{
retVal = true;
}
}
- if( n.getKind()==FORALL ){
-#ifdef NESTED_PATTERN_SELECTION
- //if( collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt ) ){
- // retVal = true;
- //}
- if( collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt ) ){
- retVal = true;
- }
-#endif
- }else{
+ if( n.getKind()!=FORALL ){
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){
+ bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol;
+ bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol;
+ if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){
retVal = true;
}
}
@@ -367,7 +428,7 @@ void Trigger::collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vecto
}
}
}
- collectPatTerms2( qe, f, n, patMap, tstrt );
+ collectPatTerms2( qe, f, n, patMap, tstrt, true, true );
for( std::map< Node, bool >::iterator it = patMap.begin(); it != patMap.end(); ++it ){
if( it->second ){
patTerms.push_back( it->first );
@@ -380,9 +441,9 @@ bool Trigger::isArithmeticTrigger( Node f, Node n, std::map< Node, Node >& coeff
Assert( coeffs.empty() );
NodeBuilder<> t(kind::PLUS);
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( n[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(n[i]) ){
if( n[i].getKind()==INST_CONSTANT ){
- if( n[i].getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(n[i])==f ){
coeffs[ n[i] ] = Node::null();
}else{
coeffs.clear();
@@ -405,13 +466,13 @@ bool Trigger::isArithmeticTrigger( Node f, Node n, std::map< Node, Node >& coeff
}
return true;
}else if( n.getKind()==MULT ){
- if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){
- if( !n[1].hasAttribute(InstConstantAttribute()) ){
+ if( n[0].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[0])==f ){
+ if( !quantifiers::TermDb::hasInstConstAttr(n[1]) ){
coeffs[ n[0] ] = n[1];
return true;
}
- }else if( n[1].getKind()==INST_CONSTANT && n[1].getAttribute(InstConstantAttribute())==f ){
- if( !n[0].hasAttribute(InstConstantAttribute()) ){
+ }else if( n[1].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[1])==f ){
+ if( !quantifiers::TermDb::hasInstConstAttr(n[0]) ){
coeffs[ n[1] ] = n[0];
return true;
}
diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h
index ca9124751..28fb2acda 100644
--- a/src/theory/quantifiers/trigger.h
+++ b/src/theory/quantifiers/trigger.h
@@ -92,8 +92,9 @@ public:
private:
/** is subterm of trigger usable */
static bool isUsable( Node n, Node f );
+ static Node getIsUsableTrigger( Node n, Node f, bool pol = true, bool hasPol = false );
/** collect all APPLY_UF pattern terms for f in n */
- static bool collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt );
+ static bool collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt, bool pol, bool hasPol );
public:
//different strategies for choosing trigger terms
enum {
diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp
index 0bb0f1f79..0fe50aad6 100644..100755
--- a/src/theory/quantifiers_engine.cpp
+++ b/src/theory/quantifiers_engine.cpp
@@ -27,6 +27,9 @@
#include "theory/quantifiers/trigger.h"
#include "theory/rewriterules/efficient_e_matching.h"
#include "theory/rewriterules/rr_trigger.h"
+#include "theory/quantifiers/bounded_integers.h"
+#include "theory/quantifiers/rewrite_engine.h"
+#include "theory/uf/options.h"
using namespace std;
using namespace CVC4;
@@ -47,7 +50,11 @@ d_lemmas_produced_c(u){
d_hasAddedLemma = false;
//the model object
- d_model = new quantifiers::FirstOrderModel( c, "FirstOrderModel" );
+ if( options::fmfFullModelCheck() ){
+ d_model = new quantifiers::fmcheck::FirstOrderModelFmc( this, c, "FirstOrderModelFmc" );
+ }else{
+ d_model = new quantifiers::FirstOrderModelIG( c, "FirstOrderModelIG" );
+ }
//add quantifiers modules
if( !options::finiteModelFind() || options::fmfInstEngine() ){
@@ -57,11 +64,24 @@ d_lemmas_produced_c(u){
}else{
d_inst_engine = NULL;
}
- if( options::finiteModelFind() ){
+ if( options::finiteModelFind() ){
d_model_engine = new quantifiers::ModelEngine( c, this );
d_modules.push_back( d_model_engine );
+ if( options::fmfBoundInt() ){
+ d_bint = new quantifiers::BoundedIntegers( c, this );
+ d_modules.push_back( d_bint );
+ }else{
+ d_bint = NULL;
+ }
}else{
d_model_engine = NULL;
+ d_bint = NULL;
+ }
+ if( options::rewriteRulesAsAxioms() ){
+ d_rr_engine = new quantifiers::RewriteEngine( c, this );
+ d_modules.push_back(d_rr_engine);
+ }else{
+ d_rr_engine = NULL;
}
//options
@@ -251,7 +271,6 @@ void QuantifiersEngine::computeTermVector( Node f, InstMatch& m, std::vector< No
bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ){
Assert( f.getKind()==FORALL );
- Assert( !f.hasAttribute(InstConstantAttribute()) );
Assert( vars.size()==terms.size() );
Node body = getInstantiation( f, vars, terms );
//make the lemma
@@ -266,7 +285,7 @@ bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& vars, std
Trace("inst") << "*** Instantiate " << f << " with " << std::endl;
uint64_t maxInstLevel = 0;
for( int i=0; i<(int)terms.size(); i++ ){
- if( terms[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(terms[i]) ){
Debug("inst")<< "***& Bad Instantiate " << f << " with " << std::endl;
for( int i=0; i<(int)terms.size(); i++ ){
Debug("inst") << " " << terms[i] << std::endl;
@@ -432,8 +451,6 @@ bool QuantifiersEngine::addSplit( Node n, bool reqPhase, bool reqPhasePol ){
}
bool QuantifiersEngine::addSplitEquality( Node n1, Node n2, bool reqPhase, bool reqPhasePol ){
- //Assert( !n1.hasAttribute(InstConstantAttribute()) );
- //Assert( !n2.hasAttribute(InstConstantAttribute()) );
//Assert( !areEqual( n1, n2 ) );
//Assert( !areDisequal( n1, n2 ) );
Kind knd = n1.getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL;
@@ -463,7 +480,6 @@ void QuantifiersEngine::getPhaseReqTerms( Node f, std::vector< Node >& nodes ){
if( d_phase_reqs[f]->isPhaseReq( nodes[i] ) ){
bool preq = d_phase_reqs[f]->getPhaseReq( nodes[i] );
nodes[i] = NodeManager::currentNM()->mkNode( IFF, nodes[i], NodeManager::currentNM()->mkConst<bool>(preq) );
- d_term_db->setInstantiationConstantAttr( nodes[i], f );
nodeChanged = true;
}
//else if( qe->isPhaseReqEquality( f, trNodes[i] ) ){
@@ -617,35 +633,48 @@ Node EqualityQueryQuantifiersEngine::getInternalRepresentative( Node a, Node f,
}else{
int sortId = 0;
if( optInternalRepSortInference() ){
+ //if( options::ufssSymBreak() ){
sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( f, f[0][index] );
}
if( d_int_rep[sortId].find( r )==d_int_rep[sortId].end() ){
- std::vector< Node > eqc;
- getEquivalenceClass( r, eqc );
//find best selection for representative
Node r_best;
- int r_best_score = -1;
- for( size_t i=0; i<eqc.size(); i++ ){
- int score = getRepScore( eqc[i], f, index );
- if( optInternalRepSortInference() ){
- int e_sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( eqc[i]);
- if( score>=0 && e_sortId!=sortId ){
- score += 100;
- }
+ if( options::fmfRelevantDomain() ){
+ Trace("internal-rep-debug") << "Consult relevant domain to mkRep " << r << std::endl;
+ r_best = d_qe->getModelEngine()->getRelevantDomain()->getRelevantTerm( f, index, r );
+ Trace("internal-rep-debug") << "Returned " << r_best << " " << r << std::endl;
+ }else{
+ std::vector< Node > eqc;
+ getEquivalenceClass( r, eqc );
+ int r_best_score = -1;
+ for( size_t i=0; i<eqc.size(); i++ ){
+ int score = getRepScore( eqc[i], f, index );
+ if( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(eqc[i]) ){
+ if( optInternalRepSortInference() ){
+ int e_sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( eqc[i]);
+ if( score>=0 && e_sortId!=sortId ){
+ score += 100;
+ }
+ }
+ //score prefers earliest use of this term as a representative
+ if( r_best.isNull() || ( score>=0 && ( r_best_score<0 || score<r_best_score ) ) ){
+ r_best = eqc[i];
+ r_best_score = score;
+ }
+ }
}
- //score prefers earliest use of this term as a representative
- if( r_best.isNull() || ( score>=0 && ( r_best_score<0 || score<r_best_score ) ) ){
- r_best = eqc[i];
- r_best_score = score;
+ if( r_best.isNull() ){
+ Node ic = d_qe->getTermDatabase()->getInstantiationConstant( f, index );
+ r_best = d_qe->getTermDatabase()->getFreeVariableForInstConstant( ic );
+ }
+ //now, make sure that no other member of the class is an instance
+ if( !optInternalRepSortInference() ){
+ r_best = getInstance( r_best, eqc );
+ }
+ //store that this representative was chosen at this point
+ if( d_rep_score.find( r_best )==d_rep_score.end() ){
+ d_rep_score[ r_best ] = d_reset_count;
}
- }
- //now, make sure that no other member of the class is an instance
- if( !optInternalRepSortInference() ){
- r_best = getInstance( r_best, eqc );
- }
- //store that this representative was chosen at this point
- if( d_rep_score.find( r_best )==d_rep_score.end() ){
- d_rep_score[ r_best ] = d_reset_count;
}
d_int_rep[sortId][r] = r_best;
if( r_best!=a ){
@@ -723,4 +752,4 @@ int EqualityQueryQuantifiersEngine::getRepScore( Node n, Node f, int index ){
bool EqualityQueryQuantifiersEngine::optInternalRepSortInference() {
return false; //shown to be not effective
-} \ No newline at end of file
+}
diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h
index bfa19bb98..b075f7be8 100644
--- a/src/theory/quantifiers_engine.h
+++ b/src/theory/quantifiers_engine.h
@@ -56,14 +56,17 @@ public:
virtual void assertNode( Node n ) = 0;
virtual void propagate( Theory::Effort level ){}
virtual Node getNextDecisionRequest() { return TNode::null(); }
- virtual Node explain(TNode n) = 0;
+ virtual Node explain(TNode n) { return TNode::null(); }
};/* class QuantifiersModule */
namespace quantifiers {
- class InstantiationEngine;
- class ModelEngine;
class TermDb;
class FirstOrderModel;
+ //modules
+ class InstantiationEngine;
+ class ModelEngine;
+ class BoundedIntegers;
+ class RewriteEngine;
}/* CVC4::theory::quantifiers */
namespace inst {
@@ -80,6 +83,7 @@ class EqualityQueryQuantifiersEngine;
class QuantifiersEngine {
friend class quantifiers::InstantiationEngine;
friend class quantifiers::ModelEngine;
+ friend class quantifiers::RewriteEngine;
friend class inst::InstMatch;
private:
typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap;
@@ -87,10 +91,6 @@ private:
TheoryEngine* d_te;
/** vector of modules for quantifiers */
std::vector< QuantifiersModule* > d_modules;
- /** instantiation engine */
- quantifiers::InstantiationEngine* d_inst_engine;
- /** model engine */
- quantifiers::ModelEngine* d_model_engine;
/** equality query class */
EqualityQueryQuantifiersEngine* d_eq_query;
/** for computing relevance of quantifiers */
@@ -99,6 +99,14 @@ private:
std::map< Node, QuantPhaseReq* > d_phase_reqs;
/** efficient e-matcher */
EfficientEMatcher* d_eem;
+ /** instantiation engine */
+ quantifiers::InstantiationEngine* d_inst_engine;
+ /** model engine */
+ quantifiers::ModelEngine* d_model_engine;
+ /** bounded integers utility */
+ quantifiers::BoundedIntegers * d_bint;
+ /** rewrite rules utility */
+ quantifiers::RewriteEngine * d_rr_engine;
private:
/** list of all quantifiers seen */
std::vector< Node > d_quants;
@@ -155,6 +163,8 @@ public:
void getPhaseReqTerms( Node f, std::vector< Node >& nodes );
/** get efficient e-matching utility */
EfficientEMatcher* getEfficientEMatcher() { return d_eem; }
+ /** get bounded integers utility */
+ quantifiers::BoundedIntegers * getBoundedIntegers() { return d_bint; }
public:
/** initialize */
void finishInit();
diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp
index f6da32bbf..800e007f7 100644
--- a/src/theory/rep_set.cpp
+++ b/src/theory/rep_set.cpp
@@ -14,6 +14,7 @@
#include "theory/rep_set.h"
#include "theory/type_enumerator.h"
+#include "theory/quantifiers/bounded_integers.h"
using namespace std;
using namespace CVC4;
@@ -94,26 +95,38 @@ void RepSet::toStream(std::ostream& out){
}
-RepSetIterator::RepSetIterator( RepSet* rs ) : d_rep_set( rs ){
+RepSetIterator::RepSetIterator( QuantifiersEngine * qe, RepSet* rs ) : d_qe(qe), d_rep_set( rs ){
d_incomplete = false;
+}
+int RepSetIterator::domainSize( int i ) {
+ Assert(i>=0);
+ if( d_enum_type[i]==ENUM_DOMAIN_ELEMENTS ){
+ return d_domain[i].size();
+ }else{
+ return d_domain[i][0];
+ }
}
bool RepSetIterator::setQuantifier( Node f ){
+ Trace("rsi") << "Make rsi for " << f << std::endl;
Assert( d_types.empty() );
//store indicies
for( size_t i=0; i<f[0].getNumChildren(); i++ ){
d_types.push_back( f[0][i].getType() );
}
+ d_owner = f;
return initialize();
}
bool RepSetIterator::setFunctionDomain( Node op ){
+ Trace("rsi") << "Make rsi for " << op << std::endl;
Assert( d_types.empty() );
TypeNode tn = op.getType();
for( size_t i=0; i<tn.getNumChildren()-1; i++ ){
d_types.push_back( tn[i] );
}
+ d_owner = op;
return initialize();
}
@@ -132,22 +145,80 @@ bool RepSetIterator::initialize(){
Trace("mkVar") << "RepSetIterator:: Make variable " << var << " : " << tn << std::endl;
d_rep_set->add( var );
}
- }else if( tn.isInteger() || tn.isReal() ){
- Trace("fmf-incomplete") << "Incomplete because of infinite type " << tn << std::endl;
- d_incomplete = true;
- //enumerate if the sort is reasonably small, the upper bound of 128 is chosen arbitrarily for now
- }else if( tn.getCardinality().isFinite() && tn.getCardinality().getFiniteCardinality().toUnsignedInt()<=128 ){
+ }else if( tn.isInteger() ){
+ bool inc = false;
+ //check if it is bound
+ if( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() ){
+ if( d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][i] ) ){
+ Trace("bound-int-rsi") << "Rep set iterator: variable #" << i << " is bounded integer." << std::endl;
+ d_enum_type.push_back( ENUM_RANGE );
+ }else{
+ inc = true;
+ }
+ }else{
+ inc = true;
+ }
+ if( inc ){
+ //check if it is otherwise bound
+ if( d_bounds[0].find(i)!=d_bounds[0].end() && d_bounds[1].find(i)!=d_bounds[1].end() ){
+ Trace("bound-int-rsi") << "Rep set iterator: variable #" << i << " is bounded." << std::endl;
+ d_enum_type.push_back( ENUM_RANGE );
+ }else{
+ Trace("fmf-incomplete") << "Incomplete because of integer quantification of " << d_owner[0][i] << "." << std::endl;
+ d_incomplete = true;
+ }
+ }
+ //enumerate if the sort is reasonably small, the upper bound of 1000 is chosen arbitrarily for now
+ }else if( tn.getCardinality().isFinite() && tn.getCardinality().getFiniteCardinality().toUnsignedInt()<=1000 ){
d_rep_set->complete( tn );
}else{
- Trace("fmf-incomplete") << "Incomplete because of unknown type " << tn << std::endl;
+ Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl;
d_incomplete = true;
}
- if( d_rep_set->hasType( tn ) ){
- for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){
- d_domain[i].push_back( j );
+ if( d_enum_type.size()<=i ){
+ d_enum_type.push_back( ENUM_DOMAIN_ELEMENTS );
+ if( d_rep_set->hasType( tn ) ){
+ for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){
+ d_domain[i].push_back( j );
+ }
+ }else{
+ return false;
}
- }else{
- return false;
+ }
+ }
+ //must set a variable index order based on bounded integers
+ if (d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers()) {
+ Trace("bound-int-rsi") << "Calculating variable order..." << std::endl;
+ std::vector< int > varOrder;
+ for( unsigned i=0; i<d_qe->getBoundedIntegers()->getNumBoundVars(d_owner); i++ ){
+ varOrder.push_back(d_qe->getBoundedIntegers()->getBoundVarNum(d_owner,i));
+ }
+ for( unsigned i=0; i<d_owner[0].getNumChildren(); i++) {
+ if( !d_qe->getBoundedIntegers()->isBoundVar(d_owner, d_owner[0][i])) {
+ varOrder.push_back(i);
+ }
+ }
+ Trace("bound-int-rsi") << "Variable order : ";
+ for( unsigned i=0; i<varOrder.size(); i++) {
+ Trace("bound-int-rsi") << varOrder[i] << " ";
+ }
+ Trace("bound-int-rsi") << std::endl;
+ std::vector< int > indexOrder;
+ indexOrder.resize(varOrder.size());
+ for( unsigned i=0; i<varOrder.size(); i++){
+ indexOrder[varOrder[i]] = i;
+ }
+ Trace("bound-int-rsi") << "Will use index order : ";
+ for( unsigned i=0; i<indexOrder.size(); i++) {
+ Trace("bound-int-rsi") << indexOrder[i] << " ";
+ }
+ Trace("bound-int-rsi") << std::endl;
+ setIndexOrder(indexOrder);
+ }
+ //now reset the indices
+ for (unsigned i=0; i<d_index.size(); i++) {
+ if (!resetIndex(i, true)){
+ break;
}
}
return true;
@@ -161,7 +232,7 @@ void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){
d_var_order[d_index_order[i]] = i;
}
}
-
+/*
void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){
d_domain.clear();
d_domain.insert( d_domain.begin(), domain.begin(), domain.end() );
@@ -172,29 +243,104 @@ void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){
}
}
}
+*/
+bool RepSetIterator::resetIndex( int i, bool initial ) {
+ d_index[i] = 0;
+ int ii = d_index_order[i];
+ Trace("bound-int-rsi") << "Reset " << i << " " << ii << " " << initial << std::endl;
+ //determine the current range
+ if( d_enum_type[ii]==ENUM_RANGE ){
+ if( initial || ( d_qe->getBoundedIntegers() && !d_qe->getBoundedIntegers()->isGroundRange( d_owner, d_owner[0][ii] ) ) ){
+ Trace("bound-int-rsi") << "Getting range of " << d_owner[0][ii] << std::endl;
+ Node l, u;
+ if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){
+ d_qe->getBoundedIntegers()->getBoundValues( d_owner, d_owner[0][ii], this, l, u );
+ }
+ for( unsigned b=0; b<2; b++ ){
+ if( d_bounds[b].find(ii)!=d_bounds[b].end() ){
+ Trace("bound-int-rsi") << "May further limit bound(" << b << ") based on " << d_bounds[b][ii] << std::endl;
+ if( b==0 && (l.isNull() || d_bounds[b][ii].getConst<Rational>() > l.getConst<Rational>()) ){
+ l = d_bounds[b][ii];
+ }else if( b==1 && (u.isNull() || d_bounds[b][ii].getConst<Rational>() <= u.getConst<Rational>()) ){
+ u = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][ii],
+ NodeManager::currentNM()->mkConst( Rational(1) ) );
+ u = Rewriter::rewrite( u );
+ }
+ }
+ }
+
+ if( l.isNull() || u.isNull() ){
+ //failed, abort the iterator
+ d_index.clear();
+ return false;
+ }else{
+ Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][ii] << " to " << l << "..." << u << std::endl;
+ Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) );
+ Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) );
+ d_domain[ii].clear();
+ Node tl = l;
+ Node tu = u;
+ if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){
+ d_qe->getBoundedIntegers()->getBounds( d_owner, d_owner[0][ii], this, tl, tu );
+ }
+ d_lower_bounds[ii] = tl;
+ if( ra==NodeManager::currentNM()->mkConst(true) ){
+ long rr = range.getConst<Rational>().getNumerator().getLong()+1;
+ Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl;
+ d_domain[ii].push_back( (int)rr );
+ }else{
+ Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << d_owner[0][ii] << "." << std::endl;
+ d_incomplete = true;
+ d_domain[ii].push_back( 0 );
+ }
+ }
+ }else{
+ Trace("bound-int-rsi") << d_owner[0][ii] << " has ground range, skip." << std::endl;
+ }
+ }
+ return true;
+}
-void RepSetIterator::increment2( int counter ){
+int RepSetIterator::increment2( int counter ){
Assert( !isFinished() );
#ifdef DISABLE_EVAL_SKIP_MULTIPLE
counter = (int)d_index.size()-1;
#endif
//increment d_index
- while( counter>=0 && d_index[counter]==(int)(d_domain[counter].size()-1) ){
+ if( counter>=0){
+ Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl;
+ }
+ while( counter>=0 && d_index[counter]>=(int)(domainSize(counter)-1) ){
counter--;
+ if( counter>=0){
+ Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl;
+ }
}
if( counter==-1 ){
d_index.clear();
}else{
+ d_index[counter]++;
+ bool emptyDomain = false;
for( int i=(int)d_index.size()-1; i>counter; i-- ){
- d_index[i] = 0;
+ if (!resetIndex(i)){
+ break;
+ }else if( domainSize(i)<=0 ){
+ emptyDomain = true;
+ }
+ }
+ if( emptyDomain ){
+ Trace("rsi-debug") << "This is an empty domain, increment again." << std::endl;
+ return increment();
}
- d_index[counter]++;
}
+ return counter;
}
-void RepSetIterator::increment(){
+int RepSetIterator::increment(){
if( !isFinished() ){
- increment2( (int)d_index.size()-1 );
+ return increment2( (int)d_index.size()-1 );
+ }else{
+ return -1;
}
}
@@ -203,10 +349,18 @@ bool RepSetIterator::isFinished(){
}
Node RepSetIterator::getTerm( int i ){
- TypeNode tn = d_types[d_index_order[i]];
- Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() );
int index = d_index_order[i];
- return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]];
+ if( d_enum_type[index]==ENUM_DOMAIN_ELEMENTS ){
+ TypeNode tn = d_types[index];
+ Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() );
+ return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]];
+ }else{
+ Trace("rsi-debug") << index << " " << d_lower_bounds[index] << " " << d_index[index] << std::endl;
+ Node t = NodeManager::currentNM()->mkNode(PLUS, d_lower_bounds[index],
+ NodeManager::currentNM()->mkConst( Rational(d_index[index]) ) );
+ t = Rewriter::rewrite( t );
+ return t;
+ }
}
void RepSetIterator::debugPrint( const char* c ){
diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h
index 24fa7473e..a619915ee 100644
--- a/src/theory/rep_set.h
+++ b/src/theory/rep_set.h
@@ -23,6 +23,8 @@
namespace CVC4 {
namespace theory {
+class QuantifiersEngine;
+
/** this class stores a representative set */
class RepSet {
public:
@@ -52,11 +54,27 @@ typedef std::vector< int > RepDomain;
/** this class iterates over a RepSet */
class RepSetIterator {
+public:
+ enum {
+ ENUM_DOMAIN_ELEMENTS,
+ ENUM_RANGE,
+ };
private:
+ QuantifiersEngine * d_qe;
//initialize function
bool initialize();
+ //for enum ranges
+ std::map< int, Node > d_lower_bounds;
+ //domain size
+ int domainSize( int i );
+ //node this is rep set iterator is for
+ Node d_owner;
+ //reset index
+ bool resetIndex( int i, bool initial = false );
+ /** set index order */
+ void setIndexOrder( std::vector< int >& indexOrder );
public:
- RepSetIterator( RepSet* rs );
+ RepSetIterator( QuantifiersEngine * qe, RepSet* rs );
~RepSetIterator(){}
//set that this iterator will be iterating over instantiations for a quantifier
bool setQuantifier( Node f );
@@ -65,6 +83,8 @@ public:
public:
//pointer to model
RepSet* d_rep_set;
+ //enumeration type?
+ std::vector< int > d_enum_type;
//index we are considering
std::vector< int > d_index;
//types we are considering
@@ -86,15 +106,13 @@ public:
// for example, if d_index_order = { 2, 0, 1 }
// then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 }
std::map< int, int > d_var_order;
+ //intervals
+ std::map< int, Node > d_bounds[2];
public:
- /** set index order */
- void setIndexOrder( std::vector< int >& indexOrder );
- /** set domain */
- void setDomain( std::vector< RepDomain >& domain );
/** increment the iterator at index=counter */
- void increment2( int counter );
+ int increment2( int counter );
/** increment the iterator */
- void increment();
+ int increment();
/** is the iterator finished? */
bool isFinished();
/** get the i_th term we are considering */
diff --git a/src/theory/rewriterules/rr_inst_match.cpp b/src/theory/rewriterules/rr_inst_match.cpp
index a4c111f86..4908e5ec0 100644
--- a/src/theory/rewriterules/rr_inst_match.cpp
+++ b/src/theory/rewriterules/rr_inst_match.cpp
@@ -137,14 +137,13 @@ class DumbPatMatcher: public PatMatcher{
/* The order of the matching is:
reset arg1, nextMatch arg1, reset arg2, nextMatch arg2, ... */
ApplyMatcher::ApplyMatcher( Node pat, QuantifiersEngine* qe): d_pattern(pat){
- // Assert( pat.hasAttribute(InstConstantAttribute()) );
//set-up d_variables, d_constants, d_childrens
for( size_t i=0; i< d_pattern.getNumChildren(); ++i ){
//EqualityQuery* q = qe->getEqualityQuery(d_pattern[i].getType());
EqualityQuery* q = qe->getEqualityQuery();
Assert( q != NULL );
- if( d_pattern[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(d_pattern[i]) ){
if( d_pattern[i].getKind()==INST_CONSTANT ){
//It's a variable
d_variables.push_back(make_triple((TNode)d_pattern[i],i,q));
@@ -172,7 +171,7 @@ bool ApplyMatcher::reset(TNode t, InstMatch & m, QuantifiersEngine* qe){
//if t is null
Assert( !t.isNull() );
- Assert( !t.hasAttribute(InstConstantAttribute()) );
+ Assert( !quantifiers::TermDb::hasInstConstAttr(t) );
Assert( t.getKind()==d_pattern.getKind() );
Assert( (t.getKind()!=APPLY_UF && t.getKind()!=APPLY_CONSTRUCTOR)
|| t.getOperator()==d_pattern.getOperator() );
@@ -411,7 +410,7 @@ public:
size_t numFreeVar(TNode t){
size_t n = 0;
for( size_t i=0, size =t.getNumChildren(); i < size; ++i ){
- if( t[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(t[i]) ){
if( t[i].getKind()==INST_CONSTANT ){
//variable
++n;
@@ -958,7 +957,7 @@ Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ){
/** TODO: something simpler to see if the pattern is a good
arithmetic pattern */
std::map< Node, Node > d_arith_coeffs;
- if( !Trigger::getPatternArithmetic( pat.getAttribute(InstConstantAttribute()), pat, d_arith_coeffs ) ){
+ if( !Trigger::getPatternArithmetic( quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) ){
Message() << "(?) Unknown matching pattern is " << pat << std::endl;
Unimplemented("pattern not implemented");
return new DumbMatcher();
@@ -983,17 +982,17 @@ Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ){
PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){
Debug("inst-match-gen") << "Pattern term is " << pat << std::endl;
- Assert( pat.hasAttribute(InstConstantAttribute()) );
+ Assert( quantifiers::TermDb::hasInstConstAttr(pat) );
if( pat.getKind()==kind::NOT && pat[0].getKind() == kind::EQUAL){
/* Difference */
return new UfDeqMatcher(pat, qe);
} else if (pat.getKind() == kind::EQUAL){
- if( !pat[0].hasAttribute(InstConstantAttribute() )){
- Assert(pat[1].hasAttribute(InstConstantAttribute()));
+ if( !quantifiers::TermDb::hasInstConstAttr(pat[0])){
+ Assert(quantifiers::TermDb::hasInstConstAttr(pat[1]));
return new UfCstEqMatcher(pat[1], pat[0], qe);
- }else if( !pat[1].hasAttribute(InstConstantAttribute() )){
- Assert(pat[0].hasAttribute(InstConstantAttribute()));
+ }else if( !quantifiers::TermDb::hasInstConstAttr(pat[1] )){
+ Assert(quantifiers::TermDb::hasInstConstAttr(pat[0]));
return new UfCstEqMatcher(pat[0], pat[1], qe);
}else{
std::vector< Node > pats(pat.begin(),pat.end());
@@ -1009,7 +1008,7 @@ PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){
/** TODO: something simpler to see if the pattern is a good
arithmetic pattern */
std::map< Node, Node > d_arith_coeffs;
- if( !Trigger::getPatternArithmetic( pat.getAttribute(InstConstantAttribute()), pat, d_arith_coeffs ) ){
+ if( !Trigger::getPatternArithmetic( quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) ){
Debug("inst-match-gen") << "(?) Unknown matching pattern is " << pat << std::endl;
Message() << "(?) Unknown matching pattern is " << pat << std::endl;
return new DumbPatMatcher();
@@ -1033,7 +1032,7 @@ PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){
ArithMatcher::ArithMatcher(Node pat, QuantifiersEngine* qe): d_pattern(pat){
- if(Trigger::getPatternArithmetic(pat.getAttribute(InstConstantAttribute()), pat, d_arith_coeffs ) )
+ if(Trigger::getPatternArithmetic(quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) )
{
Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_pattern << std::endl;
Assert(false);
diff --git a/src/theory/rewriterules/rr_inst_match.h b/src/theory/rewriterules/rr_inst_match.h
index 3ec9438fd..aa89430c1 100644
--- a/src/theory/rewriterules/rr_inst_match.h
+++ b/src/theory/rewriterules/rr_inst_match.h
@@ -67,7 +67,7 @@ public:
/** legal candidate */
static inline bool isLegalCandidate( TNode n ){
return !n.getAttribute(NoMatchAttribute()) &&
- ( !options::cbqi() || !n.hasAttribute(InstConstantAttribute())) &&
+ ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n)) &&
( !options::efficientEMatching() || n.hasAttribute(AvailableInTermDb()) );
}
diff --git a/src/theory/rewriterules/rr_trigger.cpp b/src/theory/rewriterules/rr_trigger.cpp
index 7e35be7ad..13250cf1b 100644
--- a/src/theory/rewriterules/rr_trigger.cpp
+++ b/src/theory/rewriterules/rr_trigger.cpp
@@ -65,7 +65,7 @@ bool Trigger::getNextMatch(){
int Trigger::addInstantiations( InstMatch& baseMatch ){
int addedLemmas = d_mg->addInstantiations( baseMatch,
- d_nodes[0].getAttribute(InstConstantAttribute()),
+ quantifiers::TermDb::getInstConstAttr(d_nodes[0]),
d_quantEngine);
if( addedLemmas>0 ){
Debug("inst-trigger") << "Added " << addedLemmas << " lemmas, trigger was ";
@@ -91,7 +91,7 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >&
qe->getTermDatabase()->computeVarContains( temp[i] );
for( int j=0; j<(int)qe->getTermDatabase()->d_var_contains[ temp[i] ].size(); j++ ){
Node v = qe->getTermDatabase()->d_var_contains[ temp[i] ][j];
- if( v.getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(v)==f ){
if( vars.find( v )==vars.end() ){
vars[ v ] = true;
foundVar = true;
@@ -181,7 +181,7 @@ bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){
}
bool Trigger::isUsable( Node n, Node f ){
- if( n.getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(n)==f ){
if( !isAtomicTrigger( n ) && n.getKind()!=INST_CONSTANT ){
std::map< Node, Node > coeffs;
return getPatternArithmetic( f, n, coeffs );
@@ -201,7 +201,7 @@ bool Trigger::isUsable( Node n, Node f ){
bool Trigger::isSimpleTrigger( Node n ){
if( isAtomicTrigger( n ) ){
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){
+ if( n[i].getKind()!=INST_CONSTANT && quantifiers::TermDb::hasInstConstAttr(n[i]) ){
return false;
}
}
@@ -318,9 +318,9 @@ bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coef
Assert( coeffs.empty() );
NodeBuilder<> t(kind::PLUS);
for( int i=0; i<(int)n.getNumChildren(); i++ ){
- if( n[i].hasAttribute(InstConstantAttribute()) ){
+ if( quantifiers::TermDb::hasInstConstAttr(n[i]) ){
if( n[i].getKind()==INST_CONSTANT ){
- if( n[i].getAttribute(InstConstantAttribute())==f ){
+ if( quantifiers::TermDb::getInstConstAttr(n[i])==f ){
coeffs[ n[i] ] = Node::null();
}else{
coeffs.clear();
@@ -343,12 +343,12 @@ bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coef
}
return true;
}else if( n.getKind()==MULT ){
- if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){
- Assert( !n[1].hasAttribute(InstConstantAttribute()) );
+ if( n[0].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[0])==f ){
+ Assert( !quantifiers::TermDb::hasInstConstAttr(n[1]) );
coeffs[ n[0] ] = n[1];
return true;
- }else if( n[1].getKind()==INST_CONSTANT && n[1].getAttribute(InstConstantAttribute())==f ){
- Assert( !n[0].hasAttribute(InstConstantAttribute()) );
+ }else if( n[1].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[1])==f ){
+ Assert( !quantifiers::TermDb::hasInstConstAttr(n[0]) );
coeffs[ n[1] ] = n[0];
return true;
}
diff --git a/src/theory/rewriterules/rr_trigger.h b/src/theory/rewriterules/rr_trigger.h
index f1a37d937..f02f38d0e 100644
--- a/src/theory/rewriterules/rr_trigger.h
+++ b/src/theory/rewriterules/rr_trigger.h
@@ -94,8 +94,7 @@ public:
public:
/** is usable trigger */
static inline bool isUsableTrigger( TNode n, TNode f ){
- //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF;
- return n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f );
+ return quantifiers::TermDb::getInstConstAttr(n)==f && isAtomicTrigger( n ) && isUsable( n, f );
}
static inline bool isAtomicTrigger( TNode n ){
return
diff --git a/src/theory/rewriterules/theory_rewriterules.h b/src/theory/rewriterules/theory_rewriterules.h
index 19cb3e987..a542214b2 100644
--- a/src/theory/rewriterules/theory_rewriterules.h
+++ b/src/theory/rewriterules/theory_rewriterules.h
@@ -263,7 +263,7 @@ private:
rewriter::Subst & vars);
//create inst variable
- std::vector<Node> createInstVariable( std::vector<Node> & vars );
+ std::vector<Node> createInstVariable( Node r, std::vector<Node> & vars );
/** statistics class */
class Statistics {
diff --git a/src/theory/rewriterules/theory_rewriterules_rules.cpp b/src/theory/rewriterules/theory_rewriterules_rules.cpp
index 589556802..7e1d42bb2 100644
--- a/src/theory/rewriterules/theory_rewriterules_rules.cpp
+++ b/src/theory/rewriterules/theory_rewriterules_rules.cpp
@@ -151,7 +151,7 @@ void TheoryRewriteRules::addRewriteRule(const Node r)
vars.push_back(*v);
};
/* Instantiation version */
- std::vector<Node> inst_constants = createInstVariable(vars);
+ std::vector<Node> inst_constants = createInstVariable(r,vars);
/* Body/Remove_term/Guards/Triggers */
Node body = r[2][1];
TNode new_terms = r[2][1];
@@ -232,7 +232,11 @@ void TheoryRewriteRules::addRewriteRule(const Node r)
NodeBuilder<> patternListB(kind::INST_PATTERN_LIST);
patternListB << static_cast<Node>(patternB);
forallB << static_cast<Node>(patternListB);
- getOutputChannel().lemma((Node) forallB);
+ Node lem = (Node) forallB;
+ lem = Rewriter::rewrite(lem);
+ QRewriteRuleAttribute qra;
+ lem.setAttribute(qra,r);
+ getOutputChannel().lemma(lem);
return;
}
@@ -376,7 +380,7 @@ bool TheoryRewriteRules::addRewritePattern(TNode pattern, TNode body,
}
-std::vector<Node> TheoryRewriteRules::createInstVariable( std::vector<Node> & vars ){
+std::vector<Node> TheoryRewriteRules::createInstVariable( Node r, std::vector<Node> & vars ){
std::vector<Node> inst_constant;
inst_constant.reserve(vars.size());
for( std::vector<Node>::const_iterator v = vars.begin();
@@ -384,6 +388,11 @@ std::vector<Node> TheoryRewriteRules::createInstVariable( std::vector<Node> & va
//make instantiation constants
Node ic = NodeManager::currentNM()->mkInstConstant( (*v).getType() );
inst_constant.push_back( ic );
+ InstConstantAttribute ica;
+ ic.setAttribute(ica,r);
+ //also set the no-match attribute
+ NoMatchAttribute nma;
+ ic.setAttribute(nma,true);
};
return inst_constant;
}
diff --git a/src/theory/rewriterules/theory_rewriterules_type_rules.h b/src/theory/rewriterules/theory_rewriterules_type_rules.h
index 256957855..fa6bb2227 100644
--- a/src/theory/rewriterules/theory_rewriterules_type_rules.h
+++ b/src/theory/rewriterules/theory_rewriterules_type_rules.h
@@ -33,6 +33,8 @@ public:
* Compute the type for (and optionally typecheck) a term belonging
* to the theory of rewriterules.
*
+ * @param nodeManager the NodeManager in use
+ * @param n the node to compute the type of
* @param check if true, the node's type should be checked as well
* as computed.
*/
diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp
index 58b8cf697..3a767b5c3 100644
--- a/src/theory/shared_terms_database.cpp
+++ b/src/theory/shared_terms_database.cpp
@@ -131,9 +131,9 @@ bool SharedTermsDatabase::propagateSharedEquality(TheoryId theory, TNode a, TNod
// Propagate away
Node equality = a.eqNode(b);
if (value) {
- d_theoryEngine->assertToTheory(equality, theory, THEORY_BUILTIN);
+ d_theoryEngine->assertToTheory(equality, equality, theory, THEORY_BUILTIN);
} else {
- d_theoryEngine->assertToTheory(equality.notNode(), theory, THEORY_BUILTIN);
+ d_theoryEngine->assertToTheory(equality.notNode(), equality.notNode(), theory, THEORY_BUILTIN);
}
// As you were
diff --git a/src/theory/strings/Makefile b/src/theory/strings/Makefile
new file mode 100644
index 000000000..e92c24ab7
--- /dev/null
+++ b/src/theory/strings/Makefile
@@ -0,0 +1,4 @@
+topdir = ../../..
+srcdir = src/theory/strings
+
+include $(topdir)/Makefile.subdir
diff --git a/src/theory/strings/Makefile.am b/src/theory/strings/Makefile.am
new file mode 100644
index 000000000..38efa33f3
--- /dev/null
+++ b/src/theory/strings/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = \
+ -D__BUILDING_CVC4LIB \
+ -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../..
+AM_CXXFLAGS = -Wall $(FLAG_VISIBILITY_HIDDEN)
+
+noinst_LTLIBRARIES = libstrings.la
+
+libstrings_la_SOURCES = \
+ theory_strings.h \
+ theory_strings.cpp \
+ theory_strings_rewriter.h \
+ theory_strings_rewriter.cpp \
+ theory_strings_type_rules.h \
+ type_enumerator.h \
+ theory_strings_preprocess.h \
+ theory_strings_preprocess.cpp
+
+EXTRA_DIST = \
+ kinds
diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds
new file mode 100644
index 000000000..814276a7c
--- /dev/null
+++ b/src/theory/strings/kinds
@@ -0,0 +1,105 @@
+# kinds [for strings theory]
+#
+
+theory THEORY_STRINGS ::CVC4::theory::strings::TheoryStrings "theory/strings/theory_strings.h"
+
+properties check parametric propagate
+
+rewriter ::CVC4::theory::strings::TheoryStringsRewriter "theory/strings/theory_strings_rewriter.h"
+
+typechecker "theory/strings/theory_strings_type_rules.h"
+
+
+operator STRING_CONCAT 2: "string concat"
+
+operator STRING_IN_REGEXP 2 "membership"
+
+operator STRING_LENGTH 1 "string length"
+
+#sort CHAR_TYPE \
+# Cardinality::INTEGERS \
+# well-founded \
+# "NodeManager::currentNM()->mkConst(::CVC4::String())" \
+# "util/regexp.h" \
+# "String type"
+
+sort STRING_TYPE \
+ Cardinality::INTEGERS \
+ well-founded \
+ "NodeManager::currentNM()->mkConst(::CVC4::String())" \
+ "util/regexp.h" \
+ "String type"
+
+sort REGEXP_TYPE \
+ Cardinality::INTEGERS \
+ well-founded \
+ "NodeManager::currentNM()->mkConst(::CVC4::RegExp())" \
+ "util/regexp.h" \
+ "RegExp type"
+
+enumerator STRING_TYPE \
+ "::CVC4::theory::strings::StringEnumerator" \
+ "theory/strings/type_enumerator.h"
+
+#enumerator REGEXP_TYPE \
+# "::CVC4::theory::strings::RegExpEnumerator" \
+# "theory/strings/type_enumerator.h"
+
+constant CONST_STRING \
+ ::CVC4::String \
+ ::CVC4::strings::StringHashFunction \
+ "util/regexp.h" \
+ "a string of characters"
+
+constant CONST_REGEXP \
+ ::CVC4::RegExp \
+ ::CVC4::RegExpHashFunction \
+ "util/regexp.h" \
+ "a regular expression"
+
+typerule CONST_STRING ::CVC4::theory::strings::StringConstantTypeRule
+typerule CONST_REGEXP ::CVC4::theory::strings::RegExpConstantTypeRule
+
+# equal equal / less than / output
+operator STRING_TO_REGEXP 1 "convert string to regexp"
+operator REGEXP_CONCAT 2: "regexp concat"
+operator REGEXP_OR 2: "regexp or"
+operator REGEXP_INTER 2: "regexp intersection"
+operator REGEXP_STAR 1 "regexp *"
+operator REGEXP_PLUS 1 "regexp +"
+operator REGEXP_OPT 1 "regexp ?"
+
+#constant REGEXP_EMPTY \
+# ::CVC4::RegExp \
+# ::CVC4::RegExpHashFunction \
+# "util/string.h" \
+# "a regexp contains nothing"
+
+#constant REGEXP_ALL \
+# ::CVC4::RegExp \
+# ::CVC4::RegExpHashFunction \
+# "util/string.h" \
+# "a regexp contains all strings"
+
+#constant REGEXP_SIGMA \
+# ::CVC4::RegExp \
+# ::CVC4::RegExpHashFunction \
+# "util/string.h" \
+# "a regexp contains an arbitrary charactor"
+
+typerule REGEXP_CONCAT ::CVC4::theory::strings::RegExpConcatTypeRule
+typerule REGEXP_OR ::CVC4::theory::strings::RegExpOrTypeRule
+typerule REGEXP_INTER ::CVC4::theory::strings::RegExpInterTypeRule
+typerule REGEXP_STAR ::CVC4::theory::strings::RegExpStarTypeRule
+typerule REGEXP_PLUS ::CVC4::theory::strings::RegExpPlusTypeRule
+typerule REGEXP_OPT ::CVC4::theory::strings::RegExpOptTypeRule
+
+typerule STRING_TO_REGEXP ::CVC4::theory::strings::StringToRegExpTypeRule
+
+
+typerule STRING_CONCAT ::CVC4::theory::strings::StringConcatTypeRule
+typerule STRING_LENGTH ::CVC4::theory::strings::StringLengthTypeRule
+
+typerule STRING_IN_REGEXP ::CVC4::theory::strings::StringInRegExpTypeRule
+
+endtheory
diff --git a/src/theory/strings/options b/src/theory/strings/options
new file mode 100644
index 000000000..9226f9999
--- /dev/null
+++ b/src/theory/strings/options
@@ -0,0 +1,11 @@
+#
+# Option specification file for CVC4
+# See src/options/base_options for a description of this file format
+#
+
+module STRINGS "theory/strings/options.h" Strings theory
+
+option stringCharCardinality str-alphabet-card --str-alphabet-card=N int16_t :default 256 :read-write
+ the cardinality of the characters used by the theory of string, default 256
+
+endmodule
diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp
new file mode 100644
index 000000000..7d5edd0f7
--- /dev/null
+++ b/src/theory/strings/theory_strings.cpp
@@ -0,0 +1,1711 @@
+/********************* */
+/*! \file theory_strings.cpp
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: Tianyi Liang, Andrew Reynolds
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of the theory of strings.
+ **
+ ** Implementation of the theory of strings.
+ **/
+
+
+#include "theory/strings/theory_strings.h"
+#include "theory/valuation.h"
+#include "expr/kind.h"
+#include "theory/rewriter.h"
+#include "expr/command.h"
+#include "theory/model.h"
+#include "smt/logic_exception.h"
+#include "theory/strings/options.h"
+#include "theory/strings/type_enumerator.h"
+#include <cmath>
+
+#define STR_UNROLL_INDUCTION
+
+using namespace std;
+using namespace CVC4::context;
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe)
+ : Theory(THEORY_STRINGS, c, u, out, valuation, logicInfo, qe),
+ d_notify( *this ),
+ d_equalityEngine(d_notify, c, "theory::strings::TheoryStrings"),
+ d_conflict( c, false ),
+ d_infer(c),
+ d_infer_exp(c),
+ d_nf_pairs(c),
+ d_ind_map1(c),
+ d_ind_map2(c),
+ d_ind_map_exp(c),
+ d_ind_map_lemma(c),
+ //d_lit_to_decide_index( c, 0 ),
+ //d_lit_to_decide( c ),
+ d_lit_to_unroll( c )
+{
+ // The kinds we are treating as function application in congruence
+ d_equalityEngine.addFunctionKind(kind::STRING_IN_REGEXP);
+ d_equalityEngine.addFunctionKind(kind::STRING_LENGTH);
+ d_equalityEngine.addFunctionKind(kind::STRING_CONCAT);
+
+ d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) );
+ d_emptyString = NodeManager::currentNM()->mkConst( ::CVC4::String("") );
+ d_true = NodeManager::currentNM()->mkConst( true );
+ d_false = NodeManager::currentNM()->mkConst( false );
+}
+
+TheoryStrings::~TheoryStrings() {
+
+}
+
+Node TheoryStrings::getRepresentative( Node t ) {
+ if( d_equalityEngine.hasTerm( t ) ){
+ return d_equalityEngine.getRepresentative( t );
+ }else{
+ return t;
+ }
+}
+
+bool TheoryStrings::hasTerm( Node a ){
+ return d_equalityEngine.hasTerm( a );
+}
+
+bool TheoryStrings::areEqual( Node a, Node b ){
+ if( a==b ){
+ return true;
+ }else if( hasTerm( a ) && hasTerm( b ) ){
+ return d_equalityEngine.areEqual( a, b );
+ }else{
+ return false;
+ }
+}
+
+bool TheoryStrings::areDisequal( Node a, Node b ){
+ if( hasTerm( a ) && hasTerm( b ) ){
+ return d_equalityEngine.areDisequal( a, b, false );
+ }else{
+ return false;
+ }
+}
+
+Node TheoryStrings::getLength( Node t ) {
+ EqcInfo * ei = getOrMakeEqcInfo( t );
+ Node length_term = ei->d_length_term;
+ if( length_term.isNull()) {
+ //typically shouldnt be necessary
+ length_term = t;
+ }
+ return NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, length_term );
+}
+
+void TheoryStrings::setMasterEqualityEngine(eq::EqualityEngine* eq) {
+ d_equalityEngine.setMasterEqualityEngine(eq);
+}
+
+void TheoryStrings::addSharedTerm(TNode t) {
+ Debug("strings") << "TheoryStrings::addSharedTerm(): "
+ << t << " " << t.getType().isBoolean() << endl;
+ d_equalityEngine.addTriggerTerm(t, THEORY_STRINGS);
+ Debug("strings") << "TheoryStrings::addSharedTerm() finished" << std::endl;
+}
+
+EqualityStatus TheoryStrings::getEqualityStatus(TNode a, TNode b) {
+ if( d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b) ){
+ if (d_equalityEngine.areEqual(a, b)) {
+ // The terms are implied to be equal
+ return EQUALITY_TRUE;
+ }
+ if (d_equalityEngine.areDisequal(a, b, false)) {
+ // The terms are implied to be dis-equal
+ return EQUALITY_FALSE;
+ }
+ }
+ return EQUALITY_UNKNOWN;
+}
+
+void TheoryStrings::propagate(Effort e)
+{
+ // direct propagation now
+}
+
+bool TheoryStrings::propagate(TNode literal) {
+ Debug("strings-propagate") << "TheoryStrings::propagate(" << literal << ")" << std::endl;
+ // If already in conflict, no more propagation
+ if (d_conflict) {
+ Debug("strings-propagate") << "TheoryStrings::propagate(" << literal << "): already in conflict" << std::endl;
+ return false;
+ }
+ Trace("strings-prop") << "strPropagate " << literal << std::endl;
+ // Propagate out
+ bool ok = d_out->propagate(literal);
+ if (!ok) {
+ d_conflict = true;
+ }
+ return ok;
+}
+
+/** explain */
+void TheoryStrings::explain(TNode literal, std::vector<TNode>& assumptions){
+ Debug("strings-explain") << "Explain " << literal << std::endl;
+ bool polarity = literal.getKind() != kind::NOT;
+ TNode atom = polarity ? literal : literal[0];
+ if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) {
+ d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions);
+ } else {
+ d_equalityEngine.explainPredicate(atom, polarity, assumptions);
+ }
+}
+
+Node TheoryStrings::explain( TNode literal ){
+ std::vector< TNode > assumptions;
+ explain( literal, assumptions );
+ if( assumptions.empty() ){
+ return d_true;
+ }else if( assumptions.size()==1 ){
+ return assumptions[0];
+ }else{
+ return NodeManager::currentNM()->mkNode( kind::AND, assumptions );
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// MODEL GENERATION
+/////////////////////////////////////////////////////////////////////////////
+
+
+void TheoryStrings::collectModelInfo( TheoryModel* m, bool fullModel ) {
+ Trace("strings-model") << "TheoryStrings : Collect model info, fullModel = " << fullModel << std::endl;
+ Trace("strings-model") << "TheoryStrings : assertEqualityEngine." << std::endl;
+ m->assertEqualityEngine( &d_equalityEngine );
+ // Generate model
+ std::vector< Node > nodes;
+ getEquivalenceClasses( nodes );
+ std::map< Node, Node > processed;
+ std::vector< std::vector< Node > > col;
+ std::vector< Node > lts;
+ seperateByLength( nodes, col, lts );
+ //step 1 : get all values for known lengths
+ std::vector< Node > lts_values;
+ //std::map< Node, bool > values_used;
+ for( unsigned i=0; i<col.size(); i++ ){
+ Trace("strings-model") << "Checking length for " << col[i][0] << " (length is " << lts[i] << ")" << std::endl;
+ if( lts[i].isConst() ){
+ lts_values.push_back( lts[i] );
+ //values_used[ lts[i] ] = true;
+ }else{
+ //get value for lts[i];
+ if( !lts[i].isNull() ){
+ Node v = d_valuation.getModelValue(lts[i]);
+ //Node v = m->getValue(lts[i]);
+ Trace("strings-model") << "Model value for " << lts[i] << " is " << v << std::endl;
+ lts_values.push_back( v );
+ //values_used[ v ] = true;
+ }else{
+ Trace("strings-model-warn") << "No length for eqc " << col[i][0] << std::endl;
+ Assert( false );
+ lts_values.push_back( Node::null() );
+ }
+ }
+ }
+ ////step 2 : assign arbitrary values for unknown lengths?
+ //for( unsigned i=0; i<col.size(); i++ ){
+ // if(
+ //}
+ Trace("strings-model") << "Assign to equivalence classes..." << std::endl;
+ //step 3 : assign values to equivalence classes that are pure variables
+ for( unsigned i=0; i<col.size(); i++ ){
+ std::vector< Node > pure_eq;
+ Trace("strings-model") << "The equivalence classes ";
+ for( unsigned j=0; j<col[i].size(); j++ ) {
+ Trace("strings-model") << col[i][j] << " ";
+ //check if col[i][j] has only variables
+ EqcInfo* ei = getOrMakeEqcInfo( col[i][j], false );
+ Node cst = ei ? ei->d_const_term : Node::null();
+ if( cst.isNull() ){
+ Assert( d_normal_forms.find( col[i][j] )!=d_normal_forms.end() );
+ if( d_normal_forms[col[i][j]].size()==1 ){//&& d_normal_forms[col[i][j]][0]==col[i][j] ){
+ pure_eq.push_back( col[i][j] );
+ }
+ }else{
+ processed[col[i][j]] = cst;
+ }
+ }
+ Trace("strings-model") << "have length " << lts_values[i] << std::endl;
+
+ Trace("strings-model") << "Need to assign values of length " << lts_values[i] << " to equivalence classes ";
+ for( unsigned j=0; j<pure_eq.size(); j++ ){
+ Trace("strings-model") << pure_eq[j] << " ";
+ }
+ Trace("strings-model") << std::endl;
+
+ //use type enumerator
+ StringEnumeratorLength sel(lts_values[i].getConst<Rational>().getNumerator().toUnsignedInt());
+ for( unsigned j=0; j<pure_eq.size(); j++ ){
+ Assert( !sel.isFinished() );
+ Node c = *sel;
+ while( d_equalityEngine.hasTerm( c ) ){
+ ++sel;
+ Assert( !sel.isFinished() );
+ c = *sel;
+ }
+ ++sel;
+ Trace("strings-model") << "*** Assigned constant " << c << " for " << pure_eq[j] << std::endl;
+ processed[pure_eq[j]] = c;
+ m->assertEquality( pure_eq[j], c, true );
+ }
+ }
+ Trace("strings-model") << "String Model : Finished." << std::endl;
+ //step 4 : assign constants to all other equivalence classes
+ for( unsigned i=0; i<nodes.size(); i++ ){
+ if( processed.find( nodes[i] )==processed.end() ){
+ Assert( d_normal_forms.find( nodes[i] )!=d_normal_forms.end() );
+ Trace("strings-model") << "Construct model for " << nodes[i] << " based on normal form ";
+ for( unsigned j=0; j<d_normal_forms[nodes[i]].size(); j++ ) {
+ if( j>0 ) Trace("strings-model") << " ++ ";
+ Trace("strings-model") << d_normal_forms[nodes[i]][j];
+ Node r = getRepresentative( d_normal_forms[nodes[i]][j] );
+ if( !r.isConst() && processed.find( r )==processed.end() ){
+ Trace("strings-model") << "(UNPROCESSED)";
+ }
+ }
+ Trace("strings-model") << std::endl;
+ std::vector< Node > nc;
+ for( unsigned j=0; j<d_normal_forms[nodes[i]].size(); j++ ) {
+ Node r = getRepresentative( d_normal_forms[nodes[i]][j] );
+ Assert( r.isConst() || processed.find( r )!=processed.end() );
+ nc.push_back(r.isConst() ? r : processed[r]);
+ }
+ Node cc = mkConcat( nc );
+ Assert( cc.getKind()==kind::CONST_STRING );
+ Trace("strings-model") << "*** Determined constant " << cc << " for " << nodes[i] << std::endl;
+ processed[nodes[i]] = cc;
+ m->assertEquality( nodes[i], cc, true );
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// MAIN SOLVER
+/////////////////////////////////////////////////////////////////////////////
+
+void TheoryStrings::preRegisterTerm(TNode n) {
+ Debug("strings-prereg") << "TheoryStrings::preRegisterTerm() " << n << endl;
+ //collectTerms( n );
+ switch (n.getKind()) {
+ case kind::EQUAL:
+ d_equalityEngine.addTriggerEquality(n);
+ break;
+ case kind::STRING_IN_REGEXP:
+ d_equalityEngine.addTriggerPredicate(n);
+ break;
+ default:
+ if(n.getKind() == kind::VARIABLE || n.getKind()==kind::SKOLEM) {
+ if( std::find( d_length_intro_vars.begin(), d_length_intro_vars.end(), n )==d_length_intro_vars.end() ){
+ Node n_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n);
+ Node n_len_geq_zero = NodeManager::currentNM()->mkNode( kind::GEQ, n_len, d_zero);
+ Trace("strings-lemma") << "Strings: Add lemma " << n_len_geq_zero << std::endl;
+ d_out->lemma(n_len_geq_zero);
+ }
+ }
+ if (n.getType().isBoolean()) {
+ // Get triggered for both equal and dis-equal
+ d_equalityEngine.addTriggerPredicate(n);
+ } else {
+ // Function applications/predicates
+ d_equalityEngine.addTerm(n);
+ }
+ break;
+ }
+}
+
+void TheoryStrings::check(Effort e) {
+ bool polarity;
+ TNode atom;
+
+ if( !done() && !hasTerm( d_emptyString ) ){
+ preRegisterTerm( d_emptyString );
+ }
+
+ // Trace("strings-process") << "Theory of strings, check : " << e << std::endl;
+ Trace("strings-check") << "Theory of strings, check : " << e << std::endl;
+ while ( !done() && !d_conflict)
+ {
+ // Get all the assertions
+ Assertion assertion = get();
+ TNode fact = assertion.assertion;
+
+ Trace("strings-assertion") << "get assertion: " << fact << endl;
+
+ polarity = fact.getKind() != kind::NOT;
+ atom = polarity ? fact : fact[0];
+ if (atom.getKind() == kind::EQUAL) {
+ d_equalityEngine.assertEquality(atom, polarity, fact);
+ } else {
+ d_equalityEngine.assertPredicate(atom, polarity, fact);
+ }
+#ifdef STR_UNROLL_INDUCTION
+ //check if it is a literal to unroll?
+ if( d_lit_to_unroll.find( atom )!=d_lit_to_unroll.end() ){
+ Trace("strings-ind") << "Strings-ind : Possibly unroll for : " << atom << ", polarity = " << polarity << std::endl;
+ }
+#endif
+ }
+ doPendingFacts();
+
+
+ bool addedLemma = false;
+ if( e == EFFORT_FULL && !d_conflict ) {
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine );
+ while( !eqcs_i.isFinished() ){
+ Node eqc = (*eqcs_i);
+ //if eqc.getType is string
+ if (eqc.getType().isString()) {
+ //EqcInfo* ei = getOrMakeEqcInfo( eqc, true );
+ //get the constant for the equivalence class
+ //int c_len = ...;
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine );
+ while( !eqc_i.isFinished() ){
+ Node n = (*eqc_i);
+
+ //if n is concat, and
+ //if n has not instantiatied the concat..length axiom
+ //then, add lemma
+ if( n.getKind() == kind::STRING_CONCAT || n.getKind() == kind::CONST_STRING ){
+ if( d_length_inst.find(n)==d_length_inst.end() ){
+ d_length_inst[n] = true;
+ Trace("strings-debug") << "get n: " << n << endl;
+ Node sk = NodeManager::currentNM()->mkSkolem( "lsym_$$", n.getType(), "created for concat lemma" );
+ d_length_intro_vars.push_back( sk );
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, n );
+ eq = Rewriter::rewrite(eq);
+ Trace("strings-lemma") << "Strings: Add lemma " << eq << std::endl;
+ d_out->lemma(eq);
+ Node skl = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk );
+ Node lsum;
+ if( n.getKind() == kind::STRING_CONCAT ){
+ //add lemma
+ std::vector<Node> node_vec;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ) {
+ Node lni = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n[i] );
+ node_vec.push_back(lni);
+ }
+ lsum = NodeManager::currentNM()->mkNode( kind::PLUS, node_vec );
+ }else{
+ //add lemma
+ lsum = NodeManager::currentNM()->mkConst( ::CVC4::Rational( n.getConst<String>().size() ) );
+ }
+ Node ceq = NodeManager::currentNM()->mkNode( kind::EQUAL, skl, lsum );
+ ceq = Rewriter::rewrite(ceq);
+ Trace("strings-lemma") << "Strings: Add lemma " << ceq << std::endl;
+ d_out->lemma(ceq);
+ addedLemma = true;
+ }
+ }
+ ++eqc_i;
+ }
+ }
+ ++eqcs_i;
+ }
+ if( !addedLemma ){
+ addedLemma = checkNormalForms();
+ Trace("strings-process") << "Done check normal forms, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl;
+ if(!d_conflict && !addedLemma) {
+ addedLemma = checkCardinality();
+ Trace("strings-process") << "Done check cardinality, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl;
+ if( !d_conflict && !addedLemma ){
+ addedLemma = checkInductiveEquations();
+ Trace("strings-process") << "Done check inductive equations, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl;
+ }
+ }
+ }
+ }
+ Trace("strings-process") << "Theory of strings, done check : " << e << std::endl;
+}
+
+TheoryStrings::EqcInfo::EqcInfo( context::Context* c ) : d_const_term(c), d_length_term(c), d_cardinality_lem_k(c) {
+
+}
+
+TheoryStrings::EqcInfo * TheoryStrings::getOrMakeEqcInfo( Node eqc, bool doMake ) {
+ std::map< Node, EqcInfo* >::iterator eqc_i = d_eqc_info.find( eqc );
+ if( eqc_i==d_eqc_info.end() ){
+ if( doMake ){
+ EqcInfo* ei = new EqcInfo( getSatContext() );
+ d_eqc_info[eqc] = ei;
+ return ei;
+ }else{
+ return NULL;
+ }
+ }else{
+ return (*eqc_i).second;
+ }
+}
+
+
+/** Conflict when merging two constants */
+void TheoryStrings::conflict(TNode a, TNode b){
+ Node conflictNode;
+ if (a.getKind() == kind::CONST_BOOLEAN) {
+ conflictNode = explain( a.iffNode(b) );
+ } else {
+ conflictNode = explain( a.eqNode(b) );
+ }
+ Debug("strings-conflict") << "CONFLICT: Eq engine conflict : " << conflictNode << std::endl;
+ d_out->conflict( conflictNode );
+ d_conflict = true;
+}
+
+/** called when a new equivalance class is created */
+void TheoryStrings::eqNotifyNewClass(TNode t){
+ if( t.getKind() == kind::CONST_STRING ){
+ EqcInfo * ei =getOrMakeEqcInfo( t, true );
+ ei->d_const_term = t;
+ }
+ if( t.getKind() == kind::STRING_LENGTH ){
+ Trace("strings-debug") << "New length eqc : " << t << std::endl;
+ Node r = d_equalityEngine.getRepresentative(t[0]);
+ EqcInfo * ei = getOrMakeEqcInfo( r, true );
+ ei->d_length_term = t[0];
+ }
+}
+
+/** called when two equivalance classes will merge */
+void TheoryStrings::eqNotifyPreMerge(TNode t1, TNode t2){
+ EqcInfo * e2 = getOrMakeEqcInfo(t2, false);
+ if( e2 ){
+ EqcInfo * e1 = getOrMakeEqcInfo( t1 );
+ //add information from e2 to e1
+ if( !e2->d_const_term.get().isNull() ){
+ e1->d_const_term.set( e2->d_const_term );
+ }
+ if( !e2->d_length_term.get().isNull() ){
+ e1->d_length_term.set( e2->d_length_term );
+ }
+ if( e2->d_cardinality_lem_k.get()>e1->d_cardinality_lem_k.get() ) {
+ e1->d_cardinality_lem_k.set( e2->d_cardinality_lem_k );
+ }
+ }
+ if( hasTerm( d_zero ) ){
+ Node leqc;
+ if( areEqual(d_zero, t1) ){
+ leqc = t2;
+ }else if( areEqual(d_zero, t2) ){
+ leqc = t1;
+ }
+ if( !leqc.isNull() ){
+ //scan equivalence class to see if we apply
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( leqc, &d_equalityEngine );
+ while( !eqc_i.isFinished() ){
+ Node n = (*eqc_i);
+ if( n.getKind()==kind::STRING_LENGTH ){
+ if( !hasTerm( d_emptyString ) || !areEqual(n[0], d_emptyString ) ){
+ //apply the rule length(n[0])==0 => n[0] == ""
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, n[0], d_emptyString );
+ d_pending.push_back( eq );
+ Node eq_exp = NodeManager::currentNM()->mkNode( kind::EQUAL, n, d_zero );
+ d_pending_exp[eq] = eq_exp;
+ Trace("strings-infer") << "Strings : Infer " << eq << " from " << eq_exp << std::endl;
+ d_infer.push_back(eq);
+ d_infer_exp.push_back(eq_exp);
+ }
+ }
+ ++eqc_i;
+ }
+ }
+ }
+}
+
+/** called when two equivalance classes have merged */
+void TheoryStrings::eqNotifyPostMerge(TNode t1, TNode t2) {
+
+}
+
+/** called when two equivalance classes are disequal */
+void TheoryStrings::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
+
+}
+
+void TheoryStrings::computeCareGraph(){
+ Theory::computeCareGraph();
+}
+
+void TheoryStrings::doPendingFacts() {
+ int i=0;
+ while( !d_conflict && i<(int)d_pending.size() ){
+ Node fact = d_pending[i];
+ Node exp = d_pending_exp[ fact ];
+ Trace("strings-pending") << "Process pending fact : " << fact << " from " << exp << std::endl;
+ bool polarity = fact.getKind() != kind::NOT;
+ TNode atom = polarity ? fact : fact[0];
+ if (atom.getKind() == kind::EQUAL) {
+ Assert( d_equalityEngine.hasTerm( atom[0] ) );
+ Assert( d_equalityEngine.hasTerm( atom[1] ) );
+ d_equalityEngine.assertEquality( atom, polarity, exp );
+ }else{
+ d_equalityEngine.assertPredicate( atom, polarity, exp );
+ }
+ i++;
+ }
+ d_pending.clear();
+ d_pending_exp.clear();
+}
+void TheoryStrings::doPendingLemmas() {
+ if( !d_conflict && !d_lemma_cache.empty() ){
+ for( unsigned i=0; i<d_lemma_cache.size(); i++ ){
+ Trace("strings-pending") << "Process pending lemma : " << d_lemma_cache[i] << std::endl;
+ d_out->lemma( d_lemma_cache[i] );
+ }
+ for( std::map< Node, bool >::iterator it = d_pending_req_phase.begin(); it != d_pending_req_phase.end(); ++it ){
+ Trace("strings-pending") << "Require phase : " << it->first << ", polarity = " << it->second << std::endl;
+ d_out->requirePhase( it->first, it->second );
+ }
+ d_lemma_cache.clear();
+ d_pending_req_phase.clear();
+ }
+}
+
+void TheoryStrings::getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf,
+ std::vector< std::vector< Node > > &normal_forms, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< Node > &normal_form_src) {
+ // EqcItr
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine );
+ while( !eqc_i.isFinished() ) {
+ Node n = (*eqc_i);
+ Trace("strings-process") << "Process term " << n << std::endl;
+ if( n.getKind() == kind::CONST_STRING || n.getKind() == kind::STRING_CONCAT ) {
+ std::vector<Node> nf_n;
+ std::vector<Node> nf_exp_n;
+ if( n.getKind() == kind::CONST_STRING ){
+ if( n!=d_emptyString ) {
+ nf_n.push_back( n );
+ }
+ } else if( n.getKind() == kind::STRING_CONCAT ) {
+ for( unsigned i=0; i<n.getNumChildren(); i++ ) {
+ Node nr = d_equalityEngine.getRepresentative( n[i] );
+ std::vector< Node > nf_temp;
+ std::vector< Node > nf_exp_temp;
+ Trace("strings-process") << "Normalizing subterm " << n[i] << " = " << nr << std::endl;
+ normalizeEquivalenceClass( nr, visited, nf_temp, nf_exp_temp );
+ if( d_conflict || !d_pending.empty() || !d_lemma_cache.empty() ) {
+ return;
+ }
+ if( nf.size()!=1 || nf[0]!=d_emptyString ) {
+ for( unsigned r=0; r<nf_temp.size(); r++ ) {
+ Assert( nf_temp[r].getKind()!=kind::STRING_CONCAT );
+ }
+ nf_n.insert( nf_n.end(), nf_temp.begin(), nf_temp.end() );
+ }
+ nf_exp_n.insert( nf_exp_n.end(), nf_exp_temp.begin(), nf_exp_temp.end() );
+ if( nr!=n[i] ) {
+ nf_exp_n.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n[i], nr ) );
+ }
+ }
+ }
+ normal_forms.push_back(nf_n);
+ normal_forms_exp.push_back(nf_exp_n);
+ normal_form_src.push_back(n);
+ }
+ /* should we add these?
+ else {
+ //var/sk?
+ std::vector<Node> nf_n;
+ std::vector<Node> nf_exp_n;
+ nf_n.push_back(n);
+ normal_forms.push_back(nf_n);
+ normal_forms_exp.push_back(nf_exp_n);
+ normal_form_src.push_back(n);
+ }*/
+ ++eqc_i;
+ }
+
+ // Test the result
+ if( !normal_forms.empty() ) {
+ Trace("strings-solve") << "--- Normal forms for equivlance class " << eqc << " : " << std::endl;
+ for( unsigned i=0; i<normal_forms.size(); i++ ) {
+ Trace("strings-solve") << "#" << i << " (from " << normal_form_src[i] << ") : ";
+ for( unsigned j=0; j<normal_forms[i].size(); j++ ) {
+ if(j>0) Trace("strings-solve") << ", ";
+ Trace("strings-solve") << normal_forms[i][j];
+ }
+ Trace("strings-solve") << std::endl;
+ Trace("strings-solve") << " Explanation is : ";
+ if(normal_forms_exp[i].size() == 0) {
+ Trace("strings-solve") << "NONE";
+ } else {
+ for( unsigned j=0; j<normal_forms_exp[i].size(); j++ ) {
+ if(j>0) Trace("strings-solve") << " AND ";
+ Trace("strings-solve") << normal_forms_exp[i][j];
+ }
+ }
+ Trace("strings-solve") << std::endl;
+ }
+ }
+}
+//nf_exp is conjunction
+void TheoryStrings::normalizeEquivalenceClass( Node eqc, std::vector< Node > & visited, std::vector< Node > & nf, std::vector< Node > & nf_exp ) {
+ Trace("strings-process") << "Process equivalence class " << eqc << std::endl;
+ if( std::find( visited.begin(), visited.end(), eqc )!=visited.end() ){
+ //nf.push_back( eqc );
+ if( eqc.getKind()==kind::STRING_CONCAT ){
+ for( unsigned i=0; i<eqc.getNumChildren(); i++ ){
+ if( !d_equalityEngine.hasTerm(d_emptyString) || !d_equalityEngine.areEqual( eqc[i], d_emptyString ) ){
+ nf.push_back( eqc[i] );
+ }
+ }
+ }else if( !d_equalityEngine.hasTerm(d_emptyString) || !d_equalityEngine.areEqual( eqc, d_emptyString ) ){
+ nf.push_back( eqc );
+ }
+ Trace("strings-process") << "Return process equivalence class " << eqc << " : already visited." << std::endl;
+ } else if (d_equalityEngine.hasTerm(d_emptyString) && d_equalityEngine.areEqual( eqc, d_emptyString )){
+ //do nothing
+ Trace("strings-process") << "Return process equivalence class " << eqc << " : empty." << std::endl;
+ d_normal_forms[eqc].clear();
+ d_normal_forms_exp[eqc].clear();
+ } else {
+ visited.push_back( eqc );
+ if(d_normal_forms.find(eqc)==d_normal_forms.end() ){
+ //phi => t = s1 * ... * sn
+ // normal form for each non-variable term in this eqc (s1...sn)
+ std::vector< std::vector< Node > > normal_forms;
+ // explanation for each normal form (phi)
+ std::vector< std::vector< Node > > normal_forms_exp;
+ // record terms for each normal form (t)
+ std::vector< Node > normal_form_src;
+ //Get Normal Forms
+ getNormalForms(eqc, visited, nf, normal_forms, normal_forms_exp, normal_form_src);
+ if( d_conflict || !d_pending.empty() || !d_lemma_cache.empty() ) {
+ return;
+ }
+
+ unsigned i = 0;
+ //unify each normal form > 0 with normal_forms[0]
+ for( unsigned j=1; j<normal_forms.size(); j++ ) {
+
+ Trace("strings-solve") << "Process normal form #0 against #" << j << "..." << std::endl;
+ if( isNormalFormPair( normal_form_src[i], normal_form_src[j] ) ){
+ Trace("strings-solve") << "Already normalized (in cache)." << std::endl;
+ }else{
+ Trace("strings-solve") << "Not in cache." << std::endl;
+ //the current explanation for why the prefix is equal
+ std::vector< Node > curr_exp;
+ curr_exp.insert(curr_exp.end(), normal_forms_exp[i].begin(), normal_forms_exp[i].end() );
+ curr_exp.insert(curr_exp.end(), normal_forms_exp[j].begin(), normal_forms_exp[j].end() );
+ curr_exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, normal_form_src[i], normal_form_src[j] ) );
+ //ensure that normal_forms[i] and normal_forms[j] are the same modulo equality
+ unsigned index_i = 0;
+ unsigned index_j = 0;
+ bool success;
+ do
+ {
+ success = false;
+ //if we are at the end
+ if(index_i==normal_forms[i].size() || index_j==normal_forms[j].size() ) {
+ if( index_i==normal_forms[i].size() && index_j==normal_forms[j].size() ){
+ //we're done
+ addNormalFormPair( normal_form_src[i], normal_form_src[j] );
+ }else{
+ //the remainder must be empty
+ unsigned k = index_i==normal_forms[i].size() ? j : i;
+ unsigned index_k = index_i==normal_forms[i].size() ? index_j : index_i;
+ while(!d_conflict && index_k<normal_forms[k].size()) {
+ //can infer that this string must be empty
+ Node eq_exp;
+ if( curr_exp.empty() ) {
+ eq_exp = d_true;
+ } else if( curr_exp.size() == 1 ) {
+ eq_exp = curr_exp[0];
+ } else {
+ eq_exp = NodeManager::currentNM()->mkNode( kind::AND, curr_exp );
+ }
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, d_emptyString, normal_forms[k][index_k] );
+ Trace("strings-lemma") << "Strings : Infer " << eq << " from " << eq_exp << std::endl;
+ Assert( !d_equalityEngine.areEqual( d_emptyString, normal_forms[k][index_k] ) );
+ d_pending.push_back( eq );
+ d_pending_exp[eq] = eq_exp;
+ d_infer.push_back(eq);
+ d_infer_exp.push_back(eq_exp);
+ index_k++;
+ }
+ }
+ }else {
+ Trace("strings-solve-debug") << "Process " << normal_forms[i][index_i] << " ... " << normal_forms[j][index_j] << std::endl;
+ if(areEqual(normal_forms[i][index_i],normal_forms[j][index_j])){
+ Trace("strings-solve-debug") << "Case 1 : strings are equal" << std::endl;
+ //terms are equal, continue
+ if( normal_forms[i][index_i]!=normal_forms[j][index_j] ){
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL,normal_forms[i][index_i],
+ normal_forms[j][index_j]);
+ Trace("strings-solve-debug") << "Add to explanation : " << eq << std::endl;
+ curr_exp.push_back(eq);
+ }
+ index_j++;
+ index_i++;
+ success = true;
+ }else{
+ Node length_term_i = getLength( normal_forms[i][index_i] );
+ Node length_term_j = getLength( normal_forms[j][index_j] );
+ //check if length(normal_forms[i][index]) == length(normal_forms[j][index])
+ if( areEqual(length_term_i, length_term_j) ){
+ Trace("strings-solve-debug") << "Case 2 : string lengths are equal" << std::endl;
+ //length terms are equal, merge equivalence classes if not already done so
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[i][index_i], normal_forms[j][index_j] );
+ std::vector< Node > temp_exp;
+ temp_exp.insert(temp_exp.end(), curr_exp.begin(), curr_exp.end() );
+ temp_exp.push_back(NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ));
+ Node eq_exp = temp_exp.empty() ? d_true :
+ temp_exp.size() == 1 ? temp_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, temp_exp );
+ Trace("strings-lemma") << "Strings : Infer " << eq << " from " << eq_exp << std::endl;
+ //d_equalityEngine.assertEquality( eq, true, eq_exp );
+ d_pending.push_back( eq );
+ d_pending_exp[eq] = eq_exp;
+ d_infer.push_back(eq);
+ d_infer_exp.push_back(eq_exp);
+ return;
+ }else if( ( normal_forms[i][index_i].getKind()!=kind::CONST_STRING && index_i==normal_forms[i].size()-1 ) ||
+ ( normal_forms[j][index_j].getKind()!=kind::CONST_STRING && index_j==normal_forms[j].size()-1 ) ){
+ Trace("strings-solve-debug") << "Case 3 : at endpoint" << std::endl;
+ Node conc;
+ std::vector< Node > antec;
+ antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() );
+ std::vector< Node > antec_new_lits;
+ std::vector< Node > eqn;
+ for( unsigned r=0; r<2; r++ ){
+ int index_k = r==0 ? index_i : index_j;
+ int k = r==0 ? i : j;
+ std::vector< Node > eqnc;
+ for( unsigned index_l=index_k; index_l<normal_forms[k].size(); index_l++ ){
+ eqnc.push_back( normal_forms[k][index_l] );
+ }
+ eqn.push_back( mkConcat( eqnc ) );
+ }
+ conc = eqn[0].eqNode( eqn[1] );
+ Node ant = mkExplain( antec, antec_new_lits );
+ sendLemma( ant, conc, "Endpoint" );
+ return;
+ }else{
+ Trace("strings-solve-debug") << "Case 4 : must compare strings" << std::endl;
+ Node conc;
+ std::vector< Node > antec;
+ std::vector< Node > antec_new_lits;
+ //check for loops
+ //Trace("strings-loop") << "Check for loops i,j = " << (index_i+1) << "/" << normal_forms[i].size() << " " << (index_j+1) << "/" << normal_forms[j].size() << std::endl;
+ int has_loop[2] = { -1, -1 };
+ for( unsigned r=0; r<2; r++ ){
+ int index = (r==0 ? index_i : index_j);
+ int other_index = (r==0 ? index_j : index_i );
+ int n_index = (r==0 ? i : j);
+ int other_n_index = (r==0 ? j : i);
+ if( normal_forms[other_n_index][other_index].getKind() != kind::CONST_STRING ) {
+ for( unsigned lp = index+1; lp<normal_forms[n_index].size(); lp++ ){
+ if( normal_forms[n_index][lp]==normal_forms[other_n_index][other_index] ){
+ has_loop[r] = lp;
+ break;
+ }
+ }
+ }
+ }
+ if( has_loop[0]!=-1 || has_loop[1]!=-1 ){
+ int loop_n_index = has_loop[0]!=-1 ? i : j;
+ int other_n_index = has_loop[0]!=-1 ? j : i;
+ int loop_index = has_loop[0]!=-1 ? has_loop[0] : has_loop[1];
+ int index = has_loop[0]!=-1 ? index_i : index_j;
+ int other_index = has_loop[0]!=-1 ? index_j : index_i;
+ Trace("strings-loop") << "Detected possible loop for " << normal_forms[loop_n_index][loop_index];
+ Trace("strings-loop") << " ... " << normal_forms[other_n_index][other_index] << std::endl;
+
+ //we have x * s1 * .... * sm = t1 * ... * tn * x * r1 * ... * rp
+ //check if
+ //t1 * ... * tn = n[loop_n_index][index]....n[loop_n_index][loop_index-1] = y * z
+ // and
+ //s1 * ... * sk = n[other_n_index][other_index+1].....n[other_n_index][k+1] = z * y
+ // for some y,z,k
+
+ Trace("strings-loop") << "Must add lemma." << std::endl;
+ //need to break
+ Node sk_y= NodeManager::currentNM()->mkSkolem( "ysym_$$", normal_forms[i][index_i].getType(), "created for loop detection split" );
+ Node sk_z= NodeManager::currentNM()->mkSkolem( "zsym_$$", normal_forms[i][index_i].getType(), "created for loop detection split" );
+
+ antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() );
+ //require that x is non-empty
+ Node x_empty = normal_forms[loop_n_index][loop_index].eqNode( d_emptyString );
+ x_empty = Rewriter::rewrite( x_empty );
+ //if( d_equalityEngine.hasTerm( d_emptyString ) && d_equalityEngine.areDisequal( normal_forms[loop_n_index][loop_index], d_emptyString, true ) ){
+ // antec.push_back( x_empty.negate() );
+ //}else{
+ antec_new_lits.push_back( x_empty.negate() );
+ //}
+ d_pending_req_phase[ x_empty ] = true;
+
+
+ //t1 * ... * tn = y * z
+ std::vector< Node > c1c;
+ //n[loop_n_index][index]....n[loop_n_index][loop_lindex-1]
+ for( int r=index; r<=loop_index-1; r++ ) {
+ c1c.push_back( normal_forms[loop_n_index][r] );
+ }
+ Node conc1 = mkConcat( c1c );
+ conc1 = NodeManager::currentNM()->mkNode( kind::EQUAL, conc1,
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk_y, sk_z ) );
+ std::vector< Node > c2c;
+ //s1 * ... * sk = n[other_n_index][other_index+1].....n[other_n_index][k+1]
+ for( int r=other_index+1; r < (int)normal_forms[other_n_index].size(); r++ ) {
+ c2c.push_back( normal_forms[other_n_index][r] );
+ }
+ Node left2 = mkConcat( c2c );
+ std::vector< Node > c3c;
+ c3c.push_back( sk_z );
+ c3c.push_back( sk_y );
+ //r1 * ... * rk = n[loop_n_index][loop_index+1]....n[loop_n_index][loop_index-1]
+ for( int r=loop_index+1; r < (int)normal_forms[loop_n_index].size(); r++ ) {
+ c3c.push_back( normal_forms[loop_n_index][r] );
+ }
+ Node conc2 = NodeManager::currentNM()->mkNode( kind::EQUAL, left2,
+ mkConcat( c3c ) );
+
+ Node sk_y_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk_y );
+ //Node sk_z_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk_z );
+ //Node len_y_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_y_len, d_zero);
+ //Node len_z_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_z_len, d_zero);
+ //Node len_y_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_y, d_emptyString);
+ //Node zz_imp_yz = NodeManager::currentNM()->mkNode( kind::IMPLIES, len_z_eq_zero, len_y_eq_zero);
+
+ //Node z_neq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_z, d_emptyString).negate();
+ //Node len_x_gt_len_y = NodeManager::currentNM()->mkNode( kind::GT,
+ // NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, normal_forms[other_n_index][other_index]),
+ // sk_y_len );
+ Node ant = mkExplain( antec, antec_new_lits );
+ conc = NodeManager::currentNM()->mkNode( kind::AND, conc1, conc2 );//, x_eq_y_rest );// , z_neq_empty //, len_x_gt_len_y
+
+ //Node x_eq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[other_n_index][other_index], d_emptyString);
+ //conc = NodeManager::currentNM()->mkNode( kind::OR, x_eq_empty, conc );
+
+ //we will be done
+ addNormalFormPair( normal_form_src[i], normal_form_src[j] );
+ sendLemma( ant, conc, "Loop" );
+ addInductiveEquation( normal_forms[other_n_index][other_index], sk_y, sk_z, ant, "Loop Induction" );
+ return;
+ }else{
+ Trace("strings-solve-debug") << "No loops detected." << std::endl;
+ if( normal_forms[i][index_i].getKind() == kind::CONST_STRING ||
+ normal_forms[j][index_j].getKind() == kind::CONST_STRING) {
+ unsigned const_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? i : j;
+ unsigned const_index_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? index_i : index_j;
+ unsigned nconst_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? j : i;
+ unsigned nconst_index_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? index_j : index_i;
+ Node const_str = normal_forms[const_k][const_index_k];
+ Node other_str = normal_forms[nconst_k][nconst_index_k];
+ if( other_str.getKind() == kind::CONST_STRING ) {
+ unsigned len_short = const_str.getConst<String>().size() <= other_str.getConst<String>().size() ? const_str.getConst<String>().size() : other_str.getConst<String>().size();
+ if( const_str.getConst<String>().strncmp(other_str.getConst<String>(), len_short) ) {
+ //same prefix
+ //k is the index of the string that is shorter
+ int k = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? i : j;
+ int index_k = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? index_i : index_j;
+ int l = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? j : i;
+ int index_l = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? index_j : index_i;
+ Node remainderStr = NodeManager::currentNM()->mkConst( normal_forms[l][index_l].getConst<String>().substr(len_short) );
+ Trace("strings-solve-debug-test") << "Break normal form of " << normal_forms[l][index_l] << " into " << normal_forms[k][index_k] << ", " << remainderStr << std::endl;
+ normal_forms[l].insert( normal_forms[l].begin()+index_l + 1, remainderStr );
+ normal_forms[l][index_l] = normal_forms[k][index_k];
+ success = true;
+ } else {
+ //curr_exp is conflict
+ antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() );
+ Node ant = mkExplain( antec, antec_new_lits );
+ sendLemma( ant, conc, "Conflict" );
+ return;
+ }
+ } else {
+ Assert( other_str.getKind()!=kind::STRING_CONCAT );
+ antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() );
+ Node firstChar = const_str.getConst<String>().size() == 1 ? const_str :
+ NodeManager::currentNM()->mkConst( const_str.getConst<String>().substr(0, 1) );
+ //split the string
+ Node sk = NodeManager::currentNM()->mkSkolem( "ssym_$$", normal_forms[i][index_i].getType(), "created for split" );
+
+ Node eq1 = NodeManager::currentNM()->mkNode( kind::EQUAL, other_str, d_emptyString );
+ Node eq2_m = NodeManager::currentNM()->mkNode( kind::EQUAL, other_str,
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, firstChar, sk ) );
+ Node eq2 = eq2_m;//NodeManager::currentNM()->mkNode( kind::AND, eq2_m, sk_len_geq_zero );
+ conc = NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 );
+ Trace("strings-solve-debug") << "Break normal form constant/variable " << std::endl;
+
+ Node ant = mkExplain( antec, antec_new_lits );
+ sendLemma( ant, conc, "Constant Split" );
+ return;
+ }
+ }else{
+ antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() );
+
+ Node ldeq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ).negate();
+ if( d_equalityEngine.areDisequal( length_term_i, length_term_j, true ) ){
+ antec.push_back( ldeq );
+ }else{
+ antec_new_lits.push_back(ldeq);
+ }
+ Node sk = NodeManager::currentNM()->mkSkolem( "ssym_$$", normal_forms[i][index_i].getType(), "created for split" );
+ Node eq1 = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[i][index_i],
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, normal_forms[j][index_j], sk ) );
+ Node eq2 = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[j][index_j],
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, normal_forms[i][index_i], sk ) );
+ conc = NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 );
+ // |sk| > 0
+ //Node sk_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk );
+ //Node sk_gt_zero = NodeManager::currentNM()->mkNode( kind::GT, sk_len, d_zero);
+ Node sk_gt_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, d_emptyString).negate();
+ Trace("strings-lemma") << "Strings lemma : " << sk_gt_zero << std::endl;
+ //d_out->lemma(sk_gt_zero);
+ d_lemma_cache.push_back( sk_gt_zero );
+
+ Node ant = mkExplain( antec, antec_new_lits );
+ sendLemma( ant, conc, "Split" );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }while(success);
+ }
+ }
+
+ //construct the normal form
+ if( normal_forms.empty() ){
+ Trace("strings-solve-debug2") << "construct the normal form" << std::endl;
+ nf.push_back( eqc );
+ } else {
+ Trace("strings-solve-debug2") << "just take the first normal form" << std::endl;
+ //just take the first normal form
+ nf.insert( nf.end(), normal_forms[0].begin(), normal_forms[0].end() );
+ nf_exp.insert( nf_exp.end(), normal_forms_exp[0].begin(), normal_forms_exp[0].end() );
+ if( eqc!=normal_form_src[0] ){
+ nf_exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, eqc, normal_form_src[0] ) );
+ }
+ Trace("strings-solve-debug2") << "just take the first normal form ... done" << std::endl;
+ }
+ //if( visited.empty() ){
+ //TODO : cache?
+ //}
+ d_normal_forms[eqc].insert( d_normal_forms[eqc].end(), nf.begin(), nf.end() );
+ d_normal_forms_exp[eqc].insert( d_normal_forms_exp[eqc].end(), nf_exp.begin(), nf_exp.end() );
+ Trace("strings-process") << "Return process equivalence class " << eqc << " : returned." << std::endl;
+ }else{
+ Trace("strings-process") << "Return process equivalence class " << eqc << " : already computed." << std::endl;
+ nf.insert( nf.end(), d_normal_forms[eqc].begin(), d_normal_forms[eqc].end() );
+ nf_exp.insert( nf_exp.end(), d_normal_forms_exp[eqc].begin(), d_normal_forms_exp[eqc].end() );
+ }
+ visited.pop_back();
+ }
+}
+
+bool TheoryStrings::normalizeDisequality( Node ni, Node nj ) {
+ //Assert( areDisequal( ni, nj ) );
+ if( d_normal_forms[ni].size()>1 || d_normal_forms[nj].size()>1 ){
+ unsigned index = 0;
+ while( index<d_normal_forms[ni].size() ){
+ Node i = d_normal_forms[ni][index];
+ Node j = d_normal_forms[nj][index];
+ Trace("strings-solve-debug") << "...Processing " << i << " " << j << std::endl;
+ if( !areEqual( i, j ) ){
+ Node li = getLength( i );
+ Node lj = getLength( j );
+ if( !areEqual(li, lj) ){
+ Trace("strings-solve") << "Case 2 : add lemma " << std::endl;
+ //must add lemma
+ std::vector< Node > antec;
+ std::vector< Node > antec_new_lits;
+ antec.insert( antec.end(), d_normal_forms_exp[ni].begin(), d_normal_forms_exp[ni].end() );
+ antec.insert( antec.end(), d_normal_forms_exp[nj].begin(), d_normal_forms_exp[nj].end() );
+ antec.push_back( ni.eqNode( nj ).negate() );
+ antec_new_lits.push_back( li.eqNode( lj ) );
+ std::vector< Node > conc;
+ Node sk1 = NodeManager::currentNM()->mkSkolem( "w1sym_$$", ni.getType(), "created for disequality normalization" );
+ Node sk2 = NodeManager::currentNM()->mkSkolem( "w2sym_$$", ni.getType(), "created for disequality normalization" );
+ Node sk3 = NodeManager::currentNM()->mkSkolem( "w3sym_$$", ni.getType(), "created for disequality normalization" );
+ Node sk4 = NodeManager::currentNM()->mkSkolem( "w4sym_$$", ni.getType(), "created for disequality normalization" );
+ Node sk5 = NodeManager::currentNM()->mkSkolem( "w5sym_$$", ni.getType(), "created for disequality normalization" );
+ Node w1w2w3 = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3 );
+ Node w1w4w5 = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk4, sk5 );
+ Node s_eq_w1w2w3 = NodeManager::currentNM()->mkNode( kind::EQUAL, ni, w1w2w3 );
+ conc.push_back( s_eq_w1w2w3 );
+ Node t_eq_w1w4w5 = NodeManager::currentNM()->mkNode( kind::EQUAL, nj, w1w4w5 );
+ conc.push_back( t_eq_w1w4w5 );
+ Node w2_neq_w4 = sk2.eqNode( sk4 ).negate();
+ conc.push_back( w2_neq_w4 );
+ Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational( 1 ) );
+ Node w2_len_one = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2), one);
+ conc.push_back( w2_len_one );
+ Node w4_len_one = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk4), one);
+ conc.push_back( w4_len_one );
+
+ //Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2),
+ // NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk4) );
+ //conc.push_back( eq );
+ sendLemma( mkExplain( antec, antec_new_lits ), NodeManager::currentNM()->mkNode( kind::AND, conc ), "Disequality Normalize" );
+ return true;
+ }else if( areDisequal( i, j ) ){
+ Trace("strings-solve") << "Case 1 : found equal length disequal sub strings " << i << " " << j << std::endl;
+ //we are done
+ return false;
+ }
+ }
+ index++;
+ }
+ Assert( false );
+ }
+ return false;
+}
+
+void TheoryStrings::addNormalFormPair( Node n1, Node n2 ) {
+ if( !isNormalFormPair( n1, n2 ) ){
+ NodeList* lst;
+ NodeListMap::iterator nf_i = d_nf_pairs.find( n1 );
+ if( nf_i == d_nf_pairs.end() ){
+ if( d_nf_pairs.find( n2 )!=d_nf_pairs.end() ){
+ addNormalFormPair( n2, n1 );
+ return;
+ }
+ lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false,
+ ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) );
+ d_nf_pairs.insertDataFromContextMemory( n1, lst );
+ Trace("strings-nf") << "Create cache for " << n1 << std::endl;
+ }else{
+ lst = (*nf_i).second;
+ }
+ Trace("strings-nf") << "Add normal form pair : " << n1 << " " << n2 << std::endl;
+ lst->push_back( n2 );
+ Assert( isNormalFormPair( n1, n2 ) );
+ }else{
+ Trace("strings-nf-debug") << "Already a normal form pair " << n1 << " " << n2 << std::endl;
+ }
+
+}
+bool TheoryStrings::isNormalFormPair( Node n1, Node n2 ) {
+ //TODO: modulo equality?
+ return isNormalFormPair2( n1, n2 ) || isNormalFormPair2( n2, n1 );
+}
+bool TheoryStrings::isNormalFormPair2( Node n1, Node n2 ) {
+ //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl;
+ NodeList* lst;
+ NodeListMap::iterator nf_i = d_nf_pairs.find( n1 );
+ if( nf_i != d_nf_pairs.end() ){
+ lst = (*nf_i).second;
+ for( NodeList::const_iterator i = lst->begin(); i != lst->end(); ++i ) {
+ Node n = *i;
+ if( n==n2 ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool TheoryStrings::addInductiveEquation( Node x, Node y, Node z, Node exp, const char * c ) {
+ Trace("strings-solve-debug") << "add inductive equation for " << x << " = (" << y << " " << z << ")* " << y << std::endl;
+#ifdef STR_UNROLL_INDUCTION
+ Node w = NodeManager::currentNM()->mkSkolem( "wsym_$$", x.getType(), "created for induction" );
+ Node x_eq_y_w = NodeManager::currentNM()->mkNode( kind::EQUAL, x,
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, y, w ) );
+ Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, exp, x_eq_y_w );
+ Trace("strings-lemma") << "Strings " << c << " lemma : " << lem << std::endl;
+ d_lemma_cache.push_back( lem );
+
+ //add initial induction
+ Node lit1 = w.eqNode( d_emptyString );
+ lit1 = Rewriter::rewrite( lit1 );
+ Node wp = NodeManager::currentNM()->mkSkolem( "wpsym_$$", x.getType(), "created for induction" );
+ Node lit2 = w.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, z, y, wp ) );
+ lit2 = Rewriter::rewrite( lit2 );
+ Node split_lem = NodeManager::currentNM()->mkNode( kind::OR, lit1, lit2 );
+ Trace("strings-ind") << "Strings : Lemma " << c << " for unrolling " << split_lem << std::endl;
+ Trace("strings-lemma") << "Strings : Lemma " << c << " for unrolling " << split_lem << std::endl;
+ d_lemma_cache.push_back( split_lem );
+
+ //d_lit_to_decide.push_back( lit1 );
+ d_lit_to_unroll[lit2] = true;
+ d_pending_req_phase[lit1] = true;
+ d_pending_req_phase[lit2] = false;
+
+ x = w;
+ std::vector< Node > skc;
+ skc.push_back( y );
+ skc.push_back( z );
+ y = d_emptyString;
+ z = mkConcat( skc );
+#endif
+
+ NodeListMap::iterator itr_x_y = d_ind_map1.find(x);
+ NodeList* lst1;
+ NodeList* lst2;
+ NodeList* lste;
+ NodeList* lstl;
+ if( itr_x_y == d_ind_map1.end() ) {
+ // add x->y
+ lst1 = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false,
+ ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) );
+ d_ind_map1.insertDataFromContextMemory( x, lst1 );
+ // add x->z
+ lst2 = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false,
+ ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) );
+ d_ind_map2.insertDataFromContextMemory( x, lst2 );
+ // add x->exp
+ lste = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false,
+ ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) );
+ d_ind_map_exp.insertDataFromContextMemory( x, lste );
+ // add x->hasLemma false
+ lstl = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false,
+ ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) );
+ d_ind_map_lemma.insertDataFromContextMemory( x, lstl );
+ } else {
+ //TODO: x in (yz)*y (exp) vs x in (y1 z1)*y1 (exp1)
+ lst1 = (*itr_x_y).second;
+ lst2 = (*d_ind_map2.find(x)).second;
+ lste = (*d_ind_map_exp.find(x)).second;
+ lstl = (*d_ind_map_lemma.find(x)).second;
+ Trace("strings-solve-debug") << "Already in maps " << x << " = (" << lst1 << " " << lst2 << ")* " << lst1 << std::endl;
+ Trace("strings-solve-debug") << "... with exp = " << lste << std::endl;
+ }
+ lst1->push_back( y );
+ lst2->push_back( z );
+ lste->push_back( exp );
+#ifdef STR_UNROLL_INDUCTION
+ return true;
+#else
+ return false;
+#endif
+}
+
+void TheoryStrings::sendLemma( Node ant, Node conc, const char * c ) {
+ if( conc.isNull() ){
+ d_out->conflict(ant);
+ Trace("strings-conflict") << "CONFLICT : Strings conflict : " << ant << std::endl;
+ d_conflict = true;
+ }else{
+ Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, ant, conc );
+ Trace("strings-lemma") << "Strings " << c << " lemma : " << lem << std::endl;
+ d_lemma_cache.push_back( lem );
+ }
+}
+
+void TheoryStrings::sendSplit( Node a, Node b, const char * c ) {
+ Node eq = a.eqNode( b );
+ eq = Rewriter::rewrite( eq );
+ Node neq = NodeManager::currentNM()->mkNode( kind::NOT, eq );
+ Node lemma_or = NodeManager::currentNM()->mkNode( kind::OR, eq, neq );
+ Trace("strings-lemma") << "Strings " << c << " split lemma : " << lemma_or << std::endl;
+ d_lemma_cache.push_back(lemma_or);
+ d_pending_req_phase[eq] = true;
+}
+
+Node TheoryStrings::mkConcat( std::vector< Node >& c ) {
+ Node cc = c.size()>1 ? NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, c ) : ( c.size()==1 ? c[0] : d_emptyString );
+ return Rewriter::rewrite( cc );
+}
+
+Node TheoryStrings::mkExplain( std::vector< Node >& a, std::vector< Node >& an ) {
+ std::vector< TNode > antec_exp;
+ for( unsigned i=0; i<a.size(); i++ ){
+ Trace("strings-solve-debug") << "Ask for explanation of " << a[i] << std::endl;
+ //assert
+ if(a[i].getKind() == kind::EQUAL) {
+ //assert( hasTerm(a[i][0]) );
+ //assert( hasTerm(a[i][1]) );
+ Assert( areEqual(a[i][0], a[i][1]) );
+ } else if( a[i].getKind()==kind::NOT && a[i][0].getKind()==kind::EQUAL ){
+ Assert( hasTerm(a[i][0][0]) );
+ Assert( hasTerm(a[i][0][1]) );
+ Assert( d_equalityEngine.areDisequal(a[i][0][0], a[i][0][1], true) );
+ }
+ unsigned ps = antec_exp.size();
+ explain(a[i], antec_exp);
+ Trace("strings-solve-debug") << "Done, explanation was : " << std::endl;
+ for( unsigned j=ps; j<antec_exp.size(); j++ ){
+ Trace("strings-solve-debug") << " " << antec_exp[j] << std::endl;
+ }
+ Trace("strings-solve-debug") << std::endl;
+ }
+ for( unsigned i=0; i<an.size(); i++ ){
+ Trace("strings-solve-debug") << "Add to explanation (new literal) " << an[i] << std::endl;
+ antec_exp.push_back(an[i]);
+ }
+ Node ant;
+ if( antec_exp.empty() ) {
+ ant = d_true;
+ } else if( antec_exp.size()==1 ) {
+ ant = antec_exp[0];
+ } else {
+ ant = NodeManager::currentNM()->mkNode( kind::AND, antec_exp );
+ }
+ ant = Rewriter::rewrite( ant );
+ return ant;
+}
+
+bool TheoryStrings::checkNormalForms() {
+ Trace("strings-process") << "Normalize equivalence classes...." << std::endl;
+ eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator( &d_equalityEngine );
+ for( unsigned t=0; t<2; t++ ){
+ Trace("strings-eqc") << (t==0 ? "STRINGS:" : "OTHER:") << std::endl;
+ while( !eqcs2_i.isFinished() ){
+ Node eqc = (*eqcs2_i);
+ bool print = (t==0 && eqc.getType().isString() ) || (t==1 && !eqc.getType().isString() );
+ if (print) {
+ eq::EqClassIterator eqc2_i = eq::EqClassIterator( eqc, &d_equalityEngine );
+ Trace("strings-eqc") << "Eqc( " << eqc << " ) : ";
+ while( !eqc2_i.isFinished() ) {
+ if( (*eqc2_i)!=eqc ){
+ Trace("strings-eqc") << (*eqc2_i) << " ";
+ }
+ ++eqc2_i;
+ }
+ Trace("strings-eqc") << std::endl;
+ }
+ ++eqcs2_i;
+ }
+ Trace("strings-eqc") << std::endl;
+ }
+ Trace("strings-eqc") << std::endl;
+ for( NodeListMap::const_iterator it = d_nf_pairs.begin(); it != d_nf_pairs.end(); ++it ){
+ NodeList* lst = (*it).second;
+ NodeList::const_iterator it2 = lst->begin();
+ Trace("strings-nf") << (*it).first << " has been unified with ";
+ while( it2!=lst->end() ){
+ Trace("strings-nf") << (*it2);
+ ++it2;
+ }
+ Trace("strings-nf") << std::endl;
+ }
+ Trace("strings-nf") << std::endl;
+ Trace("strings-nf") << "Current inductive equations : " << std::endl;
+ for( NodeListMap::const_iterator it = d_ind_map1.begin(); it != d_ind_map1.end(); ++it ){
+ Node x = (*it).first;
+ NodeList* lst1 = (*it).second;
+ NodeList* lst2 = (*d_ind_map2.find(x)).second;
+ NodeList::const_iterator i1 = lst1->begin();
+ NodeList::const_iterator i2 = lst2->begin();
+ while( i1!=lst1->end() ){
+ Node y = *i1;
+ Node z = *i2;
+ Trace("strings-nf") << "Inductive equation : " << x << " = ( " << y << " ++ " << z << " ) * " << y << std::endl;
+ ++i1;
+ ++i2;
+ }
+ }
+
+ bool addedFact;
+ do {
+ Trace("strings-process") << "Check Normal Forms........next round" << std::endl;
+ //calculate normal forms for each equivalence class, possibly adding splitting lemmas
+ d_normal_forms.clear();
+ d_normal_forms_exp.clear();
+ std::map< Node, Node > nf_to_eqc;
+ std::map< Node, Node > eqc_to_exp;
+ d_lemma_cache.clear();
+ d_pending_req_phase.clear();
+ //get equivalence classes
+ std::vector< Node > eqcs;
+ getEquivalenceClasses( eqcs );
+ for( unsigned i=0; i<eqcs.size(); i++ ){
+ Node eqc = eqcs[i];
+ Trace("strings-process") << "- Verify normal forms are the same for " << eqc << std::endl;
+ std::vector< Node > visited;
+ std::vector< Node > nf;
+ std::vector< Node > nf_exp;
+ normalizeEquivalenceClass(eqc, visited, nf, nf_exp);
+ if( d_conflict ){
+ return true;
+ }else if ( d_pending.empty() && d_lemma_cache.empty() ){
+ Node nf_term;
+ if( nf.size()==0 ){
+ nf_term = d_emptyString;
+ }else if( nf.size()==1 ) {
+ nf_term = nf[0];
+ } else {
+ nf_term = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, nf );
+ }
+ nf_term = Rewriter::rewrite( nf_term );
+ Trace("strings-debug") << "Make nf_term_exp..." << std::endl;
+ Node nf_term_exp = nf_exp.empty() ? d_true :
+ nf_exp.size()==1 ? nf_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, nf_exp );
+ if( nf_to_eqc.find(nf_term)!=nf_to_eqc.end() ){
+ //Trace("strings-debug") << "Merge because of normal form : " << eqc << " and " << nf_to_eqc[nf_term] << " both have normal form " << nf_term << std::endl;
+ //two equivalence classes have same normal form, merge
+ Node eq_exp = NodeManager::currentNM()->mkNode( kind::AND, nf_term_exp, eqc_to_exp[nf_to_eqc[nf_term]] );
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, eqc, nf_to_eqc[nf_term] );
+ Trace("strings-lemma") << "Strings (by normal forms) : Infer " << eq << " from " << eq_exp << std::endl;
+ //d_equalityEngine.assertEquality( eq, true, eq_exp );
+ d_pending.push_back( eq );
+ d_pending_exp[eq] = eq_exp;
+ d_infer.push_back(eq);
+ d_infer_exp.push_back(eq_exp);
+ }else{
+ nf_to_eqc[nf_term] = eqc;
+ eqc_to_exp[eqc] = nf_term_exp;
+ }
+ }
+ Trace("strings-process") << "Done verifying normal forms are the same for " << eqc << std::endl;
+ }
+
+ Trace("strings-nf-debug") << "**** Normal forms are : " << std::endl;
+ for( std::map< Node, Node >::iterator it = nf_to_eqc.begin(); it != nf_to_eqc.end(); ++it ){
+ Trace("strings-nf-debug") << " normal_form(" << it->second << ") = " << it->first << std::endl;
+ }
+ Trace("strings-nf-debug") << std::endl;
+ addedFact = !d_pending.empty();
+ doPendingFacts();
+ } while ( !d_conflict && d_lemma_cache.empty() && addedFact );
+
+
+ //process disequalities between equivalence classes
+ if( !d_conflict && d_lemma_cache.empty() ){
+ std::vector< Node > eqcs;
+ getEquivalenceClasses( eqcs );
+ std::vector< std::vector< Node > > cols;
+ std::vector< Node > lts;
+ seperateByLength( eqcs, cols, lts );
+ for( unsigned i=0; i<cols.size(); i++ ){
+ if( cols[i].size()>1 && d_lemma_cache.empty() ){
+ Trace("strings-solve") << "- Verify disequalities are processed for ";
+ printConcat( d_normal_forms[cols[i][0]], "strings-solve" );
+ Trace("strings-solve") << "..." << std::endl;
+ //must ensure that normal forms are disequal
+ for( unsigned j=1; j<cols[i].size(); j++ ){
+ if( !d_equalityEngine.areDisequal( cols[i][0], cols[i][j], false ) ){
+ sendSplit( cols[i][0], cols[i][j], "Disequality Normalization" );
+ break;
+ }else{
+ Trace("strings-solve") << " against ";
+ printConcat( d_normal_forms[cols[i][j]], "strings-solve" );
+ Trace("strings-solve") << "..." << std::endl;
+ if( normalizeDisequality( cols[i][0], cols[i][j] ) ){
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //flush pending lemmas
+ if( !d_conflict && !d_lemma_cache.empty() ){
+ doPendingLemmas();
+ return true;
+ }else{
+ return false;
+ }
+}
+
+bool TheoryStrings::checkCardinality() {
+ int cardinality = options::stringCharCardinality();
+ Trace("strings-solve-debug2") << "get cardinality: " << cardinality << endl;
+
+ std::vector< Node > eqcs;
+ getEquivalenceClasses( eqcs );
+
+ std::vector< std::vector< Node > > cols;
+ std::vector< Node > lts;
+ seperateByLength( eqcs, cols, lts );
+
+ for( unsigned i = 0; i<cols.size(); ++i ){
+ Node lr = lts[i];
+ Trace("string-cardinality") << "Number of strings with length equal to " << lr << " is " << cols[i].size() << std::endl;
+ // size > c^k
+ double k = std::log( cols[i].size() ) / log((double) cardinality);
+ unsigned int int_k = (unsigned int)k;
+ Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) );
+ //double c_k = pow ( (double)cardinality, (double)lr );
+ if( cols[i].size() > 1 ) {
+ bool allDisequal = true;
+ for( std::vector< Node >::iterator itr1 = cols[i].begin();
+ itr1 != cols[i].end(); ++itr1) {
+ for( std::vector< Node >::iterator itr2 = itr1 + 1;
+ itr2 != cols[i].end(); ++itr2) {
+ if(!d_equalityEngine.areDisequal( *itr1, *itr2, false )) {
+ allDisequal = false;
+ // add split lemma
+ sendSplit( *itr1, *itr2, "Cardinality" );
+ doPendingLemmas();
+ return true;
+ }
+ }
+ }
+ if(allDisequal) {
+ EqcInfo* ei = getOrMakeEqcInfo( lr, true );
+ Trace("string-cardinality") << "Previous cardinality used for " << lr << " is " << ((int)ei->d_cardinality_lem_k.get()-1) << std::endl;
+ if( int_k+1 > ei->d_cardinality_lem_k.get() ){
+ //add cardinality lemma
+ Node dist = NodeManager::currentNM()->mkNode( kind::DISTINCT, cols[i] );
+ std::vector< Node > vec_node;
+ vec_node.push_back( dist );
+ for( std::vector< Node >::iterator itr1 = cols[i].begin();
+ itr1 != cols[i].end(); ++itr1) {
+ Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr1 );
+ if( len!=lr ){
+ Node len_eq_lr = NodeManager::currentNM()->mkNode( kind::EQUAL, lr, len );
+ vec_node.push_back( len_eq_lr );
+ }
+ }
+ Node antc = NodeManager::currentNM()->mkNode( kind::AND, vec_node );
+ Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] );
+ Node cons = NodeManager::currentNM()->mkNode( kind::GT, len, k_node );
+ /*
+ sendLemma( antc, cons, "Cardinality" );
+ ei->d_cardinality_lem_k.set( int_k+1 );
+ if( !d_lemma_cache.empty() ){
+ doPendingLemmas();
+ return true;
+ }
+ */
+ Node lemma = NodeManager::currentNM()->mkNode( kind::IMPLIES, antc, cons );
+ lemma = Rewriter::rewrite( lemma );
+ ei->d_cardinality_lem_k.set( int_k+1 );
+ if( lemma!=d_true ){
+ Trace("strings-lemma") << "Strings cardinality lemma : " << lemma << std::endl;
+ d_out->lemma(lemma);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+int TheoryStrings::gcd ( int a, int b ) {
+ int c;
+ while ( a != 0 ) {
+ c = a; a = b%a; b = c;
+ }
+ return b;
+}
+
+bool TheoryStrings::checkInductiveEquations() {
+ bool hasEq = false;
+ if(d_ind_map1.size() != 0){
+ Trace("strings-ind") << "We are sat, with these inductive equations : " << std::endl;
+ for( NodeListMap::const_iterator it = d_ind_map1.begin(); it != d_ind_map1.end(); ++it ){
+ Node x = (*it).first;
+ Trace("strings-ind-debug") << "Check eq for " << x << std::endl;
+ NodeList* lst1 = (*it).second;
+ NodeList* lst2 = (*d_ind_map2.find(x)).second;
+ NodeList* lste = (*d_ind_map_exp.find(x)).second;
+ //NodeList* lstl = (*d_ind_map_lemma.find(x)).second;
+ NodeList::const_iterator i1 = lst1->begin();
+ NodeList::const_iterator i2 = lst2->begin();
+ NodeList::const_iterator ie = lste->begin();
+ //NodeList::const_iterator il = lstl->begin();
+ while( i1!=lst1->end() ){
+ Node y = *i1;
+ Node z = *i2;
+ //Trace("strings-ind-debug") << "Check y=" << y << " , z=" << z << std::endl;
+ //if( il==lstl->end() ) {
+ std::vector< Node > nf_y, nf_z, exp_y, exp_z;
+
+ //getFinalNormalForm( y, nf_y, exp_y);
+ //getFinalNormalForm( z, nf_z, exp_z);
+ //std::vector< Node > vec_empty;
+ //Node nexp_y = mkExplain( exp_y, vec_empty );
+ //Trace("strings-ind-debug") << "Check nexp_y=" << nexp_y << std::endl;
+ //Node nexp_z = mkExplain( exp_z, vec_empty );
+
+ //Node exp = *ie;
+ //Trace("strings-ind-debug") << "Check exp=" << exp << std::endl;
+
+ //exp = NodeManager::currentNM()->mkNode( kind::AND, exp, nexp_y, nexp_z );
+ //exp = Rewriter::rewrite( exp );
+
+ Trace("strings-ind") << "Inductive equation : " << x << " = ( " << y << " ++ " << z << " )* " << y << std::endl;
+ /*
+ for( std::vector< Node >::const_iterator itr = nf_y.begin(); itr != nf_y.end(); ++itr) {
+ Trace("strings-ind") << (*itr) << " ";
+ }
+ Trace("strings-ind") << " ++ ";
+ for( std::vector< Node >::const_iterator itr = nf_z.begin(); itr != nf_z.end(); ++itr) {
+ Trace("strings-ind") << (*itr) << " ";
+ }
+ Trace("strings-ind") << " )* ";
+ for( std::vector< Node >::const_iterator itr = nf_y.begin(); itr != nf_y.end(); ++itr) {
+ Trace("strings-ind") << (*itr) << " ";
+ }
+ Trace("strings-ind") << std::endl;
+ */
+ /*
+ Trace("strings-ind") << "Explanation is : " << exp << std::endl;
+ std::vector< Node > nf_yz;
+ nf_yz.insert( nf_yz.end(), nf_y.begin(), nf_y.end() );
+ nf_yz.insert( nf_yz.end(), nf_z.begin(), nf_z.end() );
+ std::vector< std::vector< Node > > cols;
+ std::vector< Node > lts;
+ seperateByLength( nf_yz, cols, lts );
+ Trace("strings-ind") << "This can be grouped into collections : " << std::endl;
+ for( unsigned j=0; j<cols.size(); j++ ){
+ Trace("strings-ind") << " : ";
+ for( unsigned k=0; k<cols[j].size(); k++ ){
+ Trace("strings-ind") << cols[j][k] << " ";
+ }
+ Trace("strings-ind") << std::endl;
+ }
+ Trace("strings-ind") << std::endl;
+
+ Trace("strings-ind") << "Add length lemma..." << std::endl;
+ std::vector< int > co;
+ co.push_back(0);
+ for(unsigned int k=0; k<lts.size(); ++k) {
+ if(lts[k].isConst() && lts[k].getType().isInteger()) {
+ int len = lts[k].getConst<Rational>().getNumerator().toUnsignedInt();
+ co[0] += cols[k].size() * len;
+ } else {
+ co.push_back( cols[k].size() );
+ }
+ }
+ int g_co = co[0];
+ for(unsigned k=1; k<co.size(); ++k) {
+ g_co = gcd(g_co, co[k]);
+ }
+ Node lemma_len;
+ // both constants
+ Node len_x = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, x );
+ Node sk = NodeManager::currentNM()->mkSkolem( "argsym_$$", NodeManager::currentNM()->integerType(), "created for length inductive lemma" );
+ Node g_co_node = NodeManager::currentNM()->mkConst( CVC4::Rational(g_co) );
+ Node sk_m_gcd = NodeManager::currentNM()->mkNode( kind::MULT, g_co_node, sk );
+ Node len_y = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, y );
+ Node sk_m_g_p_y = NodeManager::currentNM()->mkNode( kind::PLUS, sk_m_gcd, len_y );
+ lemma_len = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_m_g_p_y, len_x );
+ //Node sk_geq_zero = NodeManager::currentNM()->mkNode( kind::GEQ, sk, d_zero );
+ //lemma_len = NodeManager::currentNM()->mkNode( kind::AND, lemma_len, sk_geq_zero );
+ lemma_len = NodeManager::currentNM()->mkNode( kind::IMPLIES, exp, lemma_len );
+ Trace("strings-lemma") << "Strings: Add lemma " << lemma_len << std::endl;
+ d_out->lemma(lemma_len);
+ lstl->push_back( d_true );
+ return true;*/
+ //}
+ ++i1;
+ ++i2;
+ ++ie;
+ //++il;
+ if( !d_equalityEngine.hasTerm( d_emptyString ) || !d_equalityEngine.areEqual( y, d_emptyString ) || !d_equalityEngine.areEqual( x, d_emptyString ) ){
+ hasEq = true;
+ }
+ }
+ }
+ }
+ if( hasEq ){
+ Trace("strings-ind") << "It is incomplete." << std::endl;
+ d_out->setIncomplete();
+ }else{
+ Trace("strings-ind") << "We can answer SAT." << std::endl;
+ }
+ return false;
+}
+
+void TheoryStrings::getEquivalenceClasses( std::vector< Node >& eqcs ) {
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine );
+ while( !eqcs_i.isFinished() ) {
+ Node eqc = (*eqcs_i);
+ //if eqc.getType is string
+ if (eqc.getType().isString()) {
+ eqcs.push_back( eqc );
+ }
+ ++eqcs_i;
+ }
+}
+
+void TheoryStrings::getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp ) {
+ if( n!=d_emptyString ){
+ if( n.getKind()==kind::STRING_CONCAT ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ getFinalNormalForm( n[i], nf, exp );
+ }
+ }else{
+ Trace("strings-debug") << "Get final normal form " << n << std::endl;
+ Assert( d_equalityEngine.hasTerm( n ) );
+ Node nr = d_equalityEngine.getRepresentative( n );
+ EqcInfo *eqc_n = getOrMakeEqcInfo( nr, false );
+ Node nc = eqc_n ? eqc_n->d_const_term.get() : Node::null();
+ if( !nc.isNull() ){
+ nf.push_back( nc );
+ if( n!=nc ){
+ exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nc ) );
+ }
+ }else{
+ Assert( d_normal_forms.find( nr )!=d_normal_forms.end() );
+ if( d_normal_forms[nr][0]==nr ){
+ Assert( d_normal_forms[nr].size()==1 );
+ nf.push_back( nr );
+ if( n!=nr ){
+ exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nr ) );
+ }
+ }else{
+ for( unsigned i=0; i<d_normal_forms[nr].size(); i++ ){
+ Assert( d_normal_forms[nr][i]!=nr );
+ getFinalNormalForm( d_normal_forms[nr][i], nf, exp );
+ }
+ exp.insert( exp.end(), d_normal_forms_exp[nr].begin(), d_normal_forms_exp[nr].end() );
+ }
+ }
+ Trace("strings-ind-nf") << "The final normal form of " << n << " is " << nf << std::endl;
+ }
+ }
+}
+
+void TheoryStrings::seperateByLength( std::vector< Node >& n, std::vector< std::vector< Node > >& cols,
+ std::vector< Node >& lts ) {
+ unsigned leqc_counter = 0;
+ std::map< Node, unsigned > eqc_to_leqc;
+ std::map< unsigned, Node > leqc_to_eqc;
+ std::map< unsigned, std::vector< Node > > eqc_to_strings;
+ for( unsigned i=0; i<n.size(); i++ ){
+ Node eqc = n[i];
+ Assert( d_equalityEngine.getRepresentative(eqc)==eqc );
+ EqcInfo* ei = getOrMakeEqcInfo( eqc, false );
+ Node lt = ei ? ei->d_length_term : Node::null();
+ if( !lt.isNull() ){
+ lt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt );
+ Node r = d_equalityEngine.getRepresentative( lt );
+ if( eqc_to_leqc.find( r )==eqc_to_leqc.end() ){
+ eqc_to_leqc[r] = leqc_counter;
+ leqc_to_eqc[leqc_counter] = r;
+ leqc_counter++;
+ }
+ eqc_to_strings[ eqc_to_leqc[r] ].push_back( eqc );
+ }else{
+ eqc_to_strings[leqc_counter].push_back( eqc );
+ leqc_counter++;
+ }
+ }
+ for( std::map< unsigned, std::vector< Node > >::iterator it = eqc_to_strings.begin(); it != eqc_to_strings.end(); ++it ){
+ std::vector< Node > vec;
+ vec.insert( vec.end(), it->second.begin(), it->second.end() );
+ lts.push_back( leqc_to_eqc[it->first] );
+ cols.push_back( vec );
+ }
+}
+
+void TheoryStrings::printConcat( std::vector< Node >& n, const char * c ) {
+ for( unsigned i=0; i<n.size(); i++ ){
+ if( i>0 ) Trace(c) << " ++ ";
+ Trace(c) << n[i];
+ }
+}
+
+/*
+Node TheoryStrings::getNextDecisionRequest() {
+ if( d_lit_to_decide_index.get()<d_lit_to_decide.size() ){
+ Node l = d_lit_to_decide[d_lit_to_decide_index.get()];
+ d_lit_to_decide_index.set( d_lit_to_decide_index.get() + 1 );
+ Trace("strings-ind") << "Strings-ind : decide on " << l << std::endl;
+ return l;
+ }else{
+ return Node::null();
+ }
+}
+*/
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h
new file mode 100644
index 000000000..6b8144d6e
--- /dev/null
+++ b/src/theory/strings/theory_strings.h
@@ -0,0 +1,244 @@
+/********************* */
+/*! \file theory_strings.h
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Theory of strings
+ **
+ ** Theory of strings.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__STRINGS__THEORY_STRINGS_H
+#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_H
+
+#include "theory/theory.h"
+#include "theory/uf/equality_engine.h"
+
+#include "context/cdchunk_list.h"
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+/**
+ * Decision procedure for strings.
+ *
+ */
+
+class TheoryStrings : public Theory {
+ typedef context::CDChunkList<Node> NodeList;
+ typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap;
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+ public:
+
+ TheoryStrings(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe);
+ ~TheoryStrings();
+
+ void setMasterEqualityEngine(eq::EqualityEngine* eq);
+
+ std::string identify() const { return std::string("TheoryStrings"); }
+
+ Node getRepresentative( Node t );
+ bool hasTerm( Node a );
+ bool areEqual( Node a, Node b );
+ bool areDisequal( Node a, Node b );
+ Node getLength( Node t );
+ public:
+
+ void propagate(Effort e);
+ bool propagate(TNode literal);
+ void explain( TNode literal, std::vector<TNode>& assumptions );
+ Node explain( TNode literal );
+
+
+ // NotifyClass for equality engine
+ class NotifyClass : public eq::EqualityEngineNotify {
+ TheoryStrings& d_str;
+ public:
+ NotifyClass(TheoryStrings& t_str): d_str(t_str) {}
+ bool eqNotifyTriggerEquality(TNode equality, bool value) {
+ Debug("strings") << "NotifyClass::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false" )<< ")" << std::endl;
+ if (value) {
+ return d_str.propagate(equality);
+ } else {
+ // We use only literal triggers so taking not is safe
+ return d_str.propagate(equality.notNode());
+ }
+ }
+ bool eqNotifyTriggerPredicate(TNode predicate, bool value) {
+ Debug("strings") << "NotifyClass::eqNotifyTriggerPredicate(" << predicate << ", " << (value ? "true" : "false") << ")" << std::endl;
+ if (value) {
+ return d_str.propagate(predicate);
+ } else {
+ return d_str.propagate(predicate.notNode());
+ }
+ }
+ bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
+ Debug("strings") << "NotifyClass::eqNotifyTriggerTermMerge(" << tag << ", " << t1 << ", " << t2 << ")" << std::endl;
+ if (value) {
+ return d_str.propagate(t1.eqNode(t2));
+ } else {
+ return d_str.propagate(t1.eqNode(t2).notNode());
+ }
+ }
+ void eqNotifyConstantTermMerge(TNode t1, TNode t2) {
+ Debug("strings") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl;
+ d_str.conflict(t1, t2);
+ }
+ void eqNotifyNewClass(TNode t) {
+ Debug("strings") << "NotifyClass::eqNotifyNewClass(" << t << std::endl;
+ d_str.eqNotifyNewClass(t);
+ }
+ void eqNotifyPreMerge(TNode t1, TNode t2) {
+ Debug("strings") << "NotifyClass::eqNotifyPreMerge(" << t1 << ", " << t2 << std::endl;
+ d_str.eqNotifyPreMerge(t1, t2);
+ }
+ void eqNotifyPostMerge(TNode t1, TNode t2) {
+ Debug("strings") << "NotifyClass::eqNotifyPostMerge(" << t1 << ", " << t2 << std::endl;
+ d_str.eqNotifyPostMerge(t1, t2);
+ }
+ void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
+ Debug("strings") << "NotifyClass::eqNotifyDisequal(" << t1 << ", " << t2 << ", " << reason << std::endl;
+ d_str.eqNotifyDisequal(t1, t2, reason);
+ }
+ };/* class TheoryStrings::NotifyClass */
+
+ private:
+ /** The notify class */
+ NotifyClass d_notify;
+ /** Equaltity engine */
+ eq::EqualityEngine d_equalityEngine;
+ /** Are we in conflict */
+ context::CDO<bool> d_conflict;
+ std::vector< Node > d_length_intro_vars;
+ Node d_emptyString;
+ Node d_true;
+ Node d_false;
+ Node d_zero;
+ //list of pairs of nodes to merge
+ std::map< Node, Node > d_pending_exp;
+ std::vector< Node > d_pending;
+ std::vector< Node > d_lemma_cache;
+ std::map< Node, bool > d_pending_req_phase;
+ /** inferences */
+ NodeList d_infer;
+ NodeList d_infer_exp;
+ //map of pairs of terms that have the same normal form
+ NodeListMap d_nf_pairs;
+ void addNormalFormPair( Node n1, Node n2 );
+ bool isNormalFormPair( Node n1, Node n2 );
+ bool isNormalFormPair2( Node n1, Node n2 );
+
+ NodeListMap d_ind_map1;
+ NodeListMap d_ind_map2;
+ NodeListMap d_ind_map_exp;
+ NodeListMap d_ind_map_lemma;
+ bool addInductiveEquation( Node x, Node y, Node z, Node exp, const char * c );
+
+ //for unrolling inductive equations
+ NodeBoolMap d_lit_to_unroll;
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ // MODEL GENERATION
+ /////////////////////////////////////////////////////////////////////////////
+ public:
+
+ void collectModelInfo(TheoryModel* m, bool fullModel);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // NOTIFICATIONS
+ /////////////////////////////////////////////////////////////////////////////
+
+ public:
+
+ void shutdown() { }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // MAIN SOLVER
+ /////////////////////////////////////////////////////////////////////////////
+ private:
+ void addSharedTerm(TNode n);
+ EqualityStatus getEqualityStatus(TNode a, TNode b);
+
+ private:
+ class EqcInfo
+ {
+ public:
+ EqcInfo( context::Context* c );
+ ~EqcInfo(){}
+ //constant in this eqc
+ context::CDO< Node > d_const_term;
+ context::CDO< Node > d_length_term;
+ context::CDO< unsigned > d_cardinality_lem_k;
+ };
+ /** map from representatives to information necessary for equivalence classes */
+ std::map< Node, EqcInfo* > d_eqc_info;
+ EqcInfo * getOrMakeEqcInfo( Node eqc, bool doMake = true );
+ //maintain which concat terms have the length lemma instantiatied
+ std::map< Node, bool > d_length_inst;
+ private:
+ std::map< Node, std::vector< Node > > d_normal_forms;
+ std::map< Node, std::vector< Node > > d_normal_forms_exp;
+ void getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf,
+ std::vector< std::vector< Node > > &normal_forms, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< Node > &normal_form_src);
+ void normalizeEquivalenceClass( Node n, std::vector< Node > & visited, std::vector< Node > & nf, std::vector< Node > & nf_exp );
+ bool normalizeDisequality( Node n1, Node n2 );
+
+ bool checkNormalForms();
+ bool checkCardinality();
+ bool checkInductiveEquations();
+ int gcd(int a, int b);
+ public:
+ void preRegisterTerm(TNode n);
+ void check(Effort e);
+
+ /** Conflict when merging two constants */
+ void conflict(TNode a, TNode b);
+ /** called when a new equivalance class is created */
+ void eqNotifyNewClass(TNode t);
+ /** called when two equivalance classes will merge */
+ void eqNotifyPreMerge(TNode t1, TNode t2);
+ /** called when two equivalance classes have merged */
+ void eqNotifyPostMerge(TNode t1, TNode t2);
+ /** called when two equivalence classes are made disequal */
+ void eqNotifyDisequal(TNode t1, TNode t2, TNode reason);
+protected:
+ /** compute care graph */
+ void computeCareGraph();
+
+ //do pending merges
+ void doPendingFacts();
+ void doPendingLemmas();
+
+ void sendLemma( Node ant, Node conc, const char * c );
+ void sendSplit( Node a, Node b, const char * c );
+ /** mkConcat **/
+ Node mkConcat( std::vector< Node >& c );
+ /** mkExplain **/
+ Node mkExplain( std::vector< Node >& a, std::vector< Node >& an );
+
+ //get equivalence classes
+ void getEquivalenceClasses( std::vector< Node >& eqcs );
+ //get final normal form
+ void getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp );
+
+ //seperate into collections with equal length
+ void seperateByLength( std::vector< Node >& n, std::vector< std::vector< Node > >& col, std::vector< Node >& lts );
+private:
+ void printConcat( std::vector< Node >& n, const char * c );
+};/* class TheoryStrings */
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_H */
diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp
new file mode 100644
index 000000000..8fa4345e5
--- /dev/null
+++ b/src/theory/strings/theory_strings_preprocess.cpp
@@ -0,0 +1,135 @@
+/********************* */
+/*! \file theory_strings_preprocess.cpp
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: Tianyi Liang, Andrew Reynolds
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Strings Preprocess
+ **
+ ** Strings Preprocess.
+ **/
+
+#include "theory/strings/theory_strings_preprocess.h"
+#include "expr/kind.h"
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+void StringsPreprocess::simplifyRegExp( Node s, Node r, std::vector< Node > &ret ) {
+ int k = r.getKind();
+ switch( k ) {
+ case kind::STRING_TO_REGEXP:
+ {
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, s, r[0] );
+ ret.push_back( eq );
+ }
+ break;
+ case kind::REGEXP_CONCAT:
+ {
+ std::vector< Node > cc;
+ for(unsigned i=0; i<r.getNumChildren(); ++i) {
+ Node sk = NodeManager::currentNM()->mkSkolem( "recsym_$$", s.getType(), "created for regular expression concat" );
+ simplifyRegExp( sk, r[i], ret );
+ cc.push_back( sk );
+ }
+ Node cc_eq = NodeManager::currentNM()->mkNode( kind::EQUAL, s,
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, cc ) );
+ ret.push_back( cc_eq );
+ }
+ break;
+ case kind::REGEXP_OR:
+ {
+ std::vector< Node > c_or;
+ for(unsigned i=0; i<r.getNumChildren(); ++i) {
+ simplifyRegExp( s, r[i], c_or );
+ }
+ Node eq = NodeManager::currentNM()->mkNode( kind::OR, c_or );
+ ret.push_back( eq );
+ }
+ break;
+ case kind::REGEXP_INTER:
+ for(unsigned i=0; i<r.getNumChildren(); ++i) {
+ simplifyRegExp( s, r[i], ret );
+ }
+ break;
+ case kind::REGEXP_STAR:
+ {
+ Node sk = NodeManager::currentNM()->mkSkolem( "ressym_$$", s.getType(), "created for regular expression star" );
+ Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL,
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk, s ),
+ NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, s, sk ));
+ ret.push_back( eq );
+ simplifyRegExp( sk, r[0], ret );
+ }
+ break;
+ case kind::REGEXP_OPT:
+ {
+ Node eq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, s, NodeManager::currentNM()->mkConst( ::CVC4::String("") ) );
+ std::vector< Node > rr;
+ simplifyRegExp( s, r[0], rr );
+ Node nrr = rr.size()==1 ? rr[0] : NodeManager::currentNM()->mkNode( kind::AND, rr );
+ ret.push_back( NodeManager::currentNM()->mkNode( kind::OR, eq_empty, nrr) );
+ }
+ break;
+ default:
+ //TODO:case kind::REGEXP_PLUS:
+ //TODO: special sym: sigma, none, all
+ break;
+ }
+}
+
+Node StringsPreprocess::simplify( Node t ) {
+ std::hash_map<TNode, Node, TNodeHashFunction>::const_iterator i = d_cache.find(t);
+ if(i != d_cache.end()) {
+ return (*i).second.isNull() ? t : (*i).second;
+ }
+
+ if( t.getKind() == kind::STRING_IN_REGEXP ){
+ // t0 in t1
+ //rewrite it
+ std::vector< Node > ret;
+ simplifyRegExp( t[0], t[1], ret );
+
+ Node n = ret.size() == 1 ? ret[0] : NodeManager::currentNM()->mkNode( kind::AND, ret );
+ d_cache[t] = (t == n) ? Node::null() : n;
+ return n;
+ }else if( t.getNumChildren()>0 ){
+ std::vector< Node > cc;
+ if (t.getMetaKind() == kind::metakind::PARAMETERIZED) {
+ cc.push_back(t.getOperator());
+ }
+ bool changed = false;
+ for( unsigned i=0; i<t.getNumChildren(); i++ ){
+ Node tn = simplify( t[i] );
+ cc.push_back( tn );
+ changed = changed || tn!=t[i];
+ }
+ if(changed) {
+ Node n = NodeManager::currentNM()->mkNode( t.getKind(), cc );
+ d_cache[t] = n;
+ return n;
+ } else {
+ d_cache[t] = Node::null();
+ return t;
+ }
+ }else{
+ d_cache[t] = Node::null();
+ return t;
+ }
+}
+
+void StringsPreprocess::simplify(std::vector< Node > &vec_node) {
+ for( unsigned i=0; i<vec_node.size(); i++ ){
+ vec_node[i] = simplify( vec_node[i] );
+ }
+}
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h
new file mode 100644
index 000000000..f82a3cf24
--- /dev/null
+++ b/src/theory/strings/theory_strings_preprocess.h
@@ -0,0 +1,44 @@
+/********************* */
+/*! \file theory_strings_preprocess.h
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: Tianyi Liang, Andrew Reynolds
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Strings Preprocess
+ **
+ ** Strings Preprocess.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__STRINGS__PREPROCESS_H
+#define __CVC4__THEORY__STRINGS__PREPROCESS_H
+
+#include <vector>
+#include "util/hash.h"
+#include "theory/theory.h"
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+class StringsPreprocess {
+ // NOTE: this class is NOT context-dependent
+ std::hash_map<TNode, Node, TNodeHashFunction> d_cache;
+private:
+ void simplifyRegExp( Node s, Node r, std::vector< Node > &ret );
+ Node simplify( Node t );
+public:
+void simplify(std::vector< Node > &vec_node);
+};
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__STRINGS__PREPROCESS_H */
diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp
new file mode 100644
index 000000000..412135675
--- /dev/null
+++ b/src/theory/strings/theory_strings_rewriter.cpp
@@ -0,0 +1,156 @@
+/********************* */
+/*! \file theory_strings_rewriter.cpp
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of the theory of strings.
+ **
+ ** Implementation of the theory of strings.
+ **/
+#include "theory/strings/theory_strings_rewriter.h"
+
+using namespace std;
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::theory;
+using namespace CVC4::theory::strings;
+
+Node TheoryStringsRewriter::rewriteConcatString(TNode node) {
+ Trace("strings-prerewrite") << "Strings::rewriteConcatString start " << node << std::endl;
+ Node retNode = node;
+ std::vector<Node> node_vec;
+ Node preNode = Node::null();
+ for(unsigned int i=0; i<node.getNumChildren(); ++i) {
+ Node tmpNode = node[i];
+ if(node[i].getKind() == kind::STRING_CONCAT) {
+ tmpNode = rewriteConcatString(node[i]);
+ if(tmpNode.getKind() == kind::STRING_CONCAT) {
+ unsigned int j=0;
+ if(!preNode.isNull()) {
+ if(tmpNode[0].isConst()) {
+ preNode = NodeManager::currentNM()->mkConst( preNode.getConst<String>().concat( tmpNode[0].getConst<String>() ) );
+ node_vec.push_back( preNode );
+ preNode = Node::null();
+ ++j;
+ } else {
+ node_vec.push_back( preNode );
+ preNode = Node::null();
+ node_vec.push_back( tmpNode[0] );
+ ++j;
+ }
+ }
+ for(; j<tmpNode.getNumChildren() - 1; ++j) {
+ node_vec.push_back( tmpNode[j] );
+ }
+ tmpNode = tmpNode[j];
+ }
+ }
+ if(!tmpNode.isConst()) {
+ if(preNode != Node::null()) {
+ if(preNode.getKind() == kind::CONST_STRING && preNode.getConst<String>().toString()=="" ) {
+ preNode = Node::null();
+ } else {
+ node_vec.push_back( preNode );
+ preNode = Node::null();
+ }
+ }
+ node_vec.push_back( tmpNode );
+ } else {
+ if(preNode.isNull()) {
+ preNode = tmpNode;
+ } else {
+ preNode = NodeManager::currentNM()->mkConst( preNode.getConst<String>().concat( tmpNode.getConst<String>() ) );
+ }
+ }
+ }
+ if(preNode != Node::null()) {
+ node_vec.push_back( preNode );
+ }
+ if(node_vec.size() > 1) {
+ retNode = NodeManager::currentNM()->mkNode(kind::STRING_CONCAT, node_vec);
+ } else {
+ retNode = node_vec[0];
+ }
+ Trace("strings-prerewrite") << "Strings::rewriteConcatString end " << retNode << std::endl;
+ return retNode;
+}
+
+RewriteResponse TheoryStringsRewriter::postRewrite(TNode node) {
+ Trace("strings-postrewrite") << "Strings::postRewrite start " << node << std::endl;
+ Node retNode = node;
+
+ if(node.getKind() == kind::STRING_CONCAT) {
+ retNode = rewriteConcatString(node);
+ } else if(node.getKind() == kind::EQUAL) {
+ Node leftNode = node[0];
+ if(node[0].getKind() == kind::STRING_CONCAT) {
+ leftNode = rewriteConcatString(node[0]);
+ }
+ Node rightNode = node[1];
+ if(node[1].getKind() == kind::STRING_CONCAT) {
+ rightNode = rewriteConcatString(node[1]);
+ }
+
+ if(leftNode == rightNode) {
+ retNode = NodeManager::currentNM()->mkConst(true);
+ } else if(leftNode.isConst() && rightNode.isConst()) {
+ retNode = NodeManager::currentNM()->mkConst(false);
+ } else if(leftNode > rightNode) {
+ retNode = NodeManager::currentNM()->mkNode(kind::EQUAL, rightNode, leftNode);
+ } else if( leftNode != node[0] || rightNode != node[1]) {
+ retNode = NodeManager::currentNM()->mkNode(kind::EQUAL, leftNode, rightNode);
+ }
+ } else if(node.getKind() == kind::STRING_IN_REGEXP) {
+ Node leftNode = node[0];
+ if(node[0].getKind() == kind::STRING_CONCAT) {
+ leftNode = rewriteConcatString(node[0]);
+ }
+ // TODO: right part
+ Node rightNode = node[1];
+ // merge
+ if( leftNode != node[0] || rightNode != node[1]) {
+ retNode = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, leftNode, rightNode);
+ }
+ } else if(node.getKind() == kind::STRING_LENGTH) {
+ if(node[0].isConst()) {
+ retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( node[0].getConst<String>().size() ) );
+ } else if(node[0].getKind() == kind::STRING_CONCAT) {
+ Node tmpNode = rewriteConcatString(node[0]);
+ if(tmpNode.isConst()) {
+ retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode.getConst<String>().size() ) );
+ } else {
+ // it has to be string concat
+ std::vector<Node> node_vec;
+ for(unsigned int i=0; i<tmpNode.getNumChildren(); ++i) {
+ if(tmpNode[i].isConst()) {
+ node_vec.push_back( NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode[i].getConst<String>().size() ) ) );
+ } else {
+ node_vec.push_back( NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, tmpNode[i]) );
+ }
+ }
+ retNode = NodeManager::currentNM()->mkNode(kind::PLUS, node_vec);
+ }
+ }
+ }
+
+ Trace("strings-postrewrite") << "Strings::postRewrite returning " << node << std::endl;
+ return RewriteResponse(REWRITE_DONE, retNode);
+}
+
+RewriteResponse TheoryStringsRewriter::preRewrite(TNode node) {
+ Node retNode = node;
+ Trace("strings-prerewrite") << "Strings::preRewrite start " << node << std::endl;
+
+ if(node.getKind() == kind::STRING_CONCAT) {
+ retNode = rewriteConcatString(node);
+ }
+
+ Trace("strings-prerewrite") << "Strings::preRewrite returning " << retNode << std::endl;
+ return RewriteResponse(REWRITE_DONE, retNode);
+}
diff --git a/src/theory/strings/theory_strings_rewriter.h b/src/theory/strings/theory_strings_rewriter.h
new file mode 100644
index 000000000..3bccd91de
--- /dev/null
+++ b/src/theory/strings/theory_strings_rewriter.h
@@ -0,0 +1,49 @@
+/********************* */
+/*! \file theory_strings_rewriter.h
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief [[ Add one-line brief description here ]]
+ **
+ ** [[ Add lengthier description here ]]
+ ** \todo document this file
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__STRINGS__THEORY_STRINGS_REWRITER_H
+#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_REWRITER_H
+
+#include "theory/rewriter.h"
+#include "theory/type_enumerator.h"
+#include "expr/attribute.h"
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+class TheoryStringsRewriter {
+
+public:
+ static Node rewriteConcatString(TNode node);
+
+ static RewriteResponse postRewrite(TNode node);
+
+ static RewriteResponse preRewrite(TNode node);
+
+ static inline void init() {}
+ static inline void shutdown() {}
+
+};/* class TheoryStringsRewriter */
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_REWRITER_H */
diff --git a/src/theory/strings/theory_strings_type_rules.h b/src/theory/strings/theory_strings_type_rules.h
new file mode 100644
index 000000000..8fc630206
--- /dev/null
+++ b/src/theory/strings/theory_strings_type_rules.h
@@ -0,0 +1,223 @@
+/********************* */
+/*! \file theory_strings_type_rules.h
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2013-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Typing and cardinality rules for the theory of arrays
+ **
+ ** Typing and cardinality rules for the theory of arrays.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H
+#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+class StringConstantTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ return nodeManager->stringType();
+ }
+};
+
+class StringConcatTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ for (; it != it_end; ++ it) {
+ TypeNode t = (*it).getType(check);
+ if (!t.isString()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting string terms in string concat");
+ }
+ }
+ return nodeManager->stringType();
+ }
+};
+
+class StringLengthTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ if( check ){
+ TypeNode t = n[0].getType(check);
+ if (!t.isString()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting string terms in string length");
+ }
+ }
+ return nodeManager->integerType();
+ }
+};
+
+class RegExpConstantTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpConcatTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ for (; it != it_end; ++ it) {
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ }
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpOrTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ for (; it != it_end; ++ it) {
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ }
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpInterTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ for (; it != it_end; ++ it) {
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ }
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpStarTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ if(++it != it_end) {
+ throw TypeCheckingExceptionPrivate(n, "too many regexp");
+ }
+
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpPlusTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ if(++it != it_end) {
+ throw TypeCheckingExceptionPrivate(n, "too many regexp");
+ }
+
+ return nodeManager->regexpType();
+ }
+};
+
+class RegExpOptTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ TypeNode t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ if(++it != it_end) {
+ throw TypeCheckingExceptionPrivate(n, "too many regexp");
+ }
+
+ return nodeManager->regexpType();
+ }
+};
+
+class StringToRegExpTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ TypeNode t = (*it).getType(check);
+ if (!t.isString()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting string terms");
+ }
+ if( (*it).getKind() != kind::CONST_STRING ) {
+ throw TypeCheckingExceptionPrivate(n, "expecting constant string terms");
+ }
+ if(++it != it_end) {
+ throw TypeCheckingExceptionPrivate(n, "too many terms");
+ }
+
+ return nodeManager->regexpType();
+ }
+};
+
+class StringInRegExpTypeRule {
+public:
+ inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check)
+ throw (TypeCheckingExceptionPrivate, AssertionException) {
+ TNode::iterator it = n.begin();
+ TNode::iterator it_end = n.end();
+ TypeNode t = (*it).getType(check);
+ if (!t.isString()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting string terms");
+ }
+ ++it;
+ t = (*it).getType(check);
+ if (!t.isRegExp()) {
+ throw TypeCheckingExceptionPrivate(n, "expecting regexp terms");
+ }
+ if(++it != it_end) {
+ throw TypeCheckingExceptionPrivate(n, "too many terms");
+ }
+
+ return nodeManager->booleanType();
+ }
+};
+
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H */
diff --git a/src/theory/strings/type_enumerator.h b/src/theory/strings/type_enumerator.h
new file mode 100644
index 000000000..3dc12009b
--- /dev/null
+++ b/src/theory/strings/type_enumerator.h
@@ -0,0 +1,130 @@
+/********************* */
+/*! \file type_enumerator.h
+ ** \verbatim
+ ** Original author: Tianyi Liang
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Enumerators for strings
+ **
+ ** Enumerators for strings.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H
+#define __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H
+
+#include <sstream>
+
+#include "util/regexp.h"
+#include "theory/type_enumerator.h"
+#include "expr/type_node.h"
+#include "expr/kind.h"
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+class StringEnumerator : public TypeEnumeratorBase<StringEnumerator> {
+ std::vector< unsigned > d_data;
+ unsigned d_cardinality;
+ Node d_curr;
+ void mkCurr() {
+ //make constant from d_data
+ d_curr = NodeManager::currentNM()->mkConst( ::CVC4::String( d_data ) );
+ }
+public:
+
+ StringEnumerator(TypeNode type) throw(AssertionException) :
+ TypeEnumeratorBase<StringEnumerator>(type) {
+ Assert(type.getKind() == kind::TYPE_CONSTANT &&
+ type.getConst<TypeConstant>() == STRING_TYPE);
+ d_cardinality = 256;
+ mkCurr();
+ }
+ Node operator*() throw() {
+ return d_curr;
+ }
+ StringEnumerator& operator++() throw() {
+ bool changed = false;
+ do{
+ for(unsigned i=0; i<d_data.size(); ++i) {
+ if( d_data[i] + 1 < d_cardinality ) {
+ ++d_data[i]; changed = true;
+ break;
+ } else {
+ d_data[i] = 0;
+ }
+ }
+
+ if(!changed) {
+ d_data.push_back( 0 );
+ }
+ }while(!changed);
+
+ mkCurr();
+ return *this;
+ }
+
+ bool isFinished() throw() {
+ return d_curr.isNull();
+ }
+
+};/* class StringEnumerator */
+
+
+class StringEnumeratorLength {
+private:
+ unsigned d_cardinality;
+ std::vector< unsigned > d_data;
+ Node d_curr;
+ void mkCurr() {
+ //make constant from d_data
+ d_curr = NodeManager::currentNM()->mkConst( ::CVC4::String( d_data ) );
+ }
+public:
+ StringEnumeratorLength(unsigned length, unsigned card = 256) : d_cardinality(card) {
+ for( unsigned i=0; i<length; i++ ){
+ d_data.push_back( 0 );
+ }
+ mkCurr();
+ }
+
+ Node operator*() throw() {
+ return d_curr;
+ }
+
+ StringEnumeratorLength& operator++() throw() {
+ bool changed = false;
+ for(unsigned i=0; i<d_data.size(); ++i) {
+ if( d_data[i] + 1 < d_cardinality ) {
+ ++d_data[i]; changed = true;
+ break;
+ } else {
+ d_data[i] = 0;
+ }
+ }
+
+ if(!changed) {
+ d_curr = Node::null();
+ }else{
+ mkCurr();
+ }
+ return *this;
+ }
+
+ bool isFinished() throw() {
+ return d_curr.isNull();
+ }
+};
+
+}/* CVC4::theory::strings namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H */
diff --git a/src/theory/substitutions.cpp b/src/theory/substitutions.cpp
index c12129d01..c4f06e396 100644
--- a/src/theory/substitutions.cpp
+++ b/src/theory/substitutions.cpp
@@ -29,7 +29,7 @@ struct substitution_stack_element {
: node(node), children_added(false) {}
};/* struct substitution_stack_element */
-Node SubstitutionMap::internalSubstitute(TNode t) {
+Node SubstitutionMap::internalSubstitute(TNode t, NodeCache& cache) {
Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << ")" << endl;
@@ -50,8 +50,8 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): processing " << current << endl;
// If node already in the cache we're done, pop from the stack
- NodeCache::iterator find = d_substitutionCache.find(current);
- if (find != d_substitutionCache.end()) {
+ NodeCache::iterator find = cache.find(current);
+ if (find != cache.end()) {
toVisit.pop_back();
continue;
}
@@ -59,7 +59,7 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
if (!d_substituteUnderQuantifiers &&
(current.getKind() == kind::FORALL || current.getKind() == kind::EXISTS)) {
Debug("substitution::internal") << "--not substituting under quantifier" << endl;
- d_substitutionCache[current] = current;
+ cache[current] = current;
toVisit.pop_back();
continue;
}
@@ -68,9 +68,9 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
if (find2 != d_substitutions.end()) {
Node rhs = (*find2).second;
Assert(rhs != current);
- internalSubstitute(rhs);
- d_substitutions[current] = d_substitutionCache[rhs];
- d_substitutionCache[current] = d_substitutionCache[rhs];
+ internalSubstitute(rhs, cache);
+ d_substitutions[current] = cache[rhs];
+ cache[current] = cache[rhs];
toVisit.pop_back();
continue;
}
@@ -80,17 +80,17 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
// Children have been processed, so substitute
NodeBuilder<> builder(current.getKind());
if (current.getMetaKind() == kind::metakind::PARAMETERIZED) {
- builder << Node(d_substitutionCache[current.getOperator()]);
+ builder << Node(cache[current.getOperator()]);
}
for (unsigned i = 0; i < current.getNumChildren(); ++ i) {
- Assert(d_substitutionCache.find(current[i]) != d_substitutionCache.end());
- builder << Node(d_substitutionCache[current[i]]);
+ Assert(cache.find(current[i]) != cache.end());
+ builder << Node(cache[current[i]]);
}
// Mark the substitution and continue
Node result = builder;
if (result != current) {
- find = d_substitutionCache.find(result);
- if (find != d_substitutionCache.end()) {
+ find = cache.find(result);
+ if (find != cache.end()) {
result = find->second;
}
else {
@@ -98,15 +98,15 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
if (find2 != d_substitutions.end()) {
Node rhs = (*find2).second;
Assert(rhs != result);
- internalSubstitute(rhs);
- d_substitutions[result] = d_substitutionCache[rhs];
- d_substitutionCache[result] = d_substitutionCache[rhs];
- result = d_substitutionCache[rhs];
+ internalSubstitute(rhs, cache);
+ d_substitutions[result] = cache[rhs];
+ cache[result] = cache[rhs];
+ result = cache[rhs];
}
}
}
Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << result << endl;
- d_substitutionCache[current] = result;
+ cache[current] = result;
toVisit.pop_back();
} else {
// Mark that we have added the children if any
@@ -115,34 +115,33 @@ Node SubstitutionMap::internalSubstitute(TNode t) {
// We need to add the operator, if any
if(current.getMetaKind() == kind::metakind::PARAMETERIZED) {
TNode opNode = current.getOperator();
- NodeCache::iterator opFind = d_substitutionCache.find(opNode);
- if (opFind == d_substitutionCache.end()) {
+ NodeCache::iterator opFind = cache.find(opNode);
+ if (opFind == cache.end()) {
toVisit.push_back(opNode);
}
}
// We need to add the children
for(TNode::iterator child_it = current.begin(); child_it != current.end(); ++ child_it) {
TNode childNode = *child_it;
- NodeCache::iterator childFind = d_substitutionCache.find(childNode);
- if (childFind == d_substitutionCache.end()) {
+ NodeCache::iterator childFind = cache.find(childNode);
+ if (childFind == cache.end()) {
toVisit.push_back(childNode);
}
}
} else {
// No children, so we're done
Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << current << endl;
- d_substitutionCache[current] = current;
+ cache[current] = current;
toVisit.pop_back();
}
}
}
// Return the substituted version
- return d_substitutionCache[t];
+ return cache[t];
}/* SubstitutionMap::internalSubstitute() */
- /*
void SubstitutionMap::simplifyRHS(const SubstitutionMap& subMap)
{
// Put the new substitutions into the old ones
@@ -160,10 +159,10 @@ void SubstitutionMap::simplifyRHS(TNode x, TNode t) {
tempCache[x] = t;
// Put the new substitution into the old ones
- NodeMap::iterator it = d_substitutionsOld.begin();
- NodeMap::iterator it_end = d_substitutionsOld.end();
+ NodeMap::iterator it = d_substitutions.begin();
+ NodeMap::iterator it_end = d_substitutions.end();
for(; it != it_end; ++ it) {
- d_substitutionsOld[(*it).first] = internalSubstituteOld((*it).second, tempCache);
+ d_substitutions[(*it).first] = internalSubstitute((*it).second, tempCache);
}
// it = d_substitutionsLazy.begin();
// it_end = d_substitutionsLazy.end();
@@ -171,7 +170,7 @@ void SubstitutionMap::simplifyRHS(TNode x, TNode t) {
// d_substitutionsLazy[(*it).first] = internalSubstitute((*it).second, tempCache);
// }
}
-*/
+
/* We use subMap to simplify the left-hand sides of the current substitution map. If rewrite is true,
* we also apply the rewriter to the result.
@@ -283,6 +282,24 @@ void SubstitutionMap::addSubstitution(TNode x, TNode t, bool invalidateCache)
}
}
+
+void SubstitutionMap::addSubstitutions(SubstitutionMap& subMap, bool invalidateCache)
+{
+ SubstitutionMap::NodeMap::const_iterator it = subMap.begin();
+ SubstitutionMap::NodeMap::const_iterator it_end = subMap.end();
+ for (; it != it_end; ++ it) {
+ Assert(d_substitutions.find((*it).first) == d_substitutions.end());
+ d_substitutions[(*it).first] = (*it).second;
+ if (!invalidateCache) {
+ d_substitutionCache[(*it).first] = d_substitutions[(*it).first];
+ }
+ }
+ if (invalidateCache) {
+ d_cacheInvalidated = true;
+ }
+}
+
+
static bool check(TNode node, const SubstitutionMap::NodeMap& substitutions) CVC4_UNUSED;
static bool check(TNode node, const SubstitutionMap::NodeMap& substitutions) {
SubstitutionMap::NodeMap::const_iterator it = substitutions.begin();
@@ -311,7 +328,7 @@ Node SubstitutionMap::apply(TNode t) {
}
// Perform the substitution
- Node result = internalSubstitute(t);
+ Node result = internalSubstitute(t, d_substitutionCache);
Debug("substitution") << "SubstitutionMap::apply(" << t << ") => " << result << endl;
// Assert(check(result, d_substitutions));
diff --git a/src/theory/substitutions.h b/src/theory/substitutions.h
index bde7dfdbc..5a478a250 100644
--- a/src/theory/substitutions.h
+++ b/src/theory/substitutions.h
@@ -68,8 +68,11 @@ private:
/** Has the cache been invalidated? */
bool d_cacheInvalidated;
+ /** Whether to keep substitutions in solved form */
+ bool d_solvedForm;
+
/** Internal method that performs substitution */
- Node internalSubstitute(TNode t);
+ Node internalSubstitute(TNode t, NodeCache& cache);
/** Helper class to invalidate cache on user pop */
class CacheInvalidator : public context::ContextNotifyObj {
@@ -98,13 +101,15 @@ private:
public:
- SubstitutionMap(context::Context* context, bool substituteUnderQuantifiers = true) :
+ SubstitutionMap(context::Context* context, bool substituteUnderQuantifiers = true, bool solvedForm = false) :
d_context(context),
d_substitutions(context),
d_substitutionCache(),
d_substituteUnderQuantifiers(substituteUnderQuantifiers),
d_cacheInvalidated(false),
- d_cacheInvalidator(context, d_cacheInvalidated) {
+ d_solvedForm(solvedForm),
+ d_cacheInvalidator(context, d_cacheInvalidated)
+ {
}
/**
@@ -113,6 +118,11 @@ public:
void addSubstitution(TNode x, TNode t, bool invalidateCache = true);
/**
+ * Merge subMap into current set of substitutions
+ */
+ void addSubstitutions(SubstitutionMap& subMap, bool invalidateCache = true);
+
+ /**
* Returns true iff x is in the substitution map
*/
bool hasSubstitution(TNode x) const {
@@ -170,13 +180,13 @@ public:
// should best interact with cache invalidation on context
// pops.
- /*
// Simplify right-hand sides of current map using the given substitutions
void simplifyRHS(const SubstitutionMap& subMap);
// Simplify right-hand sides of current map with lhs -> rhs
void simplifyRHS(TNode lhs, TNode rhs);
+ /*
// Simplify left-hand sides of current map using the given substitutions
void simplifyLHS(const SubstitutionMap& subMap,
std::vector<std::pair<Node,Node> >& equalities,
@@ -188,6 +198,8 @@ public:
bool rewrite = true);
*/
+ bool isSolvedForm() const { return d_solvedForm; }
+
/**
* Print to the output stream
*/
diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp
index 48d00130c..558975a72 100644
--- a/src/theory/term_registration_visitor.cpp
+++ b/src/theory/term_registration_visitor.cpp
@@ -127,15 +127,6 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) {
Assert(alreadyVisited(current, parent));
}
-void PreRegisterVisitor::start(TNode node) {
- d_multipleTheories = false;
-}
-
-bool PreRegisterVisitor::done(TNode node) {
- // We have multiple theories if removing the node theory from others is non-empty
- return Theory::setRemove(Theory::theoryOf(node), d_theories);
-}
-
std::string SharedTermsVisitor::toString() const {
std::stringstream ss;
TNodeVisitedMap::const_iterator it = d_visited.begin();
diff --git a/src/theory/term_registration_visitor.h b/src/theory/term_registration_visitor.h
index 768508d2c..478e82d19 100644
--- a/src/theory/term_registration_visitor.h
+++ b/src/theory/term_registration_visitor.h
@@ -55,25 +55,19 @@ class PreRegisterVisitor {
theory::Theory::Set d_theories;
/**
- * Is true if the term we're traversing involves multiple theories.
- */
- bool d_multipleTheories;
-
- /**
* String representation of the visited map, for debugging purposes.
*/
std::string toString() const;
public:
- /** Return type tells us if there are more than one theory or not */
- typedef bool return_type;
+ /** Returned set tells us which theories there are */
+ typedef theory::Theory::Set return_type;
PreRegisterVisitor(TheoryEngine* engine, context::Context* context)
: d_engine(engine)
, d_visited(context)
, d_theories(0)
- , d_multipleTheories(false)
{}
/**
@@ -89,13 +83,12 @@ public:
/**
* Marks the node as the starting literal.
*/
- void start(TNode node);
+ void start(TNode node) { }
/**
* Notifies the engine of all the theories used.
*/
- bool done(TNode node);
-
+ theory::Theory::Set done(TNode node) { return d_theories; }
};
diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp
index 9ed20cc99..9a23d5518 100644
--- a/src/theory/theory.cpp
+++ b/src/theory/theory.cpp
@@ -17,6 +17,7 @@
#include "theory/theory.h"
#include "util/cvc4_assert.h"
#include "theory/quantifiers_engine.h"
+#include "theory/substitutions.h"
#include <vector>
@@ -28,9 +29,6 @@ namespace theory {
/** Default value for the uninterpreted sorts is the UF theory */
TheoryId Theory::s_uninterpretedSortOwner = THEORY_UF;
-/** By default, we use the type based theoryOf */
-TheoryOfMode Theory::s_theoryOfMode = THEORY_OF_TYPE_BASED;
-
std::ostream& operator<<(std::ostream& os, Theory::Effort level){
switch(level){
case Theory::EFFORT_STANDARD:
@@ -72,7 +70,7 @@ TheoryId Theory::theoryOf(TheoryOfMode mode, TNode node) {
// Variables
if (node.isVar()) {
if (theoryOf(node.getType()) != theory::THEORY_BOOL) {
- // We treat the varibables as uninterpreted
+ // We treat the variables as uninterpreted
return s_uninterpretedSortOwner;
} else {
// Except for the Boolean ones, which we just ignore anyhow
@@ -209,5 +207,27 @@ void Theory::computeRelevantTerms(set<Node>& termSet)
}
+Theory::PPAssertStatus Theory::ppAssert(TNode in, SubstitutionMap& outSubstitutions)
+{
+ if (in.getKind() == kind::EQUAL) {
+ if (in[0].isVar() && !in[1].hasSubterm(in[0])) {
+ outSubstitutions.addSubstitution(in[0], in[1]);
+ return PP_ASSERT_STATUS_SOLVED;
+ }
+ if (in[1].isVar() && !in[0].hasSubterm(in[1])) {
+ outSubstitutions.addSubstitution(in[1], in[0]);
+ return PP_ASSERT_STATUS_SOLVED;
+ }
+ if (in[0].isConst() && in[1].isConst()) {
+ if (in[0] != in[1]) {
+ return PP_ASSERT_STATUS_CONFLICT;
+ }
+ }
+ }
+
+ return PP_ASSERT_STATUS_UNSOLVED;
+}
+
+
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/theory.h b/src/theory/theory.h
index 23fd67b23..21a5aacf5 100644
--- a/src/theory/theory.h
+++ b/src/theory/theory.h
@@ -23,9 +23,9 @@
#include "expr/attribute.h"
#include "expr/command.h"
#include "theory/valuation.h"
-#include "theory/substitutions.h"
#include "theory/output_channel.h"
#include "theory/logic_info.h"
+#include "theory/options.h"
#include "theory/theoryof_mode.h"
#include "context/context.h"
#include "context/cdlist.h"
@@ -49,6 +49,7 @@ namespace theory {
class QuantifiersEngine;
class TheoryModel;
+class SubstitutionMap;
namespace rrinst {
class CandidateGenerator;
@@ -298,9 +299,6 @@ protected:
void printFacts(std::ostream& os) const;
void debugPrintFacts() const;
- /** Mode of the theoryOf operation */
- static TheoryOfMode s_theoryOfMode;
-
public:
/**
@@ -333,12 +331,7 @@ public:
* Returns the ID of the theory responsible for the given node.
*/
static inline TheoryId theoryOf(TNode node) {
- return theoryOf(s_theoryOfMode, node);
- }
-
- /** Set the theoryOf mode */
- static void setTheoryOfMode(TheoryOfMode mode) {
- s_theoryOfMode = mode;
+ return theoryOf(options::theoryOfMode(), node);
}
/**
@@ -349,7 +342,7 @@ public:
}
/**
- * Set the owner of the uninterpreted sort.
+ * Get the owner of the uninterpreted sort.
*/
static TheoryId getUninterpretedSortOwner() {
return s_uninterpretedSortOwner;
@@ -516,7 +509,7 @@ public:
virtual EqualityStatus getEqualityStatus(TNode a, TNode b) { return EQUALITY_UNKNOWN; }
/**
- * Return the model value of the give shared term (or null if not avalilable.
+ * Return the model value of the give shared term (or null if not available).
*/
virtual Node getModelValue(TNode var) { return Node::null(); }
@@ -583,25 +576,7 @@ public:
* Given a literal, add the solved substitutions to the map, if any.
* The method should return true if the literal can be safely removed.
*/
- virtual PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions) {
- if (in.getKind() == kind::EQUAL) {
- if (in[0].isVar() && !in[1].hasSubterm(in[0])) {
- outSubstitutions.addSubstitution(in[0], in[1]);
- return PP_ASSERT_STATUS_SOLVED;
- }
- if (in[1].isVar() && !in[0].hasSubterm(in[1])) {
- outSubstitutions.addSubstitution(in[1], in[0]);
- return PP_ASSERT_STATUS_SOLVED;
- }
- if (in[0].isConst() && in[1].isConst()) {
- if (in[0] != in[1]) {
- return PP_ASSERT_STATUS_CONFLICT;
- }
- }
- }
-
- return PP_ASSERT_STATUS_UNSOLVED;
- }
+ virtual PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions);
/**
* Given an atom of the theory coming from the input formula, this
diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp
index 6c8341345..78710e4b9 100644
--- a/src/theory/theory_engine.cpp
+++ b/src/theory/theory_engine.cpp
@@ -110,6 +110,7 @@ TheoryEngine::TheoryEngine(context::Context* context,
d_propagationMapTimestamp(context, 0),
d_propagatedLiterals(context),
d_propagatedLiteralsIndex(context, 0),
+ d_atomRequests(context),
d_iteRemover(iteRemover),
d_combineTheoriesTime("TheoryEngine::combineTheoriesTime"),
d_true(),
@@ -186,9 +187,32 @@ void TheoryEngine::preRegister(TNode preprocessed) {
}
// Pre-register the terms in the atom
- bool multipleTheories = NodeVisitor<PreRegisterVisitor>::run(d_preRegistrationVisitor, preprocessed);
+ Theory::Set theories = NodeVisitor<PreRegisterVisitor>::run(d_preRegistrationVisitor, preprocessed);
+ theories = Theory::setRemove(THEORY_BOOL, theories);
+ // Remove the top theory, if any more that means multiple theories were involved
+ bool multipleTheories = Theory::setRemove(Theory::theoryOf(preprocessed), theories);
+ TheoryId i;
+ // These checks don't work with finite model finding, because it
+ // uses Rational constants to represent cardinality constraints,
+ // even though arithmetic isn't actually involved.
+ if(!options::finiteModelFind()) {
+ while((i = Theory::setPop(theories)) != THEORY_LAST) {
+ if(!d_logicInfo.isTheoryEnabled(i)) {
+ LogicInfo newLogicInfo = d_logicInfo.getUnlockedCopy();
+ newLogicInfo.enableTheory(i);
+ newLogicInfo.lock();
+ stringstream ss;
+ ss << "The logic was specified as " << d_logicInfo.getLogicString()
+ << ", which doesn't include " << i
+ << ", but found a term in that theory." << endl
+ << "You might want to extend your logic to "
+ << newLogicInfo.getLogicString() << endl;
+ throw LogicException(ss.str());
+ }
+ }
+ }
if (multipleTheories) {
- // Collect the shared terms if there are multipe theories
+ // Collect the shared terms if there are multiple theories
NodeVisitor<SharedTermsVisitor>::run(d_sharedTermsVisitor, preprocessed);
}
}
@@ -204,8 +228,8 @@ void TheoryEngine::printAssertions(const char* tag) {
for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) {
Theory* theory = d_theoryTable[theoryId];
if (theory && d_logicInfo.isTheoryEnabled(theoryId)) {
- Trace(tag) << "--------------------------------------------" << std::endl;
- Trace(tag) << "Assertions of " << theory->getId() << ": " << std::endl;
+ Trace(tag) << "--------------------------------------------" << endl;
+ Trace(tag) << "Assertions of " << theory->getId() << ": " << endl;
context::CDList<Assertion>::const_iterator it = theory->facts_begin(), it_end = theory->facts_end();
for (unsigned i = 0; it != it_end; ++ it, ++i) {
if ((*it).isPreregistered) {
@@ -217,7 +241,7 @@ void TheoryEngine::printAssertions(const char* tag) {
}
if (d_logicInfo.isSharingEnabled()) {
- Trace(tag) << "Shared terms of " << theory->getId() << ": " << std::endl;
+ Trace(tag) << "Shared terms of " << theory->getId() << ": " << endl;
context::CDList<TNode>::const_iterator it = theory->shared_terms_begin(), it_end = theory->shared_terms_end();
for (unsigned i = 0; it != it_end; ++ it, ++i) {
Trace(tag) << "[" << i << "]: " << (*it) << endl;
@@ -287,9 +311,7 @@ void TheoryEngine::check(Theory::Effort effort) {
#endif
#define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \
if (theory::TheoryTraits<THEORY>::hasCheck && d_logicInfo.isTheoryEnabled(THEORY)) { \
-Debug("theory") << "check<" << THEORY << ">" << std::endl; \
theoryOf(THEORY)->check(effort); \
-Debug("theory") << "done<" << THEORY << ">" << std::endl; \
if (d_inConflict) { \
break; \
} \
@@ -305,7 +327,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
// Mark the lemmas flag (no lemmas added)
d_lemmasAdded = false;
- Debug("theory") << "TheoryEngine::check(" << effort << "): d_factsAsserted = " << (d_factsAsserted ? "true" : "false") << std::endl;
+ Debug("theory") << "TheoryEngine::check(" << effort << "): d_factsAsserted = " << (d_factsAsserted ? "true" : "false") << endl;
// If in full effort, we have a fake new assertion just to jumpstart the checking
if (Theory::fullEffort(effort)) {
@@ -315,9 +337,9 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
// Check until done
while (d_factsAsserted && !d_inConflict && !d_lemmasAdded) {
- Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << std::endl;
+ Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << endl;
- Trace("theory::assertions") << std::endl;
+ Trace("theory::assertions") << endl;
if (Trace.isOn("theory::assertions")) {
printAssertions("theory::assertions");
}
@@ -334,7 +356,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
<< CheckSatCommand();
}
- Debug("theory") << "TheoryEngine::check(" << effort << "): running propagation after the initial check" << std::endl;
+ Debug("theory") << "TheoryEngine::check(" << effort << "): running propagation after the initial check" << endl;
// We are still satisfiable, propagate as much as possible
propagate(effort);
@@ -342,13 +364,14 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
// We do combination if all has been processed and we are in fullcheck
if (Theory::fullEffort(effort) && d_logicInfo.isSharingEnabled() && !d_factsAsserted && !d_lemmasAdded) {
// Do the combination
- Debug("theory") << "TheoryEngine::check(" << effort << "): running combination" << std::endl;
+ Debug("theory") << "TheoryEngine::check(" << effort << "): running combination" << endl;
combineTheories();
}
}
// Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma
if( effort == Theory::EFFORT_FULL && ! d_inConflict && ! needCheck() ) {
+ //d_theoryTable[THEORY_STRINGS]->check(Theory::EFFORT_LAST_CALL);
if(d_logicInfo.isQuantified()) {
// quantifiers engine must pass effort last call check
d_quantEngine->check(Theory::EFFORT_LAST_CALL);
@@ -365,7 +388,11 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
}
}
- Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << std::endl;
+ Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << endl;
+
+ if(!d_inConflict && Theory::fullEffort(effort) && d_masterEqualityEngine != NULL && !d_lemmasAdded) {
+ AlwaysAssert(d_masterEqualityEngine->consistent());
+ }
} catch(const theory::Interrupted&) {
Trace("theory") << "TheoryEngine::check() => interrupted" << endl;
@@ -381,7 +408,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \
void TheoryEngine::combineTheories() {
- Debug("sharing") << "TheoryEngine::combineTheories()" << std::endl;
+ Debug("sharing") << "TheoryEngine::combineTheories()" << endl;
TimerStat::CodeTimer combineTheoriesTimer(d_combineTheoriesTime);
@@ -399,7 +426,7 @@ void TheoryEngine::combineTheories() {
// Call on each parametric theory to give us its care graph
CVC4_FOR_EACH_THEORY;
- Debug("sharing") << "TheoryEngine::combineTheories(): care graph size = " << careGraph.size() << std::endl;
+ Debug("sharing") << "TheoryEngine::combineTheories(): care graph size = " << careGraph.size() << endl;
// Now add splitters for the ones we are interested in
CareGraph::const_iterator care_it = careGraph.begin();
@@ -407,47 +434,17 @@ void TheoryEngine::combineTheories() {
for (; care_it != care_it_end; ++ care_it) {
const CarePair& carePair = *care_it;
- Debug("sharing") << "TheoryEngine::combineTheories(): checking " << carePair.a << " = " << carePair.b << " from " << carePair.theory << std::endl;
+ Debug("sharing") << "TheoryEngine::combineTheories(): checking " << carePair.a << " = " << carePair.b << " from " << carePair.theory << endl;
Assert(d_sharedTerms.isShared(carePair.a) || carePair.a.isConst());
Assert(d_sharedTerms.isShared(carePair.b) || carePair.b.isConst());
- //AJR-temp Assert(!d_sharedTerms.areEqual(carePair.a, carePair.b), "Please don't care about stuff you were notified about");
- //AJR-temp Assert(!d_sharedTerms.areDisequal(carePair.a, carePair.b), "Please don't care about stuff you were notified about");
- // The equality in question
- Node equality = carePair.a < carePair.b ?
- carePair.a.eqNode(carePair.b) :
- carePair.b.eqNode(carePair.a);
-
- // Normalize the equality
- Node normalizedEquality = Rewriter::rewrite(equality);
-
- // Check if the normalized equality already has a value (this also
- // covers constants, since they always have values
- if (d_propEngine->isSatLiteral(normalizedEquality)) {
- bool value;
- if (d_propEngine->hasValue(normalizedEquality, value)) {
- Assert(equality != normalizedEquality);
- Node literal = value ? equality : equality.notNode();
- Node normalizedLiteral = value ? normalizedEquality : normalizedEquality.notNode();
- // We're sending the original literal back, backed by the normalized one
- if (markPropagation(literal, normalizedLiteral, /* to */ carePair.theory, /* from */ THEORY_SAT_SOLVER)) {
- // We assert it, and we know it's preregistereed if it's the same theory
- bool preregistered = Theory::theoryOf(literal) == carePair.theory;
- theoryOf(carePair.theory)->assertFact(literal, preregistered);
- // Mark that we have more information
- d_factsAsserted = true;
- continue;
- } else {
- Message() << "mark propagation fail: " << literal << " " << normalizedLiteral << " " << carePair.theory << std::endl;
- Unreachable();
- }
- }
- }
+ // The equality in question (order for no repetition)
+ Node equality = carePair.a.eqNode(carePair.b);
// We need to split on it
- Debug("sharing") << "TheoryEngine::combineTheories(): requesting a split " << std::endl;
- lemma(equality.orNode(equality.notNode()), false, false);
+ Debug("sharing") << "TheoryEngine::combineTheories(): requesting a split " << endl;
+ lemma(equality.orNode(equality.notNode()), false, false, carePair.theory);
}
}
@@ -471,15 +468,19 @@ void TheoryEngine::propagate(Theory::Effort effort) {
CVC4_FOR_EACH_THEORY;
if(Dump.isOn("missed-t-propagations")) {
- for(unsigned i = 0; i < d_possiblePropagations.size(); ++ i) {
+ for(unsigned i = 0; i < d_possiblePropagations.size(); ++i) {
Node atom = d_possiblePropagations[i];
bool value;
- if (!d_propEngine->hasValue(atom, value)) continue;
+ if(d_propEngine->hasValue(atom, value)) {
+ continue;
+ }
// Doesn't have a value, check it (and the negation)
if(d_hasPropagated.find(atom) == d_hasPropagated.end()) {
Dump("missed-t-propagations")
<< CommentCommand("Completeness check for T-propagations; expect invalid")
+ << EchoCommand(atom.toString())
<< QueryCommand(atom.toExpr())
+ << EchoCommand(atom.notNode().toString())
<< QueryCommand(atom.notNode().toExpr());
}
}
@@ -600,12 +601,12 @@ void TheoryEngine::collectModelInfo( theory::TheoryModel* m, bool fullModel ){
/* get model */
TheoryModel* TheoryEngine::getModel() {
- Debug("model") << "TheoryEngine::getModel()" << std::endl;
+ Debug("model") << "TheoryEngine::getModel()" << endl;
if( d_logicInfo.isQuantified() ) {
- Debug("model") << "Get model from quantifiers engine." << std::endl;
+ Debug("model") << "Get model from quantifiers engine." << endl;
return d_quantEngine->getModel();
} else {
- Debug("model") << "Get default model." << std::endl;
+ Debug("model") << "Get default model." << endl;
return d_curr_model;
}
}
@@ -879,11 +880,11 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the
PropagationMap::const_iterator find = d_propagationMap.find(toAssert);
if (find != d_propagationMap.end()) {
// The theory already knows this
- Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): already there" << std::endl;
+ Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): already there" << endl;
return false;
}
- Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): marking [" << d_propagationMapTimestamp << "] " << assertion << ", " << toTheoryId << " from " << originalAssertion << ", " << fromTheoryId << std::endl;
+ Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): marking [" << d_propagationMapTimestamp << "] " << assertion << ", " << toTheoryId << " from " << originalAssertion << ", " << fromTheoryId << endl;
// Mark the propagation
d_propagationMap[toAssert] = toExplain;
@@ -893,9 +894,9 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the
}
-void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) {
+void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) {
- Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << std::endl;
+ Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << endl;
Assert(toTheoryId != fromTheoryId);
if(! d_logicInfo.isTheoryEnabled(toTheoryId) &&
@@ -915,6 +916,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
// If sharing is disabled, things are easy
if (!d_logicInfo.isSharingEnabled()) {
+ Assert(assertion == originalAssertion);
if (fromTheoryId == THEORY_SAT_SOLVER) {
// Send to the apropriate theory
theory::Theory* toTheory = theoryOf(toTheoryId);
@@ -928,7 +930,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
bool value;
if (d_propEngine->hasValue(assertion, value)) {
if (!value) {
- Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict" << std::endl;
+ Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict" << endl;
d_inConflict = true;
} else {
return;
@@ -948,7 +950,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
// If sending to the shared terms database, it's also simple
if (toTheoryId == THEORY_BUILTIN) {
Assert(atom.getKind() == kind::EQUAL, "atom should be an EQUALity, not `%s'", atom.toString().c_str());
- if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) {
+ if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
d_sharedTerms.assertEquality(atom, polarity, assertion);
}
return;
@@ -958,9 +960,11 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
// directly to the apropriate theory
if (fromTheoryId == THEORY_SAT_SOLVER) {
// We know that this is normalized, so just send it off to the theory
- if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) {
- // We assert it, and we know it's preregistereed coming from the SAT solver directly
- theoryOf(toTheoryId)->assertFact(assertion, true);
+ if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
+ // Is it preregistered
+ bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId;
+ // We assert it
+ theoryOf(toTheoryId)->assertFact(assertion, preregistered);
// Mark that we have more information
d_factsAsserted = true;
}
@@ -970,7 +974,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
// Propagations to the SAT solver are just enqueued for pickup by
// the SAT solver later
if (toTheoryId == THEORY_SAT_SOLVER) {
- if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) {
+ if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
// Enqueue for propagation to the SAT solver
d_propagatedLiterals.push_back(assertion);
// Check for propositional conflicts
@@ -982,18 +986,16 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
return;
}
- // See if it rewrites to true or false directly
+ Assert(atom.getKind() == kind::EQUAL);
+
+ // Normalize
Node normalizedLiteral = Rewriter::rewrite(assertion);
+
+ // See if it rewrites false directly -> conflict
if (normalizedLiteral.isConst()) {
- if (normalizedLiteral.getConst<bool>()) {
- // trivially true, but theories need to share even trivially true facts
- // unless of course it is the theory that normalized it
- if (Theory::theoryOf(atom) == toTheoryId) {
- return;
- }
- } else {
+ if (!normalizedLiteral.getConst<bool>()) {
// Mark the propagation for explanations
- if (markPropagation(normalizedLiteral, assertion, toTheoryId, fromTheoryId)) {
+ if (markPropagation(normalizedLiteral, originalAssertion, toTheoryId, fromTheoryId)) {
// Get the explanation (conflict will figure out where it came from)
conflict(normalizedLiteral, toTheoryId);
} else {
@@ -1003,25 +1005,12 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
}
}
- // Normalize to lhs < rhs if not a sat literal
- Assert(atom.getKind() == kind::EQUAL);
- Assert(atom[0] != atom[1]);
-
- Node normalizedAtom = atom;
- if (!d_propEngine->isSatLiteral(normalizedAtom)) {
- Node reverse = atom[1].eqNode(atom[0]);
- if (d_propEngine->isSatLiteral(reverse) || atom[0] > atom[1]) {
- normalizedAtom = reverse;
- }
- }
- Node normalizedAssertion = polarity ? normalizedAtom : normalizedAtom.notNode();
-
// Try and assert (note that we assert the non-normalized one)
- if (markPropagation(normalizedAssertion, assertion, toTheoryId, fromTheoryId)) {
+ if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) {
// Check if has been pre-registered with the theory
- bool preregistered = d_propEngine->isSatLiteral(normalizedAssertion) && Theory::theoryOf(normalizedAtom) == toTheoryId;
+ bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId;
// Assert away
- theoryOf(toTheoryId)->assertFact(normalizedAssertion, preregistered);
+ theoryOf(toTheoryId)->assertFact(assertion, preregistered);
d_factsAsserted = true;
}
@@ -1030,7 +1019,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId,
void TheoryEngine::assertFact(TNode literal)
{
- Trace("theory") << "TheoryEngine::assertFact(" << literal << ")" << std::endl;
+ Trace("theory") << "TheoryEngine::assertFact(" << literal << ")" << endl;
d_propEngine->checkTime();
@@ -1067,22 +1056,33 @@ void TheoryEngine::assertFact(TNode literal)
// to the assert the equality to the interested theories
if (atom.getKind() == kind::EQUAL) {
// Assert it to the the owning theory
- assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
+ assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
// Shared terms manager will assert to interested theories directly, as the terms become shared
- assertToTheory(literal, /* to */ THEORY_BUILTIN, /* from */ THEORY_SAT_SOLVER);
+ assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ THEORY_SAT_SOLVER);
+
+ // Now, let's check for any atom triggers from lemmas
+ AtomRequests::atom_iterator it = d_atomRequests.getAtomIterator(atom);
+ while (!it.done()) {
+ const AtomRequests::Request& request = it.get();
+ Node toAssert = polarity ? (Node) request.atom : request.atom.notNode();
+ Debug("theory::atoms") << "TheoryEngine::assertFact(" << literal << "): sending requested " << toAssert << endl;
+ assertToTheory(toAssert, literal, request.toTheory, THEORY_SAT_SOLVER);
+ it.next();
+ }
+
} else {
// Not an equality, just assert to the appropriate theory
- assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
+ assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
}
} else {
// Assert the fact to the appropriate theory directly
- assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
+ assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER);
}
}
bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) {
- Debug("theory::propagate") << "TheoryEngine::propagate(" << literal << ", " << theory << ")" << std::endl;
+ Debug("theory::propagate") << "TheoryEngine::propagate(" << literal << ", " << theory << ")" << endl;
d_propEngine->checkTime();
@@ -1101,16 +1101,16 @@ bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) {
if (d_logicInfo.isSharingEnabled() && atom.getKind() == kind::EQUAL) {
if (d_propEngine->isSatLiteral(literal)) {
// We propagate SAT literals to SAT
- assertToTheory(literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
+ assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
}
if (theory != THEORY_BUILTIN) {
// Assert to the shared terms database
- assertToTheory(literal, /* to */ THEORY_BUILTIN, /* from */ theory);
+ assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory);
}
} else {
// Just send off to the SAT solver
Assert(d_propEngine->isSatLiteral(literal));
- assertToTheory(literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
+ assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory);
}
return !d_inConflict;
@@ -1166,7 +1166,7 @@ static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) {
Node TheoryEngine::getExplanation(TNode node) {
- Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << std::endl;
+ Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl;
bool polarity = node.getKind() != kind::NOT;
TNode atom = polarity ? node : node[0];
@@ -1174,7 +1174,7 @@ Node TheoryEngine::getExplanation(TNode node) {
// If we're not in shared mode, explanations are simple
if (!d_logicInfo.isSharingEnabled()) {
Node explanation = theoryOf(atom)->explain(node);
- Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << std::endl;
+ Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl;
return explanation;
}
@@ -1188,12 +1188,115 @@ Node TheoryEngine::getExplanation(TNode node) {
getExplanation(explanationVector);
Node explanation = mkExplanation(explanationVector);
- Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << std::endl;
+ Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl;
return explanation;
}
-theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable) {
+struct AtomsCollect {
+
+ std::vector<TNode> d_atoms;
+ std::hash_set<TNode, TNodeHashFunction> d_visited;
+
+public:
+
+ typedef void return_type;
+
+ bool alreadyVisited(TNode current, TNode parent) {
+ // Check if already visited
+ d_visited.find(current) != d_visited.end();
+ // Don't visit non-boolean
+ if (!current.getType().isBoolean()) return true;
+ // New node
+ return false;
+ }
+
+ void visit(TNode current, TNode parent) {
+ if (Theory::theoryOf(current) != theory::THEORY_BOOL) {
+ d_atoms.push_back(current);
+ }
+ d_visited.insert(current);
+ }
+
+ void start(TNode node) {}
+ void done(TNode node) {}
+
+ std::vector<TNode> getAtoms() const {
+ return d_atoms;
+ }
+};
+
+void TheoryEngine::ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId atomsTo) {
+ for (unsigned i = 0; i < atoms.size(); ++ i) {
+
+ // Non-equality atoms are either owned by theory or they don't make sense
+ if (atoms[i].getKind() != kind::EQUAL) {
+ continue;
+ }
+
+ // The equality
+ Node eq = atoms[i];
+ // Simple normalization to not repeat stuff
+ if (eq[0] > eq[1]) {
+ eq = eq[1].eqNode(eq[0]);
+ }
+
+ // Rewrite the equality
+ Node eqNormalized = Rewriter::rewrite(atoms[i]);
+
+ Debug("theory::atoms") << "TheoryEngine::ensureLemmaAtoms(): " << eq << " with nf " << eqNormalized << endl;
+
+ // If the equality is a boolean constant, we send immediately
+ if (eqNormalized.isConst()) {
+ if (eqNormalized.getConst<bool>()) {
+ assertToTheory(eq, eqNormalized, /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER);
+ } else {
+ assertToTheory(eq.notNode(), eqNormalized.notNode(), /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER);
+ }
+ continue;
+ }
+
+ Assert(eqNormalized.getKind() == kind::EQUAL);
+
+
+ // If the normalization did the just flips, keep the flip
+ if (eqNormalized[0] == eq[1] && eqNormalized[1] == eq[0]) {
+ eq = eqNormalized;
+ }
+
+ // Check if the equality is already known by the sat solver
+ if (d_propEngine->isSatLiteral(eqNormalized)) {
+ bool value;
+ if (d_propEngine->hasValue(eqNormalized, value)) {
+ if (value) {
+ assertToTheory(eq, eqNormalized, atomsTo, theory::THEORY_SAT_SOLVER);
+ continue;
+ } else {
+ assertToTheory(eq.notNode(), eqNormalized.notNode(), atomsTo, theory::THEORY_SAT_SOLVER);
+ continue;
+ }
+ }
+ }
+
+ // If the theory is asking about a different form, or the form is ok but if will go to a different theory
+ // then we must figure it out
+ if (eqNormalized != eq || Theory::theoryOf(eq) != atomsTo) {
+ // If you get eqNormalized, send atoms[i] to atomsTo
+ d_atomRequests.add(eqNormalized, eq, atomsTo);
+ }
+ }
+}
+
+theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable, theory::TheoryId atomsTo) {
+
+ // Do we need to check atoms
+ if (atomsTo != theory::THEORY_LAST) {
+ Debug("theory::atoms") << "TheoryEngine::lemma(" << node << ", " << atomsTo << ")" << endl;
+ AtomsCollect collectAtoms;
+ NodeVisitor<AtomsCollect>::run(collectAtoms, node);
+ ensureLemmaAtoms(collectAtoms.getAtoms(), atomsTo);
+ }
+
if(Dump.isOn("t-lemmas")) {
Node n = node;
if (negated) {
@@ -1215,6 +1318,18 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable
d_iteRemover.run(additionalLemmas, iteSkolemMap);
additionalLemmas[0] = theory::Rewriter::rewrite(additionalLemmas[0]);
+ if(Debug.isOn("lemma-ites")) {
+ Debug("lemma-ites") << "removed ITEs from lemma: " << node << endl;
+ Debug("lemma-ites") << " + now have the following "
+ << additionalLemmas.size() << " lemma(s):" << endl;
+ for(std::vector<Node>::const_iterator i = additionalLemmas.begin();
+ i != additionalLemmas.end();
+ ++i) {
+ Debug("lemma-ites") << " + " << *i << endl;
+ }
+ Debug("lemma-ites") << endl;
+ }
+
// assert to prop engine
d_propEngine->assertLemma(additionalLemmas[0], negated, removable);
for (unsigned i = 1; i < additionalLemmas.size(); ++ i) {
@@ -1249,7 +1364,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable
void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) {
- Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << ")" << std::endl;
+ Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << ")" << endl;
// Mark that we are in conflict
d_inConflict = true;
@@ -1267,13 +1382,13 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) {
// Process the explanation
getExplanation(explanationVector);
Node fullConflict = mkExplanation(explanationVector);
- Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << std::endl;
+ Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl;
Assert(properConflict(fullConflict));
- lemma(fullConflict, true, true);
+ lemma(fullConflict, true, true, THEORY_LAST);
} else {
// When only one theory, the conflict should need no processing
Assert(properConflict(conflict));
- lemma(conflict, true, true);
+ lemma(conflict, true, true, THEORY_LAST);
}
}
@@ -1301,9 +1416,9 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector
// Get the current literal to explain
NodeTheoryPair toExplain = explanationVector[i];
- Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << std::endl;
+ Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl;
- // If a treu constant or a negation of a false constant we can ignore it
+ // If a true constant or a negation of a false constant we can ignore it
if (toExplain.node.isConst() && toExplain.node.getConst<bool>()) {
++ i;
continue;
@@ -1321,7 +1436,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector
// If an and, expand it
if (toExplain.node.getKind() == kind::AND) {
- Debug("theory::explain") << "TheoryEngine::explain(): expanding " << toExplain.node << " got from " << toExplain.theory << std::endl;
+ Debug("theory::explain") << "TheoryEngine::explain(): expanding " << toExplain.node << " got from " << toExplain.theory << endl;
for (unsigned k = 0; k < toExplain.node.getNumChildren(); ++ k) {
NodeTheoryPair newExplain(toExplain.node[k], toExplain.theory, toExplain.timestamp);
explanationVector.push_back(newExplain);
@@ -1348,7 +1463,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector
} else {
explanation = theoryOf(toExplain.theory)->explain(toExplain.node);
}
- Debug("theory::explain") << "TheoryEngine::explain(): got explanation " << explanation << " got from " << toExplain.theory << std::endl;
+ Debug("theory::explain") << "TheoryEngine::explain(): got explanation " << explanation << " got from " << toExplain.theory << endl;
Assert(explanation != toExplain.node, "wasn't sent to you, so why are you explaining it trivially");
// Mark the explanation
NodeTheoryPair newExplain(explanation, toExplain.theory, toExplain.timestamp);
@@ -1368,7 +1483,7 @@ void TheoryEngine::ppUnconstrainedSimp(vector<Node>& assertions)
void TheoryEngine::setUserAttribute(const std::string& attr, Node n) {
- Trace("te-attr") << "set user attribute " << attr << " " << n << std::endl;
+ Trace("te-attr") << "set user attribute " << attr << " " << n << endl;
if( d_attr_handle.find( attr )!=d_attr_handle.end() ){
for( size_t i=0; i<d_attr_handle[attr].size(); i++ ){
d_attr_handle[attr][i]->setUserAttribute(attr, n);
@@ -1379,7 +1494,7 @@ void TheoryEngine::setUserAttribute(const std::string& attr, Node n) {
}
void TheoryEngine::handleUserAttribute(const char* attr, Theory* t) {
- Trace("te-attr") << "Handle user attribute " << attr << " " << t << std::endl;
+ Trace("te-attr") << "Handle user attribute " << attr << " " << t << endl;
std::string str( attr );
d_attr_handle[ str ].push_back( t );
}
@@ -1395,7 +1510,13 @@ void TheoryEngine::checkTheoryAssertionsWithModel() {
++it) {
Node assertion = (*it).assertion;
Node val = getModel()->getValue(assertion);
- Assert(val == d_true);
+ if(val != d_true) {
+ stringstream ss;
+ ss << theoryId << " has an asserted fact that the model doesn't satisfy." << endl
+ << "The fact: " << assertion << endl
+ << "Model value: " << val << endl;
+ InternalError(ss.str());
+ }
}
}
}
diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h
index c21819ea1..53ff4d167 100644
--- a/src/theory/theory_engine.h
+++ b/src/theory/theory_engine.h
@@ -28,7 +28,6 @@
#include "prop/prop_engine.h"
#include "context/cdhashset.h"
#include "theory/theory.h"
-#include "theory/substitutions.h"
#include "theory/rewriter.h"
#include "theory/substitutions.h"
#include "theory/shared_terms_database.h"
@@ -46,6 +45,7 @@
#include "theory/unconstrained_simplifier.h"
#include "theory/uf/equality_engine.h"
#include "theory/bv/bv_to_bool.h"
+#include "theory/atom_requests.h"
namespace CVC4 {
@@ -75,6 +75,9 @@ struct NodeTheoryPairHashFunction {
}
};/* struct NodeTheoryPairHashFunction */
+
+
+
namespace theory {
class TheoryModel;
class TheoryEngineModelBuilder;
@@ -271,8 +274,10 @@ class TheoryEngine {
}
void safePoint() throw(theory::Interrupted, AssertionException) {
- if (d_engine->d_interrupted)
+ spendResource();
+ if (d_engine->d_interrupted) {
throw theory::Interrupted();
+ }
}
void conflict(TNode conflictNode) throw(AssertionException) {
@@ -293,7 +298,14 @@ class TheoryEngine {
Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl;
++ d_statistics.lemmas;
d_engine->d_outputChannelUsed = true;
- return d_engine->lemma(lemma, false, removable);
+ return d_engine->lemma(lemma, false, removable, theory::THEORY_LAST);
+ }
+
+ theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException) {
+ Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl;
+ ++ d_statistics.lemmas;
+ d_engine->d_outputChannelUsed = true;
+ return d_engine->lemma(lemma, false, removable, d_theory);
}
void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException) {
@@ -329,6 +341,7 @@ class TheoryEngine {
void spendResource() throw() {
d_engine->spendResource();
}
+
void handleUserAttribute( const char* attr, theory::Theory* t ){
d_engine->handleUserAttribute( attr, t );
}
@@ -430,10 +443,20 @@ class TheoryEngine {
*/
bool d_outputChannelUsed;
+ /** Atom requests from lemmas */
+ AtomRequests d_atomRequests;
+
/**
* Adds a new lemma, returning its status.
+ * @param node the lemma
+ * @param negated should the lemma be asserted negated
+ * @param removable can the lemma be remove (restrictions apply)
+ * @param needAtoms if not THEORY_LAST, then
*/
- theory::LemmaStatus lemma(TNode node, bool negated, bool removable);
+ theory::LemmaStatus lemma(TNode node, bool negated, bool removable, theory::TheoryId atomsTo);
+
+ /** Enusre that the given atoms are send to the given theory */
+ void ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId theory);
RemoveITE& d_iteRemover;
@@ -535,13 +558,18 @@ private:
context::CDO<bool> d_factsAsserted;
/**
+ * Map from equality atoms to theories that would like to be notified about them.
+ */
+
+
+ /**
* Assert the formula to the given theory.
* @param assertion the assertion to send (not necesserily normalized)
* @param original the assertion as it was sent in from the propagating theory
* @param toTheoryId the theory to assert to
* @param fromTheoryId the theory that sent it
*/
- void assertToTheory(TNode assertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId);
+ void assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId);
/**
* Marks a theory propagation from a theory to a theory where a
@@ -744,7 +772,7 @@ private:
void dumpAssertions(const char* tag);
/** For preprocessing pass simplifying ITEs */
- ITESimplifier d_iteSimplifier;
+ theory::ITESimplifier d_iteSimplifier;
/** For preprocessing pass simplifying unconstrained expressions */
UnconstrainedSimplifier d_unconstrainedSimp;
diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h
index 237b09bc1..e4921b163 100644
--- a/src/theory/theory_test_utils.h
+++ b/src/theory/theory_test_utils.h
@@ -107,6 +107,11 @@ public:
d_callHistory.clear();
}
+ LemmaStatus splitLemma(TNode n, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException){
+ push(LEMMA, n);
+ return LemmaStatus(Node::null(), 0);
+ }
+
Node getIthNode(int i) {
Node tmp = (d_callHistory[i]).second;
return tmp;
diff --git a/src/theory/theoryof_mode.h b/src/theory/theoryof_mode.h
index cd8c68b1a..c50960257 100644
--- a/src/theory/theoryof_mode.h
+++ b/src/theory/theoryof_mode.h
@@ -14,10 +14,10 @@
** Option selection for theoryOf() operation.
**/
-#pragma once
-
#include "cvc4_public.h"
+#pragma once
+
namespace CVC4 {
namespace theory {
@@ -29,6 +29,18 @@ enum TheoryOfMode {
THEORY_OF_TERM_BASED
};/* enum TheoryOfMode */
+inline std::ostream& operator<<(std::ostream& out, TheoryOfMode m) throw() CVC4_PUBLIC;
+
+inline std::ostream& operator<<(std::ostream& out, TheoryOfMode m) throw() {
+ switch(m) {
+ case THEORY_OF_TYPE_BASED: return out << "THEORY_OF_TYPE_BASED";
+ case THEORY_OF_TERM_BASED: return out << "THEORY_OF_TERM_BASED";
+ default: return out << "TheoryOfMode!UNKNOWN";
+ }
+
+ Unreachable();
+}
+
}/* CVC4::theory namespace */
}/* CVC4 namespace */
diff --git a/src/theory/type_enumerator.h b/src/theory/type_enumerator.h
index 7a4fcdaff..467bf1927 100644
--- a/src/theory/type_enumerator.h
+++ b/src/theory/type_enumerator.h
@@ -98,13 +98,21 @@ public:
~TypeEnumerator() { delete d_te; }
bool isFinished() throw() {
-#ifdef CVC4_ASSERTIONS
+// On Mac clang, there appears to be a code generation bug in an exception
+// block here. For now, there doesn't appear a good workaround; just disable
+// assertions on that setup.
+#if defined(CVC4_ASSERTIONS) && !(defined(__APPLE__) && defined(__clang__))
if(d_te->isFinished()) {
try {
**d_te;
Assert(false, "expected an NoMoreValuesException to be thrown");
} catch(NoMoreValuesException&) {
// ignore the exception, we're just asserting that it would be thrown
+ //
+ // This block can crash on clang 3.0 on Mac OS, perhaps related to
+ // bug: http://llvm.org/bugs/show_bug.cgi?id=13359
+ //
+ // Hence the #if !(defined(__APPLE__) && defined(__clang__)) above
}
} else {
try {
@@ -113,11 +121,14 @@ public:
Assert(false, "didn't expect a NoMoreValuesException to be thrown");
}
}
-#endif /* CVC4_ASSERTIONS */
+#endif /* CVC4_ASSERTIONS && !(APPLE || clang) */
return d_te->isFinished();
}
Node operator*() throw(NoMoreValuesException) {
-#ifdef CVC4_ASSERTIONS
+// On Mac clang, there appears to be a code generation bug in an exception
+// block above (and perhaps here, too). For now, there doesn't appear a
+// good workaround; just disable assertions on that setup.
+#if defined(CVC4_ASSERTIONS) && !(defined(__APPLE__) && defined(__clang__))
try {
Node n = **d_te;
Assert(n.isConst());
@@ -127,9 +138,9 @@ public:
Assert(isFinished());
throw;
}
-#else /* CVC4_ASSERTIONS */
+#else /* CVC4_ASSERTIONS && !(APPLE || clang) */
return **d_te;
-#endif /* CVC4_ASSERTIONS */
+#endif /* CVC4_ASSERTIONS && !(APPLE || clang) */
}
TypeEnumerator& operator++() throw() { ++*d_te; return *this; }
TypeEnumerator operator++(int) throw() { TypeEnumerator te = *this; ++*d_te; return te; }
diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp
index 7f0f79ebc..199581b98 100644
--- a/src/theory/uf/equality_engine.cpp
+++ b/src/theory/uf/equality_engine.cpp
@@ -1348,7 +1348,7 @@ void EqualityEngine::propagate() {
}
// If not merging internal nodes, notify the master
- if (d_masterEqualityEngine && !d_isInternal[mergeInto]) {
+ if (d_masterEqualityEngine && !d_isInternal[t1classId] && !d_isInternal[t2classId]) {
d_masterEqualityEngine->assertEqualityInternal(d_nodes[t1classId], d_nodes[t2classId], TNode::null());
d_masterEqualityEngine->propagate();
}
diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h
index 206fb61c7..8d1b6f1d9 100644
--- a/src/theory/uf/equality_engine.h
+++ b/src/theory/uf/equality_engine.h
@@ -211,11 +211,6 @@ public:
}
};/* struct EqualityEngine::statistics */
- /**
- * Store the application lookup, with enough information to backtrack
- */
- void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId);
-
private:
/** The context we are using */
@@ -254,6 +249,11 @@ private:
/** Number of application lookups, for backtracking. */
context::CDO<DefaultSizeType> d_applicationLookupsCount;
+ /**
+ * Store the application lookup, with enough information to backtrack
+ */
+ void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId);
+
/** Map from ids to the nodes (these need to be nodes as we pick up the operators) */
std::vector<Node> d_nodes;
@@ -741,7 +741,7 @@ public:
/**
* Adds a predicate p with given polarity. The predicate asserted
* should be in the congruence closure kinds (otherwise it's
- * useless.
+ * useless).
*
* @param p the (non-negated) predicate
* @param polarity true if asserting the predicate, false if
@@ -777,7 +777,7 @@ public:
void getUseListTerms(TNode t, std::set<TNode>& output);
/**
- * Get an explanation of the equality t1 = t2 begin true of false.
+ * Get an explanation of the equality t1 = t2 being true or false.
* Returns the reasons (added when asserting) that imply it
* in the assertions vector.
*/
diff --git a/src/theory/uf/options b/src/theory/uf/options
index 33d1255ef..b9f60b83d 100644
--- a/src/theory/uf/options
+++ b/src/theory/uf/options
@@ -8,6 +8,9 @@ module UF "theory/uf/options.h" Uninterpreted functions theory
option ufSymmetryBreaker uf-symmetry-breaker --symmetry-breaker bool :read-write :default true
use UF symmetry breaker (Deharbe et al., CADE 2011)
+option condenseFunctionValues condense-function-values --condense-function-values bool :default true
+ condense models for functions rather than explicitly representing them
+
option ufssRegions /--disable-uf-ss-regions bool :default true
disable region-based method for discovering cliques and splits in uf strong solver
option ufssEagerSplits --uf-ss-eager-split bool :default false
@@ -20,6 +23,8 @@ option ufssTotalityLimited --uf-ss-totality-limited=N int :default -1
apply totality axioms, but only up to cardinality N (-1 == do not apply totality axioms, default)
option ufssTotalityLazy --uf-ss-totality-lazy bool :default false
apply totality axioms lazily
+option ufssTotalitySymBreak --uf-ss-totality-sym-break bool :default false
+ apply symmetry breaking for totality axioms
option ufssAbortCardinality --uf-ss-abort-card=N int :default -1
tells the uf strong solver a cardinality to abort at (-1 == no limit, default)
option ufssSmartSplits --uf-ss-smart-split bool :default false
@@ -30,5 +35,12 @@ option ufssSimpleCliques --uf-ss-simple-cliques bool :default true
always use simple clique lemmas for uf strong solver
option ufssDiseqPropagation --uf-ss-deq-prop bool :default false
eagerly propagate disequalities for uf strong solver
+option ufssMinimalModel /--disable-uf-ss-min-model bool :default true
+ disable finding a minimal model in uf strong solver
+option ufssCliqueSplits --uf-ss-clique-splits bool :default false
+ use cliques instead of splitting on demand to shrink model
+
+option ufssSymBreak --uf-ss-sym-break bool :default false
+ finite model finding symmetry breaking techniques
endmodule
diff --git a/src/theory/uf/symmetry_breaker.cpp b/src/theory/uf/symmetry_breaker.cpp
index fcb6c3cd5..f5d7f9235 100644
--- a/src/theory/uf/symmetry_breaker.cpp
+++ b/src/theory/uf/symmetry_breaker.cpp
@@ -52,6 +52,8 @@ namespace CVC4 {
namespace theory {
namespace uf {
+using namespace ::CVC4::context;
+
SymmetryBreaker::Template::Template() :
d_template(),
d_sets(),
@@ -165,7 +167,10 @@ void SymmetryBreaker::Template::reset() {
d_reps.clear();
}
-SymmetryBreaker::SymmetryBreaker() :
+SymmetryBreaker::SymmetryBreaker(context::Context* context) :
+ ContextNotifyObj(context),
+ d_assertionsToRerun(context),
+ d_rerunningAssertions(false),
d_phi(),
d_phiSet(),
d_permutations(),
@@ -175,6 +180,31 @@ SymmetryBreaker::SymmetryBreaker() :
d_termEqs() {
}
+class SBGuard {
+ bool& d_ref;
+ bool d_old;
+public:
+ SBGuard(bool& b) : d_ref(b), d_old(b) {}
+ ~SBGuard() { Debug("uf") << "reset to " << d_old << std::endl; d_ref = d_old; }
+};/* class SBGuard */
+
+void SymmetryBreaker::rerunAssertionsIfNecessary() {
+ if(d_rerunningAssertions || !d_phi.empty() || d_assertionsToRerun.empty()) {
+ return;
+ }
+
+ SBGuard g(d_rerunningAssertions);
+ d_rerunningAssertions = true;
+
+ Debug("ufsymm") << "UFSYMM: rerunning assertions..." << std::endl;
+ for(CDList<Node>::const_iterator i = d_assertionsToRerun.begin();
+ i != d_assertionsToRerun.end();
+ ++i) {
+ assertFormula(*i);
+ }
+ Debug("ufsymm") << "UFSYMM: DONE rerunning assertions..." << std::endl;
+}
+
Node SymmetryBreaker::norm(TNode phi) {
Node n = Rewriter::rewrite(phi);
return normInternal(n);
@@ -254,6 +284,10 @@ Node SymmetryBreaker::normInternal(TNode n) {
}
void SymmetryBreaker::assertFormula(TNode phi) {
+ rerunAssertionsIfNecessary();
+ if(!d_rerunningAssertions) {
+ d_assertionsToRerun.push_back(phi);
+ }
// use d_phi, put into d_permutations
Debug("ufsymm") << "UFSYMM assertFormula(): phi is " << phi << endl;
d_phi.push_back(phi);
@@ -322,6 +356,7 @@ void SymmetryBreaker::clear() {
}
void SymmetryBreaker::apply(std::vector<Node>& newClauses) {
+ rerunAssertionsIfNecessary();
guessPermutations();
Debug("ufsymm") << "UFSYMM =====================================================" << endl
<< "UFSYMM have " << d_permutations.size() << " permutation sets" << endl;
diff --git a/src/theory/uf/symmetry_breaker.h b/src/theory/uf/symmetry_breaker.h
index cf54b62c2..d04da048a 100644
--- a/src/theory/uf/symmetry_breaker.h
+++ b/src/theory/uf/symmetry_breaker.h
@@ -50,13 +50,15 @@
#include "expr/node.h"
#include "expr/node_builder.h"
+#include "context/context.h"
+#include "context/cdlist.h"
#include "util/statistics_registry.h"
namespace CVC4 {
namespace theory {
namespace uf {
-class SymmetryBreaker {
+class SymmetryBreaker : public context::ContextNotifyObj {
class Template {
Node d_template;
@@ -92,6 +94,19 @@ public:
private:
+ /**
+ * This class wasn't initially built to be incremental. It should
+ * be attached to a UserContext so that it clears everything when
+ * a pop occurs. This "assertionsToRerun" is a set of assertions to
+ * feed back through assertFormula() when we started getting things
+ * again. It's not just a matter of effectiveness, but also soundness;
+ * if some assertions (still in scope) are not seen by a symmetry-breaking
+ * round, then some symmetries that don't actually exist might be broken,
+ * leading to unsound results!
+ */
+ context::CDList<Node> d_assertionsToRerun;
+ bool d_rerunningAssertions;
+
std::vector<Node> d_phi;
std::set<TNode> d_phiSet;
Permutations d_permutations;
@@ -101,6 +116,7 @@ private:
TermEqs d_termEqs;
void clear();
+ void rerunAssertionsIfNecessary();
void guessPermutations();
bool invariantByPermutations(const Permutation& p);
@@ -140,9 +156,17 @@ private:
d_initNormalizationTimer,
"theory::uf::symmetry_breaker::timers::initNormalization");
+protected:
+
+ void contextNotifyPop() {
+ Debug("ufsymm") << "UFSYMM: clearing state due to pop" << std::endl;
+ clear();
+ }
+
public:
- SymmetryBreaker();
+ SymmetryBreaker(context::Context* context);
+ ~SymmetryBreaker() throw() {}
void assertFormula(TNode phi);
void apply(std::vector<Node>& newClauses);
diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp
index 69a963360..41935984f 100644
--- a/src/theory/uf/theory_uf.cpp
+++ b/src/theory/uf/theory_uf.cpp
@@ -38,7 +38,8 @@ TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel&
d_conflict(c, false),
d_literalsToPropagate(c),
d_literalsToPropagateIndex(c, 0),
- d_functionsTerms(c)
+ d_functionsTerms(c),
+ d_symb(u)
{
// The kinds we are treating as function application in congruence
d_equalityEngine.addFunctionKind(kind::APPLY_UF);
diff --git a/src/theory/uf/theory_uf_model.cpp b/src/theory/uf/theory_uf_model.cpp
index 228cfd2c4..c0d114052 100644
--- a/src/theory/uf/theory_uf_model.cpp
+++ b/src/theory/uf/theory_uf_model.cpp
@@ -17,6 +17,10 @@
#include "theory/uf/equality_engine.h"
#include "theory/uf/theory_uf.h"
#include "theory/quantifiers/term_database.h"
+#include "theory/quantifiers/options.h"
+
+#include <vector>
+#include <stack>
#define RECONSIDER_FUNC_DEFAULT_VALUE
#define USE_PARTIAL_DEFAULT_VALUES
@@ -133,28 +137,60 @@ Node UfModelTreeNode::getValue( TheoryModel* m, Node n, std::vector< int >& inde
}
}
-Node UfModelTreeNode::getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue ){
- if( !d_data.empty() ){
+Node UfModelTreeNode::getFunctionValue(std::vector<Node>& args, int index, Node argDefaultValue, bool simplify) {
+ if(!d_data.empty()) {
Node defaultValue = argDefaultValue;
- if( d_data.find( Node::null() )!=d_data.end() ){
- defaultValue = d_data[Node::null()].getFunctionValue( args, index+1, argDefaultValue );
+ if(d_data.find(Node::null()) != d_data.end()) {
+ defaultValue = d_data[Node::null()].getFunctionValue(args, index + 1, argDefaultValue, simplify);
}
- std::vector< Node > caseArgs;
- std::map< Node, Node > caseValues;
- for( std::map< Node, UfModelTreeNode >::iterator it = d_data.begin(); it != d_data.end(); ++it ){
- if( !it->first.isNull() ){
- Node val = it->second.getFunctionValue( args, index+1, defaultValue );
- caseArgs.push_back( it->first );
- caseValues[ it->first ] = val;
+
+ vector<Node> caseArgs;
+ map<Node, Node> caseValues;
+
+ for(map< Node, UfModelTreeNode>::iterator it = d_data.begin(); it != d_data.end(); ++it) {
+ if(!it->first.isNull()) {
+ Node val = it->second.getFunctionValue(args, index + 1, defaultValue, simplify);
+ caseArgs.push_back(it->first);
+ caseValues[it->first] = val;
}
}
+
+ NodeManager* nm = NodeManager::currentNM();
Node retNode = defaultValue;
- for( int i=((int)caseArgs.size()-1); i>=0; i-- ){
- retNode = NodeManager::currentNM()->mkNode( ITE, args[index].eqNode( caseArgs[ i ] ), caseValues[ caseArgs[ i ] ], retNode );
+
+ if(!simplify) {
+ // "non-simplifying" mode - expand function values to things like:
+ // IF (x=0 AND y=0 AND z=0) THEN value1
+ // ELSE IF (x=0 AND y=0 AND z=1) THEN value2
+ // [...etc...]
+ for(int i = (int)caseArgs.size() - 1; i >= 0; --i) {
+ Node val = caseValues[ caseArgs[ i ] ];
+ if(val.getKind() == ITE) {
+ // use a stack to reverse the order, since we're traversing outside-in
+ stack<TNode> stk;
+ do {
+ stk.push(val);
+ val = val[2];
+ } while(val.getKind() == ITE);
+ AlwaysAssert(val == defaultValue, "default values don't match when constructing function definition!");
+ while(!stk.empty()) {
+ val = stk.top();
+ stk.pop();
+ retNode = nm->mkNode(ITE, nm->mkNode(AND, args[index].eqNode(caseArgs[i]), val[0]), val[1], retNode);
+ }
+ } else {
+ retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode);
+ }
+ }
+ } else {
+ // "simplifying" mode - condense function values
+ for(int i = (int)caseArgs.size() - 1; i >= 0; --i) {
+ retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode);
+ }
}
return retNode;
- }else{
- Assert( !d_value.isNull() );
+ } else {
+ Assert(!d_value.isNull());
return d_value;
}
}
@@ -259,14 +295,16 @@ void UfModelTreeNode::debugPrint( std::ostream& out, TheoryModel* m, std::vector
}
}
-Node UfModelTree::getFunctionValue( std::vector< Node >& args ){
- Node body = d_tree.getFunctionValue( args, 0, Node::null() );
- body = Rewriter::rewrite( body );
+Node UfModelTree::getFunctionValue( std::vector< Node >& args, bool simplify ){
+ Node body = d_tree.getFunctionValue( args, 0, Node::null(), simplify );
+ if(simplify) {
+ body = Rewriter::rewrite( body );
+ }
Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args);
return NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, body);
}
-Node UfModelTree::getFunctionValue( const char* argPrefix ){
+Node UfModelTree::getFunctionValue( const char* argPrefix, bool simplify ){
TypeNode type = d_op.getType();
std::vector< Node > vars;
for( size_t i=0; i<type.getNumChildren()-1; i++ ){
@@ -274,7 +312,7 @@ Node UfModelTree::getFunctionValue( const char* argPrefix ){
ss << argPrefix << (i+1);
vars.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), type[i] ) );
}
- return getFunctionValue( vars );
+ return getFunctionValue( vars, simplify );
}
Node UfModelTreeGenerator::getIntersection( TheoryModel* m, Node n1, Node n2, bool& isGround ){
@@ -309,19 +347,21 @@ void UfModelTreeGenerator::setValue( TheoryModel* m, Node n, Node v, bool ground
if( !ground ){
int defSize = (int)d_defaults.size();
for( int i=0; i<defSize; i++ ){
- bool isGround;
//for soundness, to allow variable order-independent function interpretations,
// we must ensure that the intersection of all default terms
// is also defined.
//for example, if we have that f( e, a ) = ..., and f( b, e ) = ...,
// then we must define f( b, a ).
- Node ni = getIntersection( m, n, d_defaults[i], isGround );
- if( !ni.isNull() ){
- //if the intersection exists, and is not already defined
- if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() &&
- d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){
- //use the current value
- setValue( m, ni, v, isGround, false );
+ if (!options::fmfFullModelCheck()) {
+ bool isGround;
+ Node ni = getIntersection( m, n, d_defaults[i], isGround );
+ if( !ni.isNull() ){
+ //if the intersection exists, and is not already defined
+ if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() &&
+ d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){
+ //use the current value
+ setValue( m, ni, v, isGround, false );
+ }
}
}
}
diff --git a/src/theory/uf/theory_uf_model.h b/src/theory/uf/theory_uf_model.h
index 12c1cf244..133cd2cf2 100644
--- a/src/theory/uf/theory_uf_model.h
+++ b/src/theory/uf/theory_uf_model.h
@@ -46,7 +46,7 @@ public:
/** getConstant Value function */
Node getConstantValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int argIndex );
/** getFunctionValue */
- Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue );
+ Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue, bool simplify = true );
/** update function */
void update( TheoryModel* m );
/** simplify function */
@@ -125,9 +125,9 @@ public:
/** getFunctionValue
* Returns a representation of this function.
*/
- Node getFunctionValue( std::vector< Node >& args );
+ Node getFunctionValue( std::vector< Node >& args, bool simplify = true );
/** getFunctionValue for args with set prefix */
- Node getFunctionValue( const char* argPrefix );
+ Node getFunctionValue( const char* argPrefix, bool simplify = true );
/** update
* This will update all values in the tree to be representatives in m.
*/
@@ -144,18 +144,12 @@ public:
void debugPrint( std::ostream& out, TheoryModel* m, int ind = 0 ){
d_tree.debugPrint( out, m, d_index_order, ind );
}
-private:
- //helper for to ITE function.
- static Node toIte2( Node fm_node, std::vector< Node >& args, int index, Node defaultNode );
-public:
- /** to ITE function for function model nodes */
- static Node toIte( Node fm_node, std::vector< Node >& args ) { return toIte2( fm_node, args, 0, Node::null() ); }
- static Node toIte( TypeNode type, Node fm_node, const char* argPrefix );
};
+
class UfModelTreeGenerator
{
-private:
+public:
//store for set values
Node d_default_value;
std::map< Node, Node > d_set_values[2][2];
diff --git a/src/theory/uf/theory_uf_rewriter.h b/src/theory/uf/theory_uf_rewriter.h
index 94ab47d23..40713fa41 100644
--- a/src/theory/uf/theory_uf_rewriter.h
+++ b/src/theory/uf/theory_uf_rewriter.h
@@ -21,6 +21,7 @@
#define __CVC4__THEORY__UF__THEORY_UF_REWRITER_H
#include "theory/rewriter.h"
+#include "theory/substitutions.h"
namespace CVC4 {
namespace theory {
diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp
index d64f7df60..163dd3c1f 100644
--- a/src/theory/uf/theory_uf_strong_solver.cpp
+++ b/src/theory/uf/theory_uf_strong_solver.cpp
@@ -20,6 +20,8 @@
#include "theory/quantifiers/term_database.h"
#include "theory/uf/options.h"
#include "theory/model.h"
+#include "theory/quantifiers/symmetry_breaking.h"
+
//#define ONE_SPLIT_REGION
//#define DISABLE_QUICK_CLIQUE_CHECKS
@@ -117,6 +119,9 @@ void StrongSolverTheoryUF::SortModel::Region::setEqual( Node a, Node b ){
if( options::ufssDiseqPropagation() ){
d_cf->d_thss->getDisequalityPropagator()->assertDisequal(a, n, Node::null());
}
+ if( options::ufssSymBreak() ){
+ d_cf->d_thss->getSymmetryBreaker()->assertDisequal( a, n );
+ }
}
setDisequal( b, n, t, false );
nr->setDisequal( n, b, t, false );
@@ -322,6 +327,20 @@ bool StrongSolverTheoryUF::SortModel::Region::check( Theory::Effort level, int c
return false;
}
+bool StrongSolverTheoryUF::SortModel::Region::getCandidateClique( int cardinality, std::vector< Node >& clique ) {
+ if( d_testCliqueSize>=long(cardinality+1) ){
+ //test clique is a clique
+ for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){
+ if( (*it).second ){
+ clique.push_back( (*it).first );
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
void StrongSolverTheoryUF::SortModel::Region::getRepresentatives( std::vector< Node >& reps ){
for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){
RegionNodeInfo* rni = it->second;
@@ -394,9 +413,9 @@ void StrongSolverTheoryUF::SortModel::Region::debugPrint( const char* c, bool in
-StrongSolverTheoryUF::SortModel::SortModel( Node n, context::Context* c, StrongSolverTheoryUF* thss ) : d_type( n.getType() ),
+StrongSolverTheoryUF::SortModel::SortModel( Node n, context::Context* c, context::UserContext* u, StrongSolverTheoryUF* thss ) : d_type( n.getType() ),
d_thss( thss ), d_regions_index( c, 0 ), d_regions_map( c ), d_split_score( c ), d_disequalities_index( c, 0 ),
- d_reps( c, 0 ), d_conflict( c, false ), d_cardinality( c, 1 ), d_aloc_cardinality( 0 ),
+ d_reps( c, 0 ), d_conflict( c, false ), d_cardinality( c, 1 ), d_aloc_cardinality( u, 0 ),
d_cardinality_assertions( c ), d_hasCard( c, false ){
d_cardinality_term = n;
}
@@ -501,9 +520,14 @@ void StrongSolverTheoryUF::SortModel::merge( Node a, Node b ){
}
d_reps = d_reps - 1;
- if( options::ufssDiseqPropagation() && !d_conflict ){
- //notify the disequality propagator
- d_thss->getDisequalityPropagator()->merge(a, b);
+ if( !d_conflict ){
+ if( options::ufssDiseqPropagation() ){
+ //notify the disequality propagator
+ d_thss->getDisequalityPropagator()->merge(a, b);
+ }
+ if( options::ufssSymBreak() ){
+ d_thss->getSymmetryBreaker()->merge(a, b);
+ }
}
}
}
@@ -551,9 +575,14 @@ void StrongSolverTheoryUF::SortModel::assertDisequal( Node a, Node b, Node reaso
checkRegion( bi );
}
- if( options::ufssDiseqPropagation() && !d_conflict ){
- //notify the disequality propagator
- d_thss->getDisequalityPropagator()->assertDisequal(a, b, Node::null());
+ if( !d_conflict ){
+ if( options::ufssDiseqPropagation() ){
+ //notify the disequality propagator
+ d_thss->getDisequalityPropagator()->assertDisequal(a, b, Node::null());
+ }
+ if( options::ufssSymBreak() ){
+ d_thss->getSymmetryBreaker()->assertDisequal(a, b);
+ }
}
}
}
@@ -595,9 +624,11 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel
if( d_regions[i]->d_valid ){
std::vector< Node > clique;
if( d_regions[i]->check( level, d_cardinality, clique ) ){
- //add clique lemma
- addCliqueLemma( clique, out );
- return;
+ if( options::ufssMinimalModel() ){
+ //add clique lemma
+ addCliqueLemma( clique, out );
+ return;
+ }
}else{
Trace("uf-ss-debug") << "No clique in Region #" << i << std::endl;
}
@@ -623,11 +654,21 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel
//see if we have any recommended splits from large regions
for( int i=0; i<(int)d_regions_index; i++ ){
if( d_regions[i]->d_valid && d_regions[i]->getNumReps()>d_cardinality ){
- if( addSplit( d_regions[i], out ) ){
- addedLemma = true;
+ //just add the clique lemma
+ if( level==Theory::EFFORT_FULL && options::ufssCliqueSplits() ){
+ std::vector< Node > clique;
+ if( d_regions[i]->getCandidateClique( d_cardinality, clique ) ){
+ //add clique lemma
+ addCliqueLemma( clique, out );
+ return;
+ }
+ }else{
+ if( addSplit( d_regions[i], out ) ){
+ addedLemma = true;
#ifdef ONE_SPLIT_REGION
- break;
+ break;
#endif
+ }
}
}
}
@@ -644,7 +685,7 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel
for( int i=0; i<(int)d_regions_index; i++ ){
if( d_regions[i]->d_valid ){
Node op = d_regions[i]->d_nodes.begin()->first;
- int sort_id = d_thss->getTheory()->getQuantifiersEngine()->getTheoryEngine()->getSortInference()->getSortId(op);
+ int sort_id = d_thss->getSortInference()->getSortId(op);
if( sortsFound.find( sort_id )!=sortsFound.end() ){
combineRegions( sortsFound[sort_id], i );
recheck = true;
@@ -659,13 +700,17 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel
//naive strategy, force region combination involving the first valid region
for( int i=0; i<(int)d_regions_index; i++ ){
if( d_regions[i]->d_valid ){
- forceCombineRegion( i, false );
- recheck = true;
- break;
+ int fcr = forceCombineRegion( i, false );
+ Trace("uf-ss-debug") << "Combined regions " << i << " " << fcr << std::endl;
+ if( options::ufssMinimalModel() || fcr!=-1 ){
+ recheck = true;
+ break;
+ }
}
}
}
if( recheck ){
+ Trace("uf-ss-debug") << "Must recheck." << std::endl;
check( level, out );
}
}
@@ -869,8 +914,10 @@ void StrongSolverTheoryUF::SortModel::checkRegion( int ri, bool checkCombine ){
//now check if region is in conflict
std::vector< Node > clique;
if( d_regions[ri]->check( Theory::EFFORT_STANDARD, d_cardinality, clique ) ){
- //explain clique
- addCliqueLemma( clique, &d_thss->getOutputChannel() );
+ if( options::ufssMinimalModel() ){
+ //explain clique
+ addCliqueLemma( clique, &d_thss->getOutputChannel() );
+ }
}
}
}
@@ -947,18 +994,33 @@ void StrongSolverTheoryUF::SortModel::moveNode( Node n, int ri ){
void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){
if( d_aloc_cardinality>0 ){
Trace("uf-ss-fmf") << "No model of size " << d_aloc_cardinality << " exists for type " << d_type << " in this branch" << std::endl;
- if( Trace.isOn("uf-ss-cliques") ){
- Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality+1) << " : " << std::endl;
- for( size_t i=0; i<d_cliques[ d_aloc_cardinality ].size(); i++ ){
- Trace("uf-ss-cliques") << " ";
- for( size_t j=0; j<d_cliques[ d_aloc_cardinality ][i].size(); j++ ){
- Trace("uf-ss-cliques") << d_cliques[ d_aloc_cardinality ][i][j] << " ";
- }
- Trace("uf-ss-cliques") << std::endl;
+ }
+ if( Trace.isOn("uf-ss-cliques") ){
+ Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality+1) << " for " << d_type << " : " << std::endl;
+ for( size_t i=0; i<d_cliques[ d_aloc_cardinality ].size(); i++ ){
+ Trace("uf-ss-cliques") << " ";
+ for( size_t j=0; j<d_cliques[ d_aloc_cardinality ][i].size(); j++ ){
+ Trace("uf-ss-cliques") << d_cliques[ d_aloc_cardinality ][i][j] << " ";
}
+ Trace("uf-ss-cliques") << std::endl;
+ }
+ }
+ /*
+ if( options::ufssSymBreak() ){
+ std::vector< Node > reps;
+ getRepresentatives( reps );
+ if( d_aloc_cardinality>0 ){
+ d_thss->getSymmetryBreaker()->allocateCardinality( out, d_type, d_aloc_cardinality+1, d_cliques[ d_aloc_cardinality ], reps );
+ }else{
+ std::vector< Node > clique;
+ clique.push_back( d_cardinality_term );
+ std::vector< std::vector< Node > > cliques;
+ cliques.push_back( clique );
+ d_thss->getSymmetryBreaker()->allocateCardinality( out, d_type, 1, cliques, reps );
}
}
- d_aloc_cardinality++;
+ */
+ d_aloc_cardinality = d_aloc_cardinality + 1;
//check for abort case
if( options::ufssAbortCardinality()==d_aloc_cardinality ){
@@ -969,7 +1031,7 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){
if( applyTotality( d_aloc_cardinality ) ){
//must generate new cardinality lemma term
Node var;
- if( d_aloc_cardinality==1 ){
+ if( d_aloc_cardinality==1 && !options::ufssTotalitySymBreak() ){
//get arbitrary ground term
var = d_cardinality_term;
}else{
@@ -1013,8 +1075,8 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){
}
bool StrongSolverTheoryUF::SortModel::addSplit( Region* r, OutputChannel* out ){
+ Node s;
if( r->hasSplits() ){
- Node s;
if( !options::ufssSmartSplits() ){
//take the first split you find
for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){
@@ -1038,13 +1100,31 @@ bool StrongSolverTheoryUF::SortModel::addSplit( Region* r, OutputChannel* out ){
}
}
}
+ Assert( s!=Node::null() );
+ }else{
+ if( !options::ufssMinimalModel() ){
+ //since candidate clique is not reported, we may need to find splits manually
+ for ( std::map< Node, Region::RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){
+ if ( it->second->d_valid ){
+ for ( std::map< Node, Region::RegionNodeInfo* >::iterator it2 = r->d_nodes.begin(); it2 != r->d_nodes.end(); ++it2 ){
+ if ( it->second!=it2->second && it2->second->d_valid ){
+ if( !r->isDisequal( it->first, it2->first, 1 ) ){
+ s = NodeManager::currentNM()->mkNode( EQUAL, it->first, it2->first );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!s.isNull() ){
//add lemma to output channel
- Assert( s!=Node::null() && s.getKind()==EQUAL );
+ Assert( s.getKind()==EQUAL );
s = Rewriter::rewrite( s );
Trace("uf-ss-lemma") << "*** Split on " << s << std::endl;
if( options::sortInference()) {
for( int i=0; i<2; i++ ){
- int si = d_thss->getTheory()->getQuantifiersEngine()->getTheoryEngine()->getSortInference()->getSortId( s[i] );
+ int si = d_thss->getSortInference()->getSortId( s[i] );
Trace("uf-ss-split-si") << si << " ";
}
Trace("uf-ss-split-si") << std::endl;
@@ -1071,6 +1151,12 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu
while( clique.size()>size_t(d_cardinality+1) ){
clique.pop_back();
}
+ //debugging information
+ if( options::ufssSymBreak() ){
+ std::vector< Node > clique_vec;
+ clique_vec.insert( clique_vec.begin(), clique.begin(), clique.end() );
+ addClique( d_cardinality, clique_vec );
+ }
if( options::ufssSimpleCliques() && !options::ufssExplainedCliques() ){
//add as lemma
std::vector< Node > eqs;
@@ -1083,16 +1169,10 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu
}
eqs.push_back( d_cardinality_literal[ d_cardinality ].notNode() );
Node lem = NodeManager::currentNM()->mkNode( OR, eqs );
- Trace("uf-ss-lemma") << "*** Add clique conflict " << lem << std::endl;
+ Trace("uf-ss-lemma") << "*** Add clique lemma " << lem << std::endl;
++( d_thss->d_statistics.d_clique_lemmas );
out->lemma( lem );
}else{
- //debugging information
- if( Trace.isOn("uf-ss-cliques") ){
- std::vector< Node > clique_vec;
- clique_vec.insert( clique_vec.begin(), clique.begin(), clique.end() );
- d_cliques[ d_cardinality ].push_back( clique_vec );
- }
//found a clique
Debug("uf-ss-cliques") << "Found a clique (cardinality=" << d_cardinality << ") :" << std::endl;
Debug("uf-ss-cliques") << " ";
@@ -1217,19 +1297,49 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu
}
void StrongSolverTheoryUF::SortModel::addTotalityAxiom( Node n, int cardinality, OutputChannel* out ){
- Node cardLit = d_cardinality_literal[ cardinality ];
- std::vector< Node > eqs;
- for( int i=0; i<cardinality; i++ ){
- eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) );
+ if( std::find( d_totality_terms[0].begin(), d_totality_terms[0].end(), n )==d_totality_terms[0].end() ){
+ if( std::find( d_totality_lems[n].begin(), d_totality_lems[n].end(), cardinality ) == d_totality_lems[n].end() ){
+ d_totality_lems[n].push_back( cardinality );
+ Node cardLit = d_cardinality_literal[ cardinality ];
+ int sort_id = 0;
+ if( options::sortInference() ){
+ sort_id = d_thss->getSortInference()->getSortId(n);
+ }
+ Trace("uf-ss-totality") << "Add totality lemma for " << n << " " << cardinality << ", sort id is " << sort_id << std::endl;
+ int use_cardinality = cardinality;
+ if( options::ufssTotalitySymBreak() ){
+ if( d_sym_break_index.find(n)!=d_sym_break_index.end() ){
+ use_cardinality = d_sym_break_index[n];
+ }else if( (int)d_sym_break_terms[n.getType()][sort_id].size()<cardinality-1 ){
+ use_cardinality = d_sym_break_terms[n.getType()][sort_id].size() + 1;
+ d_sym_break_terms[n.getType()][sort_id].push_back( n );
+ d_sym_break_index[n] = use_cardinality;
+ Trace("uf-ss-totality") << "Allocate symmetry breaking term " << n << ", index = " << use_cardinality << std::endl;
+ }
+ }
+
+ std::vector< Node > eqs;
+ for( int i=0; i<use_cardinality; i++ ){
+ eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) );
+ }
+ Node ax = NodeManager::currentNM()->mkNode( OR, eqs );
+ Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax );
+ Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl;
+ //send as lemma to the output channel
+ d_thss->getOutputChannel().lemma( lem );
+ ++( d_thss->d_statistics.d_totality_lemmas );
+ }
}
- Node ax = NodeManager::currentNM()->mkNode( OR, eqs );
- Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax );
- Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl;
- //send as lemma to the output channel
- d_thss->getOutputChannel().lemma( lem );
- ++( d_thss->d_statistics.d_totality_lemmas );
}
+void StrongSolverTheoryUF::SortModel::addClique( int c, std::vector< Node >& clique ) {
+
+ if( d_clique_trie[c].add( clique ) ){
+ d_cliques[ c ].push_back( clique );
+ }
+}
+
+
/** apply totality */
bool StrongSolverTheoryUF::SortModel::applyTotality( int cardinality ){
return options::ufssTotality() || cardinality<=options::ufssTotalityLimited();
@@ -1307,22 +1417,16 @@ int StrongSolverTheoryUF::SortModel::getNumRegions(){
}
void StrongSolverTheoryUF::SortModel::getRepresentatives( std::vector< Node >& reps ){
- //if( !options::ufssColoringSat() ){
- bool foundRegion = false;
- for( int i=0; i<(int)d_regions_index; i++ ){
- //should not have multiple regions at this point
- if( foundRegion ){
- Assert( !d_regions[i]->d_valid );
- }
- if( d_regions[i]->d_valid ){
- //this is the only valid region
- d_regions[i]->getRepresentatives( reps );
- foundRegion = true;
- }
+ for( int i=0; i<(int)d_regions_index; i++ ){
+ //should not have multiple regions at this point
+ //if( foundRegion ){
+ // Assert( !d_regions[i]->d_valid );
+ //}
+ if( d_regions[i]->d_valid ){
+ //this is the only valid region
+ d_regions[i]->getRepresentatives( reps );
}
- //}else{
- // Unimplemented("Build representatives for fmf region sat is not implemented");
- //}
+ }
}
StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) :
@@ -1343,6 +1447,15 @@ d_rep_model_init( c )
}else{
d_deq_prop = NULL;
}
+ if( options::ufssSymBreak() ){
+ d_sym_break = new SubsortSymmetryBreaker( th->getQuantifiersEngine(), c );
+ }else{
+ d_sym_break = NULL;
+ }
+}
+
+SortInference* StrongSolverTheoryUF::getSortInference() {
+ return d_th->getQuantifiersEngine()->getTheoryEngine()->getSortInference();
}
/** get default sat context */
@@ -1361,6 +1474,9 @@ void StrongSolverTheoryUF::newEqClass( Node n ){
if( c ){
Trace("uf-ss-solver") << "StrongSolverTheoryUF: New eq class " << n << " : " << n.getType() << std::endl;
c->newEqClass( n );
+ if( options::ufssSymBreak() ){
+ d_sym_break->newEqClass( n );
+ }
}
}
@@ -1467,6 +1583,10 @@ void StrongSolverTheoryUF::check( Theory::Effort level ){
break;
}
}
+ //check symmetry breaker
+ if( !d_conflict && options::ufssSymBreak() ){
+ d_sym_break->check( level );
+ }
//disambiguate terms if necessary
//if( !d_conflict && level==Theory::EFFORT_FULL && options::ufssColoringSat() ){
// Assert( d_term_amb!=NULL );
@@ -1502,7 +1622,7 @@ void StrongSolverTheoryUF::preRegisterTerm( TNode n ){
SortModel* rm = NULL;
if( tn.isSort() ){
Trace("uf-ss-register") << "Preregister sort " << tn << "." << std::endl;
- rm = new SortModel( n, d_th->getSatContext(), this );
+ rm = new SortModel( n, d_th->getSatContext(), d_th->getUserContext(), this );
}else{
/*
if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){
@@ -1572,6 +1692,14 @@ int StrongSolverTheoryUF::getCardinality( Node n ) {
}
}
+int StrongSolverTheoryUF::getCardinality( TypeNode tn ) {
+ std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn );
+ if( it!=d_rep_model.end() && it->second ){
+ return it->second->getCardinality();
+ }
+ return -1;
+}
+
void StrongSolverTheoryUF::getRepresentatives( Node n, std::vector< Node >& reps ){
SortModel* c = getSortModel( n );
if( c ){
@@ -1626,6 +1754,7 @@ StrongSolverTheoryUF::Statistics::Statistics():
d_clique_lemmas("StrongSolverTheoryUF::Clique_Lemmas", 0),
d_split_lemmas("StrongSolverTheoryUF::Split_Lemmas", 0),
d_disamb_term_lemmas("StrongSolverTheoryUF::Disambiguate_Term_Lemmas", 0),
+ d_sym_break_lemmas("StrongSolverTheoryUF::Symmetry_Breaking_Lemmas", 0),
d_totality_lemmas("StrongSolverTheoryUF::Totality_Lemmas", 0),
d_max_model_size("StrongSolverTheoryUF::Max_Model_Size", 1)
{
@@ -1633,6 +1762,7 @@ StrongSolverTheoryUF::Statistics::Statistics():
StatisticsRegistry::registerStat(&d_clique_lemmas);
StatisticsRegistry::registerStat(&d_split_lemmas);
StatisticsRegistry::registerStat(&d_disamb_term_lemmas);
+ StatisticsRegistry::registerStat(&d_sym_break_lemmas);
StatisticsRegistry::registerStat(&d_totality_lemmas);
StatisticsRegistry::registerStat(&d_max_model_size);
}
@@ -1642,6 +1772,7 @@ StrongSolverTheoryUF::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_clique_lemmas);
StatisticsRegistry::unregisterStat(&d_split_lemmas);
StatisticsRegistry::unregisterStat(&d_disamb_term_lemmas);
+ StatisticsRegistry::unregisterStat(&d_sym_break_lemmas);
StatisticsRegistry::unregisterStat(&d_totality_lemmas);
StatisticsRegistry::unregisterStat(&d_max_model_size);
}
@@ -1785,4 +1916,4 @@ DisequalityPropagator::Statistics::Statistics():
DisequalityPropagator::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(& d_propagations);
-} \ No newline at end of file
+}
diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h
index 0cc995723..8e568444b 100644
--- a/src/theory/uf/theory_uf_strong_solver.h
+++ b/src/theory/uf/theory_uf_strong_solver.h
@@ -26,7 +26,13 @@
#include "util/statistics_registry.h"
namespace CVC4 {
+
+class SortInference;
+
namespace theory {
+
+class SubsortSymmetryBreaker;
+
namespace uf {
class TheoryUF;
@@ -40,11 +46,14 @@ protected:
typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
typedef context::CDChunkList<Node> NodeList;
typedef context::CDList<bool> BoolList;
- typedef context::CDList<bool> IntList;
typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap;
public:
/** information for incremental conflict/clique finding for a particular sort */
class SortModel {
+ private:
+ std::map< Node, std::vector< int > > d_totality_lems;
+ std::map< TypeNode, std::map< int, std::vector< Node > > > d_sym_break_terms;
+ std::map< Node, int > d_sym_break_index;
public:
/** a partition of the current equality graph for which cliques can occur internally */
class Region {
@@ -146,6 +155,8 @@ public:
public:
/** check for cliques */
bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique );
+ /** get candidate clique */
+ bool getCandidateClique( int cardinality, std::vector< Node >& clique );
//print debug
void debugPrint( const char* c, bool incClique = false );
};
@@ -196,12 +207,29 @@ public:
/** add totality axiom */
void addTotalityAxiom( Node n, int cardinality, OutputChannel* out );
private:
+ class NodeTrie {
+ std::map< Node, NodeTrie > d_children;
+ public:
+ bool add( std::vector< Node >& n, unsigned i = 0 ){
+ Assert( i<n.size() );
+ if( i==(n.size()-1) ){
+ bool ret = d_children.find( n[i] )==d_children.end();
+ d_children[n[i]].d_children.clear();
+ return ret;
+ }else{
+ return d_children[n[i]].add( n, i+1 );
+ }
+ }
+ };
+ std::map< int, NodeTrie > d_clique_trie;
+ void addClique( int c, std::vector< Node >& clique );
+ private:
/** Are we in conflict */
context::CDO<bool> d_conflict;
/** cardinality */
context::CDO< int > d_cardinality;
/** maximum allocated cardinality */
- int d_aloc_cardinality;
+ context::CDO< int > d_aloc_cardinality;
/** cardinality lemma term */
Node d_cardinality_term;
/** cardinality totality terms */
@@ -222,7 +250,7 @@ public:
/** get totality lemma terms */
Node getTotalityLemmaTerm( int cardinality, int i );
public:
- SortModel( Node n, context::Context* c, StrongSolverTheoryUF* thss );
+ SortModel( Node n, context::Context* c, context::UserContext* u, StrongSolverTheoryUF* thss );
virtual ~SortModel(){}
/** initialize */
void initialize( OutputChannel* out );
@@ -280,6 +308,8 @@ private:
TermDisambiguator* d_term_amb;
/** disequality propagator */
DisequalityPropagator* d_deq_prop;
+ /** symmetry breaking techniques */
+ SubsortSymmetryBreaker* d_sym_break;
public:
StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th);
~StrongSolverTheoryUF() {}
@@ -289,6 +319,10 @@ public:
TermDisambiguator* getTermDisambiguator() { return d_term_amb; }
/** disequality propagator */
DisequalityPropagator* getDisequalityPropagator() { return d_deq_prop; }
+ /** symmetry breaker */
+ SubsortSymmetryBreaker* getSymmetryBreaker() { return d_sym_break; }
+ /** get sort inference module */
+ SortInference* getSortInference();
/** get default sat context */
context::Context* getSatContext();
/** get default output channel */
@@ -330,8 +364,10 @@ public:
TypeNode getCardinalityType( int i ) { return d_conf_types[i]; }
/** get is in conflict */
bool isConflict() { return d_conflict; }
- /** get cardinality for sort */
+ /** get cardinality for node */
int getCardinality( Node n );
+ /** get cardinality for type */
+ int getCardinality( TypeNode tn );
/** get representatives */
void getRepresentatives( Node n, std::vector< Node >& reps );
/** minimize */
@@ -343,6 +379,7 @@ public:
IntStat d_clique_lemmas;
IntStat d_split_lemmas;
IntStat d_disamb_term_lemmas;
+ IntStat d_sym_break_lemmas;
IntStat d_totality_lemmas;
IntStat d_max_model_size;
Statistics();
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback