localqpu
A local emulator of the IBM Quantum Platform API, built for testing. Like a payment provider's "test mode", it lets you test code that calls a quantum cloud without queues, without cost, and with any failure you want to simulate.
localqpu does not verify that quantum results are correct. It verifies the code around the quantum API call: submission, waiting, retries, result parsing, and error handling.
Install and run
pip install localqpu # https://pypi.org/project/localqpu/
localqpu start # http://127.0.0.1:8787
# or
docker build -t localqpu . && docker run -p 127.0.0.1:8787:8787 localqpu
Connect existing Qiskit code
import localqpu
service = localqpu.connect(port=8787) # use this instead of QiskitRuntimeService(...)
backend = service.least_busy()
⚠️ Always use
localqpu.connect(). In qiskit-ibm-runtime the runtime authentication endpoint is hard-coded toiam.cloud.ibm.comand can only be changed through theIAM_URLenvironment variable. If you configure the client by hand and miss it, the client sends your key to the real IBM Cloud.connect()setsIAM_URLand routes both HTTP and HTTPS through localqpu, so any leaking HTTPS request is blocked. Blocked attempts are counted inblocked_connect_requestsonGET /_localqpu/health.Note that
connect()changes process-wide environment variables: it setsIAM_URLand appendslocalqpu.testtoNO_PROXY/no_proxy(so thatHTTP_PROXYsettings cannot redirect localqpu traffic to a corporate proxy). Run tests that talk to the real IBM Cloud in a separate process.
Use with pytest
The fixtures are registered automatically once the package is installed.
def test_retry_on_failure(localqpu_service, localqpu_control):
localqpu_control.set_scenario({"next_jobs": [{"outcome": "failed", "reason": "calibrating"}]})
...
| Fixture | Description |
|---|---|
localqpu_server |
A server on a free port for the whole test session |
localqpu_control |
Change scenarios, inspect jobs, reset state. Reset before and after each test |
localqpu_service |
A connected QiskitRuntimeService |
Failure scenarios
Use localqpu start --scenario scenario.json or localqpu_control.set_scenario({...}).
{
"seed": 42,
"queue": { "delay_seconds": 0, "polls_before_running": 1 },
"failures": { "rate": 0.0, "reason": "Simulated failure", "reason_code": 9999 },
"next_jobs": [
{ "outcome": "failed", "reason": "QPU calibration in progress", "reason_code": 1517 },
{ "outcome": "cancelled" }
],
"backends": { "ibm_brisbane": { "status": "offline", "queue_length": 120 } },
"usage": { "limit_seconds": 600, "consumed_seconds": 600 },
"auth": { "reject_tokens": false }
}
| Scenario | What the client sees |
|---|---|
next_jobs failure |
RuntimeJobFailureError (with the reason) |
next_jobs cancellation |
RuntimeInvalidStateError |
Failure or cancellation with reason_code: 1305 |
RuntimeJobMaxTimeoutError (the client treats 1305 as a max-time error and turns a cancelled job with this code into an error) |
job.cancel() from your code |
CANCELLED; localqpu records reason_code: 9001 |
Backend offline |
Excluded from least_busy(); submitted jobs stay QUEUED until it is back online |
Backend paused |
A "currently has a status of paused" warning, then normal processing |
| Usage limit reached | A warning, then submission fails with IBMRuntimeError (403) |
auth.reject_tokens |
InvalidAccountError when creating the service |
QiskitRuntimeServicecaches the backend list after the firstleast_busy()/backends()call, exactly as it does against IBM. After changing backend status in a scenario, create a new service (thelocalqpu_servicefixture gives you a fresh one per test).
Control API
| Method and path | Purpose |
|---|---|
GET /_localqpu/health |
Status, backends, blocked CONNECT count |
GET/PUT /_localqpu/scenario |
Read or replace the scenario |
POST /_localqpu/reset |
Reset jobs, scenario, and stats |
GET /_localqpu/jobs |
Summary of submitted jobs |
Limitations (v0.1)
- IBM Quantum Platform only. Supported programs: the legacy
SamplerV2(sampler) and the new executor-based Sampler (executor, schema v2.0). - Noiseless simulation. If the number of active qubits exceeds
--max-sim-qubits(default 24), the sampler returns a shape-correct stub result (metadata["localqpu_stub"]) and the executor fails with guidance. - Estimator, Session/Batch, and Qiskit Functions are not supported yet.
- Jobs are kept in memory only and are lost when the server restarts.
- A running simulation cannot be interrupted.
POST /_localqpu/reset(used by the pytest fixtures between tests) discards its result and starts a fresh worker pool, so later jobs are not blocked, but the old computation keeps using CPU until it finishes. - Failure reasons use localqpu codes:
9000for localqpu-side failures (invalid input, simulation limits) and9001for user cancellation.
Release files for localqpu 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| localqpu-0.1.0.tar.gz | 203.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| localqpu-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 251.0 kB
Release files / localqpu-0.1.0.tar.gz
| Download URL | localqpu-0.1.0.tar.gz |
|---|---|
| Size | 203.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ad3fd17b335c3b251e76415200047cb94a175ee20f26bb0f42d8b8d5126c5e19
|
|
BLAKE2b-256 checksum How to use checksums |
3dedc56ff36e368a16763edd7cae6e1fdf7b69e2be87e788e7e9f9872f6ecf38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / localqpu-0.1.0-py3-none-any.whl
| Download URL | localqpu-0.1.0-py3-none-any.whl |
|---|---|
| Size | 47.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1532b9822e086a4945647744d188e40d347c2a9030a574c4bc4fb042cafb8df6
|
|
BLAKE2b-256 checksum How to use checksums |
91cfd9ee6f3a253be09a156840b20d5ad656d3c11ee87882abf1495c728abff4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency log