LibreOffice Module sccomp (master) 1
LpsolveSolver.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*************************************************************************
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 *
8 * OpenOffice.org - a multi-platform office productivity suite
9 *
10 * This file is part of OpenOffice.org.
11 *
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
15 *
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
21 *
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
26 *
27 * This file incorporates work covered by the following license notice:
28 *
29 * Licensed to the Apache Software Foundation (ASF) under one or more
30 * contributor license agreements. See the NOTICE file distributed
31 * with this work for additional information regarding copyright
32 * ownership. The ASF licenses this file to you under the Apache
33 * License, Version 2.0 (the "License"); you may not use this file
34 * except in compliance with the License. You may obtain a copy of
35 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
36 *
37 ************************************************************************/
38
39#include <sal/config.h>
40
41#undef LANGUAGE_NONE
42#if defined _WIN32
43#define WINAPI __stdcall
44#endif
45#define LoadInverseLib FALSE
46#define LoadLanguageLib FALSE
47#ifdef SYSTEM_LPSOLVE
48#include <lpsolve/lp_lib.h>
49#else
50#include <lp_lib.h>
51#endif
52#undef LANGUAGE_NONE
53
54#include "SolverComponent.hxx"
55#include <strings.hrc>
56
57#include <com/sun/star/frame/XModel.hpp>
58#include <com/sun/star/table/CellAddress.hpp>
59#include <rtl/math.hxx>
60#include <algorithm>
61#include <memory>
62#include <vector>
63
64namespace com::sun::star::uno { class XComponentContext; }
65
66using namespace com::sun::star;
67
68namespace {
69
70class LpsolveSolver : public SolverComponent
71{
72public:
73 LpsolveSolver() {}
74
75private:
76 virtual void SAL_CALL solve() override;
77 virtual OUString SAL_CALL getImplementationName() override
78 {
79 return "com.sun.star.comp.Calc.LpsolveSolver";
80 }
81 virtual OUString SAL_CALL getComponentDescription() override
82 {
83 return SolverComponent::GetResourceString( RID_SOLVER_COMPONENT );
84 }
85};
86
87}
88
89void SAL_CALL LpsolveSolver::solve()
90{
91 uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY_THROW );
92
93 maStatus.clear();
94 mbSuccess = false;
95
96 if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY )
97 {
98 maStatus = SolverComponent::GetResourceString( RID_ERROR_EPSILONLEVEL );
99 return;
100 }
101
102 xModel->lockControllers();
103
104 // collect variables in vector (?)
105
106 const auto & aVariableCells = maVariables;
107 size_t nVariables = aVariableCells.size();
108 size_t nVar = 0;
109
110 // collect all dependent cells
111
112 ScSolverCellHashMap aCellsHash;
113 aCellsHash[maObjective].reserve( nVariables + 1 ); // objective function
114
115 for (const auto& rConstr : std::as_const(maConstraints))
116 {
117 table::CellAddress aCellAddr = rConstr.Left;
118 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: left hand side
119
120 if ( rConstr.Right >>= aCellAddr )
121 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side
122 }
123
124 // set all variables to zero
127 for ( const auto& rVarCell : aVariableCells )
128 {
129 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 );
130 }
131
132 // read initial values from all dependent cells
133 for ( auto& rEntry : aCellsHash )
134 {
135 double fValue = SolverComponent::GetValue( mxDoc, rEntry.first );
136 rEntry.second.push_back( fValue ); // store as first element, as-is
137 }
138
139 // loop through variables
140 for ( const auto& rVarCell : aVariableCells )
141 {
142 SolverComponent::SetValue( mxDoc, rVarCell, 1.0 ); // set to 1 to examine influence
143
144 // read value change from all dependent cells
145 for ( auto& rEntry : aCellsHash )
146 {
147 double fChanged = SolverComponent::GetValue( mxDoc, rEntry.first );
148 double fInitial = rEntry.second.front();
149 rEntry.second.push_back( fChanged - fInitial );
150 }
151
152 SolverComponent::SetValue( mxDoc, rVarCell, 2.0 ); // minimal test for linearity
153
154 for ( const auto& rEntry : aCellsHash )
155 {
156 double fInitial = rEntry.second.front();
157 double fCoeff = rEntry.second.back(); // last appended: coefficient for this variable
158 double fTwo = SolverComponent::GetValue( mxDoc, rEntry.first );
159
160 bool bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) ||
161 rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff );
162 // second comparison is needed in case fTwo is zero
163 if ( !bLinear )
164 maStatus = SolverComponent::GetResourceString( RID_ERROR_NONLINEAR );
165 }
166
167 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 ); // set back to zero for examining next variable
168 }
169
170 xModel->unlockControllers();
171
172 if ( !maStatus.isEmpty() )
173 return;
174
175
176 // build lp_solve model
177
178
179 lprec* lp = make_lp( 0, nVariables );
180 if ( !lp )
181 return;
182
183 set_outputfile( lp, const_cast<char*>( "" ) ); // no output
184
185 // set objective function
186
187 const std::vector<double>& rObjCoeff = aCellsHash[maObjective];
188 std::unique_ptr<REAL[]> pObjVal(new REAL[nVariables+1]);
189 pObjVal[0] = 0.0; // ignored
190 for (nVar=0; nVar<nVariables; nVar++)
191 pObjVal[nVar+1] = rObjCoeff[nVar+1];
192 set_obj_fn( lp, pObjVal.get() );
193 pObjVal.reset();
194 set_rh( lp, 0, rObjCoeff[0] ); // constant term of objective
195
196 // add rows
197
198 set_add_rowmode(lp, TRUE);
199
200 for (const auto& rConstr : std::as_const(maConstraints))
201 {
202 // integer constraints are set later
203 sheet::SolverConstraintOperator eOp = rConstr.Operator;
204 if ( eOp == sheet::SolverConstraintOperator_LESS_EQUAL ||
205 eOp == sheet::SolverConstraintOperator_GREATER_EQUAL ||
206 eOp == sheet::SolverConstraintOperator_EQUAL )
207 {
208 double fDirectValue = 0.0;
209 bool bRightCell = false;
210 table::CellAddress aRightAddr;
211 const uno::Any& rRightAny = rConstr.Right;
212 if ( rRightAny >>= aRightAddr )
213 bRightCell = true; // cell specified as right-hand side
214 else
215 rRightAny >>= fDirectValue; // constant value
216
217 table::CellAddress aLeftAddr = rConstr.Left;
218
219 const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr];
220 std::unique_ptr<REAL[]> pValues(new REAL[nVariables+1] );
221 pValues[0] = 0.0; // ignored?
222 for (nVar=0; nVar<nVariables; nVar++)
223 pValues[nVar+1] = rLeftCoeff[nVar+1];
224
225 // if left hand cell has a constant term, put into rhs value
226 double fRightValue = -rLeftCoeff[0];
227
228 if ( bRightCell )
229 {
230 const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr];
231 // modify pValues with rhs coefficients
232 for (nVar=0; nVar<nVariables; nVar++)
233 pValues[nVar+1] -= rRightCoeff[nVar+1];
234
235 fRightValue += rRightCoeff[0]; // constant term
236 }
237 else
238 fRightValue += fDirectValue;
239
240 int nConstrType = LE;
241 switch ( eOp )
242 {
243 case sheet::SolverConstraintOperator_LESS_EQUAL: nConstrType = LE; break;
244 case sheet::SolverConstraintOperator_GREATER_EQUAL: nConstrType = GE; break;
245 case sheet::SolverConstraintOperator_EQUAL: nConstrType = EQ; break;
246 default:
247 OSL_FAIL( "unexpected enum type" );
248 }
249 add_constraint( lp, pValues.get(), nConstrType, fRightValue );
250 }
251 }
252
253 set_add_rowmode(lp, FALSE);
254
255 // apply settings to all variables
256
257 for (nVar=0; nVar<nVariables; nVar++)
258 {
259 if ( !mbNonNegative )
260 set_unbounded(lp, nVar+1); // allow negative (default is non-negative)
262 if ( mbInteger )
263 set_int(lp, nVar+1, TRUE);
264 }
265
266 // apply single-var integer constraints
267
268 for (const auto& rConstr : std::as_const(maConstraints))
269 {
270 sheet::SolverConstraintOperator eOp = rConstr.Operator;
271 if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
272 eOp == sheet::SolverConstraintOperator_BINARY )
273 {
274 table::CellAddress aLeftAddr = rConstr.Left;
275 // find variable index for cell
276 for (nVar=0; nVar<nVariables; nVar++)
277 if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) )
278 {
279 if ( eOp == sheet::SolverConstraintOperator_INTEGER )
280 set_int(lp, nVar+1, TRUE);
281 else
282 set_binary(lp, nVar+1, TRUE);
283 }
284 }
285 }
286
287 if ( mbMaximize )
288 set_maxim(lp);
289 else
290 set_minim(lp);
291
292 if ( !mbLimitBBDepth )
293 set_bb_depthlimit( lp, 0 );
294
295 set_epslevel( lp, mnEpsilonLevel );
296 set_timeout( lp, mnTimeout );
297
298 // solve model
299
300 int nResult = ::solve( lp );
301
302 mbSuccess = ( nResult == OPTIMAL );
303 if ( mbSuccess )
304 {
305 // get solution
306
307 maSolution.realloc( nVariables );
308
309 REAL* pResultVar = nullptr;
310 get_ptr_variables( lp, &pResultVar );
311 std::copy_n(pResultVar, nVariables, maSolution.getArray());
312
313 mfResultValue = get_objective( lp );
314 }
315 else if ( nResult == INFEASIBLE )
316 maStatus = SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE );
317 else if ( nResult == UNBOUNDED )
318 maStatus = SolverComponent::GetResourceString( RID_ERROR_UNBOUNDED );
319 else if ( nResult == TIMEOUT || nResult == SUBOPTIMAL )
320 maStatus = SolverComponent::GetResourceString( RID_ERROR_TIMEOUT );
321 // SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error
322
323 delete_lp( lp );
324}
325
326extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
328 css::uno::XComponentContext *,
329 css::uno::Sequence<css::uno::Any> const &)
330{
331 return cppu::acquire(new LpsolveSolver());
332}
333
334/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const PropertyValue * pValues
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_Calc_LpsolveSolver_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
std::unordered_map< css::table::CellAddress, std::vector< double >, ScSolverCellHash, ScSolverCellEqual > ScSolverCellHashMap
bool AddressEqual(const css::table::CellAddress &rAddr1, const css::table::CellAddress &rAddr2)
static double GetValue(const css::uno::Reference< css::sheet::XSpreadsheetDocument > &xDoc, const css::table::CellAddress &rPos)
static void SetValue(const css::uno::Reference< css::sheet::XSpreadsheetDocument > &xDoc, const css::table::CellAddress &rPos, double fValue)
static OUString GetResourceString(TranslateId aId)
bool solve(Matrix &matrix, int rows, int cols, Vector &result, BaseType minPivot)
OUString getImplementationName()
bool mbSuccess
Reference< XModel > xModel
GE
LE
EQ