Source code for blueqat.eo.encoding

# Copyright 2019-2026 The Blueqat Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The 3-spin decoherence-free-subsystem (DFS) encoding of exchange-only qubits.

One logical qubit lives in the total-spin S=1/2 sector of 3 physical spins
(spin up = ``|0>``, physical qubit 3i+k is spin k of logical qubit i, qubit 0 is
the least-significant statevector bit, as everywhere in this SDK):

    ``|0_L>`` = ``|singlet(0,1)>`` ``|up(2)>``
    ``|1_L>`` = sqrt(2/3) ``|T+(0,1)>`` ``|down(2)>`` - sqrt(1/3) ``|T0(0,1)>`` ``|up(2)>``

Each logical state comes in two "gauge" copies, the total-Sz m=+1/2 sector
above and its m=-1/2 partner; exchange acts identically on both, and any
population in the fully symmetric S=3/2 quadruplet is leakage.
"""

import math
from typing import Sequence, Tuple

import torch

_SQ2 = math.sqrt(2.0)
_SQ3 = math.sqrt(3.0)
_SQ6 = math.sqrt(6.0)


def _vec(amplitudes: dict) -> torch.Tensor:
    v = torch.zeros(8, dtype=torch.complex128)
    for idx, a in amplitudes.items():
        v[idx] = a
    return v


# m = +1/2 sector. Basis-state indices are (q2 q1 q0) bit patterns.
_KET_0L_PLUS = _vec({0b010: 1 / _SQ2, 0b001: -1 / _SQ2})
_KET_1L_PLUS = _vec({0b100: math.sqrt(2 / 3), 0b010: -1 / _SQ6, 0b001: -1 / _SQ6})

# m = -1/2 sector: defined as the (normalized) total-spin lowering S_- of the
# m = +1/2 codewords, so that exchange acts with the SAME 2x2 logical block in
# both sectors (a sign flip here would conjugate the - sector's action by Z).
_KET_0L_MINUS = _vec({0b110: 1 / _SQ2, 0b101: -1 / _SQ2})
_KET_1L_MINUS = _vec({0b101: 1 / _SQ6, 0b110: 1 / _SQ6, 0b011: -math.sqrt(2 / 3)})

# Fully symmetric S=3/2 quadruplet (the leakage space).
_QUAD = torch.stack([
    _vec({0b000: 1.0}),
    _vec({0b001: 1 / _SQ3, 0b010: 1 / _SQ3, 0b100: 1 / _SQ3}),
    _vec({0b011: 1 / _SQ3, 0b101: 1 / _SQ3, 0b110: 1 / _SQ3}),
    _vec({0b111: 1.0}),
], dim=1)


[docs] def codeword_basis(m: str = '+') -> torch.Tensor: """(8, 2) matrix whose columns are ``|0_L>``, ``|1_L>`` of the requested gauge sector ('+' for total Sz = +1/2, '-' for -1/2).""" if m == '+': return torch.stack([_KET_0L_PLUS, _KET_1L_PLUS], dim=1) if m == '-': return torch.stack([_KET_0L_MINUS, _KET_1L_MINUS], dim=1) raise ValueError("m must be '+' or '-'")
[docs] def encode_state(logical_amplitudes: Sequence[Sequence[complex]], m: str = '+') -> torch.Tensor: """Encode a product state of logical qubits into 3n physical spins. `logical_amplitudes[i]` is the (alpha, beta) pair of logical qubit i. Returns the 2**(3n) statevector (logical qubit 0's spins are physical qubits 0..2, i.e. the least-significant bits).""" basis = codeword_basis(m) state = None for amps in logical_amplitudes: a, b = complex(amps[0]), complex(amps[1]) norm = math.sqrt(abs(a) ** 2 + abs(b) ** 2) if norm < 1e-12: raise ValueError('logical amplitudes must not be all zero.') triple = (a * basis[:, 0] + b * basis[:, 1]) / norm # Later logical qubits occupy more-significant bits. state = triple if state is None else torch.kron(triple, state) if state is None: raise ValueError('logical_amplitudes must not be empty.') return state
[docs] def leakage(state: torch.Tensor, triple: int = 0) -> float: """Population outside the S=1/2 subspace of the given 3-spin triple, i.e. the weight in its fully symmetric S=3/2 quadruplet.""" n_qubits = (state.numel() - 1).bit_length() n_triples = n_qubits // 3 if not 0 <= triple < n_triples: raise ValueError(f'triple must be in range(0, {n_triples}).') # Move the triple's three bits to the front: reshape so that the triple's # axes are contiguous, then contract with the quadruplet basis. t = state.reshape((2, ) * n_qubits) # axis of physical qubit q is (n_qubits - 1 - q) axes = [n_qubits - 1 - (3 * triple + k) for k in (2, 1, 0)] rest = [ax for ax in range(n_qubits) if ax not in axes] t = t.permute(axes + rest).reshape(8, -1) proj = _QUAD.conj().T.to(t.dtype) @ t return float((proj.abs() ** 2).sum().real)
[docs] def logical_action(unitary8: torch.Tensor, m: str = '+', atol: float = 1e-9) -> torch.Tensor: """Extract the 2x2 logical action of a 3-spin (8x8) unitary. Raises ValueError if the unitary leaks out of the logical subspace of the requested gauge sector (the extracted block would then be non-unitary).""" basis = codeword_basis(m).to(unitary8.dtype) block = basis.conj().T @ unitary8 @ basis eye = torch.eye(2, dtype=block.dtype) if not torch.allclose(block @ block.conj().T, eye, atol=math.sqrt(atol)): raise ValueError('unitary leaks outside the logical subspace ' f'(sector m={m}).') return block
[docs] def logical_fidelity(actual: torch.Tensor, target: torch.Tensor) -> float: """Phase-insensitive gate fidelity ``|tr(A^dagger T)|^2 / d^2`` of two equally-sized unitaries.""" d = actual.shape[0] tr = torch.trace(actual.conj().T @ target.to(actual.dtype)) return float((tr.abs() ** 2 / d ** 2).real)
[docs] def two_qubit_codeword_basis(m1: str, m2: str) -> torch.Tensor: """(64, 4) basis of a 2-logical-qubit (6-spin) sector: columns are ``|00_L>``, ``|01_L>``, ``|10_L>``, ``|11_L>`` with gauge m1 for logical qubit 0 (spins 0-2) and m2 for logical qubit 1 (spins 3-5).""" b1 = codeword_basis(m1) b2 = codeword_basis(m2) cols = [] for j in range(2): # logical qubit 1 (more significant) for i in range(2): # logical qubit 0 (less significant) cols.append(torch.kron(b2[:, j], b1[:, i])) # Column order built above is |00>, |01>, |10>, |11> with logical qubit 0 # as the least-significant logical bit. return torch.stack([cols[0], cols[1], cols[2], cols[3]], dim=1)
[docs] def two_qubit_logical_action(unitary64: torch.Tensor, m1: str = '+', m2: str = '+', atol: float = 1e-9) -> torch.Tensor: """Extract the 4x4 logical action of a 6-spin unitary on the encoded pair.""" basis = two_qubit_codeword_basis(m1, m2).to(unitary64.dtype) block = basis.conj().T @ unitary64 @ basis eye = torch.eye(4, dtype=block.dtype) if not torch.allclose(block @ block.conj().T, eye, atol=math.sqrt(atol)): raise ValueError('unitary leaks outside the 2-qubit logical subspace ' f'(sector m1={m1}, m2={m2}).') return block