Use state and inference manager in UF CardinalityExtension (#5036)
[cvc5.git] / src / theory / uf / cardinality_extension.cpp
1 /********************* */
2 /*! \file cardinality_extension.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Morgan Deters, Tim King
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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
11 **
12 ** \brief Implementation of theory of UF with cardinality.
13 **/
14
15 #include "theory/uf/cardinality_extension.h"
16
17 #include "options/uf_options.h"
18 #include "theory/uf/theory_uf.h"
19 #include "theory/uf/equality_engine.h"
20 #include "theory/theory_engine.h"
21 #include "theory/quantifiers_engine.h"
22 #include "theory/quantifiers/term_database.h"
23 #include "theory/theory_model.h"
24
25 //#define ONE_SPLIT_REGION
26 //#define COMBINE_REGIONS_SMALL_INTO_LARGE
27 //#define LAZY_REL_EQC
28
29 using namespace std;
30 using namespace CVC4::kind;
31 using namespace CVC4::context;
32
33
34 namespace CVC4 {
35 namespace theory {
36 namespace uf {
37
38 /* These are names are unambigious are we use abbreviations. */
39 typedef CardinalityExtension::SortModel SortModel;
40 typedef SortModel::Region Region;
41 typedef Region::RegionNodeInfo RegionNodeInfo;
42 typedef RegionNodeInfo::DiseqList DiseqList;
43
44 Region::Region(SortModel* cf, context::Context* c)
45 : d_cf( cf )
46 , d_testCliqueSize( c, 0 )
47 , d_splitsSize( c, 0 )
48 , d_testClique( c )
49 , d_splits( c )
50 , d_reps_size( c, 0 )
51 , d_total_diseq_external( c, 0 )
52 , d_total_diseq_internal( c, 0 )
53 , d_valid( c, true ) {}
54
55 Region::~Region() {
56 for(iterator i = begin(), iend = end(); i != iend; ++i) {
57 RegionNodeInfo* regionNodeInfo = (*i).second;
58 delete regionNodeInfo;
59 }
60 d_nodes.clear();
61 }
62
63 void Region::addRep( Node n ) {
64 setRep( n, true );
65 }
66
67 void Region::takeNode( Region* r, Node n ){
68 Assert(!hasRep(n));
69 Assert(r->hasRep(n));
70 //add representative
71 setRep( n, true );
72 //take disequalities from r
73 RegionNodeInfo* rni = r->d_nodes[n];
74 for( int t=0; t<2; t++ ){
75 DiseqList* del = rni->get(t);
76 for(DiseqList::iterator it = del->begin(); it != del->end(); ++it ){
77 if( (*it).second ){
78 r->setDisequal( n, (*it).first, t, false );
79 if( t==0 ){
80 if( hasRep( (*it).first ) ){
81 setDisequal( (*it).first, n, 0, false );
82 setDisequal( (*it).first, n, 1, true );
83 setDisequal( n, (*it).first, 1, true );
84 }else{
85 setDisequal( n, (*it).first, 0, true );
86 }
87 }else{
88 r->setDisequal( (*it).first, n, 1, false );
89 r->setDisequal( (*it).first, n, 0, true );
90 setDisequal( n, (*it).first, 0, true );
91 }
92 }
93 }
94 }
95 //remove representative
96 r->setRep( n, false );
97 }
98
99 void Region::combine( Region* r ){
100 //take all nodes from r
101 for(Region::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it) {
102 if( it->second->valid() ){
103 setRep( it->first, true );
104 }
105 }
106 for(Region::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it){
107 if( it->second->valid() ){
108 //take disequalities from r
109 Node n = it->first;
110 RegionNodeInfo* rni = it->second;
111 for( int t=0; t<2; t++ ){
112 RegionNodeInfo::DiseqList* del = rni->get(t);
113 for( RegionNodeInfo::DiseqList::iterator it2 = del->begin(),
114 it2end = del->end(); it2 != it2end; ++it2 ){
115 if( (*it2).second ){
116 if( t==0 && hasRep( (*it2).first ) ){
117 setDisequal( (*it2).first, n, 0, false );
118 setDisequal( (*it2).first, n, 1, true );
119 setDisequal( n, (*it2).first, 1, true );
120 }else{
121 setDisequal( n, (*it2).first, t, true );
122 }
123 }
124 }
125 }
126 }
127 }
128 r->d_valid = false;
129 }
130
131 /** setEqual */
132 void Region::setEqual( Node a, Node b ){
133 Assert(hasRep(a) && hasRep(b));
134 //move disequalities of b over to a
135 for( int t=0; t<2; t++ ){
136 DiseqList* del = d_nodes[b]->get(t);
137 for( DiseqList::iterator it = del->begin(); it != del->end(); ++it ){
138 if( (*it).second ){
139 Node n = (*it).first;
140 //get the region that contains the endpoint of the disequality b != ...
141 Region* nr = d_cf->d_regions[ d_cf->d_regions_map[ n ] ];
142 if( !isDisequal( a, n, t ) ){
143 setDisequal( a, n, t, true );
144 nr->setDisequal( n, a, t, true );
145 }
146 setDisequal( b, n, t, false );
147 nr->setDisequal( n, b, t, false );
148 }
149 }
150 }
151 //remove b from representatives
152 setRep( b, false );
153 }
154
155 void Region::setDisequal( Node n1, Node n2, int type, bool valid ){
156 //Debug("uf-ss-region-debug") << "set disequal " << n1 << " " << n2 << " "
157 // << type << " " << valid << std::endl;
158 //debugPrint("uf-ss-region-debug");
159 //Assert( isDisequal( n1, n2, type )!=valid );
160 if( isDisequal( n1, n2, type )!=valid ){ //DO_THIS: make assertion
161 d_nodes[ n1 ]->get(type)->setDisequal( n2, valid );
162 if( type==0 ){
163 d_total_diseq_external = d_total_diseq_external + ( valid ? 1 : -1 );
164 }else{
165 d_total_diseq_internal = d_total_diseq_internal + ( valid ? 1 : -1 );
166 if( valid ){
167 //if they are both a part of testClique, then remove split
168 if( d_testClique.find( n1 )!=d_testClique.end() && d_testClique[n1] &&
169 d_testClique.find( n2 )!=d_testClique.end() && d_testClique[n2] ){
170 Node eq = NodeManager::currentNM()->mkNode( EQUAL, n1, n2 );
171 if( d_splits.find( eq )!=d_splits.end() && d_splits[ eq ] ){
172 Debug("uf-ss-debug") << "removing split for " << n1 << " " << n2
173 << std::endl;
174 d_splits[ eq ] = false;
175 d_splitsSize = d_splitsSize - 1;
176 }
177 }
178 }
179 }
180 }
181 }
182
183 void Region::setRep( Node n, bool valid ) {
184 Assert(hasRep(n) != valid);
185 if( valid && d_nodes.find( n )==d_nodes.end() ){
186 d_nodes[n] = new RegionNodeInfo(d_cf->d_state.getSatContext());
187 }
188 d_nodes[n]->setValid(valid);
189 d_reps_size = d_reps_size + ( valid ? 1 : -1 );
190 //removing a member of the test clique from this region
191 if( d_testClique.find( n ) != d_testClique.end() && d_testClique[n] ){
192 Assert(!valid);
193 d_testClique[n] = false;
194 d_testCliqueSize = d_testCliqueSize - 1;
195 //remove all splits involving n
196 for( split_iterator it = begin_splits(); it != end_splits(); ++it ){
197 if( (*it).second ){
198 if( (*it).first[0]==n || (*it).first[1]==n ){
199 d_splits[ (*it).first ] = false;
200 d_splitsSize = d_splitsSize - 1;
201 }
202 }
203 }
204 }
205 }
206
207 bool Region::isDisequal( Node n1, Node n2, int type ) {
208 RegionNodeInfo::DiseqList* del = d_nodes[ n1 ]->get(type);
209 return del->isSet(n2) && del->getDisequalityValue(n2);
210 }
211
212 struct sortInternalDegree {
213 Region* r;
214 bool operator() (Node i, Node j) {
215 return (r->getRegionInfo(i)->getNumInternalDisequalities() >
216 r->getRegionInfo(j)->getNumInternalDisequalities());
217 }
218 };
219
220 struct sortExternalDegree {
221 Region* r;
222 bool operator() (Node i,Node j) {
223 return (r->getRegionInfo(i)->getNumExternalDisequalities() >
224 r->getRegionInfo(j)->getNumExternalDisequalities());
225 }
226 };
227
228 int gmcCount = 0;
229
230 bool Region::getMustCombine( int cardinality ){
231 if (d_total_diseq_external >= static_cast<unsigned>(cardinality))
232 {
233 //The number of external disequalities is greater than or equal to
234 //cardinality. Thus, a clique of size cardinality+1 may exist
235 //between nodes in d_regions[i] and other regions Check if this is
236 //actually the case: must have n nodes with outgoing degree
237 //(cardinality+1-n) for some n>0
238 std::vector< int > degrees;
239 for( Region::iterator it = begin(); it != end(); ++it ){
240 RegionNodeInfo* rni = it->second;
241 if( rni->valid() ){
242 if( rni->getNumDisequalities() >= cardinality ){
243 int outDeg = rni->getNumExternalDisequalities();
244 if( outDeg>=cardinality ){
245 //we have 1 node of degree greater than (cardinality)
246 return true;
247 }else if( outDeg>=1 ){
248 degrees.push_back( outDeg );
249 if( (int)degrees.size()>=cardinality ){
250 //we have (cardinality) nodes of degree 1
251 return true;
252 }
253 }
254 }
255 }
256 }
257 gmcCount++;
258 if( gmcCount%100==0 ){
259 Trace("gmc-count") << gmcCount << " " << cardinality
260 << " sample : " << degrees.size() << std::endl;
261 }
262 //this should happen relatively infrequently....
263 std::sort( degrees.begin(), degrees.end() );
264 for( int i=0; i<(int)degrees.size(); i++ ){
265 if( degrees[i]>=cardinality+1-((int)degrees.size()-i) ){
266 return true;
267 }
268 }
269 }
270 return false;
271 }
272
273 bool Region::check( Theory::Effort level, int cardinality,
274 std::vector< Node >& clique ) {
275 if( d_reps_size>unsigned(cardinality) ){
276 if( d_total_diseq_internal==d_reps_size*( d_reps_size - 1 ) ){
277 if( d_reps_size>1 ){
278 //quick clique check, all reps form a clique
279 for( iterator it = begin(); it != end(); ++it ){
280 if( it->second->valid() ){
281 clique.push_back( it->first );
282 }
283 }
284 Trace("quick-clique") << "Found quick clique" << std::endl;
285 return true;
286 }else{
287 return false;
288 }
289 }
290 else if (level==Theory::EFFORT_FULL)
291 {
292 //build test clique, up to size cardinality+1
293 if( d_testCliqueSize<=unsigned(cardinality) ){
294 std::vector< Node > newClique;
295 if( d_testCliqueSize<unsigned(cardinality) ){
296 for( iterator it = begin(); it != end(); ++it ){
297 //if not in the test clique, add it to the set of new members
298 if( it->second->valid() &&
299 ( d_testClique.find( it->first ) == d_testClique.end() ||
300 !d_testClique[ it->first ] ) ){
301 //if( it->second->getNumInternalDisequalities()>cardinality ||
302 // level==Theory::EFFORT_FULL ){
303 newClique.push_back( it->first );
304 //}
305 }
306 }
307 //choose remaining nodes with the highest degrees
308 sortInternalDegree sidObj;
309 sidObj.r = this;
310 std::sort( newClique.begin(), newClique.end(), sidObj );
311 int offset = ( cardinality - d_testCliqueSize ) + 1;
312 newClique.erase( newClique.begin() + offset, newClique.end() );
313 }else{
314 //scan for the highest degree
315 int maxDeg = -1;
316 Node maxNode;
317 for( std::map< Node, RegionNodeInfo* >::iterator
318 it = d_nodes.begin(); it != d_nodes.end(); ++it ){
319 //if not in the test clique, add it to the set of new members
320 if( it->second->valid() &&
321 ( d_testClique.find( it->first )==d_testClique.end() ||
322 !d_testClique[ it->first ] ) ){
323 if( it->second->getNumInternalDisequalities()>maxDeg ){
324 maxDeg = it->second->getNumInternalDisequalities();
325 maxNode = it->first;
326 }
327 }
328 }
329 Assert(maxNode != Node::null());
330 newClique.push_back( maxNode );
331 }
332 //check splits internal to new members
333 for( int j=0; j<(int)newClique.size(); j++ ){
334 Debug("uf-ss-debug") << "Choose to add clique member "
335 << newClique[j] << std::endl;
336 for( int k=(j+1); k<(int)newClique.size(); k++ ){
337 if( !isDisequal( newClique[j], newClique[k], 1 ) ){
338 Node at_j = newClique[j];
339 Node at_k = newClique[k];
340 Node j_eq_k =
341 NodeManager::currentNM()->mkNode( EQUAL, at_j, at_k );
342 d_splits[ j_eq_k ] = true;
343 d_splitsSize = d_splitsSize + 1;
344 }
345 }
346 //check disequalities with old members
347 for( NodeBoolMap::iterator it = d_testClique.begin();
348 it != d_testClique.end(); ++it ){
349 if( (*it).second ){
350 if( !isDisequal( (*it).first, newClique[j], 1 ) ){
351 Node at_it = (*it).first;
352 Node at_j = newClique[j];
353 Node it_eq_j = at_it.eqNode(at_j);
354 d_splits[ it_eq_j ] = true;
355 d_splitsSize = d_splitsSize + 1;
356 }
357 }
358 }
359 }
360 //add new clique members to test clique
361 for( int j=0; j<(int)newClique.size(); j++ ){
362 d_testClique[ newClique[j] ] = true;
363 d_testCliqueSize = d_testCliqueSize + 1;
364 }
365 }
366 // Check if test clique has larger size than cardinality, and
367 // forms a clique.
368 if( d_testCliqueSize >= unsigned(cardinality+1) && d_splitsSize==0 ){
369 //test clique is a clique
370 for( NodeBoolMap::iterator it = d_testClique.begin();
371 it != d_testClique.end(); ++it ){
372 if( (*it).second ){
373 clique.push_back( (*it).first );
374 }
375 }
376 return true;
377 }
378 }
379 }
380 return false;
381 }
382
383 void Region::getNumExternalDisequalities(
384 std::map< Node, int >& num_ext_disequalities ){
385 for( Region::iterator it = begin(); it != end(); ++it ){
386 RegionNodeInfo* rni = it->second;
387 if( rni->valid() ){
388 DiseqList* del = rni->get(0);
389 for( DiseqList::iterator it2 = del->begin(); it2 != del->end(); ++it2 ){
390 if( (*it2).second ){
391 num_ext_disequalities[ (*it2).first ]++;
392 }
393 }
394 }
395 }
396 }
397
398 void Region::debugPrint( const char* c, bool incClique ) {
399 Debug( c ) << "Num reps: " << d_reps_size << std::endl;
400 for( Region::iterator it = begin(); it != end(); ++it ){
401 RegionNodeInfo* rni = it->second;
402 if( rni->valid() ){
403 Node n = it->first;
404 Debug( c ) << " " << n << std::endl;
405 for( int i=0; i<2; i++ ){
406 Debug( c ) << " " << ( i==0 ? "Ext" : "Int" ) << " disequal:";
407 DiseqList* del = rni->get(i);
408 for( DiseqList::iterator it2 = del->begin(); it2 != del->end(); ++it2 ){
409 if( (*it2).second ){
410 Debug( c ) << " " << (*it2).first;
411 }
412 }
413 Debug( c ) << ", total = " << del->size() << std::endl;
414 }
415 }
416 }
417 Debug( c ) << "Total disequal: " << d_total_diseq_external << " external,";
418 Debug( c ) << " " << d_total_diseq_internal << " internal." << std::endl;
419
420 if( incClique ){
421 if( !d_testClique.empty() ){
422 Debug( c ) << "Candidate clique members: " << std::endl;
423 Debug( c ) << " ";
424 for( NodeBoolMap::iterator it = d_testClique.begin();
425 it != d_testClique.end(); ++ it ){
426 if( (*it).second ){
427 Debug( c ) << (*it).first << " ";
428 }
429 }
430 Debug( c ) << ", size = " << d_testCliqueSize << std::endl;
431 }
432 if( !d_splits.empty() ){
433 Debug( c ) << "Required splits: " << std::endl;
434 Debug( c ) << " ";
435 for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end();
436 ++ it ){
437 if( (*it).second ){
438 Debug( c ) << (*it).first << " ";
439 }
440 }
441 Debug( c ) << ", size = " << d_splitsSize << std::endl;
442 }
443 }
444 }
445
446 SortModel::CardinalityDecisionStrategy::CardinalityDecisionStrategy(
447 Node t, context::Context* satContext, Valuation valuation)
448 : DecisionStrategyFmf(satContext, valuation), d_cardinality_term(t)
449 {
450 }
451 Node SortModel::CardinalityDecisionStrategy::mkLiteral(unsigned i)
452 {
453 NodeManager* nm = NodeManager::currentNM();
454 return nm->mkNode(
455 CARDINALITY_CONSTRAINT, d_cardinality_term, nm->mkConst(Rational(i + 1)));
456 }
457 std::string SortModel::CardinalityDecisionStrategy::identify() const
458 {
459 return std::string("uf_card");
460 }
461
462 SortModel::SortModel(Node n,
463 TheoryState& state,
464 TheoryInferenceManager& im,
465 CardinalityExtension* thss)
466 : d_type(n.getType()),
467 d_state(state),
468 d_im(im),
469 d_thss(thss),
470 d_regions_index(d_state.getSatContext(), 0),
471 d_regions_map(d_state.getSatContext()),
472 d_split_score(d_state.getSatContext()),
473 d_disequalities_index(d_state.getSatContext(), 0),
474 d_reps(d_state.getSatContext(), 0),
475 d_cardinality(d_state.getSatContext(), 1),
476 d_hasCard(d_state.getSatContext(), false),
477 d_maxNegCard(d_state.getSatContext(), 0),
478 d_initialized(d_state.getUserContext(), false),
479 d_c_dec_strat(nullptr)
480 {
481 d_cardinality_term = n;
482
483 if (options::ufssMode() == options::UfssMode::FULL)
484 {
485 // Register the strategy with the decision manager of the theory.
486 // We are guaranteed that the decision manager is ready since we
487 // construct this module during TheoryUF::finishInit.
488 d_c_dec_strat.reset(new CardinalityDecisionStrategy(
489 n, d_state.getSatContext(), thss->getTheory()->getValuation()));
490 }
491 }
492
493 SortModel::~SortModel() {
494 for(std::vector<Region*>::iterator i = d_regions.begin();
495 i != d_regions.end(); ++i) {
496 Region* region = *i;
497 delete region;
498 }
499 d_regions.clear();
500 }
501
502 /** initialize */
503 void SortModel::initialize()
504 {
505 if (d_c_dec_strat.get() != nullptr && !d_initialized)
506 {
507 d_initialized = true;
508 // Strategy is user-context-dependent, since it is in sync with
509 // user-context-dependent flag d_initialized.
510 d_thss->getTheory()->getDecisionManager()->registerStrategy(
511 DecisionManager::STRAT_UF_CARD, d_c_dec_strat.get());
512 }
513 }
514
515 /** new node */
516 void SortModel::newEqClass( Node n ){
517 if (!d_state.isInConflict())
518 {
519 if( d_regions_map.find( n )==d_regions_map.end() ){
520 // Must generate totality axioms for every cardinality we have
521 // allocated thus far.
522 for( std::map< int, Node >::iterator it = d_cardinality_literal.begin();
523 it != d_cardinality_literal.end(); ++it ){
524 if( applyTotality( it->first ) ){
525 addTotalityAxiom(n, it->first);
526 }
527 }
528 if( options::ufssTotality() ){
529 // Regions map will store whether we need to equate this term
530 // with a constant equivalence class.
531 if( std::find( d_totality_terms[0].begin(), d_totality_terms[0].end(), n )==d_totality_terms[0].end() ){
532 d_regions_map[n] = 0;
533 }else{
534 d_regions_map[n] = -1;
535 }
536 }else{
537 d_regions_map[n] = d_regions_index;
538 Debug("uf-ss") << "CardinalityExtension: New Eq Class " << n
539 << std::endl;
540 Debug("uf-ss-debug") << d_regions_index << " "
541 << (int)d_regions.size() << std::endl;
542 if( d_regions_index<d_regions.size() ){
543 d_regions[ d_regions_index ]->debugPrint("uf-ss-debug",true);
544 d_regions[ d_regions_index ]->setValid(true);
545 Assert(d_regions[d_regions_index]->getNumReps() == 0);
546 }else{
547 d_regions.push_back(new Region(this, d_state.getSatContext()));
548 }
549 d_regions[ d_regions_index ]->addRep( n );
550 d_regions_index = d_regions_index + 1;
551 }
552 d_reps = d_reps + 1;
553 }
554 }
555 }
556
557 /** merge */
558 void SortModel::merge( Node a, Node b ){
559 if (!d_state.isInConflict())
560 {
561 if( options::ufssTotality() ){
562 if( d_regions_map[b]==-1 ){
563 d_regions_map[a] = -1;
564 }
565 d_regions_map[b] = -1;
566 }else{
567 Debug("uf-ss") << "CardinalityExtension: Merging " << a << " = " << b
568 << "..." << std::endl;
569 if( a!=b ){
570 Assert(d_regions_map.find(a) != d_regions_map.end());
571 Assert(d_regions_map.find(b) != d_regions_map.end());
572 int ai = d_regions_map[a];
573 int bi = d_regions_map[b];
574 Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl;
575 if( ai!=bi ){
576 if( d_regions[ai]->getNumReps()==1 ){
577 int ri = combineRegions( bi, ai );
578 d_regions[ri]->setEqual( a, b );
579 checkRegion( ri );
580 }else if( d_regions[bi]->getNumReps()==1 ){
581 int ri = combineRegions( ai, bi );
582 d_regions[ri]->setEqual( a, b );
583 checkRegion( ri );
584 }else{
585 // Either move a to d_regions[bi], or b to d_regions[ai].
586 RegionNodeInfo* a_region_info = d_regions[ai]->getRegionInfo(a);
587 RegionNodeInfo* b_region_info = d_regions[bi]->getRegionInfo(b);
588 int aex = ( a_region_info->getNumInternalDisequalities() -
589 getNumDisequalitiesToRegion( a, bi ) );
590 int bex = ( b_region_info->getNumInternalDisequalities() -
591 getNumDisequalitiesToRegion( b, ai ) );
592 // Based on which would produce the fewest number of
593 // external disequalities.
594 if( aex<bex ){
595 moveNode( a, bi );
596 d_regions[bi]->setEqual( a, b );
597 }else{
598 moveNode( b, ai );
599 d_regions[ai]->setEqual( a, b );
600 }
601 checkRegion( ai );
602 checkRegion( bi );
603 }
604 }else{
605 d_regions[ai]->setEqual( a, b );
606 checkRegion( ai );
607 }
608 d_regions_map[b] = -1;
609 }
610 d_reps = d_reps - 1;
611 }
612 }
613 }
614
615 /** assert terms are disequal */
616 void SortModel::assertDisequal( Node a, Node b, Node reason ){
617 if (!d_state.isInConflict())
618 {
619 if( options::ufssTotality() ){
620 //do nothing
621 }else{
622 //if they are not already disequal
623 eq::EqualityEngine* ee = d_thss->getTheory()->getEqualityEngine();
624 a = ee->getRepresentative(a);
625 b = ee->getRepresentative(b);
626 int ai = d_regions_map[a];
627 int bi = d_regions_map[b];
628 if( !d_regions[ai]->isDisequal( a, b, ai==bi ) ){
629 Debug("uf-ss") << "Assert disequal " << a << " != " << b << "..." << std::endl;
630 //if( reason.getKind()!=NOT || reason[0].getKind()!=EQUAL ||
631 // a!=reason[0][0] || b!=reason[0][1] ){
632 // Notice() << "Assert disequal " << a << " != " << b << ", reason = " << reason << "..." << std::endl;
633 //}
634 Debug("uf-ss-disequal") << "Assert disequal " << a << " != " << b << "..." << std::endl;
635 //add to list of disequalities
636 if( d_disequalities_index<d_disequalities.size() ){
637 d_disequalities[d_disequalities_index] = reason;
638 }else{
639 d_disequalities.push_back( reason );
640 }
641 d_disequalities_index = d_disequalities_index + 1;
642 //now, add disequalities to regions
643 Assert(d_regions_map.find(a) != d_regions_map.end());
644 Assert(d_regions_map.find(b) != d_regions_map.end());
645 Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl;
646 if( ai==bi ){
647 //internal disequality
648 d_regions[ai]->setDisequal( a, b, 1, true );
649 d_regions[ai]->setDisequal( b, a, 1, true );
650 checkRegion( ai, false ); //do not need to check if it needs to combine (no new ext. disequalities)
651 }else{
652 //external disequality
653 d_regions[ai]->setDisequal( a, b, 0, true );
654 d_regions[bi]->setDisequal( b, a, 0, true );
655 checkRegion( ai );
656 checkRegion( bi );
657 }
658 }
659 }
660 }
661 }
662
663 bool SortModel::areDisequal( Node a, Node b ) {
664 Assert(a == d_thss->getTheory()->getEqualityEngine()->getRepresentative(a));
665 Assert(b == d_thss->getTheory()->getEqualityEngine()->getRepresentative(b));
666 if( d_regions_map.find( a )!=d_regions_map.end() &&
667 d_regions_map.find( b )!=d_regions_map.end() ){
668 int ai = d_regions_map[a];
669 int bi = d_regions_map[b];
670 return d_regions[ai]->isDisequal(a, b, ai==bi ? 1 : 0);
671 }else{
672 return false;
673 }
674 }
675
676 /** check */
677 void SortModel::check(Theory::Effort level)
678 {
679 Assert(options::ufssMode() == options::UfssMode::FULL);
680 if (level >= Theory::EFFORT_STANDARD && d_hasCard && !d_state.isInConflict())
681 {
682 Debug("uf-ss") << "CardinalityExtension: Check " << level << " " << d_type
683 << std::endl;
684 if( level==Theory::EFFORT_FULL ){
685 Debug("fmf-full-check") << std::endl;
686 Debug("fmf-full-check") << "Full check for SortModel " << d_type << ", status : " << std::endl;
687 debugPrint("fmf-full-check");
688 Debug("fmf-full-check") << std::endl;
689 }
690 if( d_reps<=(unsigned)d_cardinality ){
691 Debug("uf-ss-debug") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl;
692 if( level==Theory::EFFORT_FULL ){
693 Debug("uf-ss-sat") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl;
694 //Notice() << "We have " << d_reps << " representatives for type " << d_type << ", <= " << cardinality << std::endl;
695 //Notice() << "Model size for " << d_type << " is " << cardinality << std::endl;
696 //Notice() << cardinality << " ";
697 }
698 return;
699 }else{
700 //first check if we can generate a clique conflict
701 if( !options::ufssTotality() ){
702 //do a check within each region
703 for( int i=0; i<(int)d_regions_index; i++ ){
704 if( d_regions[i]->valid() ){
705 std::vector< Node > clique;
706 if( d_regions[i]->check( level, d_cardinality, clique ) ){
707 //add clique lemma
708 addCliqueLemma(clique);
709 return;
710 }else{
711 Trace("uf-ss-debug") << "No clique in Region #" << i << std::endl;
712 }
713 }
714 }
715 }
716 if( !applyTotality( d_cardinality ) ){
717 //do splitting on demand
718 bool addedLemma = false;
719 if (level==Theory::EFFORT_FULL)
720 {
721 Trace("uf-ss-debug") << "Add splits?" << std::endl;
722 //see if we have any recommended splits from large regions
723 for( int i=0; i<(int)d_regions_index; i++ ){
724 if( d_regions[i]->valid() && d_regions[i]->getNumReps()>d_cardinality ){
725 int sp = addSplit(d_regions[i]);
726 if( sp==1 ){
727 addedLemma = true;
728 #ifdef ONE_SPLIT_REGION
729 break;
730 #endif
731 }else if( sp==-1 ){
732 check(level);
733 return;
734 }
735 }
736 }
737 }
738 //If no added lemmas, force continuation via combination of regions.
739 if( level==Theory::EFFORT_FULL ){
740 if( !addedLemma ){
741 Trace("uf-ss-debug") << "No splits added. " << d_cardinality
742 << std::endl;
743 Trace("uf-ss-si") << "Must combine region" << std::endl;
744 bool recheck = false;
745 SortInference* si = d_thss->getSortInference();
746 if (si != nullptr)
747 {
748 //If sort inference is enabled, search for regions with same sort.
749 std::map< int, int > sortsFound;
750 for( int i=0; i<(int)d_regions_index; i++ ){
751 if( d_regions[i]->valid() ){
752 Node op = d_regions[i]->frontKey();
753 int sort_id = si->getSortId(op);
754 if( sortsFound.find( sort_id )!=sortsFound.end() ){
755 Debug("fmf-full-check") << "Combined regions " << i << " " << sortsFound[sort_id] << std::endl;
756 combineRegions( sortsFound[sort_id], i );
757 recheck = true;
758 break;
759 }else{
760 sortsFound[sort_id] = i;
761 }
762 }
763 }
764 }
765 if( !recheck ) {
766 //naive strategy, force region combination involving the first valid region
767 for( int i=0; i<(int)d_regions_index; i++ ){
768 if( d_regions[i]->valid() ){
769 int fcr = forceCombineRegion( i, false );
770 Debug("fmf-full-check") << "Combined regions " << i << " " << fcr << std::endl;
771 Trace("uf-ss-debug") << "Combined regions " << i << " " << fcr << std::endl;
772 recheck = true;
773 break;
774 }
775 }
776 }
777 if( recheck ){
778 Trace("uf-ss-debug") << "Must recheck." << std::endl;
779 check(level);
780 }
781 }
782 }
783 }
784 }
785 }
786 }
787
788 void SortModel::presolve() {
789 d_initialized = false;
790 }
791
792 int SortModel::getNumDisequalitiesToRegion( Node n, int ri ){
793 int ni = d_regions_map[n];
794 int counter = 0;
795 DiseqList* del = d_regions[ni]->getRegionInfo(n)->get(0);
796 for( DiseqList::iterator it = del->begin(); it != del->end(); ++it ){
797 if( (*it).second ){
798 if( d_regions_map[ (*it).first ]==ri ){
799 counter++;
800 }
801 }
802 }
803 return counter;
804 }
805
806 void SortModel::getDisequalitiesToRegions(int ri,
807 std::map< int, int >& regions_diseq)
808 {
809 Region* region = d_regions[ri];
810 for(Region::iterator it = region->begin(); it != region->end(); ++it ){
811 if( it->second->valid() ){
812 DiseqList* del = it->second->get(0);
813 for( DiseqList::iterator it2 = del->begin(); it2 != del->end(); ++it2 ){
814 if( (*it2).second ){
815 Assert(isValid(d_regions_map[(*it2).first]));
816 //Notice() << "Found disequality with " << (*it2).first << ", region = " << d_regions_map[ (*it2).first ] << std::endl;
817 regions_diseq[ d_regions_map[ (*it2).first ] ]++;
818 }
819 }
820 }
821 }
822 }
823
824 void SortModel::setSplitScore( Node n, int s ){
825 if( d_split_score.find( n )!=d_split_score.end() ){
826 int ss = d_split_score[ n ];
827 d_split_score[ n ] = s>ss ? s : ss;
828 }else{
829 d_split_score[ n ] = s;
830 }
831 for( int i=0; i<(int)n.getNumChildren(); i++ ){
832 setSplitScore( n[i], s+1 );
833 }
834 }
835
836 void SortModel::assertCardinality(int c, bool val)
837 {
838 if (!d_state.isInConflict())
839 {
840 Trace("uf-ss-assert")
841 << "Assert cardinality " << d_type << " " << c << " " << val
842 << " level = "
843 << d_thss->getTheory()->getValuation().getAssertionLevel() << std::endl;
844 Assert(c > 0);
845 Node cl = getCardinalityLiteral( c );
846 if( val ){
847 bool doCheckRegions = !d_hasCard;
848 bool prevHasCard = d_hasCard;
849 d_hasCard = true;
850 if( !prevHasCard || c<d_cardinality ){
851 d_cardinality = c;
852 simpleCheckCardinality();
853 if (d_state.isInConflict())
854 {
855 return;
856 }
857 }
858 //should check all regions now
859 if( doCheckRegions ){
860 for( int i=0; i<(int)d_regions_index; i++ ){
861 if( d_regions[i]->valid() ){
862 checkRegion( i );
863 if (d_state.isInConflict())
864 {
865 return;
866 }
867 }
868 }
869 }
870 // we assert it positively, if its beyond the bound, abort
871 if (options::ufssAbortCardinality() != -1
872 && c >= options::ufssAbortCardinality())
873 {
874 std::stringstream ss;
875 ss << "Maximum cardinality (" << options::ufssAbortCardinality()
876 << ") for finite model finding exceeded." << std::endl;
877 throw LogicException(ss.str());
878 }
879 }
880 else
881 {
882 if( c>d_maxNegCard.get() ){
883 Trace("uf-ss-com-card-debug") << "Maximum negative cardinality for " << d_type << " is now " << c << std::endl;
884 d_maxNegCard.set( c );
885 simpleCheckCardinality();
886 }
887 }
888 }
889 }
890
891 void SortModel::checkRegion( int ri, bool checkCombine ){
892 if( isValid(ri) && d_hasCard ){
893 Assert(d_cardinality > 0);
894 if( checkCombine && d_regions[ri]->getMustCombine( d_cardinality ) ){
895 ////alternatively, check if we can reduce the number of external disequalities by moving single nodes
896 //for( std::map< Node, bool >::iterator it = d_regions[i]->d_reps.begin(); it != d_regions[i]->d_reps.end(); ++it ){
897 // if( it->second ){
898 // int inDeg = d_regions[i]->d_disequalities_size[1][ it-> first ];
899 // int outDeg = d_regions[i]->d_disequalities_size[0][ it-> first ];
900 // if( inDeg<outDeg ){
901 // }
902 // }
903 //}
904 int riNew = forceCombineRegion( ri, true );
905 if( riNew>=0 ){
906 checkRegion( riNew, checkCombine );
907 }
908 }
909 //now check if region is in conflict
910 std::vector< Node > clique;
911 if( d_regions[ri]->check( Theory::EFFORT_STANDARD, d_cardinality, clique ) ){
912 //explain clique
913 addCliqueLemma(clique);
914 }
915 }
916 }
917
918 int SortModel::forceCombineRegion( int ri, bool useDensity ){
919 if( !useDensity ){
920 for( int i=0; i<(int)d_regions_index; i++ ){
921 if( ri!=i && d_regions[i]->valid() ){
922 return combineRegions( ri, i );
923 }
924 }
925 return -1;
926 }else{
927 //this region must merge with another
928 if( Debug.isOn("uf-ss-check-region") ){
929 Debug("uf-ss-check-region") << "We must combine Region #" << ri << ". " << std::endl;
930 d_regions[ri]->debugPrint("uf-ss-check-region");
931 }
932 //take region with maximum disequality density
933 double maxScore = 0;
934 int maxRegion = -1;
935 std::map< int, int > regions_diseq;
936 getDisequalitiesToRegions( ri, regions_diseq );
937 for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){
938 Debug("uf-ss-check-region") << it->first << " : " << it->second << std::endl;
939 }
940 for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){
941 Assert(it->first != ri);
942 Assert(isValid(it->first));
943 Assert(d_regions[it->first]->getNumReps() > 0);
944 double tempScore = double(it->second)/double(d_regions[it->first]->getNumReps() );
945 if( tempScore>maxScore ){
946 maxRegion = it->first;
947 maxScore = tempScore;
948 }
949 }
950 if( maxRegion!=-1 ){
951 if( Debug.isOn("uf-ss-check-region") ){
952 Debug("uf-ss-check-region") << "Combine with region #" << maxRegion << ":" << std::endl;
953 d_regions[maxRegion]->debugPrint("uf-ss-check-region");
954 }
955 return combineRegions( ri, maxRegion );
956 }
957 return -1;
958 }
959 }
960
961
962 int SortModel::combineRegions( int ai, int bi ){
963 #ifdef COMBINE_REGIONS_SMALL_INTO_LARGE
964 if( d_regions[ai]->getNumReps()<d_regions[bi]->getNumReps() ){
965 return combineRegions( bi, ai );
966 }
967 #endif
968 Debug("uf-ss-region") << "uf-ss: Combine Region #" << bi << " with Region #" << ai << std::endl;
969 Assert(isValid(ai) && isValid(bi));
970 Region* region_bi = d_regions[bi];
971 for(Region::iterator it = region_bi->begin(); it != region_bi->end(); ++it){
972 Region::RegionNodeInfo* rni = it->second;
973 if( rni->valid() ){
974 d_regions_map[ it->first ] = ai;
975 }
976 }
977 //update regions disequal DO_THIS?
978 d_regions[ai]->combine( d_regions[bi] );
979 d_regions[bi]->setValid( false );
980 return ai;
981 }
982
983 void SortModel::moveNode( Node n, int ri ){
984 Debug("uf-ss-region") << "uf-ss: Move node " << n << " to Region #" << ri << std::endl;
985 Assert(isValid(d_regions_map[n]));
986 Assert(isValid(ri));
987 //move node to region ri
988 d_regions[ri]->takeNode( d_regions[ d_regions_map[n] ], n );
989 d_regions_map[n] = ri;
990 }
991
992 int SortModel::addSplit(Region* r)
993 {
994 Node s;
995 if( r->hasSplits() ){
996 //take the first split you find
997 for( Region::split_iterator it = r->begin_splits();
998 it != r->end_splits(); ++it ){
999 if( (*it).second ){
1000 s = (*it).first;
1001 break;
1002 }
1003 }
1004 Assert(s != Node::null());
1005 }
1006 if (!s.isNull() ){
1007 //add lemma to output channel
1008 Assert(s.getKind() == EQUAL);
1009 Node ss = Rewriter::rewrite( s );
1010 if( ss.getKind()!=EQUAL ){
1011 Node b_t = NodeManager::currentNM()->mkConst( true );
1012 Node b_f = NodeManager::currentNM()->mkConst( false );
1013 if( ss==b_f ){
1014 Trace("uf-ss-lemma") << "....Assert disequal directly : "
1015 << s[0] << " " << s[1] << std::endl;
1016 assertDisequal( s[0], s[1], b_t );
1017 return -1;
1018 }else{
1019 Trace("uf-ss-warn") << "Split on unknown literal : " << ss << std::endl;
1020 }
1021 if( ss==b_t ){
1022 Message() << "Bad split " << s << std::endl;
1023 AlwaysAssert(false);
1024 }
1025 }
1026 SortInference* si = d_thss->getSortInference();
1027 if (si != nullptr)
1028 {
1029 for( int i=0; i<2; i++ ){
1030 int sid = si->getSortId(ss[i]);
1031 Trace("uf-ss-split-si") << sid << " ";
1032 }
1033 Trace("uf-ss-split-si") << std::endl;
1034 }
1035 //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areEqual( s[0], s[1] ) << " ";
1036 //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areDisequal( s[0], s[1] ) << std::endl;
1037 //Trace("uf-ss-lemma") << s[0].getType() << " " << s[1].getType() << std::endl;
1038 //Notice() << "*** Split on " << s << std::endl;
1039 //split on the equality s
1040 Node lem = NodeManager::currentNM()->mkNode( kind::OR, ss, ss.negate() );
1041 // send lemma, with caching
1042 if (d_im.lemma(lem))
1043 {
1044 Trace("uf-ss-lemma") << "*** Split on " << s << std::endl;
1045 //tell the sat solver to explore the equals branch first
1046 d_im.requirePhase(ss, true);
1047 ++( d_thss->d_statistics.d_split_lemmas );
1048 }
1049 return 1;
1050 }else{
1051 return 0;
1052 }
1053 }
1054
1055 void SortModel::addCliqueLemma(std::vector<Node>& clique)
1056 {
1057 Assert(d_hasCard);
1058 Assert(d_cardinality > 0);
1059 while( clique.size()>size_t(d_cardinality+1) ){
1060 clique.pop_back();
1061 }
1062 // add as lemma
1063 std::vector<Node> eqs;
1064 for (unsigned i = 0, size = clique.size(); i < size; i++)
1065 {
1066 for (unsigned j = 0; j < i; j++)
1067 {
1068 eqs.push_back(clique[i].eqNode(clique[j]));
1069 }
1070 }
1071 eqs.push_back(d_cardinality_literal[d_cardinality].notNode());
1072 Node lem = NodeManager::currentNM()->mkNode(OR, eqs);
1073 // send lemma, with caching
1074 if (d_im.lemma(lem))
1075 {
1076 Trace("uf-ss-lemma") << "*** Add clique lemma " << lem << std::endl;
1077 ++(d_thss->d_statistics.d_clique_lemmas);
1078 }
1079 }
1080
1081 void SortModel::addTotalityAxiom(Node n, int cardinality)
1082 {
1083 if( std::find( d_totality_terms[0].begin(), d_totality_terms[0].end(), n )==d_totality_terms[0].end() ){
1084 if( std::find( d_totality_lems[n].begin(), d_totality_lems[n].end(), cardinality ) == d_totality_lems[n].end() ){
1085 NodeManager* nm = NodeManager::currentNM();
1086 d_totality_lems[n].push_back( cardinality );
1087 Node cardLit = d_cardinality_literal[ cardinality ];
1088 int sort_id = 0;
1089 SortInference* si = d_thss->getSortInference();
1090 if (si != nullptr)
1091 {
1092 sort_id = si->getSortId(n);
1093 }
1094 Trace("uf-ss-totality") << "Add totality lemma for " << n << " " << cardinality << ", sort id is " << sort_id << std::endl;
1095 int use_cardinality = cardinality;
1096 if( options::ufssTotalitySymBreak() ){
1097 if( d_sym_break_index.find(n)!=d_sym_break_index.end() ){
1098 use_cardinality = d_sym_break_index[n];
1099 }else if( (int)d_sym_break_terms[n.getType()][sort_id].size()<cardinality-1 ){
1100 use_cardinality = d_sym_break_terms[n.getType()][sort_id].size() + 1;
1101 d_sym_break_terms[n.getType()][sort_id].push_back( n );
1102 d_sym_break_index[n] = use_cardinality;
1103 Trace("uf-ss-totality") << "Allocate symmetry breaking term " << n << ", index = " << use_cardinality << std::endl;
1104 if( d_sym_break_terms[n.getType()][sort_id].size()>1 ){
1105 //enforce canonicity
1106 for( int i=2; i<use_cardinality; i++ ){
1107 //can only be assigned to domain constant d if someone has been assigned domain constant d-1
1108 Node eq = n.eqNode( getTotalityLemmaTerm( cardinality, i ) );
1109 std::vector< Node > eqs;
1110 for( unsigned j=0; j<(d_sym_break_terms[n.getType()][sort_id].size()-1); j++ ){
1111 eqs.push_back( d_sym_break_terms[n.getType()][sort_id][j].eqNode( getTotalityLemmaTerm( cardinality, i-1 ) ) );
1112 }
1113 Node ax = eqs.size()==1 ? eqs[0] : NodeManager::currentNM()->mkNode( OR, eqs );
1114 Node lem = NodeManager::currentNM()->mkNode( IMPLIES, eq, ax );
1115 Trace("uf-ss-lemma") << "*** Add (canonicity) totality axiom " << lem << std::endl;
1116 d_im.lemma(lem, LemmaProperty::NONE, false);
1117 }
1118 }
1119 }
1120 }
1121
1122 std::vector< Node > eqs;
1123 for( int i=0; i<use_cardinality; i++ ){
1124 eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) );
1125 }
1126 Node ax = eqs.size() == 1 ? eqs[0] : nm->mkNode(OR, eqs);
1127 Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax );
1128 Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl;
1129 //send as lemma to the output channel
1130 d_im.lemma(lem, LemmaProperty::NONE, false);
1131 ++( d_thss->d_statistics.d_totality_lemmas );
1132 }
1133 }
1134 }
1135
1136 /** apply totality */
1137 bool SortModel::applyTotality( int cardinality ){
1138 return options::ufssTotality() || cardinality<=options::ufssTotalityLimited();
1139 }
1140
1141 /** get totality lemma terms */
1142 Node SortModel::getTotalityLemmaTerm( int cardinality, int i ){
1143 return d_totality_terms[0][i];
1144 }
1145
1146 void SortModel::simpleCheckCardinality() {
1147 if( d_maxNegCard.get()!=0 && d_hasCard.get() && d_cardinality.get()<d_maxNegCard.get() ){
1148 Node lem = NodeManager::currentNM()->mkNode( AND, getCardinalityLiteral( d_cardinality.get() ),
1149 getCardinalityLiteral( d_maxNegCard.get() ).negate() );
1150 Trace("uf-ss-lemma") << "*** Simple cardinality conflict : " << lem << std::endl;
1151 d_im.conflict(lem);
1152 }
1153 }
1154
1155 void SortModel::debugPrint( const char* c ){
1156 if( Debug.isOn( c ) ){
1157 Debug( c ) << "Number of reps = " << d_reps << std::endl;
1158 Debug( c ) << "Cardinality req = " << d_cardinality << std::endl;
1159 unsigned debugReps = 0;
1160 for( unsigned i=0; i<d_regions_index; i++ ){
1161 Region* region = d_regions[i];
1162 if( region->valid() ){
1163 Debug( c ) << "Region #" << i << ": " << std::endl;
1164 region->debugPrint( c, true );
1165 Debug( c ) << std::endl;
1166 for( Region::iterator it = region->begin(); it != region->end(); ++it ){
1167 if( it->second->valid() ){
1168 if( d_regions_map[ it->first ]!=(int)i ){
1169 Debug( c ) << "***Bad regions map : " << it->first
1170 << " " << d_regions_map[ it->first ].get() << std::endl;
1171 }
1172 }
1173 }
1174 debugReps += region->getNumReps();
1175 }
1176 }
1177
1178 if( debugReps!=d_reps ){
1179 Debug( c ) << "***Bad reps: " << d_reps << ", "
1180 << "actual = " << debugReps << std::endl;
1181 }
1182 }
1183 }
1184
1185 bool SortModel::debugModel( TheoryModel* m ){
1186 if( Trace.isOn("uf-ss-warn") ){
1187 std::vector< Node > eqcs;
1188 eq::EqClassesIterator eqcs_i =
1189 eq::EqClassesIterator(m->getEqualityEngine());
1190 while( !eqcs_i.isFinished() ){
1191 Node eqc = (*eqcs_i);
1192 if( eqc.getType()==d_type ){
1193 if( std::find( eqcs.begin(), eqcs.end(), eqc )==eqcs.end() ){
1194 eqcs.push_back( eqc );
1195 //we must ensure that this equivalence class has been accounted for
1196 if( d_regions_map.find( eqc )==d_regions_map.end() ){
1197 Trace("uf-ss-warn") << "WARNING : equivalence class " << eqc << " unaccounted for." << std::endl;
1198 Trace("uf-ss-warn") << " type : " << d_type << std::endl;
1199 Trace("uf-ss-warn") << " kind : " << eqc.getKind() << std::endl;
1200 }
1201 }
1202 }
1203 ++eqcs_i;
1204 }
1205 }
1206 RepSet* rs = m->getRepSetPtr();
1207 int nReps = (int)rs->getNumRepresentatives(d_type);
1208 if( nReps!=(d_maxNegCard+1) ){
1209 Trace("uf-ss-warn") << "WARNING : Model does not have same # representatives as cardinality for " << d_type << "." << std::endl;
1210 Trace("uf-ss-warn") << " Max neg cardinality : " << d_maxNegCard << std::endl;
1211 Trace("uf-ss-warn") << " # Reps : " << nReps << std::endl;
1212 if( d_maxNegCard>=nReps ){
1213 while( (int)d_fresh_aloc_reps.size()<=d_maxNegCard ){
1214 std::stringstream ss;
1215 ss << "r_" << d_type << "_";
1216 Node nn = NodeManager::currentNM()->mkSkolem( ss.str(), d_type, "enumeration to meet negative card constraint" );
1217 d_fresh_aloc_reps.push_back( nn );
1218 }
1219 if( d_maxNegCard==0 ){
1220 rs->d_type_reps[d_type].push_back(d_fresh_aloc_reps[0]);
1221 }else{
1222 //must add lemma
1223 std::vector< Node > force_cl;
1224 for( int i=0; i<=d_maxNegCard; i++ ){
1225 for( int j=(i+1); j<=d_maxNegCard; j++ ){
1226 force_cl.push_back( d_fresh_aloc_reps[i].eqNode( d_fresh_aloc_reps[j] ).negate() );
1227 }
1228 }
1229 Node cl = getCardinalityLiteral( d_maxNegCard );
1230 Node lem = NodeManager::currentNM()->mkNode( OR, cl, NodeManager::currentNM()->mkNode( AND, force_cl ) );
1231 Trace("uf-ss-lemma") << "*** Enforce negative cardinality constraint lemma : " << lem << std::endl;
1232 d_im.lemma(lem, LemmaProperty::NONE, false);
1233 return false;
1234 }
1235 }
1236 }
1237 return true;
1238 }
1239
1240 int SortModel::getNumRegions(){
1241 int count = 0;
1242 for( int i=0; i<(int)d_regions_index; i++ ){
1243 if( d_regions[i]->valid() ){
1244 count++;
1245 }
1246 }
1247 return count;
1248 }
1249
1250 Node SortModel::getCardinalityLiteral(unsigned c)
1251 {
1252 Assert(c > 0);
1253 std::map<int, Node>::iterator itcl = d_cardinality_literal.find(c);
1254 if (itcl != d_cardinality_literal.end())
1255 {
1256 return itcl->second;
1257 }
1258 // get the literal from the decision strategy
1259 Node lit = d_c_dec_strat->getLiteral(c - 1);
1260 d_cardinality_literal[c] = lit;
1261
1262 // Since we are reasoning about cardinality c, we invoke a totality axiom
1263 if (!applyTotality(c))
1264 {
1265 // return if we are not using totality axioms
1266 return lit;
1267 }
1268
1269 NodeManager* nm = NodeManager::currentNM();
1270 Node var;
1271 if (c == 1 && !options::ufssTotalitySymBreak())
1272 {
1273 // get arbitrary ground term
1274 var = d_cardinality_term;
1275 }
1276 else
1277 {
1278 std::stringstream ss;
1279 ss << "_c_" << c;
1280 var = nm->mkSkolem(ss.str(), d_type, "is a cardinality lemma term");
1281 }
1282 if ((c - 1) < d_totality_terms[0].size())
1283 {
1284 d_totality_terms[0][c - 1] = var;
1285 }
1286 else
1287 {
1288 d_totality_terms[0].push_back(var);
1289 }
1290 // must be distinct from all other cardinality terms
1291 for (unsigned i = 1, size = d_totality_terms[0].size(); i < size; i++)
1292 {
1293 Node lem = var.eqNode(d_totality_terms[0][i - 1]).notNode();
1294 Trace("uf-ss-lemma") << "Totality distinctness lemma : " << lem
1295 << std::endl;
1296 d_im.lemma(lem, LemmaProperty::NONE, false);
1297 }
1298 // must send totality axioms for each existing term
1299 for (NodeIntMap::iterator it = d_regions_map.begin();
1300 it != d_regions_map.end();
1301 ++it)
1302 {
1303 addTotalityAxiom((*it).first, c);
1304 }
1305 return lit;
1306 }
1307
1308 CardinalityExtension::CardinalityExtension(TheoryState& state,
1309 TheoryInferenceManager& im,
1310 TheoryUF* th)
1311 : d_state(state),
1312 d_im(im),
1313 d_th(th),
1314 d_rep_model(),
1315 d_min_pos_com_card(state.getSatContext(), -1),
1316 d_cc_dec_strat(nullptr),
1317 d_initializedCombinedCardinality(state.getUserContext(), false),
1318 d_card_assertions_eqv_lemma(state.getUserContext()),
1319 d_min_pos_tn_master_card(state.getSatContext(), -1),
1320 d_rel_eqc(state.getSatContext())
1321 {
1322 if (options::ufssMode() == options::UfssMode::FULL && options::ufssFairness())
1323 {
1324 // Register the strategy with the decision manager of the theory.
1325 // We are guaranteed that the decision manager is ready since we
1326 // construct this module during TheoryUF::finishInit.
1327 d_cc_dec_strat.reset(new CombinedCardinalityDecisionStrategy(
1328 state.getSatContext(), th->getValuation()));
1329 }
1330 }
1331
1332 CardinalityExtension::~CardinalityExtension()
1333 {
1334 for (std::map<TypeNode, SortModel*>::iterator it = d_rep_model.begin();
1335 it != d_rep_model.end(); ++it) {
1336 delete it->second;
1337 }
1338 }
1339
1340 SortInference* CardinalityExtension::getSortInference()
1341 {
1342 if (!options::sortInference())
1343 {
1344 return nullptr;
1345 }
1346 QuantifiersEngine* qe = d_th->getQuantifiersEngine();
1347 if (qe != nullptr)
1348 {
1349 return qe->getTheoryEngine()->getSortInference();
1350 }
1351 return nullptr;
1352 }
1353
1354 /** ensure eqc */
1355 void CardinalityExtension::ensureEqc(SortModel* c, Node a)
1356 {
1357 if( !hasEqc( a ) ){
1358 d_rel_eqc[a] = true;
1359 Trace("uf-ss-solver") << "CardinalityExtension: New eq class " << a << " : "
1360 << a.getType() << std::endl;
1361 c->newEqClass( a );
1362 Trace("uf-ss-solver") << "CardinalityExtension: Done New eq class."
1363 << std::endl;
1364 }
1365 }
1366
1367 void CardinalityExtension::ensureEqcRec(Node n)
1368 {
1369 if( !hasEqc( n ) ){
1370 SortModel* c = getSortModel( n );
1371 if( c ){
1372 ensureEqc( c, n );
1373 }
1374 for( unsigned i=0; i<n.getNumChildren(); i++ ){
1375 ensureEqcRec( n[i] );
1376 }
1377 }
1378 }
1379
1380 /** has eqc */
1381 bool CardinalityExtension::hasEqc(Node a)
1382 {
1383 NodeBoolMap::iterator it = d_rel_eqc.find( a );
1384 return it!=d_rel_eqc.end() && (*it).second;
1385 }
1386
1387 /** new node */
1388 void CardinalityExtension::newEqClass(Node a)
1389 {
1390 SortModel* c = getSortModel( a );
1391 if( c ){
1392 #ifdef LAZY_REL_EQC
1393 //do nothing
1394 #else
1395 Trace("uf-ss-solver") << "CardinalityExtension: New eq class " << a << " : "
1396 << a.getType() << std::endl;
1397 c->newEqClass( a );
1398 Trace("uf-ss-solver") << "CardinalityExtension: Done New eq class."
1399 << std::endl;
1400 #endif
1401 }
1402 }
1403
1404 /** merge */
1405 void CardinalityExtension::merge(Node a, Node b)
1406 {
1407 //TODO: ensure they are relevant
1408 SortModel* c = getSortModel( a );
1409 if( c ){
1410 #ifdef LAZY_REL_EQC
1411 ensureEqc( c, a );
1412 if( hasEqc( b ) ){
1413 Trace("uf-ss-solver") << "CardinalityExtension: Merge " << a << " " << b
1414 << " : " << a.getType() << std::endl;
1415 c->merge( a, b );
1416 Trace("uf-ss-solver") << "CardinalityExtension: Done Merge." << std::endl;
1417 }else{
1418 //c->assignEqClass( b, a );
1419 d_rel_eqc[b] = true;
1420 }
1421 #else
1422 Trace("uf-ss-solver") << "CardinalityExtension: Merge " << a << " " << b
1423 << " : " << a.getType() << std::endl;
1424 c->merge( a, b );
1425 Trace("uf-ss-solver") << "CardinalityExtension: Done Merge." << std::endl;
1426 #endif
1427 }
1428 }
1429
1430 /** assert terms are disequal */
1431 void CardinalityExtension::assertDisequal(Node a, Node b, Node reason)
1432 {
1433 SortModel* c = getSortModel( a );
1434 if( c ){
1435 #ifdef LAZY_REL_EQC
1436 ensureEqc( c, a );
1437 ensureEqc( c, b );
1438 #endif
1439 Trace("uf-ss-solver") << "CardinalityExtension: Assert disequal " << a
1440 << " " << b << " : " << a.getType() << std::endl;
1441 c->assertDisequal( a, b, reason );
1442 Trace("uf-ss-solver") << "CardinalityExtension: Done Assert disequal."
1443 << std::endl;
1444 }
1445 }
1446
1447 /** assert a node */
1448 void CardinalityExtension::assertNode(Node n, bool isDecision)
1449 {
1450 Trace("uf-ss") << "Assert " << n << " " << isDecision << std::endl;
1451 #ifdef LAZY_REL_EQC
1452 ensureEqcRec( n );
1453 #endif
1454 bool polarity = n.getKind() != kind::NOT;
1455 TNode lit = polarity ? n : n[0];
1456 if (options::ufssMode() == options::UfssMode::FULL)
1457 {
1458 if( lit.getKind()==CARDINALITY_CONSTRAINT ){
1459 TypeNode tn = lit[0].getType();
1460 Assert(tn.isSort());
1461 Assert(d_rep_model[tn]);
1462 int nCard = lit[1].getConst<Rational>().getNumerator().getSignedInt();
1463 Node ct = d_rep_model[tn]->getCardinalityTerm();
1464 Trace("uf-ss-debug") << "...check cardinality terms : " << lit[0] << " " << ct << std::endl;
1465 if( lit[0]==ct ){
1466 if( options::ufssFairnessMonotone() ){
1467 Trace("uf-ss-com-card-debug") << "...set master/slave" << std::endl;
1468 if( tn!=d_tn_mono_master ){
1469 std::map< TypeNode, bool >::iterator it = d_tn_mono_slave.find( tn );
1470 if( it==d_tn_mono_slave.end() ){
1471 bool isMonotonic;
1472 SortInference* si = getSortInference();
1473 if (si != nullptr)
1474 {
1475 isMonotonic = si->isMonotonic(tn);
1476 }else{
1477 //if ground, everything is monotonic
1478 isMonotonic = true;
1479 }
1480 if( isMonotonic ){
1481 if( d_tn_mono_master.isNull() ){
1482 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set master : " << tn << std::endl;
1483 d_tn_mono_master = tn;
1484 }else{
1485 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set slave : " << tn << std::endl;
1486 d_tn_mono_slave[tn] = true;
1487 }
1488 }else{
1489 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set non-monotonic : " << tn << std::endl;
1490 d_tn_mono_slave[tn] = false;
1491 }
1492 }
1493 }
1494 //set the minimum positive cardinality for master if necessary
1495 if( polarity && tn==d_tn_mono_master ){
1496 Trace("uf-ss-com-card-debug") << "...set min positive cardinality" << std::endl;
1497 if( d_min_pos_tn_master_card.get()==-1 || nCard<d_min_pos_tn_master_card.get() ){
1498 d_min_pos_tn_master_card.set( nCard );
1499 }
1500 }
1501 }
1502 Trace("uf-ss-com-card-debug") << "...assert cardinality" << std::endl;
1503 d_rep_model[tn]->assertCardinality(nCard, polarity);
1504 //check if combined cardinality is violated
1505 checkCombinedCardinality();
1506 }else{
1507 //otherwise, make equal via lemma
1508 if( d_card_assertions_eqv_lemma.find( lit )==d_card_assertions_eqv_lemma.end() ){
1509 Node eqv_lit = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, ct, lit[1] );
1510 eqv_lit = lit.eqNode( eqv_lit );
1511 Trace("uf-ss-lemma") << "*** Cardinality equiv lemma : " << eqv_lit << std::endl;
1512 d_im.lemma(eqv_lit, LemmaProperty::NONE, false);
1513 d_card_assertions_eqv_lemma[lit] = true;
1514 }
1515 }
1516 }else if( lit.getKind()==COMBINED_CARDINALITY_CONSTRAINT ){
1517 if( polarity ){
1518 //safe to assume int here
1519 int nCard = lit[0].getConst<Rational>().getNumerator().getSignedInt();
1520 if( d_min_pos_com_card.get()==-1 || nCard<d_min_pos_com_card.get() ){
1521 d_min_pos_com_card.set( nCard );
1522 checkCombinedCardinality();
1523 }
1524 }
1525 }else{
1526 if( Trace.isOn("uf-ss-warn") ){
1527 ////FIXME: this is too strict: theory propagations are showing up as isDecision=true, but
1528 //// a theory propagation is not a decision.
1529 if( isDecision ){
1530 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1531 if( !it->second->hasCardinalityAsserted() ){
1532 Trace("uf-ss-warn") << "WARNING: Assert " << n << " as a decision before cardinality for " << it->first << "." << std::endl;
1533 //Message() << "Error: constraint asserted before cardinality for " << it->first << std::endl;
1534 //Unimplemented();
1535 }
1536 }
1537 }
1538 }
1539 }
1540 }
1541 else
1542 {
1543 if( lit.getKind()==CARDINALITY_CONSTRAINT || lit.getKind()==COMBINED_CARDINALITY_CONSTRAINT ){
1544 // cardinality constraint from user input, set incomplete
1545 Trace("uf-ss") << "Literal " << lit << " not handled when uf ss mode is not FULL, set incomplete." << std::endl;
1546 d_im.setIncomplete();
1547 }
1548 }
1549 Trace("uf-ss") << "Assert: done " << n << " " << isDecision << std::endl;
1550 }
1551
1552 bool CardinalityExtension::areDisequal(Node a, Node b)
1553 {
1554 if( a==b ){
1555 return false;
1556 }
1557 eq::EqualityEngine* ee = d_th->getEqualityEngine();
1558 a = ee->getRepresentative(a);
1559 b = ee->getRepresentative(b);
1560 if (ee->areDisequal(a, b, false))
1561 {
1562 return true;
1563 }
1564 SortModel* c = getSortModel(a);
1565 if (c)
1566 {
1567 return c->areDisequal(a, b);
1568 }
1569 return false;
1570 }
1571
1572 /** check */
1573 void CardinalityExtension::check(Theory::Effort level)
1574 {
1575 if (!d_state.isInConflict())
1576 {
1577 if (options::ufssMode() == options::UfssMode::FULL)
1578 {
1579 Trace("uf-ss-solver")
1580 << "CardinalityExtension: check " << level << std::endl;
1581 if (level == Theory::EFFORT_FULL)
1582 {
1583 if (Debug.isOn("uf-ss-debug"))
1584 {
1585 debugPrint("uf-ss-debug");
1586 }
1587 if (Trace.isOn("uf-ss-state"))
1588 {
1589 Trace("uf-ss-state")
1590 << "CardinalityExtension::check " << level << std::endl;
1591 for (std::pair<const TypeNode, SortModel*>& rm : d_rep_model)
1592 {
1593 Trace("uf-ss-state") << " " << rm.first << " has cardinality "
1594 << rm.second->getCardinality() << std::endl;
1595 }
1596 }
1597 }
1598 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1599 it->second->check(level);
1600 if (d_state.isInConflict())
1601 {
1602 break;
1603 }
1604 }
1605 }
1606 else if (options::ufssMode() == options::UfssMode::NO_MINIMAL)
1607 {
1608 if( level==Theory::EFFORT_FULL ){
1609 // split on an equality between two equivalence classes (at most one per type)
1610 std::map< TypeNode, std::vector< Node > > eqc_list;
1611 std::map< TypeNode, bool > type_proc;
1612 eq::EqClassesIterator eqcs_i(d_th->getEqualityEngine());
1613 while( !eqcs_i.isFinished() ){
1614 Node a = *eqcs_i;
1615 TypeNode tn = a.getType();
1616 if( tn.isSort() ){
1617 if( type_proc.find( tn )==type_proc.end() ){
1618 std::map< TypeNode, std::vector< Node > >::iterator itel = eqc_list.find( tn );
1619 if( itel!=eqc_list.end() ){
1620 for( unsigned j=0; j<itel->second.size(); j++ ){
1621 Node b = itel->second[j];
1622 if( !d_th->getEqualityEngine()->areDisequal( a, b, false ) ){
1623 Node eq = Rewriter::rewrite( a.eqNode( b ) );
1624 Node lem = NodeManager::currentNM()->mkNode( kind::OR, eq, eq.negate() );
1625 Trace("uf-ss-lemma") << "*** Split (no-minimal) : " << lem << std::endl;
1626 d_im.lemma(lem, LemmaProperty::NONE, false);
1627 d_im.requirePhase(eq, true);
1628 type_proc[tn] = true;
1629 break;
1630 }
1631 }
1632 }
1633 eqc_list[tn].push_back( a );
1634 }
1635 }
1636 ++eqcs_i;
1637 }
1638 }
1639 }
1640 else
1641 {
1642 // unhandled uf ss mode
1643 Assert(false);
1644 }
1645 Trace("uf-ss-solver") << "Done CardinalityExtension: check " << level
1646 << std::endl;
1647 }
1648 }
1649
1650 void CardinalityExtension::presolve()
1651 {
1652 d_initializedCombinedCardinality = false;
1653 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1654 it->second->presolve();
1655 it->second->initialize();
1656 }
1657 }
1658
1659 CardinalityExtension::CombinedCardinalityDecisionStrategy::
1660 CombinedCardinalityDecisionStrategy(context::Context* satContext,
1661 Valuation valuation)
1662 : DecisionStrategyFmf(satContext, valuation)
1663 {
1664 }
1665 Node CardinalityExtension::CombinedCardinalityDecisionStrategy::mkLiteral(
1666 unsigned i)
1667 {
1668 NodeManager* nm = NodeManager::currentNM();
1669 return nm->mkNode(COMBINED_CARDINALITY_CONSTRAINT, nm->mkConst(Rational(i)));
1670 }
1671
1672 std::string
1673 CardinalityExtension::CombinedCardinalityDecisionStrategy::identify() const
1674 {
1675 return std::string("uf_combined_card");
1676 }
1677
1678 void CardinalityExtension::preRegisterTerm(TNode n)
1679 {
1680 if (options::ufssMode() == options::UfssMode::FULL)
1681 {
1682 //initialize combined cardinality
1683 initializeCombinedCardinality();
1684
1685 Trace("uf-ss-register") << "Preregister " << n << "." << std::endl;
1686 //shouldn't have to preregister this type (it may be that there are no quantifiers over tn)
1687 TypeNode tn = n.getType();
1688 std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn );
1689 if( it==d_rep_model.end() ){
1690 SortModel* rm = NULL;
1691 if( tn.isSort() ){
1692 Trace("uf-ss-register") << "Create sort model " << tn << "." << std::endl;
1693 rm = new SortModel(n, d_state, d_im, this);
1694 }
1695 if( rm ){
1696 rm->initialize();
1697 d_rep_model[tn] = rm;
1698 //d_rep_model_init[tn] = true;
1699 }
1700 }else{
1701 //ensure sort model is initialized
1702 it->second->initialize();
1703 }
1704 }
1705 }
1706
1707 SortModel* CardinalityExtension::getSortModel(Node n)
1708 {
1709 TypeNode tn = n.getType();
1710 std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn );
1711 //pre-register the type if not done already
1712 if( it==d_rep_model.end() ){
1713 preRegisterTerm( n );
1714 it = d_rep_model.find( tn );
1715 }
1716 if( it!=d_rep_model.end() ){
1717 return it->second;
1718 }else{
1719 return NULL;
1720 }
1721 }
1722
1723 /** get cardinality for sort */
1724 int CardinalityExtension::getCardinality(Node n)
1725 {
1726 SortModel* c = getSortModel( n );
1727 if( c ){
1728 return c->getCardinality();
1729 }else{
1730 return -1;
1731 }
1732 }
1733
1734 int CardinalityExtension::getCardinality(TypeNode tn)
1735 {
1736 std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn );
1737 if( it!=d_rep_model.end() && it->second ){
1738 return it->second->getCardinality();
1739 }
1740 return -1;
1741 }
1742
1743 //print debug
1744 void CardinalityExtension::debugPrint(const char* c)
1745 {
1746 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1747 Debug( c ) << "Conflict find structure for " << it->first << ": " << std::endl;
1748 it->second->debugPrint( c );
1749 Debug( c ) << std::endl;
1750 }
1751 }
1752
1753 bool CardinalityExtension::debugModel(TheoryModel* m)
1754 {
1755 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1756 if( !it->second->debugModel( m ) ){
1757 return false;
1758 }
1759 }
1760 return true;
1761 }
1762
1763 /** initialize */
1764 void CardinalityExtension::initializeCombinedCardinality()
1765 {
1766 if (d_cc_dec_strat.get() != nullptr
1767 && !d_initializedCombinedCardinality.get())
1768 {
1769 d_initializedCombinedCardinality = true;
1770 d_th->getDecisionManager()->registerStrategy(
1771 DecisionManager::STRAT_UF_COMBINED_CARD, d_cc_dec_strat.get());
1772 }
1773 }
1774
1775 /** check */
1776 void CardinalityExtension::checkCombinedCardinality()
1777 {
1778 Assert(options::ufssMode() == options::UfssMode::FULL);
1779 if( options::ufssFairness() ){
1780 Trace("uf-ss-com-card-debug") << "Check combined cardinality, get maximum negative cardinalities..." << std::endl;
1781 int totalCombinedCard = 0;
1782 int maxMonoSlave = 0;
1783 TypeNode maxSlaveType;
1784 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){
1785 int max_neg = it->second->getMaximumNegativeCardinality();
1786 if( !options::ufssFairnessMonotone() ){
1787 totalCombinedCard += max_neg;
1788 }else{
1789 std::map< TypeNode, bool >::iterator its = d_tn_mono_slave.find( it->first );
1790 if( its==d_tn_mono_slave.end() || !its->second ){
1791 totalCombinedCard += max_neg;
1792 }else{
1793 if( max_neg>maxMonoSlave ){
1794 maxMonoSlave = max_neg;
1795 maxSlaveType = it->first;
1796 }
1797 }
1798 }
1799 }
1800 Trace("uf-ss-com-card-debug") << "Check combined cardinality, total combined card : " << totalCombinedCard << std::endl;
1801 if( options::ufssFairnessMonotone() ){
1802 Trace("uf-ss-com-card-debug") << "Max slave monotonic negated cardinality : " << maxMonoSlave << std::endl;
1803 if( d_min_pos_tn_master_card.get()!=-1 && maxMonoSlave>d_min_pos_tn_master_card.get() ){
1804 int mc = d_min_pos_tn_master_card.get();
1805 std::vector< Node > conf;
1806 conf.push_back( d_rep_model[d_tn_mono_master]->getCardinalityLiteral( mc ) );
1807 conf.push_back( d_rep_model[maxSlaveType]->getCardinalityLiteral( maxMonoSlave ).negate() );
1808 Node cf = NodeManager::currentNM()->mkNode( AND, conf );
1809 Trace("uf-ss-lemma") << "*** Combined monotone cardinality conflict"
1810 << " : " << cf << std::endl;
1811 Trace("uf-ss-com-card") << "*** Combined monotone cardinality conflict"
1812 << " : " << cf << std::endl;
1813 d_im.conflict(cf);
1814 return;
1815 }
1816 }
1817 int cc = d_min_pos_com_card.get();
1818 if( cc !=-1 && totalCombinedCard > cc ){
1819 //conflict
1820 Node com_lit = d_cc_dec_strat->getLiteral(cc);
1821 std::vector< Node > conf;
1822 conf.push_back( com_lit );
1823 int totalAdded = 0;
1824 for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin();
1825 it != d_rep_model.end(); ++it ){
1826 bool doAdd = true;
1827 if( options::ufssFairnessMonotone() ){
1828 std::map< TypeNode, bool >::iterator its =
1829 d_tn_mono_slave.find( it->first );
1830 if( its!=d_tn_mono_slave.end() && its->second ){
1831 doAdd = false;
1832 }
1833 }
1834 if( doAdd ){
1835 int c = it->second->getMaximumNegativeCardinality();
1836 if( c>0 ){
1837 conf.push_back( it->second->getCardinalityLiteral( c ).negate() );
1838 totalAdded += c;
1839 }
1840 if( totalAdded>cc ){
1841 break;
1842 }
1843 }
1844 }
1845 Node cf = NodeManager::currentNM()->mkNode( AND, conf );
1846 Trace("uf-ss-lemma") << "*** Combined cardinality conflict : " << cf
1847 << std::endl;
1848 Trace("uf-ss-com-card") << "*** Combined cardinality conflict : " << cf
1849 << std::endl;
1850 d_im.conflict(cf);
1851 }
1852 }
1853 }
1854
1855 CardinalityExtension::Statistics::Statistics()
1856 : d_clique_conflicts("CardinalityExtension::Clique_Conflicts", 0),
1857 d_clique_lemmas("CardinalityExtension::Clique_Lemmas", 0),
1858 d_split_lemmas("CardinalityExtension::Split_Lemmas", 0),
1859 d_disamb_term_lemmas("CardinalityExtension::Disambiguate_Term_Lemmas", 0),
1860 d_totality_lemmas("CardinalityExtension::Totality_Lemmas", 0),
1861 d_max_model_size("CardinalityExtension::Max_Model_Size", 1)
1862 {
1863 smtStatisticsRegistry()->registerStat(&d_clique_conflicts);
1864 smtStatisticsRegistry()->registerStat(&d_clique_lemmas);
1865 smtStatisticsRegistry()->registerStat(&d_split_lemmas);
1866 smtStatisticsRegistry()->registerStat(&d_disamb_term_lemmas);
1867 smtStatisticsRegistry()->registerStat(&d_totality_lemmas);
1868 smtStatisticsRegistry()->registerStat(&d_max_model_size);
1869 }
1870
1871 CardinalityExtension::Statistics::~Statistics()
1872 {
1873 smtStatisticsRegistry()->unregisterStat(&d_clique_conflicts);
1874 smtStatisticsRegistry()->unregisterStat(&d_clique_lemmas);
1875 smtStatisticsRegistry()->unregisterStat(&d_split_lemmas);
1876 smtStatisticsRegistry()->unregisterStat(&d_disamb_term_lemmas);
1877 smtStatisticsRegistry()->unregisterStat(&d_totality_lemmas);
1878 smtStatisticsRegistry()->unregisterStat(&d_max_model_size);
1879 }
1880
1881 }/* CVC4::theory namespace::uf */
1882 }/* CVC4::theory namespace */
1883 }/* CVC4 namespace */