Gyanateet Dutta
Fix Space loading: direct Streamlit, lazy imports, ReNova page, fix deps
9f8cf99
Raw
History Blame Contribute Delete
5.76 kB
"""Noise model abstractions for Stim circuit builders.
The models emit Stim instruction strings for gate, measurement, and reset noise.
Builders can optionally request extra correlated-event instructions per layer.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
import random
from typing import Iterable, List, Optional, Sequence
class NoiseModel(ABC):
"""Interface for noise-channel emission during circuit construction."""
@abstractmethod
def gate_noise(
self,
*,
gate: str,
pair_targets: Sequence[int],
idle_targets: Sequence[int],
layer_id: str,
) -> List[str]:
"""Return Stim instructions for a two-qubit gate layer."""
@abstractmethod
def measurement_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
"""Return Stim instructions before a measurement operation."""
@abstractmethod
def reset_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
"""Return Stim instructions immediately after reset operations."""
def correlated_event_instructions(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
"""Optional extra non-local/correlated noise instructions."""
return []
@dataclass
class IIDDepolarizingNoiseModel(NoiseModel):
"""Current baseline: IID depolarizing and IID X-flip reset/measurement noise."""
p: float
def gate_noise(self, *, gate: str, pair_targets: Sequence[int], idle_targets: Sequence[int], layer_id: str) -> List[str]:
lines: List[str] = []
if pair_targets:
lines.append(f"DEPOLARIZE2({self.p}) {' '.join(map(str, pair_targets))}")
if idle_targets:
lines.append(f"DEPOLARIZE1({self.p}) {' '.join(map(str, idle_targets))}")
lines.extend(self.correlated_event_instructions(qubits=list(pair_targets) + list(idle_targets), layer_id=layer_id))
return lines
def measurement_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
if not qubits:
return []
return [f"X_ERROR({self.p}) {' '.join(map(str, qubits))}"]
def reset_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
if not qubits:
return []
return [f"X_ERROR({self.p}) {' '.join(map(str, qubits))}"]
@dataclass
class BiasedNoiseModel(NoiseModel):
"""Single-qubit biased channel plus depolarizing entangling noise.
Useful for cat-qubit-like asymmetry where bit- and phase-flip rates differ.
"""
gate_p: float = 0.0
bit_flip_p: float = 0.0
phase_flip_p: float = 0.0
# Optional parameters for the new interface
p_x: float = 0.0
p_y: float = 0.0
p_z: float = 0.0
biased_pauli: str = "Z"
bias_ratio: float = 10.0
def __post_init__(self):
# Support both old and new initialization signatures
if self.p_x > 0 or self.p_z > 0:
self.bit_flip_p = self.p_x
self.phase_flip_p = self.p_z
# Use average for gate_p if not explicitly provided
if self.gate_p == 0.0:
self.gate_p = self.p_x + self.p_y + self.p_z
def gate_noise(self, *, gate: str, pair_targets: Sequence[int], idle_targets: Sequence[int], layer_id: str) -> List[str]:
lines: List[str] = []
if pair_targets:
lines.append(f"DEPOLARIZE2({self.gate_p}) {' '.join(map(str, pair_targets))}")
if idle_targets:
lines.append(self._biased_channel(idle_targets))
lines.extend(self.correlated_event_instructions(qubits=list(pair_targets) + list(idle_targets), layer_id=layer_id))
return lines
def measurement_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
return [self._biased_channel(qubits)] if qubits else []
def reset_noise(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
return [self._biased_channel(qubits)] if qubits else []
def _biased_channel(self, qubits: Sequence[int]) -> str:
return f"PAULI_CHANNEL_1({self.bit_flip_p},0,{self.phase_flip_p}) {' '.join(map(str, qubits))}"
@dataclass
class ErasureAwareNoiseModel(IIDDepolarizingNoiseModel):
"""IID depolarizing noise augmented with heralded erasure side information."""
erasure_p: float = 0.0
def correlated_event_instructions(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
if not qubits or self.erasure_p <= 0:
return []
return [f"HERALDED_ERASE({self.erasure_p}) {' '.join(map(str, qubits))}"]
@dataclass
class CorrelatedBurstNoiseModel(IIDDepolarizingNoiseModel):
"""Adds reproducible spacetime burst events via correlated errors."""
burst_probability: float = 0.0
max_cluster_size: int = 4
seed: Optional[int] = None
_rng: random.Random = field(init=False, repr=False)
def __post_init__(self) -> None:
self._rng = random.Random(self.seed)
def correlated_event_instructions(self, *, qubits: Sequence[int], layer_id: str) -> List[str]:
if not qubits or self.burst_probability <= 0:
return []
if self._rng.random() >= self.burst_probability:
return []
cluster_size = min(len(qubits), max(2, self.max_cluster_size))
cluster = self._rng.sample(list(qubits), k=cluster_size)
terms = " ".join(f"X{q}" for q in cluster)
return [f"CORRELATED_ERROR({self.p}) {terms}"]
def resolve_noise_model(p: float, noise_model: Optional[NoiseModel]) -> NoiseModel:
"""Map legacy scalar `p` usage to the IID model when needed."""
if noise_model is not None:
return noise_model
return IIDDepolarizingNoiseModel(p=p)