UNPKG

javascript-lp-solver

Version:

Easy to use, JSON oriented Linear Programming and Mixed Int. Programming Solver

305 lines (254 loc) 10.2 kB
/*global require*/ /*global console*/ var Tableau = require("./Tableau.js"); //------------------------------------------------------------------- //------------------------------------------------------------------- Tableau.prototype._putInBase = function (varIndex) { // Is varIndex in the base? var r = this.rowByVarIndex[varIndex]; if (r === -1) { // Outside the base // pivoting to take it out var c = this.colByVarIndex[varIndex]; // Selecting pivot row // (Any row with coefficient different from 0) for (var r1 = 1; r1 < this.height; r1 += 1) { var coefficient = this.matrix[r1][c]; if (coefficient < -this.precision || this.precision < coefficient) { r = r1; break; } } this.pivot(r, c); } return r; }; Tableau.prototype._takeOutOfBase = function (varIndex) { // Is varIndex in the base? var c = this.colByVarIndex[varIndex]; if (c === -1) { // Inside the base // pivoting to take it out var r = this.rowByVarIndex[varIndex]; // Selecting pivot column // (Any column with coefficient different from 0) var pivotRow = this.matrix[r]; for (var c1 = 1; c1 < this.height; c1 += 1) { var coefficient = pivotRow[c1]; if (coefficient < -this.precision || this.precision < coefficient) { c = c1; break; } } this.pivot(r, c); } return c; }; Tableau.prototype.updateVariableValues = function () { var nVars = this.variables.length; var roundingCoeff = Math.round(1 / this.precision); for (var v = 0; v < nVars; v += 1) { var variable = this.variables[v]; var varIndex = variable.index; var r = this.rowByVarIndex[varIndex]; if (r === -1) { // Variable is non basic variable.value = 0; } else { // Variable is basic var varValue = this.matrix[r][this.rhsColumn]; variable.value = Math.round((varValue + Number.EPSILON) * roundingCoeff) / roundingCoeff; } } }; Tableau.prototype.updateRightHandSide = function (constraint, difference) { // Updates RHS of given constraint var lastRow = this.height - 1; var constraintRow = this.rowByVarIndex[constraint.index]; if (constraintRow === -1) { // Slack is not in base var slackColumn = this.colByVarIndex[constraint.index]; // Upading all the RHS values for (var r = 0; r <= lastRow; r += 1) { var row = this.matrix[r]; row[this.rhsColumn] -= difference * row[slackColumn]; } var nOptionalObjectives = this.optionalObjectives.length; if (nOptionalObjectives > 0) { for (var o = 0; o < nOptionalObjectives; o += 1) { var reducedCosts = this.optionalObjectives[o].reducedCosts; reducedCosts[this.rhsColumn] -= difference * reducedCosts[slackColumn]; } } } else { // Slack variable of constraint is in base // Updating RHS with the difference between the old and the new one this.matrix[constraintRow][this.rhsColumn] -= difference; } }; Tableau.prototype.updateConstraintCoefficient = function (constraint, variable, difference) { // Updates variable coefficient within a constraint if (constraint.index === variable.index) { throw new Error("[Tableau.updateConstraintCoefficient] constraint index should not be equal to variable index !"); } var r = this._putInBase(constraint.index); var colVar = this.colByVarIndex[variable.index]; if (colVar === -1) { var rowVar = this.rowByVarIndex[variable.index]; for (var c = 0; c < this.width; c += 1){ this.matrix[r][c] += difference * this.matrix[rowVar][c]; } } else { this.matrix[r][colVar] -= difference; } }; Tableau.prototype.updateCost = function (variable, difference) { // Updates variable coefficient within the objective function var varIndex = variable.index; var lastColumn = this.width - 1; var varColumn = this.colByVarIndex[varIndex]; if (varColumn === -1) { // Variable is in base var variableRow = this.matrix[this.rowByVarIndex[varIndex]]; var c; if (variable.priority === 0) { var costRow = this.matrix[0]; // Upading all the reduced costs for (c = 0; c <= lastColumn; c += 1) { costRow[c] += difference * variableRow[c]; } } else { var reducedCosts = this.objectivesByPriority[variable.priority].reducedCosts; for (c = 0; c <= lastColumn; c += 1) { reducedCosts[c] += difference * variableRow[c]; } } } else { // Variable is not in the base // Updating coefficient with difference this.matrix[0][varColumn] -= difference; } }; Tableau.prototype.addConstraint = function (constraint) { // Adds a constraint to the tableau var sign = constraint.isUpperBound ? 1 : -1; var lastRow = this.height; var constraintRow = this.matrix[lastRow]; if (constraintRow === undefined) { constraintRow = this.matrix[0].slice(); this.matrix[lastRow] = constraintRow; } // Setting all row cells to 0 var lastColumn = this.width - 1; for (var c = 0; c <= lastColumn; c += 1) { constraintRow[c] = 0; } // Initializing RHS constraintRow[this.rhsColumn] = sign * constraint.rhs; var terms = constraint.terms; var nTerms = terms.length; for (var t = 0; t < nTerms; t += 1) { var term = terms[t]; var coefficient = term.coefficient; var varIndex = term.variable.index; var varRowIndex = this.rowByVarIndex[varIndex]; if (varRowIndex === -1) { // Variable is non basic constraintRow[this.colByVarIndex[varIndex]] += sign * coefficient; } else { // Variable is basic var varRow = this.matrix[varRowIndex]; var varValue = varRow[this.rhsColumn]; for (c = 0; c <= lastColumn; c += 1) { constraintRow[c] -= sign * coefficient * varRow[c]; } } } // Creating slack variable var slackIndex = constraint.index; this.varIndexByRow[lastRow] = slackIndex; this.rowByVarIndex[slackIndex] = lastRow; this.colByVarIndex[slackIndex] = -1; this.height += 1; }; Tableau.prototype.removeConstraint = function (constraint) { var slackIndex = constraint.index; var lastRow = this.height - 1; // Putting the constraint's slack in the base var r = this._putInBase(slackIndex); // Removing constraint // by putting the corresponding row at the bottom of the matrix // and virtually reducing the height of the matrix by 1 var tmpRow = this.matrix[lastRow]; this.matrix[lastRow] = this.matrix[r]; this.matrix[r] = tmpRow; // Removing associated slack variable from basic variables this.varIndexByRow[r] = this.varIndexByRow[lastRow]; this.varIndexByRow[lastRow] = -1; this.rowByVarIndex[slackIndex] = -1; // Putting associated slack variable index in index manager this.availableIndexes[this.availableIndexes.length] = slackIndex; constraint.slack.index = -1; this.height -= 1; }; Tableau.prototype.addVariable = function (variable) { // Adds a variable to the tableau // var sign = constraint.isUpperBound ? 1 : -1; var lastRow = this.height - 1; var lastColumn = this.width; var cost = this.model.isMinimization === true ? -variable.cost : variable.cost; var priority = variable.priority; // Setting reduced costs var nOptionalObjectives = this.optionalObjectives.length; if (nOptionalObjectives > 0) { for (var o = 0; o < nOptionalObjectives; o += 1) { this.optionalObjectives[o].reducedCosts[lastColumn] = 0; } } if (priority === 0) { this.matrix[0][lastColumn] = cost; } else { this.setOptionalObjective(priority, lastColumn, cost); this.matrix[0][lastColumn] = 0; } // Setting all other column cells to 0 for (var r = 1; r <= lastRow; r += 1) { this.matrix[r][lastColumn] = 0; } // Adding variable to trackers var varIndex = variable.index; this.varIndexByCol[lastColumn] = varIndex; this.rowByVarIndex[varIndex] = -1; this.colByVarIndex[varIndex] = lastColumn; this.width += 1; }; Tableau.prototype.removeVariable = function (variable) { var varIndex = variable.index; // Putting the variable out of the base var c = this._takeOutOfBase(varIndex); var lastColumn = this.width - 1; if (c !== lastColumn) { var lastRow = this.height - 1; for (var r = 0; r <= lastRow; r += 1) { var row = this.matrix[r]; row[c] = row[lastColumn]; } var nOptionalObjectives = this.optionalObjectives.length; if (nOptionalObjectives > 0) { for (var o = 0; o < nOptionalObjectives; o += 1) { var reducedCosts = this.optionalObjectives[o].reducedCosts; reducedCosts[c] = reducedCosts[lastColumn]; } } var switchVarIndex = this.varIndexByCol[lastColumn]; this.varIndexByCol[c] = switchVarIndex; this.colByVarIndex[switchVarIndex] = c; } // Removing variable from non basic variables this.varIndexByCol[lastColumn] = -1; this.colByVarIndex[varIndex] = -1; // Adding index into index manager this.availableIndexes[this.availableIndexes.length] = varIndex; variable.index = -1; this.width -= 1; };