From f22fe9e14cd3ac173060e63a2004e717b00ad6ac Mon Sep 17 00:00:00 2001 From: Guy Katz Date: Tue, 29 May 2018 15:12:53 +0300 Subject: [PATCH 1/4] ensure non-basics stay within their bounds --- src/engine/Tableau.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/engine/Tableau.cpp b/src/engine/Tableau.cpp index a50921c09..666fcd9af 100644 --- a/src/engine/Tableau.cpp +++ b/src/engine/Tableau.cpp @@ -1694,11 +1694,31 @@ void Tableau::updateAssignmentForPivot() // If the change ratio is 0, just maintain the current assignment if ( FloatUtils::isZero( _changeRatio ) ) { + // This should only happen when the basic variable is pressed against + // one of its bounds + ASSERT( _basicStatus[_leavingVariable] == Tableau::AT_UB || + _basicStatus[_leavingVariable] == Tableau::AT_LB ); + double basicAssignment = _basicAssignment[_leavingVariable]; double nonBasicAssignment = _nonBasicAssignment[_enteringVariable]; + // Due to numerical stability, it may be that the basic variable + // is slightly too low or too great. If so, adjust it. + double lb = _lowerBounds[_basicIndexToVariable[_leavingVariable]]; + double ub = _upperBounds[_basicIndexToVariable[_leavingVariable]]; + + if ( FloatUtils::lt( basicAssignment, lb ) ) + { + basicAssignment = lb; + } + else if ( FloatUtils::gt( basicAssignment, ub ) ) + { + basicAssignment = ub; + } + _basicAssignment[_leavingVariable] = nonBasicAssignment; _nonBasicAssignment[_enteringVariable] = basicAssignment; + return; } From 867180c207fca187e29c058dd35b1b37f0cb7f36 Mon Sep 17 00:00:00 2001 From: Guy Katz Date: Tue, 29 May 2018 15:34:22 +0300 Subject: [PATCH 2/4] assertions --- src/engine/Tableau.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/Tableau.cpp b/src/engine/Tableau.cpp index 666fcd9af..3211144a0 100644 --- a/src/engine/Tableau.cpp +++ b/src/engine/Tableau.cpp @@ -1697,7 +1697,9 @@ void Tableau::updateAssignmentForPivot() // This should only happen when the basic variable is pressed against // one of its bounds ASSERT( _basicStatus[_leavingVariable] == Tableau::AT_UB || - _basicStatus[_leavingVariable] == Tableau::AT_LB ); + _basicStatus[_leavingVariable] == Tableau::AT_LB || + _basicStatus[_leavingVariable] == Tableau::BETWEEN + ); double basicAssignment = _basicAssignment[_leavingVariable]; double nonBasicAssignment = _nonBasicAssignment[_enteringVariable]; From b6e4d3655694d04baae2778f734ac6f1111fce59 Mon Sep 17 00:00:00 2001 From: Guy Katz Date: Thu, 31 May 2018 13:53:28 +0300 Subject: [PATCH 3/4] Bug fix: When the engine iterates over several possible pivot candidates and ends up picking not the last one, make sure to store and restore the change ratio that was calculated for the selected pivot --- src/engine/Engine.cpp | 4 ++++ src/engine/ITableau.h | 1 + src/engine/Tableau.cpp | 32 ++++++++++++++++++++++++++------ src/engine/Tableau.h | 1 + src/engine/tests/MockTableau.h | 2 ++ 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index af8e9e76a..2780adf76 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -312,6 +312,7 @@ void Engine::performSimplexStep() unsigned tries = GlobalConfiguration::MAX_SIMPLEX_PIVOT_SEARCH_ITERATIONS; Set excludedEnteringVariables; unsigned bestLeaving = 0; + double bestChangeRatio = 0.0; while ( tries > 0 ) { @@ -340,6 +341,7 @@ void Engine::performSimplexStep() { bestEntering = _tableau->getEnteringVariableIndex(); bestLeaving = _tableau->getLeavingVariableIndex(); + bestChangeRatio = _tableau->getChangeRatio(); memcpy( _work, _tableau->getChangeColumn(), sizeof(double) * _tableau->getM() ); break; } @@ -352,6 +354,7 @@ void Engine::performSimplexStep() bestEntering = _tableau->getEnteringVariableIndex(); bestPivotEntry = pivotEntry; bestLeaving = leavingIndex; + bestChangeRatio = _tableau->getChangeRatio(); memcpy( _work, _tableau->getChangeColumn(), sizeof(double) * _tableau->getM() ); } @@ -397,6 +400,7 @@ void Engine::performSimplexStep() _tableau->setEnteringVariableIndex( bestEntering ); _tableau->setLeavingVariableIndex( bestLeaving ); _tableau->setChangeColumn( _work ); + _tableau->setChangeRatio( bestChangeRatio ); bool fakePivot = _tableau->performingFakePivot(); diff --git a/src/engine/ITableau.h b/src/engine/ITableau.h index edc6ee69c..9ad210c38 100644 --- a/src/engine/ITableau.h +++ b/src/engine/ITableau.h @@ -121,6 +121,7 @@ class ITableau virtual unsigned getLeavingVariable() const = 0; virtual unsigned getLeavingVariableIndex() const = 0; virtual double getChangeRatio() const = 0; + virtual void setChangeRatio( double changeRatio ) = 0; virtual bool performingFakePivot() const = 0; virtual void performPivot() = 0; virtual double ratioConstraintPerBasic( unsigned basicIndex, double coefficient, bool decrease ) = 0; diff --git a/src/engine/Tableau.cpp b/src/engine/Tableau.cpp index 3211144a0..59763fc5b 100644 --- a/src/engine/Tableau.cpp +++ b/src/engine/Tableau.cpp @@ -889,6 +889,11 @@ double Tableau::getChangeRatio() const return _changeRatio; } +void Tableau::setChangeRatio( double changeRatio ) +{ + _changeRatio = changeRatio; +} + void Tableau::computeChangeColumn() { // _a gets the entering variable's column in A @@ -1694,12 +1699,27 @@ void Tableau::updateAssignmentForPivot() // If the change ratio is 0, just maintain the current assignment if ( FloatUtils::isZero( _changeRatio ) ) { - // This should only happen when the basic variable is pressed against - // one of its bounds - ASSERT( _basicStatus[_leavingVariable] == Tableau::AT_UB || - _basicStatus[_leavingVariable] == Tableau::AT_LB || - _basicStatus[_leavingVariable] == Tableau::BETWEEN - ); + ASSERT( !performingFakePivot() ); + + DEBUG({ + // This should only happen when the basic variable is pressed against + // one of its bounds + if ( !( _basicStatus[_leavingVariable] == Tableau::AT_UB || + _basicStatus[_leavingVariable] == Tableau::AT_LB || + _basicStatus[_leavingVariable] == Tableau::BETWEEN + ) ) + { + printf( "Assertion violation!\n" ); + printf( "Basic (leaving) variable is: %u\n", _basicIndexToVariable[_leavingVariable] ); + printf( "Basic assignment: %.10lf. Bounds: [%.10lf, %.10lf]\n", + _basicAssignment[_leavingVariable], + _lowerBounds[_basicIndexToVariable[_leavingVariable]], + _upperBounds[_basicIndexToVariable[_leavingVariable]] ); + printf( "Basic status: %u\n", _basicStatus[_leavingVariable] ); + printf( "leavingVariableIncreases = %s", _leavingVariableIncreases ? "yes" : "no" ); + exit( 1 ); + } + }); double basicAssignment = _basicAssignment[_leavingVariable]; double nonBasicAssignment = _nonBasicAssignment[_enteringVariable]; diff --git a/src/engine/Tableau.h b/src/engine/Tableau.h index 8d18e036a..f62ce6ad4 100644 --- a/src/engine/Tableau.h +++ b/src/engine/Tableau.h @@ -208,6 +208,7 @@ class Tableau : public ITableau, public IBasisFactorization::BasisColumnOracle unsigned getLeavingVariable() const; unsigned getLeavingVariableIndex() const; double getChangeRatio() const; + void setChangeRatio( double changeRatio ); /* Returns true iff the current iteration is a fake pivot, i.e. the diff --git a/src/engine/tests/MockTableau.h b/src/engine/tests/MockTableau.h index 9ca7e1cca..9ae91f22a 100644 --- a/src/engine/tests/MockTableau.h +++ b/src/engine/tests/MockTableau.h @@ -260,6 +260,8 @@ class MockTableau : public ITableau } double getChangeRatio() const { return 0; } + void setChangeRatio( double /* changeRatio */ ) {} + void performPivot() {} bool performingFakePivot() const { From a09688b943b01005da6e0e4cc872433f7928bfed Mon Sep 17 00:00:00 2001 From: Guy Katz Date: Thu, 31 May 2018 15:54:34 +0300 Subject: [PATCH 4/4] removing the adjustment of non-basic values, which seems to reduce numerical stability --- src/engine/Tableau.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/engine/Tableau.cpp b/src/engine/Tableau.cpp index 59763fc5b..0742c91cc 100644 --- a/src/engine/Tableau.cpp +++ b/src/engine/Tableau.cpp @@ -1723,24 +1723,8 @@ void Tableau::updateAssignmentForPivot() double basicAssignment = _basicAssignment[_leavingVariable]; double nonBasicAssignment = _nonBasicAssignment[_enteringVariable]; - - // Due to numerical stability, it may be that the basic variable - // is slightly too low or too great. If so, adjust it. - double lb = _lowerBounds[_basicIndexToVariable[_leavingVariable]]; - double ub = _upperBounds[_basicIndexToVariable[_leavingVariable]]; - - if ( FloatUtils::lt( basicAssignment, lb ) ) - { - basicAssignment = lb; - } - else if ( FloatUtils::gt( basicAssignment, ub ) ) - { - basicAssignment = ub; - } - _basicAssignment[_leavingVariable] = nonBasicAssignment; _nonBasicAssignment[_enteringVariable] = basicAssignment; - return; }