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 (Julia)

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:

julia --project=notebooks_jl -e 'using Pkg; Pkg.instantiate()'

See local-setup.md for installing Julia itself and the full local workflow.

Colab Instructions

If not in a Colab notebook, continue to the next section.

  1. Work on a copy of this notebook: File > Save a copy in Drive.

  2. Make sure the runtime is set to Julia. If Colab opens a Python runtime, use Runtime > Change runtime type and select Julia.

  3. Execute the following setup cell to clone the repository when needed, activate the shared notebook project, and install dependencies. The first Colab run can take several minutes.

Notebook Cell

Activate Environment

Notebook Cell

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 using Julia tooling.

  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 4ti2 support can compute larger Graver bases.
Julia version: Julia 1.10+ with the notebook project environment instantiated.

About this notebook

This notebook performs simple Graver-basis computations. Because these computations become expensive, we recommend the excellent 4ti2 software for more complicated problems. It is an open-source implementation of several routines useful for studying integer programming through algebraic geometry. It can be used as a stand-alone library or called from C++ or Julia. In Julia, a binding is provided by lib4ti2_jll.

Introduction to GAMA

The Graver Augmentation Multiseed Algorithm (GAMA) was proposed by two papers by Alghassi, Dridi, and Tayur from the CMU Quantum Computing group. 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

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

We will be solving EXAMPLE 4 in 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\begin{array}{rll} \displaystyle \min & \displaystyle -\sum_{i = 1}^{n} \mu_{i} x_{i} + \sqrt{\frac{1 - \varepsilon}{\varepsilon} \sum_{i = 1}^{n} \sigma_{i}^2 x_{i}^2 } \\ \textrm{s.t.} & A \mathbf{x} = \mathbf{b} \\ ~ & \mathbf{x} \in \left\lbrace{}{-2, -1, 0, 1, 2}\right\rbrace{}^{n} \end{array}
f (generic function with 1 method)

Example

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)'.

First, we rewrite this problem as an unconstrained one by adding quadratic penalties for the linear constraints. Let’s define the problem parameters.

graver_basis (generic function with 1 method)
26292×25 Matrix{Int64}: 0 0 0 0 0 0 1 0 0 0 0 … 0 0 0 -1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -1 0 -1 0 0 0 0 0 1 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 -1 0 0 0 0 0 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 0 1 0 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 -1 0 0 0 0 -1 0 -2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 -1 0 0 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 … 0 0 0 0 0 -1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 -1 0 -2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 -1 0 0 0 0 0 0 ⋮ ⋮ ⋮ ⋱ ⋮ ⋮ 2 -1 0 0 0 -1 0 0 0 2 0 … -2 0 0 0 0 0 0 1 -1 1 2 -1 0 0 0 -2 0 0 0 2 0 -2 0 0 0 0 0 0 1 0 1 2 -1 0 0 0 0 0 0 0 2 0 -2 0 0 0 0 0 0 1 -2 1 2 -2 0 0 0 0 0 0 0 2 0 -2 0 0 0 0 0 0 0 -1 2 2 -2 0 0 0 -1 0 0 0 2 0 -2 0 0 0 0 0 0 0 0 2 2 -1 0 0 0 -1 0 0 0 2 0 … -2 0 0 0 0 0 0 0 -1 2 2 -1 0 0 0 -2 0 0 0 2 0 -2 0 0 0 0 0 0 0 0 2 2 -1 0 0 0 0 0 0 0 2 0 -2 0 0 0 0 0 0 0 -2 2 2 -2 0 0 0 0 0 0 0 1 0 -2 0 0 0 0 0 0 0 -1 2 2 -2 0 0 0 -1 0 0 0 1 0 -2 0 0 0 0 0 0 0 0 2 2 -1 0 0 0 -1 0 0 0 1 0 … -2 0 0 0 0 0 0 0 -1 2 2 -1 0 0 0 -2 0 0 0 1 0 -2 0 0 0 0 0 0 0 0 2
greedy_rule (generic function with 1 method)
single_move_rule (generic function with 4 methods)
augmentation (generic function with 2 methods)

First, we will prove our augmentation strategies, either best or greedy, and for that last case, 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
  1.256934 seconds (15.10 M allocations: 3.295 GiB, 4.59% gc time, 51.98% compilation time)
21, iterations
solution: [-2, 1, 1, 0, 0, 0, 1, 2, 0, -1, 2, 2, 0, 0, -1, -1, 0, 1, 1, 2, 0, 0, 0, 2, 0]
objective: 6.922060643119407
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
  0.184747 seconds (896.77 k allocations: 187.886 MiB, 3.65% gc time, 78.80% compilation time)
34, iterations
solution: [2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 1, 0, 0, -1, -1, 1, 0, 0, 1, 0, 0, 0, 2, -2]
objective: 2.2955158390660966
Greedy-augmentation: Choosing among the first element of G that with a single step reduces the objective
  0.484587 seconds (1.12 M allocations: 120.456 MiB, 2.63% gc time, 94.80% compilation time)
31, iterations
solution: [2, 2, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 0, 0, 0, -1, 1, 0, 0, 1, -1, 0, 0, 2, -2]
objective: 2.329468812366131
get_feasible (generic function with 1 method)
20 feasible solutions found.

We take 20 samples using DWave.jl’s local simulated annealing backend and notice that most (if not all of them) are feasible and different. If DWave.jl is not available in the active environment, the notebook loads the committed feasible starts from notebooks_data/3-GAMA_example4_feasible_starts.csv so the augmentation sections still run. D-Wave QPU workflows require a D-Wave Leap account and DWAVE_API_TOKEN, but this section does not contact a QPU. Let’s now apply the augmentation procedure to each one of them and record the final objective and the number of iterations it takes. Here we will use the 3rd augmentation strategy (Greedy) 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

Notice that we reach the globally optimal solution regardless of the initial point and that even if the initial objective was closer to the optimal objective function, it might take more iterations to reach the optimum.

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

Image produced in Jupyter

Here we can barely improve the objective and can only perform a few iterations before we cannot improve the solution. But if we compare the runtimes in both cases, we find that...

Image produced in Jupyter

...the time to do augmentation only having 10 choices is minimal. We can search for a sweet spot in between, with good solutions and little time.

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.

  • Applied augmentation directions 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: