The Quipper System

QuipperLib.Simulation.QuantumSimulation

Description

This module provides functions for simulating circuits, for testing and debugging purposes. It borrows ideas from the implementation of the Quantum IO Monad.

This module provides the internal implementation of the library, and can be imported by other libraries. The public interface to simulation is QuipperLib.Simulation.

Synopsis

# Documentation

trace :: Bool -> String -> a -> a Source #

We define our own trace function that only calls trace if the boolean argument is true.

# Simulation as a Transformer

The quantum simulator takes a Quipper circuit producing function, and uses a transformer to simulate the resulting circuit, one gate at a time. This allows the simulation to progress in a lazy manner, allowing dynamic lifting results to be passed back to the circuit producing function as and when they are required (to generate further gates in the circuit).

The implementation of the quantum simulator makes use of a State monad to carry an underlying quantum state throughout the computation. This State is updated by each quantum operation within the circuit. A quantum state is a vector of basis states, along with complex amplitudes.

type GateR r = (Cplx r, Cplx r, Cplx r, Cplx r) Source #

Gates that act on a single qubit can be defined by essentially a 2-by-2 matrix. A GateR is written by rows, such that a matrix:

would be written as (m00,m01,m10,m11).

scale :: Floating r => Cplx r -> GateR r -> GateR r Source #

Scalar multiplication of a 2-by-2 matrix by a given scalar.

reverseR :: Floating r => GateR r -> GateR r Source #

The inverse of a GateR is its conjugate transpose.

gateQ :: Floating r => String -> GateR r Source #

A simple pattern matching function that gives each "gate name" a GateR representation. Adding (single qubit) quantum gates to this function will give them an implementation in the simulator. Any single qubit named quantum gate that needs to be simulated must have a clause in this function, along with a given GateR that is its matrix representation. Note that unitarity is not enforced, so defined gates must be checked manually to be unitary operators.

Example Gates:
gateQ "x" = (0,1,1,0)
gateQ "hadamard" = (h, h, h,-h) where h = (1/sqrt 2)

Like gateQ, but also conditionally invert the gate depending on InverseFlag.

expC :: Floating r => Cplx r -> Cplx r Source #

The exponential function for Cplx numbers.

piC :: Floating r => Cplx r Source #

The constant π, as a complex number.

rotQ :: Floating r => String -> Timestep -> GateR r Source #

Like gateQ, but takes the name of a rotation and a real parameter.

rotQinv :: Floating r => String -> InverseFlag -> Timestep -> GateR r Source #

Like rotQ, but also conditionally invert the gate depending on InverseFlag.

expZtR :: Floating r => r -> GateR r Source #

Return the matrix for the QexpZt gate.

gPhase :: Floating r => r -> GateR r Source #

Return the matrix for the GPhase gate.

gateC :: String -> [Bool] -> Bool Source #

Translate a classical gate name into a boolean function. Adding classical gates to this function will give them an implementation in the simulator.

Example Gate:
gateC "if" [a,b,c] = if a then b else c 

data Vector n a Source #

The type of vectors with scalars in n over the basis a. A vector is simply a list of pairs.

Constructors

 Vector [(a, n)]

Instances

 (Floating r, Eq r) => PMonad r (Vector r) # ProbabilityDistribution forms a PMonad such that probabilistic results are "merged" by extending the probability distribution by the possible results. Methodsmerge :: r -> a -> a -> Vector r a Source # Num n => Monad (Vector n) # Any numeric indexed vector forms a Monad. Methods(>>=) :: Vector n a -> (a -> Vector n b) -> Vector n b #(>>) :: Vector n a -> Vector n b -> Vector n b #return :: a -> Vector n a #fail :: String -> Vector n a # Num n => Functor (Vector n) # Methodsfmap :: (a -> b) -> Vector n a -> Vector n b #(<\$) :: a -> Vector n b -> Vector n a # Num n => Applicative (Vector n) # Methodspure :: a -> Vector n a #(<*>) :: Vector n (a -> b) -> Vector n a -> Vector n b #(*>) :: Vector n a -> Vector n b -> Vector n b #(<*) :: Vector n a -> Vector n b -> Vector n a # (Show a, Eq a, Num n, Eq n, Show n) => Show (Vector n a) # We can show certain vectors, ignoring any 0 probabilities, and combining equal terms. MethodsshowsPrec :: Int -> Vector n a -> ShowS #show :: Vector n a -> String #showList :: [Vector n a] -> ShowS #

type Amplitudes r = Vector (Cplx r) (Map Qubit Bool) Source #

An amplitude distribution gives each classical basis state an amplitude.

type ProbabilityDistribution r a = Vector r a Source #

A probability distribution gives each element a probability.

A QuantumTrace is essentially a probability distribution for the current state of the qubits that have been traced. We can represent this using a Vector. The list of Booleans is in the same order as the list of Qubits that was being traced.

Normalizing is used to make sure the probabilities add up to 1.

data QuantumState r Source #

A QuantumState is the data structure containing the state that we update throughout the simulation. We need to keep track of the next available wire, and a quantum state in the form of a distribution of basis states. We also track a list of quantum traces, so that we have a "tracing" mechanism during the execution of quantum circuits.

Constructors

 QState Fieldsnext_wire :: Wire quantum_state :: Amplitudes r traces :: [QuantumTrace r] namespace :: Namespace trace_flag :: Bool

empty_quantum_state :: Floating r => Bool -> r -> QuantumState r Source #

When we start a simulation, we need an empty starting state.

It doesn't make sense having a quantum control on a classical gate, so we can throw an error if that is the case, and just collect the boolean result otherwise.

Map the classical_control function to all the controls, and take the and of the result

When we want a quantum control, we will be working with one "basis state" at a time, and can look up the qubit's value in that basis state to see whether the control firs.

Map the qc_control function to all the controls (under the given basis state), and take the and of the result.

magnitude :: Floating r => Cplx r -> r Source #

We can calculate the magnitude of a complex number

split :: (Floating r, Eq r, Ord r) => Amplitudes r -> Qubit -> (r, Amplitudes r, Amplitudes r) Source #

The split function splits a Amplitude distribution, by partitioning it around the state of the given qubit within each basis state. It also returns the probability of the qubit being True within the given Amplitudes. This function is used when we want to measure a qubit.

class (Floating r, Monad m) => PMonad r m where Source #

A PMonad is a Monad enriched with a merge function that takes a probability, and two results, and returns a merged version of these results under the given monad. This idea is taken directly from QIO.

Minimal complete definition

merge

Methods

merge :: r -> a -> a -> m a Source #

Instances

 (Floating r, Random r, Ord r) => PMonad r IO # IO forms a PMonad, where results are merged by choosing one probabilistically using a random number. Methodsmerge :: r -> a -> a -> IO a Source # (Floating r, Eq r) => PMonad r (Vector r) # ProbabilityDistribution forms a PMonad such that probabilistic results are "merged" by extending the probability distribution by the possible results. Methodsmerge :: r -> a -> a -> Vector r a Source # (Floating r, Random r, Ord r, RandomGen g) => PMonad r (State g) # A State Monad holding a RandomGen forms a PMonad, where results are merged by choosing one probabilistically using a random number from the RandomGen. Methodsmerge :: r -> a -> a -> State g a Source #

merge_with_result :: PMonad r m => r -> a -> a -> m (Bool, a) Source #

We can merge two measurement outcomes, and explicitly keep the first outcome as the True result, and the second as the False result.

get_trace :: Floating r => [Qubit] -> Amplitudes r -> QuantumTrace r Source #

The get_trace' function returns a probability distribution of the state of a list of qubits within a given amplitude distribution.

add :: Floating r => (Map Qubit Bool, Cplx r) -> Amplitudes r -> Amplitudes r Source #

Add an amplitude to an amplitude distribution, combining (adding) the amplitudes for equal states in the distribution.

apply :: (Floating r, Eq r) => (Map Qubit Bool -> Amplitudes r) -> Amplitudes r -> Amplitudes r Source #

The apply' function is used to apply a function on "basis states" to an entire amplitude distribution.

vector :: Floating r => (Map Qubit Bool -> Map Qubit Bool) -> Map Qubit Bool -> Amplitudes r Source #

Lift a function that returns a single basis state, to a function that returns an amplitude distribution (containing a singleton).

apply the given function only if the controls fire.

performGateQ :: Floating r => GateR r -> Qubit -> Map Qubit Bool -> Amplitudes r Source #

performGateQ defines how a single qubit gate is applied to a quantum state. The application of a GateR to a qubit in a single basis state can split the state into a pair of basis states with corresponding amplitudes.

simulation_transformer :: (PMonad r m, Ord r) => Transformer (StateT (QuantumState r) m) Qubit Bool Source #

The simulation_transformer is the actual transformer that does the simulation. The type of the simulation_transformer shows that Qubits are kept as qubits, but Bits are turned into Boolean values, i.e., the results of the computation. We use a StateT Monad, acting over the IO Monad, to store a QuantumState throughout the simulation. This means we carry a state, but also have access to the IO Monad's random number generator (for probabilistic measurement).

simulation_dynamic_transformer :: (PMonad r m, Ord r) => DynamicTransformer (StateT (QuantumState r) m) Qubit Bool Source #

The simulation_transformer is also Dynamic, as the simulated wire states can simply be used to perform dynamic liftings.

simulate_transform_unary :: (PMonad r m, Ord r) => (QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => (qa -> Circ qb) -> BType qa -> StateT (QuantumState r) m (QCType Qubit Bool (QCType Bit Bit qb)) Source #

Apply the dynamic_simulation_transformer to a (unary) circuit generating function.

qdata_concrete_shape :: QData qa => BType qa -> qa Source #

In order to simulate a circuit using an input basis vector, we need to supply each quantum leaf, with a concrete (i.e., not a dummy) qubit.

In order to simulate a circuit using an input basis vector, we need to supply the transformer with a concrete set of qubit bindings.

qdata_to_basis :: QData qa => BType qa -> Map Qubit Bool Source #

As a helper function, in order to simulate a circuit using an input basis vector, we need to be able to convert each basis into a map from concrete qubits to their value in the given basis.

qdata_vector_to_amplitudes :: (QData qa, Floating r) => Vector (Cplx r) (BType qa) -> Amplitudes r Source #

In order to simulate a circuit using an input basis vector, we need to be able to convert the basis vector into a quantum state suitable for use by the simulator i.e. of type Amplitudes.

basis_to_qdata :: QData qa => qa -> Map Qubit Bool -> BType qa Source #

As a helper function, in order to simulate a circuit using an input basis vector, we need to be able to convert a map from concrete qubits to their value into a basis of the given concrete shape.

amplitudes_to_qdata_vector :: (QData qa, Floating r) => qa -> Amplitudes r -> Vector (Cplx r) (BType qa) Source #

In order to simulate a circuit using an input basis vector, we need to be able to convert the quantum state (i.e. of type Amplitudes) into a basis vector.

simulate_amplitudes_unary :: (PMonad r m, Eq r, Ord r, QData qa, QData qb, qb ~ QCType Qubit Bool qb) => (qa -> Circ qb) -> Vector (Cplx r) (BType qa) -> m (Vector (Cplx r) (BType qb)) Source #

Apply the dynamic_simulation_transformer to a (unary) circuit generating function, starting with the quantum state set to the given vector of base states and returning the resulting vector of base states.

sim_amps :: (RandomGen g, Floating r, Random r, Ord r, QData qa, QData qb, qb ~ QCType Qubit Bool qb, Ord (BType qb)) => g -> (qa -> Circ qb) -> Map (BType qa) (Cplx r) -> Map (BType qb) (Cplx r) Source #

Input a source of randomness, a quantum circuit, and an initial state (represented as a map from basis vectors to amplitudes). Simulate the circuit and return the final state. If the circuit includes measurements, the simulation will be probabilistic.

The type of this heavily overloaded function is difficult to read. It has, for example, the following types:

sim_amps :: StdGen -> (Qubit -> Circ Qubit) -> Map Bool (Cplx Double) -> Map Bool (Cplx Double)
sim_amps :: StdGen -> ((Qubit,Qubit) -> Circ Qubit) -> Map (Bool,Bool) (Cplx Double) -> Map Bool (Cplx Double)

and so forth. Note that instead of Double, another real number type, such as FixedPrec e, can be used.

run_unary :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => g -> r -> (qa -> Circ qb) -> BType qa -> QCType Qubit Bool (QCType Bit Bit qb) Source #

Input a source of randomness, a real number, a circuit, and a basis state. Then simulate the circuit probabilistically. Measure the final state and return the resulting basis vector.

The real number argument is a dummy and is never evaluated; its only purpose is to specify the type of real numbers that will be used during the simulation.

run_unary_trace :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => g -> r -> (qa -> Circ qb) -> BType qa -> [QuantumTrace r] Source #

Like run_unary, but return the list of QuantumTrace elements that were generated during the computation. This is useful for checking the intermediary state of qubits within a computation.

run_unary_io :: (Floating r, Random r, Ord r, QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => r -> (qa -> Circ qb) -> BType qa -> IO (QCType Qubit Bool (QCType Bit Bit qb)) Source #

Like run_unary, but run in the IO monad instead of passing an explicit source of randomness.

run_unary_trace_io :: (Floating r, Random r, Ord r, QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => r -> (qa -> Circ qb) -> BType qa -> IO [QuantumTrace r] Source #

Like run_unary_trace, but run in the IO monad instead of passing an explicit source of randomness.

sim_unary :: (Floating r, Ord r, QCData qa, QCData qb, QCData (QCType Bit Bit qb), QCType Bool Bool qb ~ QCType Bool Bool (QCType Bit Bit qb)) => r -> (qa -> Circ qb) -> BType qa -> ProbabilityDistribution r (QCType Qubit Bool (QCType Bit Bit qb)) Source #

Apply the simulation_transformer to a (unary) circuit, and then evaluate the resulting stateful computation to get a probability distribution of possible results

# Generic functions

## Generic run function

Generic functions to run Quipper circuits, via a conversion to a SimCircuit using Random to simulate quantum states.

run_generic :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (QCType Bool Bool qa) (QCType Qubit Bool (QCType Bit Bit qb))) => g -> r -> qfun -> qfun' Source #

Quantum simulation of a circuit, for testing and debugging purposes. Input a source of randomness, a real number, and a quantum circuit. Output a corresponding probabilistic boolean function.

The inputs to the quantum circuit are initialized according to the given boolean arguments. The outputs of the quantum circuit are measured, and the boolean measurement outcomes are returned.

The real number argument is a dummy and is never evaluated; its only purpose is to specify the type of real numbers that will be used during the simulation.

The type of this heavily overloaded function is difficult to read. In more readable form, it has all of the following types (for example):

run_generic :: (Floating r, Random r, Ord r, RandomGen g, QCData qa) => g -> r -> Circ qa -> BType qa
run_generic :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCData qb) => g -> r -> (qa -> Circ qb) -> BType qa -> BType qb
run_generic :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCData qb, QCData qc) => g -> r -> (qa -> qb -> Circ qc) -> BType qa -> BType qb -> BType qc

and so forth.

run_generic_trace :: (Floating r, Random r, Ord r, RandomGen g, QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (QCType Bool Bool qa) [QuantumTrace r]) => g -> r -> qfun -> qfun' Source #

Like run_generic, but also output a trace of the states of the given list of qubits at each step during the evaluation.

run_generic_io :: (Floating r, Random r, Ord r, QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (QCType Bool Bool qa) (IO (QCType Qubit Bool (QCType Bit Bit qb)))) => r -> qfun -> qfun' Source #

Like run_generic, but run in the IO monad instead of passing an explicit source of randomness.

run_generic_trace_io :: (Floating r, Random r, Ord r, QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (QCType Bool Bool qa) (IO [QuantumTrace r])) => r -> qfun -> qfun' Source #

Like run_generic_trace, but run in the IO monad instead of passing an explicit source of randomness.

## Generic sim function

A generic function to simulate Quipper circuits, via a conversion to a SimCircuit returning a probability distribution of the possible results.

sim_generic :: (Floating r, Ord r, QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (QCType Bool Bool qa) (ProbabilityDistribution r (QCType Qubit Bool (QCType Bit Bit qb)))) => r -> qfun -> qfun' Source #

A generic function to simulate Quipper circuits, via a conversion to a SimCircuit returning a probability distribution of the possible results.

The type of this heavily overloaded function is difficult to read. In more readable form, it has all of the following types (for example):

sim_generic :: (Floating r, Ord r, QCData qa) => r -> Circ qa -> ProbabilityDistribution r (BType qa)
sim_generic :: (Floating r, Ord r, QCData qa, QCData qb) => r -> (qa -> Circ qb) -> BType qa -> ProbabilityDistribution r (BType qb)
sim_generic :: (Floating r, Ord r, QCData qa, QCData qb, QCData qc) => r -> (qa -> qb -> Circ qc) -> BType qa -> BType qb -> ProbabilityDistribution r (BType qc)

and so forth.