javascript-lp-solver
Version:
Easy to use, JSON oriented Linear Programming and Mixed Int. Programming Solver
198 lines (169 loc) • 6.51 kB
JavaScript
/*global describe*/
/*global require*/
/*global module*/
/*global it*/
/*global console*/
/*global process*/
/***************************************************************
* Method: polyopt
* Scope: private
* Agruments:
* model: The model we want solver to operate on.
Because we're in here, we're assuming that
we're solving a multi-objective optimization
problem. Poly-Optimization. polyopt.
This model has to be formed a little differently
because it has multiple objective functions.
Normally, a model has 2 attributes: opType (string,
"max" or "min"), and optimize (string, whatever
attribute we're optimizing.
Now, there is no opType attribute on the model,
and optimize is an object of attributes to be
optimized, and how they're to be optimized.
For example:
...
"optimize": {
"pancakes": "max",
"cost": "minimize"
}
...
**************************************************************/
module.exports = function(solver, model){
// I have no idea if this is actually works, or what,
// but here is my algorithm to solve linear programs
// with multiple objective functions
// 1. Optimize for each constraint
// 2. The results for each solution is a vector
// representing a vertex on the polytope we're creating
// 3. The results for all solutions describes the shape
// of the polytope (would be nice to have the equation
// representing this)
// 4. Find the mid-point between all vertices by doing the
// following (a_1 + a_2 ... a_n) / n;
var objectives = model.optimize,
new_constraints = JSON.parse(JSON.stringify(model.optimize)),
keys = Object.keys(model.optimize),
tmp,
counter = 0,
vectors = {},
vector_key = "",
obj = {},
pareto = [],
i,j,x,y,z;
// Delete the optimize object from the model
delete model.optimize;
// Iterate and Clear
for(i = 0; i < keys.length; i++){
// Clean up the new_constraints
new_constraints[keys[i]] = 0;
}
// Solve and add
for(i = 0; i < keys.length; i++){
// Prep the model
model.optimize = keys[i];
model.opType = objectives[keys[i]];
// solve the model
tmp = solver.Solve(model, undefined, undefined, true);
// Only the variables make it into the solution;
// not the attributes.
//
// Because of this, we have to add the attributes
// back onto the solution so we can do math with
// them later...
// Loop over the keys
for(y in keys){
// We're only worried about attributes, not variables
if(!model.variables[keys[y]]){
// Create space for the attribute in the tmp object
tmp[keys[y]] = tmp[keys[y]] ? tmp[keys[y]] : 0;
// Go over each of the variables
for(x in model.variables){
// Does the variable exist in tmp *and* does attribute exist in this model?
if(model.variables[x][keys[y]] && tmp[x]){
// Add it to tmp
tmp[keys[y]] += tmp[x] * model.variables[x][keys[y]];
}
}
}
}
// clear our key
vector_key = "base";
// this makes sure that if we get
// the same vector more than once,
// we only count it once when finding
// the midpoint
for(j = 0; j < keys.length; j++){
if(tmp[keys[j]]){
vector_key += "-" + ((tmp[keys[j]] * 1000) | 0) / 1000;
} else {
vector_key += "-0";
}
}
// Check here to ensure it doesn't exist
if(!vectors[vector_key]){
// Add the vector-key in
vectors[vector_key] = 1;
counter++;
// Iterate over the keys
// and update our new constraints
for(j = 0; j < keys.length; j++){
if(tmp[keys[j]]){
new_constraints[keys[j]] += tmp[keys[j]];
}
}
// Push the solution into the paretos
// array after cleaning it of some
// excess data markers
delete tmp.feasible;
delete tmp.result;
pareto.push(tmp);
}
}
// Trying to find the mid-point
// divide each constraint by the
// number of constraints
// *midpoint formula*
// (x1 + x2 + x3) / 3
for(i = 0; i < keys.length; i++){
model.constraints[keys[i]] = {"equal": new_constraints[keys[i]] / counter};
}
// Give the model a fake thing to optimize on
model.optimize = "cheater-" + Math.random();
model.opType = "max";
// And add the fake attribute to the variables
// in the model
for(i in model.variables){
model.variables[i].cheater = 1;
}
// Build out the object with all attributes
for(i in pareto){
for(x in pareto[i]){
obj[x] = obj[x] || {min: 1e99, max: -1e99};
}
}
// Give each pareto a full attribute list
// while getting the max and min values
// for each attribute
for(i in obj){
for(x in pareto){
if(pareto[x][i]){
if(pareto[x][i] > obj[i].max){
obj[i].max = pareto[x][i];
}
if(pareto[x][i] < obj[i].min){
obj[i].min = pareto[x][i];
}
} else {
pareto[x][i] = 0;
obj[i].min = 0;
}
}
}
// Solve the model for the midpoints
tmp = solver.Solve(model, undefined, undefined, true);
return {
midpoint: tmp,
vertices: pareto,
ranges: obj
};
};