-- | This module provides an enumeration type for gate bases. This is
-- useful, for example, to provide a uniform interface to gate base
-- decomposers, so that they can be selected via command line options
-- or function arguments.

module QuipperLib.Decompose.GateBase where

import Quipper
import Quipper.Internal

import QuipperLib.Synthesis
import QuipperLib.Decompose.Legacy
import QuipperLib.Decompose.CliffordT

import Libraries.RandomSource

import System.Random

-- ----------------------------------------------------------------------
-- * Gate bases

-- | An enumeration type for gate bases. More cases can be added in
-- the future, for example for using gates from a particular physical
-- machine description. 
-- 
-- Some gate bases carry additional parameters; for example, in the
-- case of decomposition into a discrete gate base, one may specify a
-- precision ε, a random seed, or other flags.
-- 
-- If a 'Precision' parameter is present, it specifies the desired
-- precision per gate. If a 'RandomSource' parameter is present, it
-- specifies a source of randomness.
-- 
-- If a 'KeepPhase' parameter is present, it determines whether global
-- phases are respected ('True') or disregarded ('False').
data GateBase =
  Logical   
    -- ^ Use all logical gates, i.e., leave the circuit unchanged.
  | Binary  
    -- ^ Decompose into binary gates.
  | Toffoli 
    -- ^ Decompose into Toffoli and binary gates.
  | CliffordT_old 
    -- ^ Decompose into Clifford+/T/. This is a legacy transformer
    -- that does not handle all gates correctly. For example, it does
    -- not handle /W/-gates, rotations, or phase gates. Use
    -- 'CliffordT' instead.
  | CliffordT KeepPhase Precision RandomSource 
    -- ^ Decompose into Clifford+/T/, specifically: single-qubit
    -- Clifford gates, the controlled-not gate (with positive or
    -- negative controls), and the gates /T/ and /T/[sup †].
  | Standard Precision RandomSource
    -- ^ Decompose into the standard gate set, which we define to be
    -- /X/, /Y/, /Z/, /H/, /S/, /S/[sup †], /T/, /T/[sup †], and
    -- /CNOT/. Suppresses global phases.
  | Strict Precision RandomSource
    -- ^ Decompose into /H/, /S/, /T/, /CNOT/ gates only. Suppresses
    -- global phases.
  | Approximate KeepPhase Precision RandomSource
    -- ^ Decompose rotation and phase gates into Clifford+/T/, using
    -- an approximate synthesis algorithm. Other gates are unchanged.
  | Exact 
    -- ^ Decompose gates that can be exactly represented in the
    -- Clifford+/T/ base into that base, specifically: single-qubit
    -- Clifford gates, the controlled-not gate (with positive or
    -- negative controls), and the gates /T/ and /T/[sup †]. Leave
    -- rotation and phase gates unchanged.
  | TrimControls 
    -- ^ Eliminate excess controls from gates.
  deriving Show

-- | An assignment of gate bases to names. Names are given as
-- lower-case strings.
-- 
-- This can be useful, e.g., in the definition of command line
-- options.
-- 
-- In the future, the syntax should be extended so that users can
-- specify parameters (e.g., the precision, random seed) on the
-- command line as well. For now, we just use the default precision.
gatebase_enum :: [(String, GateBase)]
gatebase_enum = [
  ("logical", Logical),
  ("binary", Binary),
  ("toffoli", Toffoli),
  ("cliffordt_old", CliffordT_old),
  ("cliffordt", CliffordT False (30 * digits) rs),
  ("cliffordt_keepphase", CliffordT True (30 * digits) rs),
  ("standard", Standard (30 * digits) rs),
  ("strict", Strict (30 * digits) rs),
  ("approximate", Approximate False (30 * digits) rs),
  ("approximate_keepphase", Approximate True (30 * digits) rs),
  ("exact", Exact),
  ("trimcontrols", TrimControls)
  ]
  where
    rs = RandomSource (read "1" :: StdGen)

-- ----------------------------------------------------------------------
-- * Generic decomposition

-- | Decompose a circuit into gates from the given 'GateBase'. This
-- can be applied to a circuit-generating function in curried form
-- with /n/ arguments, for any /n/ ≥ 0.
-- 
-- The type of this heavily overloaded function is difficult to
-- read. In more readable form, it has all of the following types:
-- 
-- > decompose_generic :: (QCData qa) => GateBase -> Circ qa -> Circ qa
-- > decompose_generic :: (QCData qa, QCData qb) => GateBase -> (qa -> Circ qb) -> (qa -> Circ qb)
-- > decompose_generic :: (QCData qa, QCData qb, QCData qc) => GateBase -> (qa -> qb -> Circ qc) -> (qa -> qb -> Circ qc)
-- 
-- and so forth.

decompose_generic :: (QCData qa, QCData qb, QCurry qfun qa qb) => GateBase -> qfun -> qfun
decompose_generic Logical = id
decompose_generic Binary = decompose_legacy_generic GB_Binary
decompose_generic Toffoli = decompose_legacy_generic GB_Toffoli
decompose_generic CliffordT_old = decompose_legacy_generic GB_CliffordT
decompose_generic (CliffordT kp prec (RandomSource g)) = exact_ct_generic . approx_ct_generic kp prec g
decompose_generic (Standard prec (RandomSource g)) = standard_generic . exact_ct_generic . approx_ct_generic False prec g
decompose_generic (Strict prec (RandomSource g)) = strict_generic . exact_ct_generic . approx_ct_generic False prec g
decompose_generic (Approximate kp prec (RandomSource g)) = approx_ct_generic kp prec g
decompose_generic Exact = exact_ct_generic
decompose_generic TrimControls = trimcontrols_generic