Skip to content

Commit f64df79

Browse files
committed
Add artificial bound for free variables
1 parent 73370b1 commit f64df79

6 files changed

Lines changed: 100 additions & 9 deletions

File tree

highs/ipm/hipo/ipm/Model.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,15 +277,12 @@ void Model::print(const Logger& logger) const {
277277
// compute max and min for bounds intervals
278278
double boundmin = kHighsInf;
279279
double boundmax = 0.0;
280-
Int free_vars = 0;
281280
for (Int i = 0; i < n_; ++i) {
282281
if (std::isfinite(lower_[i]) && std::isfinite(upper_[i])) {
283282
const double diff = std::abs(upper_[i] - lower_[i]);
284283
boundmin = std::min(boundmin, diff);
285284
boundmax = std::max(boundmax, diff);
286285
}
287-
288-
if (!std::isfinite(lower_[i]) && !std::isfinite(upper_[i])) free_vars++;
289286
}
290287
if (std::isinf(boundmin)) boundmin = 0.0;
291288

@@ -345,7 +342,6 @@ void Model::print(const Logger& logger) const {
345342

346343
if (logger.debug(1)) {
347344
preprocessor_.print(log_stream);
348-
log_stream << textline("Free variables:") << integer(free_vars) << '\n';
349345

350346
log_stream << "Expected nnz: ";
351347
log_stream << "AS " << sci(AS_nz_, 0, 1) << "; ";
@@ -421,4 +417,32 @@ void Model::printDense() const {
421417
printf("offset %6.2f\n", offset_);
422418
}
423419

420+
void Model::adjustFreeVars(std::vector<double>& x, std::vector<double>& xl,
421+
std::vector<double>& xu, const Logger& logger) {
422+
for (Int i = 0; i < n_; ++i) {
423+
if (is_free_[i]) {
424+
if (x[i] < lower_[i] * kFreeVarsCloseRatio) {
425+
// getting close to lower bound
426+
const double new_lower = std::min(lower_[i] * kFreeVarsIncreaseBound,
427+
x[i] / kFreeVarsCloseRatio);
428+
logger.printDetailed(
429+
"Free var %d is at %.1e with lb %.1e, lb changed to %.1e\n", i,
430+
x[i], lower_[i], new_lower);
431+
lower_[i] = new_lower;
432+
xl[i] = x[i] - lower_[i];
433+
}
434+
if (x[i] > upper_[i] * kFreeVarsCloseRatio) {
435+
// getting close to upper bound
436+
const double new_upper = std::max(upper_[i] * kFreeVarsIncreaseBound,
437+
x[i] / kFreeVarsCloseRatio);
438+
logger.printDetailed(
439+
"Free var %d is at %.1e with ub %.1e, ub changed to %.1e\n", i,
440+
x[i], upper_[i], new_upper);
441+
upper_[i] = new_upper;
442+
xu[i] = upper_[i] - x[i];
443+
}
444+
}
445+
}
446+
}
447+
424448
} // namespace hipo

highs/ipm/hipo/ipm/Model.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
#include <string>
66
#include <vector>
77

8-
#include "ipm/hipo/auxiliary/Logger.h"
98
#include "PreProcess.h"
109
#include "ipm/hipo/auxiliary/IntConfig.h"
10+
#include "ipm/hipo/auxiliary/Logger.h"
1111
#include "ipm/ipx/lp_solver.h"
1212
#include "lp_data/HighsLp.h"
1313
#include "model/HighsHessian.h"
@@ -65,6 +65,8 @@ class Model {
6565
std::vector<double> one_norm_cols_, one_norm_rows_, inf_norm_cols_,
6666
inf_norm_rows_;
6767

68+
std::vector<bool> is_free_;
69+
6870
void preprocess();
6971
Int checkData() const;
7072
void computeNorms();
@@ -90,6 +92,9 @@ class Model {
9092

9193
void nzBounds();
9294

95+
void adjustFreeVars(std::vector<double>& x, std::vector<double>& xl,
96+
std::vector<double>& xu, const Logger& logger);
97+
9398
// Check if variable has finite lower/upper bound
9499
bool hasLb(Int j) const { return std::isfinite(lower_[j]); }
95100
bool hasUb(Int j) const { return std::isfinite(upper_[j]); }
@@ -123,6 +128,7 @@ class Model {
123128
Int64 nzAS() const { return AS_nz_; }
124129
Int64 nzNElb() const { return NE_nz_lb_; }
125130
Int64 nzNEub() const { return NE_nz_ub_; }
131+
bool free(Int i) const { return is_free_[i]; }
126132

127133
Int loadIntoIpx(ipx::LpSolver& lps) const;
128134

@@ -131,6 +137,7 @@ class Model {
131137
friend struct PreprocessFixedVars;
132138
friend struct PreprocessScaling;
133139
friend struct PreprocessFormulation;
140+
friend struct PreprocessFreeVars;
134141
};
135142

136143
} // namespace hipo

highs/ipm/hipo/ipm/Parameters.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const double kSmallScalingCoeff = 1e-4;
5151
const double kLargeScalingCoeff = 1e4;
5252
const double kSmallBoundDiff = 1e-3;
5353

54+
// parameters for free variables
55+
const double kFreeVarsInitialBound = 1e4;
56+
const double kFreeVarsCloseRatio = 0.5;
57+
const double kFreeVarsIncreaseBound = 10.0;
58+
5459
// static regularisation
5560
struct Regularisation {
5661
double primal = 1e-12;

highs/ipm/hipo/ipm/PreProcess.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -483,11 +483,11 @@ void PreprocessScaling::undo(PreprocessorPoint& point, const Model& model,
483483

484484
// set variables that were ignored
485485
for (Int i = 0; i < n_post; ++i) {
486-
if (!model.hasLb(i)) {
486+
if (!model.hasLb(i) || model.is_free_[i]) {
487487
point.xl[i] = kHighsInf;
488488
point.zl[i] = 0.0;
489489
}
490-
if (!model.hasUb(i)) {
490+
if (!model.hasUb(i) || model.is_free_[i]) {
491491
point.xu[i] = kHighsInf;
492492
point.zu[i] = 0.0;
493493
}
@@ -561,11 +561,11 @@ void PreprocessFormulation::undo(PreprocessorPoint& point, const Model& model,
561561

562562
// force unused entries to have correct value
563563
for (Int i = 0; i < n_pre; ++i) {
564-
if (!model.hasLb(i)) {
564+
if (!model.hasLb(i) || model.is_free_[i]) {
565565
point.xl[i] = kHighsInf;
566566
point.zl[i] = 0.0;
567567
}
568-
if (!model.hasUb(i)) {
568+
if (!model.hasUb(i) || model.is_free_[i]) {
569569
point.xu[i] = kHighsInf;
570570
point.zu[i] = 0.0;
571571
}
@@ -617,6 +617,49 @@ void PreprocessFormulation::print(std::stringstream& stream) const {
617617
stream << "Added " << n_post - n_pre << " slacks\n";
618618
}
619619

620+
void PreprocessFreeVars::apply(Model& model) {
621+
Int& n = model.n_;
622+
Int& m = model.m_;
623+
HighsSparseMatrix& A = model.A_;
624+
std::vector<double>& b = model.b_;
625+
std::vector<double>& c = model.c_;
626+
std::vector<double>& lower = model.lower_;
627+
std::vector<double>& upper = model.upper_;
628+
std::vector<char>& constraints = model.constraints_;
629+
HighsHessian& Q = model.Q_;
630+
std::vector<bool>& is_free = model.is_free_;
631+
632+
n_pre = n;
633+
m_pre = m;
634+
635+
is_free.assign(n, false);
636+
637+
for (Int i = 0; i < n; ++i) {
638+
if (!std::isfinite(lower[i]) && !std::isfinite(upper[i]) &&
639+
lower[i] != upper[i]) {
640+
// free variable
641+
is_free[i] = true;
642+
++free_vars;
643+
lower[i] = -kFreeVarsInitialBound;
644+
upper[i] = kFreeVarsInitialBound;
645+
}
646+
}
647+
648+
n_post = n;
649+
m_post = m;
650+
}
651+
652+
void PreprocessFreeVars::undo(PreprocessorPoint& point, const Model& model,
653+
const Iterate& it) const {
654+
point.assertConsistency(n_post, m_post);
655+
//
656+
point.assertConsistency(n_pre, m_pre);
657+
}
658+
659+
void PreprocessFreeVars::print(std::stringstream& stream) const {
660+
if (free_vars > 0) stream << "Found " << free_vars << " free variables\n";
661+
}
662+
620663
#define APPLY_ACTION(T) \
621664
stack.push_back(std::unique_ptr<PreprocessAction>(new T)); \
622665
stack.back()->apply(model);
@@ -629,6 +672,7 @@ void Preprocessor::apply(Model& model) {
629672
APPLY_ACTION(PreprocessEmptyRows);
630673
APPLY_ACTION(PreprocessScaling);
631674
APPLY_ACTION(PreprocessFormulation);
675+
APPLY_ACTION(PreprocessFreeVars);
632676
}
633677
void Preprocessor::undo(PreprocessorPoint& point, const Model& model,
634678
const Iterate& it) const {

highs/ipm/hipo/ipm/PreProcess.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ struct PreprocessFormulation : public PreprocessAction {
8080
void print(std::stringstream& stream) const override;
8181
};
8282

83+
struct PreprocessFreeVars : public PreprocessAction {
84+
Int free_vars{};
85+
86+
void apply(Model& model) override;
87+
void undo(PreprocessorPoint& point, const Model& model,
88+
const Iterate& it) const override;
89+
void print(std::stringstream& stream) const override;
90+
};
91+
8392
struct Preprocessor {
8493
std::vector<std::unique_ptr<PreprocessAction>> stack;
8594

highs/ipm/hipo/ipm/Solver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ bool Solver::prepareIter() {
191191

192192
++iter_;
193193

194+
model_.adjustFreeVars(it_->x, it_->xl, it_->xu, logger_);
195+
194196
// Clear Newton direction
195197
it_->clearDir();
196198

0 commit comments

Comments
 (0)