haquil (empty) → 0.1.7.5
raw patch · 9 files changed
+1356/−0 lines, 9 filesdep +MonadRandomdep +QuickCheckdep +basesetup-changed
Dependencies added: MonadRandom, QuickCheck, base, hTensor, template-haskell, vector
Files
- ChangeLog.md +30/−0
- LICENSE +20/−0
- ReadMe.md +38/−0
- Setup.hs +2/−0
- haquil.cabal +55/−0
- src/Data/Int/Util.hs +85/−0
- src/Data/Qubit.hs +449/−0
- src/Data/Qubit/Gate.hs +340/−0
- src/Test.hs +337/−0
+ ChangeLog.md view
@@ -0,0 +1,30 @@+# Revision history for quil++## 0.1.7.5 -- 2017-12-19++* added example usage++## 0.1.7.3 -- 2017-12-18++* added measurement++## 0.1.5.0 -- 2017-12-17++* tested two- and three-qubit gates++## 0.1.4.0 -- 2017-12-16++* renamed product operators+* tested one-qubit gates++## 0.1.3.0 -- 2017-12-16++* added Quil gates++## 0.1.2.0 -- 2017-12-16++* added qubit types and operations++## 0.1.0.0 -- 2017-12-15++* skeleton
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2017 Brian W Bush++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ReadMe.md view
@@ -0,0 +1,38 @@+quil: An instruction set for quantum computing+==============================================++This Haskell library implements the Quil language for quantum computing, as specified in "A Practical Quantum Instruction Set Architecture" <<https://arxiv.org/abs/1608.03355/>>, using the indexing conventions in <<https://arxiv.org/abs/1711.02086/>>.++Please report issues at <<https://bwbush.atlassian.net/projects/HQUIL/issues/>>.+++Example+-------++This example creates a wavefunction for the [Bell state](https://en.wikipedia.org/wiki/Bell_state) and then performs a measurement on its highest qubit.++ > import Control.Monad.Random (evalRandIO)+ > import Data.Qubit ((^^*), groundState)+ > import Data.Qubit.Gate (H, CNOT)+ + > -- Construct the Bell state.+ > let bell = [h 0, cnot 0 1] ^^* groundState 2+ > bell+ 0.7071067811865475|00> + 0.7071067811865475|11> @ [1,0]+ + > -- Measure the Bell wavefunction.+ > bell' <- evalRandIO $ measure [1] bell+ > bell'+ ([(1,0)],1.0|00> @ [1,0])+ + > -- Measure it again.+ > evalRandIO $ measure [1] bell'+ ([(1,0)],1.0|00> @ [1,0])+ + > -- Measure another Bell wavefunction.+ > evalRandIO $ measure [1] bell+ ([(1,0)],1.0|00> @ [1,0])+ + > -- Measure another Bell wavefunction.+ > evalRandIO $ measure [1] bell+ ([(1,1)],1.0|11> @ [1,0])
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ haquil.cabal view
@@ -0,0 +1,55 @@+name : haquil+version : 0.1.7.5+synopsis : A Haskell implementation of the Quil instruction set for quantum computing.+description : This Haskell library implements the Quil language for quantum computing, as specified in "A Practical Quantum Instruction Set Architecture" \<https:\/\/arxiv.org\/abs\/1608.03355\/\>.++license : MIT+license-file : LICENSE+copyright : (c) 2017-18 Brian W Bush++author : Brian W Bush <code@functionally.io>+maintainer : Brian W Bush <code@functionally.io>++homepage : https://bitbucket.org/functionally/haquil+bug-reports : https://bwbush.atlassian.net/projects/HQUIL/issues/+package-url : https://bitbucket.org/functionally/quil/downloads/haquil-0.1.7.5.tar.gz++category : Language+stability : Unstable++cabal-version : >= 1.10+build-type : Simple+extra-source-files : ReadMe.md+ ChangeLog.md++source-repository head+ type : git+ location : https://bitbucket.org/functionally/haquil++library+ exposed-modules : Data.Qubit+ Data.Qubit.Gate+ other-modules : Data.Int.Util+ hs-source-dirs : src+ build-depends : base >= 4.9 && < 4.10+ , hTensor+ , MonadRandom+ , vector+ exposed : True+ buildable : True+ default-language : Haskell2010+ ghc-options : -Wall++test-suite haquil-test+ main-is : Test.hs+ hs-source-dirs : src+ build-depends : base+ , hTensor+ , MonadRandom+ , QuickCheck+ , template-haskell+ , vector+ type : exitcode-stdio-1.0+ buildable : True+ default-language : Haskell2010+ ghc-options : -Wall
+ src/Data/Int/Util.hs view
@@ -0,0 +1,85 @@+-----------------------------------------------------------------------------+--+-- Module : $Header$+-- Copyright : (c) 2017 Brian W Bush+-- License : MIT+--+-- Maintainer : Brian W Bush <code@functionally.io>+-- Stability : Stable+-- Portability : Portable+--+-- | Integer functions.+--+-----------------------------------------------------------------------------+++{-# LANGUAGE Safe #-}+++module Data.Int.Util (+-- * Functions+ ilog2+, isqrt+, ilog2sqrt+) where+++import Control.Arrow ((&&&))+++-- | Square root of an integer.+--+-- This is equivalent to:+--+-- @+-- isqrt x == floor (sqrt $ from Integral x)+-- @+isqrt :: (Enum a, Num a, Ord a)+ => a -- ^ The integer.+ -> a -- ^ The integral square root.+isqrt n+ | n < fst (head table) = error "isqrt: illegal argument."+ | otherwise = snd . head $ dropWhile ((< n) . fst) table+ where+ table = tabulate (^ (2 :: Int))+++-- | Base-two logarithm of an integer.+--+-- This is equivalent to:+--+-- @+-- ilog2 x == floor (logBase 2 $ fromIntegral x)+-- @+ilog2 :: (Enum a, Integral a, Ord a)+ => a -- ^ The integer.+ -> a -- ^ The integral base-two logarithm.+ilog2 n+ | n < fst (head table) = error "ilog2: illegal argument."+ | otherwise = snd . head $ dropWhile ((< n) . fst) table+ where+ table = tabulate (2^)+++-- | Base-two logarithm of the square root of an integer.+--+-- This is equivalent to:+--+-- @+-- ilog2sqrt x == floor (logBase 2 . sqrt $ fromIntegral x)+-- @+ilog2sqrt :: (Enum a, Integral a, Num a, Ord a)+ => a -- ^ The integer.+ -> a -- ^ The integral base-two logarithm of the square root.+ilog2sqrt n+ | n < fst (head table) = error "ilog2sqrt: illegal argument."+ | otherwise = snd . head $ dropWhile ((< n) . fst) table+ where+ table = tabulate (^ (2 :: Int))+++-- | Tabulate a function for later use.+tabulate :: (Enum a, Num a)+ => (a -> a) -- ^ The function.+ -> [(a, a)] -- ^ Its tabulation.+tabulate f = (f &&& id) <$> [0..]
+ src/Data/Qubit.hs view
@@ -0,0 +1,449 @@+-----------------------------------------------------------------------------+--+-- Module : $Header$+-- Copyright : (c) 2017 Brian W Bush+-- License : MIT+--+-- Maintainer : Brian W Bush <code@functionally.io>+-- Stability : Stable+-- Portability : Portable+--+-- | Qubits and operations on them, using the indexing conventions of \<<https://arxiv.org/abs/1711.02086/>\>.+--+-----------------------------------------------------------------------------+++{-# LANGUAGE GeneralizedNewtypeDeriving #-}+++module Data.Qubit (+-- * Types+ QIndex+, QState(..)+, Amplitude+, Wavefunction+, Operator+-- * Construction+, qubit+, pureQubit+, qubits+, groundState+, pureState+, qubitsOperator+-- * Properties+, wavefunctionOrder+, wavefunctionIndices+, wavefunctionAmplitudes+, rawWavefunction+, operatorOrder+, operatorIndices+, operatorAmplitudes+, rawOperator+-- * Operations+, (^*^)+, (^*)+, (*^)+, (^^*)+, (*^^)+, probabilities+, project+, measure+, wavefunctionProbability+) where+++import Control.Arrow (first, second)+import Control.Monad (replicateM)+import Control.Monad.Random.Class (fromList)+import Control.Monad.Random.Lazy (Rand, RandomGen)+import Data.Complex (Complex(..), magnitude)+import Data.Function (on)+import Data.Int.Util (ilog2, isqrt)+import Data.List (elemIndex, groupBy, intersect, intercalate, sortBy)+import Data.Maybe (catMaybes)+import Numeric.LinearAlgebra.Array ((.*))+import Numeric.LinearAlgebra.Array.Util (Idx(iName), asScalar, coords, dims, order, outers, renameExplicit, reorder)+import Numeric.LinearAlgebra.Tensor (Tensor, Variant(..), listTensor, switch)++import qualified Data.Vector.Storable as V (toList)+++-- | States of a quibit.+data QState =+ QState0+ | QState1+ deriving (Bounded, Enum, Eq, Ord)++instance Read QState where+ readsPrec = (fmap (first toEnum) .) . readsPrec++instance Show QState where+ show = show . fromEnum+++-- | Index for qubits in a wavefunction.+type QIndex = Int+++-- | Dimension names in order of storage.+names :: Tensor a -> [String]+names = fmap iName . dims+++-- | Prefixes for 'Tensor' indices.+indexPrefix :: Variant -> Char+indexPrefix Contra = 'm'+indexPrefix Co = 'n'+++-- | 'Tensor' index for a collection of qubits.+indexLabels :: Variant -- ^ Whether contravariant or covariant.+ -> [QIndex] -- ^ Indices of the qubits.+ -> [(String, String)] -- ^ Pairs of default `Tensor` indices and qubit indices.+indexLabels variant indices =+ let+ n = length indices+ o =+ case variant of+ Contra -> 0+ Co -> n+ in+ zipWith (\k v -> (show k, indexPrefix variant : show v)) [(1+o)..] $ reverse indices+++-- | 'Tensor' index for a wavefunction.+wavefunctionLabels :: Int -- ^ The number of qubits.+ -> [(String, String)] -- ^ Pairs of default 'Tensor' indices and qubit indices.+wavefunctionLabels n = indexLabels Contra [0..(n-1)]+++-- | 'Tensor' index for an operator on qubits.+operatorLabels :: [QIndex] -- ^ Indices of the qubits.+ -> [(String, String)] -- ^ Pairs of default `Tensor` indices and qubit indices.+operatorLabels indices = indexLabels Contra indices ++ indexLabels Co indices+++-- | Amplitude of a state in a wavefunction.+type Amplitude = Complex Double+++-- | Compactly show an amplitude.+showAmplitude :: Amplitude -> String+showAmplitude (a :+ b)+ | b == 0 = show a+ | a == 0 = show b ++ "i"+ | otherwise = "(" ++ show a ++ "+" ++ show b ++ "i)"+++-- | Show a state and its amplitude.+type ShowState = Int -- ^ Number of qubits in the state.+ -> ([QState], Amplitude) -- ^ The state and its amplitude.+ -> String -- ^ The string representation.+++-- | Help to compactly show a tensor.+showTensor :: (a -> Tensor Amplitude) -- ^ Function for extracting the tensor of amplitudes.+ -> ShowState -- ^ Function for showing a state and its amplitude.+ -> a -- ^ What to show.+ -> String -- ^ The string respresentation.+showTensor toTensor format x =+ let+ x' = canonicalOrder $ toTensor x+ n = order x'+ in+ (++ (show $ tensorIndices x'))+ . (++ " @ ")+ . intercalate " + "+ . fmap (format n)+ . filter ((/= 0) . snd)+ $ tensorAmplitudes x'+++-- | Indices in a tensor.+tensorIndices :: Tensor Amplitude -- ^ The tensor.+ -> [QIndex] -- ^ The qubit indices.+tensorIndices =+ fmap (read . tail)+ . filter ((== indexPrefix Contra) . head)+ . names+++-- | Transpose a tensor to canonical order.+canonicalOrder :: Tensor Amplitude -> Tensor Amplitude+canonicalOrder x =+ let+ f (v : i) (v' : i') = (v, - read i :: Int) `compare` (v', - read i')+ f _ _ = undefined+ in+ reorder (sortBy f $ names x) x+++-- | Amplitudes in a tensor.+tensorAmplitudes :: Tensor Amplitude -- ^ The tensor.+ -> [([QState], Amplitude)] -- ^ List of qubit states and their amplitudes, where indices of states are ordered according to 'tensorIndices'.+tensorAmplitudes x =+ let+ n = order x+ in+ zip (replicateM n [minBound..maxBound])+ . V.toList+ $ coords x+++-- | A wavefunction for qubits.+newtype Wavefunction = Wavefunction {rawWavefunction' :: Tensor Amplitude}+ deriving (Eq, Floating, Fractional, Num)++instance Show Wavefunction where+ show =+ showTensor rawWavefunction'+ $ \_ (k, v) ->+ showAmplitude v ++ "|" ++ concatMap show k ++ ">"+++-- | The 'Tensor' encoding the wavefunction.+rawWavefunction :: Wavefunction -> Tensor Amplitude+rawWavefunction = canonicalOrder . rawWavefunction'+++-- | Number of qubits in a wavefunction.+wavefunctionOrder :: Wavefunction -> Int+wavefunctionOrder = order . rawWavefunction'+++-- | Qubit indices in a wavefunction.+wavefunctionIndices :: Wavefunction -- ^ The wavefunction.+ -> [QIndex] -- ^ List of qubit indices.+wavefunctionIndices = tensorIndices . rawWavefunction'+++-- | Amplitudes of states in a qubit wavefunction.+wavefunctionAmplitudes :: Wavefunction -- ^ The wavefunction.+ -> [([QState], Amplitude)] -- ^ List of qubit states and their amplitudes, where indices of states are ordered according to 'wavefunctionIndices'.+wavefunctionAmplitudes = tensorAmplitudes . rawWavefunction'+++-- | Construct a qubit from the amplitudes of its states.+--+-- The squares of the norms of the amplitudes must sum to one.+qubit :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> (Amplitude, Amplitude) -- ^ The amplitude of the 0 and 1 states, respectively.+ -> Wavefunction -- ^ The wavefunction for the qubit.+qubit index (a0, a1) =+ Wavefunction+ . renameExplicit [("1", indexPrefix Contra : show index)]+ $ listTensor [2] [a0, a1]+++-- | Construct a qubit with a pure state.+pureQubit :: QIndex -- ^ Which qubit.+ -> QState -- ^ The state of the qubit.+ -> Wavefunction -- ^ The wavefunction.+pureQubit index QState0 = qubit index (1, 0)+pureQubit index QState1 = qubit index (0, 1)+++-- | Construct a wavefunction for the amplitudes of its qubit states.+--+-- Amplitudes ordered so that the 0 state appears before the 1 state and the lower qubit indices cycle faster than then higher qubit indices. For example, a two-qubit state has its amplitudes ordered |00>, |01>, |10>, |11>. This ordering can be generated as follows, where qubits are orderd from higher indices to lower ones:+--+-- >>> sequence $ replicate 3 [minBound..maxBound] :: [[QState]]+-- [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]+--+-- The squares of the norms of the amplitudes must sum to one.+qubits :: [Amplitude] -- ^ The amplitudes.+ -> Wavefunction -- ^ The wavefunction.+qubits amplitudes =+ let+ n = ilog2 $ length amplitudes+ in+ Wavefunction+ . renameExplicit (wavefunctionLabels n)+ $ listTensor (replicate n 2) amplitudes+++-- | Construct the ground state where each qubit is in state 0.+groundState :: Int -- ^ Number of qubits.+ -> Wavefunction -- ^ The ground state wavefunction.+groundState = pureState . flip replicate QState0+++-- | Constructa a pure state.+pureState :: [QState] -- ^ The state of each qubit, ordered from higher index to lower index.+ -> Wavefunction -- ^ The wavefunction.+pureState =+ Wavefunction+ . outers+ . (rawWavefunction' <$>)+ . (uncurry pureQubit <$>)+ . zip [0..]+ . reverse+++-- | An operator on wavefunctions.+newtype Operator = Operator {rawOperator' :: Tensor (Complex Double)}+ deriving (Eq, Floating, Fractional, Num)++instance Show Operator where+ show =+ showTensor rawOperator'+ $ \n (k, v) ->+ let+ (kr, kc) = splitAt (n `div` 2) $ concatMap show k+ in+ showAmplitude v ++ "|" ++ kr ++ "><" ++ kc ++ "|"+++-- | The 'Tensor' encoding the operator.+rawOperator :: Operator -> Tensor Amplitude+rawOperator = canonicalOrder . rawOperator'+++-- | Number of qubits for an operator.+operatorOrder :: Operator -> Int+operatorOrder = isqrt . order . rawOperator'+++-- | Qubit indices in an operator.+operatorIndices :: Operator -- ^ The operator.+ -> [QIndex] -- ^ List of qubit indices.+operatorIndices = tensorIndices . rawOperator'+++-- | Amplitudes of state transitions in a qubit operator.+operatorAmplitudes :: Operator -- ^ The wavefunction.+ -> [(([QState], [QState]), Amplitude)] -- ^ List of qubit state transitions and their amplitudes, in row-major order with the states ordered according to 'operatorIndices'.+operatorAmplitudes (Operator x) =+ let+ x' = tensorAmplitudes x+ n = length . fst $ head x'+ in+ first (splitAt $ n `div` 2) <$> x'+++-- | Construct an operator on qubit wavefunctions.+--+-- Amplitudes in row-major order where amplitudes are ordered so that the 0 state appears before the 1 state and the lower qubit indices cycle faster than then higher qubit indices. For example, a three-qubit operator has its amplitudes ordered \<00|00>, \<00|01>, \<00|10>, \<00|11>, \<01|00>, \<01|01>, \<01|10>, \<01|11>, \<10|00>, \<10|01>, \<10|10>, \<10|11>, \<11|00>, \<11|01>, \<11|10>, \<11|11>, where states in the bras and kets are correspond to the order of the first argument to `qubitsOperator`. This ordering can be generated as follows:+--+-- >>> fmap (splitAt 2) . sequence $ replicate (2 * 2) [minBound..maxBound] :: [([QState], [QState])] +-- [([0,0],[0,0]),([0,0],[0,1]),([0,0],[1,0]),([0,0],[1,1]),([0,1],[0,0]),([0,1],[0,1]),([0,1],[1,0]),([0,1],[1,1]),([1,0],[0,0]),([1,0],[0,1]),([1,0],[1,0]),([1,0],[1,1]),([1,1],[0,0]),([1,1],[0,1]),([1,1],[1,0]),([1,1],[1,1])]+--+-- The operator must be unitary.+qubitsOperator :: [QIndex] -- ^ The qubit indices for which the operator applies, in descending order according to \<<https://arxiv.org/pdf/1711.02086/>\>.+ -> [Amplitude] -- ^ The amplitudes of the operator matrix, in row-major order with the states ordered from higher indices to lower ones.+ -> Operator -- ^ The wavefunction operator.+qubitsOperator indices =+ let+ n = length indices+ in+ Operator+ . renameExplicit (operatorLabels $ reverse indices)+ . listTensor (replicate n 2 ++ replicate n (-2))+++-- | Multiply two wavefunction or operator tensors.+mult :: Tensor Amplitude -> Tensor Amplitude -> Tensor Amplitude+mult x y =+ let+ lft = filter ((== indexPrefix Co ) . head) $ names x+ rght = filter ((== indexPrefix Contra) . head) $ names y+ cmmn = fmap tail lft `intersect` fmap tail rght+ x' = renameExplicit [(i, '@' : tail i) | i <- filter ((`elem` cmmn) . tail) lft ] x+ y' = renameExplicit [(i, '@' : tail i) | i <- filter ((`elem` cmmn) . tail) rght] y+ in+ x' * y' +++-- | Apply two operators in sequence.+(^*^) :: Operator -> Operator -> Operator+Operator x ^*^ Operator y = Operator $ x `mult` y+infixr 7 ^*^+++-- | Apply an operator to a wavefunction.+(^*) :: Operator -> Wavefunction -> Wavefunction+Operator x ^* Wavefunction y = Wavefunction $ x `mult` y+infixr 6 ^*+++-- | Apply an operator to a wavefunction.+(*^) :: Wavefunction -> Operator -> Wavefunction+(*^) = flip (^*)+infixl 6 *^+++-- | Apply a sequence of operators to a wavefunction.+(^^*) :: Foldable t => t Operator -> Wavefunction -> Wavefunction+(^^*) = flip . foldl $ flip (^*)+infixr 6 ^^*+++-- | Apply a sequence of operators to a wavefunction.+(*^^) :: Foldable t => Wavefunction -> t Operator -> Wavefunction+(*^^) = flip (^^*)+infixl 6 *^^+++-- | Probabilities of a selection of qubits.+probabilities :: [QIndex] -- ^ Which qubits.+ -> Wavefunction -- ^ The wavefunciton.+ -> [([(QIndex, QState)], Double)] -- ^ The probabilities for the combinations of qubit states.+probabilities indices x =+ let+ indices' = wavefunctionIndices x+ positions = catMaybes $ (`elemIndex` indices') <$> indices+ in+ filter ((/= 0) . snd)+ . fmap (\kvs -> (fst $ head kvs, sum $ snd <$> kvs))+ . groupBy ((==) `on` fst)+ $ sortBy (compare `on` fst)+ [+ (+ fmap snd . filter ((`elem` positions) . fst) . zip [0..] $ zip indices' states+ , magnitude amplitude ^ (2::Int)+ )+ |+ (states, amplitude) <- wavefunctionAmplitudes x+ ]+++-- | Project a wavefunction onto a particular state.+project :: [(QIndex, QState)] -- ^ The qubits for the state.+ -> Wavefunction -- ^ The wavefunction.+ -> Wavefunction -- ^ The projected wavefunction.+project states x =+ let+ y = canonicalOrder $ rawWavefunction' x+ p =+ canonicalOrder+ . outers+ $ fmap rawWavefunction'+ [+ case i `lookup` states of+ Nothing -> qubit i (1, 1)+ Just s -> pureQubit i s+ |+ i <- [0..(order y - 1)]+ ]+ z = p .* y+ in+ Wavefunction $ z / sqrt (z * switch z)+++-- | Measure qubits in a wavefunction.+measure :: RandomGen g+ => [QIndex] -- ^ Which qubits to measure.+ -> Wavefunction -- ^ The wavefunction.+ -> Rand g ([(QIndex, QState)], Wavefunction) -- ^ Action for the resulting measurement and wavefunction.+measure indices x =+ do+ let+ candidates = probabilities indices x+ collapse <- fromList $ second toRational <$> candidates+ return (collapse, project collapse x)+++-- | The total probability for the wave function, which should be 1.+wavefunctionProbability :: Wavefunction+ -> Amplitude+wavefunctionProbability (Wavefunction x) = asScalar $ x * switch x
+ src/Data/Qubit/Gate.hs view
@@ -0,0 +1,340 @@+-----------------------------------------------------------------------------+--+-- Module : $Header$+-- Copyright : (c) 2017 Brian W Bush+-- License : MIT+--+-- Maintainer : Brian W Bush <code@functionally.io>+-- Stability : Stable+-- Portability : Portable+--+-- | Gates for qubit wavefunctions, mostly as in Quil \<<https://arxiv.org/abs/1608.03355/\>> and using the conventions of \<<https://arxiv.org/abs/1711.02086/>\>.+--+-----------------------------------------------------------------------------+++module Data.Qubit.Gate (+-- * One-Cubit Gates+ i+, x+, y+, z+, h+, s+, t+-- * Parameterized One-Cubit Gates+, phase+, rx+, ry+, rz+-- * Two-Cubit Gates+, cnot+, swap+, iswap+-- * Parameterized Two-Cubit Gates+, cphase00+, cphase01+, cphase10+, cphase+, pswap+, cz+-- * Three-Cubit Gates+, ccnot+, cswap+) where+++import Data.Complex (Complex(..), cis)+import Data.Qubit (Operator, QIndex, qubitsOperator)+++-- | The identity gate. This corresponds to I in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+i :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+i i0 =+ qubitsOperator [i0]+ [+ 1, 0+ , 0, 1+ ]+++-- | The Pauli-x gate. This corresponds to X in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+x :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+x i0 =+ qubitsOperator [i0]+ [+ 0, 1+ , 1, 0+ ]+++-- | The Pauli-y gate. This corresponds to Y in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+y :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+y i0 =+ qubitsOperator [i0]+ [+ 0 , 0 :+ (-1)+ , 0 :+ 1, 0+ ]+++-- | The Pauli-z gate. This corresponds to Z in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+z :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+z i0 =+ qubitsOperator [i0]+ [+ 1, 0+ , 0, -1+ ]+++-- | The Hadamard gate. This corresponds to H in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+h :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+h i0 =+ qubitsOperator [i0]+ [+ 1, 1+ , 1, -1+ ] / sqrt 2+++-- | The phase-shift gate. This corresponds to PHASE in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+phase :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+phase theta i0 =+ qubitsOperator [i0]+ [+ 1, 0+ , 0, cis theta+ ]+++-- | The one-qubit 90-degree phase-shift gate. This corresponds to S in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+s :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+s i0 =+ qubitsOperator [i0]+ [+ 1, 0+ , 0, 0 :+ 1+ ]+++-- | The one-qubit 45-degree phase-shift gate. This corresponds to T in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+t :: QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+t i0 =+ qubitsOperator [i0]+ [+ 1, 0+ , 0, (1 :+ 1) / sqrt 2+ ]+++-- | A controlled phase gate on |00>. This corresponds to CPHASE00 in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cphase00 :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cphase00 theta i0 i1 =+ qubitsOperator [i0, i1]+ [+ cis theta, 0, 0, 0+ , 0 , 1, 0, 0+ , 0 , 0, 1, 0+ , 0 , 0, 0, 1+ ]+++-- | A controlled phase gate on |01>. This corresponds to CPHASE01 in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cphase01 :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cphase01 theta i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0 , 0, 0+ , 0, cis theta, 0, 0+ , 0, 0 , 1, 0+ , 0, 0 , 0, 1+ ]+++-- | A conrolled pahse gate on |10>. This corresponds to CPHASE10 in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cphase10 :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cphase10 theta i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0, 0 , 0+ , 0, 1, 0 , 0+ , 0, 0, cis theta, 0+ , 0, 0, 0 , 1+ ]+++-- | A controlled phase gate on |11>. This corresponds to CPHASE in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cphase :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cphase theta i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0, 0, 0+ , 0, 1, 0, 0+ , 0, 0, 1, 0+ , 0, 0, 0, cis theta+ ]+++-- | The x-rotation gate. This corresponds to RX in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+rx :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+rx theta i0 =+ qubitsOperator [i0]+ [+ cos (theta / 2) :+ 0 , 0 :+ (- sin (theta / 2))+ , 0 :+ (- sin (theta / 2)), cos (theta / 2) :+ 0+ ]+++-- | The y-rotation gate. This corresponds to RY in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+ry :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+ry theta i0 =+ qubitsOperator [i0]+ [+ cos (theta / 2) :+ 0, (- sin (theta / 2)) :+ 0+ , sin (theta / 2) :+ 0, cos (theta / 2) :+ 0+ ]+++-- | The z-rotation gate. This corresponds to RZ in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+rz :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the qubit in the wavefunction.+ -> Operator -- ^ The operator.+rz theta i0 =+ qubitsOperator [i0]+ [+ cis (- theta / 2), 0+ , 0 , cis (theta / 2)+ ]+++-- | The controlled-not gate for two qubits. This corresponds to CNOT in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cnot :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cnot i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0, 0, 0+ , 0, 1, 0, 0+ , 0, 0, 0, 1+ , 0, 0, 1, 0+ ]+++-- | The controlled-not gate by three qubits. This corresponds to CCNOT in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+ccnot :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the middle qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+ccnot i0 i1 i2 =+ qubitsOperator [i0, i1, i2]+ [+ 1, 0, 0, 0, 0, 0, 0, 0+ , 0, 1, 0, 0, 0, 0, 0, 0+ , 0, 0, 1, 0, 0, 0, 0, 0+ , 0, 0, 0, 1, 0, 0, 0, 0+ , 0, 0, 0, 0, 1, 0, 0, 0+ , 0, 0, 0, 0, 0, 1, 0, 0+ , 0, 0, 0, 0, 0, 0, 0, 1+ , 0, 0, 0, 0, 0, 0, 1, 0+ ]+++-- | The phase-swap gate. This corresponds to PSWAP in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+pswap :: Double -- ^ The phase angle [radians].+ -> QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+pswap theta i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0 , 0 , 0+ , 0, 0 , cis theta, 0+ , 0, cis theta, 0 , 0+ , 0, 0 , 0 , 1+ ]+ ++-- | The swap gate with 0-degree phase. This corresponds to SWAP in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+swap :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+swap i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0, 0, 0+ , 0, 0, 1, 0+ , 0, 1, 0, 0+ , 0, 0, 0, 1+ ]++-- | The swap gate with 90-degree phase. This corresponds to ISWAP in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+iswap :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+iswap i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0 , 0 , 0+ , 0, 0 , 0 :+ 1, 0+ , 0, 0 :+ 1, 0 , 0+ , 0, 0 , 0 , 1+ ]++-- | The three-qubit controlled-swap gate. This corresponds to CSWAP in Quil \<<https://arxiv.org/abs/1608.03355/\>>, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cswap :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the middle qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cswap i0 i1 i2 =+ qubitsOperator [i0, i1, i2]+ [+ 1, 0, 0, 0, 0, 0, 0, 0+ , 0, 1, 0, 0, 0, 0, 0, 0+ , 0, 0, 1, 0, 0, 0, 0, 0+ , 0, 0, 0, 1, 0, 0, 0, 0+ , 0, 0, 0, 0, 1, 0, 0, 0+ , 0, 0, 0, 0, 0, 0, 1, 0+ , 0, 0, 0, 0, 0, 1, 0, 0+ , 0, 0, 0, 0, 0, 0, 0, 1+ ]+ ++-- | The controlled-z gate, with the indexing convention of \<<https://arxiv.org/pdf/1711.02086/>\>.+cz :: QIndex -- ^ The index of the higher qubit in the wavefunction.+ -> QIndex -- ^ The index of the lower qubit in the wavefunction.+ -> Operator -- ^ The operator.+cz i0 i1 =+ qubitsOperator [i0, i1]+ [+ 1, 0, 0, 0+ , 0, 1, 0, 0+ , 0, 0, 1, 0+ , 0, 0, 0, -1+ ]
+ src/Test.hs view
@@ -0,0 +1,337 @@+-----------------------------------------------------------------------------+--+-- Module : $Header$+-- Copyright : (c) 2017 Brian W Bush+-- License : MIT+--+-- Maintainer : Brian W Bush <code@functionally.io>+-- Stability : Stable+-- Portability : Portable+--+-- | Testing qbits and operations on them.+--+-- The test data is derived from pyQuil \<<http://pyquil.readthedocs.io>\ and recorded in \<<https://bitbucket.org/functionally/quil/src/99a2efb54fd2c7ac99a9bde3091da130bdadbf6b/test-data.ipynb>\>.+-----------------------------------------------------------------------------+++{-# 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