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