Skip to main content

optimally compile arbitrary two-qubit unitaries

Project description

GULPS

Python PyPI - Version CI Release DOI ecosystem Open In Colab

GULPS (Global Unitary Linear Programming Synthesis) is the first open tool that robustly compiles arbitrary two-qubit unitaries optimally into non-standard instruction sets.

Most existing compilers only target CNOT gates. Analytical rules exist for a few special cases like fractional CNOT (XX family), Berkeley (B), and $\sqrt{\text{iSWAP}}$, but nothing more general. Numerical methods can in principle handle arbitrary gates, but they are slow, unreliable, and do not scale as instruction sets grow. GULPS fills this gap by combining linear programming with lightweight numerics to achieve:

  • Support for fractional, continuous, or heterogeneous gate sets.
  • Scalability to larger ISAs, unlike black-box numerical methods.
  • A fast, practical tool integrated with Qiskit if you study gate compilation from two-body Hamiltonians or parameterized unitary families.

📌 Read the preprint: Two-Qubit Gate Synthesis via Linear Programming for Heterogeneous Instruction Sets

[!IMPORTANT] GULPS is a general-purpose numerical method. If your ISA has a known analytical decomposition (e.g., Qiskit's XXDecomposer for CX/RZX families), prefer that - specialized solvers will always be faster and more precise for the gates they target. GULPS is for everything else.


Getting Started

pip install gulps

Optional extras:

Extra Install What it adds
monodromy pip install -r requirements-monodromy.txt Precomputes monodromy polytope coverage sets for direct lookup. Uses a fork. Also requires lrslib (sudo apt-get install lrslib).
cplex pip install "gulps[cplex]" CPLEX-based continuous LP solver. Works but slower than the discrete path.
dev pip install "gulps[dev]" Plotting, Jupyter, linting, etc.
test pip install "gulps[test]" Adds pytest.

Qiskit Transpiler Plugin

If your backend's ISA is already defined in a Qiskit Target, GULPS works as a drop-in translation stage plugin:

from qiskit import transpile

output_qc = transpile(input_qc, target=my_target, translation_method="gulps")

Custom ISA

For full control, define your ISA manually. Gate costs must be additive (e.g., normalized durations where fractional gates cost proportionally to their basis gate).

from qiskit.circuit.library import iSwapGate
from gulps import GulpsDecomposer
from gulps.core.isa import DiscreteISA

isa = DiscreteISA(
    gate_set=[iSwapGate().power(1 / 2), iSwapGate().power(1 / 3)],
    costs=[1 / 2, 1 / 3],
    names=["sqrt2iswap", "sqrt3iswap"],
)
decomposer = GulpsDecomposer(isa=isa)

Once initialized, call the decomposer with a Qiskit Gate or a 4x4 np.ndarray:

from qiskit.quantum_info import random_unitary

u = random_unitary(4, seed=0)
v = decomposer(u)
v.draw()

To compile a full QuantumCircuit, use the TransformationPass. Because GULPS leaves single-qubit gates unsimplified, append Optimize1qGatesDecomposition to rewrite them:

from gulps import GulpsDecompositionPass
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.circuit.random import random_circuit

input_qc = random_circuit(4, 4, max_operands=2)
pm = PassManager(
    [
        GulpsDecompositionPass(decomposer),
        Optimize1qGatesDecomposition(basis="u3"),
    ]
)
output_qc = pm.run(input_qc)
output_qc.draw("mpl")

Overview of the Decomposition Process

The decomposition begins by identifying the cheapest feasible basis gate sentence (a sequence of native gates sufficient to construct the target unitary). We use monodromy polytopes to describe the reachable space of canonical invariants for each sentence in the ISA.

For example, this ISA:

from gulps.core.isa import DiscreteISA

isa = DiscreteISA(
    gate_set=[iSwapGate().power(1 / 2), iSwapGate().power(1 / 3)],
    costs=[1 / 2, 1 / 3],
    names=["sqrt2iswap", "sqrt3iswap"],
    precompute_polytopes=True,
)

has the following coverage set:

from gulps.core.coverage import coverage_report

coverage_report(isa.coverage_set)

isa_coverage

Once a sentence is chosen, a linear program is used to determine a trajectory of intermediate invariants. These represent the cumulative two-qubit nonlocal action after each gate in the sentence, starting from the identity and ending at the target.

from gulps.core.invariants import GateInvariants
from gulps.viz.invariant_viz import plot_decomposition

example_input = random_unitary(4, seed=31)
target_inv = GateInvariants.from_unitary(example_input)
constraint_sol = decomposer._best_decomposition(target_inv=target_inv)
plot_decomposition(
    constraint_sol.intermediates, constraint_sol.sentence, decomposer.isa
);

example_cartan_trajectory

In this example, the optimal sentence is composed of 2 $\sqrt[3]{\texttt{iSWAP}}$ gates and 1 $\sqrt[2]{\texttt{iSWAP}}$. That is, the resulting circuit falls into a parameterized ansatz like this: full_ansatz

Unlike other decomposition techniques, the linear program contains additional information about the intermediate points used to reduce the problem into simpler subproblems, each corresponding to a depth-2 circuit segment. In this case, the circuit has three segments, although the first red segment (beginning at Identity is trivial). That leaves two segments requiring synthesis:

ansatz_1 ansatz_2
Red(2) Blue

We solve for the local one-qubit gates in each segment using a Gauss-Newton solver on the Makhlin invariants, followed by a Weyl-coordinate polish. The solver (implemented in Rust) is tuned to work well across a broad range of ISAs, but there is no one-size-fits-all for every possible gate set, so edge-case performance may vary.

solutions = decomposer._local_synthesis._solve_segments(
    constraint_sol.sentence,
    constraint_sol.intermediates,
    n_inner=len(constraint_sol.sentence) - 1,
)
solutions[0]
# SegmentSolution(u0=..., u1=..., weyl_residual=1.2e-16, max_residual=4.8e-09, success=True)

After solving the individual segments, we apply a final stitching step to handle orientation between segments and to promote local equivalence into global unitary equivalence:

decomposer._local_synthesis.synthesize_segments(
    gate_list=constraint_sol.sentence,
    invariant_list=constraint_sol.intermediates,
    target=target_inv,
).draw("mpl")

final


Notebooks

Topic
00_quickstart Getting started with GULPS
01_decomposition_pipeline Step-by-step decomposition pipeline
02_benchmarks LP and solver performance benchmarks
03_continuous Continuous ISA with gate power as a free variable
04_mixed_continuous Multiple continuous gate families in one ISA
05_xxdecomposer Comparison with Qiskit's XXDecomposer

See more:


[!NOTE] This software is provided as-is with no guarantee of support or maintenance. Bug reports and pull requests are welcome, but there is no commitment to respond or resolve issues on any timeline.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gulps-0.3.4.tar.gz (13.6 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

gulps-0.3.4-cp310-abi3-win_amd64.whl (14.6 MB view details)

Uploaded CPython 3.10+Windows x86-64

gulps-0.3.4-cp310-abi3-manylinux_2_28_x86_64.whl (14.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ x86-64

gulps-0.3.4-cp310-abi3-macosx_11_0_arm64.whl (14.3 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file gulps-0.3.4.tar.gz.

File metadata

  • Download URL: gulps-0.3.4.tar.gz
  • Upload date:
  • Size: 13.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gulps-0.3.4.tar.gz
Algorithm Hash digest
SHA256 6509a99dd5133ed9dc442c81b85dd721f997b50a735c15d5c9c3ff5b82e43bb8
MD5 2cd29d301afb4803af7b0b735c8e2d27
BLAKE2b-256 10b95766573a0ba6c3998f1e42443856ffb93a47e245e8c9549433d25643f448

See more details on using hashes here.

File details

Details for the file gulps-0.3.4-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: gulps-0.3.4-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 14.6 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for gulps-0.3.4-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 0618013fbb848a720d36a40826d279a5a7912edc1f8a3f6028a6054d77c746f5
MD5 b3ffd3f38c38b3411a524be7e21ed93b
BLAKE2b-256 09381a10666842f453f6a6a3475131425c3155937ac63c0e22644fa3414163c4

See more details on using hashes here.

File details

Details for the file gulps-0.3.4-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for gulps-0.3.4-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 d0b88dd2ec8a8cb7e2c012250ad37fda03cc741dec1a3e516ba94b089481a654
MD5 a8092b8d803aec3900c5aba0d265f50f
BLAKE2b-256 6c0c33359da5461ee9e4cc729cad335c85bcd8af7a9ff5875c16b59b7d6261d7

See more details on using hashes here.

File details

Details for the file gulps-0.3.4-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gulps-0.3.4-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 35fcd86b10df835ba9d1c923281e54896bac2658115df85b9c5408fd67b02806
MD5 4ba9548cbd8a499854284c54844099ac
BLAKE2b-256 55e9be208bda2119fa538bf2ded54a0b8899a59c3b54808740ebd2a8740234c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page