symplectic-chp-0.1.0.1: src/SymplecticCHP/LargeTableau.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
-- | Large tableau support using BitVec for arbitrary qubit counts.
-- This module provides a complement to the standard Word64-based tableau.
module SymplecticCHP.LargeTableau
( -- * Large Pauli operators
LargePauli(..)
, lpPauliX
, lpPauliZ
, lpPauliY
, lpOmega
, lpMultiply
-- * Gates (redefined to avoid circular imports)
, LargeLocalSymplectic(..)
, LargeSymplecticGate(..)
, LargeMeasurementResult(..)
-- * Large Tableau
, LargeTableau(..)
, largeEmpty
, largeNQubits
, largeApplyGate
, largeMeasure
-- * Validation
, largeIsValid
, largeIsDeterminate
) where
import Data.Bits (Bits(..), popCount, xor)
import Data.Word (Word64)
import SymplecticCHP.BitVec
import System.Random (randomRIO)
import qualified Data.Vector as V
-- ============================================================================
-- Gate Types (local copies to avoid circular imports)
-- ============================================================================
data LargeLocalSymplectic
= LargeHadamard !Int
| LargePhase !Int
deriving (Show, Eq)
data LargeSymplecticGate
= LargeLocal !LargeLocalSymplectic
| LargeCNOT !Int !Int
deriving (Show, Eq)
data LargeMeasurementResult = LargeDeterminate Bool | LargeRandom Bool
deriving (Show, Eq)
-- ============================================================================
-- Large Pauli (using BitVec)
-- ============================================================================
-- | Pauli operator for arbitrary qubit count using BitVec
data LargePauli = LargePauli
{ lpX :: !BitVec
, lpZ :: !BitVec
, lpPhase :: !Int
, lpNQubits :: !Int
}
deriving (Eq, Show)
-- | Create Pauli X on qubit i
lpPauliX :: Int -> Int -> LargePauli
lpPauliX n i = LargePauli (bvSetBit (bvEmpty n) i) (bvEmpty n) 0 n
-- | Create Pauli Z on qubit i
lpPauliZ :: Int -> Int -> LargePauli
lpPauliZ n i = LargePauli (bvEmpty n) (bvSetBit (bvEmpty n) i) 0 n
-- | Create Pauli Y on qubit i
lpPauliY :: Int -> Int -> LargePauli
lpPauliY n i = LargePauli (bvSetBit (bvEmpty n) i) (bvSetBit (bvEmpty n) i) 1 n
-- | Symplectic inner product
lpOmega :: LargePauli -> LargePauli -> Bool
lpOmega (LargePauli x1 z1 _ n1) (LargePauli x2 z2 _ n2)
| n1 /= n2 = error "lpOmega: qubit count mismatch"
| otherwise = odd (bvPopCount (bvAnd x1 z2) + bvPopCount (bvAnd z1 x2))
-- | Pauli multiplication
lpMultiply :: LargePauli -> LargePauli -> LargePauli
lpMultiply (LargePauli x1 z1 r1 n1) (LargePauli x2 z2 r2 n2)
| n1 /= n2 = error "lpMultiply: qubit count mismatch"
| otherwise =
let x = bvXor x1 x2
z = bvXor z1 z2
symPhase = bvPopCount (bvAnd x1 z2) - bvPopCount (bvAnd z1 x2)
r = (r1 + r2 + symPhase) `mod` 4
in LargePauli x z r n1
-- ============================================================================
-- Large Tableau
-- ============================================================================
-- | Tableau for arbitrary qubit count
data LargeTableau = LargeTableau
{ ltStabs :: !(V.Vector LargePauli)
, ltDestabs :: !(V.Vector LargePauli)
, ltN :: !Int
}
deriving (Show)
-- | Create empty tableau (|0...0⟩ state)
largeEmpty :: Int -> LargeTableau
largeEmpty n
| n <= 0 = error "largeEmpty: n must be positive"
| otherwise = LargeTableau stabs destabs n
where
stabs = V.fromList [lpPauliZ n i | i <- [0..n-1]]
destabs = V.fromList [lpPauliX n i | i <- [0..n-1]]
-- | Get qubit count
largeNQubits :: LargeTableau -> Int
largeNQubits = ltN
-- | Apply gate to large tableau
largeApplyGate :: LargeSymplecticGate -> LargeTableau -> LargeTableau
largeApplyGate g (LargeTableau s d n) =
LargeTableau (V.map (lpApplyGate g) s) (V.map (lpApplyGate g) d) n
-- | Apply gate to large Pauli
lpApplyGate :: LargeSymplecticGate -> LargePauli -> LargePauli
lpApplyGate (LargeLocal (LargeHadamard i)) (LargePauli x z r n) =
let xi = bvTestBit x i
zi = bvTestBit z i
x' = if zi then bvSetBit (bvClearBit x i) i else bvClearBit x i
z' = if xi then bvSetBit (bvClearBit z i) i else bvClearBit z i
r' = (r + if xi && zi then 2 else 0) `mod` 4
in LargePauli x' z' r' n
lpApplyGate (LargeLocal (LargePhase i)) (LargePauli x z r n) =
let xi = bvTestBit x i
zi = bvTestBit z i
-- Z' = Z XOR X
z' = if xi then bvXor z (bvSetBit (bvEmpty n) i) else z
r' = (r + if xi && not zi then 1 else 0) `mod` 4
in LargePauli x z' r' n
lpApplyGate (LargeCNOT c t) (LargePauli x z r n) =
let xc = bvTestBit x c
zc = bvTestBit z c
xt = bvTestBit x t
zt = bvTestBit z t
-- X'[t] = X[t] XOR X[c]
x' = if xc then bvXor x (bvSetBit (bvEmpty n) t) else x
-- Z'[c] = Z[c] XOR Z[t]
z' = if zt then bvXor z (bvSetBit (bvEmpty n) c) else z
phaseTerm = if xc && zt then (if xt /= zc then 2 else 0) + 1 else 0
r' = (r + phaseTerm) `mod` 4
in LargePauli x' z' r' n
-- | Measurement on large tableau
largeMeasure :: LargeTableau -> LargePauli -> IO (LargeTableau, LargeMeasurementResult)
largeMeasure tab@(LargeTableau s d n) p
| largeIsDeterminate tab p = do
let outcome = largeComputePhase tab p
return (tab, LargeDeterminate outcome)
| otherwise = do
case largeFindAntiCommuting tab p of
Nothing -> error "Internal error in largeMeasure"
Just j -> do
let s_j = s V.! j
-- Update other stabilizers
newStabs = V.imap (\k s_k ->
if k == j
then p
else if lpOmega p s_k
then lpMultiply s_k s_j
else s_k) s
-- New destabilizer is old stabilizer
newDestabs = d V.// [(j, s_j)]
outcome <- randomRIO (0, 1) :: IO Int
let p' = p { lpPhase = (lpPhase p + if outcome == 0 then 2 else 0) `mod` 4 }
finalStabs = newStabs V.// [(j, p')]
return (LargeTableau finalStabs newDestabs n, LargeRandom (outcome == 1))
-- | Check if measurement is deterministic
largeIsDeterminate :: LargeTableau -> LargePauli -> Bool
largeIsDeterminate (LargeTableau s _ _) p =
V.all (\s_i -> not (lpOmega p s_i)) s
-- | Find anticommuting stabilizer
largeFindAntiCommuting :: LargeTableau -> LargePauli -> Maybe Int
largeFindAntiCommuting (LargeTableau s _ _) p =
V.ifoldl' (\acc i s_i ->
case acc of
Just _ -> acc
Nothing -> if lpOmega p s_i then Just i else Nothing) Nothing s
-- | Compute deterministic measurement outcome
largeComputePhase :: LargeTableau -> LargePauli -> Bool
largeComputePhase (LargeTableau s d _) p =
let scratch = V.ifoldl' (\acc j d_j ->
if lpOmega p d_j
then lpMultiply acc (s V.! j)
else acc) (LargePauli (bvEmpty (lpNQubits p)) (bvEmpty (lpNQubits p)) 0 (lpNQubits p)) d
totalPhase = (lpPhase p - lpPhase scratch) `mod` 4
in totalPhase == 0
-- | Check tableau validity (duality condition)
largeIsValid :: LargeTableau -> Bool
largeIsValid (LargeTableau s d n) =
-- Check ω(Dᵢ, Sⱼ) = δᵢⱼ
V.all (\i ->
V.all (\j ->
let di = d V.! i
sj = s V.! j
omega = if lpOmega di sj then 1 else 0
in if i == j then omega == 1 else omega == 0
) (V.fromList [0..n-1])
) (V.fromList [0..n-1])