Interior-point-optimisation  1.0-1
Interior-pointoptimisationlibrary
PhaseIModel.cc
Go to the documentation of this file.
1 /*
2  * $Id: PhaseIModel.cc 216 2014-11-04 09:00:49Z jdl3 $
3  * Copyright (C) 2011–2013, 2014 John D Lamb
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include<config.h>
22 #endif
23 
24 #include"PhaseIModel.hpp"
25 
26 using namespace ipo::detail;
27 
28 PhaseIModel::PhaseIModel( Model& model ) : model( model ){
29  parameters = model.parameters;
32  variablesChanged = true;
33  setIndices(); // force model to add and recognise constraints, objective and variables
34 }
35 
36 double
37 PhaseIModel::getInitial_s( gsl::vector const& vector ) const {
38  // Make sure model is in sane state
39  model.setIndices();
40 
41  if( not vector or vector.size() != model.indexMap.size() )
42  throw IPOE( "PhaseIModel::getInitial_s(): vector size error." );
43 
44  double s { minusInfinity };
45 
46  // Check each constraint in base model
47  for( size_t j { 0 }; j < model.constraints.size(); ++j ){
48  auto& constraint = model.constraints[j];
49  double const upperBound { constraint.getUpperBound() };
50  double const lowerBound { constraint.getLowerBound() };
51  if( upperBound < infinity or lowerBound > minusInfinity ){
52  auto& constraintIndices_j = model.constraintIndices[j];
53  auto subvector = constraintIndices_j->get( vector );
54  double const functionValue { (*constraint.getFunction())( subvector )};
55  if( upperBound < infinity )
56  s = std::max( s, functionValue - upperBound );
57  double const lowerBound { constraint.getLowerBound() };
58  if( lowerBound > minusInfinity )
59  s = std::max( s, lowerBound - functionValue );
60  }
61  }
62 
63  // Also each variable in base model
64  for( auto& entry : model.indexMap ){
65  auto& variable = entry.first;
66  size_t index = entry.second;
67  double const upperBound { variable.getUpperBound() };
68  if( upperBound < infinity )
69  s = std::max( s, vector[index] - upperBound );
70  double const lowerBound { variable.getLowerBound() };
72  s = std::max( s, lowerBound - vector[index] );
73  }
74 
75  // replace s with a strictly larger value.
76  if( s < 1000 ) s += 0.1;
77  else s *= 1.001;
78 
79  return s;
80 }
81 
82 void
84  if( model.variablesChanged == false and variablesChanged == false ) return; // nothing to do
85  model.setIndices(); // Make sure model is up to date.
86 
87  // clear anything old out of this
88  constraints.clear();
89  equalityConstraints.clear();
90  constraintIndices.clear();
91  indexMap.clear();
92 
93  // We'll need to be able to look up Variable of this from Variable of model
94  crossIndex.clear();
95 
96  // First create Variables for this
97  for( auto& entry : model.indexMap ){
98  auto& modelVariable = entry.first;
99  size_t const INDEX { entry.second };
100  // Create new unbounded variable
101  Variable variable { *this, modelVariable.getName() };
102  // Set to value of base variable
103  variable.setValue( modelVariable.getValue() );
104  // Add to indexMap and to crossIndex
105  indexMap.insert( std::make_pair( variable, INDEX ) );
106  crossIndex.insert( std::make_pair( modelVariable, variable ) );
107  }
108  // Add new variable for phase I
109  Variable s { *this, "s: phase I" };
110  size_t const INDEX = indexMap.size();
111  indexMap.insert( std::make_pair( s, INDEX ) );
112 
113  // Now create an objective function
114  ipo_function::Function const& objectiveFunction = *model.objective.getFunction();
116  PhaseIObjectiveFunctionAndDerivatives( objectiveFunction ),
117  "Phase I objective" );
118  // Add variables to the new objective
119  auto& modelObjVars = model.objective.getVariables();
120  Array vars { *this, modelObjVars.getName() };
121  for( auto& var : modelObjVars ) vars.push_back( crossIndex.find( var )->second );
122  // Add new variable for phase I
123  vars.push_back( s );
124  objective.getVariables() = vars;
125 
126  // Create up to two constraints for each constraint in model
127  for( auto& constraint : model.constraints ){
128  ipo_function::Function& constraintFunction = *constraint.getFunction();
129  double const upperBound = constraint.getUpperBound();
130  if( upperBound < infinity ){
131  // create a new constraint
132  std::string name { constraint.getName() };
133  name += " (upper bound)";
134  Constraint c { *this, new ipo_function::detail::
135  PhaseIFunctionAndDerivatives { constraintFunction, upperBound, true }, name };
136  // Add variables to the new constraint
137  auto& cVars = constraint.getVariables();
138  Array vars { *this, cVars.getName() };
139  for( auto& var : cVars ) vars.push_back( crossIndex.find( var )->second );
140  // Add new variable for phase I
141  vars.push_back( s );
142  c.getVariables() = vars;
143  // Set upper bound to zero
144  c.setUpperBound( 0.0 );
145  // Add the constraint to the model
146  constraints.push_back( c );
147  }
148  double const lowerBound { constraint.getLowerBound() };
149  if( lowerBound > minusInfinity ){
150  // create a new constraint
151  std::string name { constraint.getName() };
152  name += " (lower bound)";
153  Constraint c { *this, new ipo_function::detail::
154  PhaseIFunctionAndDerivatives { constraintFunction, lowerBound, false }, name };
155  // Add variables to the new constraint
156  auto cVars = constraint.getVariables();
157  Array vars { *this, cVars.getName() };
158  for( auto& var : cVars ) vars.push_back( crossIndex.find( var )->second );
159  // Add new variable for phase I
160  vars.push_back( s );
161  // And assign
162  c.getVariables() = vars;
163  // Set upper bound to zero
164  c.setUpperBound( 0.0 );
165  // Add the constraint to the model
166  constraints.push_back( c );
167  }
168  }
169 
170  // Create a constraint for each equalityConstraint in model
171  for( auto& equalityConstraint : model.equalityConstraints ){
172  // create a new constraint
173  LinearConstraint linearConstraint( *this, equalityConstraint.getName() );
174  // Add variables to the new constraint
175  auto cVars = equalityConstraint.getVariables();
176  Array vars( *this, cVars.getName() );
177  for( auto& var : cVars ) vars.push_back( crossIndex.find( var )->second );
178  // Add new variable for phase I
179  vars.push_back( s );
180  // And assign
181  linearConstraint.getVariables() = vars;
182  // Set coefficients
183  linearConstraint.setCoefficient( s, 0 ); // should be redundant
184  for( auto& variable : cVars )
185  linearConstraint.setCoefficient( crossIndex.find( variable )->second,
186  equalityConstraint.getCoefficient( variable ) );
187  // Set bound
188  linearConstraint.setValue( equalityConstraint.getUpperBound() );
189  // Add the constraint to the model
190  equalityConstraints.push_back( linearConstraint );
191  }
192 
193  // Create up to two constraints for each variable constraint in model
194  for( auto& entry : model.indexMap ){
195  auto const& modelVar = entry.first;
196  double const upperBound = modelVar.getUpperBound();
197  if( upperBound < infinity ){
198  // create a new constraint
199  std::string name { modelVar.getName() };
200  name += " (variable upper bound)";
201  Constraint c { *this, new ipo_function::detail::
203  // Add variables to the new constraint
204  Array vars { *this, name };
205  // First the variable
206  vars.push_back( crossIndex.find( modelVar )->second );
207  // Then s
208  vars.push_back( s );
209  // And assign
210  c.getVariables() = vars;
211  // Set upper bound to zero
212  c.setUpperBound( 0.0 );
213  // Add the constraint to the model
214  constraints.push_back( c );
215  }
216  double const lowerBound = modelVar.getLowerBound();
217  if( lowerBound > minusInfinity ){
218  // create a new constraint
219  std::string name { modelVar.getName() };
220  name += " (variable lower bound)";
221  Constraint c { *this, new ipo_function::detail::
223  // Add variables to the new constraint
224  Array vars { *this, name };
225  // First the variable
226  vars.push_back( crossIndex.find( modelVar )->second );
227  // Then s
228  vars.push_back( s );
229  // And assign
230  c.getVariables() = vars;
231  // Set upper bound to zero
232  c.setUpperBound( 0.0 );
233  // Add the constraint to the model
234  constraints.push_back( c );
235  }
236  }
237  // Next line need because objective/constraint.getVariables()
238  // sets model.variablesChanged to true even though we have not
239  // changed them.
240  model.variablesChanged = false;
241 
242  // Set objectiveIndices
244 
245  // Set constraintIndices (including constraints with no upper or lower bound)
247 
248  // Now indices are up to date; no need to check again
249  variablesChanged = false;
250 }
251 
252 bool
254  // Make sure phase I model is up to date.
255  setIndices(); // This also sets the values of variables here
256 
257  if( not findEqualityConstraintFeasibleSolution() ) return false; // not even Phase I feasible
258  if( isStrictlyFeasible() ) return true; // not a good idea to try to optimise.
259 
260  // tell phase I model to stop in Newton descent when a feasible solution is found
262 
263  // get initial variable values.
264  size_t const SIZE { indexMap.size() };
265  gsl::vector vector( SIZE );
266  for( auto& entry : indexMap ){
267  auto& variable = entry.first;
268  auto index = entry.second;
269  vector[index] = variable.getValue();
270  }
271  // s would be set to zero or last-known value: correct this
272  auto subvector = vector.subvector( 0, SIZE - 1 );
273  vector[SIZE - 1] = getInitial_s( subvector );
274 
275  // set variables
276  setVariablesFromVector( vector );
277 
278  // minimise and return
279  if( vector[SIZE - 1] < 0 or minimise() ){ // minimise sets the values of variables in this
280  // copy from variables of this to variables of model.
281  for( auto& entry : model.indexMap ){
282  auto& variable = entry.first;
283  // copy variable values directly
284  const_cast<Variable&>( variable )
285  .setValue( crossIndex.find( variable )->second.getValue() );
286  }
287  return true;
288  }
289  // Just in case
290  return false;
291 }
292 
293 bool
295  double constexpr ROOTEPS { std::sqrt( std::numeric_limits<double>::epsilon() ) };
296  size_t const ROWS { equalityConstraints.size() };
297  if( ROWS == 0 ) return true; // nothing to do
298  setIndices(); // should return immediately: but allow possibility that indices not set
299  size_t const COLS { model.indexMap.size() };
300  if( COLS == 0 ) return true; // nothing to do
301 
302  // Create matrix A, vectors x and b from equality constraints
303  gsl::matrix A { std::max( ROWS, COLS ), COLS };
304  gsl::matrix V { COLS, COLS };
305  A.set_all( 0 );
306  gsl::vector x( COLS );
307  gsl::vector S( COLS );
308  gsl::vector b( std::max( ROWS, COLS ) );
309  b.set_all( 0 );
310 
311  for( size_t i { 0 }; i < ROWS; ++i ){
312  auto equalityConstraint = equalityConstraints[i];
313  auto vars = equalityConstraint.getVariables();
314  // set coefficients
315  for( auto& var : vars ){
316  size_t const index { indexMap[var] };
317  if( index < COLS ) // ignore s
318  A.set( i, indexMap[var], equalityConstraint.getCoefficient( var ) );
319  }
320  // set values
321  b[i] = equalityConstraint.getUpperBound(); // == getLowerBound()
322  }
323  auto handler = gsl::exception::set_handler_gsl_exceptions();
324  try {
325  // Solve using SVD
326  gsl::linalg::SV_decomp( A, V, S, x );
327  for( auto& entry : S )
328  if( std::fabs( entry ) < ROOTEPS ) entry = 0;
329  gsl::linalg::SV_solve( A, V, S, b, x );
330  } catch( gsl::exception e ){
331  return false; // Singular value decomposition failed.
332  } catch( ... ) {
333  gsl::exception::set_handler( handler );
334  throw IPOE( "ipo::detail::PhaseIModel::findEqualityConstraintFeasibleSolution(): "
335  "unknown exception encountered." );
336  }
337  gsl::exception::set_handler( handler );
338 
339  // Copy values back to variables from x
340  for( auto& p : model.indexMap ){
341  auto var = p.first;
342  auto q = crossIndex.find( var );
343  if( crossIndex.end() == q )
344  // This should not happen because PhaseIModel variables and Model variables should
345  // not be modified independently
346  throw IPOE( "ipo::detail::PhaseIModel::findEqualityConstraintFeasibleSolution(): "
347  "variable in phase I model fails to match variable in model." );
348  Variable& pIvar = const_cast<Variable&>( q->second );
349  auto r = indexMap.find( pIvar );
350  if( indexMap.end() == r )
351  // This should not happen because PhaseIModel variables and Model variables should
352  // not be modified independently
353  throw IPOE( "ipo::detail::PhaseIModel::findEqualityConstraintFeasibleSolution(): "
354  "variable in phase I model not contained in its index map." );
355  size_t const index { r->second };
356  if( index < COLS ){ // ignore s
357  double const d { x[index] };
358  // set value of variable: notice that s is not set
359  pIvar.setValue( d );
360  // also set in model
361  var.setValue( d );
362  }
363  }
364 
365  return true;
366 }
std::map< Variable, size_t > indexMap
Used internally to match the variables in each variable to indices in a vector.
Definition: Model.hpp:903
void setValue(double const value)
Set value of variable.
Definition: Variable.cc:64
virtual void setIndices()
Sets the values of objectiveIndices, constraintIndices and variableIndices from constraints.
Definition: Model.cc:92
std::vector< ipo::LinearConstraint > equalityConstraints
The set of equality constraints.
Definition: Model.hpp:876
void push_back(value_type const &value)
Insert value at end of array.
Definition: Array.cc:323
bool minimise()
Try to minimise the objective function subject to the constraints.
Definition: Model.hpp:758
detail::LineSearch::Parameters lineSearchParameters
Parameters used by backtracking line search algorithm during optimisation.
Definition: Model.hpp:916
double constexpr infinity
Infinity: use for unbounded variables.
Definition: Var.hpp:30
Namespace for details of ipo_function that are not normally needed to construct and solve a convex op...
bool variablesChanged
Value that indicates if any variables, objective or constraints have been modified in such a way that...
Definition: Model.hpp:908
bool findFeasibleSolution()
Find a feasible solution to the base Model by partial optimisation of the PhaseIModel and copy the re...
Definition: PhaseIModel.cc:253
detail::NewtonDescent::Parameters newtonDescentParameters
Parameters used by Newton descent algorithm during optimisation.
Definition: Model.hpp:912
detail::NewtonDescent::Parameters & getNewtonDescentParameters()
Get Newton descent parameters by reference.
Definition: Model.cc:331
Array & getVariables()
Get variables used by Objective function.
Definition: Objective.cc:75
Objective objective
The objective function.
Definition: Model.hpp:880
Model & model
The Model this is based on.
Definition: PhaseIModel.hpp:76
std::map< Variable, Variable > crossIndex
A map so we can look up Variable of this from Variable of model.
Definition: PhaseIModel.hpp:80
Function for Phase I (feasibility) of interior-point optimisation.
Class to represent a linear combination as an Objective or Constraint.
#define IPOE(message)
Macro to allow file and line names in exceptions.
bool isStrictlyFeasible()
Test whether current values of variables give a feasible solution.
Definition: Model.cc:175
void setVariablesFromVector(gsl::vector const &vector)
Set variable values from a vector.
Definition: Model.cc:224
std::string getName() const
Get name of variable.
Definition: Variable.cc:49
std::vector< Constraint > constraints
The set of constraints.
Definition: Model.hpp:872
Class for a constraint function.
Definition: Constraint.hpp:40
Model an interior-point optimisation problem.
Definition: Model.hpp:325
Class for an objective function.
Definition: Objective.hpp:37
Namespace for classes that handle details of interior-point optimisation that are not ordinarily acce...
Definition: Model.hpp:316
Function for Phase I (feasibility) of interior-point optimisation.
virtual void setIndices()
Sets the values of objectiveIndices, constraintIndices and variableIndices from Model.
Definition: PhaseIModel.cc:83
This class computes a function at a vector.
Definition: Function.hpp:38
PhaseIModel(Model &model)
Constructor.
Definition: PhaseIModel.cc:28
void setObjectiveIndices()
Set objectiveIndices.
Definition: Model.hpp:847
This class represents a variable.
Definition: Variable.hpp:36
std::string getName() const
Get name of variable.
Definition: Array.cc:217
virtual ::ipo_function::Function * getFunction()
Get function.
bool findEqualityConstraintFeasibleSolution()
Find a solution that satisfies equality constraints.
Definition: PhaseIModel.cc:294
This class represents an array of Variable objects.
Definition: Array.hpp:45
void setConstraintIndices()
Set constraintIndices.
Definition: Model.cc:429
data lowerBound
Definition: Variable.cc:39
double constexpr minusInfinity
Negative infinity: use for unbounded variables.
Definition: Var.hpp:34
data name
Definition: Variable.cc:37
data upperBound
Definition: Variable.cc:38
Parameters parameters
Parameters for interior point optimisation.
Definition: Model.hpp:399
double getInitial_s(gsl::vector const &vector) const
Get an initial value for s from a vector of length one less than the number of variables of this mode...
Definition: PhaseIModel.cc:37
std::vector< std::shared_ptr< Subvector > > constraintIndices
Used internally to match the variables in each constraint to indices in a vector: (*constraintIndices...
Definition: Model.hpp:892
void setPhaseI(bool const phaseI)
This function is designed to be used to help find an initial (phase I) feasible solution.