/*****
* simplex.asy
* Andy Hammerlindl 2004/07/27
*
* Solves the two-variable linear programming problem using the simplex method.
* This problem is specialized in that the second variable, "b", does not have
* a non-negativity condition, and the first variable, "a", is the quantity
* being maximized.
* Correct execution of the algorithm also assumes that the coefficient of "b"
* will be +1 or -1 in every added restriction, and that the problem can be
* initialized to a valid state by pivoting b with one of the slack
* variables. This assumption may in fact be incorrect.
*****/
struct problem {
typedef int var;
static var VAR_A = 0;
static var VAR_B = 1;
static int OPTIMAL = -1;
static var UNBOUNDED = -2;
static int INVALID = -3;
struct row {
real c, t[];
}
// The variables of the rows.
// Initialized for the two variable problem.
var[] v = {VAR_A, VAR_B};
// The rows of equalities.
row rowA() {
row r = new row;
r.c = 0;
r.t = new real[] {1, 0};
return r;
}
row rowB() {
row r = new row;
r.c = 0;
r.t = new real[] {0, 1};
return r;
}
row[] rows = {rowA(), rowB()};
// The number of original variables.
int n = rows.length;
// Pivot the variable v[col] with vp.
void pivot(int col, var vp)
{
var vc=v[col];
// Recalculate rows v[col] and vp for the pivot-swap.
row rvc = rows[vc], rvp = rows[vp];
real factor=1/rvp.t[col]; // NOTE: Handle rvp.t[col] == 0 case.
rvc.c=-rvp.c*factor;
rvp.c=0;
rvc.t=-rvp.t*factor;
rvp.t *= 0;
rvc.t[col]=factor;
rvp.t[col]=1;
var a=min(vc,vp);
var b=max(vc,vp);
// Recalculate the rows other than the two used for the above pivot.
for (var i = 0; i < a; ++i) {
row r=rows[i];
real m = r.t[col];
r.c += m*rvc.c;
r.t += m*rvc.t;
r.t[col]=m*factor;
}
for (var i = a+1; i < b; ++i) {
row r=rows[i];
real m = r.t[col];
r.c += m*rvc.c;
r.t += m*rvc.t;
r.t[col]=m*factor;
}
for (var i = b+1; i < rows.length; ++i) {
row r=rows[i];
real m = r.t[col];
r.c += m*rvc.c;
r.t += m*rvc.t;
r.t[col]=m*factor;
}
// Relabel the vars.
v[col] = vp;
}
// As b does not have a non-negativity condition, it must initially be
// pivoted out for a variable that does. This selects the initial
// variable to pivot with b. It also assumes that there is a valid
// solution with a == 0 to the linear programming problem, and if so, it
// picks a pivot to get to that state. In our case, a == 0 corresponds to
// a picture with the user coordinates shrunk down to zero, and if that
// doesn't fit, nothing will.
//
// If b has a minimal value, choose a pivot that will give b its minimal
// value. Otherwise, if b has maximal value, choose a pivot to give b its
// maximal value.
var initVar()
{
real min;
var argmin;
var i=2;
for (; i < rows.length; ++i) {
row r=rows[i];
if (r.t[VAR_B] > 0) {
min=r.c/r.t[VAR_B];
argmin=i;
break;
}
}
for (; i < rows.length; ++i) {
row r=rows[i];
if (r.t[VAR_B] > 0) {
real val=r.c/r.t[VAR_B];
if (val < min) {
min=val;
argmin=i;
}
}
}
if(argmin != 0) return argmin;
real max;
var argmax;
var i=2;
for (; i < rows.length; ++i) {
row r=rows[i];
if (r.t[VAR_B] < 0) {
max=r.c/r.t[VAR_B];
argmax=i;
break;
}
}
for (; i < rows.length; ++i) {
row r=rows[i];
if (r.t[VAR_B] < 0) {
real val=r.c/r.t[VAR_B];
if (val > max) {
max=val;
argmax=i;
}
}
}
if(argmax != 0) return argmax;
return UNBOUNDED;
}
// Initialize the linear program problem by moving into an acceptable state
// this assumes that b is unrestrained and is the second variable.
// NOTE: Works in limited cases, may be bug-ridden.
void init()
{
// Find the lowest constant term in the equations.
var lowest = 0;
for (var i = 2; i < rows.length; ++i) {
if (rows[i].c < rows[lowest].c)
lowest = i;
}
// Pivot if necessary.
if (lowest != 0)
pivot(VAR_B, lowest);
}
// Selects a column to pivot on. Returns OPTIMAL if the current state is
// optimal. Assumes we are optimizing the first row.
int selectColumn()
{
int i=find(rows[0].t > 0,1);
return (i >= 0) ? i : OPTIMAL;
}
// Select the new variable associated with a pivot on the column given.
// Returns UNBOUNDED if the space is unbounded.
var selectVar(int col)
{
// We assume that the first two vars (a and b) once swapped out, won't be
// swapped back in. This finds the variable which gives the tightest
// non-negativity condition restricting our optimization. This turns
// out to be the max of c/t[col]. Note that as c is positive, and
// t[col] is negative, all c/t[col] will be negative, so we are finding
// the smallest in magnitude.
var vp=UNBOUNDED;
real max=0;
int i=2;
for (; i < rows.length; ++i) {
row r=rows[i];
if(r.c < 0) r.c=0; // Fix any numerical precision error
if(r.t[col] < 0) {
max=r.c/r.t[col]; vp=i;
break;
}
}
for (; i < rows.length; ++i) {
row r=rows[i];
if(r.c < 0) r.c=0; // Fix any numerical precision error
if(r.c < max*r.t[col]) {
max=r.c/r.t[col]; vp=i;
}
}
return vp;
}
// Checks that the rows are in a valid state.
bool valid()
{
// Checks that constants are valid.
bool validConstants() {
for (int i = 0; i < rows.length; ++i)
// Do not test the row for b, as it does not have a non-negativity
// condition.
if (i != VAR_B && rows[i].c < 0)
return false;
return true;
}
// Check a variable to see if its row is simple.
// NOTE: Simple rows could be optimized out, since they are not really
// used.
bool validVar(int col) {
var vc = v[col];
row rvc = rows[vc];
if (rvc.c != 0)
return false;
for (int i = 0; i < n; ++i)
if (rvc.t[i] != (i == col ? 1 : 0))
return false;
return true;
}
if (!validConstants()) {
return false;
}
for (int i = 0; i < n; ++i)
if (!validVar(i)) {
return false;
}
return true;
}
// Perform the algorithm to find the optimal solution. Returns OPTIMAL,
// UNBOUNDED, or INVALID (if no solution is possible).
int optimize()
{
// Put into a valid state to begin and pivot b out.
var iv=initVar();
if (iv == UNBOUNDED)
return iv;
pivot(VAR_B, iv);
if (!valid())
return INVALID;
while(true) {
int col = selectColumn();
if (col == OPTIMAL)
return col;
var vp = selectVar(col);
if (vp == UNBOUNDED)
return vp;
pivot(col, vp);
}
// Shouldn't reach here.
return INVALID;
}
// Add a restriction to the problem:
// t1*a + t2*b + c >= 0
void addRestriction(real t1, real t2, real c)
{
row r = new row;
r.c = c;
r.t = new real[] {t1, t2};
rows.push(r);
}
// Return the value of a computed.
real a()
{
return rows[VAR_A].c;
}
// Return the value of b computed.
real b()
{
return rows[VAR_B].c;
}
}