xref: /trunk/main/sccomp/source/solver/solver.cxx (revision c27c6a1846e59ecda6d21039cf901f7b88471976)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 #include <CoinMP.h>
24 
25 #include "solver.hxx"
26 #include "solver.hrc"
27 
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/container/XIndexAccess.hpp>
30 #include <com/sun/star/frame/XModel.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
33 #include <com/sun/star/sheet/XSpreadsheet.hpp>
34 #include <com/sun/star/table/CellAddress.hpp>
35 #include <com/sun/star/table/CellRangeAddress.hpp>
36 #include <com/sun/star/text/XTextRange.hpp>
37 
38 #include <rtl/math.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <cppuhelper/factory.hxx>
41 #include <vector>
42 #include <hash_map>
43 #include <unordered_map>
44 #include <string>
45 
46 #include <tools/resmgr.hxx>
47 
48 using namespace com::sun::star;
49 
50 using ::rtl::OUString;
51 
52 #define C2U(constAsciiStr) (::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( constAsciiStr ) ))
53 
54 #define STR_NONNEGATIVE   "NonNegative"
55 #define STR_INTEGER       "Integer"
56 #define STR_TIMEOUT       "Timeout"
57 #define STR_EPSILONLEVEL  "EpsilonLevel"
58 #define STR_LIMITBBDEPTH  "LimitBBDepth"
59 #define STR_NONLINEARTEST "NonLinearTest"
60 
61 // -----------------------------------------------------------------------
62 //  Resources from tools are used for translated strings
63 
64 static ResMgr* pSolverResMgr = NULL;
65 
lcl_GetResourceString(sal_uInt32 nId)66 OUString lcl_GetResourceString( sal_uInt32 nId )
67 {
68     if (!pSolverResMgr)
69         pSolverResMgr = CREATEVERSIONRESMGR( solver );
70 
71     return String( ResId( nId, *pSolverResMgr ) );
72 }
73 
74 // -----------------------------------------------------------------------
75 
76 namespace
77 {
78     enum
79     {
80         PROP_NONNEGATIVE,
81         PROP_INTEGER,
82         PROP_TIMEOUT,
83         PROP_EPSILONLEVEL,
84         PROP_LIMITBBDEPTH,
85         PROP_NONLINEARTEST
86     };
87 }
88 
89 // -----------------------------------------------------------------------
90 
91 // hash map for the coefficients of a dependent cell (objective or constraint)
92 // The size of each vector is the number of columns (variable cells) plus one, first entry is initial value.
93 
94 struct ScSolverCellHash
95 {
operator ()ScSolverCellHash96     size_t operator()( const table::CellAddress& rAddress ) const
97     {
98         return ( rAddress.Sheet << 24 ) | ( rAddress.Column << 16 ) | rAddress.Row;
99     }
100 };
101 
AddressEqual(const table::CellAddress & rAddr1,const table::CellAddress & rAddr2)102 inline bool AddressEqual( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 )
103 {
104     return rAddr1.Sheet == rAddr2.Sheet && rAddr1.Column == rAddr2.Column && rAddr1.Row == rAddr2.Row;
105 }
106 
107 struct ScSolverCellEqual
108 {
operator ()ScSolverCellEqual109     bool operator()( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 ) const
110     {
111         return AddressEqual( rAddr1, rAddr2 );
112     }
113 };
114 
115 typedef std::hash_map< table::CellAddress, std::vector<double>, ScSolverCellHash, ScSolverCellEqual > ScSolverCellHashMap;
116 
117 // -----------------------------------------------------------------------
118 
lcl_GetCell(const uno::Reference<sheet::XSpreadsheetDocument> & xDoc,const table::CellAddress & rPos)119 uno::Reference<table::XCell> lcl_GetCell( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
120                                           const table::CellAddress& rPos )
121 {
122     uno::Reference<container::XIndexAccess> xSheets( xDoc->getSheets(), uno::UNO_QUERY );
123     uno::Reference<sheet::XSpreadsheet> xSheet( xSheets->getByIndex( rPos.Sheet ), uno::UNO_QUERY );
124     return xSheet->getCellByPosition( rPos.Column, rPos.Row );
125 }
126 
lcl_SetValue(const uno::Reference<sheet::XSpreadsheetDocument> & xDoc,const table::CellAddress & rPos,double fValue)127 void lcl_SetValue( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
128                    const table::CellAddress& rPos, double fValue )
129 {
130     lcl_GetCell( xDoc, rPos )->setValue( fValue );
131 }
132 
lcl_GetValue(const uno::Reference<sheet::XSpreadsheetDocument> & xDoc,const table::CellAddress & rPos)133 double lcl_GetValue( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
134                      const table::CellAddress& rPos )
135 {
136     return lcl_GetCell( xDoc, rPos )->getValue();
137 }
138 
139 // -------------------------------------------------------------------------
140 
SolverComponent(const uno::Reference<uno::XComponentContext> &)141 SolverComponent::SolverComponent( const uno::Reference<uno::XComponentContext>& /* rSMgr */ ) :
142     OPropertyContainer( GetBroadcastHelper() ),
143     mbMaximize( sal_True ),
144     mbNonNegative( sal_False ),
145     mbInteger( sal_False ),
146     mnTimeout( 120 ),
147     mnEpsilonLevel( 0 ),
148     mbLimitBBDepth( sal_True ),
149     mbNonLinearTest( sal_True ),
150     mbSuccess( sal_False ),
151     mfResultValue( 0.0 )
152 {
153     // for XPropertySet implementation:
154     registerProperty( C2U(STR_NONNEGATIVE),  PROP_NONNEGATIVE,  0, &mbNonNegative,  getCppuType( &mbNonNegative )  );
155     registerProperty( C2U(STR_INTEGER),      PROP_INTEGER,      0, &mbInteger,      getCppuType( &mbInteger )      );
156     registerProperty( C2U(STR_TIMEOUT),      PROP_TIMEOUT,      0, &mnTimeout,      getCppuType( &mnTimeout )      );
157     registerProperty( C2U(STR_EPSILONLEVEL), PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, getCppuType( &mnEpsilonLevel ) );
158     registerProperty( C2U(STR_LIMITBBDEPTH), PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, getCppuType( &mbLimitBBDepth ) );
159     registerProperty( C2U(STR_NONLINEARTEST), PROP_NONLINEARTEST, 0, &mbNonLinearTest, getCppuType( &mbNonLinearTest ) );
160 }
161 
~SolverComponent()162 SolverComponent::~SolverComponent()
163 {
164 }
165 
IMPLEMENT_FORWARD_XINTERFACE2(SolverComponent,SolverComponent_Base,OPropertyContainer)166 IMPLEMENT_FORWARD_XINTERFACE2( SolverComponent, SolverComponent_Base, OPropertyContainer )
167 IMPLEMENT_FORWARD_XTYPEPROVIDER2( SolverComponent, SolverComponent_Base, OPropertyContainer )
168 
169 cppu::IPropertyArrayHelper* SolverComponent::createArrayHelper() const
170 {
171     uno::Sequence<beans::Property> aProps;
172     describeProperties( aProps );
173     return new cppu::OPropertyArrayHelper( aProps );
174 }
175 
getInfoHelper()176 cppu::IPropertyArrayHelper& SAL_CALL SolverComponent::getInfoHelper()
177 {
178     return *getArrayHelper();
179 }
180 
getPropertySetInfo()181 uno::Reference<beans::XPropertySetInfo> SAL_CALL SolverComponent::getPropertySetInfo()
182 {
183     return createPropertySetInfo( getInfoHelper() );
184 }
185 
186 // XSolverDescription
187 
getComponentDescription()188 OUString SAL_CALL SolverComponent::getComponentDescription()
189 {
190     return lcl_GetResourceString( RID_SOLVER_COMPONENT );
191 }
192 
getStatusDescription()193 OUString SAL_CALL SolverComponent::getStatusDescription()
194 {
195     return maStatus;
196 }
197 
getPropertyDescription(const OUString & rPropertyName)198 OUString SAL_CALL SolverComponent::getPropertyDescription( const OUString& rPropertyName )
199 {
200     sal_uInt32 nResId = 0;
201     sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
202     switch (nHandle)
203     {
204         case PROP_NONNEGATIVE:
205             nResId = RID_PROPERTY_NONNEGATIVE;
206             break;
207         case PROP_INTEGER:
208             nResId = RID_PROPERTY_INTEGER;
209             break;
210         case PROP_TIMEOUT:
211             nResId = RID_PROPERTY_TIMEOUT;
212             break;
213         case PROP_EPSILONLEVEL:
214             nResId = RID_PROPERTY_EPSILONLEVEL;
215             break;
216         case PROP_LIMITBBDEPTH:
217             nResId = RID_PROPERTY_LIMITBBDEPTH;
218             break;
219         case PROP_NONLINEARTEST:
220             nResId = RID_PROPERTY_NONLINEARTEST;
221             break;
222         default:
223             {
224                 // unknown - leave empty
225             }
226     }
227     OUString aRet;
228     if ( nResId )
229         aRet = lcl_GetResourceString( nResId );
230     return aRet;
231 }
232 
233 // XSolver: settings
234 
getDocument()235 uno::Reference<sheet::XSpreadsheetDocument> SAL_CALL SolverComponent::getDocument()
236 {
237     return mxDoc;
238 }
239 
setDocument(const uno::Reference<sheet::XSpreadsheetDocument> & _document)240 void SAL_CALL SolverComponent::setDocument( const uno::Reference<sheet::XSpreadsheetDocument>& _document )
241 {
242     mxDoc = _document;
243 }
244 
getObjective()245 table::CellAddress SAL_CALL SolverComponent::getObjective()
246 {
247     return maObjective;
248 }
249 
setObjective(const table::CellAddress & _objective)250 void SAL_CALL SolverComponent::setObjective( const table::CellAddress& _objective )
251 {
252     maObjective = _objective;
253 }
254 
getVariables()255 uno::Sequence<table::CellAddress> SAL_CALL SolverComponent::getVariables()
256 {
257     return maVariables;
258 }
259 
setVariables(const uno::Sequence<table::CellAddress> & _variables)260 void SAL_CALL SolverComponent::setVariables( const uno::Sequence<table::CellAddress>& _variables )
261 {
262     maVariables = _variables;
263 }
264 
getConstraints()265 uno::Sequence<sheet::SolverConstraint> SAL_CALL SolverComponent::getConstraints()
266 {
267     return maConstraints;
268 }
269 
setConstraints(const uno::Sequence<sheet::SolverConstraint> & _constraints)270 void SAL_CALL SolverComponent::setConstraints( const uno::Sequence<sheet::SolverConstraint>& _constraints )
271 {
272     maConstraints = _constraints;
273 }
274 
getMaximize()275 sal_Bool SAL_CALL SolverComponent::getMaximize()
276 {
277     return mbMaximize;
278 }
279 
setMaximize(sal_Bool _maximize)280 void SAL_CALL SolverComponent::setMaximize( sal_Bool _maximize )
281 {
282     mbMaximize = _maximize;
283 }
284 
285 // XSolver: get results
286 
getSuccess()287 sal_Bool SAL_CALL SolverComponent::getSuccess()
288 {
289     return mbSuccess;
290 }
291 
getResultValue()292 double SAL_CALL SolverComponent::getResultValue()
293 {
294     return mfResultValue;
295 }
296 
getSolution()297 uno::Sequence<double> SAL_CALL SolverComponent::getSolution()
298 {
299     return maSolution;
300 }
301 
302 // -------------------------------------------------------------------------
303 
solve()304 void SAL_CALL SolverComponent::solve()
305 {
306     uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY );
307     if ( !xModel.is() )
308         throw uno::RuntimeException();
309 
310     maStatus = OUString();
311     mbSuccess = false;
312 
313     xModel->lockControllers();
314 
315     // collect variables in vector (?)
316 
317     std::vector<table::CellAddress> aVariableCells;
318     for (sal_Int32 nPos=0; nPos<maVariables.getLength(); nPos++)
319         aVariableCells.push_back( maVariables[nPos] );
320     size_t nVariables = aVariableCells.size();
321     size_t nVar = 0;
322 
323     // collect all dependent cells
324 
325     ScSolverCellHashMap aCellsHash;
326     aCellsHash[maObjective].reserve( nVariables + 1 );                  // objective function
327 
328     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
329     {
330         table::CellAddress aCellAddr = maConstraints[nConstrPos].Left;
331         aCellsHash[aCellAddr].reserve( nVariables + 1 );                // constraints: left hand side
332 
333         if ( maConstraints[nConstrPos].Right >>= aCellAddr )
334             aCellsHash[aCellAddr].reserve( nVariables + 1 );            // constraints: right hand side
335     }
336 
337     // Save the current values of the changing cells for use as a possible
338     // initial solution, then reset the cells to zero for model construction.
339     std::vector<double> aInitValues(nVariables);
340     for (nVar = 0; nVar < nVariables; ++nVar)
341     {
342         // read current value of the variable cell and store as initial value
343         aInitValues[nVar] = lcl_GetValue( mxDoc, aVariableCells[nVar] );
344         lcl_SetValue( mxDoc, aVariableCells[nVar], 0.0 );
345     }
346     std::vector<table::CellAddress>::const_iterator aVarIter;
347 
348     // read initial values from all dependent cells
349     ScSolverCellHashMap::iterator aCellsIter;
350     for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
351     {
352         double fValue = lcl_GetValue( mxDoc, aCellsIter->first );
353         aCellsIter->second.push_back( fValue );                         // store as first element, as-is
354     }
355 
356     // loop through variables
357     for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter )
358     {
359         lcl_SetValue( mxDoc, *aVarIter, 1.0 );      // set to 1 to examine influence
360 
361         // read value change from all dependent cells
362         for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
363         {
364             double fChanged = lcl_GetValue( mxDoc, aCellsIter->first );
365             double fInitial = aCellsIter->second.front();
366             aCellsIter->second.push_back( fChanged - fInitial );
367         }
368 
369         lcl_SetValue( mxDoc, *aVarIter, 2.0 );      // minimal test for linearity
370 
371         for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
372         {
373             double fInitial = aCellsIter->second.front();
374             double fCoeff   = aCellsIter->second.back();       // last appended: coefficient for this variable
375             double fTwo     = lcl_GetValue( mxDoc, aCellsIter->first );
376 
377           if ( mbNonLinearTest )
378           {
379               bool bLinear ( sal_True );
380               bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) ||
381               rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff );
382             // second comparison is needed in case fTwo is zero
383               if ( !bLinear )
384                   maStatus = lcl_GetResourceString( RID_ERROR_NONLINEAR );
385            }
386         }
387 
388         lcl_SetValue( mxDoc, *aVarIter, 0.0 );      // set back to zero for examining next variable
389     }
390 
391     xModel->unlockControllers();
392 
393     if ( maStatus.getLength() )
394         return;
395 
396     //
397     // build parameter arrays for CoinMP
398     //
399 
400     // set objective function
401 
402     const std::vector<double>& rObjCoeff = aCellsHash[maObjective];
403     double* pObjectCoeffs = new double[nVariables];
404     for (nVar=0; nVar<nVariables; nVar++)
405         pObjectCoeffs[nVar] = rObjCoeff[nVar+1];
406     double nObjectConst = rObjCoeff[0];             // constant term of objective
407 
408     // add rows
409 
410     size_t nRows = maConstraints.getLength();
411     size_t nCompSize = nVariables * nRows;
412     double* pCompMatrix = new double[nCompSize];    // first collect all coefficients, row-wise
413     for (size_t i=0; i<nCompSize; i++)
414         pCompMatrix[i] = 0.0;
415 
416     double* pRHS = new double[nRows];
417     char* pRowType = new char[nRows];
418     for (size_t i=0; i<nRows; i++)
419     {
420         pRHS[i] = 0.0;
421         pRowType[i] = 'N';
422     }
423 
424     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
425     {
426         // integer constraints are set later
427         sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
428         if ( eOp == sheet::SolverConstraintOperator_LESS_EQUAL ||
429              eOp == sheet::SolverConstraintOperator_GREATER_EQUAL ||
430              eOp == sheet::SolverConstraintOperator_EQUAL )
431         {
432             double fDirectValue = 0.0;
433             bool bRightCell = false;
434             table::CellAddress aRightAddr;
435             const uno::Any& rRightAny = maConstraints[nConstrPos].Right;
436             if ( rRightAny >>= aRightAddr )
437                 bRightCell = true;                  // cell specified as right-hand side
438             else
439                 rRightAny >>= fDirectValue;         // constant value
440 
441             table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
442 
443             const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr];
444             double* pValues = &pCompMatrix[nConstrPos * nVariables];
445             for (nVar=0; nVar<nVariables; nVar++)
446                 pValues[nVar] = rLeftCoeff[nVar+1];
447 
448             // if left hand cell has a constant term, put into rhs value
449             double fRightValue = -rLeftCoeff[0];
450 
451             if ( bRightCell )
452             {
453                 const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr];
454                 // modify pValues with rhs coefficients
455                 for (nVar=0; nVar<nVariables; nVar++)
456                     pValues[nVar] -= rRightCoeff[nVar+1];
457 
458                 fRightValue += rRightCoeff[0];      // constant term
459             }
460             else
461                 fRightValue += fDirectValue;
462 
463             switch ( eOp )
464             {
465                 case sheet::SolverConstraintOperator_LESS_EQUAL:    pRowType[nConstrPos] = 'L'; break;
466                 case sheet::SolverConstraintOperator_GREATER_EQUAL: pRowType[nConstrPos] = 'G'; break;
467                 case sheet::SolverConstraintOperator_EQUAL:         pRowType[nConstrPos] = 'E'; break;
468                 default:
469                     OSL_ENSURE( false, "unexpected enum type" );
470             }
471             pRHS[nConstrPos] = fRightValue;
472         }
473     }
474 
475     // Try to combine complementary <= and >= rows with identical coefficients
476     // into a single ranged row 'R' with RANGE = upper - lower.
477     // We do this before building the column-wise matrix. When a pair is
478     // merged we zero out the coefficients of the removed row so it is
479     // ignored when building the sparse column representation.
480     // Allocate a row-indexed range array up-front (one entry per original row).
481     double* pRangeValues = new double[nRows];
482     for (size_t i = 0; i < nRows; ++i) pRangeValues[i] = 0.0;
483 
484     // Two-pass approach: first collect best lower/upper per coefficient
485     // signature (exact bitwise signature). Second, convert compatibles to
486     // ranged rows. This avoids online erase/replace semantics and ensures
487     // decisions are based on the original model.
488     struct RowPair { size_t lowerIdx; double lowerVal; size_t upperIdx; double upperVal; };
489     const size_t npos = static_cast<size_t>(-1);
490     std::unordered_map< std::string, RowPair > rowMap;
491     rowMap.reserve(nRows * 2);
492 
493     // Pass 1: populate rowMap with tightest lower (max G) and tightest
494     // upper (min L) for each coefficient signature.
495     for (size_t i = 0; i < nRows; ++i)
496     {
497         char ti = pRowType[i];
498         if ( ti != 'L' && ti != 'G' )
499             continue;
500 
501         const char* data = reinterpret_cast<const char*>(&pCompMatrix[i * nVariables]);
502         size_t len = (size_t)nVariables * sizeof(double);
503         std::string sig;
504         sig.assign(data, len);
505 
506         auto it = rowMap.find(sig);
507         if ( it == rowMap.end() )
508         {
509             RowPair rp; rp.lowerIdx = npos; rp.upperIdx = npos; rp.lowerVal = 0.0; rp.upperVal = 0.0;
510             std::pair<std::unordered_map<std::string, RowPair>::iterator, bool> res = rowMap.insert(std::make_pair(sig, rp));
511             it = res.first;
512         }
513 
514         RowPair &rp = it->second;
515         if ( ti == 'L' )
516         {
517             double v = pRHS[i];
518             if ( rp.upperIdx == npos || v < rp.upperVal )
519             {
520                 rp.upperIdx = i;
521                 rp.upperVal = v;
522             }
523         }
524         else // 'G'
525         {
526             double v = pRHS[i];
527             if ( rp.lowerIdx == npos || v > rp.lowerVal )
528             {
529                 rp.lowerIdx = i;
530                 rp.lowerVal = v;
531             }
532         }
533     }
534 
535     // Pass 2: perform conversions for entries that have both bounds.
536     size_t nMergedRows = 0;
537     for (auto &kv : rowMap)
538     {
539         RowPair &rp = kv.second;
540         if ( rp.lowerIdx == npos || rp.upperIdx == npos )
541             continue;
542 
543         size_t idxLower = rp.lowerIdx;
544         size_t idxUpper = rp.upperIdx;
545         double lower = rp.lowerVal;
546         double upper = rp.upperVal;
547 
548         if ( lower <= upper )
549         {
550             // make upper the ranged row and mark lower as removed
551             pRowType[idxUpper] = 'R';
552             pRHS[idxUpper] = upper;
553             pRangeValues[idxUpper] = upper - lower;
554 
555             pRowType[idxLower] = 'N';
556             pRHS[idxLower] = 0.0;
557 
558             ++nMergedRows;
559             OSL_TRACE("Solver: merging rows %lu (G %.17g) and %lu (L %.17g) into ranged row %lu [%.17g, %.17g]\n",
560                       static_cast<unsigned long>(idxLower), lower,
561                       static_cast<unsigned long>(idxUpper), upper,
562                       static_cast<unsigned long>(idxUpper), lower, upper);
563         }
564         else
565         {
566             // invalid (contradictory) bounds: leave rows unchanged
567             OSL_TRACE("Solver: contradictory bounds for coeff-signature - lowerRow=%lu (%.17g) upperRow=%lu (%.17g); leaving rows unchanged\n",
568                       static_cast<unsigned long>(idxLower), lower,
569                       static_cast<unsigned long>(idxUpper), upper);
570         }
571     }
572 
573     // After Pass 2 produce a summary trace (rows merged, ranged rows created,
574     // rows eliminated). This is the default diagnostic; per-row traces are
575     // emitted above and can be enabled/disabled by adjusting trace levels.
576     int nRangeCount_tmp = 0;
577     int nEliminated = 0;
578     for (size_t i = 0; i < nRows; ++i)
579     {
580         if ( pRowType[i] == 'R' ) ++nRangeCount_tmp;
581         if ( pRowType[i] == 'N' ) ++nEliminated;
582     }
583     OSL_TRACE("Solver: %lu input rows, %d ranged rows created, %d rows eliminated, %lu merged pairs\n",
584               static_cast<unsigned long>(nRows), nRangeCount_tmp, nEliminated, static_cast<unsigned long>(nMergedRows));
585 
586     // Find non-zero coefficients, column-wise
587 
588     int* pMatrixBegin = new int[nVariables+1];
589     int* pMatrixCount = new int[nVariables];
590     double* pMatrix = new double[nCompSize];    // not always completely used
591     int* pMatrixIndex = new int[nCompSize];
592     int nMatrixPos = 0;
593     for (nVar=0; nVar<nVariables; nVar++)
594     {
595         int nBegin = nMatrixPos;
596         for (size_t nRow=0; nRow<nRows; nRow++)
597         {
598             if ( pRowType[nRow] == 'N' )
599                 continue;
600             double fCoeff = pCompMatrix[ nRow * nVariables + nVar ];    // row-wise
601             if ( fCoeff != 0.0 )
602             {
603                 pMatrix[nMatrixPos] = fCoeff;
604                 pMatrixIndex[nMatrixPos] = nRow;
605                 ++nMatrixPos;
606             }
607         }
608         pMatrixBegin[nVar] = nBegin;
609         pMatrixCount[nVar] = nMatrixPos - nBegin;
610     }
611     pMatrixBegin[nVariables] = nMatrixPos;
612     delete[] pCompMatrix;
613     pCompMatrix = NULL;
614 
615     // Count ranged rows and keep the row-indexed pRangeValues allocated above.
616     int nRangeCount = 0;
617     for (size_t i = 0; i < nRows; ++i)
618         if ( pRowType[i] == 'R' )
619             ++nRangeCount;
620 
621     // apply settings to all variables
622 
623     double* pLowerBounds = new double[nVariables];
624     double* pUpperBounds = new double[nVariables];
625     for (nVar=0; nVar<nVariables; nVar++)
626     {
627         pLowerBounds[nVar] = mbNonNegative ? 0.0 : -DBL_MAX;
628         pUpperBounds[nVar] = DBL_MAX;
629 
630         // bounds could possibly be further restricted from single-cell constraints
631     }
632 
633     char* pColType = new char[nVariables];
634     for (nVar=0; nVar<nVariables; nVar++)
635         pColType[nVar] = mbInteger ? 'I' : 'C';
636 
637     // apply single-var integer constraints
638 
639     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
640     {
641         sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
642         if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
643              eOp == sheet::SolverConstraintOperator_BINARY )
644         {
645             table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
646             // find variable index for cell
647             for (nVar=0; nVar<nVariables; nVar++)
648                 if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) )
649                 {
650                     if ( eOp == sheet::SolverConstraintOperator_INTEGER )
651                         pColType[nVar] = 'I';
652                     else
653                     {
654                         pColType[nVar] = 'B';
655                         pLowerBounds[nVar] = 0.0;
656                         pUpperBounds[nVar] = 1.0;
657                     }
658                 }
659         }
660     }
661 
662     int nObjectSense = mbMaximize ? SOLV_OBJSENS_MAX : SOLV_OBJSENS_MIN;
663 
664     HPROB hProb = CoinCreateProblem("");
665     int nResult = CoinLoadProblem( hProb, nVariables, nRows, nMatrixPos, nRangeCount,
666                     nObjectSense, nObjectConst, pObjectCoeffs,
667                     pLowerBounds, pUpperBounds, pRowType, pRHS, pRangeValues,
668                     pMatrixBegin, pMatrixCount, pMatrixIndex, pMatrix,
669                     NULL, NULL, NULL );
670     if ( nResult == SOLV_CALL_SUCCESS )
671         nResult = CoinLoadInteger( hProb, pColType );
672 
673     if ( pRangeValues )
674     {
675         delete[] pRangeValues;
676         pRangeValues = NULL;
677     }
678 
679     // Supply the current variable values as a possible initial solution.
680     // Pass the spreadsheet values unchanged; CoinMP/CBC is responsible for
681     // validating the supplied initial solution.
682     if ( nResult == SOLV_CALL_SUCCESS && !aInitValues.empty() )
683     {
684         int nLoadRc = CoinLoadInitValues( hProb, aInitValues.data() );
685         if ( nLoadRc != SOLV_CALL_SUCCESS )
686         {
687             // Non-fatal: initial values are an optimization hint only; proceed without them.
688             OSL_TRACE("CoinLoadInitValues failed: %d\n", nLoadRc);
689         }
690     }
691 
692     delete[] pColType;
693     delete[] pMatrixIndex;
694     delete[] pMatrix;
695     delete[] pMatrixCount;
696     delete[] pMatrixBegin;
697     delete[] pUpperBounds;
698     delete[] pLowerBounds;
699     delete[] pRowType;
700     delete[] pRHS;
701     delete[] pObjectCoeffs;
702 
703     CoinSetRealOption( hProb, COIN_REAL_MAXSECONDS, mnTimeout );
704     CoinSetRealOption( hProb, COIN_REAL_MIPMAXSEC, mnTimeout );
705 
706     // TODO: handle (or remove) settings: epsilon, B&B depth
707 
708     // solve model
709 
710     nResult = CoinCheckProblem( hProb );
711     if (nResult != SOLV_CALL_SUCCESS)
712     {
713         // report invalid model
714 
715        maStatus = lcl_GetResourceString( RID_ERROR_INVALIDMODEL );
716         CoinUnloadProblem(hProb);
717         return;
718     }
719     nResult = CoinOptimizeProblem( hProb, 0 );
720 
721     mbSuccess = ( nResult == SOLV_CALL_SUCCESS );
722     if ( mbSuccess )
723     {
724         // get solution
725 
726         maSolution.realloc( nVariables );
727         CoinGetSolutionValues( hProb, maSolution.getArray(), NULL, NULL, NULL );
728         mfResultValue = CoinGetObjectValue( hProb );
729     }
730     else
731     {
732         int nSolutionStatus = CoinGetSolutionStatus( hProb );
733         if ( nSolutionStatus == 1 )
734             maStatus = lcl_GetResourceString( RID_ERROR_INFEASIBLE );
735         else if ( nSolutionStatus == 2 )
736             maStatus = lcl_GetResourceString( RID_ERROR_UNBOUNDED );
737     else if ( nSolutionStatus == 3 )
738             maStatus = lcl_GetResourceString(  RID_ERROR_ITERATIONLIMIT );
739     else if ( nSolutionStatus == 4 )
740             maStatus = lcl_GetResourceString( RID_ERROR_SOLVERERROR );
741         else if ( nSolutionStatus == 5 )
742             maStatus = lcl_GetResourceString( RID_ERROR_USERSTOP );
743     else if ( nSolutionStatus >= 6 )
744             maStatus = lcl_GetResourceString( RID_ERROR_UNKNOWN );
745 
746     }
747 
748     CoinUnloadProblem( hProb );
749 }
750 
751 // -------------------------------------------------------------------------
752 
753 // XServiceInfo
754 
SolverComponent_getSupportedServiceNames()755 uno::Sequence< OUString > SolverComponent_getSupportedServiceNames()
756 {
757     uno::Sequence< OUString > aServiceNames( 1 );
758     aServiceNames[ 0 ] = OUString::createFromAscii( "com.sun.star.sheet.Solver" );
759     return aServiceNames;
760 }
761 
SolverComponent_getImplementationName()762 OUString SolverComponent_getImplementationName()
763 {
764     return OUString::createFromAscii( "com.sun.star.comp.Calc.Solver" );
765 }
766 
getImplementationName()767 OUString SAL_CALL SolverComponent::getImplementationName()
768 {
769     return SolverComponent_getImplementationName();
770 }
771 
supportsService(const OUString & rServiceName)772 sal_Bool SAL_CALL SolverComponent::supportsService( const OUString& rServiceName )
773 {
774     const uno::Sequence< OUString > aServices = SolverComponent_getSupportedServiceNames();
775     const OUString* pArray = aServices.getConstArray();
776     const OUString* pArrayEnd = pArray + aServices.getLength();
777     return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
778 }
779 
getSupportedServiceNames()780 uno::Sequence<OUString> SAL_CALL SolverComponent::getSupportedServiceNames()
781 {
782     return SolverComponent_getSupportedServiceNames();
783 }
784 
SolverComponent_createInstance(const uno::Reference<uno::XComponentContext> & rSMgr)785 uno::Reference<uno::XInterface> SolverComponent_createInstance( const uno::Reference<uno::XComponentContext>& rSMgr )
786 {
787     return (cppu::OWeakObject*) new SolverComponent( rSMgr );
788 }
789 
790 // -------------------------------------------------------------------------
791 
792 extern "C"
793 {
component_getImplementationEnvironment(const sal_Char ** ppEnvTypeName,uno_Environment **)794     SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment(
795         const sal_Char ** ppEnvTypeName, uno_Environment ** )
796     {
797         *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
798     }
799 
800     // -------------------------------------------------------------------------
801 
component_getFactory(const sal_Char * pImplName,void * pServiceManager,void *)802     SAL_DLLPUBLIC_EXPORT void* SAL_CALL component_getFactory( const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
803     {
804         OUString    aImplName( OUString::createFromAscii( pImplName ) );
805         void*       pRet = 0;
806 
807         if( pServiceManager )
808         {
809             uno::Reference< lang::XSingleComponentFactory > xFactory;
810             if( aImplName.equals( SolverComponent_getImplementationName() ) )
811                 xFactory = cppu::createSingleComponentFactory(
812                         SolverComponent_createInstance,
813                         OUString::createFromAscii( pImplName ),
814                         SolverComponent_getSupportedServiceNames() );
815 
816             if( xFactory.is() )
817             {
818                 xFactory->acquire();
819                 pRet = xFactory.get();
820             }
821         }
822         return pRet;
823     }
824 }
825