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