mimiqcircuits.backends.backend¶
Abstract base classes for simulator backends.
This module defines the contract every simulator must satisfy:
Backend— the common interface, advertising identity, capabilities, limits, topology, and anexecute()entry point.LocalBackend— for in-process simulators. Addsbuild_state(),compile(),evolve(), and a defaultexecute()loop built on top of them.RemoteBackend— for cloud or submit/poll execution. Addssubmit()and provides a defaultexecute()that waits on the returned job.
See Implementing a Custom Backend for a step-by-step guide.
Classes
|
Abstract base class for any simulator backend. |
Base class for simulators that run in the local process. |
|
|
Four independent RNG streams, one per simulation stage. |
Base class for simulators that run on a remote service. |
|
|
Composite simulation state held by a backend. |
- class mimiqcircuits.backends.backend.RNGs(shot, noise, trajectory, pass_)[source]¶
Bases:
objectFour independent RNG streams, one per simulation stage.
Keeping the streams separate means seeding one stage (for example sampling) does not affect the determinism of another (for example noise selection), even when the same execution touches both.
- shot¶
Sampling shots from the final state.
- Type:
- noise¶
Kraus and mixed-unitary noise sampling.
- Type:
- trajectory¶
Monte Carlo trajectory selection.
- Type:
- pass_¶
Randomised compilation passes (e.g. simulated-annealing reorderers).
- Type:
- Construct from a single int seed via :py:meth:`from_seed`.
- static from_seed(master)[source]¶
Derive four streams from a single
masterseed.XOR-tags the seed bits per stream — cheap, deterministic, and stable across Python versions, which matters because the downstream backends must reproduce results bit-for-bit from the same input.
- static from_generator(gen)[source]¶
Draw a master seed from
genand forward tofrom_seed().
- __init__(shot, noise, trajectory, pass_)¶
- class mimiqcircuits.backends.backend.State[source]¶
Bases:
ABCComposite simulation state held by a backend.
A state bundles three registers:
the quantum register (whatever representation the backend uses; state vector, MPS, tensor network, …);
the classical-bit register written by measurements and
IfStatementoutcomes;the complex-valued register written by non-destructive observations (
Amplitude,ExpectationValue,BondDim, …).
Required surface for every subclass:
num_qubits,num_bits,num_zvars— register sizes.amplitude(),sample()— observation primitives.classical_bits,complex_values— register accessors.
Optional surface:
expectation()— backend overrides when it can compute⟨ψ|O|ψ⟩non-destructively (gated by the"expectation_state"capability).reset()— backend overrides when it can rebuild itself in place to the zero state.
- abstract sample(nsamples, rng=None, *, seed=None)[source]¶
Sample
nsamplesmeasurement outcomes from the state.Exactly one source of randomness is consumed:
rng(positional or keyword): arandom.Randomwhosegetrandbits(63)produces the seed forwarded to the simulator’s PRNG.seed(keyword): an int that seeds the simulator’s default PRNG directly.neither: the simulator draws fresh cryptographic entropy.
Passing both
rngandseedmust raiseTypeError. The two arguments are mutually exclusive.
- expectation(op, *qubits)[source]¶
Compute
⟨ψ|op|ψ⟩on this state.Most backends implement expectation on the Backend rather than the State (so they can route through the simulator’s compile / evolve machinery). State-level expectation is provided as a convenience hook for backends whose quantum register supports it directly.
- abstract property classical_bits¶
- abstract property complex_values¶
- class mimiqcircuits.backends.backend.Backend[source]¶
Bases:
ABCAbstract base class for any simulator backend.
A concrete backend must:
advertise its feature set via
capabilities(),limits(),topology();implement
execute()(or subclassLocalBackend/RemoteBackendand inherit a sensible default).
Pick the right base for your simulator:
LocalBackend— the simulator runs in-process and you can hand it aStateit mutates with each instruction.RemoteBackend— the simulator runs elsewhere (cloud service, queued executor) and your wrapper submits jobs and polls for results.
See Implementing a Custom Backend.
- can_handle(circuit)[source]¶
Default admission check against
limits()and the backend’s advertisedcapabilities().Backends with richer admission criteria (bond-dimension estimates, hardware connectivity, gate-set whitelists) should override.
Rejects (returns
Inadmissible) when:the circuit exceeds one of
max_qubits/max_classical_bits/max_zvarsdeclared bylimits();the circuit contains a loss-bearing operation (
LossErr,QubitLoss) but the backend does not advertise the"loss"capability.
Returns
Admissibleotherwise.
- stochastic_kind(op_or_instruction)[source]¶
Classify how this backend resolves
op_or_instruction—StochasticKind.Deterministic,StochasticKind.TrajectorySampleable, orStochasticKind.RuntimeOnly.The default delegates to
default_stochastic_kind()(mix-unitary Kraus is TS; non-mix-unitary Kraus, mid-circuitMeasure, andLossErrare RT; everything else Deterministic). Backends that handle a specific op type differently override this method.Backend-dependent: a
MixedUnitaryisTrajectorySampleablefor an MPS-style sampler but could beRuntimeOnlyfor a backend that resolves it state-dependently. The classification lives on the backend, not on the op.
- abstract execute(circuit, *, nsamples=1000, seed=None, rng=None, passes=None, callback=None, param_grid=None, strict_pass_order=True)[source]¶
Run
circuiton this backend and returnQCSResults.Pass
circuitas a singleCircuitor a list of circuits — a list returns a list of results in the same shape.seedandrngare mutually exclusive sources of randomness; pass at most one. With neither, the backend draws fresh entropy. To request specific amplitudes, pushAmplitudeinstructions intocircuitand read the resultingresults.zstates.
- expectation(state, op, *qubits)[source]¶
Compute
⟨ψ|op|ψ⟩onstatenon-destructively.qubitsis the list of qubit indicesopacts on (0-based). Backends override when they advertise the"expectation_state"capability; the default raisesNotImplementedErrorso undeclared backends fail loudly rather than silently degrading.
- class mimiqcircuits.backends.backend.LocalBackend[source]¶
Bases:
BackendBase class for simulators that run in the local process.
Subclasses implement a handful of primitives and inherit a full
execute()driver — the Python analog of the JuliaAbstractQCSs.executedriver. Once the hooks are wired up, routing (sampling-vs-trajectory), the final-block projection circuit, loss-sampling pre-pass, and amplitude lookups all work without any per-backend duplication.Required hooks:
build_state()— allocate a fresh zero state.compile()— turn aCircuitinto aCompiledCircuit(a backend-specific lowered form).evolve()— apply the compiled circuit to a state and return the mutated state plus a typedFidelity.
Optional hooks (sensible defaults provided):
prepare_trajectory()— refresh the compiled artifact once per Monte Carlo trajectory (default: identity).recompile_per_trajectory()— predicate; default returnsTrueiff the circuit contains a mixed-unitary Kraus channel.bind()— substitute parameters into a parametric compile artifact (default: re-compile after substitution).
- prepare_trajectory(compiled, rng)[source]¶
Refresh
compiledfor one Monte Carlo trajectory.Override when the compiled artifact contains stochastic elements (sampled mixed-unitary channels, sampled Kraus branches, …) that must be redrawn per trajectory. The default leaves
compiledunchanged, which is correct for fully deterministic compilation.
- recompile_per_trajectory(circuit)[source]¶
Return
Trueiffcompile()should be re-run for every trajectory. The default fires on any mixed-unitarykrauschannel: those backends sample a branch at compile time, so the compiled artifact has to be regenerated per trajectory to expose a fresh sample. Backends that own their per-trajectory sampling internally (insideprepare_trajectory()orevolve()) should override to returnFalse.Mirrors the Julia
AbstractQCSs.recompile_per_trajectory.
- bind(compiled, params)[source]¶
Substitute
paramsinto a parametric compiled circuit.The default implementation substitutes the symbols in the source circuit and re-runs
compile(). This is correct but pays the full compile cost at every parameter point. Override when your backend can re-bind a pre-compiled artifact in-place (slot maps, pre-baked gate templates, …).
- execute(circuit, *, nsamples=1000, seed=None, rng=None, passes=None, callback=None, param_grid=None, strict_pass_order=True, stopped=None, num_qubits=None)[source]¶
Run
circuiton this backend and returnQCSResults.Pass
circuitas a singleCircuitor a list of circuits — a list returns a list of results in the same shape.seedandrngare mutually exclusive sources of randomness; pass at most one. With neither, the backend draws fresh entropy. To request specific amplitudes, pushAmplitudeinstructions intocircuitand read the resultingresults.zstates.
- class mimiqcircuits.backends.backend.RemoteBackend[source]¶
Bases:
BackendBase class for simulators that run on a remote service.
Subclasses implement
submit(), which dispatches the request and returns a job handle. The inheritedexecute()callssubmit(), then blocks on the returnedjob.wait().- abstract submit(circuits, nsamples, **kwargs)[source]¶
Send the request and return a job handle.
The job handle must expose a
wait()method that blocks until results are available and returnsmimiqcircuits.QCSResults(or a list whencircuitswas a list).
- execute(circuit, *, nsamples=1000, seed=None, rng=None, passes=None, callback=None, param_grid=None, strict_pass_order=True)[source]¶
Run
circuiton this backend and returnQCSResults.Pass
circuitas a singleCircuitor a list of circuits — a list returns a list of results in the same shape.seedandrngare mutually exclusive sources of randomness; pass at most one. With neither, the backend draws fresh entropy. To request specific amplitudes, pushAmplitudeinstructions intocircuitand read the resultingresults.zstates.