GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/approx_simplex.cpp Lines: 4 145 2.8 %
Date: 2021-05-22 Branches: 2 427 0.5 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Aina Niemetz, Mathias Preiner
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * [[ Add one-line brief description here ]]
14
 *
15
 * [[ Add lengthier description here ]]
16
 * \todo document this file
17
 */
18
#include "theory/arith/approx_simplex.h"
19
20
#include <math.h>
21
22
#include <cfloat>
23
#include <cmath>
24
#include <unordered_set>
25
26
#include "base/cvc5config.h"
27
#include "base/output.h"
28
#include "smt/smt_statistics_registry.h"
29
#include "theory/arith/constraint.h"
30
#include "theory/arith/cut_log.h"
31
#include "theory/arith/matrix.h"
32
#include "theory/arith/normal_form.h"
33
#include "theory/eager_proof_generator.h"
34
35
#ifdef CVC5_USE_GLPK
36
#include "theory/arith/partial_model.h"
37
#endif
38
39
using namespace std;
40
41
namespace cvc5 {
42
namespace theory {
43
namespace arith {
44
45
struct AuxInfo {
46
  TreeLog* tl;
47
  int pivotLimit;
48
  int branchLimit;
49
  int branchDepth;
50
  MipResult term; /* terminatation */
51
};
52
53
enum SlackReplace { SlackUndef=0, SlackLB, SlackUB, SlackVLB, SlackVUB };
54
55
std::ostream& operator<<(std::ostream& out, MipResult res){
56
  switch(res){
57
  case MipUnknown:
58
    out << "MipUnknown"; break;
59
  case MipBingo:
60
    out << "MipBingo"; break;
61
  case MipClosed:
62
    out << "MipClosed"; break;
63
  case BranchesExhausted:
64
    out << "BranchesExhausted"; break;
65
  case PivotsExhauasted:
66
    out << "PivotsExhauasted"; break;
67
  case ExecExhausted:
68
    out << "ExecExhausted"; break;
69
  default:
70
    out << "Unexpected Mip Value!"; break;
71
  }
72
  return out;
73
}
74
struct VirtualBound {
75
  // Either x <= d * y or x >= d * y
76
  ArithVar x; // variable being bounded
77
  Kind k; // either LEQ or GEQ
78
  Rational d; // the multiple on y
79
  ArithVar y; // the variable that is the upper bound
80
  ConstraintP c; // the original constraint relating x and y
81
82
  VirtualBound()
83
    : x(ARITHVAR_SENTINEL)
84
    , k(kind::UNDEFINED_KIND)
85
    , d()
86
    , y(ARITHVAR_SENTINEL)
87
    , c(NullConstraint)
88
  {}
89
  VirtualBound(ArithVar toBound, Kind rel, const Rational& coeff, ArithVar bounding, ConstraintP orig)
90
    : x(toBound)
91
    , k(rel)
92
    , d(coeff)
93
    , y(bounding)
94
    , c(orig)
95
  {
96
    Assert(k == kind::LEQ || k == kind::GEQ);
97
  }
98
};
99
100
struct CutScratchPad {
101
  bool d_failure; // if the construction was unsuccessful
102
103
  /* GOMORY CUTS Datastructures */
104
  ArithVar d_basic; // a variable that is basic in the approximate solver
105
  DenseVector d_tabRow;           // a row in the tableau not including d_basic, equal to 0
106
  DenseMap<ConstraintP> d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound
107
108
  /* MIR CUTS Datastructures */
109
  DenseMap<SlackReplace> d_slacks;// The x'[i] selected for x[i]
110
  DenseMap<VirtualBound> d_vub;   // Virtual upper bounds.
111
  DenseMap<VirtualBound> d_vlb;   // Virtual lower bounds.
112
  DenseMap<Rational> d_compRanges;
113
114
  // a sum of rows in the tableau, with possible replacements for fixed
115
  // sum aggLhs[i] x[i] = aggRhs;
116
  DenseVector d_agg;
117
  // Takes agg and replaces x[i] with a slack variable x'[i]
118
  // Takes agg and replaces x[i] with a slack variable x'[i]
119
  // sum modLhs[i] x'[i] = modRhs;
120
  DenseVector d_mod;
121
122
  // Takes mod, and performs c-Mir on it
123
  // sum alpha[i] x'[i] <= beta
124
  DenseVector d_alpha;
125
126
  /* The constructed cut */
127
  // sum cut[i] x[i] <= cutRhs
128
  DenseVector d_cut;
129
  Kind d_cutKind;
130
131
  /* The constraints used throughout construction. */
132
  std::set<ConstraintP> d_explanation; // use pointer equality
133
  CutScratchPad(){
134
    clear();
135
  }
136
  void clear(){
137
    d_failure = false;
138
    d_basic = ARITHVAR_SENTINEL;
139
    d_tabRow.purge();
140
    d_toBound.purge();
141
142
    d_slacks.purge();
143
    d_vub.purge();
144
    d_vlb.purge();
145
    d_compRanges.purge();
146
147
    d_agg.purge();
148
    d_mod.purge();
149
    d_alpha.purge();
150
151
    d_cut.purge();
152
    d_cutKind = kind::UNDEFINED_KIND;
153
    d_explanation.clear();
154
  }
155
};
156
157
ApproximateStatistics::ApproximateStatistics()
158
    : d_branchMaxDepth(
159
        smtStatisticsRegistry().registerInt("z::approx::branchMaxDepth")),
160
      d_branchesMaxOnAVar(
161
          smtStatisticsRegistry().registerInt("z::approx::branchesMaxOnAVar")),
162
      d_gaussianElimConstructTime(smtStatisticsRegistry().registerTimer(
163
          "z::approx::gaussianElimConstruct::time")),
164
      d_gaussianElimConstruct(smtStatisticsRegistry().registerInt(
165
          "z::approx::gaussianElimConstruct::calls")),
166
      d_averageGuesses(
167
          smtStatisticsRegistry().registerAverage("z::approx::averageGuesses"))
168
{
169
}
170
171
9397
Integer ApproximateSimplex::s_defaultMaxDenom(1<<26);
172
173
ApproximateSimplex::ApproximateSimplex(const ArithVariables& v, TreeLog& l,
174
                                       ApproximateStatistics& s)
175
  : d_vars(v)
176
  , d_log(l)
177
  , d_stats(s)
178
  , d_pivotLimit(std::numeric_limits<int>::max())
179
  , d_branchLimit(std::numeric_limits<int>::max())
180
  , d_maxDepth(std::numeric_limits<int>::max())
181
{}
182
183
void ApproximateSimplex::setPivotLimit(int pl){
184
  Assert(pl >= 0);
185
  d_pivotLimit = pl;
186
}
187
188
void ApproximateSimplex::setBranchingDepth(int bd){
189
  Assert(bd >= 0);
190
  d_maxDepth = bd;
191
}
192
193
void ApproximateSimplex::setBranchOnVariableLimit(int bl){
194
  Assert(bl >= 0);
195
  d_branchLimit = bl;
196
}
197
198
const double ApproximateSimplex::SMALL_FIXED_DELTA = .000000001;
199
const double ApproximateSimplex::TOLERENCE = 1 + .000000001;
200
201
bool ApproximateSimplex::roughlyEqual(double a, double b){
202
  if (a == 0){
203
    return -SMALL_FIXED_DELTA <= b && b <= SMALL_FIXED_DELTA;
204
  }else if (b == 0){
205
    return -SMALL_FIXED_DELTA <= a && a <= SMALL_FIXED_DELTA;
206
  }else{
207
    return std::abs(b/a) <= TOLERENCE && std::abs(a/b) <= TOLERENCE;
208
  }
209
}
210
211
Rational ApproximateSimplex::cfeToRational(const vector<Integer>& exp){
212
  if(exp.empty()){
213
    return Rational(0);
214
  }else{
215
    Rational result = exp.back();
216
    vector<Integer>::const_reverse_iterator exp_iter = exp.rbegin();
217
    vector<Integer>::const_reverse_iterator exp_end = exp.rend();
218
    ++exp_iter;
219
    while(exp_iter != exp_end){
220
      result = result.inverse();
221
      const Integer& i = *exp_iter;
222
      result += i;
223
      ++exp_iter;
224
    }
225
    return result;
226
  }
227
}
228
std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int depth){
229
  vector<Integer> mods;
230
  if(!q.isZero()){
231
    Rational carry = q;
232
    for(int i = 0; i <= depth; ++i){
233
      Assert(!carry.isZero());
234
      mods.push_back(Integer());
235
      Integer& back = mods.back();
236
      back = carry.floor();
237
      Debug("rationalToCfe") << "  cfe["<<i<<"]: " << back << endl;
238
      carry -= back;
239
      if(carry.isZero()){
240
        break;
241
      }else if(ApproximateSimplex::roughlyEqual(carry.getDouble(), 0.0)){
242
        break;
243
      }else{
244
        carry = carry.inverse();
245
      }
246
    }
247
  }
248
  return mods;
249
}
250
251
252
Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){
253
  Debug("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <<endl;
254
  // references
255
  // page 4: http://carlossicoli.free.fr/C/Cassels_J.W.S.-An_introduction_to_diophantine_approximation-University_Press(1965).pdf
256
  // http://en.wikipedia.org/wiki/Continued_fraction
257
  Assert(K >= Integer(1));
258
  if( r.getDenominator() <= K ){
259
    return r;
260
  }
261
262
  // current numerator and denominator that has not been resolved in the cfe
263
  Integer num = r.getNumerator(), den = r.getDenominator();
264
  Integer quot,rem;
265
266
  unsigned t = 0;
267
  // For a sequence of candidate solutions q_t/p_t
268
  // we keep only 3 time steps: 0[prev], 1[current], 2[next]
269
  // timesteps with a fake timestep 0 (p is 0 and q is 1)
270
  // at timestep 1
271
  Integer p[3]; // h
272
  Integer q[3]; // k
273
  // load the first 3 time steps manually
274
  p[0] =    0; q[0] = 1; // timestep -2
275
  p[1] =    1; q[1] = 0; // timestep -1
276
277
  Integer::floorQR(quot, rem, num, den);
278
  num = den; den = rem;
279
280
  q[2] = q[0] + quot*q[1];
281
  p[2] = p[0] + quot*p[1];
282
  Debug("estimateWithCFE") <<  "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
283
  while( q[2] <= K ){
284
    p[0] = p[1]; p[1] = p[2];
285
    q[0] = q[1]; q[1] = q[2];
286
287
288
    Integer::floorQR(quot, rem, num, den);
289
    num = den; den = rem;
290
291
    p[2] = p[0]+quot*p[1];
292
    q[2] = q[0]+quot*q[1];
293
    ++t;
294
    Debug("estimateWithCFE") << "  cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl;
295
  }
296
297
  Integer k = (K-q[0]).floorDivideQuotient(q[1]);
298
  Rational cand_prev(p[0]+k*p[1], q[0]+k*q[1]);
299
  Rational cand_curr(p[1], q[1]);
300
  Rational dist_prev = (cand_prev - r).abs();
301
  Rational dist_curr = (cand_curr - r).abs();
302
  if(dist_prev <= dist_curr){
303
    Debug("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl;
304
    return cand_prev;
305
  }else{
306
    Debug("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl;
307
    return cand_curr;
308
  }
309
}
310
311
Maybe<Rational> ApproximateSimplex::estimateWithCFE(double d, const Integer& D)
312
{
313
  if (Maybe<Rational> from_double = Rational::fromDouble(d))
314
  {
315
    return estimateWithCFE(from_double.value(), D);
316
  }
317
  return Maybe<Rational>();
318
}
319
320
Maybe<Rational> ApproximateSimplex::estimateWithCFE(double d)
321
{
322
  return estimateWithCFE(d, s_defaultMaxDenom);
323
}
324
325
class ApproxNoOp : public ApproximateSimplex {
326
public:
327
  ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s)
328
  : ApproximateSimplex(v,l,s)
329
  {}
330
  ~ApproxNoOp(){}
331
332
  LinResult solveRelaxation() override { return LinUnknown; }
333
  Solution extractRelaxation() const override { return Solution(); }
334
335
  ArithRatPairVec heuristicOptCoeffs() const override
336
  {
337
    return ArithRatPairVec();
338
  }
339
340
  MipResult solveMIP(bool al) override { return MipUnknown; }
341
  Solution extractMIP() const override { return Solution(); }
342
343
  void setOptCoeffs(const ArithRatPairVec& ref) override {}
344
345
  void tryCut(int nid, CutInfo& cut) override {}
346
347
  std::vector<const CutInfo*> getValidCuts(const NodeLog& node) override
348
  {
349
    return std::vector<const CutInfo*>();
350
  }
351
352
  ArithVar getBranchVar(const NodeLog& nl) const override
353
  {
354
    return ARITHVAR_SENTINEL;
355
  }
356
357
  double sumInfeasibilities(bool mip) const override { return 0.0; }
358
};
359
360
}  // namespace arith
361
}  // namespace theory
362
}  // namespace cvc5
363
364
/* Begin the declaration of GLPK specific code. */
365
#ifdef CVC5_USE_GLPK
366
extern "C" {
367
#include <glpk.h>
368
}/* extern "C" */
369
370
namespace cvc5 {
371
namespace theory {
372
namespace arith {
373
374
Kind glpk_type_to_kind(int glpk_cut_type){
375
  switch(glpk_cut_type){
376
  case GLP_LO: return kind::GEQ;
377
  case GLP_UP: return kind::LEQ;
378
  case GLP_FX: return kind::EQUAL;
379
  case GLP_DB:
380
  case GLP_FR:
381
  default:     return kind::UNDEFINED_KIND;
382
  }
383
}
384
385
class GmiInfo;
386
class MirInfo;
387
class BranchCutInfo;
388
389
class ApproxGLPK : public ApproximateSimplex {
390
private:
391
  glp_prob* d_inputProb; /* a copy of the input prob */
392
  glp_prob* d_realProb;  /* a copy of the real relaxation output */
393
  glp_prob* d_mipProb;   /* a copy of the integer prob */
394
395
  DenseMap<int> d_colIndices;
396
  DenseMap<int> d_rowIndices;
397
398
  NodeLog::RowIdMap d_rootRowIds;
399
  //DenseMap<ArithVar> d_rowToArithVar;
400
  DenseMap<ArithVar> d_colToArithVar;
401
402
  int d_instanceID;
403
404
  bool d_solvedRelaxation;
405
  bool d_solvedMIP;
406
407
  static int s_verbosity;
408
409
  CutScratchPad d_pad;
410
411
  std::vector<Integer> d_denomGuesses;
412
413
public:
414
  ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s);
415
  ~ApproxGLPK();
416
417
  LinResult solveRelaxation() override;
418
  Solution extractRelaxation() const override { return extractSolution(false); }
419
420
  ArithRatPairVec heuristicOptCoeffs() const override;
421
422
  MipResult solveMIP(bool al) override;
423
  Solution extractMIP() const override { return extractSolution(true); }
424
  void setOptCoeffs(const ArithRatPairVec& ref) override;
425
  std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) override;
426
  ArithVar getBranchVar(const NodeLog& con) const override;
427
428
  static void printGLPKStatus(int status, std::ostream& out);
429
430
431
private:
432
 Solution extractSolution(bool mip) const;
433
 int guessDir(ArithVar v) const;
434
435
 // get this stuff out of here
436
 void tryCut(int nid, CutInfo& cut) override;
437
438
 ArithVar _getArithVar(int nid, int M, int ind) const;
439
 ArithVar getArithVarFromRow(int nid, int ind) const
440
 {
441
   if (ind >= 0)
442
   {
443
     const NodeLog& nl = d_log.getNode(nid);
444
     return nl.lookupRowId(ind);
445
   }
446
   return ARITHVAR_SENTINEL;
447
  }
448
449
  // virtual void mapRowId(int nid, int ind, ArithVar v){
450
  //   NodeLog& nl = d_log.getNode(nid);
451
  //   nl.mapRowId(ind, v);
452
  // }
453
  // virtual void applyRowsDeleted(int nid, const RowsDeleted& rd){
454
  //   NodeLog& nl = d_log.getNode(nid);
455
  //   nl.applyRowsDeleted(rd);
456
  // }
457
458
  ArithVar getArithVarFromStructural(int ind) const{
459
    if(ind >= 0){
460
      unsigned u = (unsigned) ind;
461
      if(d_colToArithVar.isKey(u)){
462
        return d_colToArithVar[u];
463
      }
464
    }
465
    return ARITHVAR_SENTINEL;
466
  }
467
468
  /**
469
   * Attempts to make the row vector vec on the pad.
470
   * If this is not in the row span of the original tableau this
471
   * raises the failure flag.
472
   */
473
  bool attemptConstructTableRow(int node, int M, const PrimitiveVec& vec);
474
  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec);
475
  bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec, const Integer& D);
476
  bool gaussianElimConstructTableRow(int node, int M, const PrimitiveVec& vec);
477
478
  /* This is a guess of a vector in the row span of the tableau.
479
   * Attempt to cancel out all of the variables.
480
   * returns true if this is constructable.
481
   */
482
  bool guessIsConstructable(const DenseMap<Rational>& guess) const;
483
484
  /**
485
   * Loads a vector of statuses into a dense map over bounds.
486
   * returns true on failure.
487
   */
488
  bool loadToBound(int node, int M, int len, int* inds, int* statuses,
489
                   DenseMap<ConstraintP>& toBound) const;
490
491
  /** checks the cut on the pad for whether it is sufficiently similar to cut. */
492
  bool checkCutOnPad(int nid, const CutInfo& cut) const;
493
494
495
  /** turns the pad into a node and creates an explanation. */
496
  //std::pair<Node, Node> makeCutNodes(int nid, const CutInfo& cut) const;
497
498
  // true means failure!
499
  // BRANCH CUTS
500
  bool attemptBranchCut(int nid, const BranchCutInfo& br);
501
502
  // GOMORY CUTS
503
  bool attemptGmi(int nid, const GmiInfo& gmi);
504
  /** tries to turn the information on the pad into a cut. */
505
  bool constructGmiCut();
506
507
  // MIR CUTS
508
  bool attemptMir(int nid, const MirInfo& mir);
509
  bool applyCMIRRule(int nid, const MirInfo& mir);
510
  bool makeRangeForComplemented(int nid, const MirInfo& mir);
511
  bool loadSlacksIntoPad(int nid, const MirInfo& mir);
512
  bool loadVirtualBoundsIntoPad(int nid, const MirInfo& mir);
513
  bool loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& mir);
514
  bool buildModifiedRow(int nid, const MirInfo& mir);
515
  bool constructMixedKnapsack();
516
  bool replaceSlacksOnCuts();
517
  bool loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp);
518
519
  double sumInfeasibilities(bool mip) const override
520
  {
521
    return sumInfeasibilities(mip? d_mipProb : d_realProb);
522
  }
523
  double sumInfeasibilities(glp_prob* prob, bool mip) const;
524
};
525
526
int ApproxGLPK::s_verbosity = 0;
527
528
}  // namespace arith
529
}  // namespace theory
530
}  // namespace cvc5
531
#endif /*#ifdef CVC5_USE_GLPK */
532
/* End the declaration of GLPK specific code. */
533
534
/* Begin GPLK/NOGLPK Glue code. */
535
namespace cvc5 {
536
namespace theory {
537
namespace arith {
538
ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){
539
#ifdef CVC5_USE_GLPK
540
  return new ApproxGLPK(vars, l, s);
541
#else
542
  return new ApproxNoOp(vars, l, s);
543
#endif
544
}
545
1267778
bool ApproximateSimplex::enabled() {
546
#ifdef CVC5_USE_GLPK
547
  return true;
548
#else
549
1267778
  return false;
550
#endif
551
}
552
}  // namespace arith
553
}  // namespace theory
554
28191
}  // namespace cvc5
555
/* End GPLK/NOGLPK Glue code. */
556
557
/* Begin GPLK implementation. */
558
#ifdef CVC5_USE_GLPK
559
namespace cvc5 {
560
namespace theory {
561
namespace arith {
562
563
#ifdef CVC5_ASSERTIONS
564
static CutInfoKlass fromGlpkClass(int klass){
565
  switch(klass){
566
  case GLP_RF_GMI: return GmiCutKlass;
567
  case GLP_RF_MIR: return MirCutKlass;
568
  case GLP_RF_COV:
569
  case GLP_RF_CLQ:
570
  default:         return UnknownKlass;
571
  }
572
}
573
#endif
574
575
ApproxGLPK::ApproxGLPK(const ArithVariables& var,
576
                       TreeLog& l,
577
                       ApproximateStatistics& s)
578
    : ApproximateSimplex(var, l, s),
579
      d_inputProb(nullptr),
580
      d_realProb(nullptr),
581
      d_mipProb(nullptr),
582
      d_solvedRelaxation(false),
583
      d_solvedMIP(false)
584
{
585
  static int instance = 0;
586
  ++instance;
587
  d_instanceID = instance;
588
589
  d_denomGuesses.push_back(Integer(1<<22));
590
  d_denomGuesses.push_back(ApproximateSimplex::s_defaultMaxDenom);
591
  d_denomGuesses.push_back(Integer(1ul<<29));
592
  d_denomGuesses.push_back(Integer(1ul<<31));
593
594
  d_inputProb = glp_create_prob();
595
  d_realProb = glp_create_prob();
596
  d_mipProb = glp_create_prob();
597
  glp_set_obj_dir(d_inputProb, GLP_MAX);
598
  glp_set_prob_name(d_inputProb, "ApproximateSimplex::approximateFindModel");
599
600
  int numRows = 0;
601
  int numCols = 0;
602
603
  // Assign each variable to a row and column variable as it appears in the input
604
  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
605
    ArithVar v = *vi;
606
607
    if(d_vars.isAuxiliary(v)){
608
      ++numRows;
609
      d_rowIndices.set(v, numRows);
610
      //mapRowId(d_log.getRootId(), numRows, v);
611
      d_rootRowIds.insert(make_pair(numRows, v));
612
      //d_rowToArithVar.set(numRows, v);
613
      Debug("approx") << "Row vars: " << v << "<->" << numRows << endl;
614
    }else{
615
      ++numCols;
616
      d_colIndices.set(v, numCols);
617
      d_colToArithVar.set(numCols, v);
618
      Debug("approx") << "Col vars: " << v << "<->" << numCols << endl;
619
    }
620
  }
621
  Assert(numRows > 0);
622
  Assert(numCols > 0);
623
624
  glp_add_rows(d_inputProb, numRows);
625
  glp_add_cols(d_inputProb, numCols);
626
627
  // Assign the upper/lower bounds and types to each variable
628
  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
629
    ArithVar v = *vi;
630
631
    if (s_verbosity >= 2)
632
    {
633
      // CVC5Message() << v  << " ";
634
      // d_vars.printModel(v, CVC5Message());
635
    }
636
637
    int type;
638
    double lb = 0.0;
639
    double ub = 0.0;
640
    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
641
      if(d_vars.boundsAreEqual(v)){
642
        type = GLP_FX;
643
      }else{
644
        type = GLP_DB;
645
      }
646
      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
647
      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
648
    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
649
      type = GLP_UP;
650
      ub = d_vars.getUpperBound(v).approx(SMALL_FIXED_DELTA);
651
    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
652
      type = GLP_LO;
653
      lb = d_vars.getLowerBound(v).approx(SMALL_FIXED_DELTA);
654
    }else{
655
      type = GLP_FR;
656
    }
657
658
    if(d_vars.isAuxiliary(v)){
659
      int rowIndex = d_rowIndices[v];
660
      glp_set_row_bnds(d_inputProb, rowIndex, type, lb, ub);
661
    }else{
662
      int colIndex = d_colIndices[v];
663
      // is input is correct here
664
      int kind = d_vars.isInteger(v) ? GLP_IV : GLP_CV;
665
      glp_set_col_kind(d_inputProb, colIndex, kind);
666
      glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub);
667
    }
668
  }
669
670
  // Count the number of entries
671
  int numEntries = 0;
672
  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
673
    ArithVar v = *i;
674
    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
675
    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
676
    {
677
      ++numEntries;
678
    }
679
  }
680
681
  int* ia = new int[numEntries+1];
682
  int* ja = new int[numEntries+1];
683
  double* ar = new double[numEntries+1];
684
685
  int entryCounter = 0;
686
  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
687
    ArithVar v = *i;
688
    int rowIndex = d_rowIndices[v];
689
690
    Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
691
692
    for (Polynomial::iterator j = p.begin(), end = p.end(); j != end; ++j)
693
    {
694
      const Monomial& mono = *j;
695
      const Constant& constant = mono.getConstant();
696
      const VarList& variable = mono.getVarList();
697
698
      Node n = variable.getNode();
699
700
      Assert(d_vars.hasArithVar(n));
701
      ArithVar av = d_vars.asArithVar(n);
702
      int colIndex = d_colIndices[av];
703
      double coeff = constant.getValue().getDouble();
704
705
      ++entryCounter;
706
      ia[entryCounter] = rowIndex;
707
      ja[entryCounter] = colIndex;
708
      ar[entryCounter] = coeff;
709
    }
710
  }
711
  glp_load_matrix(d_inputProb, numEntries, ia, ja, ar);
712
713
  delete[] ia;
714
  delete[] ja;
715
  delete[] ar;
716
}
717
int ApproxGLPK::guessDir(ArithVar v) const{
718
  if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
719
    return -1;
720
  }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
721
    return 1;
722
  }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
723
    return 0;
724
  }else{
725
    int ubSgn = d_vars.getUpperBound(v).sgn();
726
    int lbSgn = d_vars.getLowerBound(v).sgn();
727
728
    if(ubSgn != 0 && lbSgn == 0){
729
      return -1;
730
    }else if(ubSgn == 0 && lbSgn != 0){
731
      return 1;
732
    }
733
734
    return 1;
735
  }
736
}
737
738
ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{
739
  ArithRatPairVec ret;
740
741
  // Strategies are guess:
742
  // 1 simple shared "ceiling" variable: danoint, pk1
743
  //  x1 >= c, x1 >= tmp1, x1 >= tmp2, ...
744
  // 1 large row: fixnet, vpm2, pp08a
745
  //  (+ .......... ) <= c
746
  // Not yet supported:
747
  // 1 complex shared "ceiling" variable: opt1217
748
  //  x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... )
749
  //  and all of the ... are the same sign
750
751
752
  // Candidates:
753
  // 1) Upper and lower bounds are not equal.
754
  // 2) The variable is not integer
755
  // 3a) For columns look for a ceiling variable
756
  // 3B) For rows look for a large row with
757
758
  DenseMap<BoundCounts> d_colCandidates;
759
  DenseMap<uint32_t> d_rowCandidates;
760
761
  double sumRowLength = 0.0;
762
  uint32_t maxRowLength = 0;
763
  for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){
764
    ArithVar v = *vi;
765
766
    if (s_verbosity >= 2)
767
    {
768
      CVC5Message() << v << " ";
769
      d_vars.printModel(v, CVC5Message());
770
    }
771
772
    int type;
773
    if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
774
      if(d_vars.boundsAreEqual(v)){
775
        type = GLP_FX;
776
      }else{
777
        type = GLP_DB;
778
      }
779
    }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){
780
      type = GLP_UP;
781
    }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){
782
      type = GLP_LO;
783
    }else{
784
      type = GLP_FR;
785
    }
786
787
    if(type != GLP_FX && type != GLP_FR){
788
789
      if(d_vars.isAuxiliary(v)){
790
        Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
791
        uint32_t len = p.size();
792
        d_rowCandidates.set(v, len);
793
        sumRowLength += len;
794
        maxRowLength = std::max(maxRowLength, len);
795
      }else if(!d_vars.isInteger(v)){
796
        d_colCandidates.set(v, BoundCounts());
797
      }
798
    }
799
  }
800
801
  uint32_t maxCount = 0;
802
  for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){
803
    ArithVar v = *i;
804
805
    bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v);
806
    bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v);
807
808
    if(lbCap || ubCap){
809
      ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v)
810
        : d_vars.getUpperBoundConstraint(v);
811
812
      if(!(b->getValue()).noninfinitesimalIsZero()){ continue; }
813
814
      Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v));
815
      if(poly.size() != 2) { continue; }
816
817
      Polynomial::iterator j = poly.begin();
818
      Monomial first = *j;
819
      ++j;
820
      Monomial second = *j;
821
822
      bool firstIsPos = first.constantIsPositive();
823
      bool secondIsPos = second.constantIsPositive();
824
825
      if(firstIsPos == secondIsPos){ continue; }
826
827
      Monomial pos = firstIsPos == lbCap ? first : second;
828
      Monomial neg = firstIsPos != lbCap ? first : second;
829
      // pos >= neg
830
      VarList p = pos.getVarList();
831
      VarList n = neg.getVarList();
832
      if(d_vars.hasArithVar(p.getNode())){
833
        ArithVar ap = d_vars.asArithVar(p.getNode());
834
        if( d_colCandidates.isKey(ap)){
835
          BoundCounts bc = d_colCandidates.get(ap);
836
          bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1);
837
          maxCount = std::max(maxCount, bc.upperBoundCount());
838
          d_colCandidates.set(ap, bc);
839
        }
840
      }
841
      if(d_vars.hasArithVar(n.getNode())){
842
        ArithVar an = d_vars.asArithVar(n.getNode());
843
        if( d_colCandidates.isKey(an)){
844
          BoundCounts bc = d_colCandidates.get(an);
845
          bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount());
846
          maxCount = std::max(maxCount, bc.lowerBoundCount());
847
          d_colCandidates.set(an, bc);
848
        }
849
      }
850
    }
851
  }
852
853
  // Attempt row
854
  double avgRowLength = d_rowCandidates.size() >= 1 ?
855
    ( sumRowLength / d_rowCandidates.size() ) : 0.0;
856
857
  // There is a large row among the candidates
858
  bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength);
859
860
  double rowLengthReq = (maxRowLength * .9);
861
862
  if (guessARowCandidate)
