https://github.com/denis-bz/glp
numpy <--> GLPK Keywords: linear programming, python, numpy, scipy, GLPK 22 Oct
https://github.com/denis-bz/glp
glpk linear-programming numpy pyglpk python scipy
Last synced: 2 months ago
JSON representation
numpy <--> GLPK Keywords: linear programming, python, numpy, scipy, GLPK 22 Oct
- Host: GitHub
- URL: https://github.com/denis-bz/glp
- Owner: denis-bz
- Created: 2019-10-22T08:37:32.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2019-10-29T12:07:23.000Z (over 5 years ago)
- Last Synced: 2025-01-08T03:11:16.910Z (4 months ago)
- Topics: glpk, linear-programming, numpy, pyglpk, python, scipy
- Language: Python
- Homepage:
- Size: 39.1 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
#### glp: numpy <--> GLPK, the gnu linear programming kit
Keywords, tags: linear programming, python, numpy, scipy, GLPK, GMPL, bridge
`glp` is a lightweight bridge between
the Gnu Linear Programming Kit [GLPK](https://www.gnu.org/software/glpk/)
and the numpy-scipy-python world:numpy/scipy arrays <--> LP( A b c ... ) <--> LP files
* numpy and scipy help construct large sparse LP models, and connect to dozens of specialized tools
* GLPK reads and writes LP models in several formats: .mod, .lp (aka cplex), .mpsRequirements: [GLPK](https://www.gnu.org/software/glpk/) and
[PyGLPK](https://github.com/bradfordboyle/pyglpk) -- see "installing" below.#### Example: file -> numpy --
import glp
lp = glp.load_lp( ".../my.mps" ) # an LP record with lp.A lp.b lp.c ..., see below#### Example: numpy -> file -> solver --
# make numpy/scipy arrays A b c ... that describe your problem
# and put them in a Bag, see "LP standard form" and "LP()" below --
lp = glp.LP( A= b= c= blo= lb= ub= )# save A b c ... in a text file --
gnulp = glp.save_lp( "my.lp", lp ) # or my.mps or my.glp# run my.lp -> your solver in a shell or a python subprocess
# read, plot the solution file#### LP standard form: `A b c blo lb ub`
In `glp`, LP problems are described by a record (struct, Bag)
with 6 numpy arrays `A, b, c, blo, lb, ub`:minimize c . x
subject to
blo <= Ax <= b # row bounds, default Ax <= b
lb <= x <= ub # column (variable) bounds, default 0 <= xIn this form, the row bounds `b blo` and column (variable) bounds `lb ub`
are nicely symmetric,
and cover all combinations of 1-sided and 2-sided bounds. For `lb <= x <= ub`:Lower only: lb .. inf -- no upper bound
Upper only: -inf .. ub -- no lower bound
Both: lb .. ub
Equal: lb == ub
no bounds: -inf .. infRow bounds `blo <= Ax <= b` have the same 5 cases.
(Rows with no bounds are irrelevant, so `glpsol` removes them, `lp_to_linprog` too.)#### The main data structure: `LP( A, b, c ... )`
lp = glp.LP( A, b, c, blo=-np.inf, lb=0, ub=np.inf, problemname="name" )
puts `A b c ...` into a `Bag` with fields `lp.A lp.b ...`.
`A` is made [sparse](https://docs.scipy.org/doc/scipy/reference/sparse.html)
if it isn't already.
Bounds arrays `b blo lb ub` are expanded to numpy arrays of float64
of the appropriate size.
These may have `+-inf`, but no `None`, no `NaN`.
`LP( ... blo=-np.inf )`, the default, constrains all rows `Ax <= b`;
`LP( ... blo=b )` constrains `Ax = b`.(A Bag aka Dotdict aka flexible struct
is a dict with `bag.key` short for `bag["key"]` --
easier to pass around than long arg lists,
and `bag.` in IPython shows you the fields.
See the 5-line `class Bag( dict )` in `zutil.py`.)#### File and function overview
lprec.py
def LP( A, b, c, blo=-inf, lb=0, ub=inf,
def print_lp( lp, verbose=1, header="", footer="" ):load_lp.py
def load_lp( lpfile, to="lp", verbose=1 ):
""" GLPK file .mod .mps ... to= "lp" | "linprog" | "glpk"
def save_lp( outfile, lp ):
""" LP() or gnulp -> outfile .mod .mps ... """lp_gnu.py
def gnulp_to_lp( gnulp, drop_uncon=True, verbose=1 ):
""" gnulp .matrix .rows .cols .obj -> LP( A b c ... ) numpy/scipy arrays
def lp_to_gnulp( lp, verbose=1 ):
""" LP( A b c blo lb ub ... ) -> glpk .matrix .rows .cols ...
# def gnulp_solve( gnulp, solver="interior", verbose=1 ):
# better save_lp | glpsol | load_lplp_linprog.py
def lp_to_linprog( lp, verbose=1 ):
""" LP( A b c ... ), see lprec.py
def linprog_to_lp( linp, name="noname" ):
""" test-loopback.py: lp_to_linprog, linprog_to_lp
def rows_le( A, b, blo ):
""" -inf <= Ax <= inf: drop these rows, unconstrained
def split_Ab( A, b, blo ):
""" -> Bag( A_ub b_ub A_eq b_eq )zutil.py
class Bag( dict ):
""" a dict with d.key short for d["key"], aka Dotdict """
def scan_args( sysargv, globals_, locals_ ):
""" run my.py [a=1 b=None 'c = expr' ...] [file* ...] in shell or IPython#### Notes on GLPK and `glpsol`
GLPK reads and writes LP problems in various formats:
* .lp aka CPLEX format
* .mps, an old format used for e.g. Matlab <-> solvers
* .glp, easy to parse in python or awk
* .mod, the gnu MathProg language [GMPL](https://en.wikibooks.org/wiki/GLPK/GMPL_(MathProg)).
This is roughly a subset of the [AMPL](https://ampl.com) language.`glp.save_lp( "my.mod", lp )` writes a `.mod` file similar to `.lp` aka cplex
which is readable in AMPL -- in simple cases, mttiw.GLPK's solver `glpsol` has over 50 options, for simplex, interior-point, and mixed-integer (MIP).
It carries the user's constraint names and variable names through to solution files;
this is essential for making solutions to big problems understandable --
splitting, sorting, plotting thousands of variables.
The simplex solver (not ip ?) does preprocessing to make problems smaller,
in some cases a good deal smaller.To check a file, `glpsol --check --format my.file`.
To translate e.g. `.mps` to `.lp`, `glpsol --check --freemps in.mps --wlp out.lp`.
(`in.mps.gz` or `out.lp.gz` compress / uncompress with `gzip` on the fly.)`glp` has a minimal `glp_solve()`, but I prefer to run `glpsol` with files, like this:
ipython: ... save_lp( "my.glp", LP( A, b, c ... ))
glpsol --options ... my.glp -w my.sol --log my.glpsollog # or whatever solver you like
# see glpsol -h and bin/glpsols
ipython: ... parse my.glp and my.sol, plot#### Look at LP in the whole flow, input - optimize - output
For real-world optimization problems,
an LP solver (optimizer) is only part of a flow, a cycle, a process:* input: map a problem to a sea of numbers `A b c ...`
* run the LP solver -> a sea of numbers `x[...]`
* map back: make the solution `x[...]` *understandable*, with plots and talks
* (sotto voce) check that there are no mistakes along the way.Experts say that commercial solvers are much faster than GLPK,
but GLPK may be fast enough for *your* problem.#### Installing glpk and pyglpk
# first glpk:
download and unpack https://www.gnu.org/software/glpk/.../glpk-4.65.tar.gz
configure --prefix=/opt/local; make; make install# pyglpk:
pip install --user git+https://github.com/bradfordboyle/pyglpk
# (git clone gets examples/*.py too)# test:
ipython
import glpk # i.e. pyglpk
print glpk.__file__ # .../glpk.so
gnulp = glpk.LPX() # empty
gnulp = glpk.LPX( gmp="xx.mod" ) # Reading ... Generating ...#### Links
[GLPK](https://www.gnu.org/software/glpk/)
[PyGLPK on github](https://github.com/bradfordboyle/pyglpk)
[PyGLPK brief reference](https://github.com/bradfordboyle/pyglpk/blob/master/docs/brief-reference.rst)
[scipy.sparse](https://docs.scipy.org/doc/scipy/reference/sparse.html)
[LPsolve FAQ](http://lpsolve.sourceforge.net/5.0/LinearProgrammingFAQ.htm)
[LPsolve doc on file formats](http://lpsolve.sourceforge.net/5.5/formulate.htm)
[Numerical Recipes p. 537-549: Linear Programming Interior-Point Methods](http://apps.nrbook.com/empanel/index.html?pg=537)
GLPK runtimes for Netlib and other test cases are under [my gists](https://gist.github.com/denis-bz)#### Comments welcome, test cases welcome.
cheers
-- denis 29 October 2019