summaryrefslogtreecommitdiff
path: root/src/theory/strings/inference_manager.cpp
diff options
context:
space:
mode:
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>2019-07-05 20:40:49 -0500
committerGitHub <noreply@github.com>2019-07-05 20:40:49 -0500
commit1e71ddfb9ac9e368c9f99d351ae7954fb502663e (patch)
tree856648156a3fdf6083d4c0530c41421efb533aa8 /src/theory/strings/inference_manager.cpp
parent2c289524f23a2ec481224b2ea569397acbb5e39e (diff)
Refactor strings to use an inference manager object (#3076)
Diffstat (limited to 'src/theory/strings/inference_manager.cpp')
-rw-r--r--src/theory/strings/inference_manager.cpp407
1 files changed, 407 insertions, 0 deletions
diff --git a/src/theory/strings/inference_manager.cpp b/src/theory/strings/inference_manager.cpp
new file mode 100644
index 000000000..ec144aa6d
--- /dev/null
+++ b/src/theory/strings/inference_manager.cpp
@@ -0,0 +1,407 @@
+/********************* */
+/*! \file inference_manager.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of the inference manager for the theory of strings.
+ **/
+
+#include "theory/strings/inference_manager.h"
+
+#include "expr/kind.h"
+#include "options/strings_options.h"
+#include "theory/rewriter.h"
+#include "theory/strings/theory_strings.h"
+#include "theory/strings/theory_strings_rewriter.h"
+#include "theory/strings/theory_strings_utils.h"
+
+using namespace std;
+using namespace CVC4::context;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace strings {
+
+InferenceManager::InferenceManager(TheoryStrings& p,
+ context::Context* c,
+ context::UserContext* u,
+ eq::EqualityEngine& ee,
+ OutputChannel& out)
+ : d_parent(p), d_ee(ee), d_out(out), d_keep(c)
+{
+ d_true = NodeManager::currentNM()->mkConst(true);
+ d_false = NodeManager::currentNM()->mkConst(false);
+}
+
+bool InferenceManager::sendInternalInference(std::vector<Node>& exp,
+ Node conc,
+ const char* c)
+{
+ if (conc.getKind() == AND
+ || (conc.getKind() == NOT && conc[0].getKind() == OR))
+ {
+ Node conj = conc.getKind() == AND ? conc : conc[0];
+ bool pol = conc.getKind() == AND;
+ bool ret = true;
+ for (const Node& cc : conj)
+ {
+ bool retc = sendInternalInference(exp, pol ? cc : cc.negate(), c);
+ ret = ret && retc;
+ }
+ return ret;
+ }
+ bool pol = conc.getKind() != NOT;
+ Node lit = pol ? conc : conc[0];
+ if (lit.getKind() == EQUAL)
+ {
+ for (unsigned i = 0; i < 2; i++)
+ {
+ if (!lit[i].isConst() && !d_parent.hasTerm(lit[i]))
+ {
+ // introduces a new non-constant term, do not infer
+ return false;
+ }
+ }
+ // does it already hold?
+ if (pol ? d_parent.areEqual(lit[0], lit[1])
+ : d_parent.areDisequal(lit[0], lit[1]))
+ {
+ return true;
+ }
+ }
+ else if (lit.isConst())
+ {
+ if (lit.getConst<bool>())
+ {
+ Assert(pol);
+ // trivially holds
+ return true;
+ }
+ }
+ else if (!d_parent.hasTerm(lit))
+ {
+ // introduces a new non-constant term, do not infer
+ return false;
+ }
+ else if (d_parent.areEqual(lit, pol ? d_true : d_false))
+ {
+ // already holds
+ return true;
+ }
+ sendInference(exp, conc, c);
+ return true;
+}
+
+void InferenceManager::sendInference(std::vector<Node>& exp,
+ std::vector<Node>& exp_n,
+ Node eq,
+ const char* c,
+ bool asLemma)
+{
+ eq = eq.isNull() ? d_false : Rewriter::rewrite(eq);
+ if (eq == d_true)
+ {
+ return;
+ }
+ if (Trace.isOn("strings-infer-debug"))
+ {
+ Trace("strings-infer-debug")
+ << "By " << c << ", infer : " << eq << " from: " << std::endl;
+ for (unsigned i = 0; i < exp.size(); i++)
+ {
+ Trace("strings-infer-debug") << " " << exp[i] << std::endl;
+ }
+ for (unsigned i = 0; i < exp_n.size(); i++)
+ {
+ Trace("strings-infer-debug") << " N:" << exp_n[i] << std::endl;
+ }
+ }
+ // check if we should send a lemma or an inference
+ if (asLemma || eq == d_false || eq.getKind() == OR || !exp_n.empty()
+ || options::stringInferAsLemmas())
+ {
+ Node eq_exp;
+ if (options::stringRExplainLemmas())
+ {
+ eq_exp = d_parent.mkExplain(exp, exp_n);
+ }
+ else
+ {
+ if (exp.empty())
+ {
+ eq_exp = utils::mkAnd(exp_n);
+ }
+ else if (exp_n.empty())
+ {
+ eq_exp = utils::mkAnd(exp);
+ }
+ else
+ {
+ std::vector<Node> ev;
+ ev.insert(ev.end(), exp.begin(), exp.end());
+ ev.insert(ev.end(), exp_n.begin(), exp_n.end());
+ eq_exp = NodeManager::currentNM()->mkNode(AND, ev);
+ }
+ }
+ // if we have unexplained literals, this lemma is not a conflict
+ if (eq == d_false && !exp_n.empty())
+ {
+ eq = eq_exp.negate();
+ eq_exp = d_true;
+ }
+ sendLemma(eq_exp, eq, c);
+ }
+ else
+ {
+ sendInfer(utils::mkAnd(exp), eq, c);
+ }
+}
+
+void InferenceManager::sendInference(std::vector<Node>& exp,
+ Node eq,
+ const char* c,
+ bool asLemma)
+{
+ std::vector<Node> exp_n;
+ sendInference(exp, exp_n, eq, c, asLemma);
+}
+
+void InferenceManager::sendLemma(Node ant, Node conc, const char* c)
+{
+ if (conc.isNull() || conc == d_false)
+ {
+ Trace("strings-conflict")
+ << "Strings::Conflict : " << c << " : " << ant << std::endl;
+ Trace("strings-lemma") << "Strings::Conflict : " << c << " : " << ant
+ << std::endl;
+ Trace("strings-assert")
+ << "(assert (not " << ant << ")) ; conflict " << c << std::endl;
+ d_out.conflict(ant);
+ d_parent.d_conflict = true;
+ return;
+ }
+ Node lem;
+ if (ant == d_true)
+ {
+ lem = conc;
+ }
+ else
+ {
+ lem = NodeManager::currentNM()->mkNode(IMPLIES, ant, conc);
+ }
+ Trace("strings-lemma") << "Strings::Lemma " << c << " : " << lem << std::endl;
+ Trace("strings-assert") << "(assert " << lem << ") ; lemma " << c
+ << std::endl;
+ d_pendingLem.push_back(lem);
+}
+
+void InferenceManager::sendInfer(Node eq_exp, Node eq, const char* c)
+{
+ if (options::stringInferSym())
+ {
+ std::vector<Node> vars;
+ std::vector<Node> subs;
+ std::vector<Node> unproc;
+ inferSubstitutionProxyVars(eq_exp, vars, subs, unproc);
+ if (unproc.empty())
+ {
+ Node eqs =
+ eq.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
+ if (Trace.isOn("strings-lemma-debug"))
+ {
+ Trace("strings-lemma-debug") << "Strings::Infer " << eq << " from "
+ << eq_exp << " by " << c << std::endl;
+ Trace("strings-lemma-debug")
+ << "Strings::Infer Alternate : " << eqs << std::endl;
+ for (unsigned i = 0, nvars = vars.size(); i < nvars; i++)
+ {
+ Trace("strings-lemma-debug")
+ << " " << vars[i] << " -> " << subs[i] << std::endl;
+ }
+ }
+ sendLemma(d_true, eqs, c);
+ return;
+ }
+ if (Trace.isOn("strings-lemma-debug"))
+ {
+ for (const Node& u : unproc)
+ {
+ Trace("strings-lemma-debug")
+ << " non-trivial exp : " << u << std::endl;
+ }
+ }
+ }
+ Trace("strings-lemma") << "Strings::Infer " << eq << " from " << eq_exp
+ << " by " << c << std::endl;
+ Trace("strings-assert") << "(assert (=> " << eq_exp << " " << eq
+ << ")) ; infer " << c << std::endl;
+ d_pending.push_back(eq);
+ d_pendingExp[eq] = eq_exp;
+ d_keep.insert(eq);
+ d_keep.insert(eq_exp);
+}
+
+bool InferenceManager::sendSplit(Node a, Node b, const char* c, bool preq)
+{
+ Node eq = a.eqNode(b);
+ eq = Rewriter::rewrite(eq);
+ if (eq.isConst())
+ {
+ return false;
+ }
+ NodeManager* nm = NodeManager::currentNM();
+ Node lemma_or = nm->mkNode(OR, eq, nm->mkNode(NOT, eq));
+ Trace("strings-lemma") << "Strings::Lemma " << c << " SPLIT : " << lemma_or
+ << std::endl;
+ d_pendingLem.push_back(lemma_or);
+ sendPhaseRequirement(eq, preq);
+ return true;
+}
+
+void InferenceManager::sendPhaseRequirement(Node lit, bool pol)
+{
+ d_pendingReqPhase[lit] = pol;
+}
+
+void InferenceManager::doPendingFacts()
+{
+ size_t i = 0;
+ while (!hasConflict() && i < d_pending.size())
+ {
+ Node fact = d_pending[i];
+ Node exp = d_pendingExp[fact];
+ if (fact.getKind() == AND)
+ {
+ for (const Node& lit : fact)
+ {
+ bool polarity = lit.getKind() != NOT;
+ TNode atom = polarity ? lit : lit[0];
+ d_parent.assertPendingFact(atom, polarity, exp);
+ }
+ }
+ else
+ {
+ bool polarity = fact.getKind() != NOT;
+ TNode atom = polarity ? fact : fact[0];
+ d_parent.assertPendingFact(atom, polarity, exp);
+ }
+ i++;
+ }
+ d_pending.clear();
+ d_pendingExp.clear();
+}
+
+void InferenceManager::doPendingLemmas()
+{
+ if (!hasConflict())
+ {
+ for (const Node& lc : d_pendingLem)
+ {
+ Trace("strings-pending") << "Process pending lemma : " << lc << std::endl;
+ d_out.lemma(lc);
+ }
+ for (const std::pair<const Node, bool>& prp : d_pendingReqPhase)
+ {
+ Trace("strings-pending") << "Require phase : " << prp.first
+ << ", polarity = " << prp.second << std::endl;
+ d_out.requirePhase(prp.first, prp.second);
+ }
+ }
+ d_pendingLem.clear();
+ d_pendingReqPhase.clear();
+}
+
+bool InferenceManager::hasConflict() const { return d_parent.d_conflict; }
+
+void InferenceManager::inferSubstitutionProxyVars(
+ Node n,
+ std::vector<Node>& vars,
+ std::vector<Node>& subs,
+ std::vector<Node>& unproc) const
+{
+ if (n.getKind() == AND)
+ {
+ for (const Node& nc : n)
+ {
+ inferSubstitutionProxyVars(nc, vars, subs, unproc);
+ }
+ return;
+ }
+ if (n.getKind() == EQUAL)
+ {
+ Node ns = n.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
+ ns = Rewriter::rewrite(ns);
+ if (ns.getKind() == EQUAL)
+ {
+ Node s;
+ Node v;
+ for (unsigned i = 0; i < 2; i++)
+ {
+ Node ss;
+ // determine whether this side has a proxy variable
+ if (ns[i].getAttribute(StringsProxyVarAttribute()))
+ {
+ // it is a proxy variable
+ ss = ns[i];
+ }
+ else if (ns[i].isConst())
+ {
+ ss = d_parent.getProxyVariableFor(ns[i]);
+ }
+ if (!ss.isNull())
+ {
+ v = ns[1 - i];
+ // if the other side is a constant or variable
+ if (v.getNumChildren() == 0)
+ {
+ if (s.isNull())
+ {
+ s = ss;
+ }
+ else
+ {
+ // both sides of the equality correspond to a proxy variable
+ if (ss == s)
+ {
+ // it is a trivial equality, e.g. between a proxy variable
+ // and its definition
+ return;
+ }
+ else
+ {
+ // equality between proxy variables, non-trivial
+ s = Node::null();
+ }
+ }
+ }
+ }
+ }
+ if (!s.isNull())
+ {
+ // the equality can be turned into a substitution
+ subs.push_back(s);
+ vars.push_back(v);
+ return;
+ }
+ }
+ else
+ {
+ n = ns;
+ }
+ }
+ if (n != d_true)
+ {
+ unproc.push_back(n);
+ }
+}
+
+} // namespace strings
+} // namespace theory
+} // namespace CVC4
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback