quipper-libraries (empty) → 0.9.0.0
raw patch · 135 files changed
+22007/−0 lines, 135 filesdep +QuickCheckdep +basedep +containersbuild-type:Customsetup-changedbinary-added
Dependencies added: QuickCheck, base, containers, deepseq, mtl, newsynth, quipper-language, quipper-utils, random
Files
- COPYRIGHT +66/−0
- ChangeLog +76/−0
- Quipper/Libraries/Arith.hs +1756/−0
- Quipper/Libraries/ClassicalOptim.hs +35/−0
- Quipper/Libraries/ClassicalOptim/AlgExp.hs +163/−0
- Quipper/Libraries/ClassicalOptim/Circuit.hs +128/−0
- Quipper/Libraries/ClassicalOptim/QuickCheck.hs +158/−0
- Quipper/Libraries/ClassicalOptim/QuickCheckAlgExp.hs +64/−0
- Quipper/Libraries/ClassicalOptim/QuickCheckArith.hs +194/−0
- Quipper/Libraries/ClassicalOptim/QuickCheckMain.hs +23/−0
- Quipper/Libraries/ClassicalOptim/QuipperInterface.hs +171/−0
- Quipper/Libraries/ClassicalOptim/Simplification.hs +1003/−0
- Quipper/Libraries/Decompose.hs +20/−0
- Quipper/Libraries/Decompose/CliffordT.hs +1067/−0
- Quipper/Libraries/Decompose/GateBase.hs +127/−0
- Quipper/Libraries/Decompose/Legacy.hs +240/−0
- Quipper/Libraries/DynamicLiftings.hs +653/−0
- Quipper/Libraries/FPReal.hs +718/−0
- Quipper/Libraries/GateDecompositions.hs +645/−0
- Quipper/Libraries/QFT.hs +107/−0
- Quipper/Libraries/QFTAdd.hs +48/−0
- Quipper/Libraries/Qram.hs +65/−0
- Quipper/Libraries/QuantumIf.hs +126/−0
- Quipper/Libraries/QuipperASCIIParser.hs +57/−0
- Quipper/Libraries/QuipperASCIIParser/ASCIICirc.hs +334/−0
- Quipper/Libraries/QuipperASCIIParser/CircInfo.hs +236/−0
- Quipper/Libraries/QuipperASCIIParser/Parse.hs +674/−0
- Quipper/Libraries/Qureg.hs +264/−0
- Quipper/Libraries/Simulation.hs +36/−0
- Quipper/Libraries/Simulation/ClassicalSimulation.hs +211/−0
- Quipper/Libraries/Simulation/CliffordSimulation.hs +255/−0
- Quipper/Libraries/Simulation/QuantumSimulation.hs +850/−0
- Quipper/Libraries/Synthesis.hs +502/−0
- Quipper/Libraries/Unboxing.hs +90/−0
- Setup.hs +16/−0
- dist/build/Quipper/Libraries/Arith.hs +1760/−0
- dist/build/Quipper/Libraries/ClassicalOptim.hs +39/−0
- dist/build/Quipper/Libraries/ClassicalOptim/AlgExp.hs +167/−0
- dist/build/Quipper/Libraries/ClassicalOptim/Circuit.hs +132/−0
- dist/build/Quipper/Libraries/ClassicalOptim/QuipperInterface.hs +175/−0
- dist/build/Quipper/Libraries/ClassicalOptim/Simplification.hs +1007/−0
- dist/build/Quipper/Libraries/Decompose.hs +24/−0
- dist/build/Quipper/Libraries/Decompose/CliffordT.hs +1071/−0
- dist/build/Quipper/Libraries/Decompose/GateBase.hs +131/−0
- dist/build/Quipper/Libraries/Decompose/Legacy.hs +244/−0
- dist/build/Quipper/Libraries/DynamicLiftings.hs +657/−0
- dist/build/Quipper/Libraries/FPReal.hs +722/−0
- dist/build/Quipper/Libraries/GateDecompositions.hs +649/−0
- dist/build/Quipper/Libraries/QFT.hs +111/−0
- dist/build/Quipper/Libraries/QFTAdd.hs +52/−0
- dist/build/Quipper/Libraries/Qram.hs +69/−0
- dist/build/Quipper/Libraries/QuantumIf.hs +130/−0
- dist/build/Quipper/Libraries/QuipperASCIIParser.hs +61/−0
- dist/build/Quipper/Libraries/QuipperASCIIParser/ASCIICirc.hs +338/−0
- dist/build/Quipper/Libraries/QuipperASCIIParser/CircInfo.hs +240/−0
- dist/build/Quipper/Libraries/QuipperASCIIParser/Parse.hs +678/−0
- dist/build/Quipper/Libraries/Qureg.hs +268/−0
- dist/build/Quipper/Libraries/Simulation.hs +40/−0
- dist/build/Quipper/Libraries/Simulation/ClassicalSimulation.hs +215/−0
- dist/build/Quipper/Libraries/Simulation/CliffordSimulation.hs +259/−0
- dist/build/Quipper/Libraries/Simulation/QuantumSimulation.hs +854/−0
- dist/build/Quipper/Libraries/Synthesis.hs +506/−0
- dist/build/Quipper/Libraries/Unboxing.hs +94/−0
- images/CosetState.png binary
- images/E.png binary
- images/ERot_phase.png binary
- images/ERot_zx.png binary
- images/G.png binary
- images/GateR.png binary
- images/Rz.png binary
- images/T.png binary
- images/V.png binary
- images/W.png binary
- images/Y.png binary
- images/adder-optimized.png binary
- images/adder-unoptimized.png binary
- images/b0-template.png binary
- images/b1-template.png binary
- images/b2-orthodox.png binary
- images/b2-template.png binary
- images/b3-template.png binary
- images/b4-template.png binary
- images/cH_AMMR.png binary
- images/ccZ_AMMR.png binary
- images/ccZ_S.png binary
- images/cc_iX.png binary
- images/cc_iX_S.png binary
- images/cc_iX_simple.png binary
- images/contfrac.png binary
- images/controlled-phase-decomp.png binary
- images/controlled-zrot.png binary
- images/controlled_E.png binary
- images/controlled_S.png binary
- images/controlled_T.png binary
- images/controlled_V.png binary
- images/controlled_W.png binary
- images/controlled_YY.png binary
- images/controlled_iX.png binary
- images/controlled_phase.png binary
- images/coord.png binary
- images/dashed-cnot-bwd.png binary
- images/dashed-cnot-fwd.png binary
- images/decompose2Controls.png binary
- images/decomposeControls.png binary
- images/def_f.png binary
- images/def_g.png binary
- images/def_g1.png binary
- images/def_h.png binary
- images/expDA.png binary
- images/floor.png binary
- images/floor2.png binary
- images/fredkin.png binary
- images/gate_W_CliffordT.png binary
- images/hex-coord.png binary
- images/hex1.png binary
- images/hex2.png binary
- images/iX.png binary
- images/multi_ciX_noancilla.png binary
- images/multi_cnot_barenco.png binary
- images/phase.png binary
- images/quantum_and.png binary
- images/quantum_and_iX.png binary
- images/rGate.png binary
- images/spin1.png binary
- images/spin2.png binary
- images/toffoli_AMMR.png binary
- images/toffoli_NC.png binary
- images/toffoli_S.png binary
- images/toffoli_V.png binary
- images/weldedtree.png binary
- images/with_combined_controls1.png binary
- images/with_combined_controls2.png binary
- images/with_computed.png binary
- images/with_signed_qubit.png binary
- quipper-libraries.cabal +136/−0
@@ -0,0 +1,66 @@+Contributors are listed here, in alphabetical order by last name.+Unless otherwise noted, the copyright for his or her contributions+rests with each individual author. For contributions by authors whose+name is marked (ACS), the copyright rests with Applied Communication+Sciences.++Copyright (C) 2011-2019. All rights reserved.+Copyright (C) 2012-2013 Applied Communication Sciences. All rights+reserved.++Richard Eisenberg+Alexander S. Green+Peter LeFanu Lumsdaine+Keith Kim (ACS)+Siun-Chuon Mau (ACS)+Baranidharan Mohan+Won Ng (ACS)+Joel Ravelomanantsoa-Ratsimihah+Neil J. Ross+Artur Scherer (ACS)+Peter Selinger+Benoît Valiron+Alexandr Virodov (ACS)+Stephan A. Zdancewic++This research was supported by the Intelligence Advanced Research+Projects Activity (IARPA) via Department of Interior National Business+Center contract numbers D11PC20168 and D12PC00527. The U.S. Government+is authorized to reproduce and distribute reprints for Governmental+purposes notwithstanding any copyright annotation thereon. Disclaimer:+The views and conclusions contained herein are those of the authors+and should not be interpreted as necessarily representing the official+policies or endorsements, either expressed or implied, of IARPA,+DoI/NBC, or the U.S. Government.++----------------------------------------------------------------------+LICENSE++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the+ distribution.++3. The name of the authors and copyright holders may not be used to+ endorse or promote products derived from this software without+ specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.+----------------------------------------------------------------------
@@ -0,0 +1,76 @@+December 29, 2019: Release 0.9.0.0++ * Overhauled module structure:+ + Old: New:+ ==== ====+ + Quipper.XYZ Quipper.Internal.XYZ+ QuipperLib.XYZ Quipper.Libraries.XYZ+ Libraries.XYZ Quipper.Utils.XYZ+ Algorithms.XYZ Quipper.Algorithms.XYZ+ tests Quipper.Demos+ Programs Quipper.Programs++ * Re-packaged Quipper as Cabal packages. Added executables quipper,+ quipper-pp, quipperi, quipperdoc in lieu of shell scripts.+ * Moved PDF Previewer to a separate library in Quipper.Utils.Preview+ * Added a MonadFail instance to Circ, to keep ghc >= 7.4 happy+ * Use type class synonyms to avoid warnings about simplifiable class.+ * Compatibility: removed obsolete functoin Map.insertWith'.+ * Added MultiControlledNot demo.+ * Removed dependency on set-monad, which is broken upstream.+ * Fixed some bugs in the stabilizer simulation.+ * Moved QuantumIf from BF to Libraries.+ * Added --help option to all Quipper tools.+ * Removed Quipper.Utils.ShowAll+ * Fixed some compiler errors and removed some unnecessary type class+ assumptions.++July 27, 2016: Release 0.8++ * Portability: compatibility fixes for GHC 8.0. Note: GHC 7.10 is too+ broken and will not be supported by Quipper.+ * Added tests/SimulationTest+ * Added QPrep and QUnPrep to the simulator++October 14, 2014: Release 0.7++ * Portability: compatibility fixes for GHC 7.8.++January 16, 2014: Release 0.6++ * Minor edits and documentation updates.+ * Added a new gate gate_iX_inv+ * Added "alternate" version of synthesis algorithm, using only+ generators of determinant 1 if possible.+ * Synthesis code is now in an external package "newsynth".+ * Rendering code is now in an external package "easyrender".+ * Updated for use with fixedprec-0.2.1.0.++September 2, 2013: Release 0.5++ * Portability: compatibility fixes for GHC 7.6.2.+ * Portability: fixed Windows incompatibility bug. Handling of Ctrl-C+ may or may not work on Windows, depending on console.+ * Added quipperi script, analogous to ghci.+ * New library QuipperLib.ClassicalOptim: algebraic optimization of+ auto-generated classical circuits. Added "optimized" oracle to BWT+ algorithm.+ * QuipperLib.Decompose: Added decomposition into a "standard" gate+ set, consisting of X, Y, Z, H, S, S-dagger, T, T-dagger, and CNOT.+ Added KeepPhase flag to some transformers.+ * QuipperLib.GateDecompositions: added more gates.+ * New library Libraries.Synthesis.RotationDecomposition: implements a+ variant of the algorithm from Nielsen and Chuang to decompose an+ nxn unitary operator into one- and two-level rotations.+ * New library QuipperLib.Unboxing: unboxing transformers.+ * Updated ASCII output format; improved circuit parser efficiency.+ * Miscellaneous bug fixes: malformed W-gates, qdata_of_qubits,+ floorlog.+ * Fixed handling of iterated subroutines in depth transformer.+ * Documentation updates and minor refactoring.++June 19, 2013: Release 0.4++ * First public release.
@@ -0,0 +1,1756 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}++{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This library provides a type of quantum integers, as well as+-- basic arithmetic functions on them.+-- +-- The type 'QDInt' holds a fixed-size, ℓ-qubit quantum integer,+-- considered modulo 2[sup ℓ]. The integers may be regarded as+-- signed or unsigned, depending on the operation. If signs are used,+-- they are assumed to be in two's complement.+-- +-- Some of the arithmetic operations are adapted from the GFI for the+-- Triangle Finding algorithm. Most algorithms used are, for now, very+-- naïve (ripple adders, etc). Gate count estimates are given in the+-- Toffoli gatebase.++-- Documentation note: the table of contents is organized slightly+-- differently than the source code. The guiding idea is that the+-- documentation should expose the interface that is accessible to+-- user-level code, and changeable implementation details are hidden.++module Quipper.Libraries.Arith (+ -- * Quantum integers+ -- ** Data type definitions+ -- $DATATYPES+ XInt, -- constructors not exported+ QDInt,+ CInt,+ IntM,+ -- ** Operations on QDInt+ qulist_of_qdint_bh,+ qdint_of_qulist_bh,+ qulist_of_qdint_lh,+ qdint_of_qulist_lh,+ qdint_length,+ qdint_extend_unsigned,+ qdint_extend_signed,+ -- ** Operations on CInt+ bitlist_of_cint_bh,+ cint_of_bitlist_bh,+ bitlist_of_cint_lh,+ cint_of_bitlist_lh,+ cint_length,+ cint_extend_unsigned,+ cint_extend_signed,+ -- ** Operations on IntM+ -- $INTMCLASSES+ boollist_of_intm_bh,+ intm_of_boollist_bh,+ intm_length,+ integer_of_intm_unsigned,+ integer_of_intm_signed,+ intm_with_length,+ intm_of_integer,+ intm,+ intm_promote,+ intm_interval_signed,+ intm_interval_unsigned,+ intm_extend_unsigned,+ intm_extend_signed,+ -- ** Shape parameters+ qdint_shape,+ cint_shape,+ -- ** Operations on XInt+ xint_maybe_length,+ list_of_xint_bh,+ xint_of_list_bh,+ list_of_xint_lh,+ xint_of_list_lh,+ -- * Quantum arithmetic operations+ -- ** The QNum type class+ QNum(..),+ -- ** In-place increment and decrement+ q_increment,+ q_decrement,+ -- ** In-place addition and subtraction+ q_add_in_place,+ q_sub_in_place,+ q_negate_in_place,+ -- ** Arithmetic with classical parameter+ q_add_param,+ q_sub_param,+ q_add_param_in_place,+ q_sub_param_in_place,+ q_mult_param,+ -- ** Comparison+ q_le_unsigned,+ q_le_signed,+ q_lt_signed,+ q_negative,+ -- ** Division and remainder+ q_moddiv_unsigned_in_place,+ q_mod_unsigned,+ q_divrem_unsigned,+ q_div_unsigned,+ q_div,+ q_quot,+ q_div_exact_unsigned,+ q_div_exact,+ -- ** Specialized functions+ q_ext_euclid,+ -- * Lifting of arithmetic functions+ -- $LIFTING+ template_symb_plus_,+ ) where++import Quipper+import Quipper.Internal++import Quipper.Utils.Sampling+import Quipper.Utils.Auxiliary++import Control.Monad+import Data.Typeable++-- ======================================================================+-- * Quantum integers++-- ** Data type definitions++-- $DATATYPES +-- We define three versions of the fixed-length integer type: quantum,+-- classical input, and classical parameter. The triple ('IntM',+-- 'QDInt', 'CInt') forms an instance of the 'QShape' class. All three+-- types are special cases of the type 'XInt' /x/.++-- | 'XInt' /x/ is the type of fixed-length integers, but using+-- elements of type /x/ instead of bits. It is an abstract type, and+-- details of its implementation is not exposed to user-level code.++-- Implemenation notes: The integer types are currently implemented as+-- big-headian bit lists. However, the details of the implementation+-- are not exposed to user-level code, and are subject to change.+-- +-- The form ('XInt_indet' /n/ /id/) is permitted as a special case, to+-- represent an integer of indeterminate length. Such a value can only+-- be used when the length is deducible from the context. This form is+-- only allowed in the special case /x/ = 'Bool', and we use an+-- identity type to enforce this.+-- +-- Integers of indeterminate length may only be used in certain+-- operations where the shape information is available from other+-- data. It can be used, for example, for terminating or controlling a+-- 'QDInt', but not for initializing a 'QDInt'.+data XInt x = XInt [x] | XInt_indet Integer (Identity Bool x)+ deriving (Show, Typeable)++-- | The type of fixed-length /m/-qubit quantum integers. This is a+-- circuit execution time value.+type QDInt = XInt Qubit++instance Show QDInt where+ show (XInt l) = "#" ++ show l+ show (XInt_indet n id) = error "IntM: internal error"++-- A better implementation might be something like:+-- show (XInt l) = "qdint_of_qulist_bh " ++ show l+-- However, as 'CInt' and 'QDInt' are currently synonyms, it seems best+-- to keep the instance agnostic between them, while still distinguishing+-- them somehow from naked lists.++-- | The type of fixed-length /m/-bit classical integer inputs. This+-- is a circuit execution time value.+type CInt = XInt Bit++-- Currently, 'CInt' is literally a synonym of 'QDInt', since 'Qubit' is+-- a synonym of 'Bit'. If this changes, the following instance will be+-- required:+--+-- instance Show CInt where+-- show (XInt l) = show l+-- show (XInt_indet n id) = error "IntM: internal error"++-- | The type of fixed-length /m/-bit integer parameters. Values of+-- this type are parameters, i.e., they are classical and known at+-- circuit generation time.+-- +-- Unlike values of type 'QDInt' and 'CInt', a value of type 'IntM'+-- may have an indeterminate length. This happens, for example, if the+-- value is specified by means of an integer literal (e.g., 17), which+-- does not carry length information. In such cases, the value can+-- only be used when it can be deduced from the context. For example,+-- such values may be used for terminating or controlling a 'QDInt',+-- but not for initializing a 'QDInt'.+type IntM = XInt Bool++instance Show IntM where+ show (XInt l) = "intm " ++ show (length l) ++ " " ++ show (int_of_boollist_signed_bh l)+ show (XInt_indet n id) = show n++-- Note: for the purpose of forming intervals, we regard 'IntM' as an+-- /unsigned/ type. This disagrees with the 'Enum' instance, where+-- 'IntM' is regarded as a /signed/ type. So for example, when /x/ =+-- 'intm' 4 (-2) = 'intm' 4 14 and /y/ = 'intm' 4 10, then [/x/../y/]+-- and 'interval' /y/ /x/ are non-empty, but [/y/../x/] and 'interval'+-- /x/ /y/ are empty. +-- +-- This is confusing but for the time being there is no better+-- alternative, unless we create distinct signed and unsigned types.++instance Interval IntM where+ interval x y = intm_interval_unsigned x y++instance Zero IntM where+ zero x = intm_with_length (intm_length x) 0+ +-- ----------------------------------------------------------------------+-- ** Primitive combinators on XInt++-- $ 'XInt' is intended to be an abstract data type, and all access to+-- it should pass through the three access functions of this+-- section.++-- *** Constructors++-- | Create a 'XInt' /x/ from a list of /x/s. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+xint_of_list_bh :: [x] -> XInt x+xint_of_list_bh xs = XInt xs++-- | Create an 'IntM' of indeterminate length from an 'Integer'. +intm_of_integer :: Integer -> IntM+intm_of_integer n = XInt_indet n reflexivity++-- *** Destructor++-- | If the 'XInt' is of determinate length, return its list of digits+-- as a big-headian list, i.e., the head of the list holds the most+-- significant digit. If the 'XInt' is of indeterminate length, return+-- (/n/, /id/), where /n/ is the underlying 'Integer' and /id/ is the+-- witness proving that /x/ = 'Bool'.+-- +-- This is a lowest-level access function not intended to be used by+-- user-level code.+xint_case :: XInt x -> Either [x] (Integer, Identity Bool x)+xint_case (XInt xs) = Left xs+xint_case (XInt_indet n id) = Right (n, id)++-- ----------------------------------------------------------------------+-- ** Other low-level operations++-- $ The operations in this section are the only ones intended to use+-- 'xint_case' directly.++-- | Set the length of an 'XInt' to /m/ ≥ 0. This operation is only+-- legal if the input (a) has indeterminate length or (b) has+-- determinate length already equal to /m/. In particular, it cannot+-- be used to change the length from anything other than from+-- indeterminate to determinate.+-- +-- If both arguments already have determinate lengths, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.+-- +-- Note that if /x/ ≠ 'Bool', the input is guaranteed to have+-- determinate length. However, we cannot test for equality of types+-- in a polymorphic function. This is where the 'id' argument to+-- 'XInt_indet' is used.+xint_set_length :: Int -> XInt x -> String -> XInt x+xint_set_length m x errmsg | m < 0 = + error "xint_set_length: negative length not permitted" +xint_set_length m x errmsg =+ case xint_case x of+ Left xs | m == length xs -> x+ | otherwise -> error errmsg+ Right (n, id) -> XInt xs where+ xs = [ identity id b | b <- boollist_of_int_bh m n ]++-- | Return 'True' if the 'XInt' is of determinate length, and 'False'+-- if it is of indeterminate length. +xint_is_determinate :: XInt x -> Bool+xint_is_determinate x = + case xint_case x of+ Left _ -> True+ Right _ -> False++-- | From a 'XInt', which must be of determinate length, extract a+-- list of /x/s. The conversion is big-headian, i.e., the head of the+-- list holds the most significant digit. It is an error to call this+-- function with an 'XInt' of indeterminate length.+list_of_xint_bh :: XInt x -> [x]+list_of_xint_bh x = + case xint_case x of+ Left xs -> xs+ Right _ -> error "list_of_xint_bh: integer has indeterminate length"++-- | Return the size of a 'XInt', or 'Nothing' if indeterminate.+xint_maybe_length :: XInt x -> Maybe Int+xint_maybe_length x =+ case xint_case x of+ Left xs -> Just (length xs)+ Right _ -> Nothing++-- | Convert an 'IntM' of length /m/ to an 'Integer' in the range {0,+-- …, 2[sup /m/]-1}. If the 'IntM' has indeterminate length, return the+-- original 'Integer'.+integer_of_intm_unsigned :: IntM -> Integer+integer_of_intm_unsigned x = + case xint_case x of+ Left xs -> int_of_boollist_unsigned_bh xs+ Right (n, id) -> n++-- | Convert an 'IntM' of length /m/ to an 'Integer' in the range+-- {-2[sup /m/-1], …, 2[sup /m/-1]-1}. If the 'IntM' has indeterminate+-- length, return the original 'Integer'.+integer_of_intm_signed :: IntM -> Integer+integer_of_intm_signed x =+ case xint_case x of+ Left xs -> int_of_boollist_signed_bh xs+ Right (n, id) -> n++-- | Equality test. If at least one argument has determinate length,+-- test equality modulo 2[sup /m/]. If both have indeterminate length,+-- check equality of the underlying integers.+xint_equals :: (Eq x) => XInt x -> XInt x -> Bool +xint_equals x y =+ case (xint_case x, xint_case y) of+ (Left xs, Left ys) + | length xs == length ys -> xs == ys+ | otherwise -> error "Equality test on XInt: operands must be of equal length"+ (_, Left ys) -> xint_equals (xint_set_length m x "xint_equals") y+ where m = length ys+ (Left xs, _) -> xint_equals x (xint_set_length m y "xint_equals")+ where m = length xs+ (Right (n, _), Right (n', _)) -> n == n'+ +-- ----------------------------------------------------------------------+-- ** Derived operations on XInt++-- | Get the nominal length of an integer (in bits). It is an error to+-- apply this function to an integer of indeterminate length.+xint_length :: XInt x -> Int+xint_length x = + case xint_maybe_length x of+ Just m -> m+ Nothing -> error "xint_length: integer has indeterminate length"++-- | Convert an integer to a bit list. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+list_of_xint_lh :: XInt x -> [x]+list_of_xint_lh = reverse . list_of_xint_bh++-- | Convert a bit list to an integer. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+xint_of_list_lh :: [x] -> XInt x+xint_of_list_lh = xint_of_list_bh . reverse++-- | Extend a 'XInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to /zero/. It is an error to call this+-- function when the new length is shorter than the old one.+xint_extend_unsigned :: (Monad m) => Int -> m x -> XInt x -> m (XInt x)+xint_extend_unsigned len zero x + | len < m =+ error "pad_xint: requested length is shorter than current length"+ | otherwise = do+ pad <- sequence (replicate extra zero)+ return $ xint_of_list_bh (pad ++ digits)+ where+ digits = list_of_xint_bh x+ m = length digits+ extra = len - m++-- | Extend a 'XInt' to the given length without changing its (signed)+-- value. This is done by adding the required number of high bits+-- initialized to copies of the sign bit (or to /zero/ if the original+-- integer was of length 0). It is an error to call this function when+-- the new length is shorter than the old one.+xint_extend_signed :: (Monad m) => Int -> m x -> (x -> m x) -> XInt x -> m (XInt x)+xint_extend_signed len zero copy x+ | len < m + = error "pad_xint: requested length is shorter than current length"+ | m == 0+ = xint_extend_unsigned len zero x+ | otherwise = do+ pad <- sequence (replicate extra (copy sign))+ return $ xint_of_list_bh (pad ++ digits)+ where+ digits = list_of_xint_bh x+ m = length digits+ extra = len - m+ sign = head digits++-- ----------------------------------------------------------------------+-- ** Operations on IntM++-- | Return the size of an 'IntM', or 'Nothing' if indeterminate.+intm_length :: IntM -> Maybe Int+intm_length = xint_maybe_length+ +-- | Convert an 'IntM' to a list of booleans. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit. As usual, 'False' is 0 and 'True' is 1. It is an error to+-- apply this operation to an 'IntM' whose length is indeterminate.+boollist_of_intm_bh :: IntM -> [Bool]+boollist_of_intm_bh = list_of_xint_bh++-- | Convert a boolean list to an 'IntM'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+intm_of_boollist_bh :: [Bool] -> IntM+intm_of_boollist_bh = xint_of_list_bh++-- | Create an 'IntM' of the specified length (first argument) and+-- value (second argument).+intm :: Int -> Integer -> IntM+intm m n = intm_set_length m (intm_of_integer n) "intm: internal error"++-- | Set the length of an 'IntM' to /m/ ≥ 0. This operation is only+-- legal if the input (a) has indeterminate length or (b) has+-- determinate length already equal to /m/. In particular, it cannot+-- be used to change the length from anything other than from+-- indeterminate to determinate. (Use 'intm_extend_unsigned' or+-- 'intm_extend_signed' to increase a determinate length).+-- +-- If both arguments already have determinate lengths, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.+intm_set_length :: Int -> IntM -> String -> IntM+intm_set_length = xint_set_length++-- | Extend an 'IntM' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+intm_extend_unsigned :: Int -> IntM -> IntM+intm_extend_unsigned len x =+ getId $ xint_extend_unsigned len (return False) x++-- | Extend an 'IntM' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+intm_extend_signed :: Int -> IntM -> IntM+intm_extend_signed len x =+ getId $ xint_extend_signed len (return False) (return) x++-- ----------------------------------------------------------------------+-- ** Operations on CInt++-- | Convert a 'CInt' to a list of qubits. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+bitlist_of_cint_bh :: CInt -> [Bit]+bitlist_of_cint_bh = list_of_xint_bh++-- | Convert a list of qubits to a 'CInt'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+cint_of_bitlist_bh :: [Bit] -> CInt+cint_of_bitlist_bh = xint_of_list_bh++-- | Convert a 'CInt' to a list of bits. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+bitlist_of_cint_lh :: CInt -> [Bit]+bitlist_of_cint_lh = list_of_xint_lh++-- | Convert a list of bits to a 'CInt'. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+cint_of_bitlist_lh :: [Bit] -> CInt+cint_of_bitlist_lh = xint_of_list_lh++-- | Return the length of a 'CInt', in bits.+cint_length :: CInt -> Int+cint_length = xint_length++-- | Extend a 'CInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+cint_extend_unsigned :: Int -> CInt -> Circ CInt+cint_extend_unsigned len x =+ xint_extend_unsigned len (cinit False) x++-- | Extend a 'CInt' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+cint_extend_signed :: Int -> CInt -> Circ CInt+cint_extend_signed len x =+ xint_extend_signed len (cinit False) (qc_copy) x++-- ----------------------------------------------------------------------+-- ** Operations on QDInt++-- | Convert a 'QDInt' to a list of qubits. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+qulist_of_qdint_bh :: QDInt -> [Qubit]+qulist_of_qdint_bh = list_of_xint_bh++-- | Convert a list of qubits to a 'QDInt'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+qdint_of_qulist_bh :: [Qubit] -> QDInt+qdint_of_qulist_bh = xint_of_list_bh++-- | Convert a 'QDInt' to a list of qubits. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+qulist_of_qdint_lh :: QDInt -> [Qubit]+qulist_of_qdint_lh = list_of_xint_lh++-- | Convert a list of qubits to a 'QDInt'. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+qdint_of_qulist_lh :: [Qubit] -> QDInt+qdint_of_qulist_lh = xint_of_list_lh++-- | Return the length of a 'QDInt', in bits.+qdint_length :: QDInt -> Int+qdint_length = xint_length++-- | Extend a 'QDInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+qdint_extend_unsigned :: Int -> QDInt -> Circ QDInt+qdint_extend_unsigned len x =+ xint_extend_unsigned len (qinit False) x++-- | Extend a 'QDInt' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+qdint_extend_signed :: Int -> QDInt -> Circ QDInt+qdint_extend_signed len x =+ xint_extend_signed len (qinit False) (qc_copy) x++-- ----------------------------------------------------------------------+-- ** Shape parameters++-- | Return a piece of shape data to represent an /m/-qubit quantum+-- integer. Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+qdint_shape :: Int -> QDInt+qdint_shape m = xint_of_list_bh (replicate m qubit)++-- | Return a piece of shape data to represent an /m/-bit 'CInt'.+-- Please note that the data can only be used as shape; it will be+-- undefined at the leaves.+cint_shape :: Int -> CInt+cint_shape m = xint_of_list_bh (replicate m bit)++-- ======================================================================+-- ** Type class instances++-- Note: instance declarations do not show up in the documentation++-- | Input the lengths of two inputs to a binary operation, and return+-- the common length. If one length is indeterminate, return the other;+-- if both are indeterminate, return 'Nothing'; if both are determinate but+-- not equal, throw an error with the given error message.+combine_length :: String -> Maybe Int -> Maybe Int -> Maybe Int+combine_length s Nothing m = m+combine_length s m Nothing = m+combine_length s (Just m) (Just m') | m == m' = Just m+ | otherwise = error s++-- | Try to set the length of an 'IntM' to that of another 'XInt'+-- value (which could be a 'QDInt', a 'CInt', or another 'IntM'). This+-- will fail with an error if both numbers already have determinate+-- lengths that don't coincide. In this case, the string argument is+-- used as an error message.+intm_promote :: IntM -> XInt x -> String -> IntM+intm_promote bi xi errmsg = + case xint_maybe_length xi of+ Nothing -> bi+ Just m -> intm_set_length m bi errmsg+ +type instance QCType x y (XInt z) = XInt (QCType x y z)+type instance QTypeB IntM = QDInt++instance QCLeaf x => QCData (XInt x) where+ qcdata_mapM shape f g xs = + mmap xint_of_list_bh $ qcdata_mapM (list_of_xint_bh shape) f g (list_of_xint_bh xs)+ qcdata_zip shape q c q' c' xs ys e = + xint_of_list_bh $ qcdata_zip (list_of_xint_bh shape) q c q' c' (list_of_xint_bh xs) (list_of_xint_bh ys) (const $ e "XInt length mismatch")+ qcdata_promote b q e = intm_promote b q (e "IntM length mismatch")++-- Labeling of QDInt is s[m-1], ..., s[0], with the least significant+-- bit at index 0.+instance QCLeaf x => Labelable (XInt x) String where+ label_rec qa = label_rec (list_of_xint_lh qa)++instance CircLiftingUnpack (Circ QDInt) (Circ QDInt) where+ pack x = x+ unpack x = x++-- ======================================================================+-- * Classical arithmetic on 'IntM'++-- ----------------------------------------------------------------------+-- ** Auxiliary functions+ +-- | A useful auxiliary function that converts a list of 'IntM's to a+-- list of 'Integer's, while also returning their common length. All+-- arguments whose length is not indeterminate must have the same common+-- length. If the lengths don't match, throw an error with the error+-- message specified by the 'String' argument.+integers_of_intms_signed :: [IntM] -> String -> (Maybe Int, [Integer])+integers_of_intms_signed xs s = (m, is) where+ m = foldl (combine_length s) Nothing [ intm_length x | x <- xs ]+ is = [ integer_of_intm_signed x | x <- xs ] ++-- | Like 'integers_of_intms_signed', but regards the 'IntM's as+-- unsigned integers.+integers_of_intms_unsigned :: [IntM] -> String -> (Maybe Int, [Integer])+integers_of_intms_unsigned xs s = (m, is) where+ m = foldl (combine_length s) Nothing [ intm_length x | x <- xs ]+ is = [ integer_of_intm_unsigned x | x <- xs ] ++-- | Create an 'IntM' of the given length and value. Leave the length+-- indeterminate if it is given as 'Nothing'.+intm_with_length :: Maybe Int -> Integer -> IntM+intm_with_length (Just m) n = intm m n+intm_with_length Nothing n = intm_of_integer n++-- | Auxiliary function for lifting a binary operator from 'Integer'+-- to 'IntM'. The string argument is the name of the operator, for+-- error messages.+intm_binop :: (Integer -> Integer -> Integer) -> String -> IntM -> IntM -> IntM +intm_binop op opname x y = intm_with_length m (op x' y') where+ (m, [x',y']) = integers_of_intms_signed [x, y] ("Binary operation " ++ opname ++ " on IntM: operands must be of equal length")++-- | Auxiliary function for lifting a unary operator from 'Integer' to+-- 'IntM'.+intm_unop :: (Integer -> Integer) -> IntM -> IntM+intm_unop op x = intm_with_length xm (op x') where+ xm = intm_length x+ x' = integer_of_intm_signed x+ +-- ----------------------------------------------------------------------+-- ** Type class instances++-- $INTMCLASSES +-- +-- 'IntM' is an instance of Haskell's 'Eq', 'Num', 'Ord', 'Real',+-- 'Enum', and 'Integral' type classes. This means that integer+-- literals (e.g., 17), and the usual arithmetic functions, such as+-- '+', '-', '*', 'abs', 'succ', 'pred', 'mod', 'div', and others, can+-- be used for values of type 'IntM'. In general, we treat 'IntM' as a+-- signed integer type. Use 'fromIntegral' to convert an integer to an+-- 'IntM' of indeterminate length.+-- +-- The general convention for binary operations (such as+-- multiplication) is: both operands must have the same length,+-- except: if one operand has indeterminate length, it takes on the+-- length of the other; if both operands have indeterminate length, the+-- result will have indeterminate length.++instance Eq x => Eq (XInt x) where+ x == y = xint_equals x y+ +instance Num IntM where+ (+) = intm_binop (+) "+"+ (*) = intm_binop (*) "*"+ (-) = intm_binop (-) "-"+ abs = intm_unop abs+ signum = intm_unop signum+ fromInteger = intm_of_integer++instance Ord IntM where+ compare x y = compare (toInteger x) (toInteger y)++instance Real IntM where+ toRational = toRational . integer_of_intm_signed++instance Enum IntM where+ succ = intm_unop succ+ pred = intm_unop pred+ toEnum = intm_of_integer . fromIntegral+ fromEnum = fromIntegral . integer_of_intm_signed+ enumFrom x = map (intm_with_length m) [x'..] where+ (m, [x']) = integers_of_intms_signed [x] "enumeration: IntM"+ enumFromThen x y = map (intm_with_length m) [x',y'..] where+ (m, [x',y']) = integers_of_intms_signed [x,y] "enumeration: IntM operands must be of equal length"+ enumFromTo x y = map (intm_with_length m) [x'..y'] where+ (m, [x',y']) = integers_of_intms_signed [x,y] "enumeration: IntM operands must be of equal length"+ enumFromThenTo x y z = map (intm_with_length m) [x',y'..z'] where+ (m, [x',y',z']) = integers_of_intms_signed [x,y,z] "enumeration: IntM operands must be of equal length"++-- | Return the interval @[/x/../y/]@, with /x/ and /y/ regarded as+-- signed values of type 'IntM'.+intm_interval_signed :: IntM -> IntM -> [IntM]+intm_interval_signed x y = [x..y] -- this comes from the 'Enum' instance defined above++-- | Return the interval @[/x/../y/]@, with /x/ and /y/ regarded as+-- unsigned values of type 'IntM'.+intm_interval_unsigned :: IntM -> IntM -> [IntM]+intm_interval_unsigned x y = map (intm_with_length m) [x'..y'] where+ (m, [x',y']) = integers_of_intms_unsigned [x,y] "intm_interval: operands must be of equal length"++instance Integral IntM where+ toInteger = integer_of_intm_signed+ quotRem x y = (intm_with_length m q', intm_with_length m r') where+ (m, [x',y']) = integers_of_intms_signed [x, y] "Division on IntM: operands must be of equal length"+ (q',r') = quotRem x' y'++-- ======================================================================+-- * Quantum arithmetic on 'QDInt'++-- Developer note: each quantum arithmetic function is implemented by+-- an underlying low-level function operating on qubit lists. However,+-- these lower-level functions are *not* exported. If any module needs+-- them, it should define them explicitly using 'qulist_of_qdint_bh'+-- and 'qdint_of_qulist_bh'. In particular, the low-level functions+-- may not perform error checking if it is already performed by their+-- high-level counterparts.++-- ----------------------------------------------------------------------+-- ** Auxiliary functions++-- | @'common_value' error_str [n1,n2,n3]@: if /n1/, /n2/, /n3/ all equal, return this value; else throw an error. Useful for checking that sizes of 'QDInt's match.+common_value :: (Eq a) => String -> [a] -> a+common_value _ [] = error "common_value: no inputs given"+common_value _ [n] = n+common_value error_str (n:ns) = if common_value error_str ns == n then n else error error_str++-- | @'common_length' error_str [x1,x2,x3]@: if /x1/, /x2/, /x3/ all have the same length, return this value; else throw an error. +common_length :: String -> [QDInt] -> Int+common_length error_str xs = common_value error_str $ map qdint_length xs++-- ----------------------------------------------------------------------+-- ** Incrementing and decrementing++-- | Increment a 'QDInt' in place. /O/(ℓ) gates. +-- +-- Implementation note: currently tries to minimize gate count, at the cost of a rather long Quipper description. Can the latter be reduced without increasing the former? +q_increment :: QDInt -> Circ QDInt+q_increment = mmap xint_of_list_bh . q_increment_qulist . list_of_xint_bh++-- | Low-level implementation of 'q_increment': represents integers as+-- big-headian qubit lists.+q_increment_qulist :: [Qubit] -> Circ [Qubit]+q_increment_qulist [] = return []+q_increment_qulist [x_0] = do x_0 <- qnot x_0; return [x_0]+q_increment_qulist [x_1,x_0] = do x_0 <- qnot x_0; x_1 <- qnot x_1 `controlled` (x_0 .==. 0); return [x_1,x_0]+q_increment_qulist [x_2,x_1,x_0] = do+ x_0 <- qnot x_0+ x_1 <- qnot x_1 `controlled` (x_0 .==. 0)+ x_2 <- qnot x_2 `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ return [x_2,x_1,x_0]+q_increment_qulist x_bits = do+ let x_0 = last x_bits+ x_1 = last $ init x_bits+ x_higher = init $ init $ x_bits+ x_0 <- qnot x_0+ x_1 <- qnot x_1 `controlled` (x_0 .==. 0)+ (x_higher,x_1,x_0) <- with_ancilla (\c -> do+ c <- qnot c `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ (c,rev_x_higher) <- q_increment_qulist_aux c (reverse x_higher)+ c <- qnot c `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ return (reverse rev_x_higher, x_1, x_0))+ return (x_higher ++ [x_1,x_0])+ where+ -- increment a LITTLE-endian bit-string, controlled by a single qubit.+ q_increment_qulist_aux :: Qubit -> [Qubit] -> Circ (Qubit,[Qubit])+ q_increment_qulist_aux b [] = return (b,[])+ q_increment_qulist_aux b [x_0] =+ do x_0 <- qnot x_0 `controlled` b; return (b,[x_0])+ q_increment_qulist_aux b [x_0,x_1] = do+ x_0 <- qnot x_0 `controlled` b+ x_1 <- qnot x_1 `controlled` b .&&. (x_0 .==. 0)+ return (b,[x_0,x_1])+ q_increment_qulist_aux b (x_0:x_higher) = do+ x_0 <- qnot x_0 `controlled` b + (b,x_0,x_higher) <- with_ancilla (\c -> do+ c <- qnot c `controlled` b .&&. (x_0 .==. 0)+ (c,x_higher) <- q_increment_qulist_aux c x_higher+ c <- qnot c `controlled` b .&&. (x_0 .==. 0)+ return (b,x_0,x_higher))+ return (b, (x_0:x_higher))+ +-- | Decrement a 'QDInt' in place. Inverse of 'q_increment'. /O/(ℓ).+q_decrement :: QDInt -> Circ QDInt+q_decrement = reverse_generic_endo q_increment++-- ----------------------------------------------------------------------+-- ** Addition and subtraction++-- | Add two 'QDInt's into a fresh one. Arguments are assumed to be of equal size. /O/(ℓ) gates, both before and after transformation to Toffoli.+q_add_qdint :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_add_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_add_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_add_qdint': represents integers as+-- big-headian qubit lists.+q_add_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_add_qulist x y = do+ let l = length x+ when (l /= length y) $ do+ error "q_add_qdint: cannot add QDInts of different lengths"++ ((x,y),s_out) <- with_computed_fun (x,y)+ (\(x,y) -> do+ s <- qinit (replicate l False) -- holds the eventual sum+ c <- qinit (replicate l False) -- holds the carries++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ (x,y,s,c) <- loop_with_indexM (l-1) (x,y,s,c) (\j (x,y,s,c) -> do+ let c_j1 = c !! (j+1)+ let s_j = s !! j+ -- If any two of the current bits x_j, y_j, and the carry c_j are true, then set the next carry:+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. y!!j)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. c!!j)+ c_j1 <- qnot c_j1 `controlled` (y!!j .&&. c!!j)+ -- Now the current sum digit is computed mod 2:+ s_j <- qnot s_j `controlled` (x!!j)+ s_j <- qnot s_j `controlled` (y!!j)+ s_j <- qnot s_j `controlled` (c!!j) + c <- return $ overwriteAt (j+1) c_j1 c+ s <- return $ overwriteAt j s_j s++ return (x,y,s,c))++ -- Final sum digit; no carry required:+ let s_l1 = s !! (l-1)+ s_l1 <- qnot s_l1 `controlled` (x!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (y!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (c!!(l-1)) + s <- return $ overwriteAt (l-1) s_l1 s+ + x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ return (x,y,s,c))+ + (\(x,y,s,c) -> do+ (s,s_out) <- qc_copy_fun s+ return ((x,y,s,c), s_out))+ return (x, y, s_out)++-- | Subtract two 'QDInt's, into a fresh one. Arguments are assumed to be of equal size. /O/(ℓ).+q_sub_qdint :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt)+q_sub_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_sub_qulist x' y'+ let x = xint_of_list_bh x' + let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_sub_qdint': represents integers as+-- big-headian qubit lists.+q_sub_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_sub_qulist x y = do+ let l = length x+ when (l /= length y) $ do+ error "q_sub_qdint: cannot subtract QDInts of different lengths"+ + ((x,y),d_out) <- with_computed_fun (x,y)+ (\(x,y) -> do+ d <- qinit (replicate l False) -- holds the eventual difference+ b <- qinit (replicate l False) -- holds the borrows++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ d <- return $ reverse d+ b <- return $ reverse b++ (x,y,d,b) <- loop_with_indexM (l-1) (x,y,d,b) (\j (x,y,d,b) -> do+ let b_j1 = b !! (j+1)+ let d_j = d !! j+ -- If any two of (not x_j), y_j, and the borrow c_j hold, then set the next borrow.+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ b_j1 <- qnot b_j1 `controlled` (x!!j .==. 0) .&&. (y!!j .==. 1)+ b_j1 <- qnot b_j1 `controlled` (x!!j .==. 0) .&&. (b!!j .==. 1)+ b_j1 <- qnot b_j1 `controlled` (y!!j .==. 1) .&&. (b!!j .==. 1)+ -- Now the current difference digit is computed mod 2:+ d_j <- qnot d_j `controlled` (x!!j)+ d_j <- qnot d_j `controlled` (y!!j)+ d_j <- qnot d_j `controlled` (b!!j) + b <- return $ overwriteAt (j+1) b_j1 b+ d <- return $ overwriteAt j d_j d+ return (x,y,d,b))++ -- Final difference digit; no carry required:+ let d_l1 = d !! (l-1)+ d_l1 <- qnot d_l1 `controlled` (x!!(l-1))+ d_l1 <- qnot d_l1 `controlled` (y!!(l-1))+ d_l1 <- qnot d_l1 `controlled` (b!!(l-1)) + d <- return $ overwriteAt (l-1) d_l1 d++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ d <- return $ reverse d+ b <- return $ reverse b++ return (x,y,d,b))+ + (\(x,y,d,b) -> do+ (d,d_out) <- qc_copy_fun d+ return ((x,y,d,b), d_out))+ return (x, y, d_out)++-- | Add one 'QDInt' onto a second, in place; i.e. (/x/,/y/) ↦ (/x/,/x/+/y/). Arguments are assumed to be of equal size. /O/(ℓ) gates.+q_add_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+q_add_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y') <- q_add_in_place_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x, y)++-- | Low-level implementation of 'q_add_in_place': represents integers+-- as big-headian qubit lists.+q_add_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit])+q_add_in_place_qulist [] [] = return ([], [])+q_add_in_place_qulist [x0] [y0] = do+ y0 <- qnot y0 `controlled` x0+ return ([x0], [y0])+q_add_in_place_qulist x y = do+ let (x0:x_higher) = reverse x + (y0:y_higher) = reverse y++ y0 <- qnot y0 `controlled` x0++ ((x0,y0),(x_higher,y_higher)) <- with_computed_fun (x0,y0)+ (\(x0,y0) -> do+ c <- qinit False+ c <- qnot c `controlled` (x0 .==. 1) .&&. (y0 .==. 0)+ return (x0,y0,c))+ (\(x0,y0,c) -> do+ (x_higher,y_higher,c) <- q_add_aux (x_higher) (y_higher) c+ return ((x0,y0,c),(x_higher,y_higher)))+ return (reverse (x0:x_higher), reverse (y0:y_higher))+ where+ -- Aux: add two LITTLE-endian bit strings, and an optional extra 1.+ q_add_aux :: [Qubit] -> [Qubit] -> Qubit -> Circ ([Qubit],[Qubit],Qubit)+ q_add_aux [] [] c = return ([],[],c)+ q_add_aux [x0] [y0] c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ return ([x0],[y0],c)+ q_add_aux (x0:xs) (y0:ys) c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ ((x0,y0,c),(xs,ys)) <- with_computed_fun (x0,y0,c)+ (\(x0,y0,c) -> do+ c' <- qinit False+ c' <- qnot c' `controlled` (x0 .==. 1) .&&. (y0 .==. 0)+ c' <- qnot c' `controlled` (x0 .==. 1) .&&. (c .==. 1)+ c' <- qnot c' `controlled` (y0 .==. 0) .&&. (c .==. 1)+ return (x0,y0,c,c'))+ + (\(x0,y0,c,c') -> do+ (xs,ys,c') <- q_add_aux xs ys c'+ return ((x0,y0,c,c'),(xs,ys)))+ return (x0:xs,y0:ys,c)+ q_add_aux _ _ _ = error "q_add_in_place: cannot add integers of different sizes."++-- | Subtract one 'QDInt' from a second, in place; i.e. (/x/,/y/) ↦ (/x/,/y/–/x/). Arguments are assumed to be of equal size. /O/(ℓ) gates.+q_sub_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+q_sub_in_place x y = reverse_generic_endo (\(x,d) -> q_add_in_place x d) (x,y)++-- | Low-level version of 'q_sub_in_place': represents integers+-- as big-headian qubit lists.+q_sub_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit])+q_sub_in_place_qulist x y = reverse_generic_endo (\(x,d) -> q_add_in_place_qulist x d) (x,y)++-- ----------------------------------------------------------------------+-- ** Arithmetic with parameters++-- | Add a parameter 'IntM' and a 'QDInt', into a fresh 'QDInt': (/x/, /y/) ↦ (/y/, /x/+/y/). The parameter /x/ must be of the same length as /y/, or /x/ can also be of undetermined length. /O/(ℓ).+q_add_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_add_param x1 y = do+ let x = intm_promote x1 y "q_add_param: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ (y', z') <- q_add_param_qulist x' y'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (y, z)+ +-- | Low-level implementation of 'q_add_param': represents integers as+-- big-headian qubit lists. Precondition: /x/ and /y/ have the same length.+q_add_param_qulist :: [Bool] -> [Qubit] -> Circ ([Qubit], [Qubit])+q_add_param_qulist x y = do + -- Implementation note: compare with q_add_qdint. The code is almost verbatim identical, but since x consists of parameter Bools, `controlled` will omit gates when appropriate.++ -- Further slight optimisation would be possible, since if x doesn’t have low bits then no low carry qubits are required. ++ -- Implementation note: once we have a general-purpose classical+ -- circuit optimizer (even a primitive one), then this kind of+ -- source-code level optimization will hopefully become moot.+ + let l = length x++ (y,s_out) <- with_computed_fun y+ (\y -> do+ s <- qinit (replicate l False) -- holds the eventual sum+ c <- qinit (replicate l False) -- holds the carries++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ (y,s,c) <- loop_with_indexM (l-1) (y,s,c) (\j (y,s,c) -> do+ let c_j1 = c !! (j+1)+ let s_j = s !! j+ -- If any two of the current bits x_j, y_j, and the carry c_j are true, then set the next carry:+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. y!!j)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. c!!j)+ c_j1 <- qnot c_j1 `controlled` (y!!j .&&. c!!j)+ -- Now the current sum digit is computed mod 2:+ s_j <- qnot s_j `controlled` (x!!j)+ s_j <- qnot s_j `controlled` (y!!j)+ s_j <- qnot s_j `controlled` (c!!j) + c <- return $ overwriteAt (j+1) c_j1 c+ s <- return $ overwriteAt j s_j s++ return (y,s,c))++ -- Final sum digit; no carry required:+ let s_l1 = s !! (l-1)+ s_l1 <- qnot s_l1 `controlled` (x!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (y!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (c!!(l-1)) + s <- return $ overwriteAt (l-1) s_l1 s+ + x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ return (y,s,c))+ + (\(y,s,c) -> do+ (s,s_out) <- qc_copy_fun s+ return ((y,s,c), s_out))+ return (y, s_out)++-- | Subtract a parameter 'IntM' from a 'QDInt', into a fresh 'QDInt'. The 'IntM' cannot be shorter than the 'QDInt' (that would give ill-defined behavior), but can be of undetermined length. /O/(ℓ).+q_sub_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_sub_param x y = q_add_param (-x) y++-- | Add a parameter 'IntM' onto a 'QDInt', in place; i.e. (/x/,/y/) ↦ /x/+/y/. The parameter /x/ must be of the same length as /y/, or /x/ can also be of undetermined length. /O/(ℓ).+q_add_param_in_place :: IntM -> QDInt -> Circ QDInt+q_add_param_in_place x1 y = do+ let x = intm_promote x1 y "q_add_param_in_place: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ y' <- q_add_param_in_place_qulist x' y'+ let y = xint_of_list_bh y'+ return y++-- | Low-level implementation of 'q_add_param_in_place': represents+-- integers as big-headian qubit lists. Precondition: /xlist/ and /y/+-- have the same length. Precondition: /x/ and /y/ have the same length.+q_add_param_in_place_qulist :: [Bool] -> [Qubit] -> Circ [Qubit]+q_add_param_in_place_qulist [] [] = return []+q_add_param_in_place_qulist [False] [y0] = return [y0]+q_add_param_in_place_qulist [True] [y0] = do+ y0 <- qnot y0+ return [y0]+q_add_param_in_place_qulist x y = do+ let l = length x++ let (x0:x_higher) = reverse x + (y0:y_higher) = reverse y++ y0 <- qnot y0 `controlled` x0++ (y0,y_higher) <- with_computed_fun y0+ (\y0 -> do+ c <- qinit False+ c <- qnot c `controlled` (x0 == 1) .&&. (y0 .==. 0)+ return (y0,c))+ (\(y0,c) -> do+ (y_higher,c) <- q_add_aux x_higher y_higher c+ return ((y0,c),y_higher))+ return (reverse (y0:y_higher))+ where+ -- Aux: add two LITTLE-endian bit strings, and an optional extra 1.+ q_add_aux :: [Bool] -> [Qubit] -> Qubit -> Circ ([Qubit],Qubit)+ q_add_aux [] [] c = return ([],c)+ q_add_aux [x0] [y0] c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ return ([y0],c)+ q_add_aux (x0:xs) (y0:ys) c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ ((y0,c),ys) <- with_computed_fun (y0,c)+ (\(y0,c) -> do+ c' <- qinit False+ c' <- qnot c' `controlled` (x0 == 1) .&&. (y0 .==. 0)+ c' <- qnot c' `controlled` (x0 == 1) .&&. (c .==. 1)+ c' <- qnot c' `controlled` (y0 .==. 0) .&&. (c .==. 1)+ return (y0,c,c'))+ + (\(y0,c,c') -> do+ (ys,c') <- q_add_aux xs ys c'+ return ((y0,c,c'),ys))+ return (y0:ys,c)+ q_add_aux _ _ _ = error "q_add_in_place: cannot add integers of different sizes."++-- | Subtract a parameter 'IntM' from a 'QDInt', in place; i.e. (/x/,/y/) ↦ (/x/,/x/-/y/). /x/ cannot be shorter than /y/. /O/(/l/) gates.+q_sub_param_in_place :: IntM -> QDInt -> Circ QDInt+q_sub_param_in_place x = q_add_param_in_place (-x)++-- | Multiply a parameter 'IntM' by a 'QDInt', into a fresh 'QDInt'. The 'IntM' cannot be shorter than the 'QDInt' (that would give ill-defined behavior), but can be of undetermined length. /O/(ℓ).+q_mult_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_mult_param x1 y = do+ let x = intm_promote x1 y "q_add_param: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ (y', z') <- q_mult_param_qulist x' y'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (y, z)++-- | Low-level implementation of 'q_mult_param': represents integers as+-- big-headian (qu)bit lists.+q_mult_param_qulist :: [Bool] -> [Qubit] -> Circ ([Qubit],[Qubit])+q_mult_param_qulist [] [] = return ([], [])+q_mult_param_qulist xs ys = do+ let x0 = last xs + x_higher = init xs+ y_high = head ys+ y_lower = tail ys+ (y_lower, p_higher)+ <- q_mult_param_qulist x_higher y_lower+ p0 <- qinit False+ let y = y_high:y_lower+ p = p_higher ++ [p0]+ -- Once we have some optimisation, the following line should be replaced by:+ -- (y,p) <- q_add_in_place_qulist y p `controlled` x0+ (y,p) <- if x0 then q_add_in_place_qulist y p else return (y,p)+ return (y, p) ++-- ----------------------------------------------------------------------+-- ** Sign and negation++-- | Negate a (signed) 'QDInt' in place. /O/(ℓ).+q_negate_in_place :: QDInt -> Circ QDInt+q_negate_in_place x = do+ x <- mapUnary qnot x+ x <- q_increment x+ return x++-- | Low-level version of 'q_negate_in_place': represents integers as+-- big-headian qubit lists.+q_negate_in_place_qulist :: [Qubit] -> Circ [Qubit]+q_negate_in_place_qulist = mmap list_of_xint_bh . q_negate_in_place . xint_of_list_bh++-- | Compute the negation of a (signed) 'QDInt'. /O/(ℓ).+-- +-- (Fixes the minimum value, consistently with Haskell’s @'negate' 'minBound' :: 'Int'@.) +q_negate_qdint :: QDInt -> Circ (QDInt,QDInt)+q_negate_qdint x = do+ (x,nx) <- qc_copy_fun x+ nx <- q_negate_in_place nx+ return (x,nx)++-- | Compute the absolute value of a (signed) 'QDInt'. /O/(ℓ).+-- +-- (Fixes the minimum value, consistently with Haskell’s @'abs' 'minBound' :: 'Int'@.) +q_abs_qdint :: QDInt -> Circ (QDInt,QDInt)+q_abs_qdint x = do+ let x' = list_of_xint_bh x+ (x', a') <- q_abs_qulist x'+ let x = xint_of_list_bh x'+ let a = xint_of_list_bh a'+ return (x, a)++-- | Low-level implementation of 'q_abs_qdint': represents integers as+-- big-headian qubit lists.+q_abs_qulist :: [Qubit] -> Circ ([Qubit],[Qubit])+q_abs_qulist [] = return ([], [])+q_abs_qulist (x_high:x_lower) = do+ a_high <- qinit False+ (x_lower,a_lower) <- qc_copy_fun x_lower+ a_lower <- mapUnary qnot a_lower `controlled` x_high+ let a = a_high:a_lower+ a <- q_increment_qulist a `controlled` x_high+ return (x_high:x_lower, a)++-- | Compute a 'QDInt' of the same length as the input, with value 1, 0, or –1, depending on the sign of the input. Analogous to Haskell’s 'signum'. /O/(ℓ).+q_signum_qdint :: QDInt -> Circ (QDInt,QDInt)+q_signum_qdint x = do+ let x' = list_of_xint_bh x+ (x', a') <- q_signum_qulist x'+ let x = xint_of_list_bh x'+ let a = xint_of_list_bh a'+ return (x, a)++-- | Low-level implementation of 'q_abs_qdint': represents integers as+-- big-headian qubit lists.+q_signum_qulist :: [Qubit] -> Circ ([Qubit],[Qubit])+q_signum_qulist [] = return ([], [])+q_signum_qulist x = do+ let l = length x+ (s_higher, s_low) <- qinit (replicate (l-1) False, False)+ -- first set s = 1+ s_low <- qnot s_low+ -- case x < 0: set s to -1 + s_higher <- mapUnary qnot s_higher `controlled` (head x)+ -- case x = 0: reset s to 0+ s_low <- qnot s_low `controlled` (x .==. replicate l 0)+ return (x,s_higher ++ [s_low])++-- ----------------------------------------------------------------------+-- ** Comparison++-- | Comparison of two 'QDInt's, considered as unsigned. /O/(ℓ).+q_le_unsigned :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_le_unsigned x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x',y',le_out) <- q_le_unsigned_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x,y,le_out)++-- | Low-level implementation of 'q_le_unsigned': represents integers+-- as big-headian qubit lists.+q_le_unsigned_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],Qubit)+q_le_unsigned_qulist x y = do+ ((x,y),le_out) <- with_computed_fun (x,y) q_le_unsigned_aux+ (\(x,y,(le,garbage)) -> do+ (le,le_out) <- qc_copy_fun le+ return ((x,y,(le,garbage)),le_out))+ return (x,y,le_out)++-- | Same as 'q_le_unsigned', but uncurried, represents integers as+-- big-headian qubit lists, and doesn’t clean up its garbage. (If+-- each recursive call cleans up its own garbage, gate use becomes+-- /O/(2[sup /n/]).)+q_le_unsigned_aux :: ([Qubit], [Qubit]) -> Circ ([Qubit],[Qubit],(Qubit,[Qubit]))+q_le_unsigned_aux ([], []) = do q <- qinit True; return ([], [], (q,[]))+q_le_unsigned_aux (x_high:x_lower, y_high:y_lower) = do+ -- Classically, one would make the recursive call only if needed; but quantumly, it’s cheaper to do it unconditionally. + (x_lower, y_lower, (le_lower, garbage)) <- q_le_unsigned_aux (x_lower, y_lower)+ (x_high,y_high,eq_high) <- q_is_equal x_high y_high+ le <- qinit False+ le <- qnot le `controlled` (x_high .==. 0) .&&. (y_high .==. 1)+ le <- qnot le `controlled` eq_high .&&. le_lower+ return (x_high:x_lower, y_high:y_lower, (le, eq_high:le_lower:garbage))+q_le_unsigned_aux _ = error "q_le // QDInt: cannot compare QDInt’s of different lengths."++-- | Comparison of two 'QDInt's, considered as signed. Used in @instance 'QOrd' 'QDInt'@. /O/(ℓ).+q_le_signed :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_le_signed x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x',y',le_out) <- q_le_signed_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x,y,le_out)++-- | Low-level implementation of 'q_le_signed': represents integers+-- as big-headian qubit lists.++-- Implementation note: defined in terms of q_le_unsigned_aux+q_le_signed_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],Qubit)+q_le_signed_qulist [] [] = do q <- qinit True; return ([], [], q)+q_le_signed_qulist (x_high:x_lower) (y_high:y_lower) = do+ ((x,y), x_le_y) <- with_computed_fun (x_high:x_lower, y_high:y_lower)+ (\(x_high:x_lower, y_high:y_lower) -> do + -- Classically, one would make the recursive call only if needed; but quantumly, it’s cheaper to do it unconditionally. + (x_lower, y_lower, (le_lower, garbage)) <- q_le_unsigned_aux (x_lower, y_lower)+ (x_high,y_high,eq_high) <- q_is_equal x_high y_high+ return (x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage))+ (\(x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage) -> do+ le <- qinit False+ le <- qnot le `controlled` (x_high .==. 1) .&&. (y_high .==. 0)+ le <- qnot le `controlled` eq_high .&&. le_lower+ return ((x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage), le))+ return (x,y,x_le_y)+q_le_signed_qulist _ _ = error "q_le // QDInt: cannot compare QDInt’s of different lengths."++-- | Comparison of two 'QDInt's, considered as signed. Used in @instance 'QOrd' 'QDInt'@. /O/(ℓ).+q_lt_signed :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_lt_signed x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (y',x',y_le_x) <- q_le_signed_qulist y' x'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ x_lt_y <- qnot y_le_x+ return (x,y,x_lt_y)++-- | Text whether a 'QDInt' is nonnegative. /O/(1)+q_negative :: QDInt -> Circ (QDInt,Qubit)+q_negative x = do+ let (x_high:x_lower) = list_of_xint_bh x+ (x_high,x_neg) <- qc_copy_fun x_high+ return (xint_of_list_bh (x_high:x_lower),x_neg)++instance QOrd QDInt where+ q_less qx qy = do (qx,qy,q) <- q_lt_signed qx qy; return q+ q_leq qx qy = do (qx,qy,q) <- q_le_signed qx qy; return q++-- ----------------------------------------------------------------------+-- ** Multiplication++-- | Multiply two 'QDInt's into a fresh third. Arguments are assumed to be of equal size. /O/(ℓ[sup 2]).+q_mult_qdint :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt)+q_mult_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_mult_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_mult_qdint': represents integers as+-- big-headian qubit lists.+q_mult_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],[Qubit])+q_mult_qulist [] [] = return ([], [], [])+q_mult_qulist xs ys = do+ let x0 = last xs + x_higher = init xs+ y_high = head ys+ y_lower = tail ys+ (x_higher, y_lower, p_higher)+ <- q_mult_qulist x_higher y_lower+ p0 <- qinit False+ let y = y_high:y_lower+ p = p_higher ++ [p0]+ (y,p) <- q_add_in_place_qulist y p `controlled` x0+ return (x_higher ++ [x0], y, p)++-- ----------------------------------------------------------------------+-- ** Division and remainder++-- | Reduce one 'QDInt' modulo another, in place, returning also the+-- integer quotient, i.e. (/x/, /y/) ↦ (/x/ mod /y/, /y/, /x/ div /y/).+-- All inputs and outputs are considered unsigned. Inputs are assumed+-- to have the same length. Division by zero returns (/x/,0,-1).+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_moddiv_unsigned_in_place :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_moddiv_unsigned_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (r', y', q') <- q_moddiv_unsigned_in_place_qulist x' y'+ let r = xint_of_list_bh r'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (r, y, q)++-- | Low-level implementation of 'q_moddiv_unsigned_in_place':+-- represents integers as big-headian qubit lists.+-- +-- Implementation note: this algorithm can be optimized significantly:+-- [scratch] is always 0, so can be optimized away. This optimisation+-- is much clearer at the circuit level than the code level, however,+-- so is deferred for now.+q_moddiv_unsigned_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit],[Qubit])+q_moddiv_unsigned_in_place_qulist x y = do+ let l = common_value "q_divrem_unsigned_in_place: arguments must be same length" $ map length [x,y]+ -- Set quot = 0, rem = x. At each stage, we will have y * quot + rem = x.+ quot_bits <- qinit (replicate l False)+ let rem = x+ with_ancilla_init (replicate (l-1) False) (\scratch -> do+ -- For j from (l-1) to 0, compute the jth bit of the quotient, and update the remainder + (y,scratch,quot_bits,rem) <- loop_with_indexM l (y,scratch,quot_bits,rem)+ (\i (y,scratch,quot_bits,rem) -> do+ let j = l-1-i+ -- Getting y*(2^j) requires no quantum operations, just formal re-bracketing of the data:+ let y_init = take j y+ y_2j = (drop j y) ++ (take j scratch)+ scratch_tail = drop j scratch+ -- Compare (2^j) y with rem. If (2^j)y <= rem, then subtract (2^j)y from rem and flip the jth bit of quot.+ ((y_init,y_2j,rem,quot_bits),()) <- with_computed_fun + (y_init,y_2j,rem,quot_bits)+ (\(y_init,y_2j,rem,quot_bits) -> do+ -- first, test that (2^j)y has not overflowed the register, i.e. that y_init is still all 0;+ le1 <- qinit False+ le1 <- qnot le1 `controlled` (y_init .==. replicate (length y_init) False)+ -- next, test that (2^j)y <= rem (this will be correct if (2^j)y has not overflowed yet);+ (y_2j,rem,le2) <- q_le_unsigned_qulist y_2j rem+ return (y_init,y_2j,rem,quot_bits,le1,le2))+ (\(y_init,y_2j,rem,quot_bits,le1,le2) -> do+ -- flip jth bit of quot, if appropriate+ -- (leave rem unchanged for now, since it’s needed to uncompute le1)+ q_j <- qnot (quot_bits !! (l-1-j)) `controlled` le1 .&&. le2+ return ((y_init,y_2j,rem,(overwriteAt (l-1-j) q_j quot_bits),le1,le2),()))+ -- now, subtract (2^j) from rem, if appropriate.+ (y_2j,rem) <- q_sub_in_place_qulist y_2j rem `controlled` quot_bits !! (l-1-j)+ -- Finally, undo the formal re-bracketing:+ let y = y_init ++ (take (l-j) y_2j)+ scratch = (drop (l-j) y_2j) ++ scratch_tail+ return (y,scratch,quot_bits,rem))+ return (rem, y, quot_bits))++-- | Reduce one 'QDInt' modulo another, i.e. (/x/, /y/) ↦ (/x/, /y/, /x/ mod /y/).+-- All inputs and outputs are considered unsigned. Inputs are assumed+-- to have the same length. If /y/ = 0, returns (/x/,0,/x/).+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_mod_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_mod_unsigned x y = do+ ((x,y),x_mod_y) <- with_computed_fun (x,y)+ (\(x,y) -> q_moddiv_unsigned_in_place x y)+ (\(x_mod_y, y, x_div_y) -> do+ (x_mod_y, x_mod_y_out) <- qc_copy_fun x_mod_y+ return ((x_mod_y, y, x_div_y), x_mod_y_out))+ return (x,y,x_mod_y)++-- | Integer division of two 'QDInt's, returning the quotient and remainder,+-- i.e. (/x/,/y/) ↦ (/x/,/y/,/x/ div /y/,/x/ mod /y/). +-- All inputs and outputs are considered unsigned.+-- Inputs are assumed to have the same length.+-- Division by zero returns (-1,/x/).+--+-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_divrem_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt,QDInt)+q_divrem_unsigned x y = do+ (x,rem) <- qc_copy_fun x+ (rem,y,quot) <- q_moddiv_unsigned_in_place rem y+ return (x,y,quot,rem)++-- | Integer division of two 'QDInt's, returning just the quotient. +-- All inputs/outputs considered unsigned.+-- Inputs are assumed to have the same length.+-- Division by zero returns –1.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.++-- Implementation note: We use a multiplication to do the+-- uncomputation of the remainder, because this uses fewer total gates+-- than uncomputing divrem would.+q_div_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_unsigned x y = do+ (x,y,quot,rem) <- q_divrem_unsigned x y+ ((x,y,quot),()) <- with_computed_fun (x,y,quot) + (\(x,y,quot) -> do+ (y,quot,y_quot) <- q_mult y quot+ (x,y_quot,rem_copy) <- q_sub x y_quot+ return (x,y,quot,y_quot,rem_copy))+ (\(x,y,quot,y_quot,rem_copy) -> do+ rem_copy <- qc_uncopy_fun rem_copy rem+ return ((x,y,quot,y_quot,rem_copy),()))+ return (x,y,quot)++-- | Low-level version of 'q_div_unsigned': represents integers as+-- big-headian qubit lists.+q_div_unsigned_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_div_unsigned_qulist x' y' = do+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ (x, y, q) <- q_div_unsigned x y + let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ let q' = list_of_xint_bh q+ return (x', y', q')++-- | Integer division of two 'QDInt's into a fresh third, rounding towards –∞. +-- The first argument is the numerator, and is assumed to be signed.+-- The second argument is the denominator, and is assumed to be unsigned.+-- The output is signed.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_div x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', q') <- q_div_qulist x' y' + let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (x, y, q)++-- | Low-level implementation of 'q_div': represents integers as+-- big-headian qubit lists.+q_div_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_div_qulist [] [] = return ([], [], [])+q_div_qulist (x_high:x_lower) y = do+ -- Writing x/y for unsigned division, we have x `div` y = + -- if x>0 then x/y else ~((~x)/y)+ -- where ~x := -1-x --- i.e. the bitflip of x.+ --+ -- We do this with an ancilla (which can easily be optimized away): + with_ancilla_init False (\fake_x_high -> do+ -- flip x if x<0:+ x_lower <- mapUnary qnot x_lower `controlled` x_high+ let x' = fake_x_high:x_lower+ -- x' now holds (if x > 0 then x else ~x) + (x',y,quot) <- q_div_unsigned_qulist x' y+ -- restore x and flip quotient, if necessary+ let fake_x_high:x_lower = x'+ x_lower <- mapUnary qnot x_lower `controlled` x_high+ quot <- mapUnary qnot quot `controlled` x_high+ return (x_high:x_lower, y, quot))+q_div_qulist _ _ = error "q_div: arguments must have same length."++-- | Integer division of two 'QDInt's into a fresh third, rounding towards 0. +-- The first argument is the numerator, and is assumed to be signed.+-- The second argument is the denominator, and is assumed to be unsigned.+-- The output is signed.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_quot :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_quot x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', q') <- q_quot_qulist x' y' + let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (x, y, q)++-- | Low-level implementation of 'q_quot': represents integers as+-- big-headian qubit lists.+q_quot_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_quot_qulist [] [] = return ([], [], [])+q_quot_qulist (x_high:x_lower) y = do+ -- Writing x/y for unsigned division, we have x `div` y = + -- if x>0 then x/y else -((-x)/y)+ --+ -- We do this with an ancilla (which can easily be optimized away): + with_ancilla_init False (\fake_x_high -> do+ -- flip x if x<0:+ x_lower <- q_negate_in_place_qulist x_lower `controlled` x_high+ let x' = fake_x_high:x_lower+ -- x' now holds (if x > 0 then x else ~x) + (x',y,quot) <- q_div_unsigned_qulist x' y+ -- restore x and flip quotient, if necessary+ let fake_x_high:x_lower = x'+ x_lower <- q_negate_in_place_qulist x_lower `controlled` x_high+ quot <- q_negate_in_place_qulist quot `controlled` x_high+ return (x_high:x_lower, y, quot))+q_quot_qulist _ _ = error "q_quot: arguments must have same length."++-- | Integer division of two 'QDInt's, returning the quotient,+-- assuming that the second exactly divides the first and throwing an error otherwise. +-- All inputs/outputs considered unsigned.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div_exact_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_exact_unsigned x y = do+ (x,y,quot,rem) <- q_divrem_unsigned x y+ qterm 0 rem+ return (x,y,quot)++-- | Integer division of two 'QDInt's, returning the quotient,+-- assuming that the second exactly divides the first. +-- The first argument is the numerator, considered signed.+-- The second argument is the denominator, considered unsigned.+-- The output is signed.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div_exact :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_exact x y = do+ (x,(y,quot)) <- with_computed_fun x+ (\x -> do+ (x,x_neg) <- q_negative x+ x' <- q_negate_in_place x `controlled` x_neg+ return (x',x_neg))+ (\(x',x_neg) -> do+ (x',y,quot) <- q_div_exact_unsigned x' y+ quot <- q_negate_in_place quot `controlled` x_neg+ return ((x',x_neg),(y,quot)))+ return (x,y,quot)++-- ----------------------------------------------------------------------+-- * The QNum type class++-- $ This section defines a quantum analogue of Haskell’s 'Num' type+-- class. See "Quipper.Internal.QClasses" for the general philosophy behind+-- quantum type classes.+-- +-- The type class 'QNum' corresponds to Haskell’s 'Num', with methods+-- including +-- +-- > add_q :: (QNum qa) => qa -> qa -> Circ (qa,qa,qa), +-- +-- and so on.+-- +-- Note: type class instances don't show up in the documentation. View+-- the source code to see them.++-- | Quantum analogue of Haskell’s 'Num' type class. This provides+-- basic addition, subtraction, multiplication, sign operations, and+-- conversion from integers.++class (QData qa) => QNum qa where+ -- | Add two quantum numbers into a fresh one. The arguments are+ -- assumed to be of equal size. The 'QDInt' instance uses /O/(ℓ)+ -- gates, both before and after transformation to Toffoli.+ q_add :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Multiply two quantum numbers into a fresh third. The arguments+ -- are assumed to be of equal size. The 'QDInt' instance is+ -- /O/(ℓ[sup 2]).+ q_mult :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Subtract two quantum numbers into a fresh one. The arguments+ -- are assumed to be of equal size. The 'QDInt' instance uses /O/(ℓ)+ -- gates, both before and after transformation to Toffoli.+ q_sub :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Compute the absolute value of a (signed) quantum number. The+ -- 'QDInt' instance is /O/(ℓ).+ q_abs :: qa -> Circ (qa,qa)+ + -- | Compute the negation of a (signed) quantum number. The 'QDInt'+ -- instance is /O/(ℓ).+ q_negate :: qa -> Circ (qa,qa)+ + -- | Compute a quantum number of the same precision as the input,+ -- with value 1, 0, or –1, depending on the sign of the+ -- input. Analogous to Haskell’s 'signum'. The 'QDInt' instance is+ -- /O/(ℓ).+ q_signum :: qa -> Circ (qa,qa)+ + -- | Convert a 'QDInt' to a quantum number. For the 'QDInt'+ -- instance, this is just a copy operation.+ q_fromQDInt :: QDInt -> Circ (QDInt,qa)+ +instance QNum QDInt where+ q_add = q_add_qdint+ q_mult = q_mult_qdint+ q_sub = q_sub_qdint+ q_abs = q_abs_qdint+ q_negate = q_negate_qdint+ q_signum = q_signum_qdint+ q_fromQDInt = qc_copy_fun++-- ----------------------------------------------------------------------+-- ** Specialized functions++-- | Euclid's extended algorithm. 'q_ext_euclid' /a/ /b/: returns+-- (/a/,/b/,/x/,/y/,/d/) such that /ax/ + /by/ = /d/ = gcd(/a/,/b/).+q_ext_euclid :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt,QDInt,QDInt)+q_ext_euclid a b = do+ let l = common_length "q_ext_euclid: inputs must have equal length" [a,b]++ -- Extended Euclidean algorithm:+ --+ -- compute three series of numbers (a_i), (x_i), (y_i), such that+ --+ -- a_0 = a, a_1 = b,+ -- a_{i+2} = a_i `mod` a_{i+1},+ -- and always a_i = (x_i * a) + (y_i * b),+ --+ -- When we first get a_{i+2} = 0, we know that a_{i+1} is gcd(a_0,a_1),+ -- so we save a_{i+1}, x_{i+1} and y_{i+1} for output.+ --+ -- The bound on the length of this loop is standard.++ ((a,b),(x,y)) <- with_computed_fun (a,b) (\(a,b) -> do+ x0 <- qinit (intm l 1)+ y0 <- qinit (intm l 0)+ x1 <- qinit (intm l 0)+ y1 <- qinit (intm l 1)+ done_yet <- qinit False+ x_final <- qinit (intm l 0)+ y_final <- qinit (intm l 0)++ (stuff1, stuff2, stuff3, (done_yet, x_final, y_final))+ <- loopM (euclid_bound l) ((a,b,x0,x1,y0,y1),[],[],(done_yet,x_final,y_final))+ (\((a_i, a_i1, x_i, x_i1, y_i, y_i1), quots_scratch, tests_scratch, (done_yet, x_final, y_final)) -> do+ (a_i2, a_i1, q_i) <- q_moddiv_unsigned_in_place a_i a_i1+ ((x_i1, y_i1, q_i), (x_i2, y_i2)) <- with_computed_fun+ (x_i1, y_i1, q_i)+ (\(x_i1, y_i1, q_i) -> do+ (q_i, x_i1, qx) <- q_mult q_i x_i1+ (q_i, y_i1, qy) <- q_mult q_i y_i1+ return (x_i1, y_i1, q_i, qx, qy))+ (\(x_i1, y_i1, q_i, qx, qy) -> do+ (qx,x_i2) <- q_sub_in_place qx x_i+ (qy,y_i2) <- q_sub_in_place qy y_i+ return ((x_i1, y_i1, q_i, qx, qy),(x_i2,y_i2)))+ done_this_time <- qinit False+ done_this_time <- qnot done_this_time `controlled` (a_i2 .==. 0) .&&. (done_yet .==. False)+ (x_i1,x_final) <- controlled_not x_final x_i1 `controlled` done_this_time+ (y_i1,y_final) <- controlled_not y_final y_i1 `controlled` done_this_time+ done_yet <- qnot done_yet `controlled` done_this_time+ return ((a_i1, a_i2, x_i1, x_i2, y_i1, y_i2),+ (q_i:quots_scratch), (done_this_time:tests_scratch), + (done_yet, x_final, y_final)))+ qterm True done_yet -- Assert that we really did reach the end of the algorithm. + return (x_final,y_final,(stuff1, stuff2, stuff3)))++ (\(x_final, y_final, stuff) -> do+ (x_final,x) <- qc_copy_fun x_final+ (y_final,y) <- qc_copy_fun y_final+ return ((x_final, y_final, stuff),(x,y)))++ ((a,b,x,y),gcd) <- with_computed_fun+ (a,b,x,y)+ (\(a,b,x,y) -> do+ (a,x,ax) <- q_mult a x+ (b,y,by) <- q_mult b y+ return (a,b,x,y,ax,by))+ (\(a,b,x,y,ax,by) -> do+ (ax,by,gcd) <- q_add ax by+ return ((a,b,x,y,ax,by),gcd))++ return (a,b,x,y,gcd)++ where+ -- Classical bound on running time (Lamé’s theorem), assuming b < a:+ -- t <= 1 + log_{phi} b <= 1 + l / (log_2 phi)+ -- + -- We allow one extra step since we may have a < b, in which case first step swaps them.+ euclid_bound :: Int -> Int+ euclid_bound l = 2 + ceiling ( (fromIntegral l) / (logBase 2 phi) )+ phi = (1+sqrt(5))/2++-- ----------------------------------------------------------------------+-- * Lifting of arithmetic functions++-- $LIFTING +-- This sections provides templates for lifting various arithmetic+-- functions in connection with the @build_circuit@ keyword. This+-- extends the liftings given in "Quipper.Internal.CircLifting" to operations+-- of the 'Num' type class.++-- | Quantum lifting of the '+' operator.+template_symb_plus_ :: (QNum qa) => Circ (qa -> Circ (qa -> Circ qa))+template_symb_plus_ = return $ \qx -> return $ \qy -> do (qx,qy,qz) <- q_add qx qy; return qz++-- TODO: complete templates of methods.+
@@ -0,0 +1,35 @@+-- | This library provides functions for optimizing classical+-- circuits. It is intended to reduce the size of+-- automatically-generated circuits, such as those that are built+-- using Quipper's @build_circuit@ keyword.+-- +-- Note: the resulting circuit is not expected to be optimal in+-- absolute terms. In general, finding the smallest possible circuit+-- to represent a given boolean function is an NP-hard+-- problem. Rather, we transform the input circuit by applying a+-- number of heuristics and an algebraic optimization technique. This+-- results in the removal of redundant qubits and gates that are+-- typical for machine-generated circuits. It generally decreases the+-- number of gates and ancillas.+-- +-- For example, consider the circuit for a simple adder, as generated+-- by the @build_circuit@ mechanism:+-- +-- \[image adder-unoptimized.png]+-- +-- The optimized version, as generated by 'simplify_classical', looks+-- like this:+-- +-- \[image adder-optimized.png]+-- +-- The above example can be found in the file+-- @Quipper\/Demos\/OptimTest.hs@ in the @quipper-demos@ package.++module Quipper.Libraries.ClassicalOptim (+ simplify_classical,+ classical_to_reversible_optim,+ box_classical_to_reversible_optim+) where++import Quipper.Libraries.ClassicalOptim.QuipperInterface+
@@ -0,0 +1,163 @@+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}++{-# LANGUAGE CPP #-}+#if __GLASGOW_HASKELL__ < 710+ {-# LANGUAGE OverlappingInstances #-}+#endif++-- | This module contains an efficient representation of algebraic+-- boolean formulas.+module Quipper.Libraries.ClassicalOptim.AlgExp where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import Quipper.Utils.Auxiliary (bool_xor)++import Quipper.Libraries.ClassicalOptim.Circuit++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Build the characteristic function of a set.+mapOfSet :: Ord a => S.Set a -> M.Map a Int+mapOfSet s = S.foldl' (\m x -> M.insert x 1 m) M.empty s++-- | Get the set of elements whose images are odd.+setOfMap :: Ord a => M.Map a Int -> S.Set a+setOfMap m = + M.foldlWithKey' (\s x _ -> S.insert x s) S.empty $ + M.filter (\x -> mod x 2 == 1) m++-- | Split a list in the middle.+split_even :: [a] -> ([a],[a])+split_even a = splitAt (div (length a) 2) a++-- ----------------------------------------------------------------------+-- * Expressions++-- | The type of algebraic boolean expressions. +-- +-- We represent boolean expressions using \"and\" and \"xor\" as the+-- primitive connectives. Equivalently, we can regard booleans as the+-- elements of the two-element field /F/[sub 2], with operations \"*\"+-- (times) and \"+\" (plus). +-- +-- An algebraic expression+-- @x1*x2*x3 + y1*y2*y3 + z1*z2@+-- is encoded as+-- @{{x1,x2,x3},{y1,y2,y3},{z1,z2}}@.+-- +-- In particular,+-- @{} == False == 0@ and +-- @{{}} == True == 1@.+type Exp = S.Set IS.IntSet++instance {-# OVERLAPPING #-} Show Exp where+ show e = if (S.null e) then "F"+ else if (e == S.singleton (IS.empty)) then "T"+ else L.concat $ L.intersperse "+" (L.map (\e -> L.concat $ L.map (\x -> "x" ++ (show x)) $ IS.toList e) $ S.toList e)++-- | Turn an @Exp@ into a list of lists.+listOfExp :: Exp -> [[Int]]+listOfExp e = S.toList $ S.map IS.toList e++-- | Turn a list of lists into an @Exp@.+expOfList :: [[Int]] -> Exp+expOfList l = S.fromList $ L.map IS.fromList l++-- | The conjunction of two expression.+exp_and :: Exp -> Exp -> Exp+exp_and a b = + setOfMap $+ S.foldl (\exp monomial -> M.unionWith (+) exp $ exp_and_aux monomial $ mapOfSet a) M.empty b+ where+ exp_and_aux :: IS.IntSet -> M.Map IS.IntSet Int -> M.Map IS.IntSet Int+ exp_and_aux monomial exp = M.mapKeysWith (+) (IS.union monomial) exp++-- | The xor of two expressions.+exp_xor :: Exp -> Exp -> Exp+exp_xor a b = setOfMap $ M.unionWith (+) (mapOfSet a) (mapOfSet b)++-- | The expression \"False\".+exp_false :: Exp+exp_false = S.empty++-- | The expression \"True\".+exp_true :: Exp+exp_true = S.singleton IS.empty++-- | The negation of an expression.+exp_not :: Exp -> Exp +exp_not e = exp_xor e exp_true++-- | The expression /x/[sub /n/].+exp_var :: Int -> Exp+exp_var x = S.singleton $ IS.singleton x++-- ----------------------------------------------------------------------+-- * Properties of expressions++-- $ The important property of expressions is that two formulas have+-- the same truth table iff they are syntactically equal. This makes+-- the equality test of wires theoretically straightforward.++-- ----------------------------------------------------------------------+-- ** Truth tables++-- $ A /valuation/ on a set of variables is a map from variables to+-- booleans. This can be thought of as a row in a truth table. A+-- /truth table/ is a map from valuations to booleans, but we just+-- represent this as a list of booleans, listed in lexicographically+-- increasing order of valuations.++-- | Get the variables used in an expression.+vars_of_exp :: Exp -> [Int]+vars_of_exp e = IS.toList $ S.foldl (\a b -> IS.union a b) IS.empty e++-- | Evaluate the expression with respect to the given valuation. A+-- /valuation/ is a map from variables to booleans, i.e., a row in a+-- truth table.+exp_eval :: Exp -> M.Map Int Bool -> Bool+exp_eval e m = L.foldl bool_xor False $ L.map (L.foldl (&&) True) $ L.map (L.map (m M.!)) $ L.map (IS.toList) $ S.toList e++-- | Construct the list of all 2[super /n/] valuations for a given+-- list of /n/ variables.+valuations_of_vars :: [Int] -> [M.Map Int Bool]+valuations_of_vars [] = [M.empty]+valuations_of_vars (h:t) = l+ where+ l = (L.map (M.insert h False) v) ++ (L.map (M.insert h True) v)+ v = valuations_of_vars t ++-- | Build the truth table for the given expression, on the given list+-- of variables. The truth table is returned as a list of booleans in+-- lexicographic order of valuations. For example, if+-- +-- > 1 2 | exp+-- > F F | f1+-- > F T | f2+-- > T F | f3+-- > T T | f4+-- +-- then the output of the function is @[f1,f2,f3,f4]@.+truth_table_of_exp :: [Int] -> Exp -> [Bool]+truth_table_of_exp vars e = L.map (exp_eval e) (valuations_of_vars vars)++-- | Return an expression realizing the given truth table. Uses+-- variables starting with the given number.+exp_of_truth_table :: Int -> [Bool] -> Exp+exp_of_truth_table i [] = exp_true+exp_of_truth_table i [False] = exp_false+exp_of_truth_table i [True] = exp_true+exp_of_truth_table i t = ((exp_not (exp_var i)) `exp_and` e1) `exp_xor` ((exp_var i) `exp_and` e2)+ where+ (t1,t2) = split_even t+ e1 = exp_of_truth_table (i+1) t1+ e2 = exp_of_truth_table (i+1) t2+
@@ -0,0 +1,128 @@+-- | This module provides a simplified representation of classical+-- circuits.+module Quipper.Libraries.ClassicalOptim.Circuit where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Quipper.Utils.Auxiliary as Q++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++-- ----------------------------------------------------------------------+-- * Simplified circuits++-- | The type of wires. A wire is determined by an integer ID.+type Wire = Int++-- | The type of gates.+data Gate = + NoOp -- ^ No operation.+ | Init Bool Wire -- ^ Initialization.+ | Cnot Wire [(Wire,Bool)] -- ^ Multi-controlled not.+ deriving (Show,Eq)++-- | Get the wire acted upon by a gate, if any.+wireOfGate :: Gate -> Maybe Wire+wireOfGate NoOp = Nothing+wireOfGate (Init _ w) = Just w+wireOfGate (Cnot w _) = Just w++-- | Get the list of controls, if any.+ctlsOfGate :: Gate -> Maybe [(Wire,Bool)]+ctlsOfGate (Cnot _ ctls) = Just ctls+ctlsOfGate _ = Nothing++-- | Evaluate a circuit on a given initial state, and return the final+-- state. A state is represented as a map from wires to booleans.+evalCirc :: M.Map Wire Bool -> [Gate] -> M.Map Wire Bool+evalCirc m [] = m+evalCirc m (NoOp:gs) = evalCirc m gs+evalCirc m ((Init b w):gs) = evalCirc (M.insert w b m) gs+evalCirc m ((Cnot w ctls):gs) = evalCirc (M.adjust (Q.bool_xor (ands m ctls)) w m) gs+ where+ ands m [] = True+ ands m ((w,b):ctls) = ((m M.! w) `Q.bool_xor` (not b)) && (ands m ctls)++-- ----------------------------------------------------------------------+-- * Simplified Circ monad++-- | A data structure to represent a \"circuit under+-- construction\". This holds the data needed for circuit generation.+data CircState = CS {+ circuit :: [Gate], -- ^ The circuit so far.+ freshWire :: Wire -- ^ The next fresh wire.+} deriving (Show)++-- | The empty state.+emptyState :: CircState+emptyState = CS {circuit = [], freshWire = 0}++-- | A simplified @Circ@ monad.+data Circ a = Circ (CircState -> (CircState, a))++instance Monad Circ where+ return x = Circ (\y -> (y,x))+ (>>=) (Circ c) f = Circ (\s -> let (s',x) = c s in+ let (Circ c') = f x in+ c' s')++instance Applicative Circ where+ pure = return+ (<*>) = ap++instance Functor Circ where+ fmap = liftM++-- ----------------------------------------------------------------------+-- * Low-level access functions++-- | Retrieve the next fresh wire.+getFresh :: Circ Wire+getFresh = Circ (\s -> (s, freshWire s))++-- | Increment the value of the fresh wire.+incrementFresh :: Circ ()+incrementFresh = Circ (\s -> (s { freshWire = freshWire s + 1 }, ()))++-- | Add a new gate to the circuit.+addGate :: Gate -> Circ ()+addGate g = Circ (\s -> (s {circuit = g : (circuit s)}, ()))++-- | Get the circuit out of the monad.+extractCircuit :: Circ a -> [Gate]+extractCircuit (Circ c) = circuit $ fst $ c emptyState++-- ----------------------------------------------------------------------+-- * Higher-level access functions++-- | Initialize a new wire.+init :: Bool -> Circ Wire+init b = do+ w <- getFresh+ addGate (Init b w)+ incrementFresh+ return w++-- | Add a multi-controlled not gate.+cnot :: Wire -> [(Wire,Bool)] -> Circ ()+cnot w ws = do+ addGate (Cnot w ws)+ return ()++-- ----------------------------------------------------------------------+-- * Pretty-printing++-- $ These functions are only used for testing.++-- | Pretty-print a circuit as a list of gates.+printCircuit :: Circ a -> IO ()+printCircuit c = do+ mapM_ putStrLn $ map show $ reverse $ extractCircuit c ++-- | Print a gate as Quipper code.+print_quipperStyle :: Gate -> IO ()+print_quipperStyle (Init b w) = putStrLn (" x" ++ (show w) ++ " <- Q.qinit " ++ (show b))+print_quipperStyle (Cnot w ctls) = putStrLn (" Q.qnot x" ++ (show w) ++ " `Q.controlled` " ++ + (L.intercalate " Q..&&. " $ map (\(w,b) -> "x" ++ (show w) ++ " Q..==. " ++ (show b) ++ " ") ctls))+print_quipperStyle g = putStrLn (" Q.comment \"" ++ (show g) ++ "\"")
@@ -0,0 +1,158 @@+-- | This module provides some automated tests for the algebraic+-- optimization procedure. The main function are:+-- +-- * 'testArith5': runs the optimization on a reversible adder+-- generated with Template Haskell and exhaustively tests all the+-- values.+-- +-- * 'testCircSimpl' and 'testCircSwap': using the "Test.QuickCheck"+-- library, perform optimization on randomly generated circuits and+-- test validity for some random input.+module Quipper.Libraries.ClassicalOptim.QuickCheck where++import qualified Data.Map as M+import qualified Data.List as L++import qualified Test.QuickCheck as Test++import qualified Quipper as Q+import qualified Quipper.Internal.Circuit as Q+import qualified Quipper.Libraries.Simulation.ClassicalSimulation as Q+import qualified Quipper.Utils.Auxiliary as Q++import Quipper.Libraries.ClassicalOptim.Circuit+import Quipper.Libraries.ClassicalOptim.Simplification+import Quipper.Libraries.ClassicalOptim.QuipperInterface+import Quipper.Libraries.ClassicalOptim.QuickCheckArith++-- ----------------------------------------------------------------------+-- * Testing an adder++-- | Wrapper around 'template_adder'.+myAdder :: ([Q.Qubit],[Q.Qubit]) -> Q.Circ [Q.Qubit]+myAdder (x,y) = do+ Q.label (x,y) ("x","y")+ z <- Q.unpack template_adder x y+ Q.label z "z"+ return (z)++-- | A synonym for 'myAdder'.+myArith :: ([Q.Qubit], [Q.Qubit]) -> Q.Circ [Q.Qubit]+myArith = myAdder++-- | The optimized version of 'myArith'.+myCircArith :: Int -> (CircState, [Wire])+myCircArith n = quipperFunToMyCirc myArith (replicate n Q.qubit, replicate n Q.qubit)++-- | The 'Q.BCircuit' of the optimized version of 'myArith'.+bCircArith :: Int -> Q.BCircuit+bCircArith n = + fst $ simplify_classical' myArith (replicate n Q.qubit, replicate n Q.qubit) ++-- | Extract the underlying 'Wire' from a 'Q.B_Endpoint'.+wire_of_endpoint :: Q.B_Endpoint t t -> t+wire_of_endpoint (Q.Endpoint_Qubit x) = x+wire_of_endpoint (Q.Endpoint_Bit x) = x++-- | Run the reversible adder, non-optimized.+runArithDry :: Int -> Int -> Int -> Int+runArithDry n x y = + Q.int_of_boollist_unsigned_bh $ reverse $ Q.run_classical_unary myAdder (Q.boollist_of_int_lh n x, Q.boollist_of_int_lh n y)++-- | Run the reversible adder, optimized.+runArith :: Int -> Int -> Int -> Int+runArith n x y = Q.int_of_boollist_unsigned_bh $ reverse $ map wire_of_endpoint $ map (mymap M.!) out+ where+ list = (Q.boollist_of_int_lh n x) ++ (Q.boollist_of_int_lh n y)+ (simpl_bc,out) = simplify_classical' myAdder (replicate n Q.qubit, replicate n Q.qubit) + mymap = + Q.run_classical simpl_bc $ + M.fromList $ + zip [0..(length list - 1)] $ map Q.Endpoint_Qubit list++-- | Exhaustively test all the inputs of the optimized adder.+testArith5 :: Bool+testArith5 = foldl (&&) True is_good+ where+ op = runArith 5+ inputs = concat [ map (\x -> (i,x)) [0..31] | i <- [0..31]]+ results = map (uncurry $ op) inputs+ testcase = map (\(x,y) -> mod (x+y) 32) inputs+ is_good = map (\(x,y) -> x == y) $ zip results testcase++-- ----------------------------------------------------------------------+-- * Randomized tests++-- $ The following functions use the "Test.QuickCheck" library. They+-- generate random circuits, and run them classically before and after+-- optimization.++-- | Return all sublists of the given size.+choose :: [b] -> Int -> [[b]]+choose _ 0 = [[]]+choose [] _ = []+choose (x:xs) k = map (x:) (choose xs (k-1)) ++ (choose xs k)++-- | Generate a random circuit with the given number of inputs and+-- ancillas.+genCirc :: Int -> Int -> Test.Gen ([Gate],[Wire])+genCirc size_input nber_ancillas = do+ -- random wire+ let randWire = Test.choose (0, size_input + nber_ancillas - 1)+ -- random gate+ let randGate = do+ type_gate <- Test.choose (1,3) -- say if it is not, cnot or ccnot+ list <- Test.oneof $ map return $ map reverse $ choose [0..(size_input + nber_ancillas - 1)] type_gate+ let (w:ws) = list+ ctls <- mapM (\x -> do+ b <- Test.oneof $ map return [False,True]+ return (x,b)) ws+ return (Cnot w ctls)+ -- create output wires+ size_outputs <- Test.choose (1,size_input + nber_ancillas)+ outputs <- Test.oneof $ map return $ choose [0..(size_input + nber_ancillas - 1)] size_outputs+ -- create gates+ nber_gates <- Test.choose (10,50)+ -- let nber_gates = 6+ cnots <- Test.vectorOf nber_gates randGate+ let ancillas = map (\w -> Init True w) [size_input..size_input+nber_ancillas-1]+ return (ancillas ++ cnots, outputs)+++-- | Print some sample circuits generated by 'genCirc'.+printSampleCirc :: Int -> Int -> IO ()+printSampleCirc i a = do+ list <- Test.sample' (genCirc i a)+ mapM_ (\(a,ws) -> do+ putStrLn "\n\n"+ putStrLn $ show (a,ws)+ putStrLn "goes into\n"+ putStrLn $ show $ simplRec (a,ws)) list+ +-- | Restrict a map to the given domain.+restrict :: Ord k => [k] -> M.Map k a -> M.Map k a+restrict l m = L.foldl (\a b -> M.insert b (m M.! b) a) M.empty l++-- | Test of 'simplRec'.+testCircSimpl :: Test.Property+testCircSimpl = Test.forAll (genCirc 2 3) $ \(f,outs) -> + Test.forAll (Test.vectorOf 2 $ Test.oneof [return False,return True]) $ \b -> + let m = M.fromList (zip [0..10] b) in+ let (f',o') = simplRec (f,outs) in+ L.map ((evalCirc m f) M.!) outs == L.map ((evalCirc m f') M.!) o'++-- | Test of 'alg_swap'.+testCircSwap :: Test.Property+testCircSwap = Test.forAll (genCirc 2 3) $ \(f,outs) -> + Test.forAll (Test.vectorOf 2 $ Test.oneof [return False,return True]) $ \b -> + let m = M.fromList (zip [0..10] b) in+ let (f',o') = alg_swap (f,outs) in+ L.map ((evalCirc m f) M.!) outs == L.map ((evalCirc m f') M.!) o'++-- | A wrapper function to easily run a test.+-- Within the interpreter @quipperi@, use, e.g., as+-- +-- > myQuickTest testCircSimpl+myQuickTest :: Test.Property -> IO Test.Result+myQuickTest x = Test.quickCheckWithResult (Test.stdArgs {Test.maxSuccess = 1000}) x+
@@ -0,0 +1,64 @@+module Quipper.Libraries.ClassicalOptim.QuickCheckAlgExp where++import qualified Test.QuickCheck as Test++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import Quipper.Utils.Auxiliary (bool_xor)++import Quipper.Libraries.ClassicalOptim.Circuit++import Quipper.Libraries.ClassicalOptim.AlgExp++-- ----------------------------------------------------------------------+-- ** Quick-checking++-- | Compute 2[sup /n/].+twoExp :: (Integral a) => a -> Int+twoExp 0 = 1+twoExp n | mod n 2 == 0 = let a = twoExp (div n 2) in a * a+ | otherwise = 2 * (twoExp (n-1))++-- | Generate a list of 'Bool'.+genBoolList :: Integral a => a -> Test.Gen [Bool]+genBoolList n = Test.vectorOf (twoExp n) $ Test.oneof [return True, return False]++-- | Arguments for QuickCheck.+test_args :: Test.Args+test_args = Test.stdArgs { Test.maxSize = 100, + Test.maxSuccess = 100, + Test.maxDiscardRatio = 20 }++-- | First test: truth table to expression to truth table is the identity.+test_truth1 :: Int -> IO ()+test_truth1 n = Test.quickCheckWith test_args $ aux+ where+ aux = Test.forAll (genBoolList n) $ \x ->+ x == (truth_table_of_exp [1..n] $ exp_of_truth_table 1 x)++-- | Generate a random list of @Int@s.+genIntList :: [Int] -> Int -> Test.Gen [Int]+genIntList vars size = do+ s <- Test.choose (0,size)+ Test.vectorOf s $ Test.elements vars++-- | Generate a random expression out of the given variables.+genExp :: [Int] -> Test.Gen Exp+genExp vars = do+ nber <- Test.choose (0, twoExp (length vars))+ v <- Test.vectorOf nber $ Test.sized $ genIntList vars+ return $ expOfList v++-- | Second test: expression to truth table to expression is the+-- identity.+test_truth2 :: Int -> IO Test.Result+test_truth2 n = Test.quickCheckWithResult test_args $ aux+ where+ aux = Test.forAll (genExp [1..n]) $ \x ->+ x == (exp_of_truth_table 1 $ truth_table_of_exp [1..n] x)++
@@ -0,0 +1,194 @@+-- | This module contains small examples of arithmetic functions,+-- coded in Template Haskell, for use with+-- "Quipper.Libraries.ClassicalOptim.QuickCheck".+module Quipper.Libraries.ClassicalOptim.QuickCheckArith where++import qualified Test.QuickCheck as Test++import Quipper++import Data.List+import Quipper+import Quipper.Libraries.Arith+import Quipper.Utils.Auxiliary+import Prelude hiding (subtract)++-- ----------------------------------------------------------------------+-- * Binary representation of integers++-- | Compute an unsigned integer from its binary representation. The+-- input is a big-headian list of booleans. This means that the head+-- of the list is the most significant digit.+int_of_boollist :: [Bool] -> Integer+int_of_boollist = integer_of_intm_unsigned . intm_of_boollist_bh . reverse++-- | Compute the binary representation of an unsigned integer, using+-- the given number of digits. The output is the binary representation+-- as a big-headian list of booleans.+boollist_of_int :: Int -> Integer -> [Bool]+boollist_of_int m n = reverse (boollist_of_intm_bh $ intm_with_length (Just m) n)++-- ----------------------------------------------------------------------+-- * Circuit templates for common functions++-- | Template Haskell version of 'map'.+template_map :: Circ ((a -> Circ a) -> Circ ([a] -> Circ [a]))+template_map = return $ \func -> return $ \qs -> mapM func qs++-- | Template Haskell version of 'zip'.+template_zip :: Circ ([a] -> Circ ([b] -> Circ [(a,b)]))+template_zip = return $ \a -> return $ \b -> return (zip a b)++-- | Template Haskell version of 'tail'.+template_tail :: Circ ([a] -> Circ [a])+template_tail = return $ \t -> return $ tail t++-- | Template Haskell version of @[]@.+template_symb_obracket_symb_cbracket_ :: Circ [a]+template_symb_obracket_symb_cbracket_ = return []++-- | Monadic version of 'mapAccumL'.+mapAccumLM :: (acc -> x -> Circ (acc,y)) -> acc -> [x] -> Circ (acc,[y])+mapAccumLM f acc [] = return (acc,[])+mapAccumLM f acc (h:t) = do+ (a,y) <- f acc h+ (a',t') <- mapAccumLM f a t+ return (a',y:t') ++-- | Template Haskell version of 'mapAccumL'.+template_mapAccumL :: Circ ((acc -> Circ (x -> Circ (acc,y))) -> Circ (acc -> Circ ([x] -> Circ (acc,[y]))))+template_mapAccumL = return $ \fcirc -> return $ \acc -> return $ \xs ->+ let f = (\acc x -> do+ g <- fcirc acc+ g x)+ in mapAccumLM f acc xs++-- ----------------------------------------------------------------------+-- * Tests++-- ----------------------------------------------------------------------+-- ** Addition++-- | Return the majority of three booleans.+build_circuit+majority :: Bool -> Bool -> Bool -> Bool+majority a b c = if (a `bool_xor` b) then c else a+ +-- majority a b c = a `bool_xor` ((a `bool_xor` b) && (a `bool_xor` c))++-- | Bit adder. The first input is 'False' for adding, and 'True' for+-- subtracting. The second input is a triple consisting of a carry,+-- and two bits to be added. The output consists of the new carry and+-- the sum.+build_circuit+bit_adder :: Bool -> (Bool,Bool,Bool) -> (Bool,Bool)+bit_adder sign (carry, x,y) =+ let y' = y `bool_xor` sign in+ let z = carry `bool_xor` x `bool_xor` y' in+ let carry' = majority carry x y' in+ (carry', z)+++-- | Multi-bit adder. Add two /n/-bit integers, represented as+-- little-tailian bit lists.+build_circuit+adder :: [Bool] -> [Bool] -> [Bool]+adder f l = + reverse $ snd $ fold_right_zip (bit_adder False) (False, reverse l, reverse f)++-- | Test the validity of the functional implementation of 'adder'.+test_adder' :: Test.Property+test_adder' = Test.forAll (Test.choose (0,255)) $ \x ->+ Test.forAll (Test.choose (0,255)) $ \y ->+ boollist_of_int 9 (x + y) + == + adder (boollist_of_int 9 x) (boollist_of_int 9 y)++-- | Wrapper around 'test_adder''.+test_adder :: IO ()+test_adder = Test.quickCheck test_adder'++-- ----------------------------------------------------------------------+-- ** Subtraction++-- | Reversible subtraction.+build_circuit+subtract :: [Bool] -> [Bool] -> [Bool]+subtract f l = + let l' = map not l in + case l' of+ [] -> []+ (h:t) -> let one = (True:(map (\x -> False) t)) in+ adder one (adder f l')++-- | Test the validity of the functional implementation of 'subtract'.+test_subtract' :: Test.Property+test_subtract' = Test.forAll (Test.choose (0,255)) $ \x ->+ Test.forAll (Test.choose (0,255)) $ \y ->+ boollist_of_int 9 x + == + subtract (boollist_of_int 9 (x+y)) (boollist_of_int 9 y)++-- | Wrapper around 'test_subtract''.+test_subtract :: IO ()+test_subtract = Test.quickCheck test_subtract'++-- ----------------------------------------------------------------------+-- ** Multiplication++-- | Pad the second list on the right, to the length of (and using the+-- corresponding elements of) the first list.+build_circuit+pad_right :: [Bool] -> [Bool] -> [Bool]+pad_right shape l = case (shape,l) of+ (shape, []) -> shape+ ([], l) -> l+ ((_:t1), (h:t2)) -> h:(pad_right t1 t2)++-- | Shift a bit list to the right by one.+build_circuit+shift :: [Bool] -> [Bool]+shift l = False:l++-- | 'takeOnly' /shape/ /l/: take the first @('length' /shape/)@+-- elements out of /l/.+build_circuit+takeOnly :: [Bool] -> [Bool] -> [Bool]+takeOnly _ [] = []+takeOnly [] _ = []+takeOnly (h1:t1) (h2:t2) = h2:(takeOnly t1 t2) +++-- | Reversible multiplier stripping high bits.+build_circuit+multiplier' :: [Bool] -> [Bool] -> [Bool]+multiplier' l1 l2 = + let zero = map (\x -> False) (l1 ++ l2) in+ takeOnly l1 $ foldl adder zero (snd (+ mapAccumL + (\acc x -> (False:acc, pad_right zero (acc ++ (map (\y -> y && x) l1)))) [] l2+ ))+++-- | Reversible multiplier keeping high bits.+build_circuit+multiplier :: [Bool] -> [Bool] -> [Bool]+multiplier l1 l2 = + let zero = map (\x -> False) (l1 ++ l2) in+ foldl adder zero (snd (+ mapAccumL + (\acc x -> (False:acc, pad_right zero (acc ++ (map (\y -> y && x) l1)))) [] l2+ ))++-- | Test the validity of the functional implementation of 'multiplier'.+test_multiplier' :: Test.Property+test_multiplier' = Test.forAll (Test.choose (0,255)) $ \x ->+ Test.forAll (Test.choose (0,255)) $ \y ->+ boollist_of_int 18 (x * y) + == + multiplier (boollist_of_int 9 x) (boollist_of_int 9 y)++-- | Wrapper around 'test_multiplier''.+test_multiplier :: IO Test.Result+test_multiplier = Test.quickCheckResult test_multiplier'+
@@ -0,0 +1,23 @@+module Main where++import Test.QuickCheck+import System.Exit++import Quipper.Libraries.ClassicalOptim.QuickCheck+import Quipper.Libraries.ClassicalOptim.QuickCheckAlgExp+import Quipper.Libraries.ClassicalOptim.QuickCheckArith++try :: String -> IO Result -> IO ()+try name body = do+ putStrLn name+ r <- body+ if not (isSuccess r) then exitFailure else return ()++main :: IO ()+main = do+ try "test_truth2 2" $ test_truth2 2+ try "test_truth2 5" $ test_truth2 5+ try "test_truth2 10" $ test_truth2 10+ try "test_multiplier" $ test_multiplier+ try "testCircSimpl" $ myQuickTest testCircSimpl+ try "testCircSwap" $ myQuickTest testCircSwap
@@ -0,0 +1,171 @@+-- | This module contains the interface between the simplified circuit+-- model and Quipper's internal circuit model. The main useful+-- exported functions are: +-- +-- * @'simplify_classical'@, which optimizes a classical circuit such+-- as those coming from Template Haskell;+-- +-- * @'classical_to_reversible_optim'@, which provides a mechanism+-- equivalent to @'Q.classical_to_reversible'@, but with optimization+-- inlined.++module Quipper.Libraries.ClassicalOptim.QuipperInterface where++import Data.Maybe++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import qualified Quipper as Q+import qualified Quipper.Internal.Monad as Q+import qualified Quipper.Internal.QData as Q+import qualified Quipper.Internal.Circuit as Q+import qualified Quipper.Internal.Generic as Q+import qualified Quipper.Internal.Printing as Q+import qualified Quipper.Libraries.Simulation as Q+import qualified Quipper.Libraries.Simulation.ClassicalSimulation as Q+import qualified Quipper.Internal.Transformer as Q+import qualified Quipper.Internal.Control as Q ++import qualified Quipper.Libraries.Arith as Q+import qualified Quipper.Utils.Auxiliary as Q++import Quipper.Libraries.ClassicalOptim.Circuit+import Quipper.Libraries.ClassicalOptim.Simplification+++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Extract the list of wires from a piece of quantum data.+getListWire :: (Q.QData qc) => qc -> [Wire]+getListWire x = map Q.wire_of_qubit $ Q.qubits_of_qdata x++-- ----------------------------------------------------------------------+-- * Quipper circuits to simple circuits++-- | Translates a Quipper circuit to a simple circuit. The only gates+-- considered are initializations, terminations, and multi-controlled+-- NOT gates. All other gates are ignored.+-- +-- Note that simple circuits do not possess termination wires: these+-- wires are simply not terminated, and all subsequent initializations+-- using the same wire ID are sent to fresh wires.+-- +-- The state of this function is a bit complex, as it keeps track of+-- where the output wires are mapped to.+quipperGateToMyGate :: (IS.IntSet,IM.IntMap Wire,Wire) -> Q.Gate -> ((IS.IntSet,IM.IntMap Wire,Wire), Maybe Gate)+quipperGateToMyGate (s,m,f) (Q.QGate "not" _ [w] _ ctls _) = + ((s,m,f), Just $ Cnot (IM.findWithDefault w w m) $ map (\(Q.Signed a b) -> (IM.findWithDefault a a m,b)) ctls)+quipperGateToMyGate (s,m,f) (Q.QInit b w _) = case (IS.member w s) of + True -> ((s,IM.insert w f m,f+1), Just $ Init b f)+ False -> ((s,m,f), Just $ Init b w)+quipperGateToMyGate (s,m,f) (Q.QTerm b w _) = ((IS.insert w s, m,f), Nothing)+quipperGateToMyGate smf _ = (smf, Nothing)+++-- | Get the wire initialized by the gate, if it is an initialization gate.+quipperGateInitW :: Q.Gate -> Maybe Wire+quipperGateInitW (Q.QInit _ w _) = Just w+quipperGateInitW _ = Nothing++-- | Given a list of Quipper gates, get the smallest wire id not in use.+quipperGateFreshWire :: Wire -> [Q.Gate] -> Wire+quipperGateFreshWire w gs = (+) 1 $ L.foldl' max w $ catMaybes $ map quipperGateInitW gs++-- | Send a Quipper 'Q.Circuit' to a 'CircState'.+quipperCircuitToMyCirc :: Q.Circuit -> CircState+quipperCircuitToMyCirc (_, gs, _, n) = + emptyState { + circuit = catMaybes $ snd $ L.mapAccumL quipperGateToMyGate (IS.empty,IM.empty,quipperGateFreshWire n gs) gs,+ freshWire = n+ }++-- | Send a Quipper 'Q.BCircuit' to a 'CircState'.+quipperBCircuitToMyCirc :: Q.BCircuit -> CircState+quipperBCircuitToMyCirc (c,_) = quipperCircuitToMyCirc c++-- | Generate a custom error message.+myCircErrMsg :: String -> String+myCircErrMsg s = "myCirc: " ++ s++-- | Given a Quipper circuit generating function and a shape argument,+-- return a simple circuit together with the list of non-garbage+-- circuit outputs.+quipperFunToMyCirc :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> (CircState,[Wire])+quipperFunToMyCirc f shape =+ let (_, bc, output) = Q.encapsulate_generic myCircErrMsg f shape+ in (quipperBCircuitToMyCirc bc, + getListWire output)++-- ----------------------------------------------------------------------+-- * Simple circuits to Quipper circuits++-- | Translate a gate from the simple circuit model into a Quipper gate.+myGateToQuipperGate :: Gate -> Q.Gate+myGateToQuipperGate (Cnot w ctls) = Q.QGate "not" True [w] [] (map (\(w,b) -> Q.Signed w b) ctls) False+myGateToQuipperGate (Init b w) = Q.QInit b w False+myGateToQuipperGate NoOp = error "myGateToQuipperGate cannot deal with NoOp"++-- | Generate a Quipper comment. The first argument is a comment+-- string, and the second argument is a label to apply to the qubits+-- in the third argument.+makeComment :: String -> String -> [Wire] -> Q.Gate+makeComment comment label ws = + Q.Comment comment False $ map (\(i,x) -> (i, label ++ "[" ++ (show x) ++ "]")) (zip ws [0..(length ws)-1])+++-- ----------------------------------------------------------------------+-- * Algebraic optimization of Quipper circuits++-- | Optimize a Quipper 'Q.BCircuit'. The second argument is the list+-- of non-garbage outputs. A corresponding list of outputs is also+-- returned along with the circuit.+quipperBCircuitSimpl :: Q.BCircuit -> [Wire] -> (Q.BCircuit,[Wire])+quipperBCircuitSimpl (c,e) output = (((a1,c'',a2',n'),e),o')+ where+ (a1,gs,a2,n) = c+ mycirc = quipperCircuitToMyCirc c+ (c',o') = compressWires (IM.keys a1) $ simplRec $ (\x -> (x,output)) {-set_init_first output-} $ circuit $ mycirc+ i' = IM.keys a1+ c'' = (makeComment "Start classical circuit" "in" i') : + (map myGateToQuipperGate c') ++ + [makeComment "End classical circuit" "out" o']+ allwires = getAllWires c'+ a2' = IM.fromList $ map (\x -> (x,Q.Qbit)) $ IS.toAscList allwires+ n' = (+) 1 $ head $ IS.toDescList allwires+++-- | Optimize a Quipper circuit producing function (together with a+-- shape argument). Return the optimized circuit as a Quipper+-- 'Q.BCircuit', along with a list of the non-garbage circuit outputs.+simplify_classical' :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> (Q.BCircuit, [Wire])+simplify_classical' f shape = + let (_,bc,output) = Q.encapsulate_generic myCircErrMsg f shape in+ let list_output = getListWire output in+ quipperBCircuitSimpl bc list_output++-- | Optimize a Quipper circuit-producing function. This assumes that+-- the function only consists of pseudo-classical quantum gates, i.e.,+-- initializations, terminations, and (possibly multiply controlled)+-- NOT gates. The behavior on other kinds of circuits is undefined.+-- The second argument is a shape parameter.+simplify_classical :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> Q.Circ y+simplify_classical f shape =+ let (input,bc,output) = Q.encapsulate_generic myCircErrMsg f shape in+ let list_output = getListWire output in+ let (bc',list_output') = quipperBCircuitSimpl bc list_output in+ Q.unencapsulate_generic (input,bc', Q.qdata_of_qubits output $ map Q.qubit_of_wire list_output') shape++-- | Like 'Q.classical_to_reversible', but also apply circuit optimization.+classical_to_reversible_optim :: (Q.QData qa, Q.QData qb) => (qa -> Q.Circ qb) -> ((qa,qb) -> Q.Circ (qa,qb))+classical_to_reversible_optim f = Q.classical_to_reversible (simplify_classical f)++-- | Like 'classical_to_reversible_optim', but insert the optimized+-- circuit as a boxed subroutine.+box_classical_to_reversible_optim :: (Q.QData qa, Q.QData qb) => String -> (qa -> Q.Circ qb) -> ((qa,qb) -> Q.Circ (qa,qb))+box_classical_to_reversible_optim s f = Q.classical_to_reversible (Q.box s $ simplify_classical f)+
@@ -0,0 +1,1003 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE DoAndIfThenElse #-}++-- | This module contains the core of the classical circuit+-- optimization algorithm.+module Quipper.Libraries.ClassicalOptim.Simplification where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import qualified Control.DeepSeq as Seq++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++import qualified Quipper.Utils.Auxiliary as Q++import Quipper.Libraries.ClassicalOptim.Circuit+import Quipper.Libraries.ClassicalOptim.AlgExp++-- ----------------------------------------------------------------------+-- * Auxiliary definitions++-- | Internal definition of a trace, for debugging purposes. This is a+-- no-op, but can be replaced to turn on debugging.+trace :: String -> b -> b+trace a b = b++-- | Change a wire ID in a gate. The first two arguments are the old+-- and the new wire ID.+moveWire :: Wire -> Wire -> Gate -> Gate+moveWire from to NoOp = NoOp+moveWire from to (Init b w) = if (w == from) then error "moveWire" else (Init b w)+moveWire from to (Cnot w ctls) = Cnot w' ctls'+ where+ w' = if (from == w) then to else w+ ctls' = map moveCtls ctls+ moveCtls (w,b) = if (from == w) then (to,b) else (w,b)++-- | Flip the control on the given wire (from positive to negative or+-- vice versa).+flipCtl :: Wire -> Gate -> Gate+flipCtl _ NoOp = NoOp+flipCtl _ (Init b w) = Init b w+flipCtl w (Cnot w' ctls) = Cnot w' $ map (\(x,b) -> if (x == w) then (x,not b) else (x,b)) ctls++-- | Change a wire ID in a gate and flip the potential control.+moveWireFlip :: Wire -> Wire -> Gate -> Gate+moveWireFlip from to NoOp = NoOp+moveWireFlip from to (Init b w) = if (w == from) then error "moveWire" else (Init b w)+moveWireFlip from to (Cnot w ctls) = Cnot w' ctls'+ where+ w' = if (from == w) then to else w+ ctls' = map moveCtls ctls+ moveCtls (w,b) = if (from == w) then (to,b) else if (to == w) then (w,not b) else (w,b)++-- ----------------------------------------------------------------------+-- * Small, simple optimizations++-- | Suppress gates acting on garbage wires, i.e., wires that are not in the input set. +suppress_garbage :: [Gate] -> IS.IntSet -> [Gate]++suppress_garbage ((Cnot w ctls):gs) used = + if (IS.member w used) then g:gs1 else gs2+ where+ g = Cnot w ctls+ gs1 = suppress_garbage gs $ IS.union (IS.insert w used) $ IS.fromList $ L.map fst ctls+ gs2 = suppress_garbage gs used++suppress_garbage (g:gs) used = g:(suppress_garbage gs used)++suppress_garbage [] _ = []+++-- | Like 'suppress_garbage', but packaged in a manner that is friendly for composition.+suppressGarbageGates :: ([Gate],[Wire]) -> ([Gate],[Wire])++suppressGarbageGates (gs,out) = (reverse $ suppress_garbage (reverse gs) $ IS.fromList out, out)+++-- ----------------------------------------------------------------------+-- * Compression of wire numbering++-- $ As the optimization process goes on, many /init/ gates will end+-- up being discarded. The function 'compressWires' compacts the wire+-- numbering scheme to make a smaller circuit.++-- | Get the set of all wires used by the circuit.+getAllWires :: [Gate] -> IS.IntSet+getAllWires gs = L.foldl' IS.union IS.empty $ L.map aux gs+ where+ aux (Cnot w ctls) = IS.insert w $ L.foldl' (flip IS.insert) IS.empty $ L.map fst ctls+ aux (Init _ w) = IS.singleton w+ aux NoOp = IS.empty++-- | Get the set of wires initialized by the circuit.+getInitWires :: [Gate] -> IS.IntSet+getInitWires gs = L.foldl' IS.union IS.empty $ map aux gs+ where+ aux (Cnot _ _) = IS.empty+ aux (Init _ w) = IS.singleton w+ aux NoOp = IS.empty++-- | Get the set of input wires, i.e., the ones that are used but not initialized.+getInputWires :: [Gate] -> IS.IntSet+getInputWires gs = IS.difference (getAllWires gs) (getInitWires gs)++-- | Compress the wire numbering.+compressWires :: [Wire] -> ([Gate],[Wire]) -> ([Gate],[Wire])+compressWires inputwires (gs,output) = (gs',out')+ where+ iws = getInitWires gs+ begin = if inputwires == []+ then 0+ else 1 + (head $ reverse $ L.sort inputwires)+ end = begin + (IS.size iws)+ listmap = zip ([0..begin-1] ++ (IS.toAscList iws)) [0 .. end]+ remap = M.fromList $ trace (show listmap) listmap+ out' = map (remap M.!) output+ gs' = map (rewire remap) gs+ + rewire m (Cnot w ctls) = Cnot (m M.! w) $ map (\(x,b) -> (m M.! x, b)) ctls+ rewire m (Init b w) = Init b (m M.! w)+ rewire m NoOp = NoOp+++-- ----------------------------------------------------------------------+-- * A useful data structure++-- $ When considering a particular point in a circuit (i.e., in a list+-- of gates), to decide whether a given wire is used or controlled+-- before or after, we keep a data-structure 'UsedWire'.++-- | The type of gate IDs.+type GateId = Int++-- | A set of gate IDs.+type GateIdSet = IS.IntSet++-- | A map from wires to pairs of 'GateId's. The left member gives the+-- ID of the first gate using the wire, and the right member gives the+-- ID of the last gate using the wire.+type UsedWire = IM.IntMap GateIdSet++-- | Get the minimum of a set of gate IDs.+gateIdFindMin :: GateIdSet -> Maybe GateId+gateIdFindMin g = if (IS.null g) then Nothing else Just (IS.findMin g)++-- | Get the maximum of a set of gate IDs.+gateIdFindMax :: GateIdSet -> Maybe GateId+gateIdFindMax g = if (IS.null g) then Nothing else Just (IS.findMax g)++-- | Get the pair corresponding to the given wire.+pairUsedWire :: UsedWire -> Wire -> GateIdSet+pairUsedWire m w = IM.findWithDefault IS.empty w m++-- | Get the first gate using the wire in the future.+firstUsedWire :: UsedWire -> Wire -> Maybe GateId+firstUsedWire = curry $ gateIdFindMin . (uncurry pairUsedWire)++-- | Get the last gate using the wire in the past. Return 0 if none.+lastUsedWire :: UsedWire -> Wire -> GateId+lastUsedWire w w'= + case (curry $ gateIdFindMax . (uncurry pairUsedWire)) w w' of+ Just w -> w+ Nothing -> 0++-- | 'nextUsedGate' /ws/ /g/ /g/' /w/: Look for the next gate in /ws/+-- corresponding to wire /w/, starting from /g/. Return /g/' if none.+nextUsedGate :: UsedWire -> GateId -> GateId -> Wire -> GateId+nextUsedGate ws g g' w =+ case (do gs <- IM.lookup w ws; IS.lookupGT g gs) of+ Just g -> g+ Nothing -> g'++-- | For each wire, find the set of gates placing a control on it.+circuitControlWires :: GateId -> [Gate] -> UsedWire+circuitControlWires id gs = aux id IM.empty gs+ where+ aux _ m [] = m+ aux g m (Init _ _:gs) = aux (g+1) m gs+ aux g m ((Cnot _ ctls):gs) = aux (g+1) m' gs+ where+ wires = map fst ctls+ m' = L.foldl (\m'' w -> IM.alter (f g) w m'') m wires+ f g Nothing = Just $ IS.singleton g+ f g (Just s) = Just $ IS.insert g s+ aux g m (NoOp:_) = error "circuitControlWires cannot deal with NoOp"++-- | For each wire, find the set of gates acting on it with NOT.+circuitNotWires :: GateId -> [Gate] -> UsedWire+circuitNotWires id gs = aux id IM.empty gs+ where+ aux _ m [] = m+ aux g m (Init _ _:gs) = aux (g+1) m gs+ aux g m ((Cnot w _):gs) = aux (g+1) m' gs+ where+ m' = IM.alter (f g) w m+ f g Nothing = Just $ IS.singleton g+ f g (Just s) = Just $ IS.insert g s+ aux g m (_:gs) = aux (g+1) m gs++-- ----------------------------------------------------------------------+-- * Algebraic optimization method++-- $ To each wire in a circuit, we attach a set of formulas. At each+-- iteration, the wire that gets modified is updated with its new+-- value, using all the possible values, possibly together with a+-- fresh variable. At each iteration, we also strip away the+-- expressions that get too large. Here, the size of an algebraic+-- expression is measured by the 'exp_length' function.++-- | Calculate the size of an algebraic expression.+exp_length :: Exp -> Int+exp_length e = L.foldl' (+) 0 $ L.map (\x -> let y = IS.size x in seq y y) $ S.toList e++-- | Given a list of sets of expressions, form the conjunction of+-- every possible choice of one expression from each set. For example.+-- +-- > exp_list_and [{a,b}, {c,d}, {e,f}] = +-- > [a∧c∧e, a∧c∧f, a∧d∧e, a∧d∧f, b∧c∧e, b∧c∧f, b∧d∧e, b∧d∧f].+exp_list_and :: [S.Set Exp] -> S.Set Exp+exp_list_and [] = S.singleton exp_true+exp_list_and [l] = l+exp_list_and (h:k:t) = exp_list_and (S.fromList [exp_and x y | x <- S.toList h, y <- S.toList k]:t)++-- | Evaluate a control with respect to a state.+expEvalCtl :: (IM.IntMap (S.Set (Exp,Int))) -> (Wire,Bool) -> S.Set Exp+expEvalCtl m (w,True) = S.map fst (m IM.! w)+expEvalCtl m (w,False) = S.map exp_not $ S.map fst $ (IM.!) m w++-- | Evaluate a gate with respect to a state.+expEvalGate :: (IM.IntMap (S.Set (Exp,Int))) -> Gate -> IM.IntMap (S.Set (Exp,Int))++expEvalGate m (Init False w) = IM.insert w (S.singleton (exp_false,0)) m+expEvalGate m (Init True w) = IM.insert w (S.singleton (exp_true,1)) m+expEvalGate m NoOp = m++expEvalGate m (Cnot w ctls) = IM.insert w cnot m+ where+ ands = exp_list_and $ L.map (expEvalCtl m) ctls+ cnot = S.map (\x -> (x,exp_length x)) (S.fromList [exp_xor x y |+ x <- S.toList $ S.map fst $ (IM.!) m w,+ y <- S.toList ands ])++-- ----------------------------------------------------------------------+-- ** State of the optimization automaton++-- | The state of the automaton. This contains in particular the+-- current state, the past and future gates, and a fresh variable.+data ExpState = ExpState {+ gates_to_skip :: IM.IntMap Gate, -- ^ For use with 'stepSwapCirc'.+ allWiresInCirc :: IS.IntSet, -- ^ All the wires in the circuit.+ gateId :: GateId, -- ^ ID of the first gate in the future (starts at 1).+ usedControlWires :: UsedWire, -- ^ Location of the controls.+ usedNotWires :: UsedWire, -- ^ Location of the NOT gates.+ future :: [Gate], -- ^ Gates left to explore.+ past :: [Gate], -- ^ Gates already explored.+ expMap :: IM.IntMap (S.Set (Exp,Int)), -- ^ Algebraic state of the wires. Also contains the size of the expression, so we don't have to recompute it each time.+ freshVar :: Integer, -- ^ The next fresh wire.+ outWires :: [Wire], -- ^ The output wires.+ sizeCirc :: Int -- ^ Size of the circuit.+ }+++instance Seq.NFData Gate where+ rnf (Init a b) = a `seq` b `seq` ()+ rnf (Cnot w ctls) = ctls `Seq.deepseq` w `Seq.deepseq` ()+ rnf NoOp = ()++{-+instance Seq.NFData ExpState where+ rnf e = {-allWiresInCirc e `Seq.deepseq` gateId e `Seq.deepseq` usedControlWires e `Seq.deepseq` usedNotWires e `Seq.deepseq` future e `Seq.deepseq` past e `Seq.deepseq` expMap e `Seq.deepseq` freshVar e `Seq.deepseq` outWires e-} () `Seq.deepseq` ()+-}++-- | The initial state for a given set of parameters.+initExpState :: IS.IntSet -> [Wire] -> [Gate] -> ExpState+initExpState ws_in ws_out gs = ExpState {+ gates_to_skip = IM.empty,+ allWiresInCirc = getAllWires gs,+ gateId = 1,+ usedControlWires = circuitControlWires 1 gs,+ usedNotWires = circuitNotWires 1 gs,+ future = gs,+ past = [],+ expMap = IM.fromList $ L.map (\x -> (x, S.singleton (exp_var x, 1))) $ IS.toAscList ws_in,+ freshVar = fromIntegral $ (+) 1 $ IS.findMax ws_in,+ outWires = ws_out,+ sizeCirc = length gs+ }++-- ----------------------------------------------------------------------+-- ** The state monad++-- | The state monad corresponding to 'ExpState'.+data EvalCirc a = EvalCirc (ExpState -> (ExpState, a))++instance Monad EvalCirc where+ return x = EvalCirc (\y -> (y,x))+ (>>=) (EvalCirc c) f = EvalCirc (\s -> let (s',x) = c s in+ let (EvalCirc c') = f x in+ c' s')++instance Applicative EvalCirc where+ pure = return+ (<*>) = ap++instance Functor EvalCirc where+ fmap = liftM++-- ----------------------------------------------------------------------+-- ** Low-level access functions++-- | Construct an @'ExpState'@ out of an @'EvalCirc'@.+runEvalCirc :: IS.IntSet -> [Wire] -> [Gate] -> EvalCirc a -> ExpState+runEvalCirc ws_in ws_out gs (EvalCirc e) = fst $ e $ initExpState ws_in ws_out gs++-- | Retrieve the state.+getExpState :: EvalCirc ExpState+getExpState = EvalCirc (\s -> (s,s))++-- | Set the state.+setExpState :: ExpState -> EvalCirc ()+setExpState s = EvalCirc (\_ -> (s,()))++-- ----------------------------------------------------------------------+-- ** Higher-level access functions++-- | Create a fresh variable+newFreshVar :: EvalCirc Integer+newFreshVar = do+ s <- getExpState+ let v = freshVar s+ setExpState (s { freshVar = v + 1 }) + return v++-- | Pull a new gate to be analyzed out of the future.+pullNewGate :: EvalCirc (Maybe Gate)+pullNewGate = do+ s <- getExpState+ case (future s) of+ (h:t) -> do setExpState (s { future = t } )+ return (Just h)+ [] -> return Nothing++-- | Modify the future gates.+changeFuture :: [Gate] -> EvalCirc ()+changeFuture gs = do+ s <- getExpState+ setExpState (s { future = gs } )+ return ()++-- | Update the future using the given parameter function. Return two sets+-- of 'gateId's that got modified: the first set concerns the controls,+-- the second set the NOT gates.+updateFuture :: (Gate -> Gate) -> EvalCirc (IS.IntSet,IS.IntSet)+updateFuture f = do+ s <- getExpState+ let ((_,!gsModifCtls,!gsModifNots),new_future) = + L.mapAccumL (\(gid,gs,gs') g -> let g' = f g in+ ((+ gid+1+ ,+ if (ctlsOfGate g == ctlsOfGate g') + then gs+ else IS.insert gid gs+ ,+ if (wireOfGate g == wireOfGate g') + then gs'+ else IS.insert gid gs'+ ),+ g'))+ (1 + (gateId s), IS.empty,IS.empty) (future s)+ changeFuture new_future+ + return (gsModifCtls,gsModifNots)++-- | Store a gate in the past.+storeOldGate :: Gate -> EvalCirc ()+storeOldGate g = do+ s <- getExpState+ let p = past s+ seq g $ seq p $ setExpState (s { past = g:p } )+ return ()++-- | Increase the '@gateId@' (i.e., go forward).+incrGateId :: EvalCirc ()+incrGateId = do+ s <- getExpState+ setExpState (s { gateId = 1 + (gateId s) } )+ return ()++-- | Get the set of all wires.+getAllWiresInCirc :: EvalCirc IS.IntSet+getAllWiresInCirc = do+ s <- getExpState+ return (allWiresInCirc s)++-- | Set the set of all wires.+setAllWiresInCirc :: IS.IntSet -> EvalCirc ()+setAllWiresInCirc ws = do+ s <- getExpState+ ws `seq` setExpState (s {allWiresInCirc = ws})+ return ()++-- | Remove a gate from the set of all wires.+removeFromAllWiresInCirc :: Int -> EvalCirc ()+removeFromAllWiresInCirc w = do+ ws <- getAllWiresInCirc+ setAllWiresInCirc $ IS.delete w ws+ return ()++-- | Get the algebraic representation of the set of wires.+getExpMap :: EvalCirc (IM.IntMap (S.Set (Exp,Int)))+getExpMap = do+ s <- getExpState+ s `seq` return (expMap s)++-- | Set the algebraic representation of the state of wires.+setExpMap :: (IM.IntMap (S.Set (Exp,Int))) -> EvalCirc ()+setExpMap m = do+ s <- getExpState+ m `seq` setExpState (s { expMap = m } )+ return ()++-- | Update the database recording the controlled wires.+updateUsedControlWires :: (UsedWire -> UsedWire) -> EvalCirc ()+updateUsedControlWires f = do+ s <- getExpState+ let c = f $ usedControlWires s+ c `seq` setExpState (s { usedControlWires = c } )+ return ()++-- | Update the database recording the NOT gates.+updateUsedNotWires :: (UsedWire -> UsedWire) -> EvalCirc ()+updateUsedNotWires f = do+ s <- getExpState+ let c = f $ usedNotWires s+ c `seq` setExpState (s { usedNotWires = c } )+ return ()++-- | Update the list of output wires.+updateOutWires :: ([Wire] -> [Wire]) -> EvalCirc ()+updateOutWires f = do+ s <- getExpState+ let c = f $ outWires s+ c `seq` setExpState (s { outWires = c } )+ return ()++-- | Add a gate ID to the list of gates to skip.+addToSkipGates :: GateId -> Gate -> EvalCirc ()+addToSkipGates id g = do+ s <- getExpState+ let c = IM.insert id g (gates_to_skip s)+ c `seq` setExpState (s {gates_to_skip = c} )+ return ()++-- | Send a gate to the end of the future.+sendEndOfTime :: Gate -> EvalCirc ()+sendEndOfTime g = do+ s <- getExpState+ changeFuture ((future s) ++ [g])+ return ()++-- | Place a gate at the given gate ID in the future.+shiftGate :: Gate -> GateId -> EvalCirc ()+shiftGate g x = do+ s <- getExpState+ let (!head, !tail) = splitAt x (future s)+ let z = head ++ [g] ++ tail+ z `Seq.deepseq` changeFuture z+ return ()++-- ----------------------------------------------------------------------+-- ** Auxiliary functions++-- | @pairEqualExp m1 m2 ws@: returns a list of pairs of wires @(x,y)@+-- such that @m2 x = m1 x = m1 y@.+pairEqualExp :: (IM.IntMap [Exp]) -> (IM.IntMap [Exp]) -> [Wire] -> [(Wire,Wire)]+pairEqualExp m1 m2 ws = + L.map fst $ L.filter aux $ L.zip pair_ws (L.map value pair_ws)+ where+ all_pairs l = [(x,y) | x <- l, y <- l]+ pair_ws = all_pairs ws+ value (x,y) = (m2 IM.! x, m1 IM.! x, m1 IM.! y)+ aux ((_,_),(a,b,c)) = a == b && b == c++-- | From a set of expressions (annotated with sizes), prune the ones+-- whose size is larger than /n/.+pruneListExp :: Int -> S.Set (Exp,Int) -> S.Set (Exp,Int)+pruneListExp n l = S.filter (\x -> snd x <= n) l+++-- ----------------------------------------------------------------------+-- ** The algebraic optimization automaton + +-- | Perform a set of filters acting on one gate at a time, looking+-- for:+-- +-- * gates having no effect;+--+-- * orphan NOT-gates (i.e. NOT gates negating an out-wire) ;+-- +-- * simple copy-cats (both positive and negative) ;+--+-- * hidden copy-cats.+--+-- Return 'False' when the end of the circuit is reached, 'True' otherwise. +stepEvalCirc :: EvalCirc Bool+stepEvalCirc = do+ m_before <- getExpMap+ trace ("the state of the system is " ++ (show $ m_before)) $ return ()+ s <- getExpState+ if ((gateId s) `mod` 1000 == 0) then trace ("Timestamp... " ++ (show (gateId s))) (return ()) else return ()+ s <- getExpState+ trace ("outside wires " ++ (show $ outWires s)) $ return ()+ maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ case maybe_g of++ Nothing -> return False+ + Just g -> do -- analyze the gate+ m_before <- getExpMap+ let m_after = expEvalGate m_before g+ + case g of+ + NoOp -> error "stepEvalCirc cannot deal with NoOp"+ + Init b w | not ((IM.member w $ usedNotWires s) || (IM.member w $ usedControlWires s) || L.elem w (outWires s))-> do + trace "got an orphan init, removing it" $ return ()+ storeOldGate NoOp -- store a placeholder for the gate+ incrGateId+ removeFromAllWiresInCirc w+ -- we could also clean expMap from the reference to w but I think it makes no gain+ return True+ + Init _ _ -> do+ trace "got a regular init" $ return ()+ storeOldGate g+ setExpMap m_after+ incrGateId+ return True+ + Cnot w _ | not $ S.null $ S.intersection (m_before IM.! w) (m_after IM.! w) -> do+ trace "got a cnot where no change happened..." $ return ()+ trace (show m_before) $ return ()+ trace (show m_after) $ return ()+ storeOldGate NoOp+ incrGateId+ return True++ Cnot w [] | not (L.elem w $ outWires s) -> do+ trace "got a not-gate that can be removed..." $ return ()+ s <- getExpState+ -- update future+ changeFuture $ L.map (flipCtl w) $ future s+ s <- getExpState+ trace (show $ future s) $ return ()+ storeOldGate NoOp+ incrGateId+ return True+ + + Cnot w ctls | otherwise -> do+ trace "got a general cnot" $ return ()+ trace ("state after the gate is " ++ (show m_after)) $ return ()+ + allWs <- getAllWiresInCirc+ s <- getExpState+ let my_elem x = not (L.elem x $ outWires s)+ let all_ws = IS.toAscList $ IS.filter future_ctl $+ IS.filter (\x -> my_elem x) $ -- not (L.elem x $ outWires s)) $+ IS.filter (\x -> not $ S.null $ + S.intersection (m_after IM.! x)+ (m_after IM.! w)) $+ IS.filter (w /=) allWs -- IS.fromList $ L.map fst ctls + where+ future_ctl x = + (lastUsedWire (usedNotWires s) x) <= gateId s+ &&+ (lastUsedWire (usedNotWires s) w) <= gateId s++ let all_ws_neg = IS.toAscList $ IS.filter future_ctl $+ IS.filter (\x -> not (L.elem x $ outWires s)) $+ IS.filter (\x -> not $ S.null $ + S.intersection (m_after IM.! x)+ (S.map (\(e,i) -> (exp_not e, i)) (m_after IM.! w))) $+ IS.filter (w /=) $ IS.fromList $ L.map fst ctls+ where+ future_ctl x = + (lastUsedWire (usedNotWires s) x) <= gateId s+ &&+ (lastUsedWire (usedNotWires s) w) <= gateId s++ trace ("List of outside wires: " ++ (show $ outWires s)) (return ())+ trace ("List of available wires: " ++ (show all_ws)) (return ())+ trace ("List of available wires with neg: " ++ (show all_ws_neg)) (return ())+ + + case all_ws of+ + [] -> do+ + case all_ws_neg of+ [] -> do+ + -- There is no "simple" copy-cat...+ -- Let's try to find a hidden one.+ + s <- getExpState++ -- This helper function take a wire and look in+ -- the past for the closest cnot acting on it+ let getOlderCnot w = case (do set <- IM.lookup w (usedNotWires s); IS.lookupLT (gateId s) set) of+ Nothing -> Nothing -- there is no previous not+ Just g' -> -- there is one not... let's check that it is a cnot+ case ((past s) !! ((gateId s) - g' - 1)) of+ Cnot _ [ctl] -> Just (g',ctl)+ _ -> Nothing+ + -- Helper acting on controls: only return+ -- something if it is a single control.+ let getOlderCnot_actOnCtls w1 [(w,b)] = do -- monad Maybe+ other_ctl <- getOlderCnot w1+ other_ctl `seq` return ((w,b),other_ctl)+ getOlderCnot_actOnCtls _ _ = Nothing+ + let retrieveHiddenCnot w1 ctls = do -- monad Maybe+ -- if (L.elem w $ outWires s) then Nothing+ -- else return ()+ ((w2,b2),(g',(w3,b3))) <- getOlderCnot_actOnCtls w1 ctls+ -- make sure w2 and w3 are distinct+ if (w2 == w3) then Nothing else return ()+ let m = m_after+ -- check for the property w1 == w2 oplus w3+ if (S.null $ S.intersection+ (S.fromList [exp_xor x y | (x,_) <- S.toList (m IM.! w2), (y,_) <- S.toList (m IM.! w3)])+ (S.fromList [x | (x,_) <- S.toList (m IM.! w1)]))+ then Nothing+ -- We have two CNOT candidates for hidden copy-cat.+ else if ((not (L.elem w2 $ outWires s))+ &&+ (lastUsedWire (usedNotWires s) w2) <= gateId s+ &&+ (lastUsedWire (usedControlWires s) w2) <= gateId s)+ then Just ((w2,b2),(w3,b3))+ else if ((not (L.elem w3 $ outWires s))+ &&+ (lastUsedWire (usedNotWires s) w3) <= g'+ &&+ (lastUsedWire (usedControlWires s) w3) <= g')+ then Just ((w3,b3),(w2,b2))+ else Nothing+ + case retrieveHiddenCnot w ctls of+ Just ((w2,b2),(w3,b3)) -> -- we have a hidden cnot candidate. Great.+ -- w2 is the wire that is not used with NOT in future+ do+ + trace "found one hidden copy-cat" $ return ()+ updateOutWires $ map (\x -> if x == w then w2 else x) + + (gsModifCtls,gsModifNots) <- updateFuture $ moveWire w w2+ + trace ("moving " ++ (show w) ++ " to " ++ (show w2)) $ return ()+ trace (show gsModifCtls) $ return ()+ trace (show gsModifNots) $ return ()+ + + s <- getExpState+ trace ("before: usedNotWire = " ++ (show $ usedNotWires s)) $ return ()++ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls) w2 $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ + updateUsedControlWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w2 c+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (gateId s) gs+ Nothing -> Just $ IS.singleton (gateId s)) w3 c+ + updateUsedNotWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifNots+ Nothing -> Just gsModifNots) w2 $ + IM.update (\gs -> Just $ IS.difference gs gsModifNots) w c+ + updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w $+ IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (gateId s) gs+ Nothing -> Just $ IS.singleton (gateId s)) w2 c+ + s <- getExpState+ trace ("after: usedNotWire = " ++ (show $ usedNotWires s)) $ return ()++ -- Update ExpMap+ setExpMap $ IM.insert w (m_before IM.! w) $+ IM.insert w2 (m_after IM.! w) m_after+ + storeOldGate $ Cnot w2 [(w3,True)]+ incrGateId+ return True+ + _ -> -- No hidden Cnot, let's proceed...+ do+ let mw = m_after IM.! w+ f <- if ((S.foldl' (\a (_,i) -> min a i) 3 mw) <= 1) + then return id + else do+ v <- newFreshVar+ return (S.insert (exp_var $ fromIntegral v, 1))+ setExpMap $ IM.adjust (\a -> pruneListExp 3 a) w $ + IM.adjust f w m_after+ + storeOldGate g+ incrGateId+ return True++ -----------------+ -- Case of simple copy-cats+ (w':_) -> do+ s <- getExpState+ updateOutWires $ map (\x -> if x == w then w' else x)+ + s <- getExpState+ trace (show $ future s) $ return ()+ (gsModifCtls,_) <- updateFuture $ moveWireFlip w w'+ + -- update expMap: now, w is null and w' is not(old w)+ expMap <- getExpMap+ + setExpMap $ IM.insert w (m_before IM.! w) $+ IM.insert w' (S.map (\(e,i) -> (exp_not e,i)) (expMap IM.! w')) expMap+ + + + trace ("moving " ++ (show w) ++ " to " ++ (show w')) $ return ()+ trace (show gsModifCtls) $ return ()+ s <- getExpState+ trace (show $ future s) $ return ()+ + s <- getExpState+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls) w' $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w c++ storeOldGate (Cnot w' []) -- Set a flip on the w' wire + + incrGateId+ + return True+ + + + (w':_) -> do+ + s <- getExpState+ updateOutWires $ map (\x -> if x == w then w' else x) ++ s <- getExpState+ trace (show $ future s) $ return ()+ trace ("usedNotWire = " ++ (show $ usedNotWires s)) $ return ()+ (gsModifCtls,_) <- updateFuture $ moveWire w w'+ + trace ("moving " ++ (show w) ++ " to " ++ (show w')) $ return ()+ trace (show gsModifCtls) $ return ()+ s <- getExpState+ trace (show $ future s) $ return ()+ + s <- getExpState+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls+ ) w' $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w c+ + storeOldGate NoOp -- replace g with NoOp so that gateId stays accurate+ incrGateId+ + return True+++-- | Shuffle the circuit by sending the CNOT gates as far as+-- possible (i.e., until they hit a control, or to the end).+-- Return 'False' when the end of the circuit is reached, 'True' otherwise. +stepSwapCirc :: EvalCirc Bool+stepSwapCirc = do+ s <- getExpState+ + case (IM.lookup (gateId s) (gates_to_skip s)) of+ + Just g -> do + + storeOldGate g+ incrGateId+ return True+ + Nothing -> do+ + maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ if ((gateId s) `mod` 1000 == 0) then trace ("Timestamp (swap)... " ++ (show (gateId s))) {-(s `Seq.deepseq` (setExpState s))-} (return ()) else return ()+ case maybe_g of+ + Nothing -> return False+ + Just g@(Cnot w1 [(w2,b2)]) | IM.notMember (gateId s) (gates_to_skip s) -> do -- got a CNOT+ + trace ("got a cnot to analyze " ++ (show $ gateId s) ++ " " ++ (show $ gates_to_skip s)) $ return ()+ + let id = min (nextUsedGate (usedNotWires s) (gateId s) (1 + sizeCirc s) w2) $+ (nextUsedGate (usedControlWires s) (gateId s) (1 + sizeCirc s) w1)+ + trace ("found id = " ++ (show id)) $ return ()+ + if ( id > 1 + gateId s ) -- && (id <= (sizeCirc s) )+ then do ------------- there is something to move!+ trace ("can be shifted to " ++ (show (id - 1))) $ return ()+ + addToSkipGates (id - 1) g+ -- shiftGate g (id - 1 - (gateId s))+ + s <- getExpState+ trace (show $ future s) $ return ()+ + -- Remove references to (gateId s)+ + updateUsedControlWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w2 c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w1 c+ + -- Shift the ones between (gateId s) and id+ + updateUsedNotWires $+ IM.map $ IS.map $ \x -> if (x <= gateId s) || (x >= id) then x + else x - 1+ updateUsedControlWires $+ IM.map $ IS.map $ \x -> if (x <= gateId s) || (x >= id) then x + else x - 1+ + s <- getExpState+ let z = IM.mapKeys (\x -> if (x <= gateId s) || (x >= id) then x + else x - 1) (gates_to_skip s) in+ z `seq` setExpState (s { gates_to_skip = z} )+ + -- Set g in position (id - 1)+ + updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (id - 1) gs+ Nothing -> Just $ IS.singleton (id - 1)) w2 c+ updateUsedNotWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (id - 1) gs+ Nothing -> Just $ IS.singleton (id - 1)) w1 c+ + -- Make sure we skip (id - 1) later on.+ + + else do ------------- nothing to move... + trace "cannot be shifted" $ return ()+ storeOldGate g+ incrGateId+ + return True+ + Just g -> do+ trace ("got a random " ++ (show g)) $ return ()+ storeOldGate g+ incrGateId+ return True++++-- | A more elementary version of @'stepSwapCirc'@: shuffle the+-- circuit by sending to the end all the NOT gates that can be sent+-- there. Return 'False' when the end of the circuit is reached,+-- 'True' otherwise.+stepSwapCirc_simple :: EvalCirc Bool+stepSwapCirc_simple = do+ maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ case maybe_g of++ Nothing -> return False+ + Just g | (gateId s) == (length $ past s) + (length $ future s) -> do+ storeOldGate g+ return False + + Just g@(Cnot w1 [(w2,b2)]) | + (lastUsedWire (usedNotWires s) w2) <= gateId s && + (lastUsedWire (usedNotWires s) w1) <= gateId s &&+ (lastUsedWire (usedControlWires s) w1) <= gateId s -> do -- got a CNOT++ trace "got a cnot that can be sent to the end" $ return ()+ + sendEndOfTime g+ -- do not store gate, but increase gateId+ incrGateId+ return True+ + Just g -> do+ storeOldGate g+ incrGateId+ return True++-- ----------------------------------------------------------------------+-- ** Some wrappers++-- | Run the monad until 'False' occurs.+runWhile :: Monad m => (a -> Bool) -> m a -> m ()+runWhile f c = do+ r <- c+ if f r then runWhile f c else return ()++-- | Strip the 'NoOp' gates from a list of gates.+stripNoOp :: [Gate] -> [Gate]+stripNoOp = L.filter (/= NoOp) ++-- | Wrapper around 'stepEvalCirc'.+alg_simplify :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_simplify (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepEvalCirc)++-- | Wrapper around 'stepSwapCirc'.+alg_swap :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_swap (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepSwapCirc)+++-- | Wrapper around 'stepSwapCirc_simple'.+alg_swap_simple :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_swap_simple (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepSwapCirc_simple)++-- ----------------------------------------------------------------------+-- * Multi-pass optimization++-- | Auxiliary function. Simultaneously compute the maximum of the+-- lengths of two lists, and their point-wise equality.+is_equal_list :: Eq a => [a] -> [a] -> Int -> (Int,Bool)++is_equal_list [] [] n = (n,True)+is_equal_list (h1:t1) (h2:t2) n | h1 == h2 = is_equal_list t1 t2 (n+1)+is_equal_list t1 t2 n = (n + max (length t1) (length t2),False)++-- | Get the list of initialized wires from a circuit.+get_list_init :: [Gate] -> [Wire]+get_list_init ((Init _ w):gs) = w:(get_list_init gs)+get_list_init (g:gs) = get_list_init gs+get_list_init [] = []++-- | Do several passes of @'alg_simplify'@ until it reaches a fixed point.+simplRec' :: ([Gate],[Wire]) -> ([Gate],[Wire])+simplRec' (l,output) = trace (show (l,output)) $ + let (l',output') = alg_simplify (l, output) in+ let (n,b) = is_equal_list l l' 0 in+ if b then (l,output) + else trace (show n) simplRec' $ suppressGarbageGates (l',output')++-- | Do several passed of @'alg_swap'@ followed with @'simplRec'@+-- until it reaches a fixed point.+simplRec :: ([Gate],[Wire]) -> ([Gate],[Wire])+simplRec (l1,o1) = + let (l3,o3) = simplRec' $ alg_swap (l1,o1) in+ let (n,b) = is_equal_list l1 l3 0 in+ if b then (l3,o3) + else trace "Swapping!" $ simplRec $ (l3,o3)
@@ -0,0 +1,20 @@+-- | Functions to decompose circuits into various gate bases.++module Quipper.Libraries.Decompose ( + -- * Precision+ Precision,+ bits,+ digits,+ -- * Phase+ KeepPhase,+ -- * Gate bases+ GateBase (..),+ gatebase_enum,+ -- * Generic decomposition+ decompose_generic,+) where++import Quipper.Libraries.Decompose.CliffordT+import Quipper.Libraries.Decompose.GateBase+import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.Synthesis
@@ -0,0 +1,1067 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}++-- | This module provides transformers for decomposing circuits into+-- the Clifford+/T/ gate base.+-- +-- * [bold Control trimming.] This transformer uses doubly-controlled+-- /iX/-gates to reduce the number of controls on gates. Specifically,+-- it ensures that not-gates, Pauli /X/-, /Y/-, and /Z/-gates, and+-- /iX/-gates have at most two controls; that phase gates of the form+-- Diag(1, φ) have no controls; and that all other gates have at most+-- one control.+-- +-- * [bold Approximate Clifford+/T/ decomposition.] This decomposes+-- all rotation and phase gates into Clifford+/T/ up to a given+-- precision ε, using an approximate synthesis algorithm. Other gates+-- are unchanged.+-- +-- * [bold Exact Clifford+/T/ decomposition.] This decomposes all+-- gates that are exactly representable in the Clifford+/T/ base into+-- single-qubit Clifford gates, /T/, /T/[sup †], and singly-controlled+-- not-gates (with positive or negative control). Rotation and phase+-- gates are left unchanged.+-- +-- * [bold Standard gate set.] We define the standard gate set to+-- consist of the gates /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/,+-- /T/[sup †], and /CNOT/. This transformer decomposes all gates that+-- remain after applying both approximate and exact Clifford+/T/+-- decomposition into the standard gate set. If the transformer+-- encounters gates that are not single-qubit Clifford gates, /T/,+-- /T/[sup †], or singly-controlled not-gates (with positive or+-- negative control), then the output is still semantically correct,+-- but may not be in the standard gate set. This transformer+-- suppresses global phases.+-- +-- * [bold Strict gate set.] We define the strict gate set to consist+-- of the gates /H/, /S/, /T/, and /CNOT/. This transformer decomposes+-- all gates that remain after applying both approximate and exact+-- Clifford+/T/ decomposition into the strict gate set. If the+-- transformer encounters gates that are not single-qubit Clifford+-- gates, /T/, /T/[sup †], or singly-controlled not-gates (with+-- positive or negative control), then the output is still+-- semantically correct, but may not be in the strict gate set. This+-- transformer suppresses global phases.+-- +-- These above transformers may be applied in any order. Control+-- trimming is primarily for demonstration purposes; it does not need+-- to be combined with the other transformers as they do their own+-- trimming. The exact and approximate Clifford+/T/ decompositions can+-- be applied in either order; since they each target a different set+-- of gates, they must both be performed to obtain a complete+-- decomposition into the Clifford+/T/ gate set. The standard and+-- strict transformers assume that their input has already been+-- pre-processed by the exact and approximate transformers.++module Quipper.Libraries.Decompose.CliffordT where++import Quipper+import Quipper.Internal++-- Stuff we need to import because the Dynamic Transformer interface+-- is not yet stable.+import Quipper.Internal.Circuit (BoxId, TypedSubroutine(..), OCircuit(..))+import Quipper.Internal.Transformer+import Quipper.Internal.Generic (provide_subroutine_generic, transform_unary_dynamic)+import Quipper.Internal.Monad (endpoints_of_wires_in_arity, identity_dynamic_transformer, provide_subroutines, named_rotation_qulist_at, named_gate_qulist_at, subroutine)++import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.GateDecompositions+import Quipper.Libraries.Synthesis++import Quipper.Utils.Auxiliary (optional)+import Quantum.Synthesis.SymReal++import Control.Monad (when)+import System.Random+import Text.Printf++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | A version of 'with_combined_controls' that uses doubly-controlled+-- /iX/-gates to do the decomposition. For example, when /n/=2, this+-- yields circuits such as the following:+-- +-- \[image with_combined_controls2.png]+-- +-- And for /n/=1:+-- +-- \[image with_combined_controls1.png]+with_combined_controls_iX :: Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_iX = with_combined_controls cc_iX_plain_at++-- | A version of 'with_combined_controls_iX' that further decomposes+-- the doubly-controlled /iX/-gates into the Clifford+/T/ gate base.+with_combined_controls_CT :: Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_CT = with_combined_controls cc_iX_at++-- | Turn a list of signed controls into a list of positive quantum+-- controls, by applying all classical controls directly, and+-- conjugating any negative quantum controls by /X/. Call the+-- inner block with those quantum controls. Usage:+-- +-- > with_normalized_controls ctrls $ \qs -> do+-- > <<<code using qs>>>+with_normalized_controls :: [Signed Endpoint] -> ([Qubit] -> Circ a) -> Circ a+with_normalized_controls cs f = do+ let (qcs, ccs) = partition_controls cs+ with_controls ccs $ do+ aux qcs []+ + where+ aux [] qs = f (reverse qs)+ aux (qc:qcs) qs = do+ with_signed_qubit qc $ \q -> do+ aux qcs (q:qs)++-- | Like 'with_normalized_controls', but use /HSSH/ instead of /X/,+-- so as not to leave the /H/, /S/, /T/, /CNot/ gate base. +with_normalized_controls_HS :: [Signed Endpoint] -> ([Qubit] -> Circ a) -> Circ a+with_normalized_controls_HS cs f = do+ let (qcs, ccs) = partition_controls cs+ with_controls ccs $ do+ aux qcs []+ + where+ aux [] qs = f (reverse qs)+ aux (qc:qcs) qs = do+ with_signed_qubit_HS qc $ \q -> do+ aux qcs (q:qs)+ with_signed_qubit_HS (Signed q True) f = f q+ with_signed_qubit_HS (Signed q False) f = do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ b <- f q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ return b++-- | Negate the number if the boolean is true.+negate_if :: (Num r) => Bool -> r -> r+negate_if False x = x+negate_if True x = -x++-- ----------------------------------------------------------------------+-- * Transformers++-- ----------------------------------------------------------------------+-- ** Control trimming++-- | This transformer makes sure that not-gates, Pauli /X/-, /Y/-, and+-- /Z/-gates, and /iX/-gates have at most two controls; that phase+-- gates of the form Diag(1, φ) have no controls; and that all other+-- gates have at most one control.+trimcontrols_transformer :: Transformer Circ Qubit Bit+trimcontrols_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 2 cs $ \qcs -> do+ qnot_at q `controlled` qcs+ return ([q], [], cs)+trimcontrols_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ qmultinot qs `controlled` qcs+ return (qs, [], cs)+trimcontrols_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ hadamard q `controlled` qcs+ return ([q], [], cs)+trimcontrols_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ swap_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+trimcontrols_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ gate_W_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+trimcontrols_transformer gate@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are allowed two controls+ ("X", [q], []) -> 2+ ("Y", [q], []) -> 2+ ("Z", [q], []) -> 2+ ("iX", [q], []) -> 2+ _ -> 1+ with_combined_controls_iX n cs $ \qcs -> do+ named_gate_qulist_at name inv qs gctls `controlled` qcs+ return (qs,gctls,cs)+trimcontrols_transformer (T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ global_phase_anchored t qs `controlled` qcs+ return cs+trimcontrols_transformer gate@(T_QRot name _ _ inv theta ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are decomposed to zero controls+ ("R(2pi/%)", [q], []) -> 0+ ("T(%)", [q], []) -> 0+ _ -> 1+ case n of+ 0 -> do+ let [q] = qs+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ let [c] = qcs+ with_signed_qubit c $ \q -> do+ named_rotation_qulist_at name inv theta [q] gctls+ return (qs,gctls,cs)+ _ -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ named_rotation_qulist_at name inv theta qs gctls `controlled` qcs+ return (qs,gctls,cs)++-- The subroutine transformer clause is called when a subroutine gate appears,+-- for now we decompose the controls just like for other gates. The recursive+-- decomposition of a subroutine is taken care of in the dynamic transformer.+trimcontrols_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \namespace ws cs -> without_controls_if ncf $ do+ let qws = [w | Endpoint_Qubit w <- ws]+ provide_subroutines namespace+ with_combined_controls_iX 1 cs $ \qcs -> do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` qcs+ return (vs,cs)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+trimcontrols_transformer gate@(T_CNot _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CGate _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CSwap _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QPrep _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QUnprep _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QInit _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CInit _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QMeas _) = identity_transformer gate+trimcontrols_transformer gate@(T_QDiscard _) = identity_transformer gate+trimcontrols_transformer gate@(T_CDiscard _) = identity_transformer gate+trimcontrols_transformer gate@(T_DTerm _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_Comment _ _ _) = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Approximate Clifford+/T/ decomposition++-- | This transformer decomposes rotation and phase gates into the+-- Clifford+/T/ basis, using the approximate synthesis algorithm of+-- <http://arxiv.org/abs/1212.6253>. Other gates+-- are unchanged. +-- +-- This transformer requires a precision parameter, as well as a+-- source of randomness. The 'KeepPhase' flag indicates whether to+-- respect global phases.+approx_ct_transformer :: (RandomGen g) => KeepPhase -> Precision -> g -> Transformer Circ Qubit Bit+approx_ct_transformer kp prec g gate@(T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ comment_with_label (printf "ENTER: GPhase (t=%f)" t) cs "c"+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> approximate_synthesis_phase kp prec theta g+ where+ theta = pi * to_real t+ [c] -> do+ with_signed_qubit c $ \q -> do+ approximate_synthesis_phase_ctrl kp prec theta g q+ return ()+ where+ theta = pi * to_real t+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)+ comment_with_label "EXIT: GPhase" cs "c"+ return cs+approx_ct_transformer kp prec g gate@(T_QRot name _ _ inv t ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ comment_with_label (printf "ENTER: %s%s (t=%f)" name (optional inv "*") t) (qs,cs) ("q","c")+ case (name, inv, qs, gctls) of+ ("exp(-i%Z)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_zrot prec theta g q+ return ()+ where+ theta = negate_if inv (2 * to_real t)+ [c] -> do + with_signed_qubit c $ \q2 -> do+ approximate_synthesis_zrot_ctrl prec theta g q q2+ return ()+ where+ theta = negate_if inv (2 * to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("exp(% pi i)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (to_real (pi*t))+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q2+ return ()+ where+ theta = negate_if inv (to_real (pi*t))+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("R(2pi/%)", inv, [q], []) -> do+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (2 * pi / to_real t)+ [c] -> do+ with_signed_qubit c $ \q1 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q1+ return ()+ where+ theta = negate_if inv (2 * pi / to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("T(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (-to_real t)+ [c] -> do+ with_signed_qubit c $ \q1 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q1+ return ()+ where+ theta = negate_if inv (-to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("G(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (-to_real t)+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q2+ return ()+ where+ theta = negate_if inv (-to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("Rz(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_zrot prec theta g q+ return ()+ where+ theta = negate_if inv (to_real t)+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_zrot_ctrl prec theta g q q2+ return ()+ where+ theta = negate_if inv (to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ _ -> error ("approx_ct_transformer: unimplemented gate: " ++ show gate)+ comment_with_label (printf "EXIT: %s%s (t=%f)" name (optional inv "*") t) (qs,cs) ("q","c")+ return (qs, gctls, cs)+approx_ct_transformer kp prec g gate = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Exact Clifford+/T/ decomposition++-- | This transformer decomposes all gates that permit exact+-- Clifford+/T/ representations into the following concrete gate base+-- for Clifford+/T/:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Classical controls and classical gates are not subject to the gate+-- base, and are left untouched.+-- +-- Rotations and phase gates are left unchanged by this transformer,+-- but any controls on those gates will be decomposed. +exact_ct_transformer :: Transformer Circ Qubit Bit+exact_ct_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 2 cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ [c1,c2] -> do+ toffoli_AMMR_at q c1 c2+ qcs -> do+ qnot_at q `controlled` qcs + return ([q], [], cs)+exact_ct_transformer gate@(T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ sequence_ [ gate_X_at q | q <- qs ]+ qcs -> do+ sequence_ [ qnot_at q | q <- qs ] `controlled` qcs+ return (qs, [], cs)+exact_ct_transformer gate@(T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ cH_AMMR_at q c+ qcs -> do+ hadamard_at q `controlled` qcs+ return ([q], [], cs)+exact_ct_transformer gate@(T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ fredkin_at q1 q2 c+ qcs -> do+ swap_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+exact_ct_transformer gate@(T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ controlled_W_at q1 q2 c+ qcs -> do+ gate_W_CliffordT_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+exact_ct_transformer gate@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are allowed two controls+ ("X", [q], []) -> 2+ ("Y", [q], []) -> 2+ ("Z", [q], []) -> 2+ ("iX", [q], []) -> 2+ _ -> 1+ with_combined_controls_CT n cs $ \qcs -> do+ case (name, qs, gctls, qcs) of+ + ("X", [q], [], [c1, c2]) -> do+ toffoli_AMMR_at q c1 c2+ ("X", [q], [], [c]) -> do+ qnot_at q `controlled` c+ ("X", [q], [], []) -> do+ gate_X_at q+ + ("Y", [q], [], [c1, c2]) -> do+ gate_S_inv_at q+ toffoli_AMMR_at q c1 c2+ gate_S_at q+ ("Y", [q], [], [c]) -> do+ gate_S_inv_at q+ qnot_at q `controlled` c+ gate_S_at q+ ("Y", [q], [], []) -> do+ gate_Y_at q+ + ("Z", [q], [], [c1, c2]) -> do+ ccZ_AMMR_at q c1 c2+ ("Z", [q], [], [c]) -> do+ hadamard_at q+ qnot_at q `controlled` c+ hadamard_at q+ ("Z", [q], [], []) -> do+ gate_Z_at q+ + ("iX", [q], [], [c1, c2]) -> do+ reverse_imp_if inv cc_iX_at q c1 c2+ ("iX", [q], [], [c]) -> do+ reverse_imp_if inv controlled_iX_at q c+ ("iX", [q], [], []) -> + reverse_imp_if inv gate_iX_at q+ + ("S", [q], [], [c]) -> do+ reverse_imp_if inv controlled_S_at q c+ ("S", [q], [], []) -> do+ reverse_imp_if inv gate_S_at q+ + ("T", [q], [], [c]) -> do+ reverse_imp_if inv controlled_T_at q c+ ("T", [q], [], []) -> do+ reverse_imp_if inv gate_T_at q+ + ("V", [q], [], [c]) -> do+ reverse_imp_if inv controlled_V_at q c+ ("V", [q], [], []) -> do+ reverse_imp_if inv gate_V_at q+ + ("E", [q], [], [c]) -> do+ reverse_imp_if inv controlled_E_at q c+ ("E", [q], [], []) -> do+ reverse_imp_if inv gate_E_at q+ + ("YY", [q], [], [c]) -> do+ reverse_imp_if inv controlled_YY_at q c+ ("YY", [q], [], []) -> do+ reverse_imp_if inv (named_gate_at "YY") q+ + ("omega", [q], [], [c]) -> do+ with_signed_qubit c $ \c1 -> do+ reverse_imp_if inv gate_T_at c1+ ("omega", [q], [], []) -> do+ reverse_imp_if inv gate_omega_at q+ + _ -> error ("exact_ct_transformer: gate not implemented: " ++ show gate)+ return (qs,gctls,cs)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+exact_ct_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CNot _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CGate _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CSwap _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QPrep _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QUnprep _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QInit _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CInit _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QMeas _) = identity_transformer gate+exact_ct_transformer gate@(T_QDiscard _) = identity_transformer gate+exact_ct_transformer gate@(T_CDiscard _) = identity_transformer gate+exact_ct_transformer gate@(T_DTerm _ _) = identity_transformer gate+exact_ct_transformer gate@(T_Comment _ _ _) = identity_transformer gate+++-- ----------------------------------------------------------------------+-- ** Decomposition into standard gate set++-- | This transformer decomposes a circuit into the standard gate set,+-- which we define to be:+-- +-- * /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the standard gate set. The best way+-- to avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+standard_transformer :: Transformer Circ Qubit Bit+standard_transformer gate@(T_QGate "H" 1 0 _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "Y" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "Z" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "S" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "T" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "multinot" _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "swap" 2 0 _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "W" 2 0 _ _ _) = identity_transformer gate++standard_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qs -> do+ case qs of+ [] -> do+ gate_X_at q+ qs -> qnot_at q `controlled` qs+ return ([q], [], cs)++standard_transformer gate@(T_QGate "X" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q],[],cs)++standard_transformer gate@(T_QGate "iX" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ qcs -> do+ reverse_imp_if inv gate_iX_at q `controlled` qcs+ return ([q],[],cs)++standard_transformer gate@(T_QGate "V" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ reverse_imp_if inv gate_S_inv_at q+ hadamard_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "E" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_inv_at q+ return ([q],[],cs)+standard_transformer gate@(T_QGate "E" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "YY" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ reverse_imp_if inv gate_S_at q+ hadamard_at q+ reverse_imp_if inv gate_S_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "omega" 1 0 _ ncf f) = f $+ \[q] [] cs -> + return ([q],[],cs)++standard_transformer gate@(T_QGate name _ _ inv ncf f) = + error ("standard_transformer: gate not implemented: " ++ show gate)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+standard_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+standard_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_CNot _ _) = identity_transformer gate+standard_transformer gate@(T_CGate _ _ _) = identity_transformer gate+standard_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+standard_transformer gate@(T_CSwap _ _) = identity_transformer gate+standard_transformer gate@(T_QPrep _ _) = identity_transformer gate+standard_transformer gate@(T_QUnprep _ _) = identity_transformer gate+standard_transformer gate@(T_QInit _ _ _) = identity_transformer gate+standard_transformer gate@(T_CInit _ _ _) = identity_transformer gate+standard_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+standard_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+standard_transformer gate@(T_QMeas _) = identity_transformer gate+standard_transformer gate@(T_QDiscard _) = identity_transformer gate+standard_transformer gate@(T_CDiscard _) = identity_transformer gate+standard_transformer gate@(T_DTerm _ _) = identity_transformer gate+standard_transformer gate@(T_Comment _ _ _) = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Decomposition into strict gate set++-- | This transformer decomposes a circuit into the strict gate set,+-- which we define to be:+-- +-- * /H/, /S/, /T/, and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the strict gate set. The best way to+-- avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+strict_transformer :: Transformer Circ Qubit Bit+strict_transformer gate@(T_QGate "H" 1 0 _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "multinot" _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "swap" 2 0 _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "W" 2 0 _ _ _) = identity_transformer gate++strict_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qs -> do+ case qs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qs -> qnot_at q `controlled` qs+ return ([q], [], cs)++strict_transformer gate@(T_QGate "X" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qcs -> do+ case qcs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q],[],cs)++strict_transformer gate@(T_QGate "Y" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "Z" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "iX" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qcs -> do+ case qcs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qcs -> do+ reverse_imp_if inv gate_iX_at q `controlled` qcs+ return ([q],[],cs)++strict_transformer gate@(T_QGate "S" 1 0 False ncf f) = + identity_transformer gate+strict_transformer gate@(T_QGate "S" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "T" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_T_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "T" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_T_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "V" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "V" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "E" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "E" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "YY" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ when (inv) $ do+ gate_S_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ gate_S_at q+ when (inv) $ do+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "omega" 1 0 _ ncf f) = f $+ \[q] [] cs -> + return ([q],[],cs)++strict_transformer gate@(T_QGate name _ _ inv ncf f) = + error ("strict_transformer: gate not implemented: " ++ show gate)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+strict_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+strict_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_CNot _ _) = identity_transformer gate+strict_transformer gate@(T_CGate _ _ _) = identity_transformer gate+strict_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+strict_transformer gate@(T_CSwap _ _) = identity_transformer gate+strict_transformer gate@(T_QPrep _ _) = identity_transformer gate+strict_transformer gate@(T_QUnprep _ _) = identity_transformer gate+strict_transformer gate@(T_QInit _ _ _) = identity_transformer gate+strict_transformer gate@(T_CInit _ _ _) = identity_transformer gate+strict_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+strict_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+strict_transformer gate@(T_QMeas _) = identity_transformer gate+strict_transformer gate@(T_QDiscard _) = identity_transformer gate+strict_transformer gate@(T_CDiscard _) = identity_transformer gate+strict_transformer gate@(T_DTerm _ _) = identity_transformer gate+strict_transformer gate@(T_Comment _ _ _) = identity_transformer gate+++-- ----------------------------------------------------------------------+-- * Glue code for subroutines++-- $ The following is stuff we have to do because subroutines are not+-- yet handled very abstractly. It is untested whether subroutines+-- work at all.++-- | Handle subroutines for the 'trimcontrols_transformer'.+trimcontrols_subroutine :: BoxId -> TypedSubroutine -> Circ ()+trimcontrols_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = trimcontrols_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "trimcontrols_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'trimcontrols_transformer'.+trimcontrols_dtransformer :: DynamicTransformer Circ Qubit Bit+trimcontrols_dtransformer = identity_dynamic_transformer {+ transformer = trimcontrols_transformer,+ define_subroutine = trimcontrols_subroutine}++-- | Unary transformer for the 'trimcontrols_transformer'.+trimcontrols_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+trimcontrols_unary = transform_unary_dynamic trimcontrols_dtransformer++-- | Handle subroutines for the 'approx_ct_transformer'.+approx_ct_subroutine :: (RandomGen g) => KeepPhase -> Precision -> g -> BoxId -> TypedSubroutine -> Circ ()+approx_ct_subroutine kp prec g boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = approx_ct_unary kp prec g circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "approx_ct_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'approx_ct_transformer'.+approx_ct_dtransformer :: (RandomGen g) => KeepPhase -> Precision -> g -> DynamicTransformer Circ Qubit Bit+approx_ct_dtransformer kp prec g = identity_dynamic_transformer {+ transformer = approx_ct_transformer kp prec g,+ define_subroutine = approx_ct_subroutine kp prec g}++-- | Unary transformer for the 'approx_ct_transformer'.+approx_ct_unary :: (RandomGen g, QCData qa, QCData qb) => KeepPhase -> Precision -> g -> (qa -> Circ qb) -> (qa -> Circ qb)+approx_ct_unary kp prec g = transform_unary_dynamic (approx_ct_dtransformer kp prec g)++-- | Handle subroutines for the 'exact_ct_transformer'.+exact_ct_subroutine :: BoxId -> TypedSubroutine -> Circ ()+exact_ct_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = exact_ct_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "exact_ct_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'exact_ct_transformer'.+exact_ct_dtransformer :: DynamicTransformer Circ Qubit Bit+exact_ct_dtransformer = identity_dynamic_transformer {+ transformer = exact_ct_transformer,+ define_subroutine = exact_ct_subroutine}++-- | Unary transformer for the 'exact_ct_transformer'.+exact_ct_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+exact_ct_unary = transform_unary_dynamic exact_ct_dtransformer++-- | Handle subroutines for the 'standard_transformer'.+standard_subroutine :: BoxId -> TypedSubroutine -> Circ ()+standard_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = standard_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "standard_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'standard_transformer'.+standard_dtransformer :: DynamicTransformer Circ Qubit Bit+standard_dtransformer = identity_dynamic_transformer {+ transformer = standard_transformer,+ define_subroutine = standard_subroutine}++-- | Unary transformer for the 'standard_transformer'.+standard_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+standard_unary = transform_unary_dynamic standard_dtransformer++-- | Handle subroutines for the 'strict_transformer'.+strict_subroutine :: BoxId -> TypedSubroutine -> Circ ()+strict_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = strict_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "strict_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'strict_transformer'.+strict_dtransformer :: DynamicTransformer Circ Qubit Bit+strict_dtransformer = identity_dynamic_transformer {+ transformer = strict_transformer,+ define_subroutine = strict_subroutine}++-- | Unary transformer for the 'strict_transformer'.+strict_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+strict_unary = transform_unary_dynamic strict_dtransformer++-- ----------------------------------------------------------------------+-- * Generic transformers++-- $ The following generic functions form the high-level interface to+-- these decomposition transformers. This is how the transformers+-- should be accessed by user code.++-- | This transformer makes sure that not-gates, Pauli /X/-, /Y/-, and+-- /Z/-gates, and /iX/-gates have at most two controls; that phase+-- gates of the form Diag(1, φ) have no controls; and that all other+-- gates have at most one control.+trimcontrols_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+trimcontrols_generic f = h where+ f1 = quncurry f+ h1 = trimcontrols_unary f1+ h = qcurry h1++-- | This transformer decomposes rotation and phase gates into the+-- Clifford+/T/ basis, using the approximate synthesis algorithm of+-- <http://arxiv.org/abs/1212.6253>. It requires a precision+-- parameter, as well as a source of randomness. Other gates+-- are unchanged.+approx_ct_generic :: (RandomGen g, QCData qa, QCData qb, QCurry qfun qa qb) => KeepPhase -> Precision -> g -> qfun -> qfun+approx_ct_generic kp prec g f = h where+ f1 = quncurry f+ h1 = approx_ct_unary kp prec g f1+ h = qcurry h1++-- | This transformer decomposes all gates that permit exact+-- Clifford+/T/ representations into the following concrete gate base+-- for Clifford+/T/:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Classical controls and classical gates are not subject to the gate+-- base, and are left untouched.+-- +-- Rotations and phase gates are left unchanged by this transformer,+-- but any controls on those gates will be decomposed. +exact_ct_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+exact_ct_generic f = h where+ f1 = quncurry f+ h1 = exact_ct_unary f1+ h = qcurry h1++-- | This transformer decomposes a circuit into the standard gate set,+-- which we define to be:+-- +-- * /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the standard gate set. The best way+-- to avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+standard_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+standard_generic f = h where+ f1 = quncurry f+ h1 = standard_unary f1+ h = qcurry h1++-- | This transformer decomposes a circuit into the strict gate set,+-- which we define to be:+-- +-- * /H/, /S/, /T/, and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the strict gate set. The best way to+-- avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+strict_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+strict_generic f = h where+ f1 = quncurry f+ h1 = strict_unary f1+ h = qcurry h1
@@ -0,0 +1,127 @@+-- | This module provides an enumeration type for gate bases. This is+-- useful, for example, to provide a uniform interface to gate base+-- decomposers, so that they can be selected via command line options+-- or function arguments.++module Quipper.Libraries.Decompose.GateBase where++import Quipper+import Quipper.Internal++import Quipper.Libraries.Synthesis+import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.Decompose.CliffordT++import Quipper.Utils.RandomSource++import System.Random++-- ----------------------------------------------------------------------+-- * Gate bases++-- | An enumeration type for gate bases. More cases can be added in+-- the future, for example for using gates from a particular physical+-- machine description. +-- +-- Some gate bases carry additional parameters; for example, in the+-- case of decomposition into a discrete gate base, one may specify a+-- precision ε, a random seed, or other flags.+-- +-- If a 'Precision' parameter is present, it specifies the desired+-- precision per gate. If a 'RandomSource' parameter is present, it+-- specifies a source of randomness.+-- +-- If a 'KeepPhase' parameter is present, it determines whether global+-- phases are respected ('True') or disregarded ('False').+data GateBase =+ Logical + -- ^ Use all logical gates, i.e., leave the circuit unchanged.+ | Binary + -- ^ Decompose into binary gates.+ | Toffoli + -- ^ Decompose into Toffoli and binary gates.+ | CliffordT_old + -- ^ Decompose into Clifford+/T/. This is a legacy transformer+ -- that does not handle all gates correctly. For example, it does+ -- not handle /W/-gates, rotations, or phase gates. Use+ -- 'CliffordT' instead.+ | CliffordT KeepPhase Precision RandomSource + -- ^ Decompose into Clifford+/T/, specifically: single-qubit+ -- Clifford gates, the controlled-not gate (with positive or+ -- negative controls), and the gates /T/ and /T/[sup †].+ | Standard Precision RandomSource+ -- ^ Decompose into the standard gate set, which we define to be+ -- /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and+ -- /CNOT/. Suppresses global phases.+ | Strict Precision RandomSource+ -- ^ Decompose into /H/, /S/, /T/, /CNOT/ gates only. Suppresses+ -- global phases.+ | Approximate KeepPhase Precision RandomSource+ -- ^ Decompose rotation and phase gates into Clifford+/T/, using+ -- an approximate synthesis algorithm. Other gates are unchanged.+ | Exact + -- ^ Decompose gates that can be exactly represented in the+ -- Clifford+/T/ base into that base, specifically: single-qubit+ -- Clifford gates, the controlled-not gate (with positive or+ -- negative controls), and the gates /T/ and /T/[sup †]. Leave+ -- rotation and phase gates unchanged.+ | TrimControls + -- ^ Eliminate excess controls from gates.+ deriving Show++-- | An assignment of gate bases to names. Names are given as+-- lower-case strings.+-- +-- This can be useful, e.g., in the definition of command line+-- options.+-- +-- In the future, the syntax should be extended so that users can+-- specify parameters (e.g., the precision, random seed) on the+-- command line as well. For now, we just use the default precision.+gatebase_enum :: [(String, GateBase)]+gatebase_enum = [+ ("logical", Logical),+ ("binary", Binary),+ ("toffoli", Toffoli),+ ("cliffordt_old", CliffordT_old),+ ("cliffordt", CliffordT False (30 * digits) rs),+ ("cliffordt_keepphase", CliffordT True (30 * digits) rs),+ ("standard", Standard (30 * digits) rs),+ ("strict", Strict (30 * digits) rs),+ ("approximate", Approximate False (30 * digits) rs),+ ("approximate_keepphase", Approximate True (30 * digits) rs),+ ("exact", Exact),+ ("trimcontrols", TrimControls)+ ]+ where+ rs = RandomSource (read "1" :: StdGen)++-- ----------------------------------------------------------------------+-- * Generic decomposition++-- | Decompose a circuit into gates from the given 'GateBase'. This+-- 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:+-- +-- > decompose_generic :: (QCData qa) => GateBase -> Circ qa -> Circ qa+-- > decompose_generic :: (QCData qa, QCData qb) => GateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+-- > decompose_generic :: (QCData qa, QCData qb, QCData qc) => GateBase -> (qa -> qb -> Circ qc) -> (qa -> qb -> Circ qc)+-- +-- and so forth.++decompose_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => GateBase -> qfun -> qfun+decompose_generic Logical = id+decompose_generic Binary = decompose_legacy_generic GB_Binary+decompose_generic Toffoli = decompose_legacy_generic GB_Toffoli+decompose_generic CliffordT_old = decompose_legacy_generic GB_CliffordT+decompose_generic (CliffordT kp prec (RandomSource g)) = exact_ct_generic . approx_ct_generic kp prec g+decompose_generic (Standard prec (RandomSource g)) = standard_generic . exact_ct_generic . approx_ct_generic False prec g+decompose_generic (Strict prec (RandomSource g)) = strict_generic . exact_ct_generic . approx_ct_generic False prec g+decompose_generic (Approximate kp prec (RandomSource g)) = approx_ct_generic kp prec g+decompose_generic Exact = exact_ct_generic+decompose_generic TrimControls = trimcontrols_generic++
@@ -0,0 +1,240 @@+{-# LANGUAGE Rank2Types #-}++-- | Functions to decompose circuits into various gate bases. This+-- decompositions are \"legacy\". The 'GB_Toffoli' and 'GB_CliffordT'+-- decomposers contained here are being superseded by their+-- counterparts in "Quipper.Libraries.Decompose.CliffordT".++module Quipper.Libraries.Decompose.Legacy where++import Quipper+import Quipper.Internal++import Quipper.Libraries.GateDecompositions++-- The following is a bunch of stuff we need to import because,+-- temporarily, Decompose.hs uses low-level interfaces. It should be+-- re-implemented using only high-level interfaces, or in some cases,+-- more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit+import Quipper.Internal.Monad+import Quipper.Internal.Transformer (bindings_empty, bind_list, unbind_list, DynamicTransformer(..))+import Quipper.Internal.Generic (provide_subroutine_generic, transform_unary_dynamic)++import Control.Monad++-- ----------------------------------------------------------------------+-- * Legacy gatebase++-- | An enumeration type for the three gate bases handled by this+-- module.+data LegacyGateBase = GB_Toffoli | GB_Binary | GB_CliffordT+ deriving (Show)+-- ----------------------------------------------------------------------+-- * Helper functions++-- | Decompose quantum controls recursively until at most /n/ remain,+-- and then pass these reduced controls to the given circuit.+-- Precondition: /n/ ≥ 1. The decomposition is done using Toffoli+-- gates, decomposed into the gate base. Classical controls are left+-- untouched.+-- +-- For example, when /n/=2, this typically yields a circuit such as+-- the following (but with the Toffoli gates further decomposed into+-- the 'LegacyGateBase'):+-- +-- \[image decompose2Controls.png]+-- +-- And for /n/=1, the circuit typically looks like this:+-- +-- \[image decomposeControls.png]+with_combined_controls_gb :: LegacyGateBase -> Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_gb GB_Toffoli = with_combined_controls toffoli_plain_at+with_combined_controls_gb GB_Binary = with_combined_controls toffoli_V_at+with_combined_controls_gb GB_CliffordT = with_combined_controls toffoli_AMMR_at++-- | Decompose a Toffoli gate into the given 'LegacyGateBase'. The controls+-- on the Toffoli gate may be positive or negative, as specified.+decomposeQToffoli :: LegacyGateBase -> Qubit -> (Signed Qubit, Signed Qubit) -> Circ ()+decomposeQToffoli GB_Toffoli q (c1, c2) = qnot_at q `controlled` [c1,c2]+decomposeQToffoli GB_Binary q (c1, c2) = do+ toffoli_V_at q c1 c2+decomposeQToffoli GB_CliffordT q3 (c1, c2) = do+ toffoli_AMMR_at q3 c1 c2++-- | The inverse of 'decomposeQToffoli'.+decomposeQToffoli_inv :: LegacyGateBase -> Qubit -> (Signed Qubit, Signed Qubit) -> Circ ()+decomposeQToffoli_inv gb = reverse_generic_imp (decomposeQToffoli gb)++-- | Implement a QMultinot gate in terms of QNot gates.+decomposeQMultinot :: [Qubit] -> Circ ()+decomposeQMultinot [] = return ()+decomposeQMultinot (q:qs) = do+ qnot_at q+ decomposeQMultinot qs ++-- ----------------------------------------------------------------------+-- * Decomposition transformers+ +-- | A transformer to decompose a circuit into 'LegacyGateBase' gates. Note+-- that in the case of classically-controlled quantum gates, the+-- classical controls are unaffected.+decompose_transformer :: LegacyGateBase -> Transformer Circ Qubit Bit+decompose_transformer gb (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 2 cs $ \qcs -> do+ case qcs of+ -- two controls+ [c1, c2] -> do+ decomposeQToffoli gb q (c1,c2)+ return ([q], [], cs)+ -- zero or one control+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q], [], cs)+decompose_transformer gb (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ decomposeQMultinot qs `controlled` qcs+ return (qs, [], cs)+decompose_transformer gb (T_QGate "H" 1 0 _ ncf f) = f $ + \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ hadamard q `controlled` qcs+ return ([q], [], cs)+decompose_transformer gb (T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ case qcs of+ -- one control+ [c] -> do+ qnot_at q1 `controlled` q2+ decomposeQToffoli gb q2 (c,(Signed q1 True)) + qnot_at q1 `controlled` q2+ return ([q1, q2], [], cs)+ -- zero controls+ qcs -> do + swap_at q1 q2+ return ([q1, q2], [], cs)+decompose_transformer gb (T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ case qcs of+ -- one control+ [c] -> do + qnot q2 `controlled` q1+ w' <- qinit_qubit False+ decomposeQToffoli gb w' (c,(Signed q2 True))+ hadamard q1 `controlled` w'+ decomposeQToffoli_inv gb w' (c,(Signed q2 True))+ qterm_qubit False w' + qnot q2 `controlled` q1+ return ([q1, q2], [], cs)+ -- zero controls+ qcs -> do + gate_W q1 q2+ return ([q1, q2], [], cs)+decompose_transformer gb (T_QGate name _ _ inv ncf f) = f $+ \qs vs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ named_gate_qulist_at name inv qs vs `controlled` qcs+ return (qs, vs, cs)+decompose_transformer gb (T_QRot name _ _ inv theta ncf f) = f $+ \qs vs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ named_rotation_qulist_at name inv theta qs vs `controlled` qcs+ return (qs, vs, cs)+decompose_transformer gb (T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ global_phase_anchored_list t qs `controlled` qcs+ return cs++-- The subroutine transformer clause is called when a subroutine gate appears, +-- for now we decompose the controls just like for other gates. The recursive+-- decomposition of a subroutine is taken care of in the dynamic transformer.+decompose_transformer gb (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \namespace ws cs -> without_controls_if ncf $ do+ let qws = [w | Endpoint_Qubit w <- ws]+ with_combined_controls_gb gb 1 cs $ \qcs -> do + provide_subroutines namespace+ case qcs of+ -- one control+ [c] -> if length qws /= length ws then error "Classical subroutine, used with quantum controls" else do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,cs)+ -- zero controls+ qcs -> do + vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws+ return (vs,cs)+-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+decompose_transformer gb gate@(T_CNot _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CGate _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CGateInv _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CSwap _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QPrep _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QUnprep _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QInit _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CInit _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QTerm _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CTerm _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QMeas _) = identity_transformer gate+decompose_transformer gb gate@(T_QDiscard _) = identity_transformer gate+decompose_transformer gb gate@(T_CDiscard _) = identity_transformer gate+decompose_transformer gb gate@(T_DTerm _ _) = identity_transformer gate+decompose_transformer gb gate@(T_Comment _ _ _) = identity_transformer gate++-- | Return a circuit producing function from a TypedSubroutine+open_subroutine :: TypedSubroutine -> [Endpoint] -> Circ [Endpoint]+open_subroutine (TypedSubroutine ocircuit _ _ scf) inputs = do+ let OCircuit (win, circuit, wout) = ocircuit+ when (length win /= length inputs) $ do+ error ("open_subroutine: subroutine has been applied to incorrect size of QCData")+ let in_bind = bind_list win inputs bindings_empty+ out_bind <- apply_circuit_with_bindings circuit in_bind+ let outputs = unbind_list out_bind wout+ return outputs++-- | Apply the decompose transformer to the given TypedSubroutine+-- Note: by default, set the classical-control flag to false.+decompose_subroutine :: LegacyGateBase -> BoxId -> TypedSubroutine -> Circ ()+decompose_subroutine gb boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = decompose_legacy_unary gb circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "decompose_subroutine: " ++ x) boxid False circ' ein+ +-- | A dynamic transformer variant of the decompose transformer+decompose_dynamic_transformer :: LegacyGateBase -> DynamicTransformer Circ Qubit Bit+decompose_dynamic_transformer gb = identity_dynamic_transformer {+ transformer = decompose_transformer gb,+ define_subroutine = decompose_subroutine gb}++-- ----------------------------------------------------------------------+-- * Generic decomposition++-- | Decompose a circuit into gates from the given 'LegacyGateBase'.+decompose_legacy_unary :: (QCData qa, QCData qb) => LegacyGateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+decompose_legacy_unary gb circ = transform_unary_dynamic (decompose_dynamic_transformer gb) circ + +-- | Decompose a circuit into gates from the given 'LegacyGateBase'. Unlike+-- 'decompose_legacy_unary', this 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:+-- +-- > decompose_legacy_generic :: (QCData qa) => LegacyGateBase -> Circ qa -> Circ qa+-- > decompose_legacy_generic :: (QCData qa, QCData qb) => LegacyGateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+-- > decompose_legacy_generic :: (QCData qa, QCData qb, QCData qc) => LegacyGateBase -> (qa -> qb -> Circ qc) -> (qa -> qb -> Circ qc)+-- +-- and so forth.++decompose_legacy_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => LegacyGateBase -> qfun -> qfun+decompose_legacy_generic gatebase f = g where+ f1 = quncurry f+ g1 = decompose_legacy_unary gatebase f1+ g = qcurry g1+
@@ -0,0 +1,653 @@+-- | This library provides various transformers and functions for+-- performing dynamic liftings based on different mechanisms. Some use+-- simulations to do the dynamic liftings; others just do them+-- randomly. There are also functions for printing the simulated+-- circuit.+-- +-- This code is experimental.++module Quipper.Libraries.DynamicLiftings where++import Quipper++-- The following is a bunch of stuff we need to import because,+-- temporarily, DynamicLiftings.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit (Namespace, namespace_empty, TypedSubroutine(..), OCircuit(..), reverse_ocircuit, showNames)+import Quipper.Internal (BType)+import Quipper.Internal.Transformer+import Quipper.Internal.Monad+import Quipper.Internal.Generic (transform_unary_dynamic_shape)+import Quipper.Internal.Printing (getBit)++import Quipper.Libraries.Simulation.QuantumSimulation++import Quipper.Utils.Auxiliary (map_provide)++-- we use the state monad to hold our \"quantum\" state+import Control.Monad.State+-- we use a random number generator to simulate \"quantum randomness\"+import System.Random hiding (split)+-- we store \"basis\" states as a map, +--i.e., Map Qubit Bool represents a basis state.+import Data.Map (Map)+import qualified Data.Map as Map++-- ======================================================================+-- * Random Dynamic Liftings and Liftings from a List++-- | A State monad that holds a random generator.+type RandomCirc a = StateT StdGen Circ a++-- | A State monad that holds a list of booleans.+type ListCirc a = StateT [Bool] Circ a++-- | To evaluate a 'RandomCirc' we require a seed for the random generator.+evalRandomCirc :: Int -> RandomCirc a -> Circ a+evalRandomCirc seed rc = evalStateT rc (mkStdGen seed)++-- | To evaluate a 'ListCirc' we require a list of booleans.+evalListCirc :: [Bool] -> ListCirc a -> Circ a+evalListCirc bools lc = evalStateT lc bools ++-- | Lift 'evalRandomCirc' to unary random circuit generating functions.+evalRandomCirc_unary :: Int -> (a -> RandomCirc b) -> a -> Circ b+evalRandomCirc_unary seed rcirc input = evalRandomCirc seed (rcirc input)++-- | Left 'evalListCirc' to unary list circuit generating functions.+evalListCirc_unary :: [Bool] -> (a -> ListCirc b) -> a -> Circ b+evalListCirc_unary bools lcirc input = evalListCirc bools (lcirc input)++-- | Lift the underlying 'randomR' function into the RandomCirc monad.+randomRRandomCirc :: Random a => (a,a) -> RandomCirc a+randomRRandomCirc (a0,a1) = do+ stdgen <- get+ let (random_a,stdgen') = randomR (a0,a1) stdgen+ put stdgen'+ return random_a++-- | Print a RandomCirc by evaluating it with a seed in the IO monad.+print_unary_random :: (QCData qa) => Format -> (qa -> RandomCirc b) -> qa -> IO ()+print_unary_random format rcirc input = do+ seed <- randomIO+ let circ = evalRandomCirc_unary seed rcirc+ print_unary format circ input++-- | Print a LiftCirc by evaluating it in the IO Monad, so as to read+-- in a given number of booleans.+print_unary_list :: (QCData qa) => Format -> Int -> (qa -> ListCirc b) -> qa -> IO ()+print_unary_list format liftings lcirc input = do+ bools <- mapM (\() -> getBit) (replicate liftings ())+ let circ = evalListCirc_unary bools lcirc+ print_unary format circ input++-- | Lift the 'identity_transformer' using any monad transformer.+lifted_identity_transformer :: (MonadTrans t) => Transformer (t Circ) Qubit Bit+lifted_identity_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> lift $ without_controls_if ncf $ do+ q' <- qnot q `controlled` cs+ return ([q'], [] ,cs)+lifted_identity_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \ws [] cs -> lift $ without_controls_if ncf $ do+ ws' <- qmultinot_list (map (\x -> (x,True)) ws) `controlled` cs+ return (ws', [], cs)+lifted_identity_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> lift $ without_controls_if ncf $ do+ q' <- hadamard q `controlled` cs+ return ([q'], [], cs)+lifted_identity_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w,v] [] cs -> lift $ without_controls_if ncf $ do+ (w',v') <- swap_qubit w v `controlled` cs+ return ([w',v'], [], cs)+lifted_identity_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w,v] [] cs -> lift $ without_controls_if ncf $ do+ (w',v') <- gate_W w v `controlled` cs+ return ([w',v'], [], cs)+lifted_identity_transformer (T_QGate name _ _ inv ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist name inv ws vs `controlled` c+ return (ws', vs', c)+lifted_identity_transformer (T_QRot name _ _ inv t ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_rotation_qulist name inv t ws vs `controlled` c+ return (ws', vs', c)+lifted_identity_transformer (T_GPhase t ncf f) = f $+ \qs c -> lift $ without_controls_if ncf $ do+ global_phase_anchored_list t qs `controlled` c+ return c+lifted_identity_transformer (T_CNot ncf f) = f $+ \q c -> lift $ without_controls_if ncf $ do+ q' <- cnot q `controlled` c+ return (q', c)+lifted_identity_transformer (T_CGate name ncf f) = f $+ \ws -> lift $ without_controls_if ncf $ do + v <- cgate name ws+ return (v, ws)+lifted_identity_transformer (T_CGateInv name ncf f) = f $+ \v ws -> lift $ without_controls_if ncf $ do + cgateinv name v ws+ return ws+lifted_identity_transformer (T_CSwap ncf f) = f $+ \w v c -> lift $ without_controls_if ncf $ do+ (w',v') <- swap_bit w v `controlled` c+ return (w',v',c)+lifted_identity_transformer (T_QPrep ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do + v <- prepare_qubit w+ return v+lifted_identity_transformer (T_QUnprep ncf f) = f $ + \w -> lift $ without_controls_if ncf $ do + v <- unprepare_qubit w+ return v+lifted_identity_transformer (T_QInit b ncf f) = f $+ lift $ without_controls_if ncf $ do+ w <- qinit_qubit b+ return w+lifted_identity_transformer (T_CInit b ncf f) = f $+ lift $ without_controls_if ncf $ do+ w <- cinit_bit b+ return w+lifted_identity_transformer (T_QTerm b ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do+ qterm_qubit b w+ return ()+lifted_identity_transformer (T_CTerm b ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do+ cterm_bit b w+ return ()+lifted_identity_transformer (T_QMeas f) = f $ + \w -> lift $ do+ v <- measure_qubit w+ return v+lifted_identity_transformer (T_QDiscard f) = f $+ \w -> lift $ do+ qdiscard_qubit w+ return ()+lifted_identity_transformer (T_CDiscard f) = f $+ \w -> lift $ do+ cdiscard_bit w+ return ()+lifted_identity_transformer (T_DTerm b f) = f $+ \w -> lift $ do+ dterm_bit b w+ return ()+lifted_identity_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \ns ws c -> lift $ without_controls_if ncf $ do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,c)+lifted_identity_transformer (T_Comment s inv f) = f $+ \ws -> lift $ do+ comment_label s inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]+ return ()++-- | Dynamic lifting can make use of a random result (without caring which wire is+-- being lifted).+random_dynamic_lift :: Bit -> RandomCirc Bool+random_dynamic_lift _ = randomRRandomCirc (False,True)++-- | Dynamic lifting can pop the head off of a list of booleans, and use that to+-- lift the given wire.+list_dynamic_lift :: Bit -> ListCirc Bool+list_dynamic_lift _ = do+ xs <- get+ case xs of+ [] -> error "ListCirc list of liftings exhausted"+ (x:xs') -> do + put xs' + return x++-- | A dynamic transformer which is the identity transformer (lifted to+-- RandomCirc), except for the dynamic lifting operation.+random_dynamic_lift_transformer :: DynamicTransformer (StateT StdGen Circ) Qubit Bit+random_dynamic_lift_transformer = DT {+ transformer = lifted_identity_transformer,+ define_subroutine = \name subroutine -> do+ lift $ put_subroutine_definition name subroutine,+ lifting_function = random_dynamic_lift+ }++-- | A dynamic transformer which is the identity transformer (lifted to+-- ListCirc), except for the dynamic lifting operation.+list_dynamic_lift_transformer :: DynamicTransformer (StateT [Bool] Circ) Qubit Bit+list_dynamic_lift_transformer = DT {+ transformer = lifted_identity_transformer,+ define_subroutine = \name subroutine -> do+ lift $ put_subroutine_definition name subroutine,+ lifting_function = list_dynamic_lift+ }++-- | Print a circuit, using random dynamic liftings.+print_unary_with_random_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> a -> IO ()+print_unary_with_random_liftings format circ shape = do+ let lifted_circ = transform_unary_dynamic_shape random_dynamic_lift_transformer circ shape + print_unary_random format lifted_circ shape++-- | Print a circuit, using a list of dynamic liftings.+print_unary_with_list_liftings :: (QCData a,QCData b) => Format -> Int -> (a -> Circ b) -> a -> IO ()+print_unary_with_list_liftings format liftings circ shape = do+ let lifted_circ = transform_unary_dynamic_shape list_dynamic_lift_transformer circ shape + print_unary_list format liftings lifted_circ shape ++-- ======================================================================+-- * Simulating the Dynamic Liftings++-- | Add state to the Circ Monad so that we can simulate the circuit+-- and use that data for dynamic liftings.+data SimulationState = SS {+ s_quantum_state :: Amplitudes Double,+ s_classical_state :: Map Bit Bool,+ s_namespace :: Namespace, -- we need a namespace to keep track of subroutines+ s_rng :: StdGen+ }++-- | When we start a simulation, we need an empty starting state, with+-- a seed for the generator.+empty_simulation_state :: Int -> SimulationState+empty_simulation_state seed = SS { s_quantum_state = Vector [(Map.empty,1.0)], s_classical_state = Map.empty, s_namespace = namespace_empty, s_rng = mkStdGen seed}++-- | A State monad that holds our SimulationState.+type SimulatedCirc a = StateT SimulationState Circ a++-- | Evaluate a 'SimulatedCirc'. This requires a seed for the random generator.+evalSimulatedCirc :: Int -> SimulatedCirc a -> Circ a+evalSimulatedCirc seed sc = evalStateT sc (empty_simulation_state seed)++-- | Lift 'evalSimulatedCirc' to unary functions.+evalSimulatedCirc_unary :: Int -> (a -> SimulatedCirc b) -> a -> Circ b+evalSimulatedCirc_unary seed scirc input = evalSimulatedCirc seed (scirc input)++-- | Lift the underlying 'randomR' function into the SimulatedCirc monad.+randomRSimulatedCirc :: Random a => (a,a) -> SimulatedCirc a+randomRSimulatedCirc (a0,a1) = do+ state <- get+ let stdgen = s_rng state+ let (random_a,stdgen') = randomR (a0,a1) stdgen+ put (state {s_rng = stdgen'})+ return random_a++-- | A specialized put function for the quantum state that uses the current state+-- instead of a previously retrieved state.+putQS :: Amplitudes Double -> SimulatedCirc ()+putQS amps = do+ state <- get+ put (state {s_quantum_state = amps})++-- | A specialized put function for the classical state that uses the current state+-- instead of a previously retrieved state.+putCS :: Map Bit Bool -> SimulatedCirc ()+putCS bits = do+ state <- get+ put (state {s_classical_state = bits})++-- | 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 lookup the boolean+-- result otherwise.+s_classical_control :: Map Bit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool+s_classical_control bits (Signed bep val) = case bep of+ (Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit+ (Endpoint_Qubit _) -> error "CNot: Quantum Control on Classical Gate"++-- | Map the 's_classical_control' function to all the controls, and take the+-- 'and' of the result.+s_classical_controls :: Map Bit Bool -> Ctrls Qubit Bit -> Bool+s_classical_controls bits cs = and (map (s_classical_control bits) cs)++-- | 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 fires.+s_qc_control :: Map Bit Bool -> Map Qubit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool+s_qc_control bits mqb (Signed bep val) = case bep of+ (Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit+ (Endpoint_Qubit q) -> val == val' where val' = mqb Map.! q++-- | Map the 's_qc_control' function to all the controls (under the given basis +-- state), and take the 'and' of the result.+s_qc_controls :: Map Bit Bool -> Map Qubit Bool -> Ctrls Qubit Bit -> Bool+s_qc_controls bits mqb cs = and (map (s_qc_control bits mqb) cs)++-- | Apply the given function only if the controls fire.+s_if_controls :: Map Bit Bool -> Ctrls Qubit Bit -> (Map Qubit Bool -> Amplitudes Double) -> Map Qubit Bool -> Amplitudes Double+s_if_controls bits c f mqb = if (s_qc_controls bits mqb c) then f mqb else Vector [(mqb,1)]++-- | The 'simulated_lift_transformer' is the actual transformer that does the+-- simulation, while recreating the circuit.+simulated_lift_transformer :: Transformer (StateT SimulationState Circ) Qubit Bit+-- Translation of classical gates:+simulated_lift_transformer (T_CNot ncf f) = f $+ \b c -> do+ (b,c) <- lift $ without_controls_if ncf $ do+ b' <- cnot b `controlled` c+ return (b', c)+ state <- get+ let bits = s_classical_state state+ let ctrl = s_classical_controls bits c+ let val = bits Map.! b+ let bits' = if ctrl then (Map.insert b (not val) bits) else bits+ putCS bits'+ return (b,c)+simulated_lift_transformer (T_CInit val ncf f) = f $+ do+ b <- lift $ without_controls_if ncf $ do+ w <- cinit_bit val+ return w+ state <- get+ let bits = s_classical_state state+ putCS (Map.insert b val bits)+ return b+simulated_lift_transformer (T_CTerm b ncf f) = f $+ \w -> do+ lift $ without_controls_if ncf $ do+ cterm_bit b w+ return ()+ state <- get+ let bits = s_classical_state state+ let val = bits Map.! w+ if val /= b then error "CTerm: Assertion Incorrect"+ else do+ putCS (Map.delete w bits)+simulated_lift_transformer (T_CDiscard f) = f $+ \w -> do+ lift $ do+ cdiscard_bit w+ state <- get+ let bits = s_classical_state state+ putCS (Map.delete w bits)+simulated_lift_transformer (T_DTerm b f) = f $+ \w -> do+ lift $ do+ dterm_bit b w+ state <- get+ let bits = s_classical_state state+ putCS (Map.delete w bits)+simulated_lift_transformer (T_CGate name ncf f) = f $+ \ws -> do+ (v,ws) <- lift $ without_controls_if ncf $ do + v <- cgate name ws+ return (v, ws)+ state <- get+ let bits = s_classical_state state+ let list = map (\w -> bits Map.! w) ws+ let result = gateC name list+ putCS (Map.insert v result bits)+ return (v,ws) +simulated_lift_transformer g@(T_CGateInv name ncf f) = f $+ \v ws -> do+ ws <- lift $ without_controls_if ncf $ do + cgateinv name v ws+ return ws+ state <- get+ let bits = s_classical_state state+ let list = map (\w -> bits Map.! w) ws+ let result = bits Map.! v+ let result' = gateC name list+ if result == result' then return ws else error "CGateInv: Uncomputation error"+-- Translation of quantum gates:+simulated_lift_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] c -> do+ (q,c) <- lift $ without_controls_if ncf $ do+ q' <- qnot q `controlled` c+ return (q', c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q], [], c)+simulated_lift_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] c -> do+ (qs,c) <- lift $ without_controls_if ncf $ do+ qs' <- qmultinot_list (map (\x -> (x,True)) qs) `controlled` c + return (qs', c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = foldr (\q a -> apply (s_if_controls bits c (performGateQ gate q)) a) amps qs+ putQS amps'+ return (qs, [], c)+simulated_lift_transformer (T_QGate "H" 1 0 _ ncf f) = f $ + \[q] [] c -> do+ (q,c) <- lift $ without_controls_if ncf $ do+ q' <- hadamard q `controlled` c+ return (q', c)+ let gate = gateQ "hadamard"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q], [], c)+simulated_lift_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w, v] [] c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- swap_qubit w v `controlled` c+ return (w',v',c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps+ let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gate w)) amps'+ let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps''+ putQS amps'''+ return ([w, v], [], c)+simulated_lift_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w, v] [] c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- gate_W w v `controlled` c+ return (w',v',c)+ let gateX = gateQ "x"+ let gateH = gateQ "hadamard"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps+ let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gateH w)) amps'+ let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps''+ putQS amps'''+ return ([w, v], [], c)+simulated_lift_transformer (T_QGate "trace" _ _ inv ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist "trace" inv ws vs `controlled` c+ return (ws', vs', c)+simulated_lift_transformer (T_QGate name _ _ inv ncf f) = f $ + \[q] [] c -> do+ ([q],[],c) <- lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist name inv [q] [] `controlled` c+ return (ws', vs', c)+ let gate = gateQinv name inv+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q],[],c)+simulated_lift_transformer (T_QRot name _ _ inv theta ncf f) = f $ + \[q] [] c -> do+ ([q],[],c) <- lift $ without_controls_if ncf $ do+ (ws', vs') <- named_rotation_qulist name inv theta [q] [] `controlled` c+ return (ws', vs', c)+ let gate = rotQinv name inv theta+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q],[],c)+simulated_lift_transformer (T_GPhase t ncf f) = f $+ \w c -> do+ c <-lift $ without_controls_if ncf $ do+ global_phase_anchored_list t w `controlled` c+ return c+ state <- get+ let gate = rotQ "exp(% pi i)" t+ let wire = -1+ let q = qubit_of_wire wire+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (vector (Map.insert q False))) amps + let amps'' = apply (s_if_controls bits c (performGateQ gate q)) amps'+ let (p,_,ampsf) = split amps'' q+ case p of+ 0.0 -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf' + putQS ampsf'+ return c+ _ -> error "GPhase"+simulated_lift_transformer (T_QInit val ncf f) = f $+ do+ q <- lift $ without_controls_if ncf $ do+ w <- qinit_qubit val+ return w+ state <- get+ let amps = s_quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + putQS amps'+ return q+simulated_lift_transformer (T_QMeas f) = f $+ \q -> do+ b <- lift $ do+ b <- measure_qubit q+ return b+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let (p,ift,iff) = split amps q+ pp <- randomRSimulatedCirc (0,1.0)+ let (val,amps') = if p > pp then (True,ift) else (False,iff)+ let amps'' = apply (vector (Map.delete q)) amps' + let bits' = Map.insert b val bits+ putQS amps''+ putCS bits'+ return b+simulated_lift_transformer (T_QDiscard f) = f $+ \q -> do+ lift $ do+ qdiscard_qubit q+ return ()+ -- a discard is essentially a measurement, with the result thrown away, so we+ -- do that here, as it will reduce the size of the quantum state we are+ -- simulating over.+ state <- get+ let (p,ift,iff) = split (s_quantum_state state) q+ pp <- randomRSimulatedCirc (0,1.0)+ let amps = if p > pp then ift else iff+ let amps' = apply (vector (Map.delete q)) amps+ putQS amps'+ return ()+simulated_lift_transformer (T_QTerm b ncf f) = f $+ \q -> do+ lift $ without_controls_if ncf $ qterm_qubit b q+ -- with a real quantum computer, when we terminate a qubit with an assertion+ -- we have no way of actually checking the assertion. The best we can do is+ -- measure the qubit and then throw an error if the assertion is incorrect,+ -- which may only occur with a small probability. Here, we are able to split+ -- the quantum state and see if the qubit exists in the incorrect state with+ -- any non-zero probability, and throw an error.+ state <- get+ let amps = s_quantum_state state+ let (p,ampst,ampsf) = split amps q+ case (b,p) of+ (True,1.0) -> do+ let ampst' = apply (vector (Map.delete q)) ampst+ putQS ampst'+ return ()+ (False,0.0) -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf+ putQS ampsf'+ return ()+ (True,pt) -> error ("QTerm: Assertion Incorrect (True only has probability " ++ show pt ++ ")")+ (False,pt) -> error ("QTerm: Assertion Incorrect (False only has probability " ++ show (1.0 - pt) ++ ")")+simulated_lift_transformer (T_Comment name inv f) = f $+ \ws -> do+ lift $ do+ comment_label name inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]+ return ()+simulated_lift_transformer g@(T_CSwap ncf f) = f $+ \w v c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- swap_bit w v `controlled` c+ return (w',v',c)+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_QPrep ncf f) = f $+ \w -> do+ w <- lift $ without_controls_if ncf $ do + v <- prepare_qubit w+ return v+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_QUnprep ncf f) = f $+ \w -> do+ w <- lift $ without_controls_if ncf $ do + v <- unprepare_qubit w+ return v+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \ns ws c -> do+ (ws,c) <- lift $ without_controls_if ncf $ do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,c)+ case Map.lookup n ns of+ Just (TypedSubroutine sub_ocirc _ _ _) -> do+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ let in_bindings = bind_list in_wires ws bindings_empty+ let sub_bcirc = (sub_circ,ns)+ out_bind <- transform_bcircuit_rec simulated_lift_transformer sub_bcirc in_bindings+ return (unbind_list out_bind out_wires, c) + Nothing -> error $ "simulated_lift_transformer: subroutine " ++ show n ++ " not found (in " ++ showNames ns ++ ")"++-- | Dynamic lifting can make use of a simulated result.+simulated_dynamic_lift :: Bit -> SimulatedCirc Bool+simulated_dynamic_lift b = do+ state <- get+ let bits = s_classical_state state+ case Map.lookup b bits of+ Just val -> return val+ Nothing -> error $ "simulated_dynamic_lift: bit " ++ show b ++ " not found" ++-- | A dynamic transformer which simulates the circuit, whilst+-- reconstructing it with simulated lifting results.+-- Note: do not handle classical controlling.+simulated_dynamic_lift_transformer :: DynamicTransformer (StateT SimulationState Circ) Qubit Bit+simulated_dynamic_lift_transformer = DT {+ transformer = simulated_lift_transformer,+ define_subroutine = \name subroutine -> do+ lift $ do+ s <- get_namespace+ let s' = map_provide name subroutine s+ set_namespace s'+ put_subroutine_definition name subroutine,+ lifting_function = simulated_dynamic_lift+ }++-- | Print a RandomCirc by evaluating it with a seed in the IO monad.+print_simulated :: Format -> SimulatedCirc b -> IO ()+print_simulated format scirc = do+ seed <- randomIO+ let circ = evalSimulatedCirc seed scirc+ print_unary format (\() -> circ) ()++-- | Print a circuit, using simulated liftings.+print_unary_with_simulated_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> BType a -> IO ()+print_unary_with_simulated_liftings format circ input = print_simulated format (lifted_circ ())+ where+ circ' = \ () -> do+ a <- qc_init input+ circ a+ lifted_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ' () + ++-- | Pass a (possibly) dynamic circuit through the +-- 'simulated_dynamic_lift_transformer' and evaluate the liftings so as to+-- leave us with a static circuit that represents a single run of the original+-- circuit, with the given inputs. We also need to pass in a seed for the RNG.+simulate_liftings_unary :: (QCData a, QCData b) => Int -> (a -> Circ b) -> BType a -> Circ b+simulate_liftings_unary seed fcirc_in input = out_circ+ where+ circ_in = \() -> do+ a <- qc_init input+ fcirc_in a+ s_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ_in ()+ out_circ = evalSimulatedCirc seed (s_circ ())+
@@ -0,0 +1,718 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}++{-# LANGUAGE IncoherentInstances #-} +{-# LANGUAGE DeriveDataTypeable #-}++-- | This library provides a quantum implementation of fixed-precision real numbers (i.e., with precision determined at circuit-generation time), and classical counterpart types.+--+-- Currently still significantly experimental. TODO: +--+-- * decide on how much access to provide to underlying representation of 'FPReal'. Full 'fpreal_case', or more like just what’s available through Haskell’s 'RealFloat'?+--+-- * decide how to use 'ErrMsg': in internal functions only, or exported ones too?+--+-- * many specific TODO’s in code throughout this file.+--+-- * write export list.+ +module Quipper.Libraries.FPReal where++import Quipper+import Quipper.Internal++import Quipper.Libraries.Arith++import Quipper.Utils.Auxiliary++import Control.Monad (foldM)+import Data.Maybe (fromJust)+import Data.Ratio (numerator, denominator)+import Data.Typeable++-- ===========================================+-- * Fixed-precision real arithmetic: the FPReal family++-- | 'FPRealX': a family of datatypes for fixed-precision real numbers.+-- (That is, the precision is a parameter, fixed at circuit-generation time.)+-- +-- 'FPRealX' is based on the family 'XInt' of fixed-length integer types:+-- @'FPRealX' /n/ /a/@ represents 2[sup /n/] /a/, where /a/ is some fixed-length integer.+--+-- Alternately, in the specific case /x/ = 'Bool', a Haskell 'Double' may be +-- used as an 'FPReal', considered as having indeterminate length and exponent.+-- This is exactly analogous to the case of indeterminate length in 'XInt' / 'IntM'; for a more+-- detailed explanation, see the documentation there.+data FPRealX x = FPRealX Int (XInt x) | FPReal_indet Double (Identity IntM (XInt x))+ deriving (Show, Typeable)++-- | Fixed-precision real parameters. As with 'IntM', the length and/or exponent+-- of an 'FPReal' may be indeterminate; such an 'FPReal' may only be used in +-- certain contexts, typically either when the length/exponent can be inferred from context +-- (e.g., terminating a 'FPRealQ'), or where the result can again be indeterminate.+--+-- As with indeterminate 'IntM's, indeterminate 'FPReal's may be used for efficient, +-- high-precision classical calculation, and then explicitly or implicitly coerced+-- to determinate 'FPReal's when required for interfacing with quantum computation.+type FPReal = FPRealX Bool++instance Show FPReal where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet r id) = show r++-- | Fixed-precision reals for quantum circuits.+type FPRealQ = FPRealX Qubit++instance Show FPRealQ where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet _ _) = error "FPRealX: internal error"++-- | Fixed-precision reals for classical circuits.+type FPRealC = FPRealX Bit++{- -- for when Qubit /= Bit:+instance Show FPRealC where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet _ _) = error "FPRealX: internal error"+-}++-- ----------------------------------------------------------------------+-- ** Primitive combinators on FPReal++-- $ Like 'XInt', the type 'FPReal' is intended to be an abstract data type,+-- and all access to it should pass through the functions of this+-- section.++-- *** Constructors++-- | Create an 'FPRealX' from an 'XInt' together with an exponent.+fprealx :: Int -> XInt x -> FPRealX x+fprealx n x = FPRealX n x++-- | Create an indeterminate 'FPReal' from a 'Double'. +fpreal_of_double :: Double -> FPReal+fpreal_of_double r = FPReal_indet r reflexivity++-- *** Destructor++-- | If the 'FPRealX' is of determinate exponent, return its exponent and mantissa.+-- +-- If the 'FPRealX' is indeterminate, return a pair (/r/, /id/), where /r/ is the underlying 'Double', and /id/ is a witness of the fact that /x/ = 'Bool'.+-- +-- This is a lowest-level access function not intended to be used by+-- user-level code, and is not exported. +fprealx_case :: FPRealX x -> Either (Int, XInt x) (Double, Identity IntM (XInt x))+fprealx_case (FPRealX n x) = Left (n,x)+fprealx_case (FPReal_indet r id) = Right (r, id)++-- ----------------------------------------------------------------------+-- ** Other low-level operations++-- $ The operations in this section are the only ones intended to use+-- 'fprealx_case' directly.++-- TODO: fprealx_expt, fprealx_num should probably take ErrMsg arguments, at least for internal use --- I guess only the versions specialized to FPRealQ, FPRealC should be written as though unconditional? Perhaps only these, plus the 'Maybe' version specialized to 'FPReal', should be exported?+ +-- | Extract the exponent of an 'FPRealX', assumed to be determinate.+--+-- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. +fprealx_expt :: FPRealX x -> Int+fprealx_expt x = + case fprealx_case x of+ Left (n, _) -> n+ Right _ -> error "fprealx_expt: indeterminate exponent"++-- | Extract the mantissa of an 'FPRealX', assumed to be of determinate exponent.+--+-- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. +fprealx_num :: FPRealX x -> XInt x+fprealx_num x = + case fprealx_case x of+ Left (_, xi) -> xi+ Right _ -> error "fprealx_num: indeterminate exponent"++-- | Extract the length (in bits) of an 'FPRealX', assumed to be of determinate exponent and length.+fprealx_length :: FPRealX x -> Int+fprealx_length x = + case fprealx_case x of+ Left (_, xi) -> fromJust $ xint_maybe_length xi+ Right _ -> error "fprealx_length: indeterminate exponent"++-- | Set the exponent of an 'FPReal' to /n/. This operation is only+-- legal if the input (a) has indeterminate exponent or (b) has+-- determinate exponent already equal to /m/. In particular, it cannot+-- be used to change the exponent from anything other than from+-- indeterminate to determinate.+-- +-- If both arguments already have determinate exponents, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.++-- Implementation note: the “intm_of_integer” may seem unnecessary,+-- but needed so that when /r/ is “undefined”, the result is+-- “intm_of_integer undefined” not just “undefined”. +fprealx_set_expt :: Int -> FPRealX x -> String -> FPRealX x+fprealx_set_expt n xr errstr = case fprealx_case xr of+ Left (n', xi) -> if n' == n then fprealx n xi else error errstr+ Right (r, id) -> fprealx n (identity id $ intm_of_integer $ round $+ if n >= 0 then (r / 2^n) else (r * 2^(-n)))++-- | Return the (possibly indeterminate) exponent of an 'FPRealX'.+fprealx_maybe_expt :: FPRealX x -> Maybe (Int)+fprealx_maybe_expt xr = case fprealx_case xr of+ Left (n, _) -> Just n+ Right _ -> Nothing++-- | Given an 'FPReal', return either the exponent and numerator, or else the double it wraps.+--+-- A specialized and implementation-hiding wrapper for 'fprealx_case'.++-- TODO: should this be exported?+fpreal_case :: FPReal -> Either (Int, IntM) (Double)+fpreal_case (FPRealX n x) = Left (n, x)+fpreal_case (FPReal_indet r _) = Right r++-- | Equality test. If both have indeterminate exponent, check equality of underlying 'Double's. +-- Otherwise, if exponents are compatible (i.e. both determinate and equal, or one indeterminate), +-- check equality of numerators. If exponents are incompatible, throw an error (the test+-- should in this case be considered ill-typed).+fprealx_equals :: (Eq x) => FPRealX x -> FPRealX x -> Bool +fprealx_equals x y =+ case (fprealx_case x, fprealx_case y) of+ (Left (n,xi), Left (n',yi)) + | n == n' -> xi == yi+ | otherwise -> error "Equality test on FPRealx: operands must be of equal exponent"+ (_, Left (m,yi)) -> fprealx_equals (fprealx_set_expt m x "fprealx_equals") y+ (Left (n,xi), _) -> fprealx_equals x (fprealx_set_expt n y "fprealx_equals")+ (Right (r, _), Right (r', _)) -> r == r'++-- ----------------------------------------------------------------------+-- ** Shape parameters++-- | Return a piece of shape data to represent an /l/-qubit quantum+-- real with exponent /n/. +-- Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+fprealq_shape :: Int -> Int -> FPRealQ+fprealq_shape n l = fprealx n $ qdint_shape l++-- | Return a piece of shape data to represent an /l/-bit 'FPRealC', +-- with exponent /n/.+-- Please note that the data can only be used as shape; it will be+-- undefined at the leaves.+fprealc_shape :: Int -> Int -> FPRealC+fprealc_shape n l = fprealx n $ cint_shape l++-- ======================================================================+-- ** Circuit type class instances++-- Note: instance declarations do not show up in the documentation++-- | Try to set the exponent/length of an 'FPReal' to that of another 'FPRealX'+-- value (e.g. an 'FPRealQ', an 'FPRealC', or another 'FPReal').+-- This will fail with an error if both numbers already have determinate+-- lengths that don't coincide. In this case, the string argument is+-- used as an error message.+--+-- The possible “shapes” of 'FPReal's may be seen as a partial order, where +-- /s1/ ≤ /s2/ means that values of shape /s1/ are coercible to values of shape+-- /s2/. 'fpreal_promote' may be seen as taking the binary /sup/ in this poset.+fpreal_promote :: FPReal -> FPRealX x -> ErrMsg -> FPReal+fpreal_promote br xr errmsg = + case fprealx_maybe_expt xr of+ Nothing -> br+ Just n -> + let br' = fprealx_set_expt n br (errmsg "FPReal: exponent mismatch")+ in fprealx n $ intm_promote (fprealx_num br') (fprealx_num xr) (errmsg "FPReal: length mismatch") + +type instance QCType x y (FPRealX z) = FPRealX (QCType x y z)+type instance QTypeB FPReal = FPRealQ++instance QCLeaf x => QCData (FPRealX x) where+ qcdata_mapM shape f g xr+ = mmap (fprealx (fprealx_expt xr)) $ qcdata_mapM (fprealx_num shape) f g (fprealx_num xr) + qcdata_zip shape q c q' c' xr yr e+ | fprealx_expt xr == fprealx_expt yr+ = fprealx (fprealx_expt xr) + $ qcdata_zip (fprealx_num shape) q c q' c' (fprealx_num xr) (fprealx_num yr) (const $ e "FPRealX: length mismatch")+ | otherwise+ = error (e "FPRealX: exponent mismatch")+ qcdata_promote = fpreal_promote++-- Labeling of FPRealX is s[hi-1], ..., s[lo], where lo is the exponent.+instance QCLeaf x => Labelable (FPRealX x) String where+ label_rec xr s = do+ let n = fprealx_expt xr+ xi = fprealx_num xr+ qx = list_of_xint_lh xi+ sequence_ [ label_rec q s `indexed` show i | (q,i) <- zip qx [n..] ]++-- ======================================================================+-- * Classical arithmetic on FPReal++-- | Convert an 'FPReal' to a 'Double'.+double_of_fpreal :: FPReal -> Double+double_of_fpreal xr = case fpreal_case xr of+ Left (n, xi) -> if n >= 0 then (fromIntegral xi) * (2^n)+ else (fromIntegral xi) / (2^(abs n))+ Right r -> r++-- | From a list of 'FPReal's, extract a common shape, provided they have compatible shape+-- (i.e. if any have determinate exponent, they must agree; similarly for length),+-- and throw an error otherwise.+-- +-- Can be seen as producing finitary suprema in the partial order of shapes.+--+-- The 'FPReal' produced should be considered just a shape; its value is a dummy that+-- should never be used (and will throw an error if it is).+fpreal_common_shape :: [FPReal] -> ErrMsg -> FPReal +fpreal_common_shape xs e = foldl (\x y -> fpreal_promote x y e)+ (fpreal_of_double (error $ e "fpreal_common_shape: dummy value produced here was later accessed"))+ xs++-- | Auxiliary function for lifting a binary operator from 'Double'+-- to 'IntM'. The string argument is the name of the operator, for+-- error messages.+fpreal_binop :: (Double -> Double -> Double) -> String -> (FPReal -> FPReal -> FPReal) +fpreal_binop op opname x y =+ fpreal_promote + (fpreal_of_double $ op (double_of_fpreal x) (double_of_fpreal y))+ (fpreal_common_shape [x,y] errmsg)+ (const "FPReal: internal error (fpreal_binop)")+ where errmsg = (\s -> "Binary operation " ++ opname ++ " on FPReal: " ++ s)++-- | Auxiliary function for lifting a unary operator from 'Double' to+-- 'FPReal'.+fpreal_unop :: (Double -> Double) -> FPReal -> FPReal+fpreal_unop op x = fpreal_promote (fpreal_of_double $ op $ double_of_fpreal x) x + (const "FPReal: internal error (fpreal_unop)")++------+-- Classical typeclass instances+------++instance Eq x => Eq (FPRealX x) where+ (==) = fprealx_equals++instance Num FPReal where+ (+) = fpreal_binop (+) "+"+ (*) = fpreal_binop (*) "*"+ (-) = fpreal_binop (-) "-"+ abs = fpreal_unop abs+ signum = fpreal_unop signum+ -- Note: signum on determinate exponent/length FPReals has slightly + -- surprising behavior if /n/ ≤ –/l/: this will give + -- 0, since then 1 = –1 = 0 mod 2^{l + n}. In other words, the+ -- output 1.0 or -1.0 is not representable in this fixed-precision format.+ fromInteger = fpreal_of_double . fromInteger++instance Ord FPReal where+ compare x y = compare (double_of_fpreal x) (double_of_fpreal y)+-- TODO: is this the right thing to do? Perhaps we should first check they have +-- compatible shapes, and throw an error otherwise.++instance Enum FPReal where+ succ = fpreal_unop succ+ pred = fpreal_unop pred+ toEnum = fromIntegral+ fromEnum = fromEnum . double_of_fpreal++instance Real FPReal where+ toRational = toRational . double_of_fpreal++instance Fractional FPReal where+ fromRational = fpreal_of_double . fromRational+ recip = fpreal_unop recip++{- TODO: something along these lines would probably still be good to give. Work on thinking of good interface. Or maybe not really necessary??++-- | Convert from any 'RealFrac' type (eg 'Rational', 'Float') to 'FPReal', with specified precision and (possibly indeterminate) length.+fpreal_from_realfrac_with_precision :: (RealFrac a) => Int -> Maybe Int -> a -> FPReal+fpreal_from_realfrac_with_precision n l r = case l of + Just l -> FPRealX n $ intm l (round $ r * (2^n))+ Nothing -> FPRealX n $ fromIntegral (round $ r * (2^n))+-}++instance Floating FPReal where+ pi = fpreal_of_double pi+ log = fpreal_unop log+ exp = fpreal_unop exp + sin = fpreal_unop sin + cos = fpreal_unop cos + sinh = fpreal_unop sinh + cosh = fpreal_unop cosh + asin = fpreal_unop asin + acos = fpreal_unop acos + atan = fpreal_unop atan + asinh = fpreal_unop asinh + acosh = fpreal_unop acosh + atanh = fpreal_unop atanh + +instance RealFrac FPReal where+ properFraction x =+ let (x_int, x_frac_double) = properFraction $ double_of_fpreal x in+ (x_int, fpreal_promote (fpreal_of_double x_frac_double) x (const "FPReal: internal error (properFraction)")) + ceiling = ceiling . double_of_fpreal++{- TODO: would definitely be good to give.+instance RealFloat FPReal+-}++-- ** Shape/precision control++-- | Extend the length of a determinate length and precision 'FPReal' by /m/ high and /n/ low bits, without changing its value. +fpreal_pad :: Int -> Int -> FPReal -> FPReal+fpreal_pad m n x = + case fprealx_maybe_expt x of+ Nothing -> error e_expt+ Just x_expt ->+ let x_num = fprealx_num x+ in case intm_length x_num of+ Nothing -> error e_length+ Just x_length ->+ fprealx (x_expt - n) (2^n * intm_extend_signed (x_length + m + n) x_num)+ where+ e_expt = "fpreal_pad: input of indeterminate exponent"+ e_length = "fpreal_pad: input of indeterminate length"++-- | Discard the top /m/ and bottom /n/ bits of a determinate length and precision 'FPReal'.+fpreal_unpad :: Int -> Int -> FPReal -> FPReal+fpreal_unpad m n x = + let x_bits = boollist_of_intm_bh $ fprealx_num x+ x_expt = fprealx_expt x+ x_bits_new = reverse $ drop n $ reverse $ drop m x_bits+ in fprealx (x_expt + n) (intm_of_boollist_bh x_bits_new)++-- | Fix the length of an 'IntM' (to automatically-generated values), if not already determinate.+--+-- TODO: belongs in 'Quipper.Libraries.Arith'+intm_fix_length_auto :: IntM -> IntM+intm_fix_length_auto x = case intm_length x of+ Just _ -> x+ Nothing -> + let l = (1 +) $ ceiling $ logBase 2 $ fromIntegral $ abs x + (if x >= 0 then 1 else 0)+ in intm l (fromIntegral x)++-- | Fix the precision and length of an 'FPReal' (to automatically-generated values), +-- leaving unchanged any parts of the shape that are already set. +--+-- TODO: discuss \/ reconsider \/ improve implementation of this. ++-- Implementation note: aim to use as few bits as possible, while retaining accuracy.+--+-- Not clear what should be done in case denominator not a power of 2. However,+-- Haskell’s 'Double' has radix 2, so ordinarily denominator always should be a power of 2.+--+-- However, this does give pretty high-precision by default! Is that good? Seems expensive in qubits.+fpreal_fix_shape_auto :: FPReal -> FPReal+fpreal_fix_shape_auto x = case fpreal_case x of+ Left (e, n) -> fprealx e (intm_fix_length_auto n)+ Right x -> + let r = toRational x+ d = denominator r+ in if d == 2^(round $ logBase 2 $ fromIntegral d)+ then fprealx+ (negate $ round $ logBase 2 $ fromIntegral d)+ (intm_fix_length_auto $ fromIntegral $ numerator r)+ else error "fpreal_fix_shape_auto: not yet fully implemented"++-- ======================================================================+-- * Quantum operations: FPRealQ++-- ** Shape/precision control++-- | Extend the length of an 'FPRealQ' by /m/ high bits and /n/ low bits, without changing its value. +fprealq_pad :: Int -> Int -> FPRealQ -> Circ FPRealQ+fprealq_pad m n x = do+ let x_bits = qulist_of_qdint_bh $ fprealx_num x+ x_expt = fprealx_expt x+ new_high_bits <- qinit $ replicate m False+ new_high_bits <- case x_bits of + [] -> return new_high_bits + (x_high:_) -> mapUnary qnot new_high_bits `controlled` x_high+ new_low_bits <- qinit $ replicate n False+ return $ fprealx (x_expt - n) $ qdint_of_qulist_bh $ new_high_bits ++ x_bits ++ new_low_bits++-- | Cut off the top /m/ and bottom /n/ bits of an 'FPRealQ', retaining them as explicit garbage.+fprealq_unpad :: Int -> Int -> FPRealQ -> Circ (FPRealQ, [Qubit])+fprealq_unpad m n x =+ let x_bits = qulist_of_qdint_bh $ fprealx_num x+ x_expt = fprealx_expt x+ x_bits_new = reverse $ drop n $ reverse $ drop m x_bits+ garbage = (take m x_bits) ++ (take n $ reverse x_bits)+ in return (fprealx (x_expt + n) (qdint_of_qulist_bh x_bits_new), garbage)++-- | Formally shift an 'FPRealQ' up /n/ bits, i.e. add /n/ to its exponent.+fprealq_shift :: Int -> FPRealQ -> FPRealQ+fprealq_shift n x =+ let num = fprealx_num x+ expt = fprealx_expt x+ in fprealx (expt + n) num++-- ** Quantum arithmetic++-- $ Besides the functions appearing here in the documentation, basic operations ('q_add', etc) are also provided as methods of the 'QNum' type class instance; see 'QNum' for documentation of these functions.++------+-- Quantum typeclass instances+------ ++instance QNum FPRealQ where+ q_add x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ (nx,ny,ns) <- q_add nx ny+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex ns))+ else error "q_add // FPReal: exponent mismatch in arguments."+ q_mult x y = -- TODO: to implement+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ np <- qinit $ qc_false $ nx+ (nx,ny,np) <- named_gate "q_mult // FPReal" (nx,ny,np)+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex np))+ else error "q_mult // FPReal: length mismatch in arguments."+ q_sub x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ (nx,ny,nd) <- q_sub nx ny+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex nd))+ else error "q_sub // FPReal: length mismatch in arguments."+ q_abs x = + let ex = fprealx_expt x+ nx = fprealx_num x+ in do+ (nx,nx') <- q_abs nx+ return ((fprealx ex nx), (fprealx ex nx'))+ q_negate x = + let ex = fprealx_expt x+ nx = fprealx_num x+ in do+ (nx,nx') <- q_negate nx+ return ((fprealx ex nx), (fprealx ex nx'))+ q_signum = error "FPReal // q_signum: not yet implemented." -- TODO!+ q_fromQDInt x = do+ (x,x') <- qc_copy_fun x+ return (x,(fprealx 0 x'))++instance QOrd FPRealQ where+ q_less x y = + let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ x_less_y <- q_less nx ny+ return x_less_y+ else error "q_less // FPReal: length mismatch in arguments."+ q_leq x y = + let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ x_leq_y <- q_leq nx ny+ return x_leq_y+ else error "q_leq // FPReal: length mismatch in arguments."++-- | Analogue of 'q_add_in_place', for 'FPRealQ'.+fprealq_add_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_add_in_place x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ (x,y) <- q_add_in_place nx ny+ return ((fprealx ex nx), (fprealx ey ny))+ else error "q_add_in_place_fprealq // FPReal: length mismatch in arguments."++-- | Analogue of 'q_sub_in_place', for 'FPRealQ'.+fprealq_sub_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_sub_in_place x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ (x,y) <- q_sub_in_place nx ny+ return ((fprealx ex nx), (fprealx ey ny))+ else error "q_sub_in_place_fprealq // FPReal: length mismatch in arguments."++-- | Subtract a parameter 'FPReal' from an 'FPRealQ', in place. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_sub_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ+fprealq_sub_param_in_place x qy = + with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy) <- fprealq_sub_in_place qx qy+ return qy)+ where+ e_prec = "fprealq_sub_param_in_place: incompatible precision"++-- | Add a parameter 'FPReal' to an 'FPRealQ', in place. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_add_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ+fprealq_add_param_in_place x qy = + with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy) <- fprealq_add_in_place qx qy+ return qy)+ where+ e_prec = "fprealq_sub_param_in_place: incompatible precision"++-- | Multiply an 'FPRealQ' by a parameter 'FPReal'. The parameter may have any shape.+fprealq_mult_param_het :: FPReal -> FPRealQ -> Circ (FPRealQ, FPRealQ)+fprealq_mult_param_het x_in qy = do+ let x = fpreal_fix_shape_auto x_in+ e = fprealx_expt x+ l = fromJust $ intm_length $ fprealx_num x+ x_bits = boollist_of_intm_bh $ fprealx_num x+ qy_expt = fprealx_expt qy+ qy_num = fprealx_num qy+ qy_length = qdint_length qy_num+ prod_bits = [ (is_neg, i) | (x_i, i) <- zip x_bits [e+l-1,e+l-2..e]+ , x_i == True+ , let is_neg = i == e+l-1 ]+ qprod <- if null prod_bits + then qinit $ fpreal_promote 0 qy ("internal error: fprealq_mult_param_het promotion: " ++)+ else with_computed (do+ let i_max = snd $ head prod_bits+ i_min = snd $ last prod_bits+ -- Initialise an accumulating product at 0, with as many bits as may be needed in intermediate calculation:+ qprod_accum <- qinit $ fprealx (qy_expt + i_min) $ intm (1 + qy_length + i_max - i_min) 0+ -- Add the appropriate shifts of qy to it:+ qprod_accum <- foldM+ (\qprod_accum (is_neg, i) -> do + qy_i <- qc_copy $ fprealq_shift i qy+ qy_i <- fprealq_pad (1 + i_max - i) (i - i_min) qy_i+ (qy_i, qprod_accum) <- if is_neg+ then fprealq_sub_in_place qy_i qprod_accum+ else fprealq_add_in_place qy_i qprod_accum+ return qprod_accum)+ qprod_accum+ prod_bits+ -- Now shift/truncate the product to match the input length/precision:+ qprod_large <- fprealq_pad (max (-i_max) 0) (max i_min 0) qprod_accum+ (qprod, garbage) <- fprealq_unpad (1 + max i_max 0) (max (-i_min) 0) qprod_large+ return qprod)+ -- Copy it for output, before erasing garbage:+ qc_copy+ return (qy, qprod)++-- | Compare an 'FPRealQ' to a parameter 'FPReal'. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_ge_param :: FPReal -> FPRealQ -> Circ (FPRealQ, Qubit)+fprealq_ge_param x qy =+ with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy, test) <- q_ge qx qy+ return (qy, test))+ where+ e_prec = "fprealq_ge_param: incompatible precision"++-- | 'fprealq_add_het' /p/ /l/ /qx/ /qy/: add two 'FPRealQ's, of potentially different precisions and lengths, into a fresh one with precision /p/ and length /l/. +--+-- TODO: not yet implemented; currently just black box.+fprealq_add_het :: Int -> Int -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ,FPRealQ)+fprealq_add_het p l qx qy = do+ qz <- qinit (fprealx p (intm l 0))+ (qx,qy,qz) <- named_gate "fprealq_add_het" (qx,qy,qz)+ return (qx,qy,qz)++-- | @'fprealq_logBase_internal' /errmsg/ /b/ /h/ /p/ /qx/'@: compute log[sub /b/](/qx/), returning /l/ binary digits before the point and /p/ after. The underlying implementation of the rest of the 'fprealq_log' family. +--+-- Behavior on non-positive /qx/: currently unspecified. TODO: decide and fix this. Make assertion/post-selection on positivity? Or treat as unsigned?+--+-- Time-complexity (estimated): /O/((log /l/[sup /qx/] + log log /b/)(/l/[sup /qx/] + (/h/+/p/)[sup 2])).+fprealq_logBase_internal :: (Floating a, RealFrac a) => ErrMsg -> a -> Int -> Int -> FPRealQ -> Circ (FPRealQ, FPRealQ)+fprealq_logBase_internal e b h_out p_out x =+ if h_out + p_out < 0 + then error $ e "negative length specified."+ else if b <= 0+ then error $ e "base of logarithm must be > 0"+ else do+ y <- with_computed + (do+ -- Shift x into the interval [0,1], and pad it for intermediate calculation:+ let l_in = qdint_length $ fprealx_num x+ e_in = fprealx_expt x+ l_in_pad = 1 + l_in `div` 2+ p_in_pad = p_out - (floor $ logBase 2 $ log b) + 1+ x_0 <- fprealq_pad l_in_pad p_in_pad $ fprealx (1 - l_in) $ fprealx_num x+ -- Bound the length and precision needed for computing log_2 x' to precision p_out: + let y_size_bound = 1 + (ceiling $ logBase 2 $ fromIntegral $ l_in - 1)+ y_h_pad = max 0 (y_size_bound - h_out)+ y_p_pad = 2 + (ceiling $ logBase 2 $ fromIntegral $ l_in_pad + p_in_pad - 1)+ y_0 <- qinit $ fprealx (- p_out - y_p_pad) $ intm (y_h_pad + h_out + p_out + y_p_pad) 0+ -- Iteratively compute log x_0, starting with (x0,y0): + let ks_big = 2^(l_in `div` 2)+ : (reverse $ [ 2^(2^i) | i <- [0..(ceiling $ logBase 2 $ (fromIntegral l_in) / 2) - 1] ])+ ks_small = [ (2^i + 1)/(2^i) | i <- [1..p_out - (floor $ logBase 2 $ log b) + 1]]+ -- bound in ks_small chosen to ensure logBase b k_fin < 2^(-p_out)+ (x_fin,y_fin) <- foldM+ (\(xi,yi) ki -> do+ (xi, xi_ki) <- fprealq_mult_param_het ki xi+ (xi_ki, test1) <- fprealq_ge_param 1 xi_ki+ (xi_ki, xi, test2) <- q_gt xi_ki xi+ (xi1, yi1) <- qc_copy (xi,yi)+ (xi1,xi) <- controlled_not xi1 xi `controlled` test1 .&&. test2 + (xi1,xi_ki) <- controlled_not xi1 xi_ki `controlled` test1 .&&. test2 + yi1 <- fprealq_sub_param_in_place (logBase (realToFrac b) ki) yi1 `controlled` test1 .&&. test2+ return (xi1, yi1))+ (x_0,y_0)+ (ks_big ++ ks_small)+ -- Correct for the shift between x and x_, then unpad y to the output precision:+ y_fin <- fprealq_add_param_in_place ((log 2 / log (realToFrac b)) * (fromIntegral $ l_in + e_in - 1)) y_fin+ (y_fin, garbage) <- fprealq_unpad y_h_pad y_p_pad y_fin+ return y_fin)+ qc_copy+ return (x,y)++-- | Compute the natural log of an 'FPRealQ', with length and precision as in the input.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_log :: FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_log x = + let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_log: " ++ ) (exp 1) h l x++-- | Compute the log (to arbitrary base) of an 'FPRealQ', with length and precision as in the input.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_logBase :: (Floating a, RealFrac a) => a -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_logBase b x =+ let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_logBase: " ++ ) b h l x++-- | @'fprealq_log_with_shape' /x/ /y/@: compute the natural log /y/, with length and precision of /x/.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_log_with_shape :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_log_with_shape x = + let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_log_with_shape: " ++ ) (exp 1) h l++-- | @'fprealq_log_with_shape' /b/ /x/ /y/@: compute log[sup /b/] /y/, with length and precision of /x/.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_logBase_with_shape :: (Floating a, RealFrac a)+ => a -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_logBase_with_shape b x =+ let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_logBase_with_shape: " ++ ) b h l
@@ -0,0 +1,645 @@+-- | This library contains special decompositions of particular gates+-- into particular gate bases. It also contains functions for+-- decomposing multiple controls.+-- +-- For example, we provide particular decompositions of the Toffoli,+-- Fredkin, doubly-controlled /iX/-gate, and certain other controlled+-- Clifford gates, into the Clifford+/T/ base. In some cases, we+-- provide more than one decomposition.+-- +-- Many of these decompositions are taken or adapted from the+-- literature, for example, from:+-- +-- * M. A. Nielsen and I. L. Chuang, +-- /Quantum Computation and Quantum Information/,+-- Cambridge University Press, 2002.+-- +-- * M. Amy, D. Maslov, M. Mosca, and M. Roetteler,+-- A meet-in-the-middle algorithm for fast synthesis +-- of depth-optimal quantum circuits,+-- /IEEE Transactions on Computer-Aided Design of/ +-- /Integrated Circuits and Systems/ 32(6):818-830.+-- Also available from <http://arxiv.org/abs/1206.0758>.+-- +-- * A. Barenco, C. H. Bennett, R. Cleve, D. P. DiVincenzo,+-- N. Margolus, P. Shor, T. Sleator, J. A. Smolin, and H. Weinfurter,+-- Elementary gates for quantum computation,+-- /Physical Review A/ 52(5):3457-3467, 1995. +-- Also available from <http://arxiv.org/abs/quantph/9503016>.+-- +-- * P. Selinger, Quantum circuits of T-depth one,+-- /Physical Review A/ 87, 042302 (4 pages), 2013.+-- Also available from <http://arxiv.org/abs/1210.0974>.+-- +-- * B. Giles and P. Selinger, Exact synthesis of multiqubit+-- Clifford+T circuits, /Physical Review A/ 87, 032332 (7 pages),+-- 2013. Also available from <http://arxiv.org/abs/1212.0506>.++module Quipper.Libraries.GateDecompositions where++import Quipper++import Control.Monad++-- ----------------------------------------------------------------------+-- * Decomposition of gates++-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- from Nielsen and Chuang (Figure 4.9). The first argument is the+-- target, and the other two are the controls. The controls can be+-- positive or negative.+-- +-- \[image toffoli_NC.png]+toffoli_NC_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_NC_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_NC" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ hadamard_at q+ qnot_at q `controlled` w1+ reverse_imp_if (not x) gate_T_inv_at q+ qnot_at q `controlled` w2+ reverse_imp_if (x `xor` y) gate_T_at q+ qnot_at q `controlled` w1+ reverse_imp_if (not y) gate_T_inv_at q+ qnot_at q `controlled` w2+ gate_T_at q+ reverse_imp_if (not x) gate_T_inv_at w1+ hadamard_at q+ qnot_at w1 `controlled` w2+ reverse_imp_if (x `xor` y) gate_T_inv_at w1+ qnot_at w1 `controlled` w2+ reverse_imp_if (not x) gate_S_at w1+ reverse_imp_if (not y) gate_T_at w2+ comment_with_label "EXIT: toffoli_NC" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- from Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 13). The first argument is the target, and the other two are the+-- controls. The controls can be positive or negative.+-- +-- \[image toffoli_AMMR.png]+toffoli_AMMR_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_AMMR_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_AMMR" (q,c1,c2) ("q","c1","c2")+ without_comments $ do+ hadamard_at q+ ccZ_AMMR_at q c1 c2+ hadamard_at q+ comment_with_label "EXIT: toffoli_AMMR" (q,c1,c2) ("q","c1","c2")++-- | Decomposition of the Toffoli gate using controlled Clifford+-- operators, from Nielsen and Chuang (Figure 4.8). The controls can be+-- positive or negative.+-- +-- \[image toffoli_V.png]+toffoli_V_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_V_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_V" (q,c1,c2) ("q","c1","c2")+ let q1 = from_signed c1+ let q2 = from_signed c2+ gate_V_at q `controlled` c1+ qnot_at q1 `controlled` c2+ gate_V_inv_at q `controlled` c1+ qnot_at q1 `controlled` c2+ gate_V_at q `controlled` c2+ comment_with_label "EXIT: toffoli_V" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- using /T/-depth 1 and four ancillas. From+-- <http://arxiv.org/abs/1210.0974> (Figure 1). The first argument is+-- the target, and the other two are the controls. The controls can be+-- positive or negative.+-- +-- \[image toffoli_S.png]+toffoli_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_S_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_S" (q,c1,c2) ("q","c1","c2")+ without_comments $ do+ hadamard_at q+ ccZ_S_at q c1 c2+ hadamard_at q+ comment_with_label "EXIT: toffoli_S" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-count 4 and /T/-depth 2. Adapted from+-- (<http://arxiv.org/abs/1210.0974>, Figure 10). The first argument+-- is the target, and the other two are the controls. The controls can+-- be positive or negative.+-- +-- \[image cc_iX.png]+cc_iX_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ hadamard_at q + qnot_at w1 `controlled` q+ qnot_at w2 `controlled` w1+ reverse_imp_if (not x) gate_T_at w1+ reverse_imp_if (x `xor` y) gate_T_inv_at w2+ qnot_at w1 `controlled` q+ qnot_at w2 `controlled` w1+ gate_T_inv_at q+ reverse_imp_if (not y) gate_T_at w2+ qnot_at w2 `controlled` q+ hadamard_at q + when (not x && not y) $ do -- need a phase correction+ gate_omega_at w1+ gate_omega_at w2+ comment_with_label "EXIT: cc_iX" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-count 4, and using the control qubits+-- only as controls. Derived from Nielsen and Chuang (Figure 4.9). The+-- controls can be positive or negative.+-- +-- \[image cc_iX_simple.png]+cc_iX_simple_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_simple_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX_simple" (q,c1,c2) ("q","c1","c2")+ hadamard_at q+ qnot_at q `controlled` c1+ gate_T_at q+ qnot_at q `controlled` c2+ gate_T_inv_at q+ qnot_at q `controlled` c1+ gate_T_at q+ qnot_at q `controlled` c2+ gate_T_inv_at q+ hadamard_at q+ comment_with_label "EXIT: cc_iX_simple" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-depth 1 and one ancilla. Adapted from+-- (<http://arxiv.org/abs/1210.0974>, Figure 9). The first argument+-- is the target, and the other two are the controls. The controls can+-- be positive or negative.+-- +-- \[image cc_iX_S.png]+cc_iX_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_S_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX_S" (q,c1,c2) ("q","c1","c2")+ let sx = get_sign c1+ sy = get_sign c2+ x = from_signed c1+ y = from_signed c2+ z = q+ hadamard_at z+ with_ancilla $ \w -> do+ qnot y `controlled` z+ qnot w `controlled` x+ qnot x `controlled` z+ qnot w `controlled` y+ reverse_imp_if (not sx) gate_T_at x+ reverse_imp_if (not sy) gate_T_at y+ gate_T_inv_at z+ reverse_imp_if (sx `xor` sy) gate_T_inv_at w+ qnot w `controlled` y+ qnot x `controlled` z+ qnot w `controlled` x+ qnot y `controlled` z+ when (not sx && not sy) $ do -- need a phase correction+ gate_omega_at x+ gate_omega_at y+ hadamard_at z+ comment_with_label "EXIT: cc_iX_S" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)+ +-- | Decomposition of the doubly-controlled /Z/-gate into the+-- Clifford+/T/ base. Adapted from Amy et+-- al. (<http://arxiv.org/abs/1206.0758v3>, Figure 13). The controls can+-- be positive or negative.+-- +-- \[image ccZ_AMMR.png]+ccZ_AMMR_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+ccZ_AMMR_at q c1 c2 = do+ comment_with_label "ENTER: ccZ_AMMR" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ gate_T_at q+ reverse_imp_if (not x) gate_T_at w1+ reverse_imp_if (not y) gate_T_at w2+ qnot_at w2 `controlled` w1+ qnot_at w1 `controlled` q+ qnot_at q `controlled` w2+ reverse_imp_if (not x) gate_T_inv_at w1+ reverse_imp_if (x `xor` y) gate_T_at q+ qnot_at w1 `controlled` w2+ reverse_imp_if (not y) gate_T_inv_at w1+ reverse_imp_if (x `xor` y) gate_T_inv_at w2+ qnot_at w1 `controlled` q+ qnot_at q `controlled` w2+ qnot_at w2 `controlled` w1+ comment_with_label "EXIT: ccZ_AMMR" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the doubly-controlled /Z/-gate into the+-- Clifford+/T/ base, using /T/-depth 1 and four ancillas. From+-- Selinger (<http://arxiv.org/abs/1210.0974>, Figure 1). The controls+-- can be positive or negative.+-- +-- \[image ccZ_S.png]+ccZ_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+ccZ_S_at q c1 c2 = do+ comment_with_label "ENTER: ccZ_S" (q,c1,c2) ("q","c1","c2")+ let sx = get_sign c1+ sy = get_sign c2+ x = from_signed c1+ y = from_signed c2+ z = q+ with_ancilla_init (0,0,0,0) $ \(xyz, xy, yz, xz) -> do+ qnot yz `controlled` y+ qnot xyz `controlled` x+ qnot xy `controlled` y+ qnot yz `controlled` z+ qnot xz `controlled` xyz+ qnot xy `controlled` x+ qnot xz `controlled` z+ qnot xyz `controlled` yz+ reverse_imp_if (not sx) gate_T_at x+ reverse_imp_if (not sy) gate_T_at y+ gate_T_at z+ reverse_imp_if (sx `xor` sy) gate_T_at xyz+ reverse_imp_if (sx `xor` sy) gate_T_inv_at xy+ reverse_imp_if (not sy) gate_T_inv_at yz+ reverse_imp_if (not sx) gate_T_inv_at xz+ qnot xyz `controlled` yz+ qnot xz `controlled` z+ qnot xy `controlled` x+ qnot xz `controlled` xyz+ qnot yz `controlled` z+ qnot xy `controlled` y+ qnot xyz `controlled` x+ qnot yz `controlled` y+ comment_with_label "EXIT: ccZ_S" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the Fredkin (controlled-Swap) gate into the+-- Clifford+/T/ base. The first two arguments are the targets, and the+-- last one the control. The controls can be positive or negative.+-- +-- \[image fredkin.png]+fredkin_at :: Qubit -> Qubit -> Signed Qubit -> Circ ()+fredkin_at q1 q2 c = do+ comment_with_label "ENTER: fredkin" (q1,q2,c) ("q1","q2","c")+ without_controls $ do+ qnot_at q2 `controlled` q1+ toffoli_AMMR_at q1 (Signed q2 True) c+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: fredkin" (q1,q2,c) ("q1","q2","c")++-- | Decomposition of a controlled /H/-gate into the Clifford+/T/+-- base. From Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 5(a)). The first argument is the target and the second one the+-- control. The control can be positive or negative.+-- +-- \[image cH_AMMR.png]+cH_AMMR_at :: Qubit -> Signed Qubit -> Circ ()+cH_AMMR_at q c = do+ comment_with_label "ENTER: cH_AMMR" (q,c) ("q","c")+ gate_S_inv_at q+ hadamard_at q+ gate_T_inv_at q+ qnot_at q `controlled` c+ gate_T_at q+ hadamard_at q+ gate_S_at q+ comment_with_label "EXIT: cH_AMMR" (q,c) ("q","c")++-- | Decomposition of a controlled /W/-gate into the Clifford+/T/+-- base. The first two arguments are the targets, and the last+-- argument is the control. The control can be positive or negative.+-- +-- \[image controlled_W.png]+controlled_W_at :: Qubit -> Qubit -> Signed Qubit -> Circ ()+controlled_W_at q1 q2 c = do+ comment_with_label "ENTER: controlled_W" (q1,q2,c) ("W1","W2","c")+ without_comments $ do+ qnot_at q2 `controlled` q1+ gate_S_inv_at q1+ hadamard_at q1+ gate_T_inv_at q1+ toffoli_AMMR_at q1 (Signed q2 True) c+ gate_T_at q1+ hadamard_at q1+ gate_S_at q1+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: controlled_W" (q1,q2,c) ("W1","W2","c")++-- | Decomposition of a /W/-gate into the Clifford+/T/ base.+-- +-- \[image gate_W_CliffordT.png]+gate_W_CliffordT_at :: Qubit -> Qubit -> Circ ()+gate_W_CliffordT_at q1 q2 = do+ comment_with_label "ENTER: gate_W_CliffordT" (q1,q2) ("W1","W2")+ without_comments $ do+ qnot_at q2 `controlled` q1+ cH_AMMR_at q1 (Signed q2 True)+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: gate_W_CliffordT" (q1,q2) ("W1","W2")++-- | Decomposition of a controlled /iX/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_iX.png]+controlled_iX_at :: Qubit -> Signed Qubit -> Circ ()+controlled_iX_at q c = do+ comment_with_label "ENTER: controlled_iX" (q,c) ("q","c")+ let x = get_sign c+ w = from_signed c+ qnot_at q `controlled` c+ reverse_imp_if (not x) gate_S_at w+ when (not x) $ do -- need a phase correction+ gate_omega_at q+ gate_omega_at w+ comment_with_label "EXIT: controlled_iX" (q,c) ("q","c")++-- | Decomposition of a controlled /S/-gate into the Clifford+/T/+-- base. From Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 5(b)). The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_S.png]+controlled_S_at :: Qubit -> Signed Qubit -> Circ ()+controlled_S_at q c = do+ comment_with_label "ENTER: controlled_S" (q,c) ("q","c")+ let x = get_sign c + w = from_signed c+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_inv_at w+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_at w+ gate_T_at q+ comment_with_label "EXIT: controlled_S" (q,c) ("q","c")++-- | Decomposition of a controlled /T/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_T.png]+controlled_T_at :: Qubit -> Signed Qubit -> Circ ()+controlled_T_at q c = do+ comment_with_label "ENTER: controlled_T" (q,c) ("q","c")+ without_comments $ do+ with_ancilla_init False $ \r -> do+ cc_iX_at r (Signed q True) c+ gate_T_at r+ reverse_generic_imp cc_iX_at r (Signed q True) c+ comment_with_label "EXIT: controlled_T" (q,c) ("q","c")++-- | Decomposition of a controlled /V/-gate into the Clifford+/T/+-- base. Adapted from Amy et al. (<http://arxiv.org/abs/1206.0758v3>,+-- Figure 5(c)). Our /V/-gate is /H//S/[sup †]/H/ as in Nielsen and+-- Chuang. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_V.png]+controlled_V_at :: Qubit -> Signed Qubit -> Circ ()+controlled_V_at q c = do+ comment_with_label "ENTER: controlled_V" (q,c) ("q","c")+ let x = get_sign c+ w = from_signed c+ hadamard_at q+ reverse_imp_if (not x) gate_T_inv_at w+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_at w+ gate_T_inv_at q+ qnot w `controlled` q+ hadamard_at q+ comment_with_label "EXIT: controlled_V" (q,c) ("q","c")++-- | Decomposition of a controlled /E/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_E.png]+controlled_E_at :: Qubit -> Signed Qubit -> Circ ()+controlled_E_at q c = do+ comment_with_label "ENTER: controlled_E" (q,c) ("q","c")+ with_signed_qubit c $ \r -> do+ gate_H_at q+ gate_S_at r+ gate_T_at q+ qnot_at q `controlled` r+ gate_T_inv_at q+ gate_H_at q+ qnot_at r `controlled` q+ gate_T_at r+ gate_T_inv_at q+ qnot_at r `controlled` q+ comment_with_label "EXIT: controlled_E" (q,c) ("q","c")++-- | Decomposition of a controlled [bold Y]-gate into the Clifford+/T/+-- base. The gate is from the Ground State Estimation algorithm and is+-- defined as [bold Y] = /SHS/, or equivalently,+-- +-- \[image Y.png]+-- +-- It should not be confused with the Pauli /Y/ gate.+-- The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_YY.png]+controlled_YY_at :: Qubit -> Signed Qubit -> Circ ()+controlled_YY_at q c = do+ comment_with_label "ENTER: controlled_YY" (q,c) ("q","c")+ gate_S_at q+ qnot_at q `controlled` c+ gate_S_inv_at q+ hadamard_at q+ gate_T_inv_at q+ qnot_at q `controlled` c+ gate_T_at q+ hadamard_at q+ comment_with_label "EXIT: controlled_YY" (q,c) ("q","c")++-- | A \"plain\" Toffoli gate, not decomposed. This is provided for+-- convenience, for example to use with 'with_combined_controls'.+toffoli_plain_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_plain_at q c1 c2 = do+ qnot_at q `controlled` (c1,c2)+ +-- | A \"plain\" doubly-controlled /iX/-gate, not decomposed. This is+-- provided for convenience, for example to use with+-- 'with_combined_controls'.+cc_iX_plain_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_plain_at q c1 c2 = do+ gate_iX_at q `controlled` (c1,c2)++-- | Decomposition of an /m/-times controlled not-gate, using /m/−2+-- ancillas that do not need be initialized in a particular+-- state. Adapted from Barenco et al.+-- (<http://arxiv.org/abs/quantph/9503016>, Lemma 7.2).+-- +-- In addition to what is shown in Barenco et al., this function+-- permits some Toffoli gates to be replaced by doubly-controlled+-- /iX/-gates. This may be beneficial in gate bases, such as+-- Clifford+/T/, where a doubly-controlled /iX/-gate has a simpler+-- representation than a Toffoli gate.+-- +-- The first argument is a Toffoli gate to use in the+-- decomposition. The second argument may be either a Toffoli gate or+-- a doubly-controlled /iX/ gate. The third argument is the target,+-- the fourth argument is a list of qubits to be used as ancillas, and+-- the fifth argument is a list of signed controls. The ancillas need+-- not be initialized, and are returned in their original state.+-- +-- The size of this circuit is linear in the number of controls; the+-- decomposition uses 4/m/−8 doubly-controlled gates for /m/ ≥ 3.+-- +-- \[image multi_cnot_barenco.png]+multi_cnot_barenco_at :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Qubit -> [Qubit] -> [Signed Qubit] -> Circ ()+multi_cnot_barenco_at my_toffoli_at my_ciX_at q as cs =+ case cs of+ [] -> do+ qnot_at q+ [c] -> do+ qnot_at q `controlled` c+ [c1,c2] -> do+ my_toffoli_at q c1 c2+ c:cs -> do+ case as of+ [] -> error "multi_cnot_barenco_at: too few ancillas"+ a:as -> do+ my_toffoli_at q (Signed a True) c+ aux cs (a:as)+ my_toffoli_at q (Signed a True) c+ reverse_generic_imp aux cs (a:as)+ where+ aux :: [Signed Qubit] -> [Qubit] -> Circ ()+ aux [] as = return ()+ aux [c] as = return ()+ aux [c1,c2] (a:as) = do+ my_ciX_at a c1 c2+ aux (c:cs) (a1:a2:as) = do+ my_ciX_at a1 (Signed a2 True) c+ aux cs (a2:as)+ my_ciX_at a1 (Signed a2 True) c+ aux _ _ = error "multi_cnot_barenco_at: too few ancillas"++-- | Decomposition of a multiply-controlled /iX/-gate, using no+-- ancillas. Adapted from Giles and Selinger+-- (<http://arxiv.org/abs/1212.0506>, Section 5.2).+-- +-- The first argument is a Toffoli gate or a doubly-controlled+-- /iX/-gate. The third argument is the target, and the fourth+-- argument is a list of signed controls.+-- +-- The size of this circuit is linear in the number of controls; the+-- decomposition uses 8/m/−32 doubly-controlled gates, 4 /T/-gates,+-- and 2 /H/-gates, for /m/ ≥ 6.+-- +-- \[image multi_ciX_noancilla.png]+multi_ciX_noancilla_at :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Qubit -> [Signed Qubit] -> Circ ()+multi_ciX_noancilla_at my_ciX_at q [] = gate_iX_at q+multi_ciX_noancilla_at my_ciX_at q [c] = gate_iX_at q `controlled` c+multi_ciX_noancilla_at my_ciX_at q [c1,c2] = gate_iX_at q `controlled` [c1,c2]+multi_ciX_noancilla_at my_ciX_at q cs = do+ hadamard_at q+ gate_T_inv_at q+ multi_cnot_barenco_at my_ciX_at my_ciX_at q as1 cs2+ gate_T_at q+ multi_cnot_barenco_at my_ciX_at my_ciX_at q as2 cs1+ gate_T_inv_at q+ reverse_generic_imp (multi_cnot_barenco_at my_ciX_at my_ciX_at) q as1 cs2+ gate_T_at q+ reverse_generic_imp (multi_cnot_barenco_at my_ciX_at my_ciX_at) q as2 cs1+ hadamard_at q+ where+ n = length cs+ (cs1, cs2) = splitAt (n `div` 2) cs+ as1 = map from_signed cs1+ as2 = map from_signed cs2+ +-- ----------------------------------------------------------------------+-- * Decomposition of controls++-- | Partition a list of controls into quantum and classical.+partition_controls :: [Signed Endpoint] -> ([Signed Qubit], [Signed Bit])+partition_controls cs = (qcs, ccs) where+ qcs = [ Signed q b | Signed (Endpoint_Qubit q) b <- cs ]+ ccs = [ Signed c b | Signed (Endpoint_Bit c) b <- cs ]++-- | Given a function that expects a qubit (typically as a control),+-- turn it into a function that can handle a /signed/ (positive or+-- negative) qubit. This is done by conjugating the circuit with+-- negations on both sides, if the sign is negative. Usage:+-- +-- > with_signed_qubit c $ \q -> do+-- > <<<code using q>>>+-- +-- \[image with_signed_qubit.png]+with_signed_qubit :: Signed Qubit -> (Qubit -> Circ b) -> Circ b+with_signed_qubit (Signed q True) f = f q+with_signed_qubit (Signed q False) f = do+ gate_X_at q+ b <- f q+ gate_X_at q+ return b++-- | Decompose quantum controls recursively until at most /n/ remain,+-- and then pass these reduced controls to the given circuit.+-- Precondition: /n/ ≥ 1. +-- +-- The decomposition is done using a Toffoli-like gate that is given+-- as the first argument. This should be either a Toffoli gate, a+-- doubly-controlled /iX/-gate, a decomposition thereof, or any other+-- reversible ternary gate with the behavior+-- +-- * |000〉 ↦ |0〉|φ[sub 0]〉+-- +-- * |001〉 ↦ |0〉|φ[sub 1]〉+-- +-- * |010〉 ↦ |0〉|φ[sub 2]〉+-- +-- * |011〉 ↦ |1〉|φ[sub 3]〉,+-- +-- where the states |φ[sub 0]〉, …, |φ[sub 3]〉 are arbitrary.+-- +-- For example, when /n/=2, this typically yields a circuit such as+-- the following (here shown using the doubly-controlled /iX/-gate):+-- +-- \[image with_combined_controls2.png]+-- +-- And for /n/=1, the circuit typically looks like this:+-- +-- \[image with_combined_controls1.png]+-- +-- Classical controls are not decomposed, but are applied to the+-- resulting circuit directly.+with_combined_controls :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls my_toffoli_at n cs code = circ where+ (qcs, ccs) = partition_controls cs+ len = length qcs+ -- m is the number of qcs to remove.+ m = if len <= n then 0 else len - n+ circ = with_controls ccs $ do+ aux m qcs code++ aux 0 qcs code = code qcs+ aux n [] code = code []+ aux n [c] code = code [c]+ aux n (c1:c2:qcs) code = do+ with_computed (quantum_and c1 c2) $ \c -> do+ aux (n-1) (qcs ++ [Signed c True]) code++ quantum_and :: Signed Qubit -> Signed Qubit -> Circ Qubit+ quantum_and c1 c2 = do+ q <- qinit 0+ my_toffoli_at q c1 c2+ return q
@@ -0,0 +1,107 @@+-- | This module implements the Quantum Fourier Transform.++module Quipper.Libraries.QFT (+ qft_little_endian,+ qft_big_endian,+ qft_rev,+ qft_int+ ) where++import Quipper+import Quipper.Libraries.Arith++import Quipper.Utils.Auxiliary (mmap)++-- ----------------------------------------------------------------------+-- * Low-level implementation++-- | Like 'qft_rev', but without the comments and labels.+-- +-- Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- The input is little-endian and the output is big-endian.+-- +-- Unlike 'qft_little_endian' and 'qft_big_endian', this function can+-- be used in imperative style, i.e., it modifies its arguments \"in+-- place\".+qft_internal :: [Qubit] -> Circ [Qubit]+qft_internal [] = return []+qft_internal [x] = do + hadamard x+ return [x]+qft_internal (x:xs) = do + xs' <- qft_internal xs+ xs'' <- rotations x xs' (length xs')+ x' <- hadamard x+ return (x':xs'')+ where+ -- Auxiliary function used by 'qft'.+ rotations :: Qubit -> [Qubit] -> Int -> Circ [Qubit]+ rotations _ [] _ = return []+ rotations c (q:qs) n = do + qs' <- rotations c qs n+ q' <- rGate ((n + 1) - length qs) q `controlled` c+ return (q':qs')++-- ----------------------------------------------------------------------+-- * Wrappers++-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- Both the input and output qubit lists are little-endian, i.e., the+-- leftmost qubit (head of the list) is the least significant one.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_little_endian :: [Qubit] -> Circ [Qubit]+qft_little_endian qs = do+ comment_with_label "ENTER: qft_little_endian" qs "qs"+ qs' <- qft_internal qs+ let qs = reverse qs'+ comment_with_label "EXIT: qft_little_endian" qs "qs"+ return qs+ +-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- Both the input and output qubit lists are big-endian, i.e., the+-- leftmost qubit (head of the list) is the most significant one.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_big_endian :: [Qubit] -> Circ [Qubit] +qft_big_endian qs = do+ comment_with_label "ENTER: qft_big_endian" qs "qs"+ qs <- qft_internal (reverse qs)+ comment_with_label "EXIT: qft_big_endian" qs "qs"+ return qs++-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- The input is little-endian and the output is big-endian.+-- +-- Unlike 'qft_little_endian' and 'qft_big_endian', this function can+-- be used in imperative style, i.e., it modifies its arguments+-- \"in place\".+qft_rev :: [Qubit] -> Circ [Qubit]+qft_rev qs = do+ comment_with_label "ENTER: qft_rev" qs "qs"+ qs <- qft_internal qs+ comment_with_label "EXIT: qft_rev" qs "qs"+ return qs++-- | Apply the Quantum Fourier Transform to a 'QDInt'.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_int :: QDInt -> Circ QDInt+qft_int x = do+ comment_with_label "ENTER: qft_int" x "x"+ x <- mmap qdint_of_qulist_bh $ qft_big_endian $ qulist_of_qdint_bh x+ comment_with_label "ENTER: qft_int" x "x"+ return x++-- ----------------------------------------------------------------------+-- * Testing++-- | A simple test.+test :: Int -> IO ()+test n = print_generic Preview qft_little_endian (replicate n qubit)
@@ -0,0 +1,48 @@+-- | This module provides an implementation of the addition circuit found in +-- Thomas G. Draper's paper \"Addition on a Quantum Computer\".+module Quipper.Libraries.QFTAdd +(+ qft_add_in_place+)+where++import Quipper+import Quipper.Libraries.Arith -- we make use of the QDInt data type for quantum integers+import Quipper.Libraries.QFT -- we make use of the big endian QFT++-- | Add one 'QDInt' onto a second, in place; i.e. (/x/,/y/) ↦ (/x/,/x/+/y/). +-- Arguments are assumed to be of equal size.+-- This implementation follows the implementation in Thomas G. Draper's +-- paper \"Addition on a Quantum Computer\" which doesn't require the use of any+-- ancilla qubits through a clever use of the Quantum Fourier Transform.+qft_add_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+qft_add_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y') <- qft_add_in_place_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x, y)++-- | Low-level implementation of 'qft_add_in_place': represents integers+-- as big-headian qubit lists.+qft_add_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit])+qft_add_in_place_qulist a b = do+ label (a,b) ("a","b")+ with_computed (box "QFT" qft_big_endian b) $ \b' -> do+ qft_adder a (reverse b')+ label (a,b) ("a","b")+ return (a,b)++-- | The circuit that performs the addition after a QFT+qft_adder :: [Qubit] -> [Qubit] -> Circ ()+qft_adder _ [] = return ()+qft_adder as (b:bs) = do+ qft_adder' as b 1+ qft_adder (tail as) bs+ where+ qft_adder' :: [Qubit] -> Qubit -> Int -> Circ [Qubit]+ qft_adder' [] _ _ = return []+ qft_adder' (a:as) b n = do+ b <- rGate n b `controlled` a+ qft_adder' as b (n+1)
@@ -0,0 +1,65 @@+-- | A custom qRAM algorithm for fetching and storing quantum data+-- from a quantum array, addressed by a quantum integer.++module Quipper.Libraries.Qram (+ indexed_access,+ indexed_fetch_at,+ indexed_store_at,+ indexed_swap_at,+ ) where++import Quipper+import Quipper.Libraries.Arith++-- | Inputs a list /a/ of quantum data and a quantum integer /i/, and+-- returns the /i/th element of /a/. This is done with controlled swap+-- operations, but without ancillas, i.e., the output is the only copy+-- of that quantum data. Note that the remaining elements of the array+-- may be swapped around, so they are not useable until+-- 'indexed_access' has been reversed.+-- +-- Suggested usage:+-- +-- > with_computed (indexed_access i a) $ \x -> do+-- > <<<operate on x>>>+-- +-- If the index is out of bound, return an unpredictable element of+-- /a/. If /a/ is of length 0, raise an error.+indexed_access :: (QData qa) => [qa] -> QDInt -> Circ qa+indexed_access as i = indexed_access_qulist as (qulist_of_qdint_bh i)++-- | Auxiliary function: like 'indexed_access', but uses a qubit list+-- instead of a quantum integer. The list is big-headian, i.e., the+-- head of the list holds the most significant bit.+indexed_access_qulist :: (QData qa) => [qa] -> [Qubit] -> Circ qa+indexed_access_qulist [] i = error "indexed_access: cannot address length-0 register"+indexed_access_qulist (a:as) [] = return a+indexed_access_qulist as (i:is) = do+ let n = 2 ^ length is+ r = max 0 $ min n (length as - n)+ -- r: number of wires that need swapping if head qdigit is set+ for (r-1) 0 (-1) $ \j -> do+ swap_at (as !! j) (as !! (j+n)) `controlled` i+ a <- indexed_access_qulist as is+ return a++-- | @'indexed_fetch_at' /a/ /i/ /q/@: +-- Perform /q/ ⊕= /a/[/i/].+indexed_fetch_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_fetch_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ controlled_not_at q x++-- | @'indexed_store_at' /a/ /i/ /q/@:+-- Perform /a/[/i/] ⊕= /q/.+indexed_store_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_store_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ controlled_not_at x q++-- | @'indexed_swap_at' /a/ /i/ /q/@:+-- Swap /a/[/i/] and /q/.+indexed_swap_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_swap_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ swap_at x q
@@ -0,0 +1,126 @@+-- | This module introduces a simple way of defining boolean+-- statements acting over qubits.+module Quipper.Libraries.QuantumIf where++import Quipper++-- ----------------------------------------------------------------------+-- * Quantum \"if then else\" statements++-- $ This module introduces a simple way of defining boolean+-- statements acting over qubits, which can then be used as the+-- control in a quantum \"if then else\" statement. The idea is that+-- an ancilla is initialized that is in the state represented by the+-- boolean statement, and is then used to control the two branches of+-- the \"if then else\", before being uncomputed. The boolean+-- statements can contain \"and\", \"or\", and \"not\".++-- | We can use @(Boolean Qubit)@ to build up \"boolean statements\"+-- over qubits and use the \"boolean statement\" in an 'if_then_elseQ'+-- construct.+--+-- > Example (for qubits a, b, c, d):+-- > (a and b) or !(c and !d) is written as:+-- > (a `And` b) `Or` Not (c `And` Not d)+data Boolean a = A a -- ^ 'A' /q/ means if /q/ == 'True'. + | Not (Boolean a) -- ^ 'Not' /b/ means the negation of the boolean statement /b/.+ | And (Boolean a) (Boolean a) -- ^ 'And' /a/ /b/ means the and of the boolean statements /a/ and /b/.+ | Or (Boolean a) (Boolean a) -- ^ 'Or' /a/ /b/ means the or of the boolean statements /a/ and /b/+ deriving Show++-- Set the precedence for infix operators 'And' and 'Or'.+infixr 3 `And` -- same precedence as (&&)+infixr 2 `Or` -- same precedence as (||)++-- | Allow 'And' and 'Or' to be used as infix operators, with the same+-- precedences.++-- | Internally, a \"boolean statement\" is converted into a statement+-- that doesn't use /or/ (e.g., using De Morgan's laws).+data BooleanAnd a = AA a -- ^ 'AA' /q/ means if /q/ == 'True'.+ | NotA (BooleanAnd a) -- ^ 'NotA' /b/ means the negation of the boolean statement /b/.+ | AndA (BooleanAnd a) (BooleanAnd a) -- ^ 'AndA' /a/ /b/ means the and of the boolean statements /a/ and /b/.++-- | Convert any boolean formula to a formula using just /and/ and /not/. This conversion function uses De Morgan's law,+-- i.e., +-- +-- > A or B = !( !A and !B ),+-- +-- but does not remove double negations. For a version that also+-- removes double negations, see 'booleanToAnd'.+booleanToAnd' :: Boolean a -> BooleanAnd a+booleanToAnd' (A a) = AA a+booleanToAnd' (Not ba) = NotA (booleanToAnd' ba)+booleanToAnd' (And ba ba') = AndA (booleanToAnd' ba) (booleanToAnd' ba')+booleanToAnd' (Or ba ba') = NotA (AndA (NotA (booleanToAnd' ba)) (NotA (booleanToAnd' ba')))++-- | Strip any redundant double negations,+-- i.e., in this context @!!A = A@.+stripDoubleNot :: BooleanAnd a -> BooleanAnd a+stripDoubleNot (AA a) = AA a+stripDoubleNot (NotA (NotA ba)) = stripDoubleNot ba+stripDoubleNot (NotA ba) = NotA (stripDoubleNot ba)+stripDoubleNot (AndA ba ba') = AndA (stripDoubleNot ba) (stripDoubleNot ba')++-- | Convert any boolean formula to a formula using just /and/ and+-- /not/, removing double negations.+booleanToAnd :: Boolean a -> BooleanAnd a+booleanToAnd ba = stripDoubleNot (booleanToAnd' ba)++-- | Create a circuit from the \"boolean statement\".+booleanAnd' :: BooleanAnd Qubit -> Qubit -> Circ ()+booleanAnd' (AA q') q = do+ qnot_at q `controlled` q'+ return ()+booleanAnd' (NotA ba) q = do+ anc <- qinit False+ qnot_at anc+ booleanAnd' ba anc+ qnot_at q `controlled` anc+booleanAnd' (AndA ba ba') q = do+ anc0 <- qinit False+ booleanAnd' ba anc0+ anc1 <- qinit False+ booleanAnd' ba' anc1+ qnot_at q `controlled` [anc0,anc1]++-- | Create a circuit from the \"boolean statement\", passing in an ancilla.+booleanAnd :: BooleanAnd Qubit -> Circ Qubit+booleanAnd baq = do+ anc <- qinit False+ booleanAnd' baq anc+ return anc ++-- | The definition of a quantum if_then_else structure+-- uses a \"boolean statement\" to create a single ancilla in the state defined by+-- the boolean statement, and uses this as a control for the two branches of the+-- if statement. The ancilla then needs to be uncomputed, this is achieved using+-- the other given \"boolean statement\", i.e., a new boolean statement that would+-- produce the state of the control ancilla, from the output state of the two+-- branches.This allows the branches to update the state of qubits used in the +-- original \"boolean statement\" as long as it is done in a +-- (reversible) known-manner.+-- This is useful for the WALK algorithm, where TOPARENT and TOCHILD are controlled+-- by the state of the direction register, but also change the state of the+-- direction register. +if_then_elseQinv :: Boolean Qubit -> Circ () -> Circ () -> Boolean Qubit -> Circ ()+if_then_elseQinv b_in i e b_out = do+ with_ancilla $ \if_control -> do+ with_computed (booleanAnd (booleanToAnd b_in)) $ \anc -> do+ qnot_at if_control `controlled` anc+ with_controls if_control $ do+ i+ with_controls (if_control .==. False) $ do+ e+ with_computed (booleanAnd (booleanToAnd b_out)) $ \anc -> do+ qnot_at if_control `controlled` anc ++-- | Like 'if_then_elseQinv', but where the original \"boolean statement\" still+-- holds after the branches have taken place.+if_then_elseQ :: Boolean Qubit -> Circ () -> Circ () -> Circ ()+if_then_elseQ b i e = do+ with_computed (booleanAnd (booleanToAnd b)) $ \if_control -> do+ with_controls if_control $ do+ i+ with_controls (if_control .==. False) $ do+ e
@@ -0,0 +1,57 @@+-- ----------------------------------------------------------------------+-- | This module is the main interface to the QuipperASCIIParser+-- library. It provides functions for parsing circuits in the ASCII+-- format written by 'print_generic' and similar functions.++module Quipper.Libraries.QuipperASCIIParser where++import qualified Quipper.Libraries.QuipperASCIIParser.CircInfo as CI+import qualified Quipper.Libraries.QuipperASCIIParser.ASCIICirc as AC++import Quipper+import Quipper.Internal.Monad++import Quipper.Utils.PortableSignals+import System.IO+import Data.List++-- | Parse a string containing a circuit in the format output by+-- Quipper's ASCII format. Return a circuit producing function of the+-- parsed circuit, along with a specimen \"shape\" for the input of+-- the parsed circuit.+parse_circuit :: String -> ([Endpoint],[Endpoint] -> Circ [Endpoint])+parse_circuit all_lines = AC.run mins gates subs circ_info+ where+ split_lines = lines' all_lines+ (mins, ci) = CI.run_ascii_lines split_lines+ (gates,subs,circ_info) = CI.run ci+++-- | Like 'lines', except that the last line is omitted if it doesn't+-- end with a newline character+lines' :: String -> [String]+lines' [] = []+lines' s = case elemIndex '\n' s of+ Nothing -> []+ Just n -> (take (n) s):lines' (drop (n+1) s)++-- | Like 'parse_circuit', but read the circuit from the standard+-- input stream, rather than from a string. This can be used to build+-- stand-alone tools that process circuits in a pipeline. +parse_from_stdin :: IO ([Endpoint], [Endpoint] -> Circ [Endpoint])+parse_from_stdin = do+ all_lines <- hGetContents stdin + return $ parse_circuit all_lines++-- | Like 'parse_from_stdin', but as a special convenience, this +-- function also installs a signal handler that will intercept the first +-- kill signal (e.g., Ctrl-C) and close the standard input stream. +-- This means that whichever part of the circuit was generated before the +-- first Ctrl-C can still be processed as a partial circuit. Note that the +-- second kill signal will still kill the program. Note that this is only+-- defined for Non-Windows OS environments.+parse_from_stdin_with_handler :: IO ([Endpoint], [Endpoint] -> Circ [Endpoint])+parse_from_stdin_with_handler = do+ installHandler Interrupt (CatchOnce (hClose stdin))+ all_lines <- hGetContents stdin + return $ parse_circuit all_lines
@@ -0,0 +1,334 @@+-- ----------------------------------------------------------------------+-- | This module uses a state transformer monad to rebuild a circuit from+-- the CircInfoState representation. This can only be as lazy as the Quipper+-- ASCII output allows, as subroutine definitions need to be known before+-- a subroutine can be called.++module Quipper.Libraries.QuipperASCIIParser.ASCIICirc where++import Quipper.Libraries.QuipperASCIIParser.CircInfo hiding (run,do_gate)++import Quipper+import Quipper.Internal.Circuit+import Quipper.Internal.Monad+import Quipper.Internal.Generic++import Data.List+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import Data.Map (Map)+import qualified Data.Map as Map++import Control.Monad.State++-- | In the Quipper ASCII output, wires are identified by integers. +-- We have to map these to Quipper's native wires and types. +data WireState = WireState {+ wire_map :: IntMap (Wire,Wiretype),+ in_sub :: Bool, + subroutines_in_scope :: Map BoxId Sub+ }++-- | An initial, empty WireState, with the given subroutines_in_scope+empty_wirestate :: Map BoxId Sub -> WireState+empty_wirestate subs = WireState {wire_map = IntMap.empty, in_sub = False,+ subroutines_in_scope = subs}++-- | The 'ASCIICirc' monad is like the 'Circ' monad, except that it also+-- keeps track of an additional 'WireState'. The 'lift' function must+-- be used to lift any command for the 'Circ' monad to the 'ASCIICirc'+-- monad. +type ASCIICirc a = StateT WireState Circ a++-- | The 'in_sub' flag can be set to True, as slightly different behavior is+-- required when evaluating a subroutine.+set_in_sub :: ASCIICirc ()+set_in_sub = do+ state <- get+ put (state {in_sub = True})++-- | Look up the qubit corresponding to a ASCII integer representation of a+-- qubit. If it doesn't already exist then initialize a new qubit.+provide_qubit :: Int -> ASCIICirc Qubit+provide_qubit r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,Qbit) -> return (qubit_of_wire w)+ Just (w,Cbit) -> error ("Quantum wire " ++ show r ++ " bound as classical " ++ show w)+ Nothing -> error ("Quantum wire " ++ show r ++ " not in scope")+ True -> return (qubit_of_wire r)++-- | Look up the bit corresponding to a ASCII integer representation of a+-- bit. If it doesn't already exist then initialize a new bit.+provide_bit :: Int -> ASCIICirc Bit+provide_bit r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,Cbit) -> return (bit_of_wire w)+ Just (w,Qbit) -> error ("Classical wire " ++ show r ++ " bound as quantum " ++ show w)+ Nothing -> error ("Classical wire " ++ show r ++ " not in scope")+ True -> return (bit_of_wire r)++-- | Look up the wire corresponding to a ASCII integer representation of a+-- bit or qubit. If it doesn't already exist then initialize a new qubit.+provide_wire :: Int -> ASCIICirc Wire+provide_wire r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,_) -> return w+ Nothing -> do+ q <- provide_qubit r+ return (wire_of_qubit q)+ True -> return r ++-- | Add a new qubit to the state.+add_qubit :: Int -> Qubit -> ASCIICirc ()+add_qubit w q = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.insert w (wire_of_qubit q,Qbit) ws+ put (state {wire_map = ws'}) ++-- | Add a new bit to the state.+add_bit :: Int -> Bit -> ASCIICirc ()+add_bit w b = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.insert w (wire_of_bit b,Cbit) ws+ put (state {wire_map = ws'})++-- | Remove a wire from the state+remove_wire :: Int -> ASCIICirc ()+remove_wire w = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.delete w ws+ put (state {wire_map = ws'}) ++-- | A helper function for providing the qubits within a control structure+provide_control :: Signed Int -> ASCIICirc (Signed Wire)+provide_control (Signed r val) = do+ w <- provide_wire r+ return (Signed w val)++-- | provides quantum wires for the controls in a control list+provide_controls :: [Signed Int] -> ASCIICirc [Signed Wire]+provide_controls = mapM provide_control++-- | Lift a Quipper circuit, preventing the addition of controls depending+-- on the given boolean.+lift_ncf :: Bool -> Circ a -> ASCIICirc a+lift_ncf False ca = lift ca+lift_ncf True ca = lift $ without_controls ca++-- | Take a Gate and execute it in the 'ASCIICirc' monad.+do_gate :: Gate -> ASCIICirc ()+do_gate (QGate name iflg ws1 ws2 wbs ncf) = do+ qs1 <- mapM provide_qubit ws1+ qs2 <- mapM provide_qubit ws2+ cs <- provide_controls wbs+ lift_ncf ncf $ named_gate_qulist name iflg qs1 qs2 `controlled` cs+ return () +do_gate (QRot name iflg theta ws1 ws2 wbs ncf) = do+ qs1 <- mapM provide_qubit ws1+ qs2 <- mapM provide_qubit ws2+ cs <- provide_controls wbs + lift_ncf ncf $ named_rotation_qulist name iflg theta qs1 qs2 `controlled` cs+ return ()+do_gate (GPhase ts ws wbs ncf) = do+ qs <- mapM provide_qubit ws+ cs <- provide_controls wbs + lift_ncf ncf $ global_phase_anchored_list ts (map (\w -> endpoint_of_wire w Qbit) (map wire_of_qubit qs)) `controlled` cs+ return ()+do_gate (CNot w wbs ncf) = do+ b <- provide_bit w+ cs <- provide_controls wbs+ lift_ncf ncf $ cnot_at b `controlled` cs +do_gate (CGate name w ws ncf) = do+ bs <- mapM provide_bit ws + b <- lift_ncf ncf $ cgate name bs+ add_bit w b+do_gate (CGateInv name w ws ncf) = do+ b <- provide_bit w+ bs <- mapM provide_bit ws + lift_ncf ncf $ cgateinv name b bs +do_gate (CSwap w1 w2 wbs ncf) = do+ b1 <- provide_bit w1+ b2 <- provide_bit w2+ cs <- provide_controls wbs+ lift_ncf ncf $ swap b1 b2 `controlled` cs+ return ()+do_gate (QPrep w ncf) = do+ b <- provide_bit w+ q <- lift_ncf ncf $ prepare_qubit b+ add_qubit w q+do_gate (QUnprep w ncf) = do+ q <- provide_qubit w+ b <- lift_ncf ncf $ unprepare_qubit q+ add_bit w b+do_gate (QInit val w ncf) = do+ q <- lift_ncf ncf $ qinit val+ add_qubit w q+do_gate (CInit val w ncf) = do+ b <- lift_ncf ncf $ cinit val+ add_bit w b +do_gate (QTerm val w ncf) = do+ q <- provide_qubit w+ lift_ncf ncf $ qterm val q+ remove_wire w +do_gate (CTerm val w ncf) = do+ b <- provide_bit w+ lift_ncf ncf $ cterm val b+ remove_wire w +do_gate (QMeas w) = do+ q <- provide_qubit w+ b <- lift $ measure q+ add_bit w b+do_gate (QDiscard w) = do+ q <- provide_qubit w+ lift $ qdiscard q+ remove_wire w +do_gate (CDiscard w) = do+ b <- provide_bit w+ lift $ cdiscard b+ remove_wire w +do_gate (Subroutine boxid iflg ws_in _ ws_out _ wbs ncf _ (RepeatFlag rf)) = do+ -- Note that we ignore the given arities, as they are dummy values+ -- the true values are kept in the ASCIICirc state.+ -- The controllable flag is also ignored, as it is recalculated.+ state <- get+ let subs = subroutines_in_scope state+ case (Map.lookup boxid subs) of+ Nothing -> error ("Subroutine " ++ show boxid ++ " has not been defined in: " ++ show (Map.keys subs))+ Just sub -> do+ let ins = inputs sub+ let (_,in_as) = unzip ins+ let in_wa = zip ws_in in_as+ es_in <- mapM (\(w,wt) ->+ case wt of+ Qbit -> do+ q <- provide_qubit w+ let wire = endpoint_of_wire (wire_of_qubit q) Qbit+ return wire+ Cbit -> do+ b <- provide_bit w+ let wire = endpoint_of_wire (wire_of_bit b) Cbit+ return wire+ ) in_wa+ cs <- provide_controls wbs+ let outs = outputs sub+ let es_out = map (\(w,wt) -> endpoint_of_wire w wt) outs + let gates = reverse (circuit sub)+ let ascii_circ = \_ -> set_in_sub >> (mapM_ do_gate gates) >> return es_out + let circ = run_asciicirc subs ascii_circ+ let boxid = BoxId (name sub) (shape sub)+ let error_message = \e -> "ASCIICirc.do_gate : " ++ e+ let f = \x -> do+ -- Provide subroutine generic only uses circ if it isn't already+ -- in the namespace.+ provide_subroutine_generic error_message boxid False circ x+ call_subroutine boxid (RepeatFlag rf) x+ es_out' <- case iflg of+ False -> lift_ncf ncf $ f es_in `controlled` cs+ True -> do+ let (_,out_as) = unzip outs+ let out_wa = zip ws_in out_as+ es_out <- mapM (\(w,wt) ->+ case wt of+ Qbit -> do+ q <- provide_qubit w+ let wire = endpoint_of_wire (wire_of_qubit q) Qbit+ return wire+ Cbit -> do+ b <- provide_bit w+ let wire = endpoint_of_wire (wire_of_bit b) Cbit+ return wire+ ) out_wa + lift_ncf ncf $ (reverse_generic f es_in) es_out `controlled` cs+ -- a subroutine can add/remove wires from scope, so we remove all the input+ -- wires from the state, and add all the output wires to the state.+ -- The state may have changed, so we get it again.+ state <- get+ let wires = wire_map state+ let wires' = foldr (\w ws -> IntMap.delete w ws) wires (if iflg then ws_out else ws_in)+ let wires'' = foldr (\(w,e) ws -> IntMap.insert w + (case e of+ Endpoint_Qubit q -> (wire_of_qubit q, Qbit)+ Endpoint_Bit b -> (wire_of_bit b, Cbit)) ws) wires' + (zip (if iflg then ws_in else ws_out) es_out')+ put (state {wire_map = if (in_sub state) then wires else wires''})+do_gate (Comment c iflg wlabels) = do+ qlabels <- mapM (\(w,l) ->+ do+ pw <- provide_wire w+ return (pw,l) ) wlabels+ lift $ comment_label c iflg qlabels+do_gate (DTerm val w) = do+ b <- provide_bit w+ lift $ dterm_bit val b+ remove_wire w ++-- | Allocate an input endpoint, to an endpoint in the ASCIICirc, by adding it to+-- the map of wires in scope.+allocate_input :: (Endpoint,Endpoint) -> ASCIICirc ()+allocate_input (i,e@(Endpoint_Qubit _)) = add_qubit (wire_of_endpoint e) (qubit_of_wire (wire_of_endpoint i)) +allocate_input (i,e@(Endpoint_Bit _)) = add_bit (wire_of_endpoint e) (bit_of_wire (wire_of_endpoint i)) ++-- ----------------------------------------------------------------------+-- * Unpacking ASCIICirc++-- | Execute a parsed circuit, i.e. a CircInfoState, in the ASCIICirc monad+run_gates :: [Gate] -> Maybe [(Wire,Wiretype)] -> [Endpoint] -> [Endpoint] -> ASCIICirc [Endpoint]+run_gates gates d_outs es ins = do+ mapM_ (\i -> allocate_input i) (zip ins es)+ mapM_ (\g -> do_gate g) gates+ state <- get+ let ws_in_scope = IntMap.elems $ wire_map state+ let es_in_scope = map (\(w,wt) -> endpoint_of_wire w wt) ws_in_scope+ case d_outs of+ Nothing -> return es_in_scope+ Just out_ws -> do+ es_out <- mapM (\(w,wt) -> do+ w' <- provide_wire w+ return (endpoint_of_wire w' wt)) out_ws+ let es_in_scope' = sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) es_in_scope+ let es_out' = sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) es_out+ case es_in_scope' == es_out' of+ True -> return es_out'+ False -> return es_in_scope'++-- | Run function for the 'ASCIICirc' monad: execute the actions and+-- produce a circuit.+run_asciicirc :: Map BoxId Sub -> (a -> ASCIICirc b) -> a -> Circ b+run_asciicirc subs f es = evalStateT (f es) (empty_wirestate subs)++-- | A CircInfoState can be turned into a Circ producing function, and the required+-- input "shape".+run :: Maybe [(Wire,Wiretype)] -> [Gate] -> Map BoxId Sub -> CircInfoState -> ([Endpoint],[Endpoint] -> Circ [Endpoint])+run mins gates subs cis = (ins,circ)+ where+ d_ins = defined_inputs cis+ d_outs = defined_outputs cis+ u_ins = map set_qubit (undefined_inputs cis)+ d_es = map (\(w,wt) -> endpoint_of_wire w wt) d_ins+ u_es = map (\(w,wt) -> endpoint_of_wire w wt) u_ins+ ins = case mins of + Nothing -> (d_es ++ sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) u_es)+ Just wts -> map (\(w,wt) -> endpoint_of_wire w wt) wts+ asciicirc = run_gates gates d_outs ins+ circ = run_asciicirc subs asciicirc++-- | If the type of an undefined_input wire is unknown, then set it to+-- be a qubit.+set_qubit :: (Wire,Maybe Wiretype) -> (Wire,Wiretype)+set_qubit (w,Just wt) = (w,wt)+set_qubit (w,Nothing) = (w,Qbit)
@@ -0,0 +1,236 @@+-- ----------------------------------------------------------------------+-- | This module uses a state monad to track certain circuit information+-- that is collected during parsing. This information contains+-- the inputs and outputs of a circuit, as well as an entry for each subroutine+-- that is defined within a circuit, and all the gates that make up the circuit.++module Quipper.Libraries.QuipperASCIIParser.CircInfo where++import Quipper.Libraries.QuipperASCIIParser.Parse (GatePlus (..),parse_ascii_line)++import Quipper+import Quipper.Internal.Circuit+import Quipper.Internal.Monad++import Control.Monad.State++import Data.Map (Map)+import qualified Data.Map as Map++-- ----------------------------------------------------------------------+-- * Data-types for the State++-- | Information collected about the current subroutine+data Sub = Sub {+ name :: String,+ shape :: String,+ controllable :: ControllableFlag,+ inputs :: [(Wire,Wiretype)],+ outputs :: [(Wire,Wiretype)],+ circuit :: [Gate]+ }++-- | An initial subroutine, with only a name+new_subroutine :: String -> Sub+new_subroutine n = Sub {name = n, shape = "", controllable = NoCtl, inputs = [], outputs = [], circuit = []}++-- | A 'CircInfoState' is a record containing a list of input wires, along with their+-- types, and a list of output wires, along with their types. We also keep track of +-- whether we're in a subroutine definition, and all the subroutines that have been +-- defined.+data CircInfoState = CircInfoState {+ used_wires :: Map Wire (Maybe Wiretype),+ defined_inputs :: [(Wire,Wiretype)],+ undefined_inputs :: [(Wire,Maybe Wiretype)],+ defined_outputs :: Maybe [(Wire,Wiretype)],+ current_subroutine :: [Sub]+ }++-- | An initial, empty CircInfoState+empty_circinfostate :: CircInfoState+empty_circinfostate = CircInfoState { used_wires = Map.empty, defined_inputs = [], + undefined_inputs = [], defined_outputs = Nothing, current_subroutine = [] }++-- | The 'CircInfo' Monad is used to track a 'CircInfoState' during parsing.+type CircInfo a = State CircInfoState a++-- | The CircInfo Monad tracks wires that are inputs, these can only be given in+-- a "Input" line in the parsed ASCII, so we assume that duplicate wires don't+-- occur, and we add input wires to the state without checking.+add_wire_inputs :: [(Wire,Wiretype)] -> CircInfo ()+add_wire_inputs ws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ let ins = defined_inputs state+ let wires = used_wires state+ put (state {defined_inputs = ws ++ ins, + used_wires = Map.fromList (map (\(w,wt) -> (w,Just wt)) ws)})+ (sub:rest) -> do + let ins = inputs sub+ let sub' = sub {inputs = ins ++ ws}+ put (state {current_subroutine = (sub':rest)})++-- | The CircInfo Monad tracks wires that are outputs, these can only be given in+-- a "Output" line in the parsed ASCII, so we assume that duplicate wires don't+-- occur, and we add output wires to the state without checking.+add_wire_outputs :: [(Wire,Wiretype)] -> CircInfo ()+add_wire_outputs ws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ case defined_outputs state of+ Nothing -> put (state {defined_outputs = Just ws})+ Just outs -> put (state {defined_outputs = Just (outs ++ ws)})+ (sub:rest) -> do + let outs = outputs sub+ let sub' = sub {outputs = outs ++ ws}+ put (state {current_subroutine = (sub':rest)})++-- | Given a a wire, check whether it is already in scope.+check_input :: Map Wire (Maybe Wiretype) -> (Wire,Maybe Wiretype) -> Bool+check_input wires (w,wt) = case Map.lookup w wires of+ Just wt -> False+ Nothing -> True++-- | Given a list of wires that are inputs to a gate, check whether they+-- are already in scope. The returned wires are not in scope, when used by a gate,+-- and must be declared as undefined inputs.+check_inputs :: [(Wire,Maybe Wiretype)] -> Map Wire (Maybe Wiretype) -> [(Wire,Maybe Wiretype)]+check_inputs ins wires = filter (check_input wires) ins++-- | The CircInfo Monad keeps track of all the gates that have been parsed+-- and adds them to the relevant part of the state.+add_gate :: Gate -> [(Wire,Wiretype)] -> CircInfo CircInfoOut+add_gate gate ctws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ let wires = used_wires state+ let ui = undefined_inputs state+ let (ws_in,ws_out) = gate_arity gate+ let ws = wirelist_of_gate gate+ let ws_unchecked = filter (\w -> (notElem w (map fst ws_in)) + && (notElem w (map fst ws_out))+ && (notElem w (map fst ctws))) ws + let ws_in' = (map (\(w,wt) -> (w, Just wt)) (ws_in ++ ctws))+ ++ (zip ws_unchecked (repeat Nothing))+ let ui' = ui ++ check_inputs ws_in' wires+ let wires' = Map.union wires (Map.fromList (ws_in' + ++ (map (\(w,wt) -> (w, Just wt)) ws_out)))+ put (state {used_wires = wires',undefined_inputs = ui'})+ return (Lazy gate)+ (sub:rest) -> do+ let gates = circuit sub+ let gates' = gate:gates+ let sub' = sub {circuit = gates'}+ put (state {current_subroutine = (sub':rest)})+ return Empty++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. The entrance to the subroutine contains its name.+enter_subroutine :: String -> CircInfo ()+enter_subroutine name = do+ state <- get+ let ms = current_subroutine state+ put (state {current_subroutine = ((new_subroutine name):ms)})++-- | We can add the shape to the current subroutine+add_subroutine_shape :: String -> CircInfo ()+add_subroutine_shape s = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> error "Shape given outside of Subroutine Definition"+ (sub:rest) -> put (state {current_subroutine = ((sub {shape = s}):rest)})++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. The subroutine might be controllable.+set_controllable :: ControllableFlag -> CircInfo ()+set_controllable val = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> error "Controllable not in Subroutine Definition" + (sub:rest) -> put (state {current_subroutine = ((sub {controllable = val}):rest)})++-- | A datatype to represent the various outputs a CircInfo computation+-- may require.+data CircInfoOut = Empty | Lazy Gate | SubDef (BoxId,Sub)++-- | This function returns True if the given input defines a Gate+isGate :: CircInfoOut -> Bool+isGate (Lazy _) = True+isGate _ = False++-- | This function returns True if the given inputs defines a SubDef+isSub :: CircInfoOut -> Bool+isSub (SubDef _) = True+isSub _ = False++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. At the end of the subroutine, it stores the subroutine+-- for later use.+exit_subroutine :: CircInfo CircInfoOut+exit_subroutine = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> return Empty+ (sub:rest) -> do+ let n = name sub+ let s = shape sub+ put (state {current_subroutine = rest})+ return (SubDef ((BoxId n s),sub))++-- | Take a GatePlus and execute it in the 'CircInfo' monad.+-- Again, the executed computation may depend upon whether we're in a subroutine+-- definition.+do_gate :: GatePlus -> CircInfo CircInfoOut+do_gate (G gate wts) = add_gate gate wts+do_gate (I ws) = add_wire_inputs ws >> return Empty+do_gate (O ws) = do+ add_wire_outputs ws+ exit_subroutine+do_gate EmptyLine = return Empty+do_gate (CommentLine comment) = return Empty+do_gate (SubroutineName name) = enter_subroutine name >> return Empty+do_gate (SubroutineShape shape) = add_subroutine_shape shape >> return Empty+do_gate (Controllable b) = set_controllable b >> return Empty++-- | Monad version of 'parse_ascii_line': parse a string and execute the+-- resulting gate directly in the 'CircInfo' monad.+run_ascii_line :: String -> CircInfo CircInfoOut+run_ascii_line s = case parse_ascii_line s of+ Nothing -> error ("unrecognized line: " ++ show s)+ Just p -> do_gate p++-- | Parse a stream consisting of many lines of ASCII output and execute+-- the parsed gates in the 'CircInfo' monad, checking to see if the first+-- line defines the inputs to the circuit.+run_ascii_lines :: [String] -> (Maybe [(Wire,Wiretype)],CircInfo [CircInfoOut])+run_ascii_lines [] = (Nothing,return [])+run_ascii_lines [f] = case parse_ascii_line f of+ Just (I ws) -> (Just ws, return [])+ _ -> (Nothing, run_ascii_line f >>= \x -> return [x])+-- Special case:+-- If the first line contains the input wires, we should +-- be able to parse the circuit lazily (e.g. it is a full circuit), as+-- we don't have to parse all the gates to calculate the inputs.+run_ascii_lines (f:s) = + case parse_ascii_line f of+ Just (I ws) -> (Just ws, mapM run_ascii_line s)+ _ -> (Nothing, mapM run_ascii_line (f:s))+ +-- | Run function for the 'CircInfo' monad: evaluate the state and+-- produce a list of inputs, outputs, and used wires.+run :: CircInfo [CircInfoOut] -> ([Gate],Map BoxId Sub,CircInfoState)+run f = (gs, subs, cis)+ where+ gs = [x | Lazy x <- filter isGate ci_outs]+ subs = Map.fromList [x | SubDef x <- filter isSub ci_outs] + (ci_outs,cis) = runState f empty_circinfostate+
@@ -0,0 +1,674 @@+-- ----------------------------------------------------------------------+-- | This module contains the code for parsing the ASCII output from+-- Quipper, into a GatePlus.+-- This program is heavily based on, and heavily borrows from the QCLParser.++module Quipper.Libraries.QuipperASCIIParser.Parse where++import Quipper.Internal.Circuit+import Data.IntMap hiding (map)++import Text.ParserCombinators.ReadP+import Data.Char (isDigit)+import qualified Data.Map as Map++-- ----------------------------------------------------------------------+-- | A type for gates, plus other circuit output, empty lines, and subroutine defs.+data GatePlus = G Gate [(Wire,Wiretype)]+ | I [(Wire,Wiretype)]+ | O [(Wire,Wiretype)]+ | EmptyLine+ | CommentLine String+ | SubroutineName String+ | SubroutineShape String+ | Controllable ControllableFlag+ deriving Show++-- ----------------------------------------------------------------------+-- * Parsing++-- | Parse a string literal. +string_literal :: ReadP String+string_literal = do+ char '"'+ s <- string_with_escaped_chars ""+ char '"'+ return s+ where+ string_with_escaped_chars :: String -> ReadP String+ string_with_escaped_chars input = do+ s <- munch (\x -> x /= '\\' && x /= '"')+ let s' = input ++ s+ rest <- look+ case rest of+ '"' : _ -> return s'+ '\\' : _ -> do+ e <- escaped_char+ string_with_escaped_chars (s' ++ [e])+ _ -> do+ pfail++-- | Parse an escaped character, such as @\0@, @\n@, @\\@, etc.+escaped_char :: ReadP Char+escaped_char = do+ char '\\'+ c <- get+ return (Map.findWithDefault c c map)+ where+ -- The official escape codes. We allow any other escaped character+ -- to denote itself. We do not permit "\&" to denote the empty string.+ map = Map.fromList [+ ('0', '\0'),+ ('a', '\a'),+ ('b', '\b'),+ ('f', '\f'),+ ('n', '\n'),+ ('r', '\r'),+ ('t', '\t'),+ ('v', '\v'),+ ('"', '\"'),+ ('\'', '\''),+ ('\\', '\\')+ ]++-- | Parse a signless integer. We avoid the usual trick ('readS_to_P'+-- 'reads'), because this introduces backtracking errors.+int :: ReadP Int+int = do+ s <- munch1 isDigit+ return $ (read s :: Int)++-- | Parse a floating point number. We avoid the usual trick+-- ('readS_to_P' 'reads'), because this introduces backtracking+-- errors.+double :: ReadP Double+double = do+ (s, _) <- gather parse_double+ return $ (read s :: Double)+ where+ parse_double = do+ option '+' (char '+' +++ char '-')+ munch isDigit+ optional (char '.' >> munch1 isDigit)+ optional (char 'e' >> option '+' (char '+' +++ char '-') >> int)+ +-- | Parse a comma separated list.+commalist :: ReadP a -> ReadP [a]+commalist elt = sepBy elt (skipSpaces >> char ',' >> skipSpaces)++-- | Parse a control structure.+control :: ReadP (Signed Wire,Wiretype)+control = do+ val <- choice [char '+',char '-']+ skipSpaces+ w <- int+ c <- option 'q' (char 'c')+ let wt = case c of+ 'c' -> Cbit+ _ -> Qbit+ return (Signed w (val == '+'),wt)++-- | Parse a list of controls.+controls :: ReadP ([Signed Wire],[(Wire,Wiretype)])+controls = do+ skipSpaces+ string "with controls=["+ cwts <- commalist control+ char ']'+ let cs = map fst cwts+ let wts = map (\(Signed w _,wt) -> (w,wt)) cwts+ return (cs,wts)++-- | Parse a 'Wiretype'.+wiretype :: ReadP Wiretype+wiretype = do+ c <- choice [string "Qbit",string "Cbit"]+ case c of+ "Qbit" -> return Qbit+ "Cbit" -> return Cbit+ _ -> error "The impossible has happened"++-- | Parse a wire and its type.+wire :: ReadP (Int,Wiretype)+wire = do+ skipSpaces+ w <- int+ char ':'+ t <- wiretype+ return (w,t)++-- | Parse a list of input/output wires and types.+wires :: ReadP [(Int,Wiretype)]+wires = do+ skipSpaces+ ws <- commalist wire+ return ws++-- | Parse the string \"none\", +-- returning an empty list of input/output wires and types.+none :: ReadP [(Int,Wiretype)]+none = do+ skipSpaces+ string "none"+ return []++-- | Consume an optional \"*\". Return 'True' if consumed, and 'False'+-- otherwise.+inversechar :: ReadP Bool +inversechar = do+ c <- option '+' (char '*')+ return (c == '*')++-- | Consume a label.+label' :: ReadP (Int,String)+label' = do+ w <- int+ char ':'+ lab <- string_literal+ return (w,lab)++-- | Consumer any character other than \')\', \']\', or \',\'.+labelchar :: ReadP String+labelchar = do+ c <- satisfy (\x -> not (x `elem` ")],"))+ return [c]++-- | Consume an index of the form [...].+labelbracket :: ReadP String+labelbracket = do+ char '['+ s <- munch (\x -> not (x `elem` "[]"))+ char ']'+ return ("[" ++ s ++ "]")++-- | Consume a list of labels.+labels :: ReadP [(Int,String)]+labels = do+ skipSpaces+ char '('+ ls <- commalist label'+ char ')'+ return ls++-- | Consume a 'BoxId' followed by a \']\' character.+{-+ascii_of_boxid (BoxId name shape) = show name ++ ", shape " ++ show shape+-}+box_id :: ReadP BoxId+box_id = do+ name <- string_literal+ skipSpaces+ char ','+ skipSpaces+ string "shape"+ skipSpaces+ shape <- string_literal+ return (BoxId name shape)++-- | Consume an optional 'NoControlFlag', returning 'False' if it isn't present.+nocontrolflag :: ReadP NoControlFlag+nocontrolflag = do+ skipSpaces+ ncf <- option "" (string "with nocontrol")+ return (ncf == "with nocontrol")++-- | Parse a single line of ASCII output into a 'Gate'. This function+-- needs to be kept in line with Quipper's+-- 'Quipper.Internal.Printing.ascii_render_gate' function.+ascii_line :: ReadP GatePlus+ascii_line = choice [+ do -- Inputs: w:Type, w:Type ...+ string "Inputs:"+ ws <- choice [wires,none]+ eof+ return (I ws),+ do -- Outputs: w:Type, w:Type ...+ string "Outputs:"+ ws <- choice [wires,none]+ eof+ return (O ws),+ do -- An empty line can be parsed as an EmptyLine+ skipSpaces+ eof+ return EmptyLine,+ do -- "A comment in a circuit is any line beginnning with a # character"+ -- The comment is stored for later use as a CommentLine+ skipSpaces+ char '#'+ skipSpaces+ comment <- manyTill (satisfy (\_ -> True)) eof+ eof+ return (CommentLine comment),+ do -- Subroutine: "name" (keeping unquoted version for backward comp.)+ string "Subroutine: "+ name <- string_literal +++ manyTill (satisfy (\_ -> True)) eof+ eof+ return (SubroutineName name),+ do -- Shape: "shape" (keeping unquoted version for backward comp.)+ string "Shape: "+ shape <- string_literal +++ manyTill (satisfy (\_ -> True)) eof+ eof+ return (SubroutineShape shape),+ do -- Controllable: yes/no/classically+ string "Controllable: "+ val_string <- choice [string "yes",string "no", string "classically"]+ let val = case val_string of+ "yes" -> AllCtl+ "no" -> NoCtl+ "classically" -> OnlyClassicalCtl+ _ -> error "The impossible happened"+ eof+ return (Controllable val),+{-+For backward compatibility:+ascii_render_gate (QNot w c ncf) = + "QNot(" ++ show w ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do + string "QNot("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "not" False [w] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QMultinot ws c ncf) = + "QMultinot(" ++ string_of_list "" ", " "" "" show ws ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QMultinot("+ ws <- commalist int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "multinot" False ws [] cs ncf) wts), +{-+For backward compatibility:+ascii_render_gate (QHad w c ncf) = + "QHad(" ++ show w ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QHad("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "H" False [w] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QSwap w1 w2 c ncf) = + "QSwap(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QSwap("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "swap" False [w1, w2] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QW w1 w2 c ncf) = + "QW(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QW("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "W" False [w1, w2] [] cs ncf) wts),+{-+ascii_render_gate (GPhase t ws c ncf) = + "GPhase() with t=" ++ show t + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+ ++ string_of_list " with anchors=[" ", " "]" "" show ws+-}+ do+ string "GPhase() with t="+ t <- double+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ ws <- option [] (do+ skipSpaces+ string "with anchors=["+ ws <- commalist int+ char ']'+ return ws )+ eof+ return (G (GPhase t ws cs ncf) wts),+{-+ascii_render_gate (QGate name inv ws1 ws2 c ncf) = + "QGate[" ++ show name ++ "]" + ++ optional inv "*" + ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list "; [" "," "]" "" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QGate["+ name <- string_literal+ char ']'+ inv <- inversechar+ char '('+ ws1 <- commalist int+ char ')'+ ws2 <- option [] (do+ string "; ["+ ws <- commalist int+ char ']'+ return ws )+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate name inv ws1 ws2 cs ncf) wts), +{-+ascii_render_gate (QRot name inv theta ws1 ws2 c ncf) = + "QRot[" ++ show name ++ "," ++ (show theta) ++ "]" + ++ optional inv "*"+ ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list "; [" "," "]" "" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QRot["+ name <- string_literal+ char ','+ theta <- double+ char ']'+ inv <- inversechar+ char '('+ ws1 <- commalist int+ char ')'+ ws2 <- option [] ( do+ string "; ["+ ws <- commalist int+ char ']'+ return ws )+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QRot name inv theta ws1 ws2 cs ncf) wts),+{-+ascii_render_gate (CNot w c ncf) = + "CNot(" ++ show w ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do + string "CNot("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (CNot w cs ncf) wts),+{-+ascii_render_gate (CGate n w c ncf) = + "CGate[" ++ show n ++ "]" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CGate["+ name <- string_literal+ string "]("+ ws <- commalist int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CGate name (head ws) (tail ws) ncf) []),+{-+ascii_render_gate (CGateInv n w c ncf) = + "CGate[" ++ show n ++ "]" ++ "*" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CGate["+ name <- string_literal+ string "]*("+ ws <- commalist int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CGateInv name (head ws) (tail ws) ncf) []),+{-+ascii_render_gate (CSwap w1 w2 c ncf) = + "CSwap(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CSwap("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (CSwap w1 w2 cs ncf) wts),+{-+ascii_render_gate (QPrep w ncf) = + "QPrep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QPrep("+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QPrep w ncf) []),+{-+ascii_render_gate (QUnprep w ncf) = + "QUnprep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QUnprep("+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QUnprep w ncf) []),+{-+ascii_render_gate (QInit b w ncf) = + "QInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QInit"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QInit val w ncf) []),+{-+ascii_render_gate (CInit b w ncf) = + "CInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CInit"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CInit val w ncf) []),+{-+ascii_render_gate (QTerm b w ncf) = + "QTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QTerm"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QTerm val w ncf) []),+{-+ascii_render_gate (CTerm b w ncf) = + "CTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CTerm"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CTerm val w ncf) []),+{-+ascii_render_gate (QMeas w) = + "QMeas(" ++ show w ++ ")"+-}+ do+ string "QMeas("+ w <- int+ char ')'+ eof+ return (G (QMeas w) []),+{-+ascii_render_gate (QDiscard w) = + "QDiscard(" ++ show w ++ ")"+-}+ do+ string "QDiscard("+ w <- int+ char ')'+ eof+ return (G (QDiscard w) []),+{-+ascii_render_gate (CDiscard w) = + "CDiscard(" ++ show w ++ ")"+-}+ do+ string "CDiscard("+ w <- int+ char ')'+ eof+ return (G (CDiscard w) []),+{-+ascii_render_gate (DTerm b w) = + "DTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+-}+ do+ string "DTerm"+ vc <- char '0' +++ char '1'+ let b = case vc of+ '0' -> False+ '1' -> True+ _ -> error "The impossible has happend" + char '('+ w <- int+ char ')'+ eof+ return (G (DTerm b w) []),+{-+ascii_render_gate (Subroutine boxid inv ws1 a1 ws2 a2 c ncf scf) = + "Subroutine" ++ show_rep ++ "[" ++ show (string_of_boxid boxid) ++ "]"+ ++ optional inv "*"+ ++ " "+ ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list " -> (" "," ")" "()" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+ where+ show_rep = if rep == RepeatFlag 1 then "" else "(x" ++ show rep ++ ")"+-}+-- The parser returns a subroutine with dummy arities, and controllable flag,+-- and repeat flag, as+-- this information is not in a subroutine line in the ASCII output. +-- The information is added when the GatePlus is evaluated, as the first phase of+-- parsing will have collected the information. + do+ string "Subroutine"+ reps <- option 1 $ do+ string "(x"+ r <- int+ char ')'+ return (toInteger r)+ char '['+ boxid <- box_id+ char ']'+ inv <- inversechar+ string " ("+ ws1 <- commalist int+ string ") -> ("+ ws2 <- commalist int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ let dummy = error "attempted evaluation of a dummy value"+ return (G (Subroutine boxid inv ws1 dummy ws2 dummy cs ncf dummy (RepeatFlag reps)) wts), +{-+ascii_render_gate (Comment s inv ws) = + "Comment[" ++ show s ++ "]" + ++ optional inv "*"+ ++ (string_of_list "(" ", " ")" "()" (\(w,s) -> show w ++ ":" ++ show s) ws)+-}+ do+ string "Comment"+ skipSpaces+ char '['+ comment <- string_literal+ char ']'+ inv <- inversechar+ ls <- labels+ eof+ return (G (Comment comment inv ls) [])+ ]++-- | The overall parsing function, reading a line of ASCII output, and+-- producing a 'GatePlus'.+parse_ascii_line :: String -> Maybe GatePlus+parse_ascii_line s =+ case readP_to_S ascii_line s of+ (h, _):_ -> Just h+ _ -> Nothing+ where+ readline = do+ skipSpaces+ l <- ascii_line+ skipSpaces+ eof+ return l + +
@@ -0,0 +1,264 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This module provides a data type of quantum registers, as well as+-- associated types of classical and boolean registers.++module Quipper.Libraries.Qureg (+ -- * Quantum registers+ Qureg,+ qureg_of_qulist_te,+ qulist_of_qureg_te,+ qureg_length,+ qinit_register,+ qterm_register,+ qmeasure_register,+ with_ancilla_reg,+ with_ancilla_reg_init,+ qureg_shape,+ + -- * Bit registers+ Bitreg,+ bitreg_of_bitlist_te,+ bitlist_of_bitreg_te,+ bitreg_length,+ + -- * Boolean registers+ Boolreg,+ boolreg_of_boollist_te,+ boollist_of_boolreg_te,+ boolreg_length,+ boolreg_of_int_le,+ int_of_boolreg_unsigned_le,+ + -- * General registers+ Register,+ (.!)+ + ) where++import Quipper+import Quipper.Internal++import Quipper.Utils.Auxiliary++import qualified Data.IntMap as IntMap+import Data.IntMap (IntMap)+import Data.Typeable++-- ----------------------------------------------------------------------+-- * Quantum registers++-- ** General registers++-- | A register is an array of elements of some type /x/, indexed by+-- natural numbers in the range from 0 to /n/-1, where /n/ is the+-- length of the register.+newtype Register x = Register { unRegister :: IntMap x }+ deriving (Typeable, Show)++-- | @/r/ !.(/i/)@: Return the /i/th element of a register /r/.+(.!) :: Register x -> Int -> x+a .!(i) = (unRegister a) IntMap.! i++infixl 9 .! -- same precedence as !!++-- | Convert a list to a register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+register_of_list_te :: [x] -> Register x+register_of_list_te l = Register $ IntMap.fromList (zip [n-1, n-2 .. 0] l)+ where n = length l+ +-- | Convert a register to a list. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+list_of_register_te :: Register x -> [x]+list_of_register_te r = map (r .!) [n-1, n-2 .. 0]+ where n = register_length r++-- | Return the length of a register.+register_length :: Register x -> Int+register_length = IntMap.size . unRegister++-- ----------------------------------------------------------------------+-- ** Qubit registers++-- | The type of quantum registers. A quantum register is an array of+-- qubits, indexed by natural numbers in the range from 0 to /n/-1,+-- where /n/ is the length of the register. The syntax /a/ .!(/i/) is+-- used to access the /i/th element of the register /a/. +-- +-- The main advantage of a register over a list is constant-time+-- access. The main disadvantage is that registers don't allow easy+-- appending, pattern matching, or recursion.+type Qureg = Register Qubit++-- | Convert a 'Qulist' to a 'Qureg'. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qureg_of_qulist_te :: Qulist -> Qureg+qureg_of_qulist_te = register_of_list_te++-- | Convert a 'Qureg' to a 'Qulist'. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qulist_of_qureg_te :: Qureg -> Qulist+qulist_of_qureg_te = list_of_register_te++-- | Return the length of a 'Qureg'. +qureg_length :: Qureg -> Int+qureg_length = register_length++-- | Return a piece of shape data to represent an /m/-qubit quantum+-- register. Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+qureg_shape :: Int -> Qureg+qureg_shape m = qureg_of_qulist_te (replicate m qubit)++-- ----------------------------------------------------------------------+-- ** Bit registers++-- | The type of 'Bit' registers. The syntax /a/ .!(/i/) is used to+-- access the /i/th element of the register /a/.+type Bitreg = Register Bit++-- | Turn a bit vector into a bit register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+bitreg_of_bitlist_te :: Bitlist -> Bitreg+bitreg_of_bitlist_te = register_of_list_te++-- | Turn a bit register into a bit vector. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+bitlist_of_bitreg_te :: Bitreg -> Bitlist+bitlist_of_bitreg_te = list_of_register_te++-- | Return the length of a 'Bitreg'.+bitreg_length :: Bitreg -> Int+bitreg_length = register_length++-- ----------------------------------------------------------------------+-- ** Boolean registers++-- | The type of boolean registers.+type Boolreg = Register Bool++-- | Turn a bool vector into a bool register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+boolreg_of_boollist_te :: Boollist -> Boolreg+boolreg_of_boollist_te = register_of_list_te++-- | Turn a bool register into a bool vector. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+boollist_of_boolreg_te :: Boolreg -> Boollist+boollist_of_boolreg_te = list_of_register_te++-- | Return the length of a 'Boolreg'.+boolreg_length :: Boolreg -> Int+boolreg_length = register_length++-- ----------------------------------------------------------------------+-- * Special functions for quantum registers++-- | Creates a new quantum register, initialized from a list of+-- booleans. The conversion is tail-endian, i.e., /r/.!(0) holds the+-- tail of the list.+qinit_register :: [Bool] -> Circ Qureg+qinit_register bs = do+ qs <- qinit bs+ return (qureg_of_qulist_te qs)++-- | Terminates a quantum register, and assert that its state is as+-- specified by the list of booleans. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qterm_register :: [Bool] -> Qureg -> Circ ()+qterm_register bs r = do+ let qs = qulist_of_qureg_te r+ qterm bs qs+ +-- | Measure a quantum register, yielding a list of 'Bit's. +qmeasure_register :: Qureg -> Circ [Bit]+qmeasure_register r =+ measure (qulist_of_qureg_te r)+ +-- | Temporarily create a quantum register of size /n/ for use as an+-- ancilla. This can be used to introduce an ancilla with a local scope, like this: +-- +-- > with_ancilla_reg n $ \r -> do {+-- > <<<code block using ancilla register r>>>+-- > }+with_ancilla_reg :: Int -> (Qureg -> Circ a) -> Circ a+with_ancilla_reg n f = do+ let falselist = (take n $ repeat False)+ q <- qinit_register falselist+ a <- f q+ qterm_register falselist q+ return a++-- | Like 'with_ancilla_reg', except also initialize the register as+-- specified by a bit vector. In this case, the argument /n/ is not+-- required, because it equals the length of the bit vector. When the+-- ancilla is terminated at the end of its scope, it is asserted to be+-- in the same state it was prepared in.+with_ancilla_reg_init :: Boollist -> (Qureg -> Circ a) -> Circ a+with_ancilla_reg_init v f = do+ q <- qinit_register v+ a <- f q+ qterm_register v q+ return a++-- ----------------------------------------------------------------------+-- * Special functions for boolean registers++-- | @boolreg_of_int m x@: Initialize a bool register directly from an+-- integer /x/, regarded as a binary string of length /m/. The+-- conversion is little-endian, i.e., the register holds the least+-- significant digit at index 0.+boolreg_of_int_le :: Integral a => Int -> a -> Boolreg+boolreg_of_int_le m x = boolreg_of_boollist_te (boollist_of_int_bh m x)++-- | @int_of_boolreg_unsigned_le m r@: Turn a bool register into an+-- integer, regarded as a binary string. The conversion is+-- little-endian, i.e., the register holds the least significant digit+-- at index 0. The integer is unsigned.+int_of_boolreg_unsigned_le :: Integral a => Boolreg -> a+int_of_boolreg_unsigned_le r = int_of_boollist_unsigned_bh (boollist_of_boolreg_te r)++-- ----------------------------------------------------------------------++-- Make 'Qureg' an instance of 'QData' and 'QCData'.+type instance QCType x y Qureg = Register x+type instance QCType x y Bitreg = Register y+type instance QTypeB Boolreg = Qureg+instance QCData Qureg where+ + qcdata_mapM shape f g (Register xs) = do+ ys <- intmap_mapM f xs+ return (Register ys)+ + qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where+ zs = intmap_zip_errmsg xs ys (e "register length mismatch")++ qcdata_promote as xs e+ | register_length as == register_length xs = as+ | otherwise = error (e "register length mismatch")++instance QCData Bitreg where+ + qcdata_mapM shape f g (Register xs) = do+ ys <- intmap_mapM g xs+ return (Register ys)++ qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where+ zs = intmap_zip_errmsg xs ys (e "register length mismatch")++ qcdata_promote as xs e+ | register_length as == register_length xs = as+ | otherwise = error (e "register length mismatch")++instance Labelable Qureg String where+ label_rec (Register xs) s = do+ sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ]++instance Labelable Bitreg String where+ label_rec (Register xs) s = do+ sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ]
@@ -0,0 +1,36 @@+-- | This library provides functions for simulating certain classes of+-- circuits, for testing and debugging purposes. +-- +-- We can efficiently simulate classical (boolean) circuits and+-- Clifford (stabilizer) circuits. We also provide functions for+-- simulating arbitrary quantum circuits; however, the latter is+-- (necessarily) very inefficient.++module Quipper.Libraries.Simulation ( + -- * Classical simulation+ run_classical_generic,+ + -- * Stabilizer simulation+ run_clifford_generic,+ + -- * Quantum simulation+ run_generic,+ run_generic_io,+ sim_amps,+ + -- * Special purpose functions+ -- ** Simulation with trace+ QuantumTrace (..),+ run_generic_trace,+ run_generic_trace_io,+ + -- ** Probability distributions+ Vector (..),+ ProbabilityDistribution (..),+ sim_generic,+) where++import Quipper.Libraries.Simulation.ClassicalSimulation+import Quipper.Libraries.Simulation.QuantumSimulation+import Quipper.Libraries.Simulation.CliffordSimulation+
@@ -0,0 +1,211 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides functions for simulating certain classes of+-- circuits, for testing and debugging purposes. At the moment, the+-- only type of simulation we provide is boolean simulation. +-- +-- This module provides the internal implementation of the library,+-- and can be imported by other libraries. The public interface to+-- simulation is "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.ClassicalSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, Simulation.hs uses low-level interfaces. It should be+-- re-implemented using only high-level interfaces, or in some cases,+-- more stuff should be exported from Quipper.hs.+import Quipper.Internal.Transformer (bind_list, bindings_empty, unbind_list, Bindings, transform_bcircuit_id)+import Quipper.Internal.Circuit (RepeatFlag(..), TypedSubroutine(..), OCircuit(..), reverse_ocircuit, ControllableFlag(..), BCircuit)+import Quipper.Internal.Generic (qc_bind, qc_unbind, encapsulate_generic)++import Quipper.Utils.Auxiliary++import Data.List+import qualified Data.Map as Map++-- ======================================================================+-- * Simulation of classical circuits++-- | Auxiliary function: determine whether a \"control\" is true of false.+controls :: [Signed (B_Endpoint Bool Bool)] -> Bool+controls [] = True+controls ((Signed (Endpoint_Qubit c) b):t) = (c == b) && controls t+controls ((Signed (Endpoint_Bit c) b):t) = (c == b) && controls t++-- | Auxiliary function: compute the boolean function corresponding to+-- a 'Quipper.Internal.Circuit.CGate' of the given name.+simulate_cgate :: String -> [Bool] -> Bool+simulate_cgate "if" [a,b,c] = if a then b else c+simulate_cgate "if" list = error ("simulate_cgate: \"if\" needs 3 arguments, not " ++ show (length list))+simulate_cgate "and" list = and list+simulate_cgate "or" list = or list+simulate_cgate "xor" list = foldl' bool_xor False list+simulate_cgate "eq" [a,b] = (a == b)+simulate_cgate "eq" list = error ("simulate_cgate: \"eq\" needs 2 arguments, not " ++ show (length list))+simulate_cgate "not" [a] = not a+simulate_cgate "not" list = error ("simulate_cgate: \"not\" needs 1 argument, not " ++ show (length list))+simulate_cgate name list = error ("simulate_cgate: gate \"" ++ name ++ "\" not known")+++-- | A "Quipper.Internal.Transformer" mapping each gate to the corresponding boolean+-- function. This will fail with an error for gates that don't act+-- within the computational basis, or if some assertion is not+-- met. Some gates are not yet implemented. ++simulate_classical :: Transformer Id Bool Bool++-- Translation of classical gates:+simulate_classical (T_CNot ncf f) = f $+ \q c -> Id (if controls c then not q else q, c)+simulate_classical (T_CSwap ncf f) = f $+ \w v c -> Id (if controls c then (v,w,c) else (w,v,c))+simulate_classical (T_CInit b ncf f) = f $+ Id b+simulate_classical (T_CTerm b ncf f) = f $+ \q -> Id (if b==q then () else error "simulate_classical: CTerm assertion not met")+simulate_classical (T_CDiscard f) = f $+ \b -> Id ()+simulate_classical (T_DTerm b f) = f $+ \b -> Id ()+simulate_classical (T_CGate name ncf f) = f $+ \list -> Id (simulate_cgate name list, list)+simulate_classical (T_CGateInv name ncf f) = f $+ \q list -> if q == simulate_cgate name list + then Id list + else error ("simulate_classical: CGateInv failed assertion " ++ show q ++ " == " ++ name ++ show list)++-- Translation of quantum gates:+simulate_classical (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] c -> if controls c then Id([not q], [], c) else Id([q], [], c)+simulate_classical (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] c -> Id (if controls c then map not qs else qs, [], c) +simulate_classical (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w,v] [] c -> Id (if controls c then ([v,w], [], c) else ([w,v], [] ,c))+simulate_classical g@(T_QGate "H" 1 0 _ _ _) =+ error ("simulate_classical: gate not classical: " ++ show g)+simulate_classical g@(T_QGate "W" 2 0 _ _ _) =+ error ("simulate_classical: gate not classical: " ++ show g)+simulate_classical g@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls c -> case (name, inv, qs, gctls) of+ ("X", _, [q], []) -> + if controls c then Id([not q], [], c) else Id([q], [], c)+ ("Y", _, [q], []) ->+ if controls c then Id([not q], [], c) else Id([q], [], c)+ ("Z", _, [q], []) -> Id([q], [], c)+ ("S", _, [q], []) -> Id([q], [], c)+ ("T", _, [q], []) -> Id([q], [], c)+ ("omega", _, [q], []) -> Id([q], [], c)+ ("iX", _, [q], []) ->+ if controls c then Id([not q], [], c) else Id([q], [], c)+ _ -> error ("simulate_classical: unimplemented gate: " ++ show g)+simulate_classical g@(T_QRot name _ _ inv t ncf f) = f $+ \qs gctls c -> case (name, inv, qs, gctls) of+ ("R(2pi/%)", _, [q], []) -> Id([q], [], c)+ ("exp(-i%Z)", _, [q], []) -> Id([q], [], c)+ _ -> error ("simulate_classical: unimplemented gate: " ++ show g)+simulate_classical g@(T_GPhase t ncf f) = f $+ \q c -> Id c+simulate_classical (T_QInit b ncf f) = f $+ Id b+simulate_classical (T_QTerm b ncf f) = f $+ \q -> if b==q then Id() else error "simulate_classical: QTerm assertion not met"+simulate_classical (T_QDiscard f) = f $+ \b -> Id()+simulate_classical (T_Comment s inv f) = f $+ \b -> Id()++-- Preparation, unpreparation, and measurement:+simulate_classical g@(T_QPrep ncf f) = f $+ \w -> Id w+simulate_classical g@(T_QUnprep ncf f) = f $+ \w -> Id w+simulate_classical g@(T_QMeas f) = f $+ \w -> Id w++-- Subroutines:+simulate_classical g@(T_Subroutine sub inv ncf scf ws_pat a1_pat vs_pat a2_pat (RepeatFlag repeat) f) = f $+ \namespace in_values c -> Id $ + case Map.lookup sub namespace of+ Just (TypedSubroutine sub_ocirc _ _ _) ->+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ in_bindings = bind_list in_wires in_values bindings_empty+ out_bindings = + if (case scf of {AllCtl -> True; OnlyClassicalCtl -> True; NoCtl -> False})+ then if controls c then foldl' (\in_b _ -> run_classical (sub_circ, namespace) in_b) in_bindings [1..repeat] else in_bindings+ else if length c == 0+ then foldl' (\in_b _ -> run_classical (sub_circ, namespace) in_b) in_bindings [1..repeat]+ else error $ "simulate_classical: attempt to control non-controllable subroutine " ++ show sub+ in (unbind_list out_bindings out_wires, c) + Nothing -> error $ "simulate_classical: subroutine " ++ show sub ++ " not found"++-- If adding gates: remember to list any unimplemented gates+-- explicitly, so that the type-checker can warn about new gates that+-- must be added to the list.++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. This function makes use of the concept of a+-- /valuation/. A valuation is a partial map from circuit wires to+-- booleans, represented by the type @'Bindings' Bool@. Given a+-- circuit and a valuation for its inputs, the function+-- 'run_classical' produces a valuation for its outputs.+-- +-- The simulation will fail with an error if the circuit contains a+-- gate not acting within the computational basis, or if some+-- assertion is not met. Not all gates are currently+-- implemented. Subroutines are not currently implemented.+-- +run_classical :: BCircuit -> Bindings Bool Bool -> Bindings Bool Bool+run_classical = transform_bcircuit_id simulate_classical++-- ======================================================================+-- * Generic simulation++-- | Like 'run_classical_unary', but also takes a stub error message.+run_classical_errmsg :: (QCData qa, QCData qb) => ErrMsg -> (qa -> Circ qb) -> BType qa -> BType qb+run_classical_errmsg e (f :: qa -> Circ qb) input = output where+ shape = qcdata_makeshape (dummy :: qa) (dummy :: Bool) (dummy :: Bool) input+ (qa, circuit, qb) = encapsulate_generic e f shape+ valuation_in = qc_bind qa input+ valuation_out = run_classical circuit valuation_in+ output = qc_unbind valuation_out qb++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. Input a classical circuit, and output the corresponding+-- boolean function. This will fail with an error if the circuit+-- contains a gate not acting within the computational basis, or if+-- some assertion is not met.+run_classical_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> BType qb+run_classical_unary = run_classical_errmsg errmsg + where+ errmsg x = "run_classical_unary: operation not currently permitted by simulator: " ++ x++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. Input a classical circuit, and output the corresponding+-- boolean function. This will fail with an error if the circuit+-- contains a gate not acting within the computational basis, or if+-- some assertion is not met.+-- +-- Unlike 'run_classical_unary', this can be applied to a+-- circuit-generating function in curried form with /n/ arguments, for+-- any /n/ ≥ 0. The resulting boolean function then will also have /n/ arguments. +-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > run_classical_generic :: (QCData qa) => Circ qa -> BType qa+-- > run_classical_generic :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> BType qb+-- > run_classical_generic :: (QCData qa, QCData qb, QCData qc) => (qa -> qb -> Circ qc) -> BType qa -> BType qb -> BType qc+-- +-- and so forth.+ +run_classical_generic :: (QCData qa, QCData qb, QCurry qfun qa qb, Curry fun (BType qa) (BType qb)) => qfun -> fun+run_classical_generic f = g where+ f1 = quncurry f+ g1 = run_classical_errmsg errmsg f1+ g = mcurry g1+ errmsg x = "run_classical_generic: operation not currently permitted by simulator: " ++ x+
@@ -0,0 +1,255 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides a Quipper interface to the efficient+-- simulation of Clifford group circuits, using the Stabilizer+-- formalism.+-- +-- This module provides the internal implementation of the library,+-- and can be imported by other libraries. The public interface to+-- simulation is "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.CliffordSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, CliffordSimulation.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Generic (encapsulate_generic, qc_unbind)+import Quipper.Internal.Transformer (transform_bcircuit_rec, bindings_empty)++import Quipper.Libraries.Simulation.QuantumSimulation (gateQinv, rotQinv)+import Quipper.Libraries.Simulation.ClassicalSimulation (simulate_cgate)++import Quipper.Utils.Auxiliary+import qualified Quipper.Utils.Stabilizers.Clifford as C++import Control.Monad.State (StateT)+import Data.Either++-- ----------------------------------------------------------------------+-- * The stabilizer transformer++-- | The stabilizer transformer. Used to transform a Quipper circuit,+-- made up of Clifford group operators, directly into the 'C.CliffordCirc'+-- monad (where 'C.CliffordCirc' is a type-synonym for StateT Tableau IO).+-- Note that this transformer only deals with 1 and 2-qubit operators.+stabilizer_transformer :: Transformer (StateT C.Tableau IO) C.Qubit Bool+stabilizer_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ -- X and controlled-X are already provided by C.CliffordCirc+ \[qt] [] c -> case control_info c of+ None -> do+ C.gate_X qt+ return ([qt], [], c)+ Classical b -> do+ if b then C.gate_X qt else return ()+ return ([qt], [], c)+ OneQuantum (Signed qc positive) b -> do+ if not positive then C.gate_X qc else return ()+ if b then C.controlled_X qc qt else return ()+ if not positive then C.gate_X qc else return ()+ return ([qt], [], c)+ _ -> error "stabilizer_transformer: Toffoli gate not available"+stabilizer_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ -- X and controlled-X are already provided by C.CliffordCirc+ -- and can be mapped over a list of qubits + \ws [] c -> case control_info c of+ None -> do+ mapM_ C.gate_X ws+ return (ws, [], c)+ Classical b -> do+ if b then mapM_ C.gate_X ws else return ()+ return (ws, [], c)+ OneQuantum (Signed qc positive) b -> do+ if not positive then C.gate_X qc else return ()+ if b then mapM_ (C.controlled_X qc) ws else return ()+ if not positive then C.gate_X qc else return ()+ return (ws, [], c)+ _ -> error "stabilizer_transformer: (Multi) Toffoli gate not available"+stabilizer_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ -- Hadamard is already provided by C.CliffordCirc + \[qt] [] c -> case control_info c of+ None -> do+ C.gate_H qt+ return ([qt], [], c)+ Classical b -> do+ if b then C.gate_H qt else return ()+ return ([qt], [], c)+ -- TODO: ???+ _ -> error "stabilizer_transformer: controlled-Hadamard currently not supported"+stabilizer_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ -- (classically-controlled) swap is already provided by C.CliffordCirc+ \[w, v] [] c -> case control_info c of+ None -> do+ C.swap w v+ return ([w, v], [], [])+ Classical b -> do+ if b then C.swap w v else return ()+ return ([w, v], [], [])+ _ -> error "stabilizer_transformer: quantum controlled-swap not available"+stabilizer_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ -- TODO: ???+ \[w, v] [] c -> error "stabilizer_transformer: W currently not supported"+stabilizer_transformer (T_QGate name _ _ inv ncf f) = f $+ \ws vs c ->+ case vs of+ [] ->+ case ws of+ [qt] -> case control_info c of+ None -> do+ C.gate_Unitary u1 qt+ return ([qt], vs, c)+ Classical b -> do+ if b then C.gate_Unitary u1 qt else return ()+ return ([qt], vs, c)+ OneQuantum (Signed qc positive) b -> do+ if b then C.gate_Unitary2 u2 qc qt else return ()+ return ([qt], vs, c)+ _ -> error "stabilizer_transformer: Multiple quantum controls not supported"+ where u1 = case name of+ "X" -> C.x+ "Y" -> C.y+ "Z" -> C.z+ "S" -> C.s+ "E" -> C.e+ name -> C.from_matrix (gateQinv name inv)+ u2 = case name of+ "X" -> C.cnot+ "Z" -> C.cz+ name -> C.from_matrix_controlled (gateQinv name inv)+ _ -> error "stabilizer_transformer: Named gates on multiple Qubits not available"+ _ -> error "stabilizer_transformer: generalised controls not currently supported"+stabilizer_transformer (T_QRot name _ _ inv t ncf f) = f $+ \ws vs c -> error "stabilizer_transformer: QRot not currently supported" --return (ws, vs, c)+stabilizer_transformer (T_GPhase t ncf f) = f $+ -- TODO: ???+ \qs c -> error "stabilizer_transformer: GPhase currently not supported"+stabilizer_transformer (T_CNot ncf f) = f $+ -- we can do a classical not depending on the controls+ \q c -> case control_info c of+ None -> return (not q,c)+ Classical b -> if b then return (not q,c) else return (q,c) + _ -> error "stabilizer_transformer: Quantum control on classical gate"+stabilizer_transformer (T_CGate name ncf f) = f $+ -- we can reuse the classical simulator cgate implementation+ \ws -> return (simulate_cgate name ws,ws)+stabilizer_transformer (T_CGateInv name ncf f) = f $+ -- we can check that the inverse is correct using the classical simulator cgate implementation+ \v ws -> if (simulate_cgate name ws == v) then return ws else error "stabilizer_transformer: CGateInv not inverse"+stabilizer_transformer (T_CSwap ncf f) = f $+ -- we can do a classical swap depending on the controls+ \w v c -> case control_info c of+ None -> return (w,v,c)+ Classical b -> if b then return (v,w,c) else return (w,v,c) + _ -> error "stabilizer_transformer: Quantum control on classical gate"+stabilizer_transformer (T_QPrep ncf f) = f $+ -- TODO: ??+ \w -> error "stabilizer_transformer: QPrep currently not supported"+stabilizer_transformer (T_QUnprep ncf f) = f $+ -- TODO: ?? + \w -> error "stabilizer_transformer: QUnprep currently not supported"+stabilizer_transformer (T_QInit b ncf f) = f $+ -- initialization is already provided by C.CliffordCirc+ C.init_qubit b+stabilizer_transformer (T_CInit b ncf f) = f $+ -- initialisation of a boolean value+ return b+stabilizer_transformer (T_QTerm b ncf f) = f $+ -- we can check termination conditions at runtime, using measurement+ \w -> do+ res <- C.measure_qubit w+ if res == b then return () else error "stabilizer_transformer: QTerm condition failed"+stabilizer_transformer (T_CTerm b ncf f) = f $+ -- we can check termination conditions at runtime+ \w -> if w == b then return () else error "stabilizer_transformer: CTerm condition failed"+stabilizer_transformer (T_QMeas f) = f $+ -- measurement is already provided by C.CliffordCirc + \w -> C.measure_qubit w+stabilizer_transformer (T_QDiscard f) = f $+ -- we can ignore discards+ \w -> return ()+stabilizer_transformer (T_CDiscard f) = f $+ -- we can ignore discards+ \w -> return ()+stabilizer_transformer (T_DTerm b f) = f $+ -- TODO: ??+ \w -> error "stabilizer_transformer: DTerm currently not supported" --return ()+stabilizer_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ -- TODO: we can "open" subroutines+ \namespace ws c -> error "stabilizer_transformer: Subroutine currently not supported" --return (ws,c)+stabilizer_transformer (T_Comment s inv f) = f $+ -- we can ignore comments+ \ws -> return ()++-- | A datatype to represent controls that we can simulate+data ControlInfo = None | Classical Bool | OneQuantum (Signed C.Qubit) Bool | ManyQuantum++-- | Construct an element of ControlInfo from a list of Controls.+control_info :: Ctrls C.Qubit Bool -> ControlInfo+control_info cs = case split_controls cs of+ ([],[]) -> None+ ([],cs) -> Classical (all_equal cs)+ ([q],cs) -> OneQuantum q (all_equal cs)+ _ -> ManyQuantum+ where+ split_controls :: Ctrls a b -> ([Signed a],[Signed b])+ split_controls cs = partitionEithers (map either_control cs)+ either_control :: Signed (B_Endpoint a b) -> Either (Signed a) (Signed b)+ either_control (Signed (Endpoint_Qubit a) value) = Left (Signed a value)+ either_control (Signed (Endpoint_Bit b) value) = Right (Signed b value)+ all_equal :: [Signed Bool] -> Bool+ all_equal cs = and (map (\(Signed b val) -> b == val) cs) ++-- ----------------------------------------------------------------------+-- * High-level functions++-- | Use the 'stabilizer_transformer' to transform a Quipper circuit+-- into a 'C.CliffordCirc', ready for simulation.+toCliffordCirc :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> C.CliffordCirc (BType qb))+toCliffordCirc (f :: qa -> Circ qb) input = do+ let ((), circuit, cb) = encapsulate_generic errmsg (\() -> qc_init input >>= \qi -> f qi >>= \qi' -> qc_measure qi') ()+ out_bind' <- transform_bcircuit_rec stabilizer_transformer circuit bindings_empty+ let out_bind = out_bind'+ let output = qc_unbind out_bind cb+ return output+ where+ errmsg x = ("simulate: " ++ x)++-- | Return the tableau resulting from simulating the given Quipper+-- circuit.+eval_unary :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> IO C.Tableau)+eval_unary circ input = C.eval (toCliffordCirc circ input)++-- | Efficiently simulate a unary Quipper circuit that consists+-- entirely of Clifford group operators, using the stabilizer+-- formalism.+run_clifford_unary :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> IO (BType qb))+run_clifford_unary circ input = C.sim (toCliffordCirc circ input)++-- | Efficiently simulate a Quipper circuit that consists entirely of+-- Clifford group operators, using the stabilizer formalism. +-- +-- Inputs a quantum circuit, and outputs 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. Because the measurement outcomes are probabilistic,+-- this function takes place in the 'IO' monad.+-- +-- 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_clifford_generic :: (QCData qa) => Circ qa -> IO (BType qa)+-- > run_clifford_generic :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> IO (BType qb)+-- > run_clifford_generic :: (QCData qa, QCData qb, QCData qc) => (qa -> qb -> Circ qc) -> BType qa -> BType qb -> IO (BType qc)+-- +-- and so forth.+run_clifford_generic :: (QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (BType qa) (IO (BType qb))) => qfun -> qfun'+run_clifford_generic f = g where+ f1 = quncurry f+ g1 = run_clifford_unary f1+ g = mcurry g1
@@ -0,0 +1,850 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Rank2Types #-}++-- | 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 "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.QuantumSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, QuantumSimulation.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit+import Quipper.Internal.Transformer+import Quipper.Internal.Monad (qubit_of_wire)+import Quipper.Internal.Generic (encapsulate_dynamic, qc_unbind)++import Quipper.Utils.Auxiliary++-- we use the state monad to hold our \"quantum\" state+import Control.Monad.State+-- we use complex numbers as our probability amplitudes+import Quantum.Synthesis.Ring (Cplx (..), i)+-- we use a random number generator to simulate \"quantum randomness\"+import System.Random hiding (split)+-- we store \"basis\" states as a map, +import Data.Map (Map)+import qualified Data.Map as Map++import Data.List (partition)++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++import qualified Debug.Trace -- used in tracing the simulation of quipper computations++-- | We define our own trace function that only calls trace if the boolean+-- argument is true.+trace :: Bool -> String -> a -> a+trace False _ a = a+trace True message a = Debug.Trace.trace message a++-- ======================================================================+-- * 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.++-- | 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:+--+-- \[image GateR.png] +--+-- would be written as (m00,m01,m10,m11).+type GateR r = (Cplx r,Cplx r, Cplx r, Cplx r)++-- | Scalar multiplication of a 2-by-2 matrix by a given scalar.+scale :: (Floating r) => Cplx r -> GateR r -> GateR r+scale e (a,b,c,d) = (e*a,e*b,e*c,e*d) ++-- | The inverse of a 'GateR' is its conjugate transpose.+reverseR :: (Floating r) => GateR r -> GateR r+reverseR (m00,m01,m10,m11) = (conjugate m00, conjugate m10, conjugate m01, conjugate m11)+ where+ conjugate (Cplx a b) = Cplx a (-b) ++-- | 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)+gateQ :: (Floating r) => String -> GateR r+gateQ "x" = (0,1,1,0)+gateQ "hadamard" = (h, h, h,-h) where h = Cplx (1/sqrt 2) 0+gateQ "X" = (0,1,1,0)+gateQ "Y" = (0,-i,i,0)+gateQ "Z" = (1,0,0,-1)+gateQ "S" = (1,0,0,i)+gateQ "E" = ((-1+i)/2, (1+i)/2, (-1+i)/2, (-1-i)/2)+gateQ "YY" = (h,i*h,i*h,h) where h = Cplx (1/sqrt 2) 0+gateQ "T" = (1,0,0,omega) where omega = (Cplx (1 / sqrt 2) (1 / sqrt 2)) +gateQ "V" = scale 0.5 (a,b,b,a) where a = Cplx 1 (-1)+ b = Cplx 1 1+gateQ "omega" = (omega,0,0,omega) where omega = (Cplx (1 / sqrt 2) (1 / sqrt 2))+gateQ "iX" = (0,i,i,0)+gateQ name = error ("quantum gate: " ++ name ++ " not implemented")++-- | Like 'gateQ', but also conditionally invert the gate depending+-- on InverseFlag.+gateQinv :: (Floating r) => String -> InverseFlag -> GateR r+gateQinv name False = gateQ name+gateQinv name True = reverseR (gateQ name)++-- | The exponential function for 'Cplx' numbers.+expC :: (Floating r) => Cplx r -> Cplx r+expC (Cplx a b) = Cplx (exp a * cos b) (exp a * sin b)++-- | The constant π, as a complex number.+piC :: (Floating r) => Cplx r+piC = Cplx pi 0++-- | Like 'gateQ', but takes the name of a rotation and a real parameter. +rotQ :: (Floating r) => String -> Timestep -> GateR r+rotQ "exp(-i%Z)" theta = expZtR t+ where t = fromRational (toRational theta)+rotQ "exp(% pi i)" theta = gPhase t+ where t = fromRational (toRational theta)+rotQ "R(2pi/%)" theta = (1,0,0,expC (2*piC*i/t))+ where t = fromRational (toRational theta)+rotQ "T(%)" theta = (1,0,0,expC (-i*t))+ where t = fromRational (toRational theta)+rotQ "G(%)" theta = (expC (-i*t),0,0,expC (-i*t))+ where t = fromRational (toRational theta)+rotQ "Rz(%)" theta = (expC (-i*t/2),0,0,expC (i*t/2))+ where t = fromRational (toRational theta)+rotQ name theta = error ("quantum rotation: " ++ name ++ " not implemented")++-- | Like 'rotQ', but also conditionally invert the gate depending on+-- InverseFlag.+rotQinv :: (Floating r) => String -> InverseFlag -> Timestep -> GateR r+rotQinv name False theta = rotQ name theta+rotQinv name True theta = reverseR (rotQ name theta)++-- | Return the matrix for the 'expZt' gate.+expZtR :: (Floating r) => r -> GateR r+expZtR t = (expC (Cplx 0 (-t)),0,0,expC (Cplx 0 t))++-- | Return the matrix for the 'GPhase' gate.+gPhase :: (Floating r) => r -> GateR r+gPhase t = (expC (Cplx 0 (t * pi)),0,0,expC (Cplx 0 (t * pi)))++-- | 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 +gateC :: String -> ([Bool] -> Bool)+gateC "if" [a,b,c] = if a then b else c +gateC name inputs = error ("classical gate: " ++ name ++ ", not implemented (at least for inputs: " ++ show inputs ++ " )")++-- | The type of vectors with scalars in /n/ over the basis /a/. A+-- vector is simply a list of pairs. +data Vector n a = Vector [(a,n)]++-- | An amplitude distribution gives each classical basis state an amplitude.+type Amplitudes r = Vector (Cplx r) (Map Qubit Bool)++-- | A probability distribution gives each element a probability.+type ProbabilityDistribution r a = Vector r a++-- | 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.+type QuantumTrace r = ProbabilityDistribution r [Bool]++-- | Normalizing is used to make sure the probabilities add up to 1.+normalize :: (Floating r) => QuantumTrace r -> QuantumTrace r+normalize (Vector xs) = Vector xs'+ where+ p' = Prelude.foldr (\(_,p) accum -> accum + p) 0.0 xs+ xs' = map (\(bs,p) -> (bs,p / p')) xs ++-- | 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.+data QuantumState r = QState {+ next_wire :: Wire,+ quantum_state :: Amplitudes r,+ traces :: [QuantumTrace r], -- this will be stored in the reverse order in which+ -- the traces occured in the circuit.+ namespace :: Namespace, -- we need a namespace to keep track of subroutines+ trace_flag :: Bool -- whether or not we trace comments during the simulation+ }++-- | When we start a simulation, we need an empty starting state.+empty_quantum_state :: (Floating r) => Bool -> r -> QuantumState r+empty_quantum_state tf _ = QState { next_wire = 0, quantum_state = Vector [(Map.empty,1)], traces = [], namespace = namespace_empty, trace_flag = tf}++-- | 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.+classical_control :: Signed (B_Endpoint Qubit Bool) -> Bool+classical_control (Signed bep val) = case bep of+ (Endpoint_Bit val') -> val == val'+ (Endpoint_Qubit _) -> error "CNot: Quantum Control on Classical Gate"++-- | Map the 'classical_control' function to all the controls, and take the+-- 'and' of the result+classical_controls :: Ctrls Qubit Bool -> Bool+classical_controls cs = and (map classical_control cs)++-- | 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.+qc_control :: Map Qubit Bool -> Signed (B_Endpoint Qubit Bool) -> Bool+qc_control mqb (Signed bep val) = case bep of+ (Endpoint_Bit val') -> val == val'+ (Endpoint_Qubit q) -> val == val' where val' = mqb Map.! q++-- | Map the 'qc_control' function to all the controls (under the given basis +-- state), and take the 'and' of the result.+qc_controls :: Map Qubit Bool -> Ctrls Qubit Bool -> Bool+qc_controls mqb cs = and (map (qc_control mqb) cs)++-- | We can calculate the magnitude of a complex number+magnitude :: (Floating r) => Cplx r -> r+magnitude (Cplx a b) = sqrt (a^2 + b^2)++-- | 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.+split :: (Floating r, Eq r, Ord r) => Amplitudes r -> Qubit -> (r,Amplitudes r,Amplitudes r)+split (Vector pas) q = if p < 0 || p > 1 + then error "p < 0 or > 1" + else (p,Vector ift,Vector iff)+ where+ amp x = foldr (\(_,pa) p -> p + ((magnitude pa)*(magnitude pa))) 0 x+ apas = amp pas+ (ift,iff) = partition (\(mqb,_) -> (mqb Map.! q)) pas + p = if apas == 0 then 0 else (amp ift)/apas++-- | 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.+class (Floating r, Monad m) => PMonad r m where+ merge :: r -> a -> a -> m a++-- | We can merge two measurement outcomes, and explicitly keep the first outcome +-- as the True result, and the second as the False result.+merge_with_result :: PMonad r m => r -> a -> a -> m (Bool,a)+merge_with_result p ift iff = merge p (True,ift) (False,iff)++-- | IO forms a PMonad, where results are merged by choosing one probabilistically+-- using a random number.+instance (Floating r, Random r, Ord r) => PMonad r IO where+ merge p ift iff = do+ pp <- randomRIO (0,1)+ let res = if p > pp then ift else iff+ return res++-- | A State Monad holding a 'RandomGen' forms a 'PMonad', where results are +-- merged by choosing one probabilistically using a random number from the +-- 'RandomGen'.+instance (Floating r, Random r, Ord r, RandomGen g) => PMonad r (State g) where+ merge p ift iff = do+ gen <- get+ let (pp,gen') = randomR (0,1) gen+ put gen'+ let res = if p > pp then ift else iff+ return res++-- | Any numeric indexed vector forms a 'Monad'.+instance (Num n) => Monad (Vector n) where+ return a = Vector [(a,1)]+ (Vector ps) >>= f = Vector [(b,i*j) | (a,i) <- ps, (b,j) <- removeVector (f a)] where removeVector (Vector as) = as ++instance (Num n) => Applicative (Vector n) where+ pure = return+ (<*>) = ap++instance (Num n) => Functor (Vector n) where+ fmap = liftM++-- | We can show certain vectors, ignoring any 0 probabilities, and+-- combining equal terms.+instance (Show a,Eq a,Num n,Eq n,Show n) => Show (Vector n a) where + show (Vector ps) = show (combine (filter (\ (a,p) -> p /= 0) ps) []) + where+ combine [] as = as+ combine (x:xs) as = combine xs (combine' x as)+ combine' (a,p) [] = [(a,p)]+ combine' (a,p) ((a',p'):xs) = if a == a' then (a,p+p'):xs else (a',p'):(combine' (a,p) xs)++-- | 'ProbabilityDistribution' forms a 'PMonad' such that probabilistic results are +-- \"merged\" by extending the probability distribution by the possible results.+instance (Floating r, Eq r) => PMonad r (Vector r) where+ merge 1 ift iff = Vector [(ift,1)]+ merge 0 ift iff = Vector [(iff,1)]+ merge p ift iff = Vector [(ift,p),(iff,1-p)]++-- | The 'get_trace' function returns a probability distribution of+-- the state of a list of qubits within a given amplitude+-- distribution.+get_trace :: (Floating r) => [Qubit] -> Amplitudes r -> QuantumTrace r+get_trace qs (Vector amps) = Vector ps+ where+ ps = map (tracing qs) amps+ tracing qs (mqb,cd) = (map (\q -> mqb Map.! q) qs,(magnitude cd)*(magnitude cd)) ++-- | Add an amplitude to an amplitude distribution, combining (adding) the amplitudes for equal states in the distribution.+add :: (Floating r) => ((Map Qubit Bool),Cplx r) -> Amplitudes r -> Amplitudes r+add (a,x) (Vector axs) = Vector (add' axs)+ where add' [] = [(a,x)]+ add' ((by @ (b,y)):bys) | a == b = (b,x+y):bys+ | otherwise = by:(add' bys)++-- | The apply' function is used to apply a function on \"basis states\" to an +-- entire amplitude distribution. +apply :: (Floating r, Eq r) => (Map Qubit Bool -> Amplitudes r) -> Amplitudes r -> Amplitudes r+apply f (Vector []) = Vector []+apply f (Vector ((a,0):[])) = Vector []+apply f (Vector ((a,x):[])) = Vector (map (\(b,k) -> (b,x*k)) (fa)) where Vector fa = f a+apply f (Vector ((a,0):vas)) = apply f (Vector vas)+apply f (Vector ((a,x):vas)) = foldr add (apply f (Vector vas)) (map (\(b,k) -> (b,x*k)) (fa)) where Vector fa = f a++-- | Lift a function that returns a single basis state, to a function that+-- returns an amplitude distribution (containing a singleton).+vector :: (Floating r) => (Map Qubit Bool -> Map Qubit Bool) -> Map Qubit Bool -> Amplitudes r+vector f a = Vector [(f a,1)]++-- | apply the given function only if the controls fire.+if_controls :: (Floating r) => Ctrls Qubit Bool -> (Map Qubit Bool -> Amplitudes r) -> Map Qubit Bool -> Amplitudes r+if_controls c f mqb = if (qc_controls mqb c) then f mqb else Vector [(mqb,1)]++-- | '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.+performGateQ :: (Floating r) => GateR r -> Qubit -> Map Qubit Bool -> Amplitudes r +performGateQ (m00,m01,m10,m11) q mqb = if (mqb Map.! q) then (Vector [(Map.insert q False mqb,m01),(mqb,m11)])+ else (Vector [(mqb,m00),(Map.insert q True mqb,m10)])++-- | 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_transformer :: (PMonad r m, Ord r) => Transformer (StateT (QuantumState r) m) Qubit Bool+-- Translation of classical gates:+simulation_transformer (T_CNot ncf f) = f $+ \val c -> do+ let ctrl = classical_controls c+ let val' = if ctrl then not val else val+ return (val',c)+simulation_transformer (T_CInit val ncf f) = f $+ return val+simulation_transformer (T_CTerm b ncf f) = f $+ \val -> if val == b then return () else error "CTerm: Assertion Incorrect"+simulation_transformer (T_CDiscard f) = f $+ \val -> return ()+simulation_transformer (T_DTerm b f) = f $+ \val -> return ()+simulation_transformer (T_CGate name ncf f) = f $+ \list -> do+ let result = gateC name list+ return (result,list) +simulation_transformer g@(T_CGateInv name ncf f) = f $+ \result list -> do+ let result' = gateC name list+ if result == result' then return list else error "CGateInv: Uncomputation error"+-- Translation of quantum gates:+simulation_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls cs (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q], [], cs)+simulation_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = foldr (\q a -> apply (if_controls cs (performGateQ gate q)) a) amps qs+ put (state {quantum_state = amps'})+ return (qs, [], cs)+simulation_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> do+ let gate = gateQ "hadamard"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls cs (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q], [], cs)+simulation_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w, v] [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gate v)) amps+ let amps'' = apply (if_controls ((Signed (Endpoint_Qubit v) True):cs) (performGateQ gate w)) amps'+ let amps''' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gate v)) amps''+ put (state {quantum_state = amps'''})+ return ([w, v], [], cs)+simulation_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w, v] [] cs -> do+ let gateX = gateQ "x"+ let gateH = gateQ "hadamard"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gateX v)) amps+ let amps'' = apply (if_controls ((Signed (Endpoint_Qubit v) True):cs) (performGateQ gateH w)) amps'+ let amps''' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gateX v)) amps''+ put (state {quantum_state = amps'''})+ return ([w, v], [], cs)+simulation_transformer (T_QGate "trace" _ _ False ncf f) = f $+ \qs gc c -> do+ -- a \"trace\" gate adds the current probability distribution for the given qubits+ -- to the list of previous quantum traces+ state <- get+ let current_traces = traces state+ let amps = quantum_state state+ let new_trace = get_trace qs amps+ put (state {traces = new_trace:current_traces})+ return (qs,gc,c)+simulation_transformer (T_QGate "trace" _ _ True ncf f) = f $+ \qs gc c -> return (qs,gc,c) -- we don't do anything for the inverse \"trace\" gate+simulation_transformer (T_QGate name 1 0 inv ncf f) = f $ + \[q] [] c -> do+ let gate = gateQinv name inv+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls c (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q],[],c)+simulation_transformer (T_QRot name 1 0 inv theta ncf f) = f $ + \[q] [] c -> do+ let gate = rotQinv name inv theta+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls c (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q],[],c)+simulation_transformer (T_GPhase t ncf f) = f $+ \w c -> do+ state <- get+ let gate = rotQ "exp(% pi i)" t+ let wire = next_wire state+ let q = qubit_of_wire wire+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q False)) amps + let amps'' = apply (if_controls c (performGateQ gate q)) amps'+ let (p,ift,iff) = split amps'' q+ (val,ampsf) <- lift $ merge_with_result p ift iff + case val of+ False -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf + put (state {quantum_state = ampsf'})+ return c+ _ -> error "GPhase"+simulation_transformer (T_QInit val ncf f) = f $+ do+ state <- get+ let wire = next_wire state+ let q = qubit_of_wire wire+ let wire' = wire + 1+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + put (state {quantum_state = amps', next_wire = wire'})+ return q+simulation_transformer (T_QMeas f) = f $+ \q -> do+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ let amps'' = apply (vector (Map.delete q)) amps' + put (state {quantum_state = amps''})+ return val+simulation_transformer (T_QDiscard f) = f $+ \q -> do+ -- a discard is essentially a measurement, with the result thrown away, so we+ -- do that here, as it will reduce the size of the quantum state we are+ -- simulating over.+ state <- get+ let (p,ift,iff) = split (quantum_state state) q+ (_,amps) <- lift $ merge_with_result p ift iff+ let amps' = apply (vector (Map.delete q)) amps + put (state {quantum_state = amps'})+ return ()+simulation_transformer (T_QTerm b ncf f) = f $+ \q -> do+ -- with a real quantum computer, when we terminate a qubit with an+ -- assertion we have no way of actually checking the assertion. The+ -- best we can do is measure the qubit and then throw an error if+ -- the assertion is incorrect, which may only occur with a small+ -- probability. Here, we could split the quantum state and see if+ -- the qubit exists in the incorrect state with any non-zero+ -- probability, and throw an error. However, we don't do this+ -- because an error would sometimes be thrown due to rounding.+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ if val == b then put (state {quantum_state = amps'}) else error "QTerm: Assertion doesn't hold"+simulation_transformer (T_Comment "" inv f) = f $+ \_ -> return ()+ -- e.g. a comment can be (the) empty (string) if it only contains labels+simulation_transformer (T_Comment name inv f) = f $+ \_ -> do+ state <- get+ -- we don't need to do anything with a comment, but they can be useful+ -- to know where we are in the circuit, so we shall output a trace of+ -- the (non-empty) comments during a simulation. + trace (trace_flag state) name $ return ()+-- The remaining gates are not yet implemented:+simulation_transformer g@(T_QGate _ _ _ _ _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_QRot _ _ _ _ _ _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_CSwap _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_QPrep ncf f) = f $+ \val -> do+ state <- get+ let wire = next_wire state+ let q = qubit_of_wire wire+ let wire' = wire + 1+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + put (state {quantum_state = amps', next_wire = wire'})+ return q+simulation_transformer g@(T_QUnprep ncf f) = f $+ \q -> do+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ put (state {quantum_state = amps'})+ return val+simulation_transformer g@(T_Subroutine sub inv ncf scf ws_pat a1_pat vs_pat a2_pat rep f) = f $+ \ns in_values c -> do+ case Map.lookup sub ns of+ Just (TypedSubroutine sub_ocirc _ _ _) -> do+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ let in_bindings = bind_list in_wires in_values bindings_empty+ let sub_bcirc = (sub_circ,ns)+ out_bind <- transform_bcircuit_rec simulation_transformer sub_bcirc in_bindings+ return (unbind_list out_bind out_wires, c) + Nothing -> error $ "simulation_transformer: subroutine " ++ show sub ++ " not found (in " ++ showNames ns ++ ")"++-- | The simulation_transformer is also Dynamic, as the simulated wire states+-- can simply be used to perform dynamic liftings.+simulation_dynamic_transformer :: (PMonad r m, Ord r) => DynamicTransformer (StateT (QuantumState r) m) Qubit Bool+simulation_dynamic_transformer = DT {+ transformer = simulation_transformer,+ define_subroutine = \name subroutine -> return (),+ lifting_function = return+ } ++-- | Apply the 'simulation_dynamic_transformer' to a (unary) circuit+-- generating function.+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))+simulate_transform_unary (f :: qa -> Circ qb) input = do+ let ((), circuit) = encapsulate_dynamic (\() -> qc_init input >>= \qi -> f qi >>= \qi' -> qc_measure qi') ()+ (cb,out_bind) <- transform_dbcircuit simulation_dynamic_transformer circuit bindings_empty+ let output = qc_unbind out_bind cb+ return output++-- | 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.+qdata_concrete_shape :: (QData qa) => BType qa -> qa+qdata_concrete_shape ba = evalState mqa 0+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State Wire Qubit+ f _ = do+ w <- get+ put (w+1)+ return (qubit_of_wire w)++-- | 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_concrete_bindings :: (QData qa) => BType qa -> Bindings Qubit Bool+qdata_concrete_bindings ba = snd $ execState mqa (0,bindings_empty)+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State (Wire,Bindings Qubit Bool) ()+ f b = do+ (w,bindings) <- get+ put (w+1,bind_qubit_wire w (qubit_of_wire w) bindings)+ return () ++-- | 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_to_basis :: (QData qa) => BType qa -> Map Qubit Bool+qdata_to_basis ba = snd $ execState mqa (0,Map.empty)+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State (Wire,Map Qubit Bool) ()+ f b = do+ (w,m) <- get+ put (w+1,Map.insert (qubit_of_wire w) b m)+ return ()++-- | 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.+qdata_vector_to_amplitudes :: (QData qa, Floating r) => Vector (Cplx r) (BType qa) -> Amplitudes r+qdata_vector_to_amplitudes (Vector das) = (Vector (map (\(a,d) -> (qdata_to_basis a,d)) das))++-- | 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.+basis_to_qdata :: (QData qa) => qa -> Map Qubit Bool -> BType qa+basis_to_qdata qa m = getId $ qdata_mapM qa f qa+ where+ f :: Qubit -> Id Bool+ f q = case Map.lookup q m of+ Just res -> return res+ _ -> error "basis_to_qdata: qubit not in scope"++-- | 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.+amplitudes_to_qdata_vector :: (QData qa, Floating r) => qa -> Amplitudes r -> Vector (Cplx r) (BType qa)+amplitudes_to_qdata_vector qa (Vector das) = Vector (map (\(a,d) -> (basis_to_qdata qa a,d)) das)++-- | Apply the 'simulation_dynamic_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.+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))+simulate_amplitudes_unary f input@(Vector is) = do+ (out_shape,state) <- runStateT circ input_state+ let out_amps = quantum_state state + return (amplitudes_to_qdata_vector out_shape (apply (vector id) out_amps))+ where+ amps = qdata_vector_to_amplitudes input+ specimen = case is of+ [] -> error "simulate_amplitudes_unary: can't use empty vector"+ ((b,_):_) -> b+ shape = qdata_concrete_shape specimen+ bindings = qdata_concrete_bindings specimen+ max_wire = case wires_of_bindings bindings of+ [] -> 0+ ws -> maximum ws + input_state = (empty_quantum_state False undefined) {quantum_state = amps, next_wire = max_wire + 1}+ (_,circuit) = encapsulate_dynamic f shape+ circ = do+ (cb,out_bind) <- transform_dbcircuit simulation_dynamic_transformer circuit bindings+ let output = qc_unbind out_bind cb+ return output++-- | 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 'Data.Number.FixedPrec.FixedPrec' /e/, can be used.+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)+sim_amps gen f input_map = output_map+ where+ input_vec = Vector (Map.toList input_map)+ circ = simulate_amplitudes_unary f input_vec+ Vector output = evalState circ gen+ output_map = Map.fromList output++-- | 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 :: (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)+run_unary g r f input = evalState comp g where+ comp = evalStateT f' (empty_quantum_state False r)+ f' = simulate_transform_unary f input++-- | 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_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]+run_unary_trace g r f input = evalState comp g where+ comp = do+ state <- execStateT f' (empty_quantum_state True r)+ let qts = traces state+ return (reverse qts)+ f' = simulate_transform_unary f input++-- | Like 'run_unary', but run in the 'IO' monad instead of passing an+-- explicit source of randomness.+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))+run_unary_io r f input = do+ g <- newStdGen+ return (run_unary g r f input)++-- | Like 'run_unary_trace', 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]+run_unary_trace_io r f input = do+ g <- newStdGen+ return (run_unary_trace g r f input)++-- | Apply the 'simulation_transformer' to a (unary) circuit, and then evaluate+-- the resulting stateful computation to get a probability distribution of possible+-- results+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))+sim_unary r f input = evalStateT f' (empty_quantum_state False r)+ where f' = simulate_transform_unary f input++-- ======================================================================+-- * Generic functions++-- ** Generic run function++-- $ Generic functions to run Quipper circuits, using "Random" to+-- simulate quantum states.++-- | 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 :: (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'+run_generic gen r f = g+ where+ f1 = quncurry f+ g1 = run_unary gen r f1+ g = mcurry g1++-- | 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_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'+run_generic_trace gen r f = g + where+ f1 = quncurry f+ g1 = run_unary_trace gen r f1+ g = mcurry g1++-- | Like 'run_generic', but run in the 'IO' monad instead of passing+-- an explicit source of randomness.+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'+run_generic_io r f = g + where+ f1 = quncurry f+ g1 = run_unary_io r f1+ g = mcurry g1++-- | Like 'run_generic_trace', 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'+run_generic_trace_io r f = g + where+ f1 = quncurry f+ g1 = run_unary_trace_io r f1+ g = mcurry g1++-- ----------------------------------------------------------------------+-- ** Generic sim function++-- $ A generic function to simulate Quipper circuits, returning a+-- probability distribution of the possible results.++-- | A generic function to simulate Quipper circuits, 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.+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'+sim_generic r f = g where+ f1 = quncurry f+ g1 = sim_unary r f1+ g = mcurry g1 +
@@ -0,0 +1,502 @@+{-# LANGUAGE DoAndIfThenElse #-}++-- | A Quipper library for synthesizing Clifford+/T/ circuits directly+-- from a matrix description or Euler angle description of a unitary+-- operator. This library provides both exact and approximate+-- synthesis.++module Quipper.Libraries.Synthesis where++import Quipper++import Quipper.Internal++import Quantum.Synthesis.CliffordT+import Quantum.Synthesis.MultiQubitSynthesis+import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.Newsynth+import Quantum.Synthesis.SymReal+import Quantum.Synthesis.EulerAngles++import Quipper.Utils.Auxiliary (boollist_of_int_bh)++import Control.Monad (when)+import Data.Bits (xor, (.&.))+import System.Random+import Text.Printf++-- ----------------------------------------------------------------------+-- * Precision++-- | A type to measure precision. Precision is expressed as a number+-- /b/ of bits, i.e., binary digits, so that ε = 2[sup −/b/].+type Precision = Double++-- | Binary digits, as a unit of precision. For example, the following+-- specifies a precision of 20 binary digits:+-- +-- > prec = 20 * bits+bits :: Precision+bits = 1++-- | Decimal digits, as a unit of precision. For example, the+-- following specifies a precision of 30 decimal digits:+-- +-- > prec = 30 * digits+digits :: Precision+digits = logBase 2 10++-- ----------------------------------------------------------------------+-- * Phase++-- | A boolean flag indicating whether to respect global phases+-- during circuit synthesis ('True') or disregard them ('False').+type KeepPhase = Bool+ +-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Apply a gate (from the type 'Gate' of Clifford+/T/ operators) to+-- the given 'Qubit'.+apply_gate_at :: Gate -> Qubit -> Circ ()+apply_gate_at X q = do+ gate_X_at q+apply_gate_at Y q = do+ gate_Y_at q+apply_gate_at Z q = do+ gate_Z_at q+apply_gate_at H q = do+ hadamard_at q+apply_gate_at S q = do+ gate_S_at q+apply_gate_at T q = do+ gate_T_at q+apply_gate_at E q = do+ gate_E_at q+apply_gate_at W q = do+ gate_omega_at q++-- | Apply a gate list (from the type 'Gate' of Clifford+/T/+-- operators) to the given 'Qubit'. +-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.+apply_gates_at :: [Gate] -> Qubit -> Circ ()+apply_gates_at gates q = do+ sequence_ [ apply_gate_at g q | g <- reverse gates ]++-- | Like 'apply_gates_at', but apply the same list of gates to two+-- qubits in parallel.+apply_gates2_at :: [Gate] -> Qubit -> Qubit -> Circ ()+apply_gates2_at gates q1 q2 = do+ sequence_ [ do+ apply_gate_at g q1+ apply_gate_at g q2+ | g <- reverse gates ]++-- | Input two indices /i/ and /j/, a list of qubits /qlist/, and an+-- imperative-style single-qubit gate /U/. Apply the two-level+-- operator /U/[sub /i/,/j/] to /qlist/. Intended usage:+-- +-- > twolevel i j qlist gate_U_at+-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- This function implements an improved version of Gray codes.+twolevel :: Index -> Index -> [Qubit] -> (Qubit -> Circ ()) -> Circ ()+twolevel i j qlist body = aux l1 l2 qlist where+ n = length qlist+ l1 = boollist_of_int_bh n i+ l2 = boollist_of_int_bh n j++ aux [] [] [] = error "twolevel: i=j"+ aux (h1:t1) (h2:t2) (q:qs)+ | h1 == h2 =+ with_controls (q .==. h1) $ do+ aux t1 t2 qs+ | h1 == True =+ with_basis_change (qnot_at q) $ do+ aux2 t1 t2 q qs+ | otherwise =+ aux2 t1 t2 q qs+ aux _ _ _ = error "twolevel: internal error 1" -- not reached++ aux2 [] [] q [] =+ body q+ aux2 (h1:t1) (h2:t2) q0 (q:qs)+ | h1 == h2 =+ with_controls (q .==. h1) $ do+ aux2 t1 t2 q0 qs+ | otherwise =+ with_basis_change (qnot_at q `controlled` q0) $ do+ with_controls (q .==. h1) $ do+ aux2 t1 t2 q0 qs+ aux2 _ _ _ _ = error "twolevel: internal error 2" -- not reached++-- | Apply a /T/[sup /m/] gate. This gate is decomposed into /Z/, /S/,+-- /S/[sup †], /T/, and /T/[sup †] gates.+gate_T_power_at :: Int -> Qubit -> Circ ()+gate_T_power_at 0 q = do+ return ()+gate_T_power_at 1 q = do+ gate_T_at q+gate_T_power_at 2 q = do+ gate_S_at q+gate_T_power_at 3 q = do+ gate_S_at q+ gate_T_at q+gate_T_power_at 4 q = do+ gate_Z_at q+gate_T_power_at 5 q = do+ gate_Z_at q+ gate_T_at q+gate_T_power_at 6 q = do+ gate_S_inv_at q+gate_T_power_at 7 q = do+ gate_T_inv_at q+gate_T_power_at m q = gate_T_power_at (m `mod` 8) q++-- | Apply a 'TwoLevel' gate to the given list of qubits. +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+apply_twolevel_at :: TwoLevel -> [Qubit] -> Circ ()+apply_twolevel_at (TL_X i j) qlist =+ twolevel i j qlist $ \q -> do+ gate_X_at q+apply_twolevel_at (TL_H i j) qlist =+ twolevel i j qlist $ \q -> do+ hadamard_at q+apply_twolevel_at (TL_T m i j) qlist+ | m `mod` 8 == 0 = return ()+ | otherwise =+ twolevel i j qlist $ \q -> do+ gate_T_power_at m q+apply_twolevel_at (TL_omega m i) qlist+ | m' == 0 = do+ return ()+ | qlist == [] = do+ global_phase (fromIntegral m' * 0.25)+ | otherwise =+ apply_twolevel_at (TL_T m j i) qlist+ where+ j = if i == 0 then 1 else i .&. (i-1)+ m' = m `mod` 8++-- | Apply a list of 'TwoLevel' gates to the given list of+-- qubits. +-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.++-- Implementation note: this could be improved by combining+-- consecutive two-level gates if they share the same i and j+apply_twolevels_at :: [TwoLevel] -> [Qubit] -> Circ ()+apply_twolevels_at ops qlist =+ sequence_ [ apply_twolevel_at g qlist | g <- reverse ops ]++-- | Like 'apply_twolevel_at', but use the alternate generators for+-- two-level gates.+apply_twolevel_alt_at :: TwoLevelAlt -> [Qubit] -> Circ ()+apply_twolevel_alt_at (TL_iX j l) qlist = do+ twolevel j l qlist $ \q -> do+ gate_iX_at q+apply_twolevel_alt_at (TL_TiHT m j l) qlist = do+ twolevel j l qlist $ \q -> do+ let basischange = do+ gate_T_power_at m q+ gate_S_at q+ gate_H_at q+ gate_T_at q+ with_basis_change basischange $ do+ gate_iX_at q+apply_twolevel_alt_at (TL_W m j l) qlist + | m' == 0 = do+ return ()+ | otherwise = do+ twolevel j l qlist $ \q -> do+ gate_iX_at q+ let basischange = do+ gate_T_power_at m' q+ with_basis_change basischange $ do+ gate_iX_inv_at q+ where+ m' = m `mod` 8+apply_twolevel_alt_at (TL_omega_alt m j) qlist+ | m' == 0 = do+ return ()+ | qlist == [] = do+ global_phase (fromIntegral m' * 0.25)+ | otherwise = do+ apply_twolevel_at (TL_T m l j) qlist+ where+ l = if j == 0 then 1 else j .&. (j-1)+ m' = m `mod` 8++-- | Apply a list of 'TwoLevelAlt' gates to the given list of+-- qubits. +-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.++-- Implementation note: this could be improved by combining+-- consecutive two-level gates if they share the same i and j+apply_twolevels_alt_at :: [TwoLevelAlt] -> [Qubit] -> Circ ()+apply_twolevels_alt_at ops qlist =+ sequence_ [ apply_twolevel_alt_at g qlist | g <- reverse ops ]+++-- ----------------------------------------------------------------------+-- * Single-qubit exact synthesis++-- | Decompose the given operator exactly into a single-qubit+-- Clifford+/T/ circuit. The operator must be given in one of the+-- available exact formats, i.e., any instance of the 'ToGates' class.+-- Typical instances are:+-- +-- * 'U2' 'DOmega': a 2×2 unitary operator with entries from the ring+-- [bold D][ω] = ℤ[1\/√2, /i/];+-- +-- * 'SO3' 'DRootTwo': a 3×3 Bloch sphere operator with entries from the+-- ring ℤ[1\/√2]. In this last case, the operator will be synthesized+-- up to an unspecified global phase.+exact_synthesis1 :: (ToGates a) => a -> Qubit -> Circ Qubit+exact_synthesis1 op q = do+ comment_with_label "ENTER: exact_synthesis1" q "q"+ apply_gates_at gates q+ comment_with_label "EXIT: exact_synthesis1" q "q"+ return q+ where+ gates = convert (normalize op) :: [Gate]++-- ----------------------------------------------------------------------+-- * Multi-qubit exact synthesis++-- | Decompose the given operator exactly into a Clifford+/T/ circuit.+-- The operator must be given as an /n/×/n/-matrix with coefficients+-- in a ring that is an instance of the 'ToQOmega' class. A typical+-- example of such a ring is 'DOmega'.+-- +-- If this function is applied to a list of /m/ qubits, then we must+-- have /n/ ≤ 2[sup /m/].+-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- The generated circuit contains no ancillas, but may contain+-- multi-controlled gates whose decomposition into Clifford+/T/+-- generators requires ancillas.+exact_synthesis :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]+exact_synthesis op qlist = do+ comment_with_label "ENTER: exact_synthesis" qlist "q"+ apply_twolevels_at twolevels qlist+ comment_with_label "EXIT: exact_synthesis" qlist "q"+ return qlist+ where+ op_DOmega = matrix_map (to_dyadic . toQOmega) op+ twolevels = synthesis_nqubit op_DOmega++-- | Like 'exact_synthesis', but use the alternate algorithm from+-- Section 6 of Giles-Selinger. This means all but at most one of the+-- generated multi-controlled gates have determinant 1, which means+-- they can be further decomposed without ancillas.+exact_synthesis_alt :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]+exact_synthesis_alt op qlist = do+ comment_with_label "ENTER: exact_synthesis_alt" qlist "q"+ apply_twolevels_alt_at twolevels qlist+ comment_with_label "EXIT: exact_synthesis_alt" qlist "q"+ return qlist+ where+ op_DOmega = matrix_map (to_dyadic . toQOmega) op+ twolevels = synthesis_nqubit_alt op_DOmega++-- ----------------------------------------------------------------------+-- * Single-qubit approximate synthesis++-- ----------------------------------------------------------------------+-- ** /z/-Rotations++-- | Decompose an /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2] gate into a+-- single-qubit Clifford+/T/ circuit up to the given precision. +-- +-- \[image Rz.png]+-- +-- The parameters are:+-- +-- * a precision /b/ ≥ 0;+-- +-- * an angle θ, given as a 'SymReal' value;+-- +-- * a source of randomness /g/.+approximate_synthesis_zrot :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Circ Qubit+approximate_synthesis_zrot b theta g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_zrot (b=%.2f, theta=%s)" b (show theta)) q "q"+ q <- without_comments $ do+ exact_synthesis1 op q + comment_with_label "EXIT: approximate_synthesis_zrot" q "q"+ return q + where+ op = newsynth b theta g++-- ----------------------------------------------------------------------+-- ** Global phase gates++-- | Construct a Clifford+/T/ circuit (with no inputs and outputs)+-- that approximates a scalar global phase gate [exp /i/θ] up to the+-- given precision. The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected. (Note that if this is set to 'False', then this function+-- is just a no-op);+-- +-- * a precision /b/ ≥ 0;+-- +-- * an angle θ, given as a 'SymReal' value;+-- +-- * a source of randomness /g/.+-- +-- We use the following decomposition:+-- +-- \[image phase.png]+approximate_synthesis_phase :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Circ ()+approximate_synthesis_phase False b theta g = do+ return ()+approximate_synthesis_phase True b theta g = do+ comment (printf "ENTER: approximate_synthesis_phase (b=%.2f, theta=%s)" b (show theta))+ when (gates /= []) $ do+ q <- qinit 0+ apply_gates_at gates q+ qterm 0 q+ comment "EXIT: approximate_synthesis_phase"+ where+ op = newsynth b (-2*theta) g+ gates = convert (normalize op) :: [Gate]++-- ----------------------------------------------------------------------+-- ** /U/(2) from Euler angles++-- | Decompose the operator+-- +-- * /U/ = [exp /i/α] R[sub /z/](β) R[sub /x/](γ) R[sub /z/](δ)+-- +-- into the Clifford+/T/ gate base, up to the given precision.+-- The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected. If this is 'False', the angle α is disregarded;+-- +-- * a precision /b/ ≥ 0;+-- +-- * a tuple of Euler angles (α, β, γ, δ), given as 'SymReal' values;+-- +-- * a source of randomness /g/.+approximate_synthesis_euler :: (RandomGen g) => KeepPhase -> Precision -> (SymReal, SymReal, SymReal, SymReal) -> g -> Qubit -> Circ Qubit+approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_euler (b=%.2f, alpha=%s, beta=%s, gamma=%s, delta=%s, keepphase=%s)" b (show alpha) (show beta) (show gamma) (show delta) (show keepphase)) q "q"+ without_comments $ do+ exact_synthesis1 (op_beta * u2_H * op_gamma * u2_H * op_delta) q+ approximate_synthesis_phase keepphase b' alpha g1+ comment_with_label "EXIT: approximate_synthesis_euler" q "q"+ return q+ where+ op_beta = newsynth b' beta g2+ op_gamma = newsynth b' gamma g3+ op_delta = newsynth b' delta g4+ (g', g'') = split g+ (g1, g2) = split g'+ (g3, g4) = split g''+ b' = b + 2 -- ε' = ε / 4++-- ----------------------------------------------------------------------+-- ** /U/(2) from matrix++-- | Decompose a single-qubit unitary gate /U/ into the Clifford+/T/+-- gate base, up to the given precision, provided that det /U/ = 1.+-- The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected;+-- +-- * a precision /b/ ≥ 0;+-- +-- * a 2×2 complex matrix, with entries expressed as 'Cplx' 'SymReal' values;+-- +-- * a source of randomness /g/.+approximate_synthesis_u2 :: (RandomGen g) => KeepPhase -> Precision -> U2 (Cplx SymReal) -> g -> Qubit -> Circ Qubit+approximate_synthesis_u2 keepphase b op g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_u2 (b=%.2f, op=%s, keepphase=%s)" b (show op) (show keepphase)) q "q"+ q <- without_comments $ do+ approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q+ comment_with_label "EXIT: approximate_synthesis_u2" q "q"+ return q+ where+ (alpha, beta, gamma, delta) = euler_angles op++-- ----------------------------------------------------------------------+-- ** Controlled gates++-- | Decompose a controlled /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2] gate+-- into a single-qubit Clifford+/T/ circuit up to the given+-- precision. The parameters are as for 'approximate_synthesis_phase'.+-- The first input is the target qubit, and the second input the+-- control.+-- +-- We use the following decomposition. It has lower /T/-count than the+-- alternatives and makes good use of parallelism. Since it uses the+-- same rotation twice, only a single run of the synthesis algorithm+-- is required.+-- +-- \[image controlled-zrot.png]+approximate_synthesis_zrot_ctrl :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Qubit -> Circ Qubit+approximate_synthesis_zrot_ctrl b theta g q1 c2 = do+ comment_with_label (printf "ENTER: approximate_synthesis_zrot_ctrl (b=%.2f, theta=%s)" b (show theta)) (q1,c2) ("q", "c")+ qnot_at c2 `controlled` q1 .==. False+ apply_gates2_at gates q1 c2+ qnot_at c2 `controlled` q1 .==. False+ comment_with_label "EXIT: approximate_synthesis_zrot_ctrl" (q1,c2) ("q", "c")+ return q1+ where+ gates = convert (normalize op) :: [Gate]+ op = newsynth (b+1) (theta/2) g++-- | Decompose a controlled phase gate+-- +-- \[image controlled_phase.png]+-- +-- into the Clifford+/T/ gate base. The parameters are as for+-- 'approximate_synthesis_phase'.+-- +-- We use the following decomposition. It has lower /T/-count than the+-- alternatives and makes good use of parallelism. Since it uses the+-- same rotation twice, only a single run of the synthesis algorithm+-- is required.+-- +-- \[image controlled-phase-decomp.png]+-- +-- If the 'KeepPhase' flag is set, respect global phase; otherwise,+-- disregard it.+approximate_synthesis_phase_ctrl :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Qubit -> Circ Qubit+approximate_synthesis_phase_ctrl keepphase b theta g q1 = do+ comment_with_label (printf "ENTER: approximate_synthesis_phase_ctrl (b=%.2f, theta=%s keepphase=%s)" b (show theta) (show keepphase)) q1 "q"+ if keepphase then do+ q2 <- qinit True+ apply_gates2_at gates q2 q1+ qterm True q2+ else do+ approximate_synthesis_zrot b theta g q1+ return ()+ comment_with_label "EXIT: approximate_synthesis_phase_ctrl" q1 "q"+ return q1+ where+ gates = convert (normalize op) :: [Gate]+ op = newsynth (b+1) theta g
@@ -0,0 +1,90 @@+{-# LANGUAGE Rank2Types #-}++-- | This library provides functions for “unboxing” hierarchical circuits,+-- replacing calls to named subroutines by inlined copies of the subroutines+-- themselves.++module Quipper.Libraries.Unboxing where++import Quipper+import Quipper.Internal+import Quipper.Internal.Circuit (BoxId (..), RepeatFlag (..))+import Quipper.Internal.Monad (endpoints_of_wires_in_arity)+import Quipper.Internal.Generic (inline_subroutine, transform_unary)++-- | A transformer to peel away one level of boxing. Transforms any+-- top-level subroutine gate into its corresponding circuit.+unbox_transformer :: Transformer Circ Qubit Bit+unbox_transformer (T_Subroutine name inv ncf _ _ _ ws2 a2 (RepeatFlag reps) f) = f $+ \namespace ws c -> do+ outputs <- loopM reps ws+ ((without_controls_if ncf) .+ (with_controls c) .+ ((if inv then flip reverse_generic (endpoints_of_wires_in_arity a2 ws2) else id)+ (inline_subroutine name namespace)))+ return (outputs, c)+unbox_transformer x = identity_transformer x++-- | Peel away one level of boxing from a circuit. Transforms any+-- top-level subroutine gate into its corresponding circuit.+unbox_unary :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+unbox_unary circ = transform_unary unbox_transformer circ ++-- | Peel away one level of boxing from a circuit. Transforms any+-- top-level subroutine gate into its corresponding circuit.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox :: (QCData x) => Circ x -> Circ x+-- > unbox :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+-- > unbox :: (QCData x, QCData y, QCData z) => (x -> y -> Circ z) -> (x -> y -> Circ z)+-- +-- and so forth.+unbox :: (QCData x, QCData y, QCurry qfun x y) => qfun -> qfun+unbox = qcurry . unbox_unary . quncurry++-- | A transformer to recursively unbox some specified class of boxed subroutines.+unbox_recursive_filtered_transformer :: (BoxId -> Bool) -> Transformer Circ Qubit Bit+unbox_recursive_filtered_transformer p b@(T_Subroutine boxid inv ncf _ _ _ ws2 a2 (RepeatFlag reps) f) = + if not (p boxid)+ then identity_transformer b+ else f $+ \namespace ws c -> do+ outputs <- loopM reps ws+ ((without_controls_if ncf) .+ (with_controls c) .+ ((if inv then flip reverse_generic (endpoints_of_wires_in_arity a2 ws2) else id) $+ (unbox_recursive_filtered p) $+ (inline_subroutine boxid namespace)))+ return (outputs, c)+unbox_recursive_filtered_transformer _ x = identity_transformer x++-- | Recursively unbox all subroutines satisfying a given predicate.+unbox_recursive_filtered_unary :: (QCData x, QCData y) => (BoxId -> Bool) -> (x -> Circ y) -> (x -> Circ y)+unbox_recursive_filtered_unary p = transform_unary (unbox_recursive_filtered_transformer p)++-- | Recursively unbox all subroutines satisfying a given predicate.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox_recursive_filtered :: (QCData x) => (BoxId -> Bool) -> Circ x -> Circ x+-- > unbox_recursive_filtered :: (QCData x, QCData y) => (BoxId -> Bool) -> (x -> Circ y) -> (x -> Circ y)+-- +-- and so forth.+unbox_recursive_filtered :: (QCData x, QCData y, QCurry qfun x y) => (BoxId -> Bool) -> qfun -> qfun+unbox_recursive_filtered p = qcurry . (unbox_recursive_filtered_unary p) . quncurry++-- | Recursively unbox all subroutines of a circuit.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox_recursive :: (QCData x) => Circ x -> Circ x+-- > unbox_recursive :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+-- > unbox_recursive :: (QCData x, QCData y, QCData z) => (x -> y -> Circ z) -> (x -> y -> Circ z)+-- +-- and so forth.+unbox_recursive :: (QCData x, QCData y, QCurry qfun x y) => qfun -> qfun+unbox_recursive = unbox_recursive_filtered (const True)
@@ -0,0 +1,16 @@+{-# LANGUAGE CPP #-}++import Distribution.Simple+import Distribution.Superdoc+import Quipper.Distribution.Preprocessor++-- The following is needed because of an incompatible change in Cabal 2.+#if MIN_VERSION_Cabal(2,0,0)+wrap x = \_ _ _ -> x+#else+wrap x = \_ _ -> x+#endif++main = defaultMainWithHooks superdocHooks {+ hookedPreProcessors = [("hs", wrap ppQuipper)]+ }
@@ -0,0 +1,1760 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Arith.hs" #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}++{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This library provides a type of quantum integers, as well as+-- basic arithmetic functions on them.+-- +-- The type 'QDInt' holds a fixed-size, ℓ-qubit quantum integer,+-- considered modulo 2[sup ℓ]. The integers may be regarded as+-- signed or unsigned, depending on the operation. If signs are used,+-- they are assumed to be in two's complement.+-- +-- Some of the arithmetic operations are adapted from the GFI for the+-- Triangle Finding algorithm. Most algorithms used are, for now, very+-- naïve (ripple adders, etc). Gate count estimates are given in the+-- Toffoli gatebase.++-- Documentation note: the table of contents is organized slightly+-- differently than the source code. The guiding idea is that the+-- documentation should expose the interface that is accessible to+-- user-level code, and changeable implementation details are hidden.++module Quipper.Libraries.Arith (+ -- * Quantum integers+ -- ** Data type definitions+ -- $DATATYPES+ XInt, -- constructors not exported+ QDInt,+ CInt,+ IntM,+ -- ** Operations on QDInt+ qulist_of_qdint_bh,+ qdint_of_qulist_bh,+ qulist_of_qdint_lh,+ qdint_of_qulist_lh,+ qdint_length,+ qdint_extend_unsigned,+ qdint_extend_signed,+ -- ** Operations on CInt+ bitlist_of_cint_bh,+ cint_of_bitlist_bh,+ bitlist_of_cint_lh,+ cint_of_bitlist_lh,+ cint_length,+ cint_extend_unsigned,+ cint_extend_signed,+ -- ** Operations on IntM+ -- $INTMCLASSES+ boollist_of_intm_bh,+ intm_of_boollist_bh,+ intm_length,+ integer_of_intm_unsigned,+ integer_of_intm_signed,+ intm_with_length,+ intm_of_integer,+ intm,+ intm_promote,+ intm_interval_signed,+ intm_interval_unsigned,+ intm_extend_unsigned,+ intm_extend_signed,+ -- ** Shape parameters+ qdint_shape,+ cint_shape,+ -- ** Operations on XInt+ xint_maybe_length,+ list_of_xint_bh,+ xint_of_list_bh,+ list_of_xint_lh,+ xint_of_list_lh,+ -- * Quantum arithmetic operations+ -- ** The QNum type class+ QNum(..),+ -- ** In-place increment and decrement+ q_increment,+ q_decrement,+ -- ** In-place addition and subtraction+ q_add_in_place,+ q_sub_in_place,+ q_negate_in_place,+ -- ** Arithmetic with classical parameter+ q_add_param,+ q_sub_param,+ q_add_param_in_place,+ q_sub_param_in_place,+ q_mult_param,+ -- ** Comparison+ q_le_unsigned,+ q_le_signed,+ q_lt_signed,+ q_negative,+ -- ** Division and remainder+ q_moddiv_unsigned_in_place,+ q_mod_unsigned,+ q_divrem_unsigned,+ q_div_unsigned,+ q_div,+ q_quot,+ q_div_exact_unsigned,+ q_div_exact,+ -- ** Specialized functions+ q_ext_euclid,+ -- * Lifting of arithmetic functions+ -- $LIFTING+ template_symb_plus_,+ ) where++import Quipper+import Quipper.Internal++import Quipper.Utils.Sampling+import Quipper.Utils.Auxiliary++import Control.Monad+import Data.Typeable++-- ======================================================================+-- * Quantum integers++-- ** Data type definitions++-- $DATATYPES +-- We define three versions of the fixed-length integer type: quantum,+-- classical input, and classical parameter. The triple ('IntM',+-- 'QDInt', 'CInt') forms an instance of the 'QShape' class. All three+-- types are special cases of the type 'XInt' /x/.++-- | 'XInt' /x/ is the type of fixed-length integers, but using+-- elements of type /x/ instead of bits. It is an abstract type, and+-- details of its implementation is not exposed to user-level code.++-- Implemenation notes: The integer types are currently implemented as+-- big-headian bit lists. However, the details of the implementation+-- are not exposed to user-level code, and are subject to change.+-- +-- The form ('XInt_indet' /n/ /id/) is permitted as a special case, to+-- represent an integer of indeterminate length. Such a value can only+-- be used when the length is deducible from the context. This form is+-- only allowed in the special case /x/ = 'Bool', and we use an+-- identity type to enforce this.+-- +-- Integers of indeterminate length may only be used in certain+-- operations where the shape information is available from other+-- data. It can be used, for example, for terminating or controlling a+-- 'QDInt', but not for initializing a 'QDInt'.+data XInt x = XInt [x] | XInt_indet Integer (Identity Bool x)+ deriving (Show, Typeable)++-- | The type of fixed-length /m/-qubit quantum integers. This is a+-- circuit execution time value.+type QDInt = XInt Qubit++instance Show QDInt where+ show (XInt l) = "#" ++ show l+ show (XInt_indet n id) = error "IntM: internal error"++-- A better implementation might be something like:+-- show (XInt l) = "qdint_of_qulist_bh " ++ show l+-- However, as 'CInt' and 'QDInt' are currently synonyms, it seems best+-- to keep the instance agnostic between them, while still distinguishing+-- them somehow from naked lists.++-- | The type of fixed-length /m/-bit classical integer inputs. This+-- is a circuit execution time value.+type CInt = XInt Bit++-- Currently, 'CInt' is literally a synonym of 'QDInt', since 'Qubit' is+-- a synonym of 'Bit'. If this changes, the following instance will be+-- required:+--+-- instance Show CInt where+-- show (XInt l) = show l+-- show (XInt_indet n id) = error "IntM: internal error"++-- | The type of fixed-length /m/-bit integer parameters. Values of+-- this type are parameters, i.e., they are classical and known at+-- circuit generation time.+-- +-- Unlike values of type 'QDInt' and 'CInt', a value of type 'IntM'+-- may have an indeterminate length. This happens, for example, if the+-- value is specified by means of an integer literal (e.g., 17), which+-- does not carry length information. In such cases, the value can+-- only be used when it can be deduced from the context. For example,+-- such values may be used for terminating or controlling a 'QDInt',+-- but not for initializing a 'QDInt'.+type IntM = XInt Bool++instance Show IntM where+ show (XInt l) = "intm " ++ show (length l) ++ " " ++ show (int_of_boollist_signed_bh l)+ show (XInt_indet n id) = show n++-- Note: for the purpose of forming intervals, we regard 'IntM' as an+-- /unsigned/ type. This disagrees with the 'Enum' instance, where+-- 'IntM' is regarded as a /signed/ type. So for example, when /x/ =+-- 'intm' 4 (-2) = 'intm' 4 14 and /y/ = 'intm' 4 10, then [/x/../y/]+-- and 'interval' /y/ /x/ are non-empty, but [/y/../x/] and 'interval'+-- /x/ /y/ are empty. +-- +-- This is confusing but for the time being there is no better+-- alternative, unless we create distinct signed and unsigned types.++instance Interval IntM where+ interval x y = intm_interval_unsigned x y++instance Zero IntM where+ zero x = intm_with_length (intm_length x) 0+ +-- ----------------------------------------------------------------------+-- ** Primitive combinators on XInt++-- $ 'XInt' is intended to be an abstract data type, and all access to+-- it should pass through the three access functions of this+-- section.++-- *** Constructors++-- | Create a 'XInt' /x/ from a list of /x/s. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+xint_of_list_bh :: [x] -> XInt x+xint_of_list_bh xs = XInt xs++-- | Create an 'IntM' of indeterminate length from an 'Integer'. +intm_of_integer :: Integer -> IntM+intm_of_integer n = XInt_indet n reflexivity++-- *** Destructor++-- | If the 'XInt' is of determinate length, return its list of digits+-- as a big-headian list, i.e., the head of the list holds the most+-- significant digit. If the 'XInt' is of indeterminate length, return+-- (/n/, /id/), where /n/ is the underlying 'Integer' and /id/ is the+-- witness proving that /x/ = 'Bool'.+-- +-- This is a lowest-level access function not intended to be used by+-- user-level code.+xint_case :: XInt x -> Either [x] (Integer, Identity Bool x)+xint_case (XInt xs) = Left xs+xint_case (XInt_indet n id) = Right (n, id)++-- ----------------------------------------------------------------------+-- ** Other low-level operations++-- $ The operations in this section are the only ones intended to use+-- 'xint_case' directly.++-- | Set the length of an 'XInt' to /m/ ≥ 0. This operation is only+-- legal if the input (a) has indeterminate length or (b) has+-- determinate length already equal to /m/. In particular, it cannot+-- be used to change the length from anything other than from+-- indeterminate to determinate.+-- +-- If both arguments already have determinate lengths, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.+-- +-- Note that if /x/ ≠ 'Bool', the input is guaranteed to have+-- determinate length. However, we cannot test for equality of types+-- in a polymorphic function. This is where the 'id' argument to+-- 'XInt_indet' is used.+xint_set_length :: Int -> XInt x -> String -> XInt x+xint_set_length m x errmsg | m < 0 = + error "xint_set_length: negative length not permitted" +xint_set_length m x errmsg =+ case xint_case x of+ Left xs | m == length xs -> x+ | otherwise -> error errmsg+ Right (n, id) -> XInt xs where+ xs = [ identity id b | b <- boollist_of_int_bh m n ]++-- | Return 'True' if the 'XInt' is of determinate length, and 'False'+-- if it is of indeterminate length. +xint_is_determinate :: XInt x -> Bool+xint_is_determinate x = + case xint_case x of+ Left _ -> True+ Right _ -> False++-- | From a 'XInt', which must be of determinate length, extract a+-- list of /x/s. The conversion is big-headian, i.e., the head of the+-- list holds the most significant digit. It is an error to call this+-- function with an 'XInt' of indeterminate length.+list_of_xint_bh :: XInt x -> [x]+list_of_xint_bh x = + case xint_case x of+ Left xs -> xs+ Right _ -> error "list_of_xint_bh: integer has indeterminate length"++-- | Return the size of a 'XInt', or 'Nothing' if indeterminate.+xint_maybe_length :: XInt x -> Maybe Int+xint_maybe_length x =+ case xint_case x of+ Left xs -> Just (length xs)+ Right _ -> Nothing++-- | Convert an 'IntM' of length /m/ to an 'Integer' in the range {0,+-- …, 2[sup /m/]-1}. If the 'IntM' has indeterminate length, return the+-- original 'Integer'.+integer_of_intm_unsigned :: IntM -> Integer+integer_of_intm_unsigned x = + case xint_case x of+ Left xs -> int_of_boollist_unsigned_bh xs+ Right (n, id) -> n++-- | Convert an 'IntM' of length /m/ to an 'Integer' in the range+-- {-2[sup /m/-1], …, 2[sup /m/-1]-1}. If the 'IntM' has indeterminate+-- length, return the original 'Integer'.+integer_of_intm_signed :: IntM -> Integer+integer_of_intm_signed x =+ case xint_case x of+ Left xs -> int_of_boollist_signed_bh xs+ Right (n, id) -> n++-- | Equality test. If at least one argument has determinate length,+-- test equality modulo 2[sup /m/]. If both have indeterminate length,+-- check equality of the underlying integers.+xint_equals :: (Eq x) => XInt x -> XInt x -> Bool +xint_equals x y =+ case (xint_case x, xint_case y) of+ (Left xs, Left ys) + | length xs == length ys -> xs == ys+ | otherwise -> error "Equality test on XInt: operands must be of equal length"+ (_, Left ys) -> xint_equals (xint_set_length m x "xint_equals") y+ where m = length ys+ (Left xs, _) -> xint_equals x (xint_set_length m y "xint_equals")+ where m = length xs+ (Right (n, _), Right (n', _)) -> n == n'+ +-- ----------------------------------------------------------------------+-- ** Derived operations on XInt++-- | Get the nominal length of an integer (in bits). It is an error to+-- apply this function to an integer of indeterminate length.+xint_length :: XInt x -> Int+xint_length x = + case xint_maybe_length x of+ Just m -> m+ Nothing -> error "xint_length: integer has indeterminate length"++-- | Convert an integer to a bit list. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+list_of_xint_lh :: XInt x -> [x]+list_of_xint_lh = reverse . list_of_xint_bh++-- | Convert a bit list to an integer. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+xint_of_list_lh :: [x] -> XInt x+xint_of_list_lh = xint_of_list_bh . reverse++-- | Extend a 'XInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to /zero/. It is an error to call this+-- function when the new length is shorter than the old one.+xint_extend_unsigned :: (Monad m) => Int -> m x -> XInt x -> m (XInt x)+xint_extend_unsigned len zero x + | len < m =+ error "pad_xint: requested length is shorter than current length"+ | otherwise = do+ pad <- sequence (replicate extra zero)+ return $ xint_of_list_bh (pad ++ digits)+ where+ digits = list_of_xint_bh x+ m = length digits+ extra = len - m++-- | Extend a 'XInt' to the given length without changing its (signed)+-- value. This is done by adding the required number of high bits+-- initialized to copies of the sign bit (or to /zero/ if the original+-- integer was of length 0). It is an error to call this function when+-- the new length is shorter than the old one.+xint_extend_signed :: (Monad m) => Int -> m x -> (x -> m x) -> XInt x -> m (XInt x)+xint_extend_signed len zero copy x+ | len < m + = error "pad_xint: requested length is shorter than current length"+ | m == 0+ = xint_extend_unsigned len zero x+ | otherwise = do+ pad <- sequence (replicate extra (copy sign))+ return $ xint_of_list_bh (pad ++ digits)+ where+ digits = list_of_xint_bh x+ m = length digits+ extra = len - m+ sign = head digits++-- ----------------------------------------------------------------------+-- ** Operations on IntM++-- | Return the size of an 'IntM', or 'Nothing' if indeterminate.+intm_length :: IntM -> Maybe Int+intm_length = xint_maybe_length+ +-- | Convert an 'IntM' to a list of booleans. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit. As usual, 'False' is 0 and 'True' is 1. It is an error to+-- apply this operation to an 'IntM' whose length is indeterminate.+boollist_of_intm_bh :: IntM -> [Bool]+boollist_of_intm_bh = list_of_xint_bh++-- | Convert a boolean list to an 'IntM'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+intm_of_boollist_bh :: [Bool] -> IntM+intm_of_boollist_bh = xint_of_list_bh++-- | Create an 'IntM' of the specified length (first argument) and+-- value (second argument).+intm :: Int -> Integer -> IntM+intm m n = intm_set_length m (intm_of_integer n) "intm: internal error"++-- | Set the length of an 'IntM' to /m/ ≥ 0. This operation is only+-- legal if the input (a) has indeterminate length or (b) has+-- determinate length already equal to /m/. In particular, it cannot+-- be used to change the length from anything other than from+-- indeterminate to determinate. (Use 'intm_extend_unsigned' or+-- 'intm_extend_signed' to increase a determinate length).+-- +-- If both arguments already have determinate lengths, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.+intm_set_length :: Int -> IntM -> String -> IntM+intm_set_length = xint_set_length++-- | Extend an 'IntM' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+intm_extend_unsigned :: Int -> IntM -> IntM+intm_extend_unsigned len x =+ getId $ xint_extend_unsigned len (return False) x++-- | Extend an 'IntM' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+intm_extend_signed :: Int -> IntM -> IntM+intm_extend_signed len x =+ getId $ xint_extend_signed len (return False) (return) x++-- ----------------------------------------------------------------------+-- ** Operations on CInt++-- | Convert a 'CInt' to a list of qubits. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+bitlist_of_cint_bh :: CInt -> [Bit]+bitlist_of_cint_bh = list_of_xint_bh++-- | Convert a list of qubits to a 'CInt'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+cint_of_bitlist_bh :: [Bit] -> CInt+cint_of_bitlist_bh = xint_of_list_bh++-- | Convert a 'CInt' to a list of bits. The conversion is+-- little-headian, i.e., the head of the list holds the least+-- significant digit.+bitlist_of_cint_lh :: CInt -> [Bit]+bitlist_of_cint_lh = list_of_xint_lh++-- | Convert a list of bits to a 'CInt'. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+cint_of_bitlist_lh :: [Bit] -> CInt+cint_of_bitlist_lh = xint_of_list_lh++-- | Return the length of a 'CInt', in bits.+cint_length :: CInt -> Int+cint_length = xint_length++-- | Extend a 'CInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+cint_extend_unsigned :: Int -> CInt -> Circ CInt+cint_extend_unsigned len x =+ xint_extend_unsigned len (cinit False) x++-- | Extend a 'CInt' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+cint_extend_signed :: Int -> CInt -> Circ CInt+cint_extend_signed len x =+ xint_extend_signed len (cinit False) (qc_copy) x++-- ----------------------------------------------------------------------+-- ** Operations on QDInt++-- | Convert a 'QDInt' to a list of qubits. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+qulist_of_qdint_bh :: QDInt -> [Qubit]+qulist_of_qdint_bh = list_of_xint_bh++-- | Convert a list of qubits to a 'QDInt'. The conversion is+-- big-headian, i.e., the head of the list holds the most significant+-- digit.+qdint_of_qulist_bh :: [Qubit] -> QDInt+qdint_of_qulist_bh = xint_of_list_bh++-- | Convert a 'QDInt' to a list of qubits. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+qulist_of_qdint_lh :: QDInt -> [Qubit]+qulist_of_qdint_lh = list_of_xint_lh++-- | Convert a list of qubits to a 'QDInt'. The conversion is+-- little-headian, i.e., the head of the list holds the least significant+-- digit.+qdint_of_qulist_lh :: [Qubit] -> QDInt+qdint_of_qulist_lh = xint_of_list_lh++-- | Return the length of a 'QDInt', in bits.+qdint_length :: QDInt -> Int+qdint_length = xint_length++-- | Extend a 'QDInt' to the given length without changing its+-- (unsigned) value. This is done by adding the required number of+-- high bits initialized to 0. It is an error to call this function+-- when the new length is shorter than the old one.+qdint_extend_unsigned :: Int -> QDInt -> Circ QDInt+qdint_extend_unsigned len x =+ xint_extend_unsigned len (qinit False) x++-- | Extend a 'QDInt' to the given length without changing its+-- (signed) value. This is done by adding the required number of+-- high bits initialized to copies of the sign bit. It is an error to+-- call this function when the new length is shorter than the old one.+qdint_extend_signed :: Int -> QDInt -> Circ QDInt+qdint_extend_signed len x =+ xint_extend_signed len (qinit False) (qc_copy) x++-- ----------------------------------------------------------------------+-- ** Shape parameters++-- | Return a piece of shape data to represent an /m/-qubit quantum+-- integer. Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+qdint_shape :: Int -> QDInt+qdint_shape m = xint_of_list_bh (replicate m qubit)++-- | Return a piece of shape data to represent an /m/-bit 'CInt'.+-- Please note that the data can only be used as shape; it will be+-- undefined at the leaves.+cint_shape :: Int -> CInt+cint_shape m = xint_of_list_bh (replicate m bit)++-- ======================================================================+-- ** Type class instances++-- Note: instance declarations do not show up in the documentation++-- | Input the lengths of two inputs to a binary operation, and return+-- the common length. If one length is indeterminate, return the other;+-- if both are indeterminate, return 'Nothing'; if both are determinate but+-- not equal, throw an error with the given error message.+combine_length :: String -> Maybe Int -> Maybe Int -> Maybe Int+combine_length s Nothing m = m+combine_length s m Nothing = m+combine_length s (Just m) (Just m') | m == m' = Just m+ | otherwise = error s++-- | Try to set the length of an 'IntM' to that of another 'XInt'+-- value (which could be a 'QDInt', a 'CInt', or another 'IntM'). This+-- will fail with an error if both numbers already have determinate+-- lengths that don't coincide. In this case, the string argument is+-- used as an error message.+intm_promote :: IntM -> XInt x -> String -> IntM+intm_promote bi xi errmsg = + case xint_maybe_length xi of+ Nothing -> bi+ Just m -> intm_set_length m bi errmsg+ +type instance QCType x y (XInt z) = XInt (QCType x y z)+type instance QTypeB IntM = QDInt++instance QCLeaf x => QCData (XInt x) where+ qcdata_mapM shape f g xs = + mmap xint_of_list_bh $ qcdata_mapM (list_of_xint_bh shape) f g (list_of_xint_bh xs)+ qcdata_zip shape q c q' c' xs ys e = + xint_of_list_bh $ qcdata_zip (list_of_xint_bh shape) q c q' c' (list_of_xint_bh xs) (list_of_xint_bh ys) (const $ e "XInt length mismatch")+ qcdata_promote b q e = intm_promote b q (e "IntM length mismatch")++-- Labeling of QDInt is s[m-1], ..., s[0], with the least significant+-- bit at index 0.+instance QCLeaf x => Labelable (XInt x) String where+ label_rec qa = label_rec (list_of_xint_lh qa)++instance CircLiftingUnpack (Circ QDInt) (Circ QDInt) where+ pack x = x+ unpack x = x++-- ======================================================================+-- * Classical arithmetic on 'IntM'++-- ----------------------------------------------------------------------+-- ** Auxiliary functions+ +-- | A useful auxiliary function that converts a list of 'IntM's to a+-- list of 'Integer's, while also returning their common length. All+-- arguments whose length is not indeterminate must have the same common+-- length. If the lengths don't match, throw an error with the error+-- message specified by the 'String' argument.+integers_of_intms_signed :: [IntM] -> String -> (Maybe Int, [Integer])+integers_of_intms_signed xs s = (m, is) where+ m = foldl (combine_length s) Nothing [ intm_length x | x <- xs ]+ is = [ integer_of_intm_signed x | x <- xs ] ++-- | Like 'integers_of_intms_signed', but regards the 'IntM's as+-- unsigned integers.+integers_of_intms_unsigned :: [IntM] -> String -> (Maybe Int, [Integer])+integers_of_intms_unsigned xs s = (m, is) where+ m = foldl (combine_length s) Nothing [ intm_length x | x <- xs ]+ is = [ integer_of_intm_unsigned x | x <- xs ] ++-- | Create an 'IntM' of the given length and value. Leave the length+-- indeterminate if it is given as 'Nothing'.+intm_with_length :: Maybe Int -> Integer -> IntM+intm_with_length (Just m) n = intm m n+intm_with_length Nothing n = intm_of_integer n++-- | Auxiliary function for lifting a binary operator from 'Integer'+-- to 'IntM'. The string argument is the name of the operator, for+-- error messages.+intm_binop :: (Integer -> Integer -> Integer) -> String -> IntM -> IntM -> IntM +intm_binop op opname x y = intm_with_length m (op x' y') where+ (m, [x',y']) = integers_of_intms_signed [x, y] ("Binary operation " ++ opname ++ " on IntM: operands must be of equal length")++-- | Auxiliary function for lifting a unary operator from 'Integer' to+-- 'IntM'.+intm_unop :: (Integer -> Integer) -> IntM -> IntM+intm_unop op x = intm_with_length xm (op x') where+ xm = intm_length x+ x' = integer_of_intm_signed x+ +-- ----------------------------------------------------------------------+-- ** Type class instances++-- $INTMCLASSES +-- +-- 'IntM' is an instance of Haskell's 'Eq', 'Num', 'Ord', 'Real',+-- 'Enum', and 'Integral' type classes. This means that integer+-- literals (e.g., 17), and the usual arithmetic functions, such as+-- '+', '-', '*', 'abs', 'succ', 'pred', 'mod', 'div', and others, can+-- be used for values of type 'IntM'. In general, we treat 'IntM' as a+-- signed integer type. Use 'fromIntegral' to convert an integer to an+-- 'IntM' of indeterminate length.+-- +-- The general convention for binary operations (such as+-- multiplication) is: both operands must have the same length,+-- except: if one operand has indeterminate length, it takes on the+-- length of the other; if both operands have indeterminate length, the+-- result will have indeterminate length.++instance Eq x => Eq (XInt x) where+ x == y = xint_equals x y+ +instance Num IntM where+ (+) = intm_binop (+) "+"+ (*) = intm_binop (*) "*"+ (-) = intm_binop (-) "-"+ abs = intm_unop abs+ signum = intm_unop signum+ fromInteger = intm_of_integer++instance Ord IntM where+ compare x y = compare (toInteger x) (toInteger y)++instance Real IntM where+ toRational = toRational . integer_of_intm_signed++instance Enum IntM where+ succ = intm_unop succ+ pred = intm_unop pred+ toEnum = intm_of_integer . fromIntegral+ fromEnum = fromIntegral . integer_of_intm_signed+ enumFrom x = map (intm_with_length m) [x'..] where+ (m, [x']) = integers_of_intms_signed [x] "enumeration: IntM"+ enumFromThen x y = map (intm_with_length m) [x',y'..] where+ (m, [x',y']) = integers_of_intms_signed [x,y] "enumeration: IntM operands must be of equal length"+ enumFromTo x y = map (intm_with_length m) [x'..y'] where+ (m, [x',y']) = integers_of_intms_signed [x,y] "enumeration: IntM operands must be of equal length"+ enumFromThenTo x y z = map (intm_with_length m) [x',y'..z'] where+ (m, [x',y',z']) = integers_of_intms_signed [x,y,z] "enumeration: IntM operands must be of equal length"++-- | Return the interval @[/x/../y/]@, with /x/ and /y/ regarded as+-- signed values of type 'IntM'.+intm_interval_signed :: IntM -> IntM -> [IntM]+intm_interval_signed x y = [x..y] -- this comes from the 'Enum' instance defined above++-- | Return the interval @[/x/../y/]@, with /x/ and /y/ regarded as+-- unsigned values of type 'IntM'.+intm_interval_unsigned :: IntM -> IntM -> [IntM]+intm_interval_unsigned x y = map (intm_with_length m) [x'..y'] where+ (m, [x',y']) = integers_of_intms_unsigned [x,y] "intm_interval: operands must be of equal length"++instance Integral IntM where+ toInteger = integer_of_intm_signed+ quotRem x y = (intm_with_length m q', intm_with_length m r') where+ (m, [x',y']) = integers_of_intms_signed [x, y] "Division on IntM: operands must be of equal length"+ (q',r') = quotRem x' y'++-- ======================================================================+-- * Quantum arithmetic on 'QDInt'++-- Developer note: each quantum arithmetic function is implemented by+-- an underlying low-level function operating on qubit lists. However,+-- these lower-level functions are *not* exported. If any module needs+-- them, it should define them explicitly using 'qulist_of_qdint_bh'+-- and 'qdint_of_qulist_bh'. In particular, the low-level functions+-- may not perform error checking if it is already performed by their+-- high-level counterparts.++-- ----------------------------------------------------------------------+-- ** Auxiliary functions++-- | @'common_value' error_str [n1,n2,n3]@: if /n1/, /n2/, /n3/ all equal, return this value; else throw an error. Useful for checking that sizes of 'QDInt's match.+common_value :: (Eq a) => String -> [a] -> a+common_value _ [] = error "common_value: no inputs given"+common_value _ [n] = n+common_value error_str (n:ns) = if common_value error_str ns == n then n else error error_str++-- | @'common_length' error_str [x1,x2,x3]@: if /x1/, /x2/, /x3/ all have the same length, return this value; else throw an error. +common_length :: String -> [QDInt] -> Int+common_length error_str xs = common_value error_str $ map qdint_length xs++-- ----------------------------------------------------------------------+-- ** Incrementing and decrementing++-- | Increment a 'QDInt' in place. /O/(ℓ) gates. +-- +-- Implementation note: currently tries to minimize gate count, at the cost of a rather long Quipper description. Can the latter be reduced without increasing the former? +q_increment :: QDInt -> Circ QDInt+q_increment = mmap xint_of_list_bh . q_increment_qulist . list_of_xint_bh++-- | Low-level implementation of 'q_increment': represents integers as+-- big-headian qubit lists.+q_increment_qulist :: [Qubit] -> Circ [Qubit]+q_increment_qulist [] = return []+q_increment_qulist [x_0] = do x_0 <- qnot x_0; return [x_0]+q_increment_qulist [x_1,x_0] = do x_0 <- qnot x_0; x_1 <- qnot x_1 `controlled` (x_0 .==. 0); return [x_1,x_0]+q_increment_qulist [x_2,x_1,x_0] = do+ x_0 <- qnot x_0+ x_1 <- qnot x_1 `controlled` (x_0 .==. 0)+ x_2 <- qnot x_2 `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ return [x_2,x_1,x_0]+q_increment_qulist x_bits = do+ let x_0 = last x_bits+ x_1 = last $ init x_bits+ x_higher = init $ init $ x_bits+ x_0 <- qnot x_0+ x_1 <- qnot x_1 `controlled` (x_0 .==. 0)+ (x_higher,x_1,x_0) <- with_ancilla (\c -> do+ c <- qnot c `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ (c,rev_x_higher) <- q_increment_qulist_aux c (reverse x_higher)+ c <- qnot c `controlled` (x_0 .==. 0) .&&. (x_1 .==. 0)+ return (reverse rev_x_higher, x_1, x_0))+ return (x_higher ++ [x_1,x_0])+ where+ -- increment a LITTLE-endian bit-string, controlled by a single qubit.+ q_increment_qulist_aux :: Qubit -> [Qubit] -> Circ (Qubit,[Qubit])+ q_increment_qulist_aux b [] = return (b,[])+ q_increment_qulist_aux b [x_0] =+ do x_0 <- qnot x_0 `controlled` b; return (b,[x_0])+ q_increment_qulist_aux b [x_0,x_1] = do+ x_0 <- qnot x_0 `controlled` b+ x_1 <- qnot x_1 `controlled` b .&&. (x_0 .==. 0)+ return (b,[x_0,x_1])+ q_increment_qulist_aux b (x_0:x_higher) = do+ x_0 <- qnot x_0 `controlled` b + (b,x_0,x_higher) <- with_ancilla (\c -> do+ c <- qnot c `controlled` b .&&. (x_0 .==. 0)+ (c,x_higher) <- q_increment_qulist_aux c x_higher+ c <- qnot c `controlled` b .&&. (x_0 .==. 0)+ return (b,x_0,x_higher))+ return (b, (x_0:x_higher))+ +-- | Decrement a 'QDInt' in place. Inverse of 'q_increment'. /O/(ℓ).+q_decrement :: QDInt -> Circ QDInt+q_decrement = reverse_generic_endo q_increment++-- ----------------------------------------------------------------------+-- ** Addition and subtraction++-- | Add two 'QDInt's into a fresh one. Arguments are assumed to be of equal size. /O/(ℓ) gates, both before and after transformation to Toffoli.+q_add_qdint :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_add_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_add_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_add_qdint': represents integers as+-- big-headian qubit lists.+q_add_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_add_qulist x y = do+ let l = length x+ when (l /= length y) $ do+ error "q_add_qdint: cannot add QDInts of different lengths"++ ((x,y),s_out) <- with_computed_fun (x,y)+ (\(x,y) -> do+ s <- qinit (replicate l False) -- holds the eventual sum+ c <- qinit (replicate l False) -- holds the carries++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ (x,y,s,c) <- loop_with_indexM (l-1) (x,y,s,c) (\j (x,y,s,c) -> do+ let c_j1 = c !! (j+1)+ let s_j = s !! j+ -- If any two of the current bits x_j, y_j, and the carry c_j are true, then set the next carry:+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. y!!j)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. c!!j)+ c_j1 <- qnot c_j1 `controlled` (y!!j .&&. c!!j)+ -- Now the current sum digit is computed mod 2:+ s_j <- qnot s_j `controlled` (x!!j)+ s_j <- qnot s_j `controlled` (y!!j)+ s_j <- qnot s_j `controlled` (c!!j) + c <- return $ overwriteAt (j+1) c_j1 c+ s <- return $ overwriteAt j s_j s++ return (x,y,s,c))++ -- Final sum digit; no carry required:+ let s_l1 = s !! (l-1)+ s_l1 <- qnot s_l1 `controlled` (x!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (y!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (c!!(l-1)) + s <- return $ overwriteAt (l-1) s_l1 s+ + x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ return (x,y,s,c))+ + (\(x,y,s,c) -> do+ (s,s_out) <- qc_copy_fun s+ return ((x,y,s,c), s_out))+ return (x, y, s_out)++-- | Subtract two 'QDInt's, into a fresh one. Arguments are assumed to be of equal size. /O/(ℓ).+q_sub_qdint :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt)+q_sub_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_sub_qulist x' y'+ let x = xint_of_list_bh x' + let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_sub_qdint': represents integers as+-- big-headian qubit lists.+q_sub_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_sub_qulist x y = do+ let l = length x+ when (l /= length y) $ do+ error "q_sub_qdint: cannot subtract QDInts of different lengths"+ + ((x,y),d_out) <- with_computed_fun (x,y)+ (\(x,y) -> do+ d <- qinit (replicate l False) -- holds the eventual difference+ b <- qinit (replicate l False) -- holds the borrows++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ d <- return $ reverse d+ b <- return $ reverse b++ (x,y,d,b) <- loop_with_indexM (l-1) (x,y,d,b) (\j (x,y,d,b) -> do+ let b_j1 = b !! (j+1)+ let d_j = d !! j+ -- If any two of (not x_j), y_j, and the borrow c_j hold, then set the next borrow.+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ b_j1 <- qnot b_j1 `controlled` (x!!j .==. 0) .&&. (y!!j .==. 1)+ b_j1 <- qnot b_j1 `controlled` (x!!j .==. 0) .&&. (b!!j .==. 1)+ b_j1 <- qnot b_j1 `controlled` (y!!j .==. 1) .&&. (b!!j .==. 1)+ -- Now the current difference digit is computed mod 2:+ d_j <- qnot d_j `controlled` (x!!j)+ d_j <- qnot d_j `controlled` (y!!j)+ d_j <- qnot d_j `controlled` (b!!j) + b <- return $ overwriteAt (j+1) b_j1 b+ d <- return $ overwriteAt j d_j d+ return (x,y,d,b))++ -- Final difference digit; no carry required:+ let d_l1 = d !! (l-1)+ d_l1 <- qnot d_l1 `controlled` (x!!(l-1))+ d_l1 <- qnot d_l1 `controlled` (y!!(l-1))+ d_l1 <- qnot d_l1 `controlled` (b!!(l-1)) + d <- return $ overwriteAt (l-1) d_l1 d++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ d <- return $ reverse d+ b <- return $ reverse b++ return (x,y,d,b))+ + (\(x,y,d,b) -> do+ (d,d_out) <- qc_copy_fun d+ return ((x,y,d,b), d_out))+ return (x, y, d_out)++-- | Add one 'QDInt' onto a second, in place; i.e. (/x/,/y/) ↦ (/x/,/x/+/y/). Arguments are assumed to be of equal size. /O/(ℓ) gates.+q_add_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+q_add_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y') <- q_add_in_place_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x, y)++-- | Low-level implementation of 'q_add_in_place': represents integers+-- as big-headian qubit lists.+q_add_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit])+q_add_in_place_qulist [] [] = return ([], [])+q_add_in_place_qulist [x0] [y0] = do+ y0 <- qnot y0 `controlled` x0+ return ([x0], [y0])+q_add_in_place_qulist x y = do+ let (x0:x_higher) = reverse x + (y0:y_higher) = reverse y++ y0 <- qnot y0 `controlled` x0++ ((x0,y0),(x_higher,y_higher)) <- with_computed_fun (x0,y0)+ (\(x0,y0) -> do+ c <- qinit False+ c <- qnot c `controlled` (x0 .==. 1) .&&. (y0 .==. 0)+ return (x0,y0,c))+ (\(x0,y0,c) -> do+ (x_higher,y_higher,c) <- q_add_aux (x_higher) (y_higher) c+ return ((x0,y0,c),(x_higher,y_higher)))+ return (reverse (x0:x_higher), reverse (y0:y_higher))+ where+ -- Aux: add two LITTLE-endian bit strings, and an optional extra 1.+ q_add_aux :: [Qubit] -> [Qubit] -> Qubit -> Circ ([Qubit],[Qubit],Qubit)+ q_add_aux [] [] c = return ([],[],c)+ q_add_aux [x0] [y0] c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ return ([x0],[y0],c)+ q_add_aux (x0:xs) (y0:ys) c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ ((x0,y0,c),(xs,ys)) <- with_computed_fun (x0,y0,c)+ (\(x0,y0,c) -> do+ c' <- qinit False+ c' <- qnot c' `controlled` (x0 .==. 1) .&&. (y0 .==. 0)+ c' <- qnot c' `controlled` (x0 .==. 1) .&&. (c .==. 1)+ c' <- qnot c' `controlled` (y0 .==. 0) .&&. (c .==. 1)+ return (x0,y0,c,c'))+ + (\(x0,y0,c,c') -> do+ (xs,ys,c') <- q_add_aux xs ys c'+ return ((x0,y0,c,c'),(xs,ys)))+ return (x0:xs,y0:ys,c)+ q_add_aux _ _ _ = error "q_add_in_place: cannot add integers of different sizes."++-- | Subtract one 'QDInt' from a second, in place; i.e. (/x/,/y/) ↦ (/x/,/y/–/x/). Arguments are assumed to be of equal size. /O/(ℓ) gates.+q_sub_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+q_sub_in_place x y = reverse_generic_endo (\(x,d) -> q_add_in_place x d) (x,y)++-- | Low-level version of 'q_sub_in_place': represents integers+-- as big-headian qubit lists.+q_sub_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit])+q_sub_in_place_qulist x y = reverse_generic_endo (\(x,d) -> q_add_in_place_qulist x d) (x,y)++-- ----------------------------------------------------------------------+-- ** Arithmetic with parameters++-- | Add a parameter 'IntM' and a 'QDInt', into a fresh 'QDInt': (/x/, /y/) ↦ (/y/, /x/+/y/). The parameter /x/ must be of the same length as /y/, or /x/ can also be of undetermined length. /O/(ℓ).+q_add_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_add_param x1 y = do+ let x = intm_promote x1 y "q_add_param: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ (y', z') <- q_add_param_qulist x' y'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (y, z)+ +-- | Low-level implementation of 'q_add_param': represents integers as+-- big-headian qubit lists. Precondition: /x/ and /y/ have the same length.+q_add_param_qulist :: [Bool] -> [Qubit] -> Circ ([Qubit], [Qubit])+q_add_param_qulist x y = do + -- Implementation note: compare with q_add_qdint. The code is almost verbatim identical, but since x consists of parameter Bools, `controlled` will omit gates when appropriate.++ -- Further slight optimisation would be possible, since if x doesn’t have low bits then no low carry qubits are required. ++ -- Implementation note: once we have a general-purpose classical+ -- circuit optimizer (even a primitive one), then this kind of+ -- source-code level optimization will hopefully become moot.+ + let l = length x++ (y,s_out) <- with_computed_fun y+ (\y -> do+ s <- qinit (replicate l False) -- holds the eventual sum+ c <- qinit (replicate l False) -- holds the carries++ -- bring the lists’ indexing in line with usual numeric indexing of binary expansons+ x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ (y,s,c) <- loop_with_indexM (l-1) (y,s,c) (\j (y,s,c) -> do+ let c_j1 = c !! (j+1)+ let s_j = s !! j+ -- If any two of the current bits x_j, y_j, and the carry c_j are true, then set the next carry:+ -- (Note: we use that (a & b) xor (a & c) xor (b & c) == (a & b) or (a & c) or (b & c).)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. y!!j)+ c_j1 <- qnot c_j1 `controlled` (x!!j .&&. c!!j)+ c_j1 <- qnot c_j1 `controlled` (y!!j .&&. c!!j)+ -- Now the current sum digit is computed mod 2:+ s_j <- qnot s_j `controlled` (x!!j)+ s_j <- qnot s_j `controlled` (y!!j)+ s_j <- qnot s_j `controlled` (c!!j) + c <- return $ overwriteAt (j+1) c_j1 c+ s <- return $ overwriteAt j s_j s++ return (y,s,c))++ -- Final sum digit; no carry required:+ let s_l1 = s !! (l-1)+ s_l1 <- qnot s_l1 `controlled` (x!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (y!!(l-1))+ s_l1 <- qnot s_l1 `controlled` (c!!(l-1)) + s <- return $ overwriteAt (l-1) s_l1 s+ + x <- return $ reverse x+ y <- return $ reverse y+ s <- return $ reverse s+ c <- return $ reverse c++ return (y,s,c))+ + (\(y,s,c) -> do+ (s,s_out) <- qc_copy_fun s+ return ((y,s,c), s_out))+ return (y, s_out)++-- | Subtract a parameter 'IntM' from a 'QDInt', into a fresh 'QDInt'. The 'IntM' cannot be shorter than the 'QDInt' (that would give ill-defined behavior), but can be of undetermined length. /O/(ℓ).+q_sub_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_sub_param x y = q_add_param (-x) y++-- | Add a parameter 'IntM' onto a 'QDInt', in place; i.e. (/x/,/y/) ↦ /x/+/y/. The parameter /x/ must be of the same length as /y/, or /x/ can also be of undetermined length. /O/(ℓ).+q_add_param_in_place :: IntM -> QDInt -> Circ QDInt+q_add_param_in_place x1 y = do+ let x = intm_promote x1 y "q_add_param_in_place: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ y' <- q_add_param_in_place_qulist x' y'+ let y = xint_of_list_bh y'+ return y++-- | Low-level implementation of 'q_add_param_in_place': represents+-- integers as big-headian qubit lists. Precondition: /xlist/ and /y/+-- have the same length. Precondition: /x/ and /y/ have the same length.+q_add_param_in_place_qulist :: [Bool] -> [Qubit] -> Circ [Qubit]+q_add_param_in_place_qulist [] [] = return []+q_add_param_in_place_qulist [False] [y0] = return [y0]+q_add_param_in_place_qulist [True] [y0] = do+ y0 <- qnot y0+ return [y0]+q_add_param_in_place_qulist x y = do+ let l = length x++ let (x0:x_higher) = reverse x + (y0:y_higher) = reverse y++ y0 <- qnot y0 `controlled` x0++ (y0,y_higher) <- with_computed_fun y0+ (\y0 -> do+ c <- qinit False+ c <- qnot c `controlled` (x0 == 1) .&&. (y0 .==. 0)+ return (y0,c))+ (\(y0,c) -> do+ (y_higher,c) <- q_add_aux x_higher y_higher c+ return ((y0,c),y_higher))+ return (reverse (y0:y_higher))+ where+ -- Aux: add two LITTLE-endian bit strings, and an optional extra 1.+ q_add_aux :: [Bool] -> [Qubit] -> Qubit -> Circ ([Qubit],Qubit)+ q_add_aux [] [] c = return ([],c)+ q_add_aux [x0] [y0] c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ return ([y0],c)+ q_add_aux (x0:xs) (y0:ys) c = do+ y0 <- qnot y0 `controlled` x0+ y0 <- qnot y0 `controlled` c+ ((y0,c),ys) <- with_computed_fun (y0,c)+ (\(y0,c) -> do+ c' <- qinit False+ c' <- qnot c' `controlled` (x0 == 1) .&&. (y0 .==. 0)+ c' <- qnot c' `controlled` (x0 == 1) .&&. (c .==. 1)+ c' <- qnot c' `controlled` (y0 .==. 0) .&&. (c .==. 1)+ return (y0,c,c'))+ + (\(y0,c,c') -> do+ (ys,c') <- q_add_aux xs ys c'+ return ((y0,c,c'),ys))+ return (y0:ys,c)+ q_add_aux _ _ _ = error "q_add_in_place: cannot add integers of different sizes."++-- | Subtract a parameter 'IntM' from a 'QDInt', in place; i.e. (/x/,/y/) ↦ (/x/,/x/-/y/). /x/ cannot be shorter than /y/. /O/(/l/) gates.+q_sub_param_in_place :: IntM -> QDInt -> Circ QDInt+q_sub_param_in_place x = q_add_param_in_place (-x)++-- | Multiply a parameter 'IntM' by a 'QDInt', into a fresh 'QDInt'. The 'IntM' cannot be shorter than the 'QDInt' (that would give ill-defined behavior), but can be of undetermined length. /O/(ℓ).+q_mult_param :: IntM -> QDInt -> Circ (QDInt,QDInt)+q_mult_param x1 y = do+ let x = intm_promote x1 y "q_add_param: inputs must have equal length"+ let x' = boollist_of_intm_bh x+ let y' = list_of_xint_bh y+ (y', z') <- q_mult_param_qulist x' y'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (y, z)++-- | Low-level implementation of 'q_mult_param': represents integers as+-- big-headian (qu)bit lists.+q_mult_param_qulist :: [Bool] -> [Qubit] -> Circ ([Qubit],[Qubit])+q_mult_param_qulist [] [] = return ([], [])+q_mult_param_qulist xs ys = do+ let x0 = last xs + x_higher = init xs+ y_high = head ys+ y_lower = tail ys+ (y_lower, p_higher)+ <- q_mult_param_qulist x_higher y_lower+ p0 <- qinit False+ let y = y_high:y_lower+ p = p_higher ++ [p0]+ -- Once we have some optimisation, the following line should be replaced by:+ -- (y,p) <- q_add_in_place_qulist y p `controlled` x0+ (y,p) <- if x0 then q_add_in_place_qulist y p else return (y,p)+ return (y, p) ++-- ----------------------------------------------------------------------+-- ** Sign and negation++-- | Negate a (signed) 'QDInt' in place. /O/(ℓ).+q_negate_in_place :: QDInt -> Circ QDInt+q_negate_in_place x = do+ x <- mapUnary qnot x+ x <- q_increment x+ return x++-- | Low-level version of 'q_negate_in_place': represents integers as+-- big-headian qubit lists.+q_negate_in_place_qulist :: [Qubit] -> Circ [Qubit]+q_negate_in_place_qulist = mmap list_of_xint_bh . q_negate_in_place . xint_of_list_bh++-- | Compute the negation of a (signed) 'QDInt'. /O/(ℓ).+-- +-- (Fixes the minimum value, consistently with Haskell’s @'negate' 'minBound' :: 'Int'@.) +q_negate_qdint :: QDInt -> Circ (QDInt,QDInt)+q_negate_qdint x = do+ (x,nx) <- qc_copy_fun x+ nx <- q_negate_in_place nx+ return (x,nx)++-- | Compute the absolute value of a (signed) 'QDInt'. /O/(ℓ).+-- +-- (Fixes the minimum value, consistently with Haskell’s @'abs' 'minBound' :: 'Int'@.) +q_abs_qdint :: QDInt -> Circ (QDInt,QDInt)+q_abs_qdint x = do+ let x' = list_of_xint_bh x+ (x', a') <- q_abs_qulist x'+ let x = xint_of_list_bh x'+ let a = xint_of_list_bh a'+ return (x, a)++-- | Low-level implementation of 'q_abs_qdint': represents integers as+-- big-headian qubit lists.+q_abs_qulist :: [Qubit] -> Circ ([Qubit],[Qubit])+q_abs_qulist [] = return ([], [])+q_abs_qulist (x_high:x_lower) = do+ a_high <- qinit False+ (x_lower,a_lower) <- qc_copy_fun x_lower+ a_lower <- mapUnary qnot a_lower `controlled` x_high+ let a = a_high:a_lower+ a <- q_increment_qulist a `controlled` x_high+ return (x_high:x_lower, a)++-- | Compute a 'QDInt' of the same length as the input, with value 1, 0, or –1, depending on the sign of the input. Analogous to Haskell’s 'signum'. /O/(ℓ).+q_signum_qdint :: QDInt -> Circ (QDInt,QDInt)+q_signum_qdint x = do+ let x' = list_of_xint_bh x+ (x', a') <- q_signum_qulist x'+ let x = xint_of_list_bh x'+ let a = xint_of_list_bh a'+ return (x, a)++-- | Low-level implementation of 'q_abs_qdint': represents integers as+-- big-headian qubit lists.+q_signum_qulist :: [Qubit] -> Circ ([Qubit],[Qubit])+q_signum_qulist [] = return ([], [])+q_signum_qulist x = do+ let l = length x+ (s_higher, s_low) <- qinit (replicate (l-1) False, False)+ -- first set s = 1+ s_low <- qnot s_low+ -- case x < 0: set s to -1 + s_higher <- mapUnary qnot s_higher `controlled` (head x)+ -- case x = 0: reset s to 0+ s_low <- qnot s_low `controlled` (x .==. replicate l 0)+ return (x,s_higher ++ [s_low])++-- ----------------------------------------------------------------------+-- ** Comparison++-- | Comparison of two 'QDInt's, considered as unsigned. /O/(ℓ).+q_le_unsigned :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_le_unsigned x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x',y',le_out) <- q_le_unsigned_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x,y,le_out)++-- | Low-level implementation of 'q_le_unsigned': represents integers+-- as big-headian qubit lists.+q_le_unsigned_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],Qubit)+q_le_unsigned_qulist x y = do+ ((x,y),le_out) <- with_computed_fun (x,y) q_le_unsigned_aux+ (\(x,y,(le,garbage)) -> do+ (le,le_out) <- qc_copy_fun le+ return ((x,y,(le,garbage)),le_out))+ return (x,y,le_out)++-- | Same as 'q_le_unsigned', but uncurried, represents integers as+-- big-headian qubit lists, and doesn’t clean up its garbage. (If+-- each recursive call cleans up its own garbage, gate use becomes+-- /O/(2[sup /n/]).)+q_le_unsigned_aux :: ([Qubit], [Qubit]) -> Circ ([Qubit],[Qubit],(Qubit,[Qubit]))+q_le_unsigned_aux ([], []) = do q <- qinit True; return ([], [], (q,[]))+q_le_unsigned_aux (x_high:x_lower, y_high:y_lower) = do+ -- Classically, one would make the recursive call only if needed; but quantumly, it’s cheaper to do it unconditionally. + (x_lower, y_lower, (le_lower, garbage)) <- q_le_unsigned_aux (x_lower, y_lower)+ (x_high,y_high,eq_high) <- q_is_equal x_high y_high+ le <- qinit False+ le <- qnot le `controlled` (x_high .==. 0) .&&. (y_high .==. 1)+ le <- qnot le `controlled` eq_high .&&. le_lower+ return (x_high:x_lower, y_high:y_lower, (le, eq_high:le_lower:garbage))+q_le_unsigned_aux _ = error "q_le // QDInt: cannot compare QDInt’s of different lengths."++-- | Comparison of two 'QDInt's, considered as signed. Used in @instance 'QOrd' 'QDInt'@. /O/(ℓ).+q_le_signed :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_le_signed x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x',y',le_out) <- q_le_signed_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x,y,le_out)++-- | Low-level implementation of 'q_le_signed': represents integers+-- as big-headian qubit lists.++-- Implementation note: defined in terms of q_le_unsigned_aux+q_le_signed_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],Qubit)+q_le_signed_qulist [] [] = do q <- qinit True; return ([], [], q)+q_le_signed_qulist (x_high:x_lower) (y_high:y_lower) = do+ ((x,y), x_le_y) <- with_computed_fun (x_high:x_lower, y_high:y_lower)+ (\(x_high:x_lower, y_high:y_lower) -> do + -- Classically, one would make the recursive call only if needed; but quantumly, it’s cheaper to do it unconditionally. + (x_lower, y_lower, (le_lower, garbage)) <- q_le_unsigned_aux (x_lower, y_lower)+ (x_high,y_high,eq_high) <- q_is_equal x_high y_high+ return (x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage))+ (\(x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage) -> do+ le <- qinit False+ le <- qnot le `controlled` (x_high .==. 1) .&&. (y_high .==. 0)+ le <- qnot le `controlled` eq_high .&&. le_lower+ return ((x_high, y_high, x_lower, y_lower, le_lower, eq_high, garbage), le))+ return (x,y,x_le_y)+q_le_signed_qulist _ _ = error "q_le // QDInt: cannot compare QDInt’s of different lengths."++-- | Comparison of two 'QDInt's, considered as signed. Used in @instance 'QOrd' 'QDInt'@. /O/(ℓ).+q_lt_signed :: QDInt -> QDInt -> Circ (QDInt,QDInt,Qubit)+q_lt_signed x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (y',x',y_le_x) <- q_le_signed_qulist y' x'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ x_lt_y <- qnot y_le_x+ return (x,y,x_lt_y)++-- | Text whether a 'QDInt' is nonnegative. /O/(1)+q_negative :: QDInt -> Circ (QDInt,Qubit)+q_negative x = do+ let (x_high:x_lower) = list_of_xint_bh x+ (x_high,x_neg) <- qc_copy_fun x_high+ return (xint_of_list_bh (x_high:x_lower),x_neg)++instance QOrd QDInt where+ q_less qx qy = do (qx,qy,q) <- q_lt_signed qx qy; return q+ q_leq qx qy = do (qx,qy,q) <- q_le_signed qx qy; return q++-- ----------------------------------------------------------------------+-- ** Multiplication++-- | Multiply two 'QDInt's into a fresh third. Arguments are assumed to be of equal size. /O/(ℓ[sup 2]).+q_mult_qdint :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt)+q_mult_qdint x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', z') <- q_mult_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let z = xint_of_list_bh z'+ return (x, y, z)++-- | Low-level implementation of 'q_mult_qdint': represents integers as+-- big-headian qubit lists.+q_mult_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit],[Qubit],[Qubit])+q_mult_qulist [] [] = return ([], [], [])+q_mult_qulist xs ys = do+ let x0 = last xs + x_higher = init xs+ y_high = head ys+ y_lower = tail ys+ (x_higher, y_lower, p_higher)+ <- q_mult_qulist x_higher y_lower+ p0 <- qinit False+ let y = y_high:y_lower+ p = p_higher ++ [p0]+ (y,p) <- q_add_in_place_qulist y p `controlled` x0+ return (x_higher ++ [x0], y, p)++-- ----------------------------------------------------------------------+-- ** Division and remainder++-- | Reduce one 'QDInt' modulo another, in place, returning also the+-- integer quotient, i.e. (/x/, /y/) ↦ (/x/ mod /y/, /y/, /x/ div /y/).+-- All inputs and outputs are considered unsigned. Inputs are assumed+-- to have the same length. Division by zero returns (/x/,0,-1).+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_moddiv_unsigned_in_place :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_moddiv_unsigned_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (r', y', q') <- q_moddiv_unsigned_in_place_qulist x' y'+ let r = xint_of_list_bh r'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (r, y, q)++-- | Low-level implementation of 'q_moddiv_unsigned_in_place':+-- represents integers as big-headian qubit lists.+-- +-- Implementation note: this algorithm can be optimized significantly:+-- [scratch] is always 0, so can be optimized away. This optimisation+-- is much clearer at the circuit level than the code level, however,+-- so is deferred for now.+q_moddiv_unsigned_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit],[Qubit])+q_moddiv_unsigned_in_place_qulist x y = do+ let l = common_value "q_divrem_unsigned_in_place: arguments must be same length" $ map length [x,y]+ -- Set quot = 0, rem = x. At each stage, we will have y * quot + rem = x.+ quot_bits <- qinit (replicate l False)+ let rem = x+ with_ancilla_init (replicate (l-1) False) (\scratch -> do+ -- For j from (l-1) to 0, compute the jth bit of the quotient, and update the remainder + (y,scratch,quot_bits,rem) <- loop_with_indexM l (y,scratch,quot_bits,rem)+ (\i (y,scratch,quot_bits,rem) -> do+ let j = l-1-i+ -- Getting y*(2^j) requires no quantum operations, just formal re-bracketing of the data:+ let y_init = take j y+ y_2j = (drop j y) ++ (take j scratch)+ scratch_tail = drop j scratch+ -- Compare (2^j) y with rem. If (2^j)y <= rem, then subtract (2^j)y from rem and flip the jth bit of quot.+ ((y_init,y_2j,rem,quot_bits),()) <- with_computed_fun + (y_init,y_2j,rem,quot_bits)+ (\(y_init,y_2j,rem,quot_bits) -> do+ -- first, test that (2^j)y has not overflowed the register, i.e. that y_init is still all 0;+ le1 <- qinit False+ le1 <- qnot le1 `controlled` (y_init .==. replicate (length y_init) False)+ -- next, test that (2^j)y <= rem (this will be correct if (2^j)y has not overflowed yet);+ (y_2j,rem,le2) <- q_le_unsigned_qulist y_2j rem+ return (y_init,y_2j,rem,quot_bits,le1,le2))+ (\(y_init,y_2j,rem,quot_bits,le1,le2) -> do+ -- flip jth bit of quot, if appropriate+ -- (leave rem unchanged for now, since it’s needed to uncompute le1)+ q_j <- qnot (quot_bits !! (l-1-j)) `controlled` le1 .&&. le2+ return ((y_init,y_2j,rem,(overwriteAt (l-1-j) q_j quot_bits),le1,le2),()))+ -- now, subtract (2^j) from rem, if appropriate.+ (y_2j,rem) <- q_sub_in_place_qulist y_2j rem `controlled` quot_bits !! (l-1-j)+ -- Finally, undo the formal re-bracketing:+ let y = y_init ++ (take (l-j) y_2j)+ scratch = (drop (l-j) y_2j) ++ scratch_tail+ return (y,scratch,quot_bits,rem))+ return (rem, y, quot_bits))++-- | Reduce one 'QDInt' modulo another, i.e. (/x/, /y/) ↦ (/x/, /y/, /x/ mod /y/).+-- All inputs and outputs are considered unsigned. Inputs are assumed+-- to have the same length. If /y/ = 0, returns (/x/,0,/x/).+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_mod_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_mod_unsigned x y = do+ ((x,y),x_mod_y) <- with_computed_fun (x,y)+ (\(x,y) -> q_moddiv_unsigned_in_place x y)+ (\(x_mod_y, y, x_div_y) -> do+ (x_mod_y, x_mod_y_out) <- qc_copy_fun x_mod_y+ return ((x_mod_y, y, x_div_y), x_mod_y_out))+ return (x,y,x_mod_y)++-- | Integer division of two 'QDInt's, returning the quotient and remainder,+-- i.e. (/x/,/y/) ↦ (/x/,/y/,/x/ div /y/,/x/ mod /y/). +-- All inputs and outputs are considered unsigned.+-- Inputs are assumed to have the same length.+-- Division by zero returns (-1,/x/).+--+-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_divrem_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt,QDInt)+q_divrem_unsigned x y = do+ (x,rem) <- qc_copy_fun x+ (rem,y,quot) <- q_moddiv_unsigned_in_place rem y+ return (x,y,quot,rem)++-- | Integer division of two 'QDInt's, returning just the quotient. +-- All inputs/outputs considered unsigned.+-- Inputs are assumed to have the same length.+-- Division by zero returns –1.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.++-- Implementation note: We use a multiplication to do the+-- uncomputation of the remainder, because this uses fewer total gates+-- than uncomputing divrem would.+q_div_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_unsigned x y = do+ (x,y,quot,rem) <- q_divrem_unsigned x y+ ((x,y,quot),()) <- with_computed_fun (x,y,quot) + (\(x,y,quot) -> do+ (y,quot,y_quot) <- q_mult y quot+ (x,y_quot,rem_copy) <- q_sub x y_quot+ return (x,y,quot,y_quot,rem_copy))+ (\(x,y,quot,y_quot,rem_copy) -> do+ rem_copy <- qc_uncopy_fun rem_copy rem+ return ((x,y,quot,y_quot,rem_copy),()))+ return (x,y,quot)++-- | Low-level version of 'q_div_unsigned': represents integers as+-- big-headian qubit lists.+q_div_unsigned_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_div_unsigned_qulist x' y' = do+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ (x, y, q) <- q_div_unsigned x y + let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ let q' = list_of_xint_bh q+ return (x', y', q')++-- | Integer division of two 'QDInt's into a fresh third, rounding towards –∞. +-- The first argument is the numerator, and is assumed to be signed.+-- The second argument is the denominator, and is assumed to be unsigned.+-- The output is signed.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_div x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', q') <- q_div_qulist x' y' + let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (x, y, q)++-- | Low-level implementation of 'q_div': represents integers as+-- big-headian qubit lists.+q_div_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_div_qulist [] [] = return ([], [], [])+q_div_qulist (x_high:x_lower) y = do+ -- Writing x/y for unsigned division, we have x `div` y = + -- if x>0 then x/y else ~((~x)/y)+ -- where ~x := -1-x --- i.e. the bitflip of x.+ --+ -- We do this with an ancilla (which can easily be optimized away): + with_ancilla_init False (\fake_x_high -> do+ -- flip x if x<0:+ x_lower <- mapUnary qnot x_lower `controlled` x_high+ let x' = fake_x_high:x_lower+ -- x' now holds (if x > 0 then x else ~x) + (x',y,quot) <- q_div_unsigned_qulist x' y+ -- restore x and flip quotient, if necessary+ let fake_x_high:x_lower = x'+ x_lower <- mapUnary qnot x_lower `controlled` x_high+ quot <- mapUnary qnot quot `controlled` x_high+ return (x_high:x_lower, y, quot))+q_div_qulist _ _ = error "q_div: arguments must have same length."++-- | Integer division of two 'QDInt's into a fresh third, rounding towards 0. +-- The first argument is the numerator, and is assumed to be signed.+-- The second argument is the denominator, and is assumed to be unsigned.+-- The output is signed.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_quot :: QDInt -> QDInt -> Circ (QDInt, QDInt, QDInt)+q_quot x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y', q') <- q_quot_qulist x' y' + let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ let q = xint_of_list_bh q'+ return (x, y, q)++-- | Low-level implementation of 'q_quot': represents integers as+-- big-headian qubit lists.+q_quot_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit], [Qubit])+q_quot_qulist [] [] = return ([], [], [])+q_quot_qulist (x_high:x_lower) y = do+ -- Writing x/y for unsigned division, we have x `div` y = + -- if x>0 then x/y else -((-x)/y)+ --+ -- We do this with an ancilla (which can easily be optimized away): + with_ancilla_init False (\fake_x_high -> do+ -- flip x if x<0:+ x_lower <- q_negate_in_place_qulist x_lower `controlled` x_high+ let x' = fake_x_high:x_lower+ -- x' now holds (if x > 0 then x else ~x) + (x',y,quot) <- q_div_unsigned_qulist x' y+ -- restore x and flip quotient, if necessary+ let fake_x_high:x_lower = x'+ x_lower <- q_negate_in_place_qulist x_lower `controlled` x_high+ quot <- q_negate_in_place_qulist quot `controlled` x_high+ return (x_high:x_lower, y, quot))+q_quot_qulist _ _ = error "q_quot: arguments must have same length."++-- | Integer division of two 'QDInt's, returning the quotient,+-- assuming that the second exactly divides the first and throwing an error otherwise. +-- All inputs/outputs considered unsigned.+-- Inputs are assumed to have the same length.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div_exact_unsigned :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_exact_unsigned x y = do+ (x,y,quot,rem) <- q_divrem_unsigned x y+ qterm 0 rem+ return (x,y,quot)++-- | Integer division of two 'QDInt's, returning the quotient,+-- assuming that the second exactly divides the first. +-- The first argument is the numerator, considered signed.+-- The second argument is the denominator, considered unsigned.+-- The output is signed.+-- +-- /O/(ℓ[sup 2]) gates, /O/(ℓ) qubits.+q_div_exact :: QDInt -> QDInt -> Circ (QDInt, QDInt,QDInt)+q_div_exact x y = do+ (x,(y,quot)) <- with_computed_fun x+ (\x -> do+ (x,x_neg) <- q_negative x+ x' <- q_negate_in_place x `controlled` x_neg+ return (x',x_neg))+ (\(x',x_neg) -> do+ (x',y,quot) <- q_div_exact_unsigned x' y+ quot <- q_negate_in_place quot `controlled` x_neg+ return ((x',x_neg),(y,quot)))+ return (x,y,quot)++-- ----------------------------------------------------------------------+-- * The QNum type class++-- $ This section defines a quantum analogue of Haskell’s 'Num' type+-- class. See "Quipper.Internal.QClasses" for the general philosophy behind+-- quantum type classes.+-- +-- The type class 'QNum' corresponds to Haskell’s 'Num', with methods+-- including +-- +-- > add_q :: (QNum qa) => qa -> qa -> Circ (qa,qa,qa), +-- +-- and so on.+-- +-- Note: type class instances don't show up in the documentation. View+-- the source code to see them.++-- | Quantum analogue of Haskell’s 'Num' type class. This provides+-- basic addition, subtraction, multiplication, sign operations, and+-- conversion from integers.++class (QData qa) => QNum qa where+ -- | Add two quantum numbers into a fresh one. The arguments are+ -- assumed to be of equal size. The 'QDInt' instance uses /O/(ℓ)+ -- gates, both before and after transformation to Toffoli.+ q_add :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Multiply two quantum numbers into a fresh third. The arguments+ -- are assumed to be of equal size. The 'QDInt' instance is+ -- /O/(ℓ[sup 2]).+ q_mult :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Subtract two quantum numbers into a fresh one. The arguments+ -- are assumed to be of equal size. The 'QDInt' instance uses /O/(ℓ)+ -- gates, both before and after transformation to Toffoli.+ q_sub :: qa -> qa -> Circ (qa,qa,qa)+ + -- | Compute the absolute value of a (signed) quantum number. The+ -- 'QDInt' instance is /O/(ℓ).+ q_abs :: qa -> Circ (qa,qa)+ + -- | Compute the negation of a (signed) quantum number. The 'QDInt'+ -- instance is /O/(ℓ).+ q_negate :: qa -> Circ (qa,qa)+ + -- | Compute a quantum number of the same precision as the input,+ -- with value 1, 0, or –1, depending on the sign of the+ -- input. Analogous to Haskell’s 'signum'. The 'QDInt' instance is+ -- /O/(ℓ).+ q_signum :: qa -> Circ (qa,qa)+ + -- | Convert a 'QDInt' to a quantum number. For the 'QDInt'+ -- instance, this is just a copy operation.+ q_fromQDInt :: QDInt -> Circ (QDInt,qa)+ +instance QNum QDInt where+ q_add = q_add_qdint+ q_mult = q_mult_qdint+ q_sub = q_sub_qdint+ q_abs = q_abs_qdint+ q_negate = q_negate_qdint+ q_signum = q_signum_qdint+ q_fromQDInt = qc_copy_fun++-- ----------------------------------------------------------------------+-- ** Specialized functions++-- | Euclid's extended algorithm. 'q_ext_euclid' /a/ /b/: returns+-- (/a/,/b/,/x/,/y/,/d/) such that /ax/ + /by/ = /d/ = gcd(/a/,/b/).+q_ext_euclid :: QDInt -> QDInt -> Circ (QDInt,QDInt,QDInt,QDInt,QDInt)+q_ext_euclid a b = do+ let l = common_length "q_ext_euclid: inputs must have equal length" [a,b]++ -- Extended Euclidean algorithm:+ --+ -- compute three series of numbers (a_i), (x_i), (y_i), such that+ --+ -- a_0 = a, a_1 = b,+ -- a_{i+2} = a_i `mod` a_{i+1},+ -- and always a_i = (x_i * a) + (y_i * b),+ --+ -- When we first get a_{i+2} = 0, we know that a_{i+1} is gcd(a_0,a_1),+ -- so we save a_{i+1}, x_{i+1} and y_{i+1} for output.+ --+ -- The bound on the length of this loop is standard.++ ((a,b),(x,y)) <- with_computed_fun (a,b) (\(a,b) -> do+ x0 <- qinit (intm l 1)+ y0 <- qinit (intm l 0)+ x1 <- qinit (intm l 0)+ y1 <- qinit (intm l 1)+ done_yet <- qinit False+ x_final <- qinit (intm l 0)+ y_final <- qinit (intm l 0)++ (stuff1, stuff2, stuff3, (done_yet, x_final, y_final))+ <- loopM (euclid_bound l) ((a,b,x0,x1,y0,y1),[],[],(done_yet,x_final,y_final))+ (\((a_i, a_i1, x_i, x_i1, y_i, y_i1), quots_scratch, tests_scratch, (done_yet, x_final, y_final)) -> do+ (a_i2, a_i1, q_i) <- q_moddiv_unsigned_in_place a_i a_i1+ ((x_i1, y_i1, q_i), (x_i2, y_i2)) <- with_computed_fun+ (x_i1, y_i1, q_i)+ (\(x_i1, y_i1, q_i) -> do+ (q_i, x_i1, qx) <- q_mult q_i x_i1+ (q_i, y_i1, qy) <- q_mult q_i y_i1+ return (x_i1, y_i1, q_i, qx, qy))+ (\(x_i1, y_i1, q_i, qx, qy) -> do+ (qx,x_i2) <- q_sub_in_place qx x_i+ (qy,y_i2) <- q_sub_in_place qy y_i+ return ((x_i1, y_i1, q_i, qx, qy),(x_i2,y_i2)))+ done_this_time <- qinit False+ done_this_time <- qnot done_this_time `controlled` (a_i2 .==. 0) .&&. (done_yet .==. False)+ (x_i1,x_final) <- controlled_not x_final x_i1 `controlled` done_this_time+ (y_i1,y_final) <- controlled_not y_final y_i1 `controlled` done_this_time+ done_yet <- qnot done_yet `controlled` done_this_time+ return ((a_i1, a_i2, x_i1, x_i2, y_i1, y_i2),+ (q_i:quots_scratch), (done_this_time:tests_scratch), + (done_yet, x_final, y_final)))+ qterm True done_yet -- Assert that we really did reach the end of the algorithm. + return (x_final,y_final,(stuff1, stuff2, stuff3)))++ (\(x_final, y_final, stuff) -> do+ (x_final,x) <- qc_copy_fun x_final+ (y_final,y) <- qc_copy_fun y_final+ return ((x_final, y_final, stuff),(x,y)))++ ((a,b,x,y),gcd) <- with_computed_fun+ (a,b,x,y)+ (\(a,b,x,y) -> do+ (a,x,ax) <- q_mult a x+ (b,y,by) <- q_mult b y+ return (a,b,x,y,ax,by))+ (\(a,b,x,y,ax,by) -> do+ (ax,by,gcd) <- q_add ax by+ return ((a,b,x,y,ax,by),gcd))++ return (a,b,x,y,gcd)++ where+ -- Classical bound on running time (Lamé’s theorem), assuming b < a:+ -- t <= 1 + log_{phi} b <= 1 + l / (log_2 phi)+ -- + -- We allow one extra step since we may have a < b, in which case first step swaps them.+ euclid_bound :: Int -> Int+ euclid_bound l = 2 + ceiling ( (fromIntegral l) / (logBase 2 phi) )+ phi = (1+sqrt(5))/2++-- ----------------------------------------------------------------------+-- * Lifting of arithmetic functions++-- $LIFTING +-- This sections provides templates for lifting various arithmetic+-- functions in connection with the @build_circuit@ keyword. This+-- extends the liftings given in "Quipper.Internal.CircLifting" to operations+-- of the 'Num' type class.++-- | Quantum lifting of the '+' operator.+template_symb_plus_ :: (QNum qa) => Circ (qa -> Circ (qa -> Circ qa))+template_symb_plus_ = return $ \qx -> return $ \qy -> do (qx,qy,qz) <- q_add qx qy; return qz++-- TODO: complete templates of methods.+
@@ -0,0 +1,39 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/ClassicalOptim.hs" #-}+-- | This library provides functions for optimizing classical+-- circuits. It is intended to reduce the size of+-- automatically-generated circuits, such as those that are built+-- using Quipper's @build_circuit@ keyword.+-- +-- Note: the resulting circuit is not expected to be optimal in+-- absolute terms. In general, finding the smallest possible circuit+-- to represent a given boolean function is an NP-hard+-- problem. Rather, we transform the input circuit by applying a+-- number of heuristics and an algebraic optimization technique. This+-- results in the removal of redundant qubits and gates that are+-- typical for machine-generated circuits. It generally decreases the+-- number of gates and ancillas.+-- +-- For example, consider the circuit for a simple adder, as generated+-- by the @build_circuit@ mechanism:+-- +-- \[image adder-unoptimized.png]+-- +-- The optimized version, as generated by 'simplify_classical', looks+-- like this:+-- +-- \[image adder-optimized.png]+-- +-- The above example can be found in the file+-- @Quipper\/Demos\/OptimTest.hs@ in the @quipper-demos@ package.++module Quipper.Libraries.ClassicalOptim (+ simplify_classical,+ classical_to_reversible_optim,+ box_classical_to_reversible_optim+) where++import Quipper.Libraries.ClassicalOptim.QuipperInterface+
@@ -0,0 +1,167 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/ClassicalOptim/AlgExp.hs" #-}+{-# LANGUAGE Unsafe #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}++{-# LANGUAGE CPP #-}+#if __GLASGOW_HASKELL__ < 710+ {-# LANGUAGE OverlappingInstances #-}+#endif++-- | This module contains an efficient representation of algebraic+-- boolean formulas.+module Quipper.Libraries.ClassicalOptim.AlgExp where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import Quipper.Utils.Auxiliary (bool_xor)++import Quipper.Libraries.ClassicalOptim.Circuit++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Build the characteristic function of a set.+mapOfSet :: Ord a => S.Set a -> M.Map a Int+mapOfSet s = S.foldl' (\m x -> M.insert x 1 m) M.empty s++-- | Get the set of elements whose images are odd.+setOfMap :: Ord a => M.Map a Int -> S.Set a+setOfMap m = + M.foldlWithKey' (\s x _ -> S.insert x s) S.empty $ + M.filter (\x -> mod x 2 == 1) m++-- | Split a list in the middle.+split_even :: [a] -> ([a],[a])+split_even a = splitAt (div (length a) 2) a++-- ----------------------------------------------------------------------+-- * Expressions++-- | The type of algebraic boolean expressions. +-- +-- We represent boolean expressions using \"and\" and \"xor\" as the+-- primitive connectives. Equivalently, we can regard booleans as the+-- elements of the two-element field /F/[sub 2], with operations \"*\"+-- (times) and \"+\" (plus). +-- +-- An algebraic expression+-- @x1*x2*x3 + y1*y2*y3 + z1*z2@+-- is encoded as+-- @{{x1,x2,x3},{y1,y2,y3},{z1,z2}}@.+-- +-- In particular,+-- @{} == False == 0@ and +-- @{{}} == True == 1@.+type Exp = S.Set IS.IntSet++instance {-# OVERLAPPING #-} Show Exp where+ show e = if (S.null e) then "F"+ else if (e == S.singleton (IS.empty)) then "T"+ else L.concat $ L.intersperse "+" (L.map (\e -> L.concat $ L.map (\x -> "x" ++ (show x)) $ IS.toList e) $ S.toList e)++-- | Turn an @Exp@ into a list of lists.+listOfExp :: Exp -> [[Int]]+listOfExp e = S.toList $ S.map IS.toList e++-- | Turn a list of lists into an @Exp@.+expOfList :: [[Int]] -> Exp+expOfList l = S.fromList $ L.map IS.fromList l++-- | The conjunction of two expression.+exp_and :: Exp -> Exp -> Exp+exp_and a b = + setOfMap $+ S.foldl (\exp monomial -> M.unionWith (+) exp $ exp_and_aux monomial $ mapOfSet a) M.empty b+ where+ exp_and_aux :: IS.IntSet -> M.Map IS.IntSet Int -> M.Map IS.IntSet Int+ exp_and_aux monomial exp = M.mapKeysWith (+) (IS.union monomial) exp++-- | The xor of two expressions.+exp_xor :: Exp -> Exp -> Exp+exp_xor a b = setOfMap $ M.unionWith (+) (mapOfSet a) (mapOfSet b)++-- | The expression \"False\".+exp_false :: Exp+exp_false = S.empty++-- | The expression \"True\".+exp_true :: Exp+exp_true = S.singleton IS.empty++-- | The negation of an expression.+exp_not :: Exp -> Exp +exp_not e = exp_xor e exp_true++-- | The expression /x/[sub /n/].+exp_var :: Int -> Exp+exp_var x = S.singleton $ IS.singleton x++-- ----------------------------------------------------------------------+-- * Properties of expressions++-- $ The important property of expressions is that two formulas have+-- the same truth table iff they are syntactically equal. This makes+-- the equality test of wires theoretically straightforward.++-- ----------------------------------------------------------------------+-- ** Truth tables++-- $ A /valuation/ on a set of variables is a map from variables to+-- booleans. This can be thought of as a row in a truth table. A+-- /truth table/ is a map from valuations to booleans, but we just+-- represent this as a list of booleans, listed in lexicographically+-- increasing order of valuations.++-- | Get the variables used in an expression.+vars_of_exp :: Exp -> [Int]+vars_of_exp e = IS.toList $ S.foldl (\a b -> IS.union a b) IS.empty e++-- | Evaluate the expression with respect to the given valuation. A+-- /valuation/ is a map from variables to booleans, i.e., a row in a+-- truth table.+exp_eval :: Exp -> M.Map Int Bool -> Bool+exp_eval e m = L.foldl bool_xor False $ L.map (L.foldl (&&) True) $ L.map (L.map (m M.!)) $ L.map (IS.toList) $ S.toList e++-- | Construct the list of all 2[super /n/] valuations for a given+-- list of /n/ variables.+valuations_of_vars :: [Int] -> [M.Map Int Bool]+valuations_of_vars [] = [M.empty]+valuations_of_vars (h:t) = l+ where+ l = (L.map (M.insert h False) v) ++ (L.map (M.insert h True) v)+ v = valuations_of_vars t ++-- | Build the truth table for the given expression, on the given list+-- of variables. The truth table is returned as a list of booleans in+-- lexicographic order of valuations. For example, if+-- +-- > 1 2 | exp+-- > F F | f1+-- > F T | f2+-- > T F | f3+-- > T T | f4+-- +-- then the output of the function is @[f1,f2,f3,f4]@.+truth_table_of_exp :: [Int] -> Exp -> [Bool]+truth_table_of_exp vars e = L.map (exp_eval e) (valuations_of_vars vars)++-- | Return an expression realizing the given truth table. Uses+-- variables starting with the given number.+exp_of_truth_table :: Int -> [Bool] -> Exp+exp_of_truth_table i [] = exp_true+exp_of_truth_table i [False] = exp_false+exp_of_truth_table i [True] = exp_true+exp_of_truth_table i t = ((exp_not (exp_var i)) `exp_and` e1) `exp_xor` ((exp_var i) `exp_and` e2)+ where+ (t1,t2) = split_even t+ e1 = exp_of_truth_table (i+1) t1+ e2 = exp_of_truth_table (i+1) t2+
@@ -0,0 +1,132 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/ClassicalOptim/Circuit.hs" #-}+-- | This module provides a simplified representation of classical+-- circuits.+module Quipper.Libraries.ClassicalOptim.Circuit where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Quipper.Utils.Auxiliary as Q++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++-- ----------------------------------------------------------------------+-- * Simplified circuits++-- | The type of wires. A wire is determined by an integer ID.+type Wire = Int++-- | The type of gates.+data Gate = + NoOp -- ^ No operation.+ | Init Bool Wire -- ^ Initialization.+ | Cnot Wire [(Wire,Bool)] -- ^ Multi-controlled not.+ deriving (Show,Eq)++-- | Get the wire acted upon by a gate, if any.+wireOfGate :: Gate -> Maybe Wire+wireOfGate NoOp = Nothing+wireOfGate (Init _ w) = Just w+wireOfGate (Cnot w _) = Just w++-- | Get the list of controls, if any.+ctlsOfGate :: Gate -> Maybe [(Wire,Bool)]+ctlsOfGate (Cnot _ ctls) = Just ctls+ctlsOfGate _ = Nothing++-- | Evaluate a circuit on a given initial state, and return the final+-- state. A state is represented as a map from wires to booleans.+evalCirc :: M.Map Wire Bool -> [Gate] -> M.Map Wire Bool+evalCirc m [] = m+evalCirc m (NoOp:gs) = evalCirc m gs+evalCirc m ((Init b w):gs) = evalCirc (M.insert w b m) gs+evalCirc m ((Cnot w ctls):gs) = evalCirc (M.adjust (Q.bool_xor (ands m ctls)) w m) gs+ where+ ands m [] = True+ ands m ((w,b):ctls) = ((m M.! w) `Q.bool_xor` (not b)) && (ands m ctls)++-- ----------------------------------------------------------------------+-- * Simplified Circ monad++-- | A data structure to represent a \"circuit under+-- construction\". This holds the data needed for circuit generation.+data CircState = CS {+ circuit :: [Gate], -- ^ The circuit so far.+ freshWire :: Wire -- ^ The next fresh wire.+} deriving (Show)++-- | The empty state.+emptyState :: CircState+emptyState = CS {circuit = [], freshWire = 0}++-- | A simplified @Circ@ monad.+data Circ a = Circ (CircState -> (CircState, a))++instance Monad Circ where+ return x = Circ (\y -> (y,x))+ (>>=) (Circ c) f = Circ (\s -> let (s',x) = c s in+ let (Circ c') = f x in+ c' s')++instance Applicative Circ where+ pure = return+ (<*>) = ap++instance Functor Circ where+ fmap = liftM++-- ----------------------------------------------------------------------+-- * Low-level access functions++-- | Retrieve the next fresh wire.+getFresh :: Circ Wire+getFresh = Circ (\s -> (s, freshWire s))++-- | Increment the value of the fresh wire.+incrementFresh :: Circ ()+incrementFresh = Circ (\s -> (s { freshWire = freshWire s + 1 }, ()))++-- | Add a new gate to the circuit.+addGate :: Gate -> Circ ()+addGate g = Circ (\s -> (s {circuit = g : (circuit s)}, ()))++-- | Get the circuit out of the monad.+extractCircuit :: Circ a -> [Gate]+extractCircuit (Circ c) = circuit $ fst $ c emptyState++-- ----------------------------------------------------------------------+-- * Higher-level access functions++-- | Initialize a new wire.+init :: Bool -> Circ Wire+init b = do+ w <- getFresh+ addGate (Init b w)+ incrementFresh+ return w++-- | Add a multi-controlled not gate.+cnot :: Wire -> [(Wire,Bool)] -> Circ ()+cnot w ws = do+ addGate (Cnot w ws)+ return ()++-- ----------------------------------------------------------------------+-- * Pretty-printing++-- $ These functions are only used for testing.++-- | Pretty-print a circuit as a list of gates.+printCircuit :: Circ a -> IO ()+printCircuit c = do+ mapM_ putStrLn $ map show $ reverse $ extractCircuit c ++-- | Print a gate as Quipper code.+print_quipperStyle :: Gate -> IO ()+print_quipperStyle (Init b w) = putStrLn (" x" ++ (show w) ++ " <- Q.qinit " ++ (show b))+print_quipperStyle (Cnot w ctls) = putStrLn (" Q.qnot x" ++ (show w) ++ " `Q.controlled` " ++ + (L.intercalate " Q..&&. " $ map (\(w,b) -> "x" ++ (show w) ++ " Q..==. " ++ (show b) ++ " ") ctls))+print_quipperStyle g = putStrLn (" Q.comment \"" ++ (show g) ++ "\"")
@@ -0,0 +1,175 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/ClassicalOptim/QuipperInterface.hs" #-}+-- | This module contains the interface between the simplified circuit+-- model and Quipper's internal circuit model. The main useful+-- exported functions are: +-- +-- * @'simplify_classical'@, which optimizes a classical circuit such+-- as those coming from Template Haskell;+-- +-- * @'classical_to_reversible_optim'@, which provides a mechanism+-- equivalent to @'Q.classical_to_reversible'@, but with optimization+-- inlined.++module Quipper.Libraries.ClassicalOptim.QuipperInterface where++import Data.Maybe++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import qualified Quipper as Q+import qualified Quipper.Internal.Monad as Q+import qualified Quipper.Internal.QData as Q+import qualified Quipper.Internal.Circuit as Q+import qualified Quipper.Internal.Generic as Q+import qualified Quipper.Internal.Printing as Q+import qualified Quipper.Libraries.Simulation as Q+import qualified Quipper.Libraries.Simulation.ClassicalSimulation as Q+import qualified Quipper.Internal.Transformer as Q+import qualified Quipper.Internal.Control as Q ++import qualified Quipper.Libraries.Arith as Q+import qualified Quipper.Utils.Auxiliary as Q++import Quipper.Libraries.ClassicalOptim.Circuit+import Quipper.Libraries.ClassicalOptim.Simplification+++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Extract the list of wires from a piece of quantum data.+getListWire :: (Q.QData qc) => qc -> [Wire]+getListWire x = map Q.wire_of_qubit $ Q.qubits_of_qdata x++-- ----------------------------------------------------------------------+-- * Quipper circuits to simple circuits++-- | Translates a Quipper circuit to a simple circuit. The only gates+-- considered are initializations, terminations, and multi-controlled+-- NOT gates. All other gates are ignored.+-- +-- Note that simple circuits do not possess termination wires: these+-- wires are simply not terminated, and all subsequent initializations+-- using the same wire ID are sent to fresh wires.+-- +-- The state of this function is a bit complex, as it keeps track of+-- where the output wires are mapped to.+quipperGateToMyGate :: (IS.IntSet,IM.IntMap Wire,Wire) -> Q.Gate -> ((IS.IntSet,IM.IntMap Wire,Wire), Maybe Gate)+quipperGateToMyGate (s,m,f) (Q.QGate "not" _ [w] _ ctls _) = + ((s,m,f), Just $ Cnot (IM.findWithDefault w w m) $ map (\(Q.Signed a b) -> (IM.findWithDefault a a m,b)) ctls)+quipperGateToMyGate (s,m,f) (Q.QInit b w _) = case (IS.member w s) of + True -> ((s,IM.insert w f m,f+1), Just $ Init b f)+ False -> ((s,m,f), Just $ Init b w)+quipperGateToMyGate (s,m,f) (Q.QTerm b w _) = ((IS.insert w s, m,f), Nothing)+quipperGateToMyGate smf _ = (smf, Nothing)+++-- | Get the wire initialized by the gate, if it is an initialization gate.+quipperGateInitW :: Q.Gate -> Maybe Wire+quipperGateInitW (Q.QInit _ w _) = Just w+quipperGateInitW _ = Nothing++-- | Given a list of Quipper gates, get the smallest wire id not in use.+quipperGateFreshWire :: Wire -> [Q.Gate] -> Wire+quipperGateFreshWire w gs = (+) 1 $ L.foldl' max w $ catMaybes $ map quipperGateInitW gs++-- | Send a Quipper 'Q.Circuit' to a 'CircState'.+quipperCircuitToMyCirc :: Q.Circuit -> CircState+quipperCircuitToMyCirc (_, gs, _, n) = + emptyState { + circuit = catMaybes $ snd $ L.mapAccumL quipperGateToMyGate (IS.empty,IM.empty,quipperGateFreshWire n gs) gs,+ freshWire = n+ }++-- | Send a Quipper 'Q.BCircuit' to a 'CircState'.+quipperBCircuitToMyCirc :: Q.BCircuit -> CircState+quipperBCircuitToMyCirc (c,_) = quipperCircuitToMyCirc c++-- | Generate a custom error message.+myCircErrMsg :: String -> String+myCircErrMsg s = "myCirc: " ++ s++-- | Given a Quipper circuit generating function and a shape argument,+-- return a simple circuit together with the list of non-garbage+-- circuit outputs.+quipperFunToMyCirc :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> (CircState,[Wire])+quipperFunToMyCirc f shape =+ let (_, bc, output) = Q.encapsulate_generic myCircErrMsg f shape+ in (quipperBCircuitToMyCirc bc, + getListWire output)++-- ----------------------------------------------------------------------+-- * Simple circuits to Quipper circuits++-- | Translate a gate from the simple circuit model into a Quipper gate.+myGateToQuipperGate :: Gate -> Q.Gate+myGateToQuipperGate (Cnot w ctls) = Q.QGate "not" True [w] [] (map (\(w,b) -> Q.Signed w b) ctls) False+myGateToQuipperGate (Init b w) = Q.QInit b w False+myGateToQuipperGate NoOp = error "myGateToQuipperGate cannot deal with NoOp"++-- | Generate a Quipper comment. The first argument is a comment+-- string, and the second argument is a label to apply to the qubits+-- in the third argument.+makeComment :: String -> String -> [Wire] -> Q.Gate+makeComment comment label ws = + Q.Comment comment False $ map (\(i,x) -> (i, label ++ "[" ++ (show x) ++ "]")) (zip ws [0..(length ws)-1])+++-- ----------------------------------------------------------------------+-- * Algebraic optimization of Quipper circuits++-- | Optimize a Quipper 'Q.BCircuit'. The second argument is the list+-- of non-garbage outputs. A corresponding list of outputs is also+-- returned along with the circuit.+quipperBCircuitSimpl :: Q.BCircuit -> [Wire] -> (Q.BCircuit,[Wire])+quipperBCircuitSimpl (c,e) output = (((a1,c'',a2',n'),e),o')+ where+ (a1,gs,a2,n) = c+ mycirc = quipperCircuitToMyCirc c+ (c',o') = compressWires (IM.keys a1) $ simplRec $ (\x -> (x,output)) {-set_init_first output-} $ circuit $ mycirc+ i' = IM.keys a1+ c'' = (makeComment "Start classical circuit" "in" i') : + (map myGateToQuipperGate c') ++ + [makeComment "End classical circuit" "out" o']+ allwires = getAllWires c'+ a2' = IM.fromList $ map (\x -> (x,Q.Qbit)) $ IS.toAscList allwires+ n' = (+) 1 $ head $ IS.toDescList allwires+++-- | Optimize a Quipper circuit producing function (together with a+-- shape argument). Return the optimized circuit as a Quipper+-- 'Q.BCircuit', along with a list of the non-garbage circuit outputs.+simplify_classical' :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> (Q.BCircuit, [Wire])+simplify_classical' f shape = + let (_,bc,output) = Q.encapsulate_generic myCircErrMsg f shape in+ let list_output = getListWire output in+ quipperBCircuitSimpl bc list_output++-- | Optimize a Quipper circuit-producing function. This assumes that+-- the function only consists of pseudo-classical quantum gates, i.e.,+-- initializations, terminations, and (possibly multiply controlled)+-- NOT gates. The behavior on other kinds of circuits is undefined.+-- The second argument is a shape parameter.+simplify_classical :: (Q.QData x, Q.QData y) => (x -> Q.Circ y) -> x -> Q.Circ y+simplify_classical f shape =+ let (input,bc,output) = Q.encapsulate_generic myCircErrMsg f shape in+ let list_output = getListWire output in+ let (bc',list_output') = quipperBCircuitSimpl bc list_output in+ Q.unencapsulate_generic (input,bc', Q.qdata_of_qubits output $ map Q.qubit_of_wire list_output') shape++-- | Like 'Q.classical_to_reversible', but also apply circuit optimization.+classical_to_reversible_optim :: (Q.QData qa, Q.QData qb) => (qa -> Q.Circ qb) -> ((qa,qb) -> Q.Circ (qa,qb))+classical_to_reversible_optim f = Q.classical_to_reversible (simplify_classical f)++-- | Like 'classical_to_reversible_optim', but insert the optimized+-- circuit as a boxed subroutine.+box_classical_to_reversible_optim :: (Q.QData qa, Q.QData qb) => String -> (qa -> Q.Circ qb) -> ((qa,qb) -> Q.Circ (qa,qb))+box_classical_to_reversible_optim s f = Q.classical_to_reversible (Q.box s $ simplify_classical f)+
@@ -0,0 +1,1007 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/ClassicalOptim/Simplification.hs" #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE DoAndIfThenElse #-}++-- | This module contains the core of the classical circuit+-- optimization algorithm.+module Quipper.Libraries.ClassicalOptim.Simplification where++import qualified Data.Map as M+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap.Strict as IM {- containers-0.5.2.1 -}++import qualified Control.DeepSeq as Seq++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++import qualified Quipper.Utils.Auxiliary as Q++import Quipper.Libraries.ClassicalOptim.Circuit+import Quipper.Libraries.ClassicalOptim.AlgExp++-- ----------------------------------------------------------------------+-- * Auxiliary definitions++-- | Internal definition of a trace, for debugging purposes. This is a+-- no-op, but can be replaced to turn on debugging.+trace :: String -> b -> b+trace a b = b++-- | Change a wire ID in a gate. The first two arguments are the old+-- and the new wire ID.+moveWire :: Wire -> Wire -> Gate -> Gate+moveWire from to NoOp = NoOp+moveWire from to (Init b w) = if (w == from) then error "moveWire" else (Init b w)+moveWire from to (Cnot w ctls) = Cnot w' ctls'+ where+ w' = if (from == w) then to else w+ ctls' = map moveCtls ctls+ moveCtls (w,b) = if (from == w) then (to,b) else (w,b)++-- | Flip the control on the given wire (from positive to negative or+-- vice versa).+flipCtl :: Wire -> Gate -> Gate+flipCtl _ NoOp = NoOp+flipCtl _ (Init b w) = Init b w+flipCtl w (Cnot w' ctls) = Cnot w' $ map (\(x,b) -> if (x == w) then (x,not b) else (x,b)) ctls++-- | Change a wire ID in a gate and flip the potential control.+moveWireFlip :: Wire -> Wire -> Gate -> Gate+moveWireFlip from to NoOp = NoOp+moveWireFlip from to (Init b w) = if (w == from) then error "moveWire" else (Init b w)+moveWireFlip from to (Cnot w ctls) = Cnot w' ctls'+ where+ w' = if (from == w) then to else w+ ctls' = map moveCtls ctls+ moveCtls (w,b) = if (from == w) then (to,b) else if (to == w) then (w,not b) else (w,b)++-- ----------------------------------------------------------------------+-- * Small, simple optimizations++-- | Suppress gates acting on garbage wires, i.e., wires that are not in the input set. +suppress_garbage :: [Gate] -> IS.IntSet -> [Gate]++suppress_garbage ((Cnot w ctls):gs) used = + if (IS.member w used) then g:gs1 else gs2+ where+ g = Cnot w ctls+ gs1 = suppress_garbage gs $ IS.union (IS.insert w used) $ IS.fromList $ L.map fst ctls+ gs2 = suppress_garbage gs used++suppress_garbage (g:gs) used = g:(suppress_garbage gs used)++suppress_garbage [] _ = []+++-- | Like 'suppress_garbage', but packaged in a manner that is friendly for composition.+suppressGarbageGates :: ([Gate],[Wire]) -> ([Gate],[Wire])++suppressGarbageGates (gs,out) = (reverse $ suppress_garbage (reverse gs) $ IS.fromList out, out)+++-- ----------------------------------------------------------------------+-- * Compression of wire numbering++-- $ As the optimization process goes on, many /init/ gates will end+-- up being discarded. The function 'compressWires' compacts the wire+-- numbering scheme to make a smaller circuit.++-- | Get the set of all wires used by the circuit.+getAllWires :: [Gate] -> IS.IntSet+getAllWires gs = L.foldl' IS.union IS.empty $ L.map aux gs+ where+ aux (Cnot w ctls) = IS.insert w $ L.foldl' (flip IS.insert) IS.empty $ L.map fst ctls+ aux (Init _ w) = IS.singleton w+ aux NoOp = IS.empty++-- | Get the set of wires initialized by the circuit.+getInitWires :: [Gate] -> IS.IntSet+getInitWires gs = L.foldl' IS.union IS.empty $ map aux gs+ where+ aux (Cnot _ _) = IS.empty+ aux (Init _ w) = IS.singleton w+ aux NoOp = IS.empty++-- | Get the set of input wires, i.e., the ones that are used but not initialized.+getInputWires :: [Gate] -> IS.IntSet+getInputWires gs = IS.difference (getAllWires gs) (getInitWires gs)++-- | Compress the wire numbering.+compressWires :: [Wire] -> ([Gate],[Wire]) -> ([Gate],[Wire])+compressWires inputwires (gs,output) = (gs',out')+ where+ iws = getInitWires gs+ begin = if inputwires == []+ then 0+ else 1 + (head $ reverse $ L.sort inputwires)+ end = begin + (IS.size iws)+ listmap = zip ([0..begin-1] ++ (IS.toAscList iws)) [0 .. end]+ remap = M.fromList $ trace (show listmap) listmap+ out' = map (remap M.!) output+ gs' = map (rewire remap) gs+ + rewire m (Cnot w ctls) = Cnot (m M.! w) $ map (\(x,b) -> (m M.! x, b)) ctls+ rewire m (Init b w) = Init b (m M.! w)+ rewire m NoOp = NoOp+++-- ----------------------------------------------------------------------+-- * A useful data structure++-- $ When considering a particular point in a circuit (i.e., in a list+-- of gates), to decide whether a given wire is used or controlled+-- before or after, we keep a data-structure 'UsedWire'.++-- | The type of gate IDs.+type GateId = Int++-- | A set of gate IDs.+type GateIdSet = IS.IntSet++-- | A map from wires to pairs of 'GateId's. The left member gives the+-- ID of the first gate using the wire, and the right member gives the+-- ID of the last gate using the wire.+type UsedWire = IM.IntMap GateIdSet++-- | Get the minimum of a set of gate IDs.+gateIdFindMin :: GateIdSet -> Maybe GateId+gateIdFindMin g = if (IS.null g) then Nothing else Just (IS.findMin g)++-- | Get the maximum of a set of gate IDs.+gateIdFindMax :: GateIdSet -> Maybe GateId+gateIdFindMax g = if (IS.null g) then Nothing else Just (IS.findMax g)++-- | Get the pair corresponding to the given wire.+pairUsedWire :: UsedWire -> Wire -> GateIdSet+pairUsedWire m w = IM.findWithDefault IS.empty w m++-- | Get the first gate using the wire in the future.+firstUsedWire :: UsedWire -> Wire -> Maybe GateId+firstUsedWire = curry $ gateIdFindMin . (uncurry pairUsedWire)++-- | Get the last gate using the wire in the past. Return 0 if none.+lastUsedWire :: UsedWire -> Wire -> GateId+lastUsedWire w w'= + case (curry $ gateIdFindMax . (uncurry pairUsedWire)) w w' of+ Just w -> w+ Nothing -> 0++-- | 'nextUsedGate' /ws/ /g/ /g/' /w/: Look for the next gate in /ws/+-- corresponding to wire /w/, starting from /g/. Return /g/' if none.+nextUsedGate :: UsedWire -> GateId -> GateId -> Wire -> GateId+nextUsedGate ws g g' w =+ case (do gs <- IM.lookup w ws; IS.lookupGT g gs) of+ Just g -> g+ Nothing -> g'++-- | For each wire, find the set of gates placing a control on it.+circuitControlWires :: GateId -> [Gate] -> UsedWire+circuitControlWires id gs = aux id IM.empty gs+ where+ aux _ m [] = m+ aux g m (Init _ _:gs) = aux (g+1) m gs+ aux g m ((Cnot _ ctls):gs) = aux (g+1) m' gs+ where+ wires = map fst ctls+ m' = L.foldl (\m'' w -> IM.alter (f g) w m'') m wires+ f g Nothing = Just $ IS.singleton g+ f g (Just s) = Just $ IS.insert g s+ aux g m (NoOp:_) = error "circuitControlWires cannot deal with NoOp"++-- | For each wire, find the set of gates acting on it with NOT.+circuitNotWires :: GateId -> [Gate] -> UsedWire+circuitNotWires id gs = aux id IM.empty gs+ where+ aux _ m [] = m+ aux g m (Init _ _:gs) = aux (g+1) m gs+ aux g m ((Cnot w _):gs) = aux (g+1) m' gs+ where+ m' = IM.alter (f g) w m+ f g Nothing = Just $ IS.singleton g+ f g (Just s) = Just $ IS.insert g s+ aux g m (_:gs) = aux (g+1) m gs++-- ----------------------------------------------------------------------+-- * Algebraic optimization method++-- $ To each wire in a circuit, we attach a set of formulas. At each+-- iteration, the wire that gets modified is updated with its new+-- value, using all the possible values, possibly together with a+-- fresh variable. At each iteration, we also strip away the+-- expressions that get too large. Here, the size of an algebraic+-- expression is measured by the 'exp_length' function.++-- | Calculate the size of an algebraic expression.+exp_length :: Exp -> Int+exp_length e = L.foldl' (+) 0 $ L.map (\x -> let y = IS.size x in seq y y) $ S.toList e++-- | Given a list of sets of expressions, form the conjunction of+-- every possible choice of one expression from each set. For example.+-- +-- > exp_list_and [{a,b}, {c,d}, {e,f}] = +-- > [a∧c∧e, a∧c∧f, a∧d∧e, a∧d∧f, b∧c∧e, b∧c∧f, b∧d∧e, b∧d∧f].+exp_list_and :: [S.Set Exp] -> S.Set Exp+exp_list_and [] = S.singleton exp_true+exp_list_and [l] = l+exp_list_and (h:k:t) = exp_list_and (S.fromList [exp_and x y | x <- S.toList h, y <- S.toList k]:t)++-- | Evaluate a control with respect to a state.+expEvalCtl :: (IM.IntMap (S.Set (Exp,Int))) -> (Wire,Bool) -> S.Set Exp+expEvalCtl m (w,True) = S.map fst (m IM.! w)+expEvalCtl m (w,False) = S.map exp_not $ S.map fst $ (IM.!) m w++-- | Evaluate a gate with respect to a state.+expEvalGate :: (IM.IntMap (S.Set (Exp,Int))) -> Gate -> IM.IntMap (S.Set (Exp,Int))++expEvalGate m (Init False w) = IM.insert w (S.singleton (exp_false,0)) m+expEvalGate m (Init True w) = IM.insert w (S.singleton (exp_true,1)) m+expEvalGate m NoOp = m++expEvalGate m (Cnot w ctls) = IM.insert w cnot m+ where+ ands = exp_list_and $ L.map (expEvalCtl m) ctls+ cnot = S.map (\x -> (x,exp_length x)) (S.fromList [exp_xor x y |+ x <- S.toList $ S.map fst $ (IM.!) m w,+ y <- S.toList ands ])++-- ----------------------------------------------------------------------+-- ** State of the optimization automaton++-- | The state of the automaton. This contains in particular the+-- current state, the past and future gates, and a fresh variable.+data ExpState = ExpState {+ gates_to_skip :: IM.IntMap Gate, -- ^ For use with 'stepSwapCirc'.+ allWiresInCirc :: IS.IntSet, -- ^ All the wires in the circuit.+ gateId :: GateId, -- ^ ID of the first gate in the future (starts at 1).+ usedControlWires :: UsedWire, -- ^ Location of the controls.+ usedNotWires :: UsedWire, -- ^ Location of the NOT gates.+ future :: [Gate], -- ^ Gates left to explore.+ past :: [Gate], -- ^ Gates already explored.+ expMap :: IM.IntMap (S.Set (Exp,Int)), -- ^ Algebraic state of the wires. Also contains the size of the expression, so we don't have to recompute it each time.+ freshVar :: Integer, -- ^ The next fresh wire.+ outWires :: [Wire], -- ^ The output wires.+ sizeCirc :: Int -- ^ Size of the circuit.+ }+++instance Seq.NFData Gate where+ rnf (Init a b) = a `seq` b `seq` ()+ rnf (Cnot w ctls) = ctls `Seq.deepseq` w `Seq.deepseq` ()+ rnf NoOp = ()++{-+instance Seq.NFData ExpState where+ rnf e = {-allWiresInCirc e `Seq.deepseq` gateId e `Seq.deepseq` usedControlWires e `Seq.deepseq` usedNotWires e `Seq.deepseq` future e `Seq.deepseq` past e `Seq.deepseq` expMap e `Seq.deepseq` freshVar e `Seq.deepseq` outWires e-} () `Seq.deepseq` ()+-}++-- | The initial state for a given set of parameters.+initExpState :: IS.IntSet -> [Wire] -> [Gate] -> ExpState+initExpState ws_in ws_out gs = ExpState {+ gates_to_skip = IM.empty,+ allWiresInCirc = getAllWires gs,+ gateId = 1,+ usedControlWires = circuitControlWires 1 gs,+ usedNotWires = circuitNotWires 1 gs,+ future = gs,+ past = [],+ expMap = IM.fromList $ L.map (\x -> (x, S.singleton (exp_var x, 1))) $ IS.toAscList ws_in,+ freshVar = fromIntegral $ (+) 1 $ IS.findMax ws_in,+ outWires = ws_out,+ sizeCirc = length gs+ }++-- ----------------------------------------------------------------------+-- ** The state monad++-- | The state monad corresponding to 'ExpState'.+data EvalCirc a = EvalCirc (ExpState -> (ExpState, a))++instance Monad EvalCirc where+ return x = EvalCirc (\y -> (y,x))+ (>>=) (EvalCirc c) f = EvalCirc (\s -> let (s',x) = c s in+ let (EvalCirc c') = f x in+ c' s')++instance Applicative EvalCirc where+ pure = return+ (<*>) = ap++instance Functor EvalCirc where+ fmap = liftM++-- ----------------------------------------------------------------------+-- ** Low-level access functions++-- | Construct an @'ExpState'@ out of an @'EvalCirc'@.+runEvalCirc :: IS.IntSet -> [Wire] -> [Gate] -> EvalCirc a -> ExpState+runEvalCirc ws_in ws_out gs (EvalCirc e) = fst $ e $ initExpState ws_in ws_out gs++-- | Retrieve the state.+getExpState :: EvalCirc ExpState+getExpState = EvalCirc (\s -> (s,s))++-- | Set the state.+setExpState :: ExpState -> EvalCirc ()+setExpState s = EvalCirc (\_ -> (s,()))++-- ----------------------------------------------------------------------+-- ** Higher-level access functions++-- | Create a fresh variable+newFreshVar :: EvalCirc Integer+newFreshVar = do+ s <- getExpState+ let v = freshVar s+ setExpState (s { freshVar = v + 1 }) + return v++-- | Pull a new gate to be analyzed out of the future.+pullNewGate :: EvalCirc (Maybe Gate)+pullNewGate = do+ s <- getExpState+ case (future s) of+ (h:t) -> do setExpState (s { future = t } )+ return (Just h)+ [] -> return Nothing++-- | Modify the future gates.+changeFuture :: [Gate] -> EvalCirc ()+changeFuture gs = do+ s <- getExpState+ setExpState (s { future = gs } )+ return ()++-- | Update the future using the given parameter function. Return two sets+-- of 'gateId's that got modified: the first set concerns the controls,+-- the second set the NOT gates.+updateFuture :: (Gate -> Gate) -> EvalCirc (IS.IntSet,IS.IntSet)+updateFuture f = do+ s <- getExpState+ let ((_,!gsModifCtls,!gsModifNots),new_future) = + L.mapAccumL (\(gid,gs,gs') g -> let g' = f g in+ ((+ gid+1+ ,+ if (ctlsOfGate g == ctlsOfGate g') + then gs+ else IS.insert gid gs+ ,+ if (wireOfGate g == wireOfGate g') + then gs'+ else IS.insert gid gs'+ ),+ g'))+ (1 + (gateId s), IS.empty,IS.empty) (future s)+ changeFuture new_future+ + return (gsModifCtls,gsModifNots)++-- | Store a gate in the past.+storeOldGate :: Gate -> EvalCirc ()+storeOldGate g = do+ s <- getExpState+ let p = past s+ seq g $ seq p $ setExpState (s { past = g:p } )+ return ()++-- | Increase the '@gateId@' (i.e., go forward).+incrGateId :: EvalCirc ()+incrGateId = do+ s <- getExpState+ setExpState (s { gateId = 1 + (gateId s) } )+ return ()++-- | Get the set of all wires.+getAllWiresInCirc :: EvalCirc IS.IntSet+getAllWiresInCirc = do+ s <- getExpState+ return (allWiresInCirc s)++-- | Set the set of all wires.+setAllWiresInCirc :: IS.IntSet -> EvalCirc ()+setAllWiresInCirc ws = do+ s <- getExpState+ ws `seq` setExpState (s {allWiresInCirc = ws})+ return ()++-- | Remove a gate from the set of all wires.+removeFromAllWiresInCirc :: Int -> EvalCirc ()+removeFromAllWiresInCirc w = do+ ws <- getAllWiresInCirc+ setAllWiresInCirc $ IS.delete w ws+ return ()++-- | Get the algebraic representation of the set of wires.+getExpMap :: EvalCirc (IM.IntMap (S.Set (Exp,Int)))+getExpMap = do+ s <- getExpState+ s `seq` return (expMap s)++-- | Set the algebraic representation of the state of wires.+setExpMap :: (IM.IntMap (S.Set (Exp,Int))) -> EvalCirc ()+setExpMap m = do+ s <- getExpState+ m `seq` setExpState (s { expMap = m } )+ return ()++-- | Update the database recording the controlled wires.+updateUsedControlWires :: (UsedWire -> UsedWire) -> EvalCirc ()+updateUsedControlWires f = do+ s <- getExpState+ let c = f $ usedControlWires s+ c `seq` setExpState (s { usedControlWires = c } )+ return ()++-- | Update the database recording the NOT gates.+updateUsedNotWires :: (UsedWire -> UsedWire) -> EvalCirc ()+updateUsedNotWires f = do+ s <- getExpState+ let c = f $ usedNotWires s+ c `seq` setExpState (s { usedNotWires = c } )+ return ()++-- | Update the list of output wires.+updateOutWires :: ([Wire] -> [Wire]) -> EvalCirc ()+updateOutWires f = do+ s <- getExpState+ let c = f $ outWires s+ c `seq` setExpState (s { outWires = c } )+ return ()++-- | Add a gate ID to the list of gates to skip.+addToSkipGates :: GateId -> Gate -> EvalCirc ()+addToSkipGates id g = do+ s <- getExpState+ let c = IM.insert id g (gates_to_skip s)+ c `seq` setExpState (s {gates_to_skip = c} )+ return ()++-- | Send a gate to the end of the future.+sendEndOfTime :: Gate -> EvalCirc ()+sendEndOfTime g = do+ s <- getExpState+ changeFuture ((future s) ++ [g])+ return ()++-- | Place a gate at the given gate ID in the future.+shiftGate :: Gate -> GateId -> EvalCirc ()+shiftGate g x = do+ s <- getExpState+ let (!head, !tail) = splitAt x (future s)+ let z = head ++ [g] ++ tail+ z `Seq.deepseq` changeFuture z+ return ()++-- ----------------------------------------------------------------------+-- ** Auxiliary functions++-- | @pairEqualExp m1 m2 ws@: returns a list of pairs of wires @(x,y)@+-- such that @m2 x = m1 x = m1 y@.+pairEqualExp :: (IM.IntMap [Exp]) -> (IM.IntMap [Exp]) -> [Wire] -> [(Wire,Wire)]+pairEqualExp m1 m2 ws = + L.map fst $ L.filter aux $ L.zip pair_ws (L.map value pair_ws)+ where+ all_pairs l = [(x,y) | x <- l, y <- l]+ pair_ws = all_pairs ws+ value (x,y) = (m2 IM.! x, m1 IM.! x, m1 IM.! y)+ aux ((_,_),(a,b,c)) = a == b && b == c++-- | From a set of expressions (annotated with sizes), prune the ones+-- whose size is larger than /n/.+pruneListExp :: Int -> S.Set (Exp,Int) -> S.Set (Exp,Int)+pruneListExp n l = S.filter (\x -> snd x <= n) l+++-- ----------------------------------------------------------------------+-- ** The algebraic optimization automaton + +-- | Perform a set of filters acting on one gate at a time, looking+-- for:+-- +-- * gates having no effect;+--+-- * orphan NOT-gates (i.e. NOT gates negating an out-wire) ;+-- +-- * simple copy-cats (both positive and negative) ;+--+-- * hidden copy-cats.+--+-- Return 'False' when the end of the circuit is reached, 'True' otherwise. +stepEvalCirc :: EvalCirc Bool+stepEvalCirc = do+ m_before <- getExpMap+ trace ("the state of the system is " ++ (show $ m_before)) $ return ()+ s <- getExpState+ if ((gateId s) `mod` 1000 == 0) then trace ("Timestamp... " ++ (show (gateId s))) (return ()) else return ()+ s <- getExpState+ trace ("outside wires " ++ (show $ outWires s)) $ return ()+ maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ case maybe_g of++ Nothing -> return False+ + Just g -> do -- analyze the gate+ m_before <- getExpMap+ let m_after = expEvalGate m_before g+ + case g of+ + NoOp -> error "stepEvalCirc cannot deal with NoOp"+ + Init b w | not ((IM.member w $ usedNotWires s) || (IM.member w $ usedControlWires s) || L.elem w (outWires s))-> do + trace "got an orphan init, removing it" $ return ()+ storeOldGate NoOp -- store a placeholder for the gate+ incrGateId+ removeFromAllWiresInCirc w+ -- we could also clean expMap from the reference to w but I think it makes no gain+ return True+ + Init _ _ -> do+ trace "got a regular init" $ return ()+ storeOldGate g+ setExpMap m_after+ incrGateId+ return True+ + Cnot w _ | not $ S.null $ S.intersection (m_before IM.! w) (m_after IM.! w) -> do+ trace "got a cnot where no change happened..." $ return ()+ trace (show m_before) $ return ()+ trace (show m_after) $ return ()+ storeOldGate NoOp+ incrGateId+ return True++ Cnot w [] | not (L.elem w $ outWires s) -> do+ trace "got a not-gate that can be removed..." $ return ()+ s <- getExpState+ -- update future+ changeFuture $ L.map (flipCtl w) $ future s+ s <- getExpState+ trace (show $ future s) $ return ()+ storeOldGate NoOp+ incrGateId+ return True+ + + Cnot w ctls | otherwise -> do+ trace "got a general cnot" $ return ()+ trace ("state after the gate is " ++ (show m_after)) $ return ()+ + allWs <- getAllWiresInCirc+ s <- getExpState+ let my_elem x = not (L.elem x $ outWires s)+ let all_ws = IS.toAscList $ IS.filter future_ctl $+ IS.filter (\x -> my_elem x) $ -- not (L.elem x $ outWires s)) $+ IS.filter (\x -> not $ S.null $ + S.intersection (m_after IM.! x)+ (m_after IM.! w)) $+ IS.filter (w /=) allWs -- IS.fromList $ L.map fst ctls + where+ future_ctl x = + (lastUsedWire (usedNotWires s) x) <= gateId s+ &&+ (lastUsedWire (usedNotWires s) w) <= gateId s++ let all_ws_neg = IS.toAscList $ IS.filter future_ctl $+ IS.filter (\x -> not (L.elem x $ outWires s)) $+ IS.filter (\x -> not $ S.null $ + S.intersection (m_after IM.! x)+ (S.map (\(e,i) -> (exp_not e, i)) (m_after IM.! w))) $+ IS.filter (w /=) $ IS.fromList $ L.map fst ctls+ where+ future_ctl x = + (lastUsedWire (usedNotWires s) x) <= gateId s+ &&+ (lastUsedWire (usedNotWires s) w) <= gateId s++ trace ("List of outside wires: " ++ (show $ outWires s)) (return ())+ trace ("List of available wires: " ++ (show all_ws)) (return ())+ trace ("List of available wires with neg: " ++ (show all_ws_neg)) (return ())+ + + case all_ws of+ + [] -> do+ + case all_ws_neg of+ [] -> do+ + -- There is no "simple" copy-cat...+ -- Let's try to find a hidden one.+ + s <- getExpState++ -- This helper function take a wire and look in+ -- the past for the closest cnot acting on it+ let getOlderCnot w = case (do set <- IM.lookup w (usedNotWires s); IS.lookupLT (gateId s) set) of+ Nothing -> Nothing -- there is no previous not+ Just g' -> -- there is one not... let's check that it is a cnot+ case ((past s) !! ((gateId s) - g' - 1)) of+ Cnot _ [ctl] -> Just (g',ctl)+ _ -> Nothing+ + -- Helper acting on controls: only return+ -- something if it is a single control.+ let getOlderCnot_actOnCtls w1 [(w,b)] = do -- monad Maybe+ other_ctl <- getOlderCnot w1+ other_ctl `seq` return ((w,b),other_ctl)+ getOlderCnot_actOnCtls _ _ = Nothing+ + let retrieveHiddenCnot w1 ctls = do -- monad Maybe+ -- if (L.elem w $ outWires s) then Nothing+ -- else return ()+ ((w2,b2),(g',(w3,b3))) <- getOlderCnot_actOnCtls w1 ctls+ -- make sure w2 and w3 are distinct+ if (w2 == w3) then Nothing else return ()+ let m = m_after+ -- check for the property w1 == w2 oplus w3+ if (S.null $ S.intersection+ (S.fromList [exp_xor x y | (x,_) <- S.toList (m IM.! w2), (y,_) <- S.toList (m IM.! w3)])+ (S.fromList [x | (x,_) <- S.toList (m IM.! w1)]))+ then Nothing+ -- We have two CNOT candidates for hidden copy-cat.+ else if ((not (L.elem w2 $ outWires s))+ &&+ (lastUsedWire (usedNotWires s) w2) <= gateId s+ &&+ (lastUsedWire (usedControlWires s) w2) <= gateId s)+ then Just ((w2,b2),(w3,b3))+ else if ((not (L.elem w3 $ outWires s))+ &&+ (lastUsedWire (usedNotWires s) w3) <= g'+ &&+ (lastUsedWire (usedControlWires s) w3) <= g')+ then Just ((w3,b3),(w2,b2))+ else Nothing+ + case retrieveHiddenCnot w ctls of+ Just ((w2,b2),(w3,b3)) -> -- we have a hidden cnot candidate. Great.+ -- w2 is the wire that is not used with NOT in future+ do+ + trace "found one hidden copy-cat" $ return ()+ updateOutWires $ map (\x -> if x == w then w2 else x) + + (gsModifCtls,gsModifNots) <- updateFuture $ moveWire w w2+ + trace ("moving " ++ (show w) ++ " to " ++ (show w2)) $ return ()+ trace (show gsModifCtls) $ return ()+ trace (show gsModifNots) $ return ()+ + + s <- getExpState+ trace ("before: usedNotWire = " ++ (show $ usedNotWires s)) $ return ()++ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls) w2 $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ + updateUsedControlWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w2 c+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (gateId s) gs+ Nothing -> Just $ IS.singleton (gateId s)) w3 c+ + updateUsedNotWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifNots+ Nothing -> Just gsModifNots) w2 $ + IM.update (\gs -> Just $ IS.difference gs gsModifNots) w c+ + updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w $+ IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (gateId s) gs+ Nothing -> Just $ IS.singleton (gateId s)) w2 c+ + s <- getExpState+ trace ("after: usedNotWire = " ++ (show $ usedNotWires s)) $ return ()++ -- Update ExpMap+ setExpMap $ IM.insert w (m_before IM.! w) $+ IM.insert w2 (m_after IM.! w) m_after+ + storeOldGate $ Cnot w2 [(w3,True)]+ incrGateId+ return True+ + _ -> -- No hidden Cnot, let's proceed...+ do+ let mw = m_after IM.! w+ f <- if ((S.foldl' (\a (_,i) -> min a i) 3 mw) <= 1) + then return id + else do+ v <- newFreshVar+ return (S.insert (exp_var $ fromIntegral v, 1))+ setExpMap $ IM.adjust (\a -> pruneListExp 3 a) w $ + IM.adjust f w m_after+ + storeOldGate g+ incrGateId+ return True++ -----------------+ -- Case of simple copy-cats+ (w':_) -> do+ s <- getExpState+ updateOutWires $ map (\x -> if x == w then w' else x)+ + s <- getExpState+ trace (show $ future s) $ return ()+ (gsModifCtls,_) <- updateFuture $ moveWireFlip w w'+ + -- update expMap: now, w is null and w' is not(old w)+ expMap <- getExpMap+ + setExpMap $ IM.insert w (m_before IM.! w) $+ IM.insert w' (S.map (\(e,i) -> (exp_not e,i)) (expMap IM.! w')) expMap+ + + + trace ("moving " ++ (show w) ++ " to " ++ (show w')) $ return ()+ trace (show gsModifCtls) $ return ()+ s <- getExpState+ trace (show $ future s) $ return ()+ + s <- getExpState+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls) w' $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w c++ storeOldGate (Cnot w' []) -- Set a flip on the w' wire + + incrGateId+ + return True+ + + + (w':_) -> do+ + s <- getExpState+ updateOutWires $ map (\x -> if x == w then w' else x) ++ s <- getExpState+ trace (show $ future s) $ return ()+ trace ("usedNotWire = " ++ (show $ usedNotWires s)) $ return ()+ (gsModifCtls,_) <- updateFuture $ moveWire w w'+ + trace ("moving " ++ (show w) ++ " to " ++ (show w')) $ return ()+ trace (show gsModifCtls) $ return ()+ s <- getExpState+ trace (show $ future s) $ return ()+ + s <- getExpState+ updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.union gs gsModifCtls+ Nothing -> Just gsModifCtls+ ) w' $ + IM.update (\gs -> Just $ IS.difference gs gsModifCtls) w c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w c+ + storeOldGate NoOp -- replace g with NoOp so that gateId stays accurate+ incrGateId+ + return True+++-- | Shuffle the circuit by sending the CNOT gates as far as+-- possible (i.e., until they hit a control, or to the end).+-- Return 'False' when the end of the circuit is reached, 'True' otherwise. +stepSwapCirc :: EvalCirc Bool+stepSwapCirc = do+ s <- getExpState+ + case (IM.lookup (gateId s) (gates_to_skip s)) of+ + Just g -> do + + storeOldGate g+ incrGateId+ return True+ + Nothing -> do+ + maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ if ((gateId s) `mod` 1000 == 0) then trace ("Timestamp (swap)... " ++ (show (gateId s))) {-(s `Seq.deepseq` (setExpState s))-} (return ()) else return ()+ case maybe_g of+ + Nothing -> return False+ + Just g@(Cnot w1 [(w2,b2)]) | IM.notMember (gateId s) (gates_to_skip s) -> do -- got a CNOT+ + trace ("got a cnot to analyze " ++ (show $ gateId s) ++ " " ++ (show $ gates_to_skip s)) $ return ()+ + let id = min (nextUsedGate (usedNotWires s) (gateId s) (1 + sizeCirc s) w2) $+ (nextUsedGate (usedControlWires s) (gateId s) (1 + sizeCirc s) w1)+ + trace ("found id = " ++ (show id)) $ return ()+ + if ( id > 1 + gateId s ) -- && (id <= (sizeCirc s) )+ then do ------------- there is something to move!+ trace ("can be shifted to " ++ (show (id - 1))) $ return ()+ + addToSkipGates (id - 1) g+ -- shiftGate g (id - 1 - (gateId s))+ + s <- getExpState+ trace (show $ future s) $ return ()+ + -- Remove references to (gateId s)+ + updateUsedControlWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w2 c+ updateUsedNotWires $ \c -> + IM.update (\gs -> Just $ IS.delete (gateId s) gs) w1 c+ + -- Shift the ones between (gateId s) and id+ + updateUsedNotWires $+ IM.map $ IS.map $ \x -> if (x <= gateId s) || (x >= id) then x + else x - 1+ updateUsedControlWires $+ IM.map $ IS.map $ \x -> if (x <= gateId s) || (x >= id) then x + else x - 1+ + s <- getExpState+ let z = IM.mapKeys (\x -> if (x <= gateId s) || (x >= id) then x + else x - 1) (gates_to_skip s) in+ z `seq` setExpState (s { gates_to_skip = z} )+ + -- Set g in position (id - 1)+ + updateUsedControlWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (id - 1) gs+ Nothing -> Just $ IS.singleton (id - 1)) w2 c+ updateUsedNotWires $ \c -> + IM.alter (\maybe_gs -> case maybe_gs of+ Just gs -> Just $ IS.insert (id - 1) gs+ Nothing -> Just $ IS.singleton (id - 1)) w1 c+ + -- Make sure we skip (id - 1) later on.+ + + else do ------------- nothing to move... + trace "cannot be shifted" $ return ()+ storeOldGate g+ incrGateId+ + return True+ + Just g -> do+ trace ("got a random " ++ (show g)) $ return ()+ storeOldGate g+ incrGateId+ return True++++-- | A more elementary version of @'stepSwapCirc'@: shuffle the+-- circuit by sending to the end all the NOT gates that can be sent+-- there. Return 'False' when the end of the circuit is reached,+-- 'True' otherwise.+stepSwapCirc_simple :: EvalCirc Bool+stepSwapCirc_simple = do+ maybe_g <- pullNewGate+ trace ("pulled new gate " ++ (show maybe_g)) $ return ()+ s <- getExpState+ case maybe_g of++ Nothing -> return False+ + Just g | (gateId s) == (length $ past s) + (length $ future s) -> do+ storeOldGate g+ return False + + Just g@(Cnot w1 [(w2,b2)]) | + (lastUsedWire (usedNotWires s) w2) <= gateId s && + (lastUsedWire (usedNotWires s) w1) <= gateId s &&+ (lastUsedWire (usedControlWires s) w1) <= gateId s -> do -- got a CNOT++ trace "got a cnot that can be sent to the end" $ return ()+ + sendEndOfTime g+ -- do not store gate, but increase gateId+ incrGateId+ return True+ + Just g -> do+ storeOldGate g+ incrGateId+ return True++-- ----------------------------------------------------------------------+-- ** Some wrappers++-- | Run the monad until 'False' occurs.+runWhile :: Monad m => (a -> Bool) -> m a -> m ()+runWhile f c = do+ r <- c+ if f r then runWhile f c else return ()++-- | Strip the 'NoOp' gates from a list of gates.+stripNoOp :: [Gate] -> [Gate]+stripNoOp = L.filter (/= NoOp) ++-- | Wrapper around 'stepEvalCirc'.+alg_simplify :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_simplify (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepEvalCirc)++-- | Wrapper around 'stepSwapCirc'.+alg_swap :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_swap (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepSwapCirc)+++-- | Wrapper around 'stepSwapCirc_simple'.+alg_swap_simple :: ([Gate],[Wire]) -> ([Gate],[Wire])+alg_swap_simple (gs,out) = (stripNoOp gs',out')+ where+ gs' = (reverse $ past s) ++ (future s)+ out' = outWires s+ ws_in = getAllWires gs+ s = runEvalCirc ws_in out gs $ trace "Starting new circuit!" (runWhile id stepSwapCirc_simple)++-- ----------------------------------------------------------------------+-- * Multi-pass optimization++-- | Auxiliary function. Simultaneously compute the maximum of the+-- lengths of two lists, and their point-wise equality.+is_equal_list :: Eq a => [a] -> [a] -> Int -> (Int,Bool)++is_equal_list [] [] n = (n,True)+is_equal_list (h1:t1) (h2:t2) n | h1 == h2 = is_equal_list t1 t2 (n+1)+is_equal_list t1 t2 n = (n + max (length t1) (length t2),False)++-- | Get the list of initialized wires from a circuit.+get_list_init :: [Gate] -> [Wire]+get_list_init ((Init _ w):gs) = w:(get_list_init gs)+get_list_init (g:gs) = get_list_init gs+get_list_init [] = []++-- | Do several passes of @'alg_simplify'@ until it reaches a fixed point.+simplRec' :: ([Gate],[Wire]) -> ([Gate],[Wire])+simplRec' (l,output) = trace (show (l,output)) $ + let (l',output') = alg_simplify (l, output) in+ let (n,b) = is_equal_list l l' 0 in+ if b then (l,output) + else trace (show n) simplRec' $ suppressGarbageGates (l',output')++-- | Do several passed of @'alg_swap'@ followed with @'simplRec'@+-- until it reaches a fixed point.+simplRec :: ([Gate],[Wire]) -> ([Gate],[Wire])+simplRec (l1,o1) = + let (l3,o3) = simplRec' $ alg_swap (l1,o1) in+ let (n,b) = is_equal_list l1 l3 0 in+ if b then (l3,o3) + else trace "Swapping!" $ simplRec $ (l3,o3)
@@ -0,0 +1,24 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Decompose.hs" #-}+-- | Functions to decompose circuits into various gate bases.++module Quipper.Libraries.Decompose ( + -- * Precision+ Precision,+ bits,+ digits,+ -- * Phase+ KeepPhase,+ -- * Gate bases+ GateBase (..),+ gatebase_enum,+ -- * Generic decomposition+ decompose_generic,+) where++import Quipper.Libraries.Decompose.CliffordT+import Quipper.Libraries.Decompose.GateBase+import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.Synthesis
@@ -0,0 +1,1071 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Decompose/CliffordT.hs" #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}++-- | This module provides transformers for decomposing circuits into+-- the Clifford+/T/ gate base.+-- +-- * [bold Control trimming.] This transformer uses doubly-controlled+-- /iX/-gates to reduce the number of controls on gates. Specifically,+-- it ensures that not-gates, Pauli /X/-, /Y/-, and /Z/-gates, and+-- /iX/-gates have at most two controls; that phase gates of the form+-- Diag(1, φ) have no controls; and that all other gates have at most+-- one control.+-- +-- * [bold Approximate Clifford+/T/ decomposition.] This decomposes+-- all rotation and phase gates into Clifford+/T/ up to a given+-- precision ε, using an approximate synthesis algorithm. Other gates+-- are unchanged.+-- +-- * [bold Exact Clifford+/T/ decomposition.] This decomposes all+-- gates that are exactly representable in the Clifford+/T/ base into+-- single-qubit Clifford gates, /T/, /T/[sup †], and singly-controlled+-- not-gates (with positive or negative control). Rotation and phase+-- gates are left unchanged.+-- +-- * [bold Standard gate set.] We define the standard gate set to+-- consist of the gates /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/,+-- /T/[sup †], and /CNOT/. This transformer decomposes all gates that+-- remain after applying both approximate and exact Clifford+/T/+-- decomposition into the standard gate set. If the transformer+-- encounters gates that are not single-qubit Clifford gates, /T/,+-- /T/[sup †], or singly-controlled not-gates (with positive or+-- negative control), then the output is still semantically correct,+-- but may not be in the standard gate set. This transformer+-- suppresses global phases.+-- +-- * [bold Strict gate set.] We define the strict gate set to consist+-- of the gates /H/, /S/, /T/, and /CNOT/. This transformer decomposes+-- all gates that remain after applying both approximate and exact+-- Clifford+/T/ decomposition into the strict gate set. If the+-- transformer encounters gates that are not single-qubit Clifford+-- gates, /T/, /T/[sup †], or singly-controlled not-gates (with+-- positive or negative control), then the output is still+-- semantically correct, but may not be in the strict gate set. This+-- transformer suppresses global phases.+-- +-- These above transformers may be applied in any order. Control+-- trimming is primarily for demonstration purposes; it does not need+-- to be combined with the other transformers as they do their own+-- trimming. The exact and approximate Clifford+/T/ decompositions can+-- be applied in either order; since they each target a different set+-- of gates, they must both be performed to obtain a complete+-- decomposition into the Clifford+/T/ gate set. The standard and+-- strict transformers assume that their input has already been+-- pre-processed by the exact and approximate transformers.++module Quipper.Libraries.Decompose.CliffordT where++import Quipper+import Quipper.Internal++-- Stuff we need to import because the Dynamic Transformer interface+-- is not yet stable.+import Quipper.Internal.Circuit (BoxId, TypedSubroutine(..), OCircuit(..))+import Quipper.Internal.Transformer+import Quipper.Internal.Generic (provide_subroutine_generic, transform_unary_dynamic)+import Quipper.Internal.Monad (endpoints_of_wires_in_arity, identity_dynamic_transformer, provide_subroutines, named_rotation_qulist_at, named_gate_qulist_at, subroutine)++import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.GateDecompositions+import Quipper.Libraries.Synthesis++import Quipper.Utils.Auxiliary (optional)+import Quantum.Synthesis.SymReal++import Control.Monad (when)+import System.Random+import Text.Printf++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | A version of 'with_combined_controls' that uses doubly-controlled+-- /iX/-gates to do the decomposition. For example, when /n/=2, this+-- yields circuits such as the following:+-- +-- \[image with_combined_controls2.png]+-- +-- And for /n/=1:+-- +-- \[image with_combined_controls1.png]+with_combined_controls_iX :: Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_iX = with_combined_controls cc_iX_plain_at++-- | A version of 'with_combined_controls_iX' that further decomposes+-- the doubly-controlled /iX/-gates into the Clifford+/T/ gate base.+with_combined_controls_CT :: Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_CT = with_combined_controls cc_iX_at++-- | Turn a list of signed controls into a list of positive quantum+-- controls, by applying all classical controls directly, and+-- conjugating any negative quantum controls by /X/. Call the+-- inner block with those quantum controls. Usage:+-- +-- > with_normalized_controls ctrls $ \qs -> do+-- > <<<code using qs>>>+with_normalized_controls :: [Signed Endpoint] -> ([Qubit] -> Circ a) -> Circ a+with_normalized_controls cs f = do+ let (qcs, ccs) = partition_controls cs+ with_controls ccs $ do+ aux qcs []+ + where+ aux [] qs = f (reverse qs)+ aux (qc:qcs) qs = do+ with_signed_qubit qc $ \q -> do+ aux qcs (q:qs)++-- | Like 'with_normalized_controls', but use /HSSH/ instead of /X/,+-- so as not to leave the /H/, /S/, /T/, /CNot/ gate base. +with_normalized_controls_HS :: [Signed Endpoint] -> ([Qubit] -> Circ a) -> Circ a+with_normalized_controls_HS cs f = do+ let (qcs, ccs) = partition_controls cs+ with_controls ccs $ do+ aux qcs []+ + where+ aux [] qs = f (reverse qs)+ aux (qc:qcs) qs = do+ with_signed_qubit_HS qc $ \q -> do+ aux qcs (q:qs)+ with_signed_qubit_HS (Signed q True) f = f q+ with_signed_qubit_HS (Signed q False) f = do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ b <- f q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ return b++-- | Negate the number if the boolean is true.+negate_if :: (Num r) => Bool -> r -> r+negate_if False x = x+negate_if True x = -x++-- ----------------------------------------------------------------------+-- * Transformers++-- ----------------------------------------------------------------------+-- ** Control trimming++-- | This transformer makes sure that not-gates, Pauli /X/-, /Y/-, and+-- /Z/-gates, and /iX/-gates have at most two controls; that phase+-- gates of the form Diag(1, φ) have no controls; and that all other+-- gates have at most one control.+trimcontrols_transformer :: Transformer Circ Qubit Bit+trimcontrols_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 2 cs $ \qcs -> do+ qnot_at q `controlled` qcs+ return ([q], [], cs)+trimcontrols_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ qmultinot qs `controlled` qcs+ return (qs, [], cs)+trimcontrols_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ hadamard q `controlled` qcs+ return ([q], [], cs)+trimcontrols_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ swap_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+trimcontrols_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ gate_W_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+trimcontrols_transformer gate@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are allowed two controls+ ("X", [q], []) -> 2+ ("Y", [q], []) -> 2+ ("Z", [q], []) -> 2+ ("iX", [q], []) -> 2+ _ -> 1+ with_combined_controls_iX n cs $ \qcs -> do+ named_gate_qulist_at name inv qs gctls `controlled` qcs+ return (qs,gctls,cs)+trimcontrols_transformer (T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ with_combined_controls_iX 1 cs $ \qcs -> do+ global_phase_anchored t qs `controlled` qcs+ return cs+trimcontrols_transformer gate@(T_QRot name _ _ inv theta ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are decomposed to zero controls+ ("R(2pi/%)", [q], []) -> 0+ ("T(%)", [q], []) -> 0+ _ -> 1+ case n of+ 0 -> do+ let [q] = qs+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ let [c] = qcs+ with_signed_qubit c $ \q -> do+ named_rotation_qulist_at name inv theta [q] gctls+ return (qs,gctls,cs)+ _ -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ named_rotation_qulist_at name inv theta qs gctls `controlled` qcs+ return (qs,gctls,cs)++-- The subroutine transformer clause is called when a subroutine gate appears,+-- for now we decompose the controls just like for other gates. The recursive+-- decomposition of a subroutine is taken care of in the dynamic transformer.+trimcontrols_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \namespace ws cs -> without_controls_if ncf $ do+ let qws = [w | Endpoint_Qubit w <- ws]+ provide_subroutines namespace+ with_combined_controls_iX 1 cs $ \qcs -> do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` qcs+ return (vs,cs)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+trimcontrols_transformer gate@(T_CNot _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CGate _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CSwap _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QPrep _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QUnprep _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QInit _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CInit _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_QMeas _) = identity_transformer gate+trimcontrols_transformer gate@(T_QDiscard _) = identity_transformer gate+trimcontrols_transformer gate@(T_CDiscard _) = identity_transformer gate+trimcontrols_transformer gate@(T_DTerm _ _) = identity_transformer gate+trimcontrols_transformer gate@(T_Comment _ _ _) = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Approximate Clifford+/T/ decomposition++-- | This transformer decomposes rotation and phase gates into the+-- Clifford+/T/ basis, using the approximate synthesis algorithm of+-- <http://arxiv.org/abs/1212.6253>. Other gates+-- are unchanged. +-- +-- This transformer requires a precision parameter, as well as a+-- source of randomness. The 'KeepPhase' flag indicates whether to+-- respect global phases.+approx_ct_transformer :: (RandomGen g) => KeepPhase -> Precision -> g -> Transformer Circ Qubit Bit+approx_ct_transformer kp prec g gate@(T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ comment_with_label (printf "ENTER: GPhase (t=%f)" t) cs "c"+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> approximate_synthesis_phase kp prec theta g+ where+ theta = pi * to_real t+ [c] -> do+ with_signed_qubit c $ \q -> do+ approximate_synthesis_phase_ctrl kp prec theta g q+ return ()+ where+ theta = pi * to_real t+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)+ comment_with_label "EXIT: GPhase" cs "c"+ return cs+approx_ct_transformer kp prec g gate@(T_QRot name _ _ inv t ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ comment_with_label (printf "ENTER: %s%s (t=%f)" name (optional inv "*") t) (qs,cs) ("q","c")+ case (name, inv, qs, gctls) of+ ("exp(-i%Z)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_zrot prec theta g q+ return ()+ where+ theta = negate_if inv (2 * to_real t)+ [c] -> do + with_signed_qubit c $ \q2 -> do+ approximate_synthesis_zrot_ctrl prec theta g q q2+ return ()+ where+ theta = negate_if inv (2 * to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("exp(% pi i)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (to_real (pi*t))+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q2+ return ()+ where+ theta = negate_if inv (to_real (pi*t))+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("R(2pi/%)", inv, [q], []) -> do+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (2 * pi / to_real t)+ [c] -> do+ with_signed_qubit c $ \q1 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q1+ return ()+ where+ theta = negate_if inv (2 * pi / to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("T(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 (Signed (Endpoint_Qubit q) True : cs) $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (-to_real t)+ [c] -> do+ with_signed_qubit c $ \q1 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q1+ return ()+ where+ theta = negate_if inv (-to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("G(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_phase kp prec theta g+ return ()+ where+ theta = negate_if inv (-to_real t)+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_phase_ctrl kp prec theta g q2+ return ()+ where+ theta = negate_if inv (-to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ ("Rz(%)", inv, [q], []) -> do+ with_combined_controls_iX 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ approximate_synthesis_zrot prec theta g q+ return ()+ where+ theta = negate_if inv (to_real t)+ [c] -> do+ with_signed_qubit c $ \q2 -> do+ approximate_synthesis_zrot_ctrl prec theta g q q2+ return ()+ where+ theta = negate_if inv (to_real t)+ _ -> error ("approx_ct_transformer: internal error. " ++ show gate)++ _ -> error ("approx_ct_transformer: unimplemented gate: " ++ show gate)+ comment_with_label (printf "EXIT: %s%s (t=%f)" name (optional inv "*") t) (qs,cs) ("q","c")+ return (qs, gctls, cs)+approx_ct_transformer kp prec g gate = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Exact Clifford+/T/ decomposition++-- | This transformer decomposes all gates that permit exact+-- Clifford+/T/ representations into the following concrete gate base+-- for Clifford+/T/:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Classical controls and classical gates are not subject to the gate+-- base, and are left untouched.+-- +-- Rotations and phase gates are left unchanged by this transformer,+-- but any controls on those gates will be decomposed. +exact_ct_transformer :: Transformer Circ Qubit Bit+exact_ct_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 2 cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ [c1,c2] -> do+ toffoli_AMMR_at q c1 c2+ qcs -> do+ qnot_at q `controlled` qcs + return ([q], [], cs)+exact_ct_transformer gate@(T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [] -> do+ sequence_ [ gate_X_at q | q <- qs ]+ qcs -> do+ sequence_ [ qnot_at q | q <- qs ] `controlled` qcs+ return (qs, [], cs)+exact_ct_transformer gate@(T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ cH_AMMR_at q c+ qcs -> do+ hadamard_at q `controlled` qcs+ return ([q], [], cs)+exact_ct_transformer gate@(T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ fredkin_at q1 q2 c+ qcs -> do+ swap_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+exact_ct_transformer gate@(T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_CT 1 cs $ \qcs -> do+ case qcs of+ [c] -> do+ controlled_W_at q1 q2 c+ qcs -> do+ gate_W_CliffordT_at q1 q2 `controlled` qcs+ return ([q1, q2], [], cs)+exact_ct_transformer gate@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls cs -> without_controls_if ncf $ do+ let n = case (name, qs, gctls) of+ -- certain gates are allowed two controls+ ("X", [q], []) -> 2+ ("Y", [q], []) -> 2+ ("Z", [q], []) -> 2+ ("iX", [q], []) -> 2+ _ -> 1+ with_combined_controls_CT n cs $ \qcs -> do+ case (name, qs, gctls, qcs) of+ + ("X", [q], [], [c1, c2]) -> do+ toffoli_AMMR_at q c1 c2+ ("X", [q], [], [c]) -> do+ qnot_at q `controlled` c+ ("X", [q], [], []) -> do+ gate_X_at q+ + ("Y", [q], [], [c1, c2]) -> do+ gate_S_inv_at q+ toffoli_AMMR_at q c1 c2+ gate_S_at q+ ("Y", [q], [], [c]) -> do+ gate_S_inv_at q+ qnot_at q `controlled` c+ gate_S_at q+ ("Y", [q], [], []) -> do+ gate_Y_at q+ + ("Z", [q], [], [c1, c2]) -> do+ ccZ_AMMR_at q c1 c2+ ("Z", [q], [], [c]) -> do+ hadamard_at q+ qnot_at q `controlled` c+ hadamard_at q+ ("Z", [q], [], []) -> do+ gate_Z_at q+ + ("iX", [q], [], [c1, c2]) -> do+ reverse_imp_if inv cc_iX_at q c1 c2+ ("iX", [q], [], [c]) -> do+ reverse_imp_if inv controlled_iX_at q c+ ("iX", [q], [], []) -> + reverse_imp_if inv gate_iX_at q+ + ("S", [q], [], [c]) -> do+ reverse_imp_if inv controlled_S_at q c+ ("S", [q], [], []) -> do+ reverse_imp_if inv gate_S_at q+ + ("T", [q], [], [c]) -> do+ reverse_imp_if inv controlled_T_at q c+ ("T", [q], [], []) -> do+ reverse_imp_if inv gate_T_at q+ + ("V", [q], [], [c]) -> do+ reverse_imp_if inv controlled_V_at q c+ ("V", [q], [], []) -> do+ reverse_imp_if inv gate_V_at q+ + ("E", [q], [], [c]) -> do+ reverse_imp_if inv controlled_E_at q c+ ("E", [q], [], []) -> do+ reverse_imp_if inv gate_E_at q+ + ("YY", [q], [], [c]) -> do+ reverse_imp_if inv controlled_YY_at q c+ ("YY", [q], [], []) -> do+ reverse_imp_if inv (named_gate_at "YY") q+ + ("omega", [q], [], [c]) -> do+ with_signed_qubit c $ \c1 -> do+ reverse_imp_if inv gate_T_at c1+ ("omega", [q], [], []) -> do+ reverse_imp_if inv gate_omega_at q+ + _ -> error ("exact_ct_transformer: gate not implemented: " ++ show gate)+ return (qs,gctls,cs)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+exact_ct_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CNot _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CGate _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CSwap _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QPrep _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QUnprep _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QInit _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CInit _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+exact_ct_transformer gate@(T_QMeas _) = identity_transformer gate+exact_ct_transformer gate@(T_QDiscard _) = identity_transformer gate+exact_ct_transformer gate@(T_CDiscard _) = identity_transformer gate+exact_ct_transformer gate@(T_DTerm _ _) = identity_transformer gate+exact_ct_transformer gate@(T_Comment _ _ _) = identity_transformer gate+++-- ----------------------------------------------------------------------+-- ** Decomposition into standard gate set++-- | This transformer decomposes a circuit into the standard gate set,+-- which we define to be:+-- +-- * /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the standard gate set. The best way+-- to avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+standard_transformer :: Transformer Circ Qubit Bit+standard_transformer gate@(T_QGate "H" 1 0 _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "Y" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "Z" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "S" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "T" 1 0 _ ncf f) = identity_transformer gate+standard_transformer gate@(T_QGate "multinot" _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "swap" 2 0 _ _ _) = identity_transformer gate+standard_transformer gate@(T_QGate "W" 2 0 _ _ _) = identity_transformer gate++standard_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qs -> do+ case qs of+ [] -> do+ gate_X_at q+ qs -> qnot_at q `controlled` qs+ return ([q], [], cs)++standard_transformer gate@(T_QGate "X" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q],[],cs)++standard_transformer gate@(T_QGate "iX" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls cs $ \qcs -> do+ case qcs of+ [] -> do+ gate_X_at q+ qcs -> do+ reverse_imp_if inv gate_iX_at q `controlled` qcs+ return ([q],[],cs)++standard_transformer gate@(T_QGate "V" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ reverse_imp_if inv gate_S_inv_at q+ hadamard_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "E" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_inv_at q+ return ([q],[],cs)+standard_transformer gate@(T_QGate "E" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "YY" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ reverse_imp_if inv gate_S_at q+ hadamard_at q+ reverse_imp_if inv gate_S_at q+ return ([q],[],cs)++standard_transformer gate@(T_QGate "omega" 1 0 _ ncf f) = f $+ \[q] [] cs -> + return ([q],[],cs)++standard_transformer gate@(T_QGate name _ _ inv ncf f) = + error ("standard_transformer: gate not implemented: " ++ show gate)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+standard_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+standard_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+standard_transformer gate@(T_CNot _ _) = identity_transformer gate+standard_transformer gate@(T_CGate _ _ _) = identity_transformer gate+standard_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+standard_transformer gate@(T_CSwap _ _) = identity_transformer gate+standard_transformer gate@(T_QPrep _ _) = identity_transformer gate+standard_transformer gate@(T_QUnprep _ _) = identity_transformer gate+standard_transformer gate@(T_QInit _ _ _) = identity_transformer gate+standard_transformer gate@(T_CInit _ _ _) = identity_transformer gate+standard_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+standard_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+standard_transformer gate@(T_QMeas _) = identity_transformer gate+standard_transformer gate@(T_QDiscard _) = identity_transformer gate+standard_transformer gate@(T_CDiscard _) = identity_transformer gate+standard_transformer gate@(T_DTerm _ _) = identity_transformer gate+standard_transformer gate@(T_Comment _ _ _) = identity_transformer gate++-- ----------------------------------------------------------------------+-- ** Decomposition into strict gate set++-- | This transformer decomposes a circuit into the strict gate set,+-- which we define to be:+-- +-- * /H/, /S/, /T/, and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the strict gate set. The best way to+-- avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+strict_transformer :: Transformer Circ Qubit Bit+strict_transformer gate@(T_QGate "H" 1 0 _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "multinot" _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "swap" 2 0 _ _ _) = identity_transformer gate+strict_transformer gate@(T_QGate "W" 2 0 _ _ _) = identity_transformer gate++strict_transformer gate@(T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qs -> do+ case qs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qs -> qnot_at q `controlled` qs+ return ([q], [], cs)++strict_transformer gate@(T_QGate "X" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qcs -> do+ case qcs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q],[],cs)++strict_transformer gate@(T_QGate "Y" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "Z" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "iX" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_normalized_controls_HS cs $ \qcs -> do+ case qcs of+ [] -> do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ qcs -> do+ reverse_imp_if inv gate_iX_at q `controlled` qcs+ return ([q],[],cs)++strict_transformer gate@(T_QGate "S" 1 0 False ncf f) = + identity_transformer gate+strict_transformer gate@(T_QGate "S" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "T" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_T_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "T" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_T_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "V" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "V" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "E" 1 0 False ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ hadamard_at q+ gate_S_at q+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)+strict_transformer gate@(T_QGate "E" 1 0 True ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ gate_S_at q+ hadamard_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "YY" 1 0 inv ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_controls cs $ do+ when (inv) $ do+ gate_S_at q+ gate_S_at q+ gate_S_at q+ hadamard_at q+ gate_S_at q+ when (inv) $ do+ gate_S_at q+ gate_S_at q+ return ([q],[],cs)++strict_transformer gate@(T_QGate "omega" 1 0 _ ncf f) = f $+ \[q] [] cs -> + return ([q],[],cs)++strict_transformer gate@(T_QGate name _ _ inv ncf f) = + error ("strict_transformer: gate not implemented: " ++ show gate)++-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+strict_transformer gate@(T_QRot _ _ _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_GPhase _ _ _) = identity_transformer gate+strict_transformer gate@(T_Subroutine _ _ _ _ _ _ _ _ _ _) = identity_transformer gate+strict_transformer gate@(T_CNot _ _) = identity_transformer gate+strict_transformer gate@(T_CGate _ _ _) = identity_transformer gate+strict_transformer gate@(T_CGateInv _ _ _) = identity_transformer gate+strict_transformer gate@(T_CSwap _ _) = identity_transformer gate+strict_transformer gate@(T_QPrep _ _) = identity_transformer gate+strict_transformer gate@(T_QUnprep _ _) = identity_transformer gate+strict_transformer gate@(T_QInit _ _ _) = identity_transformer gate+strict_transformer gate@(T_CInit _ _ _) = identity_transformer gate+strict_transformer gate@(T_QTerm _ _ _) = identity_transformer gate+strict_transformer gate@(T_CTerm _ _ _) = identity_transformer gate+strict_transformer gate@(T_QMeas _) = identity_transformer gate+strict_transformer gate@(T_QDiscard _) = identity_transformer gate+strict_transformer gate@(T_CDiscard _) = identity_transformer gate+strict_transformer gate@(T_DTerm _ _) = identity_transformer gate+strict_transformer gate@(T_Comment _ _ _) = identity_transformer gate+++-- ----------------------------------------------------------------------+-- * Glue code for subroutines++-- $ The following is stuff we have to do because subroutines are not+-- yet handled very abstractly. It is untested whether subroutines+-- work at all.++-- | Handle subroutines for the 'trimcontrols_transformer'.+trimcontrols_subroutine :: BoxId -> TypedSubroutine -> Circ ()+trimcontrols_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = trimcontrols_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "trimcontrols_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'trimcontrols_transformer'.+trimcontrols_dtransformer :: DynamicTransformer Circ Qubit Bit+trimcontrols_dtransformer = identity_dynamic_transformer {+ transformer = trimcontrols_transformer,+ define_subroutine = trimcontrols_subroutine}++-- | Unary transformer for the 'trimcontrols_transformer'.+trimcontrols_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+trimcontrols_unary = transform_unary_dynamic trimcontrols_dtransformer++-- | Handle subroutines for the 'approx_ct_transformer'.+approx_ct_subroutine :: (RandomGen g) => KeepPhase -> Precision -> g -> BoxId -> TypedSubroutine -> Circ ()+approx_ct_subroutine kp prec g boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = approx_ct_unary kp prec g circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "approx_ct_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'approx_ct_transformer'.+approx_ct_dtransformer :: (RandomGen g) => KeepPhase -> Precision -> g -> DynamicTransformer Circ Qubit Bit+approx_ct_dtransformer kp prec g = identity_dynamic_transformer {+ transformer = approx_ct_transformer kp prec g,+ define_subroutine = approx_ct_subroutine kp prec g}++-- | Unary transformer for the 'approx_ct_transformer'.+approx_ct_unary :: (RandomGen g, QCData qa, QCData qb) => KeepPhase -> Precision -> g -> (qa -> Circ qb) -> (qa -> Circ qb)+approx_ct_unary kp prec g = transform_unary_dynamic (approx_ct_dtransformer kp prec g)++-- | Handle subroutines for the 'exact_ct_transformer'.+exact_ct_subroutine :: BoxId -> TypedSubroutine -> Circ ()+exact_ct_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = exact_ct_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "exact_ct_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'exact_ct_transformer'.+exact_ct_dtransformer :: DynamicTransformer Circ Qubit Bit+exact_ct_dtransformer = identity_dynamic_transformer {+ transformer = exact_ct_transformer,+ define_subroutine = exact_ct_subroutine}++-- | Unary transformer for the 'exact_ct_transformer'.+exact_ct_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+exact_ct_unary = transform_unary_dynamic exact_ct_dtransformer++-- | Handle subroutines for the 'standard_transformer'.+standard_subroutine :: BoxId -> TypedSubroutine -> Circ ()+standard_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = standard_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "standard_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'standard_transformer'.+standard_dtransformer :: DynamicTransformer Circ Qubit Bit+standard_dtransformer = identity_dynamic_transformer {+ transformer = standard_transformer,+ define_subroutine = standard_subroutine}++-- | Unary transformer for the 'standard_transformer'.+standard_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+standard_unary = transform_unary_dynamic standard_dtransformer++-- | Handle subroutines for the 'strict_transformer'.+strict_subroutine :: BoxId -> TypedSubroutine -> Circ ()+strict_subroutine boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = strict_unary circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "strict_subroutine: " ++ x) boxid False circ' ein++-- | Dynamic transformer for the 'strict_transformer'.+strict_dtransformer :: DynamicTransformer Circ Qubit Bit+strict_dtransformer = identity_dynamic_transformer {+ transformer = strict_transformer,+ define_subroutine = strict_subroutine}++-- | Unary transformer for the 'strict_transformer'.+strict_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> (qa -> Circ qb)+strict_unary = transform_unary_dynamic strict_dtransformer++-- ----------------------------------------------------------------------+-- * Generic transformers++-- $ The following generic functions form the high-level interface to+-- these decomposition transformers. This is how the transformers+-- should be accessed by user code.++-- | This transformer makes sure that not-gates, Pauli /X/-, /Y/-, and+-- /Z/-gates, and /iX/-gates have at most two controls; that phase+-- gates of the form Diag(1, φ) have no controls; and that all other+-- gates have at most one control.+trimcontrols_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+trimcontrols_generic f = h where+ f1 = quncurry f+ h1 = trimcontrols_unary f1+ h = qcurry h1++-- | This transformer decomposes rotation and phase gates into the+-- Clifford+/T/ basis, using the approximate synthesis algorithm of+-- <http://arxiv.org/abs/1212.6253>. It requires a precision+-- parameter, as well as a source of randomness. Other gates+-- are unchanged.+approx_ct_generic :: (RandomGen g, QCData qa, QCData qb, QCurry qfun qa qb) => KeepPhase -> Precision -> g -> qfun -> qfun+approx_ct_generic kp prec g f = h where+ f1 = quncurry f+ h1 = approx_ct_unary kp prec g f1+ h = qcurry h1++-- | This transformer decomposes all gates that permit exact+-- Clifford+/T/ representations into the following concrete gate base+-- for Clifford+/T/:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Classical controls and classical gates are not subject to the gate+-- base, and are left untouched.+-- +-- Rotations and phase gates are left unchanged by this transformer,+-- but any controls on those gates will be decomposed. +exact_ct_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+exact_ct_generic f = h where+ f1 = quncurry f+ h1 = exact_ct_unary f1+ h = qcurry h1++-- | This transformer decomposes a circuit into the standard gate set,+-- which we define to be:+-- +-- * /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the standard gate set. The best way+-- to avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+standard_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+standard_generic f = h where+ f1 = quncurry f+ h1 = standard_unary f1+ h = qcurry h1++-- | This transformer decomposes a circuit into the strict gate set,+-- which we define to be:+-- +-- * /H/, /S/, /T/, and /CNOT/.+-- +-- As a precondition, the input circuit must only contain the+-- following gates:+-- +-- * controlled-not (with one positive or negative control),+-- +-- * any single-qubit Clifford gates,+-- +-- * /T/ and /T/[sup †].+-- +-- Global phases are suppressed. Classical controls and classical+-- gates are not subject to the gate base, and are left untouched.+-- +-- Any gates that are not among the input gates listed above will be+-- transformed to a semantically correct circuit which may, however,+-- contain gates that are not in the strict gate set. The best way to+-- avoid this is to apply exact and approximate Clifford+/T/+-- decomposition first.+strict_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => qfun -> qfun+strict_generic f = h where+ f1 = quncurry f+ h1 = strict_unary f1+ h = qcurry h1
@@ -0,0 +1,131 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Decompose/GateBase.hs" #-}+-- | This module provides an enumeration type for gate bases. This is+-- useful, for example, to provide a uniform interface to gate base+-- decomposers, so that they can be selected via command line options+-- or function arguments.++module Quipper.Libraries.Decompose.GateBase where++import Quipper+import Quipper.Internal++import Quipper.Libraries.Synthesis+import Quipper.Libraries.Decompose.Legacy+import Quipper.Libraries.Decompose.CliffordT++import Quipper.Utils.RandomSource++import System.Random++-- ----------------------------------------------------------------------+-- * Gate bases++-- | An enumeration type for gate bases. More cases can be added in+-- the future, for example for using gates from a particular physical+-- machine description. +-- +-- Some gate bases carry additional parameters; for example, in the+-- case of decomposition into a discrete gate base, one may specify a+-- precision ε, a random seed, or other flags.+-- +-- If a 'Precision' parameter is present, it specifies the desired+-- precision per gate. If a 'RandomSource' parameter is present, it+-- specifies a source of randomness.+-- +-- If a 'KeepPhase' parameter is present, it determines whether global+-- phases are respected ('True') or disregarded ('False').+data GateBase =+ Logical + -- ^ Use all logical gates, i.e., leave the circuit unchanged.+ | Binary + -- ^ Decompose into binary gates.+ | Toffoli + -- ^ Decompose into Toffoli and binary gates.+ | CliffordT_old + -- ^ Decompose into Clifford+/T/. This is a legacy transformer+ -- that does not handle all gates correctly. For example, it does+ -- not handle /W/-gates, rotations, or phase gates. Use+ -- 'CliffordT' instead.+ | CliffordT KeepPhase Precision RandomSource + -- ^ Decompose into Clifford+/T/, specifically: single-qubit+ -- Clifford gates, the controlled-not gate (with positive or+ -- negative controls), and the gates /T/ and /T/[sup †].+ | Standard Precision RandomSource+ -- ^ Decompose into the standard gate set, which we define to be+ -- /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and+ -- /CNOT/. Suppresses global phases.+ | Strict Precision RandomSource+ -- ^ Decompose into /H/, /S/, /T/, /CNOT/ gates only. Suppresses+ -- global phases.+ | Approximate KeepPhase Precision RandomSource+ -- ^ Decompose rotation and phase gates into Clifford+/T/, using+ -- an approximate synthesis algorithm. Other gates are unchanged.+ | Exact + -- ^ Decompose gates that can be exactly represented in the+ -- Clifford+/T/ base into that base, specifically: single-qubit+ -- Clifford gates, the controlled-not gate (with positive or+ -- negative controls), and the gates /T/ and /T/[sup †]. Leave+ -- rotation and phase gates unchanged.+ | TrimControls + -- ^ Eliminate excess controls from gates.+ deriving Show++-- | An assignment of gate bases to names. Names are given as+-- lower-case strings.+-- +-- This can be useful, e.g., in the definition of command line+-- options.+-- +-- In the future, the syntax should be extended so that users can+-- specify parameters (e.g., the precision, random seed) on the+-- command line as well. For now, we just use the default precision.+gatebase_enum :: [(String, GateBase)]+gatebase_enum = [+ ("logical", Logical),+ ("binary", Binary),+ ("toffoli", Toffoli),+ ("cliffordt_old", CliffordT_old),+ ("cliffordt", CliffordT False (30 * digits) rs),+ ("cliffordt_keepphase", CliffordT True (30 * digits) rs),+ ("standard", Standard (30 * digits) rs),+ ("strict", Strict (30 * digits) rs),+ ("approximate", Approximate False (30 * digits) rs),+ ("approximate_keepphase", Approximate True (30 * digits) rs),+ ("exact", Exact),+ ("trimcontrols", TrimControls)+ ]+ where+ rs = RandomSource (read "1" :: StdGen)++-- ----------------------------------------------------------------------+-- * Generic decomposition++-- | Decompose a circuit into gates from the given 'GateBase'. This+-- 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:+-- +-- > decompose_generic :: (QCData qa) => GateBase -> Circ qa -> Circ qa+-- > decompose_generic :: (QCData qa, QCData qb) => GateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+-- > decompose_generic :: (QCData qa, QCData qb, QCData qc) => GateBase -> (qa -> qb -> Circ qc) -> (qa -> qb -> Circ qc)+-- +-- and so forth.++decompose_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => GateBase -> qfun -> qfun+decompose_generic Logical = id+decompose_generic Binary = decompose_legacy_generic GB_Binary+decompose_generic Toffoli = decompose_legacy_generic GB_Toffoli+decompose_generic CliffordT_old = decompose_legacy_generic GB_CliffordT+decompose_generic (CliffordT kp prec (RandomSource g)) = exact_ct_generic . approx_ct_generic kp prec g+decompose_generic (Standard prec (RandomSource g)) = standard_generic . exact_ct_generic . approx_ct_generic False prec g+decompose_generic (Strict prec (RandomSource g)) = strict_generic . exact_ct_generic . approx_ct_generic False prec g+decompose_generic (Approximate kp prec (RandomSource g)) = approx_ct_generic kp prec g+decompose_generic Exact = exact_ct_generic+decompose_generic TrimControls = trimcontrols_generic++
@@ -0,0 +1,244 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Decompose/Legacy.hs" #-}+{-# LANGUAGE Rank2Types #-}++-- | Functions to decompose circuits into various gate bases. This+-- decompositions are \"legacy\". The 'GB_Toffoli' and 'GB_CliffordT'+-- decomposers contained here are being superseded by their+-- counterparts in "Quipper.Libraries.Decompose.CliffordT".++module Quipper.Libraries.Decompose.Legacy where++import Quipper+import Quipper.Internal++import Quipper.Libraries.GateDecompositions++-- The following is a bunch of stuff we need to import because,+-- temporarily, Decompose.hs uses low-level interfaces. It should be+-- re-implemented using only high-level interfaces, or in some cases,+-- more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit+import Quipper.Internal.Monad+import Quipper.Internal.Transformer (bindings_empty, bind_list, unbind_list, DynamicTransformer(..))+import Quipper.Internal.Generic (provide_subroutine_generic, transform_unary_dynamic)++import Control.Monad++-- ----------------------------------------------------------------------+-- * Legacy gatebase++-- | An enumeration type for the three gate bases handled by this+-- module.+data LegacyGateBase = GB_Toffoli | GB_Binary | GB_CliffordT+ deriving (Show)+-- ----------------------------------------------------------------------+-- * Helper functions++-- | Decompose quantum controls recursively until at most /n/ remain,+-- and then pass these reduced controls to the given circuit.+-- Precondition: /n/ ≥ 1. The decomposition is done using Toffoli+-- gates, decomposed into the gate base. Classical controls are left+-- untouched.+-- +-- For example, when /n/=2, this typically yields a circuit such as+-- the following (but with the Toffoli gates further decomposed into+-- the 'LegacyGateBase'):+-- +-- \[image decompose2Controls.png]+-- +-- And for /n/=1, the circuit typically looks like this:+-- +-- \[image decomposeControls.png]+with_combined_controls_gb :: LegacyGateBase -> Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls_gb GB_Toffoli = with_combined_controls toffoli_plain_at+with_combined_controls_gb GB_Binary = with_combined_controls toffoli_V_at+with_combined_controls_gb GB_CliffordT = with_combined_controls toffoli_AMMR_at++-- | Decompose a Toffoli gate into the given 'LegacyGateBase'. The controls+-- on the Toffoli gate may be positive or negative, as specified.+decomposeQToffoli :: LegacyGateBase -> Qubit -> (Signed Qubit, Signed Qubit) -> Circ ()+decomposeQToffoli GB_Toffoli q (c1, c2) = qnot_at q `controlled` [c1,c2]+decomposeQToffoli GB_Binary q (c1, c2) = do+ toffoli_V_at q c1 c2+decomposeQToffoli GB_CliffordT q3 (c1, c2) = do+ toffoli_AMMR_at q3 c1 c2++-- | The inverse of 'decomposeQToffoli'.+decomposeQToffoli_inv :: LegacyGateBase -> Qubit -> (Signed Qubit, Signed Qubit) -> Circ ()+decomposeQToffoli_inv gb = reverse_generic_imp (decomposeQToffoli gb)++-- | Implement a QMultinot gate in terms of QNot gates.+decomposeQMultinot :: [Qubit] -> Circ ()+decomposeQMultinot [] = return ()+decomposeQMultinot (q:qs) = do+ qnot_at q+ decomposeQMultinot qs ++-- ----------------------------------------------------------------------+-- * Decomposition transformers+ +-- | A transformer to decompose a circuit into 'LegacyGateBase' gates. Note+-- that in the case of classically-controlled quantum gates, the+-- classical controls are unaffected.+decompose_transformer :: LegacyGateBase -> Transformer Circ Qubit Bit+decompose_transformer gb (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 2 cs $ \qcs -> do+ case qcs of+ -- two controls+ [c1, c2] -> do+ decomposeQToffoli gb q (c1,c2)+ return ([q], [], cs)+ -- zero or one control+ qcs -> do+ qnot_at q `controlled` qcs+ return ([q], [], cs)+decompose_transformer gb (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ decomposeQMultinot qs `controlled` qcs+ return (qs, [], cs)+decompose_transformer gb (T_QGate "H" 1 0 _ ncf f) = f $ + \[q] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ hadamard q `controlled` qcs+ return ([q], [], cs)+decompose_transformer gb (T_QGate "swap" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ case qcs of+ -- one control+ [c] -> do+ qnot_at q1 `controlled` q2+ decomposeQToffoli gb q2 (c,(Signed q1 True)) + qnot_at q1 `controlled` q2+ return ([q1, q2], [], cs)+ -- zero controls+ qcs -> do + swap_at q1 q2+ return ([q1, q2], [], cs)+decompose_transformer gb (T_QGate "W" 2 0 _ ncf f) = f $+ \[q1, q2] [] cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ case qcs of+ -- one control+ [c] -> do + qnot q2 `controlled` q1+ w' <- qinit_qubit False+ decomposeQToffoli gb w' (c,(Signed q2 True))+ hadamard q1 `controlled` w'+ decomposeQToffoli_inv gb w' (c,(Signed q2 True))+ qterm_qubit False w' + qnot q2 `controlled` q1+ return ([q1, q2], [], cs)+ -- zero controls+ qcs -> do + gate_W q1 q2+ return ([q1, q2], [], cs)+decompose_transformer gb (T_QGate name _ _ inv ncf f) = f $+ \qs vs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ named_gate_qulist_at name inv qs vs `controlled` qcs+ return (qs, vs, cs)+decompose_transformer gb (T_QRot name _ _ inv theta ncf f) = f $+ \qs vs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ named_rotation_qulist_at name inv theta qs vs `controlled` qcs+ return (qs, vs, cs)+decompose_transformer gb (T_GPhase t ncf f) = f $+ \qs cs -> without_controls_if ncf $ do+ with_combined_controls_gb gb 1 cs $ \qcs -> do+ global_phase_anchored_list t qs `controlled` qcs+ return cs++-- The subroutine transformer clause is called when a subroutine gate appears, +-- for now we decompose the controls just like for other gates. The recursive+-- decomposition of a subroutine is taken care of in the dynamic transformer.+decompose_transformer gb (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \namespace ws cs -> without_controls_if ncf $ do+ let qws = [w | Endpoint_Qubit w <- ws]+ with_combined_controls_gb gb 1 cs $ \qcs -> do + provide_subroutines namespace+ case qcs of+ -- one control+ [c] -> if length qws /= length ws then error "Classical subroutine, used with quantum controls" else do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,cs)+ -- zero controls+ qcs -> do + vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws+ return (vs,cs)+-- We list catch-all cases explicitly, so that the type-checker can+-- warn about new gates that must be added to the list.+decompose_transformer gb gate@(T_CNot _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CGate _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CGateInv _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CSwap _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QPrep _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QUnprep _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QInit _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CInit _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QTerm _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_CTerm _ _ _) = identity_transformer gate+decompose_transformer gb gate@(T_QMeas _) = identity_transformer gate+decompose_transformer gb gate@(T_QDiscard _) = identity_transformer gate+decompose_transformer gb gate@(T_CDiscard _) = identity_transformer gate+decompose_transformer gb gate@(T_DTerm _ _) = identity_transformer gate+decompose_transformer gb gate@(T_Comment _ _ _) = identity_transformer gate++-- | Return a circuit producing function from a TypedSubroutine+open_subroutine :: TypedSubroutine -> [Endpoint] -> Circ [Endpoint]+open_subroutine (TypedSubroutine ocircuit _ _ scf) inputs = do+ let OCircuit (win, circuit, wout) = ocircuit+ when (length win /= length inputs) $ do+ error ("open_subroutine: subroutine has been applied to incorrect size of QCData")+ let in_bind = bind_list win inputs bindings_empty+ out_bind <- apply_circuit_with_bindings circuit in_bind+ let outputs = unbind_list out_bind wout+ return outputs++-- | Apply the decompose transformer to the given TypedSubroutine+-- Note: by default, set the classical-control flag to false.+decompose_subroutine :: LegacyGateBase -> BoxId -> TypedSubroutine -> Circ ()+decompose_subroutine gb boxid sub@(TypedSubroutine ocirc is os ctl) = do+ let circ = open_subroutine sub+ let circ' = decompose_legacy_unary gb circ+ let OCircuit (win, (arity,_,_,_), _) = ocirc+ let ein = endpoints_of_wires_in_arity arity win+ provide_subroutine_generic (\x -> "decompose_subroutine: " ++ x) boxid False circ' ein+ +-- | A dynamic transformer variant of the decompose transformer+decompose_dynamic_transformer :: LegacyGateBase -> DynamicTransformer Circ Qubit Bit+decompose_dynamic_transformer gb = identity_dynamic_transformer {+ transformer = decompose_transformer gb,+ define_subroutine = decompose_subroutine gb}++-- ----------------------------------------------------------------------+-- * Generic decomposition++-- | Decompose a circuit into gates from the given 'LegacyGateBase'.+decompose_legacy_unary :: (QCData qa, QCData qb) => LegacyGateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+decompose_legacy_unary gb circ = transform_unary_dynamic (decompose_dynamic_transformer gb) circ + +-- | Decompose a circuit into gates from the given 'LegacyGateBase'. Unlike+-- 'decompose_legacy_unary', this 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:+-- +-- > decompose_legacy_generic :: (QCData qa) => LegacyGateBase -> Circ qa -> Circ qa+-- > decompose_legacy_generic :: (QCData qa, QCData qb) => LegacyGateBase -> (qa -> Circ qb) -> (qa -> Circ qb)+-- > decompose_legacy_generic :: (QCData qa, QCData qb, QCData qc) => LegacyGateBase -> (qa -> qb -> Circ qc) -> (qa -> qb -> Circ qc)+-- +-- and so forth.++decompose_legacy_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => LegacyGateBase -> qfun -> qfun+decompose_legacy_generic gatebase f = g where+ f1 = quncurry f+ g1 = decompose_legacy_unary gatebase f1+ g = qcurry g1+
@@ -0,0 +1,657 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/DynamicLiftings.hs" #-}+-- | This library provides various transformers and functions for+-- performing dynamic liftings based on different mechanisms. Some use+-- simulations to do the dynamic liftings; others just do them+-- randomly. There are also functions for printing the simulated+-- circuit.+-- +-- This code is experimental.++module Quipper.Libraries.DynamicLiftings where++import Quipper++-- The following is a bunch of stuff we need to import because,+-- temporarily, DynamicLiftings.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit (Namespace, namespace_empty, TypedSubroutine(..), OCircuit(..), reverse_ocircuit, showNames)+import Quipper.Internal (BType)+import Quipper.Internal.Transformer+import Quipper.Internal.Monad+import Quipper.Internal.Generic (transform_unary_dynamic_shape)+import Quipper.Internal.Printing (getBit)++import Quipper.Libraries.Simulation.QuantumSimulation++import Quipper.Utils.Auxiliary (map_provide)++-- we use the state monad to hold our \"quantum\" state+import Control.Monad.State+-- we use a random number generator to simulate \"quantum randomness\"+import System.Random hiding (split)+-- we store \"basis\" states as a map, +--i.e., Map Qubit Bool represents a basis state.+import Data.Map (Map)+import qualified Data.Map as Map++-- ======================================================================+-- * Random Dynamic Liftings and Liftings from a List++-- | A State monad that holds a random generator.+type RandomCirc a = StateT StdGen Circ a++-- | A State monad that holds a list of booleans.+type ListCirc a = StateT [Bool] Circ a++-- | To evaluate a 'RandomCirc' we require a seed for the random generator.+evalRandomCirc :: Int -> RandomCirc a -> Circ a+evalRandomCirc seed rc = evalStateT rc (mkStdGen seed)++-- | To evaluate a 'ListCirc' we require a list of booleans.+evalListCirc :: [Bool] -> ListCirc a -> Circ a+evalListCirc bools lc = evalStateT lc bools ++-- | Lift 'evalRandomCirc' to unary random circuit generating functions.+evalRandomCirc_unary :: Int -> (a -> RandomCirc b) -> a -> Circ b+evalRandomCirc_unary seed rcirc input = evalRandomCirc seed (rcirc input)++-- | Left 'evalListCirc' to unary list circuit generating functions.+evalListCirc_unary :: [Bool] -> (a -> ListCirc b) -> a -> Circ b+evalListCirc_unary bools lcirc input = evalListCirc bools (lcirc input)++-- | Lift the underlying 'randomR' function into the RandomCirc monad.+randomRRandomCirc :: Random a => (a,a) -> RandomCirc a+randomRRandomCirc (a0,a1) = do+ stdgen <- get+ let (random_a,stdgen') = randomR (a0,a1) stdgen+ put stdgen'+ return random_a++-- | Print a RandomCirc by evaluating it with a seed in the IO monad.+print_unary_random :: (QCData qa) => Format -> (qa -> RandomCirc b) -> qa -> IO ()+print_unary_random format rcirc input = do+ seed <- randomIO+ let circ = evalRandomCirc_unary seed rcirc+ print_unary format circ input++-- | Print a LiftCirc by evaluating it in the IO Monad, so as to read+-- in a given number of booleans.+print_unary_list :: (QCData qa) => Format -> Int -> (qa -> ListCirc b) -> qa -> IO ()+print_unary_list format liftings lcirc input = do+ bools <- mapM (\() -> getBit) (replicate liftings ())+ let circ = evalListCirc_unary bools lcirc+ print_unary format circ input++-- | Lift the 'identity_transformer' using any monad transformer.+lifted_identity_transformer :: (MonadTrans t) => Transformer (t Circ) Qubit Bit+lifted_identity_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> lift $ without_controls_if ncf $ do+ q' <- qnot q `controlled` cs+ return ([q'], [] ,cs)+lifted_identity_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \ws [] cs -> lift $ without_controls_if ncf $ do+ ws' <- qmultinot_list (map (\x -> (x,True)) ws) `controlled` cs+ return (ws', [], cs)+lifted_identity_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> lift $ without_controls_if ncf $ do+ q' <- hadamard q `controlled` cs+ return ([q'], [], cs)+lifted_identity_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w,v] [] cs -> lift $ without_controls_if ncf $ do+ (w',v') <- swap_qubit w v `controlled` cs+ return ([w',v'], [], cs)+lifted_identity_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w,v] [] cs -> lift $ without_controls_if ncf $ do+ (w',v') <- gate_W w v `controlled` cs+ return ([w',v'], [], cs)+lifted_identity_transformer (T_QGate name _ _ inv ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist name inv ws vs `controlled` c+ return (ws', vs', c)+lifted_identity_transformer (T_QRot name _ _ inv t ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_rotation_qulist name inv t ws vs `controlled` c+ return (ws', vs', c)+lifted_identity_transformer (T_GPhase t ncf f) = f $+ \qs c -> lift $ without_controls_if ncf $ do+ global_phase_anchored_list t qs `controlled` c+ return c+lifted_identity_transformer (T_CNot ncf f) = f $+ \q c -> lift $ without_controls_if ncf $ do+ q' <- cnot q `controlled` c+ return (q', c)+lifted_identity_transformer (T_CGate name ncf f) = f $+ \ws -> lift $ without_controls_if ncf $ do + v <- cgate name ws+ return (v, ws)+lifted_identity_transformer (T_CGateInv name ncf f) = f $+ \v ws -> lift $ without_controls_if ncf $ do + cgateinv name v ws+ return ws+lifted_identity_transformer (T_CSwap ncf f) = f $+ \w v c -> lift $ without_controls_if ncf $ do+ (w',v') <- swap_bit w v `controlled` c+ return (w',v',c)+lifted_identity_transformer (T_QPrep ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do + v <- prepare_qubit w+ return v+lifted_identity_transformer (T_QUnprep ncf f) = f $ + \w -> lift $ without_controls_if ncf $ do + v <- unprepare_qubit w+ return v+lifted_identity_transformer (T_QInit b ncf f) = f $+ lift $ without_controls_if ncf $ do+ w <- qinit_qubit b+ return w+lifted_identity_transformer (T_CInit b ncf f) = f $+ lift $ without_controls_if ncf $ do+ w <- cinit_bit b+ return w+lifted_identity_transformer (T_QTerm b ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do+ qterm_qubit b w+ return ()+lifted_identity_transformer (T_CTerm b ncf f) = f $+ \w -> lift $ without_controls_if ncf $ do+ cterm_bit b w+ return ()+lifted_identity_transformer (T_QMeas f) = f $ + \w -> lift $ do+ v <- measure_qubit w+ return v+lifted_identity_transformer (T_QDiscard f) = f $+ \w -> lift $ do+ qdiscard_qubit w+ return ()+lifted_identity_transformer (T_CDiscard f) = f $+ \w -> lift $ do+ cdiscard_bit w+ return ()+lifted_identity_transformer (T_DTerm b f) = f $+ \w -> lift $ do+ dterm_bit b w+ return ()+lifted_identity_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \ns ws c -> lift $ without_controls_if ncf $ do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,c)+lifted_identity_transformer (T_Comment s inv f) = f $+ \ws -> lift $ do+ comment_label s inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]+ return ()++-- | Dynamic lifting can make use of a random result (without caring which wire is+-- being lifted).+random_dynamic_lift :: Bit -> RandomCirc Bool+random_dynamic_lift _ = randomRRandomCirc (False,True)++-- | Dynamic lifting can pop the head off of a list of booleans, and use that to+-- lift the given wire.+list_dynamic_lift :: Bit -> ListCirc Bool+list_dynamic_lift _ = do+ xs <- get+ case xs of+ [] -> error "ListCirc list of liftings exhausted"+ (x:xs') -> do + put xs' + return x++-- | A dynamic transformer which is the identity transformer (lifted to+-- RandomCirc), except for the dynamic lifting operation.+random_dynamic_lift_transformer :: DynamicTransformer (StateT StdGen Circ) Qubit Bit+random_dynamic_lift_transformer = DT {+ transformer = lifted_identity_transformer,+ define_subroutine = \name subroutine -> do+ lift $ put_subroutine_definition name subroutine,+ lifting_function = random_dynamic_lift+ }++-- | A dynamic transformer which is the identity transformer (lifted to+-- ListCirc), except for the dynamic lifting operation.+list_dynamic_lift_transformer :: DynamicTransformer (StateT [Bool] Circ) Qubit Bit+list_dynamic_lift_transformer = DT {+ transformer = lifted_identity_transformer,+ define_subroutine = \name subroutine -> do+ lift $ put_subroutine_definition name subroutine,+ lifting_function = list_dynamic_lift+ }++-- | Print a circuit, using random dynamic liftings.+print_unary_with_random_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> a -> IO ()+print_unary_with_random_liftings format circ shape = do+ let lifted_circ = transform_unary_dynamic_shape random_dynamic_lift_transformer circ shape + print_unary_random format lifted_circ shape++-- | Print a circuit, using a list of dynamic liftings.+print_unary_with_list_liftings :: (QCData a,QCData b) => Format -> Int -> (a -> Circ b) -> a -> IO ()+print_unary_with_list_liftings format liftings circ shape = do+ let lifted_circ = transform_unary_dynamic_shape list_dynamic_lift_transformer circ shape + print_unary_list format liftings lifted_circ shape ++-- ======================================================================+-- * Simulating the Dynamic Liftings++-- | Add state to the Circ Monad so that we can simulate the circuit+-- and use that data for dynamic liftings.+data SimulationState = SS {+ s_quantum_state :: Amplitudes Double,+ s_classical_state :: Map Bit Bool,+ s_namespace :: Namespace, -- we need a namespace to keep track of subroutines+ s_rng :: StdGen+ }++-- | When we start a simulation, we need an empty starting state, with+-- a seed for the generator.+empty_simulation_state :: Int -> SimulationState+empty_simulation_state seed = SS { s_quantum_state = Vector [(Map.empty,1.0)], s_classical_state = Map.empty, s_namespace = namespace_empty, s_rng = mkStdGen seed}++-- | A State monad that holds our SimulationState.+type SimulatedCirc a = StateT SimulationState Circ a++-- | Evaluate a 'SimulatedCirc'. This requires a seed for the random generator.+evalSimulatedCirc :: Int -> SimulatedCirc a -> Circ a+evalSimulatedCirc seed sc = evalStateT sc (empty_simulation_state seed)++-- | Lift 'evalSimulatedCirc' to unary functions.+evalSimulatedCirc_unary :: Int -> (a -> SimulatedCirc b) -> a -> Circ b+evalSimulatedCirc_unary seed scirc input = evalSimulatedCirc seed (scirc input)++-- | Lift the underlying 'randomR' function into the SimulatedCirc monad.+randomRSimulatedCirc :: Random a => (a,a) -> SimulatedCirc a+randomRSimulatedCirc (a0,a1) = do+ state <- get+ let stdgen = s_rng state+ let (random_a,stdgen') = randomR (a0,a1) stdgen+ put (state {s_rng = stdgen'})+ return random_a++-- | A specialized put function for the quantum state that uses the current state+-- instead of a previously retrieved state.+putQS :: Amplitudes Double -> SimulatedCirc ()+putQS amps = do+ state <- get+ put (state {s_quantum_state = amps})++-- | A specialized put function for the classical state that uses the current state+-- instead of a previously retrieved state.+putCS :: Map Bit Bool -> SimulatedCirc ()+putCS bits = do+ state <- get+ put (state {s_classical_state = bits})++-- | 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 lookup the boolean+-- result otherwise.+s_classical_control :: Map Bit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool+s_classical_control bits (Signed bep val) = case bep of+ (Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit+ (Endpoint_Qubit _) -> error "CNot: Quantum Control on Classical Gate"++-- | Map the 's_classical_control' function to all the controls, and take the+-- 'and' of the result.+s_classical_controls :: Map Bit Bool -> Ctrls Qubit Bit -> Bool+s_classical_controls bits cs = and (map (s_classical_control bits) cs)++-- | 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 fires.+s_qc_control :: Map Bit Bool -> Map Qubit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool+s_qc_control bits mqb (Signed bep val) = case bep of+ (Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit+ (Endpoint_Qubit q) -> val == val' where val' = mqb Map.! q++-- | Map the 's_qc_control' function to all the controls (under the given basis +-- state), and take the 'and' of the result.+s_qc_controls :: Map Bit Bool -> Map Qubit Bool -> Ctrls Qubit Bit -> Bool+s_qc_controls bits mqb cs = and (map (s_qc_control bits mqb) cs)++-- | Apply the given function only if the controls fire.+s_if_controls :: Map Bit Bool -> Ctrls Qubit Bit -> (Map Qubit Bool -> Amplitudes Double) -> Map Qubit Bool -> Amplitudes Double+s_if_controls bits c f mqb = if (s_qc_controls bits mqb c) then f mqb else Vector [(mqb,1)]++-- | The 'simulated_lift_transformer' is the actual transformer that does the+-- simulation, while recreating the circuit.+simulated_lift_transformer :: Transformer (StateT SimulationState Circ) Qubit Bit+-- Translation of classical gates:+simulated_lift_transformer (T_CNot ncf f) = f $+ \b c -> do+ (b,c) <- lift $ without_controls_if ncf $ do+ b' <- cnot b `controlled` c+ return (b', c)+ state <- get+ let bits = s_classical_state state+ let ctrl = s_classical_controls bits c+ let val = bits Map.! b+ let bits' = if ctrl then (Map.insert b (not val) bits) else bits+ putCS bits'+ return (b,c)+simulated_lift_transformer (T_CInit val ncf f) = f $+ do+ b <- lift $ without_controls_if ncf $ do+ w <- cinit_bit val+ return w+ state <- get+ let bits = s_classical_state state+ putCS (Map.insert b val bits)+ return b+simulated_lift_transformer (T_CTerm b ncf f) = f $+ \w -> do+ lift $ without_controls_if ncf $ do+ cterm_bit b w+ return ()+ state <- get+ let bits = s_classical_state state+ let val = bits Map.! w+ if val /= b then error "CTerm: Assertion Incorrect"+ else do+ putCS (Map.delete w bits)+simulated_lift_transformer (T_CDiscard f) = f $+ \w -> do+ lift $ do+ cdiscard_bit w+ state <- get+ let bits = s_classical_state state+ putCS (Map.delete w bits)+simulated_lift_transformer (T_DTerm b f) = f $+ \w -> do+ lift $ do+ dterm_bit b w+ state <- get+ let bits = s_classical_state state+ putCS (Map.delete w bits)+simulated_lift_transformer (T_CGate name ncf f) = f $+ \ws -> do+ (v,ws) <- lift $ without_controls_if ncf $ do + v <- cgate name ws+ return (v, ws)+ state <- get+ let bits = s_classical_state state+ let list = map (\w -> bits Map.! w) ws+ let result = gateC name list+ putCS (Map.insert v result bits)+ return (v,ws) +simulated_lift_transformer g@(T_CGateInv name ncf f) = f $+ \v ws -> do+ ws <- lift $ without_controls_if ncf $ do + cgateinv name v ws+ return ws+ state <- get+ let bits = s_classical_state state+ let list = map (\w -> bits Map.! w) ws+ let result = bits Map.! v+ let result' = gateC name list+ if result == result' then return ws else error "CGateInv: Uncomputation error"+-- Translation of quantum gates:+simulated_lift_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] c -> do+ (q,c) <- lift $ without_controls_if ncf $ do+ q' <- qnot q `controlled` c+ return (q', c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q], [], c)+simulated_lift_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] c -> do+ (qs,c) <- lift $ without_controls_if ncf $ do+ qs' <- qmultinot_list (map (\x -> (x,True)) qs) `controlled` c + return (qs', c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = foldr (\q a -> apply (s_if_controls bits c (performGateQ gate q)) a) amps qs+ putQS amps'+ return (qs, [], c)+simulated_lift_transformer (T_QGate "H" 1 0 _ ncf f) = f $ + \[q] [] c -> do+ (q,c) <- lift $ without_controls_if ncf $ do+ q' <- hadamard q `controlled` c+ return (q', c)+ let gate = gateQ "hadamard"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q], [], c)+simulated_lift_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w, v] [] c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- swap_qubit w v `controlled` c+ return (w',v',c)+ let gate = gateQ "x"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps+ let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gate w)) amps'+ let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps''+ putQS amps'''+ return ([w, v], [], c)+simulated_lift_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w, v] [] c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- gate_W w v `controlled` c+ return (w',v',c)+ let gateX = gateQ "x"+ let gateH = gateQ "hadamard"+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps+ let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gateH w)) amps'+ let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps''+ putQS amps'''+ return ([w, v], [], c)+simulated_lift_transformer (T_QGate "trace" _ _ inv ncf f) = f $+ \ws vs c -> lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist "trace" inv ws vs `controlled` c+ return (ws', vs', c)+simulated_lift_transformer (T_QGate name _ _ inv ncf f) = f $ + \[q] [] c -> do+ ([q],[],c) <- lift $ without_controls_if ncf $ do+ (ws', vs') <- named_gate_qulist name inv [q] [] `controlled` c+ return (ws', vs', c)+ let gate = gateQinv name inv+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q],[],c)+simulated_lift_transformer (T_QRot name _ _ inv theta ncf f) = f $ + \[q] [] c -> do+ ([q],[],c) <- lift $ without_controls_if ncf $ do+ (ws', vs') <- named_rotation_qulist name inv theta [q] [] `controlled` c+ return (ws', vs', c)+ let gate = rotQinv name inv theta+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps+ putQS amps'+ return ([q],[],c)+simulated_lift_transformer (T_GPhase t ncf f) = f $+ \w c -> do+ c <-lift $ without_controls_if ncf $ do+ global_phase_anchored_list t w `controlled` c+ return c+ state <- get+ let gate = rotQ "exp(% pi i)" t+ let wire = -1+ let q = qubit_of_wire wire+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let amps' = apply (s_if_controls bits c (vector (Map.insert q False))) amps + let amps'' = apply (s_if_controls bits c (performGateQ gate q)) amps'+ let (p,_,ampsf) = split amps'' q+ case p of+ 0.0 -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf' + putQS ampsf'+ return c+ _ -> error "GPhase"+simulated_lift_transformer (T_QInit val ncf f) = f $+ do+ q <- lift $ without_controls_if ncf $ do+ w <- qinit_qubit val+ return w+ state <- get+ let amps = s_quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + putQS amps'+ return q+simulated_lift_transformer (T_QMeas f) = f $+ \q -> do+ b <- lift $ do+ b <- measure_qubit q+ return b+ state <- get+ let amps = s_quantum_state state+ let bits = s_classical_state state+ let (p,ift,iff) = split amps q+ pp <- randomRSimulatedCirc (0,1.0)+ let (val,amps') = if p > pp then (True,ift) else (False,iff)+ let amps'' = apply (vector (Map.delete q)) amps' + let bits' = Map.insert b val bits+ putQS amps''+ putCS bits'+ return b+simulated_lift_transformer (T_QDiscard f) = f $+ \q -> do+ lift $ do+ qdiscard_qubit q+ return ()+ -- a discard is essentially a measurement, with the result thrown away, so we+ -- do that here, as it will reduce the size of the quantum state we are+ -- simulating over.+ state <- get+ let (p,ift,iff) = split (s_quantum_state state) q+ pp <- randomRSimulatedCirc (0,1.0)+ let amps = if p > pp then ift else iff+ let amps' = apply (vector (Map.delete q)) amps+ putQS amps'+ return ()+simulated_lift_transformer (T_QTerm b ncf f) = f $+ \q -> do+ lift $ without_controls_if ncf $ qterm_qubit b q+ -- with a real quantum computer, when we terminate a qubit with an assertion+ -- we have no way of actually checking the assertion. The best we can do is+ -- measure the qubit and then throw an error if the assertion is incorrect,+ -- which may only occur with a small probability. Here, we are able to split+ -- the quantum state and see if the qubit exists in the incorrect state with+ -- any non-zero probability, and throw an error.+ state <- get+ let amps = s_quantum_state state+ let (p,ampst,ampsf) = split amps q+ case (b,p) of+ (True,1.0) -> do+ let ampst' = apply (vector (Map.delete q)) ampst+ putQS ampst'+ return ()+ (False,0.0) -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf+ putQS ampsf'+ return ()+ (True,pt) -> error ("QTerm: Assertion Incorrect (True only has probability " ++ show pt ++ ")")+ (False,pt) -> error ("QTerm: Assertion Incorrect (False only has probability " ++ show (1.0 - pt) ++ ")")+simulated_lift_transformer (T_Comment name inv f) = f $+ \ws -> do+ lift $ do+ comment_label name inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]+ return ()+simulated_lift_transformer g@(T_CSwap ncf f) = f $+ \w v c -> do+ (w,v,c) <- lift $ without_controls_if ncf $ do+ (w',v') <- swap_bit w v `controlled` c+ return (w',v',c)+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_QPrep ncf f) = f $+ \w -> do+ w <- lift $ without_controls_if ncf $ do + v <- prepare_qubit w+ return v+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_QUnprep ncf f) = f $+ \w -> do+ w <- lift $ without_controls_if ncf $ do + v <- unprepare_qubit w+ return v+ error ("simulated_lift_transformer: unimplemented gate: " ++ show g)+simulated_lift_transformer g@(T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ \ns ws c -> do+ (ws,c) <- lift $ without_controls_if ncf $ do+ vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c+ return (vs,c)+ case Map.lookup n ns of+ Just (TypedSubroutine sub_ocirc _ _ _) -> do+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ let in_bindings = bind_list in_wires ws bindings_empty+ let sub_bcirc = (sub_circ,ns)+ out_bind <- transform_bcircuit_rec simulated_lift_transformer sub_bcirc in_bindings+ return (unbind_list out_bind out_wires, c) + Nothing -> error $ "simulated_lift_transformer: subroutine " ++ show n ++ " not found (in " ++ showNames ns ++ ")"++-- | Dynamic lifting can make use of a simulated result.+simulated_dynamic_lift :: Bit -> SimulatedCirc Bool+simulated_dynamic_lift b = do+ state <- get+ let bits = s_classical_state state+ case Map.lookup b bits of+ Just val -> return val+ Nothing -> error $ "simulated_dynamic_lift: bit " ++ show b ++ " not found" ++-- | A dynamic transformer which simulates the circuit, whilst+-- reconstructing it with simulated lifting results.+-- Note: do not handle classical controlling.+simulated_dynamic_lift_transformer :: DynamicTransformer (StateT SimulationState Circ) Qubit Bit+simulated_dynamic_lift_transformer = DT {+ transformer = simulated_lift_transformer,+ define_subroutine = \name subroutine -> do+ lift $ do+ s <- get_namespace+ let s' = map_provide name subroutine s+ set_namespace s'+ put_subroutine_definition name subroutine,+ lifting_function = simulated_dynamic_lift+ }++-- | Print a RandomCirc by evaluating it with a seed in the IO monad.+print_simulated :: Format -> SimulatedCirc b -> IO ()+print_simulated format scirc = do+ seed <- randomIO+ let circ = evalSimulatedCirc seed scirc+ print_unary format (\() -> circ) ()++-- | Print a circuit, using simulated liftings.+print_unary_with_simulated_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> BType a -> IO ()+print_unary_with_simulated_liftings format circ input = print_simulated format (lifted_circ ())+ where+ circ' = \ () -> do+ a <- qc_init input+ circ a+ lifted_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ' () + ++-- | Pass a (possibly) dynamic circuit through the +-- 'simulated_dynamic_lift_transformer' and evaluate the liftings so as to+-- leave us with a static circuit that represents a single run of the original+-- circuit, with the given inputs. We also need to pass in a seed for the RNG.+simulate_liftings_unary :: (QCData a, QCData b) => Int -> (a -> Circ b) -> BType a -> Circ b+simulate_liftings_unary seed fcirc_in input = out_circ+ where+ circ_in = \() -> do+ a <- qc_init input+ fcirc_in a+ s_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ_in ()+ out_circ = evalSimulatedCirc seed (s_circ ())+
@@ -0,0 +1,722 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/FPReal.hs" #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}++{-# LANGUAGE IncoherentInstances #-} +{-# LANGUAGE DeriveDataTypeable #-}++-- | This library provides a quantum implementation of fixed-precision real numbers (i.e., with precision determined at circuit-generation time), and classical counterpart types.+--+-- Currently still significantly experimental. TODO: +--+-- * decide on how much access to provide to underlying representation of 'FPReal'. Full 'fpreal_case', or more like just what’s available through Haskell’s 'RealFloat'?+--+-- * decide how to use 'ErrMsg': in internal functions only, or exported ones too?+--+-- * many specific TODO’s in code throughout this file.+--+-- * write export list.+ +module Quipper.Libraries.FPReal where++import Quipper+import Quipper.Internal++import Quipper.Libraries.Arith++import Quipper.Utils.Auxiliary++import Control.Monad (foldM)+import Data.Maybe (fromJust)+import Data.Ratio (numerator, denominator)+import Data.Typeable++-- ===========================================+-- * Fixed-precision real arithmetic: the FPReal family++-- | 'FPRealX': a family of datatypes for fixed-precision real numbers.+-- (That is, the precision is a parameter, fixed at circuit-generation time.)+-- +-- 'FPRealX' is based on the family 'XInt' of fixed-length integer types:+-- @'FPRealX' /n/ /a/@ represents 2[sup /n/] /a/, where /a/ is some fixed-length integer.+--+-- Alternately, in the specific case /x/ = 'Bool', a Haskell 'Double' may be +-- used as an 'FPReal', considered as having indeterminate length and exponent.+-- This is exactly analogous to the case of indeterminate length in 'XInt' / 'IntM'; for a more+-- detailed explanation, see the documentation there.+data FPRealX x = FPRealX Int (XInt x) | FPReal_indet Double (Identity IntM (XInt x))+ deriving (Show, Typeable)++-- | Fixed-precision real parameters. As with 'IntM', the length and/or exponent+-- of an 'FPReal' may be indeterminate; such an 'FPReal' may only be used in +-- certain contexts, typically either when the length/exponent can be inferred from context +-- (e.g., terminating a 'FPRealQ'), or where the result can again be indeterminate.+--+-- As with indeterminate 'IntM's, indeterminate 'FPReal's may be used for efficient, +-- high-precision classical calculation, and then explicitly or implicitly coerced+-- to determinate 'FPReal's when required for interfacing with quantum computation.+type FPReal = FPRealX Bool++instance Show FPReal where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet r id) = show r++-- | Fixed-precision reals for quantum circuits.+type FPRealQ = FPRealX Qubit++instance Show FPRealQ where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet _ _) = error "FPRealX: internal error"++-- | Fixed-precision reals for classical circuits.+type FPRealC = FPRealX Bit++{- -- for when Qubit /= Bit:+instance Show FPRealC where+ show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"+ show (FPReal_indet _ _) = error "FPRealX: internal error"+-}++-- ----------------------------------------------------------------------+-- ** Primitive combinators on FPReal++-- $ Like 'XInt', the type 'FPReal' is intended to be an abstract data type,+-- and all access to it should pass through the functions of this+-- section.++-- *** Constructors++-- | Create an 'FPRealX' from an 'XInt' together with an exponent.+fprealx :: Int -> XInt x -> FPRealX x+fprealx n x = FPRealX n x++-- | Create an indeterminate 'FPReal' from a 'Double'. +fpreal_of_double :: Double -> FPReal+fpreal_of_double r = FPReal_indet r reflexivity++-- *** Destructor++-- | If the 'FPRealX' is of determinate exponent, return its exponent and mantissa.+-- +-- If the 'FPRealX' is indeterminate, return a pair (/r/, /id/), where /r/ is the underlying 'Double', and /id/ is a witness of the fact that /x/ = 'Bool'.+-- +-- This is a lowest-level access function not intended to be used by+-- user-level code, and is not exported. +fprealx_case :: FPRealX x -> Either (Int, XInt x) (Double, Identity IntM (XInt x))+fprealx_case (FPRealX n x) = Left (n,x)+fprealx_case (FPReal_indet r id) = Right (r, id)++-- ----------------------------------------------------------------------+-- ** Other low-level operations++-- $ The operations in this section are the only ones intended to use+-- 'fprealx_case' directly.++-- TODO: fprealx_expt, fprealx_num should probably take ErrMsg arguments, at least for internal use --- I guess only the versions specialized to FPRealQ, FPRealC should be written as though unconditional? Perhaps only these, plus the 'Maybe' version specialized to 'FPReal', should be exported?+ +-- | Extract the exponent of an 'FPRealX', assumed to be determinate.+--+-- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. +fprealx_expt :: FPRealX x -> Int+fprealx_expt x = + case fprealx_case x of+ Left (n, _) -> n+ Right _ -> error "fprealx_expt: indeterminate exponent"++-- | Extract the mantissa of an 'FPRealX', assumed to be of determinate exponent.+--+-- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. +fprealx_num :: FPRealX x -> XInt x+fprealx_num x = + case fprealx_case x of+ Left (_, xi) -> xi+ Right _ -> error "fprealx_num: indeterminate exponent"++-- | Extract the length (in bits) of an 'FPRealX', assumed to be of determinate exponent and length.+fprealx_length :: FPRealX x -> Int+fprealx_length x = + case fprealx_case x of+ Left (_, xi) -> fromJust $ xint_maybe_length xi+ Right _ -> error "fprealx_length: indeterminate exponent"++-- | Set the exponent of an 'FPReal' to /n/. This operation is only+-- legal if the input (a) has indeterminate exponent or (b) has+-- determinate exponent already equal to /m/. In particular, it cannot+-- be used to change the exponent from anything other than from+-- indeterminate to determinate.+-- +-- If both arguments already have determinate exponents, and they do not+-- coincide, throw an error. The 'String' argument is used as an error+-- message in that case.++-- Implementation note: the “intm_of_integer” may seem unnecessary,+-- but needed so that when /r/ is “undefined”, the result is+-- “intm_of_integer undefined” not just “undefined”. +fprealx_set_expt :: Int -> FPRealX x -> String -> FPRealX x+fprealx_set_expt n xr errstr = case fprealx_case xr of+ Left (n', xi) -> if n' == n then fprealx n xi else error errstr+ Right (r, id) -> fprealx n (identity id $ intm_of_integer $ round $+ if n >= 0 then (r / 2^n) else (r * 2^(-n)))++-- | Return the (possibly indeterminate) exponent of an 'FPRealX'.+fprealx_maybe_expt :: FPRealX x -> Maybe (Int)+fprealx_maybe_expt xr = case fprealx_case xr of+ Left (n, _) -> Just n+ Right _ -> Nothing++-- | Given an 'FPReal', return either the exponent and numerator, or else the double it wraps.+--+-- A specialized and implementation-hiding wrapper for 'fprealx_case'.++-- TODO: should this be exported?+fpreal_case :: FPReal -> Either (Int, IntM) (Double)+fpreal_case (FPRealX n x) = Left (n, x)+fpreal_case (FPReal_indet r _) = Right r++-- | Equality test. If both have indeterminate exponent, check equality of underlying 'Double's. +-- Otherwise, if exponents are compatible (i.e. both determinate and equal, or one indeterminate), +-- check equality of numerators. If exponents are incompatible, throw an error (the test+-- should in this case be considered ill-typed).+fprealx_equals :: (Eq x) => FPRealX x -> FPRealX x -> Bool +fprealx_equals x y =+ case (fprealx_case x, fprealx_case y) of+ (Left (n,xi), Left (n',yi)) + | n == n' -> xi == yi+ | otherwise -> error "Equality test on FPRealx: operands must be of equal exponent"+ (_, Left (m,yi)) -> fprealx_equals (fprealx_set_expt m x "fprealx_equals") y+ (Left (n,xi), _) -> fprealx_equals x (fprealx_set_expt n y "fprealx_equals")+ (Right (r, _), Right (r', _)) -> r == r'++-- ----------------------------------------------------------------------+-- ** Shape parameters++-- | Return a piece of shape data to represent an /l/-qubit quantum+-- real with exponent /n/. +-- Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+fprealq_shape :: Int -> Int -> FPRealQ+fprealq_shape n l = fprealx n $ qdint_shape l++-- | Return a piece of shape data to represent an /l/-bit 'FPRealC', +-- with exponent /n/.+-- Please note that the data can only be used as shape; it will be+-- undefined at the leaves.+fprealc_shape :: Int -> Int -> FPRealC+fprealc_shape n l = fprealx n $ cint_shape l++-- ======================================================================+-- ** Circuit type class instances++-- Note: instance declarations do not show up in the documentation++-- | Try to set the exponent/length of an 'FPReal' to that of another 'FPRealX'+-- value (e.g. an 'FPRealQ', an 'FPRealC', or another 'FPReal').+-- This will fail with an error if both numbers already have determinate+-- lengths that don't coincide. In this case, the string argument is+-- used as an error message.+--+-- The possible “shapes” of 'FPReal's may be seen as a partial order, where +-- /s1/ ≤ /s2/ means that values of shape /s1/ are coercible to values of shape+-- /s2/. 'fpreal_promote' may be seen as taking the binary /sup/ in this poset.+fpreal_promote :: FPReal -> FPRealX x -> ErrMsg -> FPReal+fpreal_promote br xr errmsg = + case fprealx_maybe_expt xr of+ Nothing -> br+ Just n -> + let br' = fprealx_set_expt n br (errmsg "FPReal: exponent mismatch")+ in fprealx n $ intm_promote (fprealx_num br') (fprealx_num xr) (errmsg "FPReal: length mismatch") + +type instance QCType x y (FPRealX z) = FPRealX (QCType x y z)+type instance QTypeB FPReal = FPRealQ++instance QCLeaf x => QCData (FPRealX x) where+ qcdata_mapM shape f g xr+ = mmap (fprealx (fprealx_expt xr)) $ qcdata_mapM (fprealx_num shape) f g (fprealx_num xr) + qcdata_zip shape q c q' c' xr yr e+ | fprealx_expt xr == fprealx_expt yr+ = fprealx (fprealx_expt xr) + $ qcdata_zip (fprealx_num shape) q c q' c' (fprealx_num xr) (fprealx_num yr) (const $ e "FPRealX: length mismatch")+ | otherwise+ = error (e "FPRealX: exponent mismatch")+ qcdata_promote = fpreal_promote++-- Labeling of FPRealX is s[hi-1], ..., s[lo], where lo is the exponent.+instance QCLeaf x => Labelable (FPRealX x) String where+ label_rec xr s = do+ let n = fprealx_expt xr+ xi = fprealx_num xr+ qx = list_of_xint_lh xi+ sequence_ [ label_rec q s `indexed` show i | (q,i) <- zip qx [n..] ]++-- ======================================================================+-- * Classical arithmetic on FPReal++-- | Convert an 'FPReal' to a 'Double'.+double_of_fpreal :: FPReal -> Double+double_of_fpreal xr = case fpreal_case xr of+ Left (n, xi) -> if n >= 0 then (fromIntegral xi) * (2^n)+ else (fromIntegral xi) / (2^(abs n))+ Right r -> r++-- | From a list of 'FPReal's, extract a common shape, provided they have compatible shape+-- (i.e. if any have determinate exponent, they must agree; similarly for length),+-- and throw an error otherwise.+-- +-- Can be seen as producing finitary suprema in the partial order of shapes.+--+-- The 'FPReal' produced should be considered just a shape; its value is a dummy that+-- should never be used (and will throw an error if it is).+fpreal_common_shape :: [FPReal] -> ErrMsg -> FPReal +fpreal_common_shape xs e = foldl (\x y -> fpreal_promote x y e)+ (fpreal_of_double (error $ e "fpreal_common_shape: dummy value produced here was later accessed"))+ xs++-- | Auxiliary function for lifting a binary operator from 'Double'+-- to 'IntM'. The string argument is the name of the operator, for+-- error messages.+fpreal_binop :: (Double -> Double -> Double) -> String -> (FPReal -> FPReal -> FPReal) +fpreal_binop op opname x y =+ fpreal_promote + (fpreal_of_double $ op (double_of_fpreal x) (double_of_fpreal y))+ (fpreal_common_shape [x,y] errmsg)+ (const "FPReal: internal error (fpreal_binop)")+ where errmsg = (\s -> "Binary operation " ++ opname ++ " on FPReal: " ++ s)++-- | Auxiliary function for lifting a unary operator from 'Double' to+-- 'FPReal'.+fpreal_unop :: (Double -> Double) -> FPReal -> FPReal+fpreal_unop op x = fpreal_promote (fpreal_of_double $ op $ double_of_fpreal x) x + (const "FPReal: internal error (fpreal_unop)")++------+-- Classical typeclass instances+------++instance Eq x => Eq (FPRealX x) where+ (==) = fprealx_equals++instance Num FPReal where+ (+) = fpreal_binop (+) "+"+ (*) = fpreal_binop (*) "*"+ (-) = fpreal_binop (-) "-"+ abs = fpreal_unop abs+ signum = fpreal_unop signum+ -- Note: signum on determinate exponent/length FPReals has slightly + -- surprising behavior if /n/ ≤ –/l/: this will give + -- 0, since then 1 = –1 = 0 mod 2^{l + n}. In other words, the+ -- output 1.0 or -1.0 is not representable in this fixed-precision format.+ fromInteger = fpreal_of_double . fromInteger++instance Ord FPReal where+ compare x y = compare (double_of_fpreal x) (double_of_fpreal y)+-- TODO: is this the right thing to do? Perhaps we should first check they have +-- compatible shapes, and throw an error otherwise.++instance Enum FPReal where+ succ = fpreal_unop succ+ pred = fpreal_unop pred+ toEnum = fromIntegral+ fromEnum = fromEnum . double_of_fpreal++instance Real FPReal where+ toRational = toRational . double_of_fpreal++instance Fractional FPReal where+ fromRational = fpreal_of_double . fromRational+ recip = fpreal_unop recip++{- TODO: something along these lines would probably still be good to give. Work on thinking of good interface. Or maybe not really necessary??++-- | Convert from any 'RealFrac' type (eg 'Rational', 'Float') to 'FPReal', with specified precision and (possibly indeterminate) length.+fpreal_from_realfrac_with_precision :: (RealFrac a) => Int -> Maybe Int -> a -> FPReal+fpreal_from_realfrac_with_precision n l r = case l of + Just l -> FPRealX n $ intm l (round $ r * (2^n))+ Nothing -> FPRealX n $ fromIntegral (round $ r * (2^n))+-}++instance Floating FPReal where+ pi = fpreal_of_double pi+ log = fpreal_unop log+ exp = fpreal_unop exp + sin = fpreal_unop sin + cos = fpreal_unop cos + sinh = fpreal_unop sinh + cosh = fpreal_unop cosh + asin = fpreal_unop asin + acos = fpreal_unop acos + atan = fpreal_unop atan + asinh = fpreal_unop asinh + acosh = fpreal_unop acosh + atanh = fpreal_unop atanh + +instance RealFrac FPReal where+ properFraction x =+ let (x_int, x_frac_double) = properFraction $ double_of_fpreal x in+ (x_int, fpreal_promote (fpreal_of_double x_frac_double) x (const "FPReal: internal error (properFraction)")) + ceiling = ceiling . double_of_fpreal++{- TODO: would definitely be good to give.+instance RealFloat FPReal+-}++-- ** Shape/precision control++-- | Extend the length of a determinate length and precision 'FPReal' by /m/ high and /n/ low bits, without changing its value. +fpreal_pad :: Int -> Int -> FPReal -> FPReal+fpreal_pad m n x = + case fprealx_maybe_expt x of+ Nothing -> error e_expt+ Just x_expt ->+ let x_num = fprealx_num x+ in case intm_length x_num of+ Nothing -> error e_length+ Just x_length ->+ fprealx (x_expt - n) (2^n * intm_extend_signed (x_length + m + n) x_num)+ where+ e_expt = "fpreal_pad: input of indeterminate exponent"+ e_length = "fpreal_pad: input of indeterminate length"++-- | Discard the top /m/ and bottom /n/ bits of a determinate length and precision 'FPReal'.+fpreal_unpad :: Int -> Int -> FPReal -> FPReal+fpreal_unpad m n x = + let x_bits = boollist_of_intm_bh $ fprealx_num x+ x_expt = fprealx_expt x+ x_bits_new = reverse $ drop n $ reverse $ drop m x_bits+ in fprealx (x_expt + n) (intm_of_boollist_bh x_bits_new)++-- | Fix the length of an 'IntM' (to automatically-generated values), if not already determinate.+--+-- TODO: belongs in 'Quipper.Libraries.Arith'+intm_fix_length_auto :: IntM -> IntM+intm_fix_length_auto x = case intm_length x of+ Just _ -> x+ Nothing -> + let l = (1 +) $ ceiling $ logBase 2 $ fromIntegral $ abs x + (if x >= 0 then 1 else 0)+ in intm l (fromIntegral x)++-- | Fix the precision and length of an 'FPReal' (to automatically-generated values), +-- leaving unchanged any parts of the shape that are already set. +--+-- TODO: discuss \/ reconsider \/ improve implementation of this. ++-- Implementation note: aim to use as few bits as possible, while retaining accuracy.+--+-- Not clear what should be done in case denominator not a power of 2. However,+-- Haskell’s 'Double' has radix 2, so ordinarily denominator always should be a power of 2.+--+-- However, this does give pretty high-precision by default! Is that good? Seems expensive in qubits.+fpreal_fix_shape_auto :: FPReal -> FPReal+fpreal_fix_shape_auto x = case fpreal_case x of+ Left (e, n) -> fprealx e (intm_fix_length_auto n)+ Right x -> + let r = toRational x+ d = denominator r+ in if d == 2^(round $ logBase 2 $ fromIntegral d)+ then fprealx+ (negate $ round $ logBase 2 $ fromIntegral d)+ (intm_fix_length_auto $ fromIntegral $ numerator r)+ else error "fpreal_fix_shape_auto: not yet fully implemented"++-- ======================================================================+-- * Quantum operations: FPRealQ++-- ** Shape/precision control++-- | Extend the length of an 'FPRealQ' by /m/ high bits and /n/ low bits, without changing its value. +fprealq_pad :: Int -> Int -> FPRealQ -> Circ FPRealQ+fprealq_pad m n x = do+ let x_bits = qulist_of_qdint_bh $ fprealx_num x+ x_expt = fprealx_expt x+ new_high_bits <- qinit $ replicate m False+ new_high_bits <- case x_bits of + [] -> return new_high_bits + (x_high:_) -> mapUnary qnot new_high_bits `controlled` x_high+ new_low_bits <- qinit $ replicate n False+ return $ fprealx (x_expt - n) $ qdint_of_qulist_bh $ new_high_bits ++ x_bits ++ new_low_bits++-- | Cut off the top /m/ and bottom /n/ bits of an 'FPRealQ', retaining them as explicit garbage.+fprealq_unpad :: Int -> Int -> FPRealQ -> Circ (FPRealQ, [Qubit])+fprealq_unpad m n x =+ let x_bits = qulist_of_qdint_bh $ fprealx_num x+ x_expt = fprealx_expt x+ x_bits_new = reverse $ drop n $ reverse $ drop m x_bits+ garbage = (take m x_bits) ++ (take n $ reverse x_bits)+ in return (fprealx (x_expt + n) (qdint_of_qulist_bh x_bits_new), garbage)++-- | Formally shift an 'FPRealQ' up /n/ bits, i.e. add /n/ to its exponent.+fprealq_shift :: Int -> FPRealQ -> FPRealQ+fprealq_shift n x =+ let num = fprealx_num x+ expt = fprealx_expt x+ in fprealx (expt + n) num++-- ** Quantum arithmetic++-- $ Besides the functions appearing here in the documentation, basic operations ('q_add', etc) are also provided as methods of the 'QNum' type class instance; see 'QNum' for documentation of these functions.++------+-- Quantum typeclass instances+------ ++instance QNum FPRealQ where+ q_add x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ (nx,ny,ns) <- q_add nx ny+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex ns))+ else error "q_add // FPReal: exponent mismatch in arguments."+ q_mult x y = -- TODO: to implement+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ np <- qinit $ qc_false $ nx+ (nx,ny,np) <- named_gate "q_mult // FPReal" (nx,ny,np)+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex np))+ else error "q_mult // FPReal: length mismatch in arguments."+ q_sub x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ in if ex == ey then do+ let nx = fprealx_num x+ ny = fprealx_num y+ (nx,ny,nd) <- q_sub nx ny+ return ((fprealx ex nx), (fprealx ey ny), (fprealx ex nd))+ else error "q_sub // FPReal: length mismatch in arguments."+ q_abs x = + let ex = fprealx_expt x+ nx = fprealx_num x+ in do+ (nx,nx') <- q_abs nx+ return ((fprealx ex nx), (fprealx ex nx'))+ q_negate x = + let ex = fprealx_expt x+ nx = fprealx_num x+ in do+ (nx,nx') <- q_negate nx+ return ((fprealx ex nx), (fprealx ex nx'))+ q_signum = error "FPReal // q_signum: not yet implemented." -- TODO!+ q_fromQDInt x = do+ (x,x') <- qc_copy_fun x+ return (x,(fprealx 0 x'))++instance QOrd FPRealQ where+ q_less x y = + let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ x_less_y <- q_less nx ny+ return x_less_y+ else error "q_less // FPReal: length mismatch in arguments."+ q_leq x y = + let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ x_leq_y <- q_leq nx ny+ return x_leq_y+ else error "q_leq // FPReal: length mismatch in arguments."++-- | Analogue of 'q_add_in_place', for 'FPRealQ'.+fprealq_add_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_add_in_place x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ (x,y) <- q_add_in_place nx ny+ return ((fprealx ex nx), (fprealx ey ny))+ else error "q_add_in_place_fprealq // FPReal: length mismatch in arguments."++-- | Analogue of 'q_sub_in_place', for 'FPRealQ'.+fprealq_sub_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_sub_in_place x y =+ let ex = fprealx_expt x+ ey = fprealx_expt y+ nx = fprealx_num x+ ny = fprealx_num y+ in if ex == ey then do+ (x,y) <- q_sub_in_place nx ny+ return ((fprealx ex nx), (fprealx ey ny))+ else error "q_sub_in_place_fprealq // FPReal: length mismatch in arguments."++-- | Subtract a parameter 'FPReal' from an 'FPRealQ', in place. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_sub_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ+fprealq_sub_param_in_place x qy = + with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy) <- fprealq_sub_in_place qx qy+ return qy)+ where+ e_prec = "fprealq_sub_param_in_place: incompatible precision"++-- | Add a parameter 'FPReal' to an 'FPRealQ', in place. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_add_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ+fprealq_add_param_in_place x qy = + with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy) <- fprealq_add_in_place qx qy+ return qy)+ where+ e_prec = "fprealq_sub_param_in_place: incompatible precision"++-- | Multiply an 'FPRealQ' by a parameter 'FPReal'. The parameter may have any shape.+fprealq_mult_param_het :: FPReal -> FPRealQ -> Circ (FPRealQ, FPRealQ)+fprealq_mult_param_het x_in qy = do+ let x = fpreal_fix_shape_auto x_in+ e = fprealx_expt x+ l = fromJust $ intm_length $ fprealx_num x+ x_bits = boollist_of_intm_bh $ fprealx_num x+ qy_expt = fprealx_expt qy+ qy_num = fprealx_num qy+ qy_length = qdint_length qy_num+ prod_bits = [ (is_neg, i) | (x_i, i) <- zip x_bits [e+l-1,e+l-2..e]+ , x_i == True+ , let is_neg = i == e+l-1 ]+ qprod <- if null prod_bits + then qinit $ fpreal_promote 0 qy ("internal error: fprealq_mult_param_het promotion: " ++)+ else with_computed (do+ let i_max = snd $ head prod_bits+ i_min = snd $ last prod_bits+ -- Initialise an accumulating product at 0, with as many bits as may be needed in intermediate calculation:+ qprod_accum <- qinit $ fprealx (qy_expt + i_min) $ intm (1 + qy_length + i_max - i_min) 0+ -- Add the appropriate shifts of qy to it:+ qprod_accum <- foldM+ (\qprod_accum (is_neg, i) -> do + qy_i <- qc_copy $ fprealq_shift i qy+ qy_i <- fprealq_pad (1 + i_max - i) (i - i_min) qy_i+ (qy_i, qprod_accum) <- if is_neg+ then fprealq_sub_in_place qy_i qprod_accum+ else fprealq_add_in_place qy_i qprod_accum+ return qprod_accum)+ qprod_accum+ prod_bits+ -- Now shift/truncate the product to match the input length/precision:+ qprod_large <- fprealq_pad (max (-i_max) 0) (max i_min 0) qprod_accum+ (qprod, garbage) <- fprealq_unpad (1 + max i_max 0) (max (-i_min) 0) qprod_large+ return qprod)+ -- Copy it for output, before erasing garbage:+ qc_copy+ return (qy, qprod)++-- | Compare an 'FPRealQ' to a parameter 'FPReal'. Assume compatible precision.+-- +-- Note: highly sub-optimal. TODO: optimize!+fprealq_ge_param :: FPReal -> FPRealQ -> Circ (FPRealQ, Qubit)+fprealq_ge_param x qy =+ with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do+ (qx, qy, test) <- q_ge qx qy+ return (qy, test))+ where+ e_prec = "fprealq_ge_param: incompatible precision"++-- | 'fprealq_add_het' /p/ /l/ /qx/ /qy/: add two 'FPRealQ's, of potentially different precisions and lengths, into a fresh one with precision /p/ and length /l/. +--+-- TODO: not yet implemented; currently just black box.+fprealq_add_het :: Int -> Int -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ,FPRealQ)+fprealq_add_het p l qx qy = do+ qz <- qinit (fprealx p (intm l 0))+ (qx,qy,qz) <- named_gate "fprealq_add_het" (qx,qy,qz)+ return (qx,qy,qz)++-- | @'fprealq_logBase_internal' /errmsg/ /b/ /h/ /p/ /qx/'@: compute log[sub /b/](/qx/), returning /l/ binary digits before the point and /p/ after. The underlying implementation of the rest of the 'fprealq_log' family. +--+-- Behavior on non-positive /qx/: currently unspecified. TODO: decide and fix this. Make assertion/post-selection on positivity? Or treat as unsigned?+--+-- Time-complexity (estimated): /O/((log /l/[sup /qx/] + log log /b/)(/l/[sup /qx/] + (/h/+/p/)[sup 2])).+fprealq_logBase_internal :: (Floating a, RealFrac a) => ErrMsg -> a -> Int -> Int -> FPRealQ -> Circ (FPRealQ, FPRealQ)+fprealq_logBase_internal e b h_out p_out x =+ if h_out + p_out < 0 + then error $ e "negative length specified."+ else if b <= 0+ then error $ e "base of logarithm must be > 0"+ else do+ y <- with_computed + (do+ -- Shift x into the interval [0,1], and pad it for intermediate calculation:+ let l_in = qdint_length $ fprealx_num x+ e_in = fprealx_expt x+ l_in_pad = 1 + l_in `div` 2+ p_in_pad = p_out - (floor $ logBase 2 $ log b) + 1+ x_0 <- fprealq_pad l_in_pad p_in_pad $ fprealx (1 - l_in) $ fprealx_num x+ -- Bound the length and precision needed for computing log_2 x' to precision p_out: + let y_size_bound = 1 + (ceiling $ logBase 2 $ fromIntegral $ l_in - 1)+ y_h_pad = max 0 (y_size_bound - h_out)+ y_p_pad = 2 + (ceiling $ logBase 2 $ fromIntegral $ l_in_pad + p_in_pad - 1)+ y_0 <- qinit $ fprealx (- p_out - y_p_pad) $ intm (y_h_pad + h_out + p_out + y_p_pad) 0+ -- Iteratively compute log x_0, starting with (x0,y0): + let ks_big = 2^(l_in `div` 2)+ : (reverse $ [ 2^(2^i) | i <- [0..(ceiling $ logBase 2 $ (fromIntegral l_in) / 2) - 1] ])+ ks_small = [ (2^i + 1)/(2^i) | i <- [1..p_out - (floor $ logBase 2 $ log b) + 1]]+ -- bound in ks_small chosen to ensure logBase b k_fin < 2^(-p_out)+ (x_fin,y_fin) <- foldM+ (\(xi,yi) ki -> do+ (xi, xi_ki) <- fprealq_mult_param_het ki xi+ (xi_ki, test1) <- fprealq_ge_param 1 xi_ki+ (xi_ki, xi, test2) <- q_gt xi_ki xi+ (xi1, yi1) <- qc_copy (xi,yi)+ (xi1,xi) <- controlled_not xi1 xi `controlled` test1 .&&. test2 + (xi1,xi_ki) <- controlled_not xi1 xi_ki `controlled` test1 .&&. test2 + yi1 <- fprealq_sub_param_in_place (logBase (realToFrac b) ki) yi1 `controlled` test1 .&&. test2+ return (xi1, yi1))+ (x_0,y_0)+ (ks_big ++ ks_small)+ -- Correct for the shift between x and x_, then unpad y to the output precision:+ y_fin <- fprealq_add_param_in_place ((log 2 / log (realToFrac b)) * (fromIntegral $ l_in + e_in - 1)) y_fin+ (y_fin, garbage) <- fprealq_unpad y_h_pad y_p_pad y_fin+ return y_fin)+ qc_copy+ return (x,y)++-- | Compute the natural log of an 'FPRealQ', with length and precision as in the input.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_log :: FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_log x = + let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_log: " ++ ) (exp 1) h l x++-- | Compute the log (to arbitrary base) of an 'FPRealQ', with length and precision as in the input.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_logBase :: (Floating a, RealFrac a) => a -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_logBase b x =+ let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_logBase: " ++ ) b h l x++-- | @'fprealq_log_with_shape' /x/ /y/@: compute the natural log /y/, with length and precision of /x/.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_log_with_shape :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_log_with_shape x = + let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_log_with_shape: " ++ ) (exp 1) h l++-- | @'fprealq_log_with_shape' /b/ /x/ /y/@: compute log[sup /b/] /y/, with length and precision of /x/.+--+-- Note: the behavior on negative inputs is unspecified.+fprealq_logBase_with_shape :: (Floating a, RealFrac a)+ => a -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ)+fprealq_logBase_with_shape b x =+ let h = fprealx_length x + fprealx_expt x+ l = - fprealx_expt x + in fprealq_logBase_internal ("fprealq_logBase_with_shape: " ++ ) b h l
@@ -0,0 +1,649 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/GateDecompositions.hs" #-}+-- | This library contains special decompositions of particular gates+-- into particular gate bases. It also contains functions for+-- decomposing multiple controls.+-- +-- For example, we provide particular decompositions of the Toffoli,+-- Fredkin, doubly-controlled /iX/-gate, and certain other controlled+-- Clifford gates, into the Clifford+/T/ base. In some cases, we+-- provide more than one decomposition.+-- +-- Many of these decompositions are taken or adapted from the+-- literature, for example, from:+-- +-- * M. A. Nielsen and I. L. Chuang, +-- /Quantum Computation and Quantum Information/,+-- Cambridge University Press, 2002.+-- +-- * M. Amy, D. Maslov, M. Mosca, and M. Roetteler,+-- A meet-in-the-middle algorithm for fast synthesis +-- of depth-optimal quantum circuits,+-- /IEEE Transactions on Computer-Aided Design of/ +-- /Integrated Circuits and Systems/ 32(6):818-830.+-- Also available from <http://arxiv.org/abs/1206.0758>.+-- +-- * A. Barenco, C. H. Bennett, R. Cleve, D. P. DiVincenzo,+-- N. Margolus, P. Shor, T. Sleator, J. A. Smolin, and H. Weinfurter,+-- Elementary gates for quantum computation,+-- /Physical Review A/ 52(5):3457-3467, 1995. +-- Also available from <http://arxiv.org/abs/quantph/9503016>.+-- +-- * P. Selinger, Quantum circuits of T-depth one,+-- /Physical Review A/ 87, 042302 (4 pages), 2013.+-- Also available from <http://arxiv.org/abs/1210.0974>.+-- +-- * B. Giles and P. Selinger, Exact synthesis of multiqubit+-- Clifford+T circuits, /Physical Review A/ 87, 032332 (7 pages),+-- 2013. Also available from <http://arxiv.org/abs/1212.0506>.++module Quipper.Libraries.GateDecompositions where++import Quipper++import Control.Monad++-- ----------------------------------------------------------------------+-- * Decomposition of gates++-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- from Nielsen and Chuang (Figure 4.9). The first argument is the+-- target, and the other two are the controls. The controls can be+-- positive or negative.+-- +-- \[image toffoli_NC.png]+toffoli_NC_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_NC_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_NC" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ hadamard_at q+ qnot_at q `controlled` w1+ reverse_imp_if (not x) gate_T_inv_at q+ qnot_at q `controlled` w2+ reverse_imp_if (x `xor` y) gate_T_at q+ qnot_at q `controlled` w1+ reverse_imp_if (not y) gate_T_inv_at q+ qnot_at q `controlled` w2+ gate_T_at q+ reverse_imp_if (not x) gate_T_inv_at w1+ hadamard_at q+ qnot_at w1 `controlled` w2+ reverse_imp_if (x `xor` y) gate_T_inv_at w1+ qnot_at w1 `controlled` w2+ reverse_imp_if (not x) gate_S_at w1+ reverse_imp_if (not y) gate_T_at w2+ comment_with_label "EXIT: toffoli_NC" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- from Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 13). The first argument is the target, and the other two are the+-- controls. The controls can be positive or negative.+-- +-- \[image toffoli_AMMR.png]+toffoli_AMMR_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_AMMR_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_AMMR" (q,c1,c2) ("q","c1","c2")+ without_comments $ do+ hadamard_at q+ ccZ_AMMR_at q c1 c2+ hadamard_at q+ comment_with_label "EXIT: toffoli_AMMR" (q,c1,c2) ("q","c1","c2")++-- | Decomposition of the Toffoli gate using controlled Clifford+-- operators, from Nielsen and Chuang (Figure 4.8). The controls can be+-- positive or negative.+-- +-- \[image toffoli_V.png]+toffoli_V_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_V_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_V" (q,c1,c2) ("q","c1","c2")+ let q1 = from_signed c1+ let q2 = from_signed c2+ gate_V_at q `controlled` c1+ qnot_at q1 `controlled` c2+ gate_V_inv_at q `controlled` c1+ qnot_at q1 `controlled` c2+ gate_V_at q `controlled` c2+ comment_with_label "EXIT: toffoli_V" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the Toffoli gate into the Clifford+/T/ base,+-- using /T/-depth 1 and four ancillas. From+-- <http://arxiv.org/abs/1210.0974> (Figure 1). The first argument is+-- the target, and the other two are the controls. The controls can be+-- positive or negative.+-- +-- \[image toffoli_S.png]+toffoli_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_S_at q c1 c2 = do+ comment_with_label "ENTER: toffoli_S" (q,c1,c2) ("q","c1","c2")+ without_comments $ do+ hadamard_at q+ ccZ_S_at q c1 c2+ hadamard_at q+ comment_with_label "EXIT: toffoli_S" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-count 4 and /T/-depth 2. Adapted from+-- (<http://arxiv.org/abs/1210.0974>, Figure 10). The first argument+-- is the target, and the other two are the controls. The controls can+-- be positive or negative.+-- +-- \[image cc_iX.png]+cc_iX_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ hadamard_at q + qnot_at w1 `controlled` q+ qnot_at w2 `controlled` w1+ reverse_imp_if (not x) gate_T_at w1+ reverse_imp_if (x `xor` y) gate_T_inv_at w2+ qnot_at w1 `controlled` q+ qnot_at w2 `controlled` w1+ gate_T_inv_at q+ reverse_imp_if (not y) gate_T_at w2+ qnot_at w2 `controlled` q+ hadamard_at q + when (not x && not y) $ do -- need a phase correction+ gate_omega_at w1+ gate_omega_at w2+ comment_with_label "EXIT: cc_iX" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-count 4, and using the control qubits+-- only as controls. Derived from Nielsen and Chuang (Figure 4.9). The+-- controls can be positive or negative.+-- +-- \[image cc_iX_simple.png]+cc_iX_simple_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_simple_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX_simple" (q,c1,c2) ("q","c1","c2")+ hadamard_at q+ qnot_at q `controlled` c1+ gate_T_at q+ qnot_at q `controlled` c2+ gate_T_inv_at q+ qnot_at q `controlled` c1+ gate_T_at q+ qnot_at q `controlled` c2+ gate_T_inv_at q+ hadamard_at q+ comment_with_label "EXIT: cc_iX_simple" (q,c1,c2) ("q","c1","c2")+ +-- | Decomposition of the doubly-controlled /iX/-gate into the+-- Clifford+/T/ base, using /T/-depth 1 and one ancilla. Adapted from+-- (<http://arxiv.org/abs/1210.0974>, Figure 9). The first argument+-- is the target, and the other two are the controls. The controls can+-- be positive or negative.+-- +-- \[image cc_iX_S.png]+cc_iX_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_S_at q c1 c2 = do+ comment_with_label "ENTER: cc_iX_S" (q,c1,c2) ("q","c1","c2")+ let sx = get_sign c1+ sy = get_sign c2+ x = from_signed c1+ y = from_signed c2+ z = q+ hadamard_at z+ with_ancilla $ \w -> do+ qnot y `controlled` z+ qnot w `controlled` x+ qnot x `controlled` z+ qnot w `controlled` y+ reverse_imp_if (not sx) gate_T_at x+ reverse_imp_if (not sy) gate_T_at y+ gate_T_inv_at z+ reverse_imp_if (sx `xor` sy) gate_T_inv_at w+ qnot w `controlled` y+ qnot x `controlled` z+ qnot w `controlled` x+ qnot y `controlled` z+ when (not sx && not sy) $ do -- need a phase correction+ gate_omega_at x+ gate_omega_at y+ hadamard_at z+ comment_with_label "EXIT: cc_iX_S" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)+ +-- | Decomposition of the doubly-controlled /Z/-gate into the+-- Clifford+/T/ base. Adapted from Amy et+-- al. (<http://arxiv.org/abs/1206.0758v3>, Figure 13). The controls can+-- be positive or negative.+-- +-- \[image ccZ_AMMR.png]+ccZ_AMMR_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+ccZ_AMMR_at q c1 c2 = do+ comment_with_label "ENTER: ccZ_AMMR" (q,c1,c2) ("q","c1","c2")+ let x = get_sign c1+ y = get_sign c2+ w1 = from_signed c1+ w2 = from_signed c2+ gate_T_at q+ reverse_imp_if (not x) gate_T_at w1+ reverse_imp_if (not y) gate_T_at w2+ qnot_at w2 `controlled` w1+ qnot_at w1 `controlled` q+ qnot_at q `controlled` w2+ reverse_imp_if (not x) gate_T_inv_at w1+ reverse_imp_if (x `xor` y) gate_T_at q+ qnot_at w1 `controlled` w2+ reverse_imp_if (not y) gate_T_inv_at w1+ reverse_imp_if (x `xor` y) gate_T_inv_at w2+ qnot_at w1 `controlled` q+ qnot_at q `controlled` w2+ qnot_at w2 `controlled` w1+ comment_with_label "EXIT: ccZ_AMMR" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the doubly-controlled /Z/-gate into the+-- Clifford+/T/ base, using /T/-depth 1 and four ancillas. From+-- Selinger (<http://arxiv.org/abs/1210.0974>, Figure 1). The controls+-- can be positive or negative.+-- +-- \[image ccZ_S.png]+ccZ_S_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+ccZ_S_at q c1 c2 = do+ comment_with_label "ENTER: ccZ_S" (q,c1,c2) ("q","c1","c2")+ let sx = get_sign c1+ sy = get_sign c2+ x = from_signed c1+ y = from_signed c2+ z = q+ with_ancilla_init (0,0,0,0) $ \(xyz, xy, yz, xz) -> do+ qnot yz `controlled` y+ qnot xyz `controlled` x+ qnot xy `controlled` y+ qnot yz `controlled` z+ qnot xz `controlled` xyz+ qnot xy `controlled` x+ qnot xz `controlled` z+ qnot xyz `controlled` yz+ reverse_imp_if (not sx) gate_T_at x+ reverse_imp_if (not sy) gate_T_at y+ gate_T_at z+ reverse_imp_if (sx `xor` sy) gate_T_at xyz+ reverse_imp_if (sx `xor` sy) gate_T_inv_at xy+ reverse_imp_if (not sy) gate_T_inv_at yz+ reverse_imp_if (not sx) gate_T_inv_at xz+ qnot xyz `controlled` yz+ qnot xz `controlled` z+ qnot xy `controlled` x+ qnot xz `controlled` xyz+ qnot yz `controlled` z+ qnot xy `controlled` y+ qnot xyz `controlled` x+ qnot yz `controlled` y+ comment_with_label "EXIT: ccZ_S" (q,c1,c2) ("q","c1","c2")+ where+ xor = (/=)++-- | Decomposition of the Fredkin (controlled-Swap) gate into the+-- Clifford+/T/ base. The first two arguments are the targets, and the+-- last one the control. The controls can be positive or negative.+-- +-- \[image fredkin.png]+fredkin_at :: Qubit -> Qubit -> Signed Qubit -> Circ ()+fredkin_at q1 q2 c = do+ comment_with_label "ENTER: fredkin" (q1,q2,c) ("q1","q2","c")+ without_controls $ do+ qnot_at q2 `controlled` q1+ toffoli_AMMR_at q1 (Signed q2 True) c+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: fredkin" (q1,q2,c) ("q1","q2","c")++-- | Decomposition of a controlled /H/-gate into the Clifford+/T/+-- base. From Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 5(a)). The first argument is the target and the second one the+-- control. The control can be positive or negative.+-- +-- \[image cH_AMMR.png]+cH_AMMR_at :: Qubit -> Signed Qubit -> Circ ()+cH_AMMR_at q c = do+ comment_with_label "ENTER: cH_AMMR" (q,c) ("q","c")+ gate_S_inv_at q+ hadamard_at q+ gate_T_inv_at q+ qnot_at q `controlled` c+ gate_T_at q+ hadamard_at q+ gate_S_at q+ comment_with_label "EXIT: cH_AMMR" (q,c) ("q","c")++-- | Decomposition of a controlled /W/-gate into the Clifford+/T/+-- base. The first two arguments are the targets, and the last+-- argument is the control. The control can be positive or negative.+-- +-- \[image controlled_W.png]+controlled_W_at :: Qubit -> Qubit -> Signed Qubit -> Circ ()+controlled_W_at q1 q2 c = do+ comment_with_label "ENTER: controlled_W" (q1,q2,c) ("W1","W2","c")+ without_comments $ do+ qnot_at q2 `controlled` q1+ gate_S_inv_at q1+ hadamard_at q1+ gate_T_inv_at q1+ toffoli_AMMR_at q1 (Signed q2 True) c+ gate_T_at q1+ hadamard_at q1+ gate_S_at q1+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: controlled_W" (q1,q2,c) ("W1","W2","c")++-- | Decomposition of a /W/-gate into the Clifford+/T/ base.+-- +-- \[image gate_W_CliffordT.png]+gate_W_CliffordT_at :: Qubit -> Qubit -> Circ ()+gate_W_CliffordT_at q1 q2 = do+ comment_with_label "ENTER: gate_W_CliffordT" (q1,q2) ("W1","W2")+ without_comments $ do+ qnot_at q2 `controlled` q1+ cH_AMMR_at q1 (Signed q2 True)+ qnot_at q2 `controlled` q1+ comment_with_label "EXIT: gate_W_CliffordT" (q1,q2) ("W1","W2")++-- | Decomposition of a controlled /iX/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_iX.png]+controlled_iX_at :: Qubit -> Signed Qubit -> Circ ()+controlled_iX_at q c = do+ comment_with_label "ENTER: controlled_iX" (q,c) ("q","c")+ let x = get_sign c+ w = from_signed c+ qnot_at q `controlled` c+ reverse_imp_if (not x) gate_S_at w+ when (not x) $ do -- need a phase correction+ gate_omega_at q+ gate_omega_at w+ comment_with_label "EXIT: controlled_iX" (q,c) ("q","c")++-- | Decomposition of a controlled /S/-gate into the Clifford+/T/+-- base. From Amy et al. (<http://arxiv.org/abs/1206.0758v3>, Figure+-- 5(b)). The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_S.png]+controlled_S_at :: Qubit -> Signed Qubit -> Circ ()+controlled_S_at q c = do+ comment_with_label "ENTER: controlled_S" (q,c) ("q","c")+ let x = get_sign c + w = from_signed c+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_inv_at w+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_at w+ gate_T_at q+ comment_with_label "EXIT: controlled_S" (q,c) ("q","c")++-- | Decomposition of a controlled /T/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_T.png]+controlled_T_at :: Qubit -> Signed Qubit -> Circ ()+controlled_T_at q c = do+ comment_with_label "ENTER: controlled_T" (q,c) ("q","c")+ without_comments $ do+ with_ancilla_init False $ \r -> do+ cc_iX_at r (Signed q True) c+ gate_T_at r+ reverse_generic_imp cc_iX_at r (Signed q True) c+ comment_with_label "EXIT: controlled_T" (q,c) ("q","c")++-- | Decomposition of a controlled /V/-gate into the Clifford+/T/+-- base. Adapted from Amy et al. (<http://arxiv.org/abs/1206.0758v3>,+-- Figure 5(c)). Our /V/-gate is /H//S/[sup †]/H/ as in Nielsen and+-- Chuang. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_V.png]+controlled_V_at :: Qubit -> Signed Qubit -> Circ ()+controlled_V_at q c = do+ comment_with_label "ENTER: controlled_V" (q,c) ("q","c")+ let x = get_sign c+ w = from_signed c+ hadamard_at q+ reverse_imp_if (not x) gate_T_inv_at w+ qnot w `controlled` q+ reverse_imp_if (not x) gate_T_at w+ gate_T_inv_at q+ qnot w `controlled` q+ hadamard_at q+ comment_with_label "EXIT: controlled_V" (q,c) ("q","c")++-- | Decomposition of a controlled /E/-gate into the Clifford+/T/+-- base. The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_E.png]+controlled_E_at :: Qubit -> Signed Qubit -> Circ ()+controlled_E_at q c = do+ comment_with_label "ENTER: controlled_E" (q,c) ("q","c")+ with_signed_qubit c $ \r -> do+ gate_H_at q+ gate_S_at r+ gate_T_at q+ qnot_at q `controlled` r+ gate_T_inv_at q+ gate_H_at q+ qnot_at r `controlled` q+ gate_T_at r+ gate_T_inv_at q+ qnot_at r `controlled` q+ comment_with_label "EXIT: controlled_E" (q,c) ("q","c")++-- | Decomposition of a controlled [bold Y]-gate into the Clifford+/T/+-- base. The gate is from the Ground State Estimation algorithm and is+-- defined as [bold Y] = /SHS/, or equivalently,+-- +-- \[image Y.png]+-- +-- It should not be confused with the Pauli /Y/ gate.+-- The first argument is the target, and the second one is the+-- control. The control can be positive or negative.+-- +-- \[image controlled_YY.png]+controlled_YY_at :: Qubit -> Signed Qubit -> Circ ()+controlled_YY_at q c = do+ comment_with_label "ENTER: controlled_YY" (q,c) ("q","c")+ gate_S_at q+ qnot_at q `controlled` c+ gate_S_inv_at q+ hadamard_at q+ gate_T_inv_at q+ qnot_at q `controlled` c+ gate_T_at q+ hadamard_at q+ comment_with_label "EXIT: controlled_YY" (q,c) ("q","c")++-- | A \"plain\" Toffoli gate, not decomposed. This is provided for+-- convenience, for example to use with 'with_combined_controls'.+toffoli_plain_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+toffoli_plain_at q c1 c2 = do+ qnot_at q `controlled` (c1,c2)+ +-- | A \"plain\" doubly-controlled /iX/-gate, not decomposed. This is+-- provided for convenience, for example to use with+-- 'with_combined_controls'.+cc_iX_plain_at :: Qubit -> Signed Qubit -> Signed Qubit -> Circ ()+cc_iX_plain_at q c1 c2 = do+ gate_iX_at q `controlled` (c1,c2)++-- | Decomposition of an /m/-times controlled not-gate, using /m/−2+-- ancillas that do not need be initialized in a particular+-- state. Adapted from Barenco et al.+-- (<http://arxiv.org/abs/quantph/9503016>, Lemma 7.2).+-- +-- In addition to what is shown in Barenco et al., this function+-- permits some Toffoli gates to be replaced by doubly-controlled+-- /iX/-gates. This may be beneficial in gate bases, such as+-- Clifford+/T/, where a doubly-controlled /iX/-gate has a simpler+-- representation than a Toffoli gate.+-- +-- The first argument is a Toffoli gate to use in the+-- decomposition. The second argument may be either a Toffoli gate or+-- a doubly-controlled /iX/ gate. The third argument is the target,+-- the fourth argument is a list of qubits to be used as ancillas, and+-- the fifth argument is a list of signed controls. The ancillas need+-- not be initialized, and are returned in their original state.+-- +-- The size of this circuit is linear in the number of controls; the+-- decomposition uses 4/m/−8 doubly-controlled gates for /m/ ≥ 3.+-- +-- \[image multi_cnot_barenco.png]+multi_cnot_barenco_at :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Qubit -> [Qubit] -> [Signed Qubit] -> Circ ()+multi_cnot_barenco_at my_toffoli_at my_ciX_at q as cs =+ case cs of+ [] -> do+ qnot_at q+ [c] -> do+ qnot_at q `controlled` c+ [c1,c2] -> do+ my_toffoli_at q c1 c2+ c:cs -> do+ case as of+ [] -> error "multi_cnot_barenco_at: too few ancillas"+ a:as -> do+ my_toffoli_at q (Signed a True) c+ aux cs (a:as)+ my_toffoli_at q (Signed a True) c+ reverse_generic_imp aux cs (a:as)+ where+ aux :: [Signed Qubit] -> [Qubit] -> Circ ()+ aux [] as = return ()+ aux [c] as = return ()+ aux [c1,c2] (a:as) = do+ my_ciX_at a c1 c2+ aux (c:cs) (a1:a2:as) = do+ my_ciX_at a1 (Signed a2 True) c+ aux cs (a2:as)+ my_ciX_at a1 (Signed a2 True) c+ aux _ _ = error "multi_cnot_barenco_at: too few ancillas"++-- | Decomposition of a multiply-controlled /iX/-gate, using no+-- ancillas. Adapted from Giles and Selinger+-- (<http://arxiv.org/abs/1212.0506>, Section 5.2).+-- +-- The first argument is a Toffoli gate or a doubly-controlled+-- /iX/-gate. The third argument is the target, and the fourth+-- argument is a list of signed controls.+-- +-- The size of this circuit is linear in the number of controls; the+-- decomposition uses 8/m/−32 doubly-controlled gates, 4 /T/-gates,+-- and 2 /H/-gates, for /m/ ≥ 6.+-- +-- \[image multi_ciX_noancilla.png]+multi_ciX_noancilla_at :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Qubit -> [Signed Qubit] -> Circ ()+multi_ciX_noancilla_at my_ciX_at q [] = gate_iX_at q+multi_ciX_noancilla_at my_ciX_at q [c] = gate_iX_at q `controlled` c+multi_ciX_noancilla_at my_ciX_at q [c1,c2] = gate_iX_at q `controlled` [c1,c2]+multi_ciX_noancilla_at my_ciX_at q cs = do+ hadamard_at q+ gate_T_inv_at q+ multi_cnot_barenco_at my_ciX_at my_ciX_at q as1 cs2+ gate_T_at q+ multi_cnot_barenco_at my_ciX_at my_ciX_at q as2 cs1+ gate_T_inv_at q+ reverse_generic_imp (multi_cnot_barenco_at my_ciX_at my_ciX_at) q as1 cs2+ gate_T_at q+ reverse_generic_imp (multi_cnot_barenco_at my_ciX_at my_ciX_at) q as2 cs1+ hadamard_at q+ where+ n = length cs+ (cs1, cs2) = splitAt (n `div` 2) cs+ as1 = map from_signed cs1+ as2 = map from_signed cs2+ +-- ----------------------------------------------------------------------+-- * Decomposition of controls++-- | Partition a list of controls into quantum and classical.+partition_controls :: [Signed Endpoint] -> ([Signed Qubit], [Signed Bit])+partition_controls cs = (qcs, ccs) where+ qcs = [ Signed q b | Signed (Endpoint_Qubit q) b <- cs ]+ ccs = [ Signed c b | Signed (Endpoint_Bit c) b <- cs ]++-- | Given a function that expects a qubit (typically as a control),+-- turn it into a function that can handle a /signed/ (positive or+-- negative) qubit. This is done by conjugating the circuit with+-- negations on both sides, if the sign is negative. Usage:+-- +-- > with_signed_qubit c $ \q -> do+-- > <<<code using q>>>+-- +-- \[image with_signed_qubit.png]+with_signed_qubit :: Signed Qubit -> (Qubit -> Circ b) -> Circ b+with_signed_qubit (Signed q True) f = f q+with_signed_qubit (Signed q False) f = do+ gate_X_at q+ b <- f q+ gate_X_at q+ return b++-- | Decompose quantum controls recursively until at most /n/ remain,+-- and then pass these reduced controls to the given circuit.+-- Precondition: /n/ ≥ 1. +-- +-- The decomposition is done using a Toffoli-like gate that is given+-- as the first argument. This should be either a Toffoli gate, a+-- doubly-controlled /iX/-gate, a decomposition thereof, or any other+-- reversible ternary gate with the behavior+-- +-- * |000〉 ↦ |0〉|φ[sub 0]〉+-- +-- * |001〉 ↦ |0〉|φ[sub 1]〉+-- +-- * |010〉 ↦ |0〉|φ[sub 2]〉+-- +-- * |011〉 ↦ |1〉|φ[sub 3]〉,+-- +-- where the states |φ[sub 0]〉, …, |φ[sub 3]〉 are arbitrary.+-- +-- For example, when /n/=2, this typically yields a circuit such as+-- the following (here shown using the doubly-controlled /iX/-gate):+-- +-- \[image with_combined_controls2.png]+-- +-- And for /n/=1, the circuit typically looks like this:+-- +-- \[image with_combined_controls1.png]+-- +-- Classical controls are not decomposed, but are applied to the+-- resulting circuit directly.+with_combined_controls :: (Qubit -> Signed Qubit -> Signed Qubit -> Circ ()) -> Int -> [Signed Endpoint] -> ([Signed Qubit] -> Circ a) -> Circ a+with_combined_controls my_toffoli_at n cs code = circ where+ (qcs, ccs) = partition_controls cs+ len = length qcs+ -- m is the number of qcs to remove.+ m = if len <= n then 0 else len - n+ circ = with_controls ccs $ do+ aux m qcs code++ aux 0 qcs code = code qcs+ aux n [] code = code []+ aux n [c] code = code [c]+ aux n (c1:c2:qcs) code = do+ with_computed (quantum_and c1 c2) $ \c -> do+ aux (n-1) (qcs ++ [Signed c True]) code++ quantum_and :: Signed Qubit -> Signed Qubit -> Circ Qubit+ quantum_and c1 c2 = do+ q <- qinit 0+ my_toffoli_at q c1 c2+ return q
@@ -0,0 +1,111 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QFT.hs" #-}+-- | This module implements the Quantum Fourier Transform.++module Quipper.Libraries.QFT (+ qft_little_endian,+ qft_big_endian,+ qft_rev,+ qft_int+ ) where++import Quipper+import Quipper.Libraries.Arith++import Quipper.Utils.Auxiliary (mmap)++-- ----------------------------------------------------------------------+-- * Low-level implementation++-- | Like 'qft_rev', but without the comments and labels.+-- +-- Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- The input is little-endian and the output is big-endian.+-- +-- Unlike 'qft_little_endian' and 'qft_big_endian', this function can+-- be used in imperative style, i.e., it modifies its arguments \"in+-- place\".+qft_internal :: [Qubit] -> Circ [Qubit]+qft_internal [] = return []+qft_internal [x] = do + hadamard x+ return [x]+qft_internal (x:xs) = do + xs' <- qft_internal xs+ xs'' <- rotations x xs' (length xs')+ x' <- hadamard x+ return (x':xs'')+ where+ -- Auxiliary function used by 'qft'.+ rotations :: Qubit -> [Qubit] -> Int -> Circ [Qubit]+ rotations _ [] _ = return []+ rotations c (q:qs) n = do + qs' <- rotations c qs n+ q' <- rGate ((n + 1) - length qs) q `controlled` c+ return (q':qs')++-- ----------------------------------------------------------------------+-- * Wrappers++-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- Both the input and output qubit lists are little-endian, i.e., the+-- leftmost qubit (head of the list) is the least significant one.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_little_endian :: [Qubit] -> Circ [Qubit]+qft_little_endian qs = do+ comment_with_label "ENTER: qft_little_endian" qs "qs"+ qs' <- qft_internal qs+ let qs = reverse qs'+ comment_with_label "EXIT: qft_little_endian" qs "qs"+ return qs+ +-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- Both the input and output qubit lists are big-endian, i.e., the+-- leftmost qubit (head of the list) is the most significant one.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_big_endian :: [Qubit] -> Circ [Qubit] +qft_big_endian qs = do+ comment_with_label "ENTER: qft_big_endian" qs "qs"+ qs <- qft_internal (reverse qs)+ comment_with_label "EXIT: qft_big_endian" qs "qs"+ return qs++-- | Apply the Quantum Fourier Transform to a list of /n/ qubits.+-- The input is little-endian and the output is big-endian.+-- +-- Unlike 'qft_little_endian' and 'qft_big_endian', this function can+-- be used in imperative style, i.e., it modifies its arguments+-- \"in place\".+qft_rev :: [Qubit] -> Circ [Qubit]+qft_rev qs = do+ comment_with_label "ENTER: qft_rev" qs "qs"+ qs <- qft_internal qs+ comment_with_label "EXIT: qft_rev" qs "qs"+ return qs++-- | Apply the Quantum Fourier Transform to a 'QDInt'.+-- +-- Note that this function cannot be used in imperative style, i.e.,+-- it does not update its arguments \"in place\". The output qubits+-- are in different physical locations than the input ones.+qft_int :: QDInt -> Circ QDInt+qft_int x = do+ comment_with_label "ENTER: qft_int" x "x"+ x <- mmap qdint_of_qulist_bh $ qft_big_endian $ qulist_of_qdint_bh x+ comment_with_label "ENTER: qft_int" x "x"+ return x++-- ----------------------------------------------------------------------+-- * Testing++-- | A simple test.+test :: Int -> IO ()+test n = print_generic Preview qft_little_endian (replicate n qubit)
@@ -0,0 +1,52 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QFTAdd.hs" #-}+-- | This module provides an implementation of the addition circuit found in +-- Thomas G. Draper's paper \"Addition on a Quantum Computer\".+module Quipper.Libraries.QFTAdd +(+ qft_add_in_place+)+where++import Quipper+import Quipper.Libraries.Arith -- we make use of the QDInt data type for quantum integers+import Quipper.Libraries.QFT -- we make use of the big endian QFT++-- | Add one 'QDInt' onto a second, in place; i.e. (/x/,/y/) ↦ (/x/,/x/+/y/). +-- Arguments are assumed to be of equal size.+-- This implementation follows the implementation in Thomas G. Draper's +-- paper \"Addition on a Quantum Computer\" which doesn't require the use of any+-- ancilla qubits through a clever use of the Quantum Fourier Transform.+qft_add_in_place :: QDInt -> QDInt -> Circ (QDInt,QDInt)+qft_add_in_place x y = do+ let x' = list_of_xint_bh x+ let y' = list_of_xint_bh y+ (x', y') <- qft_add_in_place_qulist x' y'+ let x = xint_of_list_bh x'+ let y = xint_of_list_bh y'+ return (x, y)++-- | Low-level implementation of 'qft_add_in_place': represents integers+-- as big-headian qubit lists.+qft_add_in_place_qulist :: [Qubit] -> [Qubit] -> Circ ([Qubit], [Qubit])+qft_add_in_place_qulist a b = do+ label (a,b) ("a","b")+ with_computed (box "QFT" qft_big_endian b) $ \b' -> do+ qft_adder a (reverse b')+ label (a,b) ("a","b")+ return (a,b)++-- | The circuit that performs the addition after a QFT+qft_adder :: [Qubit] -> [Qubit] -> Circ ()+qft_adder _ [] = return ()+qft_adder as (b:bs) = do+ qft_adder' as b 1+ qft_adder (tail as) bs+ where+ qft_adder' :: [Qubit] -> Qubit -> Int -> Circ [Qubit]+ qft_adder' [] _ _ = return []+ qft_adder' (a:as) b n = do+ b <- rGate n b `controlled` a+ qft_adder' as b (n+1)
@@ -0,0 +1,69 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Qram.hs" #-}+-- | A custom qRAM algorithm for fetching and storing quantum data+-- from a quantum array, addressed by a quantum integer.++module Quipper.Libraries.Qram (+ indexed_access,+ indexed_fetch_at,+ indexed_store_at,+ indexed_swap_at,+ ) where++import Quipper+import Quipper.Libraries.Arith++-- | Inputs a list /a/ of quantum data and a quantum integer /i/, and+-- returns the /i/th element of /a/. This is done with controlled swap+-- operations, but without ancillas, i.e., the output is the only copy+-- of that quantum data. Note that the remaining elements of the array+-- may be swapped around, so they are not useable until+-- 'indexed_access' has been reversed.+-- +-- Suggested usage:+-- +-- > with_computed (indexed_access i a) $ \x -> do+-- > <<<operate on x>>>+-- +-- If the index is out of bound, return an unpredictable element of+-- /a/. If /a/ is of length 0, raise an error.+indexed_access :: (QData qa) => [qa] -> QDInt -> Circ qa+indexed_access as i = indexed_access_qulist as (qulist_of_qdint_bh i)++-- | Auxiliary function: like 'indexed_access', but uses a qubit list+-- instead of a quantum integer. The list is big-headian, i.e., the+-- head of the list holds the most significant bit.+indexed_access_qulist :: (QData qa) => [qa] -> [Qubit] -> Circ qa+indexed_access_qulist [] i = error "indexed_access: cannot address length-0 register"+indexed_access_qulist (a:as) [] = return a+indexed_access_qulist as (i:is) = do+ let n = 2 ^ length is+ r = max 0 $ min n (length as - n)+ -- r: number of wires that need swapping if head qdigit is set+ for (r-1) 0 (-1) $ \j -> do+ swap_at (as !! j) (as !! (j+n)) `controlled` i+ a <- indexed_access_qulist as is+ return a++-- | @'indexed_fetch_at' /a/ /i/ /q/@: +-- Perform /q/ ⊕= /a/[/i/].+indexed_fetch_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_fetch_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ controlled_not_at q x++-- | @'indexed_store_at' /a/ /i/ /q/@:+-- Perform /a/[/i/] ⊕= /q/.+indexed_store_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_store_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ controlled_not_at x q++-- | @'indexed_swap_at' /a/ /i/ /q/@:+-- Swap /a/[/i/] and /q/.+indexed_swap_at :: (QData qa) => [qa] -> QDInt -> qa -> Circ ()+indexed_swap_at as i q = do+ with_computed (indexed_access as i) $ \x -> do+ swap_at x q
@@ -0,0 +1,130 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QuantumIf.hs" #-}+-- | This module introduces a simple way of defining boolean+-- statements acting over qubits.+module Quipper.Libraries.QuantumIf where++import Quipper++-- ----------------------------------------------------------------------+-- * Quantum \"if then else\" statements++-- $ This module introduces a simple way of defining boolean+-- statements acting over qubits, which can then be used as the+-- control in a quantum \"if then else\" statement. The idea is that+-- an ancilla is initialized that is in the state represented by the+-- boolean statement, and is then used to control the two branches of+-- the \"if then else\", before being uncomputed. The boolean+-- statements can contain \"and\", \"or\", and \"not\".++-- | We can use @(Boolean Qubit)@ to build up \"boolean statements\"+-- over qubits and use the \"boolean statement\" in an 'if_then_elseQ'+-- construct.+--+-- > Example (for qubits a, b, c, d):+-- > (a and b) or !(c and !d) is written as:+-- > (a `And` b) `Or` Not (c `And` Not d)+data Boolean a = A a -- ^ 'A' /q/ means if /q/ == 'True'. + | Not (Boolean a) -- ^ 'Not' /b/ means the negation of the boolean statement /b/.+ | And (Boolean a) (Boolean a) -- ^ 'And' /a/ /b/ means the and of the boolean statements /a/ and /b/.+ | Or (Boolean a) (Boolean a) -- ^ 'Or' /a/ /b/ means the or of the boolean statements /a/ and /b/+ deriving Show++-- Set the precedence for infix operators 'And' and 'Or'.+infixr 3 `And` -- same precedence as (&&)+infixr 2 `Or` -- same precedence as (||)++-- | Allow 'And' and 'Or' to be used as infix operators, with the same+-- precedences.++-- | Internally, a \"boolean statement\" is converted into a statement+-- that doesn't use /or/ (e.g., using De Morgan's laws).+data BooleanAnd a = AA a -- ^ 'AA' /q/ means if /q/ == 'True'.+ | NotA (BooleanAnd a) -- ^ 'NotA' /b/ means the negation of the boolean statement /b/.+ | AndA (BooleanAnd a) (BooleanAnd a) -- ^ 'AndA' /a/ /b/ means the and of the boolean statements /a/ and /b/.++-- | Convert any boolean formula to a formula using just /and/ and /not/. This conversion function uses De Morgan's law,+-- i.e., +-- +-- > A or B = !( !A and !B ),+-- +-- but does not remove double negations. For a version that also+-- removes double negations, see 'booleanToAnd'.+booleanToAnd' :: Boolean a -> BooleanAnd a+booleanToAnd' (A a) = AA a+booleanToAnd' (Not ba) = NotA (booleanToAnd' ba)+booleanToAnd' (And ba ba') = AndA (booleanToAnd' ba) (booleanToAnd' ba')+booleanToAnd' (Or ba ba') = NotA (AndA (NotA (booleanToAnd' ba)) (NotA (booleanToAnd' ba')))++-- | Strip any redundant double negations,+-- i.e., in this context @!!A = A@.+stripDoubleNot :: BooleanAnd a -> BooleanAnd a+stripDoubleNot (AA a) = AA a+stripDoubleNot (NotA (NotA ba)) = stripDoubleNot ba+stripDoubleNot (NotA ba) = NotA (stripDoubleNot ba)+stripDoubleNot (AndA ba ba') = AndA (stripDoubleNot ba) (stripDoubleNot ba')++-- | Convert any boolean formula to a formula using just /and/ and+-- /not/, removing double negations.+booleanToAnd :: Boolean a -> BooleanAnd a+booleanToAnd ba = stripDoubleNot (booleanToAnd' ba)++-- | Create a circuit from the \"boolean statement\".+booleanAnd' :: BooleanAnd Qubit -> Qubit -> Circ ()+booleanAnd' (AA q') q = do+ qnot_at q `controlled` q'+ return ()+booleanAnd' (NotA ba) q = do+ anc <- qinit False+ qnot_at anc+ booleanAnd' ba anc+ qnot_at q `controlled` anc+booleanAnd' (AndA ba ba') q = do+ anc0 <- qinit False+ booleanAnd' ba anc0+ anc1 <- qinit False+ booleanAnd' ba' anc1+ qnot_at q `controlled` [anc0,anc1]++-- | Create a circuit from the \"boolean statement\", passing in an ancilla.+booleanAnd :: BooleanAnd Qubit -> Circ Qubit+booleanAnd baq = do+ anc <- qinit False+ booleanAnd' baq anc+ return anc ++-- | The definition of a quantum if_then_else structure+-- uses a \"boolean statement\" to create a single ancilla in the state defined by+-- the boolean statement, and uses this as a control for the two branches of the+-- if statement. The ancilla then needs to be uncomputed, this is achieved using+-- the other given \"boolean statement\", i.e., a new boolean statement that would+-- produce the state of the control ancilla, from the output state of the two+-- branches.This allows the branches to update the state of qubits used in the +-- original \"boolean statement\" as long as it is done in a +-- (reversible) known-manner.+-- This is useful for the WALK algorithm, where TOPARENT and TOCHILD are controlled+-- by the state of the direction register, but also change the state of the+-- direction register. +if_then_elseQinv :: Boolean Qubit -> Circ () -> Circ () -> Boolean Qubit -> Circ ()+if_then_elseQinv b_in i e b_out = do+ with_ancilla $ \if_control -> do+ with_computed (booleanAnd (booleanToAnd b_in)) $ \anc -> do+ qnot_at if_control `controlled` anc+ with_controls if_control $ do+ i+ with_controls (if_control .==. False) $ do+ e+ with_computed (booleanAnd (booleanToAnd b_out)) $ \anc -> do+ qnot_at if_control `controlled` anc ++-- | Like 'if_then_elseQinv', but where the original \"boolean statement\" still+-- holds after the branches have taken place.+if_then_elseQ :: Boolean Qubit -> Circ () -> Circ () -> Circ ()+if_then_elseQ b i e = do+ with_computed (booleanAnd (booleanToAnd b)) $ \if_control -> do+ with_controls if_control $ do+ i+ with_controls (if_control .==. False) $ do+ e
@@ -0,0 +1,61 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QuipperASCIIParser.hs" #-}+-- ----------------------------------------------------------------------+-- | This module is the main interface to the QuipperASCIIParser+-- library. It provides functions for parsing circuits in the ASCII+-- format written by 'print_generic' and similar functions.++module Quipper.Libraries.QuipperASCIIParser where++import qualified Quipper.Libraries.QuipperASCIIParser.CircInfo as CI+import qualified Quipper.Libraries.QuipperASCIIParser.ASCIICirc as AC++import Quipper+import Quipper.Internal.Monad++import Quipper.Utils.PortableSignals+import System.IO+import Data.List++-- | Parse a string containing a circuit in the format output by+-- Quipper's ASCII format. Return a circuit producing function of the+-- parsed circuit, along with a specimen \"shape\" for the input of+-- the parsed circuit.+parse_circuit :: String -> ([Endpoint],[Endpoint] -> Circ [Endpoint])+parse_circuit all_lines = AC.run mins gates subs circ_info+ where+ split_lines = lines' all_lines+ (mins, ci) = CI.run_ascii_lines split_lines+ (gates,subs,circ_info) = CI.run ci+++-- | Like 'lines', except that the last line is omitted if it doesn't+-- end with a newline character+lines' :: String -> [String]+lines' [] = []+lines' s = case elemIndex '\n' s of+ Nothing -> []+ Just n -> (take (n) s):lines' (drop (n+1) s)++-- | Like 'parse_circuit', but read the circuit from the standard+-- input stream, rather than from a string. This can be used to build+-- stand-alone tools that process circuits in a pipeline. +parse_from_stdin :: IO ([Endpoint], [Endpoint] -> Circ [Endpoint])+parse_from_stdin = do+ all_lines <- hGetContents stdin + return $ parse_circuit all_lines++-- | Like 'parse_from_stdin', but as a special convenience, this +-- function also installs a signal handler that will intercept the first +-- kill signal (e.g., Ctrl-C) and close the standard input stream. +-- This means that whichever part of the circuit was generated before the +-- first Ctrl-C can still be processed as a partial circuit. Note that the +-- second kill signal will still kill the program. Note that this is only+-- defined for Non-Windows OS environments.+parse_from_stdin_with_handler :: IO ([Endpoint], [Endpoint] -> Circ [Endpoint])+parse_from_stdin_with_handler = do+ installHandler Interrupt (CatchOnce (hClose stdin))+ all_lines <- hGetContents stdin + return $ parse_circuit all_lines
@@ -0,0 +1,338 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QuipperASCIIParser/ASCIICirc.hs" #-}+-- ----------------------------------------------------------------------+-- | This module uses a state transformer monad to rebuild a circuit from+-- the CircInfoState representation. This can only be as lazy as the Quipper+-- ASCII output allows, as subroutine definitions need to be known before+-- a subroutine can be called.++module Quipper.Libraries.QuipperASCIIParser.ASCIICirc where++import Quipper.Libraries.QuipperASCIIParser.CircInfo hiding (run,do_gate)++import Quipper+import Quipper.Internal.Circuit+import Quipper.Internal.Monad+import Quipper.Internal.Generic++import Data.List+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import Data.Map (Map)+import qualified Data.Map as Map++import Control.Monad.State++-- | In the Quipper ASCII output, wires are identified by integers. +-- We have to map these to Quipper's native wires and types. +data WireState = WireState {+ wire_map :: IntMap (Wire,Wiretype),+ in_sub :: Bool, + subroutines_in_scope :: Map BoxId Sub+ }++-- | An initial, empty WireState, with the given subroutines_in_scope+empty_wirestate :: Map BoxId Sub -> WireState+empty_wirestate subs = WireState {wire_map = IntMap.empty, in_sub = False,+ subroutines_in_scope = subs}++-- | The 'ASCIICirc' monad is like the 'Circ' monad, except that it also+-- keeps track of an additional 'WireState'. The 'lift' function must+-- be used to lift any command for the 'Circ' monad to the 'ASCIICirc'+-- monad. +type ASCIICirc a = StateT WireState Circ a++-- | The 'in_sub' flag can be set to True, as slightly different behavior is+-- required when evaluating a subroutine.+set_in_sub :: ASCIICirc ()+set_in_sub = do+ state <- get+ put (state {in_sub = True})++-- | Look up the qubit corresponding to a ASCII integer representation of a+-- qubit. If it doesn't already exist then initialize a new qubit.+provide_qubit :: Int -> ASCIICirc Qubit+provide_qubit r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,Qbit) -> return (qubit_of_wire w)+ Just (w,Cbit) -> error ("Quantum wire " ++ show r ++ " bound as classical " ++ show w)+ Nothing -> error ("Quantum wire " ++ show r ++ " not in scope")+ True -> return (qubit_of_wire r)++-- | Look up the bit corresponding to a ASCII integer representation of a+-- bit. If it doesn't already exist then initialize a new bit.+provide_bit :: Int -> ASCIICirc Bit+provide_bit r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,Cbit) -> return (bit_of_wire w)+ Just (w,Qbit) -> error ("Classical wire " ++ show r ++ " bound as quantum " ++ show w)+ Nothing -> error ("Classical wire " ++ show r ++ " not in scope")+ True -> return (bit_of_wire r)++-- | Look up the wire corresponding to a ASCII integer representation of a+-- bit or qubit. If it doesn't already exist then initialize a new qubit.+provide_wire :: Int -> ASCIICirc Wire+provide_wire r = do+ state <- get+ case (in_sub state) of+ False -> do+ let ws = wire_map state+ case IntMap.lookup r ws of+ Just (w,_) -> return w+ Nothing -> do+ q <- provide_qubit r+ return (wire_of_qubit q)+ True -> return r ++-- | Add a new qubit to the state.+add_qubit :: Int -> Qubit -> ASCIICirc ()+add_qubit w q = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.insert w (wire_of_qubit q,Qbit) ws+ put (state {wire_map = ws'}) ++-- | Add a new bit to the state.+add_bit :: Int -> Bit -> ASCIICirc ()+add_bit w b = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.insert w (wire_of_bit b,Cbit) ws+ put (state {wire_map = ws'})++-- | Remove a wire from the state+remove_wire :: Int -> ASCIICirc ()+remove_wire w = do+ state <- get+ let ws = wire_map state+ let ws' = IntMap.delete w ws+ put (state {wire_map = ws'}) ++-- | A helper function for providing the qubits within a control structure+provide_control :: Signed Int -> ASCIICirc (Signed Wire)+provide_control (Signed r val) = do+ w <- provide_wire r+ return (Signed w val)++-- | provides quantum wires for the controls in a control list+provide_controls :: [Signed Int] -> ASCIICirc [Signed Wire]+provide_controls = mapM provide_control++-- | Lift a Quipper circuit, preventing the addition of controls depending+-- on the given boolean.+lift_ncf :: Bool -> Circ a -> ASCIICirc a+lift_ncf False ca = lift ca+lift_ncf True ca = lift $ without_controls ca++-- | Take a Gate and execute it in the 'ASCIICirc' monad.+do_gate :: Gate -> ASCIICirc ()+do_gate (QGate name iflg ws1 ws2 wbs ncf) = do+ qs1 <- mapM provide_qubit ws1+ qs2 <- mapM provide_qubit ws2+ cs <- provide_controls wbs+ lift_ncf ncf $ named_gate_qulist name iflg qs1 qs2 `controlled` cs+ return () +do_gate (QRot name iflg theta ws1 ws2 wbs ncf) = do+ qs1 <- mapM provide_qubit ws1+ qs2 <- mapM provide_qubit ws2+ cs <- provide_controls wbs + lift_ncf ncf $ named_rotation_qulist name iflg theta qs1 qs2 `controlled` cs+ return ()+do_gate (GPhase ts ws wbs ncf) = do+ qs <- mapM provide_qubit ws+ cs <- provide_controls wbs + lift_ncf ncf $ global_phase_anchored_list ts (map (\w -> endpoint_of_wire w Qbit) (map wire_of_qubit qs)) `controlled` cs+ return ()+do_gate (CNot w wbs ncf) = do+ b <- provide_bit w+ cs <- provide_controls wbs+ lift_ncf ncf $ cnot_at b `controlled` cs +do_gate (CGate name w ws ncf) = do+ bs <- mapM provide_bit ws + b <- lift_ncf ncf $ cgate name bs+ add_bit w b+do_gate (CGateInv name w ws ncf) = do+ b <- provide_bit w+ bs <- mapM provide_bit ws + lift_ncf ncf $ cgateinv name b bs +do_gate (CSwap w1 w2 wbs ncf) = do+ b1 <- provide_bit w1+ b2 <- provide_bit w2+ cs <- provide_controls wbs+ lift_ncf ncf $ swap b1 b2 `controlled` cs+ return ()+do_gate (QPrep w ncf) = do+ b <- provide_bit w+ q <- lift_ncf ncf $ prepare_qubit b+ add_qubit w q+do_gate (QUnprep w ncf) = do+ q <- provide_qubit w+ b <- lift_ncf ncf $ unprepare_qubit q+ add_bit w b+do_gate (QInit val w ncf) = do+ q <- lift_ncf ncf $ qinit val+ add_qubit w q+do_gate (CInit val w ncf) = do+ b <- lift_ncf ncf $ cinit val+ add_bit w b +do_gate (QTerm val w ncf) = do+ q <- provide_qubit w+ lift_ncf ncf $ qterm val q+ remove_wire w +do_gate (CTerm val w ncf) = do+ b <- provide_bit w+ lift_ncf ncf $ cterm val b+ remove_wire w +do_gate (QMeas w) = do+ q <- provide_qubit w+ b <- lift $ measure q+ add_bit w b+do_gate (QDiscard w) = do+ q <- provide_qubit w+ lift $ qdiscard q+ remove_wire w +do_gate (CDiscard w) = do+ b <- provide_bit w+ lift $ cdiscard b+ remove_wire w +do_gate (Subroutine boxid iflg ws_in _ ws_out _ wbs ncf _ (RepeatFlag rf)) = do+ -- Note that we ignore the given arities, as they are dummy values+ -- the true values are kept in the ASCIICirc state.+ -- The controllable flag is also ignored, as it is recalculated.+ state <- get+ let subs = subroutines_in_scope state+ case (Map.lookup boxid subs) of+ Nothing -> error ("Subroutine " ++ show boxid ++ " has not been defined in: " ++ show (Map.keys subs))+ Just sub -> do+ let ins = inputs sub+ let (_,in_as) = unzip ins+ let in_wa = zip ws_in in_as+ es_in <- mapM (\(w,wt) ->+ case wt of+ Qbit -> do+ q <- provide_qubit w+ let wire = endpoint_of_wire (wire_of_qubit q) Qbit+ return wire+ Cbit -> do+ b <- provide_bit w+ let wire = endpoint_of_wire (wire_of_bit b) Cbit+ return wire+ ) in_wa+ cs <- provide_controls wbs+ let outs = outputs sub+ let es_out = map (\(w,wt) -> endpoint_of_wire w wt) outs + let gates = reverse (circuit sub)+ let ascii_circ = \_ -> set_in_sub >> (mapM_ do_gate gates) >> return es_out + let circ = run_asciicirc subs ascii_circ+ let boxid = BoxId (name sub) (shape sub)+ let error_message = \e -> "ASCIICirc.do_gate : " ++ e+ let f = \x -> do+ -- Provide subroutine generic only uses circ if it isn't already+ -- in the namespace.+ provide_subroutine_generic error_message boxid False circ x+ call_subroutine boxid (RepeatFlag rf) x+ es_out' <- case iflg of+ False -> lift_ncf ncf $ f es_in `controlled` cs+ True -> do+ let (_,out_as) = unzip outs+ let out_wa = zip ws_in out_as+ es_out <- mapM (\(w,wt) ->+ case wt of+ Qbit -> do+ q <- provide_qubit w+ let wire = endpoint_of_wire (wire_of_qubit q) Qbit+ return wire+ Cbit -> do+ b <- provide_bit w+ let wire = endpoint_of_wire (wire_of_bit b) Cbit+ return wire+ ) out_wa + lift_ncf ncf $ (reverse_generic f es_in) es_out `controlled` cs+ -- a subroutine can add/remove wires from scope, so we remove all the input+ -- wires from the state, and add all the output wires to the state.+ -- The state may have changed, so we get it again.+ state <- get+ let wires = wire_map state+ let wires' = foldr (\w ws -> IntMap.delete w ws) wires (if iflg then ws_out else ws_in)+ let wires'' = foldr (\(w,e) ws -> IntMap.insert w + (case e of+ Endpoint_Qubit q -> (wire_of_qubit q, Qbit)+ Endpoint_Bit b -> (wire_of_bit b, Cbit)) ws) wires' + (zip (if iflg then ws_in else ws_out) es_out')+ put (state {wire_map = if (in_sub state) then wires else wires''})+do_gate (Comment c iflg wlabels) = do+ qlabels <- mapM (\(w,l) ->+ do+ pw <- provide_wire w+ return (pw,l) ) wlabels+ lift $ comment_label c iflg qlabels+do_gate (DTerm val w) = do+ b <- provide_bit w+ lift $ dterm_bit val b+ remove_wire w ++-- | Allocate an input endpoint, to an endpoint in the ASCIICirc, by adding it to+-- the map of wires in scope.+allocate_input :: (Endpoint,Endpoint) -> ASCIICirc ()+allocate_input (i,e@(Endpoint_Qubit _)) = add_qubit (wire_of_endpoint e) (qubit_of_wire (wire_of_endpoint i)) +allocate_input (i,e@(Endpoint_Bit _)) = add_bit (wire_of_endpoint e) (bit_of_wire (wire_of_endpoint i)) ++-- ----------------------------------------------------------------------+-- * Unpacking ASCIICirc++-- | Execute a parsed circuit, i.e. a CircInfoState, in the ASCIICirc monad+run_gates :: [Gate] -> Maybe [(Wire,Wiretype)] -> [Endpoint] -> [Endpoint] -> ASCIICirc [Endpoint]+run_gates gates d_outs es ins = do+ mapM_ (\i -> allocate_input i) (zip ins es)+ mapM_ (\g -> do_gate g) gates+ state <- get+ let ws_in_scope = IntMap.elems $ wire_map state+ let es_in_scope = map (\(w,wt) -> endpoint_of_wire w wt) ws_in_scope+ case d_outs of+ Nothing -> return es_in_scope+ Just out_ws -> do+ es_out <- mapM (\(w,wt) -> do+ w' <- provide_wire w+ return (endpoint_of_wire w' wt)) out_ws+ let es_in_scope' = sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) es_in_scope+ let es_out' = sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) es_out+ case es_in_scope' == es_out' of+ True -> return es_out'+ False -> return es_in_scope'++-- | Run function for the 'ASCIICirc' monad: execute the actions and+-- produce a circuit.+run_asciicirc :: Map BoxId Sub -> (a -> ASCIICirc b) -> a -> Circ b+run_asciicirc subs f es = evalStateT (f es) (empty_wirestate subs)++-- | A CircInfoState can be turned into a Circ producing function, and the required+-- input "shape".+run :: Maybe [(Wire,Wiretype)] -> [Gate] -> Map BoxId Sub -> CircInfoState -> ([Endpoint],[Endpoint] -> Circ [Endpoint])+run mins gates subs cis = (ins,circ)+ where+ d_ins = defined_inputs cis+ d_outs = defined_outputs cis+ u_ins = map set_qubit (undefined_inputs cis)+ d_es = map (\(w,wt) -> endpoint_of_wire w wt) d_ins+ u_es = map (\(w,wt) -> endpoint_of_wire w wt) u_ins+ ins = case mins of + Nothing -> (d_es ++ sortBy (\e1 e2 -> compare (wire_of_endpoint e1) (wire_of_endpoint e2)) u_es)+ Just wts -> map (\(w,wt) -> endpoint_of_wire w wt) wts+ asciicirc = run_gates gates d_outs ins+ circ = run_asciicirc subs asciicirc++-- | If the type of an undefined_input wire is unknown, then set it to+-- be a qubit.+set_qubit :: (Wire,Maybe Wiretype) -> (Wire,Wiretype)+set_qubit (w,Just wt) = (w,wt)+set_qubit (w,Nothing) = (w,Qbit)
@@ -0,0 +1,240 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QuipperASCIIParser/CircInfo.hs" #-}+-- ----------------------------------------------------------------------+-- | This module uses a state monad to track certain circuit information+-- that is collected during parsing. This information contains+-- the inputs and outputs of a circuit, as well as an entry for each subroutine+-- that is defined within a circuit, and all the gates that make up the circuit.++module Quipper.Libraries.QuipperASCIIParser.CircInfo where++import Quipper.Libraries.QuipperASCIIParser.Parse (GatePlus (..),parse_ascii_line)++import Quipper+import Quipper.Internal.Circuit+import Quipper.Internal.Monad++import Control.Monad.State++import Data.Map (Map)+import qualified Data.Map as Map++-- ----------------------------------------------------------------------+-- * Data-types for the State++-- | Information collected about the current subroutine+data Sub = Sub {+ name :: String,+ shape :: String,+ controllable :: ControllableFlag,+ inputs :: [(Wire,Wiretype)],+ outputs :: [(Wire,Wiretype)],+ circuit :: [Gate]+ }++-- | An initial subroutine, with only a name+new_subroutine :: String -> Sub+new_subroutine n = Sub {name = n, shape = "", controllable = NoCtl, inputs = [], outputs = [], circuit = []}++-- | A 'CircInfoState' is a record containing a list of input wires, along with their+-- types, and a list of output wires, along with their types. We also keep track of +-- whether we're in a subroutine definition, and all the subroutines that have been +-- defined.+data CircInfoState = CircInfoState {+ used_wires :: Map Wire (Maybe Wiretype),+ defined_inputs :: [(Wire,Wiretype)],+ undefined_inputs :: [(Wire,Maybe Wiretype)],+ defined_outputs :: Maybe [(Wire,Wiretype)],+ current_subroutine :: [Sub]+ }++-- | An initial, empty CircInfoState+empty_circinfostate :: CircInfoState+empty_circinfostate = CircInfoState { used_wires = Map.empty, defined_inputs = [], + undefined_inputs = [], defined_outputs = Nothing, current_subroutine = [] }++-- | The 'CircInfo' Monad is used to track a 'CircInfoState' during parsing.+type CircInfo a = State CircInfoState a++-- | The CircInfo Monad tracks wires that are inputs, these can only be given in+-- a "Input" line in the parsed ASCII, so we assume that duplicate wires don't+-- occur, and we add input wires to the state without checking.+add_wire_inputs :: [(Wire,Wiretype)] -> CircInfo ()+add_wire_inputs ws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ let ins = defined_inputs state+ let wires = used_wires state+ put (state {defined_inputs = ws ++ ins, + used_wires = Map.fromList (map (\(w,wt) -> (w,Just wt)) ws)})+ (sub:rest) -> do + let ins = inputs sub+ let sub' = sub {inputs = ins ++ ws}+ put (state {current_subroutine = (sub':rest)})++-- | The CircInfo Monad tracks wires that are outputs, these can only be given in+-- a "Output" line in the parsed ASCII, so we assume that duplicate wires don't+-- occur, and we add output wires to the state without checking.+add_wire_outputs :: [(Wire,Wiretype)] -> CircInfo ()+add_wire_outputs ws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ case defined_outputs state of+ Nothing -> put (state {defined_outputs = Just ws})+ Just outs -> put (state {defined_outputs = Just (outs ++ ws)})+ (sub:rest) -> do + let outs = outputs sub+ let sub' = sub {outputs = outs ++ ws}+ put (state {current_subroutine = (sub':rest)})++-- | Given a a wire, check whether it is already in scope.+check_input :: Map Wire (Maybe Wiretype) -> (Wire,Maybe Wiretype) -> Bool+check_input wires (w,wt) = case Map.lookup w wires of+ Just wt -> False+ Nothing -> True++-- | Given a list of wires that are inputs to a gate, check whether they+-- are already in scope. The returned wires are not in scope, when used by a gate,+-- and must be declared as undefined inputs.+check_inputs :: [(Wire,Maybe Wiretype)] -> Map Wire (Maybe Wiretype) -> [(Wire,Maybe Wiretype)]+check_inputs ins wires = filter (check_input wires) ins++-- | The CircInfo Monad keeps track of all the gates that have been parsed+-- and adds them to the relevant part of the state.+add_gate :: Gate -> [(Wire,Wiretype)] -> CircInfo CircInfoOut+add_gate gate ctws = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> do+ let wires = used_wires state+ let ui = undefined_inputs state+ let (ws_in,ws_out) = gate_arity gate+ let ws = wirelist_of_gate gate+ let ws_unchecked = filter (\w -> (notElem w (map fst ws_in)) + && (notElem w (map fst ws_out))+ && (notElem w (map fst ctws))) ws + let ws_in' = (map (\(w,wt) -> (w, Just wt)) (ws_in ++ ctws))+ ++ (zip ws_unchecked (repeat Nothing))+ let ui' = ui ++ check_inputs ws_in' wires+ let wires' = Map.union wires (Map.fromList (ws_in' + ++ (map (\(w,wt) -> (w, Just wt)) ws_out)))+ put (state {used_wires = wires',undefined_inputs = ui'})+ return (Lazy gate)+ (sub:rest) -> do+ let gates = circuit sub+ let gates' = gate:gates+ let sub' = sub {circuit = gates'}+ put (state {current_subroutine = (sub':rest)})+ return Empty++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. The entrance to the subroutine contains its name.+enter_subroutine :: String -> CircInfo ()+enter_subroutine name = do+ state <- get+ let ms = current_subroutine state+ put (state {current_subroutine = ((new_subroutine name):ms)})++-- | We can add the shape to the current subroutine+add_subroutine_shape :: String -> CircInfo ()+add_subroutine_shape s = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> error "Shape given outside of Subroutine Definition"+ (sub:rest) -> put (state {current_subroutine = ((sub {shape = s}):rest)})++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. The subroutine might be controllable.+set_controllable :: ControllableFlag -> CircInfo ()+set_controllable val = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> error "Controllable not in Subroutine Definition" + (sub:rest) -> put (state {current_subroutine = ((sub {controllable = val}):rest)})++-- | A datatype to represent the various outputs a CircInfo computation+-- may require.+data CircInfoOut = Empty | Lazy Gate | SubDef (BoxId,Sub)++-- | This function returns True if the given input defines a Gate+isGate :: CircInfoOut -> Bool+isGate (Lazy _) = True+isGate _ = False++-- | This function returns True if the given inputs defines a SubDef+isSub :: CircInfoOut -> Bool+isSub (SubDef _) = True+isSub _ = False++-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info+-- about that subroutine. At the end of the subroutine, it stores the subroutine+-- for later use.+exit_subroutine :: CircInfo CircInfoOut+exit_subroutine = do+ state <- get+ let ms = current_subroutine state+ case ms of+ [] -> return Empty+ (sub:rest) -> do+ let n = name sub+ let s = shape sub+ put (state {current_subroutine = rest})+ return (SubDef ((BoxId n s),sub))++-- | Take a GatePlus and execute it in the 'CircInfo' monad.+-- Again, the executed computation may depend upon whether we're in a subroutine+-- definition.+do_gate :: GatePlus -> CircInfo CircInfoOut+do_gate (G gate wts) = add_gate gate wts+do_gate (I ws) = add_wire_inputs ws >> return Empty+do_gate (O ws) = do+ add_wire_outputs ws+ exit_subroutine+do_gate EmptyLine = return Empty+do_gate (CommentLine comment) = return Empty+do_gate (SubroutineName name) = enter_subroutine name >> return Empty+do_gate (SubroutineShape shape) = add_subroutine_shape shape >> return Empty+do_gate (Controllable b) = set_controllable b >> return Empty++-- | Monad version of 'parse_ascii_line': parse a string and execute the+-- resulting gate directly in the 'CircInfo' monad.+run_ascii_line :: String -> CircInfo CircInfoOut+run_ascii_line s = case parse_ascii_line s of+ Nothing -> error ("unrecognized line: " ++ show s)+ Just p -> do_gate p++-- | Parse a stream consisting of many lines of ASCII output and execute+-- the parsed gates in the 'CircInfo' monad, checking to see if the first+-- line defines the inputs to the circuit.+run_ascii_lines :: [String] -> (Maybe [(Wire,Wiretype)],CircInfo [CircInfoOut])+run_ascii_lines [] = (Nothing,return [])+run_ascii_lines [f] = case parse_ascii_line f of+ Just (I ws) -> (Just ws, return [])+ _ -> (Nothing, run_ascii_line f >>= \x -> return [x])+-- Special case:+-- If the first line contains the input wires, we should +-- be able to parse the circuit lazily (e.g. it is a full circuit), as+-- we don't have to parse all the gates to calculate the inputs.+run_ascii_lines (f:s) = + case parse_ascii_line f of+ Just (I ws) -> (Just ws, mapM run_ascii_line s)+ _ -> (Nothing, mapM run_ascii_line (f:s))+ +-- | Run function for the 'CircInfo' monad: evaluate the state and+-- produce a list of inputs, outputs, and used wires.+run :: CircInfo [CircInfoOut] -> ([Gate],Map BoxId Sub,CircInfoState)+run f = (gs, subs, cis)+ where+ gs = [x | Lazy x <- filter isGate ci_outs]+ subs = Map.fromList [x | SubDef x <- filter isSub ci_outs] + (ci_outs,cis) = runState f empty_circinfostate+
@@ -0,0 +1,678 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/QuipperASCIIParser/Parse.hs" #-}+-- ----------------------------------------------------------------------+-- | This module contains the code for parsing the ASCII output from+-- Quipper, into a GatePlus.+-- This program is heavily based on, and heavily borrows from the QCLParser.++module Quipper.Libraries.QuipperASCIIParser.Parse where++import Quipper.Internal.Circuit+import Data.IntMap hiding (map)++import Text.ParserCombinators.ReadP+import Data.Char (isDigit)+import qualified Data.Map as Map++-- ----------------------------------------------------------------------+-- | A type for gates, plus other circuit output, empty lines, and subroutine defs.+data GatePlus = G Gate [(Wire,Wiretype)]+ | I [(Wire,Wiretype)]+ | O [(Wire,Wiretype)]+ | EmptyLine+ | CommentLine String+ | SubroutineName String+ | SubroutineShape String+ | Controllable ControllableFlag+ deriving Show++-- ----------------------------------------------------------------------+-- * Parsing++-- | Parse a string literal. +string_literal :: ReadP String+string_literal = do+ char '"'+ s <- string_with_escaped_chars ""+ char '"'+ return s+ where+ string_with_escaped_chars :: String -> ReadP String+ string_with_escaped_chars input = do+ s <- munch (\x -> x /= '\\' && x /= '"')+ let s' = input ++ s+ rest <- look+ case rest of+ '"' : _ -> return s'+ '\\' : _ -> do+ e <- escaped_char+ string_with_escaped_chars (s' ++ [e])+ _ -> do+ pfail++-- | Parse an escaped character, such as @\0@, @\n@, @\\@, etc.+escaped_char :: ReadP Char+escaped_char = do+ char '\\'+ c <- get+ return (Map.findWithDefault c c map)+ where+ -- The official escape codes. We allow any other escaped character+ -- to denote itself. We do not permit "\&" to denote the empty string.+ map = Map.fromList [+ ('0', '\0'),+ ('a', '\a'),+ ('b', '\b'),+ ('f', '\f'),+ ('n', '\n'),+ ('r', '\r'),+ ('t', '\t'),+ ('v', '\v'),+ ('"', '\"'),+ ('\'', '\''),+ ('\\', '\\')+ ]++-- | Parse a signless integer. We avoid the usual trick ('readS_to_P'+-- 'reads'), because this introduces backtracking errors.+int :: ReadP Int+int = do+ s <- munch1 isDigit+ return $ (read s :: Int)++-- | Parse a floating point number. We avoid the usual trick+-- ('readS_to_P' 'reads'), because this introduces backtracking+-- errors.+double :: ReadP Double+double = do+ (s, _) <- gather parse_double+ return $ (read s :: Double)+ where+ parse_double = do+ option '+' (char '+' +++ char '-')+ munch isDigit+ optional (char '.' >> munch1 isDigit)+ optional (char 'e' >> option '+' (char '+' +++ char '-') >> int)+ +-- | Parse a comma separated list.+commalist :: ReadP a -> ReadP [a]+commalist elt = sepBy elt (skipSpaces >> char ',' >> skipSpaces)++-- | Parse a control structure.+control :: ReadP (Signed Wire,Wiretype)+control = do+ val <- choice [char '+',char '-']+ skipSpaces+ w <- int+ c <- option 'q' (char 'c')+ let wt = case c of+ 'c' -> Cbit+ _ -> Qbit+ return (Signed w (val == '+'),wt)++-- | Parse a list of controls.+controls :: ReadP ([Signed Wire],[(Wire,Wiretype)])+controls = do+ skipSpaces+ string "with controls=["+ cwts <- commalist control+ char ']'+ let cs = map fst cwts+ let wts = map (\(Signed w _,wt) -> (w,wt)) cwts+ return (cs,wts)++-- | Parse a 'Wiretype'.+wiretype :: ReadP Wiretype+wiretype = do+ c <- choice [string "Qbit",string "Cbit"]+ case c of+ "Qbit" -> return Qbit+ "Cbit" -> return Cbit+ _ -> error "The impossible has happened"++-- | Parse a wire and its type.+wire :: ReadP (Int,Wiretype)+wire = do+ skipSpaces+ w <- int+ char ':'+ t <- wiretype+ return (w,t)++-- | Parse a list of input/output wires and types.+wires :: ReadP [(Int,Wiretype)]+wires = do+ skipSpaces+ ws <- commalist wire+ return ws++-- | Parse the string \"none\", +-- returning an empty list of input/output wires and types.+none :: ReadP [(Int,Wiretype)]+none = do+ skipSpaces+ string "none"+ return []++-- | Consume an optional \"*\". Return 'True' if consumed, and 'False'+-- otherwise.+inversechar :: ReadP Bool +inversechar = do+ c <- option '+' (char '*')+ return (c == '*')++-- | Consume a label.+label' :: ReadP (Int,String)+label' = do+ w <- int+ char ':'+ lab <- string_literal+ return (w,lab)++-- | Consumer any character other than \')\', \']\', or \',\'.+labelchar :: ReadP String+labelchar = do+ c <- satisfy (\x -> not (x `elem` ")],"))+ return [c]++-- | Consume an index of the form [...].+labelbracket :: ReadP String+labelbracket = do+ char '['+ s <- munch (\x -> not (x `elem` "[]"))+ char ']'+ return ("[" ++ s ++ "]")++-- | Consume a list of labels.+labels :: ReadP [(Int,String)]+labels = do+ skipSpaces+ char '('+ ls <- commalist label'+ char ')'+ return ls++-- | Consume a 'BoxId' followed by a \']\' character.+{-+ascii_of_boxid (BoxId name shape) = show name ++ ", shape " ++ show shape+-}+box_id :: ReadP BoxId+box_id = do+ name <- string_literal+ skipSpaces+ char ','+ skipSpaces+ string "shape"+ skipSpaces+ shape <- string_literal+ return (BoxId name shape)++-- | Consume an optional 'NoControlFlag', returning 'False' if it isn't present.+nocontrolflag :: ReadP NoControlFlag+nocontrolflag = do+ skipSpaces+ ncf <- option "" (string "with nocontrol")+ return (ncf == "with nocontrol")++-- | Parse a single line of ASCII output into a 'Gate'. This function+-- needs to be kept in line with Quipper's+-- 'Quipper.Internal.Printing.ascii_render_gate' function.+ascii_line :: ReadP GatePlus+ascii_line = choice [+ do -- Inputs: w:Type, w:Type ...+ string "Inputs:"+ ws <- choice [wires,none]+ eof+ return (I ws),+ do -- Outputs: w:Type, w:Type ...+ string "Outputs:"+ ws <- choice [wires,none]+ eof+ return (O ws),+ do -- An empty line can be parsed as an EmptyLine+ skipSpaces+ eof+ return EmptyLine,+ do -- "A comment in a circuit is any line beginnning with a # character"+ -- The comment is stored for later use as a CommentLine+ skipSpaces+ char '#'+ skipSpaces+ comment <- manyTill (satisfy (\_ -> True)) eof+ eof+ return (CommentLine comment),+ do -- Subroutine: "name" (keeping unquoted version for backward comp.)+ string "Subroutine: "+ name <- string_literal +++ manyTill (satisfy (\_ -> True)) eof+ eof+ return (SubroutineName name),+ do -- Shape: "shape" (keeping unquoted version for backward comp.)+ string "Shape: "+ shape <- string_literal +++ manyTill (satisfy (\_ -> True)) eof+ eof+ return (SubroutineShape shape),+ do -- Controllable: yes/no/classically+ string "Controllable: "+ val_string <- choice [string "yes",string "no", string "classically"]+ let val = case val_string of+ "yes" -> AllCtl+ "no" -> NoCtl+ "classically" -> OnlyClassicalCtl+ _ -> error "The impossible happened"+ eof+ return (Controllable val),+{-+For backward compatibility:+ascii_render_gate (QNot w c ncf) = + "QNot(" ++ show w ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do + string "QNot("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "not" False [w] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QMultinot ws c ncf) = + "QMultinot(" ++ string_of_list "" ", " "" "" show ws ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QMultinot("+ ws <- commalist int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "multinot" False ws [] cs ncf) wts), +{-+For backward compatibility:+ascii_render_gate (QHad w c ncf) = + "QHad(" ++ show w ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QHad("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "H" False [w] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QSwap w1 w2 c ncf) = + "QSwap(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QSwap("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "swap" False [w1, w2] [] cs ncf) wts),+{-+For backward compatibility:+ascii_render_gate (QW w1 w2 c ncf) = + "QW(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QW("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate "W" False [w1, w2] [] cs ncf) wts),+{-+ascii_render_gate (GPhase t ws c ncf) = + "GPhase() with t=" ++ show t + ++ ascii_render_controls wtm c + ++ ascii_render_nocontrolflag ncf+ ++ string_of_list " with anchors=[" ", " "]" "" show ws+-}+ do+ string "GPhase() with t="+ t <- double+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ ws <- option [] (do+ skipSpaces+ string "with anchors=["+ ws <- commalist int+ char ']'+ return ws )+ eof+ return (G (GPhase t ws cs ncf) wts),+{-+ascii_render_gate (QGate name inv ws1 ws2 c ncf) = + "QGate[" ++ show name ++ "]" + ++ optional inv "*" + ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list "; [" "," "]" "" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QGate["+ name <- string_literal+ char ']'+ inv <- inversechar+ char '('+ ws1 <- commalist int+ char ')'+ ws2 <- option [] (do+ string "; ["+ ws <- commalist int+ char ']'+ return ws )+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QGate name inv ws1 ws2 cs ncf) wts), +{-+ascii_render_gate (QRot name inv theta ws1 ws2 c ncf) = + "QRot[" ++ show name ++ "," ++ (show theta) ++ "]" + ++ optional inv "*"+ ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list "; [" "," "]" "" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QRot["+ name <- string_literal+ char ','+ theta <- double+ char ']'+ inv <- inversechar+ char '('+ ws1 <- commalist int+ char ')'+ ws2 <- option [] ( do+ string "; ["+ ws <- commalist int+ char ']'+ return ws )+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (QRot name inv theta ws1 ws2 cs ncf) wts),+{-+ascii_render_gate (CNot w c ncf) = + "CNot(" ++ show w ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do + string "CNot("+ w <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (CNot w cs ncf) wts),+{-+ascii_render_gate (CGate n w c ncf) = + "CGate[" ++ show n ++ "]" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CGate["+ name <- string_literal+ string "]("+ ws <- commalist int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CGate name (head ws) (tail ws) ncf) []),+{-+ascii_render_gate (CGateInv n w c ncf) = + "CGate[" ++ show n ++ "]" ++ "*" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CGate["+ name <- string_literal+ string "]*("+ ws <- commalist int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CGateInv name (head ws) (tail ws) ncf) []),+{-+ascii_render_gate (CSwap w1 w2 c ncf) = + "CSwap(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CSwap("+ w1 <- int+ char ','+ w2 <- int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ return (G (CSwap w1 w2 cs ncf) wts),+{-+ascii_render_gate (QPrep w ncf) = + "QPrep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QPrep("+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QPrep w ncf) []),+{-+ascii_render_gate (QUnprep w ncf) = + "QUnprep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QUnprep("+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QUnprep w ncf) []),+{-+ascii_render_gate (QInit b w ncf) = + "QInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QInit"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QInit val w ncf) []),+{-+ascii_render_gate (CInit b w ncf) = + "CInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CInit"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CInit val w ncf) []),+{-+ascii_render_gate (QTerm b w ncf) = + "QTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "QTerm"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (QTerm val w ncf) []),+{-+ascii_render_gate (CTerm b w ncf) = + "CTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+-}+ do+ string "CTerm"+ val_char <- choice [char '0',char '1']+ let val = val_char == '1'+ char '('+ w <- int+ char ')'+ ncf <- nocontrolflag+ eof+ return (G (CTerm val w ncf) []),+{-+ascii_render_gate (QMeas w) = + "QMeas(" ++ show w ++ ")"+-}+ do+ string "QMeas("+ w <- int+ char ')'+ eof+ return (G (QMeas w) []),+{-+ascii_render_gate (QDiscard w) = + "QDiscard(" ++ show w ++ ")"+-}+ do+ string "QDiscard("+ w <- int+ char ')'+ eof+ return (G (QDiscard w) []),+{-+ascii_render_gate (CDiscard w) = + "CDiscard(" ++ show w ++ ")"+-}+ do+ string "CDiscard("+ w <- int+ char ')'+ eof+ return (G (CDiscard w) []),+{-+ascii_render_gate (DTerm b w) = + "DTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+-}+ do+ string "DTerm"+ vc <- char '0' +++ char '1'+ let b = case vc of+ '0' -> False+ '1' -> True+ _ -> error "The impossible has happend" + char '('+ w <- int+ char ')'+ eof+ return (G (DTerm b w) []),+{-+ascii_render_gate (Subroutine boxid inv ws1 a1 ws2 a2 c ncf scf) = + "Subroutine" ++ show_rep ++ "[" ++ show (string_of_boxid boxid) ++ "]"+ ++ optional inv "*"+ ++ " "+ ++ (string_of_list "(" "," ")" "()" show ws1)+ ++ (string_of_list " -> (" "," ")" "()" show ws2)+ ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+ where+ show_rep = if rep == RepeatFlag 1 then "" else "(x" ++ show rep ++ ")"+-}+-- The parser returns a subroutine with dummy arities, and controllable flag,+-- and repeat flag, as+-- this information is not in a subroutine line in the ASCII output. +-- The information is added when the GatePlus is evaluated, as the first phase of+-- parsing will have collected the information. + do+ string "Subroutine"+ reps <- option 1 $ do+ string "(x"+ r <- int+ char ')'+ return (toInteger r)+ char '['+ boxid <- box_id+ char ']'+ inv <- inversechar+ string " ("+ ws1 <- commalist int+ string ") -> ("+ ws2 <- commalist int+ char ')'+ (cs,wts) <- option ([],[]) controls+ ncf <- nocontrolflag+ eof+ let dummy = error "attempted evaluation of a dummy value"+ return (G (Subroutine boxid inv ws1 dummy ws2 dummy cs ncf dummy (RepeatFlag reps)) wts), +{-+ascii_render_gate (Comment s inv ws) = + "Comment[" ++ show s ++ "]" + ++ optional inv "*"+ ++ (string_of_list "(" ", " ")" "()" (\(w,s) -> show w ++ ":" ++ show s) ws)+-}+ do+ string "Comment"+ skipSpaces+ char '['+ comment <- string_literal+ char ']'+ inv <- inversechar+ ls <- labels+ eof+ return (G (Comment comment inv ls) [])+ ]++-- | The overall parsing function, reading a line of ASCII output, and+-- producing a 'GatePlus'.+parse_ascii_line :: String -> Maybe GatePlus+parse_ascii_line s =+ case readP_to_S ascii_line s of+ (h, _):_ -> Just h+ _ -> Nothing+ where+ readline = do+ skipSpaces+ l <- ascii_line+ skipSpaces+ eof+ return l + +
@@ -0,0 +1,268 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Qureg.hs" #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DeriveDataTypeable #-}++-- | This module provides a data type of quantum registers, as well as+-- associated types of classical and boolean registers.++module Quipper.Libraries.Qureg (+ -- * Quantum registers+ Qureg,+ qureg_of_qulist_te,+ qulist_of_qureg_te,+ qureg_length,+ qinit_register,+ qterm_register,+ qmeasure_register,+ with_ancilla_reg,+ with_ancilla_reg_init,+ qureg_shape,+ + -- * Bit registers+ Bitreg,+ bitreg_of_bitlist_te,+ bitlist_of_bitreg_te,+ bitreg_length,+ + -- * Boolean registers+ Boolreg,+ boolreg_of_boollist_te,+ boollist_of_boolreg_te,+ boolreg_length,+ boolreg_of_int_le,+ int_of_boolreg_unsigned_le,+ + -- * General registers+ Register,+ (.!)+ + ) where++import Quipper+import Quipper.Internal++import Quipper.Utils.Auxiliary++import qualified Data.IntMap as IntMap+import Data.IntMap (IntMap)+import Data.Typeable++-- ----------------------------------------------------------------------+-- * Quantum registers++-- ** General registers++-- | A register is an array of elements of some type /x/, indexed by+-- natural numbers in the range from 0 to /n/-1, where /n/ is the+-- length of the register.+newtype Register x = Register { unRegister :: IntMap x }+ deriving (Typeable, Show)++-- | @/r/ !.(/i/)@: Return the /i/th element of a register /r/.+(.!) :: Register x -> Int -> x+a .!(i) = (unRegister a) IntMap.! i++infixl 9 .! -- same precedence as !!++-- | Convert a list to a register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+register_of_list_te :: [x] -> Register x+register_of_list_te l = Register $ IntMap.fromList (zip [n-1, n-2 .. 0] l)+ where n = length l+ +-- | Convert a register to a list. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+list_of_register_te :: Register x -> [x]+list_of_register_te r = map (r .!) [n-1, n-2 .. 0]+ where n = register_length r++-- | Return the length of a register.+register_length :: Register x -> Int+register_length = IntMap.size . unRegister++-- ----------------------------------------------------------------------+-- ** Qubit registers++-- | The type of quantum registers. A quantum register is an array of+-- qubits, indexed by natural numbers in the range from 0 to /n/-1,+-- where /n/ is the length of the register. The syntax /a/ .!(/i/) is+-- used to access the /i/th element of the register /a/. +-- +-- The main advantage of a register over a list is constant-time+-- access. The main disadvantage is that registers don't allow easy+-- appending, pattern matching, or recursion.+type Qureg = Register Qubit++-- | Convert a 'Qulist' to a 'Qureg'. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qureg_of_qulist_te :: Qulist -> Qureg+qureg_of_qulist_te = register_of_list_te++-- | Convert a 'Qureg' to a 'Qulist'. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qulist_of_qureg_te :: Qureg -> Qulist+qulist_of_qureg_te = list_of_register_te++-- | Return the length of a 'Qureg'. +qureg_length :: Qureg -> Int+qureg_length = register_length++-- | Return a piece of shape data to represent an /m/-qubit quantum+-- register. Please note that the data can only be used as shape; it+-- will be undefined at the leaves.+qureg_shape :: Int -> Qureg+qureg_shape m = qureg_of_qulist_te (replicate m qubit)++-- ----------------------------------------------------------------------+-- ** Bit registers++-- | The type of 'Bit' registers. The syntax /a/ .!(/i/) is used to+-- access the /i/th element of the register /a/.+type Bitreg = Register Bit++-- | Turn a bit vector into a bit register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+bitreg_of_bitlist_te :: Bitlist -> Bitreg+bitreg_of_bitlist_te = register_of_list_te++-- | Turn a bit register into a bit vector. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+bitlist_of_bitreg_te :: Bitreg -> Bitlist+bitlist_of_bitreg_te = list_of_register_te++-- | Return the length of a 'Bitreg'.+bitreg_length :: Bitreg -> Int+bitreg_length = register_length++-- ----------------------------------------------------------------------+-- ** Boolean registers++-- | The type of boolean registers.+type Boolreg = Register Bool++-- | Turn a bool vector into a bool register. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+boolreg_of_boollist_te :: Boollist -> Boolreg+boolreg_of_boollist_te = register_of_list_te++-- | Turn a bool register into a bool vector. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+boollist_of_boolreg_te :: Boolreg -> Boollist+boollist_of_boolreg_te = list_of_register_te++-- | Return the length of a 'Boolreg'.+boolreg_length :: Boolreg -> Int+boolreg_length = register_length++-- ----------------------------------------------------------------------+-- * Special functions for quantum registers++-- | Creates a new quantum register, initialized from a list of+-- booleans. The conversion is tail-endian, i.e., /r/.!(0) holds the+-- tail of the list.+qinit_register :: [Bool] -> Circ Qureg+qinit_register bs = do+ qs <- qinit bs+ return (qureg_of_qulist_te qs)++-- | Terminates a quantum register, and assert that its state is as+-- specified by the list of booleans. The conversion is tail-endian,+-- i.e., /r/.!(0) holds the tail of the list.+qterm_register :: [Bool] -> Qureg -> Circ ()+qterm_register bs r = do+ let qs = qulist_of_qureg_te r+ qterm bs qs+ +-- | Measure a quantum register, yielding a list of 'Bit's. +qmeasure_register :: Qureg -> Circ [Bit]+qmeasure_register r =+ measure (qulist_of_qureg_te r)+ +-- | Temporarily create a quantum register of size /n/ for use as an+-- ancilla. This can be used to introduce an ancilla with a local scope, like this: +-- +-- > with_ancilla_reg n $ \r -> do {+-- > <<<code block using ancilla register r>>>+-- > }+with_ancilla_reg :: Int -> (Qureg -> Circ a) -> Circ a+with_ancilla_reg n f = do+ let falselist = (take n $ repeat False)+ q <- qinit_register falselist+ a <- f q+ qterm_register falselist q+ return a++-- | Like 'with_ancilla_reg', except also initialize the register as+-- specified by a bit vector. In this case, the argument /n/ is not+-- required, because it equals the length of the bit vector. When the+-- ancilla is terminated at the end of its scope, it is asserted to be+-- in the same state it was prepared in.+with_ancilla_reg_init :: Boollist -> (Qureg -> Circ a) -> Circ a+with_ancilla_reg_init v f = do+ q <- qinit_register v+ a <- f q+ qterm_register v q+ return a++-- ----------------------------------------------------------------------+-- * Special functions for boolean registers++-- | @boolreg_of_int m x@: Initialize a bool register directly from an+-- integer /x/, regarded as a binary string of length /m/. The+-- conversion is little-endian, i.e., the register holds the least+-- significant digit at index 0.+boolreg_of_int_le :: Integral a => Int -> a -> Boolreg+boolreg_of_int_le m x = boolreg_of_boollist_te (boollist_of_int_bh m x)++-- | @int_of_boolreg_unsigned_le m r@: Turn a bool register into an+-- integer, regarded as a binary string. The conversion is+-- little-endian, i.e., the register holds the least significant digit+-- at index 0. The integer is unsigned.+int_of_boolreg_unsigned_le :: Integral a => Boolreg -> a+int_of_boolreg_unsigned_le r = int_of_boollist_unsigned_bh (boollist_of_boolreg_te r)++-- ----------------------------------------------------------------------++-- Make 'Qureg' an instance of 'QData' and 'QCData'.+type instance QCType x y Qureg = Register x+type instance QCType x y Bitreg = Register y+type instance QTypeB Boolreg = Qureg+instance QCData Qureg where+ + qcdata_mapM shape f g (Register xs) = do+ ys <- intmap_mapM f xs+ return (Register ys)+ + qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where+ zs = intmap_zip_errmsg xs ys (e "register length mismatch")++ qcdata_promote as xs e+ | register_length as == register_length xs = as+ | otherwise = error (e "register length mismatch")++instance QCData Bitreg where+ + qcdata_mapM shape f g (Register xs) = do+ ys <- intmap_mapM g xs+ return (Register ys)++ qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where+ zs = intmap_zip_errmsg xs ys (e "register length mismatch")++ qcdata_promote as xs e+ | register_length as == register_length xs = as+ | otherwise = error (e "register length mismatch")++instance Labelable Qureg String where+ label_rec (Register xs) s = do+ sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ]++instance Labelable Bitreg String where+ label_rec (Register xs) s = do+ sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ]
@@ -0,0 +1,40 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Simulation.hs" #-}+-- | This library provides functions for simulating certain classes of+-- circuits, for testing and debugging purposes. +-- +-- We can efficiently simulate classical (boolean) circuits and+-- Clifford (stabilizer) circuits. We also provide functions for+-- simulating arbitrary quantum circuits; however, the latter is+-- (necessarily) very inefficient.++module Quipper.Libraries.Simulation ( + -- * Classical simulation+ run_classical_generic,+ + -- * Stabilizer simulation+ run_clifford_generic,+ + -- * Quantum simulation+ run_generic,+ run_generic_io,+ sim_amps,+ + -- * Special purpose functions+ -- ** Simulation with trace+ QuantumTrace (..),+ run_generic_trace,+ run_generic_trace_io,+ + -- ** Probability distributions+ Vector (..),+ ProbabilityDistribution (..),+ sim_generic,+) where++import Quipper.Libraries.Simulation.ClassicalSimulation+import Quipper.Libraries.Simulation.QuantumSimulation+import Quipper.Libraries.Simulation.CliffordSimulation+
@@ -0,0 +1,215 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Simulation/ClassicalSimulation.hs" #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides functions for simulating certain classes of+-- circuits, for testing and debugging purposes. At the moment, the+-- only type of simulation we provide is boolean simulation. +-- +-- This module provides the internal implementation of the library,+-- and can be imported by other libraries. The public interface to+-- simulation is "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.ClassicalSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, Simulation.hs uses low-level interfaces. It should be+-- re-implemented using only high-level interfaces, or in some cases,+-- more stuff should be exported from Quipper.hs.+import Quipper.Internal.Transformer (bind_list, bindings_empty, unbind_list, Bindings, transform_bcircuit_id)+import Quipper.Internal.Circuit (RepeatFlag(..), TypedSubroutine(..), OCircuit(..), reverse_ocircuit, ControllableFlag(..), BCircuit)+import Quipper.Internal.Generic (qc_bind, qc_unbind, encapsulate_generic)++import Quipper.Utils.Auxiliary++import Data.List+import qualified Data.Map as Map++-- ======================================================================+-- * Simulation of classical circuits++-- | Auxiliary function: determine whether a \"control\" is true of false.+controls :: [Signed (B_Endpoint Bool Bool)] -> Bool+controls [] = True+controls ((Signed (Endpoint_Qubit c) b):t) = (c == b) && controls t+controls ((Signed (Endpoint_Bit c) b):t) = (c == b) && controls t++-- | Auxiliary function: compute the boolean function corresponding to+-- a 'Quipper.Internal.Circuit.CGate' of the given name.+simulate_cgate :: String -> [Bool] -> Bool+simulate_cgate "if" [a,b,c] = if a then b else c+simulate_cgate "if" list = error ("simulate_cgate: \"if\" needs 3 arguments, not " ++ show (length list))+simulate_cgate "and" list = and list+simulate_cgate "or" list = or list+simulate_cgate "xor" list = foldl' bool_xor False list+simulate_cgate "eq" [a,b] = (a == b)+simulate_cgate "eq" list = error ("simulate_cgate: \"eq\" needs 2 arguments, not " ++ show (length list))+simulate_cgate "not" [a] = not a+simulate_cgate "not" list = error ("simulate_cgate: \"not\" needs 1 argument, not " ++ show (length list))+simulate_cgate name list = error ("simulate_cgate: gate \"" ++ name ++ "\" not known")+++-- | A "Quipper.Internal.Transformer" mapping each gate to the corresponding boolean+-- function. This will fail with an error for gates that don't act+-- within the computational basis, or if some assertion is not+-- met. Some gates are not yet implemented. ++simulate_classical :: Transformer Id Bool Bool++-- Translation of classical gates:+simulate_classical (T_CNot ncf f) = f $+ \q c -> Id (if controls c then not q else q, c)+simulate_classical (T_CSwap ncf f) = f $+ \w v c -> Id (if controls c then (v,w,c) else (w,v,c))+simulate_classical (T_CInit b ncf f) = f $+ Id b+simulate_classical (T_CTerm b ncf f) = f $+ \q -> Id (if b==q then () else error "simulate_classical: CTerm assertion not met")+simulate_classical (T_CDiscard f) = f $+ \b -> Id ()+simulate_classical (T_DTerm b f) = f $+ \b -> Id ()+simulate_classical (T_CGate name ncf f) = f $+ \list -> Id (simulate_cgate name list, list)+simulate_classical (T_CGateInv name ncf f) = f $+ \q list -> if q == simulate_cgate name list + then Id list + else error ("simulate_classical: CGateInv failed assertion " ++ show q ++ " == " ++ name ++ show list)++-- Translation of quantum gates:+simulate_classical (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] c -> if controls c then Id([not q], [], c) else Id([q], [], c)+simulate_classical (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] c -> Id (if controls c then map not qs else qs, [], c) +simulate_classical (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w,v] [] c -> Id (if controls c then ([v,w], [], c) else ([w,v], [] ,c))+simulate_classical g@(T_QGate "H" 1 0 _ _ _) =+ error ("simulate_classical: gate not classical: " ++ show g)+simulate_classical g@(T_QGate "W" 2 0 _ _ _) =+ error ("simulate_classical: gate not classical: " ++ show g)+simulate_classical g@(T_QGate name _ _ inv ncf f) = f $+ \qs gctls c -> case (name, inv, qs, gctls) of+ ("X", _, [q], []) -> + if controls c then Id([not q], [], c) else Id([q], [], c)+ ("Y", _, [q], []) ->+ if controls c then Id([not q], [], c) else Id([q], [], c)+ ("Z", _, [q], []) -> Id([q], [], c)+ ("S", _, [q], []) -> Id([q], [], c)+ ("T", _, [q], []) -> Id([q], [], c)+ ("omega", _, [q], []) -> Id([q], [], c)+ ("iX", _, [q], []) ->+ if controls c then Id([not q], [], c) else Id([q], [], c)+ _ -> error ("simulate_classical: unimplemented gate: " ++ show g)+simulate_classical g@(T_QRot name _ _ inv t ncf f) = f $+ \qs gctls c -> case (name, inv, qs, gctls) of+ ("R(2pi/%)", _, [q], []) -> Id([q], [], c)+ ("exp(-i%Z)", _, [q], []) -> Id([q], [], c)+ _ -> error ("simulate_classical: unimplemented gate: " ++ show g)+simulate_classical g@(T_GPhase t ncf f) = f $+ \q c -> Id c+simulate_classical (T_QInit b ncf f) = f $+ Id b+simulate_classical (T_QTerm b ncf f) = f $+ \q -> if b==q then Id() else error "simulate_classical: QTerm assertion not met"+simulate_classical (T_QDiscard f) = f $+ \b -> Id()+simulate_classical (T_Comment s inv f) = f $+ \b -> Id()++-- Preparation, unpreparation, and measurement:+simulate_classical g@(T_QPrep ncf f) = f $+ \w -> Id w+simulate_classical g@(T_QUnprep ncf f) = f $+ \w -> Id w+simulate_classical g@(T_QMeas f) = f $+ \w -> Id w++-- Subroutines:+simulate_classical g@(T_Subroutine sub inv ncf scf ws_pat a1_pat vs_pat a2_pat (RepeatFlag repeat) f) = f $+ \namespace in_values c -> Id $ + case Map.lookup sub namespace of+ Just (TypedSubroutine sub_ocirc _ _ _) ->+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ in_bindings = bind_list in_wires in_values bindings_empty+ out_bindings = + if (case scf of {AllCtl -> True; OnlyClassicalCtl -> True; NoCtl -> False})+ then if controls c then foldl' (\in_b _ -> run_classical (sub_circ, namespace) in_b) in_bindings [1..repeat] else in_bindings+ else if length c == 0+ then foldl' (\in_b _ -> run_classical (sub_circ, namespace) in_b) in_bindings [1..repeat]+ else error $ "simulate_classical: attempt to control non-controllable subroutine " ++ show sub+ in (unbind_list out_bindings out_wires, c) + Nothing -> error $ "simulate_classical: subroutine " ++ show sub ++ " not found"++-- If adding gates: remember to list any unimplemented gates+-- explicitly, so that the type-checker can warn about new gates that+-- must be added to the list.++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. This function makes use of the concept of a+-- /valuation/. A valuation is a partial map from circuit wires to+-- booleans, represented by the type @'Bindings' Bool@. Given a+-- circuit and a valuation for its inputs, the function+-- 'run_classical' produces a valuation for its outputs.+-- +-- The simulation will fail with an error if the circuit contains a+-- gate not acting within the computational basis, or if some+-- assertion is not met. Not all gates are currently+-- implemented. Subroutines are not currently implemented.+-- +run_classical :: BCircuit -> Bindings Bool Bool -> Bindings Bool Bool+run_classical = transform_bcircuit_id simulate_classical++-- ======================================================================+-- * Generic simulation++-- | Like 'run_classical_unary', but also takes a stub error message.+run_classical_errmsg :: (QCData qa, QCData qb) => ErrMsg -> (qa -> Circ qb) -> BType qa -> BType qb+run_classical_errmsg e (f :: qa -> Circ qb) input = output where+ shape = qcdata_makeshape (dummy :: qa) (dummy :: Bool) (dummy :: Bool) input+ (qa, circuit, qb) = encapsulate_generic e f shape+ valuation_in = qc_bind qa input+ valuation_out = run_classical circuit valuation_in+ output = qc_unbind valuation_out qb++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. Input a classical circuit, and output the corresponding+-- boolean function. This will fail with an error if the circuit+-- contains a gate not acting within the computational basis, or if+-- some assertion is not met.+run_classical_unary :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> BType qb+run_classical_unary = run_classical_errmsg errmsg + where+ errmsg x = "run_classical_unary: operation not currently permitted by simulator: " ++ x++-- | Boolean simulation of a circuit, for testing and debugging+-- purposes. Input a classical circuit, and output the corresponding+-- boolean function. This will fail with an error if the circuit+-- contains a gate not acting within the computational basis, or if+-- some assertion is not met.+-- +-- Unlike 'run_classical_unary', this can be applied to a+-- circuit-generating function in curried form with /n/ arguments, for+-- any /n/ ≥ 0. The resulting boolean function then will also have /n/ arguments. +-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > run_classical_generic :: (QCData qa) => Circ qa -> BType qa+-- > run_classical_generic :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> BType qb+-- > run_classical_generic :: (QCData qa, QCData qb, QCData qc) => (qa -> qb -> Circ qc) -> BType qa -> BType qb -> BType qc+-- +-- and so forth.+ +run_classical_generic :: (QCData qa, QCData qb, QCurry qfun qa qb, Curry fun (BType qa) (BType qb)) => qfun -> fun+run_classical_generic f = g where+ f1 = quncurry f+ g1 = run_classical_errmsg errmsg f1+ g = mcurry g1+ errmsg x = "run_classical_generic: operation not currently permitted by simulator: " ++ x+
@@ -0,0 +1,259 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Simulation/CliffordSimulation.hs" #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides a Quipper interface to the efficient+-- simulation of Clifford group circuits, using the Stabilizer+-- formalism.+-- +-- This module provides the internal implementation of the library,+-- and can be imported by other libraries. The public interface to+-- simulation is "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.CliffordSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, CliffordSimulation.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Generic (encapsulate_generic, qc_unbind)+import Quipper.Internal.Transformer (transform_bcircuit_rec, bindings_empty)++import Quipper.Libraries.Simulation.QuantumSimulation (gateQinv, rotQinv)+import Quipper.Libraries.Simulation.ClassicalSimulation (simulate_cgate)++import Quipper.Utils.Auxiliary+import qualified Quipper.Utils.Stabilizers.Clifford as C++import Control.Monad.State (StateT)+import Data.Either++-- ----------------------------------------------------------------------+-- * The stabilizer transformer++-- | The stabilizer transformer. Used to transform a Quipper circuit,+-- made up of Clifford group operators, directly into the 'C.CliffordCirc'+-- monad (where 'C.CliffordCirc' is a type-synonym for StateT Tableau IO).+-- Note that this transformer only deals with 1 and 2-qubit operators.+stabilizer_transformer :: Transformer (StateT C.Tableau IO) C.Qubit Bool+stabilizer_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ -- X and controlled-X are already provided by C.CliffordCirc+ \[qt] [] c -> case control_info c of+ None -> do+ C.gate_X qt+ return ([qt], [], c)+ Classical b -> do+ if b then C.gate_X qt else return ()+ return ([qt], [], c)+ OneQuantum (Signed qc positive) b -> do+ if not positive then C.gate_X qc else return ()+ if b then C.controlled_X qc qt else return ()+ if not positive then C.gate_X qc else return ()+ return ([qt], [], c)+ _ -> error "stabilizer_transformer: Toffoli gate not available"+stabilizer_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ -- X and controlled-X are already provided by C.CliffordCirc+ -- and can be mapped over a list of qubits + \ws [] c -> case control_info c of+ None -> do+ mapM_ C.gate_X ws+ return (ws, [], c)+ Classical b -> do+ if b then mapM_ C.gate_X ws else return ()+ return (ws, [], c)+ OneQuantum (Signed qc positive) b -> do+ if not positive then C.gate_X qc else return ()+ if b then mapM_ (C.controlled_X qc) ws else return ()+ if not positive then C.gate_X qc else return ()+ return (ws, [], c)+ _ -> error "stabilizer_transformer: (Multi) Toffoli gate not available"+stabilizer_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ -- Hadamard is already provided by C.CliffordCirc + \[qt] [] c -> case control_info c of+ None -> do+ C.gate_H qt+ return ([qt], [], c)+ Classical b -> do+ if b then C.gate_H qt else return ()+ return ([qt], [], c)+ -- TODO: ???+ _ -> error "stabilizer_transformer: controlled-Hadamard currently not supported"+stabilizer_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ -- (classically-controlled) swap is already provided by C.CliffordCirc+ \[w, v] [] c -> case control_info c of+ None -> do+ C.swap w v+ return ([w, v], [], [])+ Classical b -> do+ if b then C.swap w v else return ()+ return ([w, v], [], [])+ _ -> error "stabilizer_transformer: quantum controlled-swap not available"+stabilizer_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ -- TODO: ???+ \[w, v] [] c -> error "stabilizer_transformer: W currently not supported"+stabilizer_transformer (T_QGate name _ _ inv ncf f) = f $+ \ws vs c ->+ case vs of+ [] ->+ case ws of+ [qt] -> case control_info c of+ None -> do+ C.gate_Unitary u1 qt+ return ([qt], vs, c)+ Classical b -> do+ if b then C.gate_Unitary u1 qt else return ()+ return ([qt], vs, c)+ OneQuantum (Signed qc positive) b -> do+ if b then C.gate_Unitary2 u2 qc qt else return ()+ return ([qt], vs, c)+ _ -> error "stabilizer_transformer: Multiple quantum controls not supported"+ where u1 = case name of+ "X" -> C.x+ "Y" -> C.y+ "Z" -> C.z+ "S" -> C.s+ "E" -> C.e+ name -> C.from_matrix (gateQinv name inv)+ u2 = case name of+ "X" -> C.cnot+ "Z" -> C.cz+ name -> C.from_matrix_controlled (gateQinv name inv)+ _ -> error "stabilizer_transformer: Named gates on multiple Qubits not available"+ _ -> error "stabilizer_transformer: generalised controls not currently supported"+stabilizer_transformer (T_QRot name _ _ inv t ncf f) = f $+ \ws vs c -> error "stabilizer_transformer: QRot not currently supported" --return (ws, vs, c)+stabilizer_transformer (T_GPhase t ncf f) = f $+ -- TODO: ???+ \qs c -> error "stabilizer_transformer: GPhase currently not supported"+stabilizer_transformer (T_CNot ncf f) = f $+ -- we can do a classical not depending on the controls+ \q c -> case control_info c of+ None -> return (not q,c)+ Classical b -> if b then return (not q,c) else return (q,c) + _ -> error "stabilizer_transformer: Quantum control on classical gate"+stabilizer_transformer (T_CGate name ncf f) = f $+ -- we can reuse the classical simulator cgate implementation+ \ws -> return (simulate_cgate name ws,ws)+stabilizer_transformer (T_CGateInv name ncf f) = f $+ -- we can check that the inverse is correct using the classical simulator cgate implementation+ \v ws -> if (simulate_cgate name ws == v) then return ws else error "stabilizer_transformer: CGateInv not inverse"+stabilizer_transformer (T_CSwap ncf f) = f $+ -- we can do a classical swap depending on the controls+ \w v c -> case control_info c of+ None -> return (w,v,c)+ Classical b -> if b then return (v,w,c) else return (w,v,c) + _ -> error "stabilizer_transformer: Quantum control on classical gate"+stabilizer_transformer (T_QPrep ncf f) = f $+ -- TODO: ??+ \w -> error "stabilizer_transformer: QPrep currently not supported"+stabilizer_transformer (T_QUnprep ncf f) = f $+ -- TODO: ?? + \w -> error "stabilizer_transformer: QUnprep currently not supported"+stabilizer_transformer (T_QInit b ncf f) = f $+ -- initialization is already provided by C.CliffordCirc+ C.init_qubit b+stabilizer_transformer (T_CInit b ncf f) = f $+ -- initialisation of a boolean value+ return b+stabilizer_transformer (T_QTerm b ncf f) = f $+ -- we can check termination conditions at runtime, using measurement+ \w -> do+ res <- C.measure_qubit w+ if res == b then return () else error "stabilizer_transformer: QTerm condition failed"+stabilizer_transformer (T_CTerm b ncf f) = f $+ -- we can check termination conditions at runtime+ \w -> if w == b then return () else error "stabilizer_transformer: CTerm condition failed"+stabilizer_transformer (T_QMeas f) = f $+ -- measurement is already provided by C.CliffordCirc + \w -> C.measure_qubit w+stabilizer_transformer (T_QDiscard f) = f $+ -- we can ignore discards+ \w -> return ()+stabilizer_transformer (T_CDiscard f) = f $+ -- we can ignore discards+ \w -> return ()+stabilizer_transformer (T_DTerm b f) = f $+ -- TODO: ??+ \w -> error "stabilizer_transformer: DTerm currently not supported" --return ()+stabilizer_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $+ -- TODO: we can "open" subroutines+ \namespace ws c -> error "stabilizer_transformer: Subroutine currently not supported" --return (ws,c)+stabilizer_transformer (T_Comment s inv f) = f $+ -- we can ignore comments+ \ws -> return ()++-- | A datatype to represent controls that we can simulate+data ControlInfo = None | Classical Bool | OneQuantum (Signed C.Qubit) Bool | ManyQuantum++-- | Construct an element of ControlInfo from a list of Controls.+control_info :: Ctrls C.Qubit Bool -> ControlInfo+control_info cs = case split_controls cs of+ ([],[]) -> None+ ([],cs) -> Classical (all_equal cs)+ ([q],cs) -> OneQuantum q (all_equal cs)+ _ -> ManyQuantum+ where+ split_controls :: Ctrls a b -> ([Signed a],[Signed b])+ split_controls cs = partitionEithers (map either_control cs)+ either_control :: Signed (B_Endpoint a b) -> Either (Signed a) (Signed b)+ either_control (Signed (Endpoint_Qubit a) value) = Left (Signed a value)+ either_control (Signed (Endpoint_Bit b) value) = Right (Signed b value)+ all_equal :: [Signed Bool] -> Bool+ all_equal cs = and (map (\(Signed b val) -> b == val) cs) ++-- ----------------------------------------------------------------------+-- * High-level functions++-- | Use the 'stabilizer_transformer' to transform a Quipper circuit+-- into a 'C.CliffordCirc', ready for simulation.+toCliffordCirc :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> C.CliffordCirc (BType qb))+toCliffordCirc (f :: qa -> Circ qb) input = do+ let ((), circuit, cb) = encapsulate_generic errmsg (\() -> qc_init input >>= \qi -> f qi >>= \qi' -> qc_measure qi') ()+ out_bind' <- transform_bcircuit_rec stabilizer_transformer circuit bindings_empty+ let out_bind = out_bind'+ let output = qc_unbind out_bind cb+ return output+ where+ errmsg x = ("simulate: " ++ x)++-- | Return the tableau resulting from simulating the given Quipper+-- circuit.+eval_unary :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> IO C.Tableau)+eval_unary circ input = C.eval (toCliffordCirc circ input)++-- | Efficiently simulate a unary Quipper circuit that consists+-- entirely of Clifford group operators, using the stabilizer+-- formalism.+run_clifford_unary :: (QCData qa, QCDataPlus qb) => (qa -> Circ qb) -> (BType qa -> IO (BType qb))+run_clifford_unary circ input = C.sim (toCliffordCirc circ input)++-- | Efficiently simulate a Quipper circuit that consists entirely of+-- Clifford group operators, using the stabilizer formalism. +-- +-- Inputs a quantum circuit, and outputs 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. Because the measurement outcomes are probabilistic,+-- this function takes place in the 'IO' monad.+-- +-- 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_clifford_generic :: (QCData qa) => Circ qa -> IO (BType qa)+-- > run_clifford_generic :: (QCData qa, QCData qb) => (qa -> Circ qb) -> BType qa -> IO (BType qb)+-- > run_clifford_generic :: (QCData qa, QCData qb, QCData qc) => (qa -> qb -> Circ qc) -> BType qa -> BType qb -> IO (BType qc)+-- +-- and so forth.+run_clifford_generic :: (QCData qa, QCDataPlus qb, QCurry qfun qa qb, Curry qfun' (BType qa) (IO (BType qb))) => qfun -> qfun'+run_clifford_generic f = g where+ f1 = quncurry f+ g1 = run_clifford_unary f1+ g = mcurry g1
@@ -0,0 +1,854 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Simulation/QuantumSimulation.hs" #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Rank2Types #-}++-- | 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 "Quipper.Libraries.Simulation".++module Quipper.Libraries.Simulation.QuantumSimulation where++import Quipper+import Quipper.Internal++-- The following is a bunch of stuff we need to import because,+-- temporarily, QuantumSimulation.hs uses low-level interfaces. It+-- should be re-implemented using only high-level interfaces, or in+-- some cases, more stuff should be exported from Quipper.hs.+import Quipper.Internal.Circuit+import Quipper.Internal.Transformer+import Quipper.Internal.Monad (qubit_of_wire)+import Quipper.Internal.Generic (encapsulate_dynamic, qc_unbind)++import Quipper.Utils.Auxiliary++-- we use the state monad to hold our \"quantum\" state+import Control.Monad.State+-- we use complex numbers as our probability amplitudes+import Quantum.Synthesis.Ring (Cplx (..), i)+-- we use a random number generator to simulate \"quantum randomness\"+import System.Random hiding (split)+-- we store \"basis\" states as a map, +import Data.Map (Map)+import qualified Data.Map as Map++import Data.List (partition)++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++import qualified Debug.Trace -- used in tracing the simulation of quipper computations++-- | We define our own trace function that only calls trace if the boolean+-- argument is true.+trace :: Bool -> String -> a -> a+trace False _ a = a+trace True message a = Debug.Trace.trace message a++-- ======================================================================+-- * 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.++-- | 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:+--+-- \[image GateR.png] +--+-- would be written as (m00,m01,m10,m11).+type GateR r = (Cplx r,Cplx r, Cplx r, Cplx r)++-- | Scalar multiplication of a 2-by-2 matrix by a given scalar.+scale :: (Floating r) => Cplx r -> GateR r -> GateR r+scale e (a,b,c,d) = (e*a,e*b,e*c,e*d) ++-- | The inverse of a 'GateR' is its conjugate transpose.+reverseR :: (Floating r) => GateR r -> GateR r+reverseR (m00,m01,m10,m11) = (conjugate m00, conjugate m10, conjugate m01, conjugate m11)+ where+ conjugate (Cplx a b) = Cplx a (-b) ++-- | 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)+gateQ :: (Floating r) => String -> GateR r+gateQ "x" = (0,1,1,0)+gateQ "hadamard" = (h, h, h,-h) where h = Cplx (1/sqrt 2) 0+gateQ "X" = (0,1,1,0)+gateQ "Y" = (0,-i,i,0)+gateQ "Z" = (1,0,0,-1)+gateQ "S" = (1,0,0,i)+gateQ "E" = ((-1+i)/2, (1+i)/2, (-1+i)/2, (-1-i)/2)+gateQ "YY" = (h,i*h,i*h,h) where h = Cplx (1/sqrt 2) 0+gateQ "T" = (1,0,0,omega) where omega = (Cplx (1 / sqrt 2) (1 / sqrt 2)) +gateQ "V" = scale 0.5 (a,b,b,a) where a = Cplx 1 (-1)+ b = Cplx 1 1+gateQ "omega" = (omega,0,0,omega) where omega = (Cplx (1 / sqrt 2) (1 / sqrt 2))+gateQ "iX" = (0,i,i,0)+gateQ name = error ("quantum gate: " ++ name ++ " not implemented")++-- | Like 'gateQ', but also conditionally invert the gate depending+-- on InverseFlag.+gateQinv :: (Floating r) => String -> InverseFlag -> GateR r+gateQinv name False = gateQ name+gateQinv name True = reverseR (gateQ name)++-- | The exponential function for 'Cplx' numbers.+expC :: (Floating r) => Cplx r -> Cplx r+expC (Cplx a b) = Cplx (exp a * cos b) (exp a * sin b)++-- | The constant π, as a complex number.+piC :: (Floating r) => Cplx r+piC = Cplx pi 0++-- | Like 'gateQ', but takes the name of a rotation and a real parameter. +rotQ :: (Floating r) => String -> Timestep -> GateR r+rotQ "exp(-i%Z)" theta = expZtR t+ where t = fromRational (toRational theta)+rotQ "exp(% pi i)" theta = gPhase t+ where t = fromRational (toRational theta)+rotQ "R(2pi/%)" theta = (1,0,0,expC (2*piC*i/t))+ where t = fromRational (toRational theta)+rotQ "T(%)" theta = (1,0,0,expC (-i*t))+ where t = fromRational (toRational theta)+rotQ "G(%)" theta = (expC (-i*t),0,0,expC (-i*t))+ where t = fromRational (toRational theta)+rotQ "Rz(%)" theta = (expC (-i*t/2),0,0,expC (i*t/2))+ where t = fromRational (toRational theta)+rotQ name theta = error ("quantum rotation: " ++ name ++ " not implemented")++-- | Like 'rotQ', but also conditionally invert the gate depending on+-- InverseFlag.+rotQinv :: (Floating r) => String -> InverseFlag -> Timestep -> GateR r+rotQinv name False theta = rotQ name theta+rotQinv name True theta = reverseR (rotQ name theta)++-- | Return the matrix for the 'expZt' gate.+expZtR :: (Floating r) => r -> GateR r+expZtR t = (expC (Cplx 0 (-t)),0,0,expC (Cplx 0 t))++-- | Return the matrix for the 'GPhase' gate.+gPhase :: (Floating r) => r -> GateR r+gPhase t = (expC (Cplx 0 (t * pi)),0,0,expC (Cplx 0 (t * pi)))++-- | 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 +gateC :: String -> ([Bool] -> Bool)+gateC "if" [a,b,c] = if a then b else c +gateC name inputs = error ("classical gate: " ++ name ++ ", not implemented (at least for inputs: " ++ show inputs ++ " )")++-- | The type of vectors with scalars in /n/ over the basis /a/. A+-- vector is simply a list of pairs. +data Vector n a = Vector [(a,n)]++-- | An amplitude distribution gives each classical basis state an amplitude.+type Amplitudes r = Vector (Cplx r) (Map Qubit Bool)++-- | A probability distribution gives each element a probability.+type ProbabilityDistribution r a = Vector r a++-- | 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.+type QuantumTrace r = ProbabilityDistribution r [Bool]++-- | Normalizing is used to make sure the probabilities add up to 1.+normalize :: (Floating r) => QuantumTrace r -> QuantumTrace r+normalize (Vector xs) = Vector xs'+ where+ p' = Prelude.foldr (\(_,p) accum -> accum + p) 0.0 xs+ xs' = map (\(bs,p) -> (bs,p / p')) xs ++-- | 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.+data QuantumState r = QState {+ next_wire :: Wire,+ quantum_state :: Amplitudes r,+ traces :: [QuantumTrace r], -- this will be stored in the reverse order in which+ -- the traces occured in the circuit.+ namespace :: Namespace, -- we need a namespace to keep track of subroutines+ trace_flag :: Bool -- whether or not we trace comments during the simulation+ }++-- | When we start a simulation, we need an empty starting state.+empty_quantum_state :: (Floating r) => Bool -> r -> QuantumState r+empty_quantum_state tf _ = QState { next_wire = 0, quantum_state = Vector [(Map.empty,1)], traces = [], namespace = namespace_empty, trace_flag = tf}++-- | 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.+classical_control :: Signed (B_Endpoint Qubit Bool) -> Bool+classical_control (Signed bep val) = case bep of+ (Endpoint_Bit val') -> val == val'+ (Endpoint_Qubit _) -> error "CNot: Quantum Control on Classical Gate"++-- | Map the 'classical_control' function to all the controls, and take the+-- 'and' of the result+classical_controls :: Ctrls Qubit Bool -> Bool+classical_controls cs = and (map classical_control cs)++-- | 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.+qc_control :: Map Qubit Bool -> Signed (B_Endpoint Qubit Bool) -> Bool+qc_control mqb (Signed bep val) = case bep of+ (Endpoint_Bit val') -> val == val'+ (Endpoint_Qubit q) -> val == val' where val' = mqb Map.! q++-- | Map the 'qc_control' function to all the controls (under the given basis +-- state), and take the 'and' of the result.+qc_controls :: Map Qubit Bool -> Ctrls Qubit Bool -> Bool+qc_controls mqb cs = and (map (qc_control mqb) cs)++-- | We can calculate the magnitude of a complex number+magnitude :: (Floating r) => Cplx r -> r+magnitude (Cplx a b) = sqrt (a^2 + b^2)++-- | 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.+split :: (Floating r, Eq r, Ord r) => Amplitudes r -> Qubit -> (r,Amplitudes r,Amplitudes r)+split (Vector pas) q = if p < 0 || p > 1 + then error "p < 0 or > 1" + else (p,Vector ift,Vector iff)+ where+ amp x = foldr (\(_,pa) p -> p + ((magnitude pa)*(magnitude pa))) 0 x+ apas = amp pas+ (ift,iff) = partition (\(mqb,_) -> (mqb Map.! q)) pas + p = if apas == 0 then 0 else (amp ift)/apas++-- | 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.+class (Floating r, Monad m) => PMonad r m where+ merge :: r -> a -> a -> m a++-- | We can merge two measurement outcomes, and explicitly keep the first outcome +-- as the True result, and the second as the False result.+merge_with_result :: PMonad r m => r -> a -> a -> m (Bool,a)+merge_with_result p ift iff = merge p (True,ift) (False,iff)++-- | IO forms a PMonad, where results are merged by choosing one probabilistically+-- using a random number.+instance (Floating r, Random r, Ord r) => PMonad r IO where+ merge p ift iff = do+ pp <- randomRIO (0,1)+ let res = if p > pp then ift else iff+ return res++-- | A State Monad holding a 'RandomGen' forms a 'PMonad', where results are +-- merged by choosing one probabilistically using a random number from the +-- 'RandomGen'.+instance (Floating r, Random r, Ord r, RandomGen g) => PMonad r (State g) where+ merge p ift iff = do+ gen <- get+ let (pp,gen') = randomR (0,1) gen+ put gen'+ let res = if p > pp then ift else iff+ return res++-- | Any numeric indexed vector forms a 'Monad'.+instance (Num n) => Monad (Vector n) where+ return a = Vector [(a,1)]+ (Vector ps) >>= f = Vector [(b,i*j) | (a,i) <- ps, (b,j) <- removeVector (f a)] where removeVector (Vector as) = as ++instance (Num n) => Applicative (Vector n) where+ pure = return+ (<*>) = ap++instance (Num n) => Functor (Vector n) where+ fmap = liftM++-- | We can show certain vectors, ignoring any 0 probabilities, and+-- combining equal terms.+instance (Show a,Eq a,Num n,Eq n,Show n) => Show (Vector n a) where + show (Vector ps) = show (combine (filter (\ (a,p) -> p /= 0) ps) []) + where+ combine [] as = as+ combine (x:xs) as = combine xs (combine' x as)+ combine' (a,p) [] = [(a,p)]+ combine' (a,p) ((a',p'):xs) = if a == a' then (a,p+p'):xs else (a',p'):(combine' (a,p) xs)++-- | 'ProbabilityDistribution' forms a 'PMonad' such that probabilistic results are +-- \"merged\" by extending the probability distribution by the possible results.+instance (Floating r, Eq r) => PMonad r (Vector r) where+ merge 1 ift iff = Vector [(ift,1)]+ merge 0 ift iff = Vector [(iff,1)]+ merge p ift iff = Vector [(ift,p),(iff,1-p)]++-- | The 'get_trace' function returns a probability distribution of+-- the state of a list of qubits within a given amplitude+-- distribution.+get_trace :: (Floating r) => [Qubit] -> Amplitudes r -> QuantumTrace r+get_trace qs (Vector amps) = Vector ps+ where+ ps = map (tracing qs) amps+ tracing qs (mqb,cd) = (map (\q -> mqb Map.! q) qs,(magnitude cd)*(magnitude cd)) ++-- | Add an amplitude to an amplitude distribution, combining (adding) the amplitudes for equal states in the distribution.+add :: (Floating r) => ((Map Qubit Bool),Cplx r) -> Amplitudes r -> Amplitudes r+add (a,x) (Vector axs) = Vector (add' axs)+ where add' [] = [(a,x)]+ add' ((by @ (b,y)):bys) | a == b = (b,x+y):bys+ | otherwise = by:(add' bys)++-- | The apply' function is used to apply a function on \"basis states\" to an +-- entire amplitude distribution. +apply :: (Floating r, Eq r) => (Map Qubit Bool -> Amplitudes r) -> Amplitudes r -> Amplitudes r+apply f (Vector []) = Vector []+apply f (Vector ((a,0):[])) = Vector []+apply f (Vector ((a,x):[])) = Vector (map (\(b,k) -> (b,x*k)) (fa)) where Vector fa = f a+apply f (Vector ((a,0):vas)) = apply f (Vector vas)+apply f (Vector ((a,x):vas)) = foldr add (apply f (Vector vas)) (map (\(b,k) -> (b,x*k)) (fa)) where Vector fa = f a++-- | Lift a function that returns a single basis state, to a function that+-- returns an amplitude distribution (containing a singleton).+vector :: (Floating r) => (Map Qubit Bool -> Map Qubit Bool) -> Map Qubit Bool -> Amplitudes r+vector f a = Vector [(f a,1)]++-- | apply the given function only if the controls fire.+if_controls :: (Floating r) => Ctrls Qubit Bool -> (Map Qubit Bool -> Amplitudes r) -> Map Qubit Bool -> Amplitudes r+if_controls c f mqb = if (qc_controls mqb c) then f mqb else Vector [(mqb,1)]++-- | '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.+performGateQ :: (Floating r) => GateR r -> Qubit -> Map Qubit Bool -> Amplitudes r +performGateQ (m00,m01,m10,m11) q mqb = if (mqb Map.! q) then (Vector [(Map.insert q False mqb,m01),(mqb,m11)])+ else (Vector [(mqb,m00),(Map.insert q True mqb,m10)])++-- | 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_transformer :: (PMonad r m, Ord r) => Transformer (StateT (QuantumState r) m) Qubit Bool+-- Translation of classical gates:+simulation_transformer (T_CNot ncf f) = f $+ \val c -> do+ let ctrl = classical_controls c+ let val' = if ctrl then not val else val+ return (val',c)+simulation_transformer (T_CInit val ncf f) = f $+ return val+simulation_transformer (T_CTerm b ncf f) = f $+ \val -> if val == b then return () else error "CTerm: Assertion Incorrect"+simulation_transformer (T_CDiscard f) = f $+ \val -> return ()+simulation_transformer (T_DTerm b f) = f $+ \val -> return ()+simulation_transformer (T_CGate name ncf f) = f $+ \list -> do+ let result = gateC name list+ return (result,list) +simulation_transformer g@(T_CGateInv name ncf f) = f $+ \result list -> do+ let result' = gateC name list+ if result == result' then return list else error "CGateInv: Uncomputation error"+-- Translation of quantum gates:+simulation_transformer (T_QGate "not" 1 0 _ ncf f) = f $+ \[q] [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls cs (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q], [], cs)+simulation_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $+ \qs [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = foldr (\q a -> apply (if_controls cs (performGateQ gate q)) a) amps qs+ put (state {quantum_state = amps'})+ return (qs, [], cs)+simulation_transformer (T_QGate "H" 1 0 _ ncf f) = f $+ \[q] [] cs -> do+ let gate = gateQ "hadamard"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls cs (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q], [], cs)+simulation_transformer (T_QGate "swap" 2 0 _ ncf f) = f $+ \[w, v] [] cs -> do+ let gate = gateQ "x"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gate v)) amps+ let amps'' = apply (if_controls ((Signed (Endpoint_Qubit v) True):cs) (performGateQ gate w)) amps'+ let amps''' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gate v)) amps''+ put (state {quantum_state = amps'''})+ return ([w, v], [], cs)+simulation_transformer (T_QGate "W" 2 0 _ ncf f) = f $+ \[w, v] [] cs -> do+ let gateX = gateQ "x"+ let gateH = gateQ "hadamard"+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gateX v)) amps+ let amps'' = apply (if_controls ((Signed (Endpoint_Qubit v) True):cs) (performGateQ gateH w)) amps'+ let amps''' = apply (if_controls ((Signed (Endpoint_Qubit w) True):cs) (performGateQ gateX v)) amps''+ put (state {quantum_state = amps'''})+ return ([w, v], [], cs)+simulation_transformer (T_QGate "trace" _ _ False ncf f) = f $+ \qs gc c -> do+ -- a \"trace\" gate adds the current probability distribution for the given qubits+ -- to the list of previous quantum traces+ state <- get+ let current_traces = traces state+ let amps = quantum_state state+ let new_trace = get_trace qs amps+ put (state {traces = new_trace:current_traces})+ return (qs,gc,c)+simulation_transformer (T_QGate "trace" _ _ True ncf f) = f $+ \qs gc c -> return (qs,gc,c) -- we don't do anything for the inverse \"trace\" gate+simulation_transformer (T_QGate name 1 0 inv ncf f) = f $ + \[q] [] c -> do+ let gate = gateQinv name inv+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls c (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q],[],c)+simulation_transformer (T_QRot name 1 0 inv theta ncf f) = f $ + \[q] [] c -> do+ let gate = rotQinv name inv theta+ state <- get+ let amps = quantum_state state+ let amps' = apply (if_controls c (performGateQ gate q)) amps+ put (state {quantum_state = amps'})+ return ([q],[],c)+simulation_transformer (T_GPhase t ncf f) = f $+ \w c -> do+ state <- get+ let gate = rotQ "exp(% pi i)" t+ let wire = next_wire state+ let q = qubit_of_wire wire+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q False)) amps + let amps'' = apply (if_controls c (performGateQ gate q)) amps'+ let (p,ift,iff) = split amps'' q+ (val,ampsf) <- lift $ merge_with_result p ift iff + case val of+ False -> do+ let ampsf' = apply (vector (Map.delete q)) ampsf + put (state {quantum_state = ampsf'})+ return c+ _ -> error "GPhase"+simulation_transformer (T_QInit val ncf f) = f $+ do+ state <- get+ let wire = next_wire state+ let q = qubit_of_wire wire+ let wire' = wire + 1+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + put (state {quantum_state = amps', next_wire = wire'})+ return q+simulation_transformer (T_QMeas f) = f $+ \q -> do+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ let amps'' = apply (vector (Map.delete q)) amps' + put (state {quantum_state = amps''})+ return val+simulation_transformer (T_QDiscard f) = f $+ \q -> do+ -- a discard is essentially a measurement, with the result thrown away, so we+ -- do that here, as it will reduce the size of the quantum state we are+ -- simulating over.+ state <- get+ let (p,ift,iff) = split (quantum_state state) q+ (_,amps) <- lift $ merge_with_result p ift iff+ let amps' = apply (vector (Map.delete q)) amps + put (state {quantum_state = amps'})+ return ()+simulation_transformer (T_QTerm b ncf f) = f $+ \q -> do+ -- with a real quantum computer, when we terminate a qubit with an+ -- assertion we have no way of actually checking the assertion. The+ -- best we can do is measure the qubit and then throw an error if+ -- the assertion is incorrect, which may only occur with a small+ -- probability. Here, we could split the quantum state and see if+ -- the qubit exists in the incorrect state with any non-zero+ -- probability, and throw an error. However, we don't do this+ -- because an error would sometimes be thrown due to rounding.+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ if val == b then put (state {quantum_state = amps'}) else error "QTerm: Assertion doesn't hold"+simulation_transformer (T_Comment "" inv f) = f $+ \_ -> return ()+ -- e.g. a comment can be (the) empty (string) if it only contains labels+simulation_transformer (T_Comment name inv f) = f $+ \_ -> do+ state <- get+ -- we don't need to do anything with a comment, but they can be useful+ -- to know where we are in the circuit, so we shall output a trace of+ -- the (non-empty) comments during a simulation. + trace (trace_flag state) name $ return ()+-- The remaining gates are not yet implemented:+simulation_transformer g@(T_QGate _ _ _ _ _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_QRot _ _ _ _ _ _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_CSwap _ _) =+ error ("simulation_transformer: unimplemented gate: " ++ show g)+simulation_transformer g@(T_QPrep ncf f) = f $+ \val -> do+ state <- get+ let wire = next_wire state+ let q = qubit_of_wire wire+ let wire' = wire + 1+ let amps = quantum_state state+ let amps' = apply (vector (Map.insert q val)) amps + put (state {quantum_state = amps', next_wire = wire'})+ return q+simulation_transformer g@(T_QUnprep ncf f) = f $+ \q -> do+ state <- get+ let amps = quantum_state state+ let (p,ift,iff) = split amps q+ (val,amps') <- lift $ merge_with_result p ift iff+ put (state {quantum_state = amps'})+ return val+simulation_transformer g@(T_Subroutine sub inv ncf scf ws_pat a1_pat vs_pat a2_pat rep f) = f $+ \ns in_values c -> do+ case Map.lookup sub ns of+ Just (TypedSubroutine sub_ocirc _ _ _) -> do+ let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc+ let in_bindings = bind_list in_wires in_values bindings_empty+ let sub_bcirc = (sub_circ,ns)+ out_bind <- transform_bcircuit_rec simulation_transformer sub_bcirc in_bindings+ return (unbind_list out_bind out_wires, c) + Nothing -> error $ "simulation_transformer: subroutine " ++ show sub ++ " not found (in " ++ showNames ns ++ ")"++-- | The simulation_transformer is also Dynamic, as the simulated wire states+-- can simply be used to perform dynamic liftings.+simulation_dynamic_transformer :: (PMonad r m, Ord r) => DynamicTransformer (StateT (QuantumState r) m) Qubit Bool+simulation_dynamic_transformer = DT {+ transformer = simulation_transformer,+ define_subroutine = \name subroutine -> return (),+ lifting_function = return+ } ++-- | Apply the 'simulation_dynamic_transformer' to a (unary) circuit+-- generating function.+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))+simulate_transform_unary (f :: qa -> Circ qb) input = do+ let ((), circuit) = encapsulate_dynamic (\() -> qc_init input >>= \qi -> f qi >>= \qi' -> qc_measure qi') ()+ (cb,out_bind) <- transform_dbcircuit simulation_dynamic_transformer circuit bindings_empty+ let output = qc_unbind out_bind cb+ return output++-- | 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.+qdata_concrete_shape :: (QData qa) => BType qa -> qa+qdata_concrete_shape ba = evalState mqa 0+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State Wire Qubit+ f _ = do+ w <- get+ put (w+1)+ return (qubit_of_wire w)++-- | 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_concrete_bindings :: (QData qa) => BType qa -> Bindings Qubit Bool+qdata_concrete_bindings ba = snd $ execState mqa (0,bindings_empty)+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State (Wire,Bindings Qubit Bool) ()+ f b = do+ (w,bindings) <- get+ put (w+1,bind_qubit_wire w (qubit_of_wire w) bindings)+ return () ++-- | 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_to_basis :: (QData qa) => BType qa -> Map Qubit Bool+qdata_to_basis ba = snd $ execState mqa (0,Map.empty)+ where+ shape = shapetype_b ba+ mqa = qdata_mapM shape f ba+ f :: Bool -> State (Wire,Map Qubit Bool) ()+ f b = do+ (w,m) <- get+ put (w+1,Map.insert (qubit_of_wire w) b m)+ return ()++-- | 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.+qdata_vector_to_amplitudes :: (QData qa, Floating r) => Vector (Cplx r) (BType qa) -> Amplitudes r+qdata_vector_to_amplitudes (Vector das) = (Vector (map (\(a,d) -> (qdata_to_basis a,d)) das))++-- | 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.+basis_to_qdata :: (QData qa) => qa -> Map Qubit Bool -> BType qa+basis_to_qdata qa m = getId $ qdata_mapM qa f qa+ where+ f :: Qubit -> Id Bool+ f q = case Map.lookup q m of+ Just res -> return res+ _ -> error "basis_to_qdata: qubit not in scope"++-- | 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.+amplitudes_to_qdata_vector :: (QData qa, Floating r) => qa -> Amplitudes r -> Vector (Cplx r) (BType qa)+amplitudes_to_qdata_vector qa (Vector das) = Vector (map (\(a,d) -> (basis_to_qdata qa a,d)) das)++-- | Apply the 'simulation_dynamic_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.+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))+simulate_amplitudes_unary f input@(Vector is) = do+ (out_shape,state) <- runStateT circ input_state+ let out_amps = quantum_state state + return (amplitudes_to_qdata_vector out_shape (apply (vector id) out_amps))+ where+ amps = qdata_vector_to_amplitudes input+ specimen = case is of+ [] -> error "simulate_amplitudes_unary: can't use empty vector"+ ((b,_):_) -> b+ shape = qdata_concrete_shape specimen+ bindings = qdata_concrete_bindings specimen+ max_wire = case wires_of_bindings bindings of+ [] -> 0+ ws -> maximum ws + input_state = (empty_quantum_state False undefined) {quantum_state = amps, next_wire = max_wire + 1}+ (_,circuit) = encapsulate_dynamic f shape+ circ = do+ (cb,out_bind) <- transform_dbcircuit simulation_dynamic_transformer circuit bindings+ let output = qc_unbind out_bind cb+ return output++-- | 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 'Data.Number.FixedPrec.FixedPrec' /e/, can be used.+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)+sim_amps gen f input_map = output_map+ where+ input_vec = Vector (Map.toList input_map)+ circ = simulate_amplitudes_unary f input_vec+ Vector output = evalState circ gen+ output_map = Map.fromList output++-- | 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 :: (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)+run_unary g r f input = evalState comp g where+ comp = evalStateT f' (empty_quantum_state False r)+ f' = simulate_transform_unary f input++-- | 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_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]+run_unary_trace g r f input = evalState comp g where+ comp = do+ state <- execStateT f' (empty_quantum_state True r)+ let qts = traces state+ return (reverse qts)+ f' = simulate_transform_unary f input++-- | Like 'run_unary', but run in the 'IO' monad instead of passing an+-- explicit source of randomness.+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))+run_unary_io r f input = do+ g <- newStdGen+ return (run_unary g r f input)++-- | Like 'run_unary_trace', 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]+run_unary_trace_io r f input = do+ g <- newStdGen+ return (run_unary_trace g r f input)++-- | Apply the 'simulation_transformer' to a (unary) circuit, and then evaluate+-- the resulting stateful computation to get a probability distribution of possible+-- results+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))+sim_unary r f input = evalStateT f' (empty_quantum_state False r)+ where f' = simulate_transform_unary f input++-- ======================================================================+-- * Generic functions++-- ** Generic run function++-- $ Generic functions to run Quipper circuits, using "Random" to+-- simulate quantum states.++-- | 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 :: (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'+run_generic gen r f = g+ where+ f1 = quncurry f+ g1 = run_unary gen r f1+ g = mcurry g1++-- | 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_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'+run_generic_trace gen r f = g + where+ f1 = quncurry f+ g1 = run_unary_trace gen r f1+ g = mcurry g1++-- | Like 'run_generic', but run in the 'IO' monad instead of passing+-- an explicit source of randomness.+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'+run_generic_io r f = g + where+ f1 = quncurry f+ g1 = run_unary_io r f1+ g = mcurry g1++-- | Like 'run_generic_trace', 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'+run_generic_trace_io r f = g + where+ f1 = quncurry f+ g1 = run_unary_trace_io r f1+ g = mcurry g1++-- ----------------------------------------------------------------------+-- ** Generic sim function++-- $ A generic function to simulate Quipper circuits, returning a+-- probability distribution of the possible results.++-- | A generic function to simulate Quipper circuits, 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.+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'+sim_generic r f = g where+ f1 = quncurry f+ g1 = sim_unary r f1+ g = mcurry g1 +
@@ -0,0 +1,506 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Synthesis.hs" #-}+{-# LANGUAGE DoAndIfThenElse #-}++-- | A Quipper library for synthesizing Clifford+/T/ circuits directly+-- from a matrix description or Euler angle description of a unitary+-- operator. This library provides both exact and approximate+-- synthesis.++module Quipper.Libraries.Synthesis where++import Quipper++import Quipper.Internal++import Quantum.Synthesis.CliffordT+import Quantum.Synthesis.MultiQubitSynthesis+import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.Newsynth+import Quantum.Synthesis.SymReal+import Quantum.Synthesis.EulerAngles++import Quipper.Utils.Auxiliary (boollist_of_int_bh)++import Control.Monad (when)+import Data.Bits (xor, (.&.))+import System.Random+import Text.Printf++-- ----------------------------------------------------------------------+-- * Precision++-- | A type to measure precision. Precision is expressed as a number+-- /b/ of bits, i.e., binary digits, so that ε = 2[sup −/b/].+type Precision = Double++-- | Binary digits, as a unit of precision. For example, the following+-- specifies a precision of 20 binary digits:+-- +-- > prec = 20 * bits+bits :: Precision+bits = 1++-- | Decimal digits, as a unit of precision. For example, the+-- following specifies a precision of 30 decimal digits:+-- +-- > prec = 30 * digits+digits :: Precision+digits = logBase 2 10++-- ----------------------------------------------------------------------+-- * Phase++-- | A boolean flag indicating whether to respect global phases+-- during circuit synthesis ('True') or disregard them ('False').+type KeepPhase = Bool+ +-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Apply a gate (from the type 'Gate' of Clifford+/T/ operators) to+-- the given 'Qubit'.+apply_gate_at :: Gate -> Qubit -> Circ ()+apply_gate_at X q = do+ gate_X_at q+apply_gate_at Y q = do+ gate_Y_at q+apply_gate_at Z q = do+ gate_Z_at q+apply_gate_at H q = do+ hadamard_at q+apply_gate_at S q = do+ gate_S_at q+apply_gate_at T q = do+ gate_T_at q+apply_gate_at E q = do+ gate_E_at q+apply_gate_at W q = do+ gate_omega_at q++-- | Apply a gate list (from the type 'Gate' of Clifford+/T/+-- operators) to the given 'Qubit'. +-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.+apply_gates_at :: [Gate] -> Qubit -> Circ ()+apply_gates_at gates q = do+ sequence_ [ apply_gate_at g q | g <- reverse gates ]++-- | Like 'apply_gates_at', but apply the same list of gates to two+-- qubits in parallel.+apply_gates2_at :: [Gate] -> Qubit -> Qubit -> Circ ()+apply_gates2_at gates q1 q2 = do+ sequence_ [ do+ apply_gate_at g q1+ apply_gate_at g q2+ | g <- reverse gates ]++-- | Input two indices /i/ and /j/, a list of qubits /qlist/, and an+-- imperative-style single-qubit gate /U/. Apply the two-level+-- operator /U/[sub /i/,/j/] to /qlist/. Intended usage:+-- +-- > twolevel i j qlist gate_U_at+-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- This function implements an improved version of Gray codes.+twolevel :: Index -> Index -> [Qubit] -> (Qubit -> Circ ()) -> Circ ()+twolevel i j qlist body = aux l1 l2 qlist where+ n = length qlist+ l1 = boollist_of_int_bh n i+ l2 = boollist_of_int_bh n j++ aux [] [] [] = error "twolevel: i=j"+ aux (h1:t1) (h2:t2) (q:qs)+ | h1 == h2 =+ with_controls (q .==. h1) $ do+ aux t1 t2 qs+ | h1 == True =+ with_basis_change (qnot_at q) $ do+ aux2 t1 t2 q qs+ | otherwise =+ aux2 t1 t2 q qs+ aux _ _ _ = error "twolevel: internal error 1" -- not reached++ aux2 [] [] q [] =+ body q+ aux2 (h1:t1) (h2:t2) q0 (q:qs)+ | h1 == h2 =+ with_controls (q .==. h1) $ do+ aux2 t1 t2 q0 qs+ | otherwise =+ with_basis_change (qnot_at q `controlled` q0) $ do+ with_controls (q .==. h1) $ do+ aux2 t1 t2 q0 qs+ aux2 _ _ _ _ = error "twolevel: internal error 2" -- not reached++-- | Apply a /T/[sup /m/] gate. This gate is decomposed into /Z/, /S/,+-- /S/[sup †], /T/, and /T/[sup †] gates.+gate_T_power_at :: Int -> Qubit -> Circ ()+gate_T_power_at 0 q = do+ return ()+gate_T_power_at 1 q = do+ gate_T_at q+gate_T_power_at 2 q = do+ gate_S_at q+gate_T_power_at 3 q = do+ gate_S_at q+ gate_T_at q+gate_T_power_at 4 q = do+ gate_Z_at q+gate_T_power_at 5 q = do+ gate_Z_at q+ gate_T_at q+gate_T_power_at 6 q = do+ gate_S_inv_at q+gate_T_power_at 7 q = do+ gate_T_inv_at q+gate_T_power_at m q = gate_T_power_at (m `mod` 8) q++-- | Apply a 'TwoLevel' gate to the given list of qubits. +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+apply_twolevel_at :: TwoLevel -> [Qubit] -> Circ ()+apply_twolevel_at (TL_X i j) qlist =+ twolevel i j qlist $ \q -> do+ gate_X_at q+apply_twolevel_at (TL_H i j) qlist =+ twolevel i j qlist $ \q -> do+ hadamard_at q+apply_twolevel_at (TL_T m i j) qlist+ | m `mod` 8 == 0 = return ()+ | otherwise =+ twolevel i j qlist $ \q -> do+ gate_T_power_at m q+apply_twolevel_at (TL_omega m i) qlist+ | m' == 0 = do+ return ()+ | qlist == [] = do+ global_phase (fromIntegral m' * 0.25)+ | otherwise =+ apply_twolevel_at (TL_T m j i) qlist+ where+ j = if i == 0 then 1 else i .&. (i-1)+ m' = m `mod` 8++-- | Apply a list of 'TwoLevel' gates to the given list of+-- qubits. +-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.++-- Implementation note: this could be improved by combining+-- consecutive two-level gates if they share the same i and j+apply_twolevels_at :: [TwoLevel] -> [Qubit] -> Circ ()+apply_twolevels_at ops qlist =+ sequence_ [ apply_twolevel_at g qlist | g <- reverse ops ]++-- | Like 'apply_twolevel_at', but use the alternate generators for+-- two-level gates.+apply_twolevel_alt_at :: TwoLevelAlt -> [Qubit] -> Circ ()+apply_twolevel_alt_at (TL_iX j l) qlist = do+ twolevel j l qlist $ \q -> do+ gate_iX_at q+apply_twolevel_alt_at (TL_TiHT m j l) qlist = do+ twolevel j l qlist $ \q -> do+ let basischange = do+ gate_T_power_at m q+ gate_S_at q+ gate_H_at q+ gate_T_at q+ with_basis_change basischange $ do+ gate_iX_at q+apply_twolevel_alt_at (TL_W m j l) qlist + | m' == 0 = do+ return ()+ | otherwise = do+ twolevel j l qlist $ \q -> do+ gate_iX_at q+ let basischange = do+ gate_T_power_at m' q+ with_basis_change basischange $ do+ gate_iX_inv_at q+ where+ m' = m `mod` 8+apply_twolevel_alt_at (TL_omega_alt m j) qlist+ | m' == 0 = do+ return ()+ | qlist == [] = do+ global_phase (fromIntegral m' * 0.25)+ | otherwise = do+ apply_twolevel_at (TL_T m l j) qlist+ where+ l = if j == 0 then 1 else j .&. (j-1)+ m' = m `mod` 8++-- | Apply a list of 'TwoLevelAlt' gates to the given list of+-- qubits. +-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- Note: the operators in the list are applied right-to-left, i.e.,+-- the gate list is assumed given in matrix multiplication order, but+-- are applied in circuit order.++-- Implementation note: this could be improved by combining+-- consecutive two-level gates if they share the same i and j+apply_twolevels_alt_at :: [TwoLevelAlt] -> [Qubit] -> Circ ()+apply_twolevels_alt_at ops qlist =+ sequence_ [ apply_twolevel_alt_at g qlist | g <- reverse ops ]+++-- ----------------------------------------------------------------------+-- * Single-qubit exact synthesis++-- | Decompose the given operator exactly into a single-qubit+-- Clifford+/T/ circuit. The operator must be given in one of the+-- available exact formats, i.e., any instance of the 'ToGates' class.+-- Typical instances are:+-- +-- * 'U2' 'DOmega': a 2×2 unitary operator with entries from the ring+-- [bold D][ω] = ℤ[1\/√2, /i/];+-- +-- * 'SO3' 'DRootTwo': a 3×3 Bloch sphere operator with entries from the+-- ring ℤ[1\/√2]. In this last case, the operator will be synthesized+-- up to an unspecified global phase.+exact_synthesis1 :: (ToGates a) => a -> Qubit -> Circ Qubit+exact_synthesis1 op q = do+ comment_with_label "ENTER: exact_synthesis1" q "q"+ apply_gates_at gates q+ comment_with_label "EXIT: exact_synthesis1" q "q"+ return q+ where+ gates = convert (normalize op) :: [Gate]++-- ----------------------------------------------------------------------+-- * Multi-qubit exact synthesis++-- | Decompose the given operator exactly into a Clifford+/T/ circuit.+-- The operator must be given as an /n/×/n/-matrix with coefficients+-- in a ring that is an instance of the 'ToQOmega' class. A typical+-- example of such a ring is 'DOmega'.+-- +-- If this function is applied to a list of /m/ qubits, then we must+-- have /n/ ≤ 2[sup /m/].+-- +-- The qubits in /qlist/ are ordered lexicographically left-to-right,+-- e.g., [|00〉, |01〉, |10〉, |11〉].+-- +-- The generated circuit contains no ancillas, but may contain+-- multi-controlled gates whose decomposition into Clifford+/T/+-- generators requires ancillas.+exact_synthesis :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]+exact_synthesis op qlist = do+ comment_with_label "ENTER: exact_synthesis" qlist "q"+ apply_twolevels_at twolevels qlist+ comment_with_label "EXIT: exact_synthesis" qlist "q"+ return qlist+ where+ op_DOmega = matrix_map (to_dyadic . toQOmega) op+ twolevels = synthesis_nqubit op_DOmega++-- | Like 'exact_synthesis', but use the alternate algorithm from+-- Section 6 of Giles-Selinger. This means all but at most one of the+-- generated multi-controlled gates have determinant 1, which means+-- they can be further decomposed without ancillas.+exact_synthesis_alt :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]+exact_synthesis_alt op qlist = do+ comment_with_label "ENTER: exact_synthesis_alt" qlist "q"+ apply_twolevels_alt_at twolevels qlist+ comment_with_label "EXIT: exact_synthesis_alt" qlist "q"+ return qlist+ where+ op_DOmega = matrix_map (to_dyadic . toQOmega) op+ twolevels = synthesis_nqubit_alt op_DOmega++-- ----------------------------------------------------------------------+-- * Single-qubit approximate synthesis++-- ----------------------------------------------------------------------+-- ** /z/-Rotations++-- | Decompose an /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2] gate into a+-- single-qubit Clifford+/T/ circuit up to the given precision. +-- +-- \[image Rz.png]+-- +-- The parameters are:+-- +-- * a precision /b/ ≥ 0;+-- +-- * an angle θ, given as a 'SymReal' value;+-- +-- * a source of randomness /g/.+approximate_synthesis_zrot :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Circ Qubit+approximate_synthesis_zrot b theta g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_zrot (b=%.2f, theta=%s)" b (show theta)) q "q"+ q <- without_comments $ do+ exact_synthesis1 op q + comment_with_label "EXIT: approximate_synthesis_zrot" q "q"+ return q + where+ op = newsynth b theta g++-- ----------------------------------------------------------------------+-- ** Global phase gates++-- | Construct a Clifford+/T/ circuit (with no inputs and outputs)+-- that approximates a scalar global phase gate [exp /i/θ] up to the+-- given precision. The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected. (Note that if this is set to 'False', then this function+-- is just a no-op);+-- +-- * a precision /b/ ≥ 0;+-- +-- * an angle θ, given as a 'SymReal' value;+-- +-- * a source of randomness /g/.+-- +-- We use the following decomposition:+-- +-- \[image phase.png]+approximate_synthesis_phase :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Circ ()+approximate_synthesis_phase False b theta g = do+ return ()+approximate_synthesis_phase True b theta g = do+ comment (printf "ENTER: approximate_synthesis_phase (b=%.2f, theta=%s)" b (show theta))+ when (gates /= []) $ do+ q <- qinit 0+ apply_gates_at gates q+ qterm 0 q+ comment "EXIT: approximate_synthesis_phase"+ where+ op = newsynth b (-2*theta) g+ gates = convert (normalize op) :: [Gate]++-- ----------------------------------------------------------------------+-- ** /U/(2) from Euler angles++-- | Decompose the operator+-- +-- * /U/ = [exp /i/α] R[sub /z/](β) R[sub /x/](γ) R[sub /z/](δ)+-- +-- into the Clifford+/T/ gate base, up to the given precision.+-- The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected. If this is 'False', the angle α is disregarded;+-- +-- * a precision /b/ ≥ 0;+-- +-- * a tuple of Euler angles (α, β, γ, δ), given as 'SymReal' values;+-- +-- * a source of randomness /g/.+approximate_synthesis_euler :: (RandomGen g) => KeepPhase -> Precision -> (SymReal, SymReal, SymReal, SymReal) -> g -> Qubit -> Circ Qubit+approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_euler (b=%.2f, alpha=%s, beta=%s, gamma=%s, delta=%s, keepphase=%s)" b (show alpha) (show beta) (show gamma) (show delta) (show keepphase)) q "q"+ without_comments $ do+ exact_synthesis1 (op_beta * u2_H * op_gamma * u2_H * op_delta) q+ approximate_synthesis_phase keepphase b' alpha g1+ comment_with_label "EXIT: approximate_synthesis_euler" q "q"+ return q+ where+ op_beta = newsynth b' beta g2+ op_gamma = newsynth b' gamma g3+ op_delta = newsynth b' delta g4+ (g', g'') = split g+ (g1, g2) = split g'+ (g3, g4) = split g''+ b' = b + 2 -- ε' = ε / 4++-- ----------------------------------------------------------------------+-- ** /U/(2) from matrix++-- | Decompose a single-qubit unitary gate /U/ into the Clifford+/T/+-- gate base, up to the given precision, provided that det /U/ = 1.+-- The parameters are:+-- +-- * a flag /keepphase/ to indicate whether global phase should be+-- respected;+-- +-- * a precision /b/ ≥ 0;+-- +-- * a 2×2 complex matrix, with entries expressed as 'Cplx' 'SymReal' values;+-- +-- * a source of randomness /g/.+approximate_synthesis_u2 :: (RandomGen g) => KeepPhase -> Precision -> U2 (Cplx SymReal) -> g -> Qubit -> Circ Qubit+approximate_synthesis_u2 keepphase b op g q = do+ comment_with_label (printf "ENTER: approximate_synthesis_u2 (b=%.2f, op=%s, keepphase=%s)" b (show op) (show keepphase)) q "q"+ q <- without_comments $ do+ approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q+ comment_with_label "EXIT: approximate_synthesis_u2" q "q"+ return q+ where+ (alpha, beta, gamma, delta) = euler_angles op++-- ----------------------------------------------------------------------+-- ** Controlled gates++-- | Decompose a controlled /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2] gate+-- into a single-qubit Clifford+/T/ circuit up to the given+-- precision. The parameters are as for 'approximate_synthesis_phase'.+-- The first input is the target qubit, and the second input the+-- control.+-- +-- We use the following decomposition. It has lower /T/-count than the+-- alternatives and makes good use of parallelism. Since it uses the+-- same rotation twice, only a single run of the synthesis algorithm+-- is required.+-- +-- \[image controlled-zrot.png]+approximate_synthesis_zrot_ctrl :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Qubit -> Circ Qubit+approximate_synthesis_zrot_ctrl b theta g q1 c2 = do+ comment_with_label (printf "ENTER: approximate_synthesis_zrot_ctrl (b=%.2f, theta=%s)" b (show theta)) (q1,c2) ("q", "c")+ qnot_at c2 `controlled` q1 .==. False+ apply_gates2_at gates q1 c2+ qnot_at c2 `controlled` q1 .==. False+ comment_with_label "EXIT: approximate_synthesis_zrot_ctrl" (q1,c2) ("q", "c")+ return q1+ where+ gates = convert (normalize op) :: [Gate]+ op = newsynth (b+1) (theta/2) g++-- | Decompose a controlled phase gate+-- +-- \[image controlled_phase.png]+-- +-- into the Clifford+/T/ gate base. The parameters are as for+-- 'approximate_synthesis_phase'.+-- +-- We use the following decomposition. It has lower /T/-count than the+-- alternatives and makes good use of parallelism. Since it uses the+-- same rotation twice, only a single run of the synthesis algorithm+-- is required.+-- +-- \[image controlled-phase-decomp.png]+-- +-- If the 'KeepPhase' flag is set, respect global phase; otherwise,+-- disregard it.+approximate_synthesis_phase_ctrl :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Qubit -> Circ Qubit+approximate_synthesis_phase_ctrl keepphase b theta g q1 = do+ comment_with_label (printf "ENTER: approximate_synthesis_phase_ctrl (b=%.2f, theta=%s keepphase=%s)" b (show theta) (show keepphase)) q1 "q"+ if keepphase then do+ q2 <- qinit True+ apply_gates2_at gates q2 q1+ qterm True q2+ else do+ approximate_synthesis_zrot b theta g q1+ return ()+ comment_with_label "EXIT: approximate_synthesis_phase_ctrl" q1 "q"+ return q1+ where+ gates = convert (normalize op) :: [Gate]+ op = newsynth (b+1) theta g
@@ -0,0 +1,94 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LINE 1 "Quipper/Libraries/Unboxing.hs" #-}+{-# LANGUAGE Rank2Types #-}++-- | This library provides functions for “unboxing” hierarchical circuits,+-- replacing calls to named subroutines by inlined copies of the subroutines+-- themselves.++module Quipper.Libraries.Unboxing where++import Quipper+import Quipper.Internal+import Quipper.Internal.Circuit (BoxId (..), RepeatFlag (..))+import Quipper.Internal.Monad (endpoints_of_wires_in_arity)+import Quipper.Internal.Generic (inline_subroutine, transform_unary)++-- | A transformer to peel away one level of boxing. Transforms any+-- top-level subroutine gate into its corresponding circuit.+unbox_transformer :: Transformer Circ Qubit Bit+unbox_transformer (T_Subroutine name inv ncf _ _ _ ws2 a2 (RepeatFlag reps) f) = f $+ \namespace ws c -> do+ outputs <- loopM reps ws+ ((without_controls_if ncf) .+ (with_controls c) .+ ((if inv then flip reverse_generic (endpoints_of_wires_in_arity a2 ws2) else id)+ (inline_subroutine name namespace)))+ return (outputs, c)+unbox_transformer x = identity_transformer x++-- | Peel away one level of boxing from a circuit. Transforms any+-- top-level subroutine gate into its corresponding circuit.+unbox_unary :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+unbox_unary circ = transform_unary unbox_transformer circ ++-- | Peel away one level of boxing from a circuit. Transforms any+-- top-level subroutine gate into its corresponding circuit.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox :: (QCData x) => Circ x -> Circ x+-- > unbox :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+-- > unbox :: (QCData x, QCData y, QCData z) => (x -> y -> Circ z) -> (x -> y -> Circ z)+-- +-- and so forth.+unbox :: (QCData x, QCData y, QCurry qfun x y) => qfun -> qfun+unbox = qcurry . unbox_unary . quncurry++-- | A transformer to recursively unbox some specified class of boxed subroutines.+unbox_recursive_filtered_transformer :: (BoxId -> Bool) -> Transformer Circ Qubit Bit+unbox_recursive_filtered_transformer p b@(T_Subroutine boxid inv ncf _ _ _ ws2 a2 (RepeatFlag reps) f) = + if not (p boxid)+ then identity_transformer b+ else f $+ \namespace ws c -> do+ outputs <- loopM reps ws+ ((without_controls_if ncf) .+ (with_controls c) .+ ((if inv then flip reverse_generic (endpoints_of_wires_in_arity a2 ws2) else id) $+ (unbox_recursive_filtered p) $+ (inline_subroutine boxid namespace)))+ return (outputs, c)+unbox_recursive_filtered_transformer _ x = identity_transformer x++-- | Recursively unbox all subroutines satisfying a given predicate.+unbox_recursive_filtered_unary :: (QCData x, QCData y) => (BoxId -> Bool) -> (x -> Circ y) -> (x -> Circ y)+unbox_recursive_filtered_unary p = transform_unary (unbox_recursive_filtered_transformer p)++-- | Recursively unbox all subroutines satisfying a given predicate.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox_recursive_filtered :: (QCData x) => (BoxId -> Bool) -> Circ x -> Circ x+-- > unbox_recursive_filtered :: (QCData x, QCData y) => (BoxId -> Bool) -> (x -> Circ y) -> (x -> Circ y)+-- +-- and so forth.+unbox_recursive_filtered :: (QCData x, QCData y, QCurry qfun x y) => (BoxId -> Bool) -> qfun -> qfun+unbox_recursive_filtered p = qcurry . (unbox_recursive_filtered_unary p) . quncurry++-- | Recursively unbox all subroutines of a circuit.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > unbox_recursive :: (QCData x) => Circ x -> Circ x+-- > unbox_recursive :: (QCData x, QCData y) => (x -> Circ y) -> (x -> Circ y)+-- > unbox_recursive :: (QCData x, QCData y, QCData z) => (x -> y -> Circ z) -> (x -> y -> Circ z)+-- +-- and so forth.+unbox_recursive :: (QCData x, QCData y, QCurry qfun x y) => qfun -> qfun+unbox_recursive = unbox_recursive_filtered (const True)
binary file changed (absent → 3311 bytes)
binary file changed (absent → 1496 bytes)
binary file changed (absent → 1572 bytes)
binary file changed (absent → 3957 bytes)
binary file changed (absent → 1738 bytes)
binary file changed (absent → 3363 bytes)
binary file changed (absent → 1963 bytes)
binary file changed (absent → 1561 bytes)
binary file changed (absent → 1804 bytes)
binary file changed (absent → 2532 bytes)
binary file changed (absent → 1360 bytes)
binary file changed (absent → 4858 bytes)
binary file changed (absent → 8371 bytes)
binary file changed (absent → 887 bytes)
binary file changed (absent → 5013 bytes)
binary file changed (absent → 5628 bytes)
binary file changed (absent → 1304 bytes)
binary file changed (absent → 7265 bytes)
binary file changed (absent → 13792 bytes)
binary file changed (absent → 1544 bytes)
binary file changed (absent → 2929 bytes)
binary file changed (absent → 7580 bytes)
binary file changed (absent → 3038 bytes)
binary file changed (absent → 3642 bytes)
binary file changed (absent → 2484 bytes)
binary file changed (absent → 1151 bytes)
binary file changed (absent → 1433 bytes)
binary file changed (absent → 1940 bytes)
binary file changed (absent → 2085 bytes)
binary file changed (absent → 1308 bytes)
binary file changed (absent → 4450 bytes)
binary file changed (absent → 1716 bytes)
binary file changed (absent → 4991 bytes)
binary file changed (absent → 1878 bytes)
binary file changed (absent → 963 bytes)
binary file changed (absent → 1551 bytes)
binary file changed (absent → 14953 bytes)
binary file changed (absent → 2436 bytes)
binary file changed (absent → 2433 bytes)
binary file changed (absent → 3058 bytes)
binary file changed (absent → 4290 bytes)
binary file changed (absent → 4864 bytes)
binary file changed (absent → 3545 bytes)
binary file changed (absent → 1268 bytes)
binary file changed (absent → 2669 bytes)
binary file changed (absent → 2247 bytes)
binary file changed (absent → 1738 bytes)
binary file changed (absent → 1002 bytes)
binary file changed (absent → 3523 bytes)
binary file changed (absent → 2403 bytes)
binary file changed (absent → 12639 bytes)
binary file changed (absent → 19618 bytes)
binary file changed (absent → 6734 bytes)
binary file changed (absent → 1318 bytes)
binary file changed (absent → 4358 bytes)
binary file changed (absent → 5276 bytes)
binary file changed (absent → 1167 bytes)
binary file changed (absent → 1152 bytes)
binary file changed (absent → 913 bytes)
binary file changed (absent → 1664 bytes)
binary file changed (absent → 2436 bytes)
binary file changed (absent → 3399 bytes)
binary file changed (absent → 3310 bytes)
binary file changed (absent → 3209 bytes)
binary file changed (absent → 7982 bytes)
binary file changed (absent → 1768 bytes)
binary file changed (absent → 8963 bytes)
binary file changed (absent → 4107 bytes)
binary file changed (absent → 3598 bytes)
binary file changed (absent → 5029 bytes)
binary file changed (absent → 1052 bytes)
@@ -0,0 +1,136 @@+-- The name of the package.+name: quipper-libraries++-- The package version. See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.9.0.0++-- A short (one-line) description of the package.+synopsis: The standard libraries for Quipper.++-- A longer description of the package.+description: ++ This package provides the standard libraries for Quipper, the+ embedded functional programming language for quantum computing.+ This includes libraries for quantum integer and fixed-point+ arithmetic, decomposition into standard gate sets, the quantum+ Fourier transform, simulation, among other things.+ +-- URL for the project homepage or repository.+homepage: http://www.mathstat.dal.ca/~selinger/quipper/++-- The license under which the package is released.+license: BSD3++-- The file containing the license text.+license-file: COPYRIGHT++-- The package author(s).+author: Alexander S. Green, Peter LeFanu Lumsdaine,+ Neil J. Ross, Peter Selinger, Benoît Valiron++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer: selinger@mathstat.dal.ca++-- A copyright notice.+copyright: Copyright (c) 2011-2019. All rights reserved.++-- A classification category for future use by the package catalogue+-- Hackage. These categories have not yet been specified, but the+-- upper levels of the module hierarchy make a good start.+category: Quipper++-- The type of build used by this package.+build-type: Custom++-- Constraint on the version of Cabal needed to build this package.+cabal-version: >= 1.8++-- A list of additional files to be included in source distributions+-- built with setup sdist.+extra-source-files: images/*.png ChangeLog++library+ -- Modules exported by the library.+ exposed-modules:+ Quipper.Libraries.Arith+ Quipper.Libraries.Decompose+ Quipper.Libraries.Decompose.CliffordT+ Quipper.Libraries.Decompose.GateBase+ Quipper.Libraries.Decompose.Legacy+ Quipper.Libraries.DynamicLiftings+ Quipper.Libraries.GateDecompositions+ Quipper.Libraries.FPReal+ Quipper.Libraries.QFT+ Quipper.Libraries.QFTAdd+ Quipper.Libraries.Qram+ Quipper.Libraries.QuantumIf+ Quipper.Libraries.QuipperASCIIParser.Parse+ Quipper.Libraries.QuipperASCIIParser.CircInfo+ Quipper.Libraries.QuipperASCIIParser.ASCIICirc+ Quipper.Libraries.QuipperASCIIParser+ Quipper.Libraries.Qureg+ Quipper.Libraries.Simulation+ Quipper.Libraries.Simulation.ClassicalSimulation+ Quipper.Libraries.Simulation.CliffordSimulation+ Quipper.Libraries.Simulation.QuantumSimulation+ Quipper.Libraries.Synthesis+ Quipper.Libraries.Unboxing+ Quipper.Libraries.ClassicalOptim.Circuit+ Quipper.Libraries.ClassicalOptim.AlgExp+ Quipper.Libraries.ClassicalOptim+ Quipper.Libraries.ClassicalOptim.QuipperInterface+ Quipper.Libraries.ClassicalOptim.Simplification+ + -- Modules included in this library but not exported.+ other-modules: + + -- Other library packages from which modules are imported.+ build-depends: base >= 4.5 && < 5,+ containers >= 0.5.2.1,+ mtl >= 2.1.2,+ random >= 1.0.1.1,+ newsynth >= 0.3.0.1,+ deepseq >= 1.4,+ quipper-utils >= 0.9.0.0,+ quipper-language >= 0.9.0.0++test-suite quickcheck+ type: exitcode-stdio-1.0+ main-is: Quipper/Libraries/ClassicalOptim/QuickCheckMain.hs+ other-modules:+ Quipper.Libraries.Arith+ Quipper.Libraries.ClassicalOptim.Circuit+ Quipper.Libraries.ClassicalOptim.QuickCheckArith+ Quipper.Libraries.ClassicalOptim.QuickCheckAlgExp+ Quipper.Libraries.ClassicalOptim.AlgExp+ Quipper.Libraries.ClassicalOptim.QuickCheck+ Quipper.Libraries.ClassicalOptim.QuipperInterface+ Quipper.Libraries.ClassicalOptim.Simplification+ Quipper.Libraries.Simulation+ Quipper.Libraries.Simulation.ClassicalSimulation+ Quipper.Libraries.Simulation.CliffordSimulation+ Quipper.Libraries.Simulation.QuantumSimulation++ build-depends:+ base,+ containers,+ mtl,+ deepseq,+ newsynth,+ random,+ QuickCheck >= 2.12.5,+ quipper-utils >= 0.9.0.0,+ quipper-language >= 0.9.0.0++custom-setup+ setup-depends: base >= 4.5,+ superdoc >= 0.1,+ Cabal >= 1.24,+ quipper-cabal >= 0.9.0.0