From 2ea95d42a147747c79cade5c9b7f299d47367be7 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 27 Nov 2012 23:43:34 +0200 Subject: [PATCH] Make LinearProgram robust against degenerate solutions If the container is exactly the size required by the widgets, there will be redundant equations, which causes a degeneracy in the phase 1 solution. --- source/layout.cpp | 145 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 110 insertions(+), 35 deletions(-) diff --git a/source/layout.cpp b/source/layout.cpp index 2972ec8..34afd72 100644 --- a/source/layout.cpp +++ b/source/layout.cpp @@ -1,3 +1,4 @@ +#include #include #include "container.h" #include "layout.h" @@ -45,9 +46,12 @@ public: Row add_row(); Row operator[](unsigned); Row get_objective_row(); - bool solve(); float get_variable(unsigned); + bool solve(); private: + void prepare_columns(); + void add_artificial_variables(); + void remove_artificial_variables(); unsigned find_minimal_ratio(unsigned); void make_basic_column(unsigned, unsigned); bool pivot(); @@ -380,34 +384,9 @@ bool Layout::LinearProgram::solve() objective variable is kept implicit, as it would never change during the execution of the algorithm. */ - /* Force all columns fully into existence and relocate objective row to - bottom in preparation of phase 1. A new objective row is calculated by - pricing out the constraint rows. */ - for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) - { - float objective = 0.0f; - if(!i->values.empty()) - { - objective = i->values.front(); - i->values.front() = 0.0f; - for(vector::iterator j=i->values.begin(); j!=i->values.end(); ++j) - i->values.front() += *j; - } - i->values.resize(n_rows+1, 0.0f); - i->values.back() = objective; - } + prepare_columns(); - /* Create artificial variables for phase 1. This ensures that each row has - a basic variable associated with it. The original objective row already - contains the implicit objective variable, which is basic. */ - columns.resize(n_columns+n_rows-1); - columns.back() = columns[n_columns-1]; - columns[n_columns-1].values.clear(); - for(unsigned i=1; i basic_coeff(n_rows, 0.0f); + const vector &constants = columns.back().values; + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + { + if(i->values.size()>=2 && i->values.back()!=0.0f && (constants.size()values.size() || i->values.back()*constants[i->values.size()-1]>=0.0f) && basic_coeff[i->values.size()-1]==0.0f) + { + bool basic = true; + for(unsigned j=1; (basic && j+1values.size()); ++j) + basic = (i->values[j]==0.0f); + if(basic) + { + i->basic = i->values.size()-1; + basic_coeff[i->basic] = -i->values.front()/i->values.back(); + i->values.clear(); + } + } + } + + // Price out the newly-created basic variables. + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + if(!i->values.empty()) + { + for(unsigned j=0; jvalues.size(); ++j) + i->values.front() += basic_coeff[j]*i->values[j]; + } +} + +void Layout::LinearProgram::add_artificial_variables() +{ + vector artificial_rows(n_rows-1); + for(unsigned i=0; i::iterator i=columns.begin(); i!=columns.end(); ++i) + if(i->basic) + artificial_rows[i->basic-1] = 0; + artificial_rows.erase(std::remove(artificial_rows.begin(), artificial_rows.end(), 0), artificial_rows.end()); + + /* Force all non-basic columns fully into existence and relocate objective + row to bottom in preparation of phase 1. A new objective row is calculated + by pricing out the constraint rows. */ + for(vector::iterator i=columns.begin(); i!=columns.end(); ++i) + if(!i->basic) + { + float objective = 0.0f; + if(!i->values.empty()) + { + objective = i->values.front(); + i->values.front() = 0.0f; + for(vector::iterator j=artificial_rows.begin(); j!=artificial_rows.end(); ++j) + if(*jvalues.size()) + i->values.front() += i->values[*j]; + } + i->values.resize(n_rows+1, 0.0f); + i->values.back() = objective; + } + + if(artificial_rows.empty()) + return; + + /* Create artificial variables for phase 1. This ensures that each row has + a basic variable associated with it. The original objective row already + contains the implicit objective variable, which is basic. */ + columns.resize(n_columns+artificial_rows.size()); + columns.back() = columns[n_columns-1]; + columns[n_columns-1].values.clear(); + for(unsigned i=0; ivalues.front() = i->values.back(); i->values.pop_back(); } - - // Solve the phase 2 problem. We already know it to be feasible. - while(pivot()) ; - - solved = true; - - return true; } unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c) -- 2.43.0