Skip to content

Commit d65bf9f

Browse files
Class for sparse arrays representations
The string solver is implicitely interpreting the array in the result of the underlying solver as "sparse-arrays" where only the values at some meaningful indexes are given. The classes sparse_arrayt and interval_sparse_arrayt make the way we interpret these results more explicit.
1 parent 773721b commit d65bf9f

File tree

2 files changed

+99
-38
lines changed

2 files changed

+99
-38
lines changed

src/solvers/refinement/string_refinement.cpp

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Author: Alberto Griggio, [email protected]
2020
#include <solvers/refinement/string_refinement.h>
2121

2222
#include <iomanip>
23+
#include <numeric>
2324
#include <stack>
2425
#include <util/expr_iterator.h>
2526
#include <util/arith_tools.h>
@@ -1221,62 +1222,87 @@ void debug_model(
12211222
stream << messaget::eom;
12221223
}
12231224

1224-
/// Create a new expression where 'with' expressions on arrays are replaced by
1225-
/// 'if' expressions. e.g. for an array access arr[index], where: `arr :=
1226-
/// array_of(12) with {0:=24} with {2:=42}` the constructed expression will be:
1227-
/// `index==0 ? 24 : index==2 ? 42 : 12`
1228-
/// If `left_propagate` is set to true, the expression will look like
1229-
/// `index<=0 ? 24 : index<=2 ? 42 : 12`
1230-
/// \param expr: A (possibly nested) 'with' expression on an `array_of`
1231-
/// expression. The function checks that the expression is of the form
1232-
/// `with_expr(with_expr(...(array_of(...)))`. This is the form in which
1233-
/// array valuations coming from the underlying solver are given.
1234-
/// \param index: An index with which to build the equality condition
1235-
/// \return An expression containing no 'with' expression
1236-
static exprt substitute_array_access(
1237-
const with_exprt &expr,
1238-
const exprt &index,
1239-
const bool left_propagate)
1225+
sparse_arrayt::sparse_arrayt(const with_exprt &expr)
12401226
{
1241-
std::vector<std::pair<std::size_t, exprt>> entries;
1242-
std::reference_wrapper<const exprt> ref =
1243-
std::ref(static_cast<const exprt &>(expr));
1227+
auto ref = std::ref(static_cast<const exprt &>(expr));
12441228
while(can_cast_expr<with_exprt>(ref.get()))
12451229
{
12461230
const auto &with_expr = expr_dynamic_cast<with_exprt>(ref.get());
1247-
auto current_index = numeric_cast_v<std::size_t>(with_expr.where());
1248-
entries.push_back(std::make_pair(current_index, with_expr.new_value()));
1231+
const auto current_index = numeric_cast_v<std::size_t>(with_expr.where());
1232+
entries.emplace_back(current_index, with_expr.new_value());
12491233
ref = with_expr.old();
12501234
}
12511235

12521236
// This function only handles 'with' and 'array_of' expressions
12531237
PRECONDITION(ref.get().id() == ID_array_of);
1238+
default_value = expr_dynamic_cast<array_of_exprt>(ref.get()).what();
1239+
}
12541240

1255-
// sort entries by increasing index
1241+
exprt sparse_arrayt::to_if_expression(const exprt &index) const
1242+
{
1243+
return std::accumulate(
1244+
entries.begin(),
1245+
entries.end(),
1246+
default_value,
1247+
[&](
1248+
const exprt if_expr,
1249+
const std::pair<std::size_t, exprt> &entry) { // NOLINT
1250+
const exprt entry_index = from_integer(entry.first, index.type());
1251+
const exprt &then_expr = entry.second;
1252+
CHECK_RETURN(then_expr.type() == if_expr.type());
1253+
const equal_exprt index_equal(index, entry_index);
1254+
return if_exprt(index_equal, then_expr, if_expr, if_expr.type());
1255+
});
1256+
}
1257+
1258+
interval_sparse_arrayt::interval_sparse_arrayt(const with_exprt &expr)
1259+
: sparse_arrayt(expr)
1260+
{
1261+
// Entries are sorted so that successive entries correspond to intervals
12561262
std::sort(
12571263
entries.begin(),
12581264
entries.end(),
12591265
[](
12601266
const std::pair<std::size_t, exprt> &a,
12611267
const std::pair<std::size_t, exprt> &b) { return a.first < b.first; });
1268+
}
12621269

