1 /********************* */
2 /*! \file sygus_unif_io.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2017 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
12 ** \brief Implementation of sygus_unif_io
15 #include "theory/quantifiers/sygus/sygus_unif_io.h"
17 #include "theory/datatypes/datatypes_rewriter.h"
18 #include "theory/quantifiers/sygus/term_database_sygus.h"
19 #include "theory/quantifiers/term_util.h"
20 #include "util/random.h"
22 using namespace CVC4::kind
;
26 namespace quantifiers
{
28 UnifContextIo::UnifContextIo() : d_curr_role(role_invalid
)
30 d_true
= NodeManager::currentNM()->mkConst(true);
31 d_false
= NodeManager::currentNM()->mkConst(false);
34 NodeRole
UnifContextIo::getCurrentRole() { return d_curr_role
; }
36 bool UnifContextIo::updateContext(SygusUnifIo
* sui
,
37 std::vector
<Node
>& vals
,
40 Assert(d_vals
.size() == vals
.size());
42 Node poln
= pol
? d_true
: d_false
;
43 for (unsigned i
= 0; i
< vals
.size(); i
++)
47 if (d_vals
[i
] == d_true
)
61 bool UnifContextIo::updateStringPosition(SygusUnifIo
* sui
,
62 std::vector
<unsigned>& pos
,
65 Assert(pos
.size() == d_str_pos
.size());
67 for (unsigned i
= 0; i
< pos
.size(); i
++)
71 d_str_pos
[i
] += pos
[i
];
83 void UnifContextIo::initialize(SygusUnifIo
* sui
)
85 // clear previous data
88 d_curr_role
= role_equal
;
92 // initialize with #examples
93 unsigned sz
= sui
->d_examples
.size();
94 for (unsigned i
= 0; i
< sz
; i
++)
96 d_vals
.push_back(d_true
);
99 if (!sui
->d_examples_out
.empty())
101 // output type of the examples
102 TypeNode exotn
= sui
->d_examples_out
[0].getType();
104 if (exotn
.isString())
106 for (unsigned i
= 0; i
< sz
; i
++)
108 d_str_pos
.push_back(0);
112 d_visit_role
.clear();
115 void UnifContextIo::getCurrentStrings(SygusUnifIo
* sui
,
116 const std::vector
<Node
>& vals
,
117 std::vector
<String
>& ex_vals
)
119 bool isPrefix
= d_curr_role
== role_string_prefix
;
121 for (unsigned i
= 0; i
< vals
.size(); i
++)
123 if (d_vals
[i
] == sui
->d_true
)
125 Assert(vals
[i
].isConst());
126 unsigned pos_value
= d_str_pos
[i
];
129 Assert(d_curr_role
!= role_invalid
);
130 String s
= vals
[i
].getConst
<String
>();
131 Assert(pos_value
<= s
.size());
132 ex_vals
.push_back(isPrefix
? s
.suffix(s
.size() - pos_value
)
133 : s
.prefix(s
.size() - pos_value
));
137 ex_vals
.push_back(vals
[i
].getConst
<String
>());
142 // irrelevant, add dummy
143 ex_vals
.push_back(dummy
);
148 bool UnifContextIo::getStringIncrement(SygusUnifIo
* sui
,
150 const std::vector
<String
>& ex_vals
,
151 const std::vector
<Node
>& vals
,
152 std::vector
<unsigned>& inc
,
155 for (unsigned j
= 0; j
< vals
.size(); j
++)
158 if (d_vals
[j
] == sui
->d_true
)
160 // example is active in this context
161 Assert(vals
[j
].isConst());
162 String mystr
= vals
[j
].getConst
<String
>();
164 if (mystr
.size() <= ex_vals
[j
].size())
166 if (!(isPrefix
? ex_vals
[j
].strncmp(mystr
, ival
)
167 : ex_vals
[j
].rstrncmp(mystr
, ival
)))
169 Trace("sygus-sui-dt-debug") << "X";
175 Trace("sygus-sui-dt-debug") << "X";
179 Trace("sygus-sui-dt-debug") << ival
;
185 bool UnifContextIo::isStringSolved(SygusUnifIo
* sui
,
186 const std::vector
<String
>& ex_vals
,
187 const std::vector
<Node
>& vals
)
189 for (unsigned j
= 0; j
< vals
.size(); j
++)
191 if (d_vals
[j
] == sui
->d_true
)
193 // example is active in this context
194 Assert(vals
[j
].isConst());
195 String mystr
= vals
[j
].getConst
<String
>();
196 if (ex_vals
[j
] != mystr
)
205 // status : 0 : exact, -1 : vals is subset, 1 : vals is superset
206 Node
SubsumeTrie::addTermInternal(Node t
,
207 const std::vector
<Node
>& vals
,
209 std::vector
<Node
>& subsumed
,
213 bool checkExistsOnly
,
216 if (index
== vals
.size())
220 // set the term if checkExistsOnly = false
221 if (d_term
.isNull() && !checkExistsOnly
)
226 else if (status
== 1)
228 Assert(checkSubsume
);
229 // found a subsumed term
230 if (!d_term
.isNull())
232 subsumed
.push_back(d_term
);
233 if (!checkExistsOnly
)
235 // remove it if checkExistsOnly = false
236 d_term
= Node::null();
242 Assert(!checkExistsOnly
&& checkSubsume
);
246 NodeManager
* nm
= NodeManager::currentNM();
248 Assert(pol
|| (vals
[index
].isConst() && vals
[index
].getType().isBoolean()));
249 Node cv
= pol
? vals
[index
] : nm
->mkConst(!vals
[index
].getConst
<bool>());
250 // if checkExistsOnly = false, check if the current value is subsumed if
251 // checkSubsume = true, if so, don't add
252 if (!checkExistsOnly
&& checkSubsume
)
254 Assert(cv
.isConst() && cv
.getType().isBoolean());
255 std::vector
<bool> check_subsumed_by
;
258 if (!cv
.getConst
<bool>())
260 check_subsumed_by
.push_back(spol
);
263 else if (status
== -1)
265 check_subsumed_by
.push_back(spol
);
266 if (!cv
.getConst
<bool>())
268 check_subsumed_by
.push_back(!spol
);
271 // check for subsumed nodes
272 for (unsigned i
= 0, size
= check_subsumed_by
.size(); i
< size
; i
++)
274 bool csbi
= check_subsumed_by
[i
];
275 Node csval
= nm
->mkConst(csbi
);
277 std::map
<Node
, SubsumeTrie
>::iterator itc
= d_children
.find(csval
);
278 if (itc
!= d_children
.end())
280 Node ret
= itc
->second
.addTermInternal(t
,
298 std::vector
<bool> check_subsume
;
303 std::map
<Node
, SubsumeTrie
>::iterator itc
= d_children
.find(cv
);
304 if (itc
!= d_children
.end())
306 ret
= itc
->second
.addTermInternal(t
,
320 ret
= d_children
[cv
].addTermInternal(t
,
331 // we were subsumed by ret, return
337 Assert(cv
.isConst() && cv
.getType().isBoolean());
338 // check for subsuming
339 if (cv
.getConst
<bool>())
341 check_subsume
.push_back(!spol
);
345 else if (status
== 1)
347 Assert(checkSubsume
);
348 Assert(cv
.isConst() && cv
.getType().isBoolean());
349 check_subsume
.push_back(!spol
);
350 if (cv
.getConst
<bool>())
352 check_subsume
.push_back(spol
);
357 // check for subsumed terms
358 for (unsigned i
= 0, size
= check_subsume
.size(); i
< size
; i
++)
360 Node csval
= nm
->mkConst
<bool>(check_subsume
[i
]);
361 std::map
<Node
, SubsumeTrie
>::iterator itc
= d_children
.find(csval
);
362 if (itc
!= d_children
.end())
364 itc
->second
.addTermInternal(t
,
374 if (itc
->second
.isEmpty())
376 Assert(!checkExistsOnly
);
377 d_children
.erase(csval
);
385 Node
SubsumeTrie::addTerm(Node t
,
386 const std::vector
<Node
>& vals
,
388 std::vector
<Node
>& subsumed
)
390 return addTermInternal(t
, vals
, pol
, subsumed
, true, 0, 0, false, true);
393 Node
SubsumeTrie::addCond(Node c
, const std::vector
<Node
>& vals
, bool pol
)
395 std::vector
<Node
> subsumed
;
396 return addTermInternal(c
, vals
, pol
, subsumed
, true, 0, 0, false, false);
399 void SubsumeTrie::getSubsumed(const std::vector
<Node
>& vals
,
401 std::vector
<Node
>& subsumed
)
403 addTermInternal(Node::null(), vals
, pol
, subsumed
, true, 0, 1, true, true);
406 void SubsumeTrie::getSubsumedBy(const std::vector
<Node
>& vals
,
408 std::vector
<Node
>& subsumed_by
)
412 Node::null(), vals
, !pol
, subsumed_by
, false, 0, 1, true, true);
415 void SubsumeTrie::getLeavesInternal(const std::vector
<Node
>& vals
,
417 std::map
<int, std::vector
<Node
> >& v
,
421 if (index
== vals
.size())
423 Assert(!d_term
.isNull());
424 Assert(std::find(v
[status
].begin(), v
[status
].end(), d_term
)
426 v
[status
].push_back(d_term
);
430 Assert(vals
[index
].isConst() && vals
[index
].getType().isBoolean());
431 bool curr_val_true
= vals
[index
].getConst
<bool>() == pol
;
432 for (std::map
<Node
, SubsumeTrie
>::iterator it
= d_children
.begin();
433 it
!= d_children
.end();
436 int new_status
= status
;
437 // if the current value is true
442 Assert(it
->first
.isConst() && it
->first
.getType().isBoolean());
443 new_status
= (it
->first
.getConst
<bool>() ? 1 : -1);
444 if (status
!= -2 && new_status
!= status
)
450 it
->second
.getLeavesInternal(vals
, pol
, v
, index
+ 1, new_status
);
455 void SubsumeTrie::getLeaves(const std::vector
<Node
>& vals
,
457 std::map
<int, std::vector
<Node
> >& v
)
459 getLeavesInternal(vals
, pol
, v
, 0, -2);
462 SygusUnifIo::SygusUnifIo() : d_check_sol(false), d_cond_count(0)
464 d_true
= NodeManager::currentNM()->mkConst(true);
465 d_false
= NodeManager::currentNM()->mkConst(false);
468 SygusUnifIo::~SygusUnifIo() {}
469 void SygusUnifIo::initialize(QuantifiersEngine
* qe
,
470 const std::vector
<Node
>& funs
,
471 std::vector
<Node
>& enums
,
472 std::vector
<Node
>& lemmas
)
474 Assert(funs
.size() == 1);
476 d_examples_out
.clear();
478 d_candidate
= funs
[0];
479 SygusUnif::initialize(qe
, funs
, enums
, lemmas
);
480 // learn redundant operators based on the strategy
481 d_strategy
[d_candidate
].staticLearnRedundantOps(lemmas
);
484 void SygusUnifIo::addExample(const std::vector
<Node
>& input
, Node output
)
486 d_examples
.push_back(input
);
487 d_examples_out
.push_back(output
);
490 void SygusUnifIo::notifyEnumeration(Node e
, Node v
, std::vector
<Node
>& lemmas
)
492 Trace("sygus-sui-enum") << "Notify enumeration for " << e
<< " : " << v
494 Node c
= d_candidate
;
495 Assert(!d_examples
.empty());
496 Assert(d_examples
.size() == d_examples_out
.size());
498 EnumInfo
& ei
= d_strategy
[c
].getEnumInfo(e
);
499 // The explanation for why the current value should be excluded in future
503 TypeNode xtn
= e
.getType();
504 Node bv
= d_tds
->sygusToBuiltin(v
, xtn
);
505 std::vector
<Node
> base_results
;
506 // compte the results
507 for (unsigned j
= 0, size
= d_examples
.size(); j
< size
; j
++)
509 Node res
= d_tds
->evaluateBuiltin(xtn
, bv
, d_examples
[j
]);
510 Trace("sygus-sui-enum-debug")
511 << "...got res = " << res
<< " from " << bv
<< std::endl
;
512 base_results
.push_back(res
);
514 // is it excluded for domain-specific reason?
515 std::vector
<Node
> exp_exc_vec
;
516 if (getExplanationForEnumeratorExclude(e
, v
, base_results
, exp_exc_vec
))
518 Assert(!exp_exc_vec
.empty());
519 exp_exc
= exp_exc_vec
.size() == 1
521 : NodeManager::currentNM()->mkNode(AND
, exp_exc_vec
);
522 Trace("sygus-sui-enum")
523 << " ...fail : term is excluded (domain-specific)" << std::endl
;
528 Assert(!ei
.d_enum_slave
.empty());
529 // explanation for why this value should be excluded
530 for (unsigned s
= 0; s
< ei
.d_enum_slave
.size(); s
++)
532 Node xs
= ei
.d_enum_slave
[s
];
534 EnumInfo
& eiv
= d_strategy
[c
].getEnumInfo(xs
);
536 EnumCache
& ecv
= d_ecache
[xs
];
538 Trace("sygus-sui-enum") << "Process " << xs
<< " from " << s
<< std::endl
;
539 // bool prevIsCover = false;
540 if (eiv
.getRole() == enum_io
)
542 Trace("sygus-sui-enum") << " IO-Eval of ";
543 // prevIsCover = eiv.isFeasible();
547 Trace("sygus-sui-enum") << "Evaluation of ";
549 Trace("sygus-sui-enum") << xs
<< " : ";
550 // evaluate all input/output examples
551 std::vector
<Node
> results
;
552 Node templ
= eiv
.d_template
;
553 TNode templ_var
= eiv
.d_template_arg
;
554 std::map
<Node
, bool> cond_vals
;
555 for (unsigned j
= 0, size
= base_results
.size(); j
< size
; j
++)
557 Node res
= base_results
[j
];
558 Assert(res
.isConst());
562 res
= templ
.substitute(templ_var
, res
);
563 res
= Rewriter::rewrite(res
);
564 Assert(res
.isConst());
567 if (eiv
.getRole() == enum_io
)
569 Node out
= d_examples_out
[j
];
570 Assert(out
.isConst());
571 resb
= res
== out
? d_true
: d_false
;
577 cond_vals
[resb
] = true;
578 results
.push_back(resb
);
579 if (Trace
.isOn("sygus-sui-enum"))
581 if (resb
.getType().isBoolean())
583 Trace("sygus-sui-enum") << (resb
== d_true
? "1" : "0");
587 Trace("sygus-sui-enum") << "?";
592 if (eiv
.getRole() == enum_io
)
594 // latter is the degenerate case of no examples
595 if (cond_vals
.find(d_true
) != cond_vals
.end() || cond_vals
.empty())
597 // check subsumbed/subsuming
598 std::vector
<Node
> subsume
;
599 if (cond_vals
.find(d_false
) == cond_vals
.end())
601 // it is the entire solution, we are done
602 Trace("sygus-sui-enum")
603 << " ...success, full solution added to PBE pool : "
604 << d_tds
->sygusToBuiltin(v
) << std::endl
;
608 // it subsumes everything
609 ecv
.d_term_trie
.clear();
610 ecv
.d_term_trie
.addTerm(v
, results
, true, subsume
);
616 Node val
= ecv
.d_term_trie
.addTerm(v
, results
, true, subsume
);
619 Trace("sygus-sui-enum") << " ...success";
620 if (!subsume
.empty())
622 ecv
.d_enum_subsume
.insert(
623 ecv
.d_enum_subsume
.end(), subsume
.begin(), subsume
.end());
624 Trace("sygus-sui-enum")
625 << " and subsumed " << subsume
.size() << " terms";
627 Trace("sygus-sui-enum")
628 << "! add to PBE pool : " << d_tds
->sygusToBuiltin(v
)
634 Assert(subsume
.empty());
635 Trace("sygus-sui-enum") << " ...fail : subsumed" << std::endl
;
641 Trace("sygus-sui-enum")
642 << " ...fail : it does not satisfy examples." << std::endl
;
647 // must be unique up to examples
648 Node val
= ecv
.d_term_trie
.addCond(v
, results
, true);
651 Trace("sygus-sui-enum") << " ...success! add to PBE pool : "
652 << d_tds
->sygusToBuiltin(v
) << std::endl
;
657 Trace("sygus-sui-enum")
658 << " ...fail : term is not unique" << std::endl
;
664 // notify to retry the build of solution
666 ecv
.addEnumValue(v
, results
);
671 // exclude this value on subsequent iterations
672 if (exp_exc
.isNull())
674 // if we did not already explain why this should be excluded, use default
675 exp_exc
= d_tds
->getExplain()->getExplanationForEquality(e
, v
);
677 exp_exc
= exp_exc
.negate();
678 Trace("sygus-sui-enum-lemma")
679 << "SygusUnifIo : enumeration exclude lemma : " << exp_exc
<< std::endl
;
680 lemmas
.push_back(exp_exc
);
683 bool SygusUnifIo::constructSolution(std::vector
<Node
>& sols
)
685 Node sol
= constructSolutionNode();
694 Node
SygusUnifIo::constructSolutionNode()
696 Node c
= d_candidate
;
697 if (!d_solution
.isNull())
699 // already has a solution
702 // only check if an enumerator updated
705 Trace("sygus-pbe") << "Construct solution, #iterations = " << d_cond_count
708 // try multiple times if we have done multiple conditions, due to
711 for (unsigned i
= 0; i
<= d_cond_count
; i
++)
713 Trace("sygus-pbe-dt") << "ConstructPBE for candidate: " << c
<< std::endl
;
714 // initialize a call to construct solution
715 initializeConstructSol();
716 initializeConstructSolFor(c
);
717 // call the virtual construct solution method
718 Node e
= d_strategy
[c
].getRootEnumerator();
719 Node vcc
= constructSol(c
, e
, role_equal
, 1);
720 // if we constructed the solution, and we either did not previously have
721 // a solution, or the new solution is better (smaller).
723 && (vc
.isNull() || (!vc
.isNull()
724 && d_tds
->getSygusTermSize(vcc
)
725 < d_tds
->getSygusTermSize(vc
))))
727 Trace("sygus-pbe") << "**** SygusUnif SOLVED : " << c
<< " = " << vcc
729 Trace("sygus-pbe") << "...solved at iteration " << i
<< std::endl
;
738 Trace("sygus-pbe") << "...failed to solve." << std::endl
;
743 // ------------------------------------ solution construction from enumeration
745 bool SygusUnifIo::useStrContainsEnumeratorExclude(Node e
)
747 TypeNode xbt
= d_tds
->sygusToBuiltinType(e
.getType());
750 std::map
<Node
, bool>::iterator itx
= d_use_str_contains_eexc
.find(e
);
751 if (itx
!= d_use_str_contains_eexc
.end())
755 Trace("sygus-sui-enum-debug")
756 << "Is " << e
<< " is str.contains exclusion?" << std::endl
;
757 d_use_str_contains_eexc
[e
] = true;
758 Node c
= d_candidate
;
759 EnumInfo
& ei
= d_strategy
[c
].getEnumInfo(e
);
760 for (const Node
& sn
: ei
.d_enum_slave
)
762 EnumInfo
& eis
= d_strategy
[c
].getEnumInfo(sn
);
763 EnumRole er
= eis
.getRole();
764 if (er
!= enum_io
&& er
!= enum_concat_term
)
766 Trace("sygus-sui-enum-debug") << " incompatible slave : " << sn
767 << ", role = " << er
<< std::endl
;
768 d_use_str_contains_eexc
[e
] = false;
771 if (eis
.isConditional())
773 Trace("sygus-sui-enum-debug")
774 << " conditional slave : " << sn
<< std::endl
;
775 d_use_str_contains_eexc
[e
] = false;
779 Trace("sygus-sui-enum-debug")
780 << "...can use str.contains exclusion." << std::endl
;
781 return d_use_str_contains_eexc
[e
];
786 bool SygusUnifIo::getExplanationForEnumeratorExclude(Node e
,
788 std::vector
<Node
>& results
,
789 std::vector
<Node
>& exp
)
791 if (useStrContainsEnumeratorExclude(e
))
793 NodeManager
* nm
= NodeManager::currentNM();
794 // This check whether the example evaluates to something that is larger than
795 // the output for some input/output pair. If so, then this term is never
796 // useful. We generalize its explanation below.
798 if (Trace
.isOn("sygus-sui-cterm-debug"))
800 Trace("sygus-sui-enum") << std::endl
;
802 // check if all examples had longer length that the output
803 Assert(d_examples_out
.size() == results
.size());
804 Trace("sygus-sui-cterm-debug")
805 << "Check enumerator exclusion for " << e
<< " -> "
806 << d_tds
->sygusToBuiltin(v
) << " based on str.contains." << std::endl
;
807 std::vector
<unsigned> cmp_indices
;
808 for (unsigned i
= 0, size
= results
.size(); i
< size
; i
++)
810 Assert(results
[i
].isConst());
811 Assert(d_examples_out
[i
].isConst());
812 Trace("sygus-sui-cterm-debug")
813 << " " << results
[i
] << " <> " << d_examples_out
[i
];
814 Node cont
= nm
->mkNode(STRING_STRCTN
, d_examples_out
[i
], results
[i
]);
815 Node contr
= Rewriter::rewrite(cont
);
816 if (contr
== d_false
)
818 cmp_indices
.push_back(i
);
819 Trace("sygus-sui-cterm-debug") << "...not contained." << std::endl
;
823 Trace("sygus-sui-cterm-debug") << "...contained." << std::endl
;
826 if (!cmp_indices
.empty())
828 // we check invariance with respect to a negative contains test
829 NegContainsSygusInvarianceTest ncset
;
830 ncset
.init(e
, d_examples
, d_examples_out
, cmp_indices
);
831 // construct the generalized explanation
832 d_tds
->getExplain()->getExplanationFor(e
, v
, exp
, ncset
);
833 Trace("sygus-sui-cterm")
834 << "PBE-cterm : enumerator exclude " << d_tds
->sygusToBuiltin(v
)
835 << " due to negative containment." << std::endl
;
842 void SygusUnifIo::EnumCache::addEnumValue(Node v
, std::vector
<Node
>& results
)
844 // should not have been enumerated before
845 Assert(d_enum_val_to_index
.find(v
) == d_enum_val_to_index
.end());
846 d_enum_val_to_index
[v
] = d_enum_vals
.size();
847 d_enum_vals
.push_back(v
);
848 d_enum_vals_res
.push_back(results
);
851 void SygusUnifIo::initializeConstructSol() { d_context
.initialize(this); }
852 void SygusUnifIo::initializeConstructSolFor(Node f
)
854 Assert(d_candidate
== f
);
857 Node
SygusUnifIo::constructSol(Node f
, Node e
, NodeRole nrole
, int ind
)
859 Assert(d_candidate
== f
);
860 UnifContextIo
& x
= d_context
;
861 TypeNode etn
= e
.getType();
862 if (Trace
.isOn("sygus-sui-dt-debug"))
864 indent("sygus-sui-dt-debug", ind
);
865 Trace("sygus-sui-dt-debug") << "ConstructPBE: (" << e
<< ", " << nrole
866 << ") for type " << etn
<< " in context ";
867 print_val("sygus-sui-dt-debug", x
.d_vals
);
868 NodeRole ctx_role
= x
.getCurrentRole();
869 Trace("sygus-sui-dt-debug") << ", context role [" << ctx_role
;
870 if (ctx_role
== role_string_prefix
|| ctx_role
== role_string_suffix
)
872 Trace("sygus-sui-dt-debug") << ", string pos : ";
873 for (unsigned i
= 0, size
= x
.d_str_pos
.size(); i
< size
; i
++)
875 Trace("sygus-sui-dt-debug") << " " << x
.d_str_pos
[i
];
878 Trace("sygus-sui-dt-debug") << "]";
879 Trace("sygus-sui-dt-debug") << std::endl
;
881 // enumerator type info
882 EnumTypeInfo
& tinfo
= d_strategy
[f
].getEnumTypeInfo(etn
);
884 // get the enumerator information
885 EnumInfo
& einfo
= d_strategy
[f
].getEnumInfo(e
);
887 EnumCache
& ecache
= d_ecache
[e
];
890 if (nrole
== role_equal
)
892 if (!x
.isReturnValueModified())
894 if (ecache
.isSolved())
896 // this type has a complete solution
897 ret_dt
= ecache
.getSolved();
898 indent("sygus-sui-dt", ind
);
899 Trace("sygus-sui-dt") << "return PBE: success : solved "
900 << d_tds
->sygusToBuiltin(ret_dt
) << std::endl
;
901 Assert(!ret_dt
.isNull());
905 // could be conditionally solved
906 std::vector
<Node
> subsumed_by
;
907 ecache
.d_term_trie
.getSubsumedBy(x
.d_vals
, true, subsumed_by
);
908 if (!subsumed_by
.empty())
910 ret_dt
= constructBestSolvedTerm(subsumed_by
);
911 indent("sygus-sui-dt", ind
);
912 Trace("sygus-sui-dt") << "return PBE: success : conditionally solved"
913 << d_tds
->sygusToBuiltin(ret_dt
) << std::endl
;
917 indent("sygus-sui-dt-debug", ind
);
918 Trace("sygus-sui-dt-debug")
919 << " ...not currently conditionally solved." << std::endl
;
925 if (d_tds
->sygusToBuiltinType(e
.getType()).isString())
927 // check if a current value that closes all examples
928 // get the term enumerator for this type
929 std::map
<EnumRole
, Node
>::iterator itnt
=
930 tinfo
.d_enum
.find(enum_concat_term
);
931 if (itnt
!= tinfo
.d_enum
.end())
933 Node et
= itnt
->second
;
935 EnumCache
& ecachet
= d_ecache
[et
];
936 // get the current examples
937 std::vector
<String
> ex_vals
;
938 x
.getCurrentStrings(this, d_examples_out
, ex_vals
);
939 Assert(ecache
.d_enum_vals
.size() == ecache
.d_enum_vals_res
.size());
941 // test each example in the term enumerator for the type
942 std::vector
<Node
> str_solved
;
943 for (unsigned i
= 0, size
= ecachet
.d_enum_vals
.size(); i
< size
; i
++)
945 if (x
.isStringSolved(this, ex_vals
, ecachet
.d_enum_vals_res
[i
]))
947 str_solved
.push_back(ecachet
.d_enum_vals
[i
]);
950 if (!str_solved
.empty())
952 ret_dt
= constructBestStringSolvedTerm(str_solved
);
953 indent("sygus-sui-dt", ind
);
954 Trace("sygus-sui-dt") << "return PBE: success : string solved "
955 << d_tds
->sygusToBuiltin(ret_dt
) << std::endl
;
959 indent("sygus-sui-dt-debug", ind
);
960 Trace("sygus-sui-dt-debug")
961 << " ...not currently string solved." << std::endl
;
967 else if (nrole
== role_string_prefix
|| nrole
== role_string_suffix
)
969 // check if each return value is a prefix/suffix of all open examples
970 if (!x
.isReturnValueModified() || x
.getCurrentRole() == nrole
)
972 std::map
<Node
, std::vector
<unsigned> > incr
;
973 bool isPrefix
= nrole
== role_string_prefix
;
974 std::map
<Node
, unsigned> total_inc
;
975 std::vector
<Node
> inc_strs
;
976 // make the value of the examples
977 std::vector
<String
> ex_vals
;
978 x
.getCurrentStrings(this, d_examples_out
, ex_vals
);
979 if (Trace
.isOn("sygus-sui-dt-debug"))
981 indent("sygus-sui-dt-debug", ind
);
982 Trace("sygus-sui-dt-debug") << "current strings : " << std::endl
;
983 for (unsigned i
= 0, size
= ex_vals
.size(); i
< size
; i
++)
985 indent("sygus-sui-dt-debug", ind
+ 1);
986 Trace("sygus-sui-dt-debug") << ex_vals
[i
] << std::endl
;
990 // check if there is a value for which is a prefix/suffix of all active
992 Assert(ecache
.d_enum_vals
.size() == ecache
.d_enum_vals_res
.size());
994 for (unsigned i
= 0, size
= ecache
.d_enum_vals
.size(); i
< size
; i
++)
996 Node val_t
= ecache
.d_enum_vals
[i
];
997 Assert(incr
.find(val_t
) == incr
.end());
998 indent("sygus-sui-dt-debug", ind
);
999 Trace("sygus-sui-dt-debug")
1000 << "increment string values : " << val_t
<< " : ";
1001 Assert(ecache
.d_enum_vals_res
[i
].size() == d_examples_out
.size());
1003 bool exsuccess
= x
.getStringIncrement(this,
1006 ecache
.d_enum_vals_res
[i
],
1012 Trace("sygus-sui-dt-debug") << "...fail" << std::endl
;
1016 total_inc
[val_t
] = tot
;
1017 inc_strs
.push_back(val_t
);
1018 Trace("sygus-sui-dt-debug")
1019 << "...success, total increment = " << tot
<< std::endl
;
1025 ret_dt
= constructBestStringToConcat(inc_strs
, total_inc
, incr
);
1026 Assert(!ret_dt
.isNull());
1027 indent("sygus-sui-dt", ind
);
1028 Trace("sygus-sui-dt")
1029 << "PBE: CONCAT strategy : choose " << (isPrefix
? "pre" : "suf")
1030 << "fix value " << d_tds
->sygusToBuiltin(ret_dt
) << std::endl
;
1031 // update the context
1032 bool ret
= x
.updateStringPosition(this, incr
[ret_dt
], nrole
);
1033 AlwaysAssert(ret
== (total_inc
[ret_dt
] > 0));
1037 indent("sygus-sui-dt", ind
);
1038 Trace("sygus-sui-dt") << "PBE: failed CONCAT strategy, no values are "
1039 << (isPrefix
? "pre" : "suf")
1040 << "fix of all examples." << std::endl
;
1045 indent("sygus-sui-dt", ind
);
1046 Trace("sygus-sui-dt")
1047 << "PBE: failed CONCAT strategy, prefix/suffix mismatch."
1051 if (ret_dt
.isNull() && !einfo
.isTemplated())
1053 // we will try a single strategy
1054 EnumTypeInfoStrat
* etis
= nullptr;
1055 std::map
<NodeRole
, StrategyNode
>::iterator itsn
=
1056 tinfo
.d_snodes
.find(nrole
);
1057 if (itsn
!= tinfo
.d_snodes
.end())
1060 StrategyNode
& snode
= itsn
->second
;
1061 if (x
.d_visit_role
[e
].find(nrole
) == x
.d_visit_role
[e
].end())
1063 x
.d_visit_role
[e
][nrole
] = true;
1064 // try a random strategy
1065 if (snode
.d_strats
.size() > 1)
1067 std::random_shuffle(snode
.d_strats
.begin(), snode
.d_strats
.end());
1069 // get an eligible strategy index
1070 unsigned sindex
= 0;
1071 while (sindex
< snode
.d_strats
.size()
1072 && !snode
.d_strats
[sindex
]->isValid(x
))
1076 // if we found a eligible strategy
1077 if (sindex
< snode
.d_strats
.size())
1079 etis
= snode
.d_strats
[sindex
];
1083 if (etis
!= nullptr)
1085 StrategyType strat
= etis
->d_this
;
1086 indent("sygus-sui-dt", ind
+ 1);
1087 Trace("sygus-sui-dt")
1088 << "...try STRATEGY " << strat
<< "..." << std::endl
;
1090 std::map
<unsigned, Node
> look_ahead_solved_children
;
1091 std::vector
<Node
> dt_children_cons
;
1092 bool success
= true;
1095 Node split_cond_enum
;
1096 int split_cond_res_index
= -1;
1098 for (unsigned sc
= 0, size
= etis
->d_cenum
.size(); sc
< size
; sc
++)
1100 indent("sygus-sui-dt", ind
+ 1);
1101 Trace("sygus-sui-dt")
1102 << "construct PBE child #" << sc
<< "..." << std::endl
;
1104 std::map
<unsigned, Node
>::iterator itla
=
1105 look_ahead_solved_children
.find(sc
);
1106 if (itla
!= look_ahead_solved_children
.end())
1108 rec_c
= itla
->second
;
1109 indent("sygus-sui-dt-debug", ind
+ 1);
1110 Trace("sygus-sui-dt-debug")
1111 << "ConstructPBE: look ahead solved : "
1112 << d_tds
->sygusToBuiltin(rec_c
) << std::endl
;
1116 std::pair
<Node
, NodeRole
>& cenum
= etis
->d_cenum
[sc
];
1118 // update the context
1119 std::vector
<Node
> prev
;
1120 if (strat
== strat_ITE
&& sc
> 0)
1122 EnumCache
& ecache_cond
= d_ecache
[split_cond_enum
];
1123 Assert(split_cond_res_index
>= 0);
1124 Assert(split_cond_res_index
1125 < (int)ecache_cond
.d_enum_vals_res
.size());
1127 bool ret
= x
.updateContext(
1129 ecache_cond
.d_enum_vals_res
[split_cond_res_index
],
1135 if (strat
== strat_ITE
&& sc
== 0)
1137 Node ce
= cenum
.first
;
1139 EnumCache
& ecache_child
= d_ecache
[ce
];
1141 // only used if the return value is not modified
1142 if (!x
.isReturnValueModified())
1144 if (x
.d_uinfo
.find(ce
) == x
.d_uinfo
.end())
1146 Trace("sygus-sui-dt-debug2")
1147 << " reg : PBE: Look for direct solutions for conditional "
1149 << ce
<< " ... " << std::endl
;
1150 Assert(ecache_child
.d_enum_vals
.size()
1151 == ecache_child
.d_enum_vals_res
.size());
1152 for (unsigned i
= 1; i
<= 2; i
++)
1154 std::pair
<Node
, NodeRole
>& te_pair
= etis
->d_cenum
[i
];
1155 Node te
= te_pair
.first
;
1156 EnumCache
& ecache_te
= d_ecache
[te
];
1157 bool branch_pol
= (i
== 1);
1158 // for each condition, get terms that satisfy it in this
1160 for (unsigned k
= 0, size
= ecache_child
.d_enum_vals
.size();
1164 Node cond
= ecache_child
.d_enum_vals
[k
];
1165 std::vector
<Node
> solved
;
1166 ecache_te
.d_term_trie
.getSubsumedBy(
1167 ecache_child
.d_enum_vals_res
[k
], branch_pol
, solved
);
1168 Trace("sygus-sui-dt-debug2")
1169 << " reg : PBE: " << d_tds
->sygusToBuiltin(cond
)
1170 << " has " << solved
.size() << " solutions in branch "
1172 if (!solved
.empty())
1174 Node slv
= constructBestSolvedTerm(solved
);
1175 Trace("sygus-sui-dt-debug2")
1176 << " reg : PBE: ..." << d_tds
->sygusToBuiltin(slv
)
1177 << " is a solution under branch " << i
;
1178 Trace("sygus-sui-dt-debug2")
1179 << " of condition " << d_tds
->sygusToBuiltin(cond
)
1181 x
.d_uinfo
[ce
].d_look_ahead_sols
[cond
][i
] = slv
;
1188 // get the conditionals in the current context : they must be
1190 std::map
<int, std::vector
<Node
> > possible_cond
;
1191 std::map
<Node
, int> solved_cond
; // stores branch
1192 ecache_child
.d_term_trie
.getLeaves(x
.d_vals
, true, possible_cond
);
1194 std::map
<int, std::vector
<Node
> >::iterator itpc
=
1195 possible_cond
.find(0);
1196 if (itpc
!= possible_cond
.end())
1198 if (Trace
.isOn("sygus-sui-dt-debug"))
1200 indent("sygus-sui-dt-debug", ind
+ 1);
1201 Trace("sygus-sui-dt-debug")
1202 << "PBE : We have " << itpc
->second
.size()
1203 << " distinguishable conditionals:" << std::endl
;
1204 for (Node
& cond
: itpc
->second
)
1206 indent("sygus-sui-dt-debug", ind
+ 2);
1207 Trace("sygus-sui-dt-debug")
1208 << d_tds
->sygusToBuiltin(cond
) << std::endl
;
1212 // static look ahead conditional : choose conditionals that have
1213 // solved terms in at least one branch
1214 // only applicable if we have not modified the return value
1215 std::map
<int, std::vector
<Node
> > solved_cond
;
1216 if (!x
.isReturnValueModified())
1218 Assert(x
.d_uinfo
.find(ce
) != x
.d_uinfo
.end());
1220 for (Node
& cond
: itpc
->second
)
1222 std::map
<Node
, std::map
<unsigned, Node
> >::iterator itla
=
1223 x
.d_uinfo
[ce
].d_look_ahead_sols
.find(cond
);
1224 if (itla
!= x
.d_uinfo
[ce
].d_look_ahead_sols
.end())
1226 int nsolved
= itla
->second
.size();
1227 solve_max
= nsolved
> solve_max
? nsolved
: solve_max
;
1228 solved_cond
[nsolved
].push_back(cond
);
1234 if (!solved_cond
[n
].empty())
1236 rec_c
= constructBestSolvedConditional(solved_cond
[n
]);
1237 indent("sygus-sui-dt", ind
+ 1);
1238 Trace("sygus-sui-dt")
1239 << "PBE: ITE strategy : choose solved conditional "
1240 << d_tds
->sygusToBuiltin(rec_c
) << " with " << n
1241 << " solved children..." << std::endl
;
1242 std::map
<Node
, std::map
<unsigned, Node
> >::iterator itla
=
1243 x
.d_uinfo
[ce
].d_look_ahead_sols
.find(rec_c
);
1244 Assert(itla
!= x
.d_uinfo
[ce
].d_look_ahead_sols
.end());
1245 for (std::pair
<const unsigned, Node
>& las
: itla
->second
)
1247 look_ahead_solved_children
[las
.first
] = las
.second
;
1255 // otherwise, guess a conditional
1258 rec_c
= constructBestConditional(itpc
->second
);
1259 Assert(!rec_c
.isNull());
1260 indent("sygus-sui-dt", ind
);
1261 Trace("sygus-sui-dt")
1262 << "PBE: ITE strategy : choose random conditional "
1263 << d_tds
->sygusToBuiltin(rec_c
) << std::endl
;
1268 // TODO (#1250) : degenerate case where children have different
1270 indent("sygus-sui-dt", ind
);
1271 Trace("sygus-sui-dt") << "return PBE: failed ITE strategy, "
1272 "cannot find a distinguishable condition"
1275 if (!rec_c
.isNull())
1277 Assert(ecache_child
.d_enum_val_to_index
.find(rec_c
)
1278 != ecache_child
.d_enum_val_to_index
.end());
1279 split_cond_res_index
= ecache_child
.d_enum_val_to_index
[rec_c
];
1280 split_cond_enum
= ce
;
1281 Assert(split_cond_res_index
>= 0);
1282 Assert(split_cond_res_index
1283 < (int)ecache_child
.d_enum_vals_res
.size());
1288 rec_c
= constructSol(f
, cenum
.first
, cenum
.second
, ind
+ 2);
1291 // undo update the context
1292 if (strat
== strat_ITE
&& sc
> 0)
1297 if (!rec_c
.isNull())
1299 dt_children_cons
.push_back(rec_c
);
1309 Assert(dt_children_cons
.size() == etis
->d_sol_templ_args
.size());
1310 // ret_dt = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR,
1312 ret_dt
= etis
->d_sol_templ
;
1313 ret_dt
= ret_dt
.substitute(etis
->d_sol_templ_args
.begin(),
1314 etis
->d_sol_templ_args
.end(),
1315 dt_children_cons
.begin(),
1316 dt_children_cons
.end());
1317 indent("sygus-sui-dt-debug", ind
);
1318 Trace("sygus-sui-dt-debug")
1319 << "PBE: success : constructed for strategy " << strat
<< std::endl
;
1323 indent("sygus-sui-dt-debug", ind
);
1324 Trace("sygus-sui-dt-debug")
1325 << "PBE: failed for strategy " << strat
<< std::endl
;
1330 if (!ret_dt
.isNull())
1332 Assert(ret_dt
.getType() == e
.getType());
1334 indent("sygus-sui-dt", ind
);
1335 Trace("sygus-sui-dt") << "ConstructPBE: returned " << ret_dt
<< std::endl
;
1339 } /* CVC4::theory::quantifiers namespace */
1340 } /* CVC4::theory namespace */
1341 } /* CVC4 namespace */