quipper-0.7: An embedded, scalable functional programming language for quantum computing.

Safe HaskellNone

Quipper.Generic

Contents

Description

This module provides functions and operators that are "generic" on quantum data. We say that a function is generic if it works at any quantum data type, rather than just a specific type such as Qubit. For example, the generic function qinit can be used to initialize a qubit from a boolean, or a pair of qubits from a pair of booleans, or a list of qubits from a list of booleans, and so forth.

Some functions are also generic in the number of arguments they take, in addition to the type of the arguments.

Synopsis

Generic gates

Initialization and termination

qinit :: QShape ba qa ca => ba -> Circ qaSource

Initialize a qubit from a boolean parameter. More generally, initialize a data structure of qubits from a corresponding data structure of boolean parameters. Examples:

 q <- qinit False
 (q0, q1) <- qinit (True, False)
 [q0, q1, q2] <- qinit [True, False, True]

qterm :: QShape ba qa ca => ba -> qa -> Circ ()Source

Terminate a qubit, asserting its state to equal the boolean parameter. More generally, terminate a data structure of qubits, asserting that their state is as given by a data structure of booleans parameters. Examples:

 qterm False q
 qterm (False, False) (q0, q1)
 qterm [False, False, False] [q0, q1, q2]

In some cases, it is permissible for some aspect of the parameter's shape to be underspecified, e.g., a longer than necessary list, or an integer of indeterminate length. It is therefore possible, for example, to write:

 qterm 17 qa          -- when qa :: QDInt,
 qterm [False..] qa   -- when qa :: [Qubit].

The rules for when a boolean argument can be "promoted" in this way are specific to each individual data type.

qdiscard :: QData qa => qa -> Circ ()Source

Discard a qubit, ignoring its state. This can leave the quantum system in a mixed state, so is not a reversible operation. More generally, discard all the qubits in a quantum data structure. Examples:

 qdiscard q
 qdiscard (q0, q1)
 qdiscard [q0, q1, q2]

cinit :: QShape ba qa ca => ba -> Circ caSource

Initialize a Bit (boolean input) from a Bool (boolean parameter). More generally, initialize the a data structure of Bits from a corresponding data structure of Bools. Examples:

 b <- cinit False
 (b0, b1) <- cinit (True, False)
 [b0, b1, b2] <- cinit [True, False, True]

cterm :: QShape ba qa ca => ba -> ca -> Circ ()Source

Terminate a Bit, asserting its state to equal the given Bool. More generally, terminate a data structure of Bits, asserting that their state is as given by a data structure of Bools. Examples:

 cterm False b
 cterm (False, False) (b0, b1)
 cterm [False, False, False] [b0, b1, b2]

In some cases, it is permissible for some aspect of the parameter's shape to be underspecified, e.g., a longer than necessary list, or an integer of indeterminate length. It is therefore possible, for example, to write:

 cterm 17 ca          -- when ca :: CInt,
 cterm [False..] ca   -- when ca :: [Bit].

The rules for when a boolean argument can be "promoted" in this way are specific to each individual data type.

cdiscard :: CData ca => ca -> Circ ()Source

Discard a Bit, ignoring its state. This can leave the system in a mixed state, so is not a reversible operation. More generally, discard all the Bits in a data structure. Examples:

 cdiscard b
 cdiscard (b0, b1)
 cdiscard [b0, b1, b2]

qc_init :: QCData qc => BType qc -> Circ qcSource

Heterogeneous version of qinit. Please note that the type of the result of this function cannot be inferred from the type of the argument. For example,

 x <- qc_init False

is ambiguous, unless it can be inferred from the context whether x is a Bit or a Qubit. If the type cannot be inferred from the context, it needs to be stated explicitly, like this:

 x <- qc_init False :: Circ Qubit

Alternatively, qc_init_with_shape can be used to fix a specific type.

qc_init_with_shape :: QCData qc => qc -> BType qc -> Circ qcSource

A version of qc_init that uses a shape type parameter. The first argument is the shape type parameter, and the second argument is a data structure containing boolean initializers. The shape type argument determines which booleans are used to initialize qubits, and which ones are used to initialize classical bits.

Example:

 (x,y) <- qc_init_with_shape (bit,[qubit]) (True, [False,True])

This will assign to x a classical bit initialized to 1, and to y a list of two qubits initialized to |0〉 and |1〉, respectively.

qc_term :: QCData qc => BType qc -> qc -> Circ ()Source

Heterogeneous version of qterm.

qc_discard :: QCData qc => qc -> Circ ()Source

Heterogeneous version of qdiscard.

Measurement and preparation

measure :: QShape ba qa ca => qa -> Circ caSource

Measure a Qubit, resulting in a Bit. More generally, measure all the Qubits in a quantum data structure, resulting in a corresponding data structure of Bits. This is not a reversible operation. Examples:

 b <- measure q
 (b0, b1) <- measure (q0, q1)
 [b0, b1, b2] <- measure [q0, q1, q2]

prepare :: QShape ba qa ca => ca -> Circ qaSource

Prepare a Qubit initialized from a Bit. More generally, prepare a data structure of Qubits, initialized from a corresponding data structure of Bits. Examples:

 q <- prepare b
 (q0, q1) <- prepare (b0, b1)
 [q0, q1, q2] <- prepare [b0, b1, b2]

qc_measure :: QCData qc => qc -> Circ (QCType Bit Bit qc)Source

Heterogeneous version of measure. Given a heterogeneous data structure, measure all of its qubits, and leave any classical bits unchanged.

qc_prepare :: QCData qc => qc -> Circ (QCType Qubit Qubit qc)Source

Heterogeneous version of measure. Given a heterogeneous data structure, prepare qubits from all classical bits, and leave any qubits unchanged.

Global phase gate

global_phase_anchored :: QCData qc => Double -> qc -> Circ ()Source

Like global_phase, except the gate is also "anchored" at a qubit, a bit, or more generally at some quantum data. The anchor is only used as a hint for graphical display. The gate, which is a zero-qubit gate, will potentially be displayed near the anchor(s).

Mapped gates

map_hadamard :: QData qa => qa -> Circ qaSource

Apply a Hadamard gate to every qubit in a quantum data structure.

map_hadamard_at :: QData qa => qa -> Circ ()Source

Imperative version of map_hadamard.

swap :: QCData qc => qc -> qc -> Circ (qc, qc)Source

Apply a swap gate to two qubits. More generally, apply swap gates to every corresponding pair of qubits in two pieces of quantum data.

swap_at :: QCData qc => qc -> qc -> Circ ()Source

Apply a swap gate to two qubits. More generally, apply swap gates to every corresponding pair of qubits in two pieces of quantum data.

controlled_not :: QCData qc => qc -> qc -> Circ (qc, qc)Source

Apply a controlled-not gate to every corresponding pair of quantum or classical bits in two pieces of QCData. The first argument is the target and the second the (positive) control.

For now, we require both pieces of QCData to have the same type, i.e., classical bits can be controlled only by classical bits and quantum bits can be controlled only by quantum bits.

Example:

 ((a',b'), (x,y)) <- controlled_not (a,b) (x,y)

is equivalent to

 a' <- qnot a `controlled` x
 b' <- qnot b `controlled` y

controlled_not_at :: QCData qc => qc -> qc -> Circ ()Source

Imperative version of controlled_not. Apply a controlled-not gate to every corresponding pair of quantum or classical bits in two pieces of QCData. The first argument is the target and the second the (positive) control.

bool_controlled_not :: QCData qc => qc -> BType qc -> Circ qcSource

A version of controlled_not where the control consists of boolean data. Example:

 bool_controlled_not (q, r, s) (True, True, False)

negates q and r, but not s.

bool_controlled_not_at :: QCData qc => qc -> BType qc -> Circ ()Source

A version of controlled_not_at where the control consists of boolean data. Example:

 bool_controlled_not_at (q, r, s) (True, True, False)

negates q and r, but not s.

qmultinot :: QData qa => qa -> Circ qaSource

Negate all qubits in a quantum data structure.

qmultinot_at :: QData qa => qa -> Circ ()Source

Negate all qubits in a quantum data structure.

Copying and uncopying

qc_copy_fun :: QCData qc => qc -> Circ (qc, qc)Source

Initialize a new piece of quantum data, as a copy of a given piece. Returns both the original and the copy.

qc_uncopy_fun :: QCData qc => qc -> qc -> Circ qcSource

Given two pieces of quantum data, assumed equal (w.r.t. the computational basis), terminate the second piece (and return the first, unmodified). This is the inverse of qc_copy_fun, in the sense that the following sequence of instructions behaves like the identity function:

 (orig, copy) <- qc_copy_fun orig
 orig <- qc_uncopy_fun orig copy

qc_copy :: QCData qc => qc -> Circ qcSource

