1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Andres Noetzli, Mathias Preiner |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Implementation of sygus_sampler. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/quantifiers/sygus_sampler.h" |
17 |
|
|
18 |
|
#include <sstream> |
19 |
|
|
20 |
|
#include "expr/dtype.h" |
21 |
|
#include "expr/dtype_cons.h" |
22 |
|
#include "expr/node_algorithm.h" |
23 |
|
#include "options/base_options.h" |
24 |
|
#include "options/quantifiers_options.h" |
25 |
|
#include "printer/printer.h" |
26 |
|
#include "theory/quantifiers/lazy_trie.h" |
27 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
28 |
|
#include "theory/rewriter.h" |
29 |
|
#include "util/bitvector.h" |
30 |
|
#include "util/random.h" |
31 |
|
#include "util/rational.h" |
32 |
|
#include "util/sampler.h" |
33 |
|
#include "util/string.h" |
34 |
|
|
35 |
|
namespace cvc5 { |
36 |
|
namespace theory { |
37 |
|
namespace quantifiers { |
38 |
|
|
39 |
5778 |
SygusSampler::SygusSampler(Env& env) |
40 |
5778 |
: d_env(env), d_tds(nullptr), d_use_sygus_type(false), d_is_valid(false) |
41 |
|
{ |
42 |
5778 |
} |
43 |
|
|
44 |
64 |
void SygusSampler::initialize(TypeNode tn, |
45 |
|
const std::vector<Node>& vars, |
46 |
|
unsigned nsamples, |
47 |
|
bool unique_type_ids) |
48 |
|
{ |
49 |
64 |
d_tds = nullptr; |
50 |
64 |
d_use_sygus_type = false; |
51 |
64 |
d_is_valid = true; |
52 |
64 |
d_ftn = TypeNode::null(); |
53 |
64 |
d_type_vars.clear(); |
54 |
64 |
d_vars.clear(); |
55 |
64 |
d_rvalue_cindices.clear(); |
56 |
64 |
d_rvalue_null_cindices.clear(); |
57 |
64 |
d_rstring_alphabet.clear(); |
58 |
64 |
d_var_sygus_types.clear(); |
59 |
64 |
d_const_sygus_types.clear(); |
60 |
64 |
d_vars.insert(d_vars.end(), vars.begin(), vars.end()); |
61 |
128 |
std::map<TypeNode, unsigned> type_to_type_id; |
62 |
64 |
unsigned type_id_counter = 0; |
63 |
431 |
for (const Node& sv : d_vars) |
64 |
|
{ |
65 |
734 |
TypeNode svt = sv.getType(); |
66 |
367 |
unsigned tnid = 0; |
67 |
367 |
if (unique_type_ids) |
68 |
|
{ |
69 |
|
tnid = type_id_counter; |
70 |
|
type_id_counter++; |
71 |
|
} |
72 |
|
else |
73 |
|
{ |
74 |
367 |
std::map<TypeNode, unsigned>::iterator itt = type_to_type_id.find(svt); |
75 |
367 |
if (itt == type_to_type_id.end()) |
76 |
|
{ |
77 |
65 |
type_to_type_id[svt] = type_id_counter; |
78 |
65 |
type_id_counter++; |
79 |
|
} |
80 |
|
else |
81 |
|
{ |
82 |
302 |
tnid = itt->second; |
83 |
|
} |
84 |
|
} |
85 |
734 |
Trace("sygus-sample-debug") |
86 |
367 |
<< "Type id for " << sv << " is " << tnid << std::endl; |
87 |
367 |
d_var_index[sv] = d_type_vars[tnid].size(); |
88 |
367 |
d_type_vars[tnid].push_back(sv); |
89 |
367 |
d_type_ids[sv] = tnid; |
90 |
|
} |
91 |
64 |
initializeSamples(nsamples); |
92 |
64 |
} |
93 |
|
|
94 |
21 |
void SygusSampler::initializeSygus(TermDbSygus* tds, |
95 |
|
Node f, |
96 |
|
unsigned nsamples, |
97 |
|
bool useSygusType) |
98 |
|
{ |
99 |
21 |
d_tds = tds; |
100 |
21 |
d_use_sygus_type = useSygusType; |
101 |
21 |
d_is_valid = true; |
102 |
21 |
d_ftn = f.getType(); |
103 |
21 |
Assert(d_ftn.isDatatype()); |
104 |
21 |
const DType& dt = d_ftn.getDType(); |
105 |
21 |
Assert(dt.isSygus()); |
106 |
|
|
107 |
21 |
Trace("sygus-sample") << "Register sampler for " << f << std::endl; |
108 |
|
|
109 |
21 |
d_vars.clear(); |
110 |
21 |
d_type_vars.clear(); |
111 |
21 |
d_var_index.clear(); |
112 |
21 |
d_type_vars.clear(); |
113 |
21 |
d_rvalue_cindices.clear(); |
114 |
21 |
d_rvalue_null_cindices.clear(); |
115 |
21 |
d_var_sygus_types.clear(); |
116 |
|
// get the sygus variable list |
117 |
42 |
Node var_list = dt.getSygusVarList(); |
118 |
21 |
if (!var_list.isNull()) |
119 |
|
{ |
120 |
85 |
for (const Node& sv : var_list) |
121 |
|
{ |
122 |
64 |
d_vars.push_back(sv); |
123 |
|
} |
124 |
|
} |
125 |
|
// register sygus type |
126 |
21 |
registerSygusType(d_ftn); |
127 |
|
// Variables are associated with type ids based on the set of sygus types they |
128 |
|
// appear in. |
129 |
42 |
std::map<Node, unsigned> var_to_type_id; |
130 |
21 |
unsigned type_id_counter = 0; |
131 |
85 |
for (const Node& sv : d_vars) |
132 |
|
{ |
133 |
128 |
TypeNode svt = sv.getType(); |
134 |
|
// is it equivalent to a previous variable? |
135 |
162 |
for (const auto& v : var_to_type_id) |
136 |
|
{ |
137 |
196 |
Node svc = v.first; |
138 |
98 |
if (svc.getType() == svt) |
139 |
|
{ |
140 |
86 |
if (d_var_sygus_types[sv].size() == d_var_sygus_types[svc].size()) |
141 |
|
{ |
142 |
78 |
bool success = true; |
143 |
134 |
for (unsigned t = 0, size = d_var_sygus_types[sv].size(); t < size; |
144 |
|
t++) |
145 |
|
{ |
146 |
74 |
if (d_var_sygus_types[sv][t] != d_var_sygus_types[svc][t]) |
147 |
|
{ |
148 |
18 |
success = false; |
149 |
18 |
break; |
150 |
|
} |
151 |
|
} |
152 |
78 |
if (success) |
153 |
|
{ |
154 |
60 |
var_to_type_id[sv] = var_to_type_id[svc]; |
155 |
|
} |
156 |
|
} |
157 |
|
} |
158 |
|
} |
159 |
64 |
if (var_to_type_id.find(sv) == var_to_type_id.end()) |
160 |
|
{ |
161 |
38 |
var_to_type_id[sv] = type_id_counter; |
162 |
38 |
type_id_counter++; |
163 |
|
} |
164 |
64 |
unsigned tnid = var_to_type_id[sv]; |
165 |
128 |
Trace("sygus-sample-debug") |
166 |
64 |
<< "Type id for " << sv << " is " << tnid << std::endl; |
167 |
64 |
d_var_index[sv] = d_type_vars[tnid].size(); |
168 |
64 |
d_type_vars[tnid].push_back(sv); |
169 |
64 |
d_type_ids[sv] = tnid; |
170 |
|
} |
171 |
|
|
172 |
21 |
initializeSamples(nsamples); |
173 |
21 |
} |
174 |
|
|
175 |
85 |
void SygusSampler::initializeSamples(unsigned nsamples) |
176 |
|
{ |
177 |
85 |
d_samples.clear(); |
178 |
170 |
std::vector<TypeNode> types; |
179 |
516 |
for (const Node& v : d_vars) |
180 |
|
{ |
181 |
862 |
TypeNode vt = v.getType(); |
182 |
431 |
types.push_back(vt); |
183 |
862 |
Trace("sygus-sample") << " var #" << types.size() << " : " << v << " : " |
184 |
431 |
<< vt << std::endl; |
185 |
|
} |
186 |
170 |
std::map<unsigned, std::map<Node, std::vector<TypeNode> >::iterator> sts; |
187 |
85 |
if (options::sygusSampleGrammar()) |
188 |
|
{ |
189 |
516 |
for (unsigned j = 0, size = types.size(); j < size; j++) |
190 |
|
{ |
191 |
431 |
sts[j] = d_var_sygus_types.find(d_vars[j]); |
192 |
|
} |
193 |
|
} |
194 |
|
|
195 |
85 |
unsigned nduplicates = 0; |
196 |
123822 |
for (unsigned i = 0; i < nsamples; i++) |
197 |
|
{ |
198 |
247484 |
std::vector<Node> sample_pt; |
199 |
638902 |
for (unsigned j = 0, size = types.size(); j < size; j++) |
200 |
|
{ |
201 |
1030310 |
Node v = d_vars[j]; |
202 |
1030310 |
Node r; |
203 |
515155 |
if (options::sygusSampleGrammar()) |
204 |
|
{ |
205 |
|
// choose a random start sygus type, if possible |
206 |
515155 |
if (sts[j] != d_var_sygus_types.end()) |
207 |
|
{ |
208 |
312543 |
unsigned ntypes = sts[j]->second.size(); |
209 |
312543 |
if(ntypes > 0) |
210 |
|
{ |
211 |
307175 |
unsigned index = Random::getRandom().pick(0, ntypes - 1); |
212 |
307175 |
if (index < ntypes) |
213 |
|
{ |
214 |
|
// currently hard coded to 0.0, 0.5 |
215 |
307175 |
r = getSygusRandomValue(sts[j]->second[index], 0.0, 0.5); |
216 |
|
} |
217 |
|
} |
218 |
|
} |
219 |
|
} |
220 |
515155 |
if (r.isNull()) |
221 |
|
{ |
222 |
207980 |
r = getRandomValue(types[j]); |
223 |
207980 |
if (r.isNull()) |
224 |
|
{ |
225 |
|
d_is_valid = false; |
226 |
|
} |
227 |
|
} |
228 |
515155 |
sample_pt.push_back(r); |
229 |
|
} |
230 |
123747 |
if (d_samples_trie.add(sample_pt)) |
231 |
|
{ |
232 |
15344 |
if (Trace.isOn("sygus-sample")) |
233 |
|
{ |
234 |
|
Trace("sygus-sample") << "Sample point #" << i << " : "; |
235 |
|
for (const Node& r : sample_pt) |
236 |
|
{ |
237 |
|
Trace("sygus-sample") << r << " "; |
238 |
|
} |
239 |
|
Trace("sygus-sample") << std::endl; |
240 |
|
} |
241 |
15344 |
d_samples.push_back(sample_pt); |
242 |
|
} |
243 |
|
else |
244 |
|
{ |
245 |
108403 |
i--; |
246 |
108403 |
nduplicates++; |
247 |
108403 |
if (nduplicates == nsamples * 10) |
248 |
|
{ |
249 |
20 |
Trace("sygus-sample") |
250 |
10 |
<< "...WARNING: excessive duplicates, cut off sampling at " << i |
251 |
10 |
<< "/" << nsamples << " points." << std::endl; |
252 |
10 |
break; |
253 |
|
} |
254 |
|
} |
255 |
|
} |
256 |
|
|
257 |
85 |
d_trie.clear(); |
258 |
85 |
} |
259 |
|
|
260 |
123747 |
bool SygusSampler::PtTrie::add(std::vector<Node>& pt) |
261 |
|
{ |
262 |
123747 |
PtTrie* curr = this; |
263 |
638902 |
for (unsigned i = 0, size = pt.size(); i < size; i++) |
264 |
|
{ |
265 |
515155 |
curr = &(curr->d_children[pt[i]]); |
266 |
|
} |
267 |
123747 |
bool retVal = curr->d_children.empty(); |
268 |
123747 |
curr = &(curr->d_children[Node::null()]); |
269 |
123747 |
return retVal; |
270 |
|
} |
271 |
|
|
272 |
2252 |
Node SygusSampler::registerTerm(Node n, bool forceKeep) |
273 |
|
{ |
274 |
2252 |
if (!d_is_valid) |
275 |
|
{ |
276 |
|
// do nothing |
277 |
|
return n; |
278 |
|
} |
279 |
4504 |
Node bn = n; |
280 |
4504 |
TypeNode tn = n.getType(); |
281 |
|
// If we are using sygus types, get the builtin analog of n. |
282 |
2252 |
if (d_use_sygus_type) |
283 |
|
{ |
284 |
1396 |
bn = d_tds->sygusToBuiltin(n); |
285 |
1396 |
d_builtin_to_sygus[tn][bn] = n; |
286 |
|
} |
287 |
|
// cache based on the (original) type of n |
288 |
4504 |
Node res = d_trie[tn].add(bn, this, 0, d_samples.size(), forceKeep); |
289 |
|
// If we are using sygus types, map back to an original. |
290 |
|
// Notice that d_builtin_to_sygus is not necessarily bijective. |
291 |
2252 |
if (d_use_sygus_type) |
292 |
|
{ |
293 |
1396 |
std::map<Node, Node>& bts = d_builtin_to_sygus[tn]; |
294 |
1396 |
Assert(bts.find(res) != bts.end()); |
295 |
1396 |
res = res != bn ? bts[res] : n; |
296 |
|
} |
297 |
2252 |
return res; |
298 |
|
} |
299 |
|
|
300 |
|
bool SygusSampler::isContiguous(Node n) |
301 |
|
{ |
302 |
|
// compute free variables in n |
303 |
|
std::vector<Node> fvs; |
304 |
|
computeFreeVariables(n, fvs); |
305 |
|
// compute contiguous condition |
306 |
|
for (const std::pair<const unsigned, std::vector<Node> >& p : d_type_vars) |
307 |
|
{ |
308 |
|
bool foundNotFv = false; |
309 |
|
for (const Node& v : p.second) |
310 |
|
{ |
311 |
|
bool hasFv = std::find(fvs.begin(), fvs.end(), v) != fvs.end(); |
312 |
|
if (!hasFv) |
313 |
|
{ |
314 |
|
foundNotFv = true; |
315 |
|
} |
316 |
|
else if (foundNotFv) |
317 |
|
{ |
318 |
|
return false; |
319 |
|
} |
320 |
|
} |
321 |
|
} |
322 |
|
return true; |
323 |
|
} |
324 |
|
|
325 |
84 |
void SygusSampler::computeFreeVariables(Node n, std::vector<Node>& fvs) |
326 |
|
{ |
327 |
168 |
std::unordered_set<TNode> visited; |
328 |
84 |
std::unordered_set<TNode>::iterator it; |
329 |
168 |
std::vector<TNode> visit; |
330 |
168 |
TNode cur; |
331 |
84 |
visit.push_back(n); |
332 |
274 |
do |
333 |
|
{ |
334 |
358 |
cur = visit.back(); |
335 |
358 |
visit.pop_back(); |
336 |
358 |
if (visited.find(cur) == visited.end()) |
337 |
|
{ |
338 |
284 |
visited.insert(cur); |
339 |
284 |
if (cur.isVar()) |
340 |
|
{ |
341 |
127 |
if (d_var_index.find(cur) != d_var_index.end()) |
342 |
|
{ |
343 |
127 |
fvs.push_back(cur); |
344 |
|
} |
345 |
|
} |
346 |
558 |
for (const Node& cn : cur) |
347 |
|
{ |
348 |
274 |
visit.push_back(cn); |
349 |
|
} |
350 |
|
} |
351 |
358 |
} while (!visit.empty()); |
352 |
84 |
} |
353 |
|
|
354 |
|
bool SygusSampler::isOrdered(Node n) { return checkVariables(n, true, false); } |
355 |
|
|
356 |
|
bool SygusSampler::isLinear(Node n) { return checkVariables(n, false, true); } |
357 |
|
|
358 |
620 |
bool SygusSampler::checkVariables(Node n, bool checkOrder, bool checkLinear) |
359 |
|
{ |
360 |
|
// compute free variables in n for each type |
361 |
1240 |
std::map<unsigned, std::vector<Node> > fvs; |
362 |
|
|
363 |
1240 |
std::unordered_set<TNode> visited; |
364 |
620 |
std::unordered_set<TNode>::iterator it; |
365 |
1240 |
std::vector<TNode> visit; |
366 |
1240 |
TNode cur; |
367 |
620 |
visit.push_back(n); |
368 |
1043 |
do |
369 |
|
{ |
370 |
1663 |
cur = visit.back(); |
371 |
1663 |
visit.pop_back(); |
372 |
1663 |
if (visited.find(cur) == visited.end()) |
373 |
|
{ |
374 |
1460 |
visited.insert(cur); |
375 |
1460 |
if (cur.isVar()) |
376 |
|
{ |
377 |
618 |
std::map<Node, unsigned>::iterator itv = d_var_index.find(cur); |
378 |
618 |
if (itv != d_var_index.end()) |
379 |
|
{ |
380 |
618 |
if (checkOrder) |
381 |
|
{ |
382 |
618 |
unsigned tnid = d_type_ids[cur]; |
383 |
|
// if this variable is out of order |
384 |
618 |
if (itv->second != fvs[tnid].size()) |
385 |
|
{ |
386 |
226 |
return false; |
387 |
|
} |
388 |
392 |
fvs[tnid].push_back(cur); |
389 |
|
} |
390 |
392 |
if (checkLinear) |
391 |
|
{ |
392 |
|
if (expr::hasSubtermMulti(n, cur)) |
393 |
|
{ |
394 |
|
return false; |
395 |
|
} |
396 |
|
} |
397 |
|
} |
398 |
|
} |
399 |
2476 |
for (unsigned j = 0, nchildren = cur.getNumChildren(); j < nchildren; j++) |
400 |
|
{ |
401 |
1242 |
visit.push_back(cur[(nchildren - j) - 1]); |
402 |
|
} |
403 |
|
} |
404 |
1437 |
} while (!visit.empty()); |
405 |
394 |
return true; |
406 |
|
} |
407 |
|
|
408 |
84 |
bool SygusSampler::containsFreeVariables(Node a, Node b, bool strict) |
409 |
|
{ |
410 |
|
// compute free variables in a |
411 |
168 |
std::vector<Node> fvs; |
412 |
84 |
computeFreeVariables(a, fvs); |
413 |
168 |
std::vector<Node> fv_found; |
414 |
|
|
415 |
168 |
std::unordered_set<TNode> visited; |
416 |
84 |
std::unordered_set<TNode>::iterator it; |
417 |
168 |
std::vector<TNode> visit; |
418 |
168 |
TNode cur; |
419 |
84 |
visit.push_back(b); |
420 |
52 |
do |
421 |
|
{ |
422 |
136 |
cur = visit.back(); |
423 |
136 |
visit.pop_back(); |
424 |
136 |
if (visited.find(cur) == visited.end()) |
425 |
|
{ |
426 |
129 |
visited.insert(cur); |
427 |
129 |
if (cur.isVar()) |
428 |
|
{ |
429 |
26 |
if (std::find(fvs.begin(), fvs.end(), cur) == fvs.end()) |
430 |
|
{ |
431 |
1 |
return false; |
432 |
|
} |
433 |
25 |
else if (strict) |
434 |
|
{ |
435 |
25 |
if (fv_found.size() + 1 == fvs.size()) |
436 |
|
{ |
437 |
12 |
return false; |
438 |
|
} |
439 |
|
// cur should only be visited once |
440 |
13 |
Assert(std::find(fv_found.begin(), fv_found.end(), cur) |
441 |
|
== fv_found.end()); |
442 |
13 |
fv_found.push_back(cur); |
443 |
|
} |
444 |
|
} |
445 |
174 |
for (const Node& cn : cur) |
446 |
|
{ |
447 |
58 |
visit.push_back(cn); |
448 |
|
} |
449 |
|
} |
450 |
123 |
} while (!visit.empty()); |
451 |
71 |
return true; |
452 |
|
} |
453 |
|
|
454 |
149 |
void SygusSampler::getVariables(std::vector<Node>& vars) const |
455 |
|
{ |
456 |
149 |
vars.insert(vars.end(), d_vars.begin(), d_vars.end()); |
457 |
149 |
} |
458 |
|
|
459 |
7 |
void SygusSampler::getSamplePoint(unsigned index, |
460 |
|
std::vector<Node>& pt) |
461 |
|
{ |
462 |
7 |
Assert(index < d_samples.size()); |
463 |
7 |
std::vector<Node>& spt = d_samples[index]; |
464 |
7 |
pt.insert(pt.end(), spt.begin(), spt.end()); |
465 |
7 |
} |
466 |
|
|
467 |
135 |
void SygusSampler::addSamplePoint(std::vector<Node>& pt) |
468 |
|
{ |
469 |
135 |
Assert(pt.size() == d_vars.size()); |
470 |
135 |
d_samples.push_back(pt); |
471 |
135 |
} |
472 |
|
|
473 |
745590 |
Node SygusSampler::evaluate(Node n, unsigned index) |
474 |
|
{ |
475 |
745590 |
Assert(index < d_samples.size()); |
476 |
|
// do beta-reductions in n first |
477 |
745590 |
n = d_env.getRewriter()->rewrite(n); |
478 |
|
// use efficient rewrite for substitution + rewrite |
479 |
745590 |
Node ev = d_env.evaluate(n, d_vars, d_samples[index], true); |
480 |
745590 |
Assert(!ev.isNull()); |
481 |
745590 |
Trace("sygus-sample-ev") << "Evaluate ( " << n << ", " << index << " ) -> "; |
482 |
745590 |
Trace("sygus-sample-ev") << ev << std::endl; |
483 |
745590 |
return ev; |
484 |
|
} |
485 |
|
|
486 |
|
int SygusSampler::getDiffSamplePointIndex(Node a, Node b) |
487 |
|
{ |
488 |
|
for (unsigned i = 0, nsamp = d_samples.size(); i < nsamp; i++) |
489 |
|
{ |
490 |
|
Node ae = evaluate(a, i); |
491 |
|
Node be = evaluate(b, i); |
492 |
|
if (ae != be) |
493 |
|
{ |
494 |
|
return i; |
495 |
|
} |
496 |
|
} |
497 |
|
return -1; |
498 |
|
} |
499 |
|
|
500 |
694226 |
Node SygusSampler::getRandomValue(TypeNode tn) |
501 |
|
{ |
502 |
694226 |
NodeManager* nm = NodeManager::currentNM(); |
503 |
694226 |
if (tn.isBoolean()) |
504 |
|
{ |
505 |
495574 |
return nm->mkConst(Random::getRandom().pickWithProb(0.5)); |
506 |
|
} |
507 |
198652 |
else if (tn.isBitVector()) |
508 |
|
{ |
509 |
117005 |
unsigned sz = tn.getBitVectorSize(); |
510 |
117005 |
return nm->mkConst(Sampler::pickBvUniform(sz)); |
511 |
|
} |
512 |
81647 |
else if (tn.isFloatingPoint()) |
513 |
|
{ |
514 |
|
unsigned e = tn.getFloatingPointExponentSize(); |
515 |
|
unsigned s = tn.getFloatingPointSignificandSize(); |
516 |
|
return nm->mkConst(options::sygusSampleFpUniform() |
517 |
|
? Sampler::pickFpUniform(e, s) |
518 |
|
: Sampler::pickFpBiased(e, s)); |
519 |
|
} |
520 |
81647 |
else if (tn.isString() || tn.isInteger()) |
521 |
|
{ |
522 |
|
// if string, determine the alphabet |
523 |
81647 |
if (tn.isString() && d_rstring_alphabet.empty()) |
524 |
|
{ |
525 |
4 |
Trace("sygus-sample-str-alpha") |
526 |
2 |
<< "Setting string alphabet..." << std::endl; |
527 |
4 |
std::unordered_set<unsigned> alphas; |
528 |
10 |
for (const std::pair<const Node, std::vector<TypeNode> >& c : |
529 |
2 |
d_const_sygus_types) |
530 |
|
{ |
531 |
10 |
if (c.first.getType().isString()) |
532 |
|
{ |
533 |
12 |
Trace("sygus-sample-str-alpha") |
534 |
6 |
<< "...have constant " << c.first << std::endl; |
535 |
6 |
Assert(c.first.isConst()); |
536 |
12 |
std::vector<unsigned> svec = c.first.getConst<String>().getVec(); |
537 |
10 |
for (unsigned ch : svec) |
538 |
|
{ |
539 |
4 |
alphas.insert(ch); |
540 |
|
} |
541 |
|
} |
542 |
|
} |
543 |
|
// can limit to 1 extra characters beyond those in the grammar (2 if |
544 |
|
// there are none in the grammar) |
545 |
2 |
unsigned num_fresh_char = alphas.empty() ? 2 : 1; |
546 |
2 |
unsigned fresh_char = 0; |
547 |
4 |
for (unsigned i = 0; i < num_fresh_char; i++) |
548 |
|
{ |
549 |
2 |
while (alphas.find(fresh_char) != alphas.end()) |
550 |
|
{ |
551 |
|
fresh_char++; |
552 |
|
} |
553 |
2 |
alphas.insert(fresh_char); |
554 |
|
} |
555 |
4 |
Trace("sygus-sample-str-alpha") |
556 |
2 |
<< "Sygus sampler: limit strings alphabet to : " << std::endl |
557 |
2 |
<< " "; |
558 |
8 |
for (unsigned ch : alphas) |
559 |
|
{ |
560 |
6 |
d_rstring_alphabet.push_back(ch); |
561 |
6 |
Trace("sygus-sample-str-alpha") << " \\u" << ch; |
562 |
|
} |
563 |
2 |
Trace("sygus-sample-str-alpha") << std::endl; |
564 |
|
} |
565 |
|
|
566 |
81647 |
std::vector<unsigned> vec; |
567 |
81647 |
double ext_freq = .5; |
568 |
81647 |
unsigned base = tn.isString() ? d_rstring_alphabet.size() : 10; |
569 |
243925 |
while (Random::getRandom().pickWithProb(ext_freq)) |
570 |
|
{ |
571 |
|
// add a digit |
572 |
81139 |
unsigned digit = Random::getRandom().pick(0, base - 1); |
573 |
81139 |
if (tn.isString()) |
574 |
|
{ |
575 |
6759 |
digit = d_rstring_alphabet[digit]; |
576 |
|
} |
577 |
81139 |
vec.push_back(digit); |
578 |
|
} |
579 |
81647 |
if (tn.isString()) |
580 |
|
{ |
581 |
6858 |
return nm->mkConst(String(vec)); |
582 |
|
} |
583 |
74789 |
else if (tn.isInteger()) |
584 |
|
{ |
585 |
149578 |
Rational baser(base); |
586 |
149578 |
Rational curr(1); |
587 |
149578 |
std::vector<Node> sum; |
588 |
149169 |
for (unsigned j = 0, size = vec.size(); j < size; j++) |
589 |
|
{ |
590 |
148760 |
Node digit = nm->mkConst(Rational(vec[j]) * curr); |
591 |
74380 |
sum.push_back(digit); |
592 |
74380 |
curr = curr * baser; |
593 |
|
} |
594 |
149578 |
Node ret; |
595 |
74789 |
if (sum.empty()) |
596 |
|
{ |
597 |
37135 |
ret = nm->mkConst(Rational(0)); |
598 |
|
} |
599 |
37654 |
else if (sum.size() == 1) |
600 |
|
{ |
601 |
18906 |
ret = sum[0]; |
602 |
|
} |
603 |
|
else |
604 |
|
{ |
605 |
18748 |
ret = nm->mkNode(kind::PLUS, sum); |
606 |
|
} |
607 |
|
|
608 |
74789 |
if (Random::getRandom().pickWithProb(0.5)) |
609 |
|
{ |
610 |
|
// negative |
611 |
37420 |
ret = nm->mkNode(kind::UMINUS, ret); |
612 |
|
} |
613 |
74789 |
ret = d_env.getRewriter()->rewrite(ret); |
614 |
74789 |
Assert(ret.isConst()); |
615 |
74789 |
return ret; |
616 |
|
} |
617 |
|
} |
618 |
|
else if (tn.isReal()) |
619 |
|
{ |
620 |
|
Node s = getRandomValue(nm->integerType()); |
621 |
|
Node r = getRandomValue(nm->integerType()); |
622 |
|
if (!s.isNull() && !r.isNull()) |
623 |
|
{ |
624 |
|
Rational sr = s.getConst<Rational>(); |
625 |
|
Rational rr = r.getConst<Rational>(); |
626 |
|
if (rr.sgn() == 0) |
627 |
|
{ |
628 |
|
return s; |
629 |
|
} |
630 |
|
else |
631 |
|
{ |
632 |
|
return nm->mkConst(sr / rr); |
633 |
|
} |
634 |
|
} |
635 |
|
} |
636 |
|
// default: use type enumerator |
637 |
|
unsigned counter = 0; |
638 |
|
while (Random::getRandom().pickWithProb(0.5)) |
639 |
|
{ |
640 |
|
counter++; |
641 |
|
} |
642 |
|
Node ret = d_tenum.getEnumerateTerm(tn, counter); |
643 |
|
if (ret.isNull()) |
644 |
|
{ |
645 |
|
// beyond bounds, return the first |
646 |
|
ret = d_tenum.getEnumerateTerm(tn, 0); |
647 |
|
} |
648 |
|
return ret; |
649 |
|
} |
650 |
|
|
651 |
1028251 |
Node SygusSampler::getSygusRandomValue(TypeNode tn, |
652 |
|
double rchance, |
653 |
|
double rinc, |
654 |
|
unsigned depth) |
655 |
|
{ |
656 |
1028251 |
if (!tn.isDatatype()) |
657 |
|
{ |
658 |
|
return getRandomValue(tn); |
659 |
|
} |
660 |
1028251 |
const DType& dt = tn.getDType(); |
661 |
1028251 |
if (!dt.isSygus()) |
662 |
|
{ |
663 |
|
return getRandomValue(tn); |
664 |
|
} |
665 |
1028251 |
Assert(d_rvalue_cindices.find(tn) != d_rvalue_cindices.end()); |
666 |
2056502 |
Trace("sygus-sample-grammar") |
667 |
1028251 |
<< "Sygus random value " << tn << ", depth = " << depth |
668 |
1028251 |
<< ", rchance = " << rchance << std::endl; |
669 |
|
// check if we terminate on this call |
670 |
|
// we refuse to enumerate terms of 10+ depth as a hard limit |
671 |
1028251 |
bool terminate = Random::getRandom().pickWithProb(rchance) || depth >= 10; |
672 |
|
// if we terminate, only nullary constructors can be chosen |
673 |
|
std::vector<unsigned>& cindices = |
674 |
1028251 |
terminate ? d_rvalue_null_cindices[tn] : d_rvalue_cindices[tn]; |
675 |
1028251 |
unsigned ncons = cindices.size(); |
676 |
|
// select a random constructor, or random value when index=ncons. |
677 |
1028251 |
unsigned index = Random::getRandom().pick(0, ncons); |
678 |
2056502 |
Trace("sygus-sample-grammar") |
679 |
1028251 |
<< "Random index 0..." << ncons << " was : " << index << std::endl; |
680 |
1028251 |
if (index < ncons) |
681 |
|
{ |
682 |
1084010 |
Trace("sygus-sample-grammar") |
683 |
542005 |
<< "Recurse constructor index #" << index << std::endl; |
684 |
542005 |
unsigned cindex = cindices[index]; |
685 |
542005 |
Assert(cindex < dt.getNumConstructors()); |
686 |
542005 |
const DTypeConstructor& dtc = dt[cindex]; |
687 |
|
// more likely to terminate in recursive calls |
688 |
542005 |
double rchance_new = rchance + (1.0 - rchance) * rinc; |
689 |
542005 |
std::map<int, Node> pre; |
690 |
542005 |
bool success = true; |
691 |
|
// generate random values for all arguments |
692 |
1263081 |
for (unsigned i = 0, nargs = dtc.getNumArgs(); i < nargs; i++) |
693 |
|
{ |
694 |
1442152 |
TypeNode tnc = d_tds->getArgType(dtc, i); |
695 |
1442152 |
Node c = getSygusRandomValue(tnc, rchance_new, rinc, depth + 1); |
696 |
721076 |
if (c.isNull()) |
697 |
|
{ |
698 |
|
success = false; |
699 |
|
Trace("sygus-sample-grammar") << "...fail." << std::endl; |
700 |
|
break; |
701 |
|
} |
702 |
1442152 |
Trace("sygus-sample-grammar") |
703 |
721076 |
<< " child #" << i << " : " << c << std::endl; |
704 |
721076 |
pre[i] = c; |
705 |
|
} |
706 |
542005 |
if (success) |
707 |
|
{ |
708 |
542005 |
Trace("sygus-sample-grammar") << "mkGeneric" << std::endl; |
709 |
542005 |
Node ret = d_tds->mkGeneric(dt, cindex, pre); |
710 |
542005 |
Trace("sygus-sample-grammar") << "...returned " << ret << std::endl; |
711 |
542005 |
ret = d_env.getRewriter()->rewrite(ret); |
712 |
542005 |
Trace("sygus-sample-grammar") << "...after rewrite " << ret << std::endl; |
713 |
|
// A rare case where we generate a non-constant value from constant |
714 |
|
// leaves is (/ n 0). |
715 |
542005 |
if(ret.isConst()) |
716 |
|
{ |
717 |
542005 |
return ret; |
718 |
|
} |
719 |
|
} |
720 |
|
} |
721 |
486246 |
Trace("sygus-sample-grammar") << "...resort to random value" << std::endl; |
722 |
|
// if we did not generate based on the grammar, pick a random value |
723 |
486246 |
return getRandomValue(dt.getSygusType()); |
724 |
|
} |
725 |
|
|
726 |
|
// recursion depth bounded by number of types in grammar (small) |
727 |
367 |
void SygusSampler::registerSygusType(TypeNode tn) |
728 |
|
{ |
729 |
367 |
if (d_rvalue_cindices.find(tn) == d_rvalue_cindices.end()) |
730 |
|
{ |
731 |
41 |
d_rvalue_cindices[tn].clear(); |
732 |
41 |
if (!tn.isDatatype()) |
733 |
|
{ |
734 |
|
return; |
735 |
|
} |
736 |
41 |
const DType& dt = tn.getDType(); |
737 |
41 |
if (!dt.isSygus()) |
738 |
|
{ |
739 |
|
return; |
740 |
|
} |
741 |
349 |
for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++) |
742 |
|
{ |
743 |
308 |
const DTypeConstructor& dtc = dt[i]; |
744 |
616 |
Node sop = dtc.getSygusOp(); |
745 |
308 |
bool isVar = std::find(d_vars.begin(), d_vars.end(), sop) != d_vars.end(); |
746 |
308 |
if (isVar) |
747 |
|
{ |
748 |
|
// if it is a variable, add it to the list of sygus types for that var |
749 |
56 |
d_var_sygus_types[sop].push_back(tn); |
750 |
|
} |
751 |
|
else |
752 |
|
{ |
753 |
|
// otherwise, it is a constructor for sygus random value |
754 |
252 |
d_rvalue_cindices[tn].push_back(i); |
755 |
252 |
if (dtc.getNumArgs() == 0) |
756 |
|
{ |
757 |
54 |
d_rvalue_null_cindices[tn].push_back(i); |
758 |
54 |
if (sop.isConst()) |
759 |
|
{ |
760 |
34 |
d_const_sygus_types[sop].push_back(tn); |
761 |
|
} |
762 |
|
} |
763 |
|
} |
764 |
|
// recurse on all subfields |
765 |
654 |
for (unsigned j = 0, nargs = dtc.getNumArgs(); j < nargs; j++) |
766 |
|
{ |
767 |
692 |
TypeNode tnc = d_tds->getArgType(dtc, j); |
768 |
346 |
registerSygusType(tnc); |
769 |
|
} |
770 |
|
} |
771 |
|
} |
772 |
|
} |
773 |
|
|
774 |
1627 |
void SygusSampler::checkEquivalent(Node bv, Node bvr, std::ostream& out) |
775 |
|
{ |
776 |
1627 |
if (bv == bvr) |
777 |
|
{ |
778 |
1124 |
return; |
779 |
|
} |
780 |
2130 |
Trace("sygus-rr-verify") << "Testing rewrite rule " << bv << " ---> " << bvr |
781 |
1065 |
<< std::endl; |
782 |
|
|
783 |
|
// see if they evaluate to same thing on all sample points |
784 |
1065 |
bool ptDisequal = false; |
785 |
1065 |
bool ptDisequalConst = false; |
786 |
1065 |
unsigned pt_index = 0; |
787 |
2130 |
Node bve, bvre; |
788 |
314761 |
for (unsigned i = 0, npoints = getNumSamplePoints(); i < npoints; i++) |
789 |
|
{ |
790 |
313696 |
bve = evaluate(bv, i); |
791 |
313696 |
bvre = evaluate(bvr, i); |
792 |
313696 |
if (bve != bvre) |
793 |
|
{ |
794 |
|
ptDisequal = true; |
795 |
|
pt_index = i; |
796 |
|
if (bve.isConst() && bvre.isConst()) |
797 |
|
{ |
798 |
|
ptDisequalConst = true; |
799 |
|
break; |
800 |
|
} |
801 |
|
} |
802 |
|
} |
803 |
|
// bv and bvr should be equivalent under examples |
804 |
1065 |
if (ptDisequal) |
805 |
|
{ |
806 |
|
std::vector<Node> vars; |
807 |
|
getVariables(vars); |
808 |
|
std::vector<Node> pt; |
809 |
|
getSamplePoint(pt_index, pt); |
810 |
|
Assert(vars.size() == pt.size()); |
811 |
|
std::stringstream ptOut; |
812 |
|
for (unsigned i = 0, size = pt.size(); i < size; i++) |
813 |
|
{ |
814 |
|
ptOut << " " << vars[i] << " -> " << pt[i] << std::endl; |
815 |
|
} |
816 |
|
if (!ptDisequalConst) |
817 |
|
{ |
818 |
|
d_env.verbose(1) |
819 |
|
<< "Warning: " << bv << " and " << bvr |
820 |
|
<< " evaluate to different (non-constant) values on point:" |
821 |
|
<< std::endl; |
822 |
|
d_env.verbose(1) << ptOut.str(); |
823 |
|
return; |
824 |
|
} |
825 |
|
// we have detected unsoundness in the rewriter |
826 |
|
out << "(unsound-rewrite " << bv << " " << bvr << ")" << std::endl; |
827 |
|
// debugging information |
828 |
|
out << "Terms are not equivalent for : " << std::endl; |
829 |
|
out << ptOut.str(); |
830 |
|
Assert(bve != bvre); |
831 |
|
out << "where they evaluate to " << bve << " and " << bvre << std::endl; |
832 |
|
|
833 |
|
if (options::sygusRewVerifyAbort()) |
834 |
|
{ |
835 |
|
AlwaysAssert(false) |
836 |
|
<< "--sygus-rr-verify detected unsoundness in the rewriter!"; |
837 |
|
} |
838 |
|
} |
839 |
|
} |
840 |
|
|
841 |
|
} // namespace quantifiers |
842 |
|
} // namespace theory |
843 |
31137 |
} // namespace cvc5 |