quipper-tools-0.9.0.0: Quipper/Programs/Tools/Simulate.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
-- ----------------------------------------------------------------------
-- | This tool reads a circuit from standard input, and then simulates
-- it by applying it to every possible basis vector. This is only
-- intended for debugging (of very small circuits), and is by no means
-- an industrial-strength simulator. Any measurements in the circuit
-- will be simulated probabilistically.
module Main where
import Quipper
import Quipper.Internal
import Quipper.Libraries.Simulation
import Quipper.Libraries.QuipperASCIIParser
import Quipper.Utils.Auxiliary (string_of_list)
import Quipper.Utils.CommandLine
import Quipper.Utils.Sampling
import Quantum.Synthesis.Ring
import Quantum.Synthesis.SymReal
import Data.Map (Map)
import qualified Data.Map as Map
import Control.Monad
import Data.Number.FixedPrec
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.Random
-- ----------------------------------------------------------------------
-- * Command line options
-- | A data type to hold the command line options.
data Options = Options {
prec :: Integer, -- ^ Precision in decimal digits.
addprec :: Int -- ^ Additional precision to use internally.
} deriving Show
-- | The default options.
defaultOptions :: Options
defaultOptions = Options
{ prec = 30,
addprec = 10
}
-- | The list of command line options, in the format required by 'getOpt'.
options =
[ Option ['h'] ["help"]
(NoArg help)
"print usage info and exit",
Option ['d'] ["digits"]
(ReqArg opt_digits "<num>")
"precision in decimal digits (default: 30)",
Option ['a'] ["addprec"]
(ReqArg opt_additional "<num>")
"additional digits to use internally (default: 10)"
]
where
opt_digits :: String -> Options -> IO Options
opt_digits str o =
case parse_int str of
Just d -> return o { prec = d }
_ -> optfail ("Invalid value for option -d -- " ++ str ++ "\n")
opt_additional :: String -> Options -> IO Options
opt_additional str o =
case parse_int str of
Just d -> return o { addprec = d }
_ -> optfail ("Invalid value for option -a -- " ++ str ++ "\n")
help :: Options -> IO Options
help o = do
usage
exitSuccess
-- | Process /argv/-style command line options into an 'Options' structure.
dopts :: [String] -> IO Options
dopts argv =
case getOpt Permute options argv of
(o, [], []) -> (foldM (flip id) defaultOptions o)
(_, _, []) -> optfail "Too many non-option arguments\n"
(_, _, errs) -> optfail (concat errs)
-- | Print a usage message to 'stdout'.
usage :: IO ()
usage = do
name <- getProgName
putStr (usageInfo (header name) options)
where header name =
name ++ ": reads a circuit from standard input, and then simulates\n"
++ "it by applying it to every possible basis vector. This is only\n"
++ "intended for debugging (of very small circuits), and is by no means\n"
++ "an industrial-strength simulator. Any measurements in the circuit\n"
++ "will be simulated probabilistically.\n\n"
++ "Usage: " ++ name ++ " [option...]"
-- ----------------------------------------------------------------------
-- * Payload functions
-- | Convert a boolean vector to ket notation.
show_ket :: [Bool] -> String
show_ket = string_of_list "|" "" ">" "|>" show_bit where
show_bit False = "0"
show_bit True = "1"
-- | A version of 'sim_amps' that works on 'FixedPrec' numbers and
-- uses /n/ digits of additional precision internally during the
-- simulation.
sim_amps_addprec :: forall g p qa qb.(RandomGen g, Precision p, QData qa, QData qb, Ord (BType qb)) => g -> Int -> (qa -> Circ qb) -> Map (BType qa) (Cplx (FixedPrec p)) -> Map (BType qb) (Cplx (FixedPrec p))
sim_amps_addprec g addprec circ state =
with_added_digits addprec f (dummy state) :: Map (BType qb) (Cplx (FixedPrec p)) where
dummy :: Map a (Cplx f) -> f
dummy = undefined
f :: (Precision e) => FixedPrec e -> Map (BType qb) (Cplx (FixedPrec p))
f arg = Map.map cast_cplx out_state where
in_state = Map.map cast_cplx state
out_state = sim_amps g circ in_state
_ = dummy in_state `attypeof` arg
attypeof :: a -> a -> a
attypeof x y = x
cast_cplx (Cplx a b) = Cplx (cast a) (cast b)
-- | Input a source of randomness, an added precision, a real number,
-- a circuit, and a basis vector. Simulate the circuit on the given
-- basis vector, and print out the result of the simulation in the
-- format
--
-- > |011> ->
-- > |011> 0.7071067811
-- > |010> -0.7071067811
--
-- The real number argument is a dummy argument; it serves to
-- determine the real number type (e.g., 'Double', 'FixedPrec' /e/) to
-- be used for the simulation. In this way, it is possible to simulate
-- up to arbitrary precision.
print_simulation_internal :: (RandomGen g, Precision e) => g -> Int -> FixedPrec e -> ([Qubit] -> Circ [Qubit]) -> [Bool] -> IO ()
print_simulation_internal g addprec r circ l = do
putStrLn $ show_ket l ++ " ->"
foreach amps $ \(ket, amp) -> do
when (amp /= 0) $ do
putStrLn $ [' ' | x <- l] ++ " " ++ show_ket ket ++ " " ++ show amp
where
state = Map.fromList [(l, Cplx (1 `attypeof` r) 0)]
amps = Map.toList $ sim_amps_addprec g addprec circ state
attypeof :: a -> a -> a
attypeof x y = x
-- | Like 'print_simulation_internal', but instead of a dummy real number
-- argument, specify a number of decimal digits for the simulation.
print_simulation :: (RandomGen g) => g -> Integer -> Int -> ([Qubit] -> Circ [Qubit]) -> [Bool] -> IO ()
print_simulation g d addprec = dynamic_fixedprec d (print_simulation_internal g addprec) (undefined :: Double)
-- | Input a source of randomness, a precision (in decimal digits), a
-- quantum circuit and a shape argument. Simulate the circuit for
-- every possible basis vector of the given shape, and print out the
-- result of the simulations in the format
--
-- > |011> ->
-- > |011> 0.7071067811
-- > |010> -0.7071067811
simulate_all :: (RandomGen g) => g -> Integer -> Int -> ([Qubit] -> Circ [Qubit]) -> [a] -> IO ()
simulate_all g d addprec circ ins = do
let ls = sample_all0 [ True | x <- ins ]
foreach ls $ \l -> do
print_simulation g d addprec circ l
-- ----------------------------------------------------------------------
-- * The main function
-- | Main function: read from 'stdin', then simulate.
main :: IO ()
main = do
-- Process command line options
argv <- getArgs
options <- dopts argv
let d = prec options
let a = addprec options
-- Read circuit
(ins, circuit) <- parse_from_stdin
let circ :: [Qubit] -> Circ [Qubit]
circ qs = do
let es = [ Endpoint_Qubit q | q <- qs ]
es <- circuit es
let qs = [ q | Endpoint_Qubit q <- es ]
return qs
-- Simulate circuit
g <- newStdGen
simulate_all g d a circ ins