{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LINE 1 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

-- | An experimental adaptation of the code from "Quipper.Algorithms.CL.Types", "Quipper.Algorithms.CL.Math", etc. to build circuits automatically using Template Haskell and "Quipper.Internal.CircLifting".
--
-- Extremely incomplete.

module Quipper.Algorithms.CL.RegulatorTemplate where

import Quipper
import Text.Printf
import Quipper.Libraries.FPReal
import Quipper.Algorithms.CL.Types
import Quipper.Libraries.Arith
import Control.Monad

-- * Auxiliary functions specific to the Class Number algorithm

d_of_ideal :: Ideal -> Integer
d_of_ideal (Ideal bigD m l a b) = d_of_bigD bigD

d_of_idealQ :: IdealQ -> Integer
d_of_idealQ (Ideal bigD m l a b) = d_of_bigD bigD

equal_d_of_ideals :: Ideal -> Ideal -> BoolParam
equal_d_of_ideals i j = if ((d_of_ideal i) == (d_of_ideal j)) then PTrue else PFalse

equal_d_of_idealQs :: IdealQ -> IdealQ -> BoolParam
equal_d_of_idealQs i j = if ((d_of_idealQ i) == (d_of_idealQ j)) then PTrue else PFalse

template_equal_d_of_ideals :: Circ (IdealQ -> Circ (IdealQ -> Circ BoolParam))
template_equal_d_of_ideals = return $ \i -> return $ \j -> return (equal_d_of_idealQs i j)

template_and :: Circ ([Qubit] -> Circ Qubit)
template_and = return $ \qs -> do
  result_qubit <- qinit False
  qnot result_qubit `controlled` qs

m_of_ideal :: Ideal -> IntM
m_of_ideal (Ideal bigD m l a b) = m

l_of_ideal :: Ideal -> IntM
l_of_ideal (Ideal bigD m l a b) = l

a_of_ideal :: Ideal -> IntM
a_of_ideal (Ideal bigD m l a b) = a

b_of_ideal :: Ideal -> IntM
b_of_ideal (Ideal bigD m l a b) = b

template_m_of_ideal :: Circ (IdealQ -> Circ QDInt)
template_m_of_ideal = return $ \(Ideal bigD m l a b) -> return m

template_l_of_ideal :: Circ (IdealQ -> Circ QDInt)
template_l_of_ideal = return $ \(Ideal bigD m l a b) -> return l

template_a_of_ideal :: Circ (IdealQ -> Circ QDInt)
template_a_of_ideal = return $ \(Ideal bigD m l a b) -> return a

template_b_of_ideal :: Circ (IdealQ -> Circ QDInt)
template_b_of_ideal = return $ \(Ideal bigD m l a b) -> return b

one_of_size :: IntM -> IntM
one_of_size n =
  case intm_length n of
    Just m -> intm m 1
    Nothing -> error "one_of_size: indeterminate size"

template_one_of_size :: Circ (QDInt -> Circ QDInt)
template_one_of_size = return (\qx -> qinit $ one_of_size $ qc_false qx)

-- * Functions from "Quipper.Algorithms.CL.Types"

-- | Return 'True' if the given ideal is reduced.

isReduced :: Ideal -> Bool
isReduced i = (m_of_ideal i == (one_of_size (m_of_ideal i)) && (l_of_ideal i == a_of_ideal i))


{-# LINE 83 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| isReduced :: Ideal -> Bool
                      isReduced i = (m_of_ideal i == (one_of_size (m_of_ideal i)) && (l_of_ideal i == a_of_ideal i))


 |] )
{-# LINE 84 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
-- assertReduced can't really be implemented as a quantum circuit. The closest we could
-- define is an asserted termination, that would throw an error at circuit run-time if
-- the assertion doesn't hold.
-- | An assert function will throw an error if the assertion is False.
assert :: Bool -> a -> a
assert True a = a
assert False _ = error "False Assertion"

-- | A hand-lifted version of assert, that will produce a circuit run-time error
-- if the assertion doesn't hold. This is done by using an asserted discard.
template_assert :: Circ (Qubit -> Circ (a -> Circ a))
template_assert = return $ \assertion -> return $ \a -> do
  qterm True assertion
  return a

-- | This will throw an error if the given ideal isn't reduced. 
-- The corresponding circuit will use an asserted termination, that will throw an 
-- error at circuit run-time if the assertion doesn't hold.

assertReduced :: Ideal -> a -> a
assertReduced i rest =
  let assertion = isReduced i in
  assert assertion rest


{-# LINE 107 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| assertReduced :: Ideal -> a -> a
                      assertReduced i rest =
                        let assertion = isReduced i in
                        assert assertion rest


 |] )
{-# LINE 108 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
-- | Check if a given ideal equals another ideal.

idealEquals :: Ideal -> Ideal -> Bool
idealEquals i j = case equal_d_of_ideals i j of
  PFalse -> error "Comparing two ideals of different d"
  PTrue -> and [(m_of_ideal i == m_of_ideal j),
                (l_of_ideal i == l_of_ideal j),
                (a_of_ideal i == a_of_ideal j),
                (b_of_ideal i == b_of_ideal j)]


{-# LINE 117 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| idealEquals :: Ideal -> Ideal -> Bool
                      idealEquals i j = case equal_d_of_ideals i j of
                        PFalse -> error "Comparing two ideals of different d"
                        PTrue -> and [(m_of_ideal i == m_of_ideal j),
                                      (l_of_ideal i == l_of_ideal j),
                                      (a_of_ideal i == a_of_ideal j),
                                      (b_of_ideal i == b_of_ideal j)]


 |] )
{-# LINE 118 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
-- * Functions from "Quipper.Algorithms.CL.CL"

-- * Some dummy functions for testing

data BoolPair = BoolPair Bool Bool

data QubitPair = QubitPair Qubit Qubit

boolPair = BoolPair
template_boolPair = return $ \x -> return $ \y -> return $ QubitPair x y
template_BoolPair = QubitPair


truePair :: BoolPair
truePair = boolPair True True


{-# LINE 133 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| truePair :: BoolPair
                      truePair = boolPair True True


 |] )
{-# LINE 134 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
{-

myAnd :: BoolPair -> Bool
myAnd (BoolPair x y) = x && y

{-# LINE 137 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| myAnd :: BoolPair -> Bool
                      myAnd (BoolPair x y) = x && y
                      
 |] ) 
{-# LINE 138 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
-}


myPlus :: Int -> Int -> Int
myPlus x y = x + y


{-# LINE 143 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| myPlus :: Int -> Int -> Int
                      myPlus x y = x + y


 |] )
{-# LINE 144 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
-- * Some test functions
test_is_reduced :: IO ()
test_is_reduced = let zero = qdint_shape 4
                      ideal = Ideal 17 zero zero zero zero
                  in print_generic ASCII (unpack template_isReduced) ideal

-- * A main function
main :: IO ()
main = test_is_reduced

{-
Notes:

- it’s not quite clear to me what assumptions CircLifting makes about lingering arguments, linearity, etc. e.g.: should 'template_m_of_ideal' and the like return copies of the components, not the components themselves?  Or is this kosher as currently written? 

- I guess at the moment, it *does* assume all input arguments are left lingering, and moreover unmodified (in the sense of the computational basis).  A version not assuming this could be written by using the alternate type translation

#(a -> b) = a -> Circ (a,#b)

- linearity is much more of an issue!  if we write something like 


twice x = x + x


{-# LINE 167 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
$( decToCircMonad [d| twice x = x + x
                      
                      
 |] ) 
{-# LINE 168 "Quipper/Algorithms/CL/RegulatorTemplate.hs" #-}
this may well (depending on implementation of 'template_symb_plus_') build to a non-linear circuit (so, runtime error).  With a little more effort we can write examples which will *not* produce runtime errors, but *will* produce incorrect circuit behaviour.

Solution?  One can imagine writing safe circuits like

add_safe qx qy = if (qd_disjoint qx qy) then add qx qy
  else do
    ((qx,qy),qz) <- with_computed_fun (qx,qy)
      (\(qx,qy) -> do
        (qx,qx') <- qc_copy_fun qx
        (qy,qy') <- qc_copy_fun qy
        return (qx,qy,qx',qy'))
      (\(qx,qy,qx',qy') -> 
        (qx',qy',qz) <- add qx' qy'
        return ((qx,qy,qx',qy'),qz))
    return (qx,qy,qz)

One can even imagine doing this generically, so that add_safe = make_safe q_add.  The necessary assumption is just, I think, that q_add leaves qx, qy unmodified.  Given a QData method qd_disjoint, this would not I think be hard.

This may be the wrong solution to the problem, though.  Thoughts?

- It would be really nice if the lifting could deal better with (a) pattern-matching; (b) numeric literals.  (e.g. how to write an algebraic operation like (p x = 17*x + 5)?  Can write this for 'IntM', but can’t translate it correctly.

-}