Skip to content

Commit e4e2b10

Browse files
committed
TG-592 Implemented the correct instantiation procedure for not contains constraints
The method of insantiating not contains constraints is correct, see TG-591. This involved a localized change to `string_constraint_instantiation.cpp:instantiate_not_contains`, but also involved the activation of counter-examples in `string_refinementt`. The reason for this is that the new (correct) instances are relatively weak, and the solver will end up with an empty index set. The current behavior of returning SAT in this case is incorrect, and will be fixed shortly by TG-672. Additionally, the choice of indices at which to instantiate not contains constraints has been changed to be more sensible. Finally, the relevant unit tests have been updated.
1 parent 0e70863 commit e4e2b10

File tree

5 files changed

+101
-83
lines changed

5 files changed

+101
-83
lines changed

src/solvers/refinement/string_constraint_instantiation.cpp

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,61 +11,46 @@ Author: Jesse Sigal, [email protected]
1111

1212
#include <solvers/refinement/string_constraint_instantiation.h>
1313

14-
#include <solvers/refinement/string_constraint.h>
15-
#include <solvers/refinement/string_constraint_generator.h>
16-
#include <solvers/refinement/string_refinement.h>
17-
1814
/// Instantiates a quantified formula representing `not_contains` by
1915
/// substituting the quantifiers and generating axioms.
2016
/// \related string_refinementt
2117
/// \param [in] axiom: the axiom to instantiate
22-
/// \param [in] index_set0: the index set for `axiom.s0()`
23-
/// \param [in] index_set1: the index set for `axiom.s1()`
18+
/// \param [in] index_pairs: the pairs of indices to at which to instantiate
2419
/// \param [in] generator: generator to be used to get `axiom`'s witness
2520
/// \return the lemmas produced through instantiation
2621
std::vector<exprt> instantiate_not_contains(
2722
const string_not_contains_constraintt &axiom,
28-
const std::set<exprt> &index_set0,
29-
const std::set<exprt> &index_set1,
23+
const std::set<std::pair<exprt, exprt>> &index_pairs,
3024
const string_constraint_generatort &generator)
3125
{
3226
std::vector<exprt> lemmas;
3327

34-
const string_exprt s0=to_string_expr(axiom.s0());
35-
const string_exprt s1=to_string_expr(axiom.s1());
36-
37-
for(const auto &i0 : index_set0)
38-
for(const auto &i1 : index_set1)
39-
{
40-
const minus_exprt val(i0, i1);
41-
const exprt witness=generator.get_witness_of(axiom, val);
42-
const and_exprt prem_and_is_witness(
43-
axiom.premise(),
44-
equal_exprt(witness, i1));
45-
46-
const not_exprt differ(equal_exprt(s0[i0], s1[i1]));
47-
const implies_exprt lemma(prem_and_is_witness, differ);
48-
lemmas.push_back(lemma);
49-
50-
// we put bounds on the witnesses:
51-
// 0 <= v <= |s0| - |s1| ==> 0 <= v+w[v] < |s0| && 0 <= w[v] < |s1|
52-
const exprt zero=from_integer(0, val.type());
53-
const binary_relation_exprt c1(zero, ID_le, plus_exprt(val, witness));
54-
const binary_relation_exprt c2(
55-
s0.length(), ID_gt, plus_exprt(val, witness));
56-
const binary_relation_exprt c3(s1.length(), ID_gt, witness);
57-
const binary_relation_exprt c4(zero, ID_le, witness);
58-
59-
const minus_exprt diff(s0.length(), s1.length());
60-
61-
const and_exprt premise(
62-
binary_relation_exprt(zero, ID_le, val),
63-
binary_relation_exprt(diff, ID_ge, val));
64-
const implies_exprt witness_bounds(
65-
premise,
66-
and_exprt(and_exprt(c1, c2), and_exprt(c3, c4)));
67-
lemmas.push_back(witness_bounds);
68-
}
28+
const string_exprt &s0=to_string_expr(axiom.s0());
29+
const string_exprt &s1=to_string_expr(axiom.s1());
30+
31+
for(const auto &pair : index_pairs)
32+
{
33+
// We have s0[x+f(x)] and s1[f(x)], so to have i0 indexing s0 and i1
34+
// indexing s1, we need x = i0 - i1 and f(i0 - i1) = f(x) = i1.
35+
const exprt &i0=pair.first;
36+
const exprt &i1=pair.second;
37+
const minus_exprt val(i0, i1);
38+
const and_exprt universal_bound(
39+
binary_relation_exprt(axiom.univ_lower_bound(), ID_le, val),
40+
binary_relation_exprt(axiom.univ_upper_bound(), ID_gt, val));
41+
const exprt f=generator.get_witness_of(axiom, val);
42+
const equal_exprt relevancy(f, i1);
43+
const and_exprt premise(relevancy, axiom.premise(), universal_bound);
44+
45+
const notequal_exprt differ(s0[i0], s1[i1]);
46+
const and_exprt existential_bound(
47+
binary_relation_exprt(axiom.exists_lower_bound(), ID_le, i1),
48+
binary_relation_exprt(axiom.exists_upper_bound(), ID_gt, i1));
49+
const and_exprt body(differ, existential_bound);
50+
51+
const implies_exprt lemma(premise, body);
52+
lemmas.push_back(lemma);
53+
}
6954

7055
return lemmas;
7156
}

src/solvers/refinement/string_constraint_instantiation.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ Author: Jesse Sigal, [email protected]
1717

1818
std::vector<exprt> instantiate_not_contains(
1919
const string_not_contains_constraintt &axiom,
20-
const std::set<exprt> &index_set0,
21-
const std::set<exprt> &index_set1,
20+
const std::set<std::pair<exprt, exprt>> &index_pairs,
2221
const string_constraint_generatort &generator);
2322

2423
#endif // CPROVER_SOLVERS_REFINEMENT_STRING_CONSTRAINT_INSTANTIATION_H

src/solvers/refinement/string_refinement.cpp

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static std::vector<exprt> instantiate_not_contains(
9999
const namespacet &ns,
100100
const string_not_contains_constraintt &axiom,
101101
const std::map<exprt, std::set<exprt>> &index_set,
102+
const std::map<exprt, std::set<exprt>> &current_index_set,
102103
const string_constraint_generatort &generator);
103104

104105
static exprt get_array(
@@ -850,7 +851,12 @@ decision_proceduret::resultt string_refinementt::dec_solve()
850851
debug() << "constraint " << i << '\n';
851852
const std::vector<exprt> lemmas=
852853
instantiate_not_contains(
853-
debug(), ns, not_contains_axioms[i], index_set, generator);
854+
debug(),
855+
ns,
856+
not_contains_axioms[i],
857+
index_set,
858+
current_index_set,
859+
generator);
854860
for(const exprt &lemma : lemmas)
855861
add_lemma(lemma);
856862
}
@@ -1510,23 +1516,26 @@ static std::pair<bool, std::vector<exprt>> check_axioms(
15101516

15111517
if(use_counter_example)
15121518
{
1513-
// TODO: add counter examples for not_contains?
1519+
stream << "Adding counter-examples: " << eom;
1520+
// TODO: add counter-examples for universal constraints?
15141521

1515-
// Checking if the current solution satisfies the constraints
15161522
std::vector<exprt> lemmas;
1517-
for(const auto &v : violated)
1523+
for(const auto &v : violated_not_contains)
15181524
{
15191525
const exprt &val=v.second;
1520-
const string_constraintt &axiom=universal_axioms[v.first];
1521-
1522-
exprt premise(axiom.premise());
1523-
exprt body(axiom.body());
1524-
implies_exprt instance(premise, body);
1525-
replace_expr(symbol_resolve, instance);
1526-
replace_expr(axiom.univ_var(), val, instance);
1527-
stream << "adding counter example " << from_expr(ns, "", instance)
1528-
<< eom;
1529-
lemmas.push_back(instance);
1526+
const string_not_contains_constraintt &axiom=
1527+
not_contains_axioms[v.first];
1528+
1529+
const exprt func_val=generator.get_witness_of(axiom, val);
1530+
const exprt comp_val=simplify_sum(plus_exprt(val, func_val));
1531+
1532+
std::set<std::pair<exprt, exprt>> indices;
1533+
indices.insert(std::pair<exprt, exprt>(comp_val, func_val));
1534+
const exprt counter=::instantiate_not_contains(
1535+
axiom, indices, generator)[0];
1536+
1537+
stream << " - " << from_expr(ns, "", counter) << eom;
1538+
lemmas.push_back(counter);
15301539
}
15311540
return { false, lemmas };
15321541
}
@@ -1960,19 +1969,36 @@ static std::vector<exprt> instantiate_not_contains(
19601969
const namespacet &ns,
19611970
const string_not_contains_constraintt &axiom,
19621971
const std::map<exprt, std::set<exprt>> &index_set,
1972+
const std::map<exprt, std::set<exprt>> &current_index_set,
19631973
const string_constraint_generatort &generator)
19641974
{
1965-
const string_exprt s0=to_string_expr(axiom.s0());
1966-
const string_exprt s1=to_string_expr(axiom.s1());
1975+
const string_exprt &s0=axiom.s0();
1976+
const string_exprt &s1=axiom.s1();
19671977

19681978
stream << "instantiate not contains " << from_expr(ns, "", s0) << " : "
19691979
<< from_expr(ns, "", s1) << messaget::eom;
1970-
const auto &i0=index_set.find(s0.content());
1971-
const auto &i1=index_set.find(s1.content());
1972-
if(i0!=index_set.end() && i1!=index_set.end())
1980+
1981+
const auto &index_set0=index_set.find(s0.content());
1982+
const auto &index_set1=index_set.find(s1.content());
1983+
const auto &current_index_set0=current_index_set.find(s0.content());
1984+
const auto &current_index_set1=current_index_set.find(s1.content());
1985+
1986+
if(index_set0!=index_set.end() &&
1987+
index_set1!=index_set.end() &&
1988+
current_index_set0!=index_set.end() &&
1989+
current_index_set1!=index_set.end())
19731990
{
1974-
return ::instantiate_not_contains(
1975-
axiom, i0->second, i1->second, generator);
1991+
typedef std::pair<exprt, exprt> expr_pairt;
1992+
std::set<expr_pairt> index_pairs;
1993+
1994+
for(const auto &ic0 : current_index_set0->second)
1995+
for(const auto &i1 : index_set1->second)
1996+
index_pairs.insert(expr_pairt(ic0, i1));
1997+
for(const auto &ic1 : current_index_set1->second)
1998+
for(const auto &i0 : index_set0->second)
1999+
index_pairs.insert(expr_pairt(i0, ic1));
2000+
2001+
return ::instantiate_not_contains(axiom, index_pairs, generator);
19762002
}
19772003
return { };
19782004
}

src/solvers/refinement/string_refinement.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class string_refinementt final: public bv_refinementt
3939
bool string_non_empty=false;
4040
/// Concretize strings after solver is finished
4141
bool trace=false;
42-
bool use_counter_example=false;
42+
bool use_counter_example=true;
4343
};
4444
public:
4545
/// string_refinementt constructor arguments

unit/solvers/refinement/string_constraint_instantiation/instantiate_not_contains.cpp

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111
#include <solvers/refinement/string_constraint_instantiation.h>
1212

1313
#include <solvers/sat/satcheck.h>
14-
#include <solvers/refinement/bv_refinement.h>
1514
#include <java_bytecode/java_types.h>
1615
#include <langapi/mode.h>
1716
#include <java_bytecode/java_bytecode_language.h>
18-
#include <util/namespace.h>
19-
#include <util/symbol_table.h>
2017
#include <util/simplify_expr.h>
2118

2219
/// \class Types used throughout the test. Currently it is impossible to
@@ -48,7 +45,7 @@ const tt t;
4845
/// Creates a `constant_exprt` of the proper length type.
4946
/// \param [in] i: integer to convert
5047
/// \return corresponding `constant_exprt`
51-
constant_exprt from_integer(const mp_integer i)
48+
constant_exprt from_integer(const mp_integer &i)
5249
{
5350
return from_integer(i, t.length_type());
5451
}
@@ -81,6 +78,17 @@ std::set<exprt> full_index_set(const string_exprt &s)
8178
return ret;
8279
}
8380

81+
/// Create the cartesian product of two sets.
82+
template<class T, class U>
83+
std::set<std::pair<T, U>> product(const std::set<T> ts, const std::set<U> us)
84+
{
85+
std::set<std::pair<T, U>> s;
86+
for(const auto &t : ts)
87+
for(const auto &u : us)
88+
s.insert(std::pair<T, U>(t, u));
89+
return s;
90+
}
91+
8492
/// Simplifies, and returns the conjunction of the lemmas.
8593
/// \param [in] lemmas: lemmas to process
8694
/// \param [in] ns: namespace for simplifying
@@ -152,7 +160,7 @@ SCENARIO("instantiate_not_contains",
152160

153161
// Generating the corresponding axioms and simplifying, recording info
154162
symbol_tablet symtab;
155-
namespacet empty_ns(symtab);
163+
const namespacet empty_ns(symtab);
156164
string_constraint_generatort::infot info;
157165
string_constraint_generatort generator(info, ns);
158166
exprt res=generator.add_axioms_for_function_application(func);
@@ -196,7 +204,7 @@ SCENARIO("instantiate_not_contains",
196204
for(const auto &axiom : nc_axioms)
197205
{
198206
const std::vector<exprt> l=instantiate_not_contains(
199-
axiom, index_set_ab, index_set_b, generator);
207+
axiom, product(index_set_ab, index_set_b), generator);
200208
lemmas.insert(lemmas.end(), l.begin(), l.end());
201209
}
202210

@@ -239,7 +247,7 @@ SCENARIO("instantiate_not_contains",
239247

240248
// Create witness for axiom
241249
symbol_tablet symtab;
242-
namespacet empty_ns(symtab);
250+
const namespacet empty_ns(symtab);
243251
string_constraint_generatort::infot info;
244252
string_constraint_generatort generator(info, ns);
245253
generator.witness[vacuous]=
@@ -255,7 +263,7 @@ SCENARIO("instantiate_not_contains",
255263

256264
// Instantiate the lemmas
257265
std::vector<exprt> lemmas=instantiate_not_contains(
258-
vacuous, index_set_a, index_set_a, generator);
266+
vacuous, product(index_set_a, index_set_a), generator);
259267

260268
const exprt conj=combine_lemmas(lemmas, ns);
261269
const std::string info=create_info(lemmas, ns);
@@ -297,7 +305,7 @@ SCENARIO("instantiate_not_contains",
297305

298306
// Create witness for axiom
299307
symbol_tablet symtab;
300-
namespacet ns(symtab);
308+
const namespacet ns(symtab);
301309
string_constraint_generatort::infot info;
302310
string_constraint_generatort generator(info, ns);
303311
generator.witness[trivial]=
@@ -314,7 +322,7 @@ SCENARIO("instantiate_not_contains",
314322

315323
// Instantiate the lemmas
316324
std::vector<exprt> lemmas=instantiate_not_contains(
317-
trivial, index_set_a, index_set_b, generator);
325+
trivial, product(index_set_a, index_set_b), generator);
318326

319327
const exprt conj=combine_lemmas(lemmas, ns);
320328
const std::string info=create_info(lemmas, ns);
@@ -356,7 +364,7 @@ SCENARIO("instantiate_not_contains",
356364

357365
// Create witness for axiom
358366
symbol_tablet symtab;
359-
namespacet empty_ns(symtab);
367+
const namespacet empty_ns(symtab);
360368
string_constraint_generatort::infot info;
361369
string_constraint_generatort generator(info, ns);
362370
generator.witness[trivial]=
@@ -374,7 +382,7 @@ SCENARIO("instantiate_not_contains",
374382

375383
// Instantiate the lemmas
376384
std::vector<exprt> lemmas=instantiate_not_contains(
377-
trivial, index_set_a, index_set_empty, generator);
385+
trivial, product(index_set_a, index_set_empty), generator);
378386

379387
const exprt conj=combine_lemmas(lemmas, ns);
380388
const std::string info=create_info(lemmas, ns);
@@ -416,7 +424,7 @@ SCENARIO("instantiate_not_contains",
416424

417425
// Create witness for axiom
418426
symbol_tablet symtab;
419-
namespacet empty_ns(symtab);
427+
const namespacet empty_ns(symtab);
420428

421429
string_constraint_generatort::infot info;
422430
string_constraint_generatort generator(info, ns);
@@ -433,7 +441,7 @@ SCENARIO("instantiate_not_contains",
433441

434442
// Instantiate the lemmas
435443
std::vector<exprt> lemmas=instantiate_not_contains(
436-
trivial, index_set_ab, index_set_ab, generator);
444+
trivial, product(index_set_ab, index_set_ab), generator);
437445

438446
const exprt conj=combine_lemmas(lemmas, ns);
439447
const std::string info=create_info(lemmas, ns);
@@ -476,7 +484,7 @@ SCENARIO("instantiate_not_contains",
476484

477485
// Create witness for axiom
478486
symbol_tablet symtab;
479-
namespacet empty_ns(symtab);
487+
const namespacet empty_ns(symtab);
480488
string_constraint_generatort::infot info;
481489
string_constraint_generatort generator(info, ns);
482490
generator.witness[trivial]=
@@ -493,7 +501,7 @@ SCENARIO("instantiate_not_contains",
493501

494502
// Instantiate the lemmas
495503
std::vector<exprt> lemmas=instantiate_not_contains(
496-
trivial, index_set_ab, index_set_cd, generator);
504+
trivial, product(index_set_ab, index_set_cd), generator);
497505

498506
const exprt conj=combine_lemmas(lemmas, ns);
499507
const std::string info=create_info(lemmas, ns);

0 commit comments

Comments
 (0)