javascript-lp-solver
Version:
Easy to use, JSON oriented Linear Programming and Mixed Int. Programming Solver
203 lines (159 loc) • 5.82 kB
JavaScript
/*global require*/
var Tableau = require("./Tableau.js");
var SlackVariable = require("../expressions.js").SlackVariable;
Tableau.prototype.addCutConstraints = function (cutConstraints) {
var nCutConstraints = cutConstraints.length;
var height = this.height;
var heightWithCuts = height + nCutConstraints;
// Adding rows to hold cut constraints
for (var h = height; h < heightWithCuts; h += 1) {
if (this.matrix[h] === undefined) {
this.matrix[h] = this.matrix[h - 1].slice();
}
}
// Adding cut constraints
this.height = heightWithCuts;
this.nVars = this.width + this.height - 2;
var c;
var lastColumn = this.width - 1;
for (var i = 0; i < nCutConstraints; i += 1) {
var cut = cutConstraints[i];
// Constraint row index
var r = height + i;
var sign = (cut.type === "min") ? -1 : 1;
// Variable on which the cut is applied
var varIndex = cut.varIndex;
var varRowIndex = this.rowByVarIndex[varIndex];
var constraintRow = this.matrix[r];
if (varRowIndex === -1) {
// Variable is non basic
constraintRow[this.rhsColumn] = sign * cut.value;
for (c = 1; c <= lastColumn; c += 1) {
constraintRow[c] = 0;
}
constraintRow[this.colByVarIndex[varIndex]] = sign;
} else {
// Variable is basic
var varRow = this.matrix[varRowIndex];
var varValue = varRow[this.rhsColumn];
constraintRow[this.rhsColumn] = sign * (cut.value - varValue);
for (c = 1; c <= lastColumn; c += 1) {
constraintRow[c] = -sign * varRow[c];
}
}
// Creating slack variable
var slackVarIndex = this.getNewElementIndex();
this.varIndexByRow[r] = slackVarIndex;
this.rowByVarIndex[slackVarIndex] = r;
this.colByVarIndex[slackVarIndex] = -1;
this.variablesPerIndex[slackVarIndex] = new SlackVariable("s"+slackVarIndex, slackVarIndex);
this.nVars += 1;
}
};
Tableau.prototype._addLowerBoundMIRCut = function(rowIndex) {
if(rowIndex === this.costRowIndex) {
//console.log("! IN MIR CUTS : The index of the row corresponds to the cost row. !");
return false;
}
var model = this.model;
var matrix = this.matrix;
var intVar = this.variablesPerIndex[this.varIndexByRow[rowIndex]];
if (!intVar.isInteger) {
return false;
}
var d = matrix[rowIndex][this.rhsColumn];
var frac_d = d - Math.floor(d);
if (frac_d < this.precision || 1 - this.precision < frac_d) {
return false;
}
//Adding a row
var r = this.height;
matrix[r] = matrix[r - 1].slice();
this.height += 1;
// Creating slack variable
this.nVars += 1;
var slackVarIndex = this.getNewElementIndex();
this.varIndexByRow[r] = slackVarIndex;
this.rowByVarIndex[slackVarIndex] = r;
this.colByVarIndex[slackVarIndex] = -1;
this.variablesPerIndex[slackVarIndex] = new SlackVariable("s"+slackVarIndex, slackVarIndex);
matrix[r][this.rhsColumn] = Math.floor(d);
for (var colIndex = 1; colIndex < this.varIndexByCol.length; colIndex += 1) {
var variable = this.variablesPerIndex[this.varIndexByCol[colIndex]];
if (!variable.isInteger) {
matrix[r][colIndex] = Math.min(0, matrix[rowIndex][colIndex] / (1 - frac_d));
} else {
var coef = matrix[rowIndex][colIndex];
var termCoeff = Math.floor(coef)+Math.max(0, coef - Math.floor(coef) - frac_d) / (1 - frac_d);
matrix[r][colIndex] = termCoeff;
}
}
for(var c = 0; c < this.width; c += 1) {
matrix[r][c] -= matrix[rowIndex][c];
}
return true;
};
Tableau.prototype._addUpperBoundMIRCut = function(rowIndex) {
if (rowIndex === this.costRowIndex) {
//console.log("! IN MIR CUTS : The index of the row corresponds to the cost row. !");
return false;
}
var model = this.model;
var matrix = this.matrix;
var intVar = this.variablesPerIndex[this.varIndexByRow[rowIndex]];
if (!intVar.isInteger) {
return false;
}
var b = matrix[rowIndex][this.rhsColumn];
var f = b - Math.floor(b);
if (f < this.precision || 1 - this.precision < f) {
return false;
}
//Adding a row
var r = this.height;
matrix[r] = matrix[r - 1].slice();
this.height += 1;
// Creating slack variable
this.nVars += 1;
var slackVarIndex = this.getNewElementIndex();
this.varIndexByRow[r] = slackVarIndex;
this.rowByVarIndex[slackVarIndex] = r;
this.colByVarIndex[slackVarIndex] = -1;
this.variablesPerIndex[slackVarIndex] = new SlackVariable("s"+slackVarIndex, slackVarIndex);
matrix[r][this.rhsColumn] = -f;
for(var colIndex = 1; colIndex < this.varIndexByCol.length; colIndex += 1) {
var variable = this.variablesPerIndex[this.varIndexByCol[colIndex]];
var aj = matrix[rowIndex][colIndex];
var fj = aj - Math.floor(aj);
if(variable.isInteger) {
if(fj <= f) {
matrix[r][colIndex] = -fj;
} else {
matrix[r][colIndex] = -(1 - fj) * f / fj;
}
} else {
if (aj >= 0) {
matrix[r][colIndex] = -aj;
} else {
matrix[r][colIndex] = aj * f / (1 - f);
}
}
}
return true;
};
//
// THIS MAKES SOME MILP PROBLEMS PROVIDE INCORRECT
// ANSWERS...
//
// QUICK FIX: MAKE THE FUNCTION EMPTY...
//
Tableau.prototype.applyMIRCuts = function () {
// var nRows = this.height;
// for (var cst = 0; cst < nRows; cst += 1) {
// this._addUpperBoundMIRCut(cst);
// }
// // nRows = tableau.height;
// for (cst = 0; cst < nRows; cst += 1) {
// this._addLowerBoundMIRCut(cst);
// }
};