00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "solvemodel.hxx"
00030 #include "solver.hxx"
00031 #include "tool/global.hxx"
00032 #include "unoglobal.hxx"
00033 #include "lpbuilder.hxx"
00034 #include "nlpbuilder.hxx"
00035 #include "dialog.hxx"
00036 #include "xcalc.hxx"
00037 #include "option.hxx"
00038 #include "numeric/lpmodel.hxx"
00039 #include "numeric/nlpmodel.hxx"
00040 #include "numeric/matrix.hxx"
00041 #include "numeric/type.hxx"
00042 #include "numeric/lpbase.hxx"
00043 #include "numeric/exception.hxx"
00044
00045 #include "numeric/lpsolve.hxx"
00046 #include "numeric/quasinewton.hxx"
00047 #include "numeric/cellfuncobj.hxx"
00048 #ifdef ENABLE_SCSOLVER_UNO_ALGORITHM
00049 #include "numeric/lpuno.hxx"
00050 #endif
00051
00052 #include <memory>
00053 #include <exception>
00054 #include <vector>
00055 #include <map>
00056
00057 #include "scsolver.hrc"
00058
00059 using namespace scsolver::numeric;
00060 using scsolver::numeric::Matrix;
00061 using com::sun::star::table::CellAddress;
00062 using ::std::vector;
00063 using ::std::cout;
00064 using ::std::endl;
00065 using ::std::map;
00066 using ::std::auto_ptr;
00067 using ::std::distance;
00068
00069 namespace scsolver {
00070
00071 typedef struct {
00072 short sheet;
00073 long column;
00074 long row;
00075 } cell_address_t;
00076
00082 class ConstantTermStorage
00083 {
00084 public:
00085 ConstantTermStorage() {}
00086 ~ConstantTermStorage() throw() {}
00087
00088 void setValue(const CellAddress& addr, double value)
00089 {
00090 size_t key = 0;
00091 bool isNewKey = false;
00092 getKeyFromAddress(addr, key, isNewKey);
00093 if (isNewKey)
00094 m_List.insert( map<size_t,double>::value_type(key, value) );
00095 #ifdef SCSOLVER_DEBUG
00096 else
00097 {
00098
00099 if (m_List[key] != value)
00100 throw AssertionWrong();
00101 }
00102 #endif
00103 }
00104
00105 double getValue(const CellAddress& addr)
00106 {
00107 size_t key = 0;
00108 bool isNewKey = false;
00109 getKeyFromAddress(addr, key, isNewKey);
00110 if (isNewKey)
00111 {
00112
00113
00114 throw ConstraintError();
00115 }
00116 return m_List[key];
00117 }
00118
00119 private:
00120 map<size_t, double> m_List;
00121 vector<cell_address_t> m_AddrKeys;
00122
00133 void getKeyFromAddress(const CellAddress& addr, size_t& key, bool& isNewKey)
00134 {
00135 cell_address_t cell;
00136 cell.sheet = addr.Sheet;
00137 cell.column = addr.Column;
00138 cell.row = addr.Row;
00139 vector<cell_address_t>::iterator it,
00140 itBeg = m_AddrKeys.begin(), itEnd = m_AddrKeys.end();
00141
00142 for (it = itBeg; it != itEnd; ++it)
00143 {
00144 if (cell.sheet == it->sheet && cell.column == it->column && cell.row == it->row)
00145 {
00146
00147 key = distance(itBeg, it);
00148 isNewKey = false;
00149 return;
00150 }
00151 }
00152
00153
00154 m_AddrKeys.push_back(cell);
00155 key = m_AddrKeys.size() - 1;
00156 isNewKey = true;
00157 return;
00158 }
00159 };
00160
00167 class CellUpdateSwitch
00168 {
00169 public:
00170 CellUpdateSwitch(CalcInterface* p) :
00171 m_pCalc(p)
00172 {
00173 p->disableCellUpdates();
00174 }
00175
00176 ~CellUpdateSwitch() throw()
00177 {
00178 m_pCalc->enableCellUpdates();
00179 }
00180
00181 private:
00182 CellUpdateSwitch();
00183 void* operator new(size_t size);
00184
00185 CalcInterface* m_pCalc;
00186 };
00187
00188 class SolveModelImpl
00189 {
00190 public:
00191 SolveModelImpl( SolverImpl* p ) :
00192 m_pSolverImpl(p),
00193 m_bSolved(false)
00194 {
00195 }
00196
00197 ~SolveModelImpl() throw()
00198 {
00199 }
00200
00206 void solve()
00207 {
00208 OptModelType type = getSolverImpl()->getOptionData()->getModelType();
00209 switch (type)
00210 {
00211 case OPTMODELTYPE_LP:
00212 case OPTMODELTYPE_MILP:
00213 solveLp();
00214 break;
00215 case OPTMODELTYPE_NLP:
00216 case OPTMODELTYPE_MINLP:
00217 solveNlp();
00218 break;
00219 default:
00220 break;
00221 }
00222 }
00223
00230 void solveLp()
00231 {
00232 using namespace numeric;
00233
00234 SolverDialog* pMainDlg = getSolverImpl()->getMainDialog();
00235 GoalType eGoal = pMainDlg->getGoal();
00236 if ( eGoal == GOAL_UNKNOWN )
00237 {
00238 pMainDlg->showMessage(
00239 pMainDlg->getResStr(SCSOLVER_STR_MSG_GOAL_NOT_SET) );
00240 return;
00241 }
00242
00243 auto_ptr<LpModelBuilder> pBuilder( new LpModelBuilder );
00244 pBuilder->setGoal( eGoal );
00245
00246 CellAddress addr = resolveObjectiveFuncAddress();
00247 pBuilder->setObjectiveFormulaAddress(addr);
00248
00249 pBuilder->clearDecisionVarAddresses();
00250 vector<CellAddress> addrs = resolveDecisionVarAddress();
00251 vector<CellAddress>::iterator it,
00252 itBeg = addrs.begin(), itEnd = addrs.end();
00253 for ( it = itBeg; it != itEnd; ++it )
00254 pBuilder->setDecisionVarAddress(*it);
00255
00256 resolveConstraintAddress( pBuilder.get() );
00257 parseConstraints( pBuilder.get() );
00258
00259 lp::Model aModel = pBuilder->getModel();
00260
00261 OptionData* pOption = getSolverImpl()->getOptionData();
00262 aModel.setVarPositive( pOption->getVarPositive() );
00263 aModel.setVarInteger( pOption->getVarInteger() );
00264
00265 #if SCSOLVER_DEBUG
00266 aModel.print();
00267 #endif
00268 aModel.setPrecision( 2 );
00269 auto_ptr<lp::BaseAlgorithm> algorithm = getLpAlgorithm();
00270
00271 aModel.setVerbose(true);
00272 m_bSolved = false;
00273 try
00274 {
00275 algorithm->setModel( &aModel );
00276 algorithm->solve();
00277 m_bSolved = true;
00278 m_mxSolution = algorithm->getSolution();
00279 updateCells( pBuilder.get() );
00280 pMainDlg->showSolutionFound();
00281 }
00282 catch( const ModelInfeasible& e )
00283 {
00284 Debug( "model infeasible" );
00285 pMainDlg->showSolutionInfeasible();
00286 }
00287 catch( const scsolver::RuntimeError& e )
00288 {
00289
00290 pMainDlg->showMessage( e.getMessage() );
00291 }
00292 catch( const ::std::exception& e )
00293 {
00294
00295 pMainDlg->showMessage(
00296 pMainDlg->getResStr(SCSOLVER_STR_MSG_STD_EXCEPTION_CAUGHT) );
00297 }
00298 }
00299
00303 void solveNlp()
00304 {
00305 using namespace numeric;
00306
00307 SolverDialog* pMainDlg = getSolverImpl()->getMainDialog();
00308 GoalType eGoal = pMainDlg->getGoal();
00309 if ( eGoal == GOAL_UNKNOWN )
00310 {
00311 pMainDlg->showMessage(
00312 pMainDlg->getResStr(SCSOLVER_STR_MSG_GOAL_NOT_SET) );
00313 return;
00314 }
00315
00316 auto_ptr<numeric::CellFuncObj> pFuncObj(
00317 new numeric::CellFuncObj(m_pSolverImpl->getCalcInterface()) );
00318 CellAddress addr = resolveObjectiveFuncAddress();
00319
00320
00321 NlpModelBuilder builder(m_pSolverImpl);
00322 builder.setFuncObj(pFuncObj.get());
00323 builder.setObjectiveFormulaAddress(addr);
00324 builder.clearDecVarAddresses();
00325 vector<CellAddress> addrs = resolveDecisionVarAddress();
00326 vector<CellAddress>::iterator it, itBeg = addrs.begin(), itEnd = addrs.end();
00327 for ( it = itBeg; it != itEnd; ++it )
00328 builder.appendDecVarAddress(*it);
00329
00330 nlp::Model model = builder.getModel();
00331 model.setGoal(eGoal);
00332 model.print();
00333
00334 auto_ptr<nlp::BaseAlgorithm> algorithm = getNlpAlgorithm();
00335 m_bSolved = false;
00336 try
00337 {
00338 algorithm->setModel(&model);
00339 algorithm->solve();
00340 m_bSolved = true;
00341 pMainDlg->showSolutionFound();
00342 }
00343 catch ( const IterationTimedOut& )
00344 {
00345 pMainDlg->showMessage(
00346 getSolverImpl()->getResStr(SCSOLVER_STR_MSG_ITERATION_TIMED_OUT) );
00347 }
00348 catch ( const MaxIterationReached& )
00349 {
00350 pMainDlg->showMessage(
00351 getSolverImpl()->getResStr(SCSOLVER_STR_MSG_MAX_ITERATION_REACHED) );
00352 }
00353 catch ( const RuntimeError& e )
00354 {
00355 pMainDlg->showMessage( e.getMessage() );
00356 }
00357 catch ( const ::std::exception& )
00358 {
00359 pMainDlg->showMessage(
00360 getSolverImpl()->getResStr(SCSOLVER_STR_MSG_STD_EXCEPTION_CAUGHT) );
00361 }
00362 }
00363
00364 bool isSolved() const
00365 {
00366 return m_bSolved;
00367 }
00368
00369 private:
00370 SolverImpl* m_pSolverImpl;
00371 bool m_bSolved;
00372 Matrix m_mxSolution;
00373
00374 SolverImpl* getSolverImpl() const
00375 {
00376 return m_pSolverImpl;
00377 }
00378
00379 auto_ptr<lp::BaseAlgorithm> getLpAlgorithm() const;
00380
00381 auto_ptr<nlp::BaseAlgorithm> getNlpAlgorithm() const
00382 {
00383 auto_ptr<nlp::BaseAlgorithm> p( new nlp::QuasiNewton );
00384 return p;
00385 }
00386
00394 void parseConstraints( LpModelBuilder* pBuilder )
00395 {
00396 CellUpdateSwitch aCellUpdate( m_pSolverImpl->getCalcInterface() );
00397
00398
00399
00400 vector<CellAddress> aAddrs = pBuilder->getAllDecisionVarAddresses();
00401 vector<double> aOrigVal;
00402 vector<CellAddress>::iterator pos;
00403 vector<CellAddress>::iterator aAddrsBegin = aAddrs.begin(), aAddrsEnd = aAddrs.end();
00404 CalcInterface* pCalc = m_pSolverImpl->getCalcInterface();
00405 for ( pos = aAddrsBegin; pos != aAddrsEnd; ++pos )
00406 {
00407
00408
00409 table::CellAddress aAddr = *pos;
00410 rtl::OUString sFormula = pCalc->getCellFormula( aAddr );
00411 pBuilder->setTempCellFormula( aAddr, sFormula );
00412 pCalc->setCellFormula( aAddr, ascii("=0") );
00413 }
00414
00415
00416
00417
00418 vector<ConstraintAddress> aConstAddrs = pBuilder->getAllConstraintAddresses();
00419 vector<ConstraintAddress>::iterator posCA, posCAEnd = aConstAddrs.end();
00420 ConstantTermStorage cnConstTerm;
00421 for (posCA = aConstAddrs.begin(); posCA != posCAEnd; ++posCA)
00422 {
00423 if ( !posCA->isLeftCellNumeric() )
00424 {
00425 double fValL = pCalc->getCellValue( posCA->getLeftCellAddr() );
00426 cnConstTerm.setValue(posCA->getLeftCellAddr(), fValL);
00427 }
00428 if ( !posCA->isRightCellNumeric() )
00429 {
00430 double fValR = pCalc->getCellValue( posCA->getRightCellAddr() );
00431 cnConstTerm.setValue(posCA->getRightCellAddr(), fValR);
00432 }
00433 }
00434
00435
00436
00437
00438
00439 CellAddress aObjAddr = pBuilder->getObjectiveFormulaAddress();
00440
00441 pBuilder->setConstraintMatrixSize( aConstAddrs.size(), aAddrs.size() );
00442
00443 for ( pos = aAddrsBegin; pos != aAddrsEnd; ++pos )
00444 {
00445 table::CellAddress aAddr = *pos;
00446 pCalc->setCellFormula( aAddr, ascii("=1") );
00447
00448 double fVal = pCalc->getCellValue( aObjAddr );
00449 pBuilder->setCostVector( aAddr, fVal );
00450
00451
00452
00453
00454 for ( posCA = aConstAddrs.begin(); posCA != posCAEnd; ++posCA )
00455 {
00456 double fValL = 0.0, fConstValL = 0.0;
00457 if ( posCA->isLeftCellNumeric() )
00458 fConstValL = posCA->getLeftCellValue();
00459 else
00460 {
00461 fValL = pCalc->getCellValue( posCA->getLeftCellAddr() );
00462
00463 fConstValL = cnConstTerm.getValue( posCA->getLeftCellAddr() );
00464 fValL -= fConstValL;
00465 }
00466
00467 double fValR = 0.0, fConstValR = 0.0;
00468 if ( posCA->isRightCellNumeric() )
00469 fConstValR = posCA->getRightCellValue();
00470 else
00471 {
00472 fValR = pCalc->getCellValue( posCA->getRightCellAddr() );
00473 fConstValR = cnConstTerm.getValue( posCA->getRightCellAddr() );
00474 fValL -= fValR - fConstValR;
00475 }
00476 pBuilder->setConstraintCoefficient( aAddr, *posCA, fValL, fConstValR-fConstValL );
00477 }
00478
00479 pCalc->setCellFormula( aAddr, ascii("=0") );
00480 }
00481
00482
00483 for ( pos = aAddrsBegin; pos != aAddrsEnd; ++pos )
00484 {
00485 table::CellAddress aAddr = *pos;
00486 rtl::OUString sOrigFormula = pBuilder->getTempCellFormula( aAddr );
00487 pCalc->setCellFormula( aAddr, sOrigFormula );
00488 }
00489
00490 #if SCSOLVER_DEBUG
00491
00492 for ( pos = aAddrsBegin; pos != aAddrsEnd; ++pos )
00493 {
00494 table::CellAddress aAddr = *pos;
00495 double f = pBuilder->getCostVector( aAddr );
00496 cout << aAddr.Column << ", " << aAddr.Row << " = " << f << endl;
00497 cout.flush();
00498 }
00499 #endif
00500 }
00501
00502 void resolveConstraintAddress( LpModelBuilder* pBuilder );
00503
00510 CellAddress resolveObjectiveFuncAddress()
00511 {
00512 rtl::OUString sTargetCellAddr = m_pSolverImpl->getMainDialog()->getTargetCellAddress();
00513 if ( !sTargetCellAddr.getLength() )
00514 throw RuntimeError( getSolverImpl()->getResStr(SCSOLVER_STR_TARGET_NOT_SET) );
00515
00516 CellAddress aAddr = m_pSolverImpl->getCalcInterface()->getCellAddress(
00517 sTargetCellAddr );
00518
00519 return aAddr;
00520 }
00521
00529 vector<CellAddress> resolveDecisionVarAddress()
00530 {
00531 using com::sun::star::table::CellRangeAddress;
00532
00533 rtl::OUString sAddr = m_pSolverImpl->getMainDialog()->getVarCellAddress();
00534 if ( !sAddr.getLength() )
00535 throw RuntimeError(
00536 getSolverImpl()->getResStr(SCSOLVER_STR_DECISIONVAR_NOT_SET) );
00537
00538 CellRangeAddress aRangeAddr = m_pSolverImpl->getCalcInterface()->getCellRangeAddress(sAddr);
00539
00540
00541 sal_Int16 nSheetId = aRangeAddr.Sheet;
00542 sal_Int32 nSCol = aRangeAddr.StartColumn, nSRow = aRangeAddr.StartRow;
00543 sal_Int32 nECol = aRangeAddr.EndColumn, nERow = aRangeAddr.EndRow;
00544
00545 vector<CellAddress> cn;
00546 cn.reserve( (nECol-nSCol)*(nERow-nSRow) );
00547 for ( sal_Int32 nCol = nSCol; nCol <= nECol; ++nCol )
00548 {
00549 for ( sal_Int32 nRow = nSRow; nRow <= nERow; ++nRow )
00550 {
00551 CellAddress addr;
00552 addr.Sheet = nSheetId;
00553 addr.Column = nCol;
00554 addr.Row = nRow;
00555 cn.push_back(addr);
00556 }
00557 }
00558 return cn;
00559 }
00560
00561 void updateCells( LpModelBuilder* pBuilder );
00562 };
00563
00573 auto_ptr<lp::BaseAlgorithm> SolveModelImpl::getLpAlgorithm() const
00574 {
00575 #ifdef ENABLE_SCSOLVER_UNO_ALGORITHM
00576 auto_ptr<lp::BaseAlgorithm> algorithm( new lp::UnoAlgorithm(
00577 ascii("org.openoffice.sc.solver.LpSolve"), getSolverImpl()->getCalcInterface() ) );
00578 #else
00579
00580 auto_ptr<lp::BaseAlgorithm> algorithm( new lp::LpSolve );
00581 #endif
00582
00583 return algorithm;
00584 }
00585
00586 static bool lcl_isNumeric( const rtl::OUString& sVal )
00587 {
00588 double fVal = sVal.toDouble();
00589 if ( fVal == 0.0 )
00590 {
00591 if ( sVal.indexOf( ascii("$") ) == 0 )
00592 return false;
00593 else
00594 return true;
00595 }
00596 else
00597 return true;
00598 }
00599
00605 void SolveModelImpl::resolveConstraintAddress( LpModelBuilder* pBuilder )
00606 {
00607 vector< ConstraintString > aConstraint = m_pSolverImpl->getMainDialog()->getAllConstraints();
00608 vector< ConstraintString >::const_iterator pos = aConstraint.begin(), posEnd = aConstraint.end();
00609 CalcInterface* pCalc = m_pSolverImpl->getCalcInterface();
00610 pBuilder->clearConstraintAddresses();
00611
00612 for ( ; pos != posEnd; ++pos)
00613 {
00614 ConstraintString aConstStr = *pos;
00615 bool bLHSNumeric = lcl_isNumeric(aConstStr.Left);
00616 bool bRHSNumeric = lcl_isNumeric(aConstStr.Right);
00617
00618 if (bLHSNumeric && bRHSNumeric)
00619 {
00620 Debug("Both LHS and RHS values are numeric. Skipping...");
00621 continue;
00622 }
00623
00624
00625 table::CellRangeAddress aRangeAddrL = pCalc->getCellRangeAddress( aConstStr.Left );
00626 sal_Int16 nLSheet = aRangeAddrL.Sheet;
00627 sal_Int32 nLColS = aRangeAddrL.StartColumn;
00628 sal_Int32 nLColE = aRangeAddrL.EndColumn;
00629 sal_Int32 nLRowS = aRangeAddrL.StartRow;
00630 sal_Int32 nLRowE = aRangeAddrL.EndRow;
00631
00632
00633 table::CellRangeAddress aRangeAddrR = pCalc->getCellRangeAddress( aConstStr.Right );
00634 sal_Int16 nRSheet = aRangeAddrR.Sheet;
00635 sal_Int32 nRColS = aRangeAddrR.StartColumn;
00636 sal_Int32 nRColE = aRangeAddrR.EndColumn;
00637 sal_Int32 nRRowS = aRangeAddrR.StartRow;
00638 sal_Int32 nRRowE = aRangeAddrR.EndRow;
00639
00640 if (bLHSNumeric)
00641 {
00642
00643 for ( sal_Int32 i = 0; i <= nRColE - nRColS; ++i )
00644 {
00645 for ( sal_Int32 j = 0; j <= nRRowE - nRRowS; ++j )
00646 {
00647 ConstraintAddress aConstAddr;
00648 table::CellAddress aAddrR;
00649 aAddrR.Sheet = nRSheet;
00650 aAddrR.Row = nRRowS + j;
00651 aAddrR.Column = nRColS + i;
00652 aConstAddr.setRightCellAddr( aAddrR );
00653 aConstAddr.setEquality( aConstStr.Equal );
00654 aConstAddr.setLeftCellValue( aConstStr.Left.toDouble() );
00655
00656 pBuilder->setConstraintAddress( aConstAddr );
00657 }
00658 }
00659 }
00660 else if (bRHSNumeric)
00661 {
00662
00663 for ( sal_Int32 i = 0; i <= nLColE - nLColS; ++i )
00664 {
00665 for ( sal_Int32 j = 0; j <= nLRowE - nLRowS; ++j )
00666 {
00667 ConstraintAddress aConstAddr;
00668 table::CellAddress aAddrL;
00669 aAddrL.Sheet = nLSheet;
00670 aAddrL.Row = nLRowS + j;
00671 aAddrL.Column = nLColS + i;
00672 aConstAddr.setLeftCellAddr( aAddrL );
00673 aConstAddr.setEquality( aConstStr.Equal );
00674 aConstAddr.setRightCellValue( aConstStr.Right.toDouble() );
00675
00676 pBuilder->setConstraintAddress( aConstAddr );
00677 }
00678 }
00679 }
00680 else
00681 {
00682
00683 if ( nLColE - nLColS != nRColE - nRColS || nLRowE - nLRowS != nRRowE - nRRowS )
00684 throw RuntimeError(
00685 getSolverImpl()->getResStr(SCSOLVER_STR_MSG_CELL_GEOMETRIES_DIFFER) );
00686
00687 for ( sal_Int32 i = 0; i <= nLColE - nLColS; ++i )
00688 {
00689 for ( sal_Int32 j = 0; j <= nLRowE - nLRowS; ++j )
00690 {
00691 ConstraintAddress aConstAddr;
00692 table::CellAddress aAddrL, aAddrR;
00693 aAddrL.Sheet = nLSheet;
00694 aAddrL.Row = nLRowS + j;
00695 aAddrL.Column = nLColS + i;
00696 aConstAddr.setLeftCellAddr( aAddrL );
00697 aConstAddr.setEquality( aConstStr.Equal );
00698
00699 aAddrR.Sheet = nRSheet;
00700 aAddrR.Row = nRRowS + j;
00701 aAddrR.Column = nRColS + i;
00702 aConstAddr.setRightCellAddr( aAddrR );
00703
00704 pBuilder->setConstraintAddress( aConstAddr );
00705 }
00706 }
00707 }
00708 }
00709 }
00710
00711 void SolveModelImpl::updateCells( LpModelBuilder* pBuilder )
00712 {
00713 vector<CellAddress> cnAddrs = pBuilder->getAllDecisionVarAddresses();
00714 CalcInterface* pCalc = m_pSolverImpl->getCalcInterface();
00715 OSL_ASSERT( m_mxSolution.rows() == cnAddrs.size() );
00716 vector<CellAddress>::iterator it, itEnd = cnAddrs.end();
00717 size_t nIdx = 0;
00718 for ( it = cnAddrs.begin(); it != itEnd; ++it )
00719 pCalc->setCellValue( *it, m_mxSolution( nIdx++, 0 ) );
00720 }
00721
00722
00723
00724
00725 SolveModel::SolveModel( SolverImpl* p ) :
00726 m_pImpl( new SolveModelImpl( p ) )
00727 {
00728 }
00729
00730 SolveModel::~SolveModel() throw()
00731 {
00732 }
00733
00734 void SolveModel::solve()
00735 {
00736 m_pImpl->solve();
00737 }
00738
00739 bool SolveModel::isSolved() const
00740 {
00741 return m_pImpl->isSolved();
00742 }
00743
00744
00745 }
00746
00747
00748
00749
00750