// is unknown, we apply the inference:
// len(s) != len(t) V len(s) = len(t)
STRINGS_DEQ_LENGTH_SP,
+ // Disequality extensionality
+ // x != y => ( seq.len(x) != seq.len(y) or
+ // ( seq.nth(x, d) != seq.nth(y, d) ^ 0 <= d < seq.len(x) ) )
+ STRINGS_DEQ_EXTENSIONALITY,
//-------------------- codes solver
// str.to_code( v ) = rewrite( str.to_code(c) )
// where v is the proxy variable for c.
d_im(im),
d_termReg(tr),
d_bsolver(bs),
- d_nfPairs(context())
+ d_nfPairs(context()),
+ d_extDeq(userContext())
{
d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) );
d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) );
return;
}
+ if (options::stringsDeqExt())
+ {
+ processDeqExtensionality(ni, nj);
+ return;
+ }
+
nfi = nfni.d_nf;
nfj = nfnj.d_nf;
return false;
}
+void CoreSolver::processDeqExtensionality(Node n1, Node n2)
+{
+ // hash based on equality
+ Node eq = n1 < n2 ? n1.eqNode(n2) : n2.eqNode(n1);
+ NodeSet::const_iterator it = d_extDeq.find(eq);
+ if (it != d_extDeq.end())
+ {
+ // already processed
+ return;
+ }
+ d_extDeq.insert(eq);
+
+ NodeManager* nm = NodeManager::currentNM();
+ SkolemCache* sc = d_termReg.getSkolemCache();
+ TypeNode intType = nm->integerType();
+ Node k = sc->mkTypedSkolemCached(
+ intType, n1, n2, SkolemCache::SK_DEQ_DIFF, "diff");
+ Node deq = eq.negate();
+ Node ss1, ss2;
+ if (n1.getType().isString())
+ {
+ // substring of length 1
+ ss1 = nm->mkNode(STRING_SUBSTR, n1, k, d_one);
+ ss2 = nm->mkNode(STRING_SUBSTR, n2, k, d_one);
+ }
+ else
+ {
+ // as an optimization, for sequences, use seq.nth
+ ss1 = nm->mkNode(SEQ_NTH, n1, k);
+ ss2 = nm->mkNode(SEQ_NTH, n2, k);
+ }
+ // disequality between nth/substr
+ Node conc1 = ss1.eqNode(ss2).negate();
+
+ // The skolem k is in the bounds of at least
+ // one string/sequence
+ Node len1 = nm->mkNode(STRING_LENGTH, n1);
+ Node len2 = nm->mkNode(STRING_LENGTH, n2);
+ Node conc2 = nm->mkNode(LEQ, d_zero, k);
+ Node conc3 = nm->mkNode(LT, k, len1);
+ Node lenDeq = nm->mkNode(EQUAL, len1, len2).negate();
+
+ std::vector<Node> concs = {conc1, conc2, conc3};
+ Node conc = nm->mkNode(OR, lenDeq, nm->mkAnd(concs));
+ // A != B => ( seq.len(A) != seq.len(B) or
+ // ( seq.nth(A, d) != seq.nth(B, d) ^ 0 <= d < seq.len(A) ) )
+ d_im.sendInference(
+ {deq}, conc, InferenceId::STRINGS_DEQ_EXTENSIONALITY, false, true);
+}
+
void CoreSolver::addNormalFormPair( Node n1, Node n2 ){
if (n1>n2)
{
#ifndef CVC5__THEORY__STRINGS__CORE_SOLVER_H
#define CVC5__THEORY__STRINGS__CORE_SOLVER_H
+#include "context/cdhashmap.h"
#include "context/cdhashset.h"
#include "context/cdlist.h"
#include "smt/env_obj.h"
{
friend class InferenceManager;
using NodeIntMap = context::CDHashMap<Node, int>;
+ using NodeSet = context::CDHashSet<Node>;
public:
CoreSolver(Env& env,
Node nj,
size_t& index,
bool isRev);
+ /**
+ * Process disequality by extensionality. If necessary, this may result
+ * in inferences that follow from this disequality of the form:
+ * (not (= n1 n2)) => (not (= k1 k2))
+ * where k1, k2 are either strings of length one or elements of a sequence.
+ *
+ * @param n1 The first string in the disequality
+ * @param n2 The second string in the disequality
+ */
+ void processDeqExtensionality(Node n1, Node n2);
//--------------------------end for checkNormalFormsDeq
/** The solver state object */
* the argument number of the t1 ... tn they were generated from.
*/
std::map<Node, std::vector<int> > d_flat_form_index;
+ /** Set of equalities for which we have applied extensionality. */
+ NodeSet d_extDeq;
}; /* class CoreSolver */
} // namespace strings