1 /********************* */
2 /*! \file base_solver.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8 ** in the top-level source directory) and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
12 ** \brief Base solver for the theory of strings. This class implements term
13 ** indexing and constant inference for the theory of strings.
16 #include "theory/strings/base_solver.h"
18 #include "options/strings_options.h"
19 #include "theory/strings/theory_strings_rewriter.h"
20 #include "theory/strings/theory_strings_utils.h"
23 using namespace CVC4::context
;
24 using namespace CVC4::kind
;
30 BaseSolver::BaseSolver(context::Context
* c
,
31 context::UserContext
* u
,
34 : d_state(s
), d_im(im
), d_congruent(c
)
36 d_emptyString
= NodeManager::currentNM()->mkConst(::CVC4::String(""));
37 d_false
= NodeManager::currentNM()->mkConst(false);
38 d_cardSize
= utils::getAlphabetCardinality();
41 BaseSolver::~BaseSolver() {}
43 void BaseSolver::checkInit()
47 d_eqcToConstBase
.clear();
48 d_eqcToConstExp
.clear();
52 std::map
<Kind
, uint32_t> ncongruent
;
53 std::map
<Kind
, uint32_t> congruent
;
54 eq::EqualityEngine
* ee
= d_state
.getEqualityEngine();
55 Assert(d_state
.getRepresentative(d_emptyString
) == d_emptyString
);
56 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator(ee
);
57 while (!eqcs_i
.isFinished())
60 TypeNode tn
= eqc
.getType();
65 d_stringsEqc
.push_back(eqc
);
68 eq::EqClassIterator eqc_i
= eq::EqClassIterator(eqc
, ee
);
69 while (!eqc_i
.isFinished())
74 d_eqcToConst
[eqc
] = n
;
75 d_eqcToConstBase
[eqc
] = n
;
76 d_eqcToConstExp
[eqc
] = Node::null();
78 else if (tn
.isInteger())
82 else if (n
.getNumChildren() > 0)
87 if (d_congruent
.find(n
) == d_congruent
.end())
90 Node nc
= d_termIndex
[k
].add(n
, 0, d_state
, d_emptyString
, c
);
93 // check if we have inferred a new equality by removal of empty
95 if (n
.getKind() == STRING_CONCAT
&& !d_state
.areEqual(nc
, n
))
97 std::vector
<Node
> exp
;
98 size_t count
[2] = {0, 0};
99 while (count
[0] < nc
.getNumChildren()
100 || count
[1] < n
.getNumChildren())
102 // explain empty prefixes
103 for (unsigned t
= 0; t
< 2; t
++)
105 Node nn
= t
== 0 ? nc
: n
;
107 count
[t
] < nn
.getNumChildren()
108 && (nn
[count
[t
]] == d_emptyString
109 || d_state
.areEqual(nn
[count
[t
]], d_emptyString
)))
111 if (nn
[count
[t
]] != d_emptyString
)
113 exp
.push_back(nn
[count
[t
]].eqNode(d_emptyString
));
118 // explain equal components
119 if (count
[0] < nc
.getNumChildren())
121 Assert(count
[1] < n
.getNumChildren());
122 if (nc
[count
[0]] != n
[count
[1]])
124 exp
.push_back(nc
[count
[0]].eqNode(n
[count
[1]]));
130 // infer the equality
131 d_im
.sendInference(exp
, n
.eqNode(nc
), "I_Norm");
135 // mark as congruent : only process if neither has been
137 d_im
.markCongruent(nc
, n
);
139 // this node is congruent to another one, we can ignore it
140 Trace("strings-process-debug")
141 << " congruent term : " << n
<< " (via " << nc
<< ")"
143 d_congruent
.insert(n
);
146 else if (k
== STRING_CONCAT
&& c
.size() == 1)
148 Trace("strings-process-debug")
149 << " congruent term by singular : " << n
<< " " << c
[0]
152 if (!d_state
.areEqual(c
[0], n
))
155 std::vector
<Node
> exp
;
156 // explain empty components
157 bool foundNEmpty
= false;
158 for (const Node
& nc
: n
)
160 if (d_state
.areEqual(nc
, d_emptyString
))
162 if (nc
!= d_emptyString
)
164 exp
.push_back(nc
.eqNode(d_emptyString
));
169 Assert(!foundNEmpty
);
174 AlwaysAssert(foundNEmpty
);
175 // infer the equality
176 d_im
.sendInference(exp
, n
.eqNode(ns
), "I_Norm_S");
178 d_congruent
.insert(n
);
194 if (d_congruent
.find(n
) == d_congruent
.end())
196 // We mark all but the oldest variable in the equivalence class as
204 Trace("strings-process-debug")
205 << " congruent variable : " << var
<< std::endl
;
206 d_congruent
.insert(var
);
211 Trace("strings-process-debug")
212 << " congruent variable : " << n
<< std::endl
;
213 d_congruent
.insert(n
);
222 if (Trace
.isOn("strings-process"))
224 for (std::map
<Kind
, TermIndex
>::iterator it
= d_termIndex
.begin();
225 it
!= d_termIndex
.end();
228 Trace("strings-process")
229 << " Terms[" << it
->first
<< "] = " << ncongruent
[it
->first
] << "/"
230 << (congruent
[it
->first
] + ncongruent
[it
->first
]) << std::endl
;
235 void BaseSolver::checkConstantEquivalenceClasses()
239 std::vector
<Node
> vecc
;
243 Trace("strings-process-debug")
244 << "Check constant equivalence classes..." << std::endl
;
245 prevSize
= d_eqcToConst
.size();
246 checkConstantEquivalenceClasses(&d_termIndex
[STRING_CONCAT
], vecc
);
247 } while (!d_im
.hasProcessed() && d_eqcToConst
.size() > prevSize
);
250 void BaseSolver::checkConstantEquivalenceClasses(TermIndex
* ti
,
251 std::vector
<Node
>& vecc
)
256 // construct the constant
257 Node c
= utils::mkNConcat(vecc
);
258 if (!d_state
.areEqual(n
, c
))
260 if (Trace
.isOn("strings-debug"))
262 Trace("strings-debug")
263 << "Constant eqc : " << c
<< " for " << n
<< std::endl
;
264 Trace("strings-debug") << " ";
265 for (const Node
& v
: vecc
)
267 Trace("strings-debug") << v
<< " ";
269 Trace("strings-debug") << std::endl
;
273 std::vector
<Node
> exp
;
274 while (count
< n
.getNumChildren())
276 while (count
< n
.getNumChildren()
277 && d_state
.areEqual(n
[count
], d_emptyString
))
279 d_im
.addToExplanation(n
[count
], d_emptyString
, exp
);
282 if (count
< n
.getNumChildren())
284 Trace("strings-debug")
285 << "...explain " << n
[count
] << " " << vecc
[countc
] << std::endl
;
286 if (!d_state
.areEqual(n
[count
], vecc
[countc
]))
288 Node nrr
= d_state
.getRepresentative(n
[count
]);
289 Assert(!d_eqcToConstExp
[nrr
].isNull());
290 d_im
.addToExplanation(n
[count
], d_eqcToConstBase
[nrr
], exp
);
291 exp
.push_back(d_eqcToConstExp
[nrr
]);
295 d_im
.addToExplanation(n
[count
], vecc
[countc
], exp
);
301 // exp contains an explanation of n==c
302 Assert(countc
== vecc
.size());
303 if (d_state
.hasTerm(c
))
305 d_im
.sendInference(exp
, n
.eqNode(c
), "I_CONST_MERGE");
308 else if (!d_im
.hasProcessed())
310 Node nr
= d_state
.getRepresentative(n
);
311 std::map
<Node
, Node
>::iterator it
= d_eqcToConst
.find(nr
);
312 if (it
== d_eqcToConst
.end())
314 Trace("strings-debug")
315 << "Set eqc const " << n
<< " to " << c
<< std::endl
;
316 d_eqcToConst
[nr
] = c
;
317 d_eqcToConstBase
[nr
] = n
;
318 d_eqcToConstExp
[nr
] = utils::mkAnd(exp
);
320 else if (c
!= it
->second
)
323 Trace("strings-debug")
324 << "Conflict, other constant was " << it
->second
325 << ", this constant was " << c
<< std::endl
;
326 if (d_eqcToConstExp
[nr
].isNull())
328 // n==c ^ n == c' => false
329 d_im
.addToExplanation(n
, it
->second
, exp
);
333 // n==c ^ n == d_eqcToConstBase[nr] == c' => false
334 exp
.push_back(d_eqcToConstExp
[nr
]);
335 d_im
.addToExplanation(n
, d_eqcToConstBase
[nr
], exp
);
337 d_im
.sendInference(exp
, d_false
, "I_CONST_CONFLICT");
342 Trace("strings-debug") << "Duplicate constant." << std::endl
;
347 for (std::pair
<const TNode
, TermIndex
>& p
: ti
->d_children
)
349 std::map
<Node
, Node
>::iterator itc
= d_eqcToConst
.find(p
.first
);
350 if (itc
!= d_eqcToConst
.end())
352 vecc
.push_back(itc
->second
);
353 checkConstantEquivalenceClasses(&p
.second
, vecc
);
355 if (d_im
.hasProcessed())
363 void BaseSolver::checkCardinality()
365 // This will create a partition of eqc, where each collection has length that
366 // are pairwise propagated to be equal. We do not require disequalities
367 // between the lengths of each collection, since we split on disequalities
368 // between lengths of string terms that are disequal (DEQ-LENGTH-SP).
369 std::vector
<std::vector
<Node
> > cols
;
370 std::vector
<Node
> lts
;
371 d_state
.separateByLength(d_stringsEqc
, cols
, lts
);
372 NodeManager
* nm
= NodeManager::currentNM();
373 Trace("strings-card") << "Check cardinality...." << std::endl
;
374 // for each collection
375 for (unsigned i
= 0, csize
= cols
.size(); i
< csize
; ++i
)
378 Trace("strings-card") << "Number of strings with length equal to " << lr
379 << " is " << cols
[i
].size() << std::endl
;
380 if (cols
[i
].size() <= 1)
382 // no restriction on sets in the partition of size 1
386 unsigned card_need
= 1;
387 double curr
= static_cast<double>(cols
[i
].size());
388 while (curr
> d_cardSize
)
390 curr
= curr
/ static_cast<double>(d_cardSize
);
393 Trace("strings-card")
394 << "Need length " << card_need
395 << " for this number of strings (where alphabet size is " << d_cardSize
396 << ")." << std::endl
;
397 // check if we need to split
398 bool needsSplit
= true;
401 // if constant, compare
402 Node cmp
= nm
->mkNode(GEQ
, lr
, nm
->mkConst(Rational(card_need
)));
403 cmp
= Rewriter::rewrite(cmp
);
404 needsSplit
= !cmp
.getConst
<bool>();
408 // find the minimimum constant that we are unknown to be disequal from, or
409 // otherwise stop if we increment such that cardinality does not apply
412 while (r
< card_need
&& success
)
414 Node rr
= nm
->mkConst(Rational(r
));
415 if (d_state
.areDisequal(rr
, lr
))
426 Trace("strings-card")
427 << "Symbolic length " << lr
<< " must be at least " << r
428 << " due to constant disequalities." << std::endl
;
430 needsSplit
= r
< card_need
;
435 // don't need to split
438 // first, try to split to merge equivalence classes
439 for (std::vector
<Node
>::iterator itr1
= cols
[i
].begin();
440 itr1
!= cols
[i
].end();
443 for (std::vector
<Node
>::iterator itr2
= itr1
+ 1; itr2
!= cols
[i
].end();
446 if (!d_state
.areDisequal(*itr1
, *itr2
))
449 if (d_im
.sendSplit(*itr1
, *itr2
, "CARD-SP"))
456 // otherwise, we need a length constraint
457 uint32_t int_k
= static_cast<uint32_t>(card_need
);
458 EqcInfo
* ei
= d_state
.getOrMakeEqcInfo(lr
, true);
459 Trace("strings-card") << "Previous cardinality used for " << lr
<< " is "
460 << ((int)ei
->d_cardinalityLemK
.get() - 1)
462 if (int_k
+ 1 > ei
->d_cardinalityLemK
.get())
464 Node k_node
= nm
->mkConst(Rational(int_k
));
465 // add cardinality lemma
466 Node dist
= nm
->mkNode(DISTINCT
, cols
[i
]);
467 std::vector
<Node
> vec_node
;
468 vec_node
.push_back(dist
);
469 for (std::vector
<Node
>::iterator itr1
= cols
[i
].begin();
470 itr1
!= cols
[i
].end();
473 Node len
= nm
->mkNode(STRING_LENGTH
, *itr1
);
476 Node len_eq_lr
= len
.eqNode(lr
);
477 vec_node
.push_back(len_eq_lr
);
480 Node len
= nm
->mkNode(STRING_LENGTH
, cols
[i
][0]);
481 Node cons
= nm
->mkNode(GEQ
, len
, k_node
);
482 cons
= Rewriter::rewrite(cons
);
483 ei
->d_cardinalityLemK
.set(int_k
+ 1);
484 if (!cons
.isConst() || !cons
.getConst
<bool>())
486 std::vector
<Node
> emptyVec
;
487 d_im
.sendInference(emptyVec
, vec_node
, cons
, "CARDINALITY", true);
492 Trace("strings-card") << "...end check cardinality" << std::endl
;
495 bool BaseSolver::isCongruent(Node n
)
497 return d_congruent
.find(n
) != d_congruent
.end();
500 Node
BaseSolver::getConstantEqc(Node eqc
)
502 std::map
<Node
, Node
>::iterator it
= d_eqcToConst
.find(eqc
);
503 if (it
!= d_eqcToConst
.end())
510 Node
BaseSolver::explainConstantEqc(Node n
, Node eqc
, std::vector
<Node
>& exp
)
512 std::map
<Node
, Node
>::iterator it
= d_eqcToConst
.find(eqc
);
513 if (it
!= d_eqcToConst
.end())
515 if (!d_eqcToConstExp
[eqc
].isNull())
517 exp
.push_back(d_eqcToConstExp
[eqc
]);
519 if (!d_eqcToConstBase
[eqc
].isNull())
521 d_im
.addToExplanation(n
, d_eqcToConstBase
[eqc
], exp
);
528 const std::vector
<Node
>& BaseSolver::getStringEqc() const
533 Node
BaseSolver::TermIndex::add(TNode n
,
535 const SolverState
& s
,
537 std::vector
<Node
>& c
)
539 if (index
== n
.getNumChildren())
547 Assert(index
< n
.getNumChildren());
548 TNode nir
= s
.getRepresentative(n
[index
]);
549 // if it is empty, and doing CONCAT, ignore
550 if (nir
== er
&& n
.getKind() == STRING_CONCAT
)
552 return add(n
, index
+ 1, s
, er
, c
);
555 return d_children
[nir
].add(n
, index
+ 1, s
, er
, c
);
558 } // namespace strings
559 } // namespace theory