GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/soi_simplex.cpp Lines: 23 543 4.2 %
Date: 2021-09-18 Branches: 20 2388 0.8 %

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