GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/soi_simplex.cpp Lines: 23 523 4.4 %
Date: 2021-11-07 Branches: 20 2300 0.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Aina Niemetz, Morgan Deters
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/soi_simplex.h"
17
18
#include <algorithm>
19
20
#include "base/output.h"
21
#include "options/arith_options.h"
22
#include "smt/smt_statistics_registry.h"
23
#include "theory/arith/constraint.h"
24
#include "theory/arith/error_set.h"
25
#include "theory/arith/tableau.h"
26
#include "util/statistics_stats.h"
27
28
using namespace std;
29
30
namespace cvc5 {
31
namespace theory {
32
namespace arith {
33
34
15273
SumOfInfeasibilitiesSPD::SumOfInfeasibilitiesSPD(Env& env,
35
                                                 LinearEqualityModule& linEq,
36
                                                 ErrorSet& errors,
37
                                                 RaiseConflict conflictChannel,
38
15273
                                                 TempVarMalloc tvmalloc)
39
    : SimplexDecisionProcedure(env, linEq, errors, conflictChannel, tvmalloc),
40
      d_soiVar(ARITHVAR_SENTINEL),
41
      d_pivotBudget(0),
42
      d_prevWitnessImprovement(AntiProductive),
43
      d_witnessImprovementInARow(0),
44
      d_sgnDisagreements(),
45
15273
      d_statistics("theory::arith::SOI", d_pivots)
46
15273
{ }
47
48
15273
SumOfInfeasibilitiesSPD::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_soiFoundUnsat(smtStatisticsRegistry().registerInt(name + "FoundUnsat")),
55
30546
      d_soiFoundSat(smtStatisticsRegistry().registerInt(name + "FoundSat")),
56
30546
      d_soiMissed(smtStatisticsRegistry().registerInt(name + "Missed")),
57
      d_soiConflicts(
58
30546
          smtStatisticsRegistry().registerInt(name + "ConfMin::num")),
59
      d_hasToBeMinimal(
60
30546
          smtStatisticsRegistry().registerInt(name + "HasToBeMin")),
61
      d_maybeNotMinimal(
62
30546
          smtStatisticsRegistry().registerInt(name + "MaybeNotMin")),
63
30546
      d_soiTimer(smtStatisticsRegistry().registerTimer(name + "Time")),
64
      d_soiFocusConstructionTimer(
65
30546
          smtStatisticsRegistry().registerTimer(name + "Construction")),
66
15273
      d_soiConflictMinimization(smtStatisticsRegistry().registerTimer(
67
30546
          name + "Conflict::Minimization")),
68
      d_selectUpdateForSOI(
69
30546
          smtStatisticsRegistry().registerTimer(name + "selectSOI")),
70
      d_finalCheckPivotCounter(
71
15273
          smtStatisticsRegistry().registerReference<uint32_t>(
72
198549
              name + "lastPivots", pivots))
73
{
74
15273
}
75
76
Result::Sat SumOfInfeasibilitiesSPD::findModel(bool exactResult){
77
  Assert(d_conflictVariables.empty());
78
  Assert(d_sgnDisagreements.empty());
79
80
  d_pivots = 0;
81
  static thread_local unsigned int instance = 0;
82
  instance = instance + 1;
83
84
  if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){
85
    Debug("soi::findModel") << "soiFindModel("<< instance <<") trivial" << endl;
86
    Assert(d_conflictVariables.empty());
87
    return Result::SAT;
88
  }
89
90
  // We need to reduce this because of
91
  d_errorSet.reduceToSignals();
92
93
  // We must start tracking NOW
94
  d_errorSet.setSelectionRule(options::ErrorSelectionRule::SUM_METRIC);
95
96
  if(initialProcessSignals()){
97
    d_conflictVariables.purge();
98
    Debug("soi::findModel") << "fcFindModel("<< instance <<") early conflict" << endl;
99
    Assert(d_conflictVariables.empty());
100
    return Result::UNSAT;
101
  }else if(d_errorSet.errorEmpty()){
102
    Debug("soi::findModel") << "fcFindModel("<< instance <<") fixed itself" << endl;
103
    Assert(!d_errorSet.moreSignals());
104
    Assert(d_conflictVariables.empty());
105
    return Result::SAT;
106
  }
107
108
  Debug("soi::findModel") << "fcFindModel(" << instance <<") start non-trivial" << endl;
109
110
  exactResult |= d_varOrderPivotLimit < 0;
111
112
  d_prevWitnessImprovement = HeuristicDegenerate;
113
  d_witnessImprovementInARow = 0;
114
115
  Result::Sat result = Result::SAT_UNKNOWN;
116
117
  if(result == Result::SAT_UNKNOWN){
118
    if(exactResult){
119
      d_pivotBudget = -1;
120
    }else{
121
      d_pivotBudget = d_varOrderPivotLimit;
122
    }
123
124
    result = sumOfInfeasibilities();
125
126
    if(result ==  Result::UNSAT){
127
      ++(d_statistics.d_soiFoundUnsat);
128
    }else if(d_errorSet.errorEmpty()){
129
      ++(d_statistics.d_soiFoundSat);
130
    }else{
131
      ++(d_statistics.d_soiMissed);
132
    }
133
  }
134
135
  Assert(!d_errorSet.moreSignals());
136
  if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){
137
    result = Result::SAT;
138
  }
139
140
  // ensure that the conflict variable is still in the queue.
141
  d_conflictVariables.purge();
142
143
  Debug("soi::findModel") << "end findModel() " << instance << " " << result <<  endl;
144
145
  Assert(d_conflictVariables.empty());
146
  return result;
147
}
148
149
150
void SumOfInfeasibilitiesSPD::logPivot(WitnessImprovement w){
151
  if(d_pivotBudget > 0) {
152
    --d_pivotBudget;
153
  }
154
  Assert(w != AntiProductive);
155
156
  if(w == d_prevWitnessImprovement){
157
    ++d_witnessImprovementInARow;
158
    if(d_witnessImprovementInARow == 0){
159
      --d_witnessImprovementInARow;
160
    }
161
  }else{
162
    if(w != BlandsDegenerate){
163
      d_witnessImprovementInARow = 1;
164
    }
165
    d_prevWitnessImprovement = w;
166
  }
167
  if(strongImprovement(w)){
168
    d_leavingCountSinceImprovement.purge();
169
  }
170
171
  Debug("logPivot") << "logPivot " << d_prevWitnessImprovement << " "  << d_witnessImprovementInARow << endl;
172
}
173
174
uint32_t SumOfInfeasibilitiesSPD::degeneratePivotsInARow() const {
175
  switch(d_prevWitnessImprovement){
176
  case ConflictFound:
177
  case ErrorDropped:
178
  case FocusImproved:
179
    return 0;
180
  case HeuristicDegenerate:
181
  case BlandsDegenerate:
182
    return d_witnessImprovementInARow;
183
  // Degenerate is unreachable for its own reasons
184
  case Degenerate:
185
  case FocusShrank:
186
  case AntiProductive:
187
    Unreachable();
188
    return -1;
189
  }
190
  Unreachable();
191
}
192
193
void SumOfInfeasibilitiesSPD::adjustFocusAndError(const UpdateInfo& up, const AVIntPairVec& focusChanges){
194
  uint32_t newErrorSize = d_errorSet.errorSize();
195
  adjustInfeasFunc(d_statistics.d_soiFocusConstructionTimer, d_soiVar, focusChanges);
196
  d_errorSize = newErrorSize;
197
}
198
199
200
UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePreferenceFunction upf, LinearEqualityModule::VarPreferenceFunction bpf) {
201
  UpdateInfo selected;
202
203
  static int instance = 0 ;
204
  ++instance;
205
206
  Debug("soi::selectPrimalUpdate")
207
    << "selectPrimalUpdate " << instance << endl
208
    << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar)
209
    << " " << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl;
210
211
  typedef std::vector<Cand> CandVector;
212
  CandVector candidates;
213
214
  for(Tableau::RowIterator ri = d_tableau.basicRowIterator(d_soiVar); !ri.atEnd(); ++ri){
215
    const Tableau::Entry& e = *ri;
216
    ArithVar curr = e.getColVar();
217
    if(curr == d_soiVar){ continue; }
218
219
    int sgn = e.getCoefficient().sgn();
220
    bool candidate =
221
      (sgn > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) ||
222
      (sgn < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0);
223
224
    Debug("soi::selectPrimalUpdate")
225
      << "storing " << d_soiVar
226
      << " " << curr
227
      << " " << candidate
228
      << " " << e.getCoefficient()
229
      << " " << sgn << endl;
230
231
    if(candidate) {
232
      candidates.push_back(Cand(curr, 0, sgn, &e.getCoefficient()));
233
    }
234
  }
235
236
  CompPenaltyColLength colCmp(&d_linEq, options().arith.havePenalties);
237
  CandVector::iterator i = candidates.begin();
238
  CandVector::iterator end = candidates.end();
239
  std::make_heap(i, end, colCmp);
240
241
  // For the first 3 pivots take the best
242
  // After that, once an improvement is found on look at a
243
  // small number of pivots after finding an improvement
244
  // the longer the search to more willing we are to look at more candidates
245
  int maxCandidatesAfterImprove =
246
    (d_pivots <= 2) ?  std::numeric_limits<int>::max() : d_pivots/5;
247
248
  int candidatesAfterFocusImprove = 0;
249
  while(i != end && candidatesAfterFocusImprove <= maxCandidatesAfterImprove){
250
    std::pop_heap(i, end, colCmp);
251
    --end;
252
    Cand& cand = (*end);
253
    ArithVar curr = cand.d_nb;
254
    const Rational& coeff = *cand.d_coeff;
255
256
    LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr);
257
    UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc);
258
259
    Debug("soi::selectPrimalUpdate")
260
      << "selected " << selected << endl
261
      << "currProp " << currProposal << endl
262
      << "coeff " << coeff << endl;
263
264
    Assert(!currProposal.uninitialized());
265
266
    if(candidatesAfterFocusImprove > 0){
267
      candidatesAfterFocusImprove++;
268
    }
269
270
    if(selected.uninitialized() || (d_linEq.*upf)(selected, currProposal)){
271
      selected = currProposal;
272
      WitnessImprovement w = selected.getWitness(false);
273
      Debug("soi::selectPrimalUpdate") << "selected " << w << endl;
274
      //setPenalty(curr, w);
275
      if(improvement(w)){
276
        bool exitEarly;
277
        switch(w){
278
        case ConflictFound: exitEarly = true; break;
279
        case FocusImproved:
280
          candidatesAfterFocusImprove = 1;
281
          exitEarly = false;
282
          break;
283
        default:
284
          exitEarly = false; break;
285
        }
286
        if(exitEarly){ break; }
287
      }
288
    }else{
289
      Debug("soi::selectPrimalUpdate") << "dropped "<< endl;
290
    }
291
292
  }
293
  return selected;
294
}
295
296
bool debugCheckWitness(const UpdateInfo& inf, WitnessImprovement w, bool useBlands){
297
  if(inf.getWitness(useBlands) == w){
298
    switch(w){
299
    case ConflictFound: return inf.foundConflict();
300
    case ErrorDropped: return inf.errorsChange() < 0;
301
    case FocusImproved: return inf.focusDirection() > 0;
302
    case FocusShrank: return false; // This is not a valid output
303
    case Degenerate: return false; // This is not a valid output
304
    case BlandsDegenerate: return useBlands;
305
    case HeuristicDegenerate: return !useBlands;
306
    case AntiProductive: return false;
307
    }
308
  }
309
  return false;
310
}
311
312
313
void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{
314
  Debug("updateAndSignal") << "updated basic " << updated;
315
  Debug("updateAndSignal") << " length " << d_tableau.basicRowLength(updated);
316
  Debug("updateAndSignal") << " consistent " << d_variables.assignmentIsConsistent(updated);
317
  int dir = !d_variables.assignmentIsConsistent(updated) ?
318
    d_errorSet.getSgn(updated) : 0;
319
  Debug("updateAndSignal") << " dir " << dir;
320
  Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl;
321
}
322
323
324
void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, WitnessImprovement w){
325
  ArithVar nonbasic = selected.nonbasic();
326
327
  Debug("updateAndSignal") << "updateAndSignal " << selected << endl;
328
329
  if(selected.describesPivot()){
330
    ConstraintP limiting = selected.limiting();
331
    ArithVar basic = limiting->getVariable();
332
    Assert(d_linEq.basicIsTracked(basic));
333
    d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue());
334
  }else{
335
    Assert(!selected.unbounded() || selected.errorsChange() < 0);
336
337
    DeltaRational newAssignment =
338
      d_variables.getAssignment(nonbasic) + selected.nonbasicDelta();
339
340
    d_linEq.updateTracked(nonbasic, newAssignment);
341
  }
342
  d_pivots++;
343
344
  increaseLeavingCount(nonbasic);
345
346
  vector< pair<ArithVar, int> > focusChanges;
347
  while(d_errorSet.moreSignals()){
348
    ArithVar updated = d_errorSet.topSignal();
349
    int prevFocusSgn = d_errorSet.popSignal();
350
351
    if(d_tableau.isBasic(updated)){
352
      Assert(!d_variables.assignmentIsConsistent(updated)
353
             == d_errorSet.inError(updated));
354
      if(Debug.isOn("updateAndSignal")){debugPrintSignal(updated);}
355
      if(!d_variables.assignmentIsConsistent(updated)){
356
        if(checkBasicForConflict(updated)){
357
          reportConflict(updated);
358
          //Assert(debugUpdatedBasic(selected, updated));
359
        }
360
      }
361
    }else{
362
      Debug("updateAndSignal") << "updated nonbasic " << updated << endl;
363
    }
364
    int currFocusSgn = d_errorSet.focusSgn(updated);
365
    if(currFocusSgn != prevFocusSgn){
366
      int change = currFocusSgn - prevFocusSgn;
367
      focusChanges.push_back(make_pair(updated, change));
368
    }
369
  }
370
371
  if(Debug.isOn("error")){ d_errorSet.debugPrint(Debug("error")); }
372
373
  //Assert(debugSelectedErrorDropped(selected, d_errorSize, d_errorSet.errorSize()));
374
375
  adjustFocusAndError(selected, focusChanges);
376
}
377
378
void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){
379
  Assert(!d_qeInSoi.empty());
380
  for(uint32_t i = begin; i != end; ++i){
381
    ArithVar v = d_qeConflict[i];
382
    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
383
    d_qeInSoi.add(v);
384
  }
385
}
386
387
void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){
388
  for(uint32_t i = begin; i != end; ++i){
389
    ArithVar v = d_qeConflict[i];
390
    removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v);
391
    d_qeInSoi.remove(v);
392
  }
393
  Assert(!d_qeInSoi.empty());
394
}
395
396
void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){
397
  for(uint32_t i = 0; i < N; ++i){
398
    std::swap(d_qeConflict[r+i], d_qeConflict[s+i]);
399
  }
400
}
401
402
/**
403
 * Region notation:
404
 * A region is either
405
 *  - A single element X@i with the name X at the position i
406
 *  - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive
407
 *  - A concatenation of regions R1 and R2, R1;R2
408
 *
409
 * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd)
410
 * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal
411
 * subset of U, Delta, s.t. C \cup Delta is in conflict.
412
 *
413
 * Pre:
414
 *  [0, uEnd) is a set and is in conflict.
415
 *    uEnd <= assumptions.size()
416
 *  [0, cEnd) is in d_inSoi.
417
 *
418
 * Invariants: [0,cEnd) is never modified
419
 *
420
 * Post:
421
 *  [0, cEnd); [cEnd, deltaEnd) is in conflict
422
 *  [0, deltaEnd) is a set
423
 *  [0, deltaEnd) is in d_inSoi
424
 */
425
uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){
426
  Assert(cEnd <= uEnd);
427
  Assert(d_qeInUAndNotInSoi.empty());
428
  Assert(d_qeGreedyOrder.empty());
429
430
  const Tableau::Entry* spoiler = NULL;
431
432
  if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){
433
    // already in conflict
434
    return cEnd;
435
  }
436
437
  Assert(cEnd < uEnd);
438
439
  // Phase 1 : Construct the conflict greedily
440
441
  for(uint32_t i = cEnd; i < uEnd; ++i){
442
    d_qeInUAndNotInSoi.add(d_qeConflict[i]);
443
  }
444
  if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty
445
    ArithVar first = d_qeConflict[cEnd];
446
    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first);
447
    d_qeInSoi.add(first);
448
    d_qeInUAndNotInSoi.remove(first);
449
    d_qeGreedyOrder.push_back(first);
450
  }
451
  while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
452
    Assert(!d_qeInUAndNotInSoi.empty());
453
454
    ArithVar nb = spoiler->getColVar();
455
    int oppositeSgn = -(spoiler->getCoefficient().sgn());
456
    Assert(oppositeSgn != 0);
457
458
    ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true);
459
    Assert(basicWithOp != ARITHVAR_SENTINEL);
460
461
    addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
462
    d_qeInSoi.add(basicWithOp);
463
    d_qeInUAndNotInSoi.remove(basicWithOp);
464
    d_qeGreedyOrder.push_back(basicWithOp);
465
  }
466
  Assert(spoiler == NULL);
467
468
  // Compact the set u
469
  uint32_t newEnd = cEnd + d_qeGreedyOrder.size();
470
  std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd);
471
472
  d_qeInUAndNotInSoi.purge();
473
  d_qeGreedyOrder.clear();
474
475
   // Phase 2 : Recursively determine the minimal set of rows
476
477
  uint32_t xPos = cEnd;
478
  std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]);
479
  uint32_t uBegin = xPos + 1;
480
  uint32_t split = (newEnd - uBegin)/2 + uBegin;
481
482
  //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd)
483
  // [0, newEnd) == d_inSoi
484
485
  uint32_t compactU2;
486
  if(split == newEnd){ // U2 is empty
487
    compactU2 = newEnd;
488
  }else{
489
    // Remove U2 from Soi
490
    qeRemoveRange(split, newEnd);
491
    // [0, split) == d_inSoi
492
493
    // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd)
494
    compactU2 = quickExplainRec(split, newEnd);
495
    // post:
496
    //  assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2)
497
    //  d_inSoi = [0, compactU2)
498
  }
499
  uint32_t deltaSize = compactU2 - split;
500
  qeSwapRange(deltaSize, uBegin, split);
501
  uint32_t d2End = uBegin+deltaSize;
502
  // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2)
503
  //  d_inSoi == [0, compactU2)
504
505
  uint32_t d1End;
506
  if(d2End == compactU2){ // U1 is empty
507
    d1End = d2End;
508
  }else{
509
    qeRemoveRange(d2End, compactU2);
510
511
    //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2);
512
    d1End = quickExplainRec(d2End, compactU2);
513
    //post:
514
    // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End);
515
    // d_inSoi = [0, d1End)
516
  }
517
  //After both:
518
  // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End);
519
520
  Assert(d_qeInUAndNotInSoi.empty());
521
  Assert(d_qeGreedyOrder.empty());
522
  return d1End;
523
}
524
525
void SumOfInfeasibilitiesSPD::quickExplain(){
526
  Assert(d_qeInSoi.empty());
527
  Assert(d_qeInUAndNotInSoi.empty());
528
  Assert(d_qeGreedyOrder.empty());
529
  Assert(d_soiVar == ARITHVAR_SENTINEL);
530
  Assert(d_qeSgns.empty());
531
532
  d_qeConflict.clear();
533
  d_errorSet.pushFocusInto(d_qeConflict);
534
535
  //cout <<  d_qeConflict.size() << " ";
536
  uint32_t size = d_qeConflict.size();
537
538
  if(size > 2){
539
    for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
540
      ArithVar e = *iter;
541
      addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e));
542
    }
543
    uint32_t end = quickExplainRec(0u, size);
544
    Assert(end <= d_qeConflict.size());
545
    Assert(d_soiVar != ARITHVAR_SENTINEL);
546
    Assert(!d_qeInSoi.empty());
547
548
    d_qeConflict.resize(end);
549
    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
550
    d_soiVar = ARITHVAR_SENTINEL;
551
    d_qeInSoi.purge();
552
    d_qeSgns.clear();
553
  }
554
555
  //cout << d_qeConflict.size() << endl;
556
557
  Assert(d_qeInSoi.empty());
558
  Assert(d_qeInUAndNotInSoi.empty());
559
  Assert(d_qeGreedyOrder.empty());
560
  Assert(d_soiVar == ARITHVAR_SENTINEL);
561
  Assert(d_qeSgns.empty());
562
}
563
564
unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){
565
  Assert(d_soiVar == ARITHVAR_SENTINEL);
566
  bool success = false;
567
  if(set.size() >= 2){
568
    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, set);
569
    success = d_linEq.selectSlackEntry(d_soiVar, false) == NULL;
570
571
    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
572
    d_soiVar = ARITHVAR_SENTINEL;
573
  }
574
  return success ? set.size() : std::numeric_limits<int>::max();
575
}
576
577
std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){
578
  Debug("arith::greedyConflictSubsets") << "greedyConflictSubsets start" << endl;
579
580
  std::vector< ArithVarVec > subsets;
581
  Assert(d_soiVar == ARITHVAR_SENTINEL);
582
583
  if(d_errorSize <= 2){
584
    ArithVarVec inError;
585
    d_errorSet.pushFocusInto(inError);
586
587
    Assert(debugIsASet(inError));
588
    subsets.push_back(inError);
589
    return subsets;
590
  }
591
  Assert(d_errorSize > 2);
592
593
  //sgns_table< <nonbasic,sgn>, [basics] >;
594
  // Phase 0: Construct the sgns table
595
  sgn_table sgns;
596
  DenseSet hasParticipated; //Has participated in a conflict
597
  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
598
    ArithVar e = *iter;
599
    addRowSgns(sgns, e, d_errorSet.getSgn(e));
600
601
    Debug("arith::greedyConflictSubsets") << "basic error var: " << e << endl;
602
    if(Debug.isOn("arith::greedyConflictSubsets")){
603
      d_tableau.debugPrintIsBasic(e);
604
      d_tableau.printBasicRow(e, Debug("arith::greedyConflictSubsets"));
605
    }
606
  }
607
608
  // Phase 1: Try to find at least 1 pair for every element
609
  ArithVarVec tmp;
610
  tmp.push_back(0);
611
  tmp.push_back(0);
612
  for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){
613
    ArithVar e = *iter;
614
    tmp[0] = e;
615
616
    int errSgn = d_errorSet.getSgn(e);
617
    bool decreasing = errSgn < 0;
618
    const Tableau::Entry* spoiler = d_linEq.selectSlackEntry(e, decreasing);
619
    Assert(spoiler != NULL);
620
    ArithVar nb = spoiler->getColVar();
621
    int oppositeSgn = -(errSgn * (spoiler->getCoefficient().sgn()));
622
623
    sgn_table::const_iterator opposites = find_sgns(sgns, nb, oppositeSgn);
624
    Assert(opposites != sgns.end());
625
626
    const ArithVarVec& choices = (*opposites).second;
627
    for(ArithVarVec::const_iterator j = choices.begin(), jend = choices.end(); j != jend; ++j){
628
      ArithVar b = *j;
629
      if(b < e){ continue; }
630
      tmp[0] = e;
631
      tmp[1] = b;
632
      if(trySet(tmp) == 2){
633
        Debug("arith::greedyConflictSubsets")  << "found a pair " << b << " " << e << endl;
634
        hasParticipated.softAdd(b);
635
        hasParticipated.softAdd(e);
636
        Assert(debugIsASet(tmp));
637
        subsets.push_back(tmp);
638
        ++(d_statistics.d_soiConflicts);
639
        ++(d_statistics.d_hasToBeMinimal);
640
      }
641
    }
642
  }
643
644
645
  // Phase 2: If there is a variable that has not participated attempt to start a conflict
646
  ArithVarVec possibleStarts; //List of elements that can be tried for starts.
647
  d_errorSet.pushFocusInto(possibleStarts);
648
  while(!possibleStarts.empty()){
649
    Assert(d_soiVar == ARITHVAR_SENTINEL);
650
651
    ArithVar v = possibleStarts.back();
652
    possibleStarts.pop_back();
653
    if(hasParticipated.isMember(v)){ continue; }
654
655
    hasParticipated.add(v);
656
657
    Assert(d_soiVar == ARITHVAR_SENTINEL);
658
    //d_soiVar's row =  \sumofinfeasibilites underConstruction
659
    ArithVarVec underConstruction;
660
    underConstruction.push_back(v);
661
    d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v);
662
663
    Debug("arith::greedyConflictSubsets") << "trying " << v << endl;
664
665
    const Tableau::Entry* spoiler = NULL;
666
    while( (spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){
667
      ArithVar nb = spoiler->getColVar();
668
      int oppositeSgn = -(spoiler->getCoefficient().sgn());
669
      Assert(oppositeSgn != 0);
670
671
      Debug("arith::greedyConflictSubsets") << "looking for " << nb << " " << oppositeSgn << endl;
672
673
      ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false);
674
675
      if(basicWithOp == ARITHVAR_SENTINEL){
676
        Debug("arith::greedyConflictSubsets") << "search did not work  for " << nb << endl;
677
        // greedy construction has failed
678
        break;
679
      }else{
680
        Debug("arith::greedyConflictSubsets") << "found  " << basicWithOp << endl;
681
682
        addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp);
683
        hasParticipated.softAdd(basicWithOp);
684
        underConstruction.push_back(basicWithOp);
685
      }
686
    }
687
    if(spoiler == NULL){
688
      Debug("arith::greedyConflictSubsets") << "success" << endl;
689
      //then underConstruction contains a conflicting subset
690
      Assert(debugIsASet(underConstruction));
691
      subsets.push_back(underConstruction);
692
      ++d_statistics.d_soiConflicts;
693
      if(underConstruction.size() == 3){
694
        ++d_statistics.d_hasToBeMinimal;
695
      }else{
696
        ++d_statistics.d_maybeNotMinimal;
697
      }
698
    }else{
699
      Debug("arith::greedyConflictSubsets") << "failure" << endl;
700
    }
701
    tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
702
    d_soiVar = ARITHVAR_SENTINEL;
703
    // if(false && spoiler == NULL){
704
    //   ArithVarVec tmp;
705
    //   int smallest = tryAllSubsets(underConstruction, 0, tmp);
706
    //   cout << underConstruction.size() << " " << smallest << endl;
707
    //   Assert(smallest >= underConstruction.size());
708
    //   if(smallest < underConstruction.size()){
709
    //     exit(-1);
710
    //   }
711
    // }
712
  }
713
714
  Assert(d_soiVar == ARITHVAR_SENTINEL);
715
  Debug("arith::greedyConflictSubsets") << "greedyConflictSubsets done" << endl;
716
  return subsets;
717
}
718
719
bool SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){
720
  Assert(d_soiVar == ARITHVAR_SENTINEL);
721
  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset);
722
  Assert(!subset.empty());
723
  Assert(!d_conflictBuilder->underConstruction());
724
725
  Debug("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) start" << endl;
726
727
  bool success = false;
728
729
  for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){
730
    ArithVar e = *iter;
731
    ConstraintP violated = d_errorSet.getViolated(e);
732
    Assert(violated != NullConstraint);
733
734
    int sgn = d_errorSet.getSgn(e);
735
    const Rational& violatedCoeff = sgn > 0 ? d_negOne : d_posOne;
736
    Debug("arith::generateSOIConflict") << "basic error var: "
737
                                        << "(" <<  violatedCoeff << ")"
738
                                        << " " << violated
739
                                        << endl;
740
741
742
    d_conflictBuilder->addConstraint(violated, violatedCoeff);
743
    Assert(violated->hasProof());
744
    if(!success && !violated->negationHasProof()){
745
      success = true;
746
      d_conflictBuilder->makeLastConsequent();
747
    }
748
  }
749
750
  if(!success){
751
    // failure
752
    d_conflictBuilder->reset();
753
  } else {
754
    // pick a violated constraint arbitrarily. any of them may be selected for the conflict
755
    Assert(d_conflictBuilder->underConstruction());
756
    Assert(d_conflictBuilder->consequentIsSet());
757
758
    for(Tableau::RowIterator i = d_tableau.basicRowIterator(d_soiVar); !i.atEnd(); ++i){
759
      const Tableau::Entry& entry = *i;
760
      ArithVar v = entry.getColVar();
761
      if(v == d_soiVar){ continue; }
762
      const Rational& coeff = entry.getCoefficient();
763
764
      ConstraintP c = (coeff.sgn() > 0) ?
765
        d_variables.getUpperBoundConstraint(v) :
766
        d_variables.getLowerBoundConstraint(v);
767
768
      Debug("arith::generateSOIConflict") << "non-basic var: "
769
                                          << "(" <<  coeff << ")"
770
                                          << " " << c
771
                                          << endl;
772
      d_conflictBuilder->addConstraint(c, coeff);
773
    }
774
    ConstraintCP conflicted = d_conflictBuilder->commitConflict();
775
    d_conflictChannel.raiseConflict(conflicted,
776
                                    InferenceId::ARITH_CONF_SOI_SIMPLEX);
777
  }
778
779
  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
780
  d_soiVar = ARITHVAR_SENTINEL;
781
  Debug("arith::generateSOIConflict") << "SumOfInfeasibilitiesSPD::generateSOIConflict(...) done" << endl;
782
  Assert(d_soiVar == ARITHVAR_SENTINEL);
783
  Assert(!d_conflictBuilder->underConstruction());
784
  return success;
785
}
786
787
788
WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){
789
  static int instance = 0;
790
  instance++;
791
792
  Debug("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() start "
793
                              << instance << ": |E| = " << d_errorSize << endl;
794
  if(Debug.isOn("arith::SOIConflict")){
795
    d_errorSet.debugPrint(cout);
796
  }
797
  Debug("arith::SOIConflict") << endl;
798
799
  tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar);
800
  d_soiVar = ARITHVAR_SENTINEL;
801
802
  if (options().arith.soiQuickExplain)
803
  {
804
    quickExplain();
805
    generateSOIConflict(d_qeConflict);
806
  }
807
  else
808
  {
809
    vector<ArithVarVec> subsets = greedyConflictSubsets();
810
    Assert(d_soiVar == ARITHVAR_SENTINEL);
811
    bool anySuccess = false;
812
    Assert(!subsets.empty());
813
    for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){
814
      const ArithVarVec& subset = *i;
815
      Assert(debugIsASet(subset));
816
      anySuccess = generateSOIConflict(subset) || anySuccess;
817
      //Node conflict = generateSOIConflict(subset);
818
      //cout << conflict << endl;
819
820
      //reportConflict(conf); do not do this. We need a custom explanations!
821
      //d_conflictChannel(conflict);
822
    }
823
    Assert(anySuccess);
824
  }
825
  Assert(d_soiVar == ARITHVAR_SENTINEL);
826
  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization);
827
828
  //reportConflict(conf); do not do this. We need a custom explanations!
829
  d_conflictVariables.add(d_soiVar);
830
831
  Debug("arith::SOIConflict") << "SumOfInfeasibilitiesSPD::SOIConflict() done "
832
                              << instance << "end" << endl;
833
  return ConflictFound;
834
}
835
836
WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() {
837
  Assert(d_soiVar != ARITHVAR_SENTINEL);
838
839
  bool useBlands = degeneratePivotsInARow() >= s_maxDegeneratePivotsBeforeBlandsOnLeaving;
840
  LinearEqualityModule::UpdatePreferenceFunction upf;
841
  if(useBlands) {
842
    upf = &LinearEqualityModule::preferWitness<false>;
843
  } else {
844
    upf = &LinearEqualityModule::preferWitness<true>;
845
  }
846
847
  LinearEqualityModule::VarPreferenceFunction bpf = useBlands ?
848
    &LinearEqualityModule::minVarOrder :
849
    &LinearEqualityModule::minRowLength;
850
  bpf = &LinearEqualityModule::minVarOrder;
851
852
  UpdateInfo selected = selectUpdate(upf, bpf);
853
854
  if(selected.uninitialized()){
855
    Debug("selectFocusImproving") << "SOI is optimum, but we don't have sat/conflict yet" << endl;
856
    return SOIConflict();
857
  }else{
858
    Assert(!selected.uninitialized());
859
    WitnessImprovement w = selected.getWitness(false);
860
    Assert(debugCheckWitness(selected, w, false));
861
862
    updateAndSignal(selected, w);
863
    logPivot(w);
864
    return w;
865
  }
866
}
867
868
bool SumOfInfeasibilitiesSPD::debugSOI(WitnessImprovement w, ostream& out, int instance) const{
869
  return true;
870
  // out << "DLV("<<instance<<") ";
871
  // switch(w){
872
  // case ConflictFound:
873
  //   out << "found conflict" << endl;
874
  //   return !d_conflictVariables.empty();
875
  // case ErrorDropped:
876
  //   return false;
877
  //   // out << "dropped " << prevErrorSize - d_errorSize << endl;
878
  //   // return d_errorSize < prevErrorSize;
879
  // case FocusImproved:
880
  //   out << "focus improved"<< endl;
881
  //   return d_errorSize == prevErrorSize;
882
  // case FocusShrank:
883
  //   Unreachable();
884
  //   return false;
885
  // case BlandsDegenerate:
886
  //   out << "bland degenerate"<< endl;
887
  //   return true;
888
  // case HeuristicDegenerate:
889
  //   out << "heuristic degenerate"<< endl;
890
  //   return true;
891
  // case AntiProductive:
892
  // case Degenerate:
893
  //   return false;
894
  // }
895
  // return false;
896
}
897
898
Result::Sat SumOfInfeasibilitiesSPD::sumOfInfeasibilities(){
899
  static int instance = 0;
900
901
  TimerStat::CodeTimer codeTimer(d_statistics.d_soiTimer);
902
903
  Assert(d_sgnDisagreements.empty());
904
  Assert(d_pivotBudget != 0);
905
  Assert(d_errorSize == d_errorSet.errorSize());
906
  Assert(d_errorSize > 0);
907
  Assert(d_conflictVariables.empty());
908
  Assert(d_soiVar == ARITHVAR_SENTINEL);
909
910
  //d_scores.purge();
911
  d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer);
912
913
914
  while(d_pivotBudget != 0  && d_errorSize > 0 && d_conflictVariables.empty()){
915
    ++instance;
916
    Debug("dualLike") << "dualLike " << instance << endl;
917
918
    Assert(d_errorSet.noSignals());
919
    // Possible outcomes:
920
    // - conflict
921
    // - budget was exhausted
922
    // - focus went down
923
    WitnessImprovement w = soiRound();
924
    Debug("dualLike") << "selectFocusImproving -> " << w << endl;
925
926
    Assert(d_errorSize == d_errorSet.errorSize());
927
928
    Assert(debugSOI(w, Debug("dualLike"), instance));
929
  }
930
931
932
  if(d_soiVar != ARITHVAR_SENTINEL){
933
    tearDownInfeasiblityFunction(d_statistics.d_soiFocusConstructionTimer, d_soiVar);
934
    d_soiVar = ARITHVAR_SENTINEL;
935
  }
936
937
  Assert(d_soiVar == ARITHVAR_SENTINEL);
938
  if(!d_conflictVariables.empty()){
939
    return Result::UNSAT;
940
  }else if(d_errorSet.errorEmpty()){
941
    Assert(d_errorSet.noSignals());
942
    return Result::SAT;
943
  }else{
944
    Assert(d_pivotBudget == 0);
945
    return Result::SAT_UNKNOWN;
946
  }
947
}
948
949
}  // namespace arith
950
}  // namespace theory
951
31137
}  // namespace cvc5