Models & Methods

The models subpackage contains quantum circuit templates (ansätze), error mitigation techniques, and analysis tools.

Ansätze: Quantum Circuit Templates

Ansätze are pre-built quantum circuit patterns used as templates for quantum algorithms.

class mpstab.models.ansatze.Ansatz(nqubits: int, density_matrix: bool = False)[source]

Bases: ABC

Abstract ansatz to generate quantum states.

property circuit
density_matrix: bool = False
execute(nshots: int = None, initial_state: Circuit = None, with_noise: bool = False)[source]

Execute the circuit and return the outcome.

property nparams
nqubits: int
partitionate_circuit(replacement_probability: float, replacement_method: str)[source]

Partitionate the circuit replacing non-Clifford (magic) gates with a given probability.

For each gate in the original circuit:

  • If the gate is non-Clifford, with probability replacement_probability it is replaced by a Clifford gate via replace_non_clifford_gate.

  • The processed gate (whether replaced or not) is added to the overall circuit.

  • Simultaneously, consecutive gates of the same type (Clifford or non-Clifford) are collected into blocks.

Returns:

the complete processed circuit. clifford_blocks (List[Circuit]): list of circuits, each a block of consecutive Clifford gates. non_clifford_blocks (List[Circuit]): list of circuits, each a block of consecutive non-Clifford gates.

Return type:

partitioned_circuit (Circuit)

update_noise_model(noise_model: NoiseModel)[source]

Construct an attribute which is the noisy version of circuit.

class mpstab.models.ansatze.CircuitAnsatz(density_matrix: bool = False, *, qibo_circuit: Circuit)[source]

Bases: Ansatz

A simple wrapper that converts a Qibo Circuit into an Ansatz object.

Parameters:

input_circuit (Circuit) – The Qibo circuit to wrap.

property circuit
nqubits: int
qibo_circuit: Circuit
class mpstab.models.ansatze.FloquetAnsatz(nqubits: int, density_matrix: bool = False, nlayers: int = 2, b: float = 1.2566370614359172, theta: float = 1.5707963267948966, target_qubit: int = 1, decompose_rzz: bool = True)[source]

Bases: Ansatz

Floquet echo: U = (FL)^t · Rz(theta) on qubit q · (FL^t)†.

Parameters:
  • nlayers (int) – number of Floquet layers.

  • b (float) – parameter controlling the magic in the circuit;

  • theta (float) – controls the signal;

  • target_qubit (int) – this is the qubit, in the circuit, on which we perform the final local measurement (and the one on which we apply H and RZ).

  • decompose_rzz (bool) – if True, RZZ is decomposed into CNOTs and RZ.

b: float = 1.2566370614359172
decompose_rzz: bool = True
nlayers: int = 2
partitionate_circuit(replacement_probability: float, replacement_method: str)[source]

Partitionate the full Floquet circuit U = H · half_sandwich · RZ · (half_sandwich)†, but only sweep the half_sandwich for magic gates. Returns ((magic_gates, clifford_only_circuit), full_circuit).

partitionate_sub_circuit(circuit: Circuit, replacement_probability: float, replacement_method: str)[source]

Partitionate a sub-circuit replacing non-Clifford (magic) gates with a given probability. Returns ((magic_gates, clifford_only_circuit), full_circuit) where magic_gates is List of (local_bp, gate) with local_bp starting at 0.

target_qubit: int = 1
theta: float = 1.5707963267948966
class mpstab.models.ansatze.HammingWeightPreserving(nqubits: int, density_matrix: bool = False, nlayers: int = 1)[source]

Bases: Ansatz

Hamming-weight-preserving ansatz built from decomposed RBS gates.

Each RBS gate rotates within the {|01⟩, |10⟩} subspace of a qubit pair, leaving |00⟩ and |11⟩ unchanged:

RBS(θ)|01⟩ = cos(θ)|01⟩ + sin(θ)|10⟩ RBS(θ)|10⟩ = -sin(θ)|01⟩ + cos(θ)|10⟩

RBS is decomposed into gates mpstab supports natively:

CNOT(ctrl=q1, tgt=q0) RY(q1, θ) ← magic gate CNOT(ctrl=q0, tgt=q1) RY(q1, -θ) ← magic gate CNOT(ctrl=q0, tgt=q1) CNOT(ctrl=q1, tgt=q0)

The circuit always starts with n//2 X gates to prepare the half-filling state |1…10…0⟩, giving a fixed Hamming weight of n//2.

Within each layer the connectivity pattern mirrors hw_preserving from Qibo examples: four connect_qubits passes with jump sizes 1 and 2 and different starting offsets to explore both nearest- and next-nearest- neighbour correlations with circular boundary conditions.

Parameters:
  • nqubits – Number of qubits (must be even).

  • nlayers – Number of RBS layers.

nlayers: int = 1
class mpstab.models.ansatze.HardwareEfficient(nqubits: int, density_matrix: bool = False, nlayers: int = 1, entangling: bool = True)[source]

Bases: Ansatz

Hardware Efficient ansatz.

entanglement_layer()[source]

Construct an entanglement layer compatible with the target quantum circuit.

entangling: bool = True
nlayers: int = 1
property parameters_per_layers
parametric_layer(layer_index: int)[source]

Return the gates composing a parametric layer.

class mpstab.models.ansatze.HardwareEfficientBrickwork(nqubits: int, density_matrix: bool = False, nlayers: int = 1)[source]

Bases: Ansatz

Hardware Efficient ansatz with a full brickwork entanglement pattern.

Each layer applies two sublayers of single-qubit RY+RZ rotations interleaved with CZ entanglement:

sublayer 1: RY+RZ on all qubits → CZ on even bonds (0-1, 2-3, …) sublayer 2: RY+RZ on all qubits → CZ on odd bonds (1-2, 3-4, …) + CZ(0, n-1)

A final layer of RY rotations closes the circuit. The ring-closing CZ(0, n-1) in the odd sublayer creates long-range entanglement.

Parameters:
  • nqubits – Number of qubits.

  • nlayers – Number of full brickwork layers.

nlayers: int = 1
class mpstab.models.ansatze.TranspiledAnsatz(density_matrix: bool = False, *, original_circuit: Circuit, native_gates: List | None = <factory>, connectivity: Graph | None = None)[source]

Bases: Ansatz

Any ansatz which is also transpiled into native gates of a given quantum device presenting a given connectivity.

Parameters:
  • original_circuit – The circuit to be transpiled.

  • native_gates – Optional[List]: list of native gates of the used device. Default is [gates.GPI2, gates.RZ, gates.Z, gates.CZ].

  • connectivity – Optional[nx.Graph]: graph representing the topology of the used device. Default is None and in this case the transpilation does not take into account any connectivity constraint.

property circuit
connectivity: Graph | None = None
native_gates: List | None
nqubits: int
original_circuit: Circuit

Available Ansätze:

  • Ansatz: Abstract base class for all ansätze

  • CircuitAnsatz: Wrapper for Qibo circuits

  • HardwareEfficient: Industry-standard pattern for NISQ devices

Key Methods:

  • execute(nshots, initial_state, with_noise): Execute the circuit

  • update_noise_model(noise_model): Add noise to simulation

  • partitionate_circuit(replacement_probability, replacement_method): Partition circuit

  • nparams: Number of circuit parameters

  • circuit: Access the underlying QiboCircuit

Example Usage:

from mpstab.models.ansatze import HardwareEfficient
from mpstab import HSMPO

# Create ansatz
ansatz = HardwareEfficient(nqubits=5, nlayers=3)

# Use with HSMPO
simulator = HSMPO(ansatz=ansatz, max_bond_dimension=32)
result = simulator.expectation("ZZZZZ")

See Using Ansätze for detailed examples.

Error Mitigation

Techniques for reducing approximation errors and improving simulation accuracy.

mpstab.models.mitigation_methods.TNCDR(observable: str, ansatz: Ansatz, noise_model: NoiseModel, replacement_probability: float, initial_state: Circuit = None, replacement_method: str = 'closest', ncircuits: int = 50, nshots: int | None = None, random_seed: int = 42, fit_map=<function <lambda>>, expval_threshold: float = 1e-07, max_bond_dimension: int | None = None)[source]
mpstab.models.mitigation_methods.density_matrix_circuit(circuit)[source]

Helper method to convert a circuit into its correspondent with density_matrix=True.

Entanglement Metrics & Analysis

Tools for analyzing entanglement and state properties.

mpstab.models.entropies.generate_pauli_strings(n: int) → list[str][source]

Generate all possible n-qubit Pauli strings.

mpstab.models.entropies.stabilizer_renyi_entropy(state: ndarray, alpha: int) → float[source]

Compute the exact Stabilizer Rényi Entropy of order alpha for a dense state vector. Implementation follows Eq. (1) of https://arxiv.org/pdf/2207.13076.

Cost is O(4^n) — only suitable for n ≲ 12.

mpstab.models.entropies.stabilizer_renyi_entropy_mps(hsmpo: HSMPO, alpha: int = 2, n_samples: int = 1000, seed: int | None = None) → float[source]

Stochastic estimate of the Stabilizer Rényi Entropy of order alpha, using importance-free uniform Pauli sampling over the MPS representation.

Follows the estimator of Lami & Collura (PRL 2023):

S_α ≈ (1/(1-α)) * log( (4^n / K) * Σ_{k=1}^{K} |⟨P_k⟩|^{2α} / 2^n )

where P_k are sampled uniformly at random from the 4^n Pauli strings. This is an unbiased Monte Carlo estimate of the exact sum.

Exploits the precomputed MPS cache in HSMPO (original_circuit_mps, clifford_circuit), so no MPS re-initialization is performed.

Parameters:
  • hsmpo (HSMPO) – The hybrid stabilizer MPO object with a precomputed MPS cache.

  • alpha (int) – Rényi order, must be ≥ 2.

  • n_samples (int) – Number of random Pauli strings to sample. Higher gives lower variance. As a rough guide, n_samples ~ 1000–5000 is sufficient for n ≲ 25.

  • seed (int or None) – Random seed for reproducibility.

Returns:

Stochastic estimate of SRE_α.

Return type:

float

Available Functions:

  • stabilizer_renyi_entropy(state, alpha): Stabilizer Renyi entropy for a given state and order

Additional Utilities

mpstab.models.utils.build_noise_model(nqubits: int, local_pauli_noise_sigma: float, readout_bit_flip_prob: float = None)[source]

Costruct noise model as a local Pauli noise channel + readout noise.

mpstab.models.utils.get_closest_angle(old_angle, candidates)[source]

Return the closest angle to old_angle among some candidates.

mpstab.models.utils.hardware_compatible_circuit(circuit: ~qibo.models.circuit.Circuit, native_gates: ~typing.List | None = [<class 'qibo.gates.gates.GPI2'>, <class 'qibo.gates.gates.RZ'>, <class 'qibo.gates.gates.Z'>, <class 'qibo.gates.gates.CZ'>], connectivity: ~networkx.classes.graph.Graph | None = None)[source]
mpstab.models.utils.obs_string_to_qibo_hamiltonian(observable: str, backend: Backend = None) → SymbolicHamiltonian[source]

Convert a string representation of a Pauli observable to a Qibo symbolic Hamiltonian.

Parameters:

observable (str) – A string representing the Pauli observable, e.g., “XZIY”.

Returns:

The corresponding Qibo symbolic Hamiltonian.

Return type:

hamiltonians.SymbolicHamiltonian

mpstab.models.utils.replace_non_clifford_gate(gate, replacement_method, candidates=None)[source]

Replace non‐Clifford RX/RY/RZ or GPI2 gate with a Clifford one.

Quick Reference