Wrapping any MIMIQ backend¶
MimiqBackend wraps anything that produces a MIMIQ QCSResults.
There are three accepted shapes for the constructor argument:
A MIMIQ ``RemoteConnection`` (e.g.
MimiqConnection). Wrapped internally inmimiqcircuits.backends.MimiqRemoteBackendso the polling and result post-processing already in mimiqcircuits is reused.A MIMIQ ``Backend`` instance, any class inheriting from
mimiqcircuits.backends.Backend. Used as is via itsexecutemethod.A plain callable
(circuit, *, nsamples, seed) -> QCSResults, an escape hatch for custom executors or tests.
Example: wrapping a local in-process simulator¶
mimiq-exaqt is a state-vector simulator on PyPI whose ExaqtQCS is
a mimiqcircuits LocalBackend, so it needs no cloud credentials:
from exaqt import ExaqtQCS
from mimiq_qiskit import MimiqBackend
backend = MimiqBackend(ExaqtQCS(), name="exaqt", num_qubits=24)
Any other class inheriting from mimiqcircuits.backends.Backend wraps
the same way. A complete example on a local simulator works this example through the whole
API — counts, both primitives, mid-circuit measurement, transpilation, and
a check against Qiskit’s own reference.
A local backend can be asked for terms directly¶
Shape 2 above is more than a way to avoid the network. A backend that runs
in this process can be driven one step at a time, and
MimiqEstimatorV2 does exactly that when the backend advertises the
expectation_state capability: a deterministic circuit is evolved once
and each Pauli term is read off the resulting state, instead of each term
being pushed onto the circuit as an ExpectationValue operation and read
back out of the z-register.
Nothing about this is opt-in or visible in the values — it is the same numbers by a shorter path, and the saving is in the term handling, so it counts for most where the terms are many and the state is small. A backend that does not advertise the capability, a remote connection, a bare callable, and any circuit that needs an evolution per trajectory all take the portable route unchanged.
from exaqt import ExaqtQCS
from mimiq_qiskit import MimiqBackend, MimiqEstimatorV2
"expectation_state" in ExaqtQCS().capabilities() # True
estimator = MimiqEstimatorV2(MimiqBackend(ExaqtQCS()))
To offer this from your own backend, implement
expectation(state, op, *qubits) — accepting a PauliString of any
length, not just a one- or two-qubit operator — and add
expectation_state to what capabilities() returns. See
mimiq_qiskit.local_terms for what the estimator then calls.
Run options are not portable across backends¶
MIMIQ backends do not all take the same options. The cloud accepts every
knob; a local simulator names only what it implements, so
backend.run(qc, bonddim=64) against ExaqtQCS raises a
ValueError naming the backend and the option rather than failing
somewhere inside mimiqcircuits. Set MPS and job options only on backends
that have them.
Example: a stub for unit tests¶
from bitarray import bitarray
from mimiqcircuits import QCSResults
from mimiq_qiskit import MimiqBackend
def runner(circuit, *, nsamples, seed):
return QCSResults(cstates=[bitarray("0" * circuit.num_bits())] * nsamples)
backend = MimiqBackend(runner)
The Target advertised to Qiskit lists every standard gate that the
converter recognises. transpile(qc, backend=backend) will decompose
unsupported gates into that set before they reach the converter.