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