Dual Simplex algorithm linear programming solver
authorMonty <xiphmont@xiph.org>
Tue, 23 Nov 1999 06:42:10 +0000 (06:42 +0000)
committerMonty <xiphmont@xiph.org>
Tue, 23 Nov 1999 06:42:10 +0000 (06:42 +0000)
Monty

svn path=/trunk/vorbis/; revision=175

vq/minit.c [new file with mode: 0644]
vq/minit.h [new file with mode: 0644]

diff --git a/vq/minit.c b/vq/minit.c
new file mode 100644 (file)
index 0000000..e3aa0a4
--- /dev/null
@@ -0,0 +1,457 @@
+/****************************** MINIT *****************************************
+ * This file contains procedures to solve a Linear Programming
+ * problem of n variables and m constraints, the last p of which
+ * are equality constraints by the dual simplex method.
+ *
+ * The code was originally in Algol 60 and was published in Collected
+ * Algorithms from CACM (alg #333) by Rudolfo C. Salazar and Subrata
+ * K. Sen in 1968 under the title MINIT (MINimum ITerations) algorithm
+ * for Linear Programming.
+ * It was directly translated into C by Badri Lokanathan, Dept. of EE,
+ * University of Rochester, with no modification to code structure. 
+ * Monty <xiphmont@mit.edu> restructured the code in C style, eliminating
+ * some verbosity and uneeded vector storage.
+ * 
+ * The problem statement is
+ * Maximise z = cX
+ *
+ * subject to
+ * AX <= b
+ * X >=0
+ *
+ * c is a (1*n) row vector
+ * A is a (m*n) matrix
+ * b is a (m*1) column vector.
+ * e is a matrix with with (m+1) rows and lcol columns (lcol = m+n-p+1)
+ *   and forms the initial tableau of the algorithm.
+ * td is read into the procedure and should be a very small number,
+ *   say 1e-9
+ * 
+ * The condition of optimality is the non-negativity of
+ * e[1,j] for j = 1 ... lcol-1 and of e[1,lcol] = 2 ... m+1.
+ * If the e[i,j] values are greater than or equal to -td they are
+ * considered to be non-negative. The value of td should reflect the 
+ * magnitude of the co-efficient matrix.
+ *
+ *****************************************************************************/
+
+/*** Vorbis notes: We don't actually use the equality solving code,
+   but I couldn't bring myself to remove it.  This is such a nice
+   little piece of code that I don't have the heart to cripple it and
+   perhaps someone else will want an unneutered version in the future.
+
+   Note that the equality code has *not* been well tested after my
+   restructuring due to me not having any real problems to throw at it
+   :-) --Monty 19991122 */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "minit.h"
+
+/************************ minit_init ******************************************
+ * This routine performs space maintenance and initializes local arrays.     
+ * n dimensions, m total constraints (lte and eq), p equality constraints 
+ *****************************************************************************/
+
+void minit_init(minit *l,long n,long m,long p){
+  long i;
+  double **tmp;
+  long MS = m + 1;
+  long LS = m + n - p + 1;
+
+  memset(l,0,sizeof(minit));
+
+  l->m = m;
+  l->n = n;
+  l->p = p;
+  l->l = m + n - p + 1;
+      
+  l->ind1 = calloc(MS,sizeof(long));
+  l->chk  = calloc(MS,sizeof(long));
+  l->ind  = calloc(LS,sizeof(long));
+      
+  tmp=l->e=malloc(MS*sizeof(double *));
+  for(i=0;i<MS;i++)
+    *(tmp++)=calloc(LS,sizeof(double));
+}
+
+/************************ minit_clear *****************************************
+ * This routine clears the non-flat storage fromt he local arrays.           
+ *****************************************************************************/
+
+void minit_clear(minit *l){
+  long i;
+  if(l){
+    if(l->ind1){
+      free(l->ind1);
+      free(l->chk);
+      free(l->ind);
+      for(i = 0; i <= l->m; i++)free(l->e[i]);
+      free(l->e);
+    }
+    memset(l,0,sizeof(minit));
+  }
+}
+
+/****************************** rowtrans **************************************
+ * Performs the usual tableau transformations in a linear programming
+ * problem, (p,q) being the pivotal element.
+ * Returns the following error codes:
+ * 0: Everything was OK.
+ * 1: No solution.
+ *****************************************************************************/
+static int rowtrans(minit *l,long p,long q){
+  long i, j;
+  double dummy;
+  
+  if(p == -1 || q == -1) return(1);
+  dummy = l->e[p][q];
+
+  /*if(debug)printf("--\nPivot element is e[%ld][%ld] = %f\n",p,q,dummy);*/
+
+  for(j = 0; j < l->l; j++)l->e[p][j] /= dummy; 
+  
+  for(i = 0; i < l->m + 1; i++){
+    if(i != p){
+      if(l->e[i][q] != 0.0){
+       dummy = l->e[i][q];
+       for(j = 0; j < l->l; j++)
+         l->e[i][j] -= l->e[p][j] * dummy;
+      }
+    }
+  }
+
+  l->chk[p] = q;
+  return(0);
+} 
+
+/****************************** progamma **************************************
+ * Performs calculations over columns to determine the pivot element.
+ *****************************************************************************/
+static long progamma(minit *l,long L, double td, long *im, double *gmin){
+  long jmin = -1;
+  long   ll = l->l-1;
+  long   i, L1;
+
+  for(L1 = 0; L1 < L; L1++){
+    long   ind  = l->ind[L1];
+    long   imin = 0;
+    double thmin;
+
+    for(i = 1; i < l->m + 1; i++){
+      if(l->e[i][ind] > td && l->e[i][ll] >= -td){
+       double theta = l->e[i][ll] / l->e[i][ind];
+       if(!imin || theta < thmin){
+         thmin = theta;
+         imin  = i;
+       }
+      }
+    }
+
+    if(imin){
+      double gamma = thmin * l->e[0][ind];
+      if(jmin==-1 || gamma<*gmin){
+        jmin = ind;
+       *gmin = gamma;
+       *im   = imin;
+      }
+    }
+  }
+  
+  return jmin;
+} 
+
+/****************************** prophi ****************************************
+ * Performs calculations over rows to determine the pivot element.
+ *****************************************************************************/
+static long prophi(minit *l,long k, double td,long *jm, double *phimax){
+  long imax = -1;
+  long ll   = l->l-1;
+  long j, k1;
+       
+  for(k1 = 0; k1 < k ; k1++){
+    long ind =l->ind1[k1];
+    long jmax=-1;
+    double delmax;
+    
+    for(j = 0; j < ll; j++){
+      if(l->e[ind][j] < -td && l->e[0][j] >= -td){
+       double delta = l->e[0][j] / l->e[ind][j];
+       if(jmax<0 || delta > delmax){
+         delmax = delta;
+         jmax   = j;
+       }
+      }
+    }
+    
+    if(jmax!=-1){
+      double phi = delmax * l->e[ind][ll];
+      if(imax==-1 || phi>*phimax){
+        imax   = ind;
+       *phimax = phi;
+       *jm     = jmax;
+      }
+    }
+  }
+  return imax;
+}
+
+/****************************** tab *******************************************
+ * The following procedure is for debugging. It simply prints the
+ * current tableau.
+ *****************************************************************************/
+static void tab(minit *l){
+  long i, j;
+
+  printf("\n");
+  for(i = 0; i < 35; i++)
+    printf("-");
+  printf(" TABLEAU ");
+  for(i = 0; i < 35; i++)
+    printf("-");
+  printf("\n");
+
+  for(i = 0; i < l->m+1; i++){
+    for(j = 0; j < l->l; j++)
+      printf("%6.3f ",l->e[i][j]);
+    
+    printf("\n");
+  }
+}
+
+/****************************** phase1 ****************************************
+ * Applied only to equality constraints if any.
+ *****************************************************************************/
+static int phase1(minit *l,double td){
+  long m=l->m,n=l->n,p=l->p,lcol=l->l;
+  double gmin;
+  long i, j, k, r;
+
+  /* Fix suggested by Messham to allow negative coeffs. in
+   * equality constraints.
+   */
+  for(i = m - p + 1; i < m + 1; i++)
+    if(l->e[i][lcol - 1] < 0)
+      for(j = 0; j < lcol; j++)
+       l->e[i][j] = -l->e[i][j];
+
+  for(r = 0; r < p; r++){
+    long L    = 0;
+    long im   = -1;
+    long im1  = -1;
+    long jmin = -1;
+    long jmin1= -1;
+    long first= 1;
+    
+    /* Fix suggested by Kolm and Dahlstrand */
+    /* if(e[0,j] < 0) */
+    for(j = 0; j < n; j++)
+      if(l->e[0][j] < -td)
+       l->ind[L++] = j;
+    
+    while(1){
+
+      if(L == 0){
+       for(j = 0; j < n; j++)
+         l->ind[j] = j;
+       L = n;
+      }
+      
+      /* I eliminated some local storage on the assumtion that it was
+        unneccessary... this checks that assumption ont he theory that
+        it's better to break than get the wrong answer */
+      for(k = 0; k < L; k++){
+       for(j=k+1;j < L; j++){
+         if(l->ind[k]==l->ind[j]){
+           printf("Internal error, failed assertion\n");
+           exit(1);
+         }
+       }
+      }
+      
+      for(k = 0; k < L; k++){
+       long ind=l->ind[k];
+       double thmin;
+       long imin=-1;
+       
+       for(i = m - p + 1; i < m + 1; i++)
+         if(l->chk[i] == -1){
+           /* Fix suggested by Kolm
+            * and Dahlstrand
+            */
+           /* if(e[i][ind[k]] > 0.0) */
+           if(l->e[i][ind] > td){
+             double theta = l->e[i][lcol-1] / l->e[i][ind];
+             if(imin<0 || theta < thmin){
+               thmin = theta;
+               imin = i;
+             }
+           }
+         }
+       
+       /* Fix suggested by Obradovic overrides
+        * fixes suggested by Kolm and Dahstrand
+        * as well as Messham.
+        */
+       if(imin!=-1){
+         double gamma = thmin * l->e[0][ind];
+         if(jmin==-1 || gamma < gmin){
+           jmin = ind;
+           gmin = gamma;
+           im   = imin;
+         }
+       }
+      }
+      
+      if(jmin == -1){
+       if(first){
+         first = 0;
+         L = 0;
+         continue;
+       }else
+         im = -1;
+      }
+      
+      if(im == im1 && jmin == jmin1){
+       L = 0;
+       continue;
+      }
+      
+      /* if(debug)tab(l);      */
+      
+      if(rowtrans(l,im,jmin))return(1);
+      
+      im1 = im;
+      jmin1 = jmin;
+      break;
+    }
+  }
+  return(0);
+} 
+
+/********************************* MINIT **************************************
+ * This is the main procedure. It is invoked with the various matrices,
+ * after space for other arrays has been generated elsewhere
+ * It returns
+ * 0 if everything went OK and x, w, z as the solutions.
+ * 1 if no solution existed.
+ * 2 if primal had no feasible solutions.
+ * 3 if primal had unbounded solutions.
+ *****************************************************************************/
+int minit_solve(minit *l,double **A,double *b,double *c,double td,
+         double *x,double *w,double *z){
+  long i, j;
+  long m=l->m,n=l->n,p=l->p,lcol=l->l;
+  long imax,jm,jmin,im;
+  double phimax,gmin;
+
+  /* Generate initial tableau. */
+  for(j = 0; j < n; j++)
+    l->e[0][j] = -c[j];
+
+  for(i = 0; i < m; i++)
+    for(j = 0; j < n; j++)
+      l->e[i+1][j] = A[i][j];
+
+  for(j = 0; j < m; j++)
+    l->e[j+1][lcol-1] = b[j];
+
+  for(i = 0; i < m - p; i++)
+    l->e[1+i][n+i] = 1.0;
+
+  /* Now begins the actual algorithm. */
+  for(i = 1; i < m+1; i++)
+    l->chk[i] = -1;
+
+  if(p)
+    if(phase1(l,td))return(1);
+
+  while(1){
+    long L=0,k=0;
+
+    /*if(debug)tab(l); */
+    
+    for(j = 0; j < lcol - 1; j++)
+      if(l->e[0][j] < -td)
+       l->ind[L++] = j; /* ind[L] keeps track of the columns in which
+                           e[0][j] is negative */
+    
+    for(i=1; i<m+1; i++)
+      if(l->e[i][lcol-1] < -td)
+       l->ind1[k++] = i; /* ind1[k] keeps track of the rows in which
+                            e[i][lcol] is negative */
+
+    if(L == 0){
+      if(k == 0) break; /* results */
+
+      if(k == 1){
+       for(j = 0; j < lcol - 1; j++)
+         if(l->e[l->ind1[0]][j] < 0){
+           printf(":R:\n");
+           imax=prophi(l,k,td,&jm,&phimax);
+           if(rowtrans(l,imax,jm))return(1);
+           break;
+         }
+       if(j==lcol-1)return(2); /* Primal problem has no feasible
+                                   solutions. */
+      }else{
+       printf(":R:\n");
+       imax=prophi(l,k,td,&jm,&phimax);
+       if(rowtrans(l,imax,jm))return(1);
+      }
+    }else{
+      if(k==0){
+       if(L == 1){
+         for(i = 1; i < m + 1; i++)
+           if(l->e[i][l->ind[0]] > 0){
+             printf(":C:\n");
+             jmin=progamma(l,L,td,&im,&gmin);
+             if(rowtrans(l,im,jmin))return(1);
+             break;
+           }
+         if(i==m+1)return(3);  /* Primal problem is unbounded. */
+       }else{
+         printf(":C:\n");
+         jmin=progamma(l,L,td,&im,&gmin);
+         if(rowtrans(l,im,jmin))return(1);
+       }
+      }else{
+       
+       printf(":S:\n");
+       jmin=progamma(l,L,td,&im,&gmin);
+       imax=prophi(l,k,td,&jm,&phimax);
+
+       if(jmin==-1){
+         if(rowtrans(l,imax,jm))return(1);
+       }else if(imax==-1){
+         if(rowtrans(l,im,jmin))return(1);
+       }else if(fabs(phimax) > fabs(gmin)){
+         if(rowtrans(l,imax,jm))return(1);
+       }else{
+         if(rowtrans(l,im,jmin))return(1);
+       }
+      }
+    }
+  }
+
+  /* Output results here. */
+  if(z)*z = l->e[0][lcol-1];
+
+  if(x){
+    memset(x,0,n*sizeof(double));    
+    for(i = 1; i < m + 1; i++){
+      if(l->chk[i] < n)
+       x[l->chk[i]] = l->e[i][lcol-1];
+    }
+  }
+  
+  if(w){
+    memset(w,0,m*sizeof(double));
+    for(j = n; j < lcol - 1; j++)
+      w[j-n] = l->e[0][j];
+  }
+
+  return(0);
+}
+
diff --git a/vq/minit.h b/vq/minit.h
new file mode 100644 (file)
index 0000000..bd172f9
--- /dev/null
@@ -0,0 +1,39 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE.  *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS DISTRIBUTING.                            *
+ *                                                                  *
+ * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-1999             *
+ * by 1999 Monty <monty@xiph.org> and The XIPHOPHORUS Company       *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+
+  function: linear programming (dual simplex) solver includes
+
+ ********************************************************************/
+
+#ifndef _V_MINIT_H_
+#define _V_MINIT_H_
+
+typedef struct {
+  long m;
+  long n;
+  long p;
+  long l;
+  
+  long *ind1;
+  long *chk;
+  long *ind;
+  double **e;
+
+} minit;
+
+extern void minit_init(minit *l,long n,long m,long p);
+extern void minit_clear(minit *l);
+extern int minit_solve(minit *l,double **A,double *b,double *c,double td,
+                      double *x,double *w,double *z);
+
+#endif