1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, Aina Niemetz |
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 |
|
* Utility for processing programming by examples synthesis conjectures. |
14 |
|
*/ |
15 |
|
#include "theory/quantifiers/sygus/sygus_pbe.h" |
16 |
|
|
17 |
|
#include "options/quantifiers_options.h" |
18 |
|
#include "theory/datatypes/sygus_datatype_utils.h" |
19 |
|
#include "theory/quantifiers/sygus/example_infer.h" |
20 |
|
#include "theory/quantifiers/sygus/sygus_unif_io.h" |
21 |
|
#include "theory/quantifiers/sygus/synth_conjecture.h" |
22 |
|
#include "theory/quantifiers/sygus/term_database_sygus.h" |
23 |
|
#include "theory/quantifiers/term_util.h" |
24 |
|
#include "util/random.h" |
25 |
|
|
26 |
|
using namespace cvc5; |
27 |
|
using namespace cvc5::kind; |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
namespace theory { |
31 |
|
namespace quantifiers { |
32 |
|
|
33 |
1151 |
SygusPbe::SygusPbe(QuantifiersInferenceManager& qim, |
34 |
|
TermDbSygus* tds, |
35 |
1151 |
SynthConjecture* p) |
36 |
1151 |
: SygusModule(qim, tds, p) |
37 |
|
{ |
38 |
1151 |
d_true = NodeManager::currentNM()->mkConst(true); |
39 |
1151 |
d_false = NodeManager::currentNM()->mkConst(false); |
40 |
1151 |
d_is_pbe = false; |
41 |
1151 |
} |
42 |
|
|
43 |
2302 |
SygusPbe::~SygusPbe() {} |
44 |
|
|
45 |
240 |
bool SygusPbe::initialize(Node conj, |
46 |
|
Node n, |
47 |
|
const std::vector<Node>& candidates) |
48 |
|
{ |
49 |
240 |
Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl; |
50 |
240 |
NodeManager* nm = NodeManager::currentNM(); |
51 |
|
|
52 |
240 |
if (!options::sygusUnifPbe()) |
53 |
|
{ |
54 |
|
// we are not doing unification |
55 |
31 |
return false; |
56 |
|
} |
57 |
|
|
58 |
|
// check if all candidates are valid examples |
59 |
209 |
ExampleInfer* ei = d_parent->getExampleInfer(); |
60 |
209 |
d_is_pbe = true; |
61 |
256 |
for (const Node& c : candidates) |
62 |
|
{ |
63 |
|
// if it has no examples or the output of the examples is invalid |
64 |
212 |
if (ei->getNumExamples(c) == 0 || !ei->hasExamplesOut(c)) |
65 |
|
{ |
66 |
165 |
d_is_pbe = false; |
67 |
165 |
return false; |
68 |
|
} |
69 |
|
} |
70 |
91 |
for (const Node& c : candidates) |
71 |
|
{ |
72 |
47 |
Assert(ei->hasExamples(c)); |
73 |
47 |
d_sygus_unif[c].reset(new SygusUnifIo(d_parent)); |
74 |
94 |
Trace("sygus-pbe") << "Initialize unif utility for " << c << "..." |
75 |
47 |
<< std::endl; |
76 |
94 |
std::map<Node, std::vector<Node>> strategy_lemmas; |
77 |
94 |
d_sygus_unif[c]->initializeCandidate( |
78 |
47 |
d_tds, c, d_candidate_to_enum[c], strategy_lemmas); |
79 |
47 |
Assert(!d_candidate_to_enum[c].empty()); |
80 |
94 |
Trace("sygus-pbe") << "Initialize " << d_candidate_to_enum[c].size() |
81 |
47 |
<< " enumerators for " << c << "..." << std::endl; |
82 |
|
// collect list per type of strategy points with strategy lemmas |
83 |
94 |
std::map<TypeNode, std::vector<Node>> tn_to_strategy_pt; |
84 |
101 |
for (const std::pair<const Node, std::vector<Node>>& p : strategy_lemmas) |
85 |
|
{ |
86 |
108 |
TypeNode tnsp = p.first.getType(); |
87 |
54 |
tn_to_strategy_pt[tnsp].push_back(p.first); |
88 |
|
} |
89 |
|
// initialize the enumerators |
90 |
110 |
for (const Node& e : d_candidate_to_enum[c]) |
91 |
|
{ |
92 |
126 |
TypeNode etn = e.getType(); |
93 |
63 |
d_tds->registerEnumerator(e, c, d_parent, ROLE_ENUM_POOL); |
94 |
63 |
d_enum_to_candidate[e] = c; |
95 |
126 |
TNode te = e; |
96 |
|
// initialize static symmetry breaking lemmas for it |
97 |
|
// we register only one "master" enumerator per type |
98 |
|
// thus, the strategy lemmas (which are for individual strategy points) |
99 |
|
// are applicable (disjunctively) to the master enumerator |
100 |
|
std::map<TypeNode, std::vector<Node>>::iterator itt = |
101 |
63 |
tn_to_strategy_pt.find(etn); |
102 |
63 |
if (itt != tn_to_strategy_pt.end()) |
103 |
|
{ |
104 |
72 |
std::vector<Node> disj; |
105 |
88 |
for (const Node& sp : itt->second) |
106 |
|
{ |
107 |
|
std::map<Node, std::vector<Node>>::iterator itsl = |
108 |
52 |
strategy_lemmas.find(sp); |
109 |
52 |
Assert(itsl != strategy_lemmas.end()); |
110 |
52 |
if (!itsl->second.empty()) |
111 |
|
{ |
112 |
104 |
TNode tsp = sp; |
113 |
98 |
Node lem = itsl->second.size() == 1 ? itsl->second[0] |
114 |
150 |
: nm->mkNode(AND, itsl->second); |
115 |
52 |
if (tsp != te) |
116 |
|
{ |
117 |
16 |
lem = lem.substitute(tsp, te); |
118 |
|
} |
119 |
52 |
if (std::find(disj.begin(), disj.end(), lem) == disj.end()) |
120 |
|
{ |
121 |
38 |
disj.push_back(lem); |
122 |
|
} |
123 |
|
} |
124 |
|
} |
125 |
|
// add its active guard |
126 |
72 |
Node ag = d_tds->getActiveGuardForEnumerator(e); |
127 |
36 |
Assert(!ag.isNull()); |
128 |
36 |
disj.push_back(ag.negate()); |
129 |
72 |
Node lem = disj.size() == 1 ? disj[0] : nm->mkNode(OR, disj); |
130 |
|
// Apply extended rewriting on the lemma. This helps utilities like |
131 |
|
// SygusEnumerator more easily recognize the shape of this lemma, e.g. |
132 |
|
// ( ~is-ite(x) or ( ~is-ite(x) ^ P ) ) --> ~is-ite(x). |
133 |
36 |
lem = d_tds->getExtRewriter()->extendedRewrite(lem); |
134 |
72 |
Trace("sygus-pbe") << " static redundant op lemma : " << lem |
135 |
36 |
<< std::endl; |
136 |
|
// Register as a symmetry breaking lemma with the term database. |
137 |
|
// This will either be processed via a lemma on the output channel |
138 |
|
// of the sygus extension of the datatypes solver, or internally |
139 |
|
// encoded as a constraint to an active enumerator. |
140 |
36 |
d_tds->registerSymBreakLemma(e, lem, etn, 0, false); |
141 |
|
} |
142 |
|
} |
143 |
|
} |
144 |
44 |
return true; |
145 |
|
} |
146 |
|
|
147 |
|
// ------------------------------------------- solution construction from enumeration |
148 |
|
|
149 |
3721 |
void SygusPbe::getTermList(const std::vector<Node>& candidates, |
150 |
|
std::vector<Node>& terms) |
151 |
|
{ |
152 |
7536 |
for( unsigned i=0; i<candidates.size(); i++ ){ |
153 |
7630 |
Node v = candidates[i]; |
154 |
|
std::map<Node, std::vector<Node> >::iterator it = |
155 |
3815 |
d_candidate_to_enum.find(v); |
156 |
3815 |
if (it != d_candidate_to_enum.end()) |
157 |
|
{ |
158 |
3815 |
terms.insert(terms.end(), it->second.begin(), it->second.end()); |
159 |
|
} |
160 |
|
} |
161 |
3721 |
} |
162 |
|
|
163 |
3721 |
bool SygusPbe::allowPartialModel() { return !options::sygusPbeMultiFair(); } |
164 |
|
|
165 |
489 |
bool SygusPbe::constructCandidates(const std::vector<Node>& enums, |
166 |
|
const std::vector<Node>& enum_values, |
167 |
|
const std::vector<Node>& candidates, |
168 |
|
std::vector<Node>& candidate_values) |
169 |
|
{ |
170 |
489 |
Assert(enums.size() == enum_values.size()); |
171 |
489 |
if( !enums.empty() ){ |
172 |
489 |
unsigned min_term_size = 0; |
173 |
489 |
Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl; |
174 |
978 |
std::vector<unsigned> szs; |
175 |
1160 |
for (unsigned i = 0, esize = enums.size(); i < esize; i++) |
176 |
|
{ |
177 |
671 |
Trace("sygus-pbe-enum") << " " << enums[i] << " -> "; |
178 |
671 |
TermDbSygus::toStreamSygus("sygus-pbe-enum", enum_values[i]); |
179 |
671 |
Trace("sygus-pbe-enum") << std::endl; |
180 |
671 |
if (!enum_values[i].isNull()) |
181 |
|
{ |
182 |
545 |
unsigned sz = datatypes::utils::getSygusTermSize(enum_values[i]); |
183 |
545 |
szs.push_back(sz); |
184 |
545 |
if (i == 0 || sz < min_term_size) |
185 |
|
{ |
186 |
468 |
min_term_size = sz; |
187 |
|
} |
188 |
|
} |
189 |
|
else |
190 |
|
{ |
191 |
126 |
szs.push_back(0); |
192 |
|
} |
193 |
|
} |
194 |
|
// Assume two enumerators of types T1 and T2. |
195 |
|
// If options::sygusPbeMultiFair() is true, |
196 |
|
// we ensure that all values of type T1 and size n are enumerated before |
197 |
|
// any term of type T2 of size n+d, and vice versa, where d is |
198 |
|
// set by options::sygusPbeMultiFairDiff(). If d is zero, then our |
199 |
|
// enumeration is such that all terms of T1 or T2 of size n are considered |
200 |
|
// before any term of size n+1. |
201 |
489 |
int diffAllow = options::sygusPbeMultiFairDiff(); |
202 |
978 |
std::vector<unsigned> enum_consider; |
203 |
1160 |
for (unsigned i = 0, esize = enums.size(); i < esize; i++) |
204 |
|
{ |
205 |
671 |
if (!enum_values[i].isNull()) |
206 |
|
{ |
207 |
545 |
Assert(szs[i] >= min_term_size); |
208 |
545 |
int diff = szs[i] - min_term_size; |
209 |
545 |
if (!options::sygusPbeMultiFair() || diff <= diffAllow) |
210 |
|
{ |
211 |
545 |
enum_consider.push_back(i); |
212 |
|
} |
213 |
|
} |
214 |
|
} |
215 |
|
|
216 |
|
// only consider the enumerators that are at minimum size (for fairness) |
217 |
489 |
Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / " << enums.size() << std::endl; |
218 |
489 |
NodeManager* nm = NodeManager::currentNM(); |
219 |
1034 |
for (unsigned i = 0, ecsize = enum_consider.size(); i < ecsize; i++) |
220 |
|
{ |
221 |
545 |
unsigned j = enum_consider[i]; |
222 |
1090 |
Node e = enums[j]; |
223 |
1090 |
Node v = enum_values[j]; |
224 |
545 |
Assert(d_enum_to_candidate.find(e) != d_enum_to_candidate.end()); |
225 |
1090 |
Node c = d_enum_to_candidate[e]; |
226 |
1090 |
std::vector<Node> enum_lems; |
227 |
545 |
d_sygus_unif[c]->notifyEnumeration(e, v, enum_lems); |
228 |
545 |
if (!enum_lems.empty()) |
229 |
|
{ |
230 |
|
// the lemmas must be guarded by the active guard of the enumerator |
231 |
14 |
Node g = d_tds->getActiveGuardForEnumerator(e); |
232 |
7 |
Assert(!g.isNull()); |
233 |
14 |
for (unsigned k = 0, size = enum_lems.size(); k < size; k++) |
234 |
|
{ |
235 |
14 |
Node lem = nm->mkNode(OR, g.negate(), enum_lems[k]); |
236 |
7 |
d_qim.addPendingLemma(lem, |
237 |
|
InferenceId::QUANTIFIERS_SYGUS_PBE_EXCLUDE); |
238 |
|
} |
239 |
|
} |
240 |
|
} |
241 |
|
} |
242 |
540 |
for( unsigned i=0; i<candidates.size(); i++ ){ |
243 |
548 |
Node c = candidates[i]; |
244 |
|
//build decision tree for candidate |
245 |
548 |
std::vector<Node> sol; |
246 |
548 |
std::vector<Node> lems; |
247 |
497 |
bool solSuccess = d_sygus_unif[c]->constructSolution(sol, lems); |
248 |
497 |
for (const Node& lem : lems) |
249 |
|
{ |
250 |
|
d_qim.addPendingLemma(lem, |
251 |
|
InferenceId::QUANTIFIERS_SYGUS_PBE_CONSTRUCT_SOL); |
252 |
|
} |
253 |
497 |
if (solSuccess) |
254 |
|
{ |
255 |
51 |
Assert(sol.size() == 1); |
256 |
51 |
candidate_values.push_back(sol[0]); |
257 |
|
} |
258 |
|
else |
259 |
|
{ |
260 |
446 |
return false; |
261 |
|
} |
262 |
|
} |
263 |
43 |
return true; |
264 |
|
} |
265 |
|
|
266 |
|
} |
267 |
|
} |
268 |
29337 |
} // namespace cvc5 |