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