bump product version to 6.4.0.3
[LibreOffice.git] / sccomp / source / solver / LpsolveSolver.cxx
blob01f4bfba2bb105e99f25d8ebfe23ae9ae6fef403
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 <memory>
61 #include <vector>
63 namespace com::sun::star::uno { class XComponentContext; }
65 using namespace com::sun::star;
67 class LpsolveSolver : public SolverComponent
69 public:
70 LpsolveSolver() {}
72 private:
73 virtual void SAL_CALL solve() override;
74 virtual OUString SAL_CALL getImplementationName() override
76 return "com.sun.star.comp.Calc.LpsolveSolver";
78 virtual OUString SAL_CALL getComponentDescription() override
80 return SolverComponent::GetResourceString( RID_SOLVER_COMPONENT );
84 void SAL_CALL LpsolveSolver::solve()
86 uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY_THROW );
88 maStatus.clear();
89 mbSuccess = false;
91 if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY )
93 maStatus = SolverComponent::GetResourceString( RID_ERROR_EPSILONLEVEL );
94 return;
97 xModel->lockControllers();
99 // collect variables in vector (?)
101 auto aVariableCells = comphelper::sequenceToContainer<std::vector<table::CellAddress>>(maVariables);
102 size_t nVariables = aVariableCells.size();
103 size_t nVar = 0;
105 // collect all dependent cells
107 ScSolverCellHashMap aCellsHash;
108 aCellsHash[maObjective].reserve( nVariables + 1 ); // objective function
110 for (const auto& rConstr : std::as_const(maConstraints))
112 table::CellAddress aCellAddr = rConstr.Left;
113 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: left hand side
115 if ( rConstr.Right >>= aCellAddr )
116 aCellsHash[aCellAddr].reserve( nVariables + 1 ); // constraints: right hand side
119 // set all variables to zero
120 //! store old values?
121 //! use old values as initial values?
122 for ( const auto& rVarCell : aVariableCells )
124 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 );
127 // read initial values from all dependent cells
128 for ( auto& rEntry : aCellsHash )
130 double fValue = SolverComponent::GetValue( mxDoc, rEntry.first );
131 rEntry.second.push_back( fValue ); // store as first element, as-is
134 // loop through variables
135 for ( const auto& rVarCell : aVariableCells )
137 SolverComponent::SetValue( mxDoc, rVarCell, 1.0 ); // set to 1 to examine influence
139 // read value change from all dependent cells
140 for ( auto& rEntry : aCellsHash )
142 double fChanged = SolverComponent::GetValue( mxDoc, rEntry.first );
143 double fInitial = rEntry.second.front();
144 rEntry.second.push_back( fChanged - fInitial );
147 SolverComponent::SetValue( mxDoc, rVarCell, 2.0 ); // minimal test for linearity
149 for ( const auto& rEntry : aCellsHash )
151 double fInitial = rEntry.second.front();
152 double fCoeff = rEntry.second.back(); // last appended: coefficient for this variable
153 double fTwo = SolverComponent::GetValue( mxDoc, rEntry.first );
155 bool bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) ||
156 rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff );
157 // second comparison is needed in case fTwo is zero
158 if ( !bLinear )
159 maStatus = SolverComponent::GetResourceString( RID_ERROR_NONLINEAR );
162 SolverComponent::SetValue( mxDoc, rVarCell, 0.0 ); // set back to zero for examining next variable
165 xModel->unlockControllers();
167 if ( !maStatus.isEmpty() )
168 return;
171 // build lp_solve model
174 lprec* lp = make_lp( 0, nVariables );
175 if ( !lp )
176 return;
178 set_outputfile( lp, const_cast<char*>( "" ) ); // no output
180 // set objective function
182 const std::vector<double>& rObjCoeff = aCellsHash[maObjective];
183 std::unique_ptr<REAL[]> pObjVal(new REAL[nVariables+1]);
184 pObjVal[0] = 0.0; // ignored
185 for (nVar=0; nVar<nVariables; nVar++)
186 pObjVal[nVar+1] = rObjCoeff[nVar+1];
187 set_obj_fn( lp, pObjVal.get() );
188 pObjVal.reset();
189 set_rh( lp, 0, rObjCoeff[0] ); // constant term of objective
191 // add rows
193 set_add_rowmode(lp, TRUE);
195 for (const auto& rConstr : std::as_const(maConstraints))
197 // integer constraints are set later
198 sheet::SolverConstraintOperator eOp = rConstr.Operator;
199 if ( eOp == sheet::SolverConstraintOperator_LESS_EQUAL ||
200 eOp == sheet::SolverConstraintOperator_GREATER_EQUAL ||
201 eOp == sheet::SolverConstraintOperator_EQUAL )
203 double fDirectValue = 0.0;
204 bool bRightCell = false;
205 table::CellAddress aRightAddr;
206 const uno::Any& rRightAny = rConstr.Right;
207 if ( rRightAny >>= aRightAddr )
208 bRightCell = true; // cell specified as right-hand side
209 else
210 rRightAny >>= fDirectValue; // constant value
212 table::CellAddress aLeftAddr = rConstr.Left;
214 const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr];
215 std::unique_ptr<REAL[]> pValues(new REAL[nVariables+1] );
216 pValues[0] = 0.0; // ignored?
217 for (nVar=0; nVar<nVariables; nVar++)
218 pValues[nVar+1] = rLeftCoeff[nVar+1];
220 // if left hand cell has a constant term, put into rhs value
221 double fRightValue = -rLeftCoeff[0];
223 if ( bRightCell )
225 const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr];
226 // modify pValues with rhs coefficients
227 for (nVar=0; nVar<nVariables; nVar++)
228 pValues[nVar+1] -= rRightCoeff[nVar+1];
230 fRightValue += rRightCoeff[0]; // constant term
232 else
233 fRightValue += fDirectValue;
235 int nConstrType = LE;
236 switch ( eOp )
238 case sheet::SolverConstraintOperator_LESS_EQUAL: nConstrType = LE; break;
239 case sheet::SolverConstraintOperator_GREATER_EQUAL: nConstrType = GE; break;
240 case sheet::SolverConstraintOperator_EQUAL: nConstrType = EQ; break;
241 default:
242 OSL_FAIL( "unexpected enum type" );
244 add_constraint( lp, pValues.get(), nConstrType, fRightValue );
248 set_add_rowmode(lp, FALSE);
250 // apply settings to all variables
252 for (nVar=0; nVar<nVariables; nVar++)
254 if ( !mbNonNegative )
255 set_unbounded(lp, nVar+1); // allow negative (default is non-negative)
256 //! collect bounds from constraints?
257 if ( mbInteger )
258 set_int(lp, nVar+1, TRUE);
261 // apply single-var integer constraints
263 for (const auto& rConstr : std::as_const(maConstraints))
265 sheet::SolverConstraintOperator eOp = rConstr.Operator;
266 if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
267 eOp == sheet::SolverConstraintOperator_BINARY )
269 table::CellAddress aLeftAddr = rConstr.Left;
270 // find variable index for cell
271 for (nVar=0; nVar<nVariables; nVar++)
272 if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) )
274 if ( eOp == sheet::SolverConstraintOperator_INTEGER )
275 set_int(lp, nVar+1, TRUE);
276 else
277 set_binary(lp, nVar+1, TRUE);
282 if ( mbMaximize )
283 set_maxim(lp);
284 else
285 set_minim(lp);
287 if ( !mbLimitBBDepth )
288 set_bb_depthlimit( lp, 0 );
290 set_epslevel( lp, mnEpsilonLevel );
291 set_timeout( lp, mnTimeout );
293 // solve model
295 int nResult = ::solve( lp );
297 mbSuccess = ( nResult == OPTIMAL );
298 if ( mbSuccess )
300 // get solution
302 maSolution.realloc( nVariables );
304 REAL* pResultVar = nullptr;
305 get_ptr_variables( lp, &pResultVar );
306 for (nVar=0; nVar<nVariables; nVar++)
307 maSolution[nVar] = pResultVar[nVar];
309 mfResultValue = get_objective( lp );
311 else if ( nResult == INFEASIBLE )
312 maStatus = SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE );
313 else if ( nResult == UNBOUNDED )
314 maStatus = SolverComponent::GetResourceString( RID_ERROR_UNBOUNDED );
315 else if ( nResult == TIMEOUT || nResult == SUBOPTIMAL )
316 maStatus = SolverComponent::GetResourceString( RID_ERROR_TIMEOUT );
317 // SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error
319 delete_lp( lp );
322 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
323 com_sun_star_comp_Calc_LpsolveSolver_get_implementation(
324 css::uno::XComponentContext *,
325 css::uno::Sequence<css::uno::Any> const &)
327 return cppu::acquire(new LpsolveSolver());
330 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */