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