@@ -923,13 +923,73 @@ void string_refinementt::substitute_array_access(exprt &expr) const
923
923
}
924
924
}
925
925
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)
933
993
{
934
994
exprt lb=axiom.lower_bound ();
935
995
exprt ub=axiom.upper_bound ();
@@ -941,24 +1001,23 @@ void string_refinementt::add_negation_of_constraint_to_solver(
941
1001
if (ub_int<=lb_int)
942
1002
{
943
1003
debug () << " empty constraint with current model" << eom;
944
- solver << false_exprt ();
945
- return ;
1004
+ return false_exprt ();
946
1005
}
947
1006
}
948
1007
949
1008
if (axiom.premise ()==false_exprt ())
950
1009
{
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 ();
954
1012
}
955
1013
956
1014
and_exprt premise (axiom.premise (), axiom.univ_within_bounds ());
957
1015
and_exprt negaxiom (premise, not_exprt (axiom.body ()));
958
1016
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;
960
1019
substitute_array_access (negaxiom);
961
- solver << negaxiom;
1020
+ return negaxiom;
962
1021
}
963
1022
964
1023
// / return true if the current model satisfies all the axioms
@@ -979,63 +1038,89 @@ bool string_refinementt::check_axioms()
979
1038
for (size_t i=0 ; i<universal_axioms.size (); i++)
980
1039
{
981
1040
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 ();
987
1046
988
- string_constraintt axiom_in_model (
1047
+ const string_constraintt axiom_in_model (
989
1048
univ_var, get (bound_inf), get (bound_sup), get (prem), get (body));
990
1049
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;
995
1052
996
- switch (solver ())
1053
+ bool is_sat=is_axiom_sat (negaxiom, univ_var, witness);
1054
+
1055
+ if (is_sat)
997
1056
{
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;
1011
1061
}
1012
1062
}
1013
1063
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
+
1016
1067
debug () << " there are " << not_contains_axioms.size ()
1017
1068
<< " 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++)
1020
1070
{
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
+ }
1026
1106
}
1027
1107
1028
- if (violated.empty () && ! violated_not_contains)
1108
+ if (violated.empty () && violated_not_contains. empty () )
1029
1109
{
1030
1110
debug () << " no violated property" << eom;
1031
1111
return true ;
1032
1112
}
1033
1113
else
1034
1114
{
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;
1036
1119
1037
1120
if (use_counter_example)
1038
1121
{
1122
+ // TODO: add counter examples for not_contains?
1123
+
1039
1124
// Checking if the current solution satisfies the constraints
1040
1125
for (const auto &v : violated)
1041
1126
{
@@ -1076,10 +1161,8 @@ std::map<exprt, int> string_refinementt::map_representation_of_sum(
1076
1161
to_process.pop_back ();
1077
1162
if (cur.id ()==ID_plus)
1078
1163
{
1079
- for (const exprt &op : cur.operands ())
1080
- {
1164
+ for (const auto &op : cur.operands ())
1081
1165
to_process.push_back (std::make_pair (op, positive));
1082
- }
1083
1166
}
1084
1167
else if (cur.id ()==ID_minus)
1085
1168
{
@@ -1535,3 +1618,33 @@ exprt string_refinementt::get(const exprt &expr) const
1535
1618
1536
1619
return substitute_array_lists (ecopy);
1537
1620
}
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
+ }
0 commit comments