863
  {
864
    for (ArithVar r : d_rowCandidates)
865
    {
866
      uint32_t len = d_rowCandidates[r];
867
868
      int dir = guessDir(r);
869
      if(len >= rowLengthReq){
870
        if (s_verbosity >= 1)
871
        {
872
          CVC5Message() << "high row " << r << " " << len << " " << avgRowLength
873
                        << " " << dir << endl;
874
          d_vars.printModel(r, CVC5Message());
875
        }
876
        ret.push_back(ArithRatPair(r, Rational(dir)));
877
      }
878
    }
879
  }
880
881
  // Attempt columns
882
  bool guessAColCandidate = maxCount >= 4;
883
  if (guessAColCandidate)
884
  {
885
    for (ArithVar c : d_colCandidates)
886
    {
887
      BoundCounts bc = d_colCandidates[c];
888
889
      int dir = guessDir(c);
890
      double ubScore = double(bc.upperBoundCount()) / maxCount;
891
      double lbScore = double(bc.lowerBoundCount()) / maxCount;
892
      if(ubScore  >= .9 || lbScore >= .9){
893
        if (s_verbosity >= 1)
894
        {
895
          CVC5Message() << "high col " << c << " " << bc << " " << ubScore
896
                        << " " << lbScore << " " << dir << endl;
897
          d_vars.printModel(c, CVC5Message());
898
        }
899
        ret.push_back(ArithRatPair(c, Rational(c)));
900
      }
901
    }
902
  }
903
904
  return ret;
905
}
906
907
void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){
908
  DenseMap<double> nbCoeffs;
909
910
  for(ArithRatPairVec::const_iterator i = ref.begin(), iend = ref.end(); i != iend; ++i){
911
    ArithVar v = (*i).first;
912
    const Rational& q = (*i).second;
913
914
    if(d_vars.isAuxiliary(v)){
915
      // replace the variable by its definition and multiply by q
916
      Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v));
917
      Polynomial pq = p * q;
918
919
      for(Polynomial::iterator j = pq.begin(), jend = pq.end(); j != jend; ++j){
920
        const Monomial& mono = *j;
921
        const Constant& constant = mono.getConstant();
922
        const VarList& variable = mono.getVarList();
923
924
        Node n = variable.getNode();
925
926
        Assert(d_vars.hasArithVar(n));
927
        ArithVar av = d_vars.asArithVar(n);
928
        int colIndex = d_colIndices[av];
929
        double coeff = constant.getValue().getDouble();
930
        if(!nbCoeffs.isKey(colIndex)){
931
          nbCoeffs.set(colIndex, 0.0);
932
        }
933
        nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
934
      }
935
    }else{
936
      int colIndex = d_colIndices[v];
937
      double coeff = q.getDouble();
938
      if(!nbCoeffs.isKey(colIndex)){
939
        nbCoeffs.set(colIndex, 0.0);
940
      }
941
      nbCoeffs.set(colIndex, nbCoeffs[colIndex]+coeff);
942
    }
943
  }
944
  for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){
945
    Index colIndex = *ci;
946
    double coeff = nbCoeffs[colIndex];
947
    glp_set_obj_coef(d_inputProb, colIndex, coeff);
948
  }
949
}
950
951
952
/*
953
 * rough strategy:
954
 *  real relaxation
955
 *   try approximate real optimization of error function
956
 *   pivot in its basis
957
 *   update to its assignment
958
 *   check with FCSimplex
959
 *  check integer solution
960
 *   try approximate mixed integer problem
961
 *   stop at the first feasible point
962
 *   pivot in its basis
963
 *   update to its assignment
964
 *   check with FCSimplex
965
 */
966
967
void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){
968
  switch(status){
969
  case GLP_OPT:
970
    out << "GLP_OPT" << endl;
971
    break;
972
  case GLP_FEAS:
973
    out << "GLP_FEAS" << endl;
974
    break;
975
  case GLP_INFEAS:
976
    out << "GLP_INFEAS" << endl;
977
    break;
978
  case GLP_NOFEAS:
979
    out << "GLP_NOFEAS" << endl;
980
    break;
981
  case GLP_UNBND:
982
    out << "GLP_UNBND" << endl;
983
    break;
984
  case GLP_UNDEF:
985
    out << "GLP_UNDEF" << endl;
986
    break;
987
  default:
988
    out << "Status unknown" << endl;
989
    break;
990
  }
991
}
992
993
ApproxGLPK::~ApproxGLPK(){
994
  glp_delete_prob(d_inputProb);
995
  glp_delete_prob(d_realProb);
996
  glp_delete_prob(d_mipProb);
997
998
}
999
1000
ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const
1001
{
1002
  Assert(d_solvedRelaxation);
1003
  Assert(!mip || d_solvedMIP);
1004
1005
  ApproximateSimplex::Solution sol;
1006
  DenseSet& newBasis = sol.newBasis;
1007
  DenseMap<DeltaRational>& newValues = sol.newValues;
1008
1009
  glp_prob* prob = mip ? d_mipProb : d_realProb;
1010
1011
  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
1012
    ArithVar vi = *i;
1013
    bool isAux = d_vars.isAuxiliary(vi);
1014
    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
1015
1016
    int status = isAux ? glp_get_row_stat(prob, glpk_index)
1017
      : glp_get_col_stat(prob, glpk_index);
1018
    if (s_verbosity >= 2)
1019
    {
1020
      CVC5Message() << "assignment " << vi << endl;
1021
    }
1022
1023
    bool useDefaultAssignment = false;
1024
1025
    switch(status){
1026
    case GLP_BS:
1027
      // CVC5Message() << "basic" << endl;
1028
      newBasis.add(vi);
1029
      useDefaultAssignment = true;
1030
      break;
1031
    case GLP_NL:
1032
    case GLP_NS:
1033
      if(!mip){
1034
        if (s_verbosity >= 2)
1035
        {
1036
          CVC5Message() << "non-basic lb" << endl;
1037
        }
1038
        newValues.set(vi, d_vars.getLowerBound(vi));
1039
      }else{// intentionally fall through otherwise
1040
        useDefaultAssignment = true;
1041
      }
1042
      break;
1043
    case GLP_NU:
1044
      if(!mip){
1045
        if (s_verbosity >= 2)
1046
        {
1047
          CVC5Message() << "non-basic ub" << endl;
1048
        }
1049
        newValues.set(vi, d_vars.getUpperBound(vi));
1050
      }else {// intentionally fall through otherwise
1051
        useDefaultAssignment = true;
1052
      }
1053
      break;
1054
    default:
1055
      {
1056
        useDefaultAssignment = true;
1057
      }
1058
      break;
1059
    }
1060
1061
    if(useDefaultAssignment){
1062
      if (s_verbosity >= 2)
1063
      {
1064
        CVC5Message() << "non-basic other" << endl;
1065
      }
1066
1067
      double newAssign;
1068
      if(mip){
1069
        newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
1070
                     :  glp_mip_col_val(prob, glpk_index));
1071
      }else{
1072
        newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
1073
                     :  glp_get_col_prim(prob, glpk_index));
1074
      }
1075
      const DeltaRational& oldAssign = d_vars.getAssignment(vi);
1076
1077
      if (d_vars.hasLowerBound(vi)
1078
          && roughlyEqual(newAssign,
1079
                          d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA)))
1080
      {
1081
        // CVC5Message() << "  to lb" << endl;
1082
1083
        newValues.set(vi, d_vars.getLowerBound(vi));
1084
      }
1085
      else if (d_vars.hasUpperBound(vi)
1086
               && roughlyEqual(
1087
                   newAssign,
1088
                   d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA)))
1089
      {
1090
        newValues.set(vi, d_vars.getUpperBound(vi));
1091
        // CVC5Message() << "  to ub" << endl;
1092
      }
1093
      else
1094
      {
1095
        double rounded = round(newAssign);
1096
        if (roughlyEqual(newAssign, rounded))
1097
        {
1098
          // CVC5Message() << "roughly equal " << rounded << " " << newAssign <<
1099
          // " " << oldAssign << endl;
1100
          newAssign = rounded;
1101
        }
1102
        else
1103
        {
1104
          // CVC5Message() << "not roughly equal " << rounded << " " <<
1105
          // newAssign << " " << oldAssign << endl;
1106
        }
1107
1108
        DeltaRational proposal;
1109
        if (Maybe<Rational> maybe_new = estimateWithCFE(newAssign))
1110
        {
1111
          proposal = maybe_new.value();
1112
        }
1113
        else
1114
        {
1115
          // failed to estimate the old value. defaulting to the current.
1116
          proposal = d_vars.getAssignment(vi);
1117
        }
1118
1119
        if (roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA)))
1120
        {
1121
          // CVC5Message() << "  to prev value" << newAssign << " " << oldAssign
1122
          // << endl;
1123
          proposal = d_vars.getAssignment(vi);
1124
        }
1125
1126
        if (d_vars.strictlyLessThanLowerBound(vi, proposal))
1127
        {
1128
          // CVC5Message() << "  round to lb " << d_vars.getLowerBound(vi) <<
1129
          // endl;
1130
          proposal = d_vars.getLowerBound(vi);
1131
        }
1132
        else if (d_vars.strictlyGreaterThanUpperBound(vi, proposal))
1133
        {
1134
          // CVC5Message() << "  round to ub " << d_vars.getUpperBound(vi) <<
1135
          // endl;
1136
          proposal = d_vars.getUpperBound(vi);
1137
        }
1138
        else
1139
        {
1140
          // CVC5Message() << "  use proposal" << proposal << " " << oldAssign
1141
          // << endl;
1142
        }
1143
        newValues.set(vi, proposal);
1144
      }
1145
    }
1146
  }
1147
  return sol;
1148
}
1149
1150
double ApproxGLPK::sumInfeasibilities(glp_prob* prob, bool mip) const{
1151
  /* compute the sum of dual infeasibilities */
1152
  double infeas = 0.0;
1153
1154
  for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){
1155
    ArithVar vi = *i;
1156
    bool isAux = d_vars.isAuxiliary(vi);
1157
    int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi];
1158
1159
    double newAssign;
1160
    if(mip){
1161
      newAssign = (isAux ? glp_mip_row_val(prob, glpk_index)
1162
                   :  glp_mip_col_val(prob, glpk_index));
1163
    }else{
1164
      newAssign = (isAux ? glp_get_row_prim(prob, glpk_index)
1165
                   :  glp_get_col_prim(prob, glpk_index));
1166
    }
1167
1168
1169
    double ub = isAux ?
1170
      glp_get_row_ub(prob, glpk_index) : glp_get_col_ub(prob, glpk_index);
1171
1172
    double lb = isAux ?
1173
      glp_get_row_lb(prob, glpk_index) : glp_get_col_lb(prob, glpk_index);
1174
1175
    if(ub != +DBL_MAX){
1176
      if(newAssign > ub){
1177
        double ubinf = newAssign - ub;
1178
        infeas += ubinf;
1179
        Debug("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl;
1180
      }
1181
    }
1182
    if(lb != -DBL_MAX){
1183
      if(newAssign < lb){
1184
        double lbinf = lb - newAssign;
1185
        infeas  += lbinf;
1186
1187
        Debug("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl;
1188
      }
1189
    }
1190
  }
1191
  return infeas;
1192
}
1193
1194
LinResult ApproxGLPK::solveRelaxation(){
1195
  Assert(!d_solvedRelaxation);
1196
1197
  glp_smcp parm;
1198
  glp_init_smcp(&parm);
1199
  parm.presolve = GLP_OFF;
1200
  parm.meth = GLP_PRIMAL;
1201
  parm.pricing = GLP_PT_PSE;
1202
  parm.it_lim = d_pivotLimit;
1203
  parm.msg_lev = GLP_MSG_OFF;
1204
  if(s_verbosity >= 1){
1205
    parm.msg_lev = GLP_MSG_ALL;
1206
  }
1207
1208
  glp_erase_prob(d_realProb);
1209
  glp_copy_prob(d_realProb, d_inputProb, GLP_OFF);
1210
1211
  int res = glp_simplex(d_realProb, &parm);
1212
  switch(res){
1213
  case 0:
1214
    {
1215
      int status = glp_get_status(d_realProb);
1216
      int iterationcount = glp_get_it_cnt(d_realProb);
1217
      switch(status){
1218
      case GLP_OPT:
1219
      case GLP_FEAS:
1220
      case GLP_UNBND:
1221
        d_solvedRelaxation = true;
1222
        return LinFeasible;
1223
      case GLP_INFEAS:
1224
      case GLP_NOFEAS:
1225
        d_solvedRelaxation = true;
1226
        return LinInfeasible;
1227
      default:
1228
        {
1229
          if(iterationcount >= d_pivotLimit){
1230
            return LinExhausted;
1231
          }
1232
          return LinUnknown;
1233
        }
1234
      }
1235
    }
1236
  default:
1237
    return LinUnknown;
1238
  }
1239
}
1240
1241
1242
struct MirInfo : public CutInfo {
1243
1244
  /** a sum of input rows. */
1245
  PrimitiveVec row_sum;
1246
1247
  /* the delta used */
1248
  double delta;
1249
1250
  /* all of these are length vars == N+M*/
1251
  int nvars;
1252
  char* cset;
1253
  char* subst;
1254
  int*  vlbRows;
1255
  int*  vubRows;
1256
  MirInfo(int execOrd, int ord)
1257
    : CutInfo(MirCutKlass, execOrd, ord)
1258
    , nvars(0)
1259
    , cset(NULL)
1260
    , subst(NULL)
1261
    , vlbRows(NULL)
1262
    , vubRows(NULL)
1263
  {}
1264
1265
  ~MirInfo(){
1266
    clearSets();
1267
  }
1268
  void clearSets(){
1269
    if(cset != NULL){
1270
      delete[] cset;
1271
      delete[] subst;
1272
      delete[] vlbRows;
1273
      delete[] vubRows;
1274
      cset = NULL;
1275
      nvars = 0;
1276
    }
1277
  }
1278
  void initSet(){
1279
    Assert(d_N >= 0);
1280
    Assert(d_mAtCreation >= 0);
1281
    clearSets();
1282
1283
    int vars = 1 + d_N + d_mAtCreation;
1284
1285
    cset = new char[1+vars];
1286
    subst = new char[1+vars];
1287
    vlbRows = new int[1+vars];
1288
    vubRows = new int[1+vars];
1289
  }
1290
};
1291
1292
struct GmiInfo : public CutInfo {
1293
  int basic;
1294
  PrimitiveVec tab_row;
1295
  int* tab_statuses;
1296
  /* has the length tab_row.length */
1297
1298
  GmiInfo(int execOrd, int ord)
1299
    : CutInfo(GmiCutKlass, execOrd, ord)
1300
    , basic(-1)
1301
    , tab_row()
1302
    , tab_statuses(NULL)
1303
  {
1304
    Assert(!initialized_tab());
1305
  }
1306
1307
  ~GmiInfo(){
1308
    if(initialized_tab()){
1309
      clear_tab();
1310
    }
1311
  }
1312
1313
  bool initialized_tab() const {
1314
    return tab_statuses != NULL;
1315
  }
1316
1317
  void init_tab(int N){
1318
    if(initialized_tab()){
1319
      clear_tab();
1320
    }
1321
    tab_row.setup(N);
1322
    tab_statuses = new int[1+N];
1323
  }
1324
1325
  void clear_tab() {
1326
    delete[] tab_statuses;
1327
    tab_statuses = NULL;
1328
    tab_row.clear();
1329
    basic = -1;
1330
  }
1331
};
1332
1333
1334
1335
static void loadCut(glp_tree *tree, CutInfo* cut){
1336
  int ord, cut_len, cut_klass;
1337
  int N, M;
1338
  int* cut_inds;
1339
  double* cut_coeffs;
1340
  int glpk_cut_type;
1341
  double cut_rhs;
1342
  glp_prob* lp;
1343
1344
  lp = glp_ios_get_prob(tree);
1345
  ord = cut->poolOrdinal();
1346
1347
  N = glp_get_num_cols(lp);
1348
  M = glp_get_num_rows(lp);
1349
1350
  cut->setDimensions(N, M);
1351
1352
1353
1354
  // Get the cut
1355
  cut_len = glp_ios_get_cut(tree, ord, NULL, NULL, &cut_klass, NULL, NULL);
1356
  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
1357
1358
  PrimitiveVec& cut_vec = cut->getCutVector();
1359
  cut_vec.setup(cut_len);
1360
  cut_inds = cut_vec.inds;
1361
  cut_coeffs = cut_vec.coeffs;
1362
1363
  cut_vec.len = glp_ios_get_cut(tree, ord, cut_inds, cut_coeffs, &cut_klass, &glpk_cut_type, &cut_rhs);
1364
  Assert(fromGlpkClass(cut_klass) == cut->getKlass());
1365
  Assert(cut_vec.len == cut_len);
1366
1367
  cut->setRhs(cut_rhs);
1368
1369
  cut->setKind( glpk_type_to_kind(glpk_cut_type) );
1370
}
1371
1372
1373
static MirInfo* mirCut(glp_tree *tree, int exec_ord, int cut_ord){
1374
  Debug("approx::mirCut") << "mirCut()" << exec_ord << endl;
1375
1376
  MirInfo* mir;
1377
  mir = new MirInfo(exec_ord, cut_ord);
1378
  loadCut(tree, mir);
1379
  mir->initSet();
1380
1381
1382
  int nrows = glp_ios_cut_get_aux_nrows(tree, cut_ord);
1383
1384
  PrimitiveVec& row_sum = mir->row_sum;
1385
  row_sum.setup(nrows);
1386
  glp_ios_cut_get_aux_rows(tree, cut_ord, row_sum.inds, row_sum.coeffs);
1387
1388
  glp_ios_cut_get_mir_cset(tree, cut_ord, mir->cset);
1389
  mir->delta = glp_ios_cut_get_mir_delta(tree, cut_ord);
1390
  glp_ios_cut_get_mir_subst(tree, cut_ord, mir->subst);
1391
  glp_ios_cut_get_mir_virtual_rows(tree, cut_ord, mir->vlbRows, mir->vubRows);
1392
1393
  if(Debug.isOn("approx::mirCut")){
1394
    Debug("approx::mirCut") << "mir_id: " << exec_ord << endl;
1395
    row_sum.print(Debug("approx::mirCut"));
1396
  }
1397
1398
  return mir;
1399
}
1400
1401
static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){
1402
  Debug("approx::gmiCut") << "gmiCut()" << exec_ord << endl;
1403
1404
  int gmi_var;
1405
  int write_pos;
1406
  int read_pos;
1407
  int stat;
1408
  int ind;
1409
  int i;
1410
1411
  GmiInfo* gmi;
1412
  glp_prob* lp;
1413
1414
  gmi = new GmiInfo(exec_ord, cut_ord);
1415
  loadCut(tree, gmi);
1416
1417
  lp = glp_ios_get_prob(tree);
1418
1419
  int N = gmi->getN();
1420
  int M = gmi->getMAtCreation();
1421
1422
  // Get the tableau row
1423
  int nrows CVC5_UNUSED = glp_ios_cut_get_aux_nrows(tree, gmi->poolOrdinal());
1424
  Assert(nrows == 1);
1425
  int rows[1+1];
1426
  glp_ios_cut_get_aux_rows(tree, gmi->poolOrdinal(), rows, NULL);
1427
  gmi_var = rows[1];
1428
1429
  gmi->init_tab(N);
1430
  gmi->basic = M+gmi_var;
1431
1432
  Debug("approx::gmiCut")
1433
    << gmi <<" " << gmi->basic << " "
1434
    << cut_ord<<" "  << M <<" " << gmi_var << endl;
1435
1436
  PrimitiveVec& tab_row = gmi->tab_row;
1437
  Debug("approx::gmiCut") << "Is N sufficient here?" << endl;
1438
  tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs);
1439
1440
  Debug("approx::gmiCut") << "gmi_var " << gmi_var << endl;
1441
1442
  Debug("approx::gmiCut") << "tab_pos " << tab_row.len << endl;
1443
  write_pos = 1;
1444
  for(read_pos = 1; read_pos <= tab_row.len; ++read_pos){
1445
    if (fabs(tab_row.coeffs[read_pos]) < 1e-10){
1446
    }else{
1447
      tab_row.coeffs[write_pos] = tab_row.coeffs[read_pos];
1448
      tab_row.inds[write_pos] = tab_row.inds[read_pos];
1449
      ++write_pos;
1450
    }
1451
  }
1452
  tab_row.len = write_pos-1;
1453
  Debug("approx::gmiCut") << "write_pos " << write_pos << endl;
1454
  Assert(tab_row.len > 0);
1455
1456
  for(i = 1; i <= tab_row.len; ++i){
1457
    ind = tab_row.inds[i];
1458
    Debug("approx::gmiCut") << "ind " << i << " " << ind << endl;
1459
    stat = (ind <= M) ?
1460
      glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M);
1461
1462
    Debug("approx::gmiCut") << "ind " << i << " " << ind << " stat " << stat << endl;
1463
    switch (stat){
1464
    case GLP_NL:
1465
    case GLP_NU:
1466
    case GLP_NS:
1467
      gmi->tab_statuses[i] = stat;
1468
      break;
1469
    case GLP_NF:
1470
    default:
1471
      Unreachable();
1472
    }
1473
  }
1474
1475
  if(Debug.isOn("approx::gmiCut")){
1476
    gmi->print(Debug("approx::gmiCut"));
1477
  }
1478
  return gmi;
1479
}
1480
1481
static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){
1482
  //(tree, br_var, br_val, dn < 0);
1483
  double rhs;
1484
  Kind k;
1485
  if(down_bad){
1486
    // down branch is infeasible
1487
    // x <= floor(v) is infeasible
1488
    // - so x >= ceiling(v) is implied
1489
    k = kind::GEQ;
1490
    rhs = std::ceil(br_val);
1491
  }else{
1492
    // up branch is infeasible
1493
    // x >= ceiling(v) is infeasible
1494
    // - so x <= floor(v) is implied
1495
    k = kind::LEQ;
1496
    rhs = std::floor(br_val);
1497
  }
1498
  BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs);
1499
  return br_cut;
1500
}
1501
1502
static void glpkCallback(glp_tree *tree, void *info){
1503
  AuxInfo* aux = (AuxInfo*)(info);
1504
  TreeLog& tl = *(aux->tl);
1505
1506
  int exec = tl.getExecutionOrd();
1507
  int glpk_node_p = -1;
1508
  int node_ord = -1;
1509
1510
  if(tl.isActivelyLogging()){
1511
    switch(glp_ios_reason(tree)){
1512
    case GLP_LI_DELROW:
1513
      {
1514
        glpk_node_p = glp_ios_curr_node(tree);
1515
        node_ord = glp_ios_node_ord(tree, glpk_node_p);
1516
1517
        int nrows = glp_ios_rows_deleted(tree, NULL);
1518
        int* num = new int[1+nrows];
1519
        glp_ios_rows_deleted(tree, num);
1520
1521
        NodeLog& node = tl.getNode(node_ord);
1522
1523
        RowsDeleted* rd = new RowsDeleted(exec, nrows, num);
1524
1525
        node.addCut(rd);
1526
        delete[] num;
1527
      }
1528
      break;
1529
    case GLP_ICUTADDED:
1530
      {
1531
        int cut_ord = glp_ios_pool_size(tree);
1532
        glpk_node_p = glp_ios_curr_node(tree);
1533
        node_ord = glp_ios_node_ord(tree, glpk_node_p);
1534
        Assert(cut_ord > 0);
1535
        Debug("approx") << "curr node " << glpk_node_p
1536
                        << " cut ordinal " << cut_ord
1537
                        << " node depth " << glp_ios_node_level(tree, glpk_node_p)
1538
                        << endl;
1539
        int klass;
1540
        glp_ios_get_cut(tree, cut_ord, NULL, NULL, &klass, NULL, NULL);
1541
1542
        NodeLog& node = tl.getNode(node_ord);
1543
        switch(klass){
1544
        case GLP_RF_GMI:
1545
          {
1546
            GmiInfo* gmi = gmiCut(tree, exec, cut_ord);
1547
            node.addCut(gmi);
1548
          }
1549
          break;
1550
        case GLP_RF_MIR:
1551
          {
1552
            MirInfo* mir = mirCut(tree, exec, cut_ord);
1553
            node.addCut(mir);
1554
          }
1555
          break;
1556
        case GLP_RF_COV:
1557
          Debug("approx") << "GLP_RF_COV" << endl;
1558
          break;
1559
        case GLP_RF_CLQ:
1560
          Debug("approx") << "GLP_RF_CLQ" << endl;
1561
          break;
1562
        default:
1563
          break;
1564
        }
1565
      }
1566
      break;
1567
    case GLP_ICUTSELECT:
1568
      {
1569
        glpk_node_p = glp_ios_curr_node(tree);
1570
        node_ord = glp_ios_node_ord(tree, glpk_node_p);
1571
        int cuts = glp_ios_pool_size(tree);
1572
        int* ords = new int[1+cuts];
1573
        int* rows = new int[1+cuts];
1574
        int N = glp_ios_selected_cuts(tree, ords, rows);
1575
1576
        NodeLog& nl = tl.getNode(node_ord);
1577
        Debug("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl;
1578
        for(int i = 1; i <= N; ++i){
1579
          Debug("approx") << "adding to " << node_ord <<" @ i= " << i
1580
                          << " ords[i] = " << ords[i]
1581
                          << " rows[i] = " << rows[i] << endl;
1582
          nl.addSelected(ords[i], rows[i]);
1583
        }
1584
        delete[] ords;
1585
        delete[] rows;
1586
        nl.applySelected();
1587
      }
1588
    break;
1589
  case GLP_LI_BRANCH:
1590
    {
1591
      // a branch was just made
1592
      int br_var;
1593
      int p, dn, up;
1594
      int p_ord, dn_ord, up_ord;
1595
      double br_val;
1596
      br_var = glp_ios_branch_log(tree, &br_val, &p, &dn, &up);
1597
      p_ord = glp_ios_node_ord(tree, p);
1598
1599
      dn_ord = (dn >= 0) ? glp_ios_node_ord(tree, dn) : -1;
1600
      up_ord = (up >= 0) ? glp_ios_node_ord(tree, up) : -1;
1601
1602
      Debug("approx::") << "branch: "<< br_var << " "  << br_val << " tree " << p << " " << dn << " " << up << endl;
1603
      Debug("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl;
1604
      if(dn < 0 && up < 0){
1605
        Debug("approx::") << "branch close " << exec << endl;
1606
        NodeLog& node = tl.getNode(p_ord);
1607
        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
1608
        node.addCut(cut_br);
1609
        tl.close(p_ord);
1610
      }else if(dn < 0 || up < 0){
1611
        Debug("approx::") << "branch cut" << exec << endl;
1612
        NodeLog& node = tl.getNode(p_ord);
1613
        BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0);
1614
        node.addCut(cut_br);
1615
      }else{
1616
        Debug("approx::") << "normal branch" << endl;
1617
        tl.branch(p_ord, br_var, br_val, dn_ord, up_ord);
1618
      }
1619
    }
1620
    break;
1621
    case GLP_LI_CLOSE:
1622
      {
1623
        glpk_node_p = glp_ios_curr_node(tree);
1624
        node_ord = glp_ios_node_ord(tree, glpk_node_p);
1625
        Debug("approx::") << "close " << glpk_node_p << endl;
1626
        tl.close(node_ord);
1627
      }
1628
      break;
1629
    default:
1630
      break;
1631
    }
1632
  }
1633
1634
  switch(glp_ios_reason(tree)){
1635
  case GLP_IBINGO:
1636
    Debug("approx::") << "bingo" << endl;
1637
    aux->term = MipBingo;
1638
    glp_ios_terminate(tree);
1639
    break;
1640
  case GLP_ICUTADDED:
1641
    {
1642
      tl.addCut();
1643
    }
1644
    break;
1645
  case GLP_LI_BRANCH:
1646
    {
1647
      int p, dn, up;
1648
      int br_var = glp_ios_branch_log(tree, NULL, &p, &dn, &up);
1649
1650
      if(br_var >= 0){
1651
        unsigned v = br_var;
1652
        tl.logBranch(v);
1653
        int depth = glp_ios_node_level(tree, p);
1654
        unsigned ubl =  (aux->branchLimit) >= 0 ? ((unsigned)(aux->branchLimit)) : 0u;
1655
        if(tl.numBranches(v) >= ubl || depth >= (aux->branchDepth)){
1656
          aux->term = BranchesExhausted;
1657
          glp_ios_terminate(tree);
1658
        }
1659
      }
1660
    }
1661
    break;
1662
  case GLP_LI_CLOSE:
1663
    break;
1664
  default:
1665
    {
1666
      glp_prob* prob = glp_ios_get_prob(tree);
1667
      int iterationcount = glp_get_it_cnt(prob);
1668
      if(exec > (aux->pivotLimit)){
1669
        aux->term = ExecExhausted;
1670
        glp_ios_terminate(tree);
1671
      }else if(iterationcount > (aux->pivotLimit)){
1672
        aux->term = PivotsExhauasted;
1673
        glp_ios_terminate(tree);
1674
      }
1675
    }
1676
    break;
1677
  }
1678
}
1679
1680
std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con)
1681
{
1682
  std::vector<const CutInfo*> proven;
1683
  int nid = con.getNodeId();
1684
  for(NodeLog::const_iterator j = con.begin(), jend=con.end(); j!=jend; ++j){
1685
    CutInfo* cut = *j;
1686
1687
    if(cut->getKlass() != RowsDeletedKlass){
1688
      if(!cut->reconstructed()){
1689
        Assert(!cut->reconstructed());
1690
        tryCut(nid, *cut);
1691
      }
1692
    }
1693
1694
    if(cut->proven()){
1695
      proven.push_back(cut);
1696
    }
1697
  }
1698
  return proven;
1699
}
1700
1701
ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{
1702
  int br_var = con.branchVariable();
1703
  return getArithVarFromStructural(br_var);
1704
}
1705
1706
1707
MipResult ApproxGLPK::solveMIP(bool activelyLog){
1708
  Assert(d_solvedRelaxation);
1709
  // Explicitly disable presolving
1710
  // We need the basis thus the presolver must be off!
1711
  // This is default, but this is just being cautious.
1712
  AuxInfo aux;
1713
  aux.pivotLimit = d_pivotLimit;
1714
  aux.branchLimit = d_branchLimit;
1715
  aux.branchDepth = d_maxDepth;
1716
  aux.tl = &d_log;
1717
  aux.term = MipUnknown;
1718
1719
  d_log.reset(d_rootRowIds);
1720
  if(activelyLog){
1721
    d_log.makeActive();
1722
  }else{
1723
    d_log.makeInactive();
1724
  }
1725
1726
  glp_iocp parm;
1727
  glp_init_iocp(&parm);
1728
  parm.presolve = GLP_OFF;
1729
  parm.pp_tech = GLP_PP_NONE;
1730
  parm.fp_heur = GLP_ON;
1731
  parm.gmi_cuts = GLP_ON;
1732
  parm.mir_cuts = GLP_ON;
1733
  parm.cov_cuts = GLP_ON;
1734
  parm.cb_func = glpkCallback;
1735
  parm.cb_info = &aux;
1736
  parm.msg_lev = GLP_MSG_OFF;
1737
  if(s_verbosity >= 1){
1738
    parm.msg_lev = GLP_MSG_ALL;
1739
  }
1740
1741
  glp_erase_prob(d_mipProb);
1742
  glp_copy_prob(d_mipProb, d_realProb, GLP_OFF);
1743
1744
  int res = glp_intopt(d_mipProb, &parm);
1745
1746
  Debug("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl;
1747
1748
  switch(res){
1749
  case 0:
1750
  case GLP_ESTOP:
1751
    {
1752
      int status = glp_mip_status(d_mipProb);
1753
      Debug("approx::") << "status " << status << endl;
1754
      switch(status){
1755
      case GLP_OPT:
1756
      case GLP_FEAS:
1757
        d_solvedMIP = true;
1758
        Debug("approx::") << "bingo here!" << endl;
1759
        return MipBingo;
1760
      case GLP_NOFEAS:
1761
        d_solvedMIP = true;
1762
        return MipClosed;
1763
      default:
1764
        if(aux.term == MipBingo){
1765
          d_solvedMIP = true;
1766
          Debug("approx::") << "bingo here?" << endl;
1767
        }
1768
        return aux.term;
1769
      }
1770
    }
1771
  default:
1772
    return MipUnknown;
1773
  }
1774
}
1775
1776
// Node explainSet(const set<ConstraintP>& inp){
1777
//   Assert(!inp.empty());
1778
//   NodeBuilder nb(kind::AND);
1779
//   set<ConstraintP>::const_iterator iter, end;
1780
//   for(iter = inp.begin(), end = inp.end(); iter != end; ++iter){
1781
//     const ConstraintP c = *iter;
1782
//     Assert(c != NullConstraint);
1783
//     c->explainForConflict(nb);
1784
//   }
1785
//   Node ret = safeConstructNary(nb);
1786
//   Node rew = Rewriter::rewrite(ret);
1787
//   if(rew.getNumChildren() < ret.getNumChildren()){
1788
//     //Debug("approx::") << "explainSet " << ret << " " << rew << endl;
1789
//   }
1790
//   return rew;
1791
// }
1792
1793
DeltaRational sumConstraints(const DenseMap<Rational>& xs, const DenseMap<ConstraintP>& cs, bool* anyinf){
1794
  if(anyinf != NULL){
1795
    *anyinf = false;
1796
  }
1797
1798
  DeltaRational beta(0);
1799
  DenseMap<Rational>::const_iterator iter, end;
1800
  iter = xs.begin();
1801
  end = xs.end();
1802
1803
  Debug("approx::sumConstraints") << "sumConstraints";
1804
  for(; iter != end; ++iter){
1805
    ArithVar x = *iter;
1806
    const Rational& psi = xs[x];
1807
    ConstraintP c = cs[x];
1808
    Assert(c != NullConstraint);
1809
1810
    const DeltaRational& bound = c->getValue();
1811
    beta += bound * psi;
1812
    Debug("approx::sumConstraints") << " +("<<bound << "*" << psi <<")";
1813
    if(anyinf != NULL ){
1814
      *anyinf = *anyinf || !bound.infinitesimalIsZero();
1815
    }
1816
  }
1817
  Debug("approx::sumConstraints") << "= " << beta << endl;
1818
1819
  return beta;
1820
}
1821
1822
// remove fixed variables from the vector
1823
void removeFixed(const ArithVariables& vars, DenseVector& dv, set<ConstraintP>& exp){
1824
  DenseMap<Rational>& vec = dv.lhs;
1825
  Rational& removed = dv.rhs;
1826
  vector<ArithVar> equal;
1827
  DenseMap<Rational>::const_iterator vec_iter, vec_end;
1828
  vec_iter = vec.begin(), vec_end = vec.end();
1829
  for(; vec_iter != vec_end; ++vec_iter){
1830
    ArithVar x = *vec_iter;
1831
    if(vars.boundsAreEqual(x)){
1832
      equal.push_back(x);
1833
    }
1834
  }
1835
  vector<ArithVar>::const_iterator equal_iter, equal_end;
1836
  equal_iter = equal.begin(), equal_end = equal.end();
1837
  for(; equal_iter != equal_end; ++equal_iter){
1838
    ArithVar x = *equal_iter;
1839
    Assert(vars.boundsAreEqual(x));
1840
    const DeltaRational& lb = vars.getLowerBound(x);
1841
    Assert(lb.infinitesimalIsZero());
1842
    removed -= (vec[x]) * lb.getNoninfinitesimalPart();
1843
1844
    vec.remove(x);
1845
1846
    std::pair<ConstraintP, ConstraintP> p = vars.explainEqualBounds(x);
1847
    exp.insert(p.first);
1848
    Debug("removeFixed") << "remove fixed " << p.first << endl;
1849
    if(p.second != NullConstraint){
1850
      exp.insert(p.second);
1851
      Debug("removeFixed") << "remove fixed " << p.second << endl;
1852
    }
1853
  }
1854
}
1855
void removeZeroes(DenseMap<Rational>& v){
1856
  // Remove Slack variables
1857
  vector<ArithVar> zeroes;
1858
  DenseMap<Rational>::const_iterator i, iend;
1859
  for(i = v.begin(), iend = v.end(); i != iend; ++i){
1860
    ArithVar x = *i;
1861
    if(v[x].isZero()){
1862
      zeroes.push_back(x);
1863
    }
1864
  }
1865
1866
  vector<ArithVar>::const_iterator j, jend;
1867
  for(j = zeroes.begin(), jend = zeroes.end(); j != jend; ++j){
1868
    ArithVar x = *j;
1869
    v.remove(x);
1870
  }
1871
}
1872
void removeZeroes(DenseVector& v){
1873
  removeZeroes(v.lhs);
1874
}
1875
1876
void removeAuxillaryVariables(const ArithVariables& vars, DenseMap<Rational>& vec){
1877
  // Remove auxillary variables
1878
  vector<ArithVar> aux;
1879
  DenseMap<Rational>::const_iterator vec_iter, vec_end;
1880
  vec_iter = vec.begin(), vec_end = vec.end();
1881
  for(; vec_iter != vec_end; ++vec_iter){
1882
    ArithVar x = *vec_iter;
1883
    if(vars.isAuxiliary(x)){
1884
      aux.push_back(x);
1885
    }
1886
  }
1887
1888
  vector<ArithVar>::const_iterator aux_iter, aux_end;
1889
  aux_iter = aux.begin(), aux_end = aux.end();
1890
  for(; aux_iter != aux_end; ++aux_iter){
1891
    ArithVar s = *aux_iter;
1892
    Rational& s_coeff = vec.get(s);
1893
    Assert(vars.isAuxiliary(s));
1894
    Assert(vars.hasNode(s));
1895
    Node sAsNode = vars.asNode(s);
1896
    Polynomial p = Polynomial::parsePolynomial(sAsNode);
1897
    for(Polynomial::iterator j = p.begin(), p_end=p.end(); j != p_end; ++j){
1898
      Monomial m = *j;
1899
      const Rational& ns_coeff = m.getConstant().getValue();
1900
      Node vl = m.getVarList().getNode();
1901
      ArithVar ns = vars.asArithVar(vl);
1902
      Rational prod = s_coeff * ns_coeff;
1903
      if(vec.isKey(ns)){
1904
        vec.get(ns) += prod;
1905
      }else{
1906
        vec.set(ns, prod);
1907
      }
1908
    }
1909
    s_coeff = Rational(0); // subtract s_coeff * s from vec
1910
  }
1911
  removeZeroes(vec);
1912
}
1913
1914
ArithVar ApproxGLPK::_getArithVar(int nid, int M, int ind) const{
1915
  if(ind <= 0){
1916
    return ARITHVAR_SENTINEL;
1917
  }else if(ind <= M){
1918
    return getArithVarFromRow(nid, ind);
1919
  }else{
1920
    return getArithVarFromStructural(ind - M);
1921
  }
1922
}
1923
1924
1925
bool ApproxGLPK::guessIsConstructable(const DenseMap<Rational>& guess) const {
1926
  // basic variable
1927
  // sum g[i] * x_i
1928
  DenseMap<Rational> g = guess;
1929
  removeAuxillaryVariables(d_vars, g);
1930
1931
  if(Debug.isOn("guessIsConstructable")){
1932
    if(!g.empty()){
1933
      Debug("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl;
1934
      DenseVector::print(Debug("approx::guessIsConstructable"), g);
1935
      Debug("approx::guessIsConstructable") << endl;
1936
    }
1937
  }
1938
  return g.empty();
1939
}
1940
1941
bool ApproxGLPK::loadToBound(int nid, int M, int len, int* inds, int* statuses, DenseMap<ConstraintP>& toBound) const{
1942
  for(int i = 1; i <= len; ++i){
1943
    int status = statuses[i];
1944
    int ind = inds[i];
1945
    ArithVar v = _getArithVar(nid, M, ind);
1946
    ConstraintP c = NullConstraint;
1947
    if(v == ARITHVAR_SENTINEL){ return true; }
1948
1949
    switch(status){
1950
    case GLP_NL:
1951
      c = d_vars.getLowerBoundConstraint(v);
1952
      break;
1953
    case GLP_NU:
1954
    case GLP_NS: // upper bound sufficies for fixed variables
1955
      c = d_vars.getUpperBoundConstraint(v);
1956
      break;
1957
    case GLP_NF:
1958
    default:
1959
      return true;
1960
    }
1961
    if(c == NullConstraint){
1962
      Debug("approx::") << "couldn't find " << v << " @ " << nid << endl;
1963
      return true;
1964
    }
1965
    Assert(c != NullConstraint);
1966
    toBound.set(v, c);
1967
  }
1968
  return false;
1969
}
1970
1971
bool ApproxGLPK::checkCutOnPad(int nid, const CutInfo& cut) const{
1972
1973
  Debug("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl;
1974
1975
  const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs;
1976
  const Rational& constructedRhs = d_pad.d_cut.rhs;
1977
  std::unordered_set<ArithVar> visited;
1978
1979
  if(constructedLhs.empty()){
1980
    Debug("approx::checkCutOnPad") << "its empty?" <<endl;
1981
    return true;
1982
  }
1983
  if(cut.getKind() != d_pad.d_cutKind) {
1984
    Debug("approx::checkCutOnPad") << "rel doesn't match" << endl;
1985
    return true;
1986
  }
1987
1988
  const PrimitiveVec& cv = cut.getCutVector();
1989
  for(int i = 1; i <= cv.len; ++i){
1990
    int ind = cv.inds[i]; // this is always a structural variable
1991
    double coeff = cv.coeffs[i];
1992
1993
1994
1995
    if(!d_colToArithVar.isKey(ind)){ return true; }
1996
    ArithVar x = d_colToArithVar[ind];
1997
    //if(x == ARITHVAR_SENTINEL){ return true; }
1998
    visited.insert(x);
1999
2000
2001
    if(!constructedLhs.isKey(x)){
2002
      if(Debug.isOn("approx::checkCutOnPad")){
2003
        Debug("approx::checkCutOnPad") << " didn't find key for " << x << std::endl;
2004
        cut.print(Debug("approx::checkCutOnPad"));
2005
        Debug("approx::checkCutOnPad") << endl;
2006
        d_pad.d_cut.print(Debug("approx::checkCutOnPad"));
2007
        Debug("approx::checkCutOnPad") << endl;
2008
      }
2009
      return true;
2010
    }
2011
2012
    const Rational& onConstructed = constructedLhs[x];
2013
2014
    Debug("approx::checkCutOnPad") << ind << " " << coeff  << " " << endl;
2015
    Debug("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl;
2016
2017
    if(!roughlyEqual(coeff, onConstructed.getDouble())){
2018
      Debug("approx::checkCutOnPad") << "coeff failure" << endl;
2019
      return true;
2020
    }
2021
  }
2022
  if(visited.size() != constructedLhs.size()){
2023
    Debug("approx::checkCutOnPad") << "size mismatch" << endl;
2024
    return true;
2025
  }
2026
2027
2028
  if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){
2029
    Debug("approx::checkCutOnPad")
2030
      << "norm rhs is off " << cut.getRhs() << " " << constructedRhs << endl;
2031
    return true;
2032
  }
2033
  return false;
2034
}
2035
2036
2037
2038
bool ApproxGLPK::attemptBranchCut(int nid, const BranchCutInfo& br_cut){
2039
  d_pad.clear();
2040
2041
  const PrimitiveVec& cut_vec = br_cut.getCutVector();
2042
  int structural = cut_vec.inds[1];
2043
  Assert(roughlyEqual(cut_vec.coeffs[1], +1.0));
2044
2045
  ArithVar x = getArithVarFromStructural(structural);
2046
  d_pad.d_failure = (x == ARITHVAR_SENTINEL);
2047
  if(d_pad.d_failure){ return true; }
2048
2049
  Kind brKind = br_cut.getKind();
2050
2051
  d_pad.d_failure = (brKind != kind::LEQ && brKind != kind::GEQ);
2052
  if(d_pad.d_failure){ return true; }
2053
2054
  d_pad.d_cutKind = brKind;
2055
  d_pad.d_cut.lhs.set(x, Rational(1));
2056
2057
  Rational& rhs = d_pad.d_cut.rhs;
2058
  Maybe<Rational> br_cut_rhs = Rational::fromDouble(br_cut.getRhs());
2059
  if (!br_cut_rhs)
2060
  {
2061
    return true;
2062
  }
2063
2064
  rhs = estimateWithCFE(br_cut_rhs.value(), Integer(1));
2065
  d_pad.d_failure = !rhs.isIntegral();
2066
  if(d_pad.d_failure) { return true; }
2067
2068
  d_pad.d_failure = checkCutOnPad(nid, br_cut);
2069
  if(d_pad.d_failure){ return true; }
2070
2071
  return false;
2072
}
2073
2074
bool ApproxGLPK::attemptGmi(int nid, const GmiInfo& gmi){
2075
  d_pad.clear();
2076
2077
  d_pad.d_cutKind = kind::GEQ;
2078
2079
  int M = gmi.getMAtCreation();
2080
  ArithVar b = _getArithVar(nid, M, gmi.basic);
2081
  d_pad.d_failure = (b == ARITHVAR_SENTINEL);
2082
  if(d_pad.d_failure){ return true; }
2083
2084
  d_pad.d_failure = !d_vars.isIntegerInput(b);
2085
  if(d_pad.d_failure){ return true; }
2086
2087
  d_pad.d_basic = b;
2088
2089
2090
  const PrimitiveVec& tab = gmi.tab_row;
2091
  d_pad.d_failure = attemptConstructTableRow(nid, M, tab);
2092
  if(d_pad.d_failure){ return true; }
2093
2094
  int* statuses = gmi.tab_statuses;
2095
  DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
2096
  d_pad.d_failure = loadToBound(nid, M, tab.len, tab.inds, statuses, toBound);
2097
  if(d_pad.d_failure){ return true; }
2098
2099
  d_pad.d_failure = constructGmiCut();
2100
  if(d_pad.d_failure){ return true; }
2101
2102
  d_pad.d_failure = checkCutOnPad(nid, gmi);
2103
  if(d_pad.d_failure){ return true; }
2104
2105
  return false;
2106
}
2107
2108
bool ApproxGLPK::applyCMIRRule(int nid, const MirInfo& mir){
2109
2110
  const DenseMap<Rational>& compRanges = d_pad.d_compRanges;
2111
2112
  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
2113
  Rational& b = d_pad.d_alpha.rhs;
2114
2115
  Maybe<Rational> delta = estimateWithCFE(mir.delta);
2116
  if (!delta)
2117
  {
2118
    return true;
2119
  }
2120
2121
  d_pad.d_failure = (delta.value().sgn() <= 0);
2122
  if(d_pad.d_failure){ return true; }
2123
2124
  Debug("approx::mir") << "applyCMIRRule() " << delta << " " << mir.delta << endl;
2125
2126
  DenseMap<Rational>::const_iterator iter, iend;
2127
  iter = alpha.begin(), iend = alpha.end();
2128
  for(; iter != iend; ++iter){
2129
    ArithVar v = *iter;
2130
    const Rational& curr = alpha[v];
2131
    Rational next = curr / delta.value();
2132
    if(compRanges.isKey(v)){
2133
      b -= curr * compRanges[v];
2134
      alpha.set(v, - next);
2135
    }else{
2136
      alpha.set(v, next);
2137
    }
2138
  }
2139
  b = b / delta.value();
2140
2141
  Rational roundB = (b + Rational(1,2)).floor();
2142
  d_pad.d_failure = (b - roundB).abs() < Rational(1,90);
2143
  // intensionally more generous than glpk here
2144
  if(d_pad.d_failure){ return true; }
2145
2146
  Rational one(1);
2147
  Rational fb = b.floor_frac();
2148
  Rational one_sub_fb = one - fb;
2149
  Rational gamma = (one / one_sub_fb);
2150
2151
  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
2152
  Rational& beta = d_pad.d_cut.rhs;
2153
2154
  iter = alpha.begin(), iend = alpha.end();
2155
  for(; iter != iend; ++iter){
2156
    ArithVar v = *iter;
2157
    const Rational& a_j = alpha[v];
2158
    if(d_vars.isIntegerInput(v)){
2159
      Rational floor_aj = a_j.floor();
2160
      Rational frac_aj = a_j.floor_frac();
2161
      if(frac_aj <= fb){
2162
        cut.set(v, floor_aj);
2163
      }else{
2164
        Rational tmp =  ((frac_aj - fb) / one_sub_fb);
2165
        cut.set(v, floor_aj + tmp);
2166
      }
2167
    }else{
2168
      cut.set(v, a_j * gamma);
2169
    }
2170
  }
2171
  beta = b.floor();
2172
2173
  iter = cut.begin(), iend = cut.end();
2174
  for(; iter != iend; ++iter){
2175
    ArithVar v = *iter;
2176
    if(compRanges.isKey(v)){
2177
      Rational neg = - cut[v];
2178
      beta += neg * compRanges[v];
2179
      cut.set(v, neg);
2180
    }
2181
  }
2182
2183
  return false;
2184
}
2185
2186
bool ApproxGLPK::attemptMir(int nid, const MirInfo& mir){
2187
  d_pad.clear();
2188
2189
  d_pad.d_cutKind = kind::LEQ;
2190
2191
  // virtual bounds must be done before slacks
2192
  d_pad.d_failure = loadVirtualBoundsIntoPad(nid, mir);
2193
  if(d_pad.d_failure){ return true; }
2194
2195
  d_pad.d_failure = loadSlacksIntoPad(nid, mir);
2196
  if(d_pad.d_failure){ return true; }
2197
2198
2199
  d_pad.d_failure = loadRowSumIntoAgg(nid, mir.getMAtCreation(), mir.row_sum);
2200
  if(d_pad.d_failure){ return true; }
2201
2202
  removeFixed(d_vars, d_pad.d_agg, d_pad.d_explanation);
2203
2204
  d_pad.d_failure = buildModifiedRow(nid, mir);
2205
  if(d_pad.d_failure){ return true; }
2206
2207
  d_pad.d_failure =  constructMixedKnapsack();
2208
  if(d_pad.d_failure){ return true; }
2209
2210
  d_pad.d_failure = makeRangeForComplemented(nid, mir);
2211
  if(d_pad.d_failure){ return true; }
2212
2213
  d_pad.d_failure = applyCMIRRule(nid, mir);
2214
  if(d_pad.d_failure){ return true; }
2215
2216
  d_pad.d_failure = replaceSlacksOnCuts();
2217
  if(d_pad.d_failure){ return true; }
2218
2219
  removeAuxillaryVariables(d_vars, d_pad.d_cut.lhs);
2220
2221
  d_pad.d_failure = checkCutOnPad(nid, mir);
2222
  if(d_pad.d_failure){ return true; }
2223
2224
  return false;
2225
  //return makeCutNodes(nid, mir);
2226
}
2227
2228
/** Returns true on failure. */
2229
bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){
2230
  if(ri <= 0) { return true; }
2231
2232
  static int instance = 0;
2233
  ++instance;
2234
  Debug("glpk::loadVB") << "loadVB() " << instance << endl;
2235
2236
  ArithVar rowVar = _getArithVar(nid, M, ri);
2237
  ArithVar contVar = _getArithVar(nid, M, j);
2238
  if(rowVar == ARITHVAR_SENTINEL){
2239
    Debug("glpk::loadVB") << "loadVB() " << instance
2240
                          << " rowVar is ARITHVAR_SENTINEL " << rowVar << endl;
2241
    return true;
2242
  }
2243
  if(contVar == ARITHVAR_SENTINEL){
2244
    Debug("glpk::loadVB") << "loadVB() " << instance
2245
                          << " contVar is ARITHVAR_SENTINEL " << contVar << endl;
2246
    return true; }
2247
2248
  if(!d_vars.isAuxiliary(rowVar)){
2249
    Debug("glpk::loadVB") << "loadVB() " << instance
2250
                          << " rowVar is not auxilliary " << rowVar << endl;
2251
    return true;
2252
  }
2253
  // is integer is correct here
2254
  if(d_vars.isInteger(contVar)){
2255
    Debug("glpk::loadVB") << "loadVB() " << instance
2256
                          << " contVar is integer " << contVar << endl;
2257
    return true;
2258
  }
2259
2260
  ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar);
2261
  ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar);
2262
2263
  if(lb != NullConstraint && ub != NullConstraint){
2264
    Debug("glpk::loadVB") << "loadVB() " << instance
2265
                          << " lb and ub are both NULL " << lb << " " << ub << endl;
2266
    return true;
2267
  }
2268
2269
  ConstraintP rcon = lb == NullConstraint ? ub : lb;
2270
  if(rcon == NullConstraint) {
2271
    Debug("glpk::loadVB") << "loadVB() " << instance
2272
                          << " rcon is NULL " << rcon << endl;
2273
    return true;
2274
  }
2275
2276
  if(!rcon->getValue().isZero()){
2277
    Debug("glpk::loadVB") << "loadVB() " << instance
2278
                          << " rcon value is not 0 " << rcon->getValue() << endl;
2279
    return true;
2280
  }
2281
2282
  if(!d_vars.hasNode(rowVar)){
2283
    Debug("glpk::loadVB") << "loadVB() " << instance
2284
                          << " does not have node " << rowVar << endl;
2285
    return true;
2286
  }
2287
2288
  Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar));
