Files
DocumentServer-v-9.2.0/sdkjs/cell/model/whatIfAnalysis.js
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

3163 lines
97 KiB
JavaScript

/*
* (c) Copyright Ascensio System SIA 2010-2024
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
"use strict";
(
/**
* @param {Window} window
* @param {undefined} undefined
*/
function (window, undefined) {
// Import
const oCNumberType = AscCommonExcel.cNumber;
const CellValueType = AscCommon.CellValueType;
// Collections for UI Solver feature
/** @enum {number} */
const c_oAscOptimizeTo = {
max: 1,
min: 2,
valueOf: 3
};
/** @enum {number} */
const c_oAscSolvingMethod = {
grgNonlinear: 1,
simplexLP: 2,
evolutionary: 3
};
/** @enum {number} */
const c_oAscDerivativeType = {
forward: 1,
central: 2
};
/** @enum {number} */
const c_oAscOperator = {
'<=': 1,
'=': 2,
'>=': 3,
integer: 4,
bin: 5,
diff: 6
};
/**@enum {number} */
const c_oAscSolverResult = {
keepSolverSolution: 0,
restoreOriginalValues: 1
};
/**@enum {number} */
const c_oResultStatus = {
foundOptimalSolution: 0,
solutionHasConverged: 1,
cannotImproveSolution: 2,
maxIterationsReached: 3,
objectiveCellNotConverge: 4,
notFindFeasibleSolution: 5,
stoppedByUser: 6,
linearityConditionsNotSatisfied: 7,
tooLargeProblem: 8,
errorValInObjectiveOrConstraintCell: 9,
maxTimeReached: 10,
notEnoughMemory: 11,
errorInModel: 12,
foundIntegerSolution: 13,
maxFeasibleSolutionReached: 14,
maxSubproblemSolutionReached: 15,
};
// Common class
/**
* Class representing base attributes and methods for features of analysis.
* @param {parserFormula} oParsedFormula - Formula object
* @param {Range} oChangingCell - Changing cells.
* For Goal Seek feature it's 1-1 object, for Solver 1-*.
* @constructor
*/
function CBaseAnalysis(oParsedFormula, oChangingCell) {
this.oParsedFormula = oParsedFormula;
this.oChangingCell = oChangingCell;
this.sRegNumDecimalSeparator = AscCommon.g_oDefaultCultureInfo.NumberDecimalSeparator;
this.nIntervalId = null;
this.bIsPause = false;
this.nDelay = 70; // in ms for interval.
this.nCurAttempt = 0;
this.bIsSingleStep = false;
this.nPrevFactValue = null;
// todo add value from settings
this.nMaxIterations = 100; // max iterations of goal seek. Default value is 100
}
/**
* Returns a result of formula with picked changing value.
* @memberof CBaseAnalysis
* @param {number} [nChangingVal]
* @param {Range} [oChangingCell]
* @returns {number} nFactValue
*/
CBaseAnalysis.prototype.calculateFormula = function(nChangingVal, oChangingCell) {
let oParsedFormula = this.getParsedFormula();
let nFactValue = null;
if (nChangingVal !== undefined && oChangingCell) {
let sRegNumDecimalSeparator = this.getRegNumDecimalSeparator();
oChangingCell.setValue(String(nChangingVal).replace('.', sRegNumDecimalSeparator));
oChangingCell.worksheet.workbook.dependencyFormulas.unlockRecal();
}
oParsedFormula.parse();
nFactValue = oParsedFormula.calculate().getValue();
// If result of formula returns type cNumber, convert to Number
if (nFactValue instanceof oCNumberType) {
nFactValue = nFactValue.toNumber();
}
return nFactValue;
};
/**
* Returns a formula object.
* @memberof CBaseAnalysis
* @returns {parserFormula}
*/
CBaseAnalysis.prototype.getParsedFormula = function() {
return this.oParsedFormula;
};
/**
* Returns changing cell.
* @memberof CBaseAnalysis
* @returns {Range}
*/
CBaseAnalysis.prototype.getChangingCell = function() {
return this.oChangingCell;
};
/**
* Sets changing cell.
* @memberof CBaseAnalysis
* @param {Range} oChangingCell
*/
CBaseAnalysis.prototype.setChangingCell = function(oChangingCell) {
this.oChangingCell = oChangingCell;
};
/**
* Returns a number decimal separator according chosen region. It may be "." or ",".
* @memberof CBaseAnalysis
* @returns {string}
*/
CBaseAnalysis.prototype.getRegNumDecimalSeparator = function() {
return this.sRegNumDecimalSeparator;
};
/**
* Returns an id of interval. Uses for clear interval in UI.
* @memberof CBaseAnalysis
* @returns {number}
*/
CBaseAnalysis.prototype.getIntervalId = function() {
return this.nIntervalId;
};
/**
* Sets an id of interval. Uses for clear interval in UI.
* @memberof CBaseAnalysis
* @param {number} nIntervalId
*/
CBaseAnalysis.prototype.setIntervalId = function(nIntervalId) {
this.nIntervalId = nIntervalId;
};
/**
* Returns a flag who recognizes calculation process is paused or not.
* @memberof CBaseAnalysis
* @returns {boolean}
*/
CBaseAnalysis.prototype.getIsPause = function() {
return this.bIsPause;
};
/**
* Sets a flag who recognizes calculation process is paused or not.
* @memberof CBaseAnalysis
* @param bIsPause
*/
CBaseAnalysis.prototype.setIsPause = function(bIsPause) {
this.bIsPause = bIsPause;
};
/**
* Returns a delay in ms. Using for interval in UI.
* @memberof CBaseAnalysis
* @returns {number}
*/
CBaseAnalysis.prototype.getDelay = function() {
return this.nDelay;
};
/**
* Returns a number of the current attempt.
* @memberof CBaseAnalysis
* @returns {number}
*/
CBaseAnalysis.prototype.getCurrentAttempt = function() {
return this.nCurAttempt;
};
/**
* Increases a value of the current attempt.
* @memberof CBaseAnalysis
*/
CBaseAnalysis.prototype.increaseCurrentAttempt = function() {
this.nCurAttempt += 1;
};
/**
* Returns a flag who recognizes goal seek is runs in "single step" mode. Uses for "step" method.
* @memberof CBaseAnalysis
* @returns {boolean}
*/
CBaseAnalysis.prototype.getIsSingleStep = function() {
return this.bIsSingleStep;
};
/**
* Sets a flag who recognizes goal seek is runs in "single step" mode.
* @memberof CBaseAnalysis
* @param {boolean} bIsSingleStep
*/
CBaseAnalysis.prototype.setIsSingleStep = function(bIsSingleStep) {
this.bIsSingleStep = bIsSingleStep;
};
/**
* Returns previous value of formula result.
* @memberof CBaseAnalysis
* @returns {number}
*/
CBaseAnalysis.prototype.getPrevFactValue = function() {
return this.nPrevFactValue;
};
/**
* Sets previous value of formula result.
* @memberof CBaseAnalysis
* @param {number} nPrevFactValue
*/
CBaseAnalysis.prototype.setPrevFactValue = function(nPrevFactValue) {
this.nPrevFactValue = nPrevFactValue;
};
/**
* Returns max iterations.
* @memberof CBaseAnalysis
* @returns {number}
*/
CBaseAnalysis.prototype.getMaxIterations = function() {
return this.nMaxIterations;
};
/**
* Sets max iterations.
* @memberof CBaseAnalysis
* @param {number} nMaxIterations
*/
CBaseAnalysis.prototype.setMaxIterations = function(nMaxIterations) {
this.nMaxIterations = nMaxIterations;
};
// Goal seek
/**
* Class representing a goal seek feature
* @param {parserFormula} oParsedFormula - Formula object.
* For a goal seek uses methods: parse - for update values in formula, calculate - for calculate formula result.
* @param {number} nExpectedVal - Expected value.
* @param {Range} oChangingCell - Changing cell.
* @constructor
*/
function CGoalSeek(oParsedFormula, nExpectedVal, oChangingCell) {
CBaseAnalysis.call(this, oParsedFormula, oChangingCell);
this.nExpectedVal = nExpectedVal;
this.sFormulaCellName = null;
this.nStepDirection = null;
this.nFirstChangingVal = null;
this.nChangingVal = null;
this.nPrevValue = null;
this.bReverseCompare = null;
this.bEnabledRidder = false;
this.nLow = null;
this.nHigh = null;
this.nRelativeError = 1e-4; // relative error of goal seek. Default value is 1e-4
}
CGoalSeek.prototype = Object.create(CBaseAnalysis.prototype);
CGoalSeek.prototype.constructor = CGoalSeek;
/**
* Fills attributes for work with goal seek.
* @memberof CGoalSeek
*/
CGoalSeek.prototype.init = function() {
let oChangingCell = this.getChangingCell();
let sChangingVal = oChangingCell.getValue();
this.setFirstChangingValue(sChangingVal);
this.setFormulaCellName(this.getParsedFormula());
this.initStepDirection();
this.initReverseCompare();
this.setChangingValue(sChangingVal ? Number(sChangingVal) : 0);
};
/**
* Main logic of calculating goal seek.
* Takes "Changing cell" and its change according to expected value until result of formula be equal to expected value.
* For calculate uses exponent step and Ridder method.
* Notice: That algorithm works for most formulas who can solve with help linear or non-linear equations.
* Exception is engineering formula like DEC2BIN, DEC2HEX etc. Those formulas can't be solved with that way because
* they are not linear or non-linear equations.
* Runs only in sync or async loop.
* @memberof CGoalSeek
* @return {boolean} The flag who recognizes end a loop of calculation goal seek. True - stop a loop, false - continue a loop.
*/
CGoalSeek.prototype.calculate = function() {
if (this.getIsPause()) {
return true;
}
const oChangingCell = this.getChangingCell();
let nChangingVal = this.getChangingValue();
let nExpectedVal = this.getExpectedVal();
let nPrevFactValue = this.getPrevFactValue();
let nFactValue, nDiff, nMedianFx, nMedianVal, nLowFx;
this.increaseCurrentAttempt();
// Exponent step mode
if (!this.getEnabledRidder()) {
nFactValue = this.calculateFormula(nChangingVal, oChangingCell);
nDiff = nFactValue - nExpectedVal;
// Checks should it switch to Ridder algorithm.
if (this.getReverseCompare()) {
this.setEnabledRidder(!!(nFactValue < nExpectedVal || (nPrevFactValue && nFactValue > nPrevFactValue)));
} else {
this.setEnabledRidder(!!(nFactValue > nExpectedVal || (nPrevFactValue && nFactValue < nPrevFactValue)));
}
}
if (this.getEnabledRidder()) {
if (this.getLowBorder() == null && this.getHighBorder() == null) {
this.setLowBorder(this.getPrevValue());
this.setHighBorder(nChangingVal);
}
let nLow = this.getLowBorder();
let nHigh = this.getHighBorder();
// Search f(lowBorder_value) and f(highBorder_value)
nLowFx = this.calculateFormula(nLow, oChangingCell) - nExpectedVal;
let nHighFx = this.calculateFormula(nHigh, oChangingCell) - nExpectedVal;
// Search avg value in interval [nLow, nHigh]
nMedianVal = (nLow + nHigh) / 2;
nMedianFx = this.calculateFormula(nMedianVal, oChangingCell) - nExpectedVal;
// Search changing value via root of exponential function
nChangingVal = nMedianVal + (nMedianVal - nLow) * Math.sign(nLowFx - nHighFx) * nMedianFx / Math.sqrt(Math.pow(nMedianFx,2) - nLowFx * nHighFx);
// If result exponential function is NaN then we use nMedianVal as changing value. It may be possible for unlinear function like sin, cos, tg.
if (isNaN(nChangingVal)) {
nChangingVal = nMedianVal;
}
nFactValue = this.calculateFormula(nChangingVal, oChangingCell);
nDiff = nFactValue - nExpectedVal;
this.setChangingValue(nChangingVal);
}
var oApi = Asc.editor;
oApi.sendEvent("asc_onGoalSeekUpdate", nExpectedVal, nFactValue, this.getCurrentAttempt(), this.getFormulaCellName());
// Check: Need a finish calculate
if (Math.abs(nDiff) < this.getRelativeError()) {
oApi.sendEvent("asc_onGoalSeekStop", true);
return true;
}
if (this.getCurrentAttempt() >= this.getMaxIterations() || isNaN(nDiff)) {
oApi.sendEvent("asc_onGoalSeekStop", false);
return true;
}
//Calculates next changing value
if (this.getEnabledRidder()) {
if (nMedianFx < 0 !== nDiff < 0) {
this.setLowBorder(nMedianVal);
this.setHighBorder(nChangingVal);
} else if (nDiff < 0 !== nLowFx < 0) {
this.setHighBorder(nChangingVal);
} else {
this.setLowBorder(nChangingVal);
}
} else { // Exponent step logic
let nCurAttempt = this.getCurrentAttempt();
let nFirstChangingVal = this.getFirstChangingValue();
let nStepDirection = this.getStepDirection();
this.setPrevValue(nChangingVal);
this.setPrevFactValue(nFactValue);
if (!nFirstChangingVal) {
this.setChangingValue((1 / 100 * nStepDirection) + (Math.pow(2, nCurAttempt - 1) - 1) * (1 / 10 * nStepDirection));
} else {
this.setChangingValue(nFirstChangingVal + (nFirstChangingVal / 100 * nStepDirection) + (Math.pow(2, nCurAttempt - 1) - 1) * (nFirstChangingVal / 10 * nStepDirection));
}
}
if (this.getIsSingleStep()) {
this.setIsPause(true);
this.setIsSingleStep(false);
return true;
}
return false;
};
/**
* Initialize step direction. Reverse direction (-1) or forward direction (+1).
* @memberof CGoalSeek
*/
CGoalSeek.prototype.initStepDirection = function() {
const oChangingCell = this.getChangingCell();
let sChangingVal = this.getChangingCell().getValue();
let nChangingVal = sChangingVal ? Number(sChangingVal) : 0;
let nExpectedVal = this.getExpectedVal();
let nFirstChangedVal = null;
// Init next changed value for find nextFormulaResult
if (nChangingVal === 0) {
nFirstChangedVal = 0.01
} else {
nFirstChangedVal = nChangingVal + nChangingVal / 100;
}
// Find first and next formula result for check step direction
let nFirstFormulaResult = this.calculateFormula(nChangingVal, oChangingCell);
let nNextFormulaResult = this.calculateFormula(nFirstChangedVal, oChangingCell);
// Init step direction
if ((nFirstFormulaResult > nExpectedVal && nNextFormulaResult > nFirstFormulaResult)) {
this.setStepDirection(-1);
} else if (nNextFormulaResult < nFirstFormulaResult && nNextFormulaResult < nExpectedVal) {
this.setStepDirection(-1);
} else if (nFirstFormulaResult === nNextFormulaResult) {
this.setStepDirection(-1);
} else {
this.setStepDirection(1);
}
};
/**
* Returns step direction.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getStepDirection = function() {
return this.nStepDirection;
};
/**
* Sets step direction.
* @memberof CGoalSeek
* @param {number} nStepDirection
*/
CGoalSeek.prototype.setStepDirection = function(nStepDirection) {
this.nStepDirection = nStepDirection;
};
/**
* Returns expected value.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getExpectedVal = function() {
return this.nExpectedVal;
};
/**
* Returns relative error.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getRelativeError = function() {
return this.nRelativeError;
};
/**
* Sets relative error.
* @memberof CGoalSeek
* @param {number} nRelativeError
*/
CGoalSeek.prototype.setRelativeError = function(nRelativeError) {
this.nRelativeError = nRelativeError;
};
/**
* Returns formula cell name.
* @returns {string}
*/
CGoalSeek.prototype.getFormulaCellName = function() {
return this.sFormulaCellName;
};
/**
* Sets formula cell name.
* @param {parserFormula} oParsedFormula
*/
CGoalSeek.prototype.setFormulaCellName = function(oParsedFormula) {
let oCellWithFormula = oParsedFormula.getParent();
let ws = oParsedFormula.getWs();
this.sFormulaCellName = ws.getRange4(oCellWithFormula.nRow, oCellWithFormula.nCol).getName();
};
/**
* Returns first changing cell value in number type.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getFirstChangingValue = function() {
return this.nFirstChangingVal;
};
/**
* Sets first changing cell value in number type.
* @memberof CGoalSeek
* @param {string} sChangingVal
*/
CGoalSeek.prototype.setFirstChangingValue = function(sChangingVal) {
this.nFirstChangingVal = sChangingVal ? Number(sChangingVal) : null;
};
/**
* Returns a value of changing cell.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getChangingValue = function() {
return this.nChangingVal
};
/**
* Sets a value of changing cell.
* @memberof CGoalSeek
* @param {number} nChangingVal
*/
CGoalSeek.prototype.setChangingValue = function(nChangingVal) {
this.nChangingVal = nChangingVal
};
/**
* Returns previous value of changing cell.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getPrevValue = function() {
return this.nPrevValue;
};
/**
* Sets previous value of changing cell.
* @memberof CGoalSeek
* @param {number} nPrevValue
*/
CGoalSeek.prototype.setPrevValue = function(nPrevValue) {
this.nPrevValue = nPrevValue;
};
/**
* Returns a flag who recognizes should be use compare reverse (when result of calculation formula is less than expected) or not.
* @memberof CGoalSeek
* @returns {boolean}
*/
CGoalSeek.prototype.getReverseCompare = function() {
return this.bReverseCompare;
};
/**
* Initializes a flag who recognizes should be use compare reverse (when result of calculation formula is less than expected) or not.
* @memberof CGoalSeek
*/
CGoalSeek.prototype.initReverseCompare = function() {
const oChangingCell = this.getChangingCell();
let nFirstFormulaResult = null;
let nFirstChangingVal = this.getFirstChangingValue() ? Number(this.getFirstChangingValue()) : 0;
this.bReverseCompare = false;
nFirstFormulaResult = this.calculateFormula(nFirstChangingVal, oChangingCell);
if (nFirstFormulaResult > this.getExpectedVal()) {
this.bReverseCompare = true;
}
};
/**
* Returns a flag who recognizes goal seek is already using Ridder method or not.
* @memberof CGoalSeek
* @returns {boolean}
*/
CGoalSeek.prototype.getEnabledRidder = function() {
return this.bEnabledRidder
};
/**
* Sets a flag who recognizes goal seek is already using Ridder method or not.
* @memberof CGoalSeek
* @param {boolean} bEnabledRidder
*/
CGoalSeek.prototype.setEnabledRidder = function(bEnabledRidder) {
this.bEnabledRidder = bEnabledRidder
};
/**
* Returns a lower border value for Ridder algorithm.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getLowBorder = function() {
return this.nLow;
};
/**
* Sets a lower border value for Ridder algorithm.
* @memberof CGoalSeek
* @param {number} nLow
*/
CGoalSeek.prototype.setLowBorder = function(nLow) {
this.nLow = nLow;
};
/**
* Returns an upper border value for Ridder algorithm.
* @memberof CGoalSeek
* @returns {number}
*/
CGoalSeek.prototype.getHighBorder = function() {
return this.nHigh;
};
/**
* Sets an upper border value for Ridder algorithm.
* @memberof CGoalSeek
* @param {number} nHigh
*/
CGoalSeek.prototype.setHighBorder = function(nHigh) {
this.nHigh = nHigh;
};
/**
* Returns error type
* @memberof CGoalSeek
* @param {AscCommonExcel.Worksheet} ws - checked sheet.
* @param {Asc.Range} range - checked range.
* @param {Asc.c_oAscSelectionDialogType} type - dialog type.
* @returns {Asc.c_oAscError}
*/
CGoalSeek.prototype.isValidDataRef = function(ws, range, type) {
let res = Asc.c_oAscError.ID.No;
if (range === null) {
//error text: the formula is missing a range...
return Asc.c_oAscError.ID.DataRangeError;
}
if (!range.isOneCell()) {
//error text: reference must be to a single cell...
//TODO check def names
res = Asc.c_oAscError.ID.MustSingleCell;
}
switch (type) {
case Asc.c_oAscSelectionDialogType.GoalSeek_Cell: {
//check formula contains
let isFormula = false;
let isNumberResult = true;
//MustFormulaResultNumber
ws && ws._getCellNoEmpty(range.r1, range.c1, function (cell) {
if (cell && cell.isFormula()) {
isFormula = true;
if (cell.number == null) {
isNumberResult = false;
}
}
});
if (!isFormula) {
res = Asc.c_oAscError.ID.MustContainFormula;
} else if (!isNumberResult) {
res = Asc.c_oAscError.ID.MustFormulaResultNumber;
}
break;
}
case Asc.c_oAscSelectionDialogType.GoalSeek_ChangingCell: {
let isValue = true;
ws && ws._getCellNoEmpty(range.r1, range.c1, function (cell) {
if (cell && cell.isFormula()) {
isValue = false;
}
});
if (!isValue) {
res = Asc.c_oAscError.ID.MustContainValue;
}
break;
}
}
return res;
};
/**
* Pauses a goal seek calculation.
* @memberof CGoalSeek
*/
CGoalSeek.prototype.pause = function() {
this.setIsPause(true);
};
/**
* Resumes a goal seek calculation.
* @memberof CGoalSeek
*/
CGoalSeek.prototype.resume = function() {
let oGoalSeek = this;
this.setIsPause(false);
this.setIntervalId(setInterval(function() {
let bIsFinish = oGoalSeek.calculate();
if (bIsFinish) {
clearInterval(oGoalSeek.getIntervalId());
}
}, this.getDelay()));
};
/**
* Resumes goal calculation by one step than pause it again.
* @memberof CGoalSeek
*/
CGoalSeek.prototype.step = function() {
let oGoalSeek = this;
this.setIsPause(false);
this.setIsSingleStep(true);
this.setIntervalId(setInterval(function() {
let bIsFinish = oGoalSeek.calculate();
if (bIsFinish) {
clearInterval(oGoalSeek.getIntervalId());
}
}, this.getDelay()));
};
// Solver
// Classes for interact with UI.
/**
* Class representing object with input data from dialogue window of Solver tool.
* @constructor
* @returns {asc_CSolverParams}
*/
function asc_CSolverParams () {
this.sObjectiveFunction = null;
this.sChangingCells = null;
this.nOptimizeResultTo = c_oAscOptimizeTo.max;
this.sValueOf = '0';
this.aConstraints = new Map();
this.bVariablesNonNegative = true;
this.nSolvingMethod = c_oAscSolvingMethod.simplexLP;
this.oOptions = new asc_COptions();
this.oDefNameToAttribute = {
'solver_opt': 'sObjectiveFunction',
'solver_typ': 'nOptimizeResultTo',
'solver_val': 'sValueOf',
'solver_adj': 'sChangingCells',
'solver_neg': 'bVariablesNonNegative',
'solver_eng': 'nSolvingMethod'
};
return this;
}
/**
* Returns value of "Set Objective" parameter.
* @memberof asc_CSolverParams
* @returns {string}
*/
asc_CSolverParams.prototype.getObjectiveFunction = function () {
return this.sObjectiveFunction;
};
/**
* Sets value of "Set Objective" parameter.
* @memberof asc_CSolverParams
* @param {string|null} objectiveFunction
*/
asc_CSolverParams.prototype.setObjectiveFunction = function (objectiveFunction) {
this.sObjectiveFunction = objectiveFunction;
};
/**
* Returns value of "By Changing Variable Cells" parameters.
* @memberof asc_CSolverParams
* @returns {string}
*/
asc_CSolverParams.prototype.getChangingCells = function () {
return this.sChangingCells;
};
/**
* Sets value of "By Changing Variable Cells" parameters.
* @param {string|null} changingCells
*/
asc_CSolverParams.prototype.setChangingCells = function (changingCells) {
this.sChangingCells = changingCells;
};
/**
* Returns value of "To" parameter.
* @memberof asc_CSolverParams
* @returns {c_oAscOptimizeTo}
*/
asc_CSolverParams.prototype.getOptimizeResultTo = function () {
return this.nOptimizeResultTo;
};
/**
* Sets value of "To" parameter.
* @memberof asc_CSolverParams
* @param {c_oAscOptimizeTo} optimizeResultTo
*/
asc_CSolverParams.prototype.setOptimizeResultTo = function (optimizeResultTo) {
this.nOptimizeResultTo = optimizeResultTo;
};
/**
* Returns value of "Value of" input field.
* @memberof asc_CSolverParams
* @param {string} sValueOf
*/
asc_CSolverParams.prototype.setValueOf = function (sValueOf) {
this.sValueOf = sValueOf;
};
/**
* Sets value of "Value of" input field.
* @memberof {asc_CSolverParams}
* @returns {string}
*/
asc_CSolverParams.prototype.getValueOf = function () {
return this.sValueOf;
};
/**
* Returns value of "Subject to the Constraints" parameter.
* @memberof asc_CSolverParams
* @returns {Map}
*/
asc_CSolverParams.prototype.getConstraints = function () {
return this.aConstraints;
};
/**
* Adds the constraint in "Subject to the Constraints" parameter.
* @memberof asc_CSolverParams
* @param {number} index - key of constraint in Map object. Starts with 1 ... N.
* @param {{cellRef:string, operator:c_oAscOperator, constraint:string}} constraint
*/
asc_CSolverParams.prototype.addConstraint = function (index, constraint) {
this.aConstraints.set(index, constraint);
};
/**
* Edits the chosen constraint in "Subject to the Constraints" parameter.
* @memberof asc_CSolverParams
* @param {number} index - index of chosen constraint
* @param {{cellRef:string, operator:c_oAscOperator, constraint:string}} constraint
*/
asc_CSolverParams.prototype.editConstraint = function (index, constraint) {
this.aConstraints.set(index, constraint);
};
/**
* Removes the chosen constraint in "Subject to the Constraints" parameter.
* @memberof asc_CSolverParams
* @param {number} index
*/
asc_CSolverParams.prototype.removeConstraint = function (index) {
this.aConstraints.delete(index);
};
/**
* Returns value of "Make Unconstrained Variables Non-negative" parameter.
* @memberof asc_CSolverParams
* @returns {boolean}
*/
asc_CSolverParams.prototype.getVariablesNonNegative = function () {
return this.bVariablesNonNegative;
};
/**
* Sets value of "Make Unconstrained Variables Non-negative" parameter.
* @memberof asc_CSolverParams
* @param {boolean} variablesNonNegative
*/
asc_CSolverParams.prototype.setVariablesNonNegative = function (variablesNonNegative) {
this.bVariablesNonNegative = variablesNonNegative;
};
/**
* Returns value of "Select a Solving Method" parameter.
* @memberof asc_CSolverParams
* @returns {c_oAscSolvingMethod}
*/
asc_CSolverParams.prototype.getSolvingMethod = function () {
return this.nSolvingMethod;
};
/**
* Sets value of "Select a Solving Method" parameter.
* @memberof asc_CSolverParams
* @param {c_oAscSolvingMethod} solvingMethod
*/
asc_CSolverParams.prototype.setSolvingMethod = function (solvingMethod) {
this.nSolvingMethod = solvingMethod;
};
/**
* Returns the options.
* @memberof asc_CSolverParams
* @returns {asc_COptions}
*/
asc_CSolverParams.prototype.getOptions = function () {
return this.oOptions;
};
/**
* Resets all params and options to default value.
* @memberof asc_CSolverParams
*/
asc_CSolverParams.prototype.resetAll = function () {
this.setObjectiveFunction(null);
this.setChangingCells(null);
this.setOptimizeResultTo(c_oAscOptimizeTo.max);
this.setValueOf('0');
this.getConstraints().clear();
this.setVariablesNonNegative(true);
this.getOptions().resetAll();
};
/**
* Returns object which connecting def name to attribute of class.
* @memberof asc_CSolverParams
* @returns {{
* solver_opt: string, solver_typ: string, solver_val: string,
* solver_adj: string, solver_neg: string, solver_eng: string
* }}
*/
asc_CSolverParams.prototype.getDefNameToAttribute = function() {
return this.oDefNameToAttribute;
};
/**
* Returns array of hidden defined names.
* @param {DependencyGraph} oDependencyFormulas
* @returns {asc_CDefName[]}
*/
function getHiddenDefinedNamesWS (oDependencyFormulas) {
const oActiveWS = oDependencyFormulas.wb.getActiveWs();
const aHiddenDefNames = [];
oDependencyFormulas._foreachDefNameSheet(oActiveWS.getId(), function(oDefName) {
if (oDefName.hidden) {
aHiddenDefNames.push(oDefName.getAscCDefName(true));
}
});
return aHiddenDefNames;
}
/**
* Adds Def names ranges according to the values of the Solver params and options.
* @memberof asc_CSolverParams
* @param {Workbook} oWbModel
*/
asc_CSolverParams.prototype.createDefNames = function(oWbModel) {
if (!oWbModel) {
return;
}
function fillDefName(sDefName, ref) {
let oOldDefName = null;
if (mSolverDefNames.size) {
oOldDefName = mSolverDefNames.get(sDefName);
}
if (ref === null) {
if (oOldDefName) {
const sSheetId = this.getSheetIdByIndex(oOldDefName.LocalSheetId);
oWbModel.dependencyFormulas._removeDefName(sSheetId, oOldDefName.Name, null);
}
return;
}
if (typeof ref === 'boolean') {
ref = ref ? 1 : 2;
}
let oNewDefName = new Asc.asc_CDefName(sDefName, ref, nWsId, undefined, true, undefined, undefined, true);
oWbModel.editDefinesNames(oOldDefName, oNewDefName);
}
const oDependencyFormulas = oWbModel.dependencyFormulas;
const aWsDefNames = getHiddenDefinedNamesWS(oDependencyFormulas);
const oDefNameToAttribute = this.getDefNameToAttribute();
const oOptions = this.getOptions();
const oOptionsDefNameToAttribute = oOptions.getDefNameToAttribute();
const mSolverDefNames = new Map();
const nWsId = oWbModel.getActive();
// Finds existed defined names for the solver.
if (aWsDefNames.length) {
aWsDefNames.forEach(function(oDefName) {
if (~oDefName.Name.indexOf('solver_')) {
mSolverDefNames.set(oDefName.Name, oDefName);
}
});
}
// Fills constant define names
fillDefName('solver_ver', 3);
fillDefName('solver_est', 1);
fillDefName('solver_nwt', 1)
// Fills Solver parameters
for (let sDefName in oDefNameToAttribute) {
fillDefName(sDefName, this[oDefNameToAttribute[sDefName]]);
}
// Fills constraints
const mConstraints = this.getConstraints();
let nIndex = 1;
if (!mConstraints.size) {
return;
}
fillDefName('solver_num', mConstraints.size);
mConstraints.forEach(function(constraint) {
fillDefName('solver_lhs' + nIndex, constraint.cellRef);
fillDefName('solver_rhs' + nIndex, constraint.constraint);
fillDefName('solver_rel' + nIndex, constraint.operator);
nIndex++;
});
// Fills options
for (let sDefName in oOptionsDefNameToAttribute) {
fillDefName(sDefName, oOptions[oOptionsDefNameToAttribute[sDefName]]);
}
};
/**
* Gets def names ranges for filling fields of solver params and options.
* @memberof asc_CSolverParams
* @param {Workbook} oWbModel
*/
asc_CSolverParams.prototype.getDefNames = function(oWbModel) {
if (!oWbModel) {
return;
}
function fillAttribute (oAttribute, sDefName) {
let oDefName = mSolverDefNames.get(sDefName);
if (oDefName) {
oAttribute = typeof oAttribute === 'boolean' ? oDefName.Ref === 1 : oDefName.Ref;
}
return oAttribute;
}
const oDependencyFormulas = oWbModel.dependencyFormulas;
const aWsDefNames = getHiddenDefinedNamesWS(oDependencyFormulas);
const oDefNameToAttribute = this.getDefNameToAttribute();
const oOptions = this.getOptions();
const oOptionsDefNameToAttribute = oOptions.getDefNameToAttribute();
const mSolverDefNames = new Map();
if (!aWsDefNames.length) {
return;
}
aWsDefNames.forEach(function(oDefName) {
if (~oDefName.Name.indexOf("solver_")) {
mSolverDefNames.set(oDefName.Name, oDefName);
}
});
if (!mSolverDefNames.size) {
return;
}
// Fills solver params
for (let sDefName in oDefNameToAttribute) {
this[oDefNameToAttribute[sDefName]] = fillAttribute(this[oDefNameToAttribute[sDefName]], sDefName);
}
// Fills constraints.
const nConstraintsSize = mSolverDefNames.get('solver_num') && mSolverDefNames.get('solver_num').Ref;
if (nConstraintsSize === undefined) {
return;
}
for (let i = 1; i <= nConstraintsSize; i++) {
/** @type {{cellRef:string, operator: c_oAscOperator, constraint:string}} */
const oConstraint = {};
oConstraint.cellRef = fillAttribute(oConstraint.cellRef, 'solver_lhs' + i);
oConstraint.operator = fillAttribute(oConstraint.operator, 'solver_rel' + i);
oConstraint.constraint = fillAttribute(oConstraint.constraint, 'solver_rhs' + i);
if (oConstraint.cellRef && oConstraint.operator && oConstraint.constraint) {
this.addConstraint(i, oConstraint);
}
}
// Fills options
for (let sDefName in oOptionsDefNameToAttribute) {
oOptions[oOptionsDefNameToAttribute[sDefName]] = fillAttribute(oOptions[oOptionsDefNameToAttribute[sDefName]], sDefName);
}
};
/**
* Class representing options for calculating.
* @constructor
*/
function asc_COptions () {
const sNumberDecimalSeparator = AscCommon.g_oDefaultCultureInfo.NumberDecimalSeparator;
// All methods
this.sConstraintPrecision = '0.000001'.replace('.', sNumberDecimalSeparator);
this.bAutomaticScaling = false;
this.bShowIterResults = false;
this.bIgnoreIntConstriants = false;
this.sIntOptimal = '1';
this.sMaxTime = '2147483647';
this.sIterations = '2147483647';
this.sMaxSubproblems = '2147483647';
this.sMaxFeasibleSolution = '2147483647';
// GRG Nonlinear and Evolutionary
this.sConvergence = '0.0001'.replace('.', sNumberDecimalSeparator);
this.nDerivatives = c_oAscDerivativeType.forward;
this.bMultistart = false; // ?
this.sPopulationSize = '100';
this.sRandomSeed = '0';
this.bRequireBounds = false;
this.sMutationRate = '0.075'.replace('.', sNumberDecimalSeparator);
this.sEvoMaxTime = '30';
this.oDefNameToAttribute = {
'solver_pre': 'sConstraintPrecision',
'solver_scl': 'bAutomaticScaling',
'solver_sho': 'bShowIterResults',
'solver_rlx': 'bIgnoreIntConstriants',
'solver_tol': 'sIntOptimal',
'solver_tim': 'sMaxTime',
'solver_itr': 'sIterations',
'solver_nod': 'sMaxSubproblems',
'solver_mip': 'sMaxFeasibleSolution',
'solver_cvg': 'sConvergence',
'solver_drv': 'nDerivatives',
'solver_msl': 'bMultistart',
'solver_ssz': 'sPopulationSize',
'solver_rsd': 'sRandomSeed',
'solver_rbv': 'bRequireBounds',
'solver_mrt': 'sMutationRate',
'solver_mni': 'sEvoMaxTime'
};
}
/**
* Returns value of "Constraint Precision" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getConstraintPrecision = function() {
return this.sConstraintPrecision;
};
/**
* Returns value of "Use automatic Scaling" parameter.
* @memberof asc_COptions
* @returns {boolean}
*/
asc_COptions.prototype.getAutomaticScaling = function() {
return this.bAutomaticScaling;
};
/**
* Returns value of "Show Iteration Results" parameter.
* @memberof asc_COptions
* @returns {boolean}
*/
asc_COptions.prototype.getShowIterResults = function() {
return this.bShowIterResults;
};
/**
* Returns value of "Ignore Integer Constraints" parameter.
* @memberof asc_COptions
* @returns {boolean}
*/
asc_COptions.prototype.getIgnoreIntConstraints = function() {
return this.bIgnoreIntConstriants;
};
/**
* Returns value of "Integer Optimality (%)" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getIntOptimal = function () {
return this.sIntOptimal;
};
/**
* Returns value of "Max Time (Seconds)" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getMaxTime = function () {
return this.sMaxTime;
};
/**
* Returns value of "Iterations"
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getIterations = function () {
return this.sIterations;
};
/**
* Returns value of "Max Subproblems" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getMaxSubproblems = function () {
return this.sMaxSubproblems;
};
/**
* Returns value of "Max Feasible Solutions" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getMaxFeasibleSolution = function () {
return this.sMaxFeasibleSolution;
};
/**
* Returns value of "Convergence" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getConvergence = function () {
return this.sConvergence;
};
/**
* Returns value of "Derivatives" parameter.
* @memberof asc_COptions
* @returns {c_oAscDerivativeType}
*/
asc_COptions.prototype.getDerivatives = function () {
return this.nDerivatives;
};
/**
* Returns value of "Use multistart" parameter.
* @memberof asc_COptions
* @returns {boolean}
*/
asc_COptions.prototype.getMultistart = function () {
return this.bMultistart;
};
/**
* Returns value of "Population Size" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getPopulationSize = function () {
return this.sPopulationSize;
};
/**
* Returns value of "Random seed" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getRandomSeed = function () {
return this.sRandomSeed;
};
/**
* Returns value of "Require Bounds on Variables" parameter.
* @memberof asc_COptions
* @returns {boolean}
*/
asc_COptions.prototype.getRequireBounds = function () {
return this.bRequireBounds;
};
/**
* Returns value of "Mutation Rate" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getMutationRate = function () {
return this.sMutationRate;
};
/**
* Returns value of "Maximum Time without improvement" parameter.
* @memberof asc_COptions
* @returns {string}
*/
asc_COptions.prototype.getEvoMaxTime = function () {
return this.sEvoMaxTime;
};
/**
* Returns object which connecting def name to attribute of class.
* @memberof asc_COptions
* @returns {{
* solver_pre: string, solver_scl: string, solver_sho: string, solver_rlx: string, solver_tol: string,
* solver_tim: string,solver_itr: string, solver_nod: string,solver_mip: string, solver_cvg: string,
* solver_drv: string, solver_msl: string, solver_ssz: string, solver_rsd: string, solver_rbv: string,
* solver_mrt: string, solver_mni: string
* }}
*/
asc_COptions.prototype.getDefNameToAttribute = function() {
return this.oDefNameToAttribute;
};
/**
* Sets value of "Constraint Precision" parameter.
* @memberof asc_COptions
* @param {string} constraintPrecision
*/
asc_COptions.prototype.setConstraintPrecision = function (constraintPrecision) {
this.sConstraintPrecision = constraintPrecision;
};
/**
* Sets value of "Use automatic Scaling" parameter.
* @memberof asc_COptions
* @param {boolean} automaticScaling
*/
asc_COptions.prototype.setAutomaticScaling = function (automaticScaling) {
this.bAutomaticScaling = automaticScaling;
};
/**
* Sets value of "Show Iteration Results" parameter.
* @memberof asc_COptions
* @param {boolean} showIterResults
*/
asc_COptions.prototype.setShowIterResults = function (showIterResults) {
this.bShowIterResults = showIterResults;
};
/**
* Sets value of "Ignore Integer Constraints" parameter.
* @memberof asc_COptions
* @param {boolean} ignoreIntConstraints
*/
asc_COptions.prototype.setIgnoreIntConstraints = function (ignoreIntConstraints) {
this.bIgnoreIntConstriants = ignoreIntConstraints;
};
/**
* Sets value of "Integer Optimality (%)" parameter.
* @memberof asc_COptions
* @param {string} intOptimality
*/
asc_COptions.prototype.setIntOptimal = function (intOptimality) {
this.sIntOptimal = intOptimality;
};
/**
* Sets value of "Max Time (Seconds)" parameter.
* @memberof asc_COptions
* @param {string} maxTime
*/
asc_COptions.prototype.setMaxTime = function (maxTime) {
this.sMaxTime = maxTime;
};
/**
* Sets value of "Iterations" parameter
* @memberof asc_COptions
* @param {string} iterations
*/
asc_COptions.prototype.setIterations = function (iterations) {
this.sIterations = iterations;
};
/**
* Sets value of "Max Subproblems" parameter.
* @memberof asc_COptions
* @param {string} maxSubproblems
*/
asc_COptions.prototype.setMaxSubproblems = function (maxSubproblems) {
this.sMaxSubproblems = maxSubproblems;
};
/**
* Sets value of "Max Feasible Solution" parameter.
* @memberof asc_COptions
* @param {string} maxFeasibleSolution
*/
asc_COptions.prototype.setMaxFeasibleSolution = function (maxFeasibleSolution) {
this.sMaxFeasibleSolution = maxFeasibleSolution;
};
/**
* Sets value of "Convergence" parameter.
* @memberof asc_COptions
* @param {string} convergence
*/
asc_COptions.prototype.setConvergence = function (convergence) {
this.sConvergence = convergence;
};
/**
* Sets value of "Derivatives" parameter.
* @memberof asc_COptions
* @param {c_oAscDerivativeType} derivatives
*/
asc_COptions.prototype.setDerivatives = function (derivatives) {
this.nDerivatives = derivatives;
};
/**
* Sets value of "Use Multistart" parameter.
* @memberof asc_COptions
* @param {boolean} multistart
*/
asc_COptions.prototype.setMultistart = function (multistart) {
this.bMultistart = multistart;
};
/**
* Sets value of "Population Size" parameter.
* @memberof asc_COptions
* @param {string} populationSize
*/
asc_COptions.prototype.setPopulationSize = function (populationSize) {
this.sPopulationSize = populationSize;
};
/**
* Sets value of "Random Seed" parameter.
* @memberof asc_COptions
* @param {string} randomSeed
*/
asc_COptions.prototype.setRandomSeed = function (randomSeed) {
this.sRandomSeed = randomSeed;
};
/**
* Sets value of "Require Bounds on Variables" parameter.
* @memberof asc_COptions
* @param {boolean} requireBounds
*/
asc_COptions.prototype.setRequireBounds = function (requireBounds) {
this.bRequireBounds = requireBounds;
};
/**
* Sets value of "Mutation Rate" parameter.
* @memberof asc_COptions
* @param {string} mutationRate
*/
asc_COptions.prototype.setMutationRate = function (mutationRate) {
this.sMutationRate = mutationRate;
};
/**
* Sets value of "Maximum Time without improvement" parameter.
* @memberof asc_COptions
* @param {string} evoMaxTime
*/
asc_COptions.prototype.setEvoMaxTime = function (evoMaxTime) {
this.sEvoMaxTime = evoMaxTime;
};
/**
* Resets all options to default value.
* @memberof asc_COptions
*/
asc_COptions.prototype.resetAll = function() {
this.setConstraintPrecision('0.000001');
this.setAutomaticScaling(false);
this.setShowIterResults(false);
this.setIgnoreIntConstraints(false);
this.setIntOptimal('1');
this.setMaxTime('2147483647');
this.setIterations('2147483647');
this.setMaxSubproblems('2147483647');
this.setMaxFeasibleSolution('2147483647');
this.setConvergence('0.0001');
this.setDerivatives(c_oAscDerivativeType.forward);
this.setMultistart(false);
this.setPopulationSize('100');
this.setRandomSeed('0');
this.setRequireBounds(false);
this.setMutationRate('0.075');
this.setEvoMaxTime('30');
};
/**
* Class representing Solver Results dialogue window data.
* @constructor
* @returns {asc_CSolverResults}
*/
function asc_CSolverResults () {
this.sStatus = null;
this.sDescription = null;
this.nAction = c_oAscSolverResult.keepSolverSolution;
this.bReturnToSolverParams = false;
return this;
}
/**
* Returns status of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @returns {string}
*/
asc_CSolverResults.prototype.getStatus = function() {
return this.sStatus;
};
/**
* Returns status description of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @returns {string}
*/
asc_CSolverResults.prototype.getDescription = function() {
return this.sDescription;
};
/**
* Returns current action of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @returns {c_oAscSolverResult}
*/
asc_CSolverResults.prototype.getAction = function() {
return this.nAction;
};
/**
* Returns state of "Return to solver params" checkbox of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @returns {boolean}
*/
asc_CSolverResults.prototype.getReturnToSolverParams = function() {
return this.bReturnToSolverParams;
};
/**
* Sets status of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @param {string} sStatus
*/
asc_CSolverResults.prototype.setStatus = function(sStatus) {
this.sStatus = sStatus;
};
/**
* Sets status description of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @param {string} sDescription
*/
asc_CSolverResults.prototype.setDescription = function(sDescription) {
this.sDescription = sDescription;
};
/**
* Sets current action of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @param {c_oAscSolverResult} nAction
*/
asc_CSolverResults.prototype.setAction = function(nAction) {
this.nAction = nAction;
};
/**
* Sets state of "Return to solver params" checkbox of Solver Results dialogue window.
* @memberof asc_CSolverResults
* @param {boolean} bReturnToSolverParams
*/
asc_CSolverResults.prototype.setReturnToSolverParams = function(bReturnToSolverParams) {
this.bReturnToSolverParams = bReturnToSolverParams;
};
// Classes and functions for main logic of Solver feature
/**
* Returns worksheet that has selected cell(s) from parameters.
* @param {string} sRef - selected cell(s) reference from parameters
* @param {Worksheet} oWs
* @returns {Worksheet}
*/
function actualWsByRef (sRef, oWs) {
let oActualWs = oWs;
if (~sRef.indexOf("!")) {
const sSheetName = sRef.split("!")[0].replace(/'/g, "");
if (sSheetName !== oActualWs.getName()) {
oActualWs = oWs.workbook.getWorksheetByName(sSheetName);
}
}
return oActualWs;
}
/**
* Converts absolute reference containing the worksheet name to reference with only selected cell(s) name.
* @param {string} sRef - selected cell(s) reference from parameters
* @returns {string}
*/
function convertToAbsoluteRef (sRef) {
let sConvertedRef = sRef;
if (~sRef.indexOf("!")) {
sConvertedRef = sRef.split("!")[1];
}
return sConvertedRef;
}
/**
* Class representing a constraints option for Solver.
* @param {Range|Cell} oCell
* @param {c_oAscOperator} nOperator
* @param {Range|number|string} constraint
* @constructor
*/
function CConstraint (oCell, nOperator, constraint) {
this.oCell = oCell;
this.nOperator = nOperator;
this.constraint = constraint;
}
/**
* Returns cell reference.
* @memberof CConstraint
* @returns {Range|Cell}
*/
CConstraint.prototype.getCell = function() {
return this.oCell;
};
/**
* Returns comparison operator.
* @memberof CConstraint
* @returns {c_oAscOperator}
*/
CConstraint.prototype.getOperator = function() {
return this.nOperator;
};
/**
* Returns constraint. Element that comparisons with reference cell.
* @memberof CConstraint
* @returns {Range|number|string}
*/
CConstraint.prototype.getConstraint = function() {
return this.constraint;
};
/**
* Class representing logic of solving linear programming by Simplex method.
* @param {CSolver} oModel
* @constructor
*/
function CSimplexTableau (oModel) {
this.oModel = oModel;
this.aMatrix = null;
this.nWidth = 0;
this.nHeight = 0;
this.aVariablesIndexes = null;
this.aConstraintsIndexes = null;
this.nObjectiveRowIndex = 0;
this.nRhsColumn = 0;
this.oVarIndexByCellName = null;
this.oUnrestrictedVars = null;
// Solution attributes
this.nLastColumnId = null;
this.nLastRowId = null;
this.aVarIndexesCycle = [];
this.mRepeatedVars = new Map();
this.bFeasible = false;
this.bSolutionIsFound = false;
this.aVarIndexByRow = null;
this.aVarIndexByCol = null;
this.aRowByVarIndex = null;
this.aColByVarIndex = null;
this.nPrecision = 1e-8;
this.nLastElementIndex = 0;
this.oVariables = null;
this.nVars = 0;
this.bBounded = true;
this.nUnboundedVarIndex = null;
this.nBranchAndCutIters = 0;
}
/**
* Initializes a start data for calculating logic.
* @memberof CSimplexTableau
*/
CSimplexTableau.prototype.init = function () {
const oModel = this.getModel();
const oOptions = oModel.getOptions();
const oChangingCells = oModel.getChangingCell();
const oBboxChangingCells = oChangingCells.bbox;
const nTotalCountVariables = oBboxChangingCells.getWidth() * oBboxChangingCells.getHeight();
const aConstraints = oModel.getConstraints();
const aSimplexConstraints = this.getSimplexConstraints(aConstraints);
const nTotalCountConstraints = aSimplexConstraints.length;
const nOptionPrecision = Number(oOptions.getConstraintPrecision().replace(/,/g, "."));
if (!isNaN(nOptionPrecision)) {
this.setPrecision(nOptionPrecision);
}
this.setVariables(oChangingCells);
// Init width and height of matrix.
this.setWidth(nTotalCountVariables + 1);
this.setHeight(nTotalCountConstraints + 1);
// Init indexes of variables and constraints
this.setConstraintsIndexes(this.fillConstraintsIndexes(nTotalCountConstraints));
this.setVariablesIndexes(this.fillVariablesIndexes(nTotalCountVariables, nTotalCountConstraints));
// Init matrix
const aMatrix = this.createMatrix();
const nHeight = this.getHeight();
const nWidth = this.getWidth();
this.setVarIndexByRow(new Array(nHeight));
this.setVarIndexByCol(new Array(nWidth));
this.addVarIndexByRowElem(-1, 0);
this.addVarIndexByColElem(-1, 0);
this.setVarsCount(nWidth + nHeight - 2);
this.setRowByVarIndex(new Array(this.getVarsCount()));
this.setColByVarIndex(new Array(this.getVarsCount()));
this.setLastElementIndex(this.getVarsCount());
// Fills matrix
this.fillMatrix(aMatrix, aSimplexConstraints);
// Init last index row and column of matrix
this.setLastColumnIndex(nWidth - 1);
this.setLastRowIndex(nHeight - 1);
};
/**
* Calculates solution using Simplex LP method.
* @memberof CSimplexTableau
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
* @see {@link https://en.wikibooks.org/wiki/Operations_Research/The_Simplex_Method#The_Simplex_method|Theory material}
*/
CSimplexTableau.prototype.calculate = function () {
let bStopCalculating = false;
if (!this.getFeasible() && !bStopCalculating) {
bStopCalculating = this.phase1();
}
if (this.getFeasible() && !bStopCalculating) {
// current solution is feasible
bStopCalculating = this.phase2();
}
return bStopCalculating;
};
/**
* Obtains a Basic Feasible Solution (BFS)
* Convert a non-standard form tableau to a standard form tableau by eliminating
* all negative values on the Right Hand Side (RHS)
* This results in a Basic Feasible Solution (BFS)
* @memberof CSimplexTableau
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSimplexTableau.prototype.phase1 = function () {
const oModel = this.getModel();
const aMatrix = this.getMatrix();
const nRhsColumnId = this.getRhsColumn();
const nLastColumnId = this.getLastColumnIndex();
const nLastRowId = this.getLastRowIndex();
const aVarIndexesCycle = this.getVarIndexesCycle();
let nLeavingRowIndex = 0;
let nRhsValue = -this.getPrecision();
// Step 1: Find pivot row. Selecting leaving variable (feasibility condition). Basic variable with most negative value.
for (let i = 1; i <= nLastRowId; i++) {
const nValue = aMatrix[i][nRhsColumnId];
if (nValue < nRhsValue) {
nRhsValue = nValue;
nLeavingRowIndex = i;
}
}
// If the leaving row isn't found, for 1st phase, found a feasible solution. Finished 1st phase.
if (nLeavingRowIndex === 0) {
this.setFeasible(true);
return false;
}
// Step 2: Find pivot column. Selecting entering variable.
const aObjectiveRow = aMatrix[this.getObjectiveRowIndex()];
const aLeavingRow = aMatrix[nLeavingRowIndex];
let nEnteringColumnIndex = 0;
let nMaxQuotient = -Infinity;
for (let i = 1; i <= nLastColumnId; i++) {
const nCoefficient = aLeavingRow[i];
if (nCoefficient < -this.getPrecision()) {
const nQuotient = -aObjectiveRow[i] / nCoefficient;
if (nMaxQuotient < nQuotient) {
nMaxQuotient = nQuotient;
nEnteringColumnIndex = i;
}
}
}
// If the entering row isn't found, feasible solution isn't found too.
if (nEnteringColumnIndex === 0) {
this.setFeasible(false);
//TODO call api to show error about solution isn't found
return true;
}
// Check for cycles
const aVarIndexByRow = this.getVarIndexByRow();
const aVarIndexByCol = this.getVarIndexByCol();
aVarIndexesCycle.push([aVarIndexByRow[nLeavingRowIndex], aVarIndexByCol[nEnteringColumnIndex]]);
if (this.checkForCycles(aVarIndexesCycle)) {
this.setFeasible(false);
this.clearVarIndexesCycle();
this.getRepeatedVars().clear();
//TODO call api to show error about solution isn't found
return true
}
this.pivot(nLeavingRowIndex, nEnteringColumnIndex);
oModel.increaseCurrentAttempt();
return false;
};
/**
* Runs simplex on Initial Basic Feasible Solution (BFS)
* Apply simplex to obtain optimal solution used as phase2 of the simplex
* @memberof CSimplexTableau
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSimplexTableau.prototype.phase2 = function () {
const oModel = this.getModel();
const aMatrix = this.getMatrix();
const nRhsColumnId = this.getRhsColumn();
const nLastColumnId = this.getLastColumnIndex();
const nLastRowId = this.getLastRowIndex();
const aVarIndexesCycle = this.getVarIndexesCycle();
const nPrecision = this.getPrecision();
const aObjectiveRow = aMatrix[this.getObjectiveRowIndex()];
let nEnteringColumnIndex = 0;
let nEnteringValue = nPrecision;
let bReducedCostNegative = false;
for (let col = 1; col <= nLastColumnId; col++) {
const nObjectiveCoefValue = aObjectiveRow[col];
if (nObjectiveCoefValue > nEnteringValue) {
nEnteringValue = nObjectiveCoefValue;
nEnteringColumnIndex = col;
bReducedCostNegative = false;
}
}
// If no entering column could be found we're done with phase 2. Solution has been found.
if (nEnteringColumnIndex === 0) {
this.setSolutionIsFound(true);
oModel.increaseCurrentFeasibleCount();
return true;
}
// Selecting leaving variable
let nLeavingRowIndex = 0;
let nMinQuotient = Infinity;
for (let row = 1; row <= nLastRowId; row++) {
const aRow = aMatrix[row];
const nRhsValue = aRow[nRhsColumnId];
const nColValue = aRow[nEnteringColumnIndex];
if (-nPrecision < nColValue && nColValue < nPrecision) {
continue;
}
if (nColValue > 0 && nPrecision > nRhsValue && nRhsValue > -nPrecision) {
nMinQuotient = 0;
nLeavingRowIndex = row;
break;
}
const nQuotient = bReducedCostNegative ? -nRhsValue / nColValue : nRhsValue / nColValue;
if (nQuotient > nPrecision && nMinQuotient > nQuotient) {
nMinQuotient = nQuotient;
nLeavingRowIndex = row;
}
}
if (nMinQuotient === Infinity) {
// Optimal value is -Infinity or solution hasn't been found.
this.setSolutionIsFound(false);
this.setBounded(false);
this.setUnboundVarIndex(this.getVarIndexByCol()[nEnteringColumnIndex]);
//TODO call api to show error about solution isn't found
return true;
}
// Check for cycles
const aVarIndexByRow = this.getVarIndexByRow();
const aVarIndexByCol = this.getVarIndexByCol();
aVarIndexesCycle.push([aVarIndexByRow[nLeavingRowIndex], aVarIndexByCol[nEnteringColumnIndex]]);
if (this.checkForCycles(aVarIndexesCycle)) {
this.setFeasible(false);
this.clearVarIndexesCycle();
this.getRepeatedVars().clear();
//TODO call api to show error about solution isn't found
return true
}
this.pivot(nLeavingRowIndex, nEnteringColumnIndex);
oModel.increaseCurrentAttempt();
return false;
};
/**
* Returns the main model object with the necessary data for solving a task by the Simplex method.
* @memberof CSimplexTableau
* @returns {CSolver}
*/
CSimplexTableau.prototype.getModel = function () {
return this.oModel;
};
/**
* Returns constraint precision.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getPrecision = function () {
return this.nPrecision;
};
/**
* Sets constraint precision.
* @memberof CSimplexTableau
* @param {number} nPrecision
*/
CSimplexTableau.prototype.setPrecision = function (nPrecision) {
this.nPrecision = nPrecision;
};
/**
* Returns variables of model. It's the changing variable cells.
* @memberof CSimplexTableau
* @returns {Range}
*/
CSimplexTableau.prototype.getVariables = function () {
return this.oVariables;
};
/**
* Sets variables of model. It's the changing variable cells.
* @memberof CSimplexTableau
* @param {Range} oVariables
*/
CSimplexTableau.prototype.setVariables = function (oVariables) {
this.oVariables = oVariables;
};
/**
* Returns width of matrix.
* @memberof
* @returns {number}
*/
CSimplexTableau.prototype.getWidth = function () {
return this.nWidth;
};
/**
* Sets width of matrix.
* @memberof
* @param {number} nWidth
*/
CSimplexTableau.prototype.setWidth = function (nWidth) {
this.nWidth = nWidth;
};
/**
* Creates new array with constraints suitable for simplex calculate logic.
* Method extracts ranges of ref cells and values of constraints for making a flat array.
* @memberof CSimplexTableau
* @param {CConstraint[]}aModelConstraints
* @returns {CConstraint[]}
*/
CSimplexTableau.prototype.getSimplexConstraints = function (aModelConstraints) {
/** @type {CConstraint[]} */
const aSimplexConstraints = [];
for (let index = 0, length = aModelConstraints.length; index < length; index++) {
const oConstraint = aModelConstraints[index];
const nOperator = oConstraint.getOperator();
const oRefCellsRange = oConstraint.getCell();
const constraintData = oConstraint.getConstraint();
let nConstraintIter = 0;
let oConstraintBbox, bVertical, oConstraintWs;
if (typeof constraintData === 'string') {
continue;
}
if (typeof constraintData === 'object') {
oConstraintBbox = constraintData.bbox;
bVertical = oConstraintBbox.c1 === oConstraintBbox.c2;
oConstraintWs = constraintData.worksheet;
}
oRefCellsRange._foreachNoEmpty(function (oRefCell) {
/** @type {Cell} */
const oCell = oRefCell.clone();
let oNewConstraint, nConstraintVal;
if (oRefCell.isFormula()) {
oCell.formulaParsed = oRefCell.getFormulaParsed().clone();
}
if (typeof constraintData === 'object') {
const nRowConstraint = bVertical ? oConstraintBbox.r1 + nConstraintIter : oConstraintBbox.r1;
const nColConstraint = bVertical ? oConstraintBbox.c1 : oConstraintBbox.c1 + nConstraintIter;
if (nRowConstraint <= oConstraintBbox.r2 && nColConstraint <= oConstraintBbox.c2) {
oConstraintWs._getCell(nRowConstraint, nColConstraint, function (oConstraintCell) {
nConstraintVal = oConstraintCell.getNumberValue();
});
nConstraintIter++;
}
} else {
nConstraintVal = constraintData;
}
if (nOperator === c_oAscOperator['=']) { // Changes the equal ("=") constraint to 2 constraints - ">=" and "<=".
// Firstly adds a constraint with >= operator, then add <=.
let nNewOperator = c_oAscOperator['>='];
oNewConstraint = new CConstraint(oCell, nNewOperator, nConstraintVal);
aSimplexConstraints.push(oNewConstraint);
nNewOperator = c_oAscOperator['<='];
oNewConstraint = new CConstraint(oCell, nNewOperator, nConstraintVal);
aSimplexConstraints.push(oNewConstraint);
} else {
oNewConstraint = new CConstraint(oCell, nOperator, nConstraintVal);
aSimplexConstraints.push(oNewConstraint);
}
});
}
return aSimplexConstraints;
};
/**
* Returns height of matrix.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getHeight = function () {
return this.nHeight;
};
/**
* Sets height of matrix.
* @memberof CSimplexTableau
* @param {number} nHeight
*/
CSimplexTableau.prototype.setHeight = function (nHeight) {
this.nHeight = nHeight;
};
/**
* Returns array with indexes of constraints.
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getConstraintsIndexes = function () {
return this.aConstraintsIndexes;
};
/**
* Sets array of constraints indexes
* @memberof CSimplexTableau
* @param {number[]} aConstraintsIndexes
*/
CSimplexTableau.prototype.setConstraintsIndexes = function (aConstraintsIndexes) {
this.aConstraintsIndexes = aConstraintsIndexes;
};
/**
* Fills array with indexes of constraints.
* @memberof CSimplexTableau
* @param {number} nTotalCountConstraints
* @returns {number[]}
*/
CSimplexTableau.prototype.fillConstraintsIndexes = function (nTotalCountConstraints) {
const aConstraintsIndexes = [];
for (let i = 0; i < nTotalCountConstraints; i++) {
aConstraintsIndexes.push(i);
}
return aConstraintsIndexes;
};
/**
* Returns array with indexes of variables.
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getVariablesIndexes = function () {
return this.aVariablesIndexes;
};
/**
* Sets array of constraints indexes
* @memberof CSimplexTableau
* @param {number[]} aVariablesIndexes
*/
CSimplexTableau.prototype.setVariablesIndexes = function (aVariablesIndexes) {
this.aVariablesIndexes = aVariablesIndexes;
};
/**
* Fills array with indexes of variables.
* @memberof CSimplexTableau
* @param {number} nTotalCountVariables
* @param {number} nLastIndexElement
* @returns {number[]}
*/
CSimplexTableau.prototype.fillVariablesIndexes = function (nTotalCountVariables, nLastIndexElement) {
const aVariablesIndexes = [];
for (let j = 0; j < nTotalCountVariables; j++) {
aVariablesIndexes.push(nLastIndexElement++);
}
return aVariablesIndexes;
};
/**
* @memberof CSimplexTableau
* @returns {number[][]}
*/
CSimplexTableau.prototype.getMatrix = function () {
return this.aMatrix;
};
/**
* Sets empty matrix that needs to be filled using addRow method.
* @memberof CSimplexTableau
* @param {[]}aMatrix
*/
CSimplexTableau.prototype.setMatrix = function (aMatrix) {
this.aMatrix = aMatrix;
};
/**
* Adds rows for filling matrix.
* @memberof CSimplexTableau
* @param {number[]} aRow
* @param {number} nRowIndex
*/
CSimplexTableau.prototype.addRow = function (aRow, nRowIndex) {
const aMatrix = this.getMatrix();
aMatrix[nRowIndex] = aRow;
};
/**
* Builds an empty matrix.
* @memberof CSimplexTableau
* @returns {number[][]}
*/
CSimplexTableau.prototype.createMatrix = function () {
const nWidth = this.getWidth();
const nHeight = this.getHeight();
const tmpRow = new Array(nWidth);
for (let i = 0, length = nWidth; i < length; i++) {
tmpRow[i] = 0;
}
this.setMatrix(new Array(nHeight));
for (let j = 0, length = nHeight; j < length; j++) {
this.addRow(tmpRow.slice(), j);
}
return this.getMatrix();
};
/**
* Extracts arguments from parserFormula.
* @memberof CSimplexTableau
* @param {[]} aOutStackFormula - arguments of formula
* @param {boolean} bIsEmpty - flag recognizes whether empty arguments are allowed
* @returns {Cell[]}
*/
CSimplexTableau.prototype.getArgsFormula = function (aOutStackFormula, bIsEmpty) {
const aArgsFormula = [];
AscCommonExcel.foreachRefElements(function (oRange) {
oRange._foreachNoEmpty(function (oCell) {
if (oCell.isFormula() || oCell.getNumberValue() || bIsEmpty) {
const oTempCell = oCell.clone();
if (oCell.isFormula()) {
oTempCell.formulaParsed = oCell.getFormulaParsed().clone();
}
aArgsFormula.push(oTempCell);
}
})
}, aOutStackFormula);
return aArgsFormula;
};
/**
* Returns an object that stores the variable index by cell name key.
* @memberof CSimplexTableau
* @returns {{}}
*/
CSimplexTableau.prototype.getVarIndexByCellName = function () {
if (!this.oVarIndexByCellName) {
this.oVarIndexByCellName = {};
}
return this.oVarIndexByCellName;
};
/**
* Fills matrix by variable and constraints data.
* @memberof CSimplexTableau
* @param {number[][]} aMatrix
* @param {CConstraint[]} aConstraints
*/
CSimplexTableau.prototype.fillMatrix = function (aMatrix, aConstraints) {
const oThis = this;
const aConstraintsIndexes = this.getConstraintsIndexes();
const aVariablesIndexes = this.getVariablesIndexes();
const aObjectiveFuncRow = aMatrix[0];
const oModel = this.getModel();
const oVariables = this.getVariables();
const nCoeff = oModel.getOptimizeResultTo() === c_oAscOptimizeTo.min ? -1 : 1;
// Extracts coefficients from objective function
const oObjectiveFunc = oModel.getParsedFormula();
const aArgsObjectiveFunc = this.getArgsFormula(oObjectiveFunc.outStack, false);
// Links variable with variable's index
let nIter = 0;
const oVarIndexByCellName = this.getVarIndexByCellName();
oVariables._foreachNoEmpty(function (oCell) {
oVarIndexByCellName[oCell.getName()] = aVariablesIndexes[nIter];
nIter++;
});
// Fills the matrix's first row coefficients of the objective function
for (let i = 0, length = aArgsObjectiveFunc.length; i < length; i++) {
function getVal (nValue) {
aObjectiveFuncRow[i + 1] = nValue * nCoeff;
oThis.addRowByVarIndexElem(-1, nVarIndex);
oThis.addColByVarIndexElem(i + 1, nVarIndex);
oThis.addVarIndexByColElem(nVarIndex, i + 1);
}
const oArg = aArgsObjectiveFunc[i];
let nVarIndex = aVariablesIndexes[i];
if (oArg.isFormula() && !oArg.getNumberValue()) { // Tries to find the coefficient in linked cells
const aArgsFormula = this.getArgsFormula(oArg.getFormulaParsed().outStack, false);
aArgsFormula.forEach(function (oCell) {
const nValue = oCell.getNumberValue();
if (nValue) {
getVal(nValue);
}
});
} else if (oArg.getNumberValue()) {
getVal(oArg.getNumberValue());
}
}
// Fills remain matrix's rows by constraints
const FIRST_COLUMN_INDEX = 0;
const aColByVarIndex = this.getColByVarIndex();
let nRowIndex = 1;
let coefficient = 1;
for (let j = 0, length = aConstraints.length; j < length; j++) {
function getVariable(oCell) {
const nColumn = aColByVarIndex[oVarIndexByCellName[oCell.getName()]];
aRow[nColumn] = nOperator === c_oAscOperator['<='] ? coefficient : -coefficient;
}
const oConstraint = aConstraints[j];
const nOperator = oConstraint.getOperator();
const oRefCell = oConstraint.getCell();
/**@type {number} */
const nConstraintValue = oConstraint.getConstraint();
const nConstraintIndex = aConstraintsIndexes[j];
this.addRowByVarIndexElem(nRowIndex, nConstraintIndex);
this.addColByVarIndexElem(-1, nConstraintIndex);
this.addVarIndexByRowElem(nConstraintIndex, nRowIndex);
const aRow = aMatrix[nRowIndex++];
// Work with terms
if (oRefCell.isFormula()) {
const aArgsFormula = this.getArgsFormula(oRefCell.getFormulaParsed().outStack, true);
aArgsFormula.forEach(function (oLinkedCell) {
if (oVariables.bbox.contains(oLinkedCell.nCol, oLinkedCell.nRow)) {
if (oLinkedCell.isFormula()) {// Try to find coefficient for variable
const aOutStack = oLinkedCell.getFormulaParsed().outStack;
const aArgsLinkFormula = oThis.getArgsFormula(aOutStack, false);
aArgsLinkFormula.forEach(function (oCell) {
if (oCell.getNumberValue()) {
coefficient = oCell.getNumberValue();
}
});
}
getVariable(oLinkedCell);
}
});
} else if (oVariables.bbox.contains(oRefCell.nCol, oRefCell.nRow)) {
getVariable(oRefCell);
}
aRow[FIRST_COLUMN_INDEX] = nOperator === c_oAscOperator['<='] ? nConstraintValue : -nConstraintValue;
}
};
/**
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getVarIndexByRow = function () {
return this.aVarIndexByRow;
};
/**
* Sets an empty array for attribute aVarIndexByRow
* @memberof CSimplexTableau
* @param {number[]} aVarIndexByRow
*/
CSimplexTableau.prototype.setVarIndexByRow = function (aVarIndexByRow) {
this.aVarIndexByRow = aVarIndexByRow;
};
/**
* Adds element to varIndexByRow array.
* @memberof CSimplexTableau
* @param {number} nValue
* @param {number} nIndex
*/
CSimplexTableau.prototype.addVarIndexByRowElem = function (nValue, nIndex) {
this.aVarIndexByRow[nIndex] = nValue;
};
/**
* Returns array of indexes of variables by matrix's columns.
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getVarIndexByCol = function () {
return this.aVarIndexByCol;
};
/**
* Sets an empty array for attribute aVarIndexByCol.
* @memberOf CSimplexTableau
* @param {[]} aVarIndexByCol
*/
CSimplexTableau.prototype.setVarIndexByCol = function (aVarIndexByCol) {
this.aVarIndexByCol = aVarIndexByCol;
};
/**
* Adds element to varIndexByCol array.
* @memberof CSimplexTableau
* @param {number} nValue
* @param {number} nIndex
*/
CSimplexTableau.prototype.addVarIndexByColElem = function (nValue, nIndex) {
this.aVarIndexByCol[nIndex] = nValue;
};
/**
* Returns total count of variables including constraints.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getVarsCount = function () {
return this.nVars;
};
/**
* Sets total count of variables including constraints.
* @memberof CSimplexTableau
* @param {number} nVars
*/
CSimplexTableau.prototype.setVarsCount = function (nVars) {
this.nVars = nVars;
};
/**
* Returns array of row's indexes by variable' index. Shows which row of matrix located variable.
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getRowByVarIndex = function () {
return this.aRowByVarIndex;
};
/**
* Sets empty array of row's indexes by variable' index. Shows which row of matrix located variable.
* @memberof CSimplexTableau
* @param {[]}aRowByVarIndex
*/
CSimplexTableau.prototype.setRowByVarIndex = function (aRowByVarIndex) {
this.aRowByVarIndex = aRowByVarIndex;
};
/**
* Adds element to array of row's indexes by variable' index. Shows which row of matrix located variable.
* @memberof CSimplexTableau
* @param {number} nValue
* @param {number} nIndex
*/
CSimplexTableau.prototype.addRowByVarIndexElem = function (nValue, nIndex) {
this.aRowByVarIndex[nIndex] = nValue;
};
/**
* Returns array of column's indexes by variable' index. Shows which column of matrix located variable.
* @memberof CSimplexTableau
* @returns {number[]}
*/
CSimplexTableau.prototype.getColByVarIndex = function () {
return this.aColByVarIndex;
};
/**
* Sets empty array of column's indexes by variable' index. Shows which column of matrix located variable.
* @param {[]} aColByVarIndex
*/
CSimplexTableau.prototype.setColByVarIndex = function (aColByVarIndex) {
this.aColByVarIndex = aColByVarIndex;
};
/**
* Adds element to array of column's indexes by variable' index. Shows which column of matrix located variable.
* @memberof CSimplexTableau
* @param {number} nValue
* @param {number} nIndex
*/
CSimplexTableau.prototype.addColByVarIndexElem = function (nValue, nIndex) {
this.aColByVarIndex[nIndex] = nValue;
};
/**
* Returns index of the matrix's last element.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getLastElementIndex = function () {
return this.nLastElementIndex;
};
/**
* Sets index of the matrix's last element.
* @memberof CSimplexTableau
* @param {number} nLastElementIndex
*/
CSimplexTableau.prototype.setLastElementIndex = function (nLastElementIndex) {
this.nLastElementIndex = nLastElementIndex;
};
/**
* Returns the index of last column in matrix.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getLastColumnIndex = function () {
return this.nLastColumnId
};
/**
* Sets the index of last column in matrix.
* @memberof CSimplexTableau
* @param {number} nLastColumnIndex
*/
CSimplexTableau.prototype.setLastColumnIndex = function (nLastColumnIndex) {
this.nLastColumnId = nLastColumnIndex;
};
/**
* Returns the index of last row in matrix.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getLastRowIndex = function () {
return this.nLastRowId;
};
/**
* Sets the index of last row in matrix.
* @memberof CSimplexTableau
* @param {number} nLastRowIndex
*/
CSimplexTableau.prototype.setLastRowIndex = function (nLastRowIndex) {
this.nLastRowId = nLastRowIndex;
};
/**
* Returns bounded attribute
* @memberof CSimplexTableau
* @returns {boolean}
*/
CSimplexTableau.prototype.getBounded = function () {
return this.bBounded;
};
/**
* Sets bounded attribute
* @memberof CSimplexTableau
* @param {boolean} bBounded
*/
CSimplexTableau.prototype.setBounded = function (bBounded) {
this.bBounded = bBounded;
};
/**
* Returns feasible attribute
* @memberof CSimplexTableau
* @returns {boolean}
*/
CSimplexTableau.prototype.getFeasible = function () {
return this.bFeasible;
};
/**
* Sets feasible attribute
* @memberof CSimplexTableau
* @param {boolean} bFeasible
*/
CSimplexTableau.prototype.setFeasible = function (bFeasible) {
this.bFeasible = bFeasible;
};
/**
* Returns the index's column with the right-hand side value of the constraint or objective function.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getRhsColumn = function () {
return this.nRhsColumn;
};
/**
* Returns an array with the index's variables in cycle.
* @memberof CSimplexTableau
* @returns {[number,number][]}
*/
CSimplexTableau.prototype.getVarIndexesCycle = function () {
return this.aVarIndexesCycle;
};
/**
* Clears elements of array with the index's variables in cycle
*/
CSimplexTableau.prototype.clearVarIndexesCycle = function () {
this.aVarIndexesCycle = [];
};
/**
* Returns repeated basic vars.
* This Map is used for checking basic variables are repeated.
* If the whole basic table is repeated. It's a cycle
* The map stores here indexes of repeated basic variables.
* @memberof CSimplexTableau
* @returns {Map<number, []>}
*/
CSimplexTableau.prototype.getRepeatedVars = function () {
return this.mRepeatedVars;
};
/**
* Checks whether the same sequence of basic variables occurred twice in a row.
* @memberOf CSimplexTableau
* @param {[number,number][]} aVarIndexesCycle
* @returns {boolean}
*/
CSimplexTableau.prototype.checkForCycles = function (aVarIndexesCycle) {
const mRepeatedVars = this.getRepeatedVars();
for (let firstIndex = 0, len1 = aVarIndexesCycle.length - 1; firstIndex < len1; firstIndex++) {
for (let secondIndex = firstIndex + 1, len2 = aVarIndexesCycle.length; secondIndex < len2; secondIndex++) {
const aFirstVarIndexes = aVarIndexesCycle[firstIndex];
const aSecondVarIndexes = aVarIndexesCycle[secondIndex];
if (aFirstVarIndexes[0] === aSecondVarIndexes[0] && aFirstVarIndexes[1] === aSecondVarIndexes[1]) {
// Continues the logic of recognizing a cycle only when the whole basic variable is repeated the second time in a row.
if (secondIndex - firstIndex > len2 - secondIndex) {
// Check and fill (if it's missing) the indexes of repeated basic variables.
if (!mRepeatedVars.has(firstIndex + secondIndex)) {
mRepeatedVars.set(firstIndex + secondIndex, [firstIndex, secondIndex]);
}
break;
}
let bCycleFound = true;
// Checks whether the last indexes of the basic variable from the first loop (presumably)
// and the last indexes from the whole array of passed basic variables are equal.
const aFirstLastIdsVar = aVarIndexesCycle[secondIndex - 1];
const aSecondLastIdsVar = aVarIndexesCycle[len2 - 1];
if (aFirstLastIdsVar[0] !== aSecondLastIdsVar[0] && aFirstLastIdsVar[1] !== aSecondLastIdsVar[1]) {
bCycleFound = false;
continue;
}
// Check and fill (if it's missing) the last indexed of repeated basic variables.
if (!mRepeatedVars.has((secondIndex - 1) - (len2 - 1))) {
mRepeatedVars.set((secondIndex - 1) - (len2 - 1), [secondIndex - 1, len2 - 1]);
}
if (bCycleFound && len2 - secondIndex === mRepeatedVars.size) {
return true;
}
}
}
}
return false;
};
/**
* Executes pivot operations over a matrix, on a given row and column.
* @memberof CSimplexTableau
* @param {number} nLeavingRowIndex
* @param {number} nEnteringColumnIndex
*/
CSimplexTableau.prototype.pivot = function (nLeavingRowIndex, nEnteringColumnIndex) {
const aMatrix = this.getMatrix();
const aPivotRow = aMatrix[nLeavingRowIndex];
const nQuotient = aMatrix[nLeavingRowIndex][nEnteringColumnIndex];
const nLastRow = this.getLastRowIndex();
const nLastColumn = this.getLastColumnIndex();
const aVarIndexByRow = this.getVarIndexByRow();
const aVarIndexByCol = this.getVarIndexByCol();
const aRowByVarIndex = this.getRowByVarIndex();
const aColByVarIndex = this.getColByVarIndex();
const nLeavingBasicIndex = aVarIndexByRow[nLeavingRowIndex];
const nEnteringBasicIndex = aVarIndexByCol[nEnteringColumnIndex];
aVarIndexByRow[nLeavingRowIndex] = nEnteringBasicIndex;
aVarIndexByCol[nEnteringColumnIndex] = nLeavingBasicIndex;
aRowByVarIndex[nEnteringBasicIndex] = nLeavingRowIndex;
aRowByVarIndex[nLeavingBasicIndex] = -1;
aColByVarIndex[nEnteringBasicIndex] = -1;
aColByVarIndex[nLeavingBasicIndex] = nEnteringColumnIndex;
// Step 1: Transforming pivot row. Calculate new pivot row using formula: newPivotRowElem = oldPivotRowElem / quotient (leading element)
/** @type {number[]} */
const aNonZeroColumns = [];
let nNonZeroColumnId = 0;
for (let col = 0; col <= nLastColumn; col++) {
// Checks that the value of pivotRow isn't zero, considering variation in calculation.
if (!(aPivotRow[col] >= -1e-16 && aPivotRow[col] <= 1e-16)) {
aPivotRow[col] /= nQuotient;
aNonZeroColumns[nNonZeroColumnId++] = col;
} else {
aPivotRow[col] = 0;
}
}
aPivotRow[nEnteringColumnIndex] = 1 / nQuotient;
// Step 2: Calculate new rows using formula: newRow = oldRow - oldCoeffOfEnteringVar * newPivotRow
for (let row = 0; row <= nLastRow; row++) {
if (row === nLeavingRowIndex) {
continue;
}
// Checks that the value of row isn't zero, considering variation in calculation.
if (aMatrix[row][nEnteringColumnIndex] >= -1e-16 && aMatrix[row][nEnteringColumnIndex] <= 1e-16) {
continue;
}
const aRow = aMatrix[row];
const nCoefficient = aRow[nEnteringColumnIndex];
if (!(nCoefficient >= -1e-16 && nCoefficient <= 1e-16)) {
for (let nonZeroColId = 0; nonZeroColId < nNonZeroColumnId; nonZeroColId++) {
let nCol = aNonZeroColumns[nonZeroColId];
const nPivotRowElem = aPivotRow[nCol];
if (!(nPivotRowElem >= -1e-16 && nPivotRowElem <= 1e-16)) {
aRow[nCol] -= nCoefficient * nPivotRowElem;
} else if (nPivotRowElem !== 0) {
aPivotRow[nCol] = 0;
}
}
aRow[nEnteringColumnIndex] = -nCoefficient / nQuotient;
} else if (nCoefficient !== 0) {
aRow[nEnteringColumnIndex] = 0;
}
}
};
/**
* Returns index of row with objective function data.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getObjectiveRowIndex = function () {
return this.nObjectiveRowIndex;
};
/**
* Returns a flag whether a solution has been found.
* @memberof CSimplexTableau
* @returns {boolean}
*/
CSimplexTableau.prototype.getSolutionIsFound = function () {
return this.bSolutionIsFound;
};
/**
* Sets a flag whether a solution has been found.
* @memberof CSimplexTableau
* @param {boolean} bSolutionIsFound
*/
CSimplexTableau.prototype.setSolutionIsFound = function (bSolutionIsFound) {
this.bSolutionIsFound = bSolutionIsFound;
};
/**
* Returns unbounded index of variable.
* @memberof CSimplexTableau
* @returns {number}
*/
CSimplexTableau.prototype.getUnboundVarIndex = function () {
return this.nUnboundedVarIndex;
};
/**
* Sets unbounded index of variable.
* @memberof CSimplexTableau
* @param {number} nUnboundVarIndex
*/
CSimplexTableau.prototype.setUnboundVarIndex = function (nUnboundVarIndex) {
this.nUnboundedVarIndex = nUnboundVarIndex;
};
/**
* Updates "Changing variables Cells" values.
* @memberof CSimplexTableau
*/
CSimplexTableau.prototype.updateVariableValues = function () {
const oVariablesCells = this.getVariables();
const nRoundingCoefficient = Math.round(1 / this.getPrecision());
const oVarIndexByCellName = this.getVarIndexByCellName();
const aRowByVarIndex = this.getRowByVarIndex();
const nRhsColumn = this.getRhsColumn();
const aMatrix = this.getMatrix();
const sRegNumDecimalSeparator = this.getModel().getRegNumDecimalSeparator();
oVariablesCells._foreachNoEmpty(function (oCell) {
const sCellName = oCell.getName();
const nVarIndex = oVarIndexByCellName[sCellName];
const nRowId = aRowByVarIndex[nVarIndex];
if (nRowId !== -1) {
const nVarValue = aMatrix[nRowId][nRhsColumn];
const nResult = Math.round((nVarValue + Number.EPSILON) * nRoundingCoefficient) / nRoundingCoefficient;
oCell.setValue(String(nResult).replace('.', sRegNumDecimalSeparator));
}
});
};
// Main class of solver feature
/**
* Class representing a solver feature.
* @param {asc_CSolverParams} oParams - solver parameters
* @param {Worksheet} oWs
* @constructor
*/
function CSolver (oParams, oWs) {
const oParsedFormula = this.convertToCell(oParams.getObjectiveFunction(), oWs).getFormulaParsed();
const oChangingCellsWs = actualWsByRef(oParams.getChangingCells(), oWs);
const sChangingCells = convertToAbsoluteRef(oParams.getChangingCells());
CBaseAnalysis.call(this, oParsedFormula, oChangingCellsWs.getRange2(sChangingCells));
// Solver parameters
this.nOptimizeResultTo = oParams.getOptimizeResultTo();
this.nValueOf = oParams.getValueOf() !== null ? parseFloat(oParams.getValueOf().replace(/,/g, ".")) : null;
this.aConstraints = this.initConstraints(oParams.getConstraints(), oWs);
this.bIsVarsNonNegative = oParams.getVariablesNonNegative();
this.nSolvingMethod = oParams.getSolvingMethod();
// Attributes for calculating logic
this.nStartTime = null;
this.nCurrentSubProblem = 0;
this.nCurrentFeasibleCount = 0;
this.nGradient = null;
this.oSimplexTableau = null;
this.oStartChangingCells = null;
// Calculating option
this.oOptions = oParams.getOptions();
// Updating nMaxIterations attribute of super class to value from calculating options.
this.setMaxIterations(parseFloat(this.oOptions.getIterations()));
}
CSolver.prototype = Object.create(CBaseAnalysis.prototype);
CSolver.prototype.constructor = CSolver;
/**
* Prepares data for calculating.
* @memberof CSolver
*/
CSolver.prototype.prepare = function () {
const oThis = this;
const aConstraints = this.getConstraints();
const nSolutionMethod = this.getSolvingMethod();
const oChangingCells = this.getChangingCell();
// Fills empty cells to 0 value for changing cells
oChangingCells._foreachNoEmpty(function (oCell) {
oThis.setStartChangingCells(oCell.getName(), oCell.getValueWithoutFormat()); // Saves original data
if (oCell.getNumberValue() === null) {
if (oCell.getType() !== CellValueType.Number) {
oCell.setTypeInternal(CellValueType.Number);
}
oCell.setValueNumberInternal(0);
}
});
switch (nSolutionMethod) {
case c_oAscSolvingMethod.grgNonlinear:
break;
case c_oAscSolvingMethod.simplexLP:
this.setSimplexTableau(new CSimplexTableau(this))
const oSimplexTableau = this.getSimplexTableau();
oSimplexTableau.init(aConstraints);
break;
case c_oAscSolvingMethod.evolutionary:
break;
}
};
/**
* Main logic of solver calculating.
* Runs only in sync or async loop.
* @memberof CSolver
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSolver.prototype.calculate = function () {
if (this.getIsPause()) {
return true;
}
const nSolutionMethod = this.getSolvingMethod();
let bCompleteCalculation = false;
if (this.isFinishCalculating()) {
return true;
}
switch (nSolutionMethod) {
case c_oAscSolvingMethod.grgNonlinear:
bCompleteCalculation = this.grgOptimization();
break;
case c_oAscSolvingMethod.simplexLP:
bCompleteCalculation = this.simplexOptimization();
break;
case c_oAscSolvingMethod.evolutionary:
bCompleteCalculation = this.evolutionOptimization();
break;
}
if (this.getIsSingleStep()) {
this.setIsPause(true);
this.setIsSingleStep(false);
return true;
}
return bCompleteCalculation;
};
/**
* Converts cell reference from UI to Cell object.
* @memberof CSolver
* @param {string} sCellRef
* @param {Worksheet}oWs
* @returns {Cell}
*/
CSolver.prototype.convertToCell = function (sCellRef, oWs) {
const oCellWs = actualWsByRef(sCellRef, oWs);
const sCellActualRef = convertToAbsoluteRef(sCellRef);
const oCellRange = oCellWs.getCell2(sCellActualRef);
let oCell = null;
oCellWs._getCell(oCellRange.bbox.r1, oCellRange.bbox.c1, function (oElem) {
oCell = oElem;
});
return oCell;
};
/**
* Initializes the constraints array with necessary for solving data.
* @memberof CSolver
* @param {Map} oConstraints
* @param {Worksheet} oWs
* @returns {CConstraint[]}
*/
CSolver.prototype.initConstraints = function (oConstraints, oWs) {
const aExcludeWords = ['integer', 'binary', 'AllDifferent'];
const aConstraints = [];
oConstraints.forEach(function (oConstraint) {
const oConstraintsWs = actualWsByRef(oConstraint.constraint, oWs);
const sConstraintsActualRef = convertToAbsoluteRef(oConstraint.constraint);
const oCellRefWs = actualWsByRef(oConstraint.cellRef, oWs);
const sCellRefActualRef = convertToAbsoluteRef(oConstraint.cellRef);
let constraintData = oConstraintsWs.getRange2(sConstraintsActualRef);
if (constraintData == null) {
constraintData = aExcludeWords.includes(oConstraint.constraint) ? oConstraint.constraint :
Number(oConstraint.constraint.replace(/,/g, "."));
}
aConstraints.push(new CConstraint(oCellRefWs.getRange2(sCellRefActualRef), oConstraint.operator, constraintData));
});
if (this.getVariablesNonNegative()) {
aConstraints.push(new CConstraint(this.getChangingCell(), c_oAscOperator['>='], 0));
}
return aConstraints;
};
/**
* Calculates and returns an array of constraint results.
* @memberof CSolver
* @returns {boolean[]}
*/
CSolver.prototype.calculateConstraints = function () { // todo Need to rework. Is it needed?
const oThis = this;
/** @type {boolean[]} */
const aConstraintsResult = [];
const aConstraints = this.getConstraints();
const oOptions = this.getOptions();
const nConstraintPrecision = Number(oOptions.getConstraintPrecision().replace(/,/g, "."));
const bIgnoreIntConstraints = oOptions.getIgnoreIntConstraints();
for (let i = 0, length = aConstraints.length; i < length; i++) {
const oConstraintCell = aConstraints[i].getCell();
let bResult = false;
/** @type {Cell} */
let oPrevConstraintCell = null;
oConstraintCell._foreachNoEmpty(function (oElem, nIndex, nCol, nStartRow) {
if (oElem.getNumberValue() !== null) {
const constraintData = aConstraints[i].getConstraint();
const nOperator = aConstraints[i].getOperator();
const oWs = oElem.ws;
let constraintValue = typeof constraintData === 'object' ? oThis.convertToCell(constraintData.getName(), oWs).getNumberValue() : constraintData;
if (constraintValue === 'integer' && !bIgnoreIntConstraints) {
bResult = oElem.getNumberValue() === parseInt(oElem.getNumberValue());
} else if (constraintValue === 'binary' && !bIgnoreIntConstraints) {
bResult = oElem.getNumberValue() === 0 || oElem.getNumberValue() === 1;
} else if (constraintValue === 'AllDifferent' && !bIgnoreIntConstraints && nIndex !== nStartRow) {
bResult = oPrevConstraintCell.getNumberValue() !== oElem.getNumberValue();
} else {
switch (nOperator) {
case c_oAscOperator['=']:
let nDiff = constraintValue - oElem.getNumberValue();
bResult = nDiff < nConstraintPrecision;
break;
case c_oAscOperator['>=']:
bResult = oElem.getNumberValue() >= constraintValue - nConstraintPrecision;
break;
case c_oAscOperator['<=']:
bResult = oElem.getNumberValue() <= constraintValue + nConstraintPrecision;
break;
}
}
oPrevConstraintCell = oElem;
if (!bResult) {
return true; // break loop
}
}
});
aConstraintsResult.push(bResult);
}
return aConstraintsResult;
};
/**
* Checks whether limits are exceeded from options.
* e.g., exceeds of maximum: iterations, time, subproblems, etc.
* @memberof CSolver
* @returns {boolean}
*/
CSolver.prototype.isFinishCalculating = function () {
const nCurrentTime = Date.now();
const nMaxIterations = this.getMaxIterations();
const oOptions = this.getOptions();
const nTimeMax = parseFloat(oOptions.getMaxTime());
const nMaxSubproblems = parseFloat(oOptions.getMaxSubproblems());
const nMaxFeasibleSolution = parseFloat(oOptions.getMaxFeasibleSolution());
let bIsTimeMax = false;
let bIterationIsReached = false;
let bMaxSubproblems = false;
let bMaxFeasibleSolution = false;
if (!isNaN(nTimeMax)) {
bIsTimeMax = nCurrentTime - this.getStartTime() >= nTimeMax * 1000;
}
if (!isNaN(nMaxIterations)) {
bIterationIsReached = this.getCurrentAttempt() >= nMaxIterations;
}
if (!isNaN(nMaxSubproblems)) {
bMaxSubproblems = this.getCurrentSubProblem() >= nMaxSubproblems;
}
if (!isNaN(nMaxFeasibleSolution)) {
bMaxFeasibleSolution = this.getCurrentFeasibleCount() >= nMaxFeasibleSolution;
}
return bIsTimeMax || bIterationIsReached || bMaxSubproblems || bMaxFeasibleSolution;
};
/**
* Tries to find solution by GRG (Generalized reduced gradient) method.
* Uses for non-linear programming tasks.
* @memberof CSolver
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSolver.prototype.grgOptimization = function () {
const oChangingCells = this.getChangingCell();
const aConstraints = this.getConstraints();
const nOptimizeResultTo = this.getOptimizeResultTo();
const oOptions = this.getOptions();
const nDerivatives = oOptions.getDerivatives();
oChangingCells._foreachNoEmpty(function (oChangingCell) {
});
};
/**
* Tries to find solution by Simplex method.
* Uses for LP tasks.
* @memberof CSolver
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSolver.prototype.simplexOptimization = function () {
const oSimplexTableau = this.getSimplexTableau();
const oOptions = this.getOptions();
const bShowIterResults = oOptions.getShowIterResults();
let bCompleteCalculation = oSimplexTableau.calculate();
if (bShowIterResults && !bCompleteCalculation) {
this.fillResult();
}
if (bCompleteCalculation && oSimplexTableau.getSolutionIsFound()) {
this.fillResult();
}
return bCompleteCalculation;
};
/**
* Tries to find solution by Evolutionary method.
* Uses for non-smooth solver problems.
* @memberof CSolver
* @returns {boolean} The flag who recognizes end a loop of solver calculation. True - stop a loop, false - continue a loop.
*/
CSolver.prototype.evolutionOptimization = function () {
};
/**
* Resumes calculation by one step than pause it again.
* @memberof CSolver
*/
CSolver.prototype.step = function () {
let oSolver = this;
this.setIsPause(false);
this.setIsSingleStep(true);
this.setIntervalId(setInterval(function() {
let bIsFinish = oSolver.calculate();
if (bIsFinish) {
clearInterval(oSolver.getIntervalId());
}
}, this.getDelay()));
};
/**
* Fills "Changing Variable Cells" values from result of calculation.
* @memberOf CSolver
*/
CSolver.prototype.fillResult = function () {
const nSolutionMethod = this.getSolvingMethod();
switch (nSolutionMethod) {
case c_oAscSolvingMethod.grgNonlinear:
break;
case c_oAscSolvingMethod.simplexLP:
const oSimplexTableau = this.getSimplexTableau();
oSimplexTableau.updateVariableValues();
break;
case c_oAscSolvingMethod.evolutionary:
break;
}
};
/**
* Returns value of "Optimize to" parameter
* @memberof CSolver
* @returns {c_oAscOptimizeTo}
*/
CSolver.prototype.getOptimizeResultTo = function () {
return this.nOptimizeResultTo;
};
/**
* Returns converted to number type value of "Value of" input field from "To" parameter.
* @memberof CSolver
* @returns {null|number}
*/
CSolver.prototype.getValueOf = function () {
return this.nValueOf;
};
/**
* Returns the array of constraints.
* @memberof CSolver
* @returns {CConstraint[]}
*/
CSolver.prototype.getConstraints = function () {
return this.aConstraints;
};
/**
* Returns value of "Make Unconstrained Variables Non-Negative".
* @memberof CSolver
* @returns {boolean}
*/
CSolver.prototype.getVariablesNonNegative = function () {
return this.bIsVarsNonNegative;
};
/**
* Returns the solving method.
* @memberof CSolver
* @returns {c_oAscSolvingMethod}
*/
CSolver.prototype.getSolvingMethod = function () {
return this.nSolvingMethod;
};
/**
* Returns solver options.
* @memberof CSolver
* @returns {asc_COptions}
*/
CSolver.prototype.getOptions = function () {
return this.oOptions;
};
/**
* Sets time from start calculation process.
* @memberof CSolver
* @param {number} nStartTime
*/
CSolver.prototype.setStartTime = function (nStartTime) {
this.nStartTime = nStartTime;
};
/**
* Returns time from start calculation process.
* @returns {number}
*/
CSolver.prototype.getStartTime = function () {
return this.nStartTime;
};
/**
* Sets current number of subproblem.
* @memberof CSolver
* @param {number} nCurrentSubProblem
*/
CSolver.prototype.setCurrentSubProblem = function (nCurrentSubProblem) {
this.nCurrentSubProblem = nCurrentSubProblem;
};
/**
* Returns current number of subproblem.
* @memberof CSolver
* @returns {number}
*/
CSolver.prototype.getCurrentSubProblem = function () {
return this.nCurrentSubProblem;
};
/**
* Increases number of subproblem.
* @memberof CSolver
*/
CSolver.prototype.increaseCurrentSubProblem = function () {
this.nCurrentSubProblem++;
};
/**
* Return current count of feasible solution.
* @memberof CSolver
* @returns {number}
*/
CSolver.prototype.getCurrentFeasibleCount = function () {
return this.nCurrentFeasibleCount;
};
/**
* Increases current count of feasible solution.
* @memberof CSolver
*/
CSolver.prototype.increaseCurrentFeasibleCount = function () {
this.nCurrentFeasibleCount++;
};
/**
* Sets result of gradient for determine direction of motion for nonbasic variables.
* @memberof CSolver
* @param {number} nGradient
*/
CSolver.prototype.setGradient = function (nGradient) {
this.nGradient = nGradient;
};
/**
* Returns result of gradient for determine direction of motion for nonbasic variables.
* @memberof CSolver
* @returns {number}
*/
CSolver.prototype.getGradient = function () {
return this.nGradient;
};
/**
* Sets simplex tableau object. Using for calculating by simplex method.
* @memberof CSolver
* @param {CSimplexTableau} oSimplexTableau
*/
CSolver.prototype.setSimplexTableau = function (oSimplexTableau) {
this.oSimplexTableau = oSimplexTableau;
};
/**
* Gets simplex tableau object. Using for calculating by simplex method.
* @memberof CSolver
* @returns {CSimplexTableau}
*/
CSolver.prototype.getSimplexTableau = function () {
return this.oSimplexTableau;
};
/**
* Sets start range with original data
* @memberof CSolver
* @param {string} sCellName
* @param {string} sValue
*/
CSolver.prototype.setStartChangingCells = function (sCellName, sValue) {
if (this.oStartChangingCells == null) {
this.oStartChangingCells = {};
}
this.oStartChangingCells[sCellName] = sValue;
};
/**
* Returns start range with original data.
* @memberof CSolver
* @returns {{cellName:string, value:string}}
*/
CSolver.prototype.getStartChangingCells = function () {
return this.oStartChangingCells;
};
/**
* Returns error type
* @memberof CSolver
* @param {AscCommonExcel.Worksheet} ws - checked sheet.
* @param {Asc.Range} range - checked range.
* @param {Asc.c_oAscSelectionDialogType} type - dialog type.
* @returns {Asc.c_oAscError}
*/
CSolver.prototype.isValidDataRef = function(ws, range, type) {
let res = Asc.c_oAscError.ID.No;
if (range && !range.isOneCell()) {
//error text: reference must be to a single cell...
//TODO check def names
res = Asc.c_oAscError.ID.MustSingleCell;
}
switch (type) {
case Asc.c_oAscSelectionDialogType.GoalSeek_Cell: {
//check formula contains
let isFormula = false;
let isNumberResult = true;
//MustFormulaResultNumber
ws && ws._getCellNoEmpty(range.r1, range.c1, function (cell) {
if (cell && cell.isFormula()) {
isFormula = true;
if (cell.number == null) {
isNumberResult = false;
}
}
});
if (!isFormula) {
res = Asc.c_oAscError.ID.MustContainFormula;
} else if (!isNumberResult) {
res = Asc.c_oAscError.ID.MustFormulaResultNumber;
}
break;
}
}
return res;
};
// Export
window['AscCommonExcel'] = window['AscCommonExcel'] || {};
window['AscCommonExcel'].CGoalSeek = CGoalSeek;
window['AscCommonExcel'].CSolver = CSolver;
window['AscCommonExcel'].actualWsByRef = actualWsByRef;
window['AscCommonExcel'].convertToAbsoluteRef = convertToAbsoluteRef;
// Collections and classes for UI part
window['AscCommonExcel'].c_oAscDerivativeType = c_oAscDerivativeType;
window['AscCommonExcel'].c_oAscOperator = c_oAscOperator;
window['AscCommonExcel'].c_oAscOptimizeTo = c_oAscOptimizeTo;
window['AscCommonExcel'].c_oAscSolvingMethod = c_oAscSolvingMethod;
window['AscCommonExcel'].c_oAscSolverResult = c_oAscSolverResult;
window['AscCommonExcel'].c_oResultStatus = c_oResultStatus;
window['AscCommonExcel'].asc_CSolverParams = asc_CSolverParams;
window['AscCommonExcel'].asc_CSolverResults = asc_CSolverResults;
})(window);