javascript-lp-solver
Version:
Easy to use, JSON oriented Linear Programming and Mixed Int. Programming Solver
200 lines (167 loc) • 6.11 kB
JavaScript
/*global describe*/
/*global require*/
/*global module*/
/*global it*/
/*global console*/
/*global process*/
//-------------------------------------------------------------------
//-------------------------------------------------------------------
function Variable(id, cost, index, priority) {
this.id = id;
this.cost = cost;
this.index = index;
this.value = 0;
this.priority = priority;
}
function IntegerVariable(id, cost, index, priority) {
Variable.call(this, id, cost, index, priority);
}
IntegerVariable.prototype.isInteger = true;
function SlackVariable(id, index) {
Variable.call(this, id, 0, index, 0);
}
SlackVariable.prototype.isSlack = true;
//-------------------------------------------------------------------
//-------------------------------------------------------------------
function Term(variable, coefficient) {
this.variable = variable;
this.coefficient = coefficient;
}
function createRelaxationVariable(model, weight, priority) {
if (priority === 0 || priority === "required") {
return null;
}
weight = weight || 1;
priority = priority || 1;
if (model.isMinimization === false) {
weight = -weight;
}
return model.addVariable(weight, "r" + (model.relaxationIndex++), false, false, priority);
}
//-------------------------------------------------------------------
//-------------------------------------------------------------------
function Constraint(rhs, isUpperBound, index, model) {
this.slack = new SlackVariable("s" + index, index);
this.index = index;
this.model = model;
this.rhs = rhs;
this.isUpperBound = isUpperBound;
this.terms = [];
this.termsByVarIndex = {};
// Error variable in case the constraint is relaxed
this.relaxation = null;
}
Constraint.prototype.addTerm = function (coefficient, variable) {
var varIndex = variable.index;
var term = this.termsByVarIndex[varIndex];
if (term === undefined) {
// No term for given variable
term = new Term(variable, coefficient);
this.termsByVarIndex[varIndex] = term;
this.terms.push(term);
if (this.isUpperBound === true) {
coefficient = -coefficient;
}
this.model.updateConstraintCoefficient(this, variable, coefficient);
} else {
// Term for given variable already exists
// updating its coefficient
var newCoefficient = term.coefficient + coefficient;
this.setVariableCoefficient(newCoefficient, variable);
}
return this;
};
Constraint.prototype.removeTerm = function (term) {
// TODO
return this;
};
Constraint.prototype.setRightHandSide = function (newRhs) {
if (newRhs !== this.rhs) {
var difference = newRhs - this.rhs;
if (this.isUpperBound === true) {
difference = -difference;
}
this.rhs = newRhs;
this.model.updateRightHandSide(this, difference);
}
return this;
};
Constraint.prototype.setVariableCoefficient = function (newCoefficient, variable) {
var varIndex = variable.index;
if (varIndex === -1) {
console.warn("[Constraint.setVariableCoefficient] Trying to change coefficient of inexistant variable.");
return;
}
var term = this.termsByVarIndex[varIndex];
if (term === undefined) {
// No term for given variable
this.addTerm(newCoefficient, variable);
} else {
// Term for given variable already exists
// updating its coefficient if changed
if (newCoefficient !== term.coefficient) {
var difference = newCoefficient - term.coefficient;
if (this.isUpperBound === true) {
difference = -difference;
}
term.coefficient = newCoefficient;
this.model.updateConstraintCoefficient(this, variable, difference);
}
}
return this;
};
Constraint.prototype.relax = function (weight, priority) {
this.relaxation = createRelaxationVariable(this.model, weight, priority);
this._relax(this.relaxation);
};
Constraint.prototype._relax = function (relaxationVariable) {
if (relaxationVariable === null) {
// Relaxation variable not created, priority was probably "required"
return;
}
if (this.isUpperBound) {
this.setVariableCoefficient(-1, relaxationVariable);
} else {
this.setVariableCoefficient(1, relaxationVariable);
}
};
//-------------------------------------------------------------------
//-------------------------------------------------------------------
function Equality(constraintUpper, constraintLower) {
this.upperBound = constraintUpper;
this.lowerBound = constraintLower;
this.model = constraintUpper.model;
this.rhs = constraintUpper.rhs;
this.relaxation = null;
}
Equality.prototype.isEquality = true;
Equality.prototype.addTerm = function (coefficient, variable) {
this.upperBound.addTerm(coefficient, variable);
this.lowerBound.addTerm(coefficient, variable);
return this;
};
Equality.prototype.removeTerm = function (term) {
this.upperBound.removeTerm(term);
this.lowerBound.removeTerm(term);
return this;
};
Equality.prototype.setRightHandSide = function (rhs) {
this.upperBound.setRightHandSide(rhs);
this.lowerBound.setRightHandSide(rhs);
this.rhs = rhs;
};
Equality.prototype.relax = function (weight, priority) {
this.relaxation = createRelaxationVariable(this.model, weight, priority);
this.upperBound.relaxation = this.relaxation;
this.upperBound._relax(this.relaxation);
this.lowerBound.relaxation = this.relaxation;
this.lowerBound._relax(this.relaxation);
};
module.exports = {
Constraint: Constraint,
Variable: Variable,
IntegerVariable: IntegerVariable,
SlackVariable: SlackVariable,
Equality: Equality,
Term: Term
};