1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Tim King, 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 |
|
* Extended theory interface. |
14 |
|
* |
15 |
|
* This implements a generic module, used by theory solvers, for performing |
16 |
|
* "context-dependent simplification", as described in Reynolds et al |
17 |
|
* "Designing Theory Solvers with Extensions", FroCoS 2017. |
18 |
|
* |
19 |
|
* At a high level, this technique implements a generic inference scheme based |
20 |
|
* on the combination of SAT-context-dependent equality reasoning and |
21 |
|
* SAT-context-indepedent rewriting. |
22 |
|
* |
23 |
|
* As a simple example, say |
24 |
|
* (1) TheoryStrings tells us that the following facts hold in the SAT context: |
25 |
|
* x = "A" ^ str.contains( str.++( x, z ), "B" ) = true. |
26 |
|
* (2) The Rewriter tells us that: |
27 |
|
* str.contains( str.++( "A", z ), "B" ) ----> str.contains( z, "B" ). |
28 |
|
* From this, this class may infer that the following lemma is T-valid: |
29 |
|
* x = "A" ^ str.contains( str.++( x, z ), "B" ) => str.contains( z, "B" ) |
30 |
|
*/ |
31 |
|
|
32 |
|
#include "cvc5_private.h" |
33 |
|
|
34 |
|
#ifndef CVC5__THEORY__EXT_THEORY_H |
35 |
|
#define CVC5__THEORY__EXT_THEORY_H |
36 |
|
|
37 |
|
#include <map> |
38 |
|
|
39 |
|
#include "context/cdhashmap.h" |
40 |
|
#include "context/cdhashset.h" |
41 |
|
#include "context/cdo.h" |
42 |
|
#include "context/context.h" |
43 |
|
#include "expr/node.h" |
44 |
|
#include "theory/theory_inference_manager.h" |
45 |
|
|
46 |
|
namespace cvc5 { |
47 |
|
namespace theory { |
48 |
|
|
49 |
|
class OutputChannel; |
50 |
|
|
51 |
|
/** Reasons for why a term was marked reduced */ |
52 |
|
enum class ExtReducedId |
53 |
|
{ |
54 |
|
UNKNOWN, |
55 |
|
// the extended function substitutes+rewrites to a constant |
56 |
|
SR_CONST, |
57 |
|
// the extended function was reduced by the callback |
58 |
|
REDUCTION, |
59 |
|
// the extended function substitutes+rewrites to zero |
60 |
|
ARITH_SR_ZERO, |
61 |
|
// the extended function substitutes+rewrites to a linear (non-zero) term |
62 |
|
ARITH_SR_LINEAR, |
63 |
|
// an extended string function substitutes+rewrites to a constant |
64 |
|
STRINGS_SR_CONST, |
65 |
|
// a negative str.contains was reduced to a disequality |
66 |
|
STRINGS_NEG_CTN_DEQ, |
67 |
|
// a positive str.contains was reduced to an equality |
68 |
|
STRINGS_POS_CTN, |
69 |
|
// a str.contains was subsumed by another based on a decomposition |
70 |
|
STRINGS_CTN_DECOMPOSE, |
71 |
|
// reduced via an intersection inference |
72 |
|
STRINGS_REGEXP_INTER, |
73 |
|
// subsumed due to intersection reasoning |
74 |
|
STRINGS_REGEXP_INTER_SUBSUME, |
75 |
|
// subsumed due to RE inclusion reasoning for positive memberships |
76 |
|
STRINGS_REGEXP_INCLUDE, |
77 |
|
// subsumed due to RE inclusion reasoning for negative memberships |
78 |
|
STRINGS_REGEXP_INCLUDE_NEG, |
79 |
|
}; |
80 |
|
/** |
81 |
|
* Converts an ext reduced identifier to a string. |
82 |
|
* |
83 |
|
* @param id The ext reduced identifier |
84 |
|
* @return The name of the ext reduced identifier |
85 |
|
*/ |
86 |
|
const char* toString(ExtReducedId id); |
87 |
|
|
88 |
|
/** |
89 |
|
* Writes an ext reduced identifier to a stream. |
90 |
|
* |
91 |
|
* @param out The stream to write to |
92 |
|
* @param id The ext reduced identifier to write to the stream |
93 |
|
* @return The stream |
94 |
|
*/ |
95 |
|
std::ostream& operator<<(std::ostream& out, ExtReducedId id); |
96 |
|
|
97 |
|
/** |
98 |
|
* A callback class for ExtTheory below. This class is responsible for |
99 |
|
* determining how to apply context-dependent simplification. |
100 |
|
*/ |
101 |
15138 |
class ExtTheoryCallback |
102 |
|
{ |
103 |
|
public: |
104 |
15133 |
virtual ~ExtTheoryCallback() {} |
105 |
|
/* |
106 |
|
* Get current substitution at an effort |
107 |
|
* @param effort The effort identifier |
108 |
|
* @param vars The variables to get a substitution for |
109 |
|
* @param subs The terms to substitute for variables, in order. This vector |
110 |
|
* should be updated to one the same size as vars. |
111 |
|
* @param exp The map containing the explanation for each variable. Together |
112 |
|
* with subs, we have that: |
113 |
|
* ( exp[vars[i]] => vars[i] = subs[i] ) holds for all i |
114 |
|
* @return true if any (non-identity) substitution was added to subs. |
115 |
|
*/ |
116 |
|
virtual bool getCurrentSubstitution(int effort, |
117 |
|
const std::vector<Node>& vars, |
118 |
|
std::vector<Node>& subs, |
119 |
|
std::map<Node, std::vector<Node> >& exp); |
120 |
|
|
121 |
|
/* |
122 |
|
* Is extended function n reduced? This returns true if n is reduced to a |
123 |
|
* form that requires no further interaction from the theory. |
124 |
|
* |
125 |
|
* @param effort The effort identifier |
126 |
|
* @param n The term to reduce |
127 |
|
* @param on The original form of n, before substitution |
128 |
|
* @param exp The explanation of on = n |
129 |
|
* @param id If this method returns true, then id is updated to the reason |
130 |
|
* why n was reduced. |
131 |
|
* @return true if n is reduced. |
132 |
|
*/ |
133 |
|
virtual bool isExtfReduced( |
134 |
|
int effort, Node n, Node on, std::vector<Node>& exp, ExtReducedId& id); |
135 |
|
|
136 |
|
/** |
137 |
|
* Get reduction for node n. |
138 |
|
* If return value is true, then n is reduced. |
139 |
|
* If satDep is updated to false, then n is reduced independent of the |
140 |
|
* SAT context (e.g. by a lemma that persists at this |
141 |
|
* user-context level). |
142 |
|
* If nr is non-null, then ( n = nr ) should be added as a lemma by caller, |
143 |
|
* and return value of this method should be true. |
144 |
|
*/ |
145 |
|
virtual bool getReduction(int effort, Node n, Node& nr, bool& satDep); |
146 |
|
}; |
147 |
|
|
148 |
|
/** Extended theory class |
149 |
|
* |
150 |
|
* This class is used for constructing generic extensions to theory solvers. |
151 |
|
* In particular, it provides methods for "context-dependent simplification" |
152 |
|
* and "model-based refinement". For details, see Reynolds et al "Design |
153 |
|
* Theory Solvers with Extensions", FroCoS 2017. |
154 |
|
* |
155 |
|
* It maintains: |
156 |
|
* (1) A set of "extended function" kinds (d_extf_kind), |
157 |
|
* (2) A database of active/inactive extended function terms (d_ext_func_terms) |
158 |
|
* that have been registered to the class. |
159 |
|
* |
160 |
|
* This class has methods doInferences/doReductions, which send out lemmas |
161 |
|
* based on the current set of active extended function terms. The former |
162 |
|
* is based on context-dependent simplification, where this class asks the |
163 |
|
* underlying theory for a "derivable substitution", whereby extended functions |
164 |
|
* may be reducable. |
165 |
|
*/ |
166 |
|
class ExtTheory |
167 |
|
{ |
168 |
|
using NodeBoolMap = context::CDHashMap<Node, bool>; |
169 |
|
using NodeExtReducedIdMap = context::CDHashMap<Node, ExtReducedId>; |
170 |
|
using NodeSet = context::CDHashSet<Node>; |
171 |
|
|
172 |
|
public: |
173 |
|
/** constructor |
174 |
|
* |
175 |
|
* If cacheEnabled is false, we do not cache results of getSubstitutedTerm. |
176 |
|
*/ |
177 |
|
ExtTheory(ExtTheoryCallback& p, |
178 |
|
context::Context* c, |
179 |
|
context::UserContext* u, |
180 |
|
TheoryInferenceManager& im); |
181 |
15133 |
virtual ~ExtTheory() {} |
182 |
|
/** Tells this class to treat terms with Kind k as extended functions */ |
183 |
219892 |
void addFunctionKind(Kind k) { d_extf_kind[k] = true; } |
184 |
|
/** Is kind k treated as an extended function by this class? */ |
185 |
152 |
bool hasFunctionKind(Kind k) const |
186 |
|
{ |
187 |
152 |
return d_extf_kind.find(k) != d_extf_kind.end(); |
188 |
|
} |
189 |
|
/** register term |
190 |
|
* |
191 |
|
* Registers term n with this class. Adds n to the set of extended function |
192 |
|
* terms known by this class (d_ext_func_terms) if n is an extended function, |
193 |
|
* that is, if addFunctionKind( n.getKind() ) was called. |
194 |
|
*/ |
195 |
|
void registerTerm(Node n); |
196 |
|
/** set n as reduced/inactive |
197 |
|
* |
198 |
|
* If satDep = false, then n remains inactive in the duration of this |
199 |
|
* user-context level |
200 |
|
*/ |
201 |
|
void markReduced(Node n, ExtReducedId rid, bool satDep = true); |
202 |
|
/** getSubstitutedTerms |
203 |
|
* |
204 |
|
* input : effort, terms |
205 |
|
* output : sterms, exp, where ( exp[i] => terms[i] = sterms[i] ) for all i. |
206 |
|
* |
207 |
|
* For each i, sterms[i] = term[i] * sigma for some "derivable substitution" |
208 |
|
* sigma. We obtain derivable substitutions and their explanations via calls |
209 |
|
* to the underlying theory's Theory::getCurrentSubstitution method. |
210 |
|
*/ |
211 |
|
void getSubstitutedTerms(int effort, |
212 |
|
const std::vector<Node>& terms, |
213 |
|
std::vector<Node>& sterms, |
214 |
|
std::vector<std::vector<Node> >& exp); |
215 |
|
/** |
216 |
|
* Same as above, but for a single term. We return the substituted form of |
217 |
|
* term and add its explanation to exp. |
218 |
|
*/ |
219 |
|
Node getSubstitutedTerm(int effort, Node term, std::vector<Node>& exp); |
220 |
|
/** doInferences |
221 |
|
* |
222 |
|
* This function performs "context-dependent simplification". The method takes |
223 |
|
* as input: |
224 |
|
* effort: an identifier used to determine which terms we reduce and the |
225 |
|
* form of the derivable substitution we will use, |
226 |
|
* terms: the set of terms to simplify, |
227 |
|
* batch: if this flag is true, we send lemmas for all terms; if it is false |
228 |
|
* we send a lemma for the first applicable term. |
229 |
|
* |
230 |
|
* Sends rewriting lemmas of the form ( exp => t = c ) where t is in terms |
231 |
|
* and c is a constant, c = rewrite( t*sigma ) where exp |= sigma. These |
232 |
|
* lemmas are determined by asking the underlying theory for a derivable |
233 |
|
* substitution (through getCurrentSubstitution with getSubstitutedTerm) |
234 |
|
* above, applying this substitution to terms in terms, rewriting |
235 |
|
* the result and checking with the theory whether the resulting term is |
236 |
|
* in reduced form (isExtfReduced). |
237 |
|
* |
238 |
|
* This method adds the extended terms that are still active to nred, and |
239 |
|
* returns true if and only if a lemma of the above form was sent. |
240 |
|
*/ |
241 |
|
bool doInferences(int effort, |
242 |
|
const std::vector<Node>& terms, |
243 |
|
std::vector<Node>& nred, |
244 |
|
bool batch = true); |
245 |
|
/** |
246 |
|
* Calls the above function, where terms is getActive(), the set of currently |
247 |
|
* active terms. |
248 |
|
*/ |
249 |
|
bool doInferences(int effort, std::vector<Node>& nred, bool batch = true); |
250 |
|
/** doReductions |
251 |
|
* |
252 |
|
* This method has the same interface as doInferences. In contrast to |
253 |
|
* doInfereces, this method will send reduction lemmas of the form ( t = t' ) |
254 |
|
* where t is in terms and t' is an equivalent reduced term. |
255 |
|
*/ |
256 |
|
bool doReductions(int effort, |
257 |
|
const std::vector<Node>& terms, |
258 |
|
std::vector<Node>& nred, |
259 |
|
bool batch = true); |
260 |
|
bool doReductions(int effort, std::vector<Node>& nred, bool batch = true); |
261 |
|
|
262 |
|
/** get the set of all extended function terms from d_ext_func_terms */ |
263 |
|
void getTerms(std::vector<Node>& terms); |
264 |
|
/** is there at least one active extended function term? */ |
265 |
|
bool hasActiveTerm() const; |
266 |
|
/** is n an active extended function term? */ |
267 |
|
bool isActive(Node n) const; |
268 |
|
/** |
269 |
|
* Same as above, but rid is updated to the reason if this method returns |
270 |
|
* false. |
271 |
|
*/ |
272 |
|
bool isActive(Node n, ExtReducedId& rid) const; |
273 |
|
/** get the set of active terms from d_ext_func_terms */ |
274 |
|
std::vector<Node> getActive() const; |
275 |
|
/** get the set of active terms from d_ext_func_terms of kind k */ |
276 |
|
std::vector<Node> getActive(Kind k) const; |
277 |
|
|
278 |
|
private: |
279 |
|
/** returns the set of variable subterms of n */ |
280 |
|
static std::vector<Node> collectVars(Node n); |
281 |
|
/** is n context dependent inactive? */ |
282 |
|
bool isContextIndependentInactive(Node n) const; |
283 |
|
/** |
284 |
|
* Same as above, but rid is updated to the reason if this method returns |
285 |
|
* true. |
286 |
|
*/ |
287 |
|
bool isContextIndependentInactive(Node n, ExtReducedId& rid) const; |
288 |
|
/** do inferences internal */ |
289 |
|
bool doInferencesInternal(int effort, |
290 |
|
const std::vector<Node>& terms, |
291 |
|
std::vector<Node>& nred, |
292 |
|
bool batch, |
293 |
|
bool isRed); |
294 |
|
/** send lemma on the output channel */ |
295 |
|
bool sendLemma(Node lem, InferenceId id, bool preprocess = false); |
296 |
|
/** reference to the callback */ |
297 |
|
ExtTheoryCallback& d_parent; |
298 |
|
/** inference manager used to send lemmas */ |
299 |
|
TheoryInferenceManager& d_im; |
300 |
|
/** the true node */ |
301 |
|
Node d_true; |
302 |
|
/** extended function terms, map to whether they are active */ |
303 |
|
NodeBoolMap d_ext_func_terms; |
304 |
|
/** mapping to why extended function terms are inactive */ |
305 |
|
NodeExtReducedIdMap d_extfExtReducedIdMap; |
306 |
|
/** |
307 |
|
* The set of terms from d_ext_func_terms that are SAT-context-independent |
308 |
|
* inactive. For instance, a term t is SAT-context-independent inactive if |
309 |
|
* a reduction lemma of the form t = t' was added in this user-context level. |
310 |
|
*/ |
311 |
|
NodeExtReducedIdMap d_ci_inactive; |
312 |
|
/** |
313 |
|
* Watched term for checking if any non-reduced extended functions exist. |
314 |
|
* This is an arbitrary active member of d_ext_func_terms. |
315 |
|
*/ |
316 |
|
context::CDO<Node> d_has_extf; |
317 |
|
/** the set of kinds we are treated as extended functions */ |
318 |
|
std::map<Kind, bool> d_extf_kind; |
319 |
|
/** information for each term in d_ext_func_terms */ |
320 |
21254 |
class ExtfInfo |
321 |
|
{ |
322 |
|
public: |
323 |
|
/** all variables in this term */ |
324 |
|
std::vector<Node> d_vars; |
325 |
|
}; |
326 |
|
std::map<Node, ExtfInfo> d_extf_info; |
327 |
|
|
328 |
|
// cache of all lemmas sent |
329 |
|
NodeSet d_lemmas; |
330 |
|
NodeSet d_pp_lemmas; |
331 |
|
}; |
332 |
|
|
333 |
|
} // namespace theory |
334 |
|
} // namespace cvc5 |
335 |
|
|
336 |
|
#endif /* CVC5__THEORY__EXT_THEORY_H */ |