optimally compile arbitrary two-qubit unitaries
Project description
GULPS
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
XXDecomposerfor 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)
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
);
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:
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:
| 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")
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:
- https://quantum-journal.org/papers/q-2020-03-26-247/
- https://quantum-journal.org/papers/q-2022-04-27-696/
- https://threeplusone.com/pubs/on_gates.pdf
- https://chromotopy.org/latex/papers/xx-synthesis.pdf
- https://github.com/qiskit-advocate/qamp-spring-23/issues/33
- https://github.com/Qiskit/qiskit/pull/9375
- https://weylchamber.readthedocs.io/en/latest/readme.html
[!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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6509a99dd5133ed9dc442c81b85dd721f997b50a735c15d5c9c3ff5b82e43bb8
|
|
| MD5 |
2cd29d301afb4803af7b0b735c8e2d27
|
|
| BLAKE2b-256 |
10b95766573a0ba6c3998f1e42443856ffb93a47e245e8c9549433d25643f448
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0618013fbb848a720d36a40826d279a5a7912edc1f8a3f6028a6054d77c746f5
|
|
| MD5 |
b3ffd3f38c38b3411a524be7e21ed93b
|
|
| BLAKE2b-256 |
09381a10666842f453f6a6a3475131425c3155937ac63c0e22644fa3414163c4
|
File details
Details for the file gulps-0.3.4-cp310-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: gulps-0.3.4-cp310-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 14.8 MB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0b88dd2ec8a8cb7e2c012250ad37fda03cc741dec1a3e516ba94b089481a654
|
|
| MD5 |
a8092b8d803aec3900c5aba0d265f50f
|
|
| BLAKE2b-256 |
6c0c33359da5461ee9e4cc729cad335c85bcd8af7a9ff5875c16b59b7d6261d7
|
File details
Details for the file gulps-0.3.4-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: gulps-0.3.4-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 14.3 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35fcd86b10df835ba9d1c923281e54896bac2658115df85b9c5408fd67b02806
|
|
| MD5 |
4ba9548cbd8a499854284c54844099ac
|
|
| BLAKE2b-256 |
55e9be208bda2119fa538bf2ded54a0b8899a59c3b54808740ebd2a8740234c7
|