1263-
exprt result = expr_dynamic_cast<array_of_exprt>(ref.get()).what();
1264-
for(const auto &entry : entries)
1265-
{
1266-
const exprt &then_expr = entry.second;
1267-
const typet &type = then_expr.type();
1268-
CHECK_RETURN(type == result.type());
1269-
const exprt entry_index = from_integer(entry.first, index.type());
1270-
if(left_propagate)
1271-
{
1270+
exprt interval_sparse_arrayt::to_if_expression(const exprt &index) const
1271+
{
1272+
return std::accumulate(
1273+
entries.rbegin(),
1274+
entries.rend(),
1275+
default_value,
1276+
[&](
1277+
const exprt if_expr,
1278+
const std::pair<std::size_t, exprt> &entry) { // NOLINT
1279+
const exprt entry_index = from_integer(entry.first, index.type());
1280+
const exprt &then_expr = entry.second;
1281+
CHECK_RETURN(then_expr.type() == if_expr.type());
12721282
const binary_relation_exprt index_small_eq(index, ID_le, entry_index);
1273-
result = if_exprt(index_small_eq, then_expr, result, type);
1274-
}
1275-
else
1276-
result =
1277-
if_exprt(equal_exprt(index, entry_index), then_expr, result, type);
1278-
}
1279-
return result;
1283+
return if_exprt(index_small_eq, then_expr, if_expr, if_expr.type());
1284+
});
1285+
}
1286+
1287+
/// Create a new expression where 'with' expressions on arrays are replaced by
1288+
/// 'if' expressions. e.g. for an array access arr[index], where: `arr :=
1289+
/// array_of(12) with {0:=24} with {2:=42}` the constructed expression will be:
1290+
/// `index==0 ? 24 : index==2 ? 42 : 12`
1291+
/// If `left_propagate` is set to true, the expression will look like
1292+
/// `index<=0 ? 24 : index<=2 ? 42 : 12`
1293+
/// \param expr: A (possibly nested) 'with' expression on an `array_of`
1294+
/// expression. The function checks that the expression is of the form
1295+
/// `with_expr(with_expr(...(array_of(...)))`. This is the form in which
1296+
/// array valuations coming from the underlying solver are given.
1297+
/// \param index: An index with which to build the equality condition
1298+
/// \return An expression containing no 'with' expression
1299+
static exprt substitute_array_access(
1300+
const with_exprt &expr,
1301+
const exprt &index,
1302+
const bool left_propagate)
1303+
{
1304+
return left_propagate ? interval_sparse_arrayt(expr).to_if_expression(index)
1305+
: sparse_arrayt(expr).to_if_expression(index);
12801306
}
12811307

12821308
/// Fill an array represented by a list of with_expr by propagating values to

src/solvers/refinement/string_refinement.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,41 @@ struct string_axiomst
4343
std::vector<string_not_contains_constraintt> not_contains;
4444
};
4545

46+
/// Represents arrays of the form `array_of(x) with {i:=a} with {j:=b} ...`
47+
/// by a default value `x` and a list of entries `(i,a)`, `(j,b)` etc.
48+
class sparse_arrayt
49+
{
50+
public:
51+
/// Initialize a sparse array from an expression of the form
52+
/// `array_of(x) with {i:=a} with {j:=b} ...`
53+
/// This is the form in which array valuations coming from the underlying
54+
/// solver are given.
55+
explicit sparse_arrayt(const with_exprt &expr);
56+
57+
/// Creates an if_expr corresponding to the result of accessing the array
58+
/// at the given index
59+
virtual exprt to_if_expression(const exprt &index) const;
60+
61+
protected:
62+
exprt default_value;
63+
std::vector<std::pair<std::size_t, exprt>> entries;
64+
};
65+
66+
/// Represents arrays by the indexes up to which the value remains the same.
67+
/// For instance `{'a', 'a', 'a', 'b', 'b', 'c'}` would be represented by
68+
/// by ('a', 2) ; ('b', 4), ('c', 5).
69+
/// This is particularly useful when the array is constant on intervals.
70+
class interval_sparse_arrayt final : public sparse_arrayt
71+
{
72+
public:
73+
/// An expression of the form `array_of(x) with {i:=a} with {j:=b}` is
74+
/// converted to an array `arr` where for all index `k` smaller than `i`,
75+
/// `arr[k]` is `a`, for all index between `i` (exclusive) and `j` it is `b`
76+
/// and for the others it is `x`.
77+
explicit interval_sparse_arrayt(const with_exprt &expr);
78+
exprt to_if_expression(const exprt &index) const override;
79+
};
80+
4681
class string_refinementt final: public bv_refinementt
4782
{
4883
private:

0 commit comments

Comments
 (0)