circuits-0.2.0.0: src/Circuit/Dagger.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- | The dagger/bimonoid layer of circuit wiring.
--
-- This module collects the algebraic structure that every wire carries in a
-- circuit category:
--
-- * 'CopyDiscard' — the comonoid on channel objects (fan-out of values).
-- * 'MergeZero' — the monoid on channel objects (fan-in of contributions).
-- * 'Bimonoid' — both together, the precondition for 'Circuit.Net.transpose'.
-- * @Dagger@ — the free dagger category over a base arrow, pairing a forward
-- arrow with a backward arrow. 'transpose' is the dagger operation.
--
-- The four structural rows of 'Circuit.Net' ('Circuit.Net.Copy',
-- 'Circuit.Net.Discard', 'Circuit.Net.Plus', 'Circuit.Net.Zero') are exactly
-- the generators of this bimonoid. In a dagger setting,
-- copy and add are adjoint, as are discard and zero. @Dagger@ makes that
-- duality explicit: a dagger wire's forward direction copies while its
-- backward direction adds.
module Circuit.Dagger
( -- * CopyDiscard
CopyDiscard (..),
-- * MergeZero
MergeZero (..),
-- * Bimonoid
Bimonoid,
-- * Dagger
Dagger (..),
transpose,
)
where
import Circuit.Category (Category (..), Discrete (..), (.>))
import Circuit.Channel (Channel (..), Strength (..), Traced (..))
import Circuit.Tensor (Action (..), Tensor (..))
import Prelude hiding (id, (.))
-- $setup
-- >> import Circuit.Dagger
-- >> import Circuit.Tensor (Action (..), Tensor (..))
-- >> import Circuit.Channel (Traced (..))
-- >> import Circuit.Category (Category (..), Discrete (..), (.>))
-- >> import Prelude hiding (id, (.))
-- ---------------------------------------------------------------------------
-- MergeZero: monoid structure on channel objects
-- ---------------------------------------------------------------------------
-- | A commutative monoid on channel objects.
--
-- Not the same as arithmetic '+'; this is the operation by which parallel
-- contributions to the same wire combine. Fan-out on the forward pass
-- becomes fan-in (summation) on the backward pass.
class MergeZero arr a where
-- | Combine two values of the channel type.
plus :: arr (a, a) a
-- | The neutral element.
zero :: arr () a
-- | The unit type carries the trivial monoid.
--
-- .> plus ((), ()) :: ()
-- ()
-- .> zero () :: ()
-- ()
instance MergeZero (->) () where
plus _ = ()
{-# INLINE plus #-}
zero _ = ()
{-# INLINE zero #-}
-- | Numeric carriers. 'plus' is addition, 'zero' is 0.
--
-- .> plus (1, 2) :: Int
-- 3
-- .> zero () :: Int
-- 0
-- .> plus (1.0, 2.0) :: Double
-- 3.0
-- .> zero () :: Double
-- 0.0
instance MergeZero (->) Int where
plus = uncurry (+)
{-# INLINE plus #-}
zero _ = 0
{-# INLINE zero #-}
instance MergeZero (->) Integer where
plus = uncurry (+)
{-# INLINE plus #-}
zero _ = 0
{-# INLINE zero #-}
instance MergeZero (->) Double where
plus = uncurry (+)
{-# INLINE plus #-}
zero _ = 0
{-# INLINE zero #-}
instance MergeZero (->) Float where
plus = uncurry (+)
{-# INLINE plus #-}
zero _ = 0
{-# INLINE zero #-}
-- | Boolean monoid under disjunction.
--
-- Idempotent because @True || True = True@.
--
-- .> plus (True, False) :: Bool
-- True
-- .> zero () :: Bool
-- False
instance MergeZero (->) Bool where
plus = uncurry (||)
{-# INLINE plus #-}
zero _ = False
{-# INLINE zero #-}
-- | Componentwise 'plus' on pairs.
--
-- .> plus ((3, 4), (5, 6)) :: (Int, Int)
-- (8,10)
instance (MergeZero (->) a, MergeZero (->) b) => MergeZero (->) (a, b) where
plus ((a, b), (a', b')) = (plus (a, a'), plus (b, b'))
{-# INLINE plus #-}
zero u = (zero u, zero u)
{-# INLINE zero #-}
-- | Lists via elementwise 'plus', padded with 'zero'.
--
-- For lists of unequal length, the shorter list is implicitly extended
-- with the element 'zero'. The unit is the empty list.
--
-- .> plus ([1, 2], [3, 4, 5]) :: [Int]
-- [4,6,5]
-- .> plus ([], [3, 4, 5]) :: [Int]
-- [3,4,5]
instance (MergeZero (->) a) => MergeZero (->) [a] where
plus (xs, ys) = go xs ys
where
go [] [] = []
go [] (y : ys') = plus (zero (), y) : go [] ys'
go (x : xs') [] = plus (x, zero ()) : go xs' []
go (x : xs') (y : ys') = plus (x, y) : go xs' ys'
{-# INLINE plus #-}
zero _ = []
{-# INLINE zero #-}
-- ---------------------------------------------------------------------------
-- CopyDiscard: comonoid structure on channel objects
-- ---------------------------------------------------------------------------
-- | A cocommutative comonoid on channel objects.
--
-- Laws:
--
-- @
-- fst . copy = id -- left unit
-- snd . copy = id -- right unit
-- (copy × id) . copy = (id × copy) . copy -- coassociativity
-- swap . copy = copy -- cocommutativity
-- @
class CopyDiscard arr a where
-- | Copy a value into a pair.
copy :: arr a (a, a)
-- | Discard a value.
discard :: arr a ()
-- | Both the comonoid and monoid on a channel object.
--
-- A constraint synonym — no instance required. On a cartesian base arrow,
-- every type carries both structures. This is the precondition for
-- 'Circuit.Net.transpose' to be total.
type Bimonoid arr a = (CopyDiscard arr a, MergeZero arr a)
-- | Every type copies for free in a cartesian category (Fox's theorem).
--
-- .> copy (42 :: Int)
-- (42,42)
-- .> discard (42 :: Int)
-- ()
instance CopyDiscard (->) a where
copy a = (a, a)
{-# INLINE copy #-}
discard _ = ()
{-# INLINE discard #-}
-- ---------------------------------------------------------------------------
-- Dagger: the free dagger category over a base arrow
-- ---------------------------------------------------------------------------
-- | The free dagger category over a base arrow.
--
-- @Dagger arr a b@ is a pair of arrows @arr a b@ (forward) and
-- @arr b a@ (backward). Composition is covariant forward, contravariant
-- backward: @Dagger f g . Dagger f' g' = Dagger (f . f') (g' . g)@.
--
-- .> let d = Dagger (+1) (subtract 1) :: Dagger (->) Int Int
-- .> front d 5
-- 6
-- .> back d 6
-- 5
data Dagger arr a b = Dagger
{ -- | The forward direction.
front :: arr a b,
-- | The backward direction.
back :: arr b a
}
-- | The dagger operation: swap forward and backward.
--
-- Involutive: @transpose . transpose = id@.
transpose :: Dagger arr a b -> Dagger arr b a
transpose (Dagger f g) = Dagger g f
instance (Category arr) => Category (Dagger arr) where
type Ob (Dagger arr) a = Ob arr a
id = Dagger id id
{-# INLINE id #-}
Dagger f g . Dagger f' g' = Dagger (f . f') (g' . g)
{-# INLINE (.) #-}
-- | Dagger of a discrete base is discrete.
instance (Discrete arr) => Discrete (Dagger arr) where
withOb @a x = withOb @arr @a x
instance (Strength t arr) => Strength t (Dagger arr) where
strength (Dagger f g) = Dagger (strength f) (strength g)
{-# INLINE strength #-}
instance (Traced t arr) => Traced t (Dagger arr) where
trace (Dagger f g) = Dagger (trace f) (trace g)
{-# INLINE trace #-}
-- | Forward copy, backward add — the bimonoid self-duality.
instance (CopyDiscard arr a, MergeZero arr a) => CopyDiscard (Dagger arr) a where
copy = Dagger copy plus
{-# INLINE copy #-}
discard = Dagger discard zero
{-# INLINE discard #-}
-- | Forward add, backward copy.
instance (CopyDiscard arr a, MergeZero arr a) => MergeZero (Dagger arr) a where
plus = Dagger plus copy
{-# INLINE plus #-}
zero = Dagger zero discard
{-# INLINE zero #-}
instance (Tensor t arr) => Tensor t (Dagger arr) where
par (Dagger f g) (Dagger f' g') = Dagger (par f f') (par g g')
{-# INLINE par #-}
unitl = Dagger unitl unitl'
{-# INLINE unitl #-}
unitl' = Dagger unitl' unitl
{-# INLINE unitl' #-}
unitr = Dagger unitr unitr'
{-# INLINE unitr #-}
unitr' = Dagger unitr' unitr
{-# INLINE unitr' #-}
instance (Action t arr) => Action t (Dagger arr) where
swap = Dagger swap swap
{-# INLINE swap #-}
-- | Lift monoidal structure through @Dagger@.
instance (Channel t arr) => Channel t (Dagger arr) where
assoc = Dagger assoc assoc'
assoc' = Dagger assoc' assoc
slide = Dagger slide slide