Skip to content

Swap γ and δ usage in the Clifford construction, Fix the Pauli‐gate mapping, and Use circ.add_circuit in‐place #279

@AiPals

Description

@AiPals

Swap γ and δ usage in the Clifford construction so that
γ → CX and S (diagonal)
δ → CZ
The original had these reversed.
Fix the Pauli‐gate mapping:
0 → Identity (no gate)
1 → X
2 → Y
3 → Z
Use circ.add_circuit in‐place rather than reassigning its (None) return value.
Cover all four Pauli values and skip the identity.

Code:

I have identify 4 errors on your code.

Fixes by Jesus Carrasco

from typing import List, Tuple

import numpy as np
from pytket import Circuit

def sample_q_mallows(n_qubits: int) -> Tuple[List[int], List[int]]:
hadamard_layer = [0] * n_qubits
permutation = [0] * n_qubits
avail_qubits = list(range(n_qubits))
log2 = np.log(2.0)

for i in range(n_qubits):
    m = len(avail_qubits)
    r = np.random.uniform(0, 1)
    index = int(2*m - np.ceil(np.log(r*(4**m - 1) + 1) / log2))
    hadamard_layer[i] = int(index < m)
    k = index if index < m else (2*m - index - 1)
    permutation[i] = avail_qubits[k]
    del avail_qubits[k]

return hadamard_layer, permutation

def clifford_canonical_F(
pauli_layer: List[int],
gamma: np.ndarray,
delta: np.ndarray
) -> Circuit:
"""Constructs an H-free Clifford in O·P·CZ·CX order (with γ→CX,S; δ→CZ)."""
n = len(pauli_layer)
circ = Circuit(n)

# 1) CX layer from gamma
for j in range(n):
    for i in range(j):
        if gamma[i, j]:
            circ.CX(i, j, opgroup="Clifford 2")

# 2) CZ layer from delta
for j in range(n):
    for i in range(j):
        if delta[i, j]:
            circ.CZ(i, j, opgroup="Clifford 2")

# 3) S-gates on delta diagonal
for i in range(n):
    if delta[i, i]:
        circ.S(i, opgroup="Clifford 1")

# 4) Pauli layer: 1→X, 2→Y, 3→Z, 0→I (skip)
for i, gate in enumerate(pauli_layer):
    if gate == 1:
        circ.X(i, opgroup="Clifford 1")
    elif gate == 2:
        circ.Y(i, opgroup="Clifford 1")
    elif gate == 3:
        circ.Z(i, opgroup="Clifford 1")
    # gate == 0: identity—no action

return circ

def find_random_gamma_delta(
n_qubits: int,
hadamard_layer: List[int],
permute_layer: List[int]
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
"""Generates two (γ, δ) pairs for H-free Cliffords in the canonical form."""
Delta1 = np.eye(n_qubits, dtype=int)
Delta2 = Delta1.copy()
Gamma1 = np.zeros((n_qubits, n_qubits), dtype=int)
Gamma2 = np.zeros_like(Gamma1)

# Diagonal bits
for i in range(n_qubits):
    Gamma2[i, i] = np.random.randint(2)
    if hadamard_layer[i]:
        Gamma1[i, i] = np.random.randint(2)

# Off‐diagonals
for i in range(n_qubits):
    for j in range(i + 1, n_qubits):
        b = np.random.randint(2)
        Gamma2[i, j] = Gamma2[j, i] = b
        Delta2[i, j] = np.random.randint(2)

        # Conditions for Gamma1
        if hadamard_layer[i] and hadamard_layer[j]:
            b = np.random.randint(2)
            Gamma1[i, j] = Gamma1[j, i] = b
        if hadamard_layer[i] and not hadamard_layer[j] and permute_layer[i] < permute_layer[j]:
            b = np.random.randint(2)
            Gamma1[i, j] = Gamma1[j, i] = b
        if not hadamard_layer[i] and hadamard_layer[j] and permute_layer[i] > permute_layer[j]:
            b = np.random.randint(2)
            Gamma1[i, j] = Gamma1[j, i] = b

        # Conditions for Delta1
        if not hadamard_layer[i] and hadamard_layer[j]:
            Delta1[i, j] = np.random.randint(2)
        if hadamard_layer[i] and hadamard_layer[j] and permute_layer[i] > permute_layer[j]:
            Delta1[i, j] = np.random.randint(2)
        if not hadamard_layer[i] and not hadamard_layer[j] and permute_layer[i] < permute_layer[j]:
            Delta1[i, j] = np.random.randint(2)

return Delta1, Delta2, Gamma1, Gamma2

def random_clifford_circ(n_qubits: int, **kwargs) -> Circuit:
"""
Samples a random n-qubit Clifford F·H·S·F' in canonical form using the quantum Mallows law.
"""
np.random.seed(kwargs.get("seed", None))
circ = Circuit(n_qubits)

hadamard, permute = sample_q_mallows(n_qubits)
D1, D2, G1, G2 = find_random_gamma_delta(n_qubits, hadamard, permute)

# First H-free Clifford F' (random Pauli)
pauli_rand = [np.random.randint(1, 4) for _ in range(n_qubits)]
Fp = clifford_canonical_F(pauli_rand, G2, D2)
circ.add_circuit(Fp, list(range(n_qubits)), [])

# Permutation layer S
for i in range(n_qubits):
    while permute[i] != i:
        j = permute[i]
        circ.SWAP(i, j, opgroup="Clifford 2")
        permute[i], permute[j] = permute[j], permute[i]

# Hadamard layer H
for i, h in enumerate(hadamard):
    if h:
        circ.H(i, opgroup="Clifford 1")

# Second H-free Clifford O (identity Pauli)
O = clifford_canonical_F([0]*n_qubits, G1, D1)
circ.add_circuit(O, list(range(n_qubits)), [])

return circ

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions