Skip to content

Commit cd29f68

Browse files
committed
Extended check_axioms for not_contains and refactored
Previously when checking if axioms were satisfied by the model returned after SAT, the check_axioms function would assume all not_contains axioms were violated. They are now actually checked. Factored out the solver creation and usage into `string_refinementt::is_axiom_sat`. It now sets the valuation of a chosen var to a witness for use outside of the function. Additionally, the orignal functions for adding negations to the solver now return expressions.
1 parent 87ff9de commit cd29f68

File tree

2 files changed

+172
-54
lines changed

2 files changed

+172
-54
lines changed

src/solvers/refinement/string_refinement.cpp

+165-52
Original file line numberDiff line numberDiff line change
@@ -923,13 +923,73 @@ void string_refinementt::substitute_array_access(exprt &expr) const
923923
}
924924
}
925925

926-
/// negates the constraint and add it to the solver. the intended usage is to
927-
/// find an assignement of the universal variable that would violate the axiom,
928-
/// to avoid false positives the symbols other than the universal variable
929-
/// should have been replaced by there valuation in the current model
930-
/// \par parameters: a string constraint and a solver for non string expressions
931-
void string_refinementt::add_negation_of_constraint_to_solver(
932-
const string_constraintt &axiom, supert &solver)
926+
/// Negates the constraint to be fed to a solver. The intended usage is to find
927+
/// an assignement of the universal variable that would violate the axiom. To
928+
/// avoid false positives, the symbols other than the universal variable should
929+
/// have been replaced by their valuation in the current model.
930+
/// \pre Symbols other than the universal variable should have been replaced by
931+
/// their valuation in the current model.
932+
/// \param axiom: the not_contains constraint to add the negation of
933+
/// \param val: the existential witness for the axiom
934+
/// \param univ_var: the universal variable for the negation of the axiom
935+
/// \return: the negation of the axiom under the current evaluation
936+
exprt string_refinementt::negation_of_not_contains_constraint(
937+
const string_not_contains_constraintt &axiom,
938+
const exprt &val,
939+
const symbol_exprt &univ_var)
940+
{
941+
exprt lbu=axiom.univ_lower_bound();
942+
exprt ubu=axiom.univ_upper_bound();
943+
if(lbu.id()==ID_constant && ubu.id()==ID_constant)
944+
{
945+
mp_integer lb_int, ub_int;
946+
to_integer(to_constant_expr(lbu), lb_int);
947+
to_integer(to_constant_expr(ubu), ub_int);
948+
if(ub_int<=lb_int)
949+
{
950+
debug() << "empty constraint with current model" << eom;
951+
return false_exprt();
952+
}
953+
}
954+
955+
exprt lbe=axiom.exists_lower_bound();
956+
exprt ube=axiom.exists_upper_bound();
957+
958+
if(axiom.premise()==false_exprt())
959+
{
960+
debug() << "(string_refinement::check_axioms) adding false" << eom;
961+
return false_exprt();
962+
}
963+
964+
// Witness is the Skolem function for the existential, which we evaluate at
965+
// univ_var.
966+
and_exprt univ_bounds(
967+
binary_relation_exprt(lbu, ID_le, univ_var),
968+
binary_relation_exprt(ubu, ID_gt, univ_var));
969+
and_exprt exists_bounds(
970+
binary_relation_exprt(lbe, ID_le, val),
971+
binary_relation_exprt(ube, ID_gt, val));
972+
equal_exprt equal_chars(
973+
axiom.s0()[plus_exprt(univ_var, val)],
974+
axiom.s1()[val]);
975+
and_exprt negaxiom(univ_bounds, axiom.premise(), exists_bounds, equal_chars);
976+
977+
debug() << "(sr::check_axioms) negated not_contains axiom: "
978+
<< from_expr(ns, "", negaxiom) << eom;
979+
substitute_array_access(negaxiom);
980+
return negaxiom;
981+
}
982+
983+
/// Negates the constraint to be fed to a solver. The intended usage is to find
984+
/// an assignement of the universal variable that would violate the axiom. To
985+
/// avoid false positives, the symbols other than the universal variable should
986+
/// have been replaced by their valuation in the current model.
987+
/// \pre Symbols other than the universal variable should have been replaced by
988+
/// their valuation in the current model.
989+
/// \param axiom: the not_contains constraint to add the negation of
990+
/// \return: the negation of the axiom under the current evaluation
991+
exprt string_refinementt::negation_of_constraint(
992+
const string_constraintt &axiom)
933993
{
934994
exprt lb=axiom.lower_bound();
935995
exprt ub=axiom.upper_bound();
@@ -941,24 +1001,23 @@ void string_refinementt::add_negation_of_constraint_to_solver(
9411001
if(ub_int<=lb_int)
9421002
{
9431003
debug() << "empty constraint with current model" << eom;
944-
solver << false_exprt();
945-
return;
1004+
return false_exprt();
9461005
}
9471006
}
9481007

9491008
if(axiom.premise()==false_exprt())
9501009
{
951-
debug() << "(string_refinement::check_axioms) adding false" << eom;
952-
solver << false_exprt();
953-
return;
1010+
debug() << "(string_refinement::check_axioms) adding false" << eom;
1011+
return false_exprt();
9541012
}
9551013

9561014
and_exprt premise(axiom.premise(), axiom.univ_within_bounds());
9571015
and_exprt negaxiom(premise, not_exprt(axiom.body()));
9581016

959-
debug() << "(sr::check_axioms) negated axiom: " << from_expr(ns, "", negaxiom) << eom;
1017+
debug() << "(sr::check_axioms) negated axiom: "
1018+
<< from_expr(ns, "", negaxiom) << eom;
9601019
substitute_array_access(negaxiom);
961-
solver << negaxiom;
1020+
return negaxiom;
9621021
}
9631022

9641023
/// return true if the current model satisfies all the axioms
@@ -979,63 +1038,89 @@ bool string_refinementt::check_axioms()
9791038
for(size_t i=0; i<universal_axioms.size(); i++)
9801039
{
9811040
const string_constraintt &axiom=universal_axioms[i];
982-
symbol_exprt univ_var=axiom.univ_var();
983-
exprt bound_inf=axiom.lower_bound();
984-
exprt bound_sup=axiom.upper_bound();
985-
exprt prem=axiom.premise();
986-
exprt body=axiom.body();
1041+
const symbol_exprt univ_var=axiom.univ_var();
1042+
const exprt bound_inf=axiom.lower_bound();
1043+
const exprt bound_sup=axiom.upper_bound();
1044+
const exprt prem=axiom.premise();
1045+
const exprt body=axiom.body();
9871046

988-
string_constraintt axiom_in_model(
1047+
const string_constraintt axiom_in_model(
9891048
univ_var, get(bound_inf), get(bound_sup), get(prem), get(body));
9901049

991-
satcheck_no_simplifiert sat_check;
992-
supert solver(ns, sat_check);
993-
solver.set_ui(ui);
994-
add_negation_of_constraint_to_solver(axiom_in_model, solver);
1050+
const exprt negaxiom=negation_of_constraint(axiom_in_model);
1051+
exprt witness;
9951052

996-
switch(solver())
1053+
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
1054+
1055+
if(is_sat)
9971056
{
998-
case decision_proceduret::resultt::D_SATISFIABLE:
999-
{
1000-
exprt val=solver.get(axiom_in_model.univ_var());
1001-
debug() << "string constraint can be violated for "
1002-
<< axiom_in_model.univ_var().get_identifier()
1003-
<< " = " << from_expr(ns, "", val) << eom;
1004-
violated[i]=val;
1005-
}
1006-
break;
1007-
case decision_proceduret::resultt::D_UNSATISFIABLE:
1008-
break;
1009-
default:
1010-
throw "failure in checking axiom";
1057+
debug() << "string constraint can be violated for "
1058+
<< univ_var.get_identifier()
1059+
<< " = " << from_expr(ns, "", witness) << eom;
1060+
violated[i]=witness;
10111061
}
10121062
}
10131063

1014-
// tells whether one of the not_contains constraint can be violated
1015-
bool violated_not_contains=false;
1064+
// Maps from indexes of violated not_contains axiom to a witness of violation
1065+
std::map<std::size_t, exprt> violated_not_contains;
1066+
10161067
debug() << "there are " << not_contains_axioms.size()
10171068
<< " not_contains axioms" << eom;
1018-
1019-
for(auto nc_axiom : not_contains_axioms)
1069+
for(size_t i=0; i<not_contains_axioms.size(); i++)
10201070
{
1021-
typet index_type=nc_axiom.s0().length().type();
1022-
exprt zero=from_integer(0, index_type);
1023-
exprt witness=generator.get_witness_of(nc_axiom, zero);
1024-
exprt val=get(witness);
1025-
violated_not_contains=true;
1071+
const string_not_contains_constraintt &nc_axiom=not_contains_axioms[i];
1072+
const exprt univ_bound_inf=nc_axiom.univ_lower_bound();
1073+
const exprt univ_bound_sup=nc_axiom.univ_upper_bound();
1074+
const exprt prem=nc_axiom.premise();
1075+
const exprt exists_bound_inf=nc_axiom.exists_lower_bound();
1076+
const exprt exists_bound_sup=nc_axiom.exists_upper_bound();
1077+
const string_exprt s0=nc_axiom.s0();
1078+
const string_exprt s1=nc_axiom.s1();
1079+
1080+
symbol_exprt univ_var=generator.fresh_univ_index(
1081+
"not_contains_univ_var", nc_axiom.s0().length().type());
1082+
exprt wit=generator.get_witness_of(nc_axiom, univ_var);
1083+
exprt val=get(wit);
1084+
const string_not_contains_constraintt nc_axiom_in_model(
1085+
get(univ_bound_inf),
1086+
get(univ_bound_sup),
1087+
get(prem),
1088+
get(exists_bound_inf),
1089+
get(exists_bound_sup),
1090+
to_string_expr(get(s0)),
1091+
to_string_expr(get(s1)));
1092+
1093+
const exprt negaxiom=negation_of_not_contains_constraint(
1094+
nc_axiom_in_model, val, univ_var);
1095+
exprt witness;
1096+
1097+
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
1098+
1099+
if(is_sat)
1100+
{
1101+
debug() << "string constraint can be violated for "
1102+
<< univ_var.get_identifier()
1103+
<< " = " << from_expr(ns, "", witness) << eom;
1104+
violated_not_contains[i]=witness;
1105+
}
10261106
}
10271107

1028-
if(violated.empty() && !violated_not_contains)
1108+
if(violated.empty() && violated_not_contains.empty())
10291109
{
10301110
debug() << "no violated property" << eom;
10311111
return true;
10321112
}
10331113
else
10341114
{
1035-
debug() << violated.size() << " string axioms can be violated" << eom;
1115+
debug() << violated.size()
1116+
<< " universal string axioms can be violated" << eom;
1117+
debug() << violated_not_contains.size()
1118+
<< " not_contains string axioms can be violated" << eom;
10361119

10371120
if(use_counter_example)
10381121
{
1122+
// TODO: add counter examples for not_contains?
1123+
10391124
// Checking if the current solution satisfies the constraints
10401125
for(const auto &v : violated)
10411126
{
@@ -1076,10 +1161,8 @@ std::map<exprt, int> string_refinementt::map_representation_of_sum(
10761161
to_process.pop_back();
10771162
if(cur.id()==ID_plus)
10781163
{
1079-
for(const exprt &op : cur.operands())
1080-
{
1164+
for(const auto &op : cur.operands())
10811165
to_process.push_back(std::make_pair(op, positive));
1082-
}
10831166
}
10841167
else if(cur.id()==ID_minus)
10851168
{
@@ -1535,3 +1618,33 @@ exprt string_refinementt::get(const exprt &expr) const
15351618

15361619
return substitute_array_lists(ecopy);
15371620
}
1621+
1622+
/// Creates a solver with `axiom` as the only formula added and runs it. If it
1623+
/// is SAT, then true is returned and the given evalutation of `var` is stored
1624+
/// in `witness`. If UNSAT, then what witness is is undefined.
1625+
/// \param [in] axiom: the axiom to be checked
1626+
/// \param [in] var: the variable whose evaluation will be stored in witness
1627+
/// \param [out] witness: the witness of the satisfying assignment if one
1628+
/// exists. If UNSAT, then behaviour is undefined.
1629+
/// \return: true if axiom is SAT, false if UNSAT
1630+
bool string_refinementt::is_axiom_sat(
1631+
const exprt &axiom, const symbol_exprt& var, exprt &witness)
1632+
{
1633+
satcheck_no_simplifiert sat_check;
1634+
supert solver(ns, sat_check);
1635+
solver.set_ui(ui);
1636+
solver << axiom;
1637+
1638+
switch(solver())
1639+
{
1640+
case decision_proceduret::resultt::D_SATISFIABLE:
1641+
{
1642+
witness=solver.get(var);
1643+
return true;
1644+
}
1645+
case decision_proceduret::resultt::D_UNSATISFIABLE:
1646+
return false;
1647+
default:
1648+
throw "failure in checking axiom";
1649+
}
1650+
}

src/solvers/refinement/string_refinement.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,15 @@ class string_refinementt: public bv_refinementt
121121
void set_to(const exprt &expr, bool value) override;
122122

123123
void add_instantiations();
124-
void add_negation_of_constraint_to_solver(
125-
const string_constraintt &axiom, supert &solver);
124+
exprt negation_of_not_contains_constraint(
125+
const string_not_contains_constraintt &axiom,
126+
const exprt &val,
127+
const symbol_exprt &univ_var);
128+
exprt negation_of_constraint(const string_constraintt &axiom);
126129
void fill_model();
127130
bool check_axioms();
131+
bool is_axiom_sat(
132+
const exprt &axiom, const symbol_exprt& var, exprt &witness);
128133

129134
void set_char_array_equality(const exprt &lhs, const exprt &rhs);
130135
void update_index_set(const exprt &formula);

0 commit comments

Comments
 (0)