-
Notifications
You must be signed in to change notification settings - Fork 5
Add Circuit.is_clifford property and replace U3 gates with Clifford equivalents for pi/2 rotations #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Circuit.is_clifford property and replace U3 gates with Clifford equivalents for pi/2 rotations #69
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from fractions import Fraction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from typing import Any, Iterable, Literal, cast, overload | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pyzx_param as zx | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -11,6 +12,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.core.graph import build_sampling_graph | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.core.parse import parse_parametric_tag, parse_stim_circuit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.noise.dem import get_detector_error_model | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.utils.clifford import parametric_to_clifford_gates | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.utils.diagram import render_svg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from tsim.utils.program_text import shorthand_to_stim, stim_to_shorthand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -229,8 +231,69 @@ def compile_m2d_converter( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @property | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def stim_circuit(self) -> stim.Circuit: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return the underlying stim circuit.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self._stim_circ.copy() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return the underlying stim circuit. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Parametric rotation instructions whose angles are all half-π multiples | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| are expanded into their equivalent Clifford gates. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| circ = stim.Circuit() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for instr in self._stim_circ: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert not isinstance(instr, stim.CircuitRepeatBlock) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if instr.name == "I" and instr.tag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = parse_parametric_tag(instr.tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gate_name, params = result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clifford_gates = parametric_to_clifford_gates(gate_name, params) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if clifford_gates is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| targets = [t.value for t in instr.targets_copy()] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for gate in clifford_gates: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| circ.append(gate, targets, []) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| circ.append(instr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return circ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @property | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def is_clifford(self) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Check if the circuit is a Clifford circuit. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A circuit is a Clifford circuit if it only contains Clifford gates (i.e. half-pi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| multiples of the rotation angles). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| True if the circuit is a Clifford circuit, otherwise False. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def is_half_pi_multiple(phase: Fraction) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return phase.denominator <= 2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for instr in self._stim_circ: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert not isinstance(instr, stim.CircuitRepeatBlock) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if instr.name in {"S", "S_DAG"} and instr.tag == "T": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if instr.name == "I" and instr.tag: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = parse_parametric_tag(instr.tag) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if result is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gate_name, params = result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if gate_name in {"R_X", "R_Y", "R_Z"}: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not is_half_pi_multiple(params["theta"]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif gate_name == "U3": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not all( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_half_pi_multiple(params[name]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name in ("theta", "phi", "lambda") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+261
to
+293
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A circuit is a Clifford circuit if it only contains Clifford gates (i.e. half-pi | |
| multiples of the rotation angles). | |
| Returns: | |
| True if the circuit is a Clifford circuit, otherwise False. | |
| """ | |
| def is_half_pi_multiple(phase: Fraction) -> bool: | |
| return phase.denominator <= 2 | |
| for instr in self._stim_circ: | |
| assert not isinstance(instr, stim.CircuitRepeatBlock) | |
| if instr.name in {"S", "S_DAG"} and instr.tag == "T": | |
| return False | |
| if instr.name == "I" and instr.tag: | |
| result = parse_parametric_tag(instr.tag) | |
| if result is None: | |
| return False | |
| gate_name, params = result | |
| if gate_name in {"R_X", "R_Y", "R_Z"}: | |
| if not is_half_pi_multiple(params["theta"]): | |
| return False | |
| elif gate_name == "U3": | |
| if not all( | |
| is_half_pi_multiple(params[name]) | |
| for name in ("theta", "phi", "lambda") | |
| ): | |
| return False | |
| else: | |
| A circuit is a Clifford circuit if it only contains Clifford gates. | |
| Returns: | |
| True if the circuit is a Clifford circuit, otherwise False. | |
| """ | |
| for instr in self._stim_circ: | |
| assert not isinstance(instr, stim.CircuitRepeatBlock) | |
| # S / S_DAG tagged as T are treated as non-Clifford. | |
| if instr.name in {"S", "S_DAG"} and instr.tag == "T": | |
| return False | |
| # Parametric single-qubit gates are encoded as tagged identity operations. | |
| if instr.name == "I" and instr.tag: | |
| result = parse_parametric_tag(instr.tag) | |
| if result is None: | |
| # Unrecognized parametric tag; conservatively treat as non-Clifford. | |
| return False | |
| gate_name, params = result | |
| # Delegate Clifford-ness to the same logic used for expansion. | |
| expanded = parametric_to_clifford_gates(gate_name, params) | |
| if expanded is None: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| """Mapping tables for converting parametric rotations with half-pi angles to Clifford gates.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from fractions import Fraction | ||
|
|
||
| # Clifford decompositions for U3(θ, φ, λ) = R_Z(φ) · R_Y(θ) · R_Z(λ). | ||
| # Keys: (θ_idx, φ_idx, λ_idx) where each index ∈ {0,1,2,3} is the angle in half-pi units. | ||
| # Values: stim gate names in circuit (time) order. | ||
| U3_CLIFFORD: dict[tuple[int, int, int], list[str]] = { | ||
| (0, 0, 0): ["I"], | ||
| (0, 0, 1): ["S"], | ||
| (0, 0, 2): ["Z"], | ||
| (0, 0, 3): ["S_DAG"], | ||
| (0, 1, 0): ["S"], | ||
| (0, 1, 1): ["Z"], | ||
| (0, 1, 2): ["S_DAG"], | ||
| (0, 1, 3): ["I"], | ||
| (1, 0, 0): ["SQRT_Y"], | ||
| (1, 0, 1): ["S", "SQRT_Y"], | ||
| (1, 0, 2): ["H"], | ||
| (1, 0, 3): ["S_DAG", "SQRT_Y"], | ||
| (1, 1, 0): ["S", "SQRT_X_DAG"], | ||
| (1, 1, 1): ["Z", "SQRT_X_DAG"], | ||
| (1, 1, 2): ["S_DAG", "SQRT_X_DAG"], | ||
| (1, 1, 3): ["SQRT_X_DAG"], | ||
| (1, 2, 0): ["Z", "SQRT_Y_DAG"], | ||
| (1, 2, 1): ["S_DAG", "SQRT_Y_DAG"], | ||
| (1, 2, 2): ["SQRT_Y_DAG"], | ||
| (1, 2, 3): ["S", "SQRT_Y_DAG"], | ||
| (1, 3, 0): ["S_DAG", "SQRT_X"], | ||
| (1, 3, 1): ["SQRT_X"], | ||
| (1, 3, 2): ["S", "SQRT_X"], | ||
| (1, 3, 3): ["Z", "SQRT_X"], | ||
| (2, 0, 0): ["Y"], | ||
| (2, 0, 1): ["S", "Y"], | ||
| (2, 0, 2): ["X"], | ||
| (2, 0, 3): ["S_DAG", "Y"], | ||
| (2, 1, 0): ["Y", "S"], | ||
| (2, 1, 1): ["Y"], | ||
| (2, 1, 2): ["S", "Y"], | ||
| (2, 1, 3): ["X"], | ||
| } | ||
|
|
||
| RZ_CLIFFORD: dict[int, str] = {0: "I", 1: "S", 2: "Z", 3: "S_DAG"} | ||
| RX_CLIFFORD: dict[int, str] = {0: "I", 1: "SQRT_X", 2: "X", 3: "SQRT_X_DAG"} | ||
| RY_CLIFFORD: dict[int, str] = {0: "I", 1: "SQRT_Y", 2: "Y", 3: "SQRT_Y_DAG"} | ||
|
|
||
|
|
||
| def _to_half_pi_index(phase: Fraction) -> int | None: | ||
| """Convert a phase (in units of π) to a half-π index 0–3, or *None*.""" | ||
| if phase.denominator > 2: | ||
| return None | ||
| return int(phase * 2) % 4 | ||
|
|
||
|
|
||
| def _equivalent_u3_key(t: int, p: int, l: int) -> tuple[int, int, int]: | ||
| """U3(θ, φ, λ) ≡ U3(2π-θ, φ+π, λ+π) up to global phase.""" | ||
| return ((4 - t) % 4, (p + 2) % 4, (l + 2) % 4) | ||
|
|
||
|
|
||
| def parametric_to_clifford_gates( | ||
| gate_name: str, params: dict[str, Fraction] | ||
| ) -> list[str] | None: | ||
| """Convert a parametric gate with half-π angles to stim Clifford gate names. | ||
|
|
||
| Args: | ||
| gate_name: One of ``"R_X"``, ``"R_Y"``, ``"R_Z"``, ``"U3"``. | ||
| params: Dict as returned by :func:`~tsim.core.parse.parse_parametric_tag`. | ||
|
|
||
| Returns: | ||
| Stim gate names in circuit order, | ||
| or ``None`` when the angles are not half-π multiples. | ||
|
|
||
| """ | ||
| if gate_name in ("R_X", "R_Y", "R_Z"): | ||
| idx = _to_half_pi_index(params["theta"]) | ||
| if idx is None: | ||
| return None | ||
| table = {"R_Z": RZ_CLIFFORD, "R_X": RX_CLIFFORD, "R_Y": RY_CLIFFORD}[gate_name] | ||
| return [table[idx]] | ||
|
|
||
| if gate_name == "U3": | ||
| theta_idx = _to_half_pi_index(params["theta"]) | ||
| phi_idx = _to_half_pi_index(params["phi"]) | ||
| lam_idx = _to_half_pi_index(params["lambda"]) | ||
| if theta_idx is None or phi_idx is None or lam_idx is None: | ||
| return None | ||
|
|
||
| key = (theta_idx, phi_idx, lam_idx) | ||
| gates = U3_CLIFFORD.get(key) | ||
| if gates is None: | ||
| gates = U3_CLIFFORD.get(_equivalent_u3_key(*key)) | ||
| assert gates is not None | ||
rafaelha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return list(gates) | ||
|
|
||
| return None | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_cliffordcan raiseKeyErroron malformed-but-parseable parametric tags (e.g.I[R_Z(phi=0.5*pi)]), becauseparse_parametric_tagdoesn't validate parameter names but this code unconditionally indexesparams["theta"]/params["phi"]/params["lambda"]. Since this is a boolean probe, it should returnFalseinstead of throwing; consider guarding missing keys (or reusingparametric_to_clifford_gatesand treating failures as non-Clifford).