Bump version to 5.0-14
[LibreOffice.git] / sccomp / source / solver / CoinMPSolver.cxx
blobb650d0673421d1a0c823baf57f6ecfb76905b311
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <CoinMP.h>
21 #include <CoinError.hpp>
23 #include "SolverComponent.hxx"
24 #include "solver.hrc"
26 #include <com/sun/star/frame/XModel.hpp>
27 #include <com/sun/star/table/CellAddress.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
30 #include <rtl/math.hxx>
31 #include <stdexcept>
32 #include <vector>
34 using namespace com::sun::star;
36 class CoinMPSolver : public SolverComponent
38 public:
39 CoinMPSolver() {}
40 virtual ~CoinMPSolver() {}
42 private:
43 virtual void SAL_CALL solve() throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
44 virtual OUString SAL_CALL getImplementationName()
45 throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE
47 return OUString("com.sun.star.comp.Calc.CoinMPSolver");
49 virtual OUString SAL_CALL getComponentDescription()
50 throw (uno::RuntimeException, std::exception) SAL_OVERRIDE
52 return SolverComponent::GetResourceString( RID_COINMP_SOLVER_COMPONENT );
56 void SAL_CALL CoinMPSolver::solve() throw(uno::RuntimeException, std::exception)
58 uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY );
59 if ( !xModel.is() )
60 throw uno::RuntimeException();
62 maStatus.clear();
63 mbSuccess = false;
65 xModel->lockControllers();
67 // collect variables in vector (?)
69 std::vector<table::CellAddress> aVariableCells;
70 for (sal_Int32 nPos=0; nPos<maVariables.getLength(); nPos++)
71 aVariableCells.push_back( maVariables[nPos] );
72 size_t nVariables = aVariableCells.size();
73 size_t nVar = 0;
75 // collect all dependent cells
77 ScSolverCellHashMap aCellsHash;
78 aCellsHash[maObjective].reserve( nVariables + 1 ); // objective function
80 for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
82 table::CellAddress aCellAddr = maConstraints[nConstrPos].Left;
83 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: left hand side
85 if ( maConstraints[nConstrPos].Right >>= aCellAddr )
86 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side
89 // set all variables to zero
90 //! store old values?
91 //! use old values as initial values?
92 std::vector<table::CellAddress>::const_iterator aVarIter;
93 for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter )
95 SolverComponent::SetValue( mxDoc, *aVarIter, 0.0 );
98 // read initial values from all dependent cells
99 ScSolverCellHashMap::iterator aCellsIter;
100 for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
102 double fValue = SolverComponent::GetValue( mxDoc, aCellsIter->first );
103 aCellsIter->second.push_back( fValue ); // store as first element, as-is
106 // loop through variables
107 for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter )
109 SolverComponent::SetValue( mxDoc, *aVarIter, 1.0 ); // set to 1 to examine influence
111 // read value change from all dependent cells
112 for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
114 double fChanged = SolverComponent::GetValue( mxDoc, aCellsIter->first );
115 double fInitial = aCellsIter->second.front();
116 aCellsIter->second.push_back( fChanged - fInitial );
119 SolverComponent::SetValue( mxDoc, *aVarIter, 2.0 ); // minimal test for linearity
121 for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
123 double fInitial = aCellsIter->second.front();
124 double fCoeff = aCellsIter->second.back(); // last appended: coefficient for this variable
125 double fTwo = SolverComponent::GetValue( mxDoc, aCellsIter->first );
127 bool bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) ||
128 rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff );
129 // second comparison is needed in case fTwo is zero
130 if ( !bLinear )
131 maStatus = SolverComponent::GetResourceString( RID_ERROR_NONLINEAR );
134 SolverComponent::SetValue( mxDoc, *aVarIter, 0.0 ); // set back to zero for examining next variable
137 xModel->unlockControllers();
139 if ( !maStatus.isEmpty() )
140 return;
143 // build parameter arrays for CoinMP
146 // set objective function
148 const std::vector<double>& rObjCoeff = aCellsHash[maObjective];
149 double* pObjectCoeffs = new double[nVariables];
150 for (nVar=0; nVar<nVariables; nVar++)
151 pObjectCoeffs[nVar] = rObjCoeff[nVar+1];
152 double nObjectConst = rObjCoeff[0]; // constant term of objective
154 // add rows
156 size_t nRows = maConstraints.getLength();
157 size_t nCompSize = nVariables * nRows;
158 double* pCompMatrix = new double[nCompSize]; // first collect all coefficients, row-wise
159 for (size_t i=0; i<nCompSize; i++)
160 pCompMatrix[i] = 0.0;
162 double* pRHS = new double[nRows];
163 char* pRowType = new char[nRows];
164 for (size_t i=0; i<nRows; i++)
166 pRHS[i] = 0.0;
167 pRowType[i] = 'N';
170 for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
172 // integer constraints are set later
173 sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
174 if ( eOp == sheet::SolverConstraintOperator_LESS_EQUAL ||
175 eOp == sheet::SolverConstraintOperator_GREATER_EQUAL ||
176 eOp == sheet::SolverConstraintOperator_EQUAL )
178 double fDirectValue = 0.0;
179 bool bRightCell = false;
180 table::CellAddress aRightAddr;
181 const uno::Any& rRightAny = maConstraints[nConstrPos].Right;
182 if ( rRightAny >>= aRightAddr )
183 bRightCell = true; // cell specified as right-hand side
184 else
185 rRightAny >>= fDirectValue; // constant value
187 table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
189 const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr];
190 double* pValues = &pCompMatrix[nConstrPos * nVariables];
191 for (nVar=0; nVar<nVariables; nVar++)
192 pValues[nVar] = rLeftCoeff[nVar+1];
194 // if left hand cell has a constant term, put into rhs value
195 double fRightValue = -rLeftCoeff[0];
197 if ( bRightCell )
199 const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr];
200 // modify pValues with rhs coefficients
201 for (nVar=0; nVar<nVariables; nVar++)
202 pValues[nVar] -= rRightCoeff[nVar+1];
204 fRightValue += rRightCoeff[0]; // constant term
206 else
207 fRightValue += fDirectValue;
209 switch ( eOp )
211 case sheet::SolverConstraintOperator_LESS_EQUAL: pRowType[nConstrPos] = 'L'; break;
212 case sheet::SolverConstraintOperator_GREATER_EQUAL: pRowType[nConstrPos] = 'G'; break;
213 case sheet::SolverConstraintOperator_EQUAL: pRowType[nConstrPos] = 'E'; break;
214 default:
215 OSL_ENSURE( false, "unexpected enum type" );
217 pRHS[nConstrPos] = fRightValue;
221 // Find non-zero coefficients, column-wise
223 int* pMatrixBegin = new int[nVariables+1];
224 int* pMatrixCount = new int[nVariables];
225 double* pMatrix = new double[nCompSize]; // not always completely used
226 int* pMatrixIndex = new int[nCompSize];
227 int nMatrixPos = 0;
228 for (nVar=0; nVar<nVariables; nVar++)
230 int nBegin = nMatrixPos;
231 for (size_t nRow=0; nRow<nRows; nRow++)
233 double fCoeff = pCompMatrix[ nRow * nVariables + nVar ]; // row-wise
234 if ( fCoeff != 0.0 )
236 pMatrix[nMatrixPos] = fCoeff;
237 pMatrixIndex[nMatrixPos] = nRow;
238 ++nMatrixPos;
241 pMatrixBegin[nVar] = nBegin;
242 pMatrixCount[nVar] = nMatrixPos - nBegin;
244 pMatrixBegin[nVariables] = nMatrixPos;
245 delete[] pCompMatrix;
246 pCompMatrix = NULL;
248 // apply settings to all variables
250 double* pLowerBounds = new double[nVariables];
251 double* pUpperBounds = new double[nVariables];
252 for (nVar=0; nVar<nVariables; nVar++)
254 pLowerBounds[nVar] = mbNonNegative ? 0.0 : -DBL_MAX;
255 pUpperBounds[nVar] = DBL_MAX;
257 // bounds could possibly be further restricted from single-cell constraints
260 char* pColType = new char[nVariables];
261 for (nVar=0; nVar<nVariables; nVar++)
262 pColType[nVar] = mbInteger ? 'I' : 'C';
264 // apply single-var integer constraints
266 for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
268 sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
269 if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
270 eOp == sheet::SolverConstraintOperator_BINARY )
272 table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
273 // find variable index for cell
274 for (nVar=0; nVar<nVariables; nVar++)
275 if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) )
277 if ( eOp == sheet::SolverConstraintOperator_INTEGER )
278 pColType[nVar] = 'I';
279 else
281 pColType[nVar] = 'B';
282 pLowerBounds[nVar] = 0.0;
283 pUpperBounds[nVar] = 1.0;
289 int nObjectSense = mbMaximize ? SOLV_OBJSENS_MAX : SOLV_OBJSENS_MIN;
291 HPROB hProb = CoinCreateProblem("");
292 int nResult = CoinLoadProblem( hProb, nVariables, nRows, nMatrixPos, 0,
293 nObjectSense, nObjectConst, pObjectCoeffs,
294 pLowerBounds, pUpperBounds, pRowType, pRHS, NULL,
295 pMatrixBegin, pMatrixCount, pMatrixIndex, pMatrix,
296 NULL, NULL, NULL );
297 nResult = CoinLoadInteger( hProb, pColType );
299 delete[] pColType;
300 delete[] pMatrixIndex;
301 delete[] pMatrix;
302 delete[] pMatrixCount;
303 delete[] pMatrixBegin;
304 delete[] pUpperBounds;
305 delete[] pLowerBounds;
306 delete[] pRowType;
307 delete[] pRHS;
308 delete[] pObjectCoeffs;
310 CoinSetRealOption( hProb, COIN_REAL_MAXSECONDS, mnTimeout );
311 CoinSetRealOption( hProb, COIN_REAL_MIPMAXSEC, mnTimeout );
313 // TODO: handle (or remove) settings: epsilon, B&B depth
315 // solve model
317 nResult = CoinCheckProblem( hProb );
321 nResult = CoinOptimizeProblem( hProb, 0 );
323 catch (const CoinError& e)
325 throw std::runtime_error(e.message());
328 mbSuccess = ( nResult == SOLV_CALL_SUCCESS );
329 if ( mbSuccess )
331 // get solution
333 maSolution.realloc( nVariables );
334 CoinGetSolutionValues( hProb, maSolution.getArray(), NULL, NULL, NULL );
335 mfResultValue = CoinGetObjectValue( hProb );
337 else
339 int nSolutionStatus = CoinGetSolutionStatus( hProb );
340 if ( nSolutionStatus == 1 )
341 maStatus = SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE );
342 else if ( nSolutionStatus == 2 )
343 maStatus = SolverComponent::GetResourceString( RID_ERROR_UNBOUNDED );
344 // TODO: detect timeout condition and report as RID_ERROR_TIMEOUT
345 // (currently reported as infeasible)
348 CoinUnloadProblem( hProb );
351 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * SAL_CALL
352 com_sun_star_comp_Calc_CoinMPSolver_get_implementation(
353 css::uno::XComponentContext *,
354 css::uno::Sequence<css::uno::Any> const &)
356 return cppu::acquire(new CoinMPSolver());
359 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */