Device IDs: qperfect:qperfect:sim:mimiq-sv (state vector, 32 qubits) and qperfect:qperfect:sim:mimiq-mps (matrix product state)  ·  batch jobs  ·  free to run  ·  accessed through QbraidProvider

Using your own MIMIQ account? See QPerfect (self-managed).

Devices on qBraid

MIMIQ is QPerfect’s cloud emulator for gate-model circuits. qBraid offers it as two devices, one per simulation engine:

DeviceDevice IDEngineQubits
MIMIQ SVqperfect:qperfect:sim:mimiq-svState vector: exactUp to 32
MIMIQ MPSqperfect:qperfect:sim:mimiq-mpsMatrix product state (MPS)No fixed limit (see below)
  • State vector simulates every amplitude, so results are exact, but memory doubles with every qubit. Circuits over 32 qubits are refused.
  • MPS compresses the state, so it handles hundreds of qubits, as long as the circuit doesn’t build up too much entanglement. A strongly entangled circuit can come back approximate, and each result reports how exact it was.

The device fixes the engine: choose it by choosing the device.

MIMIQ currently costs 0 credits to run. You need a qBraid API key, but no QPerfect account. See Pricing.

Quick start

from qbraid.runtime import QbraidProvider
from qiskit import QuantumCircuit

provider = QbraidProvider()
device = provider.get_device("qperfect:qperfect:sim:mimiq-sv")

circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure_all()

job = device.run(circuit, shots=200)
print(job.result().data.get_counts())
# {'000': 101, '111': 99}

MIMIQ takes OpenQASM, and qBraid transpiles to it for you, so a Qiskit, Cirq, Braket, pyQuil, or pytket circuit can go straight to run(). device.supported_run_inputs() lists them.

Runtime options

MIMIQ’s settings are passed as a dict through runtime_options, which qBraid forwards to the emulator:

device = provider.get_device("qperfect:qperfect:sim:mimiq-mps")

job = device.run(circuit, shots=200, runtime_options={"bonddim": 64, "seed": 7})
OptionTypeDevicesDefaultEffect
bonddimintMPS onlychosen by MIMIQMPS bond dimension: the most entanglement kept across any cut. Higher is more accurate and slower.
entdimintMPS onlychosen by MIMIQMPS entanglement dimension, which bounds the multi-qubit gates applied in one step.
mpscutofffloatMPS onlychosen by MIMIQSingular-value truncation cutoff for MPS. Smaller is more accurate and slower.
seedintbothrandomSeed for sampling. The same seed returns identical counts.
timelimitintbothchosen by MIMIQMaximum execution time, in minutes.

An option name MIMIQ doesn’t recognize fails the job rather than being ignored. See When a job fails.

Advanced options

MIMIQ can also rewrite the circuit before simulating it, and tune how MPS applies gates. Each of these is left to MIMIQ unless you set it:

OptionTypeDevicesValuesEffect
fuseboolbothTrue, FalseMerges consecutive gates on the same qubits into one gate. Fewer operations, but each one is larger.
remove_swapsboolbothTrue, FalseAbsorbs SWAP gates into the qubit ordering instead of simulating them, which can cut depth and gate count considerably.
canonicaldecomposeboolbothTrue, FalseDecomposes the circuit into MIMIQ’s canonical gate set (U and CX) before simulating.
reorderqubitsbool or strbothTrue, False, or a method name (below)Reorders qubits so that interacting qubits sit close together. This mostly helps MPS, where long-range gates are expensive.
reorderqubits_seedintbothany integerSeed for the stochastic reordering methods, independent of seed. Fixes the ordering without fixing the sampling, or the other way round.
mpsmethodstrMPS only"dmpo", "vmpoa", "vmpob"How each gate is applied to the MPS: "dmpo" applies it directly and then compresses; "vmpoa" and "vmpob" use a variational fit.
mpotraversalstrMPS only"sequential" (default), "bfs"The order in which the circuit is grouped into MPOs. Sequential is the standard; breadth-first can suit some circuit structures.
streamingboolMPSTrue, FalseOn MIMIQ MPS, uses the streaming simulator, which doesn’t keep the full state in memory. It fits larger circuits but can’t compute amplitudes.

reorderqubits=True uses "sa_warm_start". The methods are:

  • Deterministic, which ignore reorderqubits_seed: "greedy" (greedy weight-first insertion), "spectral" (Fiedler-vector ordering of the interaction graph) and "rcm" (reverse Cuthill–McKee, a bandwidth-reduction ordering).
  • Stochastic: "sa_warm_start" (a greedy start refined by simulated annealing), "sa_only" (simulated annealing alone), "memetic" (an evolutionary search), "multilevel" (coarsen, then refine), "grasp" (GRASP with path relinking) and "hybrid" (multilevel plus GRASP: the best orderings, at the highest cost). "memetic", "grasp" and "hybrid" are limited to 50 qubits.
job = mps.run(
    circuit,
    shots=200,
    runtime_options={"remove_swaps": True, "reorderqubits": "spectral", "mpsmethod": "dmpo"},
)

Pass booleans as True or False, not strings: {"fuse": "yes"} fails the job with Invalid input parameters. Malformed request. An invalid mpsmethod or mpotraversal fails with a message listing the allowed values. An unrecognized reorderqubits method name is not rejected, so check its spelling. QPerfect’s simulation parameters page has more detail on each option.

Device options do not go in the signature. device.run(circuit, bonddim=64) raises TypeError. Put them in runtime_options={...}. shots is the exception: it is an argument of run() itself.

Choosing a device

MIMIQ MPS handles 100 qubits when entanglement stays low

A GHZ circuit at 100 qubits, well past MIMIQ SV’s 32, runs on MIMIQ MPS:

def ghz(n: int) -> QuantumCircuit:
    qc = QuantumCircuit(n)
    qc.h(0)
    for i in range(n - 1):
        qc.cx(i, i + 1)
    qc.measure_all()
    return qc

mps = provider.get_device("qperfect:qperfect:sim:mimiq-mps")

result = mps.run(ghz(100), shots=200).result()
print(result.data.get_counts())
# two outcomes: '000…000' (100 shots) and '111…111' (100 shots)
print(result.data.extra.get("fidelity"))
# 0.9999999999999996, exact up to floating-point rounding

A GHZ state is only lightly entangled however wide it is: any cut through the chain carries one bit of entanglement. MPS cost depends on entanglement rather than qubit count, so this runs exactly.

The reverse also holds. A deep, highly entangling circuit can be hard for MPS even at modest width. Then you trade accuracy for speed with bonddim and mpscutoff, and cap the run with timelimit:

job = mps.run(ghz(100), shots=200, runtime_options={"bonddim": 32, "timelimit": 5})

How exact an MPS result is

When MPS has to truncate the state, the counts are approximate. Each MIMIQ MPS result reports MIMIQ’s fidelity lower bound in result.data.extra, where 1 means exact:

fidelity = result.data.extra.get("fidelity")

Use .get(): the value is left out when MIMIQ cannot report one. In a batch it is on each circuit’s result, result.results[i].data.extra, rather than the batch’s. If the fidelity is well below 1, raise bonddim, or run the circuit on MIMIQ SV if it fits in 32 qubits.

Reproducible counts with seed

Two identical runs normally return different counts. With the same seed they match exactly:

sv = provider.get_device("qperfect:qperfect:sim:mimiq-sv")

opts = {"seed": 7}
sv.run(ghz(3), shots=200, runtime_options=opts).result().data.get_counts()
# {'000': 113, '111': 87}
sv.run(ghz(3), shots=200, runtime_options=opts).result().data.get_counts()
# {'000': 113, '111': 87}

Batch jobs

Both devices support batch jobs (device.profile.batch_job_support is True). Pass a list of circuits with as_batch=True and they run as one job, with one job ID and one status, and you get one set of counts per circuit, in order:

import numpy as np

angles = np.linspace(0, 2 * np.pi, 12)
sweep = []
for theta in angles:
    qc = QuantumCircuit(1)
    qc.ry(theta, 0)
    qc.measure_all()
    sweep.append(qc)

job = sv.run(sweep, shots=500, as_batch=True)
counts = job.result().data.get_counts()  # a list of 12 count dicts

p1 = [c.get("1", 0) / 500 for c in counts]  # follows sin²(θ/2)

Without as_batch=True, a list of circuits is submitted as separate jobs, one per circuit, and run() returns a list of jobs.

Circuits in a batch share one seed. MIMIQ takes a single seed per job, drawn at random when you don’t pass one, so every circuit in a batch is sampled from the same random stream. Circuits with the same outcome distribution return identical counts: in the sweep above, mirror-image angles θ and 2π − θ come back identical.

Each circuit’s counts are correctly distributed on their own, but they are not independent of the other circuits in the batch. If your analysis needs independent samples per circuit, submit the circuits as separate jobs.

Reading the results

Results come back at your circuit’s own width, and qubit ordering is little-endian: qubit 0 is the rightmost bit.

qc = QuantumCircuit(3)
qc.x(0)
qc.measure_all()

sv.run(qc, shots=100).result().data.get_counts()
# {'001': 100}
#     ^ qubit 0

When a job fails

When a job fails, the reason is on its status:

job.wait_for_final_state()
status = job.status()
if status.name == "FAILED":
    print(status.status_message)

Read it there. On a failed job, job.result() returns success=False with no data, so get_counts() raises a generic ValueError: Counts data is not available.

Execution time

Small jobs, from a 3-qubit circuit to a 100-qubit GHZ state on MIMIQ MPS, take 20 to 45 seconds end to end. Most of that is submission and queueing overhead, not simulation. A batch of twelve circuits completes in about the same time as a single job, which makes as_batch=True the fast way to run a sweep.

Troubleshooting

SymptomCause
TypeError: submit() got an unexpected keyword argumentDevice options belong in runtime_options={...}, not the run() signature. See Runtime options.
ValueError: Counts data is not available.The job failed. Read job.status().status_message. See When a job fails.
Runtime option(s) [...] only apply to MPSAn MPS option such as bonddim was sent to MIMIQ SV. Submit to mimiq-mps.
Unsupported MIMIQ submit option(s): [...]A misspelled or unknown option name in runtime_options.
Invalid input parameters. Malformed request.An option has the wrong type, such as "yes" for a boolean. See Advanced options.
KeyError: 'fidelity'Read the fidelity with result.data.extra.get("fidelity"). See How exact an MPS result is.
Circuits in a batch return identical countsCircuits in a batch share one seed. See Batch jobs.
Results differ between identical runsSampling is random. Pass a seed to fix it.