Grove, in one sitting.
grove is a pure-Go linear-programming library. Model an LP with named variables and operator-style constraints, hand it to a built-in two-phase simplex solver, and read back duals and reduced costs — with zero CGO and no system libraries.
These docs are written to be read in order: each page leaves you with a mental model the next page builds on. If you are already familiar with PuLP or pyomo, you can skim the first two pages and land in sensitivity and integer variables, which is where grove’s personality actually shows up.
What you install
grove is a single Go module with no cgo and no transitive dependencies outside the standard library. Go 1.22 or newer.
go get github.com/jakenherman/grove
That is the entire setup. No apt-get, no
brew, no LD_LIBRARY_PATH dance.
Your first problem
Here is a two-variable shift model from the package README.
Reading it top-to-bottom gives you the shape of every grove
program: create a Problem, declare variables, set
the objective, add constraints, call Solve.
package main
import (
"fmt"
"github.com/jakenherman/grove"
)
func main() {
prob := grove.NewProblem("nurses", grove.Maximize)
day := prob.NewVar("day", grove.Continuous, grove.Bounds(0, grove.Inf))
night := prob.NewVar("night", grove.Continuous, grove.Bounds(0, grove.Inf))
prob.SetObjective(grove.Expr{day: 1, night: 1})
prob.AddConstraint("day_min", grove.Expr{day: 1}, grove.GTE, 4)
prob.AddConstraint("night_min", grove.Expr{night: 1}, grove.GTE, 2)
prob.AddConstraint("budget", grove.Expr{day: 1200, night: 1500}, grove.LTE, 18000)
res, err := prob.Solve()
if err != nil {
panic(err)
}
fmt.Println(res.Status, res.Objective)
fmt.Println("day:", res.Value(day))
fmt.Println("night:", res.Value(night))
}
Running it prints:
Optimal 14.5
day: 12.5
night: 2
Everything else in these docs is a deeper look at one piece of the above: how to build a bigger model, how to read the result object, what the numbers in the sensitivity report mean, and the one surprise you must know about if you declare variables as Integer or Binary before branch-and-bound lands in v0.4.
What is (and is not) in v0.1
- Continuous LPs with arbitrary (finite or infinite) bounds.
- LTE, GTE, and EQ constraints; free variables.
- Two-phase simplex with Bland’s anti-cycling rule.
- Duals, reduced costs, slacks, and a structured sensitivity report.
- CPLEX-LP and MPS export.
- True MIP via branch-and-bound — scheduled for v0.4. Until then,
Integer/Binaryvariables are solved as their LP relaxation, with a post-solve warning. See Integer variables. - HiGHS backend behind a build tag — v0.3.
- Reading
.lp/.mpsfiles (writing ships in v0.1) — v0.2.
Where to go next
- Problems, variables, constraints — the whole modeling DSL on one page.
- Solve and Result — the shape of the thing you get back.
- Examples — three fully worked models you can run.