GLPK/Scripting plus MathProg

This page covers the use of external scripting languages with MathProg.


Passing values via the command-line edit

Values cannot be passed as command line parameters to glpsol. Instead a script can be used to write command line parameters to a data file. This data file can be loaded together with the model.

The following Bash script test.sh takes the first command line parameter and writes it to a data file test.dat and then calls glpsol for model test.mod and data file test.dat:

#!/bin/sh
echo data\; > test.dat
echo param p := \'$1\'\; >> test.dat
echo end\; >> test.dat
glpsol -m test.mod -d test.dat

You can test the script with the following model file test.mod

param p, symbolic;
printf "%s\n", p;
end;

by executing

/test.sh 'Hello world'

The data file created test.dat will contain the following lines:

data;
param p := 'Hello world';
end;

Parametric studies and MathProg edit

The MathProg language does not offer control structures in the interests of simplicity and efficiency (a restriction that does not apply to compiled language programming with the GLPK API). This lack of control structures prevents the direct implementation of parametric studies within MathProg — where one would like to perform a simple loop over a range of parameter values with the MathProg solve statement nested inside. A parametric study is the same as a sensitivity analysis.

One work-around is to use a scripting language to generate and run a number of GLPK model instances. A more sophisticated approach would be to use a relational database for the storage of interim solutions.

GLPSOL and AWK edit

AWK is a well-established scripting language with a C-like syntax. GNU AWK is present by default on most Linux distros, otherwise install the gawk package. Windows users can obtain GNU AWK binaries from gnuwin32.sourceforge.net/packages/gawk.htm.

AWK makes it possible to create GMPL data files and then invoke glpsol on these newly-created files. This means AWK is a good choice for undertaking parametric studies in association with GMPL. It is normally better to use one volatile data file for your scanned parameters and another stable data file for the rest of your model data (glpsol supports multiple data files).

The rudimentary example below is presented as a stub for developing your own parameter scanning scripts. This example repeatedly writes the parameter iter to a volatile data file test.dat and then calls glpsol to display the value of this parameter. This script also generates the model file test.mod at the outset, but normally your model file would preexist. Windows users will need to replace the Linux remove command rm --force with del.

# AWK script
# provides a starting point for developing custom parameter scanning scripts

BEGIN {
modfile = "test.mod";
datfile = "test.dat";
system("rm --force " modfile);        # Linux remove call
printf ("Writing model file\n");
printf ("# model file\n") > modfile;
printf ("param iter;\n") > modfile;
printf ("solve;\n") > modfile;
printf ("display iter;\n") > modfile;
printf ("end;\n") > modfile;
close(modfile);
for (i = 1; i <= 5; i++) {
  system("rm --force " datfile);      # Linux remove call
  printf("\n\nIteration %i\n", i);
  printf ("Writing data file\n");
  printf("# data file %i\n", i) > datfile;
  printf("data;\n") > datfile;
  printf("param iter := %i;\n", i) > datfile;
  printf("end;\n") > datfile;
  close(datfile);
  system("glpsol --model " modfile " --data " datfile);
  }
exit;
}

Save this script as text file scan.awk.

Ensure that lines are separated by line feeds (0x0A). Beware, some OS X editors use carriage returns (0x0D).

Run the script from the command-line:

$ awk -f scan.awk 

Edited terminal output from iteration 2 is as follows:

Iteration 2
Writing data file
GLPSOL: GLPK LP/MIP Solver, v4.44
Reading model section from test.mod...
Reading data section from test.dat...
Model has been successfully generated
GLPK Simplex Optimizer, v4.44
0 rows, 0 columns, 0 non-zeros
~     0: obj =   0.000000000e+00  infeas =  0.000e+00
OPTIMAL SOLUTION FOUND
Display statement at line 4
iter = 2
Model has been successfully processed

The same basic idea can be implemented in virtually any scripting language, from bash upwards. In addition, astute readers may notice that altered model (as opposed to data) files can also be constructed on-the-fly using similar scripting methods.

GLPSOL and Visual Basic Script edit

Visual Basic Script is programming language delivered with Microsoft Windows.

Visual Basic Script makes it possible to create GMPL data files and then invoke glpsol on these newly-created files.

The example below demonstrates how this can be done.

Create a model file test.mod which will only print parameter p.

param p;
printf "Parameter p = %d\n", p;
end;

Create a script test.vbs

Const ForWriting = 2
Set wshShell = WScript.CreateObject ("WSCript.shell")
Set fso = CreateObject("Scripting.FileSystemObject")
Set sout = WScript.StdOut
For i = 1 To 3
  'Write data file
  Set MyFile = fso.OpenTextFile("test.dat", ForWriting, True)
  MyFile.WriteLine "data;"
  MyFile.WriteLine "param p := " & i & ";"
  MyFile.WriteLine "end;"
  MyFile.Close
  'Execute glpsol
  Set oExec = wshShell.exec("glpsol -m test.mod -d test.dat")
  'Copy output of glpsol to console used by script
  While Not oExec.StdOut.AtEndOfStream
    sout.Write oExec.StdOut.Read(1)
  Wend
Next

Run the script with

cscript test.vbs

GLPSOL and Visual Basic for Applications edit

Visual Basic Script is programming language delivered with Microsoft Office.

VBA makes it possible to create GMPL files and then invoke glpsol on these newly-created files.

The example below demonstrates how this can be done.

Option Explicit

Private Declare Function WaitForSingleObject Lib "kernel32" ( _
    ByVal hHandle As Long, _
    ByVal dwMilliseconds As Long) As Long

Private Declare Function OpenProcess Lib "kernel32.dll" ( _
    ByVal dwDesiredAccess As Long, _
    ByVal bInheritHandle As Long, _
    ByVal dwProcessId As Long) As Long

Private Declare Function CloseHandle Lib "kernel32" ( _
    ByVal hObject As Long) As Long
    
Private Const SYNCHRONIZE = &H100000
Private Const INFINITE = -1&
Private Const MinimizedNoFocus = 6

' Model file
Private Const modfile = "C:\TEMP\test.mod"
' Result file
Private Const resfile = "C:\TEMP\test.res"

Public Sub parametricStudy()
  Dim p As Double
  Dim r As String
  For p = 0.5 To 2.5 Step 0.2
    r = r & runGLPK(p) & vbCrLf
  Next p
  MsgBox r, vbOKOnly, "Result"
End Sub

Private Function runGLPK(p As Double) As String
  Dim f As Integer
  Dim s As String
  Dim pid As Long
  Dim h As Long
  Dim x As String
  Dim y As String
  
  ' Convert double to string
  s = p
  s = Replace(s, ",", ".")
  ' Create model file
  f = FreeFile()
  Open modfile For Output As f
  Print #f, "param f, symbolic := """ & resfile & """;"
  Print #f, "var x, >=0;"
  Print #f, "var y, >=0;"
  Print #f, "maximize obj : x + "; s; " * y; "
  Print #f, "s.t. c1 : x + y <= 4;"
  Print #f, "s.t. c2 : x + 2 * y <= 6;"
  Print #f, "solve;"
  Print #f, "printf ""%f\n"", x > f;"
  Print #f, "printf ""%f\n"", y >> f;"
  Print #f, "end;"
  Close f
  ' Delete result fle
  If Dir(resfile) <> "" Then
    Kill resfile
  End If
  ' Start glpsol
  pid = Shell("""C:\Program Files\GLPK\glpk-4.47\w32\glpsol.exe"" -m " & modfile, MinimizedNoFocus)
  If pid = 0 Then
    MsgBox "Failure to start glpsol.exe", vbCritical, "Error"
    Exit Function
  End If
  ' Wait for glpsol to end
  h = OpenProcess(SYNCHRONIZE, 0, pid)
  If h <> 0 Then
    WaitForSingleObject h, INFINITE
    CloseHandle h
  End If
  ' Check if result file written
  If Dir(resfile) = "" Then
    MsgBox "No result from glpsol.exe", vbCritical, "Error"
    Exit Function
  End If
  ' Output result
  Open resfile For Input As f
  Line Input #f, x
  Line Input #f, y
  Close f
  runGLPK = "p = " & s & " => x = " & x & ", y = " & y
End Function

Python and PyMathProg edit

If shell-command-based scripting (using AWK) is not flexible enough, then the Python language and the PyMathProg package provide a more powerful alternative. PyMathProg allows one to write linear and mixed-integer programming models — in a form very much like GMPL — using Python. A succinct example of how PyMathProg can be used to implement a subtour elimination heuristic is given here.

Python and Sage edit

Comment: this material should be extended.

Sage is an open source mathematics environment, offering both symbolic and numerical calculation and good visualization. Sage supports the Python language and GLPK is available through the Sage optimization module.

A mixed-integer model is first built using an instance of class MixedIntegerLinearProgram — and then solved using its solve method, with the solver set to GLPK:

sage: p = MixedIntegerLinearProgram(maximization=True)
sage: x = p.new_variable()
...
sage: p.solve(solver="GLPK", log="filename.log")

The overhead for installing Sage is apparently quite high, but the environment works well.

Suppressing terminal output under Python edit

Terminal output may be suppressed as follows:

import subprocess
capture = subprocess.check_output(["glpsol", "--math", "noisy.mod", "--output", "noisy.out"])
print ("complete")

Save the above scripting to a file named quiet.py and then execute it:

> python quiet.py
complete

GLPSOL's normal output is stored in capture for later use. The model's solution is saved as file noisy.out. And only the explicit print statement is sent to the console.

Similar techniques could be applied to other scripting languages, including Bash and Perl. Moreover, command-line arguments could be passed through to the final call to aid flexibility.