Skip to content

PennyLane

The qubitra.remote device makes any Qubitra Backend a PennyLane device. It ships as an extra of the SDK:

Terminal window
pip install 'qubitra-sdk[pennylane]'

PennyLane discovers the device through its plugin entry point, so there is nothing to import:

import pennylane as qml
dev = qml.device("qubitra.remote", wires=2)
@qml.qnode(dev)
def bell():
qml.Hadamard(0)
qml.CNOT(wires=[0, 1])
return qml.counts()
print(bell()) # {"00": 512, "11": 512}

Credentials resolve the same way as QubitraClient: QUBITRA_API_KEY and QUBITRA_API_URL from the environment, or explicitly:

dev = qml.device(
"qubitra.remote",
wires=2,
backend_id="sim-statevector-26q", # the default — any Backend id works
shots=1024, # default shot count for jobs from this device
api_key="qpk_...", # or pass client=an existing QubitraClient
)

An existing QubitraClient wins over key arguments — pass client= to reuse a configured one.

Measurement Runs as Notes
qml.counts() sampler computational basis; wires= subsets marginalize
qml.sample() sampler per-shot rows are synthesized from counts, not chronological
qml.probs() sampler frequencies from the returned counts
qml.expval(op) estimator op needs a Pauli representation (op.pauli_rep)

Sampler measurements need shots; qml.expval also runs analytically. Several expvals on one tape become several observables on one PUB and come back index-aligned. Anything else — qml.var, qml.state, sampling an observable, or mixing sampler measurements with expectation values on one tape — raises a DeviceError naming this table.

Operations decompose toward what the OpenQASM serializer can emit, so gates like qml.Rot work. A sampler tape is submitted as OpenQASM 2 with terminal measurements, an estimator tape as the bare circuit plus sparse Pauli terms.

PennyLane’s parameter-shift rule evaluates two shifted circuits per trainable parameter, so every gradient of a 20-parameter circuit is a 40-tape batch. The device submits any batch as one multi-PUB job, never one job per tape:

@qml.qnode(dev, diff_method="parameter-shift")
def circuit(params):
qml.RY(params[0], wires=0)
qml.RY(params[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
qml.grad(circuit)(params) # 4 shifted tapes → one job, one round trip

The platform’s round trip takes seconds. Submitted as one job per tape, a 20-parameter gradient would cost 40 sequential round trips per optimisation step; as one job it is a single submit-poll-fetch regardless of the parameter count.

Platform counts keys are little-endian — clbit 0 rightmost. PennyLane reads wires left to right with wire 0 most significant. The device translates at the boundary, so results match what default.qubit would say for the same circuit: qml.PauliX(0) on a three-wire device reads {"100": shots}, never {"001": shots}.