packages feed

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

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}

module Test.TableauSpec where

import Test.Hspec
import Test.QuickCheck
import SymplecticCHP
import Data.Bits (bit)
import Data.Maybe (fromJust)

-- Local Pauli constructors
pauliX, pauliZ, pauliY :: Int -> Pauli
pauliX i = Pauli (bit i) 0 0
pauliZ i = Pauli 0 (bit i) 0
pauliY i = Pauli (bit i) (bit i) 1

-- Orphan instance (move to Test.Arbitrary if preferred)
instance Arbitrary Pauli where
  arbitrary = do
    x <- chooseInt (0, 255) :: Gen Int
    z <- chooseInt (0, 255) :: Gen Int
    p <- chooseInt (0, 3)
    return $ Pauli (fromIntegral x) (fromIntegral z) p

newtype SmallN = SmallN Int
  deriving (Show, Eq)

instance Arbitrary SmallN where
  arbitrary = SmallN <$> chooseInt (1, 10)
  shrink (SmallN n) = SmallN <$> [n' | n' <- shrink n, n' > 0]

spec :: Spec
spec = describe "SymplecticCHP.Tableau" $ do
  describe "emptyTableauN" $ do
    it "creates valid tableau for |0...0>" $ do
      let tab = emptyTableauN 5
      isValidSome tab `shouldBe` True
    
    it "has correct number of rows" $ do
      property $ \(SmallN n) ->
        let tab = emptyTableauN n
        in length (rowsSome tab) === 2 * n
    
    it "has correct qubit count" $ do
      property $ \(SmallN n) ->
        nQubitsSome (emptyTableauN n) === n

  describe "Stabilizer properties" $ do
    it "stabilizers commute with each other" $ do
      property $ \(SmallN n) ->
        n > 0 ==>
          let tab = emptyTableauN n
              stab i = fromJust $ stabilizerSome tab i
              -- FIX: Use 'and' with list of Bools, not ==>
              allCommute = and [ i == j || commute (stab i) (stab j)
                               | i <- [0..n-1], j <- [0..n-1] ]
          in allCommute === True
    
    it "destabilizers have correct commutation with stabilizers" $ do
      property $ \(SmallN n) ->
        n > 0 ==>
          let tab = emptyTableauN n
              stab i = fromJust $ stabilizerSome tab i
              destab i = fromJust $ destabilizerSome tab i
              -- FIX: Use if-then-else inside list comprehension, not ==>
              correctPairing = and 
                [ if i == j then anticommute (destab i) (stab j)
                            else commute (destab i) (stab j)
                | i <- [0..n-1], j <- [0..n-1] ]
          in correctPairing === True

  describe "Initial state |0...0>" $ do
    it "has Z stabilizers on each qubit" $ do
      let n = 4
          tab = emptyTableauN n
          hasZStab i = xVec (fromJust $ stabilizerSome tab i) == 0 && zVec (fromJust $ stabilizerSome tab i) == bit i
      all hasZStab [0..n-1] `shouldBe` True
    
    it "has X destabilizers on each qubit" $ do
      let n = 4
          tab = emptyTableauN n
          hasXDestab i = xVec (fromJust $ destabilizerSome tab i) == bit i && zVec (fromJust $ destabilizerSome tab i) == 0
      all hasXDestab [0..n-1] `shouldBe` True
    
    it "all stabilizers have zero phase" $ do
      let n = 5
          tab = emptyTableauN n
      all (\i -> phase (fromJust $ stabilizerSome tab i) == 0) [0..n-1] `shouldBe` True

  describe "(//) operator for tableau rows" $ do
    it "updates single row correctly" $ do
      let tab = emptyTableauN 2
          newRow = Pauli 0 0 0
          rows' = rowsSome tab // [(0, newRow)]
      rows' !! 0 `shouldBe` newRow
      rows' !! 1 `shouldBe` (rowsSome tab !! 1)
      rows' !! 2 `shouldBe` (rowsSome tab !! 2)
      rows' !! 3 `shouldBe` (rowsSome tab !! 3)
    
    it "later update wins" $ do
      let tab = emptyTableauN 2
          rowA = Pauli 1 0 0
          rowB = Pauli 0 1 0
          rows' = rowsSome tab // [(0, rowA), (0, rowB)]
      rows' !! 0 `shouldBe` rowB

  describe "Tableau validity preservation" $ do
    it "remains valid after single-qubit gates" $ do
      property $ \(SmallN n) (q :: Int) ->
        let q' = q `mod` max 1 n  -- Ensure 0 <= q' < n
        in n > 0 ==>
            let tab0 = emptyTableauN n
                tabH = evolveTableauSome tab0 (Local (Hadamard q'))
                tabS = evolveTableauSome tabH (Local (Phase q'))
            in isValidSome tabH .&&. isValidSome tabS


    it "remains valid after CNOT" $ do
      property $ \(SmallN n) (cRaw :: Int) (tRaw :: Int) ->
        let n' = max 2 n
            c = cRaw `mod` n'
            t = tRaw `mod` n'
        in c /= t ==>
            let tab0 = emptyTableauN n'
                tab' = evolveTableauSome tab0 (CNOT c t)
            in isValidSome tab'

  -- describe "Generate function" $ do
  --   it "produces list of correct length" $ do
  --     property $ \(n :: Int) ->
  --       n >= 0 && n <= 100 ==>
  --         length (generate n id) === n
  --   
  --   it "applies function correctly" $ do
  --     generate 5 (*2) `shouldBe` [0,2,4,6,8]