Information gain heuristic for PBE (#2719)
[cvc5.git] / src / theory / quantifiers / sygus / sygus_unif_io.cpp
1 /********************* */
2 /*! \file sygus_unif_io.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Haniel Barbosa
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2018 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.\endverbatim
11 **
12 ** \brief Implementation of sygus_unif_io
13 **/
14
15 #include "theory/quantifiers/sygus/sygus_unif_io.h"
16
17 #include "options/quantifiers_options.h"
18 #include "theory/datatypes/datatypes_rewriter.h"
19 #include "theory/evaluator.h"
20 #include "theory/quantifiers/sygus/term_database_sygus.h"
21 #include "theory/quantifiers/term_util.h"
22 #include "util/random.h"
23
24 #include <math.h>
25
26 using namespace CVC4::kind;
27
28 namespace CVC4 {
29 namespace theory {
30 namespace quantifiers {
31
32 UnifContextIo::UnifContextIo() : d_curr_role(role_invalid)
33 {
34 d_true = NodeManager::currentNM()->mkConst(true);
35 d_false = NodeManager::currentNM()->mkConst(false);
36 }
37
38 NodeRole UnifContextIo::getCurrentRole() { return d_curr_role; }
39
40 bool UnifContextIo::updateContext(SygusUnifIo* sui,
41 std::vector<Node>& vals,
42 bool pol)
43 {
44 Assert(d_vals.size() == vals.size());
45 bool changed = false;
46 Node poln = pol ? d_true : d_false;
47 for (unsigned i = 0; i < vals.size(); i++)
48 {
49 if (vals[i] != poln)
50 {
51 if (d_vals[i] == d_true)
52 {
53 d_vals[i] = d_false;
54 changed = true;
55 }
56 }
57 }
58 if (changed)
59 {
60 d_visit_role.clear();
61 }
62 return changed;
63 }
64
65 bool UnifContextIo::updateStringPosition(SygusUnifIo* sui,
66 std::vector<unsigned>& pos,
67 NodeRole nrole)
68 {
69 Assert(pos.size() == d_str_pos.size());
70 bool changed = false;
71 for (unsigned i = 0; i < pos.size(); i++)
72 {
73 if (pos[i] > 0)
74 {
75 d_str_pos[i] += pos[i];
76 changed = true;
77 }
78 }
79 if (changed)
80 {
81 d_visit_role.clear();
82 }
83 d_curr_role = nrole;
84 return changed;
85 }
86
87 void UnifContextIo::initialize(SygusUnifIo* sui)
88 {
89 // clear previous data
90 d_vals.clear();
91 d_str_pos.clear();
92 d_curr_role = role_equal;
93 d_visit_role.clear();
94
95 // initialize with #examples
96 unsigned sz = sui->d_examples.size();
97 for (unsigned i = 0; i < sz; i++)
98 {
99 d_vals.push_back(d_true);
100 }
101
102 if (!sui->d_examples_out.empty())
103 {
104 // output type of the examples
105 TypeNode exotn = sui->d_examples_out[0].getType();
106
107 if (exotn.isString())
108 {
109 for (unsigned i = 0; i < sz; i++)
110 {
111 d_str_pos.push_back(0);
112 }
113 }
114 }
115 d_visit_role.clear();
116 }
117
118 void UnifContextIo::getCurrentStrings(SygusUnifIo* sui,
119 const std::vector<Node>& vals,
120 std::vector<String>& ex_vals)
121 {
122 bool isPrefix = d_curr_role == role_string_prefix;
123 String dummy;
124 for (unsigned i = 0; i < vals.size(); i++)
125 {
126 if (d_vals[i] == sui->d_true)
127 {
128 Assert(vals[i].isConst());
129 unsigned pos_value = d_str_pos[i];
130 if (pos_value > 0)
131 {
132 Assert(d_curr_role != role_invalid);
133 String s = vals[i].getConst<String>();
134 Assert(pos_value <= s.size());
135 ex_vals.push_back(isPrefix ? s.suffix(s.size() - pos_value)
136 : s.prefix(s.size() - pos_value));
137 }
138 else
139 {
140 ex_vals.push_back(vals[i].getConst<String>());
141 }
142 }
143 else
144 {
145 // irrelevant, add dummy
146 ex_vals.push_back(dummy);
147 }
148 }
149 }
150
151 bool UnifContextIo::getStringIncrement(SygusUnifIo* sui,
152 bool isPrefix,
153 const std::vector<String>& ex_vals,
154 const std::vector<Node>& vals,
155 std::vector<unsigned>& inc,
156 unsigned& tot)
157 {
158 for (unsigned j = 0; j < vals.size(); j++)
159 {
160 unsigned ival = 0;
161 if (d_vals[j] == sui->d_true)
162 {
163 // example is active in this context
164 Assert(vals[j].isConst());
165 String mystr = vals[j].getConst<String>();
166 ival = mystr.size();
167 if (mystr.size() <= ex_vals[j].size())
168 {
169 if (!(isPrefix ? ex_vals[j].strncmp(mystr, ival)
170 : ex_vals[j].rstrncmp(mystr, ival)))
171 {
172 Trace("sygus-sui-dt-debug") << "X";
173 return false;
174 }
175 }
176 else
177 {
178 Trace("sygus-sui-dt-debug") << "X";
179 return false;
180 }
181 Trace("sygus-sui-dt-debug") << ival;
182 tot += ival;
183 }
184 else
185 {
186 // inactive in this context
187 Trace("sygus-sui-dt-debug") << "-";
188 }
189 inc.push_back(ival);
190 }
191 return true;
192 }
193 bool UnifContextIo::isStringSolved(SygusUnifIo* sui,
194 const std::vector<String>& ex_vals,
195 const std::vector<Node>& vals)
196 {
197 for (unsigned j = 0; j < vals.size(); j++)
198 {
199 if (d_vals[j] == sui->d_true)
200 {
201 // example is active in this context
202 Assert(vals[j].isConst());
203 String mystr = vals[j].getConst<String>();
204 if (ex_vals[j] != mystr)
205 {
206 return false;
207 }
208 }
209 }
210 return true;
211 }
212
213 // status : 0 : exact, -1 : vals is subset, 1 : vals is superset
214 Node SubsumeTrie::addTermInternal(Node t,
215 const std::vector<Node>& vals,
216 bool pol,
217 std::vector<Node>& subsumed,
218 bool spol,
219 unsigned index,
220 int status,
221 bool checkExistsOnly,
222 bool checkSubsume)
223 {
224 if (index == vals.size())
225 {
226 if (status == 0)
227 {
228 // set the term if checkExistsOnly = false
229 if (d_term.isNull() && !checkExistsOnly)
230 {
231 d_term = t;
232 }
233 }
234 else if (status == 1)
235 {
236 Assert(checkSubsume);
237 // found a subsumed term
238 if (!d_term.isNull())
239 {
240 subsumed.push_back(d_term);
241 if (!checkExistsOnly)
242 {
243 // remove it if checkExistsOnly = false
244 d_term = Node::null();
245 }
246 }
247 }
248 else
249 {
250 Assert(!checkExistsOnly && checkSubsume);
251 }
252 return d_term;
253 }
254 NodeManager* nm = NodeManager::currentNM();
255 // the current value
256 Assert(pol || (vals[index].isConst() && vals[index].getType().isBoolean()));
257 Node cv = pol ? vals[index] : nm->mkConst(!vals[index].getConst<bool>());
258 // if checkExistsOnly = false, check if the current value is subsumed if
259 // checkSubsume = true, if so, don't add
260 if (!checkExistsOnly && checkSubsume)
261 {
262 Assert(cv.isConst() && cv.getType().isBoolean());
263 std::vector<bool> check_subsumed_by;
264 if (status == 0)
265 {
266 if (!cv.getConst<bool>())
267 {
268 check_subsumed_by.push_back(spol);
269 }
270 }
271 else if (status == -1)
272 {
273 check_subsumed_by.push_back(spol);
274 if (!cv.getConst<bool>())
275 {
276 check_subsumed_by.push_back(!spol);
277 }
278 }
279 // check for subsumed nodes
280 for (unsigned i = 0, size = check_subsumed_by.size(); i < size; i++)
281 {
282 bool csbi = check_subsumed_by[i];
283 Node csval = nm->mkConst(csbi);
284 // check if subsumed
285 std::map<Node, SubsumeTrie>::iterator itc = d_children.find(csval);
286 if (itc != d_children.end())
287 {
288 Node ret = itc->second.addTermInternal(t,
289 vals,
290 pol,
291 subsumed,
292 spol,
293 index + 1,
294 -1,
295 checkExistsOnly,
296 checkSubsume);
297 // ret subsumes t
298 if (!ret.isNull())
299 {
300 return ret;
301 }
302 }
303 }
304 }
305 Node ret;
306 std::vector<bool> check_subsume;
307 if (status == 0)
308 {
309 if (checkExistsOnly)
310 {
311 std::map<Node, SubsumeTrie>::iterator itc = d_children.find(cv);
312 if (itc != d_children.end())
313 {
314 ret = itc->second.addTermInternal(t,
315 vals,
316 pol,
317 subsumed,
318 spol,
319 index + 1,
320 0,
321 checkExistsOnly,
322 checkSubsume);
323 }
324 }
325 else
326 {
327 Assert(spol);
328 ret = d_children[cv].addTermInternal(t,
329 vals,
330 pol,
331 subsumed,
332 spol,
333 index + 1,
334 0,
335 checkExistsOnly,
336 checkSubsume);
337 if (ret != t)
338 {
339 // we were subsumed by ret, return
340 return ret;
341 }
342 }
343 if (checkSubsume)
344 {
345 Assert(cv.isConst() && cv.getType().isBoolean());
346 // check for subsuming
347 if (cv.getConst<bool>())
348 {
349 check_subsume.push_back(!spol);
350 }
351 }
352 }
353 else if (status == 1)
354 {
355 Assert(checkSubsume);
356 Assert(cv.isConst() && cv.getType().isBoolean());
357 check_subsume.push_back(!spol);
358 if (cv.getConst<bool>())
359 {
360 check_subsume.push_back(spol);
361 }
362 }
363 if (checkSubsume)
364 {
365 // check for subsumed terms
366 for (unsigned i = 0, size = check_subsume.size(); i < size; i++)
367 {
368 Node csval = nm->mkConst<bool>(check_subsume[i]);
369 std::map<Node, SubsumeTrie>::iterator itc = d_children.find(csval);
370 if (itc != d_children.end())
371 {
372 itc->second.addTermInternal(t,
373 vals,
374 pol,
375 subsumed,
376 spol,
377 index + 1,
378 1,
379 checkExistsOnly,
380 checkSubsume);
381 // clean up
382 if (itc->second.isEmpty())
383 {
384 Assert(!checkExistsOnly);
385 d_children.erase(csval);
386 }
387 }
388 }
389 }
390 return ret;
391 }
392
393 Node SubsumeTrie::addTerm(Node t,
394 const std::vector<Node>& vals,
395 bool pol,
396 std::vector<Node>& subsumed)
397 {
398 return addTermInternal(t, vals, pol, subsumed, true, 0, 0, false, true);
399 }
400
401 Node SubsumeTrie::addCond(Node c, const std::vector<Node>& vals, bool pol)
402 {
403 std::vector<Node> subsumed;
404 return addTermInternal(c, vals, pol, subsumed, true, 0, 0, false, false);
405 }
406
407 void SubsumeTrie::getSubsumed(const std::vector<Node>& vals,
408 bool pol,
409 std::vector<Node>& subsumed)
410 {
411 addTermInternal(Node::null(), vals, pol, subsumed, true, 0, 1, true, true);
412 }
413
414 void SubsumeTrie::getSubsumedBy(const std::vector<Node>& vals,
415 bool pol,
416 std::vector<Node>& subsumed_by)
417 {
418 // flip polarities
419 addTermInternal(
420 Node::null(), vals, !pol, subsumed_by, false, 0, 1, true, true);
421 }
422
423 void SubsumeTrie::getLeavesInternal(const std::vector<Node>& vals,
424 bool pol,
425 std::map<int, std::vector<Node> >& v,
426 unsigned index,
427 int status)
428 {
429 if (index == vals.size())
430 {
431 Assert(!d_term.isNull());
432 Assert(std::find(v[status].begin(), v[status].end(), d_term)
433 == v[status].end());
434 v[status].push_back(d_term);
435 }
436 else
437 {
438 Assert(vals[index].isConst() && vals[index].getType().isBoolean());
439 bool curr_val_true = vals[index].getConst<bool>() == pol;
440 for (std::map<Node, SubsumeTrie>::iterator it = d_children.begin();
441 it != d_children.end();
442 ++it)
443 {
444 int new_status = status;
445 // if the current value is true
446 if (curr_val_true)
447 {
448 if (status != 0)
449 {
450 Assert(it->first.isConst() && it->first.getType().isBoolean());
451 new_status = (it->first.getConst<bool>() ? 1 : -1);
452 if (status != -2 && new_status != status)
453 {
454 new_status = 0;
455 }
456 }
457 }
458 it->second.getLeavesInternal(vals, pol, v, index + 1, new_status);
459 }
460 }
461 }
462
463 void SubsumeTrie::getLeaves(const std::vector<Node>& vals,
464 bool pol,
465 std::map<int, std::vector<Node> >& v)
466 {
467 getLeavesInternal(vals, pol, v, 0, -2);
468 }
469
470 SygusUnifIo::SygusUnifIo()
471 : d_check_sol(false),
472 d_cond_count(0),
473 d_sol_cons_nondet(false),
474 d_solConsUsingInfoGain(false)
475 {
476 d_true = NodeManager::currentNM()->mkConst(true);
477 d_false = NodeManager::currentNM()->mkConst(false);
478 }
479
480 SygusUnifIo::~SygusUnifIo() {}
481 void SygusUnifIo::initializeCandidate(
482 QuantifiersEngine* qe,
483 Node f,
484 std::vector<Node>& enums,
485 std::map<Node, std::vector<Node>>& strategy_lemmas)
486 {
487 d_examples.clear();
488 d_examples_out.clear();
489 d_ecache.clear();
490 d_candidate = f;
491 SygusUnif::initializeCandidate(qe, f, enums, strategy_lemmas);
492 // learn redundant operators based on the strategy
493 d_strategy[f].staticLearnRedundantOps(strategy_lemmas);
494 }
495
496 void SygusUnifIo::addExample(const std::vector<Node>& input, Node output)
497 {
498 d_examples.push_back(input);
499 d_examples_out.push_back(output);
500 }
501
502 void SygusUnifIo::computeExamples(Node e, Node bv, std::vector<Node>& exOut)
503 {
504 std::map<Node, std::vector<Node>>& eoc = d_exOutCache[e];
505 std::map<Node, std::vector<Node>>::iterator it = eoc.find(bv);
506 if (it != eoc.end())
507 {
508 exOut.insert(exOut.end(), it->second.begin(), it->second.end());
509 return;
510 }
511 TypeNode xtn = e.getType();
512 std::vector<Node>& eocv = eoc[bv];
513 for (size_t j = 0, size = d_examples.size(); j < size; j++)
514 {
515 Node res = d_tds->evaluateBuiltin(xtn, bv, d_examples[j]);
516 exOut.push_back(res);
517 eocv.push_back(res);
518 }
519 }
520
521 void SygusUnifIo::clearExampleCache(Node e, Node bv)
522 {
523 std::map<Node, std::vector<Node>>& eoc = d_exOutCache[e];
524 Assert(eoc.find(bv) != eoc.end());
525 eoc.erase(bv);
526 }
527
528 void SygusUnifIo::notifyEnumeration(Node e, Node v, std::vector<Node>& lemmas)
529 {
530 Trace("sygus-sui-enum") << "Notify enumeration for " << e << " : " << v
531 << std::endl;
532 Node c = d_candidate;
533 Assert(!d_examples.empty());
534 Assert(d_examples.size() == d_examples_out.size());
535
536 EnumInfo& ei = d_strategy[c].getEnumInfo(e);
537 // The explanation for why the current value should be excluded in future
538 // iterations.
539 Node exp_exc;
540
541 std::vector<Node> base_results;
542 TypeNode xtn = e.getType();
543 Node bv = d_tds->sygusToBuiltin(v, xtn);
544 bv = d_tds->getExtRewriter()->extendedRewrite(bv);
545 Trace("sygus-sui-enum") << "PBE Compute Examples for " << bv << std::endl;
546 // compte the results (should be cached)
547 computeExamples(e, bv, base_results);
548 // don't need it after this
549 clearExampleCache(e, bv);
550 // get the results for each slave enumerator
551 std::map<Node, std::vector<Node>> srmap;
552 Evaluator* ev = d_tds->getEvaluator();
553 bool tryEval = options::sygusEvalOpt();
554 for (const Node& xs : ei.d_enum_slave)
555 {
556 Assert(srmap.find(xs) == srmap.end());
557 EnumInfo& eiv = d_strategy[c].getEnumInfo(xs);
558 Node templ = eiv.d_template;
559 if (!templ.isNull())
560 {
561 TNode templ_var = eiv.d_template_arg;
562 std::vector<Node> args;
563 args.push_back(templ_var);
564 std::vector<Node> sresults;
565 for (const Node& res : base_results)
566 {
567 TNode tres = res;
568 std::vector<Node> vals;
569 vals.push_back(tres);
570 Node sres;
571 if (tryEval)
572 {
573 sres = ev->eval(templ, args, vals);
574 }
575 if (sres.isNull())
576 {
577 // fall back on rewriter
578 sres = templ.substitute(templ_var, tres);
579 sres = Rewriter::rewrite(sres);
580 }
581 sresults.push_back(sres);
582 }
583 srmap[xs] = sresults;
584 }
585 else
586 {
587 srmap[xs] = base_results;
588 }
589 }
590
591 // is it excluded for domain-specific reason?
592 std::vector<Node> exp_exc_vec;
593 Assert(d_tds->isEnumerator(e));
594 bool isPassive = d_tds->isPassiveEnumerator(e);
595 if (getExplanationForEnumeratorExclude(e, v, base_results, exp_exc_vec))
596 {
597 if (isPassive)
598 {
599 Assert(!exp_exc_vec.empty());
600 exp_exc = exp_exc_vec.size() == 1
601 ? exp_exc_vec[0]
602 : NodeManager::currentNM()->mkNode(AND, exp_exc_vec);
603 }
604 Trace("sygus-sui-enum")
605 << " ...fail : term is excluded (domain-specific)" << std::endl;
606 }
607 else
608 {
609 // notify all slaves
610 Assert(!ei.d_enum_slave.empty());
611 // explanation for why this value should be excluded
612 for (unsigned s = 0; s < ei.d_enum_slave.size(); s++)
613 {
614 Node xs = ei.d_enum_slave[s];
615 EnumInfo& eiv = d_strategy[c].getEnumInfo(xs);
616 EnumCache& ecv = d_ecache[xs];
617 Trace("sygus-sui-enum") << "Process " << xs << " from " << s << std::endl;
618 // bool prevIsCover = false;
619 if (eiv.getRole() == enum_io)
620 {
621 Trace("sygus-sui-enum") << " IO-Eval of ";
622 // prevIsCover = eiv.isFeasible();
623 }
624 else
625 {
626 Trace("sygus-sui-enum") << "Evaluation of ";
627 }
628 Trace("sygus-sui-enum") << xs << " : ";
629 // evaluate all input/output examples
630 std::vector<Node> results;
631 std::map<Node, bool> cond_vals;
632 std::map<Node, std::vector<Node>>::iterator itsr = srmap.find(xs);
633 Assert(itsr != srmap.end());
634 for (unsigned j = 0, size = itsr->second.size(); j < size; j++)
635 {
636 Node res = itsr->second[j];
637 Assert(res.isConst());
638 Node resb;
639 if (eiv.getRole() == enum_io)
640 {
641 Node out = d_examples_out[j];
642 Assert(out.isConst());
643 resb = res == out ? d_true : d_false;
644 }
645 else
646 {
647 resb = res;
648 }
649 cond_vals[resb] = true;
650 results.push_back(resb);
651 if (Trace.isOn("sygus-sui-enum"))
652 {
653 if (resb.getType().isBoolean())
654 {
655 Trace("sygus-sui-enum") << (resb == d_true ? "1" : "0");
656 }
657 else
658 {
659 Trace("sygus-sui-enum") << "?";
660 }
661 }
662 }
663 bool keep = false;
664 if (eiv.getRole() == enum_io)
665 {
666 // latter is the degenerate case of no examples
667 if (cond_vals.find(d_true) != cond_vals.end() || cond_vals.empty())
668 {
669 // check subsumbed/subsuming
670 std::vector<Node> subsume;
671 if (cond_vals.find(d_false) == cond_vals.end())
672 {
673 // it is the entire solution, we are done
674 Trace("sygus-sui-enum")
675 << " ...success, full solution added to PBE pool : "
676 << d_tds->sygusToBuiltin(v) << std::endl;
677 if (!ecv.isSolved())
678 {
679 ecv.setSolved(v);
680 // it subsumes everything
681 ecv.d_term_trie.clear();
682 ecv.d_term_trie.addTerm(v, results, true, subsume);
683 }
684 keep = true;
685 }
686 else
687 {
688 Node val = ecv.d_term_trie.addTerm(v, results, true, subsume);
689 if (val == v)
690 {
691 Trace("sygus-sui-enum") << " ...success";
692 if (!subsume.empty())
693 {
694 ecv.d_enum_subsume.insert(
695 ecv.d_enum_subsume.end(), subsume.begin(), subsume.end());
696 Trace("sygus-sui-enum")
697 << " and subsumed " << subsume.size() << " terms";
698 }
699 Trace("sygus-sui-enum")
700 << "! add to PBE pool : " << d_tds->sygusToBuiltin(v)
701 << std::endl;
702 keep = true;
703 }
704 else
705 {
706 Assert(subsume.empty());
707 Trace("sygus-sui-enum") << " ...fail : subsumed" << std::endl;
708 }
709 }
710 }
711 else
712 {
713 Trace("sygus-sui-enum")
714 << " ...fail : it does not satisfy examples." << std::endl;
715 }
716 }
717 else
718 {
719 // must be unique up to examples
720 Node val = ecv.d_term_trie.addCond(v, results, true);
721 if (val == v)
722 {
723 Trace("sygus-sui-enum") << " ...success! add to PBE pool : "
724 << d_tds->sygusToBuiltin(v) << std::endl;
725 keep = true;
726 }
727 else
728 {
729 Trace("sygus-sui-enum")
730 << " ...fail : term is not unique" << std::endl;
731 }
732 }
733 if (keep)
734 {
735 // notify to retry the build of solution
736 d_check_sol = true;
737 d_cond_count++;
738 ecv.addEnumValue(v, results);
739 }
740 }
741 }
742
743 if (isPassive)
744 {
745 // exclude this value on subsequent iterations
746 if (exp_exc.isNull())
747 {
748 Trace("sygus-sui-enum-lemma") << "Use basic exclusion." << std::endl;
749 // if we did not already explain why this should be excluded, use default
750 exp_exc = d_tds->getExplain()->getExplanationForEquality(e, v);
751 }
752 exp_exc = exp_exc.negate();
753 Trace("sygus-sui-enum-lemma")
754 << "SygusUnifIo : enumeration exclude lemma : " << exp_exc << std::endl;
755 lemmas.push_back(exp_exc);
756 }
757 }
758
759 bool SygusUnifIo::constructSolution(std::vector<Node>& sols,
760 std::vector<Node>& lemmas)
761 {
762 Node sol = constructSolutionNode(lemmas);
763 if (!sol.isNull())
764 {
765 sols.push_back(sol);
766 return true;
767 }
768 return false;
769 }
770
771 Node SygusUnifIo::constructSolutionNode(std::vector<Node>& lemmas)
772 {
773 Node c = d_candidate;
774 if (!d_solution.isNull())
775 {
776 // already has a solution
777 return d_solution;
778 }
779 // only check if an enumerator updated
780 if (d_check_sol)
781 {
782 Trace("sygus-pbe") << "Construct solution, #iterations = " << d_cond_count
783 << std::endl;
784 d_check_sol = false;
785 d_solConsUsingInfoGain = false;
786 // try multiple times if we have done multiple conditions, due to
787 // non-determinism
788 unsigned sol_term_size = 0;
789 for (unsigned i = 0; i <= d_cond_count; i++)
790 {
791 Trace("sygus-pbe-dt") << "ConstructPBE for candidate: " << c << std::endl;
792 // initialize a call to construct solution
793 initializeConstructSol();
794 initializeConstructSolFor(c);
795 // call the virtual construct solution method
796 Node e = d_strategy[c].getRootEnumerator();
797 Node vcc = constructSol(c, e, role_equal, 1, lemmas);
798 // if we constructed the solution, and we either did not previously have
799 // a solution, or the new solution is better (smaller).
800 if (!vcc.isNull()
801 && (d_solution.isNull()
802 || (!d_solution.isNull()
803 && d_tds->getSygusTermSize(vcc) < sol_term_size)))
804 {
805 Trace("sygus-pbe") << "**** SygusUnif SOLVED : " << c << " = " << vcc
806 << std::endl;
807 Trace("sygus-pbe") << "...solved at iteration " << i << std::endl;
808 d_solution = vcc;
809 sol_term_size = d_tds->getSygusTermSize(vcc);
810 // We've determined its feasible, now, enable information gain and
811 // retry. We do this since information gain comes with an overhead,
812 // and we want testing feasibility to be fast.
813 if (!d_solConsUsingInfoGain)
814 {
815 d_solConsUsingInfoGain = true;
816 i = 0;
817 }
818 }
819 else if (!d_sol_cons_nondet)
820 {
821 break;
822 }
823 }
824 if (!d_solution.isNull())
825 {
826 return d_solution;
827 }
828 Trace("sygus-pbe") << "...failed to solve." << std::endl;
829 }
830 return Node::null();
831 }
832
833 // ------------------------------------ solution construction from enumeration
834
835 bool SygusUnifIo::useStrContainsEnumeratorExclude(Node e)
836 {
837 TypeNode xbt = d_tds->sygusToBuiltinType(e.getType());
838 if (xbt.isString())
839 {
840 std::map<Node, bool>::iterator itx = d_use_str_contains_eexc.find(e);
841 if (itx != d_use_str_contains_eexc.end())
842 {
843 return itx->second;
844 }
845 Trace("sygus-sui-enum-debug")
846 << "Is " << e << " is str.contains exclusion?" << std::endl;
847 d_use_str_contains_eexc[e] = true;
848 Node c = d_candidate;
849 EnumInfo& ei = d_strategy[c].getEnumInfo(e);
850 for (const Node& sn : ei.d_enum_slave)
851 {
852 EnumInfo& eis = d_strategy[c].getEnumInfo(sn);
853 EnumRole er = eis.getRole();
854 if (er != enum_io && er != enum_concat_term)
855 {
856 Trace("sygus-sui-enum-debug") << " incompatible slave : " << sn
857 << ", role = " << er << std::endl;
858 d_use_str_contains_eexc[e] = false;
859 return false;
860 }
861 if (eis.isConditional())
862 {
863 Trace("sygus-sui-enum-debug")
864 << " conditional slave : " << sn << std::endl;
865 d_use_str_contains_eexc[e] = false;
866 return false;
867 }
868 }
869 Trace("sygus-sui-enum-debug")
870 << "...can use str.contains exclusion." << std::endl;
871 return d_use_str_contains_eexc[e];
872 }
873 return false;
874 }
875
876 bool SygusUnifIo::getExplanationForEnumeratorExclude(
877 Node e,
878 Node v,
879 std::vector<Node>& results,
880 std::vector<Node>& exp)
881 {
882 NodeManager* nm = NodeManager::currentNM();
883 if (useStrContainsEnumeratorExclude(e))
884 {
885 // This check whether the example evaluates to something that is larger than
886 // the output for some input/output pair. If so, then this term is never
887 // useful. We generalize its explanation below.
888
889 if (Trace.isOn("sygus-sui-cterm-debug"))
890 {
891 Trace("sygus-sui-enum") << std::endl;
892 }
893 // check if all examples had longer length that the output
894 Assert(d_examples_out.size() == results.size());
895 Trace("sygus-sui-cterm-debug")
896 << "Check enumerator exclusion for " << e << " -> "
897 << d_tds->sygusToBuiltin(v) << " based on str.contains." << std::endl;
898 std::vector<unsigned> cmp_indices;
899 for (unsigned i = 0, size = results.size(); i < size; i++)
900 {
901 Assert(results[i].isConst());
902 Assert(d_examples_out[i].isConst());
903 Trace("sygus-sui-cterm-debug")
904 << " " << results[i] << " <> " << d_examples_out[i];
905 Node cont = nm->mkNode(STRING_STRCTN, d_examples_out[i], results[i]);
906 Node contr = Rewriter::rewrite(cont);
907 if (contr == d_false)
908 {
909 cmp_indices.push_back(i);
910 Trace("sygus-sui-cterm-debug") << "...not contained." << std::endl;
911 }
912 else
913 {
914 Trace("sygus-sui-cterm-debug") << "...contained." << std::endl;
915 }
916 }
917 if (!cmp_indices.empty())
918 {
919 // we check invariance with respect to a negative contains test
920 NegContainsSygusInvarianceTest ncset;
921 ncset.init(e, d_examples, d_examples_out, cmp_indices);
922 // construct the generalized explanation
923 d_tds->getExplain()->getExplanationFor(e, v, exp, ncset);
924 Trace("sygus-sui-cterm")
925 << "PBE-cterm : enumerator exclude " << d_tds->sygusToBuiltin(v)
926 << " due to negative containment." << std::endl;
927 return true;
928 }
929 }
930 return false;
931 }
932
933 void SygusUnifIo::EnumCache::addEnumValue(Node v, std::vector<Node>& results)
934 {
935 // should not have been enumerated before
936 Assert(d_enum_val_to_index.find(v) == d_enum_val_to_index.end());
937 d_enum_val_to_index[v] = d_enum_vals.size();
938 d_enum_vals.push_back(v);
939 d_enum_vals_res.push_back(results);
940 }
941
942 void SygusUnifIo::initializeConstructSol()
943 {
944 d_context.initialize(this);
945 d_sol_cons_nondet = false;
946 }
947
948 void SygusUnifIo::initializeConstructSolFor(Node f)
949 {
950 Assert(d_candidate == f);
951 }
952
953 Node SygusUnifIo::constructSol(
954 Node f, Node e, NodeRole nrole, int ind, std::vector<Node>& lemmas)
955 {
956 Assert(d_candidate == f);
957 UnifContextIo& x = d_context;
958 TypeNode etn = e.getType();
959 if (Trace.isOn("sygus-sui-dt-debug"))
960 {
961 indent("sygus-sui-dt-debug", ind);
962 Trace("sygus-sui-dt-debug") << "ConstructPBE: (" << e << ", " << nrole
963 << ") for type " << etn << " in context ";
964 print_val("sygus-sui-dt-debug", x.d_vals);
965 NodeRole ctx_role = x.getCurrentRole();
966 Trace("sygus-sui-dt-debug") << ", context role [" << ctx_role;
967 if (ctx_role == role_string_prefix || ctx_role == role_string_suffix)
968 {
969 Trace("sygus-sui-dt-debug") << ", string pos : ";
970 for (unsigned i = 0, size = x.d_str_pos.size(); i < size; i++)
971 {
972 Trace("sygus-sui-dt-debug") << " " << x.d_str_pos[i];
973 }
974 }
975 Trace("sygus-sui-dt-debug") << "]";
976 Trace("sygus-sui-dt-debug") << std::endl;
977 }
978 // enumerator type info
979 EnumTypeInfo& tinfo = d_strategy[f].getEnumTypeInfo(etn);
980
981 // get the enumerator information
982 EnumInfo& einfo = d_strategy[f].getEnumInfo(e);
983
984 EnumCache& ecache = d_ecache[e];
985
986 Node ret_dt;
987 if (nrole == role_equal)
988 {
989 if (!x.isReturnValueModified())
990 {
991 if (ecache.isSolved())
992 {
993 // this type has a complete solution
994 ret_dt = ecache.getSolved();
995 indent("sygus-sui-dt", ind);
996 Trace("sygus-sui-dt") << "return PBE: success : solved "
997 << d_tds->sygusToBuiltin(ret_dt) << std::endl;
998 Assert(!ret_dt.isNull());
999 }
1000 else
1001 {
1002 // could be conditionally solved
1003 std::vector<Node> subsumed_by;
1004 ecache.d_term_trie.getSubsumedBy(x.d_vals, true, subsumed_by);
1005 if (!subsumed_by.empty())
1006 {
1007 ret_dt = constructBestSolvedTerm(e, subsumed_by);
1008 indent("sygus-sui-dt", ind);
1009 Trace("sygus-sui-dt") << "return PBE: success : conditionally solved"
1010 << d_tds->sygusToBuiltin(ret_dt) << std::endl;
1011 }
1012 else
1013 {
1014 indent("sygus-sui-dt-debug", ind);
1015 Trace("sygus-sui-dt-debug")
1016 << " ...not currently conditionally solved." << std::endl;
1017 }
1018 }
1019 }
1020 if (ret_dt.isNull())
1021 {
1022 if (d_tds->sygusToBuiltinType(e.getType()).isString())
1023 {
1024 // check if a current value that closes all examples
1025 // get the term enumerator for this type
1026 std::map<EnumRole, Node>::iterator itnt =
1027 tinfo.d_enum.find(enum_concat_term);
1028 if (itnt != tinfo.d_enum.end())
1029 {
1030 Node et = itnt->second;
1031
1032 EnumCache& ecachet = d_ecache[et];
1033 // get the current examples
1034 std::vector<String> ex_vals;
1035 x.getCurrentStrings(this, d_examples_out, ex_vals);
1036 Assert(ecache.d_enum_vals.size() == ecache.d_enum_vals_res.size());
1037
1038 // test each example in the term enumerator for the type
1039 std::vector<Node> str_solved;
1040 for (unsigned i = 0, size = ecachet.d_enum_vals.size(); i < size; i++)
1041 {
1042 if (x.isStringSolved(this, ex_vals, ecachet.d_enum_vals_res[i]))
1043 {
1044 str_solved.push_back(ecachet.d_enum_vals[i]);
1045 }
1046 }
1047 if (!str_solved.empty())
1048 {
1049 ret_dt = constructBestSolvedTerm(e, str_solved);
1050 indent("sygus-sui-dt", ind);
1051 Trace("sygus-sui-dt") << "return PBE: success : string solved "
1052 << d_tds->sygusToBuiltin(ret_dt) << std::endl;
1053 }
1054 else
1055 {
1056 indent("sygus-sui-dt-debug", ind);
1057 Trace("sygus-sui-dt-debug")
1058 << " ...not currently string solved." << std::endl;
1059 }
1060 }
1061 }
1062 }
1063 }
1064 else if (nrole == role_string_prefix || nrole == role_string_suffix)
1065 {
1066 // check if each return value is a prefix/suffix of all open examples
1067 if (!x.isReturnValueModified() || x.getCurrentRole() == nrole)
1068 {
1069 std::map<Node, std::vector<unsigned> > incr;
1070 bool isPrefix = nrole == role_string_prefix;
1071 std::map<Node, unsigned> total_inc;
1072 std::vector<Node> inc_strs;
1073 // make the value of the examples
1074 std::vector<String> ex_vals;
1075 x.getCurrentStrings(this, d_examples_out, ex_vals);
1076 if (Trace.isOn("sygus-sui-dt-debug"))
1077 {
1078 indent("sygus-sui-dt-debug", ind);
1079 Trace("sygus-sui-dt-debug") << "current strings : " << std::endl;
1080 for (unsigned i = 0, size = ex_vals.size(); i < size; i++)
1081 {
1082 indent("sygus-sui-dt-debug", ind + 1);
1083 Trace("sygus-sui-dt-debug") << ex_vals[i] << std::endl;
1084 }
1085 }
1086
1087 // check if there is a value for which is a prefix/suffix of all active
1088 // examples
1089 Assert(ecache.d_enum_vals.size() == ecache.d_enum_vals_res.size());
1090
1091 for (unsigned i = 0, size = ecache.d_enum_vals.size(); i < size; i++)
1092 {
1093 Node val_t = ecache.d_enum_vals[i];
1094 Assert(incr.find(val_t) == incr.end());
1095 indent("sygus-sui-dt-debug", ind);
1096 Trace("sygus-sui-dt-debug") << "increment string values : ";
1097 TermDbSygus::toStreamSygus("sygus-sui-dt-debug", val_t);
1098 Trace("sygus-sui-dt-debug") << " : ";
1099 Assert(ecache.d_enum_vals_res[i].size() == d_examples_out.size());
1100 unsigned tot = 0;
1101 bool exsuccess = x.getStringIncrement(this,
1102 isPrefix,
1103 ex_vals,
1104 ecache.d_enum_vals_res[i],
1105 incr[val_t],
1106 tot);
1107 if (!exsuccess)
1108 {
1109 incr.erase(val_t);
1110 Trace("sygus-sui-dt-debug") << "...fail" << std::endl;
1111 }
1112 else
1113 {
1114 total_inc[val_t] = tot;
1115 inc_strs.push_back(val_t);
1116 Trace("sygus-sui-dt-debug")
1117 << "...success, total increment = " << tot << std::endl;
1118 }
1119 }
1120
1121 if (!incr.empty())
1122 {
1123 // solution construction for strings concatenation is non-deterministic
1124 // with respect to failure/success.
1125 d_sol_cons_nondet = true;
1126 ret_dt = constructBestStringToConcat(inc_strs, total_inc, incr);
1127 Assert(!ret_dt.isNull());
1128 indent("sygus-sui-dt", ind);
1129 Trace("sygus-sui-dt")
1130 << "PBE: CONCAT strategy : choose " << (isPrefix ? "pre" : "suf")
1131 << "fix value " << d_tds->sygusToBuiltin(ret_dt) << std::endl;
1132 // update the context
1133 bool ret = x.updateStringPosition(this, incr[ret_dt], nrole);
1134 AlwaysAssert(ret == (total_inc[ret_dt] > 0));
1135 }
1136 else
1137 {
1138 indent("sygus-sui-dt", ind);
1139 Trace("sygus-sui-dt") << "PBE: failed CONCAT strategy, no values are "
1140 << (isPrefix ? "pre" : "suf")
1141 << "fix of all examples." << std::endl;
1142 }
1143 }
1144 else
1145 {
1146 indent("sygus-sui-dt", ind);
1147 Trace("sygus-sui-dt")
1148 << "PBE: failed CONCAT strategy, prefix/suffix mismatch."
1149 << std::endl;
1150 }
1151 }
1152 if (!ret_dt.isNull() || einfo.isTemplated())
1153 {
1154 Assert(ret_dt.isNull() || ret_dt.getType() == e.getType());
1155 indent("sygus-sui-dt", ind);
1156 Trace("sygus-sui-dt") << "ConstructPBE: returned (pre-strategy) " << ret_dt
1157 << std::endl;
1158 return ret_dt;
1159 }
1160 // we will try a single strategy
1161 EnumTypeInfoStrat* etis = nullptr;
1162 std::map<NodeRole, StrategyNode>::iterator itsn = tinfo.d_snodes.find(nrole);
1163 if (itsn == tinfo.d_snodes.end())
1164 {
1165 indent("sygus-sui-dt", ind);
1166 Trace("sygus-sui-dt") << "ConstructPBE: returned (no-strategy) " << ret_dt
1167 << std::endl;
1168 return ret_dt;
1169 }
1170 // strategy info
1171 StrategyNode& snode = itsn->second;
1172 if (x.d_visit_role[e].find(nrole) != x.d_visit_role[e].end())
1173 {
1174 // already visited and context not changed (notice d_visit_role is cleared
1175 // when the context changes).
1176 indent("sygus-sui-dt", ind);
1177 Trace("sygus-sui-dt") << "ConstructPBE: returned (already visited) "
1178 << ret_dt << std::endl;
1179 return ret_dt;
1180 }
1181 x.d_visit_role[e][nrole] = true;
1182 // try a random strategy
1183 if (snode.d_strats.size() > 1)
1184 {
1185 std::shuffle(
1186 snode.d_strats.begin(), snode.d_strats.end(), Random::getRandom());
1187 }
1188 // ITE always first if we have not yet solved
1189 // the reasoning is that splitting on conditions only subdivides the problem
1190 // and cannot be the source of failure, whereas the wrong choice for a
1191 // concatenation term may lead to failure
1192 if (d_solution.isNull())
1193 {
1194 for (unsigned i = 0; i < snode.d_strats.size(); i++)
1195 {
1196 if (snode.d_strats[i]->d_this == strat_ITE)
1197 {
1198 // flip the two
1199 EnumTypeInfoStrat* etis = snode.d_strats[i];
1200 snode.d_strats[i] = snode.d_strats[0];
1201 snode.d_strats[0] = etis;
1202 break;
1203 }
1204 }
1205 }
1206
1207 // iterate over the strategies
1208 unsigned sindex = 0;
1209 bool did_recurse = false;
1210 while (ret_dt.isNull() && !did_recurse && sindex < snode.d_strats.size())
1211 {
1212 if (snode.d_strats[sindex]->isValid(x))
1213 {
1214 etis = snode.d_strats[sindex];
1215 Assert(etis != nullptr);
1216 StrategyType strat = etis->d_this;
1217 indent("sygus-sui-dt", ind + 1);
1218 Trace("sygus-sui-dt")
1219 << "...try STRATEGY " << strat << "..." << std::endl;
1220
1221 std::map<unsigned, Node> look_ahead_solved_children;
1222 std::vector<Node> dt_children_cons;
1223 bool success = true;
1224
1225 // for ITE
1226 Node split_cond_enum;
1227 unsigned split_cond_res_index = 0;
1228 CVC4_UNUSED bool set_split_cond_res_index = false;
1229
1230 for (unsigned sc = 0, size = etis->d_cenum.size(); sc < size; sc++)
1231 {
1232 indent("sygus-sui-dt", ind + 1);
1233 Trace("sygus-sui-dt")
1234 << "construct PBE child #" << sc << "..." << std::endl;
1235 Node rec_c;
1236 std::map<unsigned, Node>::iterator itla =
1237 look_ahead_solved_children.find(sc);
1238 if (itla != look_ahead_solved_children.end())
1239 {
1240 rec_c = itla->second;
1241 indent("sygus-sui-dt-debug", ind + 1);
1242 Trace("sygus-sui-dt-debug")
1243 << "ConstructPBE: look ahead solved : "
1244 << d_tds->sygusToBuiltin(rec_c) << std::endl;
1245 }
1246 else
1247 {
1248 std::pair<Node, NodeRole>& cenum = etis->d_cenum[sc];
1249
1250 // update the context
1251 std::vector<Node> prev;
1252 if (strat == strat_ITE && sc > 0)
1253 {
1254 EnumCache& ecache_cond = d_ecache[split_cond_enum];
1255 Assert(set_split_cond_res_index);
1256 Assert(split_cond_res_index < ecache_cond.d_enum_vals_res.size());
1257 prev = x.d_vals;
1258 bool ret = x.updateContext(
1259 this,
1260 ecache_cond.d_enum_vals_res[split_cond_res_index],
1261 sc == 1);
1262 AlwaysAssert(ret);
1263 }
1264
1265 // recurse
1266 if (strat == strat_ITE && sc == 0)
1267 {
1268 Node ce = cenum.first;
1269
1270 EnumCache& ecache_child = d_ecache[ce];
1271
1272 // get the conditionals in the current context : they must be
1273 // distinguishable
1274 std::map<int, std::vector<Node> > possible_cond;
1275 std::map<Node, int> solved_cond; // stores branch
1276 ecache_child.d_term_trie.getLeaves(x.d_vals, true, possible_cond);
1277
1278 std::map<int, std::vector<Node> >::iterator itpc =
1279 possible_cond.find(0);
1280 if (itpc != possible_cond.end())
1281 {
1282 if (Trace.isOn("sygus-sui-dt-debug"))
1283 {
1284 indent("sygus-sui-dt-debug", ind + 1);
1285 Trace("sygus-sui-dt-debug")
1286 << "PBE : We have " << itpc->second.size()
1287 << " distinguishable conditionals:" << std::endl;
1288 for (Node& cond : itpc->second)
1289 {
1290 indent("sygus-sui-dt-debug", ind + 2);
1291 Trace("sygus-sui-dt-debug")
1292 << d_tds->sygusToBuiltin(cond) << std::endl;
1293 }
1294 }
1295
1296 // otherwise, guess a conditional
1297 if (rec_c.isNull())
1298 {
1299 rec_c = constructBestConditional(ce, itpc->second);
1300 Assert(!rec_c.isNull());
1301 indent("sygus-sui-dt", ind);
1302 Trace("sygus-sui-dt")
1303 << "PBE: ITE strategy : choose best conditional "
1304 << d_tds->sygusToBuiltin(rec_c) << std::endl;
1305 }
1306 }
1307 else
1308 {
1309 // TODO (#1250) : degenerate case where children have different
1310 // types?
1311 indent("sygus-sui-dt", ind);
1312 Trace("sygus-sui-dt") << "return PBE: failed ITE strategy, "
1313 "cannot find a distinguishable condition"
1314 << std::endl;
1315 }
1316 if (!rec_c.isNull())
1317 {
1318 Assert(ecache_child.d_enum_val_to_index.find(rec_c)
1319 != ecache_child.d_enum_val_to_index.end());
1320 split_cond_res_index = ecache_child.d_enum_val_to_index[rec_c];
1321 set_split_cond_res_index = true;
1322 split_cond_enum = ce;
1323 Assert(split_cond_res_index
1324 < ecache_child.d_enum_vals_res.size());
1325 }
1326 }
1327 else
1328 {
1329 did_recurse = true;
1330 rec_c = constructSol(f, cenum.first, cenum.second, ind + 2, lemmas);
1331 }
1332
1333 // undo update the context
1334 if (strat == strat_ITE && sc > 0)
1335 {
1336 x.d_vals = prev;
1337 }
1338 }
1339 if (!rec_c.isNull())
1340 {
1341 dt_children_cons.push_back(rec_c);
1342 }
1343 else
1344 {
1345 success = false;
1346 break;
1347 }
1348 }
1349 if (success)
1350 {
1351 Assert(dt_children_cons.size() == etis->d_sol_templ_args.size());
1352 // ret_dt = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR,
1353 // dt_children );
1354 ret_dt = etis->d_sol_templ;
1355 ret_dt = ret_dt.substitute(etis->d_sol_templ_args.begin(),
1356 etis->d_sol_templ_args.end(),
1357 dt_children_cons.begin(),
1358 dt_children_cons.end());
1359 indent("sygus-sui-dt-debug", ind);
1360 Trace("sygus-sui-dt-debug")
1361 << "PBE: success : constructed for strategy " << strat << std::endl;
1362 }
1363 else
1364 {
1365 indent("sygus-sui-dt-debug", ind);
1366 Trace("sygus-sui-dt-debug")
1367 << "PBE: failed for strategy " << strat << std::endl;
1368 }
1369 }
1370 // increment
1371 sindex++;
1372 }
1373
1374 Assert(ret_dt.isNull() || ret_dt.getType() == e.getType());
1375 indent("sygus-sui-dt", ind);
1376 Trace("sygus-sui-dt") << "ConstructPBE: returned " << ret_dt << std::endl;
1377 return ret_dt;
1378 }
1379
1380 Node SygusUnifIo::constructBestConditional(Node ce,
1381 const std::vector<Node>& conds)
1382 {
1383 if (!d_solConsUsingInfoGain)
1384 {
1385 return SygusUnif::constructBestConditional(ce, conds);
1386 }
1387 UnifContextIo& x = d_context;
1388 // use information gain heuristic
1389 Trace("sygus-sui-dt-igain") << "Best information gain in context ";
1390 print_val("sygus-sui-dt-igain", x.d_vals);
1391 Trace("sygus-sui-dt-igain") << std::endl;
1392 // set of indices that are active in this branch, i.e. x.d_vals[i] is true
1393 std::vector<unsigned> activeIndices;
1394 // map (j,t,s) -> n, such that the j^th condition in the vector conds
1395 // evaluates to t (typically true/false) on n active I/O pairs with output s.
1396 std::map<unsigned, std::map<Node, std::map<Node, unsigned>>> eval;
1397 // map (j,t) -> m, such that the j^th condition in the vector conds
1398 // evaluates to t (typically true/false) for m active I/O pairs.
1399 std::map<unsigned, std::map<Node, unsigned>> evalCount;
1400 unsigned nconds = conds.size();
1401 EnumCache& ecache = d_ecache[ce];
1402 // Get the index of conds[j] in the enumerator cache, this is to look up
1403 // its evaluation on each point.
1404 std::vector<unsigned> eindex;
1405 for (unsigned j = 0; j < nconds; j++)
1406 {
1407 eindex.push_back(ecache.d_enum_val_to_index[conds[j]]);
1408 }
1409 unsigned activePoints = 0;
1410 for (unsigned i = 0, npoints = x.d_vals.size(); i < npoints; i++)
1411 {
1412 if (x.d_vals[i].getConst<bool>())
1413 {
1414 activePoints++;
1415 Node eo = d_examples_out[i];
1416 for (unsigned j = 0; j < nconds; j++)
1417 {
1418 Node resn = ecache.d_enum_vals_res[eindex[j]][i];
1419 Assert(resn.isConst());
1420 eval[j][resn][eo]++;
1421 evalCount[j][resn]++;
1422 }
1423 }
1424 }
1425 AlwaysAssert(activePoints > 0);
1426 // find the condition that leads to the lowest entropy
1427 // initially set minEntropy to > 1.0.
1428 double minEntropy = 2.0;
1429 unsigned bestIndex = 0;
1430 for (unsigned j = 0; j < nconds; j++)
1431 {
1432 // To compute the entropy for a condition C, for pair of terms (s, t), let
1433 // prob(t) be the probability C evaluates to t on an active point,
1434 // prob(s|t) be the probability that an active point on which C
1435 // evaluates to t has output s.
1436 // Then, the entropy of C is:
1437 // sum{t}. prob(t)*( sum{s}. -prob(s|t)*log2(prob(s|t)) )
1438 // where notice this is always between 0 and 1.
1439 double entropySum = 0.0;
1440 Trace("sygus-sui-dt-igain") << j << " : ";
1441 for (std::pair<const Node, std::map<Node, unsigned>>& ej : eval[j])
1442 {
1443 unsigned ecount = evalCount[j][ej.first];
1444 if (ecount > 0)
1445 {
1446 double probBranch = double(ecount) / double(activePoints);
1447 Trace("sygus-sui-dt-igain") << ej.first << " -> ( ";
1448 for (std::pair<const Node, unsigned>& eej : ej.second)
1449 {
1450 if (eej.second > 0)
1451 {
1452 double probVal = double(eej.second) / double(ecount);
1453 Trace("sygus-sui-dt-igain")
1454 << eej.first << ":" << eej.second << " ";
1455 double factor = -probVal * log2(probVal);
1456 entropySum += probBranch * factor;
1457 }
1458 }
1459 Trace("sygus-sui-dt-igain") << ") ";
1460 }
1461 }
1462 Trace("sygus-sui-dt-igain") << "..." << entropySum << std::endl;
1463 if (entropySum < minEntropy)
1464 {
1465 minEntropy = entropySum;
1466 bestIndex = j;
1467 }
1468 }
1469
1470 Assert(!conds.empty());
1471 return conds[bestIndex];
1472 }
1473
1474 } /* CVC4::theory::quantifiers namespace */
1475 } /* CVC4::theory namespace */
1476 } /* CVC4 namespace */