tdf#157519 Implement Sensitivity Report in LpSolve solver
This patch implements sensitivity analysis when using the LpSolve solver engine. It also adds the infrastructure needed for future implementations in other solver engines via the css::sheet::SensitivityReport struct. Change-Id: I74c2ed9c6201a0b2ffc29ef612d2b778d11a3bef Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173642 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
parent
860ec21856
commit
2f1dcf01d7
10 changed files with 393 additions and 18 deletions
|
@ -3467,6 +3467,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/sheet,\
|
|||
RangeSelectionEvent \
|
||||
ReferenceFlags \
|
||||
ResultEvent \
|
||||
SensitivityReport \
|
||||
SheetLinkMode \
|
||||
SingleReference \
|
||||
SolverConstraint \
|
||||
|
|
55
offapi/com/sun/star/sheet/SensitivityReport.idl
Normal file
55
offapi/com/sun/star/sheet/SensitivityReport.idl
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* This file is part of the LibreOffice project.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
module com { module sun { module star { module sheet {
|
||||
|
||||
|
||||
/** Stores all the information related to the sensitivity report of a linear programming model
|
||||
*
|
||||
* @since LibreOffice 25.2
|
||||
*/
|
||||
struct SensitivityReport
|
||||
{
|
||||
// Indicates whether a sensitivity report was successfully generated
|
||||
boolean HasReport;
|
||||
|
||||
// Coefficients of the objective function
|
||||
sequence<double> ObjCoefficients;
|
||||
|
||||
// Reduced costs of the variables in the objective function
|
||||
sequence<double> ObjReducedCosts;
|
||||
|
||||
// Allowable decrease in the coefficients of the objective function
|
||||
sequence<double> ObjAllowableDecreases;
|
||||
|
||||
// Allowable increase in the coefficients of the objective function
|
||||
sequence<double> ObjAllowableIncreases;
|
||||
|
||||
// Value of the constraint at the solution
|
||||
sequence<double> ConstrValues;
|
||||
|
||||
// Right-hand side of the constraints
|
||||
sequence<double> ConstrRHS;
|
||||
|
||||
// Shadow prices of constraints
|
||||
sequence<double> ConstrShadowPrices;
|
||||
|
||||
// Allowable decrease in the constraint resources
|
||||
sequence<double> ConstrAllowableDecreases;
|
||||
|
||||
// Allowable increase in the constraint resources
|
||||
sequence<double> ConstrAllowableIncreases;
|
||||
};
|
||||
|
||||
|
||||
}; }; }; };
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
|
@ -575,6 +575,20 @@
|
|||
#define STR_UNDO_THEME_CHANGE NC_("STR_UNDO_THEME_CHANGE", "Theme Change")
|
||||
#define STR_UNDO_THEME_COLOR_CHANGE NC_("STR_UNDO_THEME_COLOR_CHANGE", "Theme Color Change")
|
||||
#define STR_ERR_INSERT_CELLS NC_("STR_ERR_INSERT_CELLS", "Failed to insert cells")
|
||||
#define STR_SOLVER_ENGINE NC_("STR_SOLVER_ENGINE", "Solver Engine:")
|
||||
#define STR_SENSITIVITY NC_("STR_SENSITIVITY", "Sensitivity")
|
||||
#define STR_SENSITIVITY_TITLE NC_("STR_SENSITIVITY_TITLE", "Sensitivity Report")
|
||||
#define STR_SENSITIVITY_OBJCELL NC_("STR_SENSITIVITY_OBJCELL", "Objective Cell")
|
||||
#define STR_SENSITIVITY_VARCELLS NC_("STR_SENSITIVITY_VARCELLS", "Variable Cells")
|
||||
#define STR_SENSITIVITY_CONSTRAINTS NC_("STR_SENSITIVITY_CONSTRAINTS", "Constraints")
|
||||
#define STR_SENSITIVITY_CELL NC_("STR_SENSITIVITY_CELL", "Cell")
|
||||
#define STR_SENSITIVITY_FINALVALUE NC_("STR_SENSITIVITY_CELL", "Final Value")
|
||||
#define STR_SENSITIVITY_REDUCED NC_("STR_SENSITIVITY_CELL", "Reduced Cost")
|
||||
#define STR_SENSITIVITY_OBJCOEFF NC_("STR_SENSITIVITY_CELL", "Objective Coefficient")
|
||||
#define STR_SENSITIVITY_DECREASE NC_("STR_SENSITIVITY_CELL", "Allowable Decrease")
|
||||
#define STR_SENSITIVITY_INCREASE NC_("STR_SENSITIVITY_CELL", "Allowable Increase")
|
||||
#define STR_SENSITIVITY_SHADOWPRICE NC_("STR_SENSITIVITY_SHADOWPRICE", "Shadow Price")
|
||||
#define STR_SENSITIVITY_RHS NC_("STR_SENSITIVITY_RHS", "Constraint R.H. Side")
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -148,14 +148,16 @@ void ScSolverSettingsObj::testXSolverSettings()
|
|||
uno::Sequence<beans::PropertyValue> aEngProps = xSolverModel->getEngineOptions();
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("EpsilonLevel"), aEngProps[0].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(0)), aEngProps[0].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps[1].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(u"GenSensitivityReport"_ustr, aEngProps[1].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps[1].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps[2].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[2].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps[3].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps[2].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps[2].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps[3].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[3].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps[4].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), aEngProps[4].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps[4].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[4].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps[5].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), aEngProps[5].Value);
|
||||
|
||||
// Save file and reload to check if solver settings are still there
|
||||
saveAndReload(u"calc8"_ustr);
|
||||
|
@ -191,14 +193,16 @@ void ScSolverSettingsObj::testXSolverSettings()
|
|||
uno::Sequence<beans::PropertyValue> aEngProps2 = xSolverModel2->getEngineOptions();
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("EpsilonLevel"), aEngProps2[0].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(0)), aEngProps2[0].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps2[1].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(u"GenSensitivityReport"_ustr, aEngProps2[1].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps2[1].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps2[2].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[2].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps2[3].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps2[2].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps2[2].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps2[3].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[3].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps2[4].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), aEngProps2[4].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps2[4].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[4].Value);
|
||||
CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps2[5].Name);
|
||||
CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), aEngProps2[5].Value);
|
||||
}
|
||||
|
||||
void ScSolverSettingsObj::setUp()
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "docsh.hxx"
|
||||
#include <SolverSettings.hxx>
|
||||
#include <com/sun/star/uno/Sequence.hxx>
|
||||
#include <com/sun/star/table/CellAddress.hpp>
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
@ -157,6 +158,9 @@ private:
|
|||
|
||||
static sc::ConstraintOperator OperatorIndexToConstraintOperator(sal_Int32 nIndex);
|
||||
|
||||
// Return the string representation of a css::table::CellAddress
|
||||
OUString GetCellStrAddress(css::table::CellAddress aUnoAddress);
|
||||
|
||||
DECL_LINK( BtnHdl, weld::Button&, void );
|
||||
DECL_LINK( DelBtnHdl, weld::Button&, void );
|
||||
DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
|
||||
|
|
|
@ -38,11 +38,14 @@
|
|||
#include <comphelper/sequence.hxx>
|
||||
#include <optsolver.hxx>
|
||||
#include <table.hxx>
|
||||
#include <TableFillingAndNavigationTools.hxx>
|
||||
|
||||
#include <com/sun/star/beans/XPropertySetInfo.hpp>
|
||||
#include <com/sun/star/sheet/SolverConstraint.hpp>
|
||||
#include <com/sun/star/sheet/SolverConstraintOperator.hpp>
|
||||
#include <com/sun/star/sheet/XSolverDescription.hpp>
|
||||
#include <com/sun/star/sheet/XSolver.hpp>
|
||||
#include <com/sun/star/sheet/SensitivityReport.hpp>
|
||||
|
||||
using namespace com::sun::star;
|
||||
|
||||
|
@ -916,6 +919,14 @@ bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout )
|
|||
return bFound;
|
||||
}
|
||||
|
||||
OUString ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress)
|
||||
{
|
||||
ScAddress aScAddr;
|
||||
ScUnoConversion::FillScAddress(aScAddr, aUnoAddress);
|
||||
ScRange aRange(aScAddr);
|
||||
return aRange.Format(mrDoc, ScRefFlags::RANGE_ABS);
|
||||
}
|
||||
|
||||
bool ScOptSolverDlg::CallSolver() // return true -> close dialog after calling
|
||||
{
|
||||
// show progress dialog
|
||||
|
@ -1189,6 +1200,147 @@ bool ScOptSolverDlg::CallSolver() // return true -> close dialog after cal
|
|||
mpDocShell->UnlockPaint();
|
||||
}
|
||||
|
||||
// Generate sensitivity report if user wants it
|
||||
uno::Reference<css::beans::XPropertySetInfo> xInfo = xOptProp->getPropertySetInfo();
|
||||
bool bUserWantsReport = false;
|
||||
if (xInfo->hasPropertyByName("GenSensitivityReport"))
|
||||
xOptProp->getPropertyValue("GenSensitivityReport") >>= bUserWantsReport;
|
||||
|
||||
if (bSuccess && bUserWantsReport)
|
||||
{
|
||||
// Retrieve the sensitivity analysis report
|
||||
css::sheet::SensitivityReport aSensitivity;
|
||||
bool bHasReportObj = xOptProp->getPropertyValue("SensitivityReport") >>= aSensitivity;
|
||||
|
||||
if (bHasReportObj && aSensitivity.HasReport)
|
||||
{
|
||||
// Define the Tab name where the sensitivity analysis will be written to
|
||||
OUString sNewTabName;
|
||||
SCTAB nNewTab;
|
||||
mrDoc.GetName(mnCurTab, sNewTabName);
|
||||
sNewTabName += "_" + ScResId(STR_SENSITIVITY);
|
||||
// Chech if the new Tab name exists
|
||||
if (mrDoc.GetTable(sNewTabName, nNewTab))
|
||||
{
|
||||
// Add numbers to the end of the Tab name to make it unique
|
||||
SCTAB i = 1;
|
||||
OUString aName;
|
||||
do
|
||||
{
|
||||
i++;
|
||||
aName = sNewTabName + "_" + OUString::number(static_cast<sal_Int32>(i));
|
||||
}
|
||||
while(mrDoc.GetTable(aName, nNewTab));
|
||||
sNewTabName = aName;
|
||||
}
|
||||
|
||||
// Insert new sheet to the document and start writing the report
|
||||
ScDocFunc &rFunc = mpDocShell->GetDocFunc();
|
||||
rFunc.InsertTable(mnCurTab + 1, sNewTabName, false, false);
|
||||
SCTAB nReportTab;
|
||||
mrDoc.GetTable(sNewTabName, nReportTab);
|
||||
|
||||
// Used to input data in the new sheet
|
||||
ScAddress aOutputAddress(0, 0, nReportTab);
|
||||
ScAddress::Details mAddressDetails(mrDoc, aOutputAddress);
|
||||
AddressWalkerWriter aOutput(aOutputAddress, mpDocShell, mrDoc,
|
||||
formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, mAddressDetails.eConv));
|
||||
aOutput.writeBoldString(ScResId(STR_SENSITIVITY_TITLE));
|
||||
aOutput.newLine();
|
||||
aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine);
|
||||
aOutput.newLine();
|
||||
aOutput.newLine();
|
||||
|
||||
// Objective cell section
|
||||
aOutput.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL));
|
||||
aOutput.newLine();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
|
||||
aOutput.newLine();
|
||||
aOutput.writeString(GetCellStrAddress(xSolver->getObjective()));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(xSolver->getResultValue());
|
||||
aOutput.newLine();
|
||||
aOutput.newLine();
|
||||
|
||||
// Variable cell section
|
||||
aOutput.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS));
|
||||
aOutput.newLine();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_REDUCED));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_OBJCOEFF));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
|
||||
aOutput.newLine();
|
||||
|
||||
uno::Sequence<double> aSolution = xSolver->getSolution();
|
||||
uno::Sequence<double> aObjCoefficients = aSensitivity.ObjCoefficients;
|
||||
uno::Sequence<double> aObjReducedCosts = aSensitivity.ObjReducedCosts;
|
||||
uno::Sequence<double> aObjAllowableDecreases = aSensitivity.ObjAllowableDecreases;
|
||||
uno::Sequence<double> aObjAllowableIncreases = aSensitivity.ObjAllowableIncreases;
|
||||
for (sal_Int32 i = 0; i < aVariables.getLength(); i++)
|
||||
{
|
||||
aOutput.writeString(GetCellStrAddress(aVariables[i]));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aSolution[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aObjReducedCosts[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aObjCoefficients[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aObjAllowableDecreases[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aObjAllowableIncreases[i]);
|
||||
aOutput.newLine();
|
||||
}
|
||||
aOutput.newLine();
|
||||
|
||||
// Constraints section
|
||||
aOutput.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS));
|
||||
aOutput.newLine();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_SHADOWPRICE));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_RHS));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
|
||||
aOutput.newLine();
|
||||
|
||||
uno::Sequence<double> aConstrValues = aSensitivity.ConstrValues;
|
||||
uno::Sequence<double> aConstrRHS = aSensitivity.ConstrRHS;
|
||||
uno::Sequence<double> aConstrShadowPrices = aSensitivity.ConstrShadowPrices;
|
||||
uno::Sequence<double> aConstrAllowableDecreases = aSensitivity.ConstrAllowableDecreases;
|
||||
uno::Sequence<double> aConstrAllowableIncreases = aSensitivity.ConstrAllowableIncreases;
|
||||
for (sal_Int32 i = 0; i < aConstraints.getLength(); i++)
|
||||
{
|
||||
aOutput.writeString(GetCellStrAddress(aConstraints[i].Left));
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aConstrValues[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aConstrShadowPrices[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aConstrRHS[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aConstrAllowableDecreases[i]);
|
||||
aOutput.nextColumn();
|
||||
aOutput.writeValue(aConstrAllowableIncreases[i]);
|
||||
aOutput.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bClose;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define RID_PROPERTY_EPSILONLEVEL NC_("RID_PROPERTY_EPSILONLEVEL", "Epsilon level (0-3)")
|
||||
#define RID_PROPERTY_LIMITBBDEPTH NC_("RID_PROPERTY_LIMITBBDEPTH", "Limit branch-and-bound depth")
|
||||
#define RID_PROPERTY_ALGORITHM NC_("RID_PROPERTY_ALGORITHM", "Swarm algorithm (0 - Differential Evolution, 1 - Particle Swarm Optimization)")
|
||||
#define RID_PROPERTY_SENSITIVITY NC_("RID_PROPERTY_SENSITIVITY", "Generate sensitivity report")
|
||||
#define RID_ERROR_NONLINEAR NC_("RID_ERROR_NONLINEAR", "The model is not linear.")
|
||||
#define RID_ERROR_EPSILONLEVEL NC_("RID_ERROR_EPSILONLEVEL", "The epsilon level is invalid.")
|
||||
#define RID_ERROR_INFEASIBLE NC_("RID_ERROR_INFEASIBLE", "The model is infeasible. Check limiting conditions.")
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
************************************************************************/
|
||||
|
||||
#include <sal/config.h>
|
||||
#include <sal/log.hxx>
|
||||
|
||||
#undef LANGUAGE_NONE
|
||||
#if defined _WIN32
|
||||
|
@ -107,6 +108,10 @@ void SAL_CALL LpsolveSolver::solve()
|
|||
size_t nVariables = aVariableCells.size();
|
||||
size_t nVar = 0;
|
||||
|
||||
// Store all RHS values
|
||||
sal_uInt32 nConstraints = maConstraints.size();
|
||||
m_aConstrRHS.realloc(nConstraints);
|
||||
|
||||
// collect all dependent cells
|
||||
|
||||
ScSolverCellHashMap aCellsHash;
|
||||
|
@ -197,6 +202,9 @@ void SAL_CALL LpsolveSolver::solve()
|
|||
|
||||
set_add_rowmode(lp, TRUE);
|
||||
|
||||
sal_uInt32 nConstrCount(0);
|
||||
double* pConstrRHS = m_aConstrRHS.getArray();
|
||||
|
||||
for (const auto& rConstr : maConstraints)
|
||||
{
|
||||
// integer constraints are set later
|
||||
|
@ -237,6 +245,9 @@ void SAL_CALL LpsolveSolver::solve()
|
|||
else
|
||||
fRightValue += fDirectValue;
|
||||
|
||||
// Remember the RHS value used for sensitivity analysis later
|
||||
pConstrRHS[nConstrCount] = fRightValue;
|
||||
|
||||
int nConstrType = LE;
|
||||
switch ( eOp )
|
||||
{
|
||||
|
@ -247,6 +258,7 @@ void SAL_CALL LpsolveSolver::solve()
|
|||
OSL_FAIL( "unexpected enum type" );
|
||||
}
|
||||
add_constraint( lp, pValues.get(), nConstrType, fRightValue );
|
||||
nConstrCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,6 +323,112 @@ void SAL_CALL LpsolveSolver::solve()
|
|||
std::copy_n(pResultVar, nVariables, maSolution.getArray());
|
||||
|
||||
mfResultValue = get_objective( lp );
|
||||
|
||||
// Initially set to false because getting the report might fail
|
||||
m_aSensitivityReport.HasReport = false;
|
||||
|
||||
// Get sensitivity report if the user set SensitivityReport parameter to true
|
||||
if (mbGenSensitivity)
|
||||
{
|
||||
// Get sensitivity data about the objective function
|
||||
// LpSolve returns an interval for the coefficients of the objective function
|
||||
// instead of returning an allowable increase/decrease (which is what we want to show
|
||||
// in the sensitivity report; so we these from/till values are converted into increase
|
||||
// and decrease values later)
|
||||
REAL* pObjFrom = nullptr;
|
||||
REAL* pObjTill = nullptr;
|
||||
bool bHasObjReport = false;
|
||||
bHasObjReport = get_ptr_sensitivity_obj(lp, &pObjFrom, &pObjTill);
|
||||
|
||||
// Get sensitivity data about constraints
|
||||
// Similarly to the objective function, the sensitivity values returned for the
|
||||
// constraints are in the form from/till and are later converted to increase and
|
||||
// decrease values later
|
||||
REAL* pConstrValue = nullptr;
|
||||
REAL* pConstrDual = nullptr;
|
||||
REAL* pConstrFrom = nullptr;
|
||||
REAL* pConstrTill = nullptr;
|
||||
bool bHasConstrReport = false;
|
||||
bHasConstrReport = get_ptr_sensitivity_rhs(lp, &pConstrDual, &pConstrFrom, &pConstrTill);
|
||||
|
||||
// When successfull, store sensitivity data in the solver component
|
||||
if (bHasObjReport && bHasConstrReport)
|
||||
{
|
||||
m_aSensitivityReport.HasReport = true;
|
||||
m_aObjDecrease.realloc(nVariables);
|
||||
m_aObjIncrease.realloc(nVariables);
|
||||
double* pObjDecrease = m_aObjDecrease.getArray();
|
||||
double* pObjIncrease = m_aObjIncrease.getArray();
|
||||
for (size_t i = 0; i < nVariables; i++)
|
||||
{
|
||||
// Allowed decrease. Note that the indices of rObjCoeff are offset by 1
|
||||
// because of the objective function
|
||||
if (static_cast<bool>(is_infinite(lp, pObjFrom[i])))
|
||||
pObjDecrease[i] = get_infinite(lp);
|
||||
else
|
||||
pObjDecrease[i] = rObjCoeff[i + 1] - pObjFrom[i];
|
||||
|
||||
// Allowed increase
|
||||
if (static_cast<bool>(is_infinite(lp, pObjTill[i])))
|
||||
pObjIncrease[i] = get_infinite(lp);
|
||||
else
|
||||
pObjIncrease[i] = pObjTill[i] - rObjCoeff[i + 1];
|
||||
}
|
||||
|
||||
// Save objective coefficients for the sensitivity report
|
||||
double* pObjCoefficients(new double[nVariables]);
|
||||
for (size_t i = 0; i < nVariables; i++)
|
||||
pObjCoefficients[i] = rObjCoeff[i + 1];
|
||||
m_aObjCoefficients.realloc(nVariables);
|
||||
std::copy_n(pObjCoefficients, nVariables, m_aObjCoefficients.getArray());
|
||||
|
||||
// The reduced costs are in pConstrDual after the constraints
|
||||
double* pObjRedCost(new double[nVariables]);
|
||||
for (size_t i = 0; i < nVariables; i++)
|
||||
pObjRedCost[i] = pConstrDual[nConstraints + i];
|
||||
m_aObjRedCost.realloc(nVariables);
|
||||
std::copy_n(pObjRedCost, nVariables, m_aObjRedCost.getArray());
|
||||
|
||||
// Final value of constraints
|
||||
get_ptr_constraints(lp, &pConstrValue);
|
||||
m_aConstrValue.realloc(nConstraints);
|
||||
std::copy_n(pConstrValue, nConstraints, m_aConstrValue.getArray());
|
||||
|
||||
// The RHS contains information for each constraint
|
||||
m_aConstrDual.realloc(nConstraints);
|
||||
m_aConstrDecrease.realloc(nConstraints);
|
||||
m_aConstrIncrease.realloc(nConstraints);
|
||||
std::copy_n(pConstrDual, nConstraints, m_aConstrDual.getArray());
|
||||
double* pConstrDecrease = m_aConstrDecrease.getArray();
|
||||
double* pConstrIncrease = m_aConstrIncrease.getArray();
|
||||
|
||||
for (sal_uInt32 i = 0; i < nConstraints; i++)
|
||||
{
|
||||
// Allowed decrease
|
||||
pConstrDecrease[i] = m_aConstrRHS[i] - pConstrFrom[i];
|
||||
if (static_cast<bool>(is_infinite(lp, pConstrFrom[i]))
|
||||
&& maConstraints[i].Operator == sheet::SolverConstraintOperator_LESS_EQUAL)
|
||||
pConstrDecrease[i] = m_aConstrRHS[i] - m_aConstrValue[i];
|
||||
|
||||
// Allowed increase
|
||||
pConstrIncrease[i] = pConstrTill[i] - m_aConstrRHS[i];
|
||||
if (static_cast<bool>(is_infinite(lp, pConstrTill[i]))
|
||||
&& maConstraints[i].Operator == sheet::SolverConstraintOperator_GREATER_EQUAL)
|
||||
pConstrIncrease[i] = m_aConstrValue[i] - m_aConstrRHS[i];
|
||||
}
|
||||
|
||||
// Set all values of the SensitivityReport object
|
||||
m_aSensitivityReport.ObjCoefficients = m_aObjCoefficients;
|
||||
m_aSensitivityReport.ObjReducedCosts = m_aObjRedCost;
|
||||
m_aSensitivityReport.ObjAllowableDecreases = m_aObjDecrease;
|
||||
m_aSensitivityReport.ObjAllowableIncreases = m_aObjIncrease;
|
||||
m_aSensitivityReport.ConstrValues = m_aConstrValue;
|
||||
m_aSensitivityReport.ConstrRHS = m_aConstrRHS;
|
||||
m_aSensitivityReport.ConstrShadowPrices = m_aConstrDual;
|
||||
m_aSensitivityReport.ConstrAllowableDecreases = m_aConstrDecrease;
|
||||
m_aSensitivityReport.ConstrAllowableIncreases = m_aConstrIncrease;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( nResult == INFEASIBLE )
|
||||
maStatus = SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE );
|
||||
|
|
|
@ -37,6 +37,8 @@ constexpr OUStringLiteral STR_INTEGER = u"Integer";
|
|||
constexpr OUStringLiteral STR_TIMEOUT = u"Timeout";
|
||||
constexpr OUStringLiteral STR_EPSILONLEVEL = u"EpsilonLevel";
|
||||
constexpr OUStringLiteral STR_LIMITBBDEPTH = u"LimitBBDepth";
|
||||
constexpr OUStringLiteral STR_GEN_SENSITIVITY = u"GenSensitivityReport";
|
||||
constexpr OUStringLiteral STR_SENSITIVITY_REPORT = u"SensitivityReport";
|
||||
|
||||
|
||||
// Resources from tools are used for translated strings
|
||||
|
@ -64,7 +66,9 @@ namespace
|
|||
PROP_INTEGER,
|
||||
PROP_TIMEOUT,
|
||||
PROP_EPSILONLEVEL,
|
||||
PROP_LIMITBBDEPTH
|
||||
PROP_LIMITBBDEPTH,
|
||||
PROP_GEN_SENSITIVITY,
|
||||
PROP_SENSITIVITY_REPORT
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,15 +99,20 @@ SolverComponent::SolverComponent() :
|
|||
mnTimeout( 100 ),
|
||||
mnEpsilonLevel( 0 ),
|
||||
mbLimitBBDepth( true ),
|
||||
mbGenSensitivity(false),
|
||||
mbSuccess( false ),
|
||||
mfResultValue( 0.0 )
|
||||
{
|
||||
// for XPropertySet implementation:
|
||||
registerProperty( STR_NONNEGATIVE, PROP_NONNEGATIVE, 0, &mbNonNegative, cppu::UnoType<decltype(mbNonNegative)>::get() );
|
||||
registerProperty( STR_INTEGER, PROP_INTEGER, 0, &mbInteger, cppu::UnoType<decltype(mbInteger)>::get() );
|
||||
registerProperty( STR_TIMEOUT, PROP_TIMEOUT, 0, &mnTimeout, cppu::UnoType<decltype(mnTimeout)>::get() );
|
||||
registerProperty( STR_EPSILONLEVEL, PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, cppu::UnoType<decltype(mnEpsilonLevel)>::get() );
|
||||
registerProperty( STR_LIMITBBDEPTH, PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, cppu::UnoType<decltype(mbLimitBBDepth)>::get() );
|
||||
registerProperty(STR_NONNEGATIVE, PROP_NONNEGATIVE, 0, &mbNonNegative, cppu::UnoType<decltype(mbNonNegative)>::get());
|
||||
registerProperty(STR_INTEGER, PROP_INTEGER, 0, &mbInteger, cppu::UnoType<decltype(mbInteger)>::get());
|
||||
registerProperty(STR_TIMEOUT, PROP_TIMEOUT, 0, &mnTimeout, cppu::UnoType<decltype(mnTimeout)>::get());
|
||||
registerProperty(STR_EPSILONLEVEL, PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, cppu::UnoType<decltype(mnEpsilonLevel)>::get());
|
||||
registerProperty(STR_LIMITBBDEPTH, PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, cppu::UnoType<decltype(mbLimitBBDepth)>::get());
|
||||
registerProperty(STR_GEN_SENSITIVITY, PROP_GEN_SENSITIVITY, 0, &mbGenSensitivity, cppu::UnoType<decltype(mbGenSensitivity)>::get());
|
||||
|
||||
// Sensitivity report
|
||||
registerProperty(STR_SENSITIVITY_REPORT, PROP_SENSITIVITY_REPORT, 0, &m_aSensitivityReport, cppu::UnoType<decltype(m_aSensitivityReport)>::get());
|
||||
}
|
||||
|
||||
SolverComponent::~SolverComponent()
|
||||
|
@ -158,6 +167,9 @@ OUString SAL_CALL SolverComponent::getPropertyDescription( const OUString& rProp
|
|||
case PROP_LIMITBBDEPTH:
|
||||
pResId = RID_PROPERTY_LIMITBBDEPTH;
|
||||
break;
|
||||
case PROP_GEN_SENSITIVITY:
|
||||
pResId = RID_PROPERTY_SENSITIVITY;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
// unknown - leave empty
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <com/sun/star/sheet/XSolver.hpp>
|
||||
#include <com/sun/star/sheet/XSolverDescription.hpp>
|
||||
#include <com/sun/star/sheet/SensitivityReport.hpp>
|
||||
#include <com/sun/star/table/CellAddress.hpp>
|
||||
#include <com/sun/star/lang/XServiceInfo.hpp>
|
||||
#include <cppuhelper/implbase.hxx>
|
||||
|
@ -76,12 +77,25 @@ protected:
|
|||
sal_Int32 mnTimeout;
|
||||
sal_Int32 mnEpsilonLevel;
|
||||
bool mbLimitBBDepth;
|
||||
bool mbGenSensitivity;
|
||||
// results
|
||||
bool mbSuccess;
|
||||
double mfResultValue;
|
||||
css::uno::Sequence< double > maSolution;
|
||||
OUString maStatus;
|
||||
|
||||
// Sensitivity report
|
||||
css::uno::Sequence<double> m_aObjCoefficients;
|
||||
css::uno::Sequence<double> m_aObjDecrease;
|
||||
css::uno::Sequence<double> m_aObjIncrease;
|
||||
css::uno::Sequence<double> m_aObjRedCost;
|
||||
css::uno::Sequence<double> m_aConstrValue;
|
||||
css::uno::Sequence<double> m_aConstrRHS;
|
||||
css::uno::Sequence<double> m_aConstrDual;
|
||||
css::uno::Sequence<double> m_aConstrIncrease;
|
||||
css::uno::Sequence<double> m_aConstrDecrease;
|
||||
css::sheet::SensitivityReport m_aSensitivityReport;
|
||||
|
||||
static OUString GetResourceString(TranslateId aId);
|
||||
static css::uno::Reference<css::table::XCell> GetCell(
|
||||
const css::uno::Reference<css::sheet::XSpreadsheetDocument>& xDoc,
|
||||
|
|
Loading…
Reference in a new issue