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>
43 #define WINAPI __stdcall
45 #define LoadInverseLib FALSE
46 #define LoadLanguageLib FALSE
48 #include <lpsolve/lp_lib.h>
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>
64 namespace com::sun::star::uno
{ class XComponentContext
; }
66 using namespace com::sun::star
;
70 class LpsolveSolver
: public SolverComponent
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
);
96 if ( mnEpsilonLevel
< EPS_TIGHT
|| mnEpsilonLevel
> EPS_BAGGY
)
98 maStatus
= SolverComponent::GetResourceString( RID_ERROR_EPSILONLEVEL
);
102 xModel
->lockControllers();
104 // collect variables in vector (?)
106 const auto & aVariableCells
= maVariables
;
107 size_t nVariables
= aVariableCells
.size();
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
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() )
176 // build lp_solve model
179 lprec
* lp
= make_lp( 0, nVariables
);
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() );
194 set_rh( lp
, 0, rObjCoeff
[0] ); // constant term of objective
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
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];
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
238 fRightValue
+= fDirectValue
;
240 int nConstrType
= LE
;
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;
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?
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
);
282 set_binary(lp
, nVar
+1, TRUE
);
292 if ( !mbLimitBBDepth
)
293 set_bb_depthlimit( lp
, 0 );
295 set_epslevel( lp
, mnEpsilonLevel
);
296 set_timeout( lp
, mnTimeout
);
300 int nResult
= ::solve( lp
);
302 mbSuccess
= ( nResult
== OPTIMAL
);
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
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: */