Source code for blueqat.eo.sequences

# 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.
"""Analytic exchange-pulse sequences for logical gates on encoded EO qubits.

A sequence is a list of ((i, j), theta) pairs in application order, where
(i, j) are physical spin indices *within* the logical qubits involved and
theta is the exchange pulse area for `Circuit().exch(theta)[i, j]`. All
logical gates are exact up to a global phase.

Single-qubit tables and the serial Fong-Wandzura CNOT follow the constant-
amplitude constructions used in eoqrid (MIT, https://github.com/samn33/eoqrid)
and Weinstein et al., Nature 615, 817 (2023); the CNOT runs on the 6-spin
linear chain t0-t1-t2-c2-c1-c0 (nearest-neighbor pulses only) in 28 pulses.
"""

import math
from typing import List, Sequence, Tuple

from ..circuit import Circuit

Pulse = Tuple[Tuple[int, int], float]

_TWO_PI = 2.0 * math.pi
# arccos(1/3): the tilt angle between the two rotation axes generated by
# exchange on pairs (0,1) and (1,2).
_THETA_C = math.acos(1.0 / 3.0)


[docs] def rz_sequence(phase: float, offset: int = 0) -> List[Pulse]: """Logical RZ(phase): a single pulse on the (0,1) pair (the singlet in ``|0_L>`` picks up e^{i theta}, giving RZ(-theta) up to global phase).""" theta = (-phase) % _TWO_PI if abs(theta) < 1e-12 or abs(theta - _TWO_PI) < 1e-12: return [] return [((offset, offset + 1), theta)]
def z_sequence(offset: int = 0) -> List[Pulse]: return rz_sequence(math.pi, offset) def s_sequence(offset: int = 0) -> List[Pulse]: return rz_sequence(math.pi / 2, offset) def sdg_sequence(offset: int = 0) -> List[Pulse]: return rz_sequence(-math.pi / 2, offset) def t_sequence(offset: int = 0) -> List[Pulse]: return rz_sequence(math.pi / 4, offset) def tdg_sequence(offset: int = 0) -> List[Pulse]: return rz_sequence(-math.pi / 4, offset)
[docs] def x_sequence(offset: int = 0) -> List[Pulse]: """Logical X in 3 pulses.""" p01 = (offset, offset + 1) p12 = (offset + 1, offset + 2) return [(p12, math.pi + _THETA_C), (p01, _TWO_PI - _THETA_C), (p12, math.pi + _THETA_C)]
[docs] def h_sequence(offset: int = 0) -> List[Pulse]: """Logical Hadamard in 3 pulses.""" p01 = (offset, offset + 1) p12 = (offset + 1, offset + 2) t01 = (3.0 * math.pi + _THETA_C) / 2.0 return [(p01, t01), (p12, math.pi - _THETA_C), (p01, t01)]
[docs] def y_sequence(offset: int = 0) -> List[Pulse]: """Logical Y = X after Z (equal to iY, a global phase).""" return z_sequence(offset) + x_sequence(offset)
[docs] def rx_sequence(phase: float, offset: int = 0) -> List[Pulse]: """Logical RX(phase) = H RZ(phase) H.""" return h_sequence(offset) + rz_sequence(phase, offset) + h_sequence(offset)
[docs] def ry_sequence(phase: float, offset: int = 0) -> List[Pulse]: """Logical RY(phase) = S RX(phase) S^dagger (applied right-to-left).""" return (sdg_sequence(offset) + rx_sequence(phase, offset) + s_sequence(offset))
[docs] def cx_sequence(control_offset: int, target_offset: int) -> List[Pulse]: """Serial Fong-Wandzura CNOT: 28 exchange pulses on the linear chain t0-t1-t2-c2-c1-c0 (control spins c*, target spins t*), exact up to a global phase and independent of both qubits' gauge states.""" c0, c1, c2 = control_offset, control_offset + 1, control_offset + 2 t0, t1, t2 = target_offset, target_offset + 1, target_offset + 2 phi1 = math.acos(1.0 / math.sqrt(3.0)) phi2 = math.acos(2.0 * math.sqrt(2.0) / 3.0) phi3 = math.acos(-2.0 * math.sqrt(2.0) / 3.0) pi = math.pi return [ ((t1, t0), _TWO_PI - phi1), ((c1, c2), pi), ((t2, t1), phi2), ((c0, c1), pi), ((t1, t0), pi), ((c2, t2), pi), ((t2, t1), 1.5 * pi), ((c2, t2), 1.5 * pi), ((t1, t0), 0.5 * pi), ((t2, t1), 0.5 * pi), ((c1, c2), pi), ((t1, t0), pi), ((c2, t2), 0.5 * pi), ((t2, t1), 1.5 * pi), ((c1, c2), 0.5 * pi), ((c2, t2), 0.5 * pi), ((t1, t0), pi), ((c1, c2), pi), ((t2, t1), 0.5 * pi), ((t1, t0), 0.5 * pi), ((c2, t2), 1.5 * pi), ((t2, t1), 1.5 * pi), ((c2, t2), pi), ((t1, t0), pi), ((c0, c1), pi), ((t2, t1), phi3), ((c1, c2), pi), ((t1, t0), phi1), ]
[docs] def cz_sequence(control_offset: int, target_offset: int) -> List[Pulse]: """Encoded CZ = (I x H) CX (I x H) on the target logical qubit.""" return (h_sequence(target_offset) + cx_sequence(control_offset, target_offset) + h_sequence(target_offset))
[docs] def swap_sequence(offset_a: int, offset_b: int) -> List[Pulse]: """Encoded SWAP: swap the two triples spin-by-spin (3 full-SWAP pulses).""" return [((offset_a + k, offset_b + k), math.pi) for k in range(3)]
[docs] def sequence_to_circuit(sequence: Sequence[Pulse], n_physical_qubits: int) -> Circuit: """Build an exchange-pulse Circuit from a sequence of ((i, j), theta).""" c = Circuit(n_physical_qubits) for (i, j), theta in sequence: c.exch(theta)[i, j] return c