The developer SDK for multi-chip quantum computing.
Project description
limbo-quantum
The developer SDK for multi-chip quantum computing.
limbo-quantum is the client-side companion to the Limbo compiler — a
quantum compilation stack designed around the assumption that the
next-generation device isn't one giant chip but a network of chips
stitched together by photonic interconnects. The SDK gives you a single
Python entry point that ingests circuits from Qiskit, Cirq, or OpenQASM
3, runs a preflight capacity audit + hotspot scan, transpiles for your
target device, and submits to whichever backend you've configured.
The local simulator path runs fully in-process. There's also an optional hosted backend at limbosys.dev that runs the multi-chip-aware partitioner + cross-chip scheduler described in the paper — bring your own API key, the SDK doesn't require it.
Install
pip install limbo-quantum
The SDK itself is pure-Python with minimal hard dependencies. Install whichever circuit framework matches the format you'll feed in:
pip install qiskit qiskit-aer # for QuantumCircuit + local simulation
pip install cirq-core # for Cirq Circuit ingestion
pip install openqasm3 # for OpenQASM 3 source ingestion
Quickstart
from qiskit import QuantumCircuit
from limbo_quantum import DistributedCompilerSDK, LocalSimulationProvider
# 1. A familiar Qiskit circuit.
qc = QuantumCircuit(4)
qc.h(0)
for i in range(3):
qc.cx(0, i + 1)
qc.measure_all()
# 2. Configure the SDK with a local simulator backend. Everything from
# here on runs on your laptop — no cloud account required.
sdk = DistributedCompilerSDK(
provider=LocalSimulationProvider(),
num_qpus=2,
max_capacity_per_qpu=4,
)
# 3. One call → ingest → lint → compile → simulate → counts.
result = sdk.compile(qc, shots=1024)
print(result.counts)
print(f"compiled to {len(result.compiled_qasm)} sub-circuit(s)")
That's the whole workflow. To run on real hardware, swap
LocalSimulationProvider() for a CloudProductionProvider (see
Cloud backend) or a custom QuantumProvider
subclass that targets your own infrastructure.
What's in the box
| Component | What it does |
|---|---|
DistributedCompilerSDK |
One-call entry point. Ingest → lint → optional pre-optimize → compile → execute. |
CircuitParser |
Multi-framework ingestion: Qiskit / Cirq / OpenQASM 3 → standardized JSON IR. |
LocalStaticLinter |
Offline capacity audit + interaction-hotspot detection. Catches broken layouts before any network round-trip. |
ParametricRuntime + TopologicalTemplate |
Compile-once-bind-many runtime for variational workloads (VQE / QAOA). Parameters bind locally; the compile step only re-runs when the circuit's structural signature changes. |
LocalSimulationProvider |
In-process Aer simulator. Zero credentials, zero network. |
CloudProductionProvider |
HTTP client for the hosted Limbo backend at limbosys.dev. Optional. |
QuantumProvider |
Abstract base. Subclass it to point the SDK at your own infrastructure. |
TelemetryLogger |
Auto-detects Jupyter vs. terminal, prints per-step timings + payload sizes. Drop-in optional. |
Features in depth
Multi-framework ingestion
from limbo_quantum import CircuitParser
# All three return the same IR shape.
ir = CircuitParser.from_qiskit(my_qiskit_circuit)
ir = CircuitParser.from_cirq(my_cirq_circuit)
ir = CircuitParser.from_openqasm3(my_qasm3_source_string)
The IR is plain JSON (no Python-specific types) so it round-trips
cleanly through caches, message queues, and HTTP payloads. The schema
descriptor is exported as limbo_quantum.IRSchema.
Multi-chip capacity audit
The capacity linter is the SDK's most distinctive feature for multi-chip targets. Qiskit's transpiler checks coupling maps during compilation; Limbo's linter checks layout feasibility up front, with a structured exception you can catch.
from limbo_quantum import (
CircuitParser, LocalStaticLinter, TopologyCapacityError,
)
ir = CircuitParser.from_qiskit(big_circuit)
linter = LocalStaticLinter(ir, num_qpus=4, max_capacity_per_qpu=20)
try:
linter.capacity_audit()
except TopologyCapacityError as exc:
print(f"layout infeasible: requested {exc.num_qubits}q, "
f"available {exc.target_capacity}, "
f"need per-QPU >= {exc.min_qubits_per_qpu}")
Hotspot dashboard
Identifies "hub" qubits whose interaction degree dominates the circuit — the ones that will burn cross-chip traffic when partitioned. By default any qubit holding more than 30% of total degree is flagged.
linter = LocalStaticLinter(ir)
report = linter.hotspot_detection(threshold_ratio=0.30)
# When verbose=True (the default), prints a formatted ASCII table to
# stdout. The structured report is also returned for CI integration.
Parametric template caching (variational amortization)
For VQE / QAOA workflows, ParametricRuntime compiles the template
once and binds parameters locally on each subsequent call:
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from limbo_quantum import ParametricRuntime, InProcessCompilerClient
theta = Parameter("theta")
qc = QuantumCircuit(2)
qc.ry(theta, 0)
qc.cx(0, 1)
qc.measure_all()
runtime = ParametricRuntime(qc, client=InProcessCompilerClient())
for theta_val in [0.0, 0.1, 0.2, 0.3]:
result = runtime.run({theta: theta_val}, shots=512)
print(theta_val, result.counts)
The first .run() does the heavy compile. Every subsequent call binds
into the cached template and skips re-compilation. The cache key is a
SHA-256 of the circuit's structural signature, so different parameter
values share a cache entry but a different gate sequence forces a
re-compile.
Cost-preview mode
Compile without executing — useful when you want to see the metrics (number of partitions, cross-chip operations, output gate count) before spending shots:
result = sdk.compile(qc, execute=False)
print(result.compiled_qasm)
print(result.lint_report)
Custom backends
The QuantumProvider abstract base has three methods to implement:
from limbo_quantum import QuantumProvider, Job, JobResult
class MyBackend(QuantumProvider):
name = "mine"
def submit(self, payload, *, shots=1024, **options) -> Job:
...
def _fetch_status(self, job_id: str) -> JobResult:
...
def list_devices(self, credentials=None):
...
sdk = DistributedCompilerSDK(provider=MyBackend(), num_qpus=1, max_capacity_per_qpu=32)
That's enough to swap in your own infrastructure — AWS Braket, IBM Runtime directly, a self-hosted Aer cluster, anything.
Telemetry
from limbo_quantum import (
DistributedCompilerSDK, LocalSimulationProvider, TelemetryLogger,
)
sdk = DistributedCompilerSDK(
provider=LocalSimulationProvider(),
num_qpus=2,
max_capacity_per_qpu=4,
telemetry=TelemetryLogger(), # auto-detects terminal vs. Jupyter
)
Per-step timings, payload sizes, and lint outcomes print to the host as a clean timeline.
Auto-provider
from limbo_quantum import auto_provider, DistributedCompilerSDK
# CloudProductionProvider if LIMBO_API_KEY is set in the env, else local.
sdk = DistributedCompilerSDK(
provider=auto_provider(),
num_qpus=2,
max_capacity_per_qpu=8,
)
Execution backends
limbo-quantum does not lock you into any single execution path. The
SDK is structured around a narrow QuantumProvider interface
(provider.py) with three implementations shipped in
the box and a documented extension point for everything else. Pick the
one that matches your workflow:
Path A — Local simulator (no account, no network)
The default. Runs Qiskit Aer in-process.
from limbo_quantum import DistributedCompilerSDK, LocalSimulationProvider
sdk = DistributedCompilerSDK(
provider=LocalSimulationProvider(),
num_qpus=2, max_capacity_per_qpu=4,
)
Path B — Hosted Limbo backend (one account, multiple vendors)
CloudProductionProvider is an HTTP client for the hosted Limbo
service at limbosys.dev. The hosted service
holds your IBM / AWS Braket / Azure Quantum credentials encrypted
server-side and runs the multi-chip-aware partitioner + cross-chip
scheduler. You manage one API key; the service brokers the rest.
from limbo_quantum import DistributedCompilerSDK, CloudProductionProvider
sdk = DistributedCompilerSDK(
provider=CloudProductionProvider(
api_key="lmb_live_...", # from your dashboard
server_url="https://api.limbosys.dev",
),
num_qpus=4, max_capacity_per_qpu=20,
)
Path C — Direct to a vendor (IBM / AWS / Azure)
If you already have vendor quotas and want to keep them, the SDK ships first-class wrappers for the three major clouds. Install the right extra and pass an instance to the SDK — no subclassing required.
IBM Quantum (pip install 'limbo-quantum[ibm]'):
from limbo_quantum import DistributedCompilerSDK, IBMProvider
sdk = DistributedCompilerSDK(
provider=IBMProvider(
token="...", # from quantum.ibm.com
backend_name="ibm_brisbane",
),
num_qpus=1, max_capacity_per_qpu=127, # single-chip target
)
result = sdk.compile(qc, shots=1024)
AWS Braket (pip install 'limbo-quantum[aws]'):
from limbo_quantum import DistributedCompilerSDK, AWSProvider
sdk = DistributedCompilerSDK(
provider=AWSProvider(
device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1",
# s3_folder=("my-results-bucket", "limbo/"), # optional
),
num_qpus=1, max_capacity_per_qpu=34,
)
AWS credentials are picked up from the standard boto3 chain
(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars,
~/.aws/credentials, or an attached IAM role).
Azure Quantum (pip install 'limbo-quantum[azure]'):
from limbo_quantum import DistributedCompilerSDK, AzureProvider
sdk = DistributedCompilerSDK(
provider=AzureProvider(
subscription_id="...",
resource_group="quantum-rg",
workspace_name="my-workspace",
location="eastus",
target_name="ionq.qpu",
),
num_qpus=1, max_capacity_per_qpu=23,
)
Azure authentication uses DefaultAzureCredential from
azure-identity — i.e. az login on a laptop, a managed identity in
production, or service-principal env vars.
All three vendor providers handle the QASM → vendor-circuit
translation, job submission, status polling, and result decoding for
you. They lazy-import their vendor SDK so import limbo_quantum stays
fast even if you've only installed one of the three extras.
Path D — Bring your own backend
If you're targeting something else (Quantinuum, IonQ direct, Rigetti,
a self-hosted Aer cluster, a private FPGA, anything), subclass
QuantumProvider with three methods:
from limbo_quantum import QuantumProvider, Job, JobResult
class MyBackend(QuantumProvider):
name = "mine"
def submit(self, payload, *, shots=1024, **options) -> Job:
"""Send the compiled QASM. Return a Job wrapping the backend's
native job ID."""
...
def _fetch_status(self, job_id: str) -> JobResult:
"""Poll. Return a JobResult with status in
{queued, running, completed, failed}; on completion include
the measurement counts."""
...
def list_devices(self) -> list[dict]:
"""Optional device catalog for auto-routing. Can be []."""
...
The shipped IBM / AWS / Azure providers in vendors.py are full reference implementations of this pattern — copy one as a starting point.
Which path to choose
| Scenario | Use |
|---|---|
| Prototyping, CI, anything Aer can run | Path A (LocalSimulationProvider) |
| One bill, one credential, multi-chip-aware compile server-side | Path B (CloudProductionProvider) |
| You already have IBM / AWS / Azure quotas | Path C (IBMProvider / AWSProvider / AzureProvider) |
| Anything else | Path D (subclass QuantumProvider) |
All four paths get you the same client API — only the provider=
argument changes.
Correctness scope
Limbo is a faithful transport for circuits that fit on your chosen
target device. If your circuit fits on the device (i.e.
circuit_qubits <= device_qubits), the SDK ingests, optionally
pre-optimizes (Qiskit passes only), transpiles against the device's
coupling map, and submits via the provider's official SDK. The result
is semantically identical to what direct Qiskit + the vendor's SDK
would produce.
The multi-chip routing path (when your circuit doesn't fit on a
single physical device and the SDK partitions across multiple QPUs) is
research-grade. There is no commercial cloud QPU today that exposes a
multi-chip interconnect, so the cross-chip operations the SDK produces
can only be validated on a simulator. Don't ship a num_qpus > 1
configuration to a single-chip cloud backend and expect correct
results — the cross-chip entanglement won't be implemented.
Error model
| Exception | When |
|---|---|
ImportError |
Framework SDK isn't installed (from_qiskit with no qiskit, etc.) |
TypeError |
Wrong object type passed in (e.g. a Cirq circuit to from_qiskit) |
ValueError |
Malformed IR, bad QASM source, invalid configuration |
TopologyCapacityError |
Capacity audit rejected the layout |
TimeoutError |
Cloud job didn't finish within the configured polling window |
All errors are raised locally before any network call, except
TimeoutError (raised after polling).
Citing
If you use Limbo's multi-chip compilation in published work, please cite the paper (forthcoming, IEEE).
License
Apache 2.0. See LICENSE.
Status
Public release planned for Fall 2026. Pre-1.0 versions on PyPI should be considered beta — APIs may change in minor versions until then.
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 Distribution
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 limbo_quantum-0.1.0.tar.gz.
File metadata
- Download URL: limbo_quantum-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28aa6550ecf85d63f690c425f9d1ecf730ecb08db639ae3ce0edb941d6ed38ca
|
|
| MD5 |
a818d7b1f5a280da671ddf74beb70705
|
|
| BLAKE2b-256 |
326980d196d44a702771176eacf097f0c4c9b6d0e28746c636acc98916e6f1c5
|
File details
Details for the file limbo_quantum-0.1.0-py3-none-any.whl.
File metadata
- Download URL: limbo_quantum-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cd3eeefdd78580cdd162f5a02d8a3c6fae2a172ae78079555c2d5a3b8b53eb5
|
|
| MD5 |
4ca721b0fdfcfe2f0a3af9c87c03a97b
|
|
| BLAKE2b-256 |
d1e0163125f5d07e34f68fb8c24f3628b71601d0d5e185ccba73ba76358a44c6
|