2289
  if(p.size() != 2) {
2290
    Debug("glpk::loadVB") << "loadVB() " << instance << " polynomial is not binary: " << p.getNode() << endl;
2291
    return true;
2292
  }
2293
2294
  Monomial first = p.getHead(), second = p.getTail().getHead();
2295
  Rational c1 = first.getConstant().getValue();
2296
  Rational c2 = second.getConstant().getValue();
2297
  Node nx1 = first.getVarList().getNode();
2298
  Node nx2 = second.getVarList().getNode();
2299
2300
  if(!d_vars.hasArithVar(nx1)) {
2301
    Debug("glpk::loadVB") << "loadVB() " << instance
2302
                          << " does not have a variable for nx1: " << nx1 << endl;
2303
    return true;
2304
  }
2305
  if(!d_vars.hasArithVar(nx2)) {
2306
    Debug("glpk::loadVB") << "loadVB() " << instance
2307
                          << " does not have a variable for nx2 " << nx2 << endl;
2308
    return true;
2309
  }
2310
  ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2);
2311
2312
  Assert(x1 != x2);
2313
  Assert(!c1.isZero());
2314
  Assert(!c2.isZero());
2315
2316
  Debug("glpk::loadVB")
2317
    << " lb " << lb
2318
    << " ub " << ub
2319
    << " rcon " << rcon
2320
    << " x1 " << x1
2321
    << " x2 " << x2
2322
    << " c1 " << c1
2323
    << " c2 " << c2 << endl;
2324
2325
  ArithVar iv = (x1 == contVar) ? x2 : x1;
2326
  Rational& cc = (x1 == contVar) ? c1 : c2;
2327
  Rational& ic = (x1 == contVar) ? c2 : c1;
2328
2329
  Debug("glpk::loadVB")
2330
    << " cv " << contVar
2331
    << " cc " << cc
2332
    << " iv " << iv
2333
    << " c2 " << ic << endl;
2334
2335
  if(!d_vars.isIntegerInput(iv)){
2336
    Debug("glpk::loadVB") << "loadVB() " << instance
2337
                          << " iv is not an integer input variable " << iv << endl;
2338
    return true;
2339
  }
2340
  // cc * cv + ic * iv <= 0 or
2341
  // cc * cv + ic * iv <= 0
2342
2343
  if(rcon == ub){ // multiply by -1
2344
    cc = -cc; ic = - ic;
2345
  }
2346
  Debug("glpk::loadVB") << " cv " << contVar
2347
                        << " cc " << cc
2348
                        << " iv " << iv
2349
                        << " c2 " << ic << endl;
2350
2351
  // cc * cv + ic * iv >= 0
2352
  // cc * cv >= -ic * iv
2353
  // if cc < 0:
2354
  //   cv <= -ic/cc * iv
2355
  // elif cc > 0:
2356
  //   cv >= -ic/cc * iv
2357
  Assert(!cc.isZero());
2358
  Rational d = -ic/cc;
2359
  Debug("glpk::loadVB") << d << " " << cc.sgn() << endl;
2360
  bool nowUb = cc.sgn() < 0;
2361
  if(wantUb != nowUb) {
2362
    Debug("glpk::loadVB") << "loadVB() " << instance
2363
                          << " wantUb is not nowUb " << wantUb << " " << nowUb << endl;
2364
2365
    return true;
2366
  }
2367
2368
  Kind rel = wantUb ? kind::LEQ : kind::GEQ;
2369
2370
  tmp = VirtualBound(contVar, rel, d, iv, rcon);
2371
    Debug("glpk::loadVB") << "loadVB() " << instance << " was successful" << endl;
2372
  return false;
2373
}
2374
2375
bool ApproxGLPK::loadVirtualBoundsIntoPad(int nid, const MirInfo& mir){
2376
  Assert(mir.vlbRows != NULL);
2377
  Assert(mir.vubRows != NULL);
2378
2379
  int N = mir.getN();
2380
  int M = mir.getMAtCreation();
2381
2382
  // Load the virtual bounds first
2383
  VirtualBound tmp;
2384
  for(int j=1; j <= N+M; ++j){
2385
    if(!loadVB(nid, M, j, mir.vlbRows[j], false, tmp)){
2386
      if(d_pad.d_vlb.isKey(tmp.x)){ return true; }
2387
      d_pad.d_vlb.set(tmp.x, tmp);
2388
    }else if(mir.vlbRows[j] > 0){
2389
      Debug("approx::mir") << "expected vlb to work" << endl;
2390
    }
2391
    if(!loadVB(nid, M, j, mir.vubRows[j], true, tmp)){
2392
      if(d_pad.d_vub.isKey(tmp.x)){ return true; }
2393
      d_pad.d_vub.set(tmp.x, tmp);
2394
    }else if(mir.vubRows[j] > 0){
2395
      Debug("approx::mir") << "expected vub to work" << endl;
2396
    }
2397
  }
2398
  return false;
2399
}
2400
2401
bool ApproxGLPK::loadSlacksIntoPad(int nid, const MirInfo& mir){
2402
  Assert(mir.vlbRows != NULL);
2403
  Assert(mir.vubRows != NULL);
2404
2405
  int N = mir.getN();
2406
  int M = mir.getMAtCreation();
2407
2408
  bool useVB;
2409
  // Load the virtual bounds first
2410
  SlackReplace rep;
2411
  bool lb;
2412
  ConstraintP b;
2413
  Debug("approx::mir") << "loadSlacksIntoPad(): N="<<N<<", M=" << M << std::endl;
2414
  for(int j=1; j <= N+M; ++j){
2415
    ArithVar v = _getArithVar(nid, M, j);
2416
    if(v == ARITHVAR_SENTINEL){
2417
      Debug("approx::mir") << " for: " << j << " no variable" << endl;
2418
      continue;
2419
    }
2420
    rep = SlackUndef;
2421
    char sub = mir.subst[j];
2422
    switch(sub){
2423
    case 'L':
2424
    case 'U':
2425
      lb = (sub == 'L');
2426
      useVB = lb ? (mir.vlbRows[j] > 0) : (mir.vubRows[j] > 0);
2427
      if(useVB){
2428
        if(lb ? d_pad.d_vlb.isKey(v) : d_pad.d_vub.isKey(v)){
2429
          rep = lb ? SlackVLB : SlackVUB;
2430
        }
2431
      }else{
2432
        b = lb ? d_vars.getLowerBoundConstraint(v)
2433
          : d_vars.getUpperBoundConstraint(v);
2434
        if(b != NullConstraint){
2435
          if(b->getValue().infinitesimalIsZero()){
2436
            rep = lb ? SlackLB : SlackUB;
2437
          }
2438
        }
2439
      }
2440
2441
      Debug("approx::mir") << " for: " << j << ", " << v;
2442
      Debug("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " ";
2443
      Debug("approx::mir") << sub << " " << rep << " " << mir.vlbRows[j] << " " << mir.vubRows[j]
2444
                           << endl;
2445
      if(rep != SlackUndef){
2446
        d_pad.d_slacks.set(v,rep);
2447
      }
2448
      break;
2449
    case '?':
2450
      continue;
2451
    default:
2452
      Debug("approx::mir") << " for: " << j << " got subst " << (int)sub << endl;
2453
      continue;
2454
    }
2455
  }
2456
  return false;
2457
}
2458
2459
bool ApproxGLPK::replaceSlacksOnCuts(){
2460
  vector<ArithVar> virtualVars;
2461
2462
  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
2463
  Rational& cutRhs = d_pad.d_cut.rhs;
2464
2465
  DenseMap<Rational>::const_iterator iter, iend;
2466
  iter = cut.begin(), iend = cut.end();
2467
  for(; iter != iend; ++iter){
2468
    ArithVar x = *iter;
2469
    SlackReplace rep = d_pad.d_slacks[x];
2470
    if(d_vars.isIntegerInput(x)){
2471
      Assert(rep == SlackLB || rep == SlackUB);
2472
      Rational& a = cut.get(x);
2473
2474
      const DeltaRational& bound = (rep == SlackLB) ?
2475
        d_vars.getLowerBound(x) : d_vars.getUpperBound(x);
2476
      Assert(bound.infinitesimalIsZero());
2477
      Rational prod = a * bound.getNoninfinitesimalPart();
2478
      if(rep == SlackLB){
2479
        cutRhs += prod;
2480
      }else{
2481
        cutRhs -= prod;
2482
        a = -a;
2483
      }
2484
    }else if(rep == SlackVLB){
2485
      virtualVars.push_back(d_pad.d_vlb[x].y);
2486
    }else if(rep == SlackVUB){
2487
      virtualVars.push_back(d_pad.d_vub[x].y);
2488
    }
2489
  }
2490
2491
  for(size_t i = 0; i < virtualVars.size(); ++i){
2492
    ArithVar x = virtualVars[i];
2493
    if(!cut.isKey(x)){
2494
      cut.set(x, Rational(0));
2495
    }
2496
  }
2497
2498
  iter = cut.begin(), iend = cut.end();
2499
  for(; iter != iend; ++iter){
2500
    ArithVar x = *iter;
2501
    if(!d_vars.isIntegerInput(x)){
2502
      SlackReplace rep = d_pad.d_slacks[x];
2503
      Rational& a = cut.get(x);
2504
      switch(rep){
2505
      case SlackLB:
2506
        {
2507
          const DeltaRational& bound = d_vars.getLowerBound(x);
2508
          Assert(bound.infinitesimalIsZero());
2509
          cutRhs += a * bound.getNoninfinitesimalPart();
2510
        }
2511
        break;
2512
      case SlackUB:
2513
        {
2514
          const DeltaRational& bound = d_vars.getUpperBound(x);
2515
          Assert(bound.infinitesimalIsZero());
2516
          cutRhs -= a * bound.getNoninfinitesimalPart();
2517
          a = -a;
2518
        }
2519
        break;
2520
      case SlackVLB:
2521
      case SlackVUB:
2522
        {
2523
          bool lb = (rep == SlackVLB);
2524
          const VirtualBound& vb = lb ?
2525
            d_pad.d_vlb[x] : d_pad.d_vub[x];
2526
          ArithVar y = vb.y;
2527
          Assert(vb.x == x);
2528
          Assert(cut.isKey(y));
2529
          Rational& ycoeff = cut.get(y);
2530
          if(lb){
2531
            ycoeff -= a * vb.d;
2532
          }else{
2533
            ycoeff += a * vb.d;
2534
            a = -a;
2535
          }
2536
        }
2537
        break;
2538
      default:
2539
        return true;
2540
      }
2541
    }
2542
  }
2543
  removeZeroes(cut);
2544
  return false;
2545
}
2546
2547
bool ApproxGLPK::loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& row_sum){
2548
  DenseMap<Rational>& lhs = d_pad.d_agg.lhs;
2549
  d_pad.d_agg.rhs = Rational(0);
2550
2551
  int len = row_sum.len;
2552
  for(int i = 1; i <= len; ++i){
2553
    int aux_ind = row_sum.inds[i]; // auxillary index
2554
    double coeff = row_sum.coeffs[i];
2555
    ArithVar x = _getArithVar(nid, M, aux_ind);
2556
    if(x == ARITHVAR_SENTINEL){ return true; }
2557
    Maybe<Rational> c = estimateWithCFE(coeff);
2558
    if (!c)
2559
    {
2560
      return true;
2561
    }
2562
2563
    if (lhs.isKey(x))
2564
    {
2565
      lhs.get(x) -= c.value();
2566
    }
2567
    else
2568
    {
2569
      lhs.set(x, -c.value());
2570
    }
2571
  }
2572
2573
  Debug("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl;
2574
  if(Debug.isOn("approx::mir")) { DenseVector::print(Debug("approx::mir"), lhs); }
2575
  removeAuxillaryVariables(d_vars, lhs);
2576
  Debug("approx::mir") << "end loadRowSumIntoAgg() 1" << endl;
2577
2578
  if(Debug.isOn("approx::mir")){
2579
    Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
2580
    DenseVector::print(Debug("approx::mir"), lhs);
2581
    Debug("approx::mir") << "end loadRowSumIntoAgg() 2" << endl;
2582
  }
2583
2584
  for(int i = 1; i <= len; ++i){
2585
    int aux_ind = row_sum.inds[i]; // auxillary index
2586
    double coeff = row_sum.coeffs[i];
2587
    ArithVar x = _getArithVar(nid, M, aux_ind);
2588
    Assert(x != ARITHVAR_SENTINEL);
2589
    Maybe<Rational> c = estimateWithCFE(coeff);
2590
    if (!c)
2591
    {
2592
      return true;
2593
    }
2594
    Assert(!lhs.isKey(x));
2595
    lhs.set(x, c.value());
2596
  }
2597
2598
  if(Debug.isOn("approx::mir")){
2599
    Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl;
2600
    DenseVector::print(Debug("approx::mir"), lhs);
2601
    Debug("approx::mir") << "end loadRowSumIntoAgg() 3" << endl;
2602
  }
2603
  return false;
2604
}
2605
2606
bool ApproxGLPK::buildModifiedRow(int nid, const MirInfo& mir){
2607
  const DenseMap<Rational>& agg = d_pad.d_agg.lhs;
2608
  const Rational& aggRhs = d_pad.d_agg.rhs;
2609
  DenseMap<Rational>& mod = d_pad.d_mod.lhs;
2610
  Rational& modRhs = d_pad.d_mod.rhs;
2611
2612
  Debug("approx::mir")
2613
    << "buildModifiedRow()"
2614
    << " |agg|=" << d_pad.d_agg.lhs.size()
2615
    << " |mod|=" << d_pad.d_mod.lhs.size()
2616
    << " |slacks|=" << d_pad.d_slacks.size()
2617
    << " |vlb|=" << d_pad.d_vub.size()
2618
    << " |vub|=" << d_pad.d_vlb.size() << endl;
2619
2620
  mod.addAll(agg);
2621
  modRhs = aggRhs;
2622
  DenseMap<Rational>::const_iterator iter, iend;
2623
  for(iter = agg.begin(), iend = agg.end(); iter != iend; ++iter){
2624
    ArithVar x = *iter;
2625
    const Rational& c = mod[x];
2626
    if(!d_pad.d_slacks.isKey(x)){
2627
      Debug("approx::mir") << "missed x: " << x << endl;
2628
      return true;
2629
    }
2630
    SlackReplace rep = d_pad.d_slacks[x];
2631
    switch(rep){
2632
    case SlackLB: // skip for now
2633
    case SlackUB:
2634
      break;
2635
    case SlackVLB: /* x[k] = lb[k] * x[kk] + x'[k] */
2636
    case SlackVUB: /* x[k] = ub[k] * x[kk] - x'[k] */
2637
      {
2638
        Assert(!d_vars.isIntegerInput(x));
2639
        bool ub = (rep == SlackVUB);
2640
        const VirtualBound& vb =
2641
          ub ? d_pad.d_vub[x] : d_pad.d_vlb[x];
2642
        Assert(vb.x == x);
2643
        ArithVar y = vb.y;
2644
        Rational prod = c * vb.d;
2645
        if(mod.isKey(y)){
2646
          mod.get(x) += prod;
2647
        }else{
2648
          mod.set(y, prod);
2649
        }
2650
        if(ub){
2651
          mod.set(x, -c);
2652
        }
2653
        Assert(vb.c != NullConstraint);
2654
        d_pad.d_explanation.insert(vb.c);
2655
      }
2656
      break;
2657
    default:
2658
      return true;
2659
    }
2660
  }
2661
  removeZeroes(mod); /* if something cancelled we don't want it in the explanation */
2662
  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
2663
    ArithVar x = *iter;
2664
    if(!d_pad.d_slacks.isKey(x)){  return true; }
2665
2666
    SlackReplace rep = d_pad.d_slacks[x];
2667
    switch(rep){
2668
    case SlackLB: /* x = lb + x' */
2669
    case SlackUB: /* x = ub - x' */
2670
      {
2671
        bool ub = (rep == SlackUB);
2672
        ConstraintP b = ub ?  d_vars.getUpperBoundConstraint(x):
2673
          d_vars.getLowerBoundConstraint(x);
2674
2675
        Assert(b != NullConstraint);
2676
        Assert(b->getValue().infinitesimalIsZero());
2677
        const Rational& c = mod.get(x);
2678
        modRhs -= c * b->getValue().getNoninfinitesimalPart();
2679
        if(ub){
2680
          mod.set(x, -c);
2681
        }
2682
        d_pad.d_explanation.insert(b);
2683
      }
2684
      break;
2685
    case SlackVLB: /* handled earlier */
2686
    case SlackVUB:
2687
      break;
2688
    default:
2689
      return true;
2690
    }
2691
  }
2692
  removeZeroes(mod);
2693
  return false;
2694
}
2695
2696
bool ApproxGLPK::makeRangeForComplemented(int nid, const MirInfo& mir){
2697
  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
2698
  int M = mir.getMAtCreation();
2699
  int N = mir.getN();
2700
  DenseMap<Rational>& compRanges = d_pad.d_compRanges;
2701
2702
  int complemented = 0;
2703
2704
  for(int j = 1; j <= M + N; ++j){
2705
    if(mir.cset[j] != 0){
2706
      complemented++;
2707
      ArithVar x = _getArithVar(nid, M, j);
2708
      if(!alpha.isKey(x)){ return true; }
2709
      if(!d_vars.isIntegerInput(x)){ return true; }
2710
      Assert(d_pad.d_slacks.isKey(x));
2711
      Assert(d_pad.d_slacks[x] == SlackLB || d_pad.d_slacks[x] == SlackUB);
2712
2713
      ConstraintP lb = d_vars.getLowerBoundConstraint(x);
2714
      ConstraintP ub = d_vars.getUpperBoundConstraint(x);
2715
2716
      if(lb == NullConstraint) { return true; }
2717
      if(ub == NullConstraint) { return true; }
2718
2719
      if(!lb->getValue().infinitesimalIsZero()){
2720
        return true;
2721
      }
2722
      if(!ub->getValue().infinitesimalIsZero()){
2723
        return true;
2724
      }
2725
2726
      const Rational& uval = ub->getValue().getNoninfinitesimalPart();
2727
      const Rational& lval = lb->getValue().getNoninfinitesimalPart();
2728
2729
      d_pad.d_explanation.insert(lb);
2730
      d_pad.d_explanation.insert(ub);
2731
2732
      Rational u = uval - lval;
2733
      // u is the same for both rep == LP and rep == UB
2734
      if(compRanges.isKey(x)) { return true; }
2735
      compRanges.set(x,u);
2736
    }
2737
  }
2738
2739
  Debug("approx::mir") <<  "makeRangeForComplemented()" << complemented << endl;
2740
  return false;
2741
}
2742
2743
2744
bool ApproxGLPK::constructMixedKnapsack(){
2745
  const DenseMap<Rational>& mod = d_pad.d_mod.lhs;
2746
  const Rational& modRhs = d_pad.d_mod.rhs;
2747
  DenseMap<Rational>& alpha = d_pad.d_alpha.lhs;
2748
  Rational& beta = d_pad.d_alpha.rhs;
2749
2750
  Assert(alpha.empty());
2751
  beta = modRhs;
2752
2753
  unsigned intVars = 0;
2754
  unsigned remain = 0;
2755
  unsigned dropped = 0;
2756
  DenseMap<Rational>::const_iterator iter, iend;
2757
  for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){
2758
    ArithVar v = *iter;
2759
    const Rational& c = mod[v];
2760
    Assert(!c.isZero());
2761
    if(d_vars.isIntegerInput(v)){
2762
      intVars++;
2763
      alpha.set(v, c);
2764
    }else if(c.sgn() < 0){
2765
      remain++;
2766
      alpha.set(v, c);
2767
    }else{
2768
      dropped++;
2769
    }
2770
  }
2771
2772
  Debug("approx::mir")
2773
    << "constructMixedKnapsack() "
2774
    <<" dropped " << dropped
2775
    <<" remain " << remain
2776
    <<" intVars " << intVars
2777
    << endl;
2778
  return intVars == 0; // if this is 0 we have failed
2779
}
2780
2781
bool ApproxGLPK::attemptConstructTableRow(int nid, int M, const PrimitiveVec& vec){
2782
  bool failed = guessCoefficientsConstructTableRow(nid, M, vec);
2783
  if(failed){
2784
    failed = gaussianElimConstructTableRow(nid, M, vec);
2785
  }
2786
2787
  return failed;
2788
}
2789
2790
bool ApproxGLPK::gaussianElimConstructTableRow(int nid, int M, const PrimitiveVec& vec){
2791
  TimerStat::CodeTimer codeTimer(d_stats.d_gaussianElimConstructTime);
2792
  ++d_stats.d_gaussianElimConstruct;
2793
2794
  ArithVar basic = d_pad.d_basic;
2795
  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
2796
  tab.purge();
2797
  d_pad.d_tabRow.rhs = Rational(0);
2798
  Assert(basic != ARITHVAR_SENTINEL);
2799
  Assert(tab.empty());
2800
  Assert(d_pad.d_tabRow.rhs.isZero());
2801
2802
  if(d_vars.isAuxiliary(basic)) { return true; }
2803
2804
  if(Debug.isOn("gaussianElimConstructTableRow")){
2805
    Debug("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2806
    vec.print(Debug("gaussianElimConstructTableRow"));
2807
    Debug("gaussianElimConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
2808
  }
2809
2810
  set<ArithVar> onrow;
2811
  for(int i = 1; i <= vec.len; ++i){
2812
    int ind = vec.inds[i];
2813
    ArithVar var = _getArithVar(nid, M, ind);
2814
    if(var == ARITHVAR_SENTINEL){
2815
      Debug("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
2816
      return true;
2817
    }
2818
    onrow.insert(var);
2819
  }
2820
2821
2822
  Debug("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2823
2824
  Matrix<Rational> A;
2825
  A.increaseSizeTo(d_vars.getNumberOfVariables());
2826
  std::vector< std::pair<RowIndex, ArithVar> > rows;
2827
  // load the rows for auxiliary variables into A
2828
  for (ArithVar v : onrow)
2829
  {
2830
    if(d_vars.isAuxiliary(v)){
2831
      Assert(d_vars.hasNode(v));
2832
2833
      vector<Rational> coeffs;
2834
      vector<ArithVar> vars;
2835
2836
      coeffs.push_back(Rational(-1));
2837
      vars.push_back(v);
2838
2839
      Node n = d_vars.asNode(v);
2840
      Polynomial p = Polynomial::parsePolynomial(n);
2841
      Polynomial::iterator j = p.begin(), jend=p.end();
2842
      for(j=p.begin(), jend=p.end(); j!=jend; ++j){
2843
        Monomial m = *j;
2844
        if(m.isConstant()) { return true; }
2845
        VarList vl = m.getVarList();
2846
        if(!d_vars.hasArithVar(vl.getNode())){ return true; }
2847
        ArithVar x = d_vars.asArithVar(vl.getNode());
2848
        const Rational& q = m.getConstant().getValue();
2849
        coeffs.push_back(q); vars.push_back(x);
2850
      }
2851
      RowIndex rid = A.addRow(coeffs, vars);
2852
      rows.push_back(make_pair(rid, ARITHVAR_SENTINEL));
2853
    }
2854
  }
2855
  Debug("gaussianElimConstructTableRow") << "3 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2856
2857
  for(size_t i=0; i < rows.size(); ++i){
2858
    RowIndex rid = rows[i].first;
2859
    Assert(rows[i].second == ARITHVAR_SENTINEL);
2860
2861
    // substitute previous rows
2862
    for(size_t j=0; j < i; j++){
2863
      RowIndex prevRow = rows[j].first;
2864
      ArithVar other = rows[j].second;
2865
      Assert(other != ARITHVAR_SENTINEL);
2866
      const Matrix<Rational>::Entry& e = A.findEntry(rid, other);
2867
      if(!e.blank()){
2868
        // r_p : 0 = -1 * other + sum a_i x_i
2869
        // rid : 0 =  e * other + sum b_i x_i
2870
        // rid += e * r_p
2871
        //     : 0 = 0 * other + ...
2872
        Assert(!e.getCoefficient().isZero());
2873
2874
        Rational cp = e.getCoefficient();
2875
        Debug("gaussianElimConstructTableRow")
2876
          << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl;
2877
        A.rowPlusRowTimesConstant(rid, prevRow, cp);
2878
      }
2879
    }
2880
    if(Debug.isOn("gaussianElimConstructTableRow")){
2881
      A.printMatrix(Debug("gaussianElimConstructTableRow"));
2882
    }
2883
2884
    // solve the row for anything other than non-basics
2885
    bool solveForBasic = (i + 1 == rows.size());
2886
    Rational q;
2887
    ArithVar s = ARITHVAR_SENTINEL;
2888
    Matrix<Rational>::RowIterator k = A.getRow(rid).begin();
2889
    Matrix<Rational>::RowIterator k_end = A.getRow(rid).end();
2890
    for(; k != k_end; ++k){
2891
      const Matrix<Rational>::Entry& e = *k;
2892
      ArithVar colVar = e.getColVar();
2893
      bool selectColVar = false;
2894
      if(colVar == basic){
2895
        selectColVar = solveForBasic;
2896
      }else if(onrow.find(colVar) == onrow.end()) {
2897
        selectColVar = true;
2898
      }
2899
      if(selectColVar){
2900
        s = colVar;
2901
        q = e.getCoefficient();
2902
      }
2903
    }
2904
    if(s == ARITHVAR_SENTINEL || q.isZero()){
2905
      Debug("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2906
      return true;
2907
    }else{
2908
      // 0 = q * s + sum c_i * x_i
2909
      Rational mult = -(q.inverse());
2910
      Debug("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl;
2911
      Debug("gaussianElimConstructTableRow") << "selecting " << rid << " " << s << endl;
2912
      //cout << "selecting " << s << " : complexity " << mult.complexity() << " " << mult << endl;
2913
      //cout << "selecting " << rid << " " << s << endl;
2914
      A.multiplyRowByConstant(rid, mult);
2915
      rows[i].second = s;
2916
    }
2917
  }
2918
  Debug("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2919
2920
  if(rows.empty()) {
2921
    Debug("gaussianElimConstructTableRow") << "4 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2922
    return true;
2923
  }
2924
  RowIndex rid_last = rows.back().first;
2925
  ArithVar rid_var = rows.back().second;
2926
  if(rid_var != basic){
2927
    Debug("gaussianElimConstructTableRow") << "4 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2928
    return true;
2929
  }
2930
2931
  Assert(tab.empty());
2932
2933
  Matrix<Rational>::RowIterator k = A.getRow(rid_last).begin();
2934
  Matrix<Rational>::RowIterator k_end = A.getRow(rid_last).end();
2935
  for(; k != k_end; ++k){
2936
    const Matrix<Rational>::Entry& e = *k;
2937
    tab.set(e.getColVar(), e.getCoefficient());
2938
  }
2939
  Debug("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2940
  if(!tab.isKey(basic)){
2941
    Debug("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2942
    return true;
2943
  }
2944
  if(tab[basic] != Rational(-1)){
2945
    Debug("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2946
    return true;
2947
  }
2948
2949
  tab.remove(basic);
2950
  Debug("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2951
2952
  if(vec.len < 0 ){
2953
    Debug("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2954
    return true;
2955
  }
2956
  if(tab.size() != ((unsigned)vec.len) ) {
2957
    Debug("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() <<  " " << vec.len << endl;
2958
    return true;
2959
  }
2960
2961
  Debug("gaussianElimConstructTableRow") << "7 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2962
2963
  for(int i = 1; i <= vec.len; ++i){
2964
    int ind = vec.inds[i];
2965
    double coeff = vec.coeffs[i];
2966
    ArithVar var = _getArithVar(nid, M, ind);
2967
    Assert(var != ARITHVAR_SENTINEL);
2968
    if(!tab.isKey(var)){
2969
      Debug("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl;
2970
      return true;
2971
    }
2972
2973
    double est = tab[var].getDouble();
2974
2975
    if(!ApproximateSimplex::roughlyEqual(coeff, est)){
2976
      Debug("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
2977
           << " boink on " << ind << " " << var << " " << est <<endl;
2978
      return true;
2979
    }
2980
    Debug("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl;
2981
  }
2982
2983
  Debug("gaussianElimConstructTableRow")
2984
    << "gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"
2985
    << " superduper" << endl;
2986
2987
  return false;
2988
}
2989
bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec){
2990
  for(size_t i=0; i < d_denomGuesses.size(); ++i){
2991
    const Integer& D = d_denomGuesses[i];
2992
    if(!guessCoefficientsConstructTableRow(nid, M, vec, D)){
2993
      d_stats.d_averageGuesses << i+1;
2994
      Debug("approx::gmi") << "guesseditat " << i << " D=" << D << endl;
2995
      return false;
2996
    }
2997
  }
2998
  return true;
2999
}
3000
bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec, const Integer& D){
3001
  ArithVar basic = d_pad.d_basic;
3002
  DenseMap<Rational>& tab = d_pad.d_tabRow.lhs;
3003
  tab.purge();
3004
  d_pad.d_tabRow.rhs = Rational(0);
3005
  Assert(basic != ARITHVAR_SENTINEL);
3006
  Assert(tab.empty());
3007
  Assert(d_pad.d_tabRow.rhs.isZero());
3008
3009
  if(Debug.isOn("guessCoefficientsConstructTableRow")){
3010
    Debug("guessCoefficientsConstructTableRow")  << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl;
3011
    vec.print(Debug("guessCoefficientsConstructTableRow"));
3012
    Debug("guessCoefficientsConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl;
3013
  }
3014
3015
  tab.set(basic, Rational(-1));
3016
  for(int i = 1; i <= vec.len; ++i){
3017
    int ind = vec.inds[i];
3018
    double coeff = vec.coeffs[i];
3019
    ArithVar var = _getArithVar(nid, M, ind);
3020
    if(var == ARITHVAR_SENTINEL){
3021
      Debug("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl;
3022
      return true;
3023
    }
3024
    Debug("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl;
3025
3026
    Maybe<Rational> cfe = estimateWithCFE(coeff, D);
3027
    if (!cfe)
3028
    {
3029
      return true;
3030
    }
3031
    tab.set(var, cfe.value());
3032
    Debug("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl;
3033
  }
3034
  if(!guessIsConstructable(tab)){
3035
    Debug("guessCoefficientsConstructTableRow") << "failed to construct with " << D  << endl;
3036
    return true;
3037
  }
3038
  tab.remove(basic);
3039
  return false;
3040
}
3041
3042
/* Maps an ArithVar to either an upper/lower bound */
3043
bool ApproxGLPK::constructGmiCut(){
3044
  const DenseMap<Rational>& tabRow = d_pad.d_tabRow.lhs;
3045
  const DenseMap<ConstraintP>& toBound = d_pad.d_toBound;
3046
  DenseMap<Rational>& cut = d_pad.d_cut.lhs;
3047
  std::set<ConstraintP>& explanation = d_pad.d_explanation;
3048
  Rational& rhs = d_pad.d_cut.rhs;
3049
3050
  DenseMap<Rational>::const_iterator iter, end;
3051
  Assert(cut.empty());
3052
3053
  // compute beta for a "fake" assignment
3054
  bool anyInf;
3055
  DeltaRational dbeta = sumConstraints(tabRow, toBound, &anyInf);
3056
  const Rational& beta = dbeta.getNoninfinitesimalPart();
3057
  Debug("approx::gmi") << dbeta << endl;
3058
  if(anyInf || beta.isIntegral()){ return true; }
3059
3060
  Rational one = Rational(1);
3061
  Rational fbeta = beta.floor_frac();
3062
  rhs = fbeta;
3063
  Assert(fbeta.sgn() > 0);
3064
  Assert(fbeta < one);
3065
  Rational one_sub_fbeta = one - fbeta;
3066
  for(iter = tabRow.begin(), end = tabRow.end(); iter != end; ++iter){
3067
    ArithVar x = *iter;
3068
    const Rational& psi = tabRow[x];
3069
    ConstraintP c = toBound[x];
3070
    const Rational& bound = c->getValue().getNoninfinitesimalPart();
3071
    if(d_vars.boundsAreEqual(x)){
3072
      // do not add a coefficient
3073
      // implictly substitute the variable w/ its constraint
3074
      std::pair<ConstraintP, ConstraintP> exp = d_vars.explainEqualBounds(x);
3075
      explanation.insert(exp.first);
3076
      if(exp.second != NullConstraint){
3077
        explanation.insert(exp.second);
3078
      }
3079
    }else if(d_vars.isIntegerInput(x) && psi.isIntegral()){
3080
      // do not add a coefficient
3081
      // nothing to explain
3082
      Debug("approx::gmi") << "skipping " << x << endl;
3083
    }else{
3084
      explanation.insert(c);
3085
      Rational phi;
3086
      Rational alpha = (c->isUpperBound() ? psi : -psi);
3087
3088
      // x - ub <= 0 and lb - x <= 0
3089
      if(d_vars.isIntegerInput(x)){
3090
        Assert(!psi.isIntegral());
3091
        // alpha = slack_sgn * psi
3092
        Rational falpha = alpha.floor_frac();
3093
        Assert(falpha.sgn() > 0);
3094
        Assert(falpha < one);
3095
        phi = (falpha <= fbeta) ?
3096
          falpha : ((fbeta / one_sub_fbeta) * (one - falpha));
3097
      }else{
3098
        phi = (alpha >= 0) ?
3099
          alpha : ((fbeta / one_sub_fbeta) * (- alpha));
3100
      }
3101
      Assert(phi.sgn() != 0);
3102
      if(c->isUpperBound()){
3103
        cut.set(x, -phi);
3104
        rhs -= phi * bound;
3105
      }else{
3106
        cut.set(x, phi);
3107
        rhs += phi * bound;
3108
      }
3109
    }
3110
  }
3111
  if(Debug.isOn("approx::gmi")){
3112
    Debug("approx::gmi") << "pre removeSlackVariables";
3113
    d_pad.d_cut.print(Debug("approx::gmi"));
3114
    Debug("approx::gmi") << endl;
3115
  }
3116
  removeAuxillaryVariables(d_vars, cut);
3117
3118
  if(Debug.isOn("approx::gmi")){
3119
    Debug("approx::gmi") << "post removeAuxillaryVariables";
3120
    d_pad.d_cut.print(Debug("approx::gmi"));
3121
    Debug("approx::gmi") << endl;
3122
  }
3123
  removeFixed(d_vars, d_pad.d_cut, explanation);
3124
3125
  if(Debug.isOn("approx::gmi")){
3126
    Debug("approx::gmi") << "post removeFixed";
3127
    d_pad.d_cut.print(Debug("approx::gmi"));
3128
    Debug("approx::gmi") << endl;
3129
  }
3130
  return false;
3131
}
3132
3133
void ApproxGLPK::tryCut(int nid, CutInfo& cut)
3134
{
3135
  Assert(!cut.reconstructed());
3136
  Assert(cut.getKlass() != RowsDeletedKlass);
3137
  bool failure = false;
3138
  switch(cut.getKlass()){
3139
  case GmiCutKlass:
3140
    failure = attemptGmi(nid, static_cast<const GmiInfo&>(cut));
3141
    break;
3142
  case MirCutKlass:
3143
    failure = attemptMir(nid, static_cast<const MirInfo&>(cut));
3144
    break;
3145
  case BranchCutKlass:
3146
    failure = attemptBranchCut(nid, dynamic_cast<const BranchCutInfo&>(cut));
3147
    break;
3148
  default:
3149
    break;
3150
  }
3151
  Assert(failure == d_pad.d_failure);
3152
3153
  if(!failure){
3154
    // move the pad to the cut
3155
    cut.setReconstruction(d_pad.d_cut);
3156
3157
    if(cut.getKlass() != BranchCutKlass){
3158
      std::set<ConstraintP>& exp = d_pad.d_explanation;
3159
      ConstraintCPVec asvec(exp.begin(), exp.end());
3160
      cut.swapExplanation(asvec);
3161
    }
3162
  }else{
3163
    Debug("approx") << "failure " << cut.getKlass() << endl;
3164
  }
3165
}
3166
3167
}  // namespace arith
3168
}  // namespace theory
3169
}  // namespace cvc5
3170
#endif /*#ifdef CVC5_USE_GLPK */
3171
/* End GPLK implementation. */