Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sccomp / source / solver / LpsolveSolver.cxx
blob78cd25e8116778b55b0cb2eaa960631816c8b6e4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
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.
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).
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.
27 * This file incorporates work covered by the following license notice:
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 .
37 ************************************************************************/
39 #include <sal/config.h>
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
54 #include "SolverComponent.hxx"
55 #include <strings.hrc>
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>
64 namespace com::sun::star::uno { class XComponentContext; }
66 using namespace com::sun::star;
68 namespace {
70 class LpsolveSolver : public SolverComponent
72 public:
73 LpsolveSolver() {}
75 private:
76 virtual void SAL_CALL solve() override;
77 virtual OUString SAL_CALL getImplementationName() override
79 return "com.sun.star.comp.Calc.LpsolveSolver";
81 virtual OUString SAL_CALL getComponentDescription() override
83 return SolverComponent::GetResourceString( RID_SOLVER_COMPONENT );
89 void SAL_CALL LpsolveSolver::solve()
91 uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY_THROW );
93 maStatus.clear();
94 mbSuccess = false;
96 if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY )
98 maStatus = SolverComponent::GetResourceString( RID_ERROR_EPSILONLEVEL );
99 return;
102 xModel->lockControllers();
104 // collect variables in vector (?)
106 const auto & aVariableCells = maVariables;
107 size_t nVariables = aVariableCells.size();
108 size_t nVar = 0;
110 // collect all dependent cells
112 ScSolverCellHashMap aCellsHash;
113 aCellsHash[maObjective].reserve( nVariables + 1 ); // objective function
115 for (const auto& rConstr : std::as_const(maConstraints))
117 table::CellAddress aCellAddr = rConstr.Left;
118 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: left hand side
120 if ( rConstr.Right >>= aCellAddr )
121 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side
124 // set all variables to zero
125 //! store old values?
126 //! use old values as initial values?
127 for ( const auto& rVarCell : aVariableCells )
129 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 );
132 // read initial values from all dependent cells
133 for ( auto& rEntry : aCellsHash )
135 double fValue = SolverComponent::GetValue( mxDoc, rEntry.first );
136 rEntry.second.push_back( fValue ); // store as first element, as-is
139 // loop through variables
140 for ( const auto& rVarCell : aVariableCells )
142 SolverComponent::SetValue( mxDoc, rVarCell, 1.0 ); // set to 1 to examine influence
144 // read value change from all dependent cells
145 for ( auto& rEntry : aCellsHash )
147 double fChanged = SolverComponent::GetValue( mxDoc, rEntry.first );
148 double fInitial = rEntry.second.front();
149 rEntry.second.push_back( fChanged - fInitial );
152 SolverComponent::SetValue( mxDoc, rVarCell, 2.0 ); // minimal test for linearity
154 for ( const auto& rEntry : aCellsHash )
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 );
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 );
167 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 ); // set back to zero for examining next variable
170 xModel->unlockControllers();
172 if ( !maStatus.isEmpty() )
173 return;
176 // build lp_solve model
179 lprec* lp = make_lp( 0, nVariables );
180 if ( !lp )
181 return;
183 set_outputfile( lp, const_cast<char*>( "" ) ); // no output
185 // set objective function
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
196 // add rows
198 set_add_rowmode(lp, TRUE);
200 for (const auto& rConstr : std::as_const(maConstraints))
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 )
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
217 table::CellAddress aLeftAddr = rConstr.Left;
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];
225 // if left hand cell has a constant term, put into rhs value
226 double fRightValue = -rLeftCoeff[0];
228 if ( bRightCell )
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];
235 fRightValue += rRightCoeff[0]; // constant term
237 else
238 fRightValue += fDirectValue;
240 int nConstrType = LE;
241 switch ( eOp )
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" );
249 add_constraint( lp, pValues.get(), nConstrType, fRightValue );
253 set_add_rowmode(lp, FALSE);
255 // apply settings to all variables
257 for (nVar=0; nVar<nVariables; nVar++)
259 if ( !mbNonNegative )
260 set_unbounded(lp, nVar+1); // allow negative (default is non-negative)
261 //! collect bounds from constraints?
262 if ( mbInteger )
263 set_int(lp, nVar+1, TRUE);
266 // apply single-var integer constraints
268 for (const auto& rConstr : std::as_const(maConstraints))
270 sheet::SolverConstraintOperator eOp = rConstr.Operator;
271 if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
272 eOp == sheet::SolverConstraintOperator_BINARY )
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 ) )
279 if ( eOp == sheet::SolverConstraintOperator_INTEGER )
280 set_int(lp, nVar+1, TRUE);
281 else
282 set_binary(lp, nVar+1, TRUE);
287 if ( mbMaximize )
288 set_maxim(lp);
289 else
290 set_minim(lp);
292 if ( !mbLimitBBDepth )
293 set_bb_depthlimit( lp, 0 );
295 set_epslevel( lp, mnEpsilonLevel );
296 set_timeout( lp, mnTimeout );
298 // solve model
300 int nResult = ::solve( lp );
302 mbSuccess = ( nResult == OPTIMAL );
303 if ( mbSuccess )
305 // get solution
307 maSolution.realloc( nVariables );
309 REAL* pResultVar = nullptr;
310 get_ptr_variables( lp, &pResultVar );
311 std::copy_n(pResultVar, nVariables, maSolution.getArray());
313 mfResultValue = get_objective( lp );
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
323 delete_lp( lp );
326 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
327 com_sun_star_comp_Calc_LpsolveSolver_get_implementation(
328 css::uno::XComponentContext *,
329 css::uno::Sequence<css::uno::Any> const &)
331 return cppu::acquire(new LpsolveSolver());
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */