Skip to content

Concepts

The SDK uses the platform’s vocabulary throughout. This page defines each term and the SDK object that carries it.

An execution target a workload runs on: a simulator, a QPU, or a vendor service.

A Backend is identified by a Qubitra-owned slug rather than a vendor name. Backend.id is that slug, and it is what you pass as backend_id when submitting:

backend = client.backends.get("sim-statevector-26q")
print(backend.type, backend.qubit_count, backend.shots_min, backend.shots_max)

The model describes capability: type, technology, qubit count, the shot window, and the circuit formats accepted. There is no provider field, hardware owner, or access tier — the hardware behind a Backend can change without your code changing. Choosing a backend covers how to pick one.

A PUB — Primitive Unit Bloc — is a circuit and everything needed to run it: optional observables, optional parameter-value rows, and an optional shot count of its own. A Job carries an ordered list of PUBs, and a PUB’s contents determine what kind of result it returns. See Primitives and PUBs.

client.jobs.submit(circuit=…) builds a Job of exactly one PUB — the common case, with no Pub object in your code. client.jobs.run(pubs=[…]) submits several.

A Workload is the unit of computation the platform dispatches to a Backend. A Job is one submission of a workload — an ordered list of PUBs and a shot count — with a lifecycle that ends in a terminal state and a normalized result.

jobs.submit returns as soon as the platform accepts the submission:

job = client.jobs.submit(backend_id="sim-statevector-26q", circuit=BELL, shots=1024)
print(job.status) # JobStatus.PENDING

The Job exists from that moment. The workload may not run until later.

In flight
  • PENDINGrecorded, waiting for the backend
  • RUNNINGexecuting
  • CANCELLINGcancel requested, not yet terminal
Terminal — status.is_terminal
  • COMPLETEDhas a result
  • FAILEDcarries error
  • CANCELLEDyou asked
  • ABORTEDstopped by the platform

JobStatus.is_terminal is the predicate for a polling loop, and client.jobs.wait is that loop written for you. Terminal means the Job has stopped changing, not that it succeeded: a FAILED Job is terminal and carries a reason on Job.error.

A completed Job has a normalized result — the same shape on every Backend. It holds one entry per PUB, index-aligned with the submission:

result = client.jobs.result(job.id)
result.pubs # (CountsResult(counts={"00": 512, "11": 512}, shots=1024),)
result.kind # "counts"
result.counts # {"00": 512, "11": 512}
result.probabilities # None for a counts result
result.shots # 1024

result.pubs[i] belongs to the PUB submitted at index i. The properties above read the first entry, which is the complete answer for a single-circuit submission.

Each entry is one of four types, distinguished by its type field: CountsResult, ProbabilitiesResult, ExpectationValuesResult or ErrorResult. A new result shape is added as a new type rather than as a breaking change to an existing one. Results never include a raw vendor payload.

Requesting a result before the Job is COMPLETED raises InvalidRequestError.

A run of Jobs, grouped and bounded. A Session gives its Jobs a shared id, so a 200-iteration optimisation is reported as one run, and it carries max_credits and max_seconds bounds on what the whole run may spend.

session = client.sessions.create(name="vqe run", max_credits=50.0)
job = client.jobs.submit(backend_id="sim-statevector-26q", circuit=BELL, session_id=session.id)

The metered unit of platform usage and billing. A Job is charged when it is submitted and refunded if it never produced a result, so a Job that failed or was cancelled ultimately costs nothing. Running out raises InsufficientCreditsError — a separate error type from an invalid request, because the fix is topping up credits rather than changing code.

Balances and usage history are managed in the console; the SDK has no credits call.

Anything published to the marketplace: an Application (a “quantum app”), a Tool/Library, a Backend, Infrastructure, or Expertise. Offerings are published by Partners and pass Vetting before they are listed.

for offering in client.marketplace.offerings():
print(offering.type, offering.name, offering.provider_name)

Offerings are the marketplace’s catalogue. A Backend Offering is a listing; client.backends lists what you can run on now.

QubitraClient is a facade over four sub-APIs — client.backends, client.jobs, client.sessions, client.marketplace — and they are the whole public surface.

Every model is a frozen pydantic model: an object is a snapshot of what the platform said at the moment you asked. To see a change, ask again.