Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Graver Augmentation Multiseed Algorithm (Python)

Maintained by the JuliaQUBO organization
SECQUOIA  ·  PSR Energy

Open In Colab

Setup

Google Colab

Click the badge above to open this notebook in Colab. The notebook installs or activates dependencies in the setup cells below.

Local installation

Run the following from the repository root before opening this notebook locally:

python -m pip install matplotlib numpy scipy

The notebook uses bundled Graver data by default; optional 4ti2 tooling is only needed when regenerating bases.

Learning objectives

By the end of this notebook you will be able to:

  1. Explain how Graver-basis directions can augment feasible integer solutions.

  2. Run the GAMA workflow on a nonlinear integer programming instance with bundled or computed Graver data.

  3. Compare complete and sampled Graver bases across objective improvement, iterations, and runtime.

  4. Interpret the speed-quality tradeoff between exact augmentation data and smaller sampled direction sets.

Prerequisites

Mathematical background: Integer programming, feasible sets, nonlinear objective functions, and basic runtime comparisons.
Prior notebooks: Notebook 2 (QUBO) and the binary/integer modeling ideas from Notebook 1.
Accounts required: None; optional Py4ti2int32 or 4ti2 support can compute Graver bases instead of using bundled data.
Python version: Python 3.9+ with NumPy, Matplotlib, and the repository’s GAMA dependencies.

Environment and execution notes

For local execution from the repository root, run uv sync --group qubo before launching Jupyter so the Python scientific stack used below is available. If Py4ti2int32 is installed, the notebook computes the Graver basis directly; otherwise it falls back to the bundled graver.npy file so the rest of the workflow still runs.

In Google Colab, the notebook reuses the bundled graver.npy file whenever Py4ti2int32 is unavailable, which keeps the student-facing workflow focused on the GAMA steps rather than on an external 4ti2 installation.

Introduction to GAMA

The Graver Augmentation Multiseed Algorithm (GAMA) was proposed by Alghassi, Dridi, and Tayur in the works listed in Reference [1] and Reference [2]. The three main ingredients of this algorithm, designed to solve integer programs with linear constraints and nonlinear objective, are:

  • Computing the Graver basis (or a subset of it) of an integer program.

  • Performing an augmentation.

  • Initializing the algorithm from several points because Graver augmentation is guaranteed to find a global optimum only for certain objective functions.

This algorithm can be adapted to take advantage of Quantum Computers by leveraging them as black-box Ising/QUBO problem solvers. In particular, obtaining several feasible solution points for the augmentation and computing the kernel of the constraint matrix can be posed as QUBO problems. After obtaining these solutions, other routines implemented in classical computers are used to solve the optimization problems, making this a hybrid quantum-classical algorithm.

Introduction to Graver basis computation

This notebook makes simple computations of Graver basis. Because of the complexity of these computations, we suggest that for more complicated problems you install the excellent 4ti2 software, an open-source implementation of several routines useful for the study of integer programming through algebraic geometry. It can be used as a stand-alone library and called from C++ or from Python. In Python, there are two ways of accessing it, either through Sage or by compiling 4ti2 directly and installing a Python wrapper.

A Graver basis is defined as

G(A)=⋃jHj(A)\mathcal{G}(\mathbf{A}) = \bigcup_{j} \mathcal{H}_{j}(\mathbf{A})

where Hj(A)\mathcal{H}_{j}(\mathbf{A}) are the minimal Hilbert basis of A\mathbf{A} in each orthant.

Equivalently we can define the Graver basis as the ⊑\sqsubseteq-minimal set of a lattice

L(A)={x:Ax=0,x∈Zn}∖{0}=ker⁡A∩Zn\mathcal{L}(\mathbf{A}) = \left\lbrace{}{\mathbf{x} : \mathbf{A} \mathbf{x} = 0, \mathbf{x} \in \mathbb{Z}^{n}}\right\rbrace{} \setminus \left\lbrace{}{0}\right\rbrace{} = \ker A \cap \mathbb{Z}^{n}

where the partial ordering x⊑y\mathbf{x} \sqsubseteq \mathbf{y} holds whenever xiyi≥0x_i y_i \geq 0 and ∣xi∣≤∣yi∣\left\vert x_i \right\vert \leq \left\vert y_i \right\vert for all ii.

Here we do not interact with quantum hardware. Instead, we obtain a problem’s Graver basis with 4ti2 and study how the search behaves when only a subset of that basis is available.

Problem statement

By default, the GAMA computation below uses Example 4 from the code, which corresponds to Case 2 in the original GAMA paper. The problem is derived from finance and deals with the maximization of expected returns on investments and the minimization of the variance.

min⁡−∑i=1nμixi+1−ϵϵ∑i=1nσi2xi2s.t. Ax=b,  x∈{−2,−1,0,1,2}n\min -\sum_{i=1}^n \mu_i x_i + \sqrt{\frac{1-\epsilon}{\epsilon}\sum_{i=1}^n \sigma_i^2 x_i^2} \\ \text{s.t. } A x = b, \; x \in \{-2,-1,0,1,2 \}^n

This particular instance of convex INLP has n=25n=25, ϵ=0.01\epsilon=0.01, μi=Rand[0,1]\mu_i = \text{Rand}[0,1], and σi=Rand[0,μi]\sigma_i = \text{Rand}[0,\mu_i]. The matrix AA is a matrix with 1’s and 0’s and the bb vector is half of the sum of the rows of AA, in this case b=(9,8,7,5,5)⊤b = (9,8,7,5,5)^\top.

To make this Example 4 run reproducible, the notebook loads one fixed realization of the coefficient vectors from notebooks_data/3-GAMA_example4_coefficients.csv, reuses precomputed feasible starts from notebooks_data/3-GAMA_example4_feasible_starts.csv, and traverses a deterministic Graver-direction permutation. The remaining GAMA method below uses this saved Example 4 instance: complete-basis greedy augmentation, a 10-direction partial-basis greedy augmentation, and several larger fractions of the basis.

The Graver basis of this matrix AA has 29789 elements, which on a standard laptop using 4ti2 takes about 5 seconds to compute. When Py4ti2int32 is available, the notebook computes it directly; otherwise it reuses the bundled graver.npy file.

Class examples

The original GAMA class notebook used EXAMPLE = 1, EXAMPLE = 2, EXAMPLE = 3, and EXAMPLE = 4. Examples 1-3 are retained here as class-reference problem definitions. By default, the executable GAMA workflow below continues with the saved Example 4 portfolio instance.

Example 1: illustrative Graver augmentation

This example uses

A=[1111151025],b=[21156],x(0)=[11532].A = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & 5 & 10 & 25 \end{bmatrix},\quad b = \begin{bmatrix} 21 \\ 156 \end{bmatrix},\quad x^{(0)} = \begin{bmatrix} 1 & 15 & 3 & 2 \end{bmatrix}.

The variable bounds are 0≤xi≤150 \le x_i \le 15. In the original class notebook, the objective is to minimize distance to 5.

Example 2: four-variable linear example

This example uses

A=[11111234],b=[1021],c=[0102],x(0)=[1801].A = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 1 & 2 & 3 & 4 \end{bmatrix},\quad b = \begin{bmatrix} 10 \\ 21 \end{bmatrix},\quad c = \begin{bmatrix} 0 & 1 & 0 & 2 \end{bmatrix},\quad x^{(0)} = \begin{bmatrix} 1 & 8 & 0 & 1 \end{bmatrix}.

The variable bounds are 0≤xi≤210 \le x_i \le 21.

Example 3: alternate four-variable linear example

This example uses

