EigenOpt 1.0.0
Loading...
Searching...
No Matches
simplex.hxx
Go to the documentation of this file.
1#pragma once
2
7
8
9namespace EigenOpt {
10
11namespace simplex {
12
13
14template<class Scalar, class D1, class D2, class D3, class D4>
16 const Eigen::DenseBase<D1>& _f,
17 const Eigen::DenseBase<D2>& _C,
18 const Eigen::DenseBase<D3>& _d,
19 Eigen::DenseBase<D4>& x,
20 std::string& halt_reason,
21 const Scalar& small_number,
22 const Scalar& large_number
23 )
24{
25 // Typedefs 'VectorXs' and 'MatrixXs' - to make my life easier.
26 EigenOptTypedefs(Scalar);
27
28 // Check consistency of the numeric parameters
29 eigen_assert(small_number>0 && "PARAMETER 'small_number' MUST BE POSITIVE");
30
31 EigenOpt_SIMPLEX_DBG("Attempting to solve minimization problem with following parameters:");
32 EigenOpt_SIMPLEX_DBG("Objective coefficients: " << _f.transpose());
33 EigenOpt_SIMPLEX_DBG("C:" << std::endl << _C);
34 EigenOpt_SIMPLEX_DBG("d: " << _d.transpose());
35
36 // Cast objective coefficients into dense format & store the number of decision variables.
37 VectorXs f = _f;
38 int n = f.rows();
39
40 // We allow an empty objective, which means that the objective should be filled with zeros.
41 // In this case, the number of decision variables is deduced from the constraint matrix.
42 if(n == 0) {
43 n = _C.cols();
44 eigen_assert(n>0 && "THE PROBLEM DOES NOT HAVE ANY VARIABLE");
45 f = VectorXs::Zero(n);
46 EigenOpt_SIMPLEX_DBG("Objective coefficients omitted, assuming they are all zero");
47 }
48
49 // Make sure dimensions are consistent.
50 eigen_assert(_C.rows()==_d.rows() && "C MATRIX AND D VECTOR HAVE DIFFERENT NUMBER OF ROWS");
51 eigen_assert(_C.cols()==n && "C MATRIX HAS WRONG NUMBER OF COLUMNS");
52
53 // Since this function does not make prior assumptions of the bounds of the
54 // decision variables, a problem with no constraints is ill-defined - the
55 // "solution" is to let decision variables be infinite.
56 if(_C.rows() == 0) {
57 halt_reason = "No constraints given, the problem is ill-defined";
58 return false;
59 }
60
61 // Store the original constraints, removing degenerate ones (those in the
62 // form 0*x<=k, with k >=0) and detecting infeasible ones (0*x<=k<0).
63 MatrixXs C(_C.rows(), _C.cols());
64 VectorXs d(_d.rows());
65
66 // Remove degenerate constraints in the form 0*x<=k.
67 int m = 0;
68 for(unsigned int i=0; i<_C.rows(); i++) {
69 if(!_C.row(i).isZero(small_number)) {
70 // Row i is not degenerate: keep the constraint.
71 C.row(m).noalias() = _C.row(i);
72 d(m) = _d(i);
73 m++;
74 }
75 else if(_d(i) < 0) {
76 // Row i is degenerate and d_i is negative: the problem is infeasible.
77 halt_reason = "Found infeasible degenerate constraint (row " + std::to_string(i) + ").";
78 return false;
79 }
80 }
81
82 // Resize the constraints to discard unused rows.
83 C.conservativeResize(m, C.cols());
84 d.conservativeResize(m);
85
86 EigenOpt_SIMPLEX_DBG("Of the original " << _C.rows() << " constraints, " << m << " were kept:");
87 EigenOpt_SIMPLEX_DBG("C:" << std::endl << C);
88 EigenOpt_SIMPLEX_DBG("d: " << d.transpose());
89
90 // Obtain the transformation matrix and update the problem.
91 MatrixXs T;
92 if(!internal::transformation_matrix(C, d, small_number, T, halt_reason)) {
93 return false;
94 }
95 EigenOpt_SIMPLEX_DBG("Transformation matrix T =" << std::endl << T);
96
97 // Modfied constraints and objective.
98 VectorXs fs = T.transpose()*f;
99 MatrixXs Cs = C*T;
100 const unsigned int nv = T.cols();
101
102 // Simplex Tableau.
103 MatrixXs tableau;
104
105 // List of basic variables, ordered by row, i.e., the column of the basic
106 // variable used in a row is given by basic_variables[row].
107 std::vector<int> basic_variables;
108
109 // Fill the upper portion of the tableau.
110 internal::create_tableau(Cs, d, tableau, basic_variables);
111 EigenOpt_SIMPLEX_DBG("Initial tableau:" << std::endl << tableau);
112
113 // Deduce the number of artificial variables added to the tableau.
114 const unsigned int na = tableau.cols() - nv - m - 1;
115
116#ifdef EIGEN_SIMPLEX_DEBUG_ON
117 EigenOpt_SIMPLEX_DBG("Basic variables:");
118 for(unsigned int i=0; i<m; i++) {
119 EigenOpt_SIMPLEX_DBG("- " << basic_variables[i] << " (" << (basic_variables[i]<nv+m ? "slack" : "artificial") << ")");
120 }
121#endif
122
123 // Solve the problem using either a penalty or a two-steps method.
124 if(large_number > 0) {
125 if(!internal::penalty_method(fs, tableau, basic_variables, na, small_number, large_number, halt_reason)) {
126 return false;
127 }
128 }
129 else {
130 if(!internal::two_steps_method(fs, tableau, basic_variables, na, small_number, halt_reason)) {
131 return false;
132 }
133 }
134
135 // Read the solution from the tableau.
136 VectorXs xv = VectorXs::Zero(nv);
137 for(unsigned int i = 0; i<m; i++) {
138 if(basic_variables[i] < nv) {
139 xv(basic_variables[i]) = tableau(i, tableau.cols()-1);
140 }
141 }
142
143 // Project back to the original domain.
144 x = T * xv;
145 eigen_assert((C*VectorXs(x) - d).maxCoeff() < small_number && "Something went horribly wrong: Simplex optimization was completed 'successfully' but constraints are not respected.");
146 halt_reason = "Optimization completed successfully";
147 return true;
148}
149
150
151template<class Scalar, class D1, class D2, class D3, class D4, class D5, class D6>
153 const Eigen::MatrixBase<D1>& f,
154 const Eigen::MatrixBase<D2>& A,
155 const Eigen::MatrixBase<D3>& b,
156 const Eigen::MatrixBase<D4>& C,
157 const Eigen::MatrixBase<D5>& d,
158 Eigen::DenseBase<D6>& x,
159 std::string& halt_reason,
160 const Scalar& small_number,
161 const Scalar& large_number
162)
163{
164 // Typedefs 'VectorXs' and 'MatrixXs' - to make my life easier.
165 EigenOptTypedefs(Scalar);
166
167 // Solve A*x = b in least squares sense.
168 VectorXs xeq;
169 MatrixXs Z;
170 #ifdef EigenOpt_SIMPLEX_USE_QR_INSTEAD_OF_SVD
171 qr_projection(A, b, Z, xeq);
172 #else
173 svd_projection(A, b, Z, xeq);
174 #endif
175
176 // If A*x=b has no solutions, xeq is the solution in the least squares sense,
177 // which we cannot accept in this context.
178 if(!(A*xeq-b).isZero(small_number)) {
179 halt_reason = "Equality constraints are infeasible.";
180 return false;
181 }
182
183 EigenOpt_SIMPLEX_DBG("Particular solution for equality constraints: " << xeq.transpose());
184
185 // Check if the equality constraints fully constrain the decision vector.
186 if(Z.cols() == 0) {
187 // The solution xeq is compatible with all constraints, but we do not have
188 // any DOF left: this is the best we can do and there is no point in going
189 // further.
190 x = xeq;
191 halt_reason = "The solution is fully determined by equality constraints";
192 return true;
193 }
194
195 EigenOpt_SIMPLEX_DBG("Projection matrix into ker(A):" << std::endl << Z);
196
197
198 // We have more DOFs remaining; use a projection into the kernel of A to
199 // obtain the full solution! Z is the projection matrix that maps into the
200 // kernel of A, i.e., such that A*Z=0. We can thus parameterize x as:
201 // x = xeq + Z*y (with y a "free", lower-dimensional vector) and optimize
202 // over y - since for all values of y, equality constraints will be met. The
203 // reduced problem becomes:
204 // min_y (Z^T * f)^T * y s.t. C*Z*y <= d - C*xeq
205 VectorXs y;
206 bool ok = minimize(Z.transpose()*f, C*Z, d-C*xeq, y, halt_reason, small_number, large_number);
207 if(!ok) {
208 halt_reason = "Failed to solve the inequality constrained sub-problem: " + halt_reason;
209 }
210 else {
211 x = xeq + Z*y;
212 }
213 return ok;
214}
215
216} // simplex
217
218} // EigenOpt
bool simplex(Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &tableau, std::vector< int > &basic_variables, const Scalar &small_number, std::string &halt_reason)
Perform successive pivot operations until a termination condition is met.
bool transformation_matrix(const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &C, const Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &d, const Scalar &small_number, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &T, std::string &halt_reason)
Calculate a transformation matrix so that , .
void create_tableau(const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &C, const Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &d, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &tableau, std::vector< int > &basic_variables)
Create a Simplex Tableau given a set of inequality constraints.
bool two_steps_method(const Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &objective, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &tableau, std::vector< int > &basic_variables, unsigned int na, const Scalar &small_number, std::string &halt_reason)
Solve a minimization problem using the two-steps Simplex method.
bool penalty_method(const Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &objective, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &tableau, std::vector< int > &basic_variables, unsigned int na, const Scalar &small_number, const Scalar &large_number, std::string &halt_reason)
Solve a minimization problem using the penalty Simplex method.
bool minimize(const Eigen::MatrixBase< D1 > &f, const Eigen::MatrixBase< D2 > &A, const Eigen::MatrixBase< D3 > &b, const Eigen::MatrixBase< D4 > &C, const Eigen::MatrixBase< D5 > &d, Eigen::DenseBase< D6 > &x, std::string &halt_reason, const Scalar &small_number, const Scalar &large_number=-1)
Solve a constrained linear optimization problem.
Definition simplex.hxx:152
Main namespace of this project, containing all utilities.
void svd_projection(const Eigen::MatrixBase< D1 > &A, const Eigen::MatrixBase< D2 > &b, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &Z, Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &xeq)
Return all solutions to a linear system, using a kernel-based parameterization.
void qr_projection(const Eigen::MatrixBase< D1 > &A, const Eigen::MatrixBase< D2 > &b, Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &Z, Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &xeq)
Return all solutions to a linear system, using a kernel-based parameterization.
#define EigenOpt_SIMPLEX_DBG(x)
#define EigenOptTypedefs(ScalarType)
Definition typedefs.hpp:6