1 /** \file
2 * \brief Implements front-end for LP solver
3 *
4 * \author Carsten Gutwenger
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32
33 #include <ogdf/basic/basic.h>
34
35 #include <ogdf/lpsolver/LPSolver.h>
36
37 namespace ogdf {
38
LPSolver()39 LPSolver::LPSolver()
40 {
41 osi = CoinManager::createCorrectOsiSolverInterface();
42 }
43
44
infinity() const45 double LPSolver::infinity() const
46 {
47 return osi->getInfinity();
48 }
49
checkFeasibility(const Array<int> & matrixBegin,const Array<int> & matrixCount,const Array<int> & matrixIndex,const Array<double> & matrixValue,const Array<double> & rightHandSide,const Array<char> & equationSense,const Array<double> & lowerBound,const Array<double> & upperBound,const Array<double> & x) const50 bool LPSolver::checkFeasibility(
51 const Array<int> &matrixBegin, // matrixBegin[i] = begin of column i
52 const Array<int> &matrixCount, // matrixCount[i] = number of nonzeroes in column i
53 const Array<int> &matrixIndex, // matrixIndex[n] = index of matrixValue[n] in its column
54 const Array<double> &matrixValue, // matrixValue[n] = non-zero value in matrix
55 const Array<double> &rightHandSide, // right-hand side of LP constraints
56 const Array<char> &equationSense, // 'E' == 'G' >= 'L' <=
57 const Array<double> &lowerBound, // lower bound of x[i]
58 const Array<double> &upperBound, // upper bound of x[i]
59 const Array<double> &x // x-vector of optimal solution (if result is Optimal)
60 ) const
61 {
62 const int numRows = rightHandSide.size();
63 const int numCols = x.size();
64
65 double eps;
66 osi->getDblParam(OsiPrimalTolerance, eps);
67
68 for(int i = 0; i < numCols; ++i) {
69 if(x[i]+eps < lowerBound[i] || x[i]-eps > upperBound[i]) {
70 std::cerr << "column " << i << " out of range" << std::endl;
71 return false;
72 }
73 }
74
75 for(int i = 0; i < numRows; ++i) {
76 double leftHandSide = 0.0;
77
78 for(int c = 0; c < numCols; ++c) {
79 for(int j = matrixBegin[c]; j < matrixBegin[c]+matrixCount[c]; ++j)
80 if(matrixIndex[j] == i) {
81 leftHandSide += matrixValue[j] * x[c];
82 }
83 }
84
85 switch(equationSense[i]) {
86 case 'G':
87 if(leftHandSide+eps < rightHandSide[i]) {
88 std::cerr << "row " << i << " violated " << std::endl;
89 std::cerr << leftHandSide << " > " << rightHandSide[i] << std::endl;
90 return false;
91 }
92 break;
93 case 'L':
94 if(leftHandSide-eps > rightHandSide[i]) {
95 std::cerr << "row " << i << " violated " << std::endl;
96 std::cerr << leftHandSide << " < " << rightHandSide[i] << std::endl;
97 return false;
98 }
99 break;
100 case 'E':
101 if(leftHandSide+eps < rightHandSide[i] || leftHandSide-eps > rightHandSide[i]) {
102 std::cerr << "row " << i << " violated " << std::endl;
103 std::cerr << leftHandSide << " = " << rightHandSide[i] << std::endl;
104 return false;
105 }
106 break;
107 default:
108 std::cerr << "unexpected equation sense " << equationSense[i] << std::endl;
109 return false;
110 }
111 }
112 return true;
113 }
114
optimize(OptimizationGoal goal,Array<double> & obj,Array<int> & matrixBegin,Array<int> & matrixCount,Array<int> & matrixIndex,Array<double> & matrixValue,Array<double> & rightHandSide,Array<char> & equationSense,Array<double> & lowerBound,Array<double> & upperBound,double & optimum,Array<double> & x)115 LPSolver::Status LPSolver::optimize(
116 OptimizationGoal goal, // goal of optimization (minimize or maximize)
117 Array<double> &obj, // objective function vector
118 Array<int> &matrixBegin, // matrixBegin[i] = begin of column i
119 Array<int> &matrixCount, // matrixCount[i] = number of nonzeroes in column i
120 Array<int> &matrixIndex, // matrixIndex[n] = index of matrixValue[n] in its column
121 Array<double> &matrixValue, // matrixValue[n] = non-zero value in matrix
122 Array<double> &rightHandSide, // right-hand side of LP constraints
123 Array<char> &equationSense, // 'E' == 'G' >= 'L' <=
124 Array<double> &lowerBound, // lower bound of x[i]
125 Array<double> &upperBound, // upper bound of x[i]
126 double &optimum, // optimum value of objective function (if result is Optimal)
127 Array<double> &x // x-vector of optimal solution (if result is Optimal)
128 )
129 {
130 if(osi->getNumCols()>0) { // get a fresh one if necessary
131 delete osi;
132 osi = CoinManager::createCorrectOsiSolverInterface();
133 }
134
135 const int numRows = rightHandSide.size();
136 const int numCols = obj.size();
137 #ifdef OGDF_DEBUG
138 const int numNonzeroes = matrixIndex.size();
139 #endif
140
141 // assert correctness of array boundaries
142 OGDF_ASSERT(obj .low() == 0);
143 OGDF_ASSERT(obj .size() == numCols);
144 OGDF_ASSERT(matrixBegin .low() == 0);
145 OGDF_ASSERT(matrixBegin .size() == numCols);
146 OGDF_ASSERT(matrixCount .low() == 0);
147 OGDF_ASSERT(matrixCount .size() == numCols);
148 OGDF_ASSERT(matrixIndex .low() == 0);
149 OGDF_ASSERT(matrixIndex .size() == numNonzeroes);
150 OGDF_ASSERT(matrixValue .low() == 0);
151 OGDF_ASSERT(matrixValue .size() == numNonzeroes);
152 OGDF_ASSERT(rightHandSide.low() == 0);
153 OGDF_ASSERT(rightHandSide.size() == numRows);
154 OGDF_ASSERT(equationSense.low() == 0);
155 OGDF_ASSERT(equationSense.size() == numRows);
156 OGDF_ASSERT(lowerBound .low() == 0);
157 OGDF_ASSERT(lowerBound .size() == numCols);
158 OGDF_ASSERT(upperBound .low() == 0);
159 OGDF_ASSERT(upperBound .size() == numCols);
160 OGDF_ASSERT(x .low() == 0);
161 OGDF_ASSERT(x .size() == numCols);
162
163 osi->setObjSense(goal==OptimizationGoal::Minimize ? 1 : -1);
164
165 int i;
166
167 CoinPackedVector zero;
168 for(i = 0; i < numRows; ++i) {
169 osi->addRow(zero,equationSense[i],rightHandSide[i],0);
170 }
171 for(int colNo = 0; colNo < numCols; ++colNo) {
172 CoinPackedVector cpv;
173 for(i = matrixBegin[colNo]; i<matrixBegin[colNo]+matrixCount[colNo]; ++i) {
174 cpv.insert(matrixIndex[i],matrixValue[i]);
175 }
176 osi->addCol(cpv,lowerBound[colNo],upperBound[colNo],obj[colNo]);
177 }
178
179
180 osi->initialSolve();
181
182 Status status;
183 if(osi->isProvenOptimal()) {
184 optimum = osi->getObjValue();
185 const double* sol = osi->getColSolution();
186 for(i = numCols; i-- > 0;)
187 x[i]=sol[i];
188 status = Status::Optimal;
189 OGDF_HEAVY_ASSERT(checkFeasibility(matrixBegin,matrixCount,matrixIndex,matrixValue,
190 rightHandSide,equationSense,lowerBound,upperBound,x));
191
192 } else if(osi->isProvenPrimalInfeasible())
193 status = Status::Infeasible;
194 else if(osi->isProvenDualInfeasible())
195 status = Status::Unbounded;
196 else
197 OGDF_THROW_PARAM(AlgorithmFailureException, AlgorithmFailureCode::NoSolutionFound);
198
199 return status;
200 }
201
202 }
203