quipper-rendering (empty) → 0.8
raw patch · 8 files changed
+3366/−0 lines, 8 filesdep +basedep +containersdep +directorysetup-changed
Dependencies added: base, containers, directory, easyrender, mtl, primes, process, quipper-core, random, template-haskell, unix
Files
- COPYRIGHT +66/−0
- README +428/−0
- Setup.hs +2/−0
- quipper-rendering.cabal +41/−0
- src/Libraries/Auxiliary.hs +926/−0
- src/Libraries/CommandLine.hs +72/−0
- src/Libraries/PortableSignals.hs +139/−0
- src/Quipper/Printing.hs +1692/−0
+ COPYRIGHT view
@@ -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-2016. 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.+----------------------------------------------------------------------
+ README view
@@ -0,0 +1,428 @@+This file is part of Quipper. Copyright (C) 2011-2016. Please see the+file COPYRIGHT for a list of authors, copyright holders, licensing,+and other details. All rights reserved.++======================================================================++This is Quipper.++Copyright, License, and Disclaimers+===================================++See the file COPYRIGHT.++Installing the necessary components +===================================++For installing on Linux, Mac OS X, and other Unix-like systems: please+first see the instructions in INSTALLING, then continue to read below.++For installing on Windows: please first see the instructions in+INSTALLING.windows, then continue to read below.++Configuring the software environment+=====================================++Before you can compile Quipper, you have to install some Haskell+libraries:++ * random >= 1.0.1.1+ * mtl >= 2.1.2+ * primes >= 0.2.1.0+ * Lattices >= 0.0.1 (note: "Lattices" must be capitalized)+ * zlib >= 0.5.4.1+ * easyrender >= 0.1.0.0+ * fixedprec >= 0.2.1.0+ * newsynth >= 0.3.0.1+ * containers >= 0.5.2.1+ * set-monad >= 0.1.0.0+ * QuickCheck >= 2.6++This can be done using Cabal. On the command line, use the+following commands:++cabal update++then:++cabal install random+cabal install mtl+cabal install primes+cabal install Lattices+cabal install zlib+cabal install easyrender+cabal install fixedprec+cabal install newsynth+cabal install containers+cabal install set-monad+cabal install QuickCheck++Note: When upgrading from a previous version of Quipper, please ensure+that the fixedprec library is version 0.2.1.0 or newer; Quipper 0.6+will not work with earlier versions of fixedprec. Also ensure that the+newsynth library has been compiled against the same version of+fixedprec as Quipper. If you get strange error messages related to+fixedprec, try++cabal install fixedprec +cabal install newsynth --reinstall++You now have all the necessary Haskell libraries.++Special note for GHC 7.4.2:+===========================++The combination of GHC 7.4.2 and Template Haskell 2.8.0.0 is not+possible, because it triggers a GHC bug. If you get a compilation+error of the form: "ghc: panic! (the 'impossible' happened)", follow+these steps:++# Remove Template Haskell 2.8.0.0:++ghc-pkg unregister --force template-haskell-2.8.0.0++# Reinstall QuickCheck (because of a broken dependency). This will+# also install template-haskell-2.7.0.0:++cabal install QuickCheck++Special note for GHC 7.10.*:+============================++Quipper will not work with ghc 7.10. Please use ghc 7.8 or earlier, or+ghc 8.0 or later.++Browsing the documentation and source code+==========================================++While it is possible the browse the Quipper source code in a text+editor, it is much nicer to browse the documented source by pointing+your web browser to doc/frames.html in this Quipper distribution. The+documented source is fully cross-referenced and indexed, with links to+color-coded raw source files.+++Building the documentation+==========================++Please note: our documentation uses special mark-up and requires+custom tools to be built. Therefore it is not currently possible for+users to re-build the documentation.+++Building the included algorithms and programs+=============================================++Compilation, and execution of generated code, are done from the command+line interface.++The code can be built with "make" from the main directory. This will+build an executable file in each Algorithm subdirectory, which can be+run with various command line parameters to do different things. Run+each command with option --help to see a summary of the usage+information.++In the following, we describe the set of options for the algorithms+that were implemented.+++Running the bwt program+=======================++Usage for Binary Welded Tree algorithm:+---------------------------------------++Usage: bwt [OPTION...]+ -h --help print usage info and exit+ -C --circuit output the whole circuit (default)+ -O --oracle output only the oracle+ -K --oraclec output the "classical" oracle as a classical circuit+ -G --graph print colored graph computed from oracle+ -S --simulate run simulations of some circuit fragments for tree height n+ -f <format> --format=<format> output format for circuits (default: preview)+ -g <gatebase> --gatebase=<gatebase> type of gates to decompose into (default: logical)+ -o <oracle> select oracle to use (default: orthodox)+ -n <n> --height=<n> set tree height (positive; default 5)+ -c <c> --color=<c> color to use with --oracle (0..3, default 0)+ -s <s> --repeats=<s> set parameter s (iteration count; default 1)+ -l --large set large problem size: n=300, s=336960+ -t <dt> --dt=<dt> set parameter dt (simulation time step; default pi/180)+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+Possible values for oracle are: orthodox, simple, blackbox, classical, template, optimized.++Examples of command line options:+---------------------------------++* Show the complete circuit for the BWT algorithm using the+ "orthodox" (official GFI) oracle, with n=5 and s=1:++ ./bwt -C -o orthodox -n 5 -s 1++ (One can point out the different parts of the algorithm: 8 oracle+ calls, and 4 very short diffusion steps).++* Show the same, using the "Template Haskell" oracle: this oracle is+ much larger, but automatically generated from classical code (and+ completely unoptimized):++ ./bwt -C -o template -n 5 -s 1++ The "template oracle" is defined in BWT/Template.hs. See the+ documentation of the module Quipper/CircLifting for how it works.++* Show the graph of the BWT algorithm, which is obtained by+ simulating the orthodox oracle (and therefore offers some evidence+ for the correctness of the oracle implementation):++ ./bwt -G -o orthodox -n 5++* Show the orthodox oracle for n=300. Note that this will result in a+ big file. One has to zoom in substantially to see gates. ++ ./bwt -O -o orthodox -n 300++* Show the complete circuit for the BWT algorithm, but decompose+ everything into binary gates:++ ./bwt -C -o orthodox -n 5 -s 1 -g binary ++* Show the oracle from Figure 1a (alternate oracle).++ ./bwt -C -o figure1a++* The same, decomposed into binary+Toffoli gates, or binary gates+ only, respectively:++ ./bwt -C -o figure1a -g toffoli+ ./bwt -C -o figure1a -g binary++* Show gate counts for BWT algorithm with n=300 and s=1, using+ "orthodox" oracle:++ ./bwt -C -o orthodox -n 300 -s 1 -f gatecount++* Show gate counts for same, after decomposition to binary gates:++ ./bwt -C -o orthodox -n 300 -s 1 -f gatecount -g binary ++Obviously, most other combinations of command line options are also+possible, for example: decompose to toffoli gates and then simulate+and show the graph. Some other combinations are not legal: for+example, decomposing to binary gates and then simulating. (The+classical simulator will complain that the circuit is not boolean; it+contains "V" gates).++* Similarly, one can run demos for the triangle finding+ algorithm using various command line options. ++Note that the triangle finding algorithm is not a deliverable; it is a+work in progress. The only implemented algorithm that is officially a+deliverable is the "orthodox" BWT implementation in BWT.BWT.++Running the bf program+======================++Usage for the Boolean Formula algorithm:+----------------------------------------++Usage: bf [OPTION...]+ -C --circuit output the whole circuit (default)+ -D --demo run a demo of the circuit+ -H --hexboard output a representation of the initial state of the given oracle, i.e. the game played so far+ -p <part> --part=<part> which part of the circuit to use (default: whole)+ -o <oracle> --oracle=<oracle> which oracle to use (default: small)+ -m <moves> --moves=<moves> which moves have already been made (default: [])+ -f <format> --format=<format> output format for circuits (default: _preview)+ -d --dummy set to only use a dummy HEX gate instead of the full hex circuit+ -h --help print usage info and exit+ -g <gatebase> --gatebase=<gatebase> type of gates to decompose the output circuit into (default: logical)+Possible values for part are: whole, u, oracle, hex, checkwin_red, diffuse, walk, undo_oracle.+Possible values for oracle are: 9by7, small, custom x y t.+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.++Running the cl program+======================++Usage for the Class Number algorithm:+-------------------------------------++Usage: cl [OPTION...]+ -h --help print usage info and exit+ -f <format> --format=<format> output format for circuits (default: ASCII)+ -g <gatebase> --gatebase=<gatebase> gates to decompose into (default: Logical)+ -1 output the circuit for stage 1 of the algorithm (default)+ -4 output the circuit for stage 4 of the algorithm+ -S <subroutine> --sub=<subroutine> output the circuit for a specific subroutine+ -R --regulator classically, find the regulator, given Δ+ -F classically, find the fundamental unit, given Δ+ -P classically, find the fundamental solution of Pell’s equation, given Δ+ -d <N> --delta=<N> discriminant Δ (a.k.a. D) (default: 28)+ -s <N> --ss=<N> estimated bound on period S, for stage 1 (default: 2)+ -i <N> estimated bound on log_2 S, for stage 1 (default: 1)+ -r <N> --rr=<N> approximate regulator R, for stage 4 (default: 12.345)+ -q <N> The parameter q, for stage 4 (default: 4)+ -k <N> The parameter k, for stage 4 (default: 3)+ -n <N> The parameter n, for stage 4 (default: 3)+ -m <N> The parameter m, for stage 4 (default: 5)+ --seed=<N> Random seed (0 for seed from time)(default: 1)+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+Possible values for subroutine are: rho, rhoinv, normalize, dotprod, starprod, fn.++Running the gse program+=======================++Usage for Ground State Estimation algorithm:+--------------------------------------------++Usage: gse [OPTION...]+ -h --help print usage info and exit+ -C --circuit output the whole circuit (default)+ -T <indices> --template=<indices> output a particular circuit template+ -f <format> --format=<format> output format for circuits (default: Preview)+ -g <gatebase> --gatebase=<gatebase> gates to decompose into (default: Logical)+ -m <N> --orbitals=<N> number of orbitals (default: 4)+ -o <N> --occupied=<N> number of occupied orbitals (default: 2)+ -b <N> --precision=<N> number of precision qubits (default: 3)+ -D <energy> --delta_e=<energy> energy range (default: 6.5536)+ -E <energy> --e_max=<energy> maximum energy (default: -3876.941)+ --n0=<N> use N_k = n0 * 2^k (default: N_k = 1)+ -l --large set large problem size (m=208, o=84, b=12, n0=100)+ -x --orthodox use the Coulomb operator of Whitman et al.+ --h1=<file> filename for one-electron data (default: "h_1e_ascii")+ --h2=<file> filename for two-electron data (default: "h_2e_ascii")+ -d <file> --datadir=<file> directory for one- and two-electron data (default: current)+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+Indices can be specified as p,q or p,q,r,s (with no spaces)++Running the qls program+=======================++Usage for Quantum Linear Systems algorithm:+-------------------------------------------++Usage: qls [OPTION...]+ -h --help print usage info and exit+ -C --circuit output the whole circuit (default)+ -O <name> --oracle=<name> output only the oracle <name> (default: r) + -f <format> --format=<format> output format for circuits (default: gatecount)+ -g <gatebase> --gatebase=<gatebase> type of gates to decompose into (default: logical)+ -o <oracle> select oracle implementation to use (default: blackbox)+ -p <param> --param=<param> choose a set of parameters (default: dummy).+ -P <n> --peel=<n> peel <n> layers of boxed subroutines (default: 0).+Possible values for format are: ascii, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+Possible values for oracle implementation are: matlab, blackbox.+Possible values for param are: dummy, small, large.+Possible values for oracle are: r, b, A[band][t|f]. E.g. "-OA1t" asks for band 1 with boolean argument True. For all three oracles, the factors are set up to 1.0.++Running the tf program+======================++Usage for Triangle Finding algorithm:+-------------------------------------++Usage: tf [OPTION...]+ -h --help print usage info and exit+ -f <format> --format=<format> output format for circuits (default: preview)+ -g <gatebase> --gatebase=<gatebase> type of gates to decompose into (default: logical)+ -l <l> --l=<l> parameter l (default: 4)+ -n <n> --n=<n> parameter n (default: 3)+ -r <r> --r=<r> parameter r (default: 2)+ -C --QWTFP output the whole circuit (default)+ -O --oracle output only the oracle+ -s <subroutine> --subroutine=<subroutine> output the chosen subroutine (default: adder)+ -Q use alternative qRAM implementation+ -o <oracle> select oracle to use (default: blackbox)+ -A --arith test/simulate the arithmetic routines+ -T --oracletest test/simulate the oracle+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+Possible values for oracle are: orthodox, blackbox.+Possible values for subroutine are: zero, initialize, hadamard, setup, qwsh, diffuse, fetcht, storet, fetchstoret, fetche, fetchstoree, update, swap, a15, a16, a17, a18, gcqwalk, gcqwstep, convertnode, testequal, pow17, mod3, sub, add, mult.++Running the usv program+=======================++Usage for Unique Shortest Vector algorithm:+-------------------------------------------++Usage: usv [OPTION...]+ -h --help print usage info and exit+ -f <format> --format=<format> output format for circuits (default: eps)+ -g <gatebase> --gatebase=<gatebase> type of gates to decompose into (default: logical)+ -n <n> --n=<n> parameter n (default: 5)+ -b <b> --b=<b> parameter b (default: 5X5 with entries = 1)+ -s <s> --s=<s> Random number generator seed s (default: 1)+ -F output subroutine f (depends on b).+ -G output subroutine g (depends on b).+ -H output subroutine h (depends on n).+ -U output algorithm 1 (depends on b).+ -Q output algorithm 2 (depends on b).+ -R output algorithm 3 (depends on b).+ -T output algorithm 4 (depends on n).+ -S output sieving subroutine (depends on n).+ -D output algorithm 5 (depends on n).+ -t test subroutine h (depends on n).+Possible values for format are: eps, pdf, ps, postscript, ascii, preview, gatecount.+Possible values for gatebase are: logical, binary, toffoli, cliffordt_old, cliffordt, cliffordt_keepphase, standard, strict, approximate, approximate_keepphase, exact, trimcontrols.+++Invoking the Quipper compiler+=============================++The Quipper compiler is called "quipper", and is located in the+directory quipper/scripts. The easiest way to use it is to add the+"scripts" directory to the environment variable PATH. If you move the+quipper script around, make sure to keep the other scripts in the same+directory as the quipper script, and to update QUIPPER_BASE in the+"quipper" and "quipperi" scripts to point to the directory where the+Quipper sources are located. On the Windows operating system, you+should use "quipper.bat"; on all other operating systems, just+"quipper" will do. ++In reality, the "quipper" script is a wrapper around the GHC Haskell+compiler, providing some pre-processing and setting required+compilation options. There is also a "quipperi" script for an+interactive version of the compiler, which is akin to "ghci".++To try this out, the directory "tests" contains various small+stand-alone programs that can be compiled with Quipper, and are useful+for demonstrating the basic Quipper idiom. Each program can be+compiled and run like this:++For example:++# to compile and run on Unix (or on Unix with the MSYS/bash):+quipper And_gate.hs+./And_gate++# to compile and run on Windows with cmd.exe:+quipper.bat And_gate.hs+And_gate++Note that there is also a Makefile, so "make" can be used to build the+programs as well.++If the previewer is working properly, the circuit should show up in+Acrobat Reader. If not, either change "Preview" to "EPS" in the file+(for PostScript output), or trouble-shoot the previewer installation+(if you are on Windows, see INSTALLING) and/or contact Benoit Valiron+<benoit.valiron@monoidal.net> or Peter Selinger+<selinger@mathstat.dal.ca> for help.++The naming of built-in gates and many operators can be found out by+looking at the documentation of the "Quipper" module (the main public+interface of the Quipper system).+++Troubleshooting Guidelines+==========================++In case of problems, please contact++ * Benoit Valiron <benoit.valiron@monoidal.net>+ * Peter Selinger <selinger@mathstat.dal.ca>
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ quipper-rendering.cabal view
@@ -0,0 +1,41 @@+name: quipper-rendering+version: 0.8+synopsis: An embedded, scalable functional programming language for quantum computing.+description:+ Quipper is an embedded, scalable functional programming language for quantum computing. It provides, among other things:+ .+ * A high-level circuit description language. This includes gate-by-gate descriptions of circuit fragments, as well as powerful operators for assembling and manipulating circuits.+ .+ * A monadic semantics, allowing for a mixture of procedural and declarative programming styles.+ .+ * Built-in facilities for automatic synthesis of reversible quantum circuits, including from classical code.+ .+ * Support for hierarchical circuits.+ .+ * Extensible quantum data types.+ .+ * Programmable circuit transformers.+ .+ * Support for three execution phases: compile time, circuit generation time, and circuit execution time. A dynamic lifting operation to allow circuit generation to be parametric on values generated at circuit execution time.+ .+ * Extensive libraries of quantum functions, including: libraries for quantum integer and fixed-point arithmetic; the Quantum Fourier transform; an efficient Qram implementation; libraries for simulation of pseudo-classical circuits, Stabilizer circuits, and arbitrary circuits; libraries for exact and approximate decomposition of circuits into specific gate sets.+ .+ This package contains the rendering part, which has been separated to reduce dependencies in quipper-core.+homepage: http://www.mathstat.dal.ca/~selinger/quipper/+license: BSD3+license-file: COPYRIGHT+author: Applied Communication Sciences+maintainer: leonardo.taglialegne@gmail.com+copyright: Copyright (C) 2012-2013 Applied Communication Sciences. +-- category: +build-type: Simple+extra-source-files: README+cabal-version: >=1.10++library+ exposed-modules: Quipper.Printing+ other-modules: Libraries.CommandLine, Libraries.PortableSignals, Libraries.Auxiliary+ other-extensions: GADTs, RankNTypes, FlexibleInstances, OverlappingInstances, MultiParamTypeClasses, FunctionalDependencies, UndecidableInstances, CPP, StandaloneDeriving, DeriveDataTypeable, ScopedTypeVariables, TypeSynonymInstances, TemplateHaskell, BangPatterns, FlexibleContexts, TypeFamilies, Rank2Types, ExistentialQuantification+ build-depends: base >=4.6 && <4.10, quipper-core >=0.8 && <0.9, random >=1.0 && <1.2, containers >=0.5 && <0.6, unix >=2.6 && <2.8, template-haskell >=2.8 && <2.12, mtl >=2.1 && <2.3, easyrender >=0.1 && <0.2, process >=1.1 && <1.5, directory >=1.2 && <1.3, primes >=0.2 && <0.3+ hs-source-dirs: src+ default-language: Haskell2010
+ src/Libraries/Auxiliary.hs view
@@ -0,0 +1,926 @@+-- This file is part of Quipper. Copyright (C) 2011-2016. Please see the+-- file COPYRIGHT for a list of authors, copyright holders, licensing,+-- and other details. All rights reserved.+-- +-- ======================================================================++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}++-- | This module provides miscellaneous general-purpose auxiliary+-- functions.++module Libraries.Auxiliary (+ -- * List operations+ applyAt,+ overwriteAt,+ has_duplicates,+ substitute,+ + -- * Set and Map related operations+ map_provide,+ intset_inserts,+ intmap_zip,+ intmap_zip_errmsg,+ intmap_map,+ intmap_mapM,+ + -- * XIntMaps+ XIntMap,+ xintmap_delete,+ xintmap_deletes,+ xintmap_insert,+ xintmap_inserts,+ xintmap_lookup,+ xintmap_member,+ xintmap_empty,+ xintmap_freshkey,+ xintmap_freshkeys,+ xintmap_to_intmap,+ xintmap_size,+ xintmap_dirty,+ xintmap_reserves,+ xintmap_unreserves, + xintmap_makeclean,+ + -- * Various map- and fold-like list combinators+ loop,+ loop_with_index,+ fold_right_zip,+ zip_strict,+ zip_strict_errmsg,+ zip_rightstrict,+ zip_rightstrict_errmsg,+ zipWith_strict,+ zipWith_rightstrict,+ + -- * Monadic versions of list combinators+ loopM,+ loop_with_indexM,+ zipRightWithRightStrictM,+ zipRightWithRightStrictM_,+ fold_right_zipM,+ foldRightPairM,+ foldRightPairM_,+ sequence_right,+ sequence_right_,+ + -- * Loops+ -- $LOOPS+ for,+ endfor,+ foreach,+ + -- * Operations for monads+ mmap,+ monad_join1,+ + -- * Operations for disjoint unions+ map_either,+ map_eitherM,+ + -- * Operations for tuples+ map_pair,+ map_pairM,+ + -- * Arithmetic operations+ int_ceiling,+ + -- * Bit vectors+ Boollist(..),+ boollist_of_int_bh,+ boollist_of_int_lh,+ int_of_boollist_unsigned_bh,+ int_of_boollist_signed_bh,+ bool_xor,+ boollist_xor,+ + -- * Formatting of lists and strings+ string_of_list,+ optional,+ + -- * Lists optimized for fast concatenation+ BList,+ blist_of_list,+ list_of_blist,+ (+++),+ blist_empty,+ blist_concat,+ + -- * Strings optimized for fast concatenation+ Strbuf,+ strbuf_of_string,+ string_of_strbuf,+ strbuf_empty,+ strbuf_concat,+ + -- * The identity monad+ Id(..),+ + -- * Identity types+ Identity,+ reflexivity,+ symmetry,+ transitivity,+ identity,+ + -- * Error messages+ ErrMsg,+ + -- * The Curry type class+ Curry (..)+ ) where++-- import other stuff+import Data.List (foldl')++import Data.Set (Set)+import qualified Data.Set as Set++import Data.Map (Map)+import qualified Data.Map as Map++import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet++import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap++import qualified Data.Traversable as Traversable++import Control.Applicative (Applicative(..))+import Control.Monad (liftM, ap)++-- ----------------------------------------------------------------------+-- * List operations++-- | Apply a function to a specified position in a list.+applyAt :: Int -> (a -> a) -> [a] -> [a]+applyAt _ _ [] = []+applyAt 0 f (x:xs) = (f x):xs+applyAt n f (x:xs) = x:(applyAt (n-1) f xs)++-- | Overwrite an element at a specified position in a list.+overwriteAt :: Int -> a -> [a] -> [a]+overwriteAt n a = applyAt n (const a)++-- | Check whether a list has duplicates.+has_duplicates :: Ord a => [a] -> Bool+has_duplicates list = aux list (Set.empty) where+ aux [] _ = False+ aux (h:t) set = if Set.member h set then True else aux t (Set.insert h set)++-- | @'substitute' string character replacement@: +-- Replace the first occurrence of /character/ in /string/ by /replacement/.+substitute :: (Eq a) => [a] -> a -> [a] -> [a]+substitute string character replacement = + case break (== character) string of+ (x, []) -> x+ (x, h:y) -> x ++ replacement ++ y++-- ----------------------------------------------------------------------+-- * Set related operations++-- | Insert the elements of a list in an 'IntSet' (cf. 'IntSet.insert').+intset_inserts :: [Int] -> IntSet -> IntSet+intset_inserts list set =+ foldl' (\t x -> IntSet.insert x t) set list+++-- ----------------------------------------------------------------------+-- * Map related operations++-- | Insert the given key-value pair in a 'Map', but only if the given+-- key is not already present. If the key is present, keep the old+-- value.+map_provide :: Ord k => k -> a -> Map k a -> Map k a+map_provide = Map.insertWith (\x y -> y)++-- | Take two 'IntMap's /m/[sub 1] and /m/[sub 2], and form a new+-- 'IntMap' whose domain is that of /m/[sub 2], and whose value at /k/+-- is the pair (/m/[sub 1] ! /k/, /m/[sub 2] ! /k/). It is an error if+-- the domain of /m/[sub 2] is not a subset of the domain of /m/[sub 1].+intmap_zipright :: IntMap x -> IntMap y -> IntMap (x, y)+intmap_zipright m1 m2 = m where+ m = IntMap.mapWithKey f m2+ f k y = case IntMap.lookup k m1 of+ Just x -> (x, y)+ Nothing -> error "intmap_zipright: shape mismatch"+ +-- | Take two 'IntMap's with the same domain, and form a new 'IntMap'+-- whose values are pairs. It is an error if the two inputs don't have+-- identical domains.+intmap_zip :: IntMap x -> IntMap y -> IntMap (x, y)+intmap_zip m1 m2 = intmap_zip_errmsg m1 m2 "intmap_zip: shape mismatch"+ +-- | Like 'intmap_zip', but also takes an error message to use in case of+-- domain mismatch.+intmap_zip_errmsg :: IntMap x -> IntMap y -> String -> IntMap (x, y)+intmap_zip_errmsg m1 m2 errmsg = + if all (\k -> IntMap.member k m2) (IntMap.keys m1) + then intmap_zipright m1 m2+ else error errmsg+ +-- | Map a function over all values in an 'IntMap'.+intmap_map :: (x -> y) -> IntMap x -> IntMap y+intmap_map = IntMap.map++-- | Monadic version of 'intmap_map'. Map a function over all values+-- in an 'IntMap'.+intmap_mapM :: (Monad m) => (x -> m y) -> IntMap x -> m (IntMap y)+intmap_mapM = Traversable.mapM++-- ----------------------------------------------------------------------+-- * XIntMaps. ++-- | A 'XIntMap' is just like an 'IntMap', except that it supports+-- some additional efficient operations: to find the smallest unused+-- key, to find the set of all keys ever used in the past, and to+-- reserve a set of keys so that they will not be allocated. Moreover,+-- it keeps track of the highest key ever used (whether or not it is+-- still used in the current map).++-- This is implemented as a tuple (/m/, /n/, /free/, /h/), where /m/ is an+-- 'IntMap', /n/ is an integer such that dom /m/ ⊆ [0../n/-1], /free/+-- ⊆ [0../n/-1] \\ dom /m/ is a set of integers not currently reserved+-- or used, and /h/ is the set of all integers used in the past (the+-- set of /touched/ wires).++data XIntMap a = XIntMap !(IntMap a) !Int !IntSet !IntSet++instance (Show a) => Show (XIntMap a) where+ show = show . xintmap_to_intmap+ +-- | Delete a key from the 'XIntMap'.+xintmap_delete :: Int -> XIntMap a -> XIntMap a+xintmap_delete k (XIntMap m n free h) = (XIntMap m' n free' h) where+ m' = IntMap.delete k m+ free' = IntSet.insert k free+ +-- | Delete a list of keys from a 'XIntMap'.+xintmap_deletes :: [Int] -> XIntMap a -> XIntMap a+xintmap_deletes list map =+ foldl' (\map k -> xintmap_delete k map) map list++-- | Insert a new key-value pair in the 'XIntMap'. +xintmap_insert :: Int -> a -> XIntMap a -> XIntMap a+xintmap_insert k v (XIntMap m n free h) = (XIntMap m' n' free' h') where+ m' = IntMap.insert k v m+ h' = IntSet.insert k h+ n' = max n (k+1)+ free' = IntSet.delete k (intset_inserts [n..n'-1] free)++-- | Insert a list of key-value pairs in the 'XIntMap'.+xintmap_inserts :: [(Int,a)] -> XIntMap a -> XIntMap a+xintmap_inserts list map =+ foldl' (\map (k,v) -> xintmap_insert k v map) map list++-- | Look up the value at a key in the 'XIntMap'. Return 'Nothing' if+-- not found.+xintmap_lookup :: Int -> XIntMap a -> Maybe a+xintmap_lookup k (XIntMap m n free h) =+ IntMap.lookup k m++-- | Check whether the given key is in the 'XIntMap'.+xintmap_member :: Int -> XIntMap a -> Bool+xintmap_member k (XIntMap m n free h) =+ IntMap.member k m++-- | The empty 'XIntMap'.+xintmap_empty :: XIntMap a+xintmap_empty = (XIntMap m n free h) where+ m = IntMap.empty+ n = 0+ free = IntSet.empty+ h = IntSet.empty++-- | Return the first free key in the 'XIntMap', but without actually+-- using it yet.+xintmap_freshkey :: XIntMap a -> Int+xintmap_freshkey (XIntMap m n free h) = + if IntSet.null free then n else IntSet.findMin free++-- | Return the next /k/ unused keys in the 'XIntMap', but without+-- actually using them yet.+xintmap_freshkeys :: Int -> XIntMap a -> [Int]+xintmap_freshkeys k (XIntMap m n free h) = ks1 ++ ks2 where+ ks1 = take k (IntSet.elems free)+ delta = k - (length ks1)+ ks2 = [n .. n+delta-1]++-- | Convert a 'XIntMap' to an 'IntMap'.+xintmap_to_intmap :: XIntMap a -> IntMap a+xintmap_to_intmap (XIntMap m n free h) = m++-- | Return the smallest key never used in the 'XIntMap'.+xintmap_size :: XIntMap a -> Int+xintmap_size (XIntMap m n free k) = n++-- | Return the set of all keys ever used in the 'XIntMap'.+xintmap_touched :: XIntMap a -> IntSet+xintmap_touched (XIntMap m n free h) = h ++-- | A wire is /dirty/ if it is touched but currently free. +xintmap_dirty :: XIntMap a -> IntSet+xintmap_dirty (XIntMap m n free h) = h `IntSet.intersection` free++-- | Reserve a key in the 'XIntMap'. If the key is not free, do+-- nothing. The key must have been used before; for example, this is+-- the case if it was returned by 'xintmap_dirty'.+xintmap_reserve :: Int -> XIntMap a -> XIntMap a+xintmap_reserve k (XIntMap m n free h) = (XIntMap m n free' h) where+ free' = IntSet.delete k free+ +-- | Reserve a set of keys in the 'XIntMap'. For any keys that are not+-- free, do nothing. All keys must have been used before; for example,+-- this is the case if they were returned by 'xintmap_dirty'.+xintmap_reserves :: IntSet -> XIntMap a -> XIntMap a+xintmap_reserves ks (XIntMap m n free h) = (XIntMap m n free' h) where+ free' = free `IntSet.difference` ks++-- | Unreserve a key in the 'XIntMap'. If the key is currently used,+-- do nothing. The key must have been reserved before, and (therefore)+-- must have been used before.+xintmap_unreserve :: Int -> XIntMap a -> XIntMap a+xintmap_unreserve k (XIntMap m n free h) + | IntMap.member k m = (XIntMap m n free h)+ | otherwise = (XIntMap m n free' h)+ where+ free' = IntSet.insert k free++-- | Unreserve a list of keys in the 'XIntMap'. If any key is+-- currently used, do nothing. All keys must have been reserved+-- before, and (therefore) must have been used before.+xintmap_unreserves :: IntSet -> XIntMap a -> XIntMap a+xintmap_unreserves ks map = + IntSet.fold (\k map -> xintmap_unreserve k map) map ks++-- | Make an exact copy of the 'XIntMap', except that the set of+-- touched wires is initially set to the set of used wires. In other+-- words, we mark all free and reserved wires as untouched.+xintmap_makeclean :: XIntMap a -> XIntMap a+xintmap_makeclean (XIntMap m n free h) = (XIntMap m n free h') where+ h' = IntMap.keysSet m++-- ----------------------------------------------------------------------+-- * Map- and fold-like list combinators++-- ** Combinators for looping++-- | Like 'loop', but also pass a loop counter to the function being+-- iterated. Example:+-- +-- > loop_with_index 3 x f = f 2 (f 1 (f 0 x))+loop_with_index :: (Eq int, Num int) => int -> t -> (int -> t -> t) -> t+loop_with_index n x f = aux 0 x+ where+ aux i x = if i == n then x else aux (i+1) (f i x)++-- | Monadic version of 'loop_with_index'. Thus, +-- +-- > loop_with_indexM 3 x0 f+-- +-- will do the following:+-- +-- > do+-- > x1 <- f 0 x0+-- > x2 <- f 1 x1+-- > x3 <- f 2 x2 +-- > return x3+loop_with_indexM :: (Eq int, Num int, Monad m) => int -> t -> (int -> t -> m t) -> m t+loop_with_indexM n x f = aux 0 x+ where+ aux i x =+ if i == n then return x else do+ x <- f i x+ aux (i+1) x++-- | Iterate a function /n/ times. Example: +-- +-- > loop 3 x f = f (f (f x))+loop :: (Eq int, Num int) => int -> t -> (t -> t) -> t+loop n x f = loop_with_index n x (\_ -> f)++-- | Monadic version of 'loop'.+loopM :: (Eq int, Num int, Monad m) => int -> t -> (t -> m t) -> m t+loopM n x f = loop_with_indexM n x (\_ -> f)++-- ** Combinators for sequencing++-- | A right-to-left version of 'sequence': Evaluate each action in the+-- sequence from right to left, and collect the results.+sequence_right :: Monad m => [m a] -> m [a]+sequence_right [] = return []+sequence_right (x:xs) = do+ ys <- sequence_right xs+ y <- x+ return (y:ys)++-- | Same as 'sequence_right', but ignore the result.+sequence_right_ :: Monad m => [m a] -> m ()+sequence_right_ [] = return ()+sequence_right_ (x:xs) = do+ ys <- sequence_right_ xs+ y <- x+ return ()++-- ** Combinators for zipping++-- | A \"strict\" version of 'zip', i.e., raises an error when the+-- lists are not of the same length.+zip_strict :: [a] -> [b] -> [(a, b)]+zip_strict a b = zip_strict_errmsg a b "zip_strict: lists are not of the same length"++-- | Like 'zip_strict', but also takes an explicit error message to+-- use in case of failure.+zip_strict_errmsg :: [a] -> [b] -> String -> [(a, b)]+zip_strict_errmsg [] [] e = []+zip_strict_errmsg (h:t) (h':t') e = (h,h') : zip_strict_errmsg t t' e+zip_strict_errmsg _ _ e = error e++-- | A \"right strict\" version of 'zip', i.e., raises an error when the+-- left list is shorter than the right one. +zip_rightstrict :: [a] -> [b] -> [(a, b)]+zip_rightstrict a b = zip_rightstrict_errmsg a b "zip_rightstrict: list too short"++-- | A version of 'zip_rightstrict' that also takes an explicit error+-- message to use in case of failure.+zip_rightstrict_errmsg :: [a] -> [b] -> String -> [(a, b)]+zip_rightstrict_errmsg _ [] s = []+zip_rightstrict_errmsg (h:t) (h':t') s = (h,h') : zip_rightstrict_errmsg t t' s+zip_rightstrict_errmsg _ _ s = error s++-- | A \"strict\" version of 'zipWith', i.e., raises an error when the+-- lists are not of the same length.+zipWith_strict :: (a -> b -> c) -> [a] -> [b] -> [c]+zipWith_strict f [] [] = []+zipWith_strict f (h:t) (h':t') = f h h' : zipWith_strict f t t'+zipWith_strict f _ _ = error "zipWith_strict: lists are not of the same length"++-- | A \"right strict\" version of 'zipWith', i.e., raises an error when the+-- right list is shorter than the left one.+zipWith_rightstrict :: (a -> b -> c) -> [a] -> [b] -> [c]+zipWith_rightstrict f _ [] = []+zipWith_rightstrict f (h:t) (h':t') = f h h' : zipWith_rightstrict f t t'+zipWith_rightstrict f _ _ = error "zipWith_rightstrict: list too short"++-- | A right-to-left version of 'zipWithM', which is also \"right+-- strict\", i.e., raises an error when the right list is shorter than+-- the left one. Example:+-- +-- > zipRightWithM f [a,b] [x,y] = [f a x, f b y],+-- +-- computed right-to-left.+zipRightWithRightStrictM :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]+zipRightWithRightStrictM f l1 l2 =+ sequence_right $ zipWith_rightstrict f l1 l2++-- | Same as 'zipRightWithM', but ignore the result.+zipRightWithRightStrictM_ :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m ()+zipRightWithRightStrictM_ f l1 l2 =+ sequence_right_ $ zipWith_rightstrict f l1 l2++-- ** Combinators combining mapping with folding++-- | Fold over two lists with state, and do it right-to-left. For example,+-- +-- > foldRightPairM (w0, [1,2,3], [a,b,c]) f+-- +-- will do the following:+-- +-- > do+-- > w1 <- f (w0, 3, c)+-- > w2 <- f (w1, 2, b)+-- > w3 <- f (w2, 1, a)+-- > return w3+foldRightPairM :: (Monad m) => (w, [a], [b]) -> ((w, a, b) -> m w) -> m w+foldRightPairM (w, [], _) f = return w+foldRightPairM (w, _, []) f = return w+foldRightPairM (w, a:as, b:bs) f = do+ w <- foldRightPairM (w, as, bs) f+ w <- f (w, a, b)+ return w++-- | Like 'foldRightPairM', but ignore the final result.+foldRightPairM_ :: (Monad m) => (w, [a], [b]) -> ((w, a, b) -> m w) -> m ()+foldRightPairM_ x f = do+ foldRightPairM x f+ return ()++-- | Combine right-to-left zipping and folding. Example:+-- +-- > fold_right_zip f (w0, [a,b,c], [x,y,z]) = (w3, [a',b',c'])+-- > where f (w0,c,z) = (w1,c')+-- > f (w1,b,y) = (w2,b')+-- > f (w2,a,x) = (w3,a')+fold_right_zip :: ((w, a, b) -> (w, c)) -> (w, [a], [b]) -> (w, [c])+fold_right_zip f (w, [], []) = (w, [])+fold_right_zip f (w, a:bb, x:yy) = (w2, a':bb')+ where+ (w1, bb') = fold_right_zip f (w, bb, yy)+ (w2, a') = f (w1, a, x)+fold_right_zip f _ = error "fold_right_zip"++-- | Monadic version of 'fold_right_zip'.+fold_right_zipM ::+ (Monad m) => ((w, a, b) -> m(w, c)) -> (w, [a], [b]) -> m(w, [c])+fold_right_zipM f (w, [], []) = return (w, [])+fold_right_zipM f (w, a:bb, x:yy) = do+ (w1, bb') <- fold_right_zipM f (w, bb, yy)+ (w2, a') <- f (w1, a, x)+ return (w2, a':bb')+fold_right_zipM f _ = error "fold_right_zipM"++-- ----------------------------------------------------------------------+-- * Loops.++-- $LOOPS We provide a syntax for \"for\"-style loops.++-- | A \"for\" loop. Counts from /a/ to /b/ in increments of /s/.+-- +-- Standard notation: +-- +-- > for i = a to b by s do+-- > commands +-- > end for+-- +-- Our notation: +-- +-- > for a b s $ \i -> do+-- > commands+-- > endfor++for :: Monad m => Int -> Int -> Int -> (Int -> m()) -> m()+for a b s f = if s > 0 then aux a (<= b) else aux a (>= b)+ where+ aux i cond = + if cond i then do+ f i+ aux (i+s) cond+ else+ return ()++-- | Mark the end of a \"for\"-loop. This command actually does+-- nothing, but can be used to make the loop look prettier.+endfor :: Monad m => m()+endfor = return ()++-- | Iterate a parameter over a list of values. It can be used as+-- follows:+-- +-- > foreach [1,2,3,4] $ \n -> do+-- > <<<loop body depending on the parameter n>>>+-- > endfor+-- +-- The loop body will get executed once for each /n/ ∈ {1,2,3,4}.++foreach :: Monad m => [a] -> (a -> m b) -> m ()+foreach l f = mapM_ f l++-- ----------------------------------------------------------------------+-- * Operations for monads++-- | Every monad is a functor. Input a function /f/ : /a/ → /b/ and output+-- /m/ /f/ : /m/ /a/ → /m/ /b/.+mmap :: (Monad m) => (a -> b) -> m a -> m b+mmap f a = a >>= (return . f)++-- | Remove an outer application of a monad from a monadic function.+monad_join1 :: (Monad m) => m (a -> m b) -> a -> m b+monad_join1 mf a = do+ f <- mf+ f a++-- ----------------------------------------------------------------------+-- * Operations for disjoint unions++-- | Take two functions /f/ : /a/ → /b/ and /g/ : /c/ → /d/, and return+-- /f/ ⊕ /g/ : /a/ ⊕ /c/ → /c/ ⊕ /d/.+map_either :: (a -> b) -> (c -> d) -> Either a c -> Either b d+map_either f g (Left x) = Left (f x)+map_either f g (Right x) = Right (g x)++-- | Monadic version of 'map_either'.+map_eitherM :: (Monad m) => (a -> m b) -> (c -> m d) -> Either a c -> m (Either b d)+map_eitherM f g (Left x) = mmap Left (f x)+map_eitherM f g (Right x) = mmap Right (g x)++-- ----------------------------------------------------------------------+-- * Operations for tuples++-- | Take two functions /f/ : /a/ → /b/ and /g/ : /c/ → /d/, and return+-- /f/ × /g/ : /a/ × /c/ → /c/ × /d/.+map_pair :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)+map_pair f g (x, y) = (f x, g y)++-- | Monadic version of 'mappair'.+map_pairM :: (Monad m) => (a -> m b) -> (c -> m d) -> (a, c) -> m (b, d)+map_pairM f g (a, c) = do+ b <- f a+ d <- g c+ return (b, d)++-- ----------------------------------------------------------------------+-- * Arithmetic operations+ +-- | A version of the 'ceiling' function that returns an 'Integer'.+int_ceiling :: RealFrac a => a -> Integer+int_ceiling = toInteger . ceiling++-- ----------------------------------------------------------------------+-- * Bit vectors++-- | The type of bit vectors. True = 1, False = 0.+type Boollist = [Bool]++-- | Convert an integer to a bit vector. The first argument is the+-- length in bits, and the second argument the integer to be+-- converted. The conversion is big-headian (or equivalently,+-- little-tailian), i.e., the head of the list holds the integer's most+-- significant digit.+boollist_of_int_bh :: Integral a => Int -> a -> Boollist+boollist_of_int_bh m = reverse . boollist_of_int_lh m++-- | Convert an integer to a bit vector. The first argument is the+-- length in bits, and the second argument the integer to be+-- converted. The conversion is little-headian (or equivalently,+-- big-tailian), i.e., the head of the list holds the integer's least+-- significant digit.+boollist_of_int_lh :: Integral a => Int -> a -> Boollist+boollist_of_int_lh m x | m <= 0 = []+boollist_of_int_lh m x = digit : boollist_of_int_lh (m-1) tail where+ digit = (x `mod` 2 == 1)+ tail = x `div` 2++-- | Convert a bit vector to an integer. The conversion is big-headian+-- (or equivalently, little-tailian), i.e., the head of the list holds+-- the integer's most significant digit. This function is unsigned,+-- i.e., the integer returned is ≥ 0.+int_of_boollist_unsigned_bh :: Integral a => Boollist -> a+int_of_boollist_unsigned_bh v = aux v 0+ where+ aux v acc =+ case v of+ [] -> acc+ digit : vs -> aux vs (2*acc+(if digit then 1 else 0))++-- | Convert a bit vector to an integer, signed.+int_of_boollist_signed_bh :: Integral a => Boollist -> a+int_of_boollist_signed_bh [] = 0+int_of_boollist_signed_bh (False:v) = int_of_boollist_unsigned_bh v+int_of_boollist_signed_bh (True:v) = -1 - int_of_boollist_unsigned_bh (map not v)++-- | Exclusive or operation on booleans.+bool_xor :: Bool -> Bool -> Bool+bool_xor a b = (a /= b)++-- | Exclusive or operation on bit vectors.+boollist_xor :: Boollist -> Boollist -> Boollist+boollist_xor = zipWith bool_xor++-- ----------------------------------------------------------------------+-- * Formatting of lists and strings++-- | A general list-to-string function. Example:+-- +-- > string_of_list "{" ", " "}" "{}" show [1,2,3] = "{1, 2, 3}"+string_of_list :: String -> String -> String -> String -> (t -> String) -> [t] -> String+string_of_list lpar comma rpar nil string_of_elt lst =+ let string_of_tail lst =+ case lst of+ [] -> ""+ h:t -> comma ++ string_of_elt h ++ string_of_tail t+ in+ case lst of+ [] -> nil+ h:t -> lpar ++ string_of_elt h ++ string_of_tail t ++ rpar++-- | @'optional' b s@: if /b/ = 'True', return /s/, else the empty+-- string. This function is for convenience.+optional :: Bool -> String -> String+optional True s = s+optional False s = ""++-- ----------------------------------------------------------------------+-- * Lists optimized for fast concatenation++-- | The type of bidirectional lists. This is similar to [a], but+-- optimized for fast concatenation and appending on both sides.+newtype BList a = BList { getBList :: [a] -> [a] }++-- | Convert a List to a 'BList'.+blist_of_list :: [a] -> BList a+blist_of_list s = BList (\x -> s ++ x)++-- | Convert a 'BList' to a List.+list_of_blist :: BList a -> [a]+list_of_blist buf = getBList buf []++-- | Fast concatenation of 'BList's or string buffers.+(+++) :: BList a -> BList a -> BList a+(+++) buf1 buf2 = BList ((getBList buf1) . (getBList buf2))++-- | The empty 'BList'.+blist_empty :: BList a+blist_empty = BList id++-- | Concatenate a list of 'Blist's.+blist_concat :: [BList a] -> BList a+blist_concat l = foldr (+++) blist_empty l++instance (Show a) => Show (BList a) where+ show bl = show (list_of_blist bl) ++-- ----------------------------------------------------------------------+-- * Strings optimized for fast concatenation++-- | A string buffer holds a string that is optimized for fast+-- concatenation. Note that this is an instance of 'BList', and hence+-- 'BList' operations (in particular '+++') can be applied to string+-- buffers. The following functions are synonyms of the respective+-- 'BList' functions, and are provided for convenience.+type Strbuf = BList Char++-- | Convert a string to a string buffer.+strbuf_of_string :: String -> Strbuf+strbuf_of_string = blist_of_list++-- | Convert a string buffer to a string.+string_of_strbuf :: Strbuf -> String+string_of_strbuf = list_of_blist++-- | The empty string buffer.+strbuf_empty :: Strbuf+strbuf_empty = blist_empty++-- | Concatenate a list of string buffers.+strbuf_concat :: [Strbuf] -> Strbuf+strbuf_concat = blist_concat++-- ----------------------------------------------------------------------+-- * The identity monad+ +-- | The identity monad. Using /m/ = 'Id' gives useful special cases+-- of monadic functions.+newtype Id a = Id { getId :: a }++instance Monad Id where+ return a = Id a+ (Id a) >>= b = b a++instance Applicative Id where+ pure = return+ (<*>) = ap++instance Functor Id where+ fmap = liftM++-- ----------------------------------------------------------------------+-- * Identity types+ +-- | The type 'Identity' /a/ /b/ witnesses the fact that /a/ and /b/+-- are the same type. In other words, this type is non-empty if and+-- only if /a/ = /b/. This property is not guaranteed by the type+-- system, but by the API, via the fact that the operators+-- 'relexivity', 'symmetry', and 'transitivity' are the only exposed+-- constructors for this type. The implementation of this type is+-- deliberately hidden, as this is the only way to guarantee its+-- defining property.+-- +-- Identity types are useful in certain situations. For example, they+-- can be used to define a data type which is polymorphic in some type+-- variable /x/, and which has certain constructors that are only+-- available when /x/ is a particular type. For example, in the+-- declaration+-- +-- > data ExampleType x = Constructor1 x | Constructor2 x (Identity x Bool),+-- +-- @Constructor1@ is available for all /x/, but @Constructor2@ is only+-- available when /x/ = 'Bool'.+newtype Identity a b = Identity (a -> b, b -> a)++-- | Witness the fact that /a/=/a/.+reflexivity :: Identity a a+reflexivity = Identity (id, id)++-- | Witness the fact that /a/=/b/ implies /b/=/a/.+symmetry :: Identity a b -> Identity b a+symmetry (Identity (f,g)) = Identity (g,f)++-- | Witness the fact that /a/=/b/ and /b/=/c/ implies /a/=/c/.+transitivity :: Identity a b -> Identity b c -> Identity a c+transitivity (Identity (f,g)) (Identity (f',g')) = Identity (f'',g'') where+ f'' = f' . f+ g'' = g . g'++-- | The identity function 'id' : /a/ → /b/, provided that /a/ and /b/+-- are the same type.+identity :: Identity a b -> a -> b+identity (Identity (f,g)) = f++instance Show (Identity a b) where+ show x = "id"++-- ----------------------------------------------------------------------+-- * Isomorphism types++-- | The type 'Isomorphism' /a/ /b/ consists of isomorphisms between+-- /a/ and /b/, i.e. pairs (/f/,/g/) such that /g/./f/ == id :: /a/ -> /a/,+-- /f/./g/ == id :: /b/ -> /b/. +--+-- As with e.g. Haskell’s 'Monad' class, it is not possible in general+-- to guarantee that the intended laws hold; it is the programmer’s+-- responsibility to ensure this.+--+-- Under the hood, 'Isomorphism' and 'Identity' are in fact the same;+-- they differ just in the API exposed. +newtype Isomorphism a b = Isomorphism (a -> b, b -> a)++-- | Map forwards along an isomorphism.+iso_forwards :: Isomorphism a b -> a -> b+iso_forwards (Isomorphism (f,g)) = f++-- | Map backwards along an isomorphism.+iso_backwards :: Isomorphism a b -> b -> a+iso_backwards (Isomorphism (f,g)) = g++-- ======================================================================+-- * Error messages++-- | Often a low-level function, such as 'qcdata_zip' and+-- 'qcdata_promote', throws an error because of a failure of some+-- low-level condition, such as \"list too short\". To produce error+-- messages that are meaningful to user-level code, these functions do+-- not have a hard-coded error message. Instead, they input a stub+-- error message.+-- +-- A meaningful error message typically consists of at least three parts:+-- +-- * the name of the user-level function where the error occurred, for+-- example: \"reverse_generic\";+-- +-- * what the function was doing when the error occurred, for example:+-- \"operation not permitted in reversible circuit\";+-- +-- * a specific low-level reason for the error, for example: \"dynamic+-- lifting\".+-- +-- Thus, a meaningful error message may be: \"reverse_generic:+-- operation not permitted in reversible circuit: dynamic lifting\".+-- +-- The problem is that the three pieces of information are not usually+-- present in the same place. The user-level function is often a+-- wrapper function that performs several different mid-level+-- operations (e.g., transforming, reversing). The mid-level function+-- knows what operation was being performed when the error occurred,+-- but often calls a lower-level function to do the actual work (e.g.,+-- encapsulating).+-- +-- Therefore, a stub error message is a function that inputs some+-- lower-level reason for a failure (example: \"list too short\") and+-- translates this into a higher-level error message (example:+-- \"qterm: shape of parameter does not data: list too short\").+-- +-- Sometimes, the stub error message may also ignore the low-level+-- message and completely replace it by a higher-level one. For+-- example, a function that implements integers as bit lists may wish+-- to report a problem with integers, rather than a problem with the+-- underlying lists.+type ErrMsg = String -> String++-- ======================================================================+-- * The Curry type class++-- | The 'Curry' type class is used to implement functions that have a+-- variable number of arguments. It provides a family of type+-- isomorphisms+-- +-- @fun ≅ args -> res,@+-- +-- where+-- +-- > fun = a1 -> a2 -> ... -> an -> res,+-- > args = (a1, (a2, (..., (an, ())))).++class Curry fun args res | args res -> fun where+ -- | Multiple curry: map a function + -- (/a/[sub 1], (/a/[sub 2], (…, ())) → /b/ + -- to its curried form + -- /a/[sub 1] → /a/[sub 2] → … → /b/.+ mcurry :: (args -> res) -> fun+ -- | Multiple uncurry: map a function+ -- /a/[sub 1] → /a/[sub 2] → … → /b/+ -- to its uncurried form + -- (/a/[sub 1], (/a/[sub 2], (…, ())) → /b/.+ muncurry :: fun -> (args -> res)+ +instance Curry b () b where+ mcurry g = g ()+ muncurry x = const x++instance Curry fun args res => Curry (a -> fun) (a,args) res where+ mcurry g x = mcurry (\xs -> g (x,xs))+ muncurry f (x,xs) = muncurry (f x) xs+
+ src/Libraries/CommandLine.hs view
@@ -0,0 +1,72 @@+-- This file is part of Quipper. Copyright (C) 2011-2016. Please see the+-- file COPYRIGHT for a list of authors, copyright holders, licensing,+-- and other details. All rights reserved.+-- +-- ======================================================================++-- | This module provides some functions that are useful in the+-- processing of command line options, and that are shared between+-- several algorithms.++module Libraries.CommandLine where++import Libraries.Auxiliary (string_of_list)++-- import other stuff+import System.Exit+import System.IO+import Data.List+import Data.Char++-- ----------------------------------------------------------------------+-- * Option processing+ +-- | Exit with an error message after a command line error. This also+-- outputs information on where to find command line help.+optfail :: String -> IO a+optfail msg = do+ hPutStr stderr msg+ hPutStrLn stderr "Try --help for more info."+ exitFailure++-- | Parse a string to an integer, or return 'Nothing' on failure.+parse_int :: (Integral r) => String -> Maybe r+parse_int s = case reads s of+ [(n, "")] -> Just (fromInteger n)+ _ -> Nothing++-- | Parse a string to a list of integers, or return 'Nothing' on failure.+parse_list_int :: String -> Maybe [Int] +parse_list_int s = case reads s of+ [(ns, "")] -> Just ns+ _ -> Nothing++-- | Parse a string to a 'Double', or return 'Nothing' on failure.+parse_double :: String -> Maybe Double+parse_double s = case reads s of+ [(n, "")] -> Just n+ _ -> Nothing++-- | In an association list, find the key that best matches the given+-- string. If one key matches exactly, return the corresponding+-- key-value pair. Otherwise, return a list of all key-value pairs+-- whose key have the given string as a prefix. This list could be of+-- length 0 (no match), 1 (unique match), or greater (ambiguous key).+-- Note: the keys in the association list must be lower case. The+-- input string is converted to lower case as well, resulting in+-- case-insensitive matching.+match_enum :: [(String, a)] -> String -> [(String, a)]+match_enum list key =+ case lookup s list of+ Just v -> [(s,v)]+ Nothing -> filter (\(k,v) -> isPrefixOf s k) list+ where+ s = map toLower key+ +-- | Pretty-print a list of possible values for a parameter. The+-- first argument is the name of the parameter, and the second+-- argument is its enumeration.+show_enum :: String -> [(String, a)] -> String +show_enum param list =+ "Possible values for " ++ param ++ " are: " +++ string_of_list "" ", " "" "no possible values" fst list ++ ".\n"
+ src/Libraries/PortableSignals.hs view
@@ -0,0 +1,139 @@+-- This file is part of Quipper. Copyright (C) 2011-2016. Please see the+-- file COPYRIGHT for a list of authors, copyright holders, licensing,+-- and other details. All rights reserved.+-- +-- ======================================================================++{-# LANGUAGE CPP #-}++-- | This module provides a thin portability layer for handling user+-- interrupts.+-- +-- The reason is that in the standard Haskell library, this+-- functionality is only available in operating system specific+-- modules, namely "System.Posix.Signals" (for POSIX systems,+-- including Linux) and "GHC.ConsoleHandler" (for Windows).+-- +-- Note that despite this compatibility layer, there are some+-- operating system specific quirks:+-- +-- * In Windows, console events (such as Control-C) can only be+-- received by an application running in a Windows console. Certain+-- environments that look like consoles do not support console events,+-- such as xterm and rxvt windows, and Cygwin shells with @CYGWIN=tty@+-- set.+-- +-- * In Windows, setting a handler for any one signal automatically+-- overrides the handlers for all signals (effectively ignoring them).+-- Also, if the 'Default' or 'Ignore' handler is specified, it+-- applies to all signals. We do not currently provide a way to+-- specify handlers for multiple signals.++module Libraries.PortableSignals (+ Signal(..),+ Handler(Default,Ignore,Catch,CatchOnce),+ installHandler,+ with_handler+ ) where++#ifdef mingw32_HOST_OS+import qualified GHC.ConsoleHandler as OS+#else+import qualified System.Posix.Signals as OS+#endif+ +-- ----------------------------------------------------------------------+-- * Common interface++-- | A data type for signals. This can be extended as needed.+data Signal =+ Interrupt -- ^ Control-C event.+ | Close -- ^ TERM signal (POSIX) or Close event (Windows).++-- | A data type for handlers.+data Handler =+ Default -- ^ Default action.+ | Ignore -- ^ Ignore the signal.+ | Catch (IO ()) -- ^ Handle the signal in a new thread when the signal is received.+ | CatchOnce (IO ()) -- ^ Like 'Catch', but only handle the first such signal.+ | OSHandler OS.Handler -- ^ An operating system specific handler.++-- | Install a handler for the given signal. The old handler is+-- returned. +installHandler :: Signal -> Handler -> IO Handler+#ifdef mingw32_HOST_OS+installHandler = installHandler_windows+#else+installHandler = installHandler_posix+#endif++-- | Run a block of code with a given signal handler. The previous+-- handler is restored when the block terminates.+with_handler :: Signal -> Handler -> IO a -> IO a+with_handler signal handler body = do+ oldhandler <- installHandler signal handler+ a <- body+ installHandler signal oldhandler+ return a++-- ----------------------------------------------------------------------+-- * Windows specific code++#ifdef mingw32_HOST_OS++-- | Check if the Windows 'ConsoleEvent' matches the given abstract+-- 'Signal'. We implement this as a relation, rather than a function,+-- to allow for more than one 'ConsoleEvent' to match the same+-- 'Signal', or for more than one 'Signal' to match the same+-- 'ConsoleEvent'.+signal_matches :: OS.ConsoleEvent -> Signal -> Bool+signal_matches OS.ControlC Interrupt = True+signal_matches OS.Close Close = True+signal_matches _ _ = False++-- | Windows implementation of 'installHandler'.+installHandler_windows :: Signal -> Handler -> IO Handler+installHandler_windows signal handler = do+ oldhandler <- OS.installHandler (oshandler handler)+ return (OSHandler oldhandler)+ where+ oshandler Default = OS.Default+ oshandler Ignore = OS.Ignore+ oshandler (Catch body) = OS.Catch $ \event -> do+ if signal_matches event signal+ then body + else return ()+ oshandler (CatchOnce body) = OS.Catch $ \event -> do+ if signal_matches event signal + then do+ -- uninstall the handler+ OS.installHandler OS.Default+ body+ else return ()+ oshandler (OSHandler h) = h+ +-- ----------------------------------------------------------------------+-- * POSIX specific code++#else++-- | Map an abstract 'Signal' to a POSIX specific 'OS.Signal'.+ossignal :: Signal -> OS.Signal+ossignal Interrupt = OS.keyboardSignal+ossignal Close = OS.softwareTermination++-- | Map a 'Handler' to a POSIX specific handler.+oshandler :: Handler -> OS.Handler+oshandler Default = OS.Default+oshandler Ignore = OS.Ignore+oshandler (Catch body) = OS.Catch body+oshandler (CatchOnce body) = OS.CatchOnce body+oshandler (OSHandler h) = h++-- | POSIX implementation of 'installHandler'.+installHandler_posix :: Signal -> Handler -> IO Handler+installHandler_posix signal handler = do+ oldhandler <- OS.installHandler (ossignal signal) (oshandler handler) Nothing+ return (OSHandler oldhandler)++#endif
+ src/Quipper/Printing.hs view
@@ -0,0 +1,1692 @@+-- This file is part of Quipper. Copyright (C) 2011-2016. Please see the+-- file COPYRIGHT for a list of authors, copyright holders, licensing,+-- and other details. All rights reserved.+-- +-- ======================================================================++{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE BangPatterns #-}++-- | Pretty-printing of low-level quantum circuits.++module Quipper.Printing (+ -- * ASCII representation of circuits+ ascii_of_bcircuit,+ print_dbcircuit_ascii,+ getBit,+ -- * Gate counts+ print_gatecounts_bcircuit,+ -- * Graphical representation of circuits+ render_dbcircuit,+ -- * Previewing+ preview_bcircuit,+ -- * Printing to multiple formats+ Format(..),+ FormatStyle(..),+ pdf,+ eps,+ ps,+ format_enum,+ print_dbcircuit,+ print_of_document,+ print_of_document_custom,+ -- * Generic printing+ print_unary,+ print_generic,+ print_simple,+ ) where++-- import other Quipper stuff+import Libraries.Auxiliary+import Quipper.Circuit+import Quipper.Generic+import Quipper.Monad+import Quipper.QData++-- import other stuff+import Prelude+import Text.Printf+import Data.Char(isSpace)+import Data.List+import Data.Maybe+import Control.Monad(when)+import Graphics.EasyRender+import System.IO+import System.Process+import System.Directory+import System.Environment+import System.Info++import Data.Set (Set)+import qualified Data.Set as Set++import Data.Map (Map)+import qualified Data.Map as Map++import qualified Data.IntMap as IntMap+import qualified Data.List as List++-- ======================================================================+-- * Auxiliary functions++-- | Determine whether a named gate is self-inverse. The kind of a+-- gate is uniquely determined by its name, and the number of input+-- wires and generalized controls.+-- +-- For now, we only recognize "X", "Y", "Z", "H", "not", "swap", and+-- "W" as self-inverse; it is not currently possible for user code to+-- extend this list.+self_inverse :: String -> [Wire] -> [Wire] -> Bool+self_inverse "X" [q] [] = True+self_inverse "Y" [q] [] = True+self_inverse "Z" [q] [] = True+self_inverse "H" [q] [] = True+self_inverse "not" [q] [] = True+self_inverse "swap" [q1,q2] [] = True+self_inverse "W" [q1,q2] [] = True+self_inverse _ _ _ = False++-- ======================================================================+-- * ASCII representation of circuits++-- $ Convert a circuit to ASCII: one gate per line.++type WireTypeMap = IntMap.IntMap Wiretype++-- | Given a map of wiretypes, and a gate, update the wiretype in the map+-- if the gate changes it.+track_wiretype :: WireTypeMap -> Gate -> WireTypeMap+track_wiretype wtm (QInit _ w _ ) = IntMap.insert w Qbit wtm+track_wiretype wtm (CInit _ w _ ) = IntMap.insert w Cbit wtm+track_wiretype wtm (QMeas w ) = IntMap.insert w Cbit wtm+track_wiretype wtm (CGate _ w _ _) = IntMap.insert w Cbit wtm+track_wiretype wtm (CGateInv _ w _ _) = IntMap.insert w Cbit wtm+track_wiretype wtm (QPrep w _ ) = IntMap.insert w Qbit wtm+track_wiretype wtm (QUnprep w _ ) = IntMap.insert w Cbit wtm+track_wiretype wtm (Subroutine boxid inv ws1 a1 ws2 a2 c ncf scf rep) = a2 `IntMap.union` wtm +track_wiretype wtm _ = wtm++-- | Convert a 'BoxId' to the string in the format \"/name/\", shape \"/x/\".+ascii_of_boxid :: BoxId -> String+ascii_of_boxid (BoxId name shape) = show name ++ ", shape " ++ show shape++-- | Generate an ASCII representation of a control. +-- As controls are stored as untyped wires, we can lookup the wiretype in+-- the current map and annotate the control if it's classical.+ascii_render_control :: WireTypeMap -> Signed Wire -> String+ascii_render_control wtm (Signed w b) =+ (if b then "+" else "-") ++ show w ++ ascii_render_control_type wtype+ where + wtype = if (w `IntMap.member` wtm) then (wtm IntMap.! w) else Qbit+ ascii_render_control_type Qbit = ""+ ascii_render_control_type Cbit = "c"++-- | Generate an ASCII representation of a list of controls.+ascii_render_controls :: WireTypeMap -> Controls -> String+ascii_render_controls wtm c =+ string_of_list " with controls=[" ", " "]" "" (ascii_render_control wtm) c++-- | Generate an ASCII representation of a NoControlFlag+ascii_render_nocontrolflag :: NoControlFlag -> String+ascii_render_nocontrolflag False = ""+ascii_render_nocontrolflag True = " with nocontrol"++-- | Generate an ASCII representation of a single gate.+ascii_render_gate :: WireTypeMap -> Gate -> String+ascii_render_gate wtm (QGate "trace" _ _ _ _ _) = ""+ascii_render_gate wtm (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+ where+ inv' = inv && not (self_inverse name ws1 ws2)+ascii_render_gate wtm (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+ascii_render_gate wtm (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+ascii_render_gate wtm (CNot w c ncf) = + "CNot(" ++ show w ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (CGate n w c ncf) = + -- special case+ "CGate[" ++ show n ++ "]" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (CGateInv n w c ncf) = + "CGate[" ++ show n ++ "]" ++ "*" ++ (string_of_list "(" "," ")" "()" show (w:c))+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (CSwap w1 w2 c ncf) = + "CSwap(" ++ show w1 ++ "," ++ show w2 ++ ")" + ++ ascii_render_controls wtm c+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (QPrep w ncf) = + "QPrep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (QUnprep w ncf) = + "QUnprep(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (QInit b w ncf) = + "QInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (CInit b w ncf) = + "CInit" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (QTerm b w ncf) = + "QTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (CTerm b w ncf) = + "CTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ ++ ascii_render_nocontrolflag ncf+ascii_render_gate wtm (QMeas w) = + "QMeas(" ++ show w ++ ")"+ascii_render_gate wtm (QDiscard w) = + "QDiscard(" ++ show w ++ ")"+ascii_render_gate wtm (CDiscard w) = + "CDiscard(" ++ show w ++ ")"+ascii_render_gate wtm (DTerm b w) = + "DTerm" ++ (if b then "1" else "0") ++ "(" ++ show w ++ ")"+ascii_render_gate wtm (Subroutine boxid inv ws1 a1 ws2 a2 c ncf scf rep) = + "Subroutine" ++ show_rep ++ "[" ++ ascii_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 ++ ")"+ascii_render_gate wtm (Comment s inv ws) = + "Comment[" ++ show s ++ "]" + ++ optional inv "*"+ ++ (string_of_list "(" ", " ")" "()" (\(w,s) -> show w ++ ":" ++ show s) ws)+ +-- | Generate an ASCII representation of a gate list.+ascii_render_gatelist :: WireTypeMap -> [Gate] -> String+ascii_render_gatelist wtm [] = ""+ascii_render_gatelist wtm (g:gs) =+ (ascii_render_gate wtm g) ++ "\n" ++ + (ascii_render_gatelist (track_wiretype wtm g) gs)+ where++-- | Generate an ASCII representation of a wiretype.+ascii_render_wiretype :: Wiretype -> String+ascii_render_wiretype Qbit = "Qbit"+ascii_render_wiretype Cbit = "Cbit"++-- | Generate an ASCII representation of a type assignment.+ascii_render_typeas :: (Wire, Wiretype) -> String+ascii_render_typeas (w, t) =+ show w ++ ":" ++ ascii_render_wiretype t++-- | Generate an ASCII representation of an arity, preceded by a title+-- (input or output).+ascii_render_arity :: String -> Arity -> String+ascii_render_arity title a =+ title ++ ": " ++ (string_of_list "" ", " "" "none" ascii_render_typeas (IntMap.toList a)) ++ "\n"++-- | Generate an ASCII representation of an ordered arity, preceded by+-- a title (input or output).+ascii_render_oarity :: String -> [Wire] -> Arity -> String+ascii_render_oarity title ws a =+ title ++ ": " + ++ (string_of_list "" ", " "" "none" ascii_render_typeas tas_list) ++ "\n"+ where+ tas_list = [ (w, a IntMap.! w) | w <- ws ]++-- | Generate an ASCII representation of a low-level ordered quantum+-- circuit.+ascii_of_ocircuit :: OCircuit -> String+ascii_of_ocircuit ocircuit = + (ascii_render_oarity "Inputs" win a1) +++ (ascii_render_gatelist a1 gl) +++ (ascii_render_oarity "Outputs" wout a2)+ where+ OCircuit (win, circuit, wout) = ocircuit+ (a1, gl, a2, _) = circuit++-- | Generate an ASCII representation of a low-level quantum circuit.+ascii_of_circuit :: Circuit -> String+ascii_of_circuit circuit = ascii_of_ocircuit ocircuit where+ ocircuit = OCircuit (w_in, circuit, w_out)+ (a1, _, a2, _) = circuit+ w_in = IntMap.keys a1+ w_out = IntMap.keys a2++-- | Generate an ASCII representation of a low-level boxed quantum+-- circuit.+ascii_of_bcircuit :: BCircuit -> String+ascii_of_bcircuit (c,s) = + (ascii_of_circuit c) +++ (concat $ map ascii_of_subroutine (Map.toList s)) +++ "\n"++-- | Generate an ASCII representation of a named subroutine.+ascii_of_subroutine :: (BoxId, TypedSubroutine) -> String+ascii_of_subroutine (boxid, TypedSubroutine ocirc input_strux output_strux ctrble) =+ "\n" + ++ "Subroutine: " ++ show name ++ "\n"+ ++ "Shape: " ++ show shape ++ "\n"+ ++ "Controllable: " ++ (case ctrble of {AllCtl -> "yes"; NoCtl -> "no"; OnlyClassicalCtl -> "classically"}) ++ "\n"+ ++ ascii_of_ocircuit ocirc+ where+ BoxId name shape = boxid+ +-- ======================================================================+-- * Dynamic ASCII representation of circuits++-- $+-- The dynamic ASCII representation prints a circuit to standard+-- output in ASCII format, just like the static ASCII representation.+-- However, when a 'dynamic_lift' operation is encountered, it prompts+-- the user for the value of the corresponding bit. In effect, the+-- user is asked to act as the quantum device or simulator. ++-- | Write a prompt to get input from the user. Since the prompt+-- doesn't include a newline, the output must be flushed explicitly.+prompt :: String -> IO ()+prompt s = do+ putStr s+ hFlush stdout++-- | Interactively read a bit (either 0 or 1) from standard input.+-- This is intended for interactive user input, so it skips white+-- space until a 0 or 1 is encountered. In case the first+-- non-whitespace character isn't 0 or 1 or '#', the rest of the line+-- is ignored and the user is prompted to try again.+-- +-- However, this also works for non-interactive input, so that the+-- input can be redirected from a file. In this case, the characters 0+-- and 1 and whitespace, including newlines, can be interspersed+-- freely. \'@#@\' starts a comment that extends until the end of the+-- line. +getBit :: IO Bool+getBit = do+ c <- getChar+ case c of+ '0' -> return False+ '1' -> return True+ '#' -> do+ getLine+ getBit+ c | isSpace c -> getBit+ c -> do+ getLine+ prompt $ "# Expecting 0 or 1. Please try again: "+ getBit++-- | Embed a read-write computation in the 'IO' monad, by writing+-- gates to the terminal and interactively querying the user (or a+-- file on stdin) for dynamic liftings. We also update a 'Namespace'+-- while doing so, to collect any subroutines that are defined along+-- the way.+run_readwrite_ascii :: WireTypeMap -> ReadWrite a -> Namespace -> IO (a, Namespace)+run_readwrite_ascii wtm (RW_Return a) ns = return (a, ns)+run_readwrite_ascii wtm (RW_Write gate comp) ns = do+ putStrLn (ascii_render_gate wtm gate)+ run_readwrite_ascii (track_wiretype wtm gate) comp ns+run_readwrite_ascii wtm (RW_Read w cont) ns = do+ prompt $ "# Value of wire " ++ show w ++ ": "+ bool <- getBit+ putStrLn $ "# Value: " ++ show bool+ run_readwrite_ascii wtm (cont bool) ns+run_readwrite_ascii wtm (RW_Subroutine name subroutine comp) ns = do+ let !ns' = map_provide name subroutine ns+ run_readwrite_ascii wtm comp ns'++-- | Interactively output a 'DBCircuit' to standard output. This+-- supports dynamic lifting by prompting the user for bit values when+-- a dynamic lifting operation is encountered. Effectively the user is+-- asked to behave like a quantum device.+print_dbcircuit_ascii :: ErrMsg -> DBCircuit a -> IO ()+print_dbcircuit_ascii _ (a0, comp) = do+ hSetBuffering stdout LineBuffering -- flush output after each line+ putStr (ascii_render_arity "Inputs" a0)+ ((a1, _, _),ns) <- run_readwrite_ascii a0 comp namespace_empty+ putStr (ascii_render_arity "Outputs" a1)+ sequence_ [ putStr $ ascii_of_subroutine subr | subr <- Map.toList ns ]+ putStr "\n"++-- ----------------------------------------------------------------------+-- * Graphical representation of circuits++-- | The color white.+white :: Color+white = Color_Gray 1.0++-- | The color black.+black :: Color+black = Color_Gray 0.0++-- | A data type that holds all the customizable parameters.+data FormatStyle = FormatStyle {+ -- | The RenderFormat to use.+ renderformat :: RenderFormat,+ -- | The color of the background.+ backgroundcolor :: Color,+ -- | The color of the foreground (e.g. wires and gates).+ foregroundcolor :: Color,+ -- | Line width.+ linewidth :: Double,+ -- | Gap for double line representing classical bit.+ coffs :: Double,+ -- | Radius of dots for \"controlled\" gates.+ dotradius :: Double,+ -- | Radius of oplus for \"not\" gate.+ oplusradius :: Double,+ -- | Horizontal column width.+ xoff :: Double,+ -- | Difference between width of box and width of label.+ gatepad :: Double,+ -- | Height of labelled box.+ gateheight :: Double,+ -- | Width and height of \"cross\" for swap gate.+ crossradius :: Double,+ -- | Vertical shift for text labels.+ stringbase :: Double,+ -- | Width of \"bar\" bar.+ barwidth :: Double, + -- | Height of \"bar\" bar.+ barheight :: Double,+ -- | Width of \"D\" symbol.+ dwidth :: Double,+ -- | Height of \"D\" symbol.+ dheight :: Double,+ -- | Maximal width of a gate label.+ maxgatelabelwidth :: Double,+ -- | Maximal width of a wire label.+ maxlabelwidth :: Double,+ -- | Maximal width of a wire number.+ maxnumberwidth :: Double,+ -- | Font to use for labels on gates.+ gatefont :: Font,+ -- | Font to use for comments.+ commentfont :: Font,+ -- | Color to use for comments.+ commentcolor :: Color,+ -- | Font to use for labels.+ labelfont :: Font,+ -- | Color to use for labels.+ labelcolor :: Color,+ -- | Font to use for numbers.+ numberfont :: Font,+ -- | Color to use for numbers.+ numbercolor :: Color,+ -- | Whether to label each subroutine call with shape parameters+ subroutineshape :: Bool+} deriving Show++-- | A RenderFormat consisting of some default parameters, +-- along with the given RenderFormat.+defaultStyle :: RenderFormat -> FormatStyle+defaultStyle rf = FormatStyle {+ renderformat = rf,+ backgroundcolor = white,+ foregroundcolor = black,+ linewidth = 0.02, + coffs = 0.03,+ dotradius = 0.15,+ oplusradius = 0.25,+ xoff = 1.5,+ gatepad = 0.3, + gateheight = 0.8,+ crossradius = 0.2,+ stringbase = 0.25,+ barwidth = 0.1,+ barheight = 0.5,+ dwidth = 0.3,+ dheight = 0.4,+ maxgatelabelwidth = 1.1,+ maxlabelwidth = 0.7,+ maxnumberwidth = 0.7,+ gatefont = Font TimesRoman 0.5,+ commentfont = Font TimesRoman 0.3,+ commentcolor = Color_RGB 1 0.2 0.2,+ labelfont = Font TimesRoman 0.3,+ labelcolor = Color_RGB 0 0 1,+ numberfont = Font Helvetica 0.5,+ numbercolor = Color_RGB 0 0.7 0,+ subroutineshape = True+}++-- | The default PDF Style.+pdf :: FormatStyle+pdf = defaultStyle Format_PDF++-- | The default EPS Style.+eps :: FormatStyle+eps = defaultStyle (Format_EPS 1)++-- | The default PS Style.+ps :: FormatStyle+ps = defaultStyle (Format_PS)++-- ----------------------------------------------------------------------+-- ** General-purpose PostScript functions++-- | Escape special characters in a string literal.+ps_escape :: String -> String+ps_escape [] = []+ps_escape ('\\' : t) = '\\' : '\\' : ps_escape t+ps_escape ('(' : t) = '\\' : '(' : ps_escape t+ps_escape (')' : t) = '\\' : ')' : ps_escape t+ps_escape (h : t) = h : ps_escape t++-- ----------------------------------------------------------------------+-- ** String formatting++-- | Convert a 'BoxId' to the string in the format \"/name/, shape /x/\".+string_of_boxid :: BoxId -> String+string_of_boxid (BoxId name shape) = name ++ ", shape " ++ shape++-- ----------------------------------------------------------------------+-- ** Functions for dealing with x-coordinates++-- | Pre-processing: figure out the /x/-column of each gate. Returns+-- (/n/,/xgs/) where /xgs/ is a list of ('Gate', 'X') pairs, and+-- /n/ is the rightmost /x/-coordinate of the circuit. Here we start+-- from /x0/ and use constant step /xoff/ taken from the 'FormatStyle'.+assign_x_coordinates :: FormatStyle -> [Gate] -> X -> (X, [(Gate, X)])+assign_x_coordinates fs gs x0 =+ let ((x,ws), xgs) = mapAccumL (\ (x, ws) g ->+ -- count the wires attached to the gate. If there is precisely+ -- one (unary gate), merge it with adjacent unary gates. Do+ -- not merge comments.+ let merge = case (g, wirelist_of_gate g) of+ (Comment _ _ _, _) -> Nothing+ (_, [w]) -> Just w+ (_, _) -> Nothing+ in+ case merge of+ Just w ->+ if not (w `elem` ws) then+ ((x, w:ws), (g, x))+ else+ ((x + (xoff fs), [w]), (g, x + (xoff fs)))+ _ ->+ if ws == [] then+ ((x + (xoff fs), []), (g, x))+ else+ ((x + 2.0 * (xoff fs), []), (g, x + (xoff fs)))+ ) (x0, []) gs+ in+ if ws == [] then+ (x, xgs)+ else+ (x + (xoff fs), xgs)++-- | A 'Xarity' is a map from wire id's to pairs of a wiretype and a+-- starting /x/-coordinate.+type Xarity = Map Wire (Wiretype, X)++-- | Figure out how a gate at coordinate /x/ affects the current 'Xarity'.+-- Return a pair (/term/, /new/), where /term/ is the 'Xarity' of wires+-- terminated by this gate, and /new/ is the outgoing 'Xarity' of this+-- gate.+update_xarity :: Xarity -> Gate -> X -> (Xarity, Xarity)+update_xarity xarity gate x =+ let (win, wout) = gate_arity gate+ safe_lookup xarity w = + case Map.lookup w xarity of + Just x -> x+ Nothing -> (Qbit, x) -- error ("update_xarity: the wire " ++ show w ++ " does not exist. In the gate:\n" ++ ascii_render_gate gate)+ (win', wout') = (win \\ wout, wout \\ win)+ -- extract terminating wires from xarity+ xarity_term = foldl (\xar (w,_) -> Map.insert w (xarity `safe_lookup` w) xar) Map.empty win' + -- extract continuing wires from xarity+ xarity_cont = foldl (\xar (w,_) -> Map.delete w xar) xarity win'+ -- add new wires to xarity_cont+ xarity_new = foldl (\xar (w,t) -> Map.insert w (t,x) xar) xarity_cont wout'+ in+ (xarity_term, xarity_new)++-- ----------------------------------------------------------------------+-- ** Low-level drawing functions++-- | @'render_line' x0 y0 x1 y1@: Draw a line from (/x0/, /y0/)+-- to (/x1/, /y1/). In case of a zero-length line, draw nothing.+render_line :: X -> Y -> X -> Y -> Draw ()+render_line x0 y0 x1 y1 | x0 == x1 && y0 == y1 = return ()+render_line x0 y0 x1 y1 = draw_subroutine alt $ do+ moveto x0 y0+ lineto x1 y1+ stroke+ where+ alt = [custom_ps $ printf "%f %f %f %f line\n" x0 y0 x1 y1]++-- | @'render_dot' x y@: Draw a filled control dot at (/x/,/y/).+render_dot :: FormatStyle -> X -> Y -> Draw ()+render_dot fs x y = draw_subroutine alt $ do+ arc x y (dotradius fs) 0 360+ fill (foregroundcolor fs)+ where+ alt = [custom_ps $ printf "%f %f dot\n" x y]++-- | @'render_circle' x y@: Draw an empty control dot at+-- (/x/,/y/).+render_circle :: FormatStyle -> X -> Y -> Draw ()+render_circle fs x y = draw_subroutine alt $ do+ arc x y (dotradius fs) 0 360+ fillstroke (backgroundcolor fs)+ where+ alt = [custom_ps $ printf "%f %f circ\n" x y]++-- | @'render_not' x y@: Draw a \"not\" gate at (/x/,/y/).+render_not :: FormatStyle -> X -> Y -> Draw ()+render_not fs x y = draw_subroutine alt $ do+ arc x y (oplusradius fs) 0 360+ fillstroke (backgroundcolor fs)+ render_line (x-(oplusradius fs)) y (x+(oplusradius fs)) y+ render_line x (y-(oplusradius fs)) x (y+(oplusradius fs))+ where+ alt = [custom_ps $ printf "%f %f oplus\n" x y]++-- | @'render_swap' x y@: Draw a cross (swap gate component) at+-- (/x/,/y/).+render_swap :: FormatStyle -> X -> Y -> Draw ()+render_swap fs x y = draw_subroutine alt $ do+ render_line (x-(crossradius fs)) (y-(crossradius fs)) (x+(crossradius fs)) (y+(crossradius fs))+ render_line (x-(crossradius fs)) (y+(crossradius fs)) (x+(crossradius fs)) (y-(crossradius fs))+ where + alt = [custom_ps $ printf "%f %f cross\n" x y]++-- | @'render_bar' x y@: Draw an init/term bar at (/x/,/y/).+render_bar :: FormatStyle -> X -> Y -> Draw ()+render_bar fs x y = draw_subroutine alt $ do+ rectangle (x - (barwidth fs)/2) (y - (barheight fs)/2) (barwidth fs) (barheight fs)+ fill (foregroundcolor fs)+ where+ alt = [custom_ps $ printf "%f %f bar\n" x y]++-- | @'render_bar' x y@: Draw a dterm bar at (/x/,/y/).+render_dbar :: FormatStyle -> X -> Y -> Draw ()+render_dbar fs x y = draw_subroutine alt $ do+ block $ do+ translate (x+(barwidth fs)/2) y+ scale (dwidth fs) (dheight fs)+ moveto (-1) (-0.5)+ arc_append (-0.5) 0 0.5 (-90) 90+ lineto (-1) 0.5+ closepath+ fill (foregroundcolor fs)+ where+ alt = [custom_ps $ printf "%f %f dbar\n" x y]++-- | @'render_init' name x y@: Draw an \"init\" gate at+-- (/x/,/y/), with state /name/.+render_init :: FormatStyle -> String -> X -> Y -> Draw ()+render_init fs name x y = draw_subroutine alt $ do+ render_bar fs x y+ textbox align_right (gatefont fs) (foregroundcolor fs) (x-(xoff fs)/2+(gatepad fs)/2) y (x-(gatepad fs)/2) y (stringbase fs) name+ where+ alt = [custom_ps $ printf "(%s) %f %f init\n" (ps_escape name) x y]++-- | @'render_term' name x y@: Draw a \"term\" gate at+-- (/x/,/y/), with state /name/.+render_term :: FormatStyle -> String -> X -> Y -> Draw ()+render_term fs name x y = draw_subroutine alt $ do+ render_bar fs x y+ textbox align_left (gatefont fs) (foregroundcolor fs) (x+(gatepad fs)/2) y (x+(xoff fs)/2-(gatepad fs)/2) y (stringbase fs) name+ where+ alt = [custom_ps $ printf "(%s) %f %f term\n" (ps_escape name) x y]++-- | @'render_dterm' name x y@: Draw a \"dterm\" gate at+-- (/x/,/y/), with state /name/.+render_dterm :: FormatStyle -> String -> X -> Y -> Draw ()+render_dterm fs name x y = draw_subroutine alt $ do+ render_dbar fs x y+ textbox align_left (gatefont fs) (foregroundcolor fs) (x+(gatepad fs)/2) y (x+(xoff fs)/2-(gatepad fs)/2) y (stringbase fs) name+ where+ alt = [custom_ps $ printf "(%s) %f %f dterm\n" (ps_escape name) x y]++-- | @'render_namedgate' name inv x y@: draw a named box centered at+-- (/x/,/y/). If /inv/ = 'True', append an \"inverse\" symbol to the+-- end of the name.+render_namedgate :: FormatStyle -> String -> InverseFlag -> X -> Y -> Draw ()+render_namedgate fs name inv x y = draw_subroutine alt $ do+ rectangle (x-gatewidth/2) (y-(gateheight fs)/2) gatewidth (gateheight fs)+ fillstroke (backgroundcolor fs)+ textbox align_center (gatefont fs) (foregroundcolor fs) (x-labelwidth/2) y (x+labelwidth/2) y (stringbase fs) name'+ where+ alt = [custom_ps $ printf "(%s) %f %f gate\n" (ps_escape name') x y]+ name' = name ++ optional inv "*"+ w = text_width (gatefont fs) name'+ labelwidth = min w (maxgatelabelwidth fs)+ gatewidth = labelwidth + (gatepad fs)+ +-- | @'render_gphasegate' name x y@: draw a global phase gate+-- centered at (/x/,/y/).+render_gphasegate :: FormatStyle -> String -> X -> Y -> Draw ()+render_gphasegate fs name x y = draw_subroutine alt $ do+ render_circgate fs name x (y-0.5)+ where+ alt = [custom_ps $ printf "(%s) %f %f gphase\n" (ps_escape name) x y]++-- | @'render_circgate' name x y@: draw a named oval centered at+-- (/x/,/y/).+render_circgate :: FormatStyle -> String -> X -> Y -> Draw ()+render_circgate fs name x y = draw_subroutine alt $ do+ oval x y (0.5*gatewidth) (0.4*(gateheight fs))+ fillstroke (backgroundcolor fs)+ textbox align_center (gatefont fs) (foregroundcolor fs) (x-labelwidth/2) y (x+labelwidth/2) y (stringbase fs) name+ where+ alt = [custom_ps $ printf "(%s) %f %f circgate\n" (ps_escape name) x y]+ w = text_width (gatefont fs) name+ labelwidth = min w (maxgatelabelwidth fs)+ gatewidth = labelwidth + (gatepad fs)+ +-- | @'render_blankgate' name x y@: draw an empty box centered+-- at (/x/,/y/), big enough to hold /name/.+render_blankgate :: FormatStyle -> String -> X -> Y -> Draw ()+render_blankgate fs name x y = draw_subroutine alt $ do+ rectangle (x-gatewidth/2) (y-(gateheight fs)/2) gatewidth (gateheight fs)+ fillstroke (backgroundcolor fs)+ where+ alt = [custom_ps $ printf "(%s) %f %f box\n" (ps_escape name) x y]+ w = text_width (gatefont fs) name+ labelwidth = min w (maxgatelabelwidth fs)+ gatewidth = labelwidth + (gatepad fs)++-- | @'render_comment' center s x y m@: draw the given string+-- vertically, with the top of the string near the given+-- /y/-coordinate. If /center/=='True', center it at the+-- /x/-coordinate, else move it just to the left of the+-- /x/-coordinate. /m/ is the maximum height allowed for the comment.+render_comment :: FormatStyle -> Bool -> String -> X -> Y -> Y -> Draw ()+render_comment fs center s x y maxh = draw_subroutine alt $ do+ textbox align_right (commentfont fs) (commentcolor fs) x (y-maxh) x (y+0.4) b s+ where+ alt = [custom_ps $ printf "(%s) %f %f %f %f comment\n" (ps_escape s) x y maxh yshift]+ b = if center then 0.15 else -0.25+ yshift = -b * nominalsize (commentfont fs)++-- | @'render_label' center s x y@: draw the given label just above+-- the given point. If /center/=='True', center it at the+-- /x/-coordinate, else move it just to the right of the+-- /x/-coordinate.+render_label :: FormatStyle -> Bool -> String -> X -> Y -> Draw ()+render_label fs True s x y = draw_subroutine alt $ do+ textbox align_center (labelfont fs) (labelcolor fs) (x-(maxlabelwidth fs)) y' (x+(maxlabelwidth fs)) y' (-0.5) s+ where+ alt = [custom_ps $ printf "(%s) %f %f clabel\n" (ps_escape s) x y']+ y' = y + 0.5 * (coffs fs)+render_label fs False s x y = draw_subroutine alt $ do+ textbox align_left (labelfont fs) (labelcolor fs) x y' (x+(maxlabelwidth fs)) y' (-0.5) s+ where+ alt = [custom_ps $ printf "(%s) %f %f rlabel\n" (ps_escape s) x y']+ y' = y + 0.5 * (coffs fs)+ +-- | Render the number at the given point (/x/,/y/). If the boolean+-- argument is 'True', put the number to the right of /x/, else to the left. +render_number :: FormatStyle -> Int -> Bool -> X -> Y -> Draw ()+render_number fs i True x y = draw_subroutine alt $ do+ textbox align_left (numberfont fs) (numbercolor fs) (x+0.2) y (x+0.2+(maxnumberwidth fs)) y (stringbase fs) (show i)+ where+ alt = [custom_ps $ printf "(%s) %f %f rnumber\n" (ps_escape (show i)) x y]+render_number fs i False x y = draw_subroutine alt $ do+ textbox align_right (numberfont fs) (numbercolor fs) (x-0.2-(maxnumberwidth fs)) y (x-0.2) y (stringbase fs) (show i)+ where+ alt = [custom_ps $ printf "(%s) %f %f lnumber\n" (ps_escape (show i)) x y]++-- ----------------------------------------------------------------------+-- ** Higher-level rendering functions++-- | Render a horizontal wire from /x/-coordinates /oldx/ to /x/,+-- using /t/ as the type and figuring out the /y/-coordinate from /ys/+-- and /w/. Append to the given string. If the parameters are invalid+-- (/w/ not in /ys/), throw an error.+render_typeas :: FormatStyle -> Map Wire Y -> X -> X -> Wire -> Wiretype -> Draw ()+render_typeas fs ys oldx x w t =+ let y = ys Map.! w in+ case t of+ Qbit -> do+ render_line oldx y x y+ Cbit -> do+ render_line oldx (y + (coffs fs)) x (y + (coffs fs))+ render_line oldx (y - (coffs fs)) x (y - (coffs fs))++-- | Render a bunch of horizontal wires from their repective starting+-- 'Xarity' to /x/.+render_xarity :: FormatStyle -> Map Wire Y -> Xarity -> X -> Draw ()+render_xarity fs ys xarity x = do+ sequence_ [ render_typeas fs ys oldx x w t | (w,(t,oldx)) <- Map.toList xarity ]++-- | Format a floating point number in concise form, with limited+-- accuracy.+dshow :: Double -> String+dshow dbl = + if abs dbl < 0.01 + then+ printf "%.1e" dbl+ else+ (reverse . strip . reverse) (printf "%.3f" dbl)+ where+ strip [] = []+ strip ('.' : t) = t+ strip ('0' : t) = strip t+ strip t = t+ +-- | @'render_controlwire' /x/ /ys/ /ws/ /c/@: +-- Render the line connecting all the box components and all the+-- control dots of some gate. +-- +-- Parameters: /x/ is the current /x/-coordinate, /ys/ is an indexed+-- array of /y/-coordinates, /ws/ is the set of wires for boxes, and+-- /c/ is a list of controls.+render_controlwire :: X -> Map Wire Y -> [Wire] -> Controls -> Draw ()+render_controlwire x ys ws c =+ case ws of+ [] -> return ()+ w:ws -> render_line x y0 x y1 + where+ ymap w = ys Map.! w+ y = ymap w+ cy = map (\(Signed w _) -> ymap w) c+ yy = map (\w -> ymap w) ws+ y0 = foldr min y (cy ++ yy)+ y1 = foldr max y (cy ++ yy)++-- | @'render_controlwire_float' /x/ /ys/ /y/ /c/@: Render the line+-- connecting all control dots of the given controls, as well as a+-- floating \"global phase\" gate located just below (/x/, /y/). +-- +-- Parameters: /x/ is the current /x/-coordinate, /ys/ is an indexed+-- array of /y/-coordinates, /y/ is the /y/-coordinate of the wire+-- where the floating gate is attached, and /c/ is a list of controls.+render_controlwire_float :: X -> Map Wire Y -> Y -> Controls -> Draw ()+render_controlwire_float x ys y c = render_line x y0 x y1 + where+ y' = y - 0.5+ cy = map (\(Signed w _) -> ys Map.! w) c+ y0 = minimum (y':cy)+ y1 = maximum (y':cy)++-- | @'render_controldots' /x/ /ys/ /c/@: Render the control dots+-- for the given controls.+render_controldots :: FormatStyle -> X -> Map Wire Y -> Controls -> Draw ()+render_controldots fs x ys c = do+ sequence_ [ renderdot x | x <- c ]+ where+ renderdot (Signed w True) = render_dot fs x (ys Map.! w)+ renderdot (Signed w False) = render_circle fs x (ys Map.! w)++-- | @'render_multi_gate' /x/ /ys/ /name/ /inv/ /wires/@: Render the+-- boxes for an /n/-ary gate of the given /name/, potentially+-- /inv/erted, at the given list of /wires/. The first two arguments+-- are the current /x/-coordinate and an indexed array of+-- /y/-coordinates.+render_multi_gate :: FormatStyle -> X -> Map Wire Y -> String -> InverseFlag -> [Wire] -> Draw ()+render_multi_gate fs x ys name inv [w] = + render_namedgate fs name inv x (ys Map.! w)+render_multi_gate fs x ys name inv ws =+ sequence_ [ render_namedgate fs (name ++ " " ++ show i) inv x (ys Map.! a) | (a,i) <- zip ws [1..] ]++-- | @'render_multi_named_ctrl' /x/ /ys/ /wires/ /names/@: Render+-- the boxes for multiple generalized controls at the given /wires/,+-- using the given /names/. We take special care of the fact that+-- generalized controls may be used non-linearly. +render_multi_named_ctrl :: FormatStyle -> X -> Map Wire Y -> [Wire] -> [String] -> Draw ()+render_multi_named_ctrl fs x ys ws names =+ sequence_ [ render_circgate fs name x (ys Map.! a) | (a,name) <- IntMap.toList map ]+ where+ -- Combine the labels for w if w has multiple occurrences.+ map = IntMap.fromListWith (\x y -> y ++ "," ++ x) (zip ws names)++-- | @'render_multi_genctrl' /x/ /ys/ /wires/@: Render the boxes for+-- multiple (numbered) generalized controls at the given /wires/.+render_multi_genctrl :: FormatStyle -> X -> Map Wire Y -> [Wire] -> Draw ()+render_multi_genctrl fs x ys ws = render_multi_named_ctrl fs x ys ws names+ where+ names = map show [1..]+ +-- | Number a list of wires in increasing order, at the given+-- /x/-coordinate. If the boolean argument is 'True', put the numbers+-- to the right of /x/, else to the left.+render_ordering :: FormatStyle -> X -> Map Wire Y -> Bool -> [Wire] -> Draw ()+render_ordering fs x ys b ws =+ sequence_ [ render_number fs i b x (ys Map.! w) | (w,i) <- numbering ]+ where+ numbering = zip ws [1..]++-- | Render gate /g/ at /x/-coordinate /x/ and /y/-coordinates as+-- given by /ys/, which is a map from wires to+-- /y/-coordinates. Returns a pair (/s/,/t/) of draw actions for+-- background and foreground, respectively.+render_gate :: FormatStyle -> Gate -> X -> Map Wire Y -> Y -> (Draw (), Draw ())+render_gate fs g x ys maxh =+ let ymap w = ys Map.! w + in+ case g of+ -- Certain named gates are recognized for custom rendering.+ QGate "not" _ [w] [] c ncf -> (s2, t2 >> t3)+ where+ y = ymap w+ s2 = render_controlwire x ys [w] c+ t2 = render_controldots fs x ys c+ t3 = (render_not fs x y)+ QGate "multinot" _ ws [] c ncf -> (s2, t2 >> t3)+ where+ s2 = render_controlwire x ys ws c+ t2 = render_controldots fs x ys c+ t3 = sequence_ (map (\w -> (render_not fs x (ymap w))) ws)+ QGate "swap" _ [w1,w2] [] c ncf -> (s2, t2 >> t3)+ where+ y1 = ymap w1+ y2 = ymap w2+ s2 = render_controlwire x ys [w1,w2] c+ t2 = render_controldots fs x ys c+ t3 = (render_swap fs x y1) >> (render_swap fs x y2)+ QGate "trace" _ _ _ _ _ -> (return (), return ())+ QGate name inv ws1 ws2 c ncf -> (s2, t2 >> t3 >> t4)+ where+ s2 = render_controlwire x ys (ws1 ++ ws2) c+ t2 = render_multi_gate fs x ys name inv' ws1+ t3 = render_controldots fs x ys c+ t4 = render_multi_genctrl fs x ys ws2+ inv' = inv && not (self_inverse name ws1 ws2)+ QRot name inv theta ws1 ws2 c ncf -> (s2, t2 >> t3 >> t4)+ where+ s2 = render_controlwire x ys (ws1 ++ ws2) c+ t2 = render_multi_gate fs x ys name' inv ws1+ t3 = render_controldots fs x ys c+ t4 = render_multi_genctrl fs x ys ws2+ name' = substitute name '%' (dshow theta)+ GPhase t ws c ncf -> (s2, t2 >> t3)+ where+ y = case (ws, c) of+ ([], []) -> maximum (0.0 : Map.elems ys)+ ([], c) -> minimum [ ymap w | Signed w b <- c ]+ (ws, c) -> minimum [ ymap w | w <- ws ]+ s2 = render_controlwire_float x ys y c+ t2 = render_controldots fs x ys c+ t3 = (render_gphasegate fs (dshow t) x y)+ CNot w c ncf -> (s2, t2 >> t3)+ where+ y = ymap w+ s2 = render_controlwire x ys [w] c+ t2 = render_controldots fs x ys c+ t3 = (render_not fs x y)+ CGate "if" w [a,b,c] ncf -> (s2, t1 >> t3) -- special case+ where+ y = ymap w+ s2 = render_controlwire x ys [w,a,b,c] []+ t1 = render_multi_named_ctrl fs x ys [a,b,c] ["if", "then", "else"]+ t3 = render_namedgate fs ">" False x y+ CGateInv "if" w [a,b,c] ncf -> (s2, t1 >> t3) -- special case+ where+ y = ymap w+ s2 = render_controlwire x ys [w,a,b,c] []+ t1 = render_multi_named_ctrl fs x ys [a,b,c] ["if", "then", "else"]+ t3 = render_namedgate fs "<" False x y+ CGate name w c ncf -> (s2, t2 >> t3)+ where+ y = ymap w+ s2 = render_controlwire x ys (w:c) []+ t2 = render_multi_named_ctrl fs x ys c [ " " | a <- c ]+ t3 = render_namedgate fs name False x y+ CGateInv name w c ncf -> (s2, t2 >> t3)+ where+ y = ymap w+ s2 = render_controlwire x ys (w:c) []+ t2 = render_multi_named_ctrl fs x ys c [ " " | a <- c ]+ t3 = render_namedgate fs name True x y+ CSwap w1 w2 c ncf -> (s2, t2 >> t3)+ where+ y1 = ymap w1+ y2 = ymap w2+ s2 = render_controlwire x ys [w1,w2] c+ t2 = render_controldots fs x ys c+ t3 = (render_swap fs x y1) >> (render_swap fs x y2)+ QPrep w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_namedgate fs "prep" False x y)+ QUnprep w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_namedgate fs "unprep" False x y)+ QInit b w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_init fs (if b then "1" else "0") x y)+ CInit b w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_init fs (if b then "1" else "0") x y)+ QTerm b w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_term fs (if b then "1" else "0") x y)+ CTerm b w ncf -> (return (), t3)+ where+ y = ymap w+ t3 = (render_term fs (if b then "1" else "0") x y)+ QMeas w -> (return (), t3)+ where+ y = ymap w+ t3 = (render_namedgate fs "meas" False x y)+ QDiscard w -> (return (), t3)+ where+ y = ymap w+ t3 = (render_bar fs x y)+ CDiscard w -> (return (), t3)+ where+ y = ymap w+ t3 = (render_bar fs x y)+ DTerm b w -> (return (), t3)+ where+ y = ymap w+ t3 = (render_dterm fs (if b then "1" else "0") x y)+ Subroutine boxid inv ws1 a1 ws2 a2 c ncf scf rep -> (s2, t2 >> t3)+ where+ ws = union ws1 ws2+ s2 = render_controlwire x ys ws c+ t2 = render_multi_gate fs x ys label inv ws+ t3 = render_controldots fs x ys c+ show_rep = if rep == RepeatFlag 1 then "" else "(x" ++ show rep ++ ")"+ BoxId name shape = boxid+ label = name ++ show_rep ++ if (subroutineshape fs) then (", shape " ++ shape) else ""+ Comment s inv ws -> (return (), t1 >> t2)+ where+ t1 = render_comment fs (null ws) s' x (ymap 0) maxh+ t2 = sequence_ [render_label fs (null s) l x (ymap w) | (w,l) <- ws]+ s' = s ++ optional inv "*"++-- | Render the gates in the circuit. The parameters are: /xarity/:+-- the 'Xarity' of the currently pending wires. /xgs/: the list of+-- gates, paired with pre-computed /x/-coordinates. /ys/: a map from+-- wires to pre-computed /y/-coordinates. /x/: the right-most+-- /x/-coordinate where the final wires will be drawn to. /maxh/: the+-- maximal height of comments.+render_gates :: FormatStyle -> Xarity -> [(Gate, X)] -> Map Wire Y -> X -> Y -> (Draw (), Draw ())+render_gates fs xarity xgs ys x maxh =+ case xgs of+ [] ->+ let s2 = render_xarity fs ys xarity x+ in (s2, return ())+ (g,newx):gls ->+ let (xarity_term, xarity_new) = update_xarity xarity g newx in+ let s1 = render_xarity fs ys xarity_term newx in+ let (s2, t2) = render_gate fs g newx ys maxh in+ let (sx, tx) = render_gates fs xarity_new gls ys x maxh in+ (s1 >> s2 >> sx, t2 >> tx)++-- | PostScript definitions of various parameters.+ps_parameters :: FormatStyle -> String+ps_parameters fs =+ "% some parameters\n"+ ++ printf "%f setlinewidth\n" (linewidth fs)+ ++ printf "/gatepad %f def\n" (gatepad fs)+ ++ printf "/gateheight %f def\n" (gateheight fs)+ ++ printf "/stringbase %f def\n" (stringbase fs)+ ++ printf "/dotradius %f def\n" (dotradius fs)+ ++ printf "/oplusradius %f def\n" (oplusradius fs)+ ++ printf "/crossradius %f def\n" (crossradius fs)+ ++ printf "/barwidth %f def\n" (barwidth fs)+ ++ printf "/barheight %f def\n" (barheight fs)+ ++ printf "/dwidth %f def\n" (dwidth fs)+ ++ printf "/dheight %f def\n" (dheight fs)+ ++ printf "/maxgatelabelwidth %f def\n" (maxgatelabelwidth fs)+ ++ printf "/maxlabelwidth %f def\n" (maxlabelwidth fs)+ ++ printf "/maxnumberwidth %f def\n" (maxnumberwidth fs)+ ++ "/gatefont { /Times-Roman findfont .5 scalefont setfont } def\n"+ ++ "/labelfont { /Times-Roman findfont .3 scalefont setfont } def\n"+ ++ "/commentfont { /Times-Roman findfont .3 scalefont setfont } def\n"+ ++ "/numberfont { /Times-Roman findfont .5 scalefont setfont } def\n"+ ++ "/labelcolor { 0 0 1 setrgbcolor } def\n"+ ++ "/commentcolor { 1 0.2 0.2 setrgbcolor } def\n"+ ++ "/numbercolor { 0 0.7 0 setrgbcolor } def\n"++-- | PostScript definitions for various drawing subroutines. The+-- subroutines provided are:+-- +-- > x0 y0 x1 y1 line : draw a line from (x0,y0) to (x1,y1)+-- > x0 y0 x1 y1 dashedline : draw a dashed line from (x0,y0) to (x1,y1)+-- > x y h w rect : draw a rectangle of dimensions w x h centered at (x,y)+-- > x y h w oval : draw an oval of dimensions w x h centered at (x,y)+-- > x y dot : draw a filled dot at (x,y)+-- > x y circ : draw an empty dot at (x,y)+-- > x y oplus : draw a "not" gate at (x,y)+-- > x y cross : draw a cross ("swap" gate component) at (x,y)+-- > x y bar : draw an init/term bar at (x,y)+-- > x y dbar : draw a dterm bar at (x,y)+-- > name x y box : draw an empty box at (x,y), big enough to fit name+-- > name x y gate : draw a named box at (x,y)+-- > name x y circgate : draw a named round box at (x,y)+-- > name x y gphase : draw a global phase gate (x,y)+-- > b x y init : draw an "init" gate at (x,y), with state b+-- > b x y term : draw a "term" gate at (x,y), with state b+-- > b x y dterm : draw a "dterm" gate at (x,y), with state b+-- > string x y m b comment : draw a vertical comment at (x,y), with max height m and baseline adjustment b+-- > string x y clabel : draw a wire label at (x,y), x-centered+-- > string x y rlabel : draw a wire label at (x,y), right of x+-- > string x y lnumber : draw a numbered input at (x,y)+-- > string x y rnumber : draw a numbered output at (x,y)++ps_subroutines :: String+ps_subroutines = + "% subroutine definitions\n"+ ++ "/line { moveto lineto stroke } bind def\n"+ ++ "/dashedline { moveto gsave [0.3 0.2] .15 setdash lineto stroke grestore } bind def\n"+ ++ "/rect { /H exch def /W exch def -.5 W mul .5 H mul moveto W 0 rlineto 0 H neg rlineto W neg 0 rlineto closepath } bind def\n"+ ++ "/oval { /H exch def /W exch def gsave .5 W mul .5 H mul scale 0 0 1 0 360 newpath arc gsave 1.0 setgray fill grestore stroke grestore } bind def\n"+ ++ "/dot { dotradius 0 360 newpath arc gsave 0 setgray fill grestore newpath } bind def\n"+ ++ "/circ { dotradius 0 360 newpath arc gsave 1.0 setgray fill grestore stroke } bind def\n"+ ++ "/oplus { gsave translate 0 0 oplusradius 0 360 newpath arc gsave 1.0 setgray fill grestore stroke 0 oplusradius neg 0 oplusradius line oplusradius neg 0 oplusradius 0 line grestore } bind def\n"+ ++ "/cross { gsave translate crossradius dup dup neg dup line crossradius dup neg dup dup neg line grestore } bind def\n"+ ++ "/bar { gsave translate barwidth barheight rect fill grestore } bind def\n"+ ++ "/dbar { gsave translate barwidth 0.5 mul 0 translate dwidth dheight scale -1 -.5 moveto -.5 0 .5 -90 90 arc -1 .5 lineto closepath fill grestore } bind def\n"+ ++ "/box { gsave translate gatefont stringwidth pop /w exch def /w1 w gatepad add def w1 gateheight rect gsave 1.0 setgray fill grestore stroke grestore } bind def\n"+ ++ "/gate { gsave translate dup gatefont stringwidth pop /w exch def /fontscale w maxgatelabelwidth div def /fontscale fontscale 1 le {1} {fontscale} ifelse def /w2 w fontscale div def /w1 w2 gatepad add def w1 gateheight rect gsave 1.0 setgray fill grestore stroke 1 fontscale div dup scale 0 .5 w mul sub -0.5 stringbase mul moveto show grestore } bind def\n"+ ++ "/circgate { gsave translate dup gatefont stringwidth pop /w exch def /fontscale w maxgatelabelwidth div def /fontscale fontscale 1 le {1} {fontscale} ifelse def /w2 w fontscale div def /w1 w2 gatepad add def w1 0.8 gateheight mul oval gsave 1.0 setgray fill grestore stroke 1 fontscale div dup scale 0 .5 w mul sub -0.5 stringbase mul moveto show grestore } bind def\n"+ ++ "/gphase { gsave translate 0 -0.5 circgate grestore } bind def\n"+ ++ "/init { gsave translate dup gatefont stringwidth pop /w exch def /w1 w gatepad add def -.5 w1 mul 0 translate 0.5 w1 mul 0 bar 0 .5 w mul sub -0.5 stringbase mul moveto show grestore } bind def\n"+ ++ "/term { gsave translate dup gatefont stringwidth pop /w exch def /w1 w gatepad add def .5 w1 mul 0 translate -0.5 w1 mul 0 bar 0 .5 w mul sub -0.5 stringbase mul moveto show grestore } bind def\n"+ ++ "/dterm { gsave translate dup gatefont stringwidth pop /w exch def /w1 w gatepad add def .5 w1 mul 0 translate -0.5 w1 mul 0 dbar 0 .5 w mul sub -0.5 stringbase mul moveto show grestore } bind def\n"+ ++ "/comment { gsave /b exch def /maxh exch def /y exch def /x exch def commentfont commentcolor x y maxh sub x y 0.4 add 1.0 b textbox grestore } bind def\n"+ ++ "/clabel { gsave translate dup labelfont stringwidth pop /w exch def /fontscale w maxlabelwidth 2 mul div def /fontscale fontscale 1 le {1} {fontscale} ifelse def 0 0.15 translate 1 fontscale div dup scale -0.5 w mul 0 moveto labelcolor show grestore } bind def\n"+ ++ "/rlabel { gsave translate dup labelfont stringwidth pop /w exch def /fontscale w maxlabelwidth div def /fontscale fontscale 1 le {1} {fontscale} ifelse def 0 0.15 translate 1 fontscale div dup scale 0 0 moveto labelcolor show grestore } bind def\n"+ ++ "/lnumber { gsave translate dup numberfont stringwidth pop /w exch def /fontscale w maxnumberwidth div def /fontscale fontscale 1 le {1} {fontscale} ifelse def -0.2 -0.15 translate 1 fontscale div dup scale -1 w mul 0 moveto numbercolor show grestore } bind def\n"+ ++ "/rnumber { gsave translate dup numberfont stringwidth pop /w exch def /fontscale w maxnumberwidth div def /fontscale fontscale 1 le {1} {fontscale} ifelse def 0.2 -0.15 translate 1 fontscale div dup scale 0 0 moveto numbercolor show grestore } bind def\n"+ +-- | @'page_of_ocircuit' name ocirc@: Render the circuit /ocirc/ on a+-- single page.+-- +-- The rendering takes place in the following user coordinate system:+-- +-- \[image coord.png]+page_of_ocircuit :: FormatStyle -> Maybe BoxId -> OCircuit -> Document ()+page_of_ocircuit fs boxid ocirc = do+ newpage bboxx bboxy $ do+ when (isJust boxid) $ do+ comment ("drawing commands for " ++ string_of_boxid (fromJust boxid))+ + -- set up the user coordinate system+ scale sc sc+ translate ((xoff fs) + 1) 1+ + -- drawing commands+ setlinewidth (linewidth fs)+ when (isJust boxid) $ do+ textbox align_left (gatefont fs) (foregroundcolor fs) (-(xoff fs)) (raw_height-0.25) raw_width (raw_height-0.25) (stringbase fs) ("Subroutine " ++ string_of_boxid (fromJust boxid) ++ ":")+ rendered_wires+ rendered_gates+ render_ordering fs (-(xoff fs)) ys False w_in+ render_ordering fs raw_width ys True w_out+ where+ -- unit scale: distance, in points, between wires+ sc = 10+ + -- decompose OCircuit+ OCircuit (w_in, circ, w_out) = ocirc+ (a1,gs,a2,_) = circ+ + -- figure out y-coordinates and height+ ws = wirelist_of_circuit circ+ raw_height = fromIntegral $ length ws+ ys = Map.fromList (zip (reverse ws) [0.0 ..])+ maxh = raw_height + 0.3+ bboxy = sc * (raw_height + 1)+ + -- figure out x-coordinates and width+ (raw_width,xgs) = assign_x_coordinates fs gs 0.0+ bboxx = sc * (raw_width + (xoff fs) + 2.0)+ + xa1 = IntMap.map (\t -> (t, -(xoff fs))) a1+ (rendered_wires, rendered_gates) = render_gates fs (Map.fromList (IntMap.assocs xa1)) xgs ys raw_width maxh++-- | Render a low-level boxed quantum circuit as a graphical+-- 'Document'. If there are subroutines, each of them is placed on a+-- separate page.+render_bcircuit :: FormatStyle -> BCircuit -> Document ()+render_bcircuit fs (circ, namespace) = do+ page_of_ocircuit fs Nothing (OCircuit ([], circ, []))+ sequence_ [ page_of_ocircuit fs (Just boxid) ocirc | (boxid, TypedSubroutine ocirc _ _ _) <- Map.toList namespace]++-- | Render a low-level dynamic quantum circuit as a graphical+-- 'Document'. If there are subroutines, each of them is placed on a+-- separate page. If the circuit uses dynamic lifting, an error is+-- produced.+render_dbcircuit :: FormatStyle -> ErrMsg -> DBCircuit a -> Document ()+render_dbcircuit fs e dbcirc = render_bcircuit fs bcirc where+ (bcirc, _) = bcircuit_of_static_dbcircuit errmsg dbcirc+ errmsg x = e ("operation not permitted during graphical rendering: " ++ x)++-- | Print a representation of a low-level quantum circuit, in the+-- requested graphics format, directly to standard output. If there+-- are boxed subcircuits, each of them is placed on a separate page.+print_bcircuit_format :: FormatStyle -> BCircuit -> IO ()+print_bcircuit_format fs bcirc =+ render_custom_stdout (renderformat fs) cust (render_bcircuit fs bcirc)+ where+ cust = custom {+ creator = "Quipper",+ ps_defs = ps_parameters fs ++ ps_subroutines + }++-- | Print a representation of a low-level dynamic quantum circuit, in+-- the requested graphics format, directly to standard output. If+-- there are boxed subcircuits, each of them is placed on a separate+-- page. If the circuit uses dynamic lifting, an error is produced.+print_dbcircuit_format :: FormatStyle -> ErrMsg -> DBCircuit a -> IO ()+print_dbcircuit_format fs e dbcirc = + render_custom_stdout (renderformat fs) cust (render_dbcircuit fs e dbcirc)+ where + cust = custom {+ creator = "Quipper",+ ps_defs = ps_parameters fs ++ ps_subroutines+ }++-- ----------------------------------------------------------------------+-- * Interface to external programs++-- | @'system_pdf_viewer' zoom pdffile@: Call a system-specific PDF+-- viewer on /pdffile/ file. The /zoom/ argument is out of 100 and may+-- or may not be ignored by the viewer.+system_pdf_viewer :: Double -> String -> IO ()+system_pdf_viewer zoom pdffile = do+ envList <- getEnvironment+ if (List.elem ("OS", "Windows_NT") envList)+ then do+ rawSystem "acroread.bat" [pdffile]+ else if (os == "darwin")+ then do+ rawSystem "open" [pdffile]+ rawSystem "sleep" ["1"] -- required or the file may be deleted too soon+ else do+ rawSystem "acroread" ["/a", "zoom=100", pdffile]+ return ()++-- ----------------------------------------------------------------------+-- * Previewing++-- | Display a document directly in Acrobat Reader. This may not be+-- portable. It requires the external program \"acroread\" to be+-- installed.+preview_document :: Document a -> IO a+preview_document = preview_document_custom custom++-- | Display a document directly in Acrobat Reader. This may not be+-- portable. It requires the external program \"acroread\" to be+-- installed.+preview_document_custom :: Custom -> Document a -> IO a+preview_document_custom custom doc = do+ tmpdir <- getTemporaryDirectory+ (pdffile, fd) <- openTempFile tmpdir "Quipper.pdf"+ a <- render_custom_file fd Format_PDF custom doc+ hClose fd+ system_pdf_viewer 100 pdffile+ removeFile pdffile+ return a++-- | Display the circuit directly in Acrobat Reader. This may not be+-- portable. It requires the external program \"acroread\" to be+-- installed.+preview_bcircuit :: BCircuit -> IO ()+preview_bcircuit bcirc =+ preview_document doc+ where+ doc = render_bcircuit pdf bcirc++-- | Display a low-level dynamic quantum circuit directly in Acrobat+-- Reader. This may not be portable. It requires the external program+-- \"acroread\" to be installed. If the circuit uses dynamic lifting,+-- an error is produced.+preview_dbcircuit :: ErrMsg -> DBCircuit a -> IO ()+preview_dbcircuit e dbcirc = preview_bcircuit bcirc where+ (bcirc, _) = bcircuit_of_static_dbcircuit errmsg dbcirc+ errmsg x = e ("operation not permitted for PDF preview: " ++ x)++-- ----------------------------------------------------------------------+-- * Gate counts++-- ** Gate types++-- $ The type 'Gate' contains too much information to be used as the +-- index for counting gates: all 'CNot' gates, for instance,+-- should be counted together, regardless of what wires they are+-- applied to.+--+-- We define 'Gatetype' to remedy this, with each value of +-- 'Gatetype' corresponding to an equivalence class of+-- gates as they should appear in gate counts.+--+-- During gate counting, a little more information needs to be retained,+-- so that operations such as adding controls to subroutine counts can+-- be accurately performed. 'AnnGatetype' supplies this information.++-- | An abbreviated representation of the controls of a gate: +-- the number of positive and negative controls, respectively.+type ControlType = (Int,Int) ++-- | From a list of controls, extract the number of positive and+-- negative controls.+controltype :: Controls -> ControlType+controltype c =+ (length $ filter get_sign c, length $ filter (not . get_sign) c)++-- | Convenience constant for uncontrolled gates.+nocontrols :: ControlType+nocontrols = (0,0)++-- | A data type representing equivalence classes of basic gates,+-- for the output of gatecounts.+data Gatetype = + Gatetype String ControlType+ | GatetypeSubroutine BoxId InverseFlag ControlType+ deriving (Eq, Ord, Show)++-- | A data type analogous to 'Gatetype', but with extra annotations,+-- e.g. a 'NoControlFlag', for use in the computation of gate counts.+data AnnGatetype = + AnnGatetype String (Maybe String) ControlType NoControlFlag ControllableFlag+ | AnnGatetypeSubroutine BoxId InverseFlag ControlType NoControlFlag ControllableFlag+ deriving (Eq, Ord, Show)++-- | Forget the annotations of an 'AnnGatetype'+unannotate_gatetype :: AnnGatetype -> Gatetype+unannotate_gatetype (AnnGatetype n _ cs _ _) = Gatetype n cs+unannotate_gatetype (AnnGatetypeSubroutine n i cs _ _) = GatetypeSubroutine n i cs++-- | Add controls to an annotated gate type, or throw an error message if it is not controllable; +-- unless its 'NoControlFlag' is set, in which case leave it unchanged.+add_controls_gatetype :: ErrMsg -> ControlType -> AnnGatetype -> AnnGatetype+add_controls_gatetype e (x',y') g@(AnnGatetype n n_inv (x,y) ncf cf) =+ if ncf then g+ else case cf of+ AllCtl -> AnnGatetype n n_inv (x+x',y+y') ncf cf+ OnlyClassicalCtl -> AnnGatetype n n_inv (x+x',y+y') ncf cf+ NoCtl -> error $ e "add_controls_gatetype: gate " ++ n ++ " is not controllable."++add_controls_gatetype e (x',y') g@(AnnGatetypeSubroutine n inv (x,y) ncf cf) =+ if ncf then g+ else case cf of+ AllCtl -> AnnGatetypeSubroutine n inv (x+x',y+y') ncf cf+ OnlyClassicalCtl -> AnnGatetypeSubroutine n inv (x+x',y+y') ncf cf+ NoCtl -> error $ e "add_controls_gatetype: subroutine " ++ show n ++ " is not controllable."++-- | Reverse an annotated gate type, of throw an error if it is not reversible. +reverse_gatetype :: ErrMsg -> AnnGatetype -> AnnGatetype+reverse_gatetype e g@(AnnGatetype n n_inv cs ncf cf) =+ case n_inv of+ Just n' -> (AnnGatetype n' (Just n) cs ncf cf)+ Nothing -> error $ e "reverse_gatetype: gate " ++ n ++ " is not reversible"+reverse_gatetype e g@(AnnGatetypeSubroutine n inv cs ncf cf) =+ (AnnGatetypeSubroutine n (not inv) cs ncf cf)++-- | Set the 'NoControlFlag' of an annotated gate type to 'True'.+set_ncf_gatetype :: AnnGatetype -> AnnGatetype+set_ncf_gatetype (AnnGatetype n n_inv cs ncf cf) =+ (AnnGatetype n n_inv cs True cf)+set_ncf_gatetype (AnnGatetypeSubroutine n inv cs ncf cf) =+ (AnnGatetypeSubroutine n inv cs True cf)++-- | Helper function for 'gatetype': append a formatted arity to a string.+with_arity :: String -> Int -> String+n `with_arity` a = n ++ ", arity " ++ show a++-- | Convert a given low-level gate to an annotated gate type+gatetype :: Gate -> AnnGatetype+gatetype (QGate n inv ws vs c ncf) =+ AnnGatetype (n' inv') (Just $ n' $ notinv') (controltype c) ncf AllCtl+ where + n' b = (n ++ optional b "*") `with_arity` (length ws + length vs)+ inv' = inv && not (self_inverse n ws vs)+ notinv' = not inv && not (self_inverse n ws vs)+gatetype (QRot n inv t ws vs c ncf) =+ AnnGatetype (n' inv) (Just $ n' $ not inv) (controltype c) ncf AllCtl+ where n' b = (printf "Rot(%s,%f)" (n++ optional b "*") t) `with_arity` (length ws + length vs)+gatetype (GPhase t w c ncf) = + AnnGatetype (phase_name t) (Just $ phase_name (-t)) (controltype c) ncf AllCtl+ where phase_name t = (printf "exp^(%f i pi)" t)+gatetype (CNot w c ncf) = + AnnGatetype "CNot" (Just "CNot") (controltype c) ncf AllCtl+gatetype (CGate n w ws ncf) = + AnnGatetype (n' True) (Just $ n' False) nocontrols ncf AllCtl+ where n' b = n ++ optional b "*" `with_arity` length ws+gatetype (CGateInv n w ws ncf) =+ AnnGatetype (n' False) (Just $ n' True) nocontrols ncf AllCtl+ where n' b = n ++ optional b "*" `with_arity` length ws+gatetype (CSwap w v c ncf) =+ AnnGatetype "CSwap" (Just "CSwap") (controltype c) ncf AllCtl+gatetype (QPrep w ncf) =+ AnnGatetype "Prep" (Just "Unprep") nocontrols ncf NoCtl+gatetype (QUnprep w ncf) = + AnnGatetype "Unprep" (Just "Prep") nocontrols ncf NoCtl+gatetype (QInit b w ncf) =+ AnnGatetype ("Init" ++ b') (Just $ "Term" ++ b') nocontrols ncf NoCtl+ where b' = show $ if b then 1 else 0+gatetype (CInit b w ncf) =+ AnnGatetype ("CInit" ++ b') (Just $ "CTerm" ++ b') nocontrols ncf NoCtl+ where b' = show $ if b then 1 else 0+gatetype (QTerm b w ncf) =+ AnnGatetype ("Term" ++ b') (Just $ "Init" ++ b') nocontrols ncf NoCtl+ where b' = show $ if b then 1 else 0+gatetype (CTerm b w ncf) =+ AnnGatetype ("CTerm" ++ b') (Just $ "CInit" ++ b') nocontrols ncf NoCtl+ where b' = show $ if b then 1 else 0+gatetype (QMeas w) = + AnnGatetype "Meas" Nothing nocontrols False NoCtl+gatetype (QDiscard w) =+ AnnGatetype "Discard" Nothing nocontrols False NoCtl+gatetype (CDiscard w) =+ AnnGatetype "CDiscard" Nothing nocontrols False NoCtl+gatetype (DTerm b w) =+ AnnGatetype "CDiscard" Nothing nocontrols False NoCtl+gatetype (Subroutine boxid inv ws1 a1 ws2 a2 c ncf ctrble reps) =+ AnnGatetypeSubroutine boxid inv (controltype c) ncf ctrble+gatetype (Comment _ inv ws) = AnnGatetype ("Comment") (Just "Comment") nocontrols True NoCtl++-- | Convert a gate type to a human-readable string.+string_of_gatetype :: Gatetype -> String+string_of_gatetype (Gatetype s (c1,c2)) =+ printf "\"%s\"" s+ ++ if c2==0 && c1==0 then "" else+ if c2==0 then printf ", controls %d" c1 else+ printf " controls %d+%d" c1 c2+string_of_gatetype (GatetypeSubroutine boxid i (c1,c2)) =+ "Subroutine" ++ optional i "*" ++ cs ++ ": " ++ string_of_boxid boxid+ where+ cs = if c2==0 && c1==0 then "" else+ if c2==0 then printf ", controls %d" c1 else+ printf " controls %d+%d" c1 c2++-- ** Gate counts++-- | Gate counts of circuits. +type Gatecount = Map Gatetype Integer++-- | Annotated gate counts of circuits. +type AnnGatecount = Map AnnGatetype Integer++-- | Given the (annotated) gatecount of a circuit, return the gatecount of+-- the reverse circuit, or throw an error if any component is not reversible.+reverse_gatecount :: ErrMsg -> AnnGatecount -> AnnGatecount+reverse_gatecount e = Map.mapKeysWith (+) (reverse_gatetype e)++-- | Given the (annotated) gatecount of a circuit, return the gatecount of+-- the same circuit with controls added, or throw an error if any component+-- is not controllable.+add_controls_gatecount :: ErrMsg -> ControlType -> AnnGatecount -> AnnGatecount+add_controls_gatecount e cs = Map.mapKeysWith (+) (add_controls_gatetype e cs)++-- | Set the ncf of all gates in a gatecount to 'True'.+set_ncf_gatecount :: AnnGatecount -> AnnGatecount+set_ncf_gatecount = Map.mapKeysWith (+) set_ncf_gatetype++-- | Remove the annotations from a gatecount.+unannotate_gatecount :: AnnGatecount -> Gatecount+unannotate_gatecount = Map.mapKeysWith (+) unannotate_gatetype++-- | Input a list of items and output a map from items to counts.+-- Example: +-- +-- > count ['a', 'b', 'a'] = Map.fromList [('a',2), ('b',1)]+count :: (Ord a, Num int) => [(int,a)] -> Map a int+count list =+ foldl' (\mp (i,x) -> Map.insertWith' (+) x i mp) Map.empty list ++-- | Count the number of gates of each type in a circuit, with annotations,+-- treating subroutine calls as atomic gates.+anngatecount_of_circuit :: Circuit -> AnnGatecount+anngatecount_of_circuit (_,gs,_,_) = count $ map (\x -> (repeated x, gatetype x)) $ filter (not . is_comment) gs+ where+ is_comment (Comment _ _ _) = True+ is_comment _ = False+ repeated (Subroutine _ _ _ _ _ _ _ _ _ (RepeatFlag repeat)) = repeat+ repeated _ = 1++-- | Count the number of gates of each type in a circuit,+-- treating subroutine calls as atomic gates.+gatecount_of_circuit :: Circuit -> Gatecount+gatecount_of_circuit = unannotate_gatecount . anngatecount_of_circuit++-- | Given an 'AnnGatetype' describing a subroutine call+-- (possibly repeated),+-- and a gate count for the subroutine itself, return the gatecount +-- of the subroutine call.+--+-- (This may be the reverse of the original subroutine, may have+-- controls added, etc.)+gatecount_of_subroutine_call :: ErrMsg -> AnnGatetype -> RepeatFlag -> AnnGatecount -> AnnGatecount+gatecount_of_subroutine_call e (AnnGatetypeSubroutine boxid inv cs ncf ctrble) (RepeatFlag reps) =+ (if inv then reverse_gatecount err_inv else id)+ . (if cs == nocontrols then id+ else case ctrble of+ AllCtl -> add_controls_gatecount err_ctrl cs+ OnlyClassicalCtl -> add_controls_gatecount err_ctrl cs+ NoCtl -> error $ err_ctrble)+ . (if reps == 1 then id else (Map.map (* reps)))+ . (if ncf then set_ncf_gatecount else id) + where+ err_inv = e . (("gatecount_of_subroutine_call, inverting subroutine " ++ longname ++ ": ") ++)+ err_ctrl = e . (("gatecount_of_subroutine_call, controlling subroutine " ++ longname ++ ": ") ++)+ err_ctrble = e $ "gatecount_of_subroutine_call: subroutine " ++ longname ++ " not controllable"+ longname = string_of_boxid boxid+ +gatecount_of_subroutine_call e _ _ = error $ e "internal error (gatecount_of_subroutine_call called on non-subroutine)"++-- | Given a circuit and gatecounts for its subroutines, +-- give an (aggregated) gatecount for the circuit.+anngatecount_of_circuit_with_sub_cts :: ErrMsg -> Map BoxId AnnGatecount -> Circuit -> AnnGatecount+anngatecount_of_circuit_with_sub_cts e sub_cts (_,gs,_,_) =+ foldr action Map.empty gs+ where+ action (Comment _ _ _) = id+ action g@(Subroutine n _ _ _ _ _ _ _ _ reps) = + case Map.lookup n sub_cts of+ Nothing -> error $ e $ "subroutine not found: " ++ show n+ Just n_ct -> flip (Map.unionWith (+)) $+ gatecount_of_subroutine_call e (gatetype g) reps n_ct+ action g = Map.insertWith' (+) (gatetype g) 1++-- | Give the aggregate gate count of a 'BCircuit'; that is, the+-- the total count of basic gates once all subroutines are fully inlined.+aggregate_gatecounts_of_bcircuit :: BCircuit -> Gatecount+aggregate_gatecounts_of_bcircuit (main_circ, namespace)+ = unannotate_gatecount $+ anngatecount_of_circuit_with_sub_cts e sub_cts main_circ+ where+ sub_cts = Map.map (anngatecount_of_circuit_with_sub_cts e sub_cts . circuit_of_typedsubroutine) namespace+ e = ("aggregate_gatecounts_of_bcircuit: " ++)++-- ** Wire usage count++-- | Count by how much a low-level gate changes the number of wires in the arity.++-- Implementation note: writing this function explicitly case-by-case appears+-- very slightly faster (~0.5%), but more fragile/less maintainable.+gate_wires_change :: Gate -> Integer+gate_wires_change g = + let (a_in,a_out) = gate_arity g+ in fromIntegral $ length a_out - length a_in++-- | Find the maximum number of wires used simultaneously in a 'BCircuit',+-- assuming all subroutines inlined. +aggregate_maxwires_of_bcircuit :: BCircuit -> Integer+aggregate_maxwires_of_bcircuit (main_circ, namespace)+ = maxwires_of_circuit_with_sub_maxwires e sub_maxs main_circ+ where+ e = ("aggregate_maxwires_of_bcircuit: " ++)+ sub_maxs = Map.map (maxwires_of_circuit_with_sub_maxwires e sub_maxs . circuit_of_typedsubroutine) namespace++-- | Given a circuit and gatecounts for its subroutines, +-- give an (aggregated) gatecount for the circuit.+maxwires_of_circuit_with_sub_maxwires :: ErrMsg -> Map BoxId Integer -> Circuit -> Integer+maxwires_of_circuit_with_sub_maxwires e sub_maxs (a1,gs,a2,_) =+ snd $ foldl (flip action) (in_wires, in_wires) gs+ where+ in_wires = fromIntegral $ IntMap.size a1+ update w_change (!w_old, !wmax_old) =+-- Implementation note: strictness in this pattern is to avoid putting the whole+-- tower of “max” applications on the stack.+ let w_new = w_old + w_change in (w_new, max wmax_old w_new)+ action g@(Subroutine n _ ws1 _ ws2 _ _ _ _ (RepeatFlag r)) = + case Map.lookup n sub_maxs of+ Nothing -> error $ "subroutine not found: " ++ show n+ Just n_max -> (update $ (fromIntegral $ length ws2) - n_max)+ . (update $ n_max - (fromIntegral $ length ws1))+ action g = update $ gate_wires_change g++-- ** Printing gate counts++-- | Print a gate count, as a table of integers and gate types. +print_gatecount :: Gatecount -> IO ()+print_gatecount cts = mapM_+ (\(gt,k) -> putStr (printf ("%" ++ show max_digits ++ "d: %s\n") k (string_of_gatetype gt)))+ (Map.assocs cts)+ where+ max_digits = maximum $ 5:(map ((1+) . floor . logBase 10 . fromIntegral) (Map.elems cts))++-- | Print the simple gate count, plus summary information, for a simple circuit.+print_gatecounts_circuit :: Circuit -> IO ()+print_gatecounts_circuit circ@(a1,gs,a2,n) = do+ print_gatecount cts+ putStrLn $ printf "Total gates: %d" $ sum $ Map.elems cts+ putStrLn $ printf "Inputs: %d" $ IntMap.size a1+ putStrLn $ printf "Outputs: %d" $ IntMap.size a2+ putStrLn $ printf "Qubits in circuit: %d" n+ where+ cts = gatecount_of_circuit circ++-- | Print gate counts for a boxed circuit:+-- first the simple gate count for each subroutine separately,+-- then the aggregated count for the whole circuit.+print_gatecounts_bcircuit :: BCircuit -> IO ()+print_gatecounts_bcircuit bcirc@(circ@(a1,_,a2,_),namespace) = do+ print_gatecounts_circuit circ+ when (not $ Map.null namespace) $ do+ sequence_ [ (putStrLn "") >> (print_gatecounts_subroutine sub) | sub <- Map.toList namespace ]+ putStrLn ""+ putStrLn "Aggregated gate count:" + let aggregate_cts = aggregate_gatecounts_of_bcircuit bcirc+ maxwires = aggregate_maxwires_of_bcircuit bcirc+ print_gatecount aggregate_cts+ putStrLn $ printf "Total gates: %d" $ sum $ Map.elems aggregate_cts+ putStrLn $ printf "Inputs: %d" $ IntMap.size a1+ putStrLn $ printf "Outputs: %d" $ IntMap.size a2+ putStrLn $ printf "Qubits in circuit: %d" maxwires++-- | Print gate counts for a named subroutine.+print_gatecounts_subroutine :: (BoxId, TypedSubroutine) -> IO ()+print_gatecounts_subroutine (boxid, TypedSubroutine ocirc _ _ _) = do+ putStrLn ("Subroutine: " ++ show name)+ putStrLn ("Shape: " ++ show shape)+ print_gatecounts_circuit circ+ where+ OCircuit (_, circ, _) = ocirc+ BoxId name shape = boxid++-- | Print gate counts for a static 'DBCircuit'. The circuit may not+-- use any dynamic lifting, or else an error will be produced.+print_gatecounts_dbcircuit :: ErrMsg -> DBCircuit a -> IO ()+print_gatecounts_dbcircuit e dbcirc = print_gatecounts_bcircuit bcirc where+ (bcirc, _) = bcircuit_of_static_dbcircuit errmsg dbcirc+ errmsg x = e ("operation not permitted during gate count: " ++ x)++-- ----------------------------------------------------------------------+-- * Printing to multiple formats++-- | Available output formats.++data Format = + EPS -- ^ Encapsulated PostScript graphics.+ | PDF -- ^ Portable Document Format. One circuit per page.+ | PS -- ^ PostScript. One circuit per page.+ | ASCII -- ^ A textual representation of circuits.+ | Preview -- ^ Don't print anything, but preview directly on screen (requires the external program /acroread/).+ | GateCount -- ^ Print statistics on gate counts.+ | CustomStyle FormatStyle+ deriving Show+ +-- | A mapping from lower-case strings (to be used, e.g., with command+-- line options) to available formats.+format_enum :: [(String, Format)]+format_enum = [+ ("eps", EPS),+ ("pdf", PDF),+ ("ps", PS),+ ("postscript", PS),+ ("ascii", ASCII),+ ("preview", Preview),+ ("gatecount", GateCount)+ ]+ +-- | Print a low-level quantum circuit directly to the IO monad, using+-- the specified format.+print_dbcircuit :: Format -> ErrMsg -> DBCircuit a -> IO ()+print_dbcircuit EPS = print_dbcircuit_format eps+print_dbcircuit PDF = print_dbcircuit_format pdf+print_dbcircuit PS = print_dbcircuit_format ps+print_dbcircuit ASCII = print_dbcircuit_ascii+print_dbcircuit Preview = preview_dbcircuit+print_dbcircuit GateCount = print_gatecounts_dbcircuit+print_dbcircuit (CustomStyle fs) = print_dbcircuit_format fs++-- | Print a document to the requested format, which must be one of+-- 'PS', 'PDF', 'EPS', or 'Preview'.+print_of_document :: Format -> Document a -> IO a+print_of_document = print_of_document_custom custom++-- | Like 'print_of_document', but also takes a 'Custom' data+-- structure.+print_of_document_custom :: Custom -> Format -> Document a -> IO a+print_of_document_custom custom PS doc = render_custom_stdout Format_PS custom doc+print_of_document_custom custom PDF doc = render_custom_stdout Format_PDF custom doc+print_of_document_custom custom EPS doc = render_custom_stdout (Format_EPS 1) custom doc+print_of_document_custom custom Preview doc = preview_document_custom custom doc+print_of_document_custom custom format doc = error ("print_of_document: method " ++ show format ++ " can't be used in this context")++-- ======================================================================+-- * Generic printing++-- | Like 'print_unary', but also takes a stub error message.+print_errmsg :: (QCData qa) => ErrMsg -> Format -> (qa -> Circ b) -> qa -> IO ()+print_errmsg e format f shape = print_dbcircuit format e dbcircuit+ where + (in_bind, dbcircuit) = encapsulate_dynamic f shape++-- | Print a circuit generating function to the specified format; this+-- requires a shape parameter.+print_unary :: (QCData qa) => Format -> (qa -> Circ b) -> qa -> IO ()+print_unary = print_errmsg errmsg+ where + errmsg x = "print_unary: " ++ x++-- | Print a circuit generating function to the specified+-- format. Unlike 'print_unary', this can be applied to a+-- circuit-generating function in curried form with /n/ arguments, for+-- any /n >= 0/. It then requires /n/ shape parameters.+-- +-- The type of this heavily overloaded function is difficult to+-- read. In more readable form, it has all of the following types:+-- +-- > print_generic :: Format -> Circ qa -> IO ()+-- > print_generic :: (QCData qa) => Format -> (qa -> Circ qb) -> a -> IO ()+-- > print_generic :: (QCData qa, QCData qb) => Format -> (qa -> qb -> Circ qc) -> a -> b -> IO ()+-- +-- and so forth.+ +print_generic :: (QCData qa, QCurry qfun qa b, Curry fun qa (IO())) => Format -> qfun -> fun+print_generic format f = g where+ f1 = quncurry f+ g1 = print_errmsg errmsg format f1+ g = mcurry g1+ errmsg x = "print_generic: " ++ x++-- | Like 'print_generic', but only works at simple types, and+-- therefore requires no shape parameters.+print_simple :: (QCData qa, QCurry qfun qa b, Curry fun qa (IO()), QCData_Simple qa) => Format -> qfun -> IO ()+print_simple format f = print_errmsg errmsg format f1 fs_shape where+ f1 = quncurry f+ errmsg x = "print_simple: " ++ x