----------------------------------------------------------------------------- -- -- Module : $Header$ -- Copyright : (c) 2017-18 Brian W Bush -- License : MIT -- -- Maintainer : Brian W Bush -- Stability : Stable -- Portability : Portable -- -- | Testing qbits and operations on them. -- -- The test data is derived from pyQuil \<\ and recorded in \<\>. ----------------------------------------------------------------------------- {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} module Main ( main ) where import Control.Monad (replicateM) import Data.Complex (Complex(..), cis, magnitude) import Data.Int.Util (ilog2) import Data.Qubit (QState(..), (^*), groundState, pureQubit, pureState, qubit, qubits, rawWavefunction, wavefunctionAmplitudes, wavefunctionIndices, wavefunctionOrder) import Numeric.LinearAlgebra.Array ((.*)) import Numeric.LinearAlgebra.Array.Util (coords, scalar) import Test.QuickCheck.All (quickCheckAll) import System.Exit (exitFailure, exitSuccess) import qualified Data.Qubit.Gate as G import qualified Data.Vector.Storable as V (toList) -- Helper functions. i = scalar $ 0 :+ 1 cos' = scalar . (:+ 0) . cos sin' = scalar . (:+ 0) . sin cis' = scalar . cis applyGate = (rawWavefunction .) . (. (qubits . V.toList . coords)) . (^*) epsilon = 1e-5 xs ~~ ys = V.toList (coords xs) ~~~ V.toList (coords ys) infix 4 ~~ (~~~) = (and .) . zipWith (\x y -> magnitude (x - y) <= epsilon) -- Wavefunction tests. prop_qubit n a0 a1 = let q = qubit n (a0, a1) in wavefunctionOrder q == 1 && wavefunctionIndices q == [n] && wavefunctionAmplitudes q == [([QState0], a0), ([QState1], a1)] prop_pure_qubit n b = let z = if b then QState1 else QState0 q = pureQubit n z in wavefunctionOrder q == 1 && wavefunctionIndices q == [n] && fmap fst (wavefunctionAmplitudes q) == [[QState0], [QState1]] && fmap snd (wavefunctionAmplitudes q) ~~~ (if b then [0, 1] else [1, 0]) prop_qubits as = let n = ilog2 $ length as as' = take (2^n) as q = qubits as' in length as < 2 || n > 15 || wavefunctionOrder q == n && wavefunctionIndices q == reverse [0..(n-1)] && fmap fst (wavefunctionAmplitudes q) == replicateM n [minBound..maxBound] && fmap snd (wavefunctionAmplitudes q) ~~~ as' prop_ground n = let q = groundState n in n < 2 || n > 15 || wavefunctionOrder q == n && wavefunctionIndices q == [0..(n-1)] && fmap fst (wavefunctionAmplitudes q) == replicateM n [minBound..maxBound] && fmap snd (wavefunctionAmplitudes q) ~~~ (1 : replicate (2^n - 1) 0) prop_pure_state bs = let n = length bs z = [if b then QState1 else QState0 | b <- bs] q = pureState z in n < 2 || n > 15 || wavefunctionOrder q == n && wavefunctionIndices q == [0..(n-1)] && fmap fst (wavefunctionAmplitudes q) == replicateM n [minBound..maxBound] && fmap snd (wavefunctionAmplitudes q) ~~~ [if z' == reverse z then 1 else 0 | z' <- replicateM n [minBound..maxBound]] -- Tests for one-qubit gates. b0 = rawWavefunction $ pureState [QState0] b1 = rawWavefunction $ pureState [QState1] prop_gate_i = let g = applyGate $ G.i 0 in g b0 ~~ b0 && g b1 ~~ b1 prop_gate_x = let g = applyGate $ G.x 0 in g b0 ~~ b1 && g b1 ~~ b0 prop_gate_y = let g = applyGate $ G.y 0 in g b0 ~~ i .* b1 && g b1 ~~ - i .* b0 prop_gate_z = let g = applyGate $ G.z 0 in g b0 ~~ b0 && g b1 ~~ - b1 prop_gate_h = let g = applyGate $ G.h 0 in g b0 ~~ (b0 + b1) / sqrt 2 && g b1 ~~ (b0 - b1) / sqrt 2 prop_gate_phase theta = let g = applyGate $ G.phase theta 0 in g b0 ~~ b0 && g b1 ~~ cis' theta .* b1 prop_gate_s = let g = applyGate $ G.s 0 in g b0 ~~ b0 && g b1 ~~ i .* b1 prop_gate_t = let g = applyGate $ G.t 0 in g b0 ~~ b0 && g b1 ~~ scalar (1 :+ 1) .* b1 / sqrt 2 prop_gate_rx theta = let g = applyGate $ G.rx theta 0 theta' = theta / 2 in g b0 ~~ cos' theta' .* b0 - sin' theta' .* i .* b1 && g b1 ~~ - sin' theta' .* i .* b0 + cos' theta' .* b1 prop_gate_ry theta = let g = applyGate $ G.ry theta 0 theta' = theta / 2 in g b0 ~~ cos' theta' .* b0 + sin' theta' .* b1 && g b1 ~~ - sin' theta' .* b0 + cos' theta' .* b1 prop_gate_rz theta = let g = applyGate $ G.rz theta 0 theta' = theta / 2 in g b0 ~~ cis' (- theta') .* b0 && g b1 ~~ cis' theta' .* b1 -- Tests for two-cubit gates. b00 = rawWavefunction $ pureState [QState0, QState0] b01 = rawWavefunction $ pureState [QState0, QState1] b10 = rawWavefunction $ pureState [QState1, QState0] b11 = rawWavefunction $ pureState [QState1, QState1] prop_gate_cphase00 theta = let g = applyGate $ G.cphase00 theta 1 0 in g b00 ~~ cis' theta .* b00 && g b01 ~~ b01 && g b10 ~~ b10 && g b11 ~~ b11 prop_gate_cphase01 theta = let g = applyGate $ G.cphase01 theta 1 0 in g b00 ~~ b00 && g b01 ~~ cis' theta .* b01 && g b10 ~~ b10 && g b11 ~~ b11 prop_gate_cphase10 theta = let g = applyGate $ G.cphase10 theta 1 0 in g b00 ~~ b00 && g b01 ~~ b01 && g b10 ~~ cis' theta .* b10 && g b11 ~~ b11 prop_gate_cphase theta = let g = applyGate $ G.cphase theta 1 0 in g b00 ~~ b00 && g b01 ~~ b01 && g b10 ~~ b10 && g b11 ~~ cis' theta .* b11 prop_gate_cnot = let g = applyGate $ G.cnot 1 0 in g b00 ~~ b00 && g b01 ~~ b01 && g b10 ~~ b11 && g b11 ~~ b10 prop_gate_pswap theta = let g = applyGate $ G.pswap theta 1 0 in g b00 ~~ b00 && g b01 ~~ cis' theta .* b10 && g b10 ~~ cis' theta .* b01 && g b11 ~~ b11 prop_gate_swap = let g = applyGate $ G.swap 1 0 in g b00 ~~ b00 && g b01 ~~ b10 && g b10 ~~ b01 && g b11 ~~ b11 prop_gate_iswap = let g = applyGate $ G.iswap 1 0 in g b00 ~~ b00 && g b01 ~~ i .* b10 && g b10 ~~ i .* b01 && g b11 ~~ b11 prop_gate_cz = let g = applyGate $ G.cz 1 0 in g b00 ~~ b00 && g b01 ~~ b01 && g b10 ~~ b10 && g b11 ~~ - b11 -- Tests for three-cubit gates. b000 = rawWavefunction $ pureState [QState0, QState0, QState0] b001 = rawWavefunction $ pureState [QState0, QState0, QState1] b010 = rawWavefunction $ pureState [QState0, QState1, QState0] b011 = rawWavefunction $ pureState [QState0, QState1, QState1] b100 = rawWavefunction $ pureState [QState1, QState0, QState0] b101 = rawWavefunction $ pureState [QState1, QState0, QState1] b110 = rawWavefunction $ pureState [QState1, QState1, QState0] b111 = rawWavefunction $ pureState [QState1, QState1, QState1] prop_gate_ccnot = let g = applyGate $ G.ccnot 2 1 0 in g b000 ~~ b000 && g b001 ~~ b001 && g b010 ~~ b010 && g b011 ~~ b011 && g b100 ~~ b100 && g b101 ~~ b101 && g b110 ~~ b111 && g b111 ~~ b110 prop_gate_cswap = let g = applyGate $ G.cswap 2 1 0 in g b000 ~~ b000 && g b001 ~~ b001 && g b010 ~~ b010 && g b011 ~~ b011 && g b100 ~~ b100 && g b101 ~~ b110 && g b110 ~~ b101 && g b111 ~~ b111 -- Run tests. return [] main = do success <- $quickCheckAll if success then exitSuccess else exitFailure