1 /********************* */
2 /*! \file theory_uf_strong_solver.cpp
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-2017 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
12 ** \brief Implementation of theory uf strong solver class
15 #include "theory/uf/theory_uf_strong_solver.h"
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 #include "theory/quantifiers/symmetry_breaking.h"
26 //#define ONE_SPLIT_REGION
27 //#define DISABLE_QUICK_CLIQUE_CHECKS
28 //#define COMBINE_REGIONS_SMALL_INTO_LARGE
29 //#define LAZY_REL_EQC
32 using namespace CVC4::kind
;
33 using namespace CVC4::context
;
40 /* These are names are unambigious are we use abbreviations. */
41 typedef StrongSolverTheoryUF::SortModel SortModel
;
42 typedef SortModel::Region Region
;
43 typedef Region::RegionNodeInfo RegionNodeInfo
;
44 typedef RegionNodeInfo::DiseqList DiseqList
;
46 Region::Region(SortModel
* cf
, context::Context
* c
)
48 , d_testCliqueSize( c
, 0 )
49 , d_splitsSize( c
, 0 )
53 , d_total_diseq_external( c
, 0 )
54 , d_total_diseq_internal( c
, 0 )
55 , d_valid( c
, true ) {}
58 for(iterator i
= begin(), iend
= end(); i
!= iend
; ++i
) {
59 RegionNodeInfo
* regionNodeInfo
= (*i
).second
;
60 delete regionNodeInfo
;
65 void Region::addRep( Node n
) {
69 void Region::takeNode( Region
* r
, Node n
){
70 Assert( !hasRep( n
) );
71 Assert( r
->hasRep( n
) );
74 //take disequalities from r
75 RegionNodeInfo
* rni
= r
->d_nodes
[n
];
76 for( int t
=0; t
<2; t
++ ){
77 DiseqList
* del
= rni
->get(t
);
78 for(DiseqList::iterator it
= del
->begin(); it
!= del
->end(); ++it
){
80 r
->setDisequal( n
, (*it
).first
, t
, false );
82 if( hasRep( (*it
).first
) ){
83 setDisequal( (*it
).first
, n
, 0, false );
84 setDisequal( (*it
).first
, n
, 1, true );
85 setDisequal( n
, (*it
).first
, 1, true );
87 setDisequal( n
, (*it
).first
, 0, true );
90 r
->setDisequal( (*it
).first
, n
, 1, false );
91 r
->setDisequal( (*it
).first
, n
, 0, true );
92 setDisequal( n
, (*it
).first
, 0, true );
97 //remove representative
98 r
->setRep( n
, false );
101 void Region::combine( Region
* r
){
102 //take all nodes from r
103 for(Region::iterator it
= r
->d_nodes
.begin(); it
!= r
->d_nodes
.end(); ++it
) {
104 if( it
->second
->valid() ){
105 setRep( it
->first
, true );
108 for(Region::iterator it
= r
->d_nodes
.begin(); it
!= r
->d_nodes
.end(); ++it
){
109 if( it
->second
->valid() ){
110 //take disequalities from r
112 RegionNodeInfo
* rni
= it
->second
;
113 for( int t
=0; t
<2; t
++ ){
114 RegionNodeInfo::DiseqList
* del
= rni
->get(t
);
115 for( RegionNodeInfo::DiseqList::iterator it2
= del
->begin(),
116 it2end
= del
->end(); it2
!= it2end
; ++it2
){
118 if( t
==0 && hasRep( (*it2
).first
) ){
119 setDisequal( (*it2
).first
, n
, 0, false );
120 setDisequal( (*it2
).first
, n
, 1, true );
121 setDisequal( n
, (*it2
).first
, 1, true );
123 setDisequal( n
, (*it2
).first
, t
, true );
134 void Region::setEqual( Node a
, Node b
){
135 Assert( hasRep( a
) && hasRep( b
) );
136 //move disequalities of b over to a
137 for( int t
=0; t
<2; t
++ ){
138 DiseqList
* del
= d_nodes
[b
]->get(t
);
139 for( DiseqList::iterator it
= del
->begin(); it
!= del
->end(); ++it
){
141 Node n
= (*it
).first
;
142 //get the region that contains the endpoint of the disequality b != ...
143 Region
* nr
= d_cf
->d_regions
[ d_cf
->d_regions_map
[ n
] ];
144 if( !isDisequal( a
, n
, t
) ){
145 setDisequal( a
, n
, t
, true );
146 nr
->setDisequal( n
, a
, t
, true );
147 //notify the disequality propagator
148 if( options::ufssDiseqPropagation() ){
149 d_cf
->d_thss
->getDisequalityPropagator()->assertDisequal(a
, n
, Node::null());
151 if( options::ufssSymBreak() ){
152 d_cf
->d_thss
->getSymmetryBreaker()->assertDisequal( a
, n
);
155 setDisequal( b
, n
, t
, false );
156 nr
->setDisequal( n
, b
, t
, false );
160 //remove b from representatives
164 void Region::setDisequal( Node n1
, Node n2
, int type
, bool valid
){
165 //Debug("uf-ss-region-debug") << "set disequal " << n1 << " " << n2 << " "
166 // << type << " " << valid << std::endl;
167 //debugPrint("uf-ss-region-debug");
168 //Assert( isDisequal( n1, n2, type )!=valid );
169 if( isDisequal( n1
, n2
, type
)!=valid
){ //DO_THIS: make assertion
170 d_nodes
[ n1
]->get(type
)->setDisequal( n2
, valid
);
172 d_total_diseq_external
= d_total_diseq_external
+ ( valid
? 1 : -1 );
174 d_total_diseq_internal
= d_total_diseq_internal
+ ( valid
? 1 : -1 );
176 //if they are both a part of testClique, then remove split
177 if( d_testClique
.find( n1
)!=d_testClique
.end() && d_testClique
[n1
] &&
178 d_testClique
.find( n2
)!=d_testClique
.end() && d_testClique
[n2
] ){
179 Node eq
= NodeManager::currentNM()->mkNode( EQUAL
, n1
, n2
);
180 if( d_splits
.find( eq
)!=d_splits
.end() && d_splits
[ eq
] ){
181 Debug("uf-ss-debug") << "removing split for " << n1
<< " " << n2
183 d_splits
[ eq
] = false;
184 d_splitsSize
= d_splitsSize
- 1;
192 void Region::setRep( Node n
, bool valid
) {
193 Assert( hasRep( n
)!=valid
);
194 if( valid
&& d_nodes
.find( n
)==d_nodes
.end() ){
195 d_nodes
[n
] = new RegionNodeInfo( d_cf
->d_thss
->getSatContext() );
197 d_nodes
[n
]->setValid(valid
);
198 d_reps_size
= d_reps_size
+ ( valid
? 1 : -1 );
199 //removing a member of the test clique from this region
200 if( d_testClique
.find( n
) != d_testClique
.end() && d_testClique
[n
] ){
202 d_testClique
[n
] = false;
203 d_testCliqueSize
= d_testCliqueSize
- 1;
204 //remove all splits involving n
205 for( split_iterator it
= begin_splits(); it
!= end_splits(); ++it
){
207 if( (*it
).first
[0]==n
|| (*it
).first
[1]==n
){
208 d_splits
[ (*it
).first
] = false;
209 d_splitsSize
= d_splitsSize
- 1;
216 bool Region::isDisequal( Node n1
, Node n2
, int type
) {
217 RegionNodeInfo::DiseqList
* del
= d_nodes
[ n1
]->get(type
);
218 return del
->isSet(n2
) && del
->getDisequalityValue(n2
);
221 struct sortInternalDegree
{
223 bool operator() (Node i
, Node j
) {
224 return (r
->getRegionInfo(i
)->getNumInternalDisequalities() >
225 r
->getRegionInfo(j
)->getNumInternalDisequalities());
229 struct sortExternalDegree
{
231 bool operator() (Node i
,Node j
) {
232 return (r
->getRegionInfo(i
)->getNumExternalDisequalities() >
233 r
->getRegionInfo(j
)->getNumExternalDisequalities());
239 bool Region::getMustCombine( int cardinality
){
240 if( options::ufssRegions() && d_total_diseq_external
>=unsigned(cardinality
) ){
241 //The number of external disequalities is greater than or equal to
242 //cardinality. Thus, a clique of size cardinality+1 may exist
243 //between nodes in d_regions[i] and other regions Check if this is
244 //actually the case: must have n nodes with outgoing degree
245 //(cardinality+1-n) for some n>0
246 std::vector
< int > degrees
;
247 for( Region::iterator it
= begin(); it
!= end(); ++it
){
248 RegionNodeInfo
* rni
= it
->second
;
250 if( rni
->getNumDisequalities() >= cardinality
){
251 int outDeg
= rni
->getNumExternalDisequalities();
252 if( outDeg
>=cardinality
){
253 //we have 1 node of degree greater than (cardinality)
255 }else if( outDeg
>=1 ){
256 degrees
.push_back( outDeg
);
257 if( (int)degrees
.size()>=cardinality
){
258 //we have (cardinality) nodes of degree 1
266 if( gmcCount
%100==0 ){
267 Trace("gmc-count") << gmcCount
<< " " << cardinality
268 << " sample : " << degrees
.size() << std::endl
;
270 //this should happen relatively infrequently....
271 std::sort( degrees
.begin(), degrees
.end() );
272 for( int i
=0; i
<(int)degrees
.size(); i
++ ){
273 if( degrees
[i
]>=cardinality
+1-((int)degrees
.size()-i
) ){
281 bool Region::check( Theory::Effort level
, int cardinality
,
282 std::vector
< Node
>& clique
) {
283 if( d_reps_size
>unsigned(cardinality
) ){
284 if( d_total_diseq_internal
==d_reps_size
*( d_reps_size
- 1 ) ){
286 //quick clique check, all reps form a clique
287 for( iterator it
= begin(); it
!= end(); ++it
){
288 if( it
->second
->valid() ){
289 clique
.push_back( it
->first
);
292 Trace("quick-clique") << "Found quick clique" << std::endl
;
297 }else if( options::ufssRegions() || options::ufssEagerSplits() ||
298 level
==Theory::EFFORT_FULL
) {
299 //build test clique, up to size cardinality+1
300 if( d_testCliqueSize
<=unsigned(cardinality
) ){
301 std::vector
< Node
> newClique
;
302 if( d_testCliqueSize
<unsigned(cardinality
) ){
303 for( iterator it
= begin(); it
!= end(); ++it
){
304 //if not in the test clique, add it to the set of new members
305 if( it
->second
->valid() &&
306 ( d_testClique
.find( it
->first
) == d_testClique
.end() ||
307 !d_testClique
[ it
->first
] ) ){
308 //if( it->second->getNumInternalDisequalities()>cardinality ||
309 // level==Theory::EFFORT_FULL ){
310 newClique
.push_back( it
->first
);
314 //choose remaining nodes with the highest degrees
315 sortInternalDegree sidObj
;
317 std::sort( newClique
.begin(), newClique
.end(), sidObj
);
318 int offset
= ( cardinality
- d_testCliqueSize
) + 1;
319 newClique
.erase( newClique
.begin() + offset
, newClique
.end() );
321 //scan for the highest degree
324 for( std::map
< Node
, RegionNodeInfo
* >::iterator
325 it
= d_nodes
.begin(); it
!= d_nodes
.end(); ++it
){
326 //if not in the test clique, add it to the set of new members
327 if( it
->second
->valid() &&
328 ( d_testClique
.find( it
->first
)==d_testClique
.end() ||
329 !d_testClique
[ it
->first
] ) ){
330 if( it
->second
->getNumInternalDisequalities()>maxDeg
){
331 maxDeg
= it
->second
->getNumInternalDisequalities();
336 Assert( maxNode
!=Node::null() );
337 newClique
.push_back( maxNode
);
339 //check splits internal to new members
340 for( int j
=0; j
<(int)newClique
.size(); j
++ ){
341 Debug("uf-ss-debug") << "Choose to add clique member "
342 << newClique
[j
] << std::endl
;
343 for( int k
=(j
+1); k
<(int)newClique
.size(); k
++ ){
344 if( !isDisequal( newClique
[j
], newClique
[k
], 1 ) ){
345 Node at_j
= newClique
[j
];
346 Node at_k
= newClique
[k
];
348 NodeManager::currentNM()->mkNode( EQUAL
, at_j
, at_k
);
349 d_splits
[ j_eq_k
] = true;
350 d_splitsSize
= d_splitsSize
+ 1;
353 //check disequalities with old members
354 for( NodeBoolMap::iterator it
= d_testClique
.begin();
355 it
!= d_testClique
.end(); ++it
){
357 if( !isDisequal( (*it
).first
, newClique
[j
], 1 ) ){
358 Node at_it
= (*it
).first
;
359 Node at_j
= newClique
[j
];
360 Node it_eq_j
= at_it
.eqNode(at_j
);
361 d_splits
[ it_eq_j
] = true;
362 d_splitsSize
= d_splitsSize
+ 1;
367 //add new clique members to test clique
368 for( int j
=0; j
<(int)newClique
.size(); j
++ ){
369 d_testClique
[ newClique
[j
] ] = true;
370 d_testCliqueSize
= d_testCliqueSize
+ 1;
373 // Check if test clique has larger size than cardinality, and
375 if( d_testCliqueSize
>= unsigned(cardinality
+1) && d_splitsSize
==0 ){
376 //test clique is a clique
377 for( NodeBoolMap::iterator it
= d_testClique
.begin();
378 it
!= d_testClique
.end(); ++it
){
380 clique
.push_back( (*it
).first
);
390 bool Region::getCandidateClique( int cardinality
, std::vector
< Node
>& clique
)
392 if( d_testCliqueSize
>=unsigned(cardinality
+1) ){
393 //test clique is a clique
394 for( NodeBoolMap::iterator it
= d_testClique
.begin();
395 it
!= d_testClique
.end(); ++it
){
397 clique
.push_back( (*it
).first
);
405 void Region::getNumExternalDisequalities(
406 std::map
< Node
, int >& num_ext_disequalities
){
407 for( Region::iterator it
= begin(); it
!= end(); ++it
){
408 RegionNodeInfo
* rni
= it
->second
;
410 DiseqList
* del
= rni
->get(0);
411 for( DiseqList::iterator it2
= del
->begin(); it2
!= del
->end(); ++it2
){
413 num_ext_disequalities
[ (*it2
).first
]++;
420 void Region::debugPrint( const char* c
, bool incClique
) {
421 Debug( c
) << "Num reps: " << d_reps_size
<< std::endl
;
422 for( Region::iterator it
= begin(); it
!= end(); ++it
){
423 RegionNodeInfo
* rni
= it
->second
;
426 Debug( c
) << " " << n
<< std::endl
;
427 for( int i
=0; i
<2; i
++ ){
428 Debug( c
) << " " << ( i
==0 ? "Ext" : "Int" ) << " disequal:";
429 DiseqList
* del
= rni
->get(i
);
430 for( DiseqList::iterator it2
= del
->begin(); it2
!= del
->end(); ++it2
){
432 Debug( c
) << " " << (*it2
).first
;
435 Debug( c
) << ", total = " << del
->size() << std::endl
;
439 Debug( c
) << "Total disequal: " << d_total_diseq_external
<< " external,";
440 Debug( c
) << " " << d_total_diseq_internal
<< " internal." << std::endl
;
443 if( !d_testClique
.empty() ){
444 Debug( c
) << "Candidate clique members: " << std::endl
;
446 for( NodeBoolMap::iterator it
= d_testClique
.begin();
447 it
!= d_testClique
.end(); ++ it
){
449 Debug( c
) << (*it
).first
<< " ";
452 Debug( c
) << ", size = " << d_testCliqueSize
<< std::endl
;
454 if( !d_splits
.empty() ){
455 Debug( c
) << "Required splits: " << std::endl
;
457 for( NodeBoolMap::iterator it
= d_splits
.begin(); it
!= d_splits
.end();
460 Debug( c
) << (*it
).first
<< " ";
463 Debug( c
) << ", size = " << d_splitsSize
<< std::endl
;
468 SortModel::SortModel( Node n
,
470 context::UserContext
* u
,
471 StrongSolverTheoryUF
* thss
)
472 : d_type( n
.getType() )
474 , d_regions_index( c
, 0 )
477 , d_disequalities_index( c
, 0 )
479 , d_conflict( c
, false )
480 , d_cardinality( c
, 1 )
481 , d_aloc_cardinality( u
, 0 )
482 , d_hasCard( c
, false )
483 , d_maxNegCard( c
, 0 )
484 , d_initialized( u
, false )
487 d_cardinality_term
= n
;
488 //if( d_type.isSort() ){
489 // TypeEnumerator te(tn);
490 // d_cardinality_term = *te;
492 // d_cardinality_term = tn.mkGroundTerm();
496 SortModel::~SortModel() {
497 for(std::vector
<Region
*>::iterator i
= d_regions
.begin();
498 i
!= d_regions
.end(); ++i
) {
506 void SortModel::initialize( OutputChannel
* out
){
507 if( !d_initialized
){
508 d_initialized
= true;
509 allocateCardinality( out
);
514 void SortModel::newEqClass( Node n
){
516 if( d_regions_map
.find( n
)==d_regions_map
.end() ){
517 // Must generate totality axioms for every cardinality we have
518 // allocated thus far.
519 for( std::map
< int, Node
>::iterator it
= d_cardinality_literal
.begin();
520 it
!= d_cardinality_literal
.end(); ++it
){
521 if( applyTotality( it
->first
) ){
522 addTotalityAxiom( n
, it
->first
, &d_thss
->getOutputChannel() );
525 if( options::ufssTotality() ){
526 // Regions map will store whether we need to equate this term
527 // with a constant equivalence class.
528 if( std::find( d_totality_terms
[0].begin(), d_totality_terms
[0].end(), n
)==d_totality_terms
[0].end() ){
529 d_regions_map
[n
] = 0;
531 d_regions_map
[n
] = -1;
534 if( !options::ufssRegions() ){
535 // If not using regions, always add new equivalence classes
536 // to region index = 0.
539 d_regions_map
[n
] = d_regions_index
;
540 Debug("uf-ss") << "StrongSolverTheoryUF: New Eq Class " << n
542 Debug("uf-ss-debug") << d_regions_index
<< " "
543 << (int)d_regions
.size() << std::endl
;
544 if( d_regions_index
<d_regions
.size() ){
545 d_regions
[ d_regions_index
]->debugPrint("uf-ss-debug",true);
546 d_regions
[ d_regions_index
]->setValid(true);
547 Assert( !options::ufssRegions() ||
548 d_regions
[ d_regions_index
]->getNumReps()==0 );
550 d_regions
.push_back( new Region( this, d_thss
->getSatContext() ) );
552 d_regions
[ d_regions_index
]->addRep( n
);
553 d_regions_index
= d_regions_index
+ 1;
561 void SortModel::merge( Node a
, Node b
){
563 if( options::ufssTotality() ){
564 if( d_regions_map
[b
]==-1 ){
565 d_regions_map
[a
] = -1;
567 d_regions_map
[b
] = -1;
569 //Assert( a==d_th->d_equalityEngine.getRepresentative( a ) );
570 //Assert( b==d_th->d_equalityEngine.getRepresentative( b ) );
571 Debug("uf-ss") << "StrongSolverTheoryUF: Merging "
572 << a
<< " = " << b
<< "..." << std::endl
;
574 Assert( d_regions_map
.find( a
)!=d_regions_map
.end() );
575 Assert( d_regions_map
.find( b
)!=d_regions_map
.end() );
576 int ai
= d_regions_map
[a
];
577 int bi
= d_regions_map
[b
];
578 Debug("uf-ss") << " regions: " << ai
<< " " << bi
<< std::endl
;
580 if( d_regions
[ai
]->getNumReps()==1 ){
581 int ri
= combineRegions( bi
, ai
);
582 d_regions
[ri
]->setEqual( a
, b
);
584 }else if( d_regions
[bi
]->getNumReps()==1 ){
585 int ri
= combineRegions( ai
, bi
);
586 d_regions
[ri
]->setEqual( a
, b
);
589 // Either move a to d_regions[bi], or b to d_regions[ai].
590 RegionNodeInfo
* a_region_info
= d_regions
[ai
]->getRegionInfo(a
);
591 RegionNodeInfo
* b_region_info
= d_regions
[bi
]->getRegionInfo(b
);
592 int aex
= ( a_region_info
->getNumInternalDisequalities() -
593 getNumDisequalitiesToRegion( a
, bi
) );
594 int bex
= ( b_region_info
->getNumInternalDisequalities() -
595 getNumDisequalitiesToRegion( b
, ai
) );
596 // Based on which would produce the fewest number of
597 // external disequalities.
600 d_regions
[bi
]->setEqual( a
, b
);
603 d_regions
[ai
]->setEqual( a
, b
);
609 d_regions
[ai
]->setEqual( a
, b
);
612 d_regions_map
[b
] = -1;
617 if( options::ufssDiseqPropagation() ){
618 //notify the disequality propagator
619 d_thss
->getDisequalityPropagator()->merge(a
, b
);
621 if( options::ufssSymBreak() ){
622 d_thss
->getSymmetryBreaker()->merge(a
, b
);
629 /** assert terms are disequal */
630 void SortModel::assertDisequal( Node a
, Node b
, Node reason
){
632 if( options::ufssTotality() ){
635 //if they are not already disequal
636 a
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative( a
);
637 b
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative( b
);
638 int ai
= d_regions_map
[a
];
639 int bi
= d_regions_map
[b
];
640 if( !d_regions
[ai
]->isDisequal( a
, b
, ai
==bi
) ){
641 Debug("uf-ss") << "Assert disequal " << a
<< " != " << b
<< "..." << std::endl
;
642 //if( reason.getKind()!=NOT || reason[0].getKind()!=EQUAL ||
643 // a!=reason[0][0] || b!=reason[0][1] ){
644 // Notice() << "Assert disequal " << a << " != " << b << ", reason = " << reason << "..." << std::endl;
646 Debug("uf-ss-disequal") << "Assert disequal " << a
<< " != " << b
<< "..." << std::endl
;
647 //add to list of disequalities
648 if( d_disequalities_index
<d_disequalities
.size() ){
649 d_disequalities
[d_disequalities_index
] = reason
;
651 d_disequalities
.push_back( reason
);
653 d_disequalities_index
= d_disequalities_index
+ 1;
654 //now, add disequalities to regions
655 Assert( d_regions_map
.find( a
)!=d_regions_map
.end() );
656 Assert( d_regions_map
.find( b
)!=d_regions_map
.end() );
657 Debug("uf-ss") << " regions: " << ai
<< " " << bi
<< std::endl
;
659 //internal disequality
660 d_regions
[ai
]->setDisequal( a
, b
, 1, true );
661 d_regions
[ai
]->setDisequal( b
, a
, 1, true );
662 checkRegion( ai
, false ); //do not need to check if it needs to combine (no new ext. disequalities)
664 //external disequality
665 d_regions
[ai
]->setDisequal( a
, b
, 0, true );
666 d_regions
[bi
]->setDisequal( b
, a
, 0, true );
672 if( options::ufssDiseqPropagation() ){
673 //notify the disequality propagator
674 d_thss
->getDisequalityPropagator()->assertDisequal(a
, b
, Node::null());
676 if( options::ufssSymBreak() ){
677 d_thss
->getSymmetryBreaker()->assertDisequal(a
, b
);
685 bool SortModel::areDisequal( Node a
, Node b
) {
686 Assert( a
== d_thss
->getTheory()->d_equalityEngine
.getRepresentative( a
) );
687 Assert( b
== d_thss
->getTheory()->d_equalityEngine
.getRepresentative( b
) );
688 if( d_regions_map
.find( a
)!=d_regions_map
.end() &&
689 d_regions_map
.find( b
)!=d_regions_map
.end() ){
690 int ai
= d_regions_map
[a
];
691 int bi
= d_regions_map
[b
];
692 return d_regions
[ai
]->isDisequal(a
, b
, ai
==bi
? 1 : 0);
699 void SortModel::check( Theory::Effort level
, OutputChannel
* out
){
700 Assert( options::ufssMode()==UF_SS_FULL
);
701 if( level
>=Theory::EFFORT_STANDARD
&& d_hasCard
&& !d_conflict
){
702 Debug("uf-ss") << "StrongSolverTheoryUF: Check " << level
<< " " << d_type
<< std::endl
;
703 if( level
==Theory::EFFORT_FULL
){
704 Debug("fmf-full-check") << std::endl
;
705 Debug("fmf-full-check") << "Full check for SortModel " << d_type
<< ", status : " << std::endl
;
706 debugPrint("fmf-full-check");
707 Debug("fmf-full-check") << std::endl
;
709 //Notice() << "StrongSolverTheoryUF: Check " << level << std::endl;
710 if( d_reps
<=(unsigned)d_cardinality
){
711 Debug("uf-ss-debug") << "We have " << d_reps
<< " representatives for type " << d_type
<< ", <= " << d_cardinality
<< std::endl
;
712 if( level
==Theory::EFFORT_FULL
){
713 Debug("uf-ss-sat") << "We have " << d_reps
<< " representatives for type " << d_type
<< ", <= " << d_cardinality
<< std::endl
;
714 //Notice() << "We have " << d_reps << " representatives for type " << d_type << ", <= " << cardinality << std::endl;
715 //Notice() << "Model size for " << d_type << " is " << cardinality << std::endl;
716 //Notice() << cardinality << " ";
720 //first check if we can generate a clique conflict
721 if( !options::ufssTotality() ){
722 //do a check within each region
723 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
724 if( d_regions
[i
]->valid() ){
725 std::vector
< Node
> clique
;
726 if( d_regions
[i
]->check( level
, d_cardinality
, clique
) ){
728 addCliqueLemma( clique
, out
);
731 Trace("uf-ss-debug") << "No clique in Region #" << i
<< std::endl
;
736 if( !applyTotality( d_cardinality
) ){
737 //do splitting on demand
738 bool addedLemma
= false;
739 if( level
==Theory::EFFORT_FULL
|| options::ufssEagerSplits() ){
740 Trace("uf-ss-debug") << "Add splits?" << std::endl
;
741 //see if we have any recommended splits from large regions
742 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
743 if( d_regions
[i
]->valid() && d_regions
[i
]->getNumReps()>d_cardinality
){
744 //just add the clique lemma
745 if( level
==Theory::EFFORT_FULL
&& options::ufssCliqueSplits() ){
746 std::vector
< Node
> clique
;
747 if( d_regions
[i
]->getCandidateClique( d_cardinality
, clique
) ){
749 addCliqueLemma( clique
, out
);
753 int sp
= addSplit( d_regions
[i
], out
);
756 #ifdef ONE_SPLIT_REGION
767 //If no added lemmas, force continuation via combination of regions.
768 if( level
==Theory::EFFORT_FULL
){
770 Trace("uf-ss-debug") << "No splits added. " << d_cardinality
772 Trace("uf-ss-si") << "Must combine region" << std::endl
;
773 bool recheck
= false;
774 if( options::sortInference()){
775 //If sort inference is enabled, search for regions with same sort.
776 std::map
< int, int > sortsFound
;
777 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
778 if( d_regions
[i
]->valid() ){
779 Node op
= d_regions
[i
]->frontKey();
780 int sort_id
= d_thss
->getSortInference()->getSortId(op
);
781 if( sortsFound
.find( sort_id
)!=sortsFound
.end() ){
782 Debug("fmf-full-check") << "Combined regions " << i
<< " " << sortsFound
[sort_id
] << std::endl
;
783 combineRegions( sortsFound
[sort_id
], i
);
787 sortsFound
[sort_id
] = i
;
793 //naive strategy, force region combination involving the first valid region
794 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
795 if( d_regions
[i
]->valid() ){
796 int fcr
= forceCombineRegion( i
, false );
797 Debug("fmf-full-check") << "Combined regions " << i
<< " " << fcr
<< std::endl
;
798 Trace("uf-ss-debug") << "Combined regions " << i
<< " " << fcr
<< std::endl
;
805 Trace("uf-ss-debug") << "Must recheck." << std::endl
;
815 void SortModel::presolve() {
816 d_initialized
= false;
817 d_aloc_cardinality
= 0;
820 void SortModel::propagate( Theory::Effort level
, OutputChannel
* out
){
824 Node
SortModel::getNextDecisionRequest(){
825 //request the current cardinality as a decision literal, if not already asserted
826 for( int i
=1; i
<=d_aloc_cardinality
; i
++ ){
827 if( !d_hasCard
|| i
<d_cardinality
){
828 Node cn
= d_cardinality_literal
[ i
];
829 Assert( !cn
.isNull() );
831 if( !d_thss
->getTheory()->d_valuation
.hasSatValue( cn
, value
) ){
832 Trace("uf-ss-dec") << "UFSS : Get next decision " << d_type
<< " " << i
<< std::endl
;
835 Trace("uf-ss-dec-debug") << " dec : " << cn
<< " already asserted " << value
<< std::endl
;
840 Trace("uf-ss-dec") << "UFSS : no decisions for " << d_type
<< "." << std::endl
;
841 Trace("uf-ss-dec-debug") << " aloc_cardinality = " << d_aloc_cardinality
<< ", cardinality = " << d_cardinality
<< ", hasCard = " << d_hasCard
<< std::endl
;
846 bool SortModel::minimize( OutputChannel
* out
, TheoryModel
* m
){
847 if( options::ufssTotality() ){
852 // ensure that the constructed model is minimal
853 // if the model has terms that the strong solver does not know about
854 if( (int)rs
->d_type_reps
[ d_type
].size()>d_cardinality
){
855 eq::EqClassesIterator eqcs_i
= eq::EqClassesIterator( &m
->getEqualityEngine() );
856 while( !eqcs_i
.isFinished() ){
857 Node eqc
= (*eqcs_i
);
858 if( eqc
.getType()==d_type
){
859 //we must ensure that this equivalence class has been accounted for
860 if( d_regions_map
.find( eqc
)==d_regions_map
.end() ){
861 //split on unaccounted for term and cardinality lemma term (as default)
862 Node splitEq
= eqc
.eqNode( d_cardinality_term
);
863 splitEq
= Rewriter::rewrite( splitEq
);
864 Trace("uf-ss-minimize") << "Last chance minimize : " << splitEq
<< std::endl
;
865 out
->split( splitEq
);
866 //tell the sat solver to explore the equals branch first
867 out
->requirePhase( splitEq
, true );
868 ++( d_thss
->d_statistics
.d_split_lemmas
);
878 Trace("uf-ss-debug") << "Minimize the UF model..." << std::endl
;
879 //internal minimize, ensure that model forms a clique:
880 // if two equivalence classes are neither equal nor disequal, add a split
881 int validRegionIndex
= -1;
882 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
883 if( d_regions
[i
]->valid() ){
884 if( validRegionIndex
!=-1 ){
885 combineRegions( validRegionIndex
, i
);
886 if( addSplit( d_regions
[validRegionIndex
], out
)!=0 ){
887 Trace("uf-ss-debug") << "Minimize model : combined regions, found split. " << std::endl
;
891 validRegionIndex
= i
;
895 Assert( validRegionIndex
!=-1 );
896 if( addSplit( d_regions
[validRegionIndex
], out
)!=0 ){
897 Trace("uf-ss-debug") << "Minimize model : found split. " << std::endl
;
900 Trace("uf-ss-debug") << "Minimize success. " << std::endl
;
907 int SortModel::getNumDisequalitiesToRegion( Node n
, int ri
){
908 int ni
= d_regions_map
[n
];
910 DiseqList
* del
= d_regions
[ni
]->getRegionInfo(n
)->get(0);
911 for( DiseqList::iterator it
= del
->begin(); it
!= del
->end(); ++it
){
913 if( d_regions_map
[ (*it
).first
]==ri
){
921 void SortModel::getDisequalitiesToRegions(int ri
,
922 std::map
< int, int >& regions_diseq
)
924 Region
* region
= d_regions
[ri
];
925 for(Region::iterator it
= region
->begin(); it
!= region
->end(); ++it
){
926 if( it
->second
->valid() ){
927 DiseqList
* del
= it
->second
->get(0);
928 for( DiseqList::iterator it2
= del
->begin(); it2
!= del
->end(); ++it2
){
930 Assert( isValid( d_regions_map
[ (*it2
).first
] ) );
931 //Notice() << "Found disequality with " << (*it2).first << ", region = " << d_regions_map[ (*it2).first ] << std::endl;
932 regions_diseq
[ d_regions_map
[ (*it2
).first
] ]++;
939 void SortModel::setSplitScore( Node n
, int s
){
940 if( d_split_score
.find( n
)!=d_split_score
.end() ){
941 int ss
= d_split_score
[ n
];
942 d_split_score
[ n
] = s
>ss
? s
: ss
;
944 d_split_score
[ n
] = s
;
946 for( int i
=0; i
<(int)n
.getNumChildren(); i
++ ){
947 setSplitScore( n
[i
], s
+1 );
951 void SortModel::assertCardinality( OutputChannel
* out
, int c
, bool val
){
953 Trace("uf-ss-assert")
954 << "Assert cardinality "<< d_type
<< " " << c
<< " " << val
<< " level = "
955 << d_thss
->getTheory()->d_valuation
.getAssertionLevel() << std::endl
;
957 Node cl
= getCardinalityLiteral( c
);
959 bool doCheckRegions
= !d_hasCard
;
960 bool prevHasCard
= d_hasCard
;
962 if( !prevHasCard
|| c
<d_cardinality
){
964 simpleCheckCardinality();
965 if( d_thss
->d_conflict
.get() ){
969 //should check all regions now
970 if( doCheckRegions
){
971 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
972 if( d_regions
[i
]->valid() ){
982 if( options::ufssModelInference() ){
983 //check if we are at decision level 0
984 if( d_th->d_valuation.getAssertionLevel()==0 ){
985 Trace("uf-ss-mi") << "We have proved that no models of size " << c << " for type " << d_type << " exist." << std::endl;
986 Trace("uf-ss-mi") << " # Clique lemmas : " << d_cliques[c].size() << std::endl;
987 if( d_cliques[c].size()==1 ){
988 if( d_totality_terms[c+1].empty() ){
989 Trace("uf-ss-mi") << "*** Establish model" << std::endl;
990 //d_totality_terms[c+1].insert( d_totality_terms[c].begin(), d_cliques[c][0].begin(), d_cliques[c][0].end() );
996 //see if we need to request a new cardinality
998 bool needsCard
= true;
999 for( std::map
< int, Node
>::iterator it
= d_cardinality_literal
.begin(); it
!=d_cardinality_literal
.end(); ++it
){
1000 if( it
->first
<=d_aloc_cardinality
.get() ){
1002 if( !d_thss
->getTheory()->d_valuation
.hasSatValue( it
->second
, value
) ){
1003 Debug("fmf-card-debug") << "..does not need allocate because we are waiting for " << it
->second
<< std::endl
;
1010 allocateCardinality( out
);
1013 Debug("fmf-card-debug") << "..already has card = " << d_cardinality
<< std::endl
;
1015 if( c
>d_maxNegCard
.get() ){
1016 Trace("uf-ss-com-card-debug") << "Maximum negative cardinality for " << d_type
<< " is now " << c
<< std::endl
;
1017 d_maxNegCard
.set( c
);
1018 simpleCheckCardinality();
1024 void SortModel::checkRegion( int ri
, bool checkCombine
){
1025 if( isValid(ri
) && d_hasCard
){
1026 Assert( d_cardinality
>0 );
1027 if( checkCombine
&& d_regions
[ri
]->getMustCombine( d_cardinality
) ){
1028 ////alternatively, check if we can reduce the number of external disequalities by moving single nodes
1029 //for( std::map< Node, bool >::iterator it = d_regions[i]->d_reps.begin(); it != d_regions[i]->d_reps.end(); ++it ){
1030 // if( it->second ){
1031 // int inDeg = d_regions[i]->d_disequalities_size[1][ it-> first ];
1032 // int outDeg = d_regions[i]->d_disequalities_size[0][ it-> first ];
1033 // if( inDeg<outDeg ){
1037 int riNew
= forceCombineRegion( ri
, true );
1039 checkRegion( riNew
, checkCombine
);
1042 //now check if region is in conflict
1043 std::vector
< Node
> clique
;
1044 if( d_regions
[ri
]->check( Theory::EFFORT_STANDARD
, d_cardinality
, clique
) ){
1046 addCliqueLemma( clique
, &d_thss
->getOutputChannel() );
1051 int SortModel::forceCombineRegion( int ri
, bool useDensity
){
1053 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
1054 if( ri
!=i
&& d_regions
[i
]->valid() ){
1055 return combineRegions( ri
, i
);
1060 //this region must merge with another
1061 if( Debug
.isOn("uf-ss-check-region") ){
1062 Debug("uf-ss-check-region") << "We must combine Region #" << ri
<< ". " << std::endl
;
1063 d_regions
[ri
]->debugPrint("uf-ss-check-region");
1065 //take region with maximum disequality density
1066 double maxScore
= 0;
1068 std::map
< int, int > regions_diseq
;
1069 getDisequalitiesToRegions( ri
, regions_diseq
);
1070 for( std::map
< int, int >::iterator it
= regions_diseq
.begin(); it
!= regions_diseq
.end(); ++it
){
1071 Debug("uf-ss-check-region") << it
->first
<< " : " << it
->second
<< std::endl
;
1073 for( std::map
< int, int >::iterator it
= regions_diseq
.begin(); it
!= regions_diseq
.end(); ++it
){
1074 Assert( it
->first
!=ri
);
1075 Assert( isValid( it
->first
) );
1076 Assert( d_regions
[ it
->first
]->getNumReps()>0 );
1077 double tempScore
= double(it
->second
)/double(d_regions
[it
->first
]->getNumReps() );
1078 if( tempScore
>maxScore
){
1079 maxRegion
= it
->first
;
1080 maxScore
= tempScore
;
1083 if( maxRegion
!=-1 ){
1084 if( Debug
.isOn("uf-ss-check-region") ){
1085 Debug("uf-ss-check-region") << "Combine with region #" << maxRegion
<< ":" << std::endl
;
1086 d_regions
[maxRegion
]->debugPrint("uf-ss-check-region");
1088 return combineRegions( ri
, maxRegion
);
1095 int SortModel::combineRegions( int ai
, int bi
){
1096 #ifdef COMBINE_REGIONS_SMALL_INTO_LARGE
1097 if( d_regions
[ai
]->getNumReps()<d_regions
[bi
]->getNumReps() ){
1098 return combineRegions( bi
, ai
);
1101 Debug("uf-ss-region") << "uf-ss: Combine Region #" << bi
<< " with Region #" << ai
<< std::endl
;
1102 Assert( isValid( ai
) && isValid( bi
) );
1103 Region
* region_bi
= d_regions
[bi
];
1104 for(Region::iterator it
= region_bi
->begin(); it
!= region_bi
->end(); ++it
){
1105 Region::RegionNodeInfo
* rni
= it
->second
;
1107 d_regions_map
[ it
->first
] = ai
;
1110 //update regions disequal DO_THIS?
1111 d_regions
[ai
]->combine( d_regions
[bi
] );
1112 d_regions
[bi
]->setValid( false );
1116 void SortModel::moveNode( Node n
, int ri
){
1117 Debug("uf-ss-region") << "uf-ss: Move node " << n
<< " to Region #" << ri
<< std::endl
;
1118 Assert( isValid( d_regions_map
[ n
] ) );
1119 Assert( isValid( ri
) );
1120 //move node to region ri
1121 d_regions
[ri
]->takeNode( d_regions
[ d_regions_map
[n
] ], n
);
1122 d_regions_map
[n
] = ri
;
1125 void SortModel::allocateCardinality( OutputChannel
* out
){
1126 if( d_aloc_cardinality
>0 ){
1127 Trace("uf-ss-fmf") << "No model of size " << d_aloc_cardinality
<< " exists for type " << d_type
<< " in this branch" << std::endl
;
1129 Trace("uf-ss-debug") << "Allocate cardinality " << d_aloc_cardinality
<< " for type " << d_type
<< std::endl
;
1130 if( Trace
.isOn("uf-ss-cliques") ){
1131 Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality
+1) << " for " << d_type
<< " : " << std::endl
;
1132 for( size_t i
=0; i
<d_cliques
[ d_aloc_cardinality
].size(); i
++ ){
1133 Trace("uf-ss-cliques") << " ";
1134 for( size_t j
=0; j
<d_cliques
[ d_aloc_cardinality
][i
].size(); j
++ ){
1135 Trace("uf-ss-cliques") << d_cliques
[ d_aloc_cardinality
][i
][j
] << " ";
1137 Trace("uf-ss-cliques") << std::endl
;
1141 //allocate the lowest such that it is not asserted
1146 d_aloc_cardinality
= d_aloc_cardinality
+ 1;
1147 cl
= getCardinalityLiteral( d_aloc_cardinality
);
1149 if( d_thss
->getTheory()->d_valuation
.hasSatValue( cl
, value
) ){
1151 //if one is already asserted postively, abort
1157 }while( increment
);
1159 //check for abort case
1160 if (options::ufssAbortCardinality() != -1 &&
1161 d_aloc_cardinality
>= options::ufssAbortCardinality()) {
1162 Message() << "Maximum cardinality (" << options::ufssAbortCardinality()
1163 << ") for finite model finding exceeded." << std::endl
;
1166 if( applyTotality( d_aloc_cardinality
) ){
1167 //must generate new cardinality lemma term
1169 if( d_aloc_cardinality
==1 && !options::ufssTotalitySymBreak() ){
1170 //get arbitrary ground term
1171 var
= d_cardinality_term
;
1173 std::stringstream ss
;
1174 ss
<< "_c_" << d_aloc_cardinality
;
1175 var
= NodeManager::currentNM()->mkSkolem( ss
.str(), d_type
, "is a cardinality lemma term" );
1177 if( d_aloc_cardinality
-1<(int)d_totality_terms
[0].size() ){
1178 d_totality_terms
[0][d_aloc_cardinality
-1] = var
;
1180 d_totality_terms
[0].push_back( var
);
1182 Trace("mkVar") << "allocateCardinality, mkVar : " << var
<< " : " << d_type
<< std::endl
;
1183 //must be distinct from all other cardinality terms
1184 for( int i
=0; i
<(int)(d_totality_terms
[0].size()-1); i
++ ){
1185 Node lem
= NodeManager::currentNM()->mkNode( NOT
, var
.eqNode( d_totality_terms
[0][i
] ) );
1186 Trace("uf-ss-lemma") << "Totality distinctness lemma : " << lem
<< std::endl
;
1187 d_thss
->getOutputChannel().lemma( lem
);
1191 //add splitting lemma for cardinality constraint
1192 Assert( !d_cardinality_term
.isNull() );
1193 Node lem
= getCardinalityLiteral( d_aloc_cardinality
);
1194 lem
= NodeManager::currentNM()->mkNode( OR
, lem
, lem
.notNode() );
1195 d_cardinality_lemma
[ d_aloc_cardinality
] = lem
;
1196 //add as lemma to output channel
1197 if( doSendLemma( lem
) ){
1198 Trace("uf-ss-lemma") << "*** Cardinality split on : " << lem
<< std::endl
;
1201 out
->requirePhase( d_cardinality_literal
[ d_aloc_cardinality
], true );
1202 //add the appropriate lemma, propagate as decision
1203 //Trace("uf-ss-prop-as-dec") << "Propagate as decision " << lem[0] << " " << d_type << std::endl;
1204 //out->propagateAsDecision( lem[0] );
1205 d_thss
->d_statistics
.d_max_model_size
.maxAssign( d_aloc_cardinality
);
1207 if( applyTotality( d_aloc_cardinality
) ){
1208 //must send totality axioms for each existing term
1209 for( NodeIntMap::iterator it
= d_regions_map
.begin(); it
!= d_regions_map
.end(); ++it
){
1210 addTotalityAxiom( (*it
).first
, d_aloc_cardinality
, &d_thss
->getOutputChannel() );
1216 int SortModel::addSplit( Region
* r
, OutputChannel
* out
){
1218 if( r
->hasSplits() ){
1219 //take the first split you find
1220 for( Region::split_iterator it
= r
->begin_splits();
1221 it
!= r
->end_splits(); ++it
){
1227 Assert( s
!=Node::null() );
1230 //add lemma to output channel
1231 Assert( s
.getKind()==EQUAL
);
1232 Node ss
= Rewriter::rewrite( s
);
1233 if( ss
.getKind()!=EQUAL
){
1234 Node b_t
= NodeManager::currentNM()->mkConst( true );
1235 Node b_f
= NodeManager::currentNM()->mkConst( false );
1237 Trace("uf-ss-lemma") << "....Assert disequal directly : "
1238 << s
[0] << " " << s
[1] << std::endl
;
1239 assertDisequal( s
[0], s
[1], b_t
);
1242 Trace("uf-ss-warn") << "Split on unknown literal : " << ss
<< std::endl
;
1245 Message() << "Bad split " << s
<< std::endl
;
1249 if( options::sortInference()) {
1250 for( int i
=0; i
<2; i
++ ){
1251 int si
= d_thss
->getSortInference()->getSortId( ss
[i
] );
1252 Trace("uf-ss-split-si") << si
<< " ";
1254 Trace("uf-ss-split-si") << std::endl
;
1256 //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areEqual( s[0], s[1] ) << " ";
1257 //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areDisequal( s[0], s[1] ) << std::endl;
1258 //Trace("uf-ss-lemma") << s[0].getType() << " " << s[1].getType() << std::endl;
1259 //Notice() << "*** Split on " << s << std::endl;
1260 //split on the equality s
1261 Node lem
= NodeManager::currentNM()->mkNode( kind::OR
, ss
, ss
.negate() );
1262 if( doSendLemma( lem
) ){
1263 Trace("uf-ss-lemma") << "*** Split on " << s
<< std::endl
;
1264 //tell the sat solver to explore the equals branch first
1265 out
->requirePhase( ss
, true );
1266 ++( d_thss
->d_statistics
.d_split_lemmas
);
1275 void SortModel::addCliqueLemma( std::vector
< Node
>& clique
, OutputChannel
* out
){
1276 Assert( d_hasCard
);
1277 Assert( d_cardinality
>0 );
1278 while( clique
.size()>size_t(d_cardinality
+1) ){
1281 //debugging information
1282 if( Trace
.isOn("uf-ss-cliques") ){
1283 std::vector
< Node
> clique_vec
;
1284 clique_vec
.insert( clique_vec
.begin(), clique
.begin(), clique
.end() );
1285 addClique( d_cardinality
, clique_vec
);
1287 if( options::ufssSimpleCliques() && !options::ufssExplainedCliques() ){
1289 std::vector
< Node
> eqs
;
1290 for( int i
=0; i
<(int)clique
.size(); i
++ ){
1291 for( int j
=0; j
<i
; j
++ ){
1292 Node r1
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative(clique
[i
]);
1293 Node r2
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative(clique
[j
]);
1294 eqs
.push_back( clique
[i
].eqNode( clique
[j
] ) );
1297 eqs
.push_back( d_cardinality_literal
[ d_cardinality
].notNode() );
1298 Node lem
= NodeManager::currentNM()->mkNode( OR
, eqs
);
1299 if( doSendLemma( lem
) ){
1300 Trace("uf-ss-lemma") << "*** Add clique lemma " << lem
<< std::endl
;
1301 ++( d_thss
->d_statistics
.d_clique_lemmas
);
1305 Debug("uf-ss-cliques") << "Found a clique (cardinality=" << d_cardinality
<< ") :" << std::endl
;
1306 Debug("uf-ss-cliques") << " ";
1307 for( int i
=0; i
<(int)clique
.size(); i
++ ){
1308 Debug("uf-ss-cliques") << clique
[i
] << " ";
1310 Debug("uf-ss-cliques") << std::endl
;
1311 Debug("uf-ss-cliques") << "Finding clique disequalities..." << std::endl
;
1313 //we will scan through each of the disequaltities
1314 bool isSatConflict
= true;
1315 std::vector
< Node
> conflict
;
1316 //collect disequalities, and nodes that must be equal within representatives
1317 std::map
< Node
, std::map
< Node
, bool > > explained
;
1318 std::map
< Node
, std::map
< Node
, bool > > nodesWithinRep
;
1319 //map from the reprorted clique members to those reported in the lemma
1320 std::map
< Node
, Node
> cliqueRepMap
;
1321 for( int i
=0; i
<(int)d_disequalities_index
; i
++ ){
1322 //if both sides of disequality exist in clique
1323 Node r1
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative( d_disequalities
[i
][0][0] );
1324 Node r2
= d_thss
->getTheory()->d_equalityEngine
.getRepresentative( d_disequalities
[i
][0][1] );
1325 if( r1
!=r2
&& ( explained
.find( r1
)==explained
.end() || explained
[r1
].find( r2
)==explained
[r1
].end() ) &&
1326 std::find( clique
.begin(), clique
.end(), r1
)!=clique
.end() &&
1327 std::find( clique
.begin(), clique
.end(), r2
)!=clique
.end() ){
1328 explained
[r1
][r2
] = true;
1329 explained
[r2
][r1
] = true;
1330 if( options::ufssExplainedCliques() ){
1331 conflict
.push_back( d_disequalities
[i
] );
1332 Debug("uf-ss-cliques") << " -> disequality : " << d_disequalities
[i
] << std::endl
;
1333 nodesWithinRep
[r1
][ d_disequalities
[i
][0][0] ] = true;
1334 nodesWithinRep
[r2
][ d_disequalities
[i
][0][1] ] = true;
1336 //get the terms we report in the lemma
1338 if( cliqueRepMap
.find( r1
)==cliqueRepMap
.end() ){
1339 ru1
= d_disequalities
[i
][0][0];
1340 cliqueRepMap
[r1
] = ru1
;
1342 ru1
= cliqueRepMap
[r1
];
1345 if( cliqueRepMap
.find( r2
)==cliqueRepMap
.end() ){
1346 ru2
= d_disequalities
[i
][0][1];
1347 cliqueRepMap
[r2
] = ru2
;
1349 ru2
= cliqueRepMap
[r2
];
1351 if( ru1
!=d_disequalities
[i
][0][0] || ru2
!=d_disequalities
[i
][0][1] ){
1352 //disequalities have endpoints that are not connected within an equivalence class
1353 // we will be producing a lemma, introducing a new literal ru1 != ru2
1354 conflict
.push_back( ru1
.eqNode( ru2
).notNode() );
1355 isSatConflict
= false;
1357 conflict
.push_back( d_disequalities
[i
] );
1360 if( conflict
.size()==(clique
.size()*( clique
.size()-1 )/2) ){
1365 if( options::ufssExplainedCliques() ){
1366 //Debug("uf-ss-cliques") << conflict.size() << " " << clique.size() << std::endl;
1367 Assert( (int)conflict
.size()==((int)clique
.size()*( (int)clique
.size()-1 )/2) );
1368 //Assert( (int)conflict.size()==(int)clique.size()*( (int)clique.size()-1 )/2 );
1369 Debug("uf-ss-cliques") << "Finding clique equalities internal to eq classes..." << std::endl
;
1370 //now, we must explain equalities within each equivalence class
1371 for( std::map
< Node
, std::map
< Node
, bool > >::iterator it
= nodesWithinRep
.begin(); it
!= nodesWithinRep
.end(); ++it
){
1372 if( it
->second
.size()>1 ){
1374 //add explanation of t1 = t2 = ... = tn
1375 Debug("uf-ss-cliques") << "Explain ";
1376 for( std::map
< Node
, bool >::iterator it2
= it
->second
.begin(); it2
!= it
->second
.end(); ++it2
){
1377 if( prev
!=Node::null() ){
1378 Debug("uf-ss-cliques") << " = ";
1379 //explain it2->first and prev
1380 std::vector
< TNode
> expl
;
1381 d_thss
->getTheory()->d_equalityEngine
.explainEquality( it2
->first
, prev
, true, expl
);
1382 for( int i
=0; i
<(int)expl
.size(); i
++ ){
1383 if( std::find( conflict
.begin(), conflict
.end(), expl
[i
] )==conflict
.end() ){
1384 conflict
.push_back( expl
[i
] );
1389 Debug("uf-ss-cliques") << prev
;
1391 Debug("uf-ss-cliques") << std::endl
;
1394 Debug("uf-ss-cliques") << "Explanation of clique (size=" << conflict
.size() << ") = " << std::endl
;
1395 for( int i
=0; i
<(int)conflict
.size(); i
++ ){
1396 Debug("uf-ss-cliques") << conflict
[i
] << " ";
1398 Debug("uf-ss-cliques") << std::endl
;
1400 //now, make the conflict
1401 if( isSatConflict
){
1402 conflict
.push_back( d_cardinality_literal
[ d_cardinality
] );
1403 Node conflictNode
= NodeManager::currentNM()->mkNode( AND
, conflict
);
1404 Trace("uf-ss-lemma") << "*** Add clique conflict " << conflictNode
<< std::endl
;
1405 //Notice() << "*** Add clique conflict " << conflictNode << std::endl;
1406 out
->conflict( conflictNode
);
1408 ++( d_thss
->d_statistics
.d_clique_conflicts
);
1410 Node conflictNode
= conflict
.size()==1 ? conflict
[0] : NodeManager::currentNM()->mkNode( AND
, conflict
);
1411 //add cardinality constraint
1412 Node cardNode
= d_cardinality_literal
[ d_cardinality
];
1414 //bool hasValue = d_th->getValuation().hasSatValue( cardNode, value );
1415 //Assert( hasValue );
1417 conflictNode
= NodeManager::currentNM()->mkNode( IMPLIES
, conflictNode
, cardNode
.notNode() );
1418 if( doSendLemma( conflictNode
) ){
1419 Trace("uf-ss-lemma") << "*** Add clique lemma " << conflictNode
<< std::endl
;
1420 ++( d_thss
->d_statistics
.d_clique_lemmas
);
1424 //DO_THIS: ensure that the same clique is not reported??? Check standard effort after assertDisequal can produce same clique.
1428 void SortModel::addTotalityAxiom( Node n
, int cardinality
, OutputChannel
* out
){
1429 if( std::find( d_totality_terms
[0].begin(), d_totality_terms
[0].end(), n
)==d_totality_terms
[0].end() ){
1430 if( std::find( d_totality_lems
[n
].begin(), d_totality_lems
[n
].end(), cardinality
) == d_totality_lems
[n
].end() ){
1431 d_totality_lems
[n
].push_back( cardinality
);
1432 Node cardLit
= d_cardinality_literal
[ cardinality
];
1434 if( options::sortInference() ){
1435 sort_id
= d_thss
->getSortInference()->getSortId(n
);
1437 Trace("uf-ss-totality") << "Add totality lemma for " << n
<< " " << cardinality
<< ", sort id is " << sort_id
<< std::endl
;
1438 int use_cardinality
= cardinality
;
1439 if( options::ufssTotalitySymBreak() ){
1440 if( d_sym_break_index
.find(n
)!=d_sym_break_index
.end() ){
1441 use_cardinality
= d_sym_break_index
[n
];
1442 }else if( (int)d_sym_break_terms
[n
.getType()][sort_id
].size()<cardinality
-1 ){
1443 use_cardinality
= d_sym_break_terms
[n
.getType()][sort_id
].size() + 1;
1444 d_sym_break_terms
[n
.getType()][sort_id
].push_back( n
);
1445 d_sym_break_index
[n
] = use_cardinality
;
1446 Trace("uf-ss-totality") << "Allocate symmetry breaking term " << n
<< ", index = " << use_cardinality
<< std::endl
;
1447 if( d_sym_break_terms
[n
.getType()][sort_id
].size()>1 ){
1448 //enforce canonicity
1449 for( int i
=2; i
<use_cardinality
; i
++ ){
1450 //can only be assigned to domain constant d if someone has been assigned domain constant d-1
1451 Node eq
= n
.eqNode( getTotalityLemmaTerm( cardinality
, i
) );
1452 std::vector
< Node
> eqs
;
1453 for( unsigned j
=0; j
<(d_sym_break_terms
[n
.getType()][sort_id
].size()-1); j
++ ){
1454 eqs
.push_back( d_sym_break_terms
[n
.getType()][sort_id
][j
].eqNode( getTotalityLemmaTerm( cardinality
, i
-1 ) ) );
1456 Node ax
= eqs
.size()==1 ? eqs
[0] : NodeManager::currentNM()->mkNode( OR
, eqs
);
1457 Node lem
= NodeManager::currentNM()->mkNode( IMPLIES
, eq
, ax
);
1458 Trace("uf-ss-lemma") << "*** Add (canonicity) totality axiom " << lem
<< std::endl
;
1459 d_thss
->getOutputChannel().lemma( lem
);
1465 std::vector
< Node
> eqs
;
1466 for( int i
=0; i
<use_cardinality
; i
++ ){
1467 eqs
.push_back( n
.eqNode( getTotalityLemmaTerm( cardinality
, i
) ) );
1469 Node ax
= NodeManager::currentNM()->mkNode( OR
, eqs
);
1470 Node lem
= NodeManager::currentNM()->mkNode( IMPLIES
, cardLit
, ax
);
1471 Trace("uf-ss-lemma") << "*** Add totality axiom " << lem
<< std::endl
;
1472 //send as lemma to the output channel
1473 d_thss
->getOutputChannel().lemma( lem
);
1474 ++( d_thss
->d_statistics
.d_totality_lemmas
);
1479 void SortModel::addClique( int c
, std::vector
< Node
>& clique
) {
1480 //if( d_clique_trie[c].add( clique ) ){
1481 // d_cliques[ c ].push_back( clique );
1486 /** apply totality */
1487 bool SortModel::applyTotality( int cardinality
){
1488 return options::ufssTotality() || cardinality
<=options::ufssTotalityLimited();
1489 // || ( options::ufssModelInference() && !d_totality_terms[cardinality].empty() );
1492 /** get totality lemma terms */
1493 Node
SortModel::getTotalityLemmaTerm( int cardinality
, int i
){
1494 return d_totality_terms
[0][i
];
1496 // return d_totality_terms[cardinality][i];
1500 void SortModel::simpleCheckCardinality() {
1501 if( d_maxNegCard
.get()!=0 && d_hasCard
.get() && d_cardinality
.get()<d_maxNegCard
.get() ){
1502 Node lem
= NodeManager::currentNM()->mkNode( AND
, getCardinalityLiteral( d_cardinality
.get() ),
1503 getCardinalityLiteral( d_maxNegCard
.get() ).negate() );
1504 Trace("uf-ss-lemma") << "*** Simple cardinality conflict : " << lem
<< std::endl
;
1505 d_thss
->getOutputChannel().conflict( lem
);
1506 d_thss
->d_conflict
.set( true );
1510 bool SortModel::doSendLemma( Node lem
) {
1511 if( d_lemma_cache
.find( lem
)==d_lemma_cache
.end() ){
1512 d_lemma_cache
[lem
] = true;
1513 d_thss
->getOutputChannel().lemma( lem
);
1520 void SortModel::debugPrint( const char* c
){
1521 if( Debug
.isOn( c
) ){
1522 Debug( c
) << "Number of reps = " << d_reps
<< std::endl
;
1523 Debug( c
) << "Cardinality req = " << d_cardinality
<< std::endl
;
1524 unsigned debugReps
= 0;
1525 for( unsigned i
=0; i
<d_regions_index
; i
++ ){
1526 Region
* region
= d_regions
[i
];
1527 if( region
->valid() ){
1528 Debug( c
) << "Region #" << i
<< ": " << std::endl
;
1529 region
->debugPrint( c
, true );
1530 Debug( c
) << std::endl
;
1531 for( Region::iterator it
= region
->begin(); it
!= region
->end(); ++it
){
1532 if( it
->second
->valid() ){
1533 if( d_regions_map
[ it
->first
]!=(int)i
){
1534 Debug( c
) << "***Bad regions map : " << it
->first
1535 << " " << d_regions_map
[ it
->first
].get() << std::endl
;
1539 debugReps
+= region
->getNumReps();
1543 if( debugReps
!=d_reps
){
1544 Debug( c
) << "***Bad reps: " << d_reps
<< ", "
1545 << "actual = " << debugReps
<< std::endl
;
1550 bool SortModel::debugModel( TheoryModel
* m
){
1551 if( Trace
.isOn("uf-ss-warn") ){
1552 std::vector
< Node
> eqcs
;
1553 eq::EqClassesIterator eqcs_i
=
1554 eq::EqClassesIterator(m
->getEqualityEngine());
1555 while( !eqcs_i
.isFinished() ){
1556 Node eqc
= (*eqcs_i
);
1557 if( eqc
.getType()==d_type
){
1558 if( std::find( eqcs
.begin(), eqcs
.end(), eqc
)==eqcs
.end() ){
1559 eqcs
.push_back( eqc
);
1560 //we must ensure that this equivalence class has been accounted for
1561 if( d_regions_map
.find( eqc
)==d_regions_map
.end() ){
1562 Trace("uf-ss-warn") << "WARNING : equivalence class " << eqc
<< " unaccounted for." << std::endl
;
1563 Trace("uf-ss-warn") << " type : " << d_type
<< std::endl
;
1564 Trace("uf-ss-warn") << " kind : " << eqc
.getKind() << std::endl
;
1571 RepSet
* rs
= m
->getRepSetPtr();
1572 int nReps
= (int)rs
->getNumRepresentatives(d_type
);
1573 if( nReps
!=(d_maxNegCard
+1) ){
1574 Trace("uf-ss-warn") << "WARNING : Model does not have same # representatives as cardinality for " << d_type
<< "." << std::endl
;
1575 Trace("uf-ss-warn") << " Max neg cardinality : " << d_maxNegCard
<< std::endl
;
1576 Trace("uf-ss-warn") << " # Reps : " << nReps
<< std::endl
;
1577 if( d_maxNegCard
>=nReps
){
1579 for( unsigned i=0; i<d_fresh_aloc_reps.size(); i++ ){
1580 if( add>0 && !m->d_equalityEngine->hasTerm( d_fresh_aloc_reps[i] ) ){
1581 rs->d_type_reps[d_type].push_back( d_fresh_aloc_reps[i] );
1585 for( int i=0; i<add; i++ ){
1586 std::stringstream ss;
1587 ss << "r_" << d_type << "_";
1588 Node nn = NodeManager::currentNM()->mkSkolem( ss.str(), d_type,
1589 "enumeration to meet negative card constraint" );
1590 d_fresh_aloc_reps.push_back( nn );
1591 rs->d_type_reps[d_type].push_back( nn );
1594 while( (int)d_fresh_aloc_reps
.size()<=d_maxNegCard
){
1595 std::stringstream ss
;
1596 ss
<< "r_" << d_type
<< "_";
1597 Node nn
= NodeManager::currentNM()->mkSkolem( ss
.str(), d_type
, "enumeration to meet negative card constraint" );
1598 d_fresh_aloc_reps
.push_back( nn
);
1600 if( d_maxNegCard
==0 ){
1601 rs
->d_type_reps
[d_type
].push_back(d_fresh_aloc_reps
[0]);
1604 std::vector
< Node
> force_cl
;
1605 for( int i
=0; i
<=d_maxNegCard
; i
++ ){
1606 for( int j
=(i
+1); j
<=d_maxNegCard
; j
++ ){
1607 force_cl
.push_back( d_fresh_aloc_reps
[i
].eqNode( d_fresh_aloc_reps
[j
] ).negate() );
1610 Node cl
= getCardinalityLiteral( d_maxNegCard
);
1611 Node lem
= NodeManager::currentNM()->mkNode( OR
, cl
, NodeManager::currentNM()->mkNode( AND
, force_cl
) );
1612 Trace("uf-ss-lemma") << "*** Enforce negative cardinality constraint lemma : " << lem
<< std::endl
;
1613 d_thss
->getOutputChannel().lemma( lem
);
1621 int SortModel::getNumRegions(){
1623 for( int i
=0; i
<(int)d_regions_index
; i
++ ){
1624 if( d_regions
[i
]->valid() ){
1631 Node
SortModel::getCardinalityLiteral( int c
) {
1632 if( d_cardinality_literal
.find(c
) == d_cardinality_literal
.end() ){
1633 Node c_as_rational
= NodeManager::currentNM()->mkConst(Rational(c
));
1634 d_cardinality_literal
[c
] =
1635 NodeManager::currentNM()->mkNode(CARDINALITY_CONSTRAINT
,
1640 return d_cardinality_literal
[c
];
1643 StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context
* c
,
1644 context::UserContext
* u
,
1645 OutputChannel
& out
, TheoryUF
* th
)
1648 d_conflict(c
, false),
1650 d_aloc_com_card(u
, 0),
1651 d_com_card_assertions(c
),
1652 d_min_pos_com_card(c
, -1),
1653 d_card_assertions_eqv_lemma(u
),
1654 d_min_pos_tn_master_card(c
, -1),
1658 if (options::ufssDiseqPropagation()) {
1659 d_deq_prop
= new DisequalityPropagator(th
->getQuantifiersEngine(), this);
1661 if (options::ufssSymBreak()) {
1662 d_sym_break
= new SubsortSymmetryBreaker(th
->getQuantifiersEngine(), c
);
1666 StrongSolverTheoryUF::~StrongSolverTheoryUF() {
1667 for (std::map
<TypeNode
, SortModel
*>::iterator it
= d_rep_model
.begin();
1668 it
!= d_rep_model
.end(); ++it
) {
1679 SortInference
* StrongSolverTheoryUF::getSortInference() {
1680 return d_th
->getQuantifiersEngine()->getTheoryEngine()->getSortInference();
1683 /** get default sat context */
1684 context::Context
* StrongSolverTheoryUF::getSatContext() {
1685 return d_th
->getSatContext();
1688 /** get default output channel */
1689 OutputChannel
& StrongSolverTheoryUF::getOutputChannel() {
1690 return d_th
->getOutputChannel();
1694 void StrongSolverTheoryUF::ensureEqc( SortModel
* c
, Node a
) {
1696 d_rel_eqc
[a
] = true;
1697 Trace("uf-ss-solver") << "StrongSolverTheoryUF: New eq class " << a
<< " : " << a
.getType() << std::endl
;
1699 if( options::ufssSymBreak() ){
1700 d_sym_break
->newEqClass( a
);
1702 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done New eq class." << std::endl
;
1706 void StrongSolverTheoryUF::ensureEqcRec( Node n
) {
1708 SortModel
* c
= getSortModel( n
);
1712 for( unsigned i
=0; i
<n
.getNumChildren(); i
++ ){
1713 ensureEqcRec( n
[i
] );
1719 bool StrongSolverTheoryUF::hasEqc( Node a
) {
1720 NodeBoolMap::iterator it
= d_rel_eqc
.find( a
);
1721 return it
!=d_rel_eqc
.end() && (*it
).second
;
1725 void StrongSolverTheoryUF::newEqClass( Node a
){
1726 SortModel
* c
= getSortModel( a
);
1731 Trace("uf-ss-solver") << "StrongSolverTheoryUF: New eq class " << a
<< " : " << a
.getType() << std::endl
;
1733 if( options::ufssSymBreak() ){
1734 d_sym_break
->newEqClass( a
);
1736 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done New eq class." << std::endl
;
1742 void StrongSolverTheoryUF::merge( Node a
, Node b
){
1743 //TODO: ensure they are relevant
1744 SortModel
* c
= getSortModel( a
);
1749 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Merge " << a
<< " " << b
<< " : " << a
.getType() << std::endl
;
1751 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done Merge." << std::endl
;
1753 //c->assignEqClass( b, a );
1754 d_rel_eqc
[b
] = true;
1757 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Merge " << a
<< " " << b
<< " : " << a
.getType() << std::endl
;
1759 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done Merge." << std::endl
;
1762 if( options::ufssDiseqPropagation() ){
1763 d_deq_prop
->merge(a
, b
);
1768 /** assert terms are disequal */
1769 void StrongSolverTheoryUF::assertDisequal( Node a
, Node b
, Node reason
){
1770 SortModel
* c
= getSortModel( a
);
1776 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Assert disequal " << a
<< " " << b
<< " : " << a
.getType() << std::endl
;
1777 //Assert( d_th->d_equalityEngine.getRepresentative( a )==a );
1778 //Assert( d_th->d_equalityEngine.getRepresentative( b )==b );
1779 c
->assertDisequal( a
, b
, reason
);
1780 Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done Assert disequal." << std::endl
;
1782 if( options::ufssDiseqPropagation() ){
1783 d_deq_prop
->assertDisequal(a
, b
, reason
);
1788 /** assert a node */
1789 void StrongSolverTheoryUF::assertNode( Node n
, bool isDecision
){
1790 Trace("uf-ss") << "Assert " << n
<< " " << isDecision
<< std::endl
;
1794 bool polarity
= n
.getKind() != kind::NOT
;
1795 TNode lit
= polarity
? n
: n
[0];
1796 if( options::ufssMode()==UF_SS_FULL
){
1797 if( lit
.getKind()==CARDINALITY_CONSTRAINT
){
1798 TypeNode tn
= lit
[0].getType();
1799 Assert( tn
.isSort() );
1800 Assert( d_rep_model
[tn
] );
1801 int nCard
= lit
[1].getConst
<Rational
>().getNumerator().getSignedInt();
1802 Node ct
= d_rep_model
[tn
]->getCardinalityTerm();
1803 Trace("uf-ss-debug") << "...check cardinality terms : " << lit
[0] << " " << ct
<< std::endl
;
1805 if( options::ufssFairnessMonotone() ){
1806 Trace("uf-ss-com-card-debug") << "...set master/slave" << std::endl
;
1807 if( tn
!=d_tn_mono_master
){
1808 std::map
< TypeNode
, bool >::iterator it
= d_tn_mono_slave
.find( tn
);
1809 if( it
==d_tn_mono_slave
.end() ){
1811 if( d_th
->getQuantifiersEngine() ){
1812 isMonotonic
= getSortInference()->isMonotonic( tn
);
1814 //if ground, everything is monotonic
1818 if( d_tn_mono_master
.isNull() ){
1819 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set master : " << tn
<< std::endl
;
1820 d_tn_mono_master
= tn
;
1822 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set slave : " << tn
<< std::endl
;
1823 d_tn_mono_slave
[tn
] = true;
1826 Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set non-monotonic : " << tn
<< std::endl
;
1827 d_tn_mono_slave
[tn
] = false;
1831 //set the minimum positive cardinality for master if necessary
1832 if( polarity
&& tn
==d_tn_mono_master
){
1833 Trace("uf-ss-com-card-debug") << "...set min positive cardinality" << std::endl
;
1834 if( d_min_pos_tn_master_card
.get()==-1 || nCard
<d_min_pos_tn_master_card
.get() ){
1835 d_min_pos_tn_master_card
.set( nCard
);
1839 Trace("uf-ss-com-card-debug") << "...assert cardinality" << std::endl
;
1840 d_rep_model
[tn
]->assertCardinality( d_out
, nCard
, polarity
);
1841 //check if combined cardinality is violated
1842 checkCombinedCardinality();
1844 //otherwise, make equal via lemma
1845 if( d_card_assertions_eqv_lemma
.find( lit
)==d_card_assertions_eqv_lemma
.end() ){
1846 Node eqv_lit
= NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT
, ct
, lit
[1] );
1847 eqv_lit
= lit
.eqNode( eqv_lit
);
1848 Trace("uf-ss-lemma") << "*** Cardinality equiv lemma : " << eqv_lit
<< std::endl
;
1849 getOutputChannel().lemma( eqv_lit
);
1850 d_card_assertions_eqv_lemma
[lit
] = true;
1853 }else if( lit
.getKind()==COMBINED_CARDINALITY_CONSTRAINT
){
1854 d_com_card_assertions
[lit
] = polarity
;
1856 //safe to assume int here
1857 int nCard
= lit
[0].getConst
<Rational
>().getNumerator().getSignedInt();
1858 if( d_min_pos_com_card
.get()==-1 || nCard
<d_min_pos_com_card
.get() ){
1859 d_min_pos_com_card
.set( nCard
);
1860 checkCombinedCardinality();
1863 bool needsCard
= true;
1864 if( d_min_pos_com_card
.get()==-1 ){
1865 //check if all current combined cardinality constraints are asserted negatively
1866 for( std::map
< int, Node
>::iterator it
= d_com_card_literal
.begin(); it
!= d_com_card_literal
.end(); ++it
){
1867 if( d_com_card_assertions
.find( it
->second
)==d_com_card_assertions
.end() ){
1868 Trace("uf-ss-com-card-debug") << "Does not need combined cardinality : non-assertion : " << it
->first
<< std::endl
;
1872 Assert( !d_com_card_assertions
[it
->second
] );
1876 Trace("uf-ss-com-card-debug") << "Does not need combined cardinality : positive assertion : " << d_min_pos_com_card
.get() << std::endl
;
1880 allocateCombinedCardinality();
1884 if( Trace
.isOn("uf-ss-warn") ){
1885 ////FIXME: this is too strict: theory propagations are showing up as isDecision=true, but
1886 //// a theory propagation is not a decision.
1888 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
1889 if( !it
->second
->hasCardinalityAsserted() ){
1890 Trace("uf-ss-warn") << "WARNING: Assert " << n
<< " as a decision before cardinality for " << it
->first
<< "." << std::endl
;
1891 //Message() << "Error: constraint asserted before cardinality for " << it->first << std::endl;
1897 if( lit
.getKind()!=EQUAL
){
1899 if( options::ufssDiseqPropagation() ){
1900 d_deq_prop
->assertPredicate(lit
, polarity
);
1905 if( lit
.getKind()==CARDINALITY_CONSTRAINT
|| lit
.getKind()==COMBINED_CARDINALITY_CONSTRAINT
){
1906 // cardinality constraint from user input, set incomplete
1907 Trace("uf-ss") << "Literal " << lit
<< " not handled when uf ss mode is not FULL, set incomplete." << std::endl
;
1908 d_out
->setIncomplete();
1911 Trace("uf-ss") << "Assert: done " << n
<< " " << isDecision
<< std::endl
;
1914 bool StrongSolverTheoryUF::areDisequal( Node a
, Node b
) {
1918 a
= d_th
->d_equalityEngine
.getRepresentative( a
);
1919 b
= d_th
->d_equalityEngine
.getRepresentative( b
);
1920 if( d_th
->d_equalityEngine
.areDisequal( a
, b
, false ) ){
1923 SortModel
* c
= getSortModel( a
);
1925 return c
->areDisequal( a
, b
);
1934 void StrongSolverTheoryUF::check( Theory::Effort level
){
1936 if( options::ufssMode()==UF_SS_FULL
){
1937 Trace("uf-ss-solver") << "StrongSolverTheoryUF: check " << level
<< std::endl
;
1938 if( level
==Theory::EFFORT_FULL
&& Debug
.isOn( "uf-ss-debug" ) ){
1939 debugPrint( "uf-ss-debug" );
1941 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
1942 it
->second
->check( level
, d_out
);
1943 if( it
->second
->isConflict() ){
1948 //check symmetry breaker
1949 if( !d_conflict
&& options::ufssSymBreak() ){
1950 d_sym_break
->check( level
);
1952 }else if( options::ufssMode()==UF_SS_NO_MINIMAL
){
1953 if( level
==Theory::EFFORT_FULL
){
1954 // split on an equality between two equivalence classes (at most one per type)
1955 std::map
< TypeNode
, std::vector
< Node
> > eqc_list
;
1956 std::map
< TypeNode
, bool > type_proc
;
1957 eq::EqClassesIterator
eqcs_i(d_th
->getEqualityEngine());
1958 while( !eqcs_i
.isFinished() ){
1960 TypeNode tn
= a
.getType();
1962 if( type_proc
.find( tn
)==type_proc
.end() ){
1963 std::map
< TypeNode
, std::vector
< Node
> >::iterator itel
= eqc_list
.find( tn
);
1964 if( itel
!=eqc_list
.end() ){
1965 for( unsigned j
=0; j
<itel
->second
.size(); j
++ ){
1966 Node b
= itel
->second
[j
];
1967 if( !d_th
->getEqualityEngine()->areDisequal( a
, b
, false ) ){
1968 Node eq
= Rewriter::rewrite( a
.eqNode( b
) );
1969 Node lem
= NodeManager::currentNM()->mkNode( kind::OR
, eq
, eq
.negate() );
1970 Trace("uf-ss-lemma") << "*** Split (no-minimal) : " << lem
<< std::endl
;
1971 d_out
->lemma( lem
);
1972 d_out
->requirePhase( eq
, true );
1973 type_proc
[tn
] = true;
1978 eqc_list
[tn
].push_back( a
);
1985 // unhandled uf ss mode
1988 Trace("uf-ss-solver") << "Done StrongSolverTheoryUF: check " << level
<< std::endl
;
1992 void StrongSolverTheoryUF::presolve() {
1993 d_aloc_com_card
.set( 0 );
1994 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
1995 it
->second
->presolve();
1996 it
->second
->initialize( d_out
);
2000 /** get next decision request */
2001 Node
StrongSolverTheoryUF::getNextDecisionRequest( unsigned& priority
){
2002 //request the combined cardinality as a decision literal, if not already asserted
2003 if( options::ufssMode()==UF_SS_FULL
){
2004 if( options::ufssFairness() ){
2008 if( comCard
<d_aloc_com_card
.get() ){
2009 com_lit
= d_com_card_literal
.find( comCard
)!=d_com_card_literal
.end() ? d_com_card_literal
[comCard
] : Node::null();
2010 if( !com_lit
.isNull() && d_com_card_assertions
.find( com_lit
)==d_com_card_assertions
.end() ){
2011 Trace("uf-ss-dec") << "Decide on combined cardinality : " << com_lit
<< std::endl
;
2017 com_lit
= Node::null();
2019 }while( !com_lit
.isNull() );
2021 //otherwise, check each individual sort
2022 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2023 Node n
= it
->second
->getNextDecisionRequest();
2030 Trace("uf-ss-dec") << "...no UF SS decisions." << std::endl
;
2031 return Node::null();
2034 void StrongSolverTheoryUF::preRegisterTerm( TNode n
){
2035 if( options::ufssMode()==UF_SS_FULL
){
2036 //initialize combined cardinality
2037 initializeCombinedCardinality();
2039 Trace("uf-ss-register") << "Preregister " << n
<< "." << std::endl
;
2040 //shouldn't have to preregister this type (it may be that there are no quantifiers over tn)
2041 TypeNode tn
= n
.getType();
2042 std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.find( tn
);
2043 if( it
==d_rep_model
.end() ){
2044 SortModel
* rm
= NULL
;
2046 Trace("uf-ss-register") << "Create sort model " << tn
<< "." << std::endl
;
2047 rm
= new SortModel( n
, d_th
->getSatContext(), d_th
->getUserContext(), this );
2048 //getOutputChannel().lemma( n.eqNode( rm->getCardinalityTerm() ) );
2051 if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){
2052 Debug("uf-ss-na") << "Error: Cannot perform finite model finding on arithmetic quantifier";
2053 Debug("uf-ss-na") << " (" << f << ")";
2054 Debug("uf-ss-na") << std::endl;
2055 Unimplemented("Cannot perform finite model finding on arithmetic quantifier");
2056 }else if( tn.isDatatype() ){
2057 Debug("uf-ss-na") << "Error: Cannot perform finite model finding on datatype quantifier";
2058 Debug("uf-ss-na") << " (" << f << ")";
2059 Debug("uf-ss-na") << std::endl;
2060 Unimplemented("Cannot perform finite model finding on datatype quantifier");
2065 rm
->initialize( d_out
);
2066 d_rep_model
[tn
] = rm
;
2067 //d_rep_model_init[tn] = true;
2070 //ensure sort model is initialized
2071 it
->second
->initialize( d_out
);
2076 //void StrongSolverTheoryUF::registerQuantifier( Node f ){
2077 // Debug("uf-ss-register") << "Register quantifier " << f << std::endl;
2078 //must ensure the quantifier does not quantify over arithmetic
2079 //for( int i=0; i<(int)f[0].getNumChildren(); i++ ){
2080 // TypeNode tn = f[0][i].getType();
2081 // preRegisterType( tn, true );
2086 SortModel
* StrongSolverTheoryUF::getSortModel( Node n
){
2087 TypeNode tn
= n
.getType();
2088 std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.find( tn
);
2089 //pre-register the type if not done already
2090 if( it
==d_rep_model
.end() ){
2091 preRegisterTerm( n
);
2092 it
= d_rep_model
.find( tn
);
2094 if( it
!=d_rep_model
.end() ){
2101 void StrongSolverTheoryUF::notifyRestart(){}
2103 /** get cardinality for sort */
2104 int StrongSolverTheoryUF::getCardinality( Node n
) {
2105 SortModel
* c
= getSortModel( n
);
2107 return c
->getCardinality();
2113 int StrongSolverTheoryUF::getCardinality( TypeNode tn
) {
2114 std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.find( tn
);
2115 if( it
!=d_rep_model
.end() && it
->second
){
2116 return it
->second
->getCardinality();
2121 bool StrongSolverTheoryUF::minimize( TheoryModel
* m
){
2122 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2123 if( !it
->second
->minimize( d_out
, m
) ){
2127 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2128 Trace("uf-ss-minimize") << "Cardinality( " << it
->first
<< " ) : " << it
->second
->getCardinality() << std::endl
;
2134 void StrongSolverTheoryUF::debugPrint( const char* c
){
2135 //EqClassesIterator< TheoryUF::NotifyClass > eqc_iter( &((TheoryUF*)d_th)->d_equalityEngine );
2136 //while( !eqc_iter.isFinished() ){
2137 // Debug( c ) << "Eq class [[" << (*eqc_iter) << "]]" << std::endl;
2138 // EqClassIterator< TheoryUF::NotifyClass > eqc_iter2( *eqc_iter, &((TheoryUF*)d_th)->d_equalityEngine );
2139 // Debug( c ) << " ";
2140 // while( !eqc_iter2.isFinished() ){
2141 // Debug( c ) << "[" << (*eqc_iter2) << "] ";
2144 // Debug( c ) << std::endl;
2148 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2149 Debug( c
) << "Conflict find structure for " << it
->first
<< ": " << std::endl
;
2150 it
->second
->debugPrint( c
);
2151 Debug( c
) << std::endl
;
2155 bool StrongSolverTheoryUF::debugModel( TheoryModel
* m
){
2156 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2157 if( !it
->second
->debugModel( m
) ){
2165 void StrongSolverTheoryUF::initializeCombinedCardinality() {
2166 Assert( options::ufssMode()==UF_SS_FULL
);
2167 if( options::ufssFairness() ){
2168 if( d_aloc_com_card
.get()==0 ){
2169 Trace("uf-ss-com-card-debug") << "Initialize combined cardinality" << std::endl
;
2170 allocateCombinedCardinality();
2176 void StrongSolverTheoryUF::checkCombinedCardinality() {
2177 Assert( options::ufssMode()==UF_SS_FULL
);
2178 if( options::ufssFairness() ){
2179 Trace("uf-ss-com-card-debug") << "Check combined cardinality, get maximum negative cardinalities..." << std::endl
;
2180 int totalCombinedCard
= 0;
2181 int maxMonoSlave
= 0;
2182 TypeNode maxSlaveType
;
2183 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin(); it
!= d_rep_model
.end(); ++it
){
2184 int max_neg
= it
->second
->getMaximumNegativeCardinality();
2185 if( !options::ufssFairnessMonotone() ){
2186 totalCombinedCard
+= max_neg
;
2188 std::map
< TypeNode
, bool >::iterator its
= d_tn_mono_slave
.find( it
->first
);
2189 if( its
==d_tn_mono_slave
.end() || !its
->second
){
2190 totalCombinedCard
+= max_neg
;
2192 if( max_neg
>maxMonoSlave
){
2193 maxMonoSlave
= max_neg
;
2194 maxSlaveType
= it
->first
;
2199 Trace("uf-ss-com-card-debug") << "Check combined cardinality, total combined card : " << totalCombinedCard
<< std::endl
;
2200 if( options::ufssFairnessMonotone() ){
2201 Trace("uf-ss-com-card-debug") << "Max slave monotonic negated cardinality : " << maxMonoSlave
<< std::endl
;
2202 if( d_min_pos_tn_master_card
.get()!=-1 && maxMonoSlave
>d_min_pos_tn_master_card
.get() ){
2203 int mc
= d_min_pos_tn_master_card
.get();
2204 std::vector
< Node
> conf
;
2205 conf
.push_back( d_rep_model
[d_tn_mono_master
]->getCardinalityLiteral( mc
) );
2206 conf
.push_back( d_rep_model
[maxSlaveType
]->getCardinalityLiteral( maxMonoSlave
).negate() );
2207 Node cf
= NodeManager::currentNM()->mkNode( AND
, conf
);
2208 Trace("uf-ss-lemma") << "*** Combined monotone cardinality conflict"
2209 << " : " << cf
<< std::endl
;
2210 Trace("uf-ss-com-card") << "*** Combined monotone cardinality conflict"
2211 << " : " << cf
<< std::endl
;
2212 getOutputChannel().conflict( cf
);
2213 d_conflict
.set( true );
2217 int cc
= d_min_pos_com_card
.get();
2218 if( cc
!=-1 && totalCombinedCard
> cc
){
2220 Assert( d_com_card_literal
.find( cc
) != d_com_card_literal
.end() );
2221 Node com_lit
= d_com_card_literal
[cc
];
2222 Assert(d_com_card_assertions
.find(com_lit
)!=d_com_card_assertions
.end());
2223 Assert( d_com_card_assertions
[com_lit
] );
2224 std::vector
< Node
> conf
;
2225 conf
.push_back( com_lit
);
2227 for( std::map
< TypeNode
, SortModel
* >::iterator it
= d_rep_model
.begin();
2228 it
!= d_rep_model
.end(); ++it
){
2230 if( options::ufssFairnessMonotone() ){
2231 std::map
< TypeNode
, bool >::iterator its
=
2232 d_tn_mono_slave
.find( it
->first
);
2233 if( its
!=d_tn_mono_slave
.end() && its
->second
){
2238 int c
= it
->second
->getMaximumNegativeCardinality();
2240 conf
.push_back( it
->second
->getCardinalityLiteral( c
).negate() );
2243 if( totalAdded
>cc
){
2248 Node cf
= NodeManager::currentNM()->mkNode( AND
, conf
);
2249 Trace("uf-ss-lemma") << "*** Combined cardinality conflict : " << cf
2251 Trace("uf-ss-com-card") << "*** Combined cardinality conflict : " << cf
2253 getOutputChannel().conflict( cf
);
2254 d_conflict
.set( true );
2259 void StrongSolverTheoryUF::allocateCombinedCardinality() {
2260 Assert( options::ufssMode()==UF_SS_FULL
);
2261 Trace("uf-ss-com-card") << "Allocate combined cardinality (" << d_aloc_com_card
.get() << ")" << std::endl
;
2263 Node lem
= NodeManager::currentNM()->mkNode( COMBINED_CARDINALITY_CONSTRAINT
,
2264 NodeManager::currentNM()->mkConst( Rational( d_aloc_com_card
.get() ) ) );
2265 Trace("uf-ss-com-card") << "Split on " << lem
<< std::endl
;
2266 lem
= Rewriter::rewrite(lem
);
2267 d_com_card_literal
[ d_aloc_com_card
.get() ] = lem
;
2268 lem
= NodeManager::currentNM()->mkNode( OR
, lem
, lem
.notNode() );
2269 //add as lemma to output channel
2270 Trace("uf-ss-lemma") << "*** Combined cardinality split : " << lem
<< std::endl
;
2271 getOutputChannel().lemma( lem
);
2273 getOutputChannel().requirePhase( d_com_card_literal
[ d_aloc_com_card
.get() ], true );
2274 //increment cardinality
2275 d_aloc_com_card
.set( d_aloc_com_card
.get() + 1 );
2278 StrongSolverTheoryUF::Statistics::Statistics():
2279 d_clique_conflicts("StrongSolverTheoryUF::Clique_Conflicts", 0),
2280 d_clique_lemmas("StrongSolverTheoryUF::Clique_Lemmas", 0),
2281 d_split_lemmas("StrongSolverTheoryUF::Split_Lemmas", 0),
2282 d_disamb_term_lemmas("StrongSolverTheoryUF::Disambiguate_Term_Lemmas", 0),
2283 d_sym_break_lemmas("StrongSolverTheoryUF::Symmetry_Breaking_Lemmas", 0),
2284 d_totality_lemmas("StrongSolverTheoryUF::Totality_Lemmas", 0),
2285 d_max_model_size("StrongSolverTheoryUF::Max_Model_Size", 1)
2287 smtStatisticsRegistry()->registerStat(&d_clique_conflicts
);
2288 smtStatisticsRegistry()->registerStat(&d_clique_lemmas
);
2289 smtStatisticsRegistry()->registerStat(&d_split_lemmas
);
2290 smtStatisticsRegistry()->registerStat(&d_disamb_term_lemmas
);
2291 smtStatisticsRegistry()->registerStat(&d_sym_break_lemmas
);
2292 smtStatisticsRegistry()->registerStat(&d_totality_lemmas
);
2293 smtStatisticsRegistry()->registerStat(&d_max_model_size
);
2296 StrongSolverTheoryUF::Statistics::~Statistics(){
2297 smtStatisticsRegistry()->unregisterStat(&d_clique_conflicts
);
2298 smtStatisticsRegistry()->unregisterStat(&d_clique_lemmas
);
2299 smtStatisticsRegistry()->unregisterStat(&d_split_lemmas
);
2300 smtStatisticsRegistry()->unregisterStat(&d_disamb_term_lemmas
);
2301 smtStatisticsRegistry()->unregisterStat(&d_sym_break_lemmas
);
2302 smtStatisticsRegistry()->unregisterStat(&d_totality_lemmas
);
2303 smtStatisticsRegistry()->unregisterStat(&d_max_model_size
);
2307 DisequalityPropagator::DisequalityPropagator(QuantifiersEngine
* qe
,
2308 StrongSolverTheoryUF
* ufss
)
2309 : d_qe(qe
), d_ufss(ufss
)
2311 d_true
= NodeManager::currentNM()->mkConst( true );
2312 d_false
= NodeManager::currentNM()->mkConst( false );
2315 void DisequalityPropagator::checkEquivalenceClass( Node t
, Node eqc
) {
2316 if( t
.getKind()==APPLY_UF
){
2317 Node op
= t
.getOperator();
2318 eqc
= d_ufss
->getTheory()->getEqualityEngine()->getRepresentative( eqc
);
2319 eq::EqClassIterator
eqc_i(eqc
, d_ufss
->getTheory()->getEqualityEngine());
2320 while( !eqc_i
.isFinished() ){
2322 if( s
.getKind()==APPLY_UF
&& s
.getOperator()==op
){
2324 for( size_t i
=0; i
<t
.getNumChildren(); i
++ ){
2325 //should consult strong solver since it knows more disequalities
2326 if( d_ufss
->areDisequal( t
[i
], s
[i
] ) ){
2327 //if( d_qe->getEqualityQuery()->areDisequal( t[i], s[i] ) ){
2330 }else if( !d_qe
->getEqualityQuery()->areEqual( t
[i
], s
[i
] ) ){
2340 Trace("deq-prop") << "propagate disequality " << t
[unkIndex
] << " " << s
[unkIndex
] << std::endl
;
2341 d_ufss
->assertDisequal(t
[unkIndex
], s
[unkIndex
], Node::null());
2342 ++( d_statistics
.d_propagations
);
2343 if( d_ufss
->isConflict() ){
2354 void DisequalityPropagator::merge( Node a
, Node b
){
2358 /** assert terms are disequal */
2359 void DisequalityPropagator::assertDisequal( Node a
, Node b
, Node reason
){
2360 Trace("deq-prop") << "Notify disequal : " << a
<< " " << b
<< std::endl
;
2364 void DisequalityPropagator::assertPredicate( Node p
, bool polarity
) {
2365 Trace("deq-prop") << "Assert predicate : " << p
<< " " << polarity
<< std::endl
;
2366 checkEquivalenceClass( p
, polarity
? d_false
: d_true
);
2369 DisequalityPropagator::Statistics::Statistics():
2370 d_propagations("StrongSolverTheoryUF::Disequality_Propagations", 0)
2372 smtStatisticsRegistry()->registerStat(& d_propagations
);
2375 DisequalityPropagator::Statistics::~Statistics(){
2376 smtStatisticsRegistry()->unregisterStat(& d_propagations
);
2379 }/* CVC4::theory namespace::uf */
2380 }/* CVC4::theory namespace */
2381 }/* CVC4 namespace */