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