Wrapping any MIMIQ backend

MimiqBackend wraps anything that produces a MIMIQ QCSResults. There are three accepted shapes for the constructor argument:

  1. A MIMIQ ``RemoteConnection`` (e.g. MimiqConnection). Wrapped internally in mimiqcircuits.backends.MimiqRemoteBackend so the polling and result post-processing already in mimiqcircuits is reused.

  2. A MIMIQ ``Backend`` instance, any class inheriting from mimiqcircuits.backends.Backend. Used as is via its execute method.

  3. 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.