Create a fresh copy of a piece of quantum data. Note: copying is performed via a controlled-not operation, and is not cloning. This is similar to qc_copy_fun, except it returns only the copy, and not the original.

qc_uncopy :: QCData qc => qc -> qc -> Circ ()Source

"Uncopy" a piece of quantum data; i.e. terminate copy, assuming it's a copy of orig. This is the inverse of qc_copy, in the sense that the following sequence of instructions behaves like the identity function:

 b <- qc_copy a
 qc_uncopy a b

Classical gates

cgate_if :: CData ca => Bit -> ca -> ca -> Circ caSource

If a is True, return a copy of b, else return a copy of c. Here b and c can be any data structures consisting of Bits, but b and c must be of the same type and shape (for example, if they are lists, they must be of equal length). Examples:

 output <- cgate_if a b c
 (out0, out1) <- cgate_if a (b0, b1) (c0, c1)
 [out0, out1, out2] <- cgate_if a [b0, b1, b2] [c0, c1, c2]

circ_if :: CData ca => Bit -> Circ ca -> Circ ca -> Circ caSource

circ_if is an if-then-else function for classical circuits. It is a wrapper around cgate_if, intended to be used like this:

 result <- circ_if <<<condition>>> (
   <<then-part>>>
   )(
   <<<else-part>>>
   )

Unlike cgate_if, this is a meta-operation, i.e., the bodies of the "then" and "else" parts can be circuit building operations.

What makes this different from the usual boolean "if-then-else" is that the condition is of type Bit, i.e., it is only known at circuit execution time. Therefore the generated circuit contains both the "then" and "else" parts, suitably controlled. Precondition: the "then" and "else" parts must be of the same type and shape.

Named gates

named_gate :: QData qa => String -> qa -> Circ qaSource

Define a new functional-style gate of the given name. Usage:

 my_unary_gate :: Qubit -> Circ Qubit
 my_unary_gate = named_gate "Q"
 my_binary_gate :: (Qubit, Qubit) -> Circ (Qubit, Qubit)
 my_binary_gate = named_gate "R"

This defines a new unary gate and a new binary gate, which will be rendered as Q and R, respectively, in circuit diagrams.

named_gate_at :: QData qa => String -> qa -> Circ ()Source

Define a new imperative-style gate of the given name. Usage:

 my_unary_gate_at :: Qubit -> Circ ()
 my_unary_gate_at = named_gate_at "Q"
 my_binary_gate_at :: (Qubit, Qubit) -> Circ ()
 my_binary_gate_at = named_gate_at "R"

This defines a new unary gate and a new binary gate, which will be rendered as Q and R, respectively, in circuit diagrams.

named_rotation :: QData qa => String -> Timestep -> qa -> Circ qaSource

Define a new functional-style gate of the given name, and parameterized by a real-valued parameter. This is typically used for rotations or phase gates that are parameterized by an angle. The name can contain '%' as a place holder for the parameter. Usage:

 my_unary_gate :: Qubit -> Circ Qubit
 my_unary_gate = named_rotation "exp(-i%Z)" 0.123
 my_binary_gate :: TimeStep -> (Qubit, Qubit) -> Circ (Qubit, Qubit)
 my_binary_gate t = named_rotation "Q(%)" t

named_rotation_at :: QData qa => String -> Timestep -> qa -> Circ ()Source

Define a new imperative-style gate of the given name, and parameterized by a real-valued parameter. This is typically used for rotations or phase gates that are parameterized by an angle. The name can contain '%' as a place holder for the parameter. Usage:

 my_unary_gate_at :: Qubit -> Circ ()
 my_unary_gate_at = named_rotation "exp(-i%Z)" 0.123
 my_binary_gate_at :: TimeStep -> (Qubit, Qubit) -> Circ ()
 my_binary_gate_at t = named_rotation "Q(%)" t

extended_named_gate :: (QData qa, QData qb) => String -> qa -> qb -> Circ qaSource

Define a new functional-style gate of the given name. Like named_gate, except that the generated gate is extended with "generalized controls". The generalized controls are additional inputs to the gate that are guaranteed not to be modified if they are in a computational basis state. They are rendered in a special way in circuit diagrams. Usage:

 my_new_gate :: (Qubit,Qubit) -> Qubit -> Circ (Qubit,Qubit)
 my_new_gate = extended_named_gate "Q"

This defines a new gate with name Q, two inputs, and one generalized input.

extended_named_gate_at :: (QData qa, QData qb) => String -> qa -> qb -> Circ ()Source

Like extended_named_gate, except defines an imperative style gate. Usage:

 my_new_gate_at :: (Qubit,Qubit) -> Qubit -> Circ ()
 my_new_gate_at = extended_named_gate_at "Q"

This defines a new gate with name Q, two inputs, and one generalized input.

Dynamic lifting

dynamic_lift :: QShape ba qa ca => ca -> Circ baSource

Convert a Bit (boolean circuit output) to a Bool (boolean parameter). More generally, convert a data structure of Bits to a corresponding data structure of Bools.

For use in algorithms that require the output of a measurement to be used as a circuit-generation parameter. This is the case, for example, for sieving methods, and also for some iterative algorithms.

Note that this is not a gate, but a meta-operation. The input consists of classical circuit endpoints (whose values are known at circuit execution time), and the output is a boolean parameter (whose value is known at circuit generation time).

The use of this operation implies an interleaving between circuit execution and circuit generation. It is therefore a (physically) expensive operation and should be used sparingly. Using the dynamic_lift operation interrupts the batch mode operation of the quantum device (where circuits are generated ahead of time), and forces interactive operation (the quantum device must wait for the next portion of the circuit to be generated). This operation is especially expensive if the current circuit contains unmeasured qubits; in this case, the qubits must be preserved while the quantum device remains on standby.

Also note that this operation is not supported in all contexts. It is an error, for example, to use this operation in a circuit that is going to be reversed, or in the body of a boxed subroutine. Also, not all output devices (such as circuit viewers) support this operation.

Mapping

mapUnary :: QData qa => (Qubit -> Circ Qubit) -> qa -> Circ qaSource

Map a single qubit gate across every qubit in the data structure.

mapBinary :: QData qa => (Qubit -> Qubit -> Circ (Qubit, Qubit)) -> qa -> qa -> Circ (qa, qa)Source

Map a binary gate across every corresponding pair of qubits in two quantum data structures of equal shape.

mapBinary_c :: QShape ba qa ca => (Qubit -> Bit -> Circ (Qubit, Bit)) -> qa -> ca -> Circ (qa, ca)Source

Like mapBinary, except the second data structure is classical.

map2Q :: QData qa => ((Qubit, Qubit) -> Circ Qubit) -> (qa, qa) -> Circ qaSource

Map a binary qubit circuit to every pair of qubits in the quantum data-type. It is a run-time error if the two structures do not have the same size.

qc_mapBinary :: QCData qc => (Qubit -> Qubit -> Circ (Qubit, Qubit)) -> (Bit -> Bit -> Circ (Bit, Bit)) -> qc -> qc -> Circ (qc, qc)Source

Heterogeneous version of mapBinary. Map a binary gate f across every corresponding pair of qubits, and a binary gate g across every corresponding pair of bits, in two quantum data structures of equal shape.

Conversion to lists

The functions in this section can be used to convert quantum data structures to and from lists. Do not use them! The conversion is unsafe in the same way pointers to void are unsafe in the C programming language. There is almost always a better and more natural way to accomplish what you need to do.

qubits_of_qdata :: QData qa => qa -> [Qubit]Source

Return the list of qubits representing the given quantum data. The qubits are ordered in some fixed, but arbitrary way. It is guaranteed that two pieces of qdata of the same given shape will be ordered in the same way. No other property of the order is guaranteed, In particular, the order may change without notice from one version of Quipper to the next.

qdata_of_qubits :: QData qa => qa -> [Qubit] -> qaSource

Take a specimen piece of quantum data to specify the "shape" desired (length of lists, etc); then reads the given list of qubits in as a piece of quantum data of the same shape. The ordering of the input qubits is the same as qubits_of_qdata produces for the given shape.

A "length mismatch" error occurs if the list does not have exactly the required length.

endpoints_of_qcdata :: QCData qc => qc -> [Endpoint]Source

Return the list of endpoints that form the leaves of the given QCData. The leaves are ordered in some fixed, but arbitrary way. It is guaranteed that two pieces of data of the same given shape will be ordered in the same way. No other property of the order is guaranteed. In particular, the order may change without notice from one version of Quipper to the next.

qcdata_of_endpoints :: QCData qc => qc -> [Endpoint] -> qcSource

Take a specimen piece of QCData to specify the "shape" desired (length of lists, etc); then reads the given list of endpoints in as a piece of quantum data of the same shape. The ordering of the input endpoints equals that produced by endpoints_of_qcdata for the given shape.

A "length mismatch" error occurs if the list does not have exactly the required length. A "shape mismatch" error occurs if the list contains a Qubit when a Bit was expected, or vice versa.

Shape related operations

qc_false :: QCData qc => qc -> BType qcSource

Return a boolean data structure of the given shape, with every leaf initialized to False.

qshape :: QData qa => BType qa -> qaSource

Return a quantum data structure of the given boolean shape, with every leaf initialized to the undefined dummy value qubit.

Bindings

qc_bind :: QCData qc => qc -> QCType a b qc -> Bindings a bSource

Take two pieces of quantum data of the same shape (the first of which consists of wires of a low-level circuit) and create bindings.

qc_unbind :: QCData qc => Bindings a b -> qc -> QCType a b qcSource

Apply bindings to a piece of quantum and/or classical data holding low-level wires, to get data of the same shape.

Generic controls

The following functions define a convenient syntax for controls. With this, we can write controls in much the same way as one would write (a restricted class of) boolean expressions. Examples:

 q1 .==. 0 .&&. q2 .==. 1         for Qubits q1, q2
 q .&&. p                         means  q .==. 1  .&&.  p .==. 1
 qx .==. 5                        for a QDInt qx
 q1 .==. 0 .&&. z <= 7            we can combine quantum and classical controls
 q ./=. b                         the negation of q .==. b;
                                  here b is a boolean.
 [p,q,r,s]                        a list of positive controls
 [(p, True), (q, False), (r, False), (s, True)]
                                  a list of positive and negative controls

Among these infix operators, (.&&.) binds more weakly than (.==.), (./=.).

(.&&.) :: (ControlSource a, ControlSource b) => a -> b -> ControlListSource

This is an infix operator to concatenate two controls, forming their logical conjunction.

(.==.) :: QCData qc => qc -> BType qc -> ControlListSource

(qx .==. x): a control which is true just if quantum data qx is in the specified state x.

(./=.) :: QCLeaf q => q -> Bool -> ControlListSource

The notation (q ./=. x) is shorthand for (q .==. not x), when x is a boolean parameter.

Unlike .==., which is defined for any shape of quantum data, ./=. is only defined for a single control bit or qubit.

Generic encapsulation

An encapsulated circuit is a low-level circuit together with data structures holding the input endpoints and output endpoints. A circuit-generating function, with fully specified parameters, can be turned into an encapsulated circuit; conversely, an encapsulated circuit can be turned into a circuit-generating function. Thus, encapsulation and unencapsulation are the main interface for passing between high- and low-level data structures.

encapsulate_generic :: QCData x => ErrMsg -> (x -> Circ y) -> x -> (x, BCircuit, y)Source

Extract an encapsulated circuit from a circuit-generating function. This requires a shape parameter.

encapsulate_generic_in_namespace :: QCData x => ErrMsg -> (x -> Circ y) -> x -> Circ (x, BCircuit, y)Source

As encapsulate_generic, but passes the current namespace into the circuit-generating function, to save recomputing shared subroutines

unencapsulate_generic :: (QCData x, QCData y) => (x, BCircuit, y) -> x -> Circ ySource

Turn an encapsulated circuit back into a circuit-generating function.

A dynamic encapsulated circuit is to an encapsulated circuit like a DBCircuit to a BCircuit. The output is not a static circuit, but an interactive computation expressed through the ReadWrite monad, which can be run on a quantum device to get a static circuit out.

encapsulate_dynamic :: QCData x => (x -> Circ y) -> x -> (x, DBCircuit y)Source

Extract an encapsulated dynamic circuit from a circuit-generating function. This requires a shape parameter.

unencapsulate_dynamic :: (QCData x, QCData y) => (x, DBCircuit y) -> x -> Circ ySource

Turn an encapsulated dynamic circuit back into a circuit-generating function.

This currently fails if the dynamic circuit contains output liftings, because the transformer interface has not yet been updated to work with dynamic circuits.

Generic reversing

reverse_generic :: (QCData x, QCData y, TupleOrUnary xt x, QCurry x_y x y, Curry x_y_xt x (y -> Circ xt)) => x_y -> x_y_xtSource

Reverse a circuit-generating function. The reversed function requires a shape parameter, given as the input type of the original function.

The type of this highly overloaded function is quite difficult to read. It can have for example the following types:

 reverse_generic :: (QCData x, QCData y) => (x -> Circ y) -> x -> (y -> Circ x) 
 reverse_generic :: (QCData x, QCData y, QCData z) => (x -> y -> Circ z) -> x -> y -> (z -> Circ (x,y)) 

reverse_generic_curried :: (QCData x, QCData y, TupleOrUnary xt x, Tuple yt y, QCurry x_yt x yt, QCurry y_xt y xt, Curry x_y_xt x y_xt) => x_yt -> x_y_xtSource

Like reverse_generic, but takes functions whose output is a tuple, and curries the reversed function. Differs from reverse_generic in an example such as:

 f                         :: (x -> y -> Circ (z,w))
 reverse_generic f         :: x -> y -> ((z,w) -> Circ (x,y))
 reverse_generic_curried f :: x -> y -> (z -> w -> Circ (x,y))

Note: the output must be a n-tuple, where n = 0 or n ≥ 2. Applying this to a circuit whose output is a non-tuple type is a type error; in this case, reverse_generic should be used.

reverse_simple :: (QCData_Simple x, QCData y, TupleOrUnary xt x, QCurry x_y x y) => x_y -> y -> Circ xtSource

Like reverse_generic, but only works at simple types, and therefore requires no shape parameters. Typical type instances:

 reverse_simple :: (QCData_Simple x, QCData y) => (x -> Circ y) -> (y -> Circ x)
 reverse_simple :: (QCData_Simple x, QCData_Simple y, QCData z) => (x -> y -> Circ z) -> (z -> Circ (x,y))

reverse_simple_curried :: (QCData_Simple x, QCData y, TupleOrUnary xt x, Tuple yt y, QCurry x_yt x yt, QCurry y_xt y xt) => x_yt -> y_xtSource

Like reverse_simple, but takes functions whose output is a tuple, and curries the reversed function. Typical type instance:

 reverse_simple_curried :: (QCData_Simple x, QCData y, QCData z) => (x -> Circ (y,z)) -> (y -> z -> Circ x)

Note: the output must be a n-tuple, where n = 0 or n ≥ 2. Applying this to a circuit whose output is a non-tuple type is a type error; in this case, reverse_generic should be used.

reverse_generic_endo :: (QCData x, TupleOrUnary xt x, QCurry x_xt x xt) => x_xt -> x_xtSource

Like reverse_generic, but specialized to endomorphic circuits, i.e., circuits where the input and output have the same type (modulo possibly currying) and shape. In this case, unlike reverse_generic, no additional shape parameter is required, and the reversed function is curried if the original function was. Typical type instances:

 reverse_generic_endo :: (QCData x) => (x -> Circ x) -> (x -> Circ x)
 reverse_generic_endo :: (QCData x, QCData y) => (x -> y -> Circ (x,y)) -> (x -> y -> Circ (x,y))

reverse_generic_imp :: (QCData x, QCurry x__ x ()) => x__ -> x__Source

Like reverse_generic_endo, but applies to endomorphic circuits expressed in "imperative" style. Typical type instances:

 reverse_generic_endo :: (QCData x) => (x -> Circ ()) -> (x -> Circ ())
 reverse_generic_endo :: (QCData x, QCData y) => (x -> y -> Circ ()) -> (x -> y -> Circ ())

reverse_endo_if :: (QCData x, TupleOrUnary xt x, QCurry x_xt x xt) => Bool -> x_xt -> x_xtSource

Conditional version of reverse_generic_endo. Invert the endomorphic quantum circuit if the boolean is true; otherwise, insert the non-inverted circuit.

reverse_imp_if :: (QCData qa, QCurry fun qa ()) => Bool -> fun -> funSource

Conditional version of reverse_generic_imp. Invert the imperative style quantum circuit if the boolean is true; otherwise, insert the non-inverted circuit.

The QCurry type class

class QCurry fun args res | fun -> args res, args res -> fun whereSource

The QCurry type class is similar to the Curry type class, except that the result type is guarded by the Circ monad. It provides a family of type isomorphisms

fun  ≅  args -> Circ res,

where

 fun = a1 -> a2 -> ... -> an -> Circ res,
 args = (a1, (a2, (..., (an, ())))).

The benefit of having Circ in the result type is that it ensures that the result type is not itself a function type, and therefore fun has a unique arity n. Then args and res are uniquely determined by fun, which can be used to write higher-order operators that consume fun of any arity and "do the right thing".

Methods

qcurry :: (args -> Circ res) -> funSource

quncurry :: fun -> args -> Circ resSource

Instances

QCurry (Circ b) () b 
QCurry fun args res => QCurry (a -> fun) (a, args) res 

Generic circuit transformations

transform_unary_dynamic_shape :: (QCData x, QCData y, x' ~ QCType a b x, y' ~ QCType a b y, Monad m) => DynamicTransformer m a b -> (x -> Circ y) -> x -> x' -> m y'Source

Like transform_unary_shape but for a dynamic transformer

transform_unary_dynamic :: (QCData x, QCData y) => DynamicTransformer Circ Qubit Bit -> (x -> Circ y) -> x -> Circ ySource

Like transform_unary but for a dynamic transformer

transform_unary :: (QCData x, QCData y) => Transformer Circ Qubit Bit -> (x -> Circ y) -> x -> Circ ySource

Apply the given transformer to a circuit.

transform_generic :: (QCData x, QCData y, QCurry qfun x y) => Transformer Circ Qubit Bit -> qfun -> qfunSource

Apply the given transformer to a circuit. Unlike transform_unary, this function can be applied to a circuit-generating function in curried form with n arguments, for any n ≥ 0.

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

 transform_generic :: (QCData x) => Transformer Circ Qubit Bit -> Circ x -> Circ x
 transform_generic :: (QCData x, QCData y) => Transformer Circ Qubit Bit -> (x -> Circ y) -> (x -> Circ y)
 transform_generic :: (QCData x, QCData y, QCData z) => Transformer Circ Qubit Bit -> (x -> y -> Circ z) -> (x -> y -> Circ z)

and so forth.

transform_unary_shape :: (QCData x, QCData y, x' ~ QCType a b x, y' ~ QCType a b y, Monad m) => Transformer m a b -> (x -> Circ y) -> x -> x' -> m y'Source

Like transform_generic, but applies to arbitrary transformers of type

 Transformer m a b

instead of the special case

 Transformer Circ Qubit Bit.

This requires an additional shape argument.

transform_generic_shape :: (QCData x, QCData y, QCurry qfun x y, Curry qfun' x' (m y'), Curry qfun'' x qfun', x' ~ QCType a b x, y' ~ QCType a b y, Monad m) => Transformer m a b -> qfun -> qfun''Source

Like transform_generic, but applies to arbitrary transformers of type

 Transformer m a b

instead of the special case

 Transformer Circ Qubit Bit.

This requires an additional shape argument.

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

 transform_generic :: (QCData x) => Transformer m a b -> Circ x -> m (QCData a b x)
 transform_generic :: (QCData x, QCData y) => Transformer m a b -> (x -> Circ y) -> x -> (QCData a b x -> m (QCData a b y))
 transform_generic :: (QCData x, QCData y, QCData z) => Transformer m a b -> (x -> y -> Circ z) -> x -> y -> (QCData a b x -> QCData a b y -> m (QCData a b z))

and so forth.

Generic block structure

with_ancilla_init :: QShape a qa ca => a -> (qa -> Circ b) -> Circ bSource

Execute a block with local ancillas. Opens a block, initializing an ancilla with a specified classical value, and terminates it with the same value when the block closes. Note: it is the programmer's responsibility to return the ancilla to its original state at the end of the enclosed block. Usage:

 with_ancilla_init True $ \a -> do {
   <<<code block using ancilla a initialized to True>>>
 }
 with_ancilla_init [True,False,True] $ \a -> do {
   <<<code block using list of ancillas a initialized to [True,False,True]>>>
 }

with_ancilla_list :: Int -> (Qulist -> Circ a) -> Circ aSource

Like with_ancilla, but creates a list of n ancillas, all initialized to |0〉. Usage:

 with_ancilla_list n $ \a -> do {
   <<<code block using list of ancillas a>>>
 }

with_computed_fun :: (QCData x, QCData y) => x -> (x -> Circ y) -> (y -> Circ (y, b)) -> Circ (x, b)Source

with_computed_fun x f g: computes x' := f(x); then computes g(x'), which should be organized as a pair (x',y); then uncomputes x' back to x, and returns (x,y).

Important subtlety in usage: all quantum data referenced in f, even as controls, must be explicitly bound and returned by f, or the reversing may rebind it incorrectly. g, on the other hand, can safely refer to anything that is in scope outside the with_computed_fun.

with_computed :: QCData x => Circ x -> (x -> Circ b) -> Circ bSource

with_computed computation code: performs computation (with result x), then performs code x, and finally performs the reverse of computation, for example like this:

[image with_computed.png]

Both computation and code may refer to any qubits that exist in the current environment, and they may also create new qubits. computation may produce arbitrary garbage in addition to its output.

This is a very general but relatively unsafe operation. It is the user's responsibility to ensure that the computation can indeed be undone. In particular, if computation contains any initializations, then code must ensure that the corresponding assertions will be satisfied in computation[sup −1].

Related more specialized, but potentially safer, operations are:

  • with_basis_change, which is like with_computed, but assumes that computation is unitary, and
  • classical_to_reversible, which assumes that computation is classical (or pseudo-classical), and code is a simple copy-by-controlled-not operation.

with_basis_change :: Circ () -> Circ b -> Circ bSource

with_basis_change basischange code: performs a basis change, then the code, then the inverse of the basis change. Both basischange and code are in imperative style. It is the user's responsibility to ensure that the image of code is contained in the image of basischange, or else there will be unmet assertions or runtime errors. Usage:

 with_basis_change basischange $ do
   <<<code>>>

 where
   basischange = do
     <<<gates>>>

with_classical_control :: QCData qa => Bit -> String -> qa -> (qa -> Circ qa) -> Circ qaSource

Classical control on a function with same shape of input and output: if the control bit is true the function is fired, otherwise the identity map is used. Note: the constraint on the types is dynamically checked.

Boxed subcircuits

provide_subroutine_generic :: (QCData x, QCData y) => ErrMsg -> BoxId -> Bool -> (x -> Circ y) -> x -> Circ ()Source

Bind a name to a function as a subroutine in the current namespace. This requires a shape argument, as well as complete parameters, so that it is uniquely determined which actual circuit will be the subroutine. It is an error to call that subroutine later with a different shape argument. It is therefore the user's responsibility to ensure that the name is unique to the subroutine, parameters, and shape.

This function does nothing if the name already exists in the namespace; in particular, it does not check whether the given function is equal to the stored subroutine.

box :: (QCData qa, QCData qb, QCurry qa_qb qa qb) => String -> qa_qb -> qa_qbSource

A generic interface for wrapping a circuit-generating function into a boxed and named subroutine. This takes a name and a circuit-generating function, and returns a new circuit-generating function of the same type, but which inserts a boxed subroutine instead of the actual body of the subroutine.

It is intended to be used like this:

 somefunc :: Qubit -> Circ Qubit
 somefunc a = do ...
 
 somefunc_boxed :: Qubit -> Circ Qubit
 somefunc_boxed = box "somefunc" somefunc

Here, the type of somefunc is just an example; this could indeed be a function with any number and type of arguments, as long as the arguments and return type are quantum data.

It is also possible to inline the box operator directly, in which case it should be done like this:

 somefunc :: Qubit -> Circ Qubit
 somefunc = box "somefunc" $ \a -> do ...

Note: The box operator wraps around a complete function, including all of its arguments. It would be incorrect to apply the box operator after some quantum variables have already been defined. Thus, the following is incorrect:

 incorrect_somefunc :: Qubit -> Circ Qubit
 incorrect_somefunc a = box "somefunc" $ do ...

It is the user's responsibility not to use the same name for different subroutines. If box is called more than once with the same name and shape of input, Quipper assumes, without checking, that they are subsequent calls to the same subroutine.

The type of the box operator is overloaded and quite difficult to read. It can have for example the following types:

 box :: String -> (Qubit -> Circ Qubit) -> (Qubit -> Circ Qubit)
 box :: String -> (QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt)) -> (QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt))

nbox :: QCData qa => String -> Integer -> (qa -> Circ qa) -> qa -> Circ qaSource

A version of box with iteration. The second argument is an iteration count.

This can only be applied to functions of a single argument, where the input and output types are the same.

box_loopM :: (Integral int, QCData qa) => String -> int -> qa -> (qa -> Circ qa) -> Circ qaSource

A version of nbox with same type as loopM.

loopM_boxed_if :: (Integral int, QCData qa) => Bool -> String -> int -> qa -> (qa -> Circ qa) -> Circ qaSource

A version of loopM that will be boxed conditionally on a boolean condition. Typical usage:

 loopM_boxed_if (s > 1) "name" s x $ \x -> do
   <<<body>>>
   return x

inline_subroutine :: BoxId -> Namespace -> [Endpoint] -> Circ [Endpoint]Source

Like call_subroutine, except inline the subroutine body from the given namespace, instead of inserting a subroutine call.

Implementation note: this currently copies all subroutine definitions from the given namespace into the current namespace, and not just the ones used by the current subroutine.

Implementation note: this currently only works on lists of endpoints.