808 lines
28 KiB
C++
808 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
/*
|
|
* 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/.
|
|
*
|
|
*/
|
|
|
|
#include <global.hxx>
|
|
#include <compiler.hxx>
|
|
#include <table.hxx>
|
|
#include <docsh.hxx>
|
|
#include <rtl/math.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
#include <solverutil.hxx>
|
|
#include <unotools/charclass.hxx>
|
|
#include <SolverSettings.hxx>
|
|
|
|
namespace sc
|
|
{
|
|
SolverSettings::SolverSettings(ScTable& rTable)
|
|
: m_rTable(rTable)
|
|
, m_rDoc(m_rTable.GetDoc())
|
|
, m_pDocShell(m_rDoc.GetDocumentShell())
|
|
{
|
|
// Get the named range manager for this tab
|
|
std::map<OUString, ScRangeName*> rRangeMap;
|
|
m_rDoc.GetRangeNameMap(rRangeMap);
|
|
m_pRangeName = rRangeMap.find(m_rTable.GetName())->second;
|
|
|
|
Initialize();
|
|
}
|
|
|
|
void SolverSettings::Initialize()
|
|
{
|
|
// Assign default values for the solver parameters
|
|
ResetToDefaults();
|
|
|
|
// Read the parameter values in the sheet
|
|
ReadParamValue(SP_OBJ_CELL, m_sObjCell);
|
|
ReadParamValue(SP_OBJ_VAL, m_sObjVal);
|
|
ReadParamValue(SP_VAR_CELLS, m_sVariableCells);
|
|
|
|
// Read the objective type
|
|
OUString sObjType;
|
|
if (ReadParamValue(SP_OBJ_TYPE, sObjType))
|
|
{
|
|
switch (sObjType.toInt32())
|
|
{
|
|
case 1:
|
|
m_eObjType = ObjectiveType::OT_MAXIMIZE;
|
|
break;
|
|
case 2:
|
|
m_eObjType = ObjectiveType::OT_MINIMIZE;
|
|
break;
|
|
case 3:
|
|
m_eObjType = ObjectiveType::OT_VALUE;
|
|
break;
|
|
default:
|
|
m_eObjType = ObjectiveType::OT_MAXIMIZE;
|
|
}
|
|
}
|
|
|
|
// Read all constraints in the tab
|
|
ReadConstraints();
|
|
|
|
// Read the solver engine being used
|
|
ReadEngine();
|
|
|
|
// Read engine options
|
|
ReadParamValue(SP_INTEGER, m_sInteger);
|
|
ReadParamValue(SP_NON_NEGATIVE, m_sNonNegative);
|
|
ReadParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
|
|
ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
|
|
ReadParamValue(SP_TIMEOUT, m_sTimeout);
|
|
ReadParamValue(SP_ALGORITHM, m_sAlgorithm);
|
|
// Engine options common for DEPS and SCO
|
|
ReadParamValue(SP_SWARM_SIZE, m_sSwarmSize);
|
|
ReadParamValue(SP_LEARNING_CYCLES, m_sLearningCycles);
|
|
ReadParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange);
|
|
ReadDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold);
|
|
ReadParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator);
|
|
ReadParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint);
|
|
ReadParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG);
|
|
ReadParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit);
|
|
ReadDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance);
|
|
ReadParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus);
|
|
// DEPS Options
|
|
ReadDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate);
|
|
ReadDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin);
|
|
ReadDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax);
|
|
ReadDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability);
|
|
ReadDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant);
|
|
ReadDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant);
|
|
ReadDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff);
|
|
ReadDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability);
|
|
// SCO Options
|
|
ReadParamValue(SP_LIBRARY_SIZE, m_sLibrarySize);
|
|
}
|
|
|
|
// Returns the current value of the parameter in the object as a string
|
|
OUString SolverSettings::GetParameter(SolverParameter eParam)
|
|
{
|
|
switch (eParam)
|
|
{
|
|
case SP_OBJ_CELL:
|
|
return m_sObjCell;
|
|
break;
|
|
case SP_OBJ_TYPE:
|
|
return OUString::number(m_eObjType);
|
|
break;
|
|
case SP_OBJ_VAL:
|
|
return m_sObjVal;
|
|
break;
|
|
case SP_VAR_CELLS:
|
|
return m_sVariableCells;
|
|
break;
|
|
case SP_CONSTR_COUNT:
|
|
return OUString::number(m_aConstraints.size());
|
|
break;
|
|
case SP_LO_ENGINE:
|
|
return m_sLOEngineName;
|
|
break;
|
|
case SP_MS_ENGINE:
|
|
return m_sMSEngineId;
|
|
break;
|
|
case SP_INTEGER:
|
|
return m_sInteger;
|
|
break;
|
|
case SP_NON_NEGATIVE:
|
|
return m_sNonNegative;
|
|
break;
|
|
case SP_EPSILON_LEVEL:
|
|
return m_sEpsilonLevel;
|
|
break;
|
|
case SP_LIMIT_BBDEPTH:
|
|
return m_sLimitBBDepth;
|
|
break;
|
|
case SP_TIMEOUT:
|
|
return m_sTimeout;
|
|
break;
|
|
case SP_ALGORITHM:
|
|
return m_sAlgorithm;
|
|
break;
|
|
case SP_SWARM_SIZE:
|
|
return m_sSwarmSize;
|
|
break;
|
|
case SP_LEARNING_CYCLES:
|
|
return m_sLearningCycles;
|
|
break;
|
|
case SP_GUESS_VARIABLE_RANGE:
|
|
return m_sGuessVariableRange;
|
|
break;
|
|
case SP_VARIABLE_RANGE_THRESHOLD:
|
|
return m_sVariableRangeThreshold;
|
|
break;
|
|
case SP_ACR_COMPARATOR:
|
|
return m_sUseACRComparator;
|
|
break;
|
|
case SP_RND_STARTING_POINT:
|
|
return m_sUseRandomStartingPoint;
|
|
break;
|
|
case SP_STRONGER_PRNG:
|
|
return m_sUseStrongerPRNG;
|
|
break;
|
|
case SP_STAGNATION_LIMIT:
|
|
return m_sStagnationLimit;
|
|
break;
|
|
case SP_STAGNATION_TOLERANCE:
|
|
return m_sTolerance;
|
|
break;
|
|
case SP_ENHANCED_STATUS:
|
|
return m_sEnhancedSolverStatus;
|
|
break;
|
|
case SP_AGENT_SWITCH_RATE:
|
|
return m_sAgentSwitchRate;
|
|
break;
|
|
case SP_SCALING_MIN:
|
|
return m_sScalingFactorMin;
|
|
break;
|
|
case SP_SCALING_MAX:
|
|
return m_sScalingFactorMax;
|
|
break;
|
|
case SP_CROSSOVER_PROB:
|
|
return m_sCrossoverProbability;
|
|
break;
|
|
case SP_COGNITIVE_CONST:
|
|
return m_sCognitiveConstant;
|
|
break;
|
|
case SP_SOCIAL_CONST:
|
|
return m_sSocialConstant;
|
|
break;
|
|
case SP_CONSTRICTION_COEFF:
|
|
return m_sConstrictionCoeff;
|
|
break;
|
|
case SP_MUTATION_PROB:
|
|
return m_sMutationProbability;
|
|
break;
|
|
case SP_LIBRARY_SIZE:
|
|
return m_sLibrarySize;
|
|
break;
|
|
default:
|
|
return u""_ustr;
|
|
}
|
|
}
|
|
|
|
// Sets the value of a single solver parameter in the object
|
|
void SolverSettings::SetParameter(SolverParameter eParam, const OUString& sValue)
|
|
{
|
|
switch (eParam)
|
|
{
|
|
case SP_OBJ_CELL:
|
|
m_sObjCell = sValue;
|
|
break;
|
|
case SP_OBJ_TYPE:
|
|
{
|
|
sal_Int32 nObjType = sValue.toInt32();
|
|
switch (nObjType)
|
|
{
|
|
case OT_MAXIMIZE:
|
|
m_eObjType = ObjectiveType::OT_MAXIMIZE;
|
|
break;
|
|
case OT_MINIMIZE:
|
|
m_eObjType = ObjectiveType::OT_MINIMIZE;
|
|
break;
|
|
case OT_VALUE:
|
|
m_eObjType = ObjectiveType::OT_VALUE;
|
|
break;
|
|
default:
|
|
m_eObjType = ObjectiveType::OT_MAXIMIZE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SP_OBJ_VAL:
|
|
m_sObjVal = sValue;
|
|
break;
|
|
case SP_VAR_CELLS:
|
|
m_sVariableCells = sValue;
|
|
break;
|
|
case SP_LO_ENGINE:
|
|
m_sLOEngineName = sValue;
|
|
break;
|
|
case SP_INTEGER:
|
|
{
|
|
if (sValue == "0" || sValue == "1")
|
|
m_sInteger = sValue;
|
|
}
|
|
break;
|
|
case SP_NON_NEGATIVE:
|
|
{
|
|
if (sValue == "1" || sValue == "2")
|
|
m_sNonNegative = sValue;
|
|
}
|
|
break;
|
|
case SP_EPSILON_LEVEL:
|
|
m_sEpsilonLevel = sValue;
|
|
break;
|
|
case SP_LIMIT_BBDEPTH:
|
|
m_sLimitBBDepth = sValue;
|
|
break;
|
|
case SP_TIMEOUT:
|
|
m_sTimeout = sValue;
|
|
break;
|
|
case SP_ALGORITHM:
|
|
{
|
|
if (sValue == "1" || sValue == "2" || sValue == "3")
|
|
m_sAlgorithm = sValue;
|
|
}
|
|
break;
|
|
case SP_SWARM_SIZE:
|
|
m_sSwarmSize = sValue;
|
|
break;
|
|
case SP_LEARNING_CYCLES:
|
|
m_sLearningCycles = sValue;
|
|
break;
|
|
case SP_GUESS_VARIABLE_RANGE:
|
|
m_sGuessVariableRange = sValue;
|
|
break;
|
|
case SP_VARIABLE_RANGE_THRESHOLD:
|
|
m_sVariableRangeThreshold = sValue;
|
|
break;
|
|
case SP_ACR_COMPARATOR:
|
|
{
|
|
if (sValue == "0" || sValue == "1")
|
|
m_sUseACRComparator = sValue;
|
|
}
|
|
break;
|
|
case SP_RND_STARTING_POINT:
|
|
{
|
|
if (sValue == "0" || sValue == "1")
|
|
m_sUseRandomStartingPoint = sValue;
|
|
}
|
|
break;
|
|
case SP_STRONGER_PRNG:
|
|
{
|
|
if (sValue == "0" || sValue == "1")
|
|
m_sUseStrongerPRNG = sValue;
|
|
}
|
|
break;
|
|
case SP_STAGNATION_LIMIT:
|
|
m_sStagnationLimit = sValue;
|
|
break;
|
|
case SP_STAGNATION_TOLERANCE:
|
|
m_sTolerance = sValue;
|
|
break;
|
|
case SP_ENHANCED_STATUS:
|
|
{
|
|
if (sValue == "0" || sValue == "1")
|
|
m_sEnhancedSolverStatus = sValue;
|
|
}
|
|
break;
|
|
case SP_AGENT_SWITCH_RATE:
|
|
m_sAgentSwitchRate = sValue;
|
|
break;
|
|
case SP_SCALING_MIN:
|
|
m_sScalingFactorMin = sValue;
|
|
break;
|
|
case SP_SCALING_MAX:
|
|
m_sScalingFactorMax = sValue;
|
|
break;
|
|
case SP_CROSSOVER_PROB:
|
|
m_sCrossoverProbability = sValue;
|
|
break;
|
|
case SP_COGNITIVE_CONST:
|
|
m_sCognitiveConstant = sValue;
|
|
break;
|
|
case SP_SOCIAL_CONST:
|
|
m_sSocialConstant = sValue;
|
|
break;
|
|
case SP_CONSTRICTION_COEFF:
|
|
m_sConstrictionCoeff = sValue;
|
|
break;
|
|
case SP_MUTATION_PROB:
|
|
m_sMutationProbability = sValue;
|
|
break;
|
|
case SP_LIBRARY_SIZE:
|
|
m_sLibrarySize = sValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SolverSettings::SetObjectiveType(ObjectiveType eType) { m_eObjType = eType; }
|
|
|
|
// Loads all constraints in the tab
|
|
void SolverSettings::ReadConstraints()
|
|
{
|
|
// Condition indices start at 1 for MS compatibility
|
|
// The number of "lhs", "rel" and "rhs" entries will always be the same
|
|
tools::Long nConstraint = 1;
|
|
m_aConstraints.clear();
|
|
OUString sValue;
|
|
|
|
while (ReadConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, sValue))
|
|
{
|
|
// Left hand side
|
|
ModelConstraint aNewCondition;
|
|
aNewCondition.aLeftStr = sValue;
|
|
|
|
// Right hand side
|
|
if (ReadConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, sValue))
|
|
aNewCondition.aRightStr = sValue;
|
|
|
|
// Relation (operator)
|
|
if (ReadConstraintPart(CP_OPERATOR, nConstraint, sValue))
|
|
aNewCondition.nOperator = static_cast<sc::ConstraintOperator>(sValue.toInt32());
|
|
|
|
m_aConstraints.push_back(aNewCondition);
|
|
nConstraint++;
|
|
}
|
|
}
|
|
|
|
// Writes all constraints to the file
|
|
void SolverSettings::WriteConstraints()
|
|
{
|
|
// Condition indices start at 1 for MS compatibility
|
|
tools::Long nConstraint = 1;
|
|
|
|
for (auto& aConstraint : m_aConstraints)
|
|
{
|
|
// Left hand side
|
|
WriteConstraintPart(CP_LEFT_HAND_SIDE, nConstraint, aConstraint.aLeftStr);
|
|
// Relation (operator)
|
|
WriteConstraintPart(CP_OPERATOR, nConstraint, OUString::number(aConstraint.nOperator));
|
|
// Right hand side
|
|
WriteConstraintPart(CP_RIGHT_HAND_SIDE, nConstraint, aConstraint.aRightStr);
|
|
nConstraint++;
|
|
}
|
|
}
|
|
|
|
// Write a single constraint part to the file
|
|
void SolverSettings::WriteConstraintPart(ConstraintPart ePart, tools::Long nIndex,
|
|
const OUString& sValue)
|
|
{
|
|
// Empty named ranges cannot be written to the file (this corrupts MS files)
|
|
if (sValue.isEmpty())
|
|
return;
|
|
|
|
OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
|
|
ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
|
|
pNewEntry->AddType(ScRangeData::Type::Hidden);
|
|
m_pRangeName->insert(pNewEntry);
|
|
}
|
|
|
|
// Reads a single constraint part from its associated named range; returns false if the named
|
|
// range does not exist in the file
|
|
bool SolverSettings::ReadConstraintPart(ConstraintPart ePart, tools::Long nIndex, OUString& rValue)
|
|
{
|
|
OUString sRange = m_aConstraintParts[ePart] + OUString::number(nIndex);
|
|
ScRangeData* pRangeData
|
|
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
|
|
if (pRangeData)
|
|
{
|
|
rValue = pRangeData->GetSymbol();
|
|
// tdf#156814 Remove sheet name if it is a range that refers to the same sheet
|
|
ScRange aRange;
|
|
ScRefFlags nFlags = aRange.ParseAny(rValue, m_rDoc);
|
|
bool bIsValidRange = (nFlags & ScRefFlags::VALID) == ScRefFlags::VALID;
|
|
if (bIsValidRange && m_rTable.GetTab() == aRange.aStart.Tab())
|
|
rValue = aRange.Format(m_rDoc, ScRefFlags::RANGE_ABS);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Reads the engine name parameter as informed in the file in the format used in LO.
|
|
* If only a MS engine is informed, then it is converted to a LO-equivalent engine
|
|
*/
|
|
void SolverSettings::ReadEngine()
|
|
{
|
|
if (!ReadParamValue(SP_LO_ENGINE, m_sLOEngineName, true))
|
|
{
|
|
// If no engine is defined, use CoinMP solver as default
|
|
m_sLOEngineName = "com.sun.star.comp.Calc.CoinMPSolver";
|
|
}
|
|
|
|
if (SolverNamesToExcelEngines.count(m_sLOEngineName))
|
|
{
|
|
// Find equivalent MS engine code
|
|
m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
|
|
}
|
|
}
|
|
|
|
// Write solver LO and MS-equivalent engine names
|
|
void SolverSettings::WriteEngine()
|
|
{
|
|
WriteParamValue(SP_LO_ENGINE, m_sLOEngineName, true);
|
|
// Find equivalent MS engine code
|
|
if (SolverNamesToExcelEngines.count(m_sLOEngineName))
|
|
{
|
|
m_sMSEngineId = SolverNamesToExcelEngines.find(m_sLOEngineName)->second;
|
|
WriteParamValue(SP_MS_ENGINE, m_sMSEngineId);
|
|
}
|
|
}
|
|
|
|
// Assigns a new constraints vector
|
|
void SolverSettings::SetConstraints(std::vector<ModelConstraint> aConstraints)
|
|
{
|
|
m_aConstraints = std::move(aConstraints);
|
|
}
|
|
|
|
// Saves all solver settings into the file
|
|
void SolverSettings::SaveSolverSettings()
|
|
{
|
|
// Before saving, remove all existing named ranges related to the solver
|
|
DeleteAllNamedRanges();
|
|
|
|
WriteParamValue(SP_OBJ_CELL, m_sObjCell);
|
|
WriteParamValue(SP_OBJ_TYPE, OUString::number(m_eObjType));
|
|
WriteParamValue(SP_OBJ_VAL, m_sObjVal);
|
|
WriteParamValue(SP_VAR_CELLS, m_sVariableCells);
|
|
|
|
WriteConstraints();
|
|
WriteEngine();
|
|
|
|
sal_Int32 nConstrCount = m_aConstraints.size();
|
|
WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount));
|
|
|
|
// Solver engine options
|
|
WriteParamValue(SP_INTEGER, m_sInteger);
|
|
WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative);
|
|
WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel);
|
|
WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth);
|
|
WriteParamValue(SP_TIMEOUT, m_sTimeout);
|
|
WriteParamValue(SP_ALGORITHM, m_sAlgorithm);
|
|
// Engine options common for DEPS and SCO
|
|
WriteParamValue(SP_SWARM_SIZE, m_sSwarmSize);
|
|
WriteParamValue(SP_LEARNING_CYCLES, m_sLearningCycles);
|
|
WriteParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange);
|
|
WriteDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold);
|
|
WriteParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator);
|
|
WriteParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint);
|
|
WriteParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG);
|
|
WriteParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit);
|
|
WriteDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance);
|
|
WriteParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus);
|
|
// DEPS Options
|
|
WriteDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate);
|
|
WriteDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin);
|
|
WriteDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax);
|
|
WriteDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability);
|
|
WriteDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant);
|
|
WriteDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant);
|
|
WriteDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff);
|
|
WriteDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability);
|
|
// SCO Options
|
|
WriteParamValue(SP_LIBRARY_SIZE, m_sLibrarySize);
|
|
|
|
if (m_pDocShell)
|
|
m_pDocShell->SetDocumentModified();
|
|
}
|
|
|
|
/* Reads the current value of the parameter in the named range into rValue
|
|
* If the value does not exist, the rValue is left unchanged
|
|
* This is private because it is only used during initialization
|
|
* Returns true if the value exits; returns false otherwise
|
|
*/
|
|
bool SolverSettings::ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes)
|
|
{
|
|
const auto iter = m_mNamedRanges.find(eParam);
|
|
assert(iter != m_mNamedRanges.end());
|
|
OUString sRange = iter->second;
|
|
ScRangeData* pRangeData
|
|
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
|
|
if (pRangeData)
|
|
{
|
|
rValue = pRangeData->GetSymbol();
|
|
if (bRemoveQuotes)
|
|
ScGlobal::EraseQuotes(rValue, '"');
|
|
|
|
// tdf#156814 Remove sheet name from the objective cell and value if they refer to the same sheet
|
|
if (eParam == SP_OBJ_CELL || eParam == SP_OBJ_VAL)
|
|
{
|
|
ScRange aRange;
|
|
ScRefFlags nFlags = aRange.ParseAny(rValue, m_rDoc);
|
|
bool bIsValidRange = ((nFlags & ScRefFlags::VALID) == ScRefFlags::VALID);
|
|
|
|
if (bIsValidRange && m_rTable.GetTab() == aRange.aStart.Tab())
|
|
rValue = aRange.Format(m_rDoc, ScRefFlags::RANGE_ABS);
|
|
}
|
|
else if (eParam == SP_VAR_CELLS)
|
|
{
|
|
// Variable cells may contain multiple ranges separated by ';'
|
|
sal_Int32 nIdx = 0;
|
|
OUString sNewValue;
|
|
bool bFirst = true;
|
|
// Delimiter character to separate ranges
|
|
sal_Unicode cDelimiter = ScCompiler::GetNativeSymbolChar(OpCode::ocSep);
|
|
|
|
do
|
|
{
|
|
OUString aRangeStr(o3tl::getToken(rValue, 0, cDelimiter, nIdx));
|
|
ScRange aRange;
|
|
ScRefFlags nFlags = aRange.ParseAny(aRangeStr, m_rDoc);
|
|
bool bIsValidRange = (nFlags & ScRefFlags::VALID) == ScRefFlags::VALID;
|
|
|
|
if (bIsValidRange && m_rTable.GetTab() == aRange.aStart.Tab())
|
|
aRangeStr = aRange.Format(m_rDoc, ScRefFlags::RANGE_ABS);
|
|
|
|
if (bFirst)
|
|
{
|
|
sNewValue = aRangeStr;
|
|
bFirst = false;
|
|
}
|
|
else
|
|
{
|
|
sNewValue += OUStringChar(cDelimiter) + aRangeStr;
|
|
}
|
|
} while (nIdx > 0);
|
|
|
|
rValue = sNewValue;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Reads a parameter value of type 'double' from the named range and into rValue
|
|
bool SolverSettings::ReadDoubleParamValue(SolverParameter eParam, OUString& rValue)
|
|
{
|
|
const auto iter = m_mNamedRanges.find(eParam);
|
|
assert(iter != m_mNamedRanges.end());
|
|
OUString sRange = iter->second;
|
|
ScRangeData* pRangeData
|
|
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
|
|
if (pRangeData)
|
|
{
|
|
OUString sLocalizedValue = pRangeData->GetSymbol();
|
|
double fValue = rtl::math::stringToDouble(sLocalizedValue,
|
|
ScGlobal::getLocaleData().getNumDecimalSep()[0],
|
|
ScGlobal::getLocaleData().getNumThousandSep()[0]);
|
|
rValue = OUString::number(fValue);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Writes a parameter value to the file as a named range.
|
|
* Argument bQuoted indicates whether the value should be enclosed with quotes or not (used
|
|
* for string expressions that must be enclosed with quotes)
|
|
*/
|
|
void SolverSettings::WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted)
|
|
{
|
|
// Empty parameters cannot be written to the file (this corrupts MS files)
|
|
// There's no problem if the parameter is missing both for LO and MS
|
|
if (sValue.isEmpty())
|
|
return;
|
|
|
|
if (bQuoted)
|
|
ScGlobal::AddQuotes(sValue, '"');
|
|
|
|
const auto iter = m_mNamedRanges.find(eParam);
|
|
assert(iter != m_mNamedRanges.end());
|
|
OUString sRange = iter->second;
|
|
ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sValue);
|
|
pNewEntry->AddType(ScRangeData::Type::Hidden);
|
|
m_pRangeName->insert(pNewEntry);
|
|
}
|
|
|
|
// Writes a parameter value of type 'double' to the file as a named range
|
|
// The argument 'sValue' uses dot as decimal separator and needs to be localized before
|
|
// being written to the file
|
|
void SolverSettings::WriteDoubleParamValue(SolverParameter eParam, std::u16string_view sValue)
|
|
{
|
|
const auto iter = m_mNamedRanges.find(eParam);
|
|
assert(iter != m_mNamedRanges.end());
|
|
OUString sRange = iter->second;
|
|
double fValue = rtl::math::stringToDouble(sValue, '.', ',');
|
|
OUString sLocalizedValue = rtl::math::doubleToUString(
|
|
fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
|
|
ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
|
|
ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sLocalizedValue);
|
|
pNewEntry->AddType(ScRangeData::Type::Hidden);
|
|
m_pRangeName->insert(pNewEntry);
|
|
}
|
|
|
|
void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions)
|
|
{
|
|
sal_Int32 nOptionsSize = aOptions.getLength();
|
|
auto pParamValues = aOptions.getArray();
|
|
|
|
for (auto i = 0; i < nOptionsSize; i++)
|
|
{
|
|
const css::beans::PropertyValue& aProp = aOptions[i];
|
|
OUString sLOParamName = aProp.Name;
|
|
// Only try to get the parameter value if it is an expected parameter name
|
|
if (SolverParamNames.count(sLOParamName))
|
|
{
|
|
TParamInfo aParamInfo;
|
|
aParamInfo = SolverParamNames.find(sLOParamName)->second;
|
|
SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
|
|
OUString sParamType = std::get<OUString>(aParamInfo[2]);
|
|
OUString sParamValue = GetParameter(eParamId);
|
|
if (sParamType == "int")
|
|
{
|
|
css::uno::Any nValue(sParamValue.toInt32());
|
|
pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, nValue,
|
|
css::beans::PropertyState_DIRECT_VALUE);
|
|
}
|
|
if (sParamType == "double")
|
|
{
|
|
css::uno::Any fValue(sParamValue.toDouble());
|
|
pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, fValue,
|
|
css::beans::PropertyState_DIRECT_VALUE);
|
|
}
|
|
if (sParamType == "bool")
|
|
{
|
|
// The parameter NonNegative is a special case for MS compatibility
|
|
// It uses "1" for "true" and "2" for "false"
|
|
bool bTmpValue;
|
|
if (sLOParamName == "NonNegative")
|
|
bTmpValue = sParamValue == "1" ? true : false;
|
|
else
|
|
bTmpValue = sParamValue.toBoolean();
|
|
|
|
css::uno::Any bValue(bTmpValue);
|
|
pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, bValue,
|
|
css::beans::PropertyState_DIRECT_VALUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updates the object members related to solver engine options using aOptions info
|
|
void SolverSettings::SetEngineOptions(const css::uno::Sequence<css::beans::PropertyValue>& aOptions)
|
|
{
|
|
sal_Int32 nOptionsSize = aOptions.getLength();
|
|
|
|
for (auto i = 0; i < nOptionsSize; i++)
|
|
{
|
|
const css::beans::PropertyValue& aProp = aOptions[i];
|
|
OUString sLOParamName = aProp.Name;
|
|
// Only try to set the parameter value if it is an expected parameter name
|
|
if (SolverParamNames.count(sLOParamName))
|
|
{
|
|
TParamInfo aParamInfo;
|
|
aParamInfo = SolverParamNames.find(sLOParamName)->second;
|
|
SolverParameter eParamId = std::get<SolverParameter>(aParamInfo[0]);
|
|
OUString sParamType = std::get<OUString>(aParamInfo[2]);
|
|
if (sParamType == "int")
|
|
{
|
|
sal_Int32 nValue = 0;
|
|
aProp.Value >>= nValue;
|
|
SetParameter(eParamId, OUString::number(nValue));
|
|
}
|
|
if (sParamType == "double")
|
|
{
|
|
double fValue = 0;
|
|
aProp.Value >>= fValue;
|
|
SetParameter(eParamId, OUString::number(fValue));
|
|
}
|
|
if (sParamType == "bool")
|
|
{
|
|
bool bValue = false;
|
|
aProp.Value >>= bValue;
|
|
if (sLOParamName == "NonNegative")
|
|
{
|
|
// The parameter NonNegative is a special case for MS compatibility
|
|
// It uses "1" for "true" and "2" for "false"
|
|
if (bValue)
|
|
SetParameter(eParamId, OUString::number(1));
|
|
else
|
|
SetParameter(eParamId, OUString::number(2));
|
|
}
|
|
else
|
|
{
|
|
SetParameter(eParamId, OUString::number(sal_Int32(bValue)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deletes all named ranges in the current tab that are related to the solver (i.e. start with "solver_")
|
|
void SolverSettings::DeleteAllNamedRanges()
|
|
{
|
|
std::vector<ScRangeData*> aItemsToErase;
|
|
|
|
// Indices in m_pRangeName start at 1
|
|
for (size_t i = 1; i <= m_pRangeName->size(); ++i)
|
|
{
|
|
ScRangeData* pData = m_pRangeName->findByIndex(i);
|
|
if (pData && pData->GetName().startsWith("solver_"))
|
|
aItemsToErase.push_back(pData);
|
|
}
|
|
|
|
for (auto pItem : aItemsToErase)
|
|
m_pRangeName->erase(*pItem);
|
|
}
|
|
|
|
/* Sets all solver parameters to their default values and clear all constraints.
|
|
* This method only resets the object properties, but does not save changes to the
|
|
* document. To save changes, call SaveSolverSettings().
|
|
*/
|
|
void SolverSettings::ResetToDefaults()
|
|
{
|
|
m_sObjCell = "";
|
|
m_eObjType = ObjectiveType::OT_MAXIMIZE;
|
|
m_sObjVal = "";
|
|
m_sVariableCells = "";
|
|
m_sMSEngineId = "1";
|
|
|
|
css::uno::Sequence<OUString> aEngineNames;
|
|
css::uno::Sequence<OUString> aDescriptions;
|
|
ScSolverUtil::GetImplementations(aEngineNames, aDescriptions);
|
|
|
|
// tdf#162760 Set the parameters of all available solver engines to the default values
|
|
for (const auto& sEngine : aEngineNames)
|
|
{
|
|
css::uno::Sequence<css::beans::PropertyValue> aEngineProps
|
|
= ScSolverUtil::GetDefaults(sEngine);
|
|
SetEngineOptions(aEngineProps);
|
|
}
|
|
|
|
// The default solver engine is the first implementation available
|
|
m_sLOEngineName = aEngineNames[0];
|
|
|
|
// Clear all constraints
|
|
m_aConstraints.clear();
|
|
}
|
|
|
|
/* Returns true if the current sheet already has a solver model.
|
|
This is determined by checking if the current tab has the SP_OBJ_CELL named range
|
|
which is associated with solver models.
|
|
Note that the named ranges are only created after SaveSolverSettings is called,
|
|
so before it is called, no solver-related named ranges exist.
|
|
*/
|
|
bool SolverSettings::TabHasSolverModel()
|
|
{
|
|
// Check if the named range for the objective value exists in the sheet
|
|
const auto iter = m_mNamedRanges.find(SP_OBJ_CELL);
|
|
if (iter == m_mNamedRanges.end())
|
|
return false;
|
|
OUString sRange = iter->second;
|
|
ScRangeData* pRangeData
|
|
= m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange));
|
|
if (pRangeData)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
} // namespace sc
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|