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