Skip to main content

Quantum circuit simulation (statevector + stabilizer) with IQM cloud submission, quantum random numbers from IQM bit pools, and QRNG synthetic data with provable provenance.

Project description

lightrider

PyPI Python License

Quantum circuit simulation, quantum random numbers, and QRNG-driven synthetic data — in one lightweight, NumPy-only package.

lightrider provides three capabilities:

Capability Entry point What it does
Circuit simulation & cloud jobs Circuit, get_backend Build arbitrary circuits with a Qiskit-style API and run them on a fast local statevector simulator, a Stim-style stabilizer simulator, or IQM hardware in the cloud
Quantum random numbers IQM_sirius Draw random numbers from a bundled pool of real IQM hardware bits — fully offline
Synthetic data with provenance Synthesizer Generate tabular synthetic data where every random draw is quantum, certified by a signed manifest

The only required dependency is NumPy. Simulators and the QRNG pool run locally with no network access; cloud execution and live attested entropy are opt-in upgrades.

Installation

pip install lightrider             # core (NumPy only)
pip install "lightrider[pandas]"   # + pandas DataFrame support

Requires Python ≥ 3.9.

Live EMS entropy additionally needs the lr-entropy SDK, which ships with the Light Rider platform rather than on PyPI. Install it from the platform repository (pip install ./sdk-python) and EntropySource becomes available automatically.

Quickstart

from lightrider import Circuit, get_backend

# 1. Build a Bell-pair circuit
circ = Circuit(2)
circ.h(0)
circ.cx(0, 1)
circ.measure_all()

# 2. Run it on the local statevector simulator
job = get_backend("statevector").run(circ, shots=1000, seed=42)

# 3. Read the counts (Qiskit convention: clbit 0 is the rightmost character)
print(job.result().counts)   # {'00': 507, '11': 493}

Measure before you run. Counts are samples of measured classical bits, so every circuit needs measure_all() (or explicit measure() calls) before run() — otherwise run() raises BackendError: circuit has no measurements. In notebooks, build and run the circuit in the same cell: Circuit methods mutate in place, so re-running only the run() cell reuses whatever state the circuit last had.

Quantum circuits

Building circuits

Circuit follows Qiskit's builder conventions — gate methods take parameters first, then qubits, and calls chain:

from lightrider import Circuit

circ = Circuit(3)                 # 3 qubits, 3 classical bits
circ.h(0)
circ.rx(0.5, 1)                   # params first, qubits last
circ.ccx(0, 1, 2)
circ.measure_all()

The primitive gate set:

Group Gates
Single-qubit id x y z h s sdg t tdg sx
Single-qubit, parameterized rx ry rz p r u
Two-qubit cx cy cz ch swap cp rxx ryy rzz
Three-qubit ccx cswap

Composite gates are defined as macros that expand to primitives at append time:

from lightrider import custom_gate

@custom_gate(num_qubits=2)
def bell_pair(c, qubits, params):
    a, b = qubits
    c.h(a)
    c.cx(a, b)

circ = Circuit(3)
circ.append(bell_pair, [0, 1])

Choosing a backend

Every backend declares the gate set it supports, and run() validates the circuit up front — a job that submits will also execute. Inspect all backends programmatically with list_backends().

Backend name Aliases Where Gate set Best for
lightrider_statevector statevector, sv local full Exact simulation up to 24 qubits. Shots are sampled in one vectorized pass, so large shot counts are effectively free (1M shots of a 20-qubit circuit in ~1.4 s)
lightrider_stabilizer stabilizer, stim local Clifford subset (x y z h s sdg sx cx cy cz swap) Clifford circuits at hundreds of qubits; supports mid-circuit measurement
iqm cloud cloud full, transpiled server-side to IQM-native r (prx) + cz Real-hardware runs via the Light Rider IQM proxy

Running locally

from lightrider import get_backend

result = get_backend("statevector").run(circ, shots=10_000, seed=7).result()
result.counts             # {'000': 4980, '111': 5020}
result.probabilities()    # {'000': 0.498, '111': 0.502}

The stabilizer backend trades gate-set generality for scale — a 100-qubit GHZ state samples at ~6 ms/shot:

n = 100
ghz = Circuit(n)
ghz.h(0)
for q in range(n - 1):
    ghz.cx(q, q + 1)
ghz.measure_all()

counts = get_backend("stabilizer").run(ghz, shots=1000).result().counts

Submitting a non-Clifford gate to the stabilizer backend (or an unsupported gate to any backend) raises UnsupportedGateError before anything runs.

Running on IQM hardware

Cloud jobs go through the Light Rider IQM proxy and authenticate with a Light Rider lr_ API key — you never handle IQM credentials directly. The circuit is transpiled to the QPU's native gates server-side.

Getting a key: lr_ API keys are issued internally by Light Rider — request one from your administrator. There is intentionally no public self-registration; IQMBackend.register() exists for administrators only and requires the deployment's admin token.

iqm = get_backend("iqm",
                  endpoint="https://quantum.lightrider.example",  # or LR_QUANTUM_ENDPOINT
                  api_key="lr_...")                               # or LR_QUANTUM_API_KEY

job = iqm.run(circ, shots=1000)   # returns immediately
job.status()                      # WAITING | PROCESSING | COMPLETED | FAILED | ABORTED
job.result()                      # polls until the job is terminal, then returns counts

Mock deployments. If the proxy is backed by one of IQM's :mock QPU endpoints, run() emits a MockBackendWarning: mock QPUs execute the full job lifecycle but return canned mock entropy (all measured bits set to one coin flip) instead of running your circuit. Use the local simulators when the counts need to be physically meaningful.

Serialization

Circuits serialize to the lr-circuit/v1 JSON payload shared with the Light Rider proxy and lr-entropy SDK, and to a Stim-flavored text format:

payload = circ.to_payload()            # dict, JSON-safe
circ2   = Circuit.from_payload(payload)

print(circ.to_text())                  # H 0 / CX 0 1 / M 0 -> 0 ...
circ3 = Circuit.from_text(circ.to_text())

Quantum random numbers

IQM_sirius draws from a bundled pool of ~2 million bits captured from IQM hardware (Hadamard coin-flip circuits across 10 qubits, SHA-256 debiased) — no network required. Output is unbiased on any range via rejection sampling.

from lightrider import IQM_sirius

IQM_sirius(5, 1, 100)               # 5 quantum random ints in [1, 100]
IQM_sirius(3, 0.0, 1.0, step=0.1)   # 3 quantum random floats on a 0.1 grid

Capture metadata for the bundled pool lives in the repository under iqm_capture_20260507_181448/metadata.json.

Synthetic data with provenance

Synthesizer fits a Gaussian copula to tabular data and generates new rows whose every random draw comes from a quantum source. Each dataset ships with a provenance manifest binding it to the entropy that produced it.

from lightrider import Synthesizer

synth = Synthesizer(dataset_id="customers_v3").fit(df)   # DataFrame / dict / records
rows  = synth.generate(10_000)

synth.manifest.write("customers_v3.provenance.json")
print(synth.certificate())

How it works

fit:   data ─▶ marginals (empirical CDF / category freqs)
             ─▶ normal scores  z = Φ⁻¹(rank)
             ─▶ correlation Σ = corr(z),  Cholesky  Σ = L Lᵀ

gen:   QRNG ─▶ U(0,1)          (quantum draws, recorded on the manifest)
             ─▶ Z₀ = Φ⁻¹(U)    (iid standard normals)
             ─▶ Z  = Z₀ Lᵀ     (impose learned correlation)
             ─▶ U' = Φ(Z)      (back to uniform, per column)
             ─▶ x  = F⁻¹(U')   (inverse marginal → synthetic value)

The copula reproduces each column's marginal distribution and inter-column correlations; the randomness selecting each synthetic row is quantum, not a PRNG. The full mathematical treatment is in the repository under docs/qrng-synthetic-data.pdf.

Entropy modes

Mode Provider Provenance
bundled-qrng (default) BundledQrng over the packaged IQM pool Real quantum bits, SHA-256 debiased, offline, unsigned
live-attested EntropySource against a Light Rider EMS Multi-source extraction over GF(2¹²⁸), SP 800-90B health-tested, post-quantum-signed receipts
from lightrider import EntropySource, Synthesizer   # requires the lr-entropy SDK

src   = EntropySource("http://localhost:7081", dataset_id="customers_v3")
synth = Synthesizer(entropy=src).fit(df)
rows  = synth.generate(10_000)     # every draw carries a signed receipt

EntropySource(allow_failover=True) (the default) falls back to the OS CSPRNG on any EMS error so a long job never blocks. Failover draws are flagged in the manifest and excluded from the certificate's source list — the certificate never overstates its provenance.

The manifest

{
  "dataset_id": "customers_v3", "model": "qrng-copula",
  "rows": 10000, "columns": ["age", "income", "tier", "region"],
  "entropy_mode": "live-attested", "fully_attested": true,
  "signature_alg": "ML-DSA-65", "post_quantum_signed": true,
  "sources_used": ["curby_q_jila_001", "qispace_kds_001"],
  "min_quality_score": 90, "health_all_pass": true,
  "extractors": ["SHAKE256"], "failover_used": false
}

In the offline default the same manifest reports entropy_mode: "bundled-qrng" and post_quantum_signed: false — honest by construction.

Demo and development

# generate a synthetic dataset with its provenance certificate
python -m lightrider.demo --rows 2000 --out synthetic.csv --manifest cert.json

# against a live EMS
python -m lightrider.demo --endpoint http://localhost:7081 --rows 2000

# run the test suite
pytest tests -q

License

Apache-2.0. Built by Light Rider.

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

lightrider-1.0.3.tar.gz (533.1 kB view details)

Uploaded Source

Built Distribution

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

lightrider-1.0.3-py3-none-any.whl (657.7 kB view details)

Uploaded Python 3

File details

Details for the file lightrider-1.0.3.tar.gz.

File metadata

  • Download URL: lightrider-1.0.3.tar.gz
  • Upload date:
  • Size: 533.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for lightrider-1.0.3.tar.gz
Algorithm Hash digest
SHA256 51e8fd9f7b13c79d3a6268524c05870acc8c91a11525a1dc45892566c366be8d
MD5 b385c06b093a791fb8c0883e80becae0
BLAKE2b-256 199d1786ab386d3011293c0a74ba56709daed5c9af8ef12870c4d6fb84d74bac

See more details on using hashes here.

File details

Details for the file lightrider-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: lightrider-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 657.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for lightrider-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7949b5fd20df2cafe7d33662d41dd814e854643dc931186ba8605bb949c03675
MD5 57310f2d516dc14e5cb1dd36806adce6
BLAKE2b-256 f28bab4720c3f0eb215aa874779f80fd0d6ebaa6aefff6f5af328c17d323758d

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