summaryrefslogtreecommitdiff
path: root/src/theory/arith/simplex-converge.h
blob: dac3a9b49f0e6572ac2a63296469dbd768b81c99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
/*********************                                                        */
/*! \file simplex.h
 ** \verbatim
 ** Original author: taking
 ** Major contributors: none
 ** Minor contributors (to current version): none
 ** This file is part of the CVC4 prototype.
 ** Copyright (c) 2009, 2010, 2011  The Analysis of Computer Systems Group (ACSys)
 ** Courant Institute of Mathematical Sciences
 ** New York University
 ** See the file COPYING in the top-level source directory for licensing
 ** information.\endverbatim
 **
 ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure.
 **
 ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure.
 ** See the Simplex for DPLL(T) technical report for more background.(citation?)
 ** This shares with the theory a Tableau, and a PartialModel that:
 **  - satisfies the equalities in the Tableau, and
 **  - the assignment for the non-basic variables satisfies their bounds.
 ** This is required to either produce a conflict or satisifying PartialModel.
 ** Further, we require being told when a basic variable updates its value.
 **
 ** During the Simplex search we maintain a queue of variables.
 ** The queue is required to contain all of the basic variables that voilate their bounds.
 ** As elimination from the queue is more efficient to be done lazily,
 ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds.
 **
 ** The simplex procedure roughly follows Alberto's thesis. (citation?)
 ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.)
 ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.)
 ** After this, Bland's pivot rule is invoked.
 **
 ** During this proccess, we periodically inspect the queue of variables to
 ** 1) remove now extraneous extries,
 ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and
 ** 3) detect multiple conflicts.
 **
 ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict.
 **
 ** Extra things tracked atm: (Subject to change at Tim's whims)
 ** - A superset of all of the newly pivoted variables.
 ** - A queue of additional conflicts that were discovered by Simplex.
 **   These are theory valid and are currently turned into lemmas
 **/


#include "cvc4_private.h"

#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H
#define __CVC4__THEORY__ARITH__SIMPLEX_H

#include "theory/arith/arithvar.h"
#include "theory/arith/arith_priority_queue.h"
#include "theory/arith/delta_rational.h"
#include "theory/arith/matrix.h"
#include "theory/arith/partial_model.h"
#include "theory/arith/linear_equality.h"

#include "context/cdlist.h"

#include "util/dense_map.h"
#include "options/options.h"
#include "util/stats.h"
#include "util/result.h"

#include <queue>

namespace CVC4 {
namespace theory {
namespace arith {

class SimplexDecisionProcedure {
private:
  ArithVar d_conflictVariable;
  DenseSet d_successes;

  /** Linear equality module. */
  LinearEqualityModule& d_linEq;

  /**
   * Manages information about the assignment and upper and lower bounds on
   * variables.
   * Partial model matches that in LinearEqualityModule.
   */
  ArithPartialModel& d_partialModel;

  /**
   * Stores the linear equalities used by Simplex.
   * Tableau from the LinearEquality module.
   */
  Tableau& d_tableau;

  /** Contains a superset of the basic variables in violation of their bounds. */
  ArithPriorityQueue d_queue;

  /** Number of variables in the system. This is used for tuning heuristics. */
  ArithVar d_numVariables;

  /** This is the call back channel for Simplex to report conflicts. */
  NodeCallBack& d_conflictChannel;

  /** Maps a variable to how many times they have been used as a pivot in the simplex search. */
  DenseMultiset d_pivotsInRound;

  /** Stores to the DeltaRational constant 0. */
  DeltaRational d_DELTA_ZERO;

  //TODO make an option
  const static uint32_t MAX_ITERATIONS = 20;


  /** Used for requesting d_opt, bound and error variables for primal.*/
  ArithVarMalloc& d_arithVarMalloc;

  /** Used for constructing temporary variables, bound and error variables for primal.*/
  ConstraintDatabase& d_constraintDatabase;

public:
  SimplexDecisionProcedure(LinearEqualityModule& linEq,
			   NodeCallBack& conflictChannel,
			   ArithVarMalloc& variables,
			   ConstraintDatabase& constraintDatabase);

  /**
   * This must be called when the value of a basic variable may now voilate one
   * of its bounds.
   */
  void updateBasic(ArithVar x){
    d_queue.enqueueIfInconsistent(x);
  }

  /**
   * Tries to update the assignments of variables such that all of the
   * assignments are consistent with their bounds.
   * This is done by a simplex search through the possible bases of the tableau.
   *
   * If all of the variables can be made consistent with their bounds
   * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict
   * was reported on the conflictCallback passed to the Module.
   *
   * Tableau pivoting is performed so variables may switch from being basic to
   * nonbasic and vice versa.
   *
   * Corresponds to the "check()" procedure in [Cav06].
   */
  Result::Sat dualFindModel(bool exactResult);


  /**
   * Tries to update the assignments of the variables s.t. all of the assignments
   * are consistent with their bounds.
   *
   * This is a REALLY heavy hammer consider calling dualFindModel(false) first.
   *
   * !!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!!
   * This procedure needs to temporarily relax contraints to contruct a satisfiable system.
   * To do this, it is going to do a sat push.
   */
  Result::Sat primalFindModel();

private:
  

  /**
   * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure,
   * and 2 ArithVar variables and returns one of the ArithVar variables potentially
   * using the internals of the SimplexDecisionProcedure.
   *
   * Both ArithVar must be non-basic in d_tableau.
   */
  typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar);

  /**
   * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars.
   * This PreferenceFunction is used during the VarOrder stage of
   * findModel.
   */
  static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);

  /**
   * minRowCount is a PreferenceFunction for selecting the variable with the smaller
   * row count in the tableau.
   *
   * This is a heuristic rule and should not be used
   * during the VarOrder stage of findModel.
   */
  static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);
  static ArithVar minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);

  /**
   * minBoundAndRowCount is a PreferenceFunction for preferring a variable
   * without an asserted bound over variables with an asserted bound.
   * If both have bounds or both do not have bounds,
   * the rule falls back to minRowCount(...).
   *
   * This is a heuristic rule and should not be used
   * during the VarOrder stage of findModel.
   */
  static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y);


  /**
   * This is the main simplex for DPLL(T) loop.
   * It runs for at most maxIterations.
   *
   * Returns true iff it has found a conflict.
   * d_conflictVariable will be set and the conflict for this row is reported.
   */
  bool searchForFeasibleSolution(uint32_t maxIterations);

  enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch};

  bool findConflictOnTheQueue(SearchPeriod period);


  /**
   * Given the basic variable x_i,
   * this function finds the smallest nonbasic variable x_j in the row of x_i
   * in the tableau that can "take up the slack" to let x_i satisfy its bounds.
   * This returns ARITHVAR_SENTINEL if none exists.
   *
   * More formally one of the following conditions must be satisfied:
   * -  lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j)
   * -  lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j)
   * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j)
   * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j)
   *
   */
  template <bool lowerBound>  ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf);
  ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
    return selectSlack<true>(x_i, pf);
  }
  ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) {
    return selectSlack<false>(x_i, pf);
  }
  /**
   * Returns the smallest basic variable whose assignment is not consistent
   * with its upper and lower bounds.
   */
  ArithVar selectSmallestInconsistentVar();

  /**
   * Given a non-basic variable that is know to not be updatable
   * to a consistent value, construct and return a conflict.
   * Follows section 4.2 in the CAV06 paper.
   */
  Node generateConflictAboveUpperBound(ArithVar conflictVar);
  Node generateConflictBelowLowerBound(ArithVar conflictVar);

public:
  void increaseMax() {d_numVariables++;}


  void clearQueue() {
    d_queue.clear();
  }


  bool debugIsInCollectionQueue(ArithVar var) const{
    Assert(d_queue.inCollectionMode());
    return d_queue.collectionModeContains(var);
  }

  void reduceQueue(){
    d_queue.reduce();
  }

  ArithPriorityQueue::const_iterator queueBegin() const{
    return d_queue.begin();
  }

  ArithPriorityQueue::const_iterator queueEnd() const{
    return d_queue.end();
  }

private:

  /** Reports a conflict to on the output channel. */
  void reportConflict(Node conflict){
    d_conflictChannel(conflict);
    ++(d_statistics.d_simplexConflicts);
  }

  template <bool above>
  inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){
    return
      ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
      ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) ||
      (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) ||
      (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic));
  }

  /**
   * Checks a basic variable, b, to see if it is in conflict.
   * If a conflict is discovered a node summarizing the conflict is returned.
   * Otherwise, Node::null() is returned.
   */
  Node checkBasicForConflict(ArithVar b);

  Node weakenConflict(bool aboveUpper, ArithVar basicVar);
  Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic);

  /** Gets a fresh variable from TheoryArith. */
  ArithVar requestVariable(){
    return d_arithVarMalloc.request();
  }

  /** Releases a requested variable from TheoryArith.*/
  void releaseVariable(ArithVar v){
    d_arithVarMalloc.release(v);
  }


  /** An error info keeps the information associated with the construction of an ErrorVariable. */
  class ErrorInfo {
  private:
    /** The variable for which the error variable was constructed.*/
    ArithVar d_variable;

    // If false -> lowerbound
    bool d_upperbound;
    
    /** The violated constraint this was constructed to try to satisfy.*/
    Constraint d_violated;
    
  public:
    ErrorInfo(ArithVar error, bool ub, const Constraint original)
      : d_variable(error), d_upperbound(ub), d_violated(original) {}

    ErrorInfo() :
    d_variable(ARITHVAR_SENTINEL), d_upperbound(false), d_violated(NullConstraint){}

    inline ArithVar getVariable() const {
      return d_variable;
    }

    inline bool isUpperbound() const {
      return d_upperbound;
    }

    inline bool errorIsLeqZero(const ArithPartialModel& d_pm) const{
      return isUpperbound() ?
	(d_pm.getAssignment(d_variable) <= d_violated->getValue()) :
	(d_pm.getAssignment(d_variable) >= d_violated->getValue());
    }

    inline Constraint getConstraint() const {
      return d_violated;
    }

    inline DeltaRational getErrorAmount(const ArithPartialModel& d_pm) const {
      return isUpperbound() ?
	(d_pm.getAssignment(d_variable) - d_violated->getValue()) :
	(d_violated->getValue() - d_pm.getAssignment(d_variable));
    }
  };

  typedef DenseMap<ErrorInfo> ErrorMap;

  /** A map from the error variables to the associated ErrorInfo.*/
  ErrorMap d_currentErrorVariables;

  /** The optimization function is implicitly defined as
   * f_i  = d_optRow - d_negOptConstant
   *
   * d_optRow is a basic variable in the tableau.
   * The tableau maintains that it is equal to the sum of -1^{!ub} * the error variables in
   * d_currentErrorVariables.
   *
   * d_negOptConstant is explicitly the negation of the sum of the bounds that were violated
   *
   * assignment(f_i) <= 0 iff assignment(d_optRow) <= d_negOptConstant
   */
  /** The variable for the variable part of the optimization function.*/
  ArithVar d_optRow; 

  /** The constant part of the optimization function.*/
  DeltaRational d_negOptConstant;

  inline bool belowThreshold() const {
    return d_partialModel.getAssignment(d_optRow) <= d_negOptConstant;
  }

  /**
   * A PrimalResponse represents the state that the primal simplex solver is in.
   */
  enum PrimalResponse {
    // The optimization can decrease arbitrarily on some variable in the function
    FoundUnboundedVariable,

    // The optimization function has reached a threshold value and is checking back in
    ReachedThresholdValue,

    // Simplex has used up its pivot bound and is checking back in with its caller
    UsedMaxPivots,

    //Simplex can make progress on the pair of entering and leaving variables
    MakeProgressOnLeaving,

    //Simplex is not at a minimum but no leaving variable can be changed to help
    NoProgressOnLeaving,

    // Simplex has reached a minimum for its optimization function
    GlobalMinimum
  };

  /**
   * These fields are for sticking information for passing information back with the PrimalRespones.
   * These fields should be ignored as unsafe/unknown unless you have a PrimalResponse that states
   * the field makes sense.
   */
  struct PrimalPassBack {
  public:
    /**
     * A variable s.t. its value can be increased/decreased arbitrarily to change the optimization function
     * arbitrarily low.
     */
    ArithVar d_unbounded;
    
    /** The leaving variable selection for primal simplex. */
    ArithVar d_leaving;

    /** The entering variable selection for primal simplex. */
    ArithVar d_entering;

    /** The new value for the leaving variable value for primal simplex.*/
    DeltaRational d_nextEnteringValue;

    PrimalPassBack() { clear(); }
    void clear(){
      d_unbounded = (d_leaving = (d_entering = ARITHVAR_SENTINEL));
      d_nextEnteringValue = DeltaRational();
    }

    bool isClear() const {
      return d_unbounded == ARITHVAR_SENTINEL &&
	d_leaving == ARITHVAR_SENTINEL &&
	d_entering == ARITHVAR_SENTINEL &&
	d_nextEnteringValue.sgn() == 0;
    }
  } d_primalCarry;

  uint32_t d_pivotsSinceErrorProgress;
  uint32_t d_pivotsSinceOptProgress;
  uint32_t d_pivotsSinceLastCheck;

  typedef std::vector< const Tableau::Entry* > EntryVector;
  EntryVector d_improvementCandidates;

  PrimalResponse primal(uint32_t maxIterations);
  PrimalResponse primalCheck();
  Result::Sat primalConverge(int depth);
  void driveOptToZero(ArithVar unbounded);
  uint32_t contractErrorVariables(bool guaranteedSuccess);
  bool checkForRowConflicts();
  void restoreErrorVariables(ErrorMap& es);
  void constructErrorVariables();
  void constructOptimizationFunction();
  void removeOptimizationFunction();
  void reconstructOptimizationFunction();
  ArithVar selectMinimumValid(ArithVar v, bool increasing);
  ArithVar selectFirstValid(ArithVar v, bool increasing);

  void reassertErrorConstraint(ArithVar e, ErrorMap& es, bool definitelyTrue, bool definitelyFalse);
  void computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift);

  /** These fields are designed to be accessible to TheoryArith methods. */
  class Statistics {
  public:
    IntStat d_statUpdateConflicts;

    TimerStat d_findConflictOnTheQueueTime;

    IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch;
    IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch;
    IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch;
    IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch;
    IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch;

    IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings;
    TimerStat d_weakenTime;


    IntStat d_simplexConflicts;

    // Primal stuffs
    TimerStat d_primalTimer;
    TimerStat d_internalTimer;

    IntStat d_primalCalls;
    IntStat d_primalSatCalls;
    IntStat d_primalUnsatCalls;

    IntStat d_primalPivots;
    IntStat d_primalImprovingPivots;

    IntStat d_primalThresholdReachedPivot;
    IntStat d_primalThresholdReachedPivot_dropped;
    
    IntStat d_primalReachedMaxPivots;
    IntStat d_primalReachedMaxPivots_contractMadeProgress;
    IntStat d_primalReachedMaxPivots_checkForConflictWorked;

    
    IntStat d_primalGlobalMinimum;
    IntStat d_primalGlobalMinimum_rowConflictWorked;
    IntStat d_primalGlobalMinimum_firstHalfWasSat;
    IntStat d_primalGlobalMinimum_firstHalfWasUnsat;
    IntStat d_primalGlobalMinimum_contractMadeProgress;


    IntStat d_unboundedFound;
    IntStat d_unboundedFound_drive;
    IntStat d_unboundedFound_dropped;


    Statistics();
    ~Statistics();
  };

  Statistics d_statistics;

};/* class SimplexDecisionProcedure */

}/* CVC4::theory::arith namespace */
}/* CVC4::theory namespace */
}/* CVC4 namespace */

#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */

generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback