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:
Rafael Lima 2024-09-20 20:47:12 +02:00 committed by Mike Kaganski
parent 860ec21856
commit 2f1dcf01d7
10 changed files with 393 additions and 18 deletions

View file

@ -3467,6 +3467,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/sheet,\
RangeSelectionEvent \
ReferenceFlags \
ResultEvent \
SensitivityReport \
SheetLinkMode \
SingleReference \
SolverConstraint \

View 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: */

View file

@ -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

View file

@ -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()

View file

@ -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 );

View file

@ -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;
}

View file

@ -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.")

View file

@ -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 );

View file

@ -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

View file

@ -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,