1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Morgan Deters, Tim King |
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 |
|
* Theory of UF with cardinality. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY_UF_STRONG_SOLVER_H |
19 |
|
#define CVC5__THEORY_UF_STRONG_SOLVER_H |
20 |
|
|
21 |
|
#include "context/cdhashmap.h" |
22 |
|
#include "context/context.h" |
23 |
|
#include "theory/decision_strategy.h" |
24 |
|
#include "theory/theory.h" |
25 |
|
#include "util/statistics_stats.h" |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace theory { |
29 |
|
namespace uf { |
30 |
|
|
31 |
|
class TheoryUF; |
32 |
|
|
33 |
|
/** |
34 |
|
* This module implements a theory solver for UF with cardinality constraints. |
35 |
|
* For high level details, see Reynolds et al "Finite Model Finding in SMT", |
36 |
|
* CAV 2013, or Reynolds dissertation "Finite Model Finding in Satisfiability |
37 |
|
* Modulo Theories". |
38 |
|
*/ |
39 |
|
class CardinalityExtension |
40 |
|
{ |
41 |
|
protected: |
42 |
|
typedef context::CDHashMap<Node, bool> NodeBoolMap; |
43 |
|
typedef context::CDHashMap<Node, int> NodeIntMap; |
44 |
|
|
45 |
|
public: |
46 |
|
/** |
47 |
|
* Information for incremental conflict/clique finding for a |
48 |
|
* particular sort. |
49 |
|
*/ |
50 |
|
class SortModel |
51 |
|
{ |
52 |
|
private: |
53 |
|
std::map< Node, std::vector< int > > d_totality_lems; |
54 |
|
std::map< TypeNode, std::map< int, std::vector< Node > > > d_sym_break_terms; |
55 |
|
std::map< Node, int > d_sym_break_index; |
56 |
|
|
57 |
|
public: |
58 |
|
/** |
59 |
|
* A partition of the current equality graph for which cliques |
60 |
|
* can occur internally. |
61 |
|
*/ |
62 |
|
class Region |
63 |
|
{ |
64 |
|
public: |
65 |
|
/** information stored about each node in region */ |
66 |
|
class RegionNodeInfo { |
67 |
|
public: |
68 |
|
/** disequality list for node */ |
69 |
|
class DiseqList { |
70 |
|
public: |
71 |
19428 |
DiseqList( context::Context* c ) |
72 |
19428 |
: d_size( c, 0 ), d_disequalities( c ) {} |
73 |
19428 |
~DiseqList(){} |
74 |
|
|
75 |
374216 |
void setDisequal( Node n, bool valid ){ |
76 |
374216 |
Assert((!isSet(n)) || getDisequalityValue(n) != valid); |
77 |
374216 |
d_disequalities[ n ] = valid; |
78 |
374216 |
d_size = d_size + ( valid ? 1 : -1 ); |
79 |
374216 |
} |
80 |
1150430 |
bool isSet(Node n) const { |
81 |
1150430 |
return d_disequalities.find(n) != d_disequalities.end(); |
82 |
|
} |
83 |
317284 |
bool getDisequalityValue(Node n) const { |
84 |
317284 |
Assert(isSet(n)); |
85 |
317284 |
return (*(d_disequalities.find(n))).second; |
86 |
|
} |
87 |
|
|
88 |
200227 |
int size() const { return d_size; } |
89 |
|
|
90 |
|
typedef NodeBoolMap::iterator iterator; |
91 |
179217 |
iterator begin() { return d_disequalities.begin(); } |
92 |
412255 |
iterator end() { return d_disequalities.end(); } |
93 |
|
|
94 |
|
private: |
95 |
|
context::CDO< int > d_size; |
96 |
|
NodeBoolMap d_disequalities; |
97 |
|
}; /* class DiseqList */ |
98 |
|
public: |
99 |
|
/** constructor */ |
100 |
9714 |
RegionNodeInfo( context::Context* c ) |
101 |
9714 |
: d_internal(c), d_external(c), d_valid(c, true) { |
102 |
9714 |
d_disequalities[0] = &d_internal; |
103 |
9714 |
d_disequalities[1] = &d_external; |
104 |
9714 |
} |
105 |
9714 |
~RegionNodeInfo(){} |
106 |
|
|
107 |
60675 |
int getNumDisequalities() const { |
108 |
60675 |
return d_disequalities[0]->size() + d_disequalities[1]->size(); |
109 |
|
} |
110 |
46921 |
int getNumExternalDisequalities() const { |
111 |
46921 |
return d_disequalities[0]->size(); |
112 |
|
} |
113 |
31956 |
int getNumInternalDisequalities() const { |
114 |
31956 |
return d_disequalities[1]->size(); |
115 |
|
} |
116 |
|
|
117 |
1046916 |
bool valid() const { return d_valid; } |
118 |
82182 |
void setValid(bool valid) { d_valid = valid; } |
119 |
|
|
120 |
1012363 |
DiseqList* get(unsigned i) { return d_disequalities[i]; } |
121 |
|
|
122 |
|
private: |
123 |
|
DiseqList d_internal; |
124 |
|
DiseqList d_external; |
125 |
|
context::CDO< bool > d_valid; |
126 |
|
DiseqList* d_disequalities[2]; |
127 |
|
}; /* class RegionNodeInfo */ |
128 |
|
|
129 |
|
private: |
130 |
|
/** conflict find pointer */ |
131 |
|
SortModel* d_cf; |
132 |
|
|
133 |
|
context::CDO<size_t> d_testCliqueSize; |
134 |
|
context::CDO< unsigned > d_splitsSize; |
135 |
|
//a postulated clique |
136 |
|
NodeBoolMap d_testClique; |
137 |
|
//disequalities needed for this clique to happen |
138 |
|
NodeBoolMap d_splits; |
139 |
|
//number of valid representatives in this region |
140 |
|
context::CDO<size_t> d_reps_size; |
141 |
|
//total disequality size (external) |
142 |
|
context::CDO< unsigned > d_total_diseq_external; |
143 |
|
//total disequality size (internal) |
144 |
|
context::CDO< unsigned > d_total_diseq_internal; |
145 |
|
/** set rep */ |
146 |
|
void setRep( Node n, bool valid ); |
147 |
|
//region node infomation |
148 |
|
std::map< Node, RegionNodeInfo* > d_nodes; |
149 |
|
//whether region is valid |
150 |
|
context::CDO< bool > d_valid; |
151 |
|
|
152 |
|
public: |
153 |
|
//constructor |
154 |
|
Region( SortModel* cf, context::Context* c ); |
155 |
|
virtual ~Region(); |
156 |
|
|
157 |
|
typedef std::map< Node, RegionNodeInfo* >::iterator iterator; |
158 |
75730 |
iterator begin() { return d_nodes.begin(); } |
159 |
522512 |
iterator end() { return d_nodes.end(); } |
160 |
|
|
161 |
|
typedef NodeBoolMap::iterator split_iterator; |
162 |
3619 |
split_iterator begin_splits() { return d_splits.begin(); } |
163 |
23484 |
split_iterator end_splits() { return d_splits.end(); } |
164 |
|
|
165 |
|
/** Returns a RegionInfo. */ |
166 |
32216 |
RegionNodeInfo* getRegionInfo(Node n) { |
167 |
32216 |
Assert(d_nodes.find(n) != d_nodes.end()); |
168 |
32216 |
return (* (d_nodes.find(n))).second; |
169 |
|
} |
170 |
|
|
171 |
|
/** Returns whether or not d_valid is set in current context. */ |
172 |
3025503 |
bool valid() const { return d_valid; } |
173 |
|
|
174 |
|
/** Sets d_valid to the value valid in the current context.*/ |
175 |
43463 |
void setValid(bool valid) { d_valid = valid; } |
176 |
|
|
177 |
|
/** add rep */ |
178 |
|
void addRep( Node n ); |
179 |
|
//take node from region |
180 |
|
void takeNode( Region* r, Node n ); |
181 |
|
//merge with other region |
182 |
|
void combine( Region* r ); |
183 |
|
/** merge */ |
184 |
|
void setEqual( Node a, Node b ); |
185 |
|
//set n1 != n2 to value 'valid', type is whether it is internal/external |
186 |
|
void setDisequal( Node n1, Node n2, int type, bool valid ); |
187 |
|
//get num reps |
188 |
165861 |
size_t getNumReps() const { return d_reps_size; } |
189 |
|
//get test clique size |
190 |
|
size_t getTestCliqueSize() const { return d_testCliqueSize; } |
191 |
|
// has representative |
192 |
254202 |
bool hasRep( Node n ) { |
193 |
254202 |
return d_nodes.find(n) != d_nodes.end() && d_nodes[n]->valid(); |
194 |
|
} |
195 |
|
// is disequal |
196 |
|
bool isDisequal( Node n1, Node n2, int type ); |
197 |
|
/** get must merge */ |
198 |
|
bool getMustCombine( int cardinality ); |
199 |
|
/** has splits */ |
200 |
2027 |
bool hasSplits() { return d_splitsSize>0; } |
201 |
|
/** get external disequalities */ |
202 |
|
void getNumExternalDisequalities(std::map< Node, int >& num_ext_disequalities ); |
203 |
|
/** check for cliques */ |
204 |
|
bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique ); |
205 |
|
//print debug |
206 |
|
void debugPrint( const char* c, bool incClique = false ); |
207 |
|
|
208 |
|
// Returns the first key in d_nodes. |
209 |
108 |
Node frontKey() const { return d_nodes.begin()->first; } |
210 |
|
}; /* class Region */ |
211 |
|
|
212 |
|
private: |
213 |
|
/** the type this model is for */ |
214 |
|
TypeNode d_type; |
215 |
|
/** Reference to the state object */ |
216 |
|
TheoryState& d_state; |
217 |
|
/** Reference to the inference manager */ |
218 |
|
TheoryInferenceManager& d_im; |
219 |
|
/** Pointer to the cardinality extension that owns this. */ |
220 |
|
CardinalityExtension* d_thss; |
221 |
|
/** regions used to d_region_index */ |
222 |
|
context::CDO<size_t> d_regions_index; |
223 |
|
/** vector of regions */ |
224 |
|
std::vector< Region* > d_regions; |
225 |
|
/** map from Nodes to index of d_regions they exist in, -1 means invalid */ |
226 |
|
NodeIntMap d_regions_map; |
227 |
|
/** the score for each node for splitting */ |
228 |
|
NodeIntMap d_split_score; |
229 |
|
/** number of valid disequalities in d_disequalities */ |
230 |
|
context::CDO< unsigned > d_disequalities_index; |
231 |
|
/** list of all disequalities */ |
232 |
|
std::vector< Node > d_disequalities; |
233 |
|
/** number of representatives in all regions */ |
234 |
|
context::CDO< unsigned > d_reps; |
235 |
|
|
236 |
|
/** get number of disequalities from node n to region ri */ |
237 |
|
int getNumDisequalitiesToRegion( Node n, int ri ); |
238 |
|
/** get number of disequalities from Region r to other regions */ |
239 |
|
void getDisequalitiesToRegions( int ri, std::map< int, int >& regions_diseq ); |
240 |
|
/** is valid */ |
241 |
354047 |
bool isValid( int ri ) { |
242 |
354047 |
return ri>=0 && ri<(int)d_regions_index && d_regions[ ri ]->valid(); |
243 |
|
} |
244 |
|
/** set split score */ |
245 |
|
void setSplitScore( Node n, int s ); |
246 |
|
/** check if we need to combine region ri */ |
247 |
|
void checkRegion( int ri, bool checkCombine = true ); |
248 |
|
/** force combine region */ |
249 |
|
int forceCombineRegion( int ri, bool useDensity = true ); |
250 |
|
/** merge regions */ |
251 |
|
int combineRegions( int ai, int bi ); |
252 |
|
/** move node n to region ri */ |
253 |
|
void moveNode( Node n, int ri ); |
254 |
|
/** allocate cardinality */ |
255 |
|
void allocateCardinality(); |
256 |
|
/** |
257 |
|
* Add splits. Returns |
258 |
|
* 0 = no split, |
259 |
|
* -1 = entailed disequality added, or |
260 |
|
* 1 = split added. |
261 |
|
*/ |
262 |
|
int addSplit(Region* r); |
263 |
|
/** add clique lemma */ |
264 |
|
void addCliqueLemma(std::vector<Node>& clique); |
265 |
|
/** cardinality */ |
266 |
|
context::CDO<uint32_t> d_cardinality; |
267 |
|
/** cardinality lemma term */ |
268 |
|
Node d_cardinality_term; |
269 |
|
/** cardinality literals */ |
270 |
|
std::map<uint32_t, Node> d_cardinality_literal; |
271 |
|
/** whether a positive cardinality constraint has been asserted */ |
272 |
|
context::CDO< bool > d_hasCard; |
273 |
|
/** clique lemmas that have been asserted */ |
274 |
|
std::map< int, std::vector< std::vector< Node > > > d_cliques; |
275 |
|
/** maximum negatively asserted cardinality */ |
276 |
|
context::CDO<uint32_t> d_maxNegCard; |
277 |
|
/** list of fresh representatives allocated */ |
278 |
|
std::vector< Node > d_fresh_aloc_reps; |
279 |
|
/** whether we are initialized */ |
280 |
|
context::CDO< bool > d_initialized; |
281 |
|
/** simple check cardinality */ |
282 |
|
void simpleCheckCardinality(); |
283 |
|
|
284 |
|
public: |
285 |
|
SortModel(Node n, |
286 |
|
TheoryState& state, |
287 |
|
TheoryInferenceManager& im, |
288 |
|
CardinalityExtension* thss); |
289 |
|
virtual ~SortModel(); |
290 |
|
/** initialize */ |
291 |
|
void initialize(); |
292 |
|
/** new node */ |
293 |
|
void newEqClass( Node n ); |
294 |
|
/** merge */ |
295 |
|
void merge( Node a, Node b ); |
296 |
|
/** assert terms are disequal */ |
297 |
|
void assertDisequal( Node a, Node b, Node reason ); |
298 |
|
/** are disequal */ |
299 |
|
bool areDisequal( Node a, Node b ); |
300 |
|
/** check */ |
301 |
|
void check(Theory::Effort level); |
302 |
|
/** presolve */ |
303 |
|
void presolve(); |
304 |
|
/** assert cardinality */ |
305 |
|
void assertCardinality(uint32_t c, bool val); |
306 |
|
/** get cardinality */ |
307 |
|
uint32_t getCardinality() const { return d_cardinality; } |
308 |
|
/** has cardinality */ |
309 |
|
bool hasCardinalityAsserted() const { return d_hasCard; } |
310 |
|
/** get cardinality term */ |
311 |
9040 |
Node getCardinalityTerm() const { return d_cardinality_term; } |
312 |
|
/** get cardinality literal */ |
313 |
|
Node getCardinalityLiteral(uint32_t c); |
314 |
|
/** get maximum negative cardinality */ |
315 |
64257 |
uint32_t getMaximumNegativeCardinality() const |
316 |
|
{ |
317 |
64257 |
return d_maxNegCard.get(); |
318 |
|
} |
319 |
|
//print debug |
320 |
|
void debugPrint( const char* c ); |
321 |
|
/** |
322 |
|
* Check at last call effort. This will verify that the model is minimal. |
323 |
|
* This return lemmas if there are terms in the model that the cardinality |
324 |
|
* extension was not notified of. |
325 |
|
* |
326 |
|
* @return false if current model is not minimal. In this case, lemmas are |
327 |
|
* sent on the output channel of the UF theory. |
328 |
|
*/ |
329 |
|
bool checkLastCall(); |
330 |
|
/** get number of regions (for debugging) */ |
331 |
|
int getNumRegions(); |
332 |
|
|
333 |
|
private: |
334 |
|
/** |
335 |
|
* Decision strategy for cardinality constraints. This asserts |
336 |
|
* the minimal constraint positively in the SAT context. For details, see |
337 |
|
* Section 6.3 of Reynolds et al, "Constraint Solving for Finite Model |
338 |
|
* Finding in SMT Solvers", TPLP 2017. |
339 |
|
*/ |
340 |
920 |
class CardinalityDecisionStrategy : public DecisionStrategyFmf |
341 |
|
{ |
342 |
|
public: |
343 |
|
CardinalityDecisionStrategy(Node t, |
344 |
|
context::Context* satContext, |
345 |
|
Valuation valuation); |
346 |
|
/** make literal (the i^th combined cardinality literal) */ |
347 |
|
Node mkLiteral(unsigned i) override; |
348 |
|
/** identify */ |
349 |
|
std::string identify() const override; |
350 |
|
|
351 |
|
private: |
352 |
|
/** the cardinality term */ |
353 |
|
Node d_cardinality_term; |
354 |
|
}; |
355 |
|
/** cardinality decision strategy */ |
356 |
|
std::unique_ptr<CardinalityDecisionStrategy> d_c_dec_strat; |
357 |
|
}; /** class SortModel */ |
358 |
|
|
359 |
|
public: |
360 |
|
CardinalityExtension(TheoryState& state, |
361 |
|
TheoryInferenceManager& im, |
362 |
|
TheoryUF* th); |
363 |
|
~CardinalityExtension(); |
364 |
|
/** get theory */ |
365 |
22316 |
TheoryUF* getTheory() { return d_th; } |
366 |
|
/** new node */ |
367 |
|
void newEqClass( Node n ); |
368 |
|
/** merge */ |
369 |
|
void merge( Node a, Node b ); |
370 |
|
/** assert terms are disequal */ |
371 |
|
void assertDisequal( Node a, Node b, Node reason ); |
372 |
|
/** assert node */ |
373 |
|
void assertNode( Node n, bool isDecision ); |
374 |
|
/** are disequal */ |
375 |
|
bool areDisequal( Node a, Node b ); |
376 |
|
/** check */ |
377 |
|
void check( Theory::Effort level ); |
378 |
|
/** presolve */ |
379 |
|
void presolve(); |
380 |
|
/** preregister a term */ |
381 |
|
void preRegisterTerm( TNode n ); |
382 |
|
/** identify */ |
383 |
|
std::string identify() const { return std::string("CardinalityExtension"); } |
384 |
|
//print debug |
385 |
|
void debugPrint( const char* c ); |
386 |
|
/** get cardinality for node */ |
387 |
|
int getCardinality( Node n ); |
388 |
|
/** get cardinality for type */ |
389 |
|
int getCardinality( TypeNode tn ); |
390 |
|
/** has eqc */ |
391 |
|
bool hasEqc(Node a); |
392 |
|
|
393 |
|
class Statistics { |
394 |
|
public: |
395 |
|
IntStat d_clique_conflicts; |
396 |
|
IntStat d_clique_lemmas; |
397 |
|
IntStat d_split_lemmas; |
398 |
|
IntStat d_max_model_size; |
399 |
|
Statistics(); |
400 |
|
}; |
401 |
|
/** statistics class */ |
402 |
|
Statistics d_statistics; |
403 |
|
|
404 |
|
private: |
405 |
|
/** get sort model */ |
406 |
|
SortModel* getSortModel(Node n); |
407 |
|
/** initialize */ |
408 |
|
void initializeCombinedCardinality(); |
409 |
|
/** check */ |
410 |
|
void checkCombinedCardinality(); |
411 |
|
/** ensure eqc */ |
412 |
|
void ensureEqc(SortModel* c, Node a); |
413 |
|
/** ensure eqc for all subterms of n */ |
414 |
|
void ensureEqcRec(Node n); |
415 |
|
|
416 |
|
/** Reference to the state object */ |
417 |
|
TheoryState& d_state; |
418 |
|
/** Reference to the inference manager */ |
419 |
|
TheoryInferenceManager& d_im; |
420 |
|
/** theory uf pointer */ |
421 |
|
TheoryUF* d_th; |
422 |
|
/** rep model structure, one for each type */ |
423 |
|
std::map<TypeNode, SortModel*> d_rep_model; |
424 |
|
|
425 |
|
/** minimum positive combined cardinality */ |
426 |
|
context::CDO<uint32_t> d_min_pos_com_card; |
427 |
|
/** Whether the field above has been set */ |
428 |
|
context::CDO<bool> d_min_pos_com_card_set; |
429 |
|
/** |
430 |
|
* Decision strategy for combined cardinality constraints. This asserts |
431 |
|
* the minimal combined cardinality constraint positively in the SAT |
432 |
|
* context. It is enabled by options::ufssFairness(). For details, see |
433 |
|
* the extension to multiple sorts in Section 6.3 of Reynolds et al, |
434 |
|
* "Constraint Solving for Finite Model Finding in SMT Solvers", TPLP 2017. |
435 |
|
*/ |
436 |
556 |
class CombinedCardinalityDecisionStrategy : public DecisionStrategyFmf |
437 |
|
{ |
438 |
|
public: |
439 |
|
CombinedCardinalityDecisionStrategy(context::Context* satContext, |
440 |
|
Valuation valuation); |
441 |
|
/** make literal (the i^th combined cardinality literal) */ |
442 |
|
Node mkLiteral(unsigned i) override; |
443 |
|
/** identify */ |
444 |
|
std::string identify() const override; |
445 |
|
}; |
446 |
|
/** combined cardinality decision strategy */ |
447 |
|
std::unique_ptr<CombinedCardinalityDecisionStrategy> d_cc_dec_strat; |
448 |
|
/** Have we initialized combined cardinality? */ |
449 |
|
context::CDO<bool> d_initializedCombinedCardinality; |
450 |
|
|
451 |
|
/** cardinality literals for which we have added */ |
452 |
|
NodeBoolMap d_card_assertions_eqv_lemma; |
453 |
|
/** the master monotone type (if ufssFairnessMonotone enabled) */ |
454 |
|
TypeNode d_tn_mono_master; |
455 |
|
std::map<TypeNode, bool> d_tn_mono_slave; |
456 |
|
/** The minimum positive asserted master cardinality */ |
457 |
|
context::CDO<uint32_t> d_min_pos_tn_master_card; |
458 |
|
/** Whether the field above has been set */ |
459 |
|
context::CDO<bool> d_min_pos_tn_master_card_set; |
460 |
|
/** relevant eqc */ |
461 |
|
NodeBoolMap d_rel_eqc; |
462 |
|
}; /* class CardinalityExtension */ |
463 |
|
|
464 |
|
} // namespace uf |
465 |
|
} // namespace theory |
466 |
|
} // namespace cvc5 |
467 |
|
|
468 |
|
#endif /* CVC5__THEORY_UF_STRONG_SOLVER_H */ |