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"
40 #include <config_lgpl.h>
44 #define WINAPI __stdcall
46 #define LoadInverseLib FALSE
47 #define LoadLanguageLib FALSE
49 #include <lpsolve/lp_lib.h>
55 #include "SolverComponent.hxx"
58 #include <com/sun/star/frame/XModel.hpp>
59 #include <com/sun/star/table/CellAddress.hpp>
60 #include <com/sun/star/uno/XComponentContext.hpp>
61 #include <rtl/math.hxx>
62 #include <cppuhelper/supportsservice.hxx>
65 using namespace com::sun::star
;
67 class LpsolveSolver
: public SolverComponent
71 virtual ~LpsolveSolver() {}
74 virtual void SAL_CALL
solve() throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
75 virtual OUString SAL_CALL
getImplementationName()
76 throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
78 return OUString("com.sun.star.comp.Calc.LpsolveSolver");
80 virtual OUString SAL_CALL
getComponentDescription()
81 throw (uno::RuntimeException
, std::exception
) SAL_OVERRIDE
83 return SolverComponent::GetResourceString( RID_SOLVER_COMPONENT
);
87 void SAL_CALL
LpsolveSolver::solve() throw(uno::RuntimeException
, std::exception
)
89 uno::Reference
<frame::XModel
> xModel( mxDoc
, uno::UNO_QUERY
);
91 throw uno::RuntimeException();
96 if ( mnEpsilonLevel
< EPS_TIGHT
|| mnEpsilonLevel
> EPS_BAGGY
)
98 maStatus
= SolverComponent::GetResourceString( RID_ERROR_EPSILONLEVEL
);
102 xModel
->lockControllers();
104 // collect variables in vector (?)
106 std::vector
<table::CellAddress
> aVariableCells
;
107 for (sal_Int32 nPos
=0; nPos
<maVariables
.getLength(); nPos
++)
108 aVariableCells
.push_back( maVariables
[nPos
] );
109 size_t nVariables
= aVariableCells
.size();
112 // collect all dependent cells
114 ScSolverCellHashMap aCellsHash
;
115 aCellsHash
[maObjective
].reserve( nVariables
+ 1 ); // objective function
117 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
119 table::CellAddress aCellAddr
= maConstraints
[nConstrPos
].Left
;
120 aCellsHash
[aCellAddr
].reserve( nVariables
+ 1 ); // constraints: left hand side
122 if ( maConstraints
[nConstrPos
].Right
>>= aCellAddr
)
123 aCellsHash
[aCellAddr
].reserve( nVariables
+ 1 ); // constraints: right hand side
126 // set all variables to zero
127 //! store old values?
128 //! use old values as initial values?
129 std::vector
<table::CellAddress
>::const_iterator aVarIter
;
130 for ( aVarIter
= aVariableCells
.begin(); aVarIter
!= aVariableCells
.end(); ++aVarIter
)
132 SolverComponent::SetValue( mxDoc
, *aVarIter
, 0.0 );
135 // read initial values from all dependent cells
136 ScSolverCellHashMap::iterator aCellsIter
;
137 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
139 double fValue
= SolverComponent::GetValue( mxDoc
, aCellsIter
->first
);
140 aCellsIter
->second
.push_back( fValue
); // store as first element, as-is
143 // loop through variables
144 for ( aVarIter
= aVariableCells
.begin(); aVarIter
!= aVariableCells
.end(); ++aVarIter
)
146 SolverComponent::SetValue( mxDoc
, *aVarIter
, 1.0 ); // set to 1 to examine influence
148 // read value change from all dependent cells
149 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
151 double fChanged
= SolverComponent::GetValue( mxDoc
, aCellsIter
->first
);
152 double fInitial
= aCellsIter
->second
.front();
153 aCellsIter
->second
.push_back( fChanged
- fInitial
);
156 SolverComponent::SetValue( mxDoc
, *aVarIter
, 2.0 ); // minimal test for linearity
158 for ( aCellsIter
= aCellsHash
.begin(); aCellsIter
!= aCellsHash
.end(); ++aCellsIter
)
160 double fInitial
= aCellsIter
->second
.front();
161 double fCoeff
= aCellsIter
->second
.back(); // last appended: coefficient for this variable
162 double fTwo
= SolverComponent::GetValue( mxDoc
, aCellsIter
->first
);
164 bool bLinear
= rtl::math::approxEqual( fTwo
, fInitial
+ 2.0 * fCoeff
) ||
165 rtl::math::approxEqual( fInitial
, fTwo
- 2.0 * fCoeff
);
166 // second comparison is needed in case fTwo is zero
168 maStatus
= SolverComponent::GetResourceString( RID_ERROR_NONLINEAR
);
171 SolverComponent::SetValue( mxDoc
, *aVarIter
, 0.0 ); // set back to zero for examining next variable
174 xModel
->unlockControllers();
176 if ( !maStatus
.isEmpty() )
180 // build lp_solve model
183 lprec
* lp
= make_lp( 0, nVariables
);
187 set_outputfile( lp
, const_cast<char*>( "" ) ); // no output
189 // set objective function
191 const std::vector
<double>& rObjCoeff
= aCellsHash
[maObjective
];
192 REAL
* pObjVal
= new REAL
[nVariables
+1];
193 pObjVal
[0] = 0.0; // ignored
194 for (nVar
=0; nVar
<nVariables
; nVar
++)
195 pObjVal
[nVar
+1] = rObjCoeff
[nVar
+1];
196 set_obj_fn( lp
, pObjVal
);
198 set_rh( lp
, 0, rObjCoeff
[0] ); // constant term of objective
202 set_add_rowmode(lp
, TRUE
);
204 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
206 // integer constraints are set later
207 sheet::SolverConstraintOperator eOp
= maConstraints
[nConstrPos
].Operator
;
208 if ( eOp
== sheet::SolverConstraintOperator_LESS_EQUAL
||
209 eOp
== sheet::SolverConstraintOperator_GREATER_EQUAL
||
210 eOp
== sheet::SolverConstraintOperator_EQUAL
)
212 double fDirectValue
= 0.0;
213 bool bRightCell
= false;
214 table::CellAddress aRightAddr
;
215 const uno::Any
& rRightAny
= maConstraints
[nConstrPos
].Right
;
216 if ( rRightAny
>>= aRightAddr
)
217 bRightCell
= true; // cell specified as right-hand side
219 rRightAny
>>= fDirectValue
; // constant value
221 table::CellAddress aLeftAddr
= maConstraints
[nConstrPos
].Left
;
223 const std::vector
<double>& rLeftCoeff
= aCellsHash
[aLeftAddr
];
224 REAL
* pValues
= new REAL
[nVariables
+1];
225 pValues
[0] = 0.0; // ignored?
226 for (nVar
=0; nVar
<nVariables
; nVar
++)
227 pValues
[nVar
+1] = rLeftCoeff
[nVar
+1];
229 // if left hand cell has a constant term, put into rhs value
230 double fRightValue
= -rLeftCoeff
[0];
234 const std::vector
<double>& rRightCoeff
= aCellsHash
[aRightAddr
];
235 // modify pValues with rhs coefficients
236 for (nVar
=0; nVar
<nVariables
; nVar
++)
237 pValues
[nVar
+1] -= rRightCoeff
[nVar
+1];
239 fRightValue
+= rRightCoeff
[0]; // constant term
242 fRightValue
+= fDirectValue
;
244 int nConstrType
= LE
;
247 case sheet::SolverConstraintOperator_LESS_EQUAL
: nConstrType
= LE
; break;
248 case sheet::SolverConstraintOperator_GREATER_EQUAL
: nConstrType
= GE
; break;
249 case sheet::SolverConstraintOperator_EQUAL
: nConstrType
= EQ
; break;
251 OSL_FAIL( "unexpected enum type" );
253 add_constraint( lp
, pValues
, nConstrType
, fRightValue
);
259 set_add_rowmode(lp
, FALSE
);
261 // apply settings to all variables
263 for (nVar
=0; nVar
<nVariables
; nVar
++)
265 if ( !mbNonNegative
)
266 set_unbounded(lp
, nVar
+1); // allow negative (default is non-negative)
267 //! collect bounds from constraints?
269 set_int(lp
, nVar
+1, TRUE
);
272 // apply single-var integer constraints
274 for (sal_Int32 nConstrPos
= 0; nConstrPos
< maConstraints
.getLength(); ++nConstrPos
)
276 sheet::SolverConstraintOperator eOp
= maConstraints
[nConstrPos
].Operator
;
277 if ( eOp
== sheet::SolverConstraintOperator_INTEGER
||
278 eOp
== sheet::SolverConstraintOperator_BINARY
)
280 table::CellAddress aLeftAddr
= maConstraints
[nConstrPos
].Left
;
281 // find variable index for cell
282 for (nVar
=0; nVar
<nVariables
; nVar
++)
283 if ( AddressEqual( aVariableCells
[nVar
], aLeftAddr
) )
285 if ( eOp
== sheet::SolverConstraintOperator_INTEGER
)
286 set_int(lp
, nVar
+1, TRUE
);
288 set_binary(lp
, nVar
+1, TRUE
);
298 if ( !mbLimitBBDepth
)
299 set_bb_depthlimit( lp
, 0 );
301 set_epslevel( lp
, mnEpsilonLevel
);
302 set_timeout( lp
, mnTimeout
);
306 int nResult
= ::solve( lp
);
308 mbSuccess
= ( nResult
== OPTIMAL
);
313 maSolution
.realloc( nVariables
);
315 REAL
* pResultVar
= NULL
;
316 get_ptr_variables( lp
, &pResultVar
);
317 for (nVar
=0; nVar
<nVariables
; nVar
++)
318 maSolution
[nVar
] = pResultVar
[nVar
];
320 mfResultValue
= get_objective( lp
);
322 else if ( nResult
== INFEASIBLE
)
323 maStatus
= SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE
);
324 else if ( nResult
== UNBOUNDED
)
325 maStatus
= SolverComponent::GetResourceString( RID_ERROR_UNBOUNDED
);
326 else if ( nResult
== TIMEOUT
|| nResult
== SUBOPTIMAL
)
327 maStatus
= SolverComponent::GetResourceString( RID_ERROR_TIMEOUT
);
328 // SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error
333 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
334 com_sun_star_comp_Calc_LpsolveSolver_get_implementation(
335 css::uno::XComponentContext
*,
336 css::uno::Sequence
<css::uno::Any
> const &)
338 return cppu::acquire(new LpsolveSolver());
341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */