getting started

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

✔ Supported today
  • 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.
△ Coming later
  • True MIP via branch-and-bound — scheduled for v0.4. Until then, Integer/Binary variables are solved as their LP relaxation, with a post-solve warning. See Integer variables.
  • HiGHS backend behind a build tag — v0.3.
  • Reading .lp/.mps files (writing ships in v0.1) — v0.2.

Where to go next