packages feed

symplectic-chp-0.1.0.0: test/Test/GatesSpec.hs

module Test.GatesSpec where

import Test.Hspec
import Test.QuickCheck
import SymplecticCHP
import Data.Bits (bit, testBit, popCount, xor)
import Test.Arbitrary

spec :: Spec
spec = describe "SymplecticCHP.Gates" $ do
  describe "Hadamard gate" $ do
    it "converts X to Z" $ do
      let x0 = pauliX 0
          x0' = applyGate (Local (Hadamard 0)) x0
      xVec x0' `shouldBe` 0
      zVec x0' `shouldBe` 1
      xVec x0 `shouldBe` 1  -- original unchanged
    
    it "converts Z to X" $ do
      let z0 = pauliZ 0
          z0' = applyGate (Local (Hadamard 0)) z0
      xVec z0' `shouldBe` 1
      zVec z0' `shouldBe` 0
    
    it "converts Y to -Y (phase flip)" $ do
      let y0 = pauliY 0
          y0' = applyGate (Local (Hadamard 0)) y0
      xVec y0' `shouldBe` 1
      zVec y0' `shouldBe` 1
      phase y0' `shouldBe` (phase y0 + 2) `mod` 4
    
    it "is self-inverse up to phase" $ do
      property $ \(p :: Pauli) ->
        let p' = applyGate (Local (Hadamard 0)) p
            p'' = applyGate (Local (Hadamard 0)) p'
        in xVec p'' === xVec p .&&. zVec p'' === zVec p
    
    it "preserves commutation relations" $ do
      property $ \(p1 :: Pauli) (p2 :: Pauli) ->
        let g = Local (Hadamard 0)
            p1' = applyGate g p1
            p2' = applyGate g p2
        in symplecticForm p1 p2 === symplecticForm p1' p2'

  describe "Phase gate" $ do
    it "leaves Z unchanged" $ do
      let z0 = pauliZ 0
          z0' = applyGate (Local (Phase 0)) z0
      xVec z0' `shouldBe` 0
      zVec z0' `shouldBe` 1
    
    it "converts X to Y" $ do
      let x0 = pauliX 0
          x0' = applyGate (Local (Phase 0)) x0
      xVec x0' `shouldBe` 1
      zVec x0' `shouldBe` 1  -- X*Z = Y
    
    it "has correct phase for X->Y" $ do
      let x0 = pauliX 0
          yExpected = pauliY 0
          x0' = applyGate (Local (Phase 0)) x0
      xVec x0' `shouldBe` xVec yExpected
      zVec x0' `shouldBe` zVec yExpected

  describe "CNOT gate" $ do
    it "X_control -> X_control * X_target" $ do
      let xc = pauliX 0  -- control
          xc' = applyGate (CNOT 0 1) xc
      -- X on control becomes X⊗X
      testBit (xVec xc') 0 `shouldBe` True   -- control still X
      testBit (xVec xc') 1 `shouldBe` True   -- target now X too
      zVec xc' `shouldBe` 0
    
    it "Z_target -> Z_control * Z_target" $ do
      let zt = pauliZ 1  -- target
          zt' = applyGate (CNOT 0 1) zt
      -- Z on target becomes Z⊗Z
      testBit (zVec zt') 0 `shouldBe` True   -- control now Z
      testBit (zVec zt') 1 `shouldBe` True   -- target still Z
      xVec zt' `shouldBe` 0
    
    it "leaves X_target unchanged" $ do
      let xt = pauliX 1
          xt' = applyGate (CNOT 0 1) xt
      xVec xt' `shouldBe` bit 1  -- only target bit set
      zVec xt' `shouldBe` 0
    
    it "leaves Z_control unchanged" $ do
      let zc = pauliZ 0
          zc' = applyGate (CNOT 0 1) zc
      zVec zc' `shouldBe` bit 0  -- only control bit set
      xVec zc' `shouldBe` 0
    
    it "is self-inverse" $ do
      property $ \(p :: Pauli) ->
        let p' = applyGate (CNOT 0 1) p
            p'' = applyGate (CNOT 0 1) p'
        in xVec p'' === xVec p .&&. zVec p'' === zVec p
    
    it "preserves commutation relations" $ do
      property $ \(p1 :: Pauli) (p2 :: Pauli) ->
        let g = CNOT 0 1
            p1' = applyGate g p1
            p2' = applyGate g p2
        in symplecticForm p1 p2 === symplecticForm p1' p2'

  describe "Gate composition" $ do
    it "H then CNOT creates Bell stabilizers" $ do
      let tab0 = emptyTableauN 2
          tab1 = evolveTableauSome tab0 (Local (Hadamard 0))
          tab2 = evolveTableauSome tab1 (CNOT 0 1)
          rowsTab = rowsSome tab2
          s0 = rowsTab !! 0  -- First stabilizer
          s1 = rowsTab !! 1  -- Second stabilizer
      -- After H⊗CNOT, stabilizers should be XX and ZZ
      symplecticForm s0 s1 `shouldBe` True  -- They commute

  describe "Tableau evolution" $ do
    it "preserves tableau validity" $ do
      property $ \(gates :: [SymplecticGate]) ->
        let tab0 = emptyTableauN 3
            tab' = foldl evolveTableauSome tab0 (take 10 gates)
        in isValidSome tab'