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