Computer Science Experimentation

Sunday, January 30, 2011

Linear Programming in F#

Linear Programming in F#


Celso Axelrud
Jan/30/2011



Introduction

This document presents a Linear Programming function developed in F# and executed in the interactive environment.
I am targeting small LP programs on very fast computers.
I selected the simplest and shortest code from all references. It means that has no linear algebra operation and not subroutine calls.

The F# code presented here is based on the subroutine Linpro.
Fortran version at:
http://www.sie.arizona.edu/faculty/addenda/yak/SIE270/for/linpro
Pascal version at:
http://www.sie.arizona.edu/faculty/addenda/yak/SIE270/pas/linpro.pas

There are other references to a subroutine Linpro but it is not the same code.
Fortran version at:
http://www.slac.stanford.edu/accel/ilc/codes/lcopt/source/linpro.f
Python version at:
http://adorio-research.org/wordpress/?p=194

Source Code and Results
(*
 Linpro - this function computes the solution of a linear programming     
           problem by using the Simplex method.  The Objective function    
           is maximized.  Parameters a,b, and c are presumed transmitted   
           as  global variables  declared in the calling program           
  Usage:                                                                   
           Linpro n m1 m2 m3                                

  Parmeters:                                                               
           a = matrix of coefficients (global)                             
           b = right hand side vector (global)                             
           c = coefficients of the objective function(global)              
           Input:                                                          
                 n = number of variables                                   
                 m1 = number of constraints of the Le type                 
                 m2 = number of constraints of the Eq type                 
                 m3 = number of constraints of the Ge type                 
           Output:                                                         
                 x.[i] = i-th component of the optimal solution             
                        for i = 1, ... ,N                                  
                 x.[n+1] = optimal value of the objective function          
                 Kod = key showing the category of the Lp problem          
                       if Kod = 1 then an optimal exists                   
                       if Kod = 2 then no feasible solution exists         
                       if Kod = 3 then the objective function is not       
                                bounded                                    
  Remarks:                                                                 
           matrix a needs to be re-assign before calling the function again    
  Conditions:                                                              
           M1+M2+M3 must be less then or equal to 50                       
           N must not be larger then 50 unless the subroutine is           
           redimenstion                                                    
  Example:
    Maximize Z = f(x,y) = 3x + 2y 
    subject to: 2x + y ≤ 18 
        2x + 3y ≤ 42 
        3x + y ≤ 24 
        x ≥ 0 , y ≥ 0 
  result:
    (x,y) = (3,12)
    Z=33
*)


let a:float[,]= Array2D.zeroCreate 52 151
let b:float[]= Array.zeroCreate 50
let c:float[]= Array.zeroCreate 50



//Linpro
let Linpro n m1 m2 m3  =
    let x:float array = Array.zeroCreate 50
    let mutable i,i0,i1,i2=0,0,0,0
    let mutable j,j0,j1,j2,L=0,0,0,0,0
    let mutable L1,L2,L3,Kod=0,0,0,0
    let mutable m4,m5,mm,n1,n5,nm,nn=0,0,0,0,0,0,0
    let mutable key=0
    let mutable ifirst=0 
    let mutable u=0.0
    let mutable Key=0
    let eps=1.0e-3
    let kk:int array = Array.zeroCreate 150
    //Load up the initial tableau with slack and artificial
    //variables, original and secondary objective functions
    L1<- n + 1
    L2<- m1+m2+m3+1
    n1<- n + 1
    //set the size of the appended matrix
    mm<- m1+m2+m3+2
    nn<- n+m1+m2+(2*m3)+1
    //initialize both objectives to zero 
    for i= L2 to mm do
        for j= 1 to nn do
            a.[i,j]<- 0.0
    L3<- m1+m2+m3
    // initialize all slack and artificial coefficients to zero
    for i= 1 to L3 do
        for j= L1 to nn do
              a.[i,j]<- 0.0
    L<-n
    if m1 > 0 then
        // Fix slack coefficients for Le constraints
        for i= 1 to m1 do
            a.[i,L+i]<- 1.0
        L<- L + m1
    if m3 > 0 then 
        // Fix slack coefficients for Ge constraints
        for i= 1 to m3 do 
            i1<-m1+m2+i
            a.[i1,L+i]<- -1.0
        L<- L + m3
    m4<- m2 + m3
    if (m4 > 0) then
       // Fix artificial coefficient for for Eq and Ge constraints 
        for i= 1 to m4 do
            a.[m1+i,L+i]<- 1.0
    m5<- mm - 1
    // Place the objective function 
    for j= 1 to n do
        a.[m5,j]<- c.[j]
    i2<- m1+m2+m3;
    if (m4 > 0) then
        i1<- m1 + 1
        //construct the secondary objective function 
        for j= 1 to n do
            for i= i1 to i2 do
                a.[mm,j]<- a.[mm,j] + a.[i,j]
            j1<- n+m1+1
            j2<- n+m1+m3
            if (m3 > 0) then
                for j= j1 to j2 do
                    a.[mm,j]<- -1.0
            for i= i1 to i2 do
                a.[mm,nn]<- a.[mm,nn] + b.[i]
    for i= 1 to i2 do
        a.[i,nn]<- b.[i]
    a.[m5,nn]<- 0.0
    // Load up the initial basic solution 
    if (m1 > 0) then
        for i= 1 to m1 do
            kk.[i]<- i + n
    if (m4 > 0) then 
        i1<- m1 + 1
        for i= i1 to i2 do
            kk.[i]<- n+m1+m3+i-i1+1
    else
        //If no constraint of Eq and Gt type, then no secondary
        //  objective function is needed 
        mm<- mm - 1
    ifirst<- 0
    u<- 0.0
    while (ifirst = 0) || (u > eps) do 
        ifirst<- 1
        n5<- nn - 1;
        // Check for the pivot element find the largest
        // coefficient of the objective 
        u<-a.[mm,1]
        j0<- 1
        for j= 1 to n5 do
            if (a.[mm,j] > u) then
                u<- a.[mm,j]
                j0<- j
        if (u > eps) then 
            Key<- 0
            i0<- 0
            while (Key = 0) && (i0 <= i2) do
                i0<- i0 + 1
                if (a.[i0,j0] >= eps) then Key<- 1
            // Test if there is a positive a[i,j] in this column
            // if not then the objective function is bounded
            if (Key = 0) then
                Kod<- 3
                //goto 10;
            else
                if (i0 < i2) then
                    i1<- i0 + 1
                    u<- a.[i0,nn]/a.[i0,j0]
                    for i= i1 to i2 do
                        if (a.[i,j0] >= eps) then
                            if ((a.[i,nn]/a.[i,j0]) < u) then
                                i0<- i
                                u<- a.[i,nn]/a.[i,j0]
                //Perform the elimination step 
                u<- a.[i0,j0]
                for j= 1 to nn do
                    a.[i0,j]<- a.[i0,j]/u
                for i= 1 to mm do
                    if (i <> i0) then
                        for j= 1 to nn do
                            if (j <> j0) then
                                a.[i,j]<- a.[i,j]-a.[i0,j]*a.[i,j0]
                for i= 1 to mm do
                    if (i <> i0) then
                        a.[i,j0]<- 0.0;
                //Register the newest basis vector and go back to
                //perform the next step of elimination 
                kk.[i0]<- j0
        else if (m4 > 0) then
                L<- n+m1+m3;
                // Check for feasible solution from the secondary
                //objective 
                for i= 1 to i2 do
                    if (kk.[i] > L) then
                        Kod<- 2
                        //goto 10;
                if Kod<>2 then     
                    mm<- mm - 1
                    // Remove artificail variables and the secondary
                    //objective function
                    m4<- 0
                    nm<- nn-m2-m3
                    for i= 1 to mm do
                        a.[i,nm]<- a.[i,nn]
                    nn<- nm
                    ifirst<- 0
                //From here we go back to continue elimination 
    if Kod<>2 || Kod<>3 then Kod<- 1
        // Set up the optimal solution 
    for i= 1 to n do
        x.[i]<- 0.0
    for i= 1 to i2 do
        j<- kk.[i];
        x.[j]<- a.[i,nn]
    x.[n+1]<- -a.[mm,nn]
    x,Kod
    
(*
//Example 1
let n=2
let m1=3
let m2,m3=0,0

c.[1..2]<-[|3.0;2.0|]
b.[1..3]<-[|18.0;42.0;24.0|]

a.[1..3,1..2]<-array2D [[2.0;1.0];[2.0;3.0];[3.0;1.0]]

let r=Linpro n m1 m2 m3

printfn "LP category: %i" (snd r)
printfn "LP Objective Function: %A" ((fst r).[3])
printfn "LP Varaibles: %A" ((fst r).[1..2])

(*
> 
LP category: 0
LP Objective Function: 33.0
LP Varaibles: [|3.0; 12.0|]
*)
*)

//Example 2
(*
R = –2x + 5y, subject to:  
x <= 200
y <= 170
x >= 100
y >= 80  
y + x >= 200 
Solution: x=100,y=170
*)
let n=2
let m1=2
let m2=0
let m3=3

c.[1..2]<-[|-2.0;5.0|]
b.[1..3]<-[|200.0;170.0;100.0;80.0;200.0|]

a.Initialize()
a.[1..5,1..2]<-array2D [[1.0;0.0];[0.0;1.0];[1.0;0.0];[0.0;1.0];[1.0;1.0]]

let r=Linpro n m1 m2 m3

printfn "LP category: %i" (snd r)
printfn "LP Objective Function: %A" ((fst r).[3])
printfn "LP Varaibles: %A" ((fst r).[1..2])


No comments:

Post a Comment