Calling a Linear Solver C Library from Go

I wrote the golp library to provide Go bindings via cgo for the LP_Solve open source Mixed Integer Linear Programming (MILP) solver.

The glue between Go and C

Here's the part of golp's lp.go that let's it compile for and link to LP Solve's C library:

/*
#cgo linux CFLAGS: -I./lpsolve
#cgo linux LDFLAGS: -L./lpsolve -llpsolve55 -Wl,-rpath=./lpsolve
#cgo darwin CFLAGS: -I/opt/local/include/lpsolve
#cgo darwin LDFLAGS: -L/opt/local/lib -llpsolve55
#include "lp_lib.h"
...
*/
import "C"  
...

The import "C" is similar to a normal Go import in that it brings in the C package with functions you can call, but cgo also interprets the comment above import "C" in a special way as follows.

The #cgo lines with CFLAGS and LDFLAGS specify flags for the C compiler and linker respectively. The darwin and linux are Go build constraints that specify which platform those flags apply to.

The linux flags indicate that the compiler should search ./lpsolve for the header and library file, and the -Wl-rpath=./lpsolve will cause the resulting executable to search the ./lpsolve folder for lpsolve55.so to dynamically link to it. The Darwin build flags are designed to work with the version of LPSolve that will be installed by MacPorts.

After the #cgo lines, the rest of the comment is interpreted as a snippet of C code, which can include headers and can even define new functions in C for the Go code to call.

Using C types

With the initial glue in place, we can reference C types with C.[any type defined in your C snippet]. For instance, golp's wrapper LP type looks like this, where lprec is the type name for a linear program instance in LP Solve:

type LP struct {  
  ptr *C.lprec
}

Calling C functions

Calling C functions from Go is also as easy as calling C.[c function]. Here's golp's NewLP that returns a new linear program instance by calling C.make_lp (where make_lp is a function in lp_lib.h):

func NewLP(rows, cols int) *LP {  
  l := new(LP)
  l.ptr = C.make_lp(C.int(rows), C.int(cols))
  runtime.SetFinalizer(l, deleteLP)
  ...
  return l
}

Notice the runtime.SetFinalizer call. In Go, objects are garbage collected, so we don't need to explicitly free them, but make_lp is a C function and allocates memory outside of Go's garbage-collecting watch. Thus we should free it by calling LP Solve's delete_lp (which the Go wrapper deleteLP does).

The effect of doing the delete in the finalizer is that we can now use the Go LP wrapper object without needing to worry about calling the cleanup function explicitly as the Go runtime will call it right before it garbage collects the wrapper object.

Another thing to note are the type casts like C.int(rows), which are needed because int in Go and C are considered different types and could be different sizes depending on the platform and compilers.

Installing golp

To use the golp library, you'll need to:

1. Get the golp Go code

go get github.com/draffensperger/golp  

2. Get LPSolve

For Linux, download the build from SourceForge and extract it to ./lpsolve. E.g. for 64-bit,

LP_URL=https://sourceforge.net/projects/lpsolve/files/lpsolve/5.5.2.0/lp_solve_5.5.2.0_dev_ux64.tar.gz  
mkdir lpsolve  
curl -L $LP_URL | tar xvz -C lpsolve  

For OS X, install MacPorts then sudo port install lp_solve. There are Windows builds on SourceForge as well.

Using it in your project

Here's a sample program that uses golp to solve a simple linear program,
based on an example from the LPSolve documentation:

package main

import "fmt"  
import "github.com/draffensperger/golp"

func main() {  
  lp := golp.NewLP(0, 2) // 0 rows by 2 columns
  lp.AddConstraint([]float64{110.0, 30.0}, golp.LE, 4000.0)
  lp.AddConstraint([]float64{1.0, 1.0}, golp.LE, 75.0)
  lp.SetObjFn([]float64{143.0, 60.0})
  lp.SetMaximize()

  lp.Solve()
  vars := lp.Variables()
  fmt.Printf("Plant %.3f acres of barley\n", vars[0])
  fmt.Printf("And  %.3f acres of wheat\n", vars[1])
  fmt.Printf("For optimal profit of $%.2f\n", lp.Objective())

  // No need to explicitly free underlying C structure as
  // golp.LP finalizer will
}

Outputs:

Plant 21.875 acres of barley  
And  53.125 acres of wheat  
For optimal profit of $6315.62  

Other Linear Programming options for Go

LPSolve is LGPL licensed, which allows it to be called from closed-source projects. It also supports solving mixed-integer programming problems (where some variables must be integers) using branch-and-bound. See the golp README for an mixed-integer example.

There are also Go bindings for the GPL-licensed GNU Linear Programming Kit (GLPK) at github.com/lukpank/go-glpk, which would be suitable if your project has a GPL-compatible open source license.

There are a number of other commercial and open-source solvers like CBC, CLP, GLOP, Gurobi, CPLEX, SCIP, and Sulum. Google provides a unified SWIG compatible C++ interface for all those solvers in their or-tools project.

There is Go support for SWIG bindings, so it should be possible to write a wrapper that would connect to those other solvers via the or-tools library. It would involve some additional glue code compared to calling a C library with cgo though.