|
| 1 | +/*++ |
| 2 | + Copyright (c) 2020 Microsoft Corporation |
| 3 | +
|
| 4 | + Module Name: |
| 5 | +
|
| 6 | + value_generatorr.cpp |
| 7 | +
|
| 8 | + Abstract: |
| 9 | + |
| 10 | + Generate mostly different values using index as seed. |
| 11 | +
|
| 12 | + Author: |
| 13 | +
|
| 14 | + Nikolaj Bjorner 2020-04-25 |
| 15 | +
|
| 16 | +--*/ |
| 17 | + |
| 18 | +#include "ast/value_generator.h" |
| 19 | +#include "ast/datatype_decl_plugin.h" |
| 20 | +#include "ast/array_decl_plugin.h" |
| 21 | +#include "ast/arith_decl_plugin.h" |
| 22 | +#include "ast/bv_decl_plugin.h" |
| 23 | +#include "ast/seq_decl_plugin.h" |
| 24 | +#include <cmath> |
| 25 | + |
| 26 | + |
| 27 | + |
| 28 | +/* |
| 29 | + \brief inverse of the Cantor function. |
| 30 | + It converts an unsigned into a pair of unsigned numbers that are not |
| 31 | + bigger (and only equal for values 0, 1). |
| 32 | +*/ |
| 33 | +static void inverse_cantor(unsigned z, unsigned& x, unsigned& y) { |
| 34 | + unsigned w = ((unsigned)sqrt(8*z + 1) - 1)/2; |
| 35 | + unsigned t = (unsigned)(w*w + w)/2; |
| 36 | + y = z - t; |
| 37 | + x = w - y; |
| 38 | +} |
| 39 | + |
| 40 | +static bool is_small_size(sort_size const& sz) { |
| 41 | + return sz.is_finite() && sz.size() < (UINT_MAX >> 12); |
| 42 | +} |
| 43 | + |
| 44 | +class datatype_value_generator : public value_generator_core { |
| 45 | + ast_manager& m; |
| 46 | + value_generator& g; |
| 47 | + datatype_util dt; |
| 48 | + sort_ref_vector m_sorts; |
| 49 | + obj_map<sort, expr_ref_vector*> m_values; |
| 50 | + obj_map<func_decl, unsigned> m_constr2seen; |
| 51 | + random_gen m_rand; |
| 52 | + |
| 53 | + unsigned_vector inf; |
| 54 | + void index2vector(unsigned z, func_decl* c, unsigned_vector& v) { |
| 55 | + unsigned arity = c->get_arity(); |
| 56 | + v.resize(arity); |
| 57 | + inf.reset(); |
| 58 | + for (unsigned i = 0; i < arity; ++i) { |
| 59 | + sort* s = c->get_domain(i); |
| 60 | + sort_size const& sz = s->get_num_elements(); |
| 61 | + if (is_small_size(sz)) { |
| 62 | + v[i] = z % sz.size(); |
| 63 | + z = z/((unsigned)sz.size()); |
| 64 | + } |
| 65 | + else { |
| 66 | + inf.push_back(i); |
| 67 | + } |
| 68 | + } |
| 69 | + // fill in values for large domains |
| 70 | + for (unsigned i = 0; i + 1 < inf.size(); ++i) { |
| 71 | + inverse_cantor(z, v[inf[i]], z); |
| 72 | + } |
| 73 | + if (!inf.empty()) { |
| 74 | + v[inf.back()] = z; |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + bool domain_size_exhausted(unsigned j, func_decl* c) { |
| 79 | + unsigned arity = c->get_arity(); |
| 80 | + uint64_t dsz = 1; |
| 81 | + for (unsigned i = 0; i < arity; ++i) { |
| 82 | + sort* s = c->get_domain(i); |
| 83 | + sort_size const& sz = s->get_num_elements(); |
| 84 | + if (is_small_size(sz)) { |
| 85 | + dsz *= sz.size(); |
| 86 | + } |
| 87 | + else { |
| 88 | + return false; |
| 89 | + } |
| 90 | + if (dsz > j) { |
| 91 | + return false; |
| 92 | + } |
| 93 | + } |
| 94 | + return j >= dsz; |
| 95 | + } |
| 96 | + |
| 97 | + |
| 98 | +public: |
| 99 | + datatype_value_generator(value_generator& g, ast_manager& m): |
| 100 | + m(m), g(g), dt(m), m_sorts(m) {} |
| 101 | + |
| 102 | + ~datatype_value_generator() { |
| 103 | + for (auto& kv : m_values) dealloc(kv.m_value); |
| 104 | + } |
| 105 | + |
| 106 | + family_id get_fid() const override { |
| 107 | + return dt.get_family_id(); |
| 108 | + } |
| 109 | + |
| 110 | + /* |
| 111 | + In the off chance that a recursive datatype constructor is left recursive and has no base case on its own |
| 112 | + we iterate over constructors in a random order to make sure to hit the base cases eventually. |
| 113 | + In such cases m_constr2seen might not get updated accurately and we admit repetitions. |
| 114 | + |
| 115 | + */ |
| 116 | + unsigned_vector indices; |
| 117 | + expr_ref get_value(sort* s, unsigned index) override { |
| 118 | + expr_ref_vector* vp = nullptr; |
| 119 | + if (!m_values.find(s, vp)) { |
| 120 | + vp = alloc(expr_ref_vector, m); |
| 121 | + for (auto* c : *dt.get_datatype_constructors(s)) { |
| 122 | + if (c->get_arity() == 0) |
| 123 | + vp->push_back(m.mk_const(c)); |
| 124 | + } |
| 125 | + m_values.insert(s, vp); |
| 126 | + m_sorts.push_back(s); |
| 127 | + } |
| 128 | + auto& v = *vp; |
| 129 | + expr_ref_vector args(m); |
| 130 | + bool some_valid = true; |
| 131 | + while (v.size() <= index && some_valid) { |
| 132 | + some_valid = false; |
| 133 | + auto const& decls = *dt.get_datatype_constructors(s); |
| 134 | + int start = m_rand(); |
| 135 | + for (unsigned i = 0; i < decls.size(); ++i) { |
| 136 | + func_decl* c = decls[(i + start) % decls.size()]; |
| 137 | + unsigned arity = c->get_arity(); |
| 138 | + if (arity == 0) |
| 139 | + continue; |
| 140 | + args.resize(c->get_arity()); |
| 141 | + unsigned j = 0; |
| 142 | + m_constr2seen.find(c, j); |
| 143 | + if (domain_size_exhausted(j, c)) |
| 144 | + continue; |
| 145 | + m_constr2seen.insert(c, j + 1); |
| 146 | + index2vector(j, c, indices); |
| 147 | + bool valid = true; |
| 148 | + for (unsigned i = 0; valid && i < args.size(); ++i) { |
| 149 | + args[i] = g.get_value(c->get_domain(i), indices[i]); |
| 150 | + valid &= args.get(i) != nullptr; |
| 151 | + } |
| 152 | + if (valid) { |
| 153 | + v.push_back(m.mk_app(c, args)); |
| 154 | + some_valid = true; |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + return expr_ref(v.get(index, nullptr), m); |
| 159 | + } |
| 160 | +}; |
| 161 | + |
| 162 | + |
| 163 | +class arith_value_generator : public value_generator_core { |
| 164 | + ast_manager& m; |
| 165 | + arith_util a; |
| 166 | + int u2i(unsigned u) { |
| 167 | + if (0 == (u % 2)) |
| 168 | + return u/2; |
| 169 | + else |
| 170 | + return -(int)((u+1)/2); |
| 171 | + } |
| 172 | + |
| 173 | + void calkin_wilf(unsigned i, unsigned& x, unsigned& y) { |
| 174 | + while (i > 1) { |
| 175 | + if (i % 2 == 0) { |
| 176 | + x += y; |
| 177 | + } |
| 178 | + else { |
| 179 | + y += x; |
| 180 | + } |
| 181 | + i /= 2; |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | +public: |
| 186 | + arith_value_generator(ast_manager& m): m(m), a(m) {} |
| 187 | + |
| 188 | + family_id get_fid() const override { |
| 189 | + return a.get_family_id(); |
| 190 | + } |
| 191 | + |
| 192 | + expr_ref get_value(sort* s, unsigned index) override { |
| 193 | + if (a.is_int(s)) { |
| 194 | + return expr_ref(a.mk_int(u2i(index)), m); |
| 195 | + } |
| 196 | + if (index == 0) |
| 197 | + return expr_ref(a.mk_real(rational(0)), m); |
| 198 | + unsigned x = 1, y = 1; |
| 199 | + calkin_wilf((index/2) + 1, x, y); |
| 200 | + if ((index % 2) == 0) x = -(int)x; |
| 201 | + return expr_ref(a.mk_real(rational(x, y)), m); |
| 202 | + } |
| 203 | +}; |
| 204 | + |
| 205 | +class seq_value_generator : public value_generator_core { |
| 206 | + ast_manager& m; |
| 207 | + value_generator& g; |
| 208 | + seq_util seq; |
| 209 | +public: |
| 210 | + seq_value_generator(value_generator& g, ast_manager& m): g(g), m(m), seq(m) {} |
| 211 | + |
| 212 | + family_id get_fid() const override { |
| 213 | + return seq.get_family_id(); |
| 214 | + } |
| 215 | + |
| 216 | + expr_ref get_value(sort* s, unsigned index) override { |
| 217 | + sort* elem_sort = nullptr; |
| 218 | + if (!seq.is_seq(s, elem_sort)) { |
| 219 | + return expr_ref(m.mk_fresh_const("re", s), m); |
| 220 | + } |
| 221 | + if (index == 0) { |
| 222 | + return expr_ref(seq.str.mk_empty(s), m); |
| 223 | + } |
| 224 | + --index; |
| 225 | + sort_size const& sz = elem_sort->get_num_elements(); |
| 226 | + expr_ref_vector es(m); |
| 227 | + unsigned idx = 0; |
| 228 | + if (is_small_size(sz)) { |
| 229 | + // TBD: could also use a Gray-code version |
| 230 | + // TBD: could also prefer longer sequences |
| 231 | + unsigned esz = (unsigned)sz.size(); |
| 232 | + index += esz; |
| 233 | + do { |
| 234 | + idx = index % esz; |
| 235 | + expr_ref elem = g.get_value(elem_sort, idx); |
| 236 | + es.push_back(seq.str.mk_unit(elem)); |
| 237 | + index /= esz; |
| 238 | + } |
| 239 | + while (index >= esz); |
| 240 | + } |
| 241 | + else { |
| 242 | + do { |
| 243 | + inverse_cantor(index, idx, index); |
| 244 | + expr_ref elem = g.get_value(elem_sort, idx); |
| 245 | + es.push_back(seq.str.mk_unit(elem)); |
| 246 | + } |
| 247 | + while (index > 0); |
| 248 | + } |
| 249 | + return expr_ref(seq.str.mk_concat(es, s), m); |
| 250 | + } |
| 251 | +}; |
| 252 | + |
| 253 | + |
| 254 | +class array_value_generator : public value_generator_core { |
| 255 | + ast_manager& m; |
| 256 | + value_generator& g; |
| 257 | + array_util a; |
| 258 | +public: |
| 259 | + array_value_generator(value_generator& g, ast_manager& m): g(g), m(m), a(m) {} |
| 260 | + |
| 261 | + family_id get_fid() const override { |
| 262 | + return a.get_family_id(); |
| 263 | + } |
| 264 | + |
| 265 | + // if both domain and range are finite, this will create repetitions. |
| 266 | + // overall, finite domain arrays can be handled separately |
| 267 | + // repetitions also happen when the same set of indices are updated twice |
| 268 | + |
| 269 | + expr_ref get_value(sort* s, unsigned index) override { |
| 270 | + unsigned arity = get_array_arity(s); |
| 271 | + sort* r = get_array_range(s); |
| 272 | + sort_size const& sz = r->get_num_elements(); |
| 273 | + if (sz.is_finite() && sz.size() == 1) { |
| 274 | + return expr_ref(a.mk_const_array(s, g.get_value(r, 0)), m); |
| 275 | + } |
| 276 | + |
| 277 | + unsigned z = 0; |
| 278 | + if (is_small_size(sz)) { |
| 279 | + z = index % sz.size(); |
| 280 | + index = index / (unsigned)sz.size(); |
| 281 | + } |
| 282 | + else { |
| 283 | + inverse_cantor(index, z, index); |
| 284 | + } |
| 285 | + expr_ref result(a.mk_const_array(s, g.get_value(r, z)), m); |
| 286 | + unsigned default_index = z; |
| 287 | + expr_ref_vector args(m); |
| 288 | + unsigned_vector inf; |
| 289 | + args.resize(arity+2); |
| 290 | + while (index > 0) { |
| 291 | + args[0] = result; |
| 292 | + for (unsigned i = 0; i < arity; ++i) { |
| 293 | + sort* d = get_array_domain(s, i); |
| 294 | + sort_size const& dsz = d->get_num_elements(); |
| 295 | + if (is_small_size(dsz)) { |
| 296 | + args[1 + i] = g.get_value(d, index % dsz.size()); |
| 297 | + index = index / ((unsigned)dsz.size()); |
| 298 | + } |
| 299 | + else { |
| 300 | + inf.push_back(i); |
| 301 | + } |
| 302 | + } |
| 303 | + for (unsigned i : inf) { |
| 304 | + inverse_cantor(index, z, index); |
| 305 | + args[1 + i] = g.get_value(get_array_domain(s, i), z); |
| 306 | + } |
| 307 | + |
| 308 | + // ensure z is different from default_index. |
| 309 | + if (is_small_size(sz)) { |
| 310 | + z = index % (sz.size() - 1); |
| 311 | + index = index / (unsigned)sz.size(); |
| 312 | + } |
| 313 | + else { |
| 314 | + inverse_cantor(index, z, index); |
| 315 | + } |
| 316 | + if (z >= default_index) z++; |
| 317 | + args[arity+1] = g.get_value(r, z); |
| 318 | + result = a.mk_store(args); |
| 319 | + } |
| 320 | + |
| 321 | + return result; |
| 322 | + } |
| 323 | +}; |
| 324 | + |
| 325 | +class bv_value_generator : public value_generator_core { |
| 326 | + ast_manager& m; |
| 327 | + bv_util bv; |
| 328 | +public: |
| 329 | + bv_value_generator(ast_manager& m): m(m), bv(m) {} |
| 330 | + |
| 331 | + family_id get_fid() const override { |
| 332 | + return bv.get_family_id(); |
| 333 | + } |
| 334 | + |
| 335 | + expr_ref get_value(sort* s, unsigned index) override { |
| 336 | + index %= bv.get_bv_size(s); |
| 337 | + return expr_ref(bv.mk_numeral(rational(index), s), m); |
| 338 | + } |
| 339 | +}; |
| 340 | + |
| 341 | +class bool_value_generator : public value_generator_core { |
| 342 | + ast_manager& m; |
| 343 | +public: |
| 344 | + bool_value_generator(ast_manager& m): m(m) {} |
| 345 | + |
| 346 | + family_id get_fid() const override { |
| 347 | + return m.get_basic_family_id(); |
| 348 | + } |
| 349 | + |
| 350 | + expr_ref get_value(sort* s, unsigned index) override { |
| 351 | + if (!m.is_bool(s)) |
| 352 | + return expr_ref(m.mk_fresh_const("basic", s), m); |
| 353 | + if (index % 2 == 0) |
| 354 | + return expr_ref(m.mk_false(), m); |
| 355 | + return expr_ref(m.mk_true(), m); |
| 356 | + } |
| 357 | +}; |
| 358 | + |
| 359 | + |
| 360 | +value_generator::value_generator(ast_manager& m): m(m) { |
| 361 | +} |
| 362 | + |
| 363 | +void value_generator::init() { |
| 364 | + if (!m_plugins.empty()) |
| 365 | + return; |
| 366 | + add_plugin(alloc(datatype_value_generator, *this, m)); |
| 367 | + add_plugin(alloc(arith_value_generator, m)); |
| 368 | + add_plugin(alloc(bv_value_generator, m)); |
| 369 | + add_plugin(alloc(bool_value_generator, m)); |
| 370 | + add_plugin(alloc(seq_value_generator, *this, m)); |
| 371 | + add_plugin(alloc(array_value_generator, *this, m)); |
| 372 | + // fp_value_generator |
| 373 | + // uninterp_sort_value_generator |
| 374 | +} |
| 375 | + |
| 376 | +void value_generator::add_plugin(value_generator_core* v) { |
| 377 | + m_plugins.reserve(v->get_fid() + 1); |
| 378 | + m_plugins.set(v->get_fid(), v); |
| 379 | +} |
| 380 | + |
| 381 | +expr_ref value_generator::get_value(sort* s, unsigned index) { |
| 382 | + init(); |
| 383 | + auto fid = s->get_family_id(); |
| 384 | + auto* p = m_plugins.get(fid, nullptr); |
| 385 | + return p ? p->get_value(s, index) : expr_ref(m.mk_fresh_const(s->get_name(), s), m); |
| 386 | +} |
0 commit comments