QrispSimulatorBackend#
- class QrispSimulatorBackend(pm: PassManager | None = None)[source]#
The built-in Qrisp statevector simulator backend.
This is the simplest concrete
Backendimplementation. It executes circuits synchronously. That is, theQrispSimulatorJobreturned byrun_async()is alreadyDONEbeforerun_async()returns to the caller.Note
The module-level singleton
def_backend(an instance of this class) is used as the default backend throughout Qrisp when no explicit backend is specified. To change the global default, editqrisp.default_backend.- Parameters:
- pmPassManager or None, optional
An optional
PassManagerthat is applied to every circuit before it is submitted to the simulator. This allows users to inject custom transpilation or optimisation passes into the simulation pipeline. Defaults toNone.
Examples
Analytic execution (default)
We first create a
QrispSimulatorBackend:>>> from qrisp import QuantumFloat >>> from qrisp.interface import QrispSimulatorBackend >>> backend = QrispSimulatorBackend()
When
get_measurementis called, Qrisp compiles the computation into a circuit and passes it to the built-in simulator viarun_async(). AQrispSimulatorJobis returned immediately. And, because the simulator is synchronous the job is alreadyDONEbeforeget_measurementeven callsresult():>>> qf = QuantumFloat(3) >>> qf[:] = 4 >>> res = qf * qf >>> res.get_measurement(backend=backend) Simulating ... {16: 1.0}
With
shots=Nonethe result is an exact probability distribution, so{16: 1.0}means the outcome16has probability1.0.Using a PassManager to pre-process circuits
>>> from qrisp import PassManager >>> from qrisp import convert_to_cz, fuse_adjacents >>> pm = PassManager() >>> pm += convert_to_cz() >>> pm += fuse_adjacents >>> backend = QrispSimulatorBackend(pm=pm) >>> # Circuits are now passed through pm before simulation
Inspecting circuits with visualize when evaluating expectation values
pmis especially useful for understanding what circuits the simulator actually receives. By insertingvisualize()as the last pass you can inspect every circuit just before it is executed. This can for instance be used when evaluating expectation values viaQubitOperator.expectation_value: under the hood the operator groups terms by commutativity, appends change-of-basis gates, and submits one circuit per group — details that are invisible from the operator expression alone.from qrisp import QuantumFloat, ry, PassManager, visualize, decompose from qrisp.operators import X, Z from qrisp.interface import QrispSimulatorBackend import numpy as np def state_prep(theta): qv = QuantumFloat(2) ry(theta, qv) return qv H = X(0)*Z(1) + Z(0)*X(1) + X(0) # Attach visualize at the end of the pipeline pm = PassManager() pm += decompose() pm += visualize backend = QrispSimulatorBackend(pm=pm) ev_function = H.expectation_value(state_prep, backend=backend) result = ev_function(np.pi/2)
┌─────────┐┌───┐┌─┐ qv.0: ┤ Ry(π/2) ├┤ H ├┤M├ ├─────────┤└┬─┬┘└╥┘ qv.1: ┤ Ry(π/2) ├─┤M├──╫─ └─────────┘ └╥┘ ║ cb_15: ═════════════╬═══╩═ ║ cb_16: ═════════════╩═════ ┌─────────┐ ┌─┐ qv.0: ┤ Ry(π/2) ├─────┤M├─── ├─────────┤┌───┐└╥┘┌─┐ qv.1: ┤ Ry(π/2) ├┤ H ├─╫─┤M├ └─────────┘└───┘ ║ └╥┘ cb_21: ═════════════════╩══╬═ ║ cb_22: ════════════════════╩═The measured operator contains three terms where two of them commute (
X(0)*Z(1)andX(0)) and a third term that doesn’t commute (Z(0)*X(1)). Non-commuting terms can not be measured simultaneously so we need to distinct simulator calls.Each circuit sent to the simulator is printed to stdout before execution — revealing the state preparation, the change-of-basis gates (e.g. Hadamards to rotate X to Z), and the qubit measurements.
Updating options after construction
Runtime options can be updated via
update_options(). Only keys that were present at construction time may be modified:>>> backend.update_options(shots=512) >>> print(backend.options["shots"]) 512
Using the Job interface directly
run_async()returns aQrispSimulatorJobthat supports the fullJobinterface, even though the result is already available synchronously:>>> from qrisp import QuantumFloat >>> from qrisp.interface import QrispSimulatorBackend >>> backend = QrispSimulatorBackend() >>> qf3 = QuantumFloat(2) >>> qf3[:] = 3 >>> res3 = qf3 * qf3 >>> qc = res3.qs.compile() >>> qc.measure(qc.qubits) >>> job = backend.run_async(qc) Simulating ... >>> print(job.status()) done >>> result = job.result() >>> print(result.get_counts()) {'0100111': 1.0}
QrispSimulatorJob#
- class QrispSimulatorJob(backend: QrispSimulatorBackend, circuits: Sequence, shots: int | list[int] | None)[source]#
A synchronous
Jobproduced byQrispSimulatorBackend.