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