A=[11110123],b=[1015],c=[131417],x(0)=[3061].A = \begin{bmatrix} 1 & 1 & 1 & 1 \\ 0 & 1 & 2 & 3 \end{bmatrix},\quad b = \begin{bmatrix} 10 \\ 15 \end{bmatrix},\quad c = \begin{bmatrix} 1 & 3 & 14 & 17 \end{bmatrix},\quad x^{(0)} = \begin{bmatrix} 3 & 0 & 6 & 1 \end{bmatrix}.

The variable bounds are 0≤xi≤100 \le x_i \le 10.

Example 4: saved portfolio instance

Let

A=[11111111110101010111010101111010100100010010111111010001010110110110010011100000001011101111001000000111110001000100000101010]A = \begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 & 1 & 1 & 1 & 0 & 1 & 0 & 1 & 0 \\ 1 & 1 & 1 & 1 & 0 & 1 & 0 & 1 & 0 & 0 & 1 & 0 & 0 & 0 & 1 & 0 & 0 & 1 & 0 & 1 & 1 & 1 & 1 & 1 & 1 \\ 0 & 1 & 0 & 0 & 0 & 1 & 0 & 1 & 0 & 1 & 1 & 0 & 1 & 1 & 0 & 1 & 1 & 0 & 0 & 1 & 0 & 0 & 1 & 1 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 1 & 1 & 1 & 0 & 1 & 1 & 1 & 1 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 1 & 1 & 1 & 1 & 1 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 1 & 0 & 1 & 0 \\ \end{bmatrix}

This particular instance of convex INLP has m=5m = 5, n=25n = 25, ε=0.01\varepsilon = 0.01, μi=rand[0,1]\mu_{i} = \textrm{rand}[0, 1], σi=rand[0,μi]\sigma_{i} = \textrm{rand}[0, \mu_{i}]. A∈Bm×nA \in \mathbb{B}^{m \times n} and each bjb_{j} is half the sum of the jj-th row of AA. In this example, b=(9,8,7,5,5)′\mathbf{b} = \left({9, 8, 7, 5, 5}\right)'.

The remaining GAMA method below uses this saved Example 4 instance: complete-basis greedy augmentation, a 10-direction partial-basis greedy augmentation, and several larger fractions of the basis.

First we would write this problem as an unconstrained one by penalizing the linear constraints as quadratics in the objective. Let’s first define the problem parameters.

To switch among the class examples, change SELECTED_GAMA_EXAMPLE in the next code cell to 1, 2, 3, or 4, then rerun the notebook from that cell downward. Example 4 remains the default because it is the saved portfolio instance used for the executed results below.

Running Example 4: saved portfolio instance with A shape (5, 25) and 25 variables.
Py4ti2int32 is not available locally; loading the bundled graver.npy instead.

First, we will compare our augmentation strategies, either best or greedy, and for greedy, either computing the best step or a single move. In the order that was mentioned, the augmentation will take more iterations, but each one of the augmentation steps or iterations is going to be cheaper.

Best-augmentation: Choosing among the best step that each element of G can do (via bisection), the one that reduces the most the objective
6.938488156 seconds
8 iterations
solution: -2.880981111528471 [ 0  0  1  1  0  0  2 -1  0  1  1  1  0  0  1  2  0  0  0  0  1  0  0  2
  2]
Greedy-best-augmentation: Choosing among the best step that each element of G can do (via bisection), the first one encountered that reduces the objective
1.0568433070000012 seconds
42 iterations
solution: -2.880981111528471 [ 0  0  1  1  0  0  2 -1  0  1  1  1  0  0  1  2  0  0  0  0  1  0  0  2
  2]
Greedy-augmentation: Choosing among the first element of G that with a single step reduces the objective
0.2724166659999998 seconds
42 iterations
solution: -2.880981111528471 [ 0  0  1  1  0  0  2 -1  0  1  1  1  0  0  1  2  0  0  0  0  1  0  0  2
  2]

Now we can highlight another feature of the algorithm, computing starting feasible solutions. For this case we formulate a QUBO and solve it via annealing. In this particular case, we will not encode the integer variables and will only look for feasible solutions with x∈{0,1}nx \in \{0,1\}^n.

Notebook Cell

QUBO formulation for feasible starting points

We now formulate a QUBO to generate diverse feasible starting points for the augmentation stage. In this part of the notebook, we only search over binary points with x∈{0,1}nx \in \{0,1\}^n before passing those feasible candidates to the classical augmentation routine.

To do that, we minimize the squared residual of the linear constraints over binary points:

min⁡x∈{0,1}n∥Ax−b∥22=min⁡x∈{0,1}nx⊤A⊤Ax−2b⊤Ax+b⊤b.\min_{x \in \{0,1\}^n} \|Ax - b\|_2^2 = \min_{x \in \{0,1\}^n} x^\top A^\top A x - 2 b^\top A x + b^\top b.

The constant term b⊤bb^\top b does not affect the minimizers, so the notebook stores it as an offset and builds the binary quadratic model from Q = A^\top A + \operatorname{diag}(-2 b^\top A). Any sample with energy 0 is therefore a feasible starting point for the augmentation stage.

We use simulated annealing here because the goal is not a single feasible point but a diverse set of feasible starts. A MIP solver would typically stop after one optimum, while annealing can return several distinct zero-energy states of the same QUBO in one run.

Loaded 20 precomputed feasible solutions from 3-GAMA_example4_feasible_starts.csv.
Shared Graver-order file not found; generating a deterministic permutation with seed 271828.
20  feasible solutions ready.

By default the notebook loads the committed feasible starts from notebooks_data/3-GAMA_example4_feasible_starts.csv, so the experiment begins from the same saved 20 binary points on every run. If that file is unavailable, the notebook falls back to simulated annealing and regenerates the starts with the local Ocean stack. We now apply the augmentation procedure to each feasible point and record the final objective, the runtime, and the number of iterations it takes. Here we use the greedy single-move augmentation rule because of runtime.

We record the initial objective function, the one after doing the augmentation, and the number of augmentation steps.

Image produced in Jupyter

With the complete Graver basis, every starting point improves substantially, but the final objective values can still differ. Here, complete basis refers to the direction set; the augmentation rule still uses greedy single-move selection for runtime on this nonseparable convex objective, so different feasible starts can terminate at different local optima.

Now let’s try an extreme case, where we only have 10 of the elements of the Graver basis.

Image produced in Jupyter

With only 10 Graver directions, the augmentation runs quickly but often stalls after only a few improving moves.

Image produced in Jupyter

This speed/quality tradeoff motivates the next experiment, which samples larger fractions of the Graver basis to look for a better middle ground.

Image produced in Jupyter
Image produced in Jupyter
Image produced in Jupyter

Practice checkpoints

Use these checkpoints during the workshop to test the main ideas before moving on.

Notebook Cell
start = (2, 1); path = [(2, 1), (1, 2)]; objectives = [9, 6]
Notebook Cell
first_improving = (5.0, "move_a"); best_improving = (2.0, "move_b")
Notebook Cell
old_gains = [3.0, -3.0, 1.0]; new_gains = [-3.0, 3.0, 4.0]

Summary

In this notebook we:

  • Introduced Graver-basis augmentation as a structured approach for improving feasible integer solutions.

  • Loaded or computed augmentation directions and applied them to multiple feasible starts for a nonlinear integer program.

  • Compared complete and sampled direction sets using objective values, iteration counts, and runtime plots.

  • Used the experiments to reason about when smaller direction subsets may be faster but less reliable.

Learning objectives met: You practiced explaining Graver augmentation, running the GAMA workflow, comparing full and sampled bases, and interpreting the resulting runtime-quality tradeoff.

Next steps: Proceed to Notebook 4: D-Wave to see how QUBO models can be sent to quantum annealing hardware or a local fallback sampler.

Further reading:

References

Acknowledgments

This notebook was developed by: