Lines 25-36
Link Here
|
25 |
* |
25 |
* |
26 |
************************************************************************/ |
26 |
************************************************************************/ |
27 |
|
27 |
|
28 |
#undef LANGUAGE_NONE |
28 |
#include <coinmp/CoinMP.h> |
29 |
#define WINAPI __stdcall |
|
|
30 |
#define LoadInverseLib FALSE |
31 |
#define LoadLanguageLib FALSE |
32 |
#include <lpsolve/lp_lib.h> |
33 |
#undef LANGUAGE_NONE |
34 |
|
29 |
|
35 |
#include "solver.hxx" |
30 |
#include "solver.hxx" |
36 |
#include "solver.hrc" |
31 |
#include "solver.hrc" |
Lines 314-325
Link Here
|
314 |
maStatus = OUString(); |
309 |
maStatus = OUString(); |
315 |
mbSuccess = false; |
310 |
mbSuccess = false; |
316 |
|
311 |
|
317 |
if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY ) |
|
|
318 |
{ |
319 |
maStatus = lcl_GetResourceString( RID_ERROR_EPSILONLEVEL ); |
320 |
return; |
321 |
} |
322 |
|
323 |
xModel->lockControllers(); |
312 |
xModel->lockControllers(); |
324 |
|
313 |
|
325 |
// collect variables in vector (?) |
314 |
// collect variables in vector (?) |
Lines 398-426
Link Here
|
398 |
return; |
387 |
return; |
399 |
|
388 |
|
400 |
// |
389 |
// |
401 |
// build lp_solve model |
390 |
// build parameter arrays for CoinMP |
402 |
// |
391 |
// |
403 |
|
392 |
|
404 |
lprec* lp = make_lp( 0, nVariables ); |
|
|
405 |
if ( !lp ) |
406 |
return; |
407 |
|
408 |
set_outputfile( lp, const_cast<char*>( "" ) ); // no output |
409 |
|
410 |
// set objective function |
393 |
// set objective function |
411 |
|
394 |
|
412 |
const std::vector<double>& rObjCoeff = aCellsHash[maObjective]; |
395 |
const std::vector<double>& rObjCoeff = aCellsHash[maObjective]; |
413 |
REAL* pObjVal = new REAL[nVariables+1]; |
396 |
double* pObjectCoeffs = new double[nVariables]; |
414 |
pObjVal[0] = 0.0; // ignored |
|
|
415 |
for (nVar=0; nVar<nVariables; nVar++) |
397 |
for (nVar=0; nVar<nVariables; nVar++) |
416 |
pObjVal[nVar+1] = rObjCoeff[nVar+1]; |
398 |
pObjectCoeffs[nVar] = rObjCoeff[nVar+1]; |
417 |
set_obj_fn( lp, pObjVal ); |
399 |
double nObjectConst = rObjCoeff[0]; // constant term of objective |
418 |
delete[] pObjVal; |
|
|
419 |
set_rh( lp, 0, rObjCoeff[0] ); // constant term of objective |
420 |
|
400 |
|
421 |
// add rows |
401 |
// add rows |
422 |
|
402 |
|
423 |
set_add_rowmode(lp, TRUE); |
403 |
size_t nRows = maConstraints.getLength(); |
|
|
404 |
size_t nCompSize = nVariables * nRows; |
405 |
double* pCompMatrix = new double[nCompSize]; // first collect all coefficients, row-wise |
406 |
for (size_t i=0; i<nCompSize; i++) |
407 |
pCompMatrix[i] = 0.0; |
408 |
|
409 |
double* pRHS = new double[nRows]; |
410 |
char* pRowType = new char[nRows]; |
411 |
for (size_t i=0; i<nRows; i++) |
412 |
{ |
413 |
pRHS[i] = 0.0; |
414 |
pRowType[i] = 'N'; |
415 |
} |
424 |
|
416 |
|
425 |
for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos) |
417 |
for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos) |
426 |
{ |
418 |
{ |
Lines 442-451
Link Here
|
442 |
table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left; |
434 |
table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left; |
443 |
|
435 |
|
444 |
const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr]; |
436 |
const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr]; |
445 |
REAL* pValues = new REAL[nVariables+1]; |
437 |
double* pValues = &pCompMatrix[nConstrPos * nVariables]; |
446 |
pValues[0] = 0.0; // ignored? |
|
|
447 |
for (nVar=0; nVar<nVariables; nVar++) |
438 |
for (nVar=0; nVar<nVariables; nVar++) |
448 |
pValues[nVar+1] = rLeftCoeff[nVar+1]; |
439 |
pValues[nVar] = rLeftCoeff[nVar+1]; |
449 |
|
440 |
|
450 |
// if left hand cell has a constant term, put into rhs value |
441 |
// if left hand cell has a constant term, put into rhs value |
451 |
double fRightValue = -rLeftCoeff[0]; |
442 |
double fRightValue = -rLeftCoeff[0]; |
Lines 455-495
Link Here
|
455 |
const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr]; |
446 |
const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr]; |
456 |
// modify pValues with rhs coefficients |
447 |
// modify pValues with rhs coefficients |
457 |
for (nVar=0; nVar<nVariables; nVar++) |
448 |
for (nVar=0; nVar<nVariables; nVar++) |
458 |
pValues[nVar+1] -= rRightCoeff[nVar+1]; |
449 |
pValues[nVar] -= rRightCoeff[nVar+1]; |
459 |
|
450 |
|
460 |
fRightValue += rRightCoeff[0]; // constant term |
451 |
fRightValue += rRightCoeff[0]; // constant term |
461 |
} |
452 |
} |
462 |
else |
453 |
else |
463 |
fRightValue += fDirectValue; |
454 |
fRightValue += fDirectValue; |
464 |
|
455 |
|
465 |
int nConstrType = LE; |
|
|
466 |
switch ( eOp ) |
456 |
switch ( eOp ) |
467 |
{ |
457 |
{ |
468 |
case sheet::SolverConstraintOperator_LESS_EQUAL: nConstrType = LE; break; |
458 |
case sheet::SolverConstraintOperator_LESS_EQUAL: pRowType[nConstrPos] = 'L'; break; |
469 |
case sheet::SolverConstraintOperator_GREATER_EQUAL: nConstrType = GE; break; |
459 |
case sheet::SolverConstraintOperator_GREATER_EQUAL: pRowType[nConstrPos] = 'G'; break; |
470 |
case sheet::SolverConstraintOperator_EQUAL: nConstrType = EQ; break; |
460 |
case sheet::SolverConstraintOperator_EQUAL: pRowType[nConstrPos] = 'E'; break; |
471 |
default: |
461 |
default: |
472 |
OSL_ENSURE( false, "unexpected enum type" ); |
462 |
OSL_ENSURE( false, "unexpected enum type" ); |
473 |
} |
463 |
} |
474 |
add_constraint( lp, pValues, nConstrType, fRightValue ); |
464 |
pRHS[nConstrPos] = fRightValue; |
475 |
|
|
|
476 |
delete[] pValues; |
477 |
} |
465 |
} |
478 |
} |
466 |
} |
479 |
|
467 |
|
480 |
set_add_rowmode(lp, FALSE); |
468 |
// Find non-zero coefficients, column-wise |
|
|
469 |
|
470 |
int* pMatrixBegin = new int[nVariables+1]; |
471 |
int* pMatrixCount = new int[nVariables]; |
472 |
double* pMatrix = new double[nCompSize]; // not always completely used |
473 |
int* pMatrixIndex = new int[nCompSize]; |
474 |
int nMatrixPos = 0; |
475 |
for (nVar=0; nVar<nVariables; nVar++) |
476 |
{ |
477 |
int nBegin = nMatrixPos; |
478 |
for (size_t nRow=0; nRow<nRows; nRow++) |
479 |
{ |
480 |
double fCoeff = pCompMatrix[ nRow * nVariables + nVar ]; // row-wise |
481 |
if ( fCoeff != 0.0 ) |
482 |
{ |
483 |
pMatrix[nMatrixPos] = fCoeff; |
484 |
pMatrixIndex[nMatrixPos] = nRow; |
485 |
++nMatrixPos; |
486 |
} |
487 |
} |
488 |
pMatrixBegin[nVar] = nBegin; |
489 |
pMatrixCount[nVar] = nMatrixPos - nBegin; |
490 |
} |
491 |
pMatrixBegin[nVariables] = nMatrixPos; |
492 |
delete[] pCompMatrix; |
493 |
pCompMatrix = NULL; |
481 |
|
494 |
|
482 |
// apply settings to all variables |
495 |
// apply settings to all variables |
483 |
|
496 |
|
|
|
497 |
double* pLowerBounds = new double[nVariables]; |
498 |
double* pUpperBounds = new double[nVariables]; |
484 |
for (nVar=0; nVar<nVariables; nVar++) |
499 |
for (nVar=0; nVar<nVariables; nVar++) |
485 |
{ |
500 |
{ |
486 |
if ( !mbNonNegative ) |
501 |
pLowerBounds[nVar] = mbNonNegative ? 0.0 : -DBL_MAX; |
487 |
set_unbounded(lp, nVar+1); // allow negative (default is non-negative) |
502 |
pUpperBounds[nVar] = DBL_MAX; |
488 |
//! collect bounds from constraints? |
503 |
|
489 |
if ( mbInteger ) |
504 |
// bounds could possibly be further restricted from single-cell constraints |
490 |
set_int(lp, nVar+1, TRUE); |
|
|
491 |
} |
505 |
} |
492 |
|
506 |
|
|
|
507 |
char* pColType = new char[nVariables]; |
508 |
for (nVar=0; nVar<nVariables; nVar++) |
509 |
pColType[nVar] = mbInteger ? 'I' : 'C'; |
510 |
|
493 |
// apply single-var integer constraints |
511 |
// apply single-var integer constraints |
494 |
|
512 |
|
495 |
for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos) |
513 |
for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos) |
Lines 504-554
Link Here
|
504 |
if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) ) |
522 |
if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) ) |
505 |
{ |
523 |
{ |
506 |
if ( eOp == sheet::SolverConstraintOperator_INTEGER ) |
524 |
if ( eOp == sheet::SolverConstraintOperator_INTEGER ) |
507 |
set_int(lp, nVar+1, TRUE); |
525 |
pColType[nVar] = 'I'; |
508 |
else |
526 |
else |
509 |
set_binary(lp, nVar+1, TRUE); |
527 |
{ |
|
|
528 |
pColType[nVar] = 'B'; |
529 |
pLowerBounds[nVar] = 0.0; |
530 |
pUpperBounds[nVar] = 1.0; |
531 |
} |
510 |
} |
532 |
} |
511 |
} |
533 |
} |
512 |
} |
534 |
} |
513 |
|
535 |
|
514 |
if ( mbMaximize ) |
536 |
int nObjectSense = mbMaximize ? SOLV_OBJSENS_MAX : SOLV_OBJSENS_MIN; |
515 |
set_maxim(lp); |
|
|
516 |
else |
517 |
set_minim(lp); |
518 |
|
537 |
|
519 |
if ( !mbLimitBBDepth ) |
538 |
HPROB hProb = CoinCreateProblem(""); |
520 |
set_bb_depthlimit( lp, 0 ); |
539 |
int nResult = CoinLoadProblem( hProb, nVariables, nRows, nMatrixPos, 0, |
|
|
540 |
nObjectSense, nObjectConst, pObjectCoeffs, |
541 |
pLowerBounds, pUpperBounds, pRowType, pRHS, NULL, |
542 |
pMatrixBegin, pMatrixCount, pMatrixIndex, pMatrix, |
543 |
NULL, NULL, NULL ); |
544 |
nResult = CoinLoadInteger( hProb, pColType ); |
521 |
|
545 |
|
522 |
set_epslevel( lp, mnEpsilonLevel ); |
546 |
delete[] pColType; |
523 |
set_timeout( lp, mnTimeout ); |
547 |
delete[] pMatrixIndex; |
|
|
548 |
delete[] pMatrix; |
549 |
delete[] pMatrixCount; |
550 |
delete[] pMatrixBegin; |
551 |
delete[] pUpperBounds; |
552 |
delete[] pLowerBounds; |
553 |
delete[] pRowType; |
554 |
delete[] pRHS; |
555 |
delete[] pObjectCoeffs; |
556 |
|
557 |
CoinSetRealOption( hProb, COIN_REAL_MAXSECONDS, mnTimeout ); |
558 |
CoinSetRealOption( hProb, COIN_REAL_MIPMAXSEC, mnTimeout ); |
559 |
|
560 |
// TODO: handle (or remove) settings: epsilon, B&B depth |
524 |
|
561 |
|
525 |
// solve model |
562 |
// solve model |
526 |
|
563 |
|
527 |
int nResult = ::solve( lp ); |
564 |
nResult = CoinCheckProblem( hProb ); |
|
|
565 |
nResult = CoinOptimizeProblem( hProb, 0 ); |
528 |
|
566 |
|
529 |
mbSuccess = ( nResult == OPTIMAL ); |
567 |
mbSuccess = ( nResult == SOLV_CALL_SUCCESS ); |
530 |
if ( mbSuccess ) |
568 |
if ( mbSuccess ) |
531 |
{ |
569 |
{ |
532 |
// get solution |
570 |
// get solution |
533 |
|
571 |
|
534 |
maSolution.realloc( nVariables ); |
572 |
maSolution.realloc( nVariables ); |
|
|
573 |
CoinGetSolutionValues( hProb, maSolution.getArray(), NULL, NULL, NULL ); |
574 |
mfResultValue = CoinGetObjectValue( hProb ); |
575 |
} |
576 |
else |
577 |
{ |
578 |
int nSolutionStatus = CoinGetSolutionStatus( hProb ); |
579 |
if ( nSolutionStatus == 1 ) |
580 |
maStatus = lcl_GetResourceString( RID_ERROR_INFEASIBLE ); |
581 |
else if ( nSolutionStatus == 2 ) |
582 |
maStatus = lcl_GetResourceString( RID_ERROR_UNBOUNDED ); |
583 |
// TODO: detect timeout condition and report as RID_ERROR_TIMEOUT |
584 |
// (currently reported as infeasible) |
585 |
} |
535 |
|
586 |
|
536 |
REAL* pResultVar = NULL; |
587 |
CoinUnloadProblem( hProb ); |
537 |
get_ptr_variables( lp, &pResultVar ); |
|
|
538 |
for (nVar=0; nVar<nVariables; nVar++) |
539 |
maSolution[nVar] = pResultVar[nVar]; |
540 |
|
541 |
mfResultValue = get_objective( lp ); |
542 |
} |
543 |
else if ( nResult == INFEASIBLE ) |
544 |
maStatus = lcl_GetResourceString( RID_ERROR_INFEASIBLE ); |
545 |
else if ( nResult == UNBOUNDED ) |
546 |
maStatus = lcl_GetResourceString( RID_ERROR_UNBOUNDED ); |
547 |
else if ( nResult == TIMEOUT || nResult == SUBOPTIMAL ) |
548 |
maStatus = lcl_GetResourceString( RID_ERROR_TIMEOUT ); |
549 |
// SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error |
550 |
|
551 |
delete_lp( lp ); |
552 |
} |
588 |
} |
553 |
|
589 |
|
554 |
// ------------------------------------------------------------------------- |
590 |
// ------------------------------------------------------------------------- |