1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Tim King, Aina Niemetz, Mathias Preiner |
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 |
|
* This is an implementation of the Simplex Module for the Simplex for |
14 |
|
* DPLL(T)decision procedure. |
15 |
|
*/ |
16 |
|
#include "theory/arith/fc_simplex.h" |
17 |
|
|
18 |
|
#include "base/output.h" |
19 |
|
#include "options/arith_options.h" |
20 |
|
#include "smt/smt_statistics_registry.h" |
21 |
|
#include "theory/arith/constraint.h" |
22 |
|
#include "theory/arith/error_set.h" |
23 |
|
#include "util/statistics_stats.h" |
24 |
|
|
25 |
|
using namespace std; |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace theory { |
29 |
|
namespace arith { |
30 |
|
|
31 |
15273 |
FCSimplexDecisionProcedure::FCSimplexDecisionProcedure( |
32 |
|
Env& env, |
33 |
|
LinearEqualityModule& linEq, |
34 |
|
ErrorSet& errors, |
35 |
|
RaiseConflict conflictChannel, |
36 |
15273 |
TempVarMalloc tvmalloc) |
37 |
|
: SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc), |
38 |
|
d_focusSize(0), |
39 |
|
d_focusErrorVar(ARITHVAR_SENTINEL), |
40 |
|
d_focusCoefficients(), |
41 |
|
d_pivotBudget(0), |
42 |
|
d_prevWitnessImprovement(AntiProductive), |
43 |
|
d_witnessImprovementInARow(0), |
44 |
|
d_sgnDisagreements(), |
45 |
15273 |
d_statistics("theory::arith::FC::", d_pivots) |
46 |
15273 |
{ } |
47 |
|
|
48 |
15273 |
FCSimplexDecisionProcedure::Statistics::Statistics(const std::string& name, |
49 |
15273 |
uint32_t& pivots) |
50 |
|
: d_initialSignalsTime( |
51 |
30546 |
smtStatisticsRegistry().registerTimer(name + "initialProcessTime")), |
52 |
|
d_initialConflicts( |
53 |
30546 |
smtStatisticsRegistry().registerInt(name + "UpdateConflicts")), |
54 |
30546 |
d_fcFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")), |
55 |
30546 |
d_fcFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")), |
56 |
30546 |
d_fcMissed(smtStatisticsRegistry().registerInt(name + "Missed")), |
57 |
30546 |
d_fcTimer(smtStatisticsRegistry().registerTimer(name + "Timer")), |
58 |
|
d_fcFocusConstructionTimer( |
59 |
30546 |
smtStatisticsRegistry().registerTimer(name + "Construction")), |
60 |
15273 |
d_selectUpdateForDualLike(smtStatisticsRegistry().registerTimer( |
61 |
30546 |
name + "selectUpdateForDualLike")), |
62 |
15273 |
d_selectUpdateForPrimal(smtStatisticsRegistry().registerTimer( |
63 |
30546 |
name + "selectUpdateForPrimal")), |
64 |
|
d_finalCheckPivotCounter( |
65 |
15273 |
smtStatisticsRegistry().registerReference<uint32_t>( |
66 |
152730 |
name + "lastPivots", pivots)) |
67 |
|
{ |
68 |
15273 |
} |
69 |
|
|
70 |
|
Result::Sat FCSimplexDecisionProcedure::findModel(bool exactResult){ |
71 |
|
Assert(d_conflictVariables.empty()); |
72 |
|
Assert(d_sgnDisagreements.empty()); |
73 |
|
|
74 |
|
d_pivots = 0; |
75 |
|
static thread_local unsigned int instance = 0; |
76 |
|
instance = instance + 1; |
77 |
|
|
78 |
|
if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){ |
79 |
|
Debug("arith::findModel") << "fcFindModel("<< instance <<") trivial" << endl; |
80 |
|
Assert(d_conflictVariables.empty()); |
81 |
|
return Result::SAT; |
82 |
|
} |
83 |
|
|
84 |
|
// We need to reduce this because of |
85 |
|
d_errorSet.reduceToSignals(); |
86 |
|
|
87 |
|
// We must start tracking NOW |
88 |
|
d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC); |
89 |
|
|
90 |
|
if(initialProcessSignals()){ |
91 |
|
d_conflictVariables.purge(); |
92 |
|
Debug("arith::findModel") << "fcFindModel("<< instance <<") early conflict" << endl; |
93 |
|
Assert(d_conflictVariables.empty()); |
94 |
|
return Result::UNSAT; |
95 |
|
}else if(d_errorSet.errorEmpty()){ |
96 |
|
Debug("arith::findModel") << "fcFindModel("<< instance <<") fixed itself" << endl; |
97 |
|
Assert(d_conflictVariables.empty()); |
98 |
|
return Result::SAT; |
99 |
|
} |
100 |
|
|
101 |
|
Debug("arith::findModel") << "fcFindModel(" << instance <<") start non-trivial" << endl; |
102 |
|
|
103 |
|
exactResult |= d_varOrderPivotLimit < 0; |
104 |
|
|
105 |
|
d_prevWitnessImprovement = HeuristicDegenerate; |
106 |
|
d_witnessImprovementInARow = 0; |
107 |
|
|
108 |
|
Result::Sat result = Result::SAT_UNKNOWN; |
109 |
|
|
110 |
|
if(result == Result::SAT_UNKNOWN){ |
111 |
|
if(exactResult){ |
112 |
|
d_pivotBudget = -1; |
113 |
|
}else{ |
114 |
|
d_pivotBudget = d_varOrderPivotLimit; |
115 |
|
} |
116 |
|
|
117 |
|
result = dualLike(); |
118 |
|
|
119 |
|
if(result == Result::UNSAT){ |
120 |
|
++(d_statistics.d_fcFoundUnsat); |
121 |
|
}else if(d_errorSet.errorEmpty()){ |
122 |
|
++(d_statistics.d_fcFoundSat); |
123 |
|
}else{ |
124 |
|
++(d_statistics.d_fcMissed); |
125 |
|
} |
126 |
|
} |
127 |
|
|
128 |
|
Assert(!d_errorSet.moreSignals()); |
129 |
|
if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){ |
130 |
|
result = Result::SAT; |
131 |
|
} |
132 |
|
|
133 |
|
// ensure that the conflict variable is still in the queue. |
134 |
|
d_conflictVariables.purge(); |
135 |
|
|
136 |
|
Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl; |
137 |
|
|
138 |
|
Assert(d_conflictVariables.empty()); |
139 |
|
return result; |
140 |
|
} |
141 |
|
|
142 |
|
|
143 |
|
void FCSimplexDecisionProcedure::logPivot(WitnessImprovement w){ |
144 |
|
if(d_pivotBudget > 0) { |
145 |
|
--d_pivotBudget; |
146 |
|
} |
147 |
|
Assert(w != AntiProductive); |
148 |
|
|
149 |
|
if(w == d_prevWitnessImprovement){ |
150 |
|
++d_witnessImprovementInARow; |
151 |
|
// ignore overflow : probably never reached |
152 |
|
if(d_witnessImprovementInARow == 0){ |
153 |
|
--d_witnessImprovementInARow; |
154 |
|
} |
155 |
|
}else{ |
156 |
|
if(w != BlandsDegenerate){ |
157 |
|
d_witnessImprovementInARow = 1; |
158 |
|
} |
159 |
|
// if w == BlandsDegenerate do not reset the counter |
160 |
|
d_prevWitnessImprovement = w; |
161 |
|
} |
162 |
|
if(strongImprovement(w)){ |
163 |
|
d_leavingCountSinceImprovement.purge(); |
164 |
|
} |
165 |
|
|
166 |
|
Debug("logPivot") << "logPivot " << d_prevWitnessImprovement << " " << d_witnessImprovementInARow << endl; |
167 |
|
|
168 |
|
} |
169 |
|
|
170 |
|
uint32_t FCSimplexDecisionProcedure::degeneratePivotsInARow() const { |
171 |
|
switch(d_prevWitnessImprovement){ |
172 |
|
case ConflictFound: |
173 |
|
case ErrorDropped: |
174 |
|
case FocusImproved: |
175 |
|
return 0; |
176 |
|
case HeuristicDegenerate: |
177 |
|
case BlandsDegenerate: |
178 |
|
return d_witnessImprovementInARow; |
179 |
|
// Degenerate is unreachable for its own reasons |
180 |
|
case Degenerate: |
181 |
|
case FocusShrank: |
182 |
|
case AntiProductive: |
183 |
|
Unreachable(); |
184 |
|
return -1; |
185 |
|
} |
186 |
|
Unreachable(); |
187 |
|
} |
188 |
|
|
189 |
|
void FCSimplexDecisionProcedure::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){ |
190 |
|
uint32_t newErrorSize = d_errorSet.errorSize(); |
191 |
|
uint32_t newFocusSize = d_errorSet.focusSize(); |
192 |
|
|
193 |
|
//Assert(!d_conflictVariables.empty() || newFocusSize <= d_focusSize); |
194 |
|
Assert(!d_conflictVariables.empty() || newErrorSize <= d_errorSize); |
195 |
|
|
196 |
|
if(newFocusSize == 0 || !d_conflictVariables.empty() ){ |
197 |
|
tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar); |
198 |
|
d_focusErrorVar = ARITHVAR_SENTINEL; |
199 |
|
}else if(2*newFocusSize < d_focusSize ){ |
200 |
|
tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar); |
201 |
|
d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer); |
202 |
|
}else{ |
203 |
|
adjustInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, focusChanges); |
204 |
|
} |
205 |
|
|
206 |
|
d_errorSize = newErrorSize; |
207 |
|
d_focusSize = newFocusSize; |
208 |
|
} |
209 |
|
|
210 |
|
WitnessImprovement FCSimplexDecisionProcedure::adjustFocusShrank(const ArithVarVec& dropped){ |
211 |
|
Assert(dropped.size() > 0); |
212 |
|
Assert(d_errorSet.focusSize() == d_focusSize); |
213 |
|
Assert(d_errorSet.focusSize() > dropped.size()); |
214 |
|
|
215 |
|
uint32_t newFocusSize = d_focusSize - dropped.size(); |
216 |
|
Assert(newFocusSize > 0); |
217 |
|
|
218 |
|
if(2 * newFocusSize <= d_focusSize){ |
219 |
|
d_errorSet.dropFromFocusAll(dropped); |
220 |
|
tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar); |
221 |
|
d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer); |
222 |
|
}else{ |
223 |
|
shrinkInfeasFunc(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar, dropped); |
224 |
|
d_errorSet.dropFromFocusAll(dropped); |
225 |
|
} |
226 |
|
|
227 |
|
d_focusSize = newFocusSize; |
228 |
|
Assert(d_errorSet.focusSize() == d_focusSize); |
229 |
|
return FocusShrank; |
230 |
|
} |
231 |
|
|
232 |
|
WitnessImprovement FCSimplexDecisionProcedure::focusDownToJust(ArithVar v){ |
233 |
|
// uint32_t newErrorSize = d_errorSet.errorSize(); |
234 |
|
// uint32_t newFocusSize = d_errorSet.focusSize(); |
235 |
|
Assert(d_focusSize == d_errorSet.focusSize()); |
236 |
|
Assert(d_focusSize > 1); |
237 |
|
Assert(d_errorSet.inFocus(v)); |
238 |
|
|
239 |
|
d_errorSet.focusDownToJust(v); |
240 |
|
Assert(d_errorSet.focusSize() == 1); |
241 |
|
d_focusSize = 1; |
242 |
|
|
243 |
|
tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar); |
244 |
|
d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer); |
245 |
|
|
246 |
|
return FocusShrank; |
247 |
|
} |
248 |
|
|
249 |
|
|
250 |
|
|
251 |
|
UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) { |
252 |
|
UpdateInfo selected; |
253 |
|
|
254 |
|
static int instance = 0 ; |
255 |
|
++instance; |
256 |
|
|
257 |
|
Debug("arith::selectPrimalUpdate") |
258 |
|
<< "selectPrimalUpdate " << instance << endl |
259 |
|
<< basic << " " << d_tableau.basicRowLength(basic) |
260 |
|
<< " " << d_linEq.debugBasicAtBoundCount(basic) << endl; |
261 |
|
|
262 |
|
static const int s_maxCandidatesAfterImprove = 3; |
263 |
|
bool isFocus = basic == d_focusErrorVar; |
264 |
|
Assert(isFocus || d_errorSet.inError(basic)); |
265 |
|
int basicDir = isFocus? 1 : d_errorSet.getSgn(basic); |
266 |
|
bool dualLike = !isFocus && d_focusSize > 1; |
267 |
|
|
268 |
|
if(!isFocus){ |
269 |
|
loadFocusSigns(); |
270 |
|
} |
271 |
|
|
272 |
|
decreasePenalties(); |
273 |
|
|
274 |
|
typedef std::vector<Cand> CandVector; |
275 |
|
CandVector candidates; |
276 |
|
|
277 |
|
for(Tableau::RowIterator ri = d_tableau.basicRowIterator(basic); !ri.atEnd(); ++ri){ |
278 |
|
const Tableau::Entry& e = *ri; |
279 |
|
ArithVar curr = e.getColVar(); |
280 |
|
if(curr == basic){ continue; } |
281 |
|
|
282 |
|
int sgn = e.getCoefficient().sgn(); |
283 |
|
int curr_movement = basicDir * sgn; |
284 |
|
|
285 |
|
bool candidate = |
286 |
|
(curr_movement > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) || |
287 |
|
(curr_movement < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0); |
288 |
|
|
289 |
|
Debug("arith::selectPrimalUpdate") |
290 |
|
<< "storing " << basic |
291 |
|
<< " " << curr |
292 |
|
<< " " << candidate |
293 |
|
<< " " << e.getCoefficient() |
294 |
|
<< " " << curr_movement |
295 |
|
<< " " << focusCoefficient(curr) << endl; |
296 |
|
|
297 |
|
if(!candidate) { continue; } |
298 |
|
|
299 |
|
if(!isFocus){ |
300 |
|
const Rational& focusC = focusCoefficient(curr); |
301 |
|
Assert(dualLike || !focusC.isZero()); |
302 |
|
if(dualLike && curr_movement != focusC.sgn()){ |
303 |
|
Debug("arith::selectPrimalUpdate") << "sgn disagreement " << curr << endl; |
304 |
|
d_sgnDisagreements.push_back(curr); |
305 |
|
continue; |
306 |
|
}else{ |
307 |
|
candidates.push_back(Cand(curr, penalty(curr), curr_movement, &focusC)); |
308 |
|
} |
309 |
|
}else{ |
310 |
|
candidates.push_back(Cand(curr, penalty(curr), curr_movement, &e.getCoefficient())); |
311 |
|
} |
312 |
|
} |
313 |
|
|
314 |
|
CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties); |
315 |
|
CandVector::iterator i = candidates.begin(); |
316 |
|
CandVector::iterator end = candidates.end(); |
317 |
|
std::make_heap(i, end, colCmp); |
318 |
|
|
319 |
|
bool checkEverything = d_pivots == 0; |
320 |
|
|
321 |
|
int candidatesAfterFocusImprove = 0; |
322 |
|
while(i != end && (checkEverything || candidatesAfterFocusImprove <= s_maxCandidatesAfterImprove)){ |
323 |
|
std::pop_heap(i, end, colCmp); |
324 |
|
--end; |
325 |
|
Cand& cand = (*end); |
326 |
|
ArithVar curr = cand.d_nb; |
327 |
|
const Rational& coeff = *cand.d_coeff; |
328 |
|
|
329 |
|
LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr); |
330 |
|
UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc); |
331 |
|
|
332 |
|
Debug("arith::selectPrimalUpdate") |
333 |
|
<< "selected " << selected << endl |
334 |
|
<< "currProp " << currProposal << endl |
335 |
|
<< "coeff " << coeff << endl; |
336 |
|
|
337 |
|
Assert(!currProposal.uninitialized()); |
338 |
|
|
339 |
|
if(candidatesAfterFocusImprove > 0){ |
340 |
|
candidatesAfterFocusImprove++; |
341 |
|
} |
342 |
|
|
343 |
|
if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){ |
344 |
|
|
345 |
|
selected = currProposal; |
346 |
|
WitnessImprovement w = selected.getWitness(false); |
347 |
|
Debug("arith::selectPrimalUpdate") << "selected " << w << endl; |
348 |
|
setPenalty(curr, w); |
349 |
|
if(improvement(w)){ |
350 |
|
bool exitEarly; |
351 |
|
switch(w){ |
352 |
|
case ConflictFound: exitEarly = true; break; |
353 |
|
case ErrorDropped: |
354 |
|
if(checkEverything){ |
355 |
|
exitEarly = d_errorSize + selected.errorsChange() == 0; |
356 |
|
Debug("arith::selectPrimalUpdate") |
357 |
|
<< "ee " << d_errorSize << " " |
358 |
|
<< selected.errorsChange() << " " |
359 |
|
<< d_errorSize + selected.errorsChange() << endl; |
360 |
|
}else{ |
361 |
|
exitEarly = true; |
362 |
|
} |
363 |
|
break; |
364 |
|
case FocusImproved: |
365 |
|
candidatesAfterFocusImprove = 1; |
366 |
|
exitEarly = false; |
367 |
|
break; |
368 |
|
default: |
369 |
|
exitEarly = false; break; |
370 |
|
} |
371 |
|
if(exitEarly){ break; } |
372 |
|
} |
373 |
|
}else{ |
374 |
|
Debug("arith::selectPrimalUpdate") << "dropped "<< endl; |
375 |
|
} |
376 |
|
|
377 |
|
} |
378 |
|
|
379 |
|
if(!isFocus){ |
380 |
|
unloadFocusSigns(); |
381 |
|
} |
382 |
|
return selected; |
383 |
|
} |
384 |
|
|
385 |
|
bool FCSimplexDecisionProcedure::debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){ |
386 |
|
if(inf.getWitness(useBlands) == w){ |
387 |
|
switch(w){ |
388 |
|
case ConflictFound: return inf.foundConflict(); |
389 |
|
case ErrorDropped: return inf.errorsChange() < 0; |
390 |
|
case FocusImproved: return inf.focusDirection() > 0; |
391 |
|
case FocusShrank: return false; // This is not a valid output |
392 |
|
case Degenerate: return false; // This is not a valid output |
393 |
|
case BlandsDegenerate: return useBlands; |
394 |
|
case HeuristicDegenerate: return !useBlands; |
395 |
|
case AntiProductive: return false; |
396 |
|
} |
397 |
|
} |
398 |
|
return false; |
399 |
|
} |
400 |
|
|
401 |
|
WitnessImprovement FCSimplexDecisionProcedure::primalImproveError(ArithVar errorVar){ |
402 |
|
bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving; |
403 |
|
UpdateInfo selected = selectUpdateForPrimal (errorVar, useBlands); |
404 |
|
Assert(!selected.uninitialized()); |
405 |
|
WitnessImprovement w = selected.getWitness(useBlands); |
406 |
|
Assert(debugCheckWitness(selected, w, useBlands)); |
407 |
|
|
408 |
|
updateAndSignal(selected, w); |
409 |
|
logPivot(w); |
410 |
|
return w; |
411 |
|
} |
412 |
|
|
413 |
|
|
414 |
|
WitnessImprovement FCSimplexDecisionProcedure::focusUsingSignDisagreements(ArithVar basic){ |
415 |
|
Assert(!d_sgnDisagreements.empty()); |
416 |
|
Assert(d_errorSet.focusSize() >= 2); |
417 |
|
|
418 |
|
if(Debug.isOn("arith::focus")){ |
419 |
|
d_errorSet.debugPrint(Debug("arith::focus")); |
420 |
|
} |
421 |
|
|
422 |
|
ArithVar nb = d_linEq.minBy(d_sgnDisagreements, &LinearEqualityModule::minColLength); |
423 |
|
const Tableau::Entry& e_evar_nb = d_tableau.basicFindEntry(basic, nb); |
424 |
|
int oppositeSgn = - (e_evar_nb.getCoefficient().sgn()); |
425 |
|
Debug("arith::focus") << "focusUsingSignDisagreements " << basic << " " << oppositeSgn << endl; |
426 |
|
|
427 |
|
ArithVarVec dropped; |
428 |
|
|
429 |
|
Tableau::ColIterator colIter = d_tableau.colIterator(nb); |
430 |
|
for(; !colIter.atEnd(); ++colIter){ |
431 |
|
const Tableau::Entry& entry = *colIter; |
432 |
|
Assert(entry.getColVar() == nb); |
433 |
|
|
434 |
|
int sgn = entry.getCoefficient().sgn(); |
435 |
|
Debug("arith::focus") |
436 |
|
<< "on row " |
437 |
|
<< d_tableau.rowIndexToBasic(entry.getRowIndex()) |
438 |
|
<< " " |
439 |
|
<< entry.getCoefficient() << endl; |
440 |
|
ArithVar currRow = d_tableau.rowIndexToBasic(entry.getRowIndex()); |
441 |
|
if(d_errorSet.inError(currRow) && d_errorSet.inFocus(currRow)){ |
442 |
|
int errSgn = d_errorSet.getSgn(currRow); |
443 |
|
|
444 |
|
if(errSgn * sgn == oppositeSgn){ |
445 |
|
dropped.push_back(currRow); |
446 |
|
Debug("arith::focus") << "dropping from focus " << currRow << endl; |
447 |
|
} |
448 |
|
} |
449 |
|
} |
450 |
|
|
451 |
|
d_sgnDisagreements.clear(); |
452 |
|
return adjustFocusShrank(dropped); |
453 |
|
} |
454 |
|
|
455 |
|
bool debugSelectedErrorDropped(const UpdateInfo& selected, int32_t prevErrorSize, int32_t currErrorSize){ |
456 |
|
int diff = currErrorSize - prevErrorSize; |
457 |
|
return selected.foundConflict() || diff == selected.errorsChange(); |
458 |
|
} |
459 |
|
|
460 |
|
void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{ |
461 |
|
Debug("updateAndSignal") << "updated basic " << updated; |
462 |
|
Debug("updateAndSignal") << " length " << d_tableau.basicRowLength(updated); |
463 |
|
Debug("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated); |
464 |
|
int dir = !d_variables.assignmentIsConsistent(updated) ? |
465 |
|
d_errorSet.getSgn(updated) : 0; |
466 |
|
Debug("updateAndSignal") << " dir " << dir; |
467 |
|
Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl; |
468 |
|
} |
469 |
|
|
470 |
|
bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){ |
471 |
|
if(selected.describesPivot() && updated == selected.leaving()){ |
472 |
|
return selected.foundConflict(); |
473 |
|
}else{ |
474 |
|
return true; |
475 |
|
} |
476 |
|
} |
477 |
|
|
478 |
|
void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){ |
479 |
|
ArithVar nonbasic = selected.nonbasic(); |
480 |
|
|
481 |
|
Debug("updateAndSignal") << "updateAndSignal " << selected << endl; |
482 |
|
|
483 |
|
stringstream ss; |
484 |
|
|
485 |
|
if(selected.describesPivot()){ |
486 |
|
ConstraintP limiting = selected.limiting(); |
487 |
|
ArithVar basic = limiting->getVariable(); |
488 |
|
Assert(d_linEq.basicIsTracked(basic)); |
489 |
|
d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue()); |
490 |
|
}else{ |
491 |
|
Assert(!selected.unbounded() || selected.errorsChange() < 0); |
492 |
|
|
493 |
|
DeltaRational newAssignment = |
494 |
|
d_variables.getAssignment(nonbasic) + selected.nonbasicDelta(); |
495 |
|
|
496 |
|
d_linEq.updateTracked(nonbasic, newAssignment); |
497 |
|
} |
498 |
|
d_pivots++; |
499 |
|
|
500 |
|
increaseLeavingCount(nonbasic); |
501 |
|
|
502 |
|
vector< pair<ArithVar, int> > focusChanges; |
503 |
|
while(d_errorSet.moreSignals()){ |
504 |
|
ArithVar updated = d_errorSet.topSignal(); |
505 |
|
int prevFocusSgn = d_errorSet.popSignal(); |
506 |
|
|
507 |
|
if(d_tableau.isBasic(updated)){ |
508 |
|
Assert(!d_variables.assignmentIsConsistent(updated) |
509 |
|
== d_errorSet.inError(updated)); |
510 |
|
if(Debug.isOn("updateAndSignal")){debugPrintSignal(updated);} |
511 |
|
if(!d_variables.assignmentIsConsistent(updated)){ |
512 |
|
if(checkBasicForConflict(updated)){ |
513 |
|
reportConflict(updated); |
514 |
|
Assert(debugUpdatedBasic(selected, updated)); |
515 |
|
} |
516 |
|
} |
517 |
|
}else{ |
518 |
|
Debug("updateAndSignal") << "updated nonbasic " << updated << endl; |
519 |
|
} |
520 |
|
int currFocusSgn = d_errorSet.focusSgn(updated); |
521 |
|
if(currFocusSgn != prevFocusSgn){ |
522 |
|
int change = currFocusSgn - prevFocusSgn; |
523 |
|
focusChanges.push_back(make_pair(updated, change)); |
524 |
|
} |
525 |
|
} |
526 |
|
|
527 |
|
if(Debug.isOn("error")){ d_errorSet.debugPrint(Debug("error")); } |
528 |
|
|
529 |
|
Assert( |
530 |
|
debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize())); |
531 |
|
|
532 |
|
adjustFocusAndError(selected, focusChanges); |
533 |
|
} |
534 |
|
|
535 |
|
WitnessImprovement FCSimplexDecisionProcedure::dualLikeImproveError(ArithVar errorVar){ |
536 |
|
Assert(d_sgnDisagreements.empty()); |
537 |
|
Assert(d_focusSize > 1); |
538 |
|
|
539 |
|
UpdateInfo selected = selectUpdateForDualLike(errorVar); |
540 |
|
|
541 |
|
if(selected.uninitialized()){ |
542 |
|
// we found no proposals |
543 |
|
// If this is empty, there must be an error on this variable! |
544 |
|
// this should not be possible. It Should have been caught as a signal earlier |
545 |
|
WitnessImprovement dropped = focusUsingSignDisagreements(errorVar); |
546 |
|
Assert(d_sgnDisagreements.empty()); |
547 |
|
|
548 |
|
return dropped; |
549 |
|
}else{ |
550 |
|
d_sgnDisagreements.clear(); |
551 |
|
} |
552 |
|
|
553 |
|
Assert(d_sgnDisagreements.empty()); |
554 |
|
Assert(!selected.uninitialized()); |
555 |
|
|
556 |
|
if(selected.focusDirection() == 0 && |
557 |
|
d_prevWitnessImprovement == HeuristicDegenerate && |
558 |
|
d_witnessImprovementInARow >= s_focusThreshold){ |
559 |
|
|
560 |
|
Debug("focusDownToJust") << "focusDownToJust " << errorVar << endl; |
561 |
|
|
562 |
|
return focusDownToJust(errorVar); |
563 |
|
}else{ |
564 |
|
WitnessImprovement w = selected.getWitness(false); |
565 |
|
Assert(debugCheckWitness(selected, w, false)); |
566 |
|
updateAndSignal(selected, w); |
567 |
|
logPivot(w); |
568 |
|
return w; |
569 |
|
} |
570 |
|
} |
571 |
|
|
572 |
|
WitnessImprovement FCSimplexDecisionProcedure::focusDownToLastHalf(){ |
573 |
|
Assert(d_focusSize >= 2); |
574 |
|
|
575 |
|
Debug("focusDownToLastHalf") << "focusDownToLastHalf " |
576 |
|
<< d_errorSet.errorSize() << " " |
577 |
|
<< d_errorSet.focusSize() << " "; |
578 |
|
|
579 |
|
uint32_t half = d_focusSize/2; |
580 |
|
ArithVarVec buf; |
581 |
|
for(ErrorSet::focus_iterator i = d_errorSet.focusBegin(), |
582 |
|
i_end = d_errorSet.focusEnd(); i != i_end; ++i){ |
583 |
|
if(half > 0){ |
584 |
|
--half; |
585 |
|
} else{ |
586 |
|
buf.push_back(*i); |
587 |
|
} |
588 |
|
} |
589 |
|
WitnessImprovement w = adjustFocusShrank(buf); |
590 |
|
Debug("focusDownToLastHalf") << "-> " << d_errorSet.focusSize() << endl; |
591 |
|
return w; |
592 |
|
} |
593 |
|
|
594 |
|
WitnessImprovement FCSimplexDecisionProcedure::selectFocusImproving() { |
595 |
|
Assert(d_focusErrorVar != ARITHVAR_SENTINEL); |
596 |
|
Assert(d_focusSize >= 2); |
597 |
|
|
598 |
|
LinearEqualityModule::UpdatePreferenceFunction upf = |
599 |
|
&LinearEqualityModule::preferWitness<true>; |
600 |
|
|
601 |
|
LinearEqualityModule::VarPreferenceFunction bpf = |
602 |
|
&LinearEqualityModule::minRowLength; |
603 |
|
|
604 |
|
UpdateInfo selected = selectPrimalUpdate(d_focusErrorVar, upf, bpf); |
605 |
|
|
606 |
|
if(selected.uninitialized()){ |
607 |
|
Debug("selectFocusImproving") << "focus is optimum, but we don't have sat/conflict yet" << endl; |
608 |
|
|
609 |
|
return focusDownToLastHalf(); |
610 |
|
} |
611 |
|
Assert(!selected.uninitialized()); |
612 |
|
WitnessImprovement w = selected.getWitness(false); |
613 |
|
Assert(debugCheckWitness(selected, w, false)); |
614 |
|
|
615 |
|
if(degenerate(w)){ |
616 |
|
Debug("selectFocusImproving") << "only degenerate" << endl; |
617 |
|
if(d_prevWitnessImprovement == HeuristicDegenerate && |
618 |
|
d_witnessImprovementInARow >= s_focusThreshold){ |
619 |
|
Debug("selectFocusImproving") << "focus down been degenerate too long" << endl; |
620 |
|
return focusDownToLastHalf(); |
621 |
|
}else{ |
622 |
|
Debug("selectFocusImproving") << "taking degenerate" << endl; |
623 |
|
} |
624 |
|
} |
625 |
|
Debug("selectFocusImproving") << "selectFocusImproving did this " << selected << endl; |
626 |
|
|
627 |
|
updateAndSignal(selected, w); |
628 |
|
logPivot(w); |
629 |
|
return w; |
630 |
|
} |
631 |
|
|
632 |
|
bool FCSimplexDecisionProcedure::debugDualLike(WitnessImprovement w, ostream& out, int instance, uint32_t prevFocusSize, uint32_t prevErrorSize ) const{ |
633 |
|
out << "DLV("<<instance<<") "; |
634 |
|
switch(w){ |
635 |
|
case ConflictFound: |
636 |
|
out << "found conflict" << endl; |
637 |
|
return !d_conflictVariables.empty(); |
638 |
|
case ErrorDropped: |
639 |
|
out << "dropped " << prevErrorSize - d_errorSize << endl; |
640 |
|
return d_errorSize < prevErrorSize; |
641 |
|
case FocusImproved: |
642 |
|
out << "focus improved"<< endl; |
643 |
|
return d_errorSize == prevErrorSize; |
644 |
|
case FocusShrank: |
645 |
|
out << "focus shrank"<< endl; |
646 |
|
return d_errorSize == prevErrorSize && prevFocusSize > d_focusSize; |
647 |
|
case BlandsDegenerate: |
648 |
|
out << "bland degenerate"<< endl; |
649 |
|
return true; |
650 |
|
case HeuristicDegenerate: |
651 |
|
out << "heuristic degenerate"<< endl; |
652 |
|
return true; |
653 |
|
case AntiProductive: |
654 |
|
out << "focus blur" << endl; |
655 |
|
return prevFocusSize == 0; |
656 |
|
case Degenerate: |
657 |
|
return false; |
658 |
|
} |
659 |
|
return false; |
660 |
|
} |
661 |
|
|
662 |
|
Result::Sat FCSimplexDecisionProcedure::dualLike(){ |
663 |
|
static int instance = 0; |
664 |
|
|
665 |
|
TimerStat::CodeTimer codeTimer(d_statistics.d_fcTimer); |
666 |
|
|
667 |
|
Assert(d_sgnDisagreements.empty()); |
668 |
|
Assert(d_pivotBudget != 0); |
669 |
|
Assert(d_errorSize == d_errorSet.errorSize()); |
670 |
|
Assert(d_errorSize > 0); |
671 |
|
Assert(d_focusSize == d_errorSet.focusSize()); |
672 |
|
Assert(d_focusSize > 0); |
673 |
|
Assert(d_conflictVariables.empty()); |
674 |
|
Assert(d_focusErrorVar == ARITHVAR_SENTINEL); |
675 |
|
|
676 |
|
d_scores.purge(); |
677 |
|
d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer); |
678 |
|
|
679 |
|
|
680 |
|
while(d_pivotBudget != 0 && d_errorSize > 0 && d_conflictVariables.empty()){ |
681 |
|
++instance; |
682 |
|
Debug("dualLike") << "dualLike " << instance << endl; |
683 |
|
|
684 |
|
Assert(d_errorSet.noSignals()); |
685 |
|
|
686 |
|
WitnessImprovement w = AntiProductive; |
687 |
|
uint32_t prevFocusSize = d_focusSize; |
688 |
|
uint32_t prevErrorSize = d_errorSize; |
689 |
|
|
690 |
|
if(d_focusSize == 0){ |
691 |
|
Assert(d_errorSize == d_errorSet.errorSize()); |
692 |
|
Assert(d_focusErrorVar == ARITHVAR_SENTINEL); |
693 |
|
|
694 |
|
d_errorSet.blur(); |
695 |
|
|
696 |
|
d_focusSize = d_errorSet.focusSize(); |
697 |
|
|
698 |
|
Assert(d_errorSize == d_focusSize); |
699 |
|
Assert(d_errorSize >= 1); |
700 |
|
|
701 |
|
d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer); |
702 |
|
|
703 |
|
Debug("dualLike") << "blur " << d_focusSize << endl; |
704 |
|
}else if(d_focusSize == 1){ |
705 |
|
// Possible outcomes: |
706 |
|
// - errorSet size shrunk |
707 |
|
// -- fixed v |
708 |
|
// -- fixed something other than v |
709 |
|
// - conflict |
710 |
|
// - budget was exhausted |
711 |
|
|
712 |
|
ArithVar e = d_errorSet.topFocusVariable(); |
713 |
|
Debug("dualLike") << "primalImproveError " << e << endl; |
714 |
|
w = primalImproveError(e); |
715 |
|
}else{ |
716 |
|
|
717 |
|
// Possible outcomes: |
718 |
|
// - errorSet size shrunk |
719 |
|
// -- fixed v |
720 |
|
// -- fixed something other than v |
721 |
|
// - conflict |
722 |
|
// - budget was exhausted |
723 |
|
// - focus went down |
724 |
|
Assert(d_focusSize > 1); |
725 |
|
ArithVar e = d_errorSet.topFocusVariable(); |
726 |
|
static const unsigned s_sumMetricThreshold = 1; |
727 |
|
if(d_errorSet.sumMetric(e) <= s_sumMetricThreshold){ |
728 |
|
Debug("dualLike") << "dualLikeImproveError " << e << endl; |
729 |
|
w = dualLikeImproveError(e); |
730 |
|
}else{ |
731 |
|
Debug("dualLike") << "selectFocusImproving " << endl; |
732 |
|
w = selectFocusImproving(); |
733 |
|
} |
734 |
|
} |
735 |
|
Debug("dualLike") << "witnessImprovement: " << w << endl; |
736 |
|
Assert(d_focusSize == d_errorSet.focusSize()); |
737 |
|
Assert(d_errorSize == d_errorSet.errorSize()); |
738 |
|
|
739 |
|
Assert(debugDualLike( |
740 |
|
w, Debug("dualLike"), instance, prevFocusSize, prevErrorSize)); |
741 |
|
Debug("dualLike") << "Focus size " << d_focusSize << " (was " << prevFocusSize << ")" << endl; |
742 |
|
Debug("dualLike") << "Error size " << d_errorSize << " (was " << prevErrorSize << ")" << endl; |
743 |
|
} |
744 |
|
|
745 |
|
|
746 |
|
if(d_focusErrorVar != ARITHVAR_SENTINEL){ |
747 |
|
tearDownInfeasiblityFunction(d_statistics.d_fcFocusConstructionTimer, d_focusErrorVar); |
748 |
|
d_focusErrorVar = ARITHVAR_SENTINEL; |
749 |
|
} |
750 |
|
|
751 |
|
Assert(d_focusErrorVar == ARITHVAR_SENTINEL); |
752 |
|
if(!d_conflictVariables.empty()){ |
753 |
|
return Result::UNSAT; |
754 |
|
}else if(d_errorSet.errorEmpty()){ |
755 |
|
Assert(d_errorSet.noSignals()); |
756 |
|
return Result::SAT; |
757 |
|
}else{ |
758 |
|
Assert(d_pivotBudget == 0); |
759 |
|
return Result::SAT_UNKNOWN; |
760 |
|
} |
761 |
|
} |
762 |
|
|
763 |
|
|
764 |
|
void FCSimplexDecisionProcedure::loadFocusSigns(){ |
765 |
|
Assert(d_focusCoefficients.empty()); |
766 |
|
Assert(d_focusErrorVar != ARITHVAR_SENTINEL); |
767 |
|
for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){ |
768 |
|
const Tableau::Entry& e = *ri; |
769 |
|
ArithVar curr = e.getColVar(); |
770 |
|
d_focusCoefficients.set(curr, &e.getCoefficient()); |
771 |
|
} |
772 |
|
} |
773 |
|
|
774 |
|
void FCSimplexDecisionProcedure::unloadFocusSigns(){ |
775 |
|
d_focusCoefficients.purge(); |
776 |
|
} |
777 |
|
|
778 |
|
const Rational& FCSimplexDecisionProcedure::focusCoefficient(ArithVar nb) const { |
779 |
|
if(d_focusCoefficients.isKey(nb)){ |
780 |
|
return *(d_focusCoefficients[nb]); |
781 |
|
}else{ |
782 |
|
return d_zero; |
783 |
|
} |
784 |
|
} |
785 |
|
|
786 |
|
} // namespace arith |
787 |
|
} // namespace theory |
788 |
31137 |
} // namespace cvc5 |