I tried to simulate a quantum computer. Here is the datatype representing qubits:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
import Control.Monad
import Data.Maybe
import Data.Proxy
import Data.Type.Equality
import GHC.TypeNats
import Data.Group.Cyclic
data QBits :: Nat -> * where
N :: QBits 0
C :: KnownNat n => Bool -> QBits n -> QBits (n+1)
S :: KnownNat n => Cyclic 4 -> QBits n -> QBits n -> QBits (n+1)
N represents zero qubits.
C, standing for "classical", assigns the first qubit a boolean value, and specifies the rest.
S, standing for "superposed", states that the first qubit is in superposition, and specifies the rest for each possibility in which the first qubit will fall when measured. It also specifies the phase difference, which is a value in Cyclic 4, which is the ring Z/4Z and has Num instance.
For instance Eq (QBits n), I have a workaround so I won't mess with Nat:
(=?=) :: QBits m -> QBits n -> Bool
N =?= N = True
C b x =?= C c y = b == c && x =?= y
S p x y =?= S q u v = p == q && x =?= u && y =?= v
_ =?= _ = False
instance Eq (QBits n) where
(==) = (=?=)
Then I implemented swapGate, which swaps first two qubits:
castNat :: forall f m n. (KnownNat m, KnownNat n) => f m -> Maybe (f n)
castNat x = do
refl <- sameNat (Proxy :: Proxy m) (Proxy :: Proxy n)
return (castWith (apply Refl refl) x)
swapGate :: KnownNat n => QBits n -> QBits n
swapGate (C b (C c x)) = C c (C b x)
swapGate (C b (S p x y)) = S p (C b x) (C b y)
swapGate (S r (C False x) (C False y)) = let
Just y' = castNat y
in C False (S r x y')
swapGate (S r (C False x) (S q u v)) = let
Just u' = castNat u
in S (r+q) (S r x u') (C True v)
swapGate (S r (C True y) (C False u)) = S (-r) (C True u) (C False y)
swapGate (S r (C True y) (C True v)) = let
Just v' = castNat v
in C True (S r y v')
swapGate (S r (C True y) (S q u v)) = let
Just v' = castNat v
in S (-r) (C True u) (S (r+q) y v')
swapGate (S r (S p x y) (C False u)) = let
Just u' = castNat u
in S p (S r x u') (C False y)
swapGate (S r (S p x y) (C True v)) = let
Just v' = castNat v
in S p (C False x) (S (p-r) y v')
swapGate (S r (S p x y) (S q u v)) = let
Just u' = castNat u
Just v' = castNat v
in S p (S r x u') (S (q-p+r) y v')
swapGate z = z
The fact I must cast Nats is just too annoying. Is castNat truly mandatory?