packages feed

circuits-0.2.0.0: src/Circuit/Sym.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

-- | The free symmetric monoidal category over a base arrow.
--
-- 'Sym' extends the free category (@Circuit.Free.Free@) with explicit
-- monoidal product ('Par') and symmetry ('Swap') syntax.  It is the
-- intermediate layer between @Circuit.Free.Free@ and @Circuit.Net.Net@:
--
-- @
-- Free = Lift + Compose
-- Sym  = Free + Par + Swap
-- Net  = Sym + Knot + Copy + Discard + Plus + Zero
-- @
--
-- The tensor is fixed to @(,)@, matching 'Circuit.Tensor.Action'.
module Circuit.Sym
  ( Sym (..),

    -- * Free monoidal constraint
    FreeSym,
  )
where

import Circuit.Category (Category (..), Discrete (..), (.>))
import Circuit.Channel (Channel (..), Strength (..), Traced (..))
import Circuit.Layer (Layer (..), run, (:~>))
import Circuit.Tensor (Action (..), Tensor (..))
import Prelude hiding (id, (.))

-- $setup
-- >> import Circuit.Layer (run)
-- >> import Circuit.Tensor (Action (..), Tensor (..))
-- >> import Prelude hiding (id, (.))

-- | The free symmetric monoidal category over a base arrow @arr@.
--
-- Four constructors:
--
--   * 'Lift' — embed a base arrow.
--   * 'Compose' — sequential composition.
--   * 'Par' — tensor product of morphisms (parallel composition).
--   * 'Swap' — symmetry / braiding.
data Sym arr a b where
  -- | Embed a base arrow.
  Lift :: arr a b -> Sym arr a b
  -- | Sequential composition.
  --
  -- The 'Ob' constraint on the intermediate object @b@ is carried in the
  -- constructor so folding does not need a 'Discrete' base.
  Compose :: (Ob arr b) => Sym arr b c -> Sym arr a b -> Sym arr a c
  -- | Tensor product of morphisms (parallel composition on disjoint wires).
  Par :: Sym arr a b -> Sym arr c d -> Sym arr (a, c) (b, d)
  -- | Symmetric braiding.
  Swap :: Sym arr (a, b) (b, a)

-- | 'Sym' is a category.
instance (Category arr) => Category (Sym arr) where
  type Ob (Sym arr) a = Ob arr a
  id = Lift id
  (.) = Compose

-- | A discrete base yields a discrete free monoidal category.
instance (Category arr, Discrete arr) => Discrete (Sym arr) where
  withOb @a x = withOb @arr @a x

-- | 'Sym' has a tensor structure whose tensor is @(,)@.
--
-- This is the syntactic instance: 'Par' is its own interpretation.
-- The unitors require the base arrow to have its own cartesian unitors.
instance (Tensor (,) arr) => Tensor (,) (Sym arr) where
  par = Par
  unitl = Lift unitl
  unitl' = Lift unitl'
  unitr = Lift unitr
  unitr' = Lift unitr'

-- | 'Sym' has a symmetric braiding.
--
-- This is the syntactic instance: 'Swap' is its own interpretation.
instance (Tensor (,) arr) => Action (,) (Sym arr) where
  swap = Swap

-- | Lift the 'Channel' structure through 'Sym'.
instance (Category arr, Channel t arr) => Channel t (Sym arr) where
  assoc = Lift assoc
  assoc' = Lift assoc'
  slide = Lift slide

-- | 'Action' plus 'Discrete' so free 'Sym' can fold intermediate objects.
--
-- Sequential structure is folded with the target's category composition.
class (Action (,) arr, Discrete arr) => FreeSym arr

instance (Action (,) arr, Discrete arr) => FreeSym arr

instance Layer Sym where
  type Law Sym arr' = FreeSym arr'
  type Run Sym arr = (Action (,) arr, Discrete arr)
  type Bind Sym arr = Discrete arr
  unit = Lift
  run :: forall arr a b. (Run Sym arr, Ob arr a, Ob arr b) => Sym arr a b -> arr a b
  run (Lift f) = f
  run (Compose g f) = run g . run f
  run (Par (f :: Sym arr a1 b1) (g :: Sym arr c d)) =
    withOb @arr @a1 $
      withOb @arr @b1 $
        withOb @arr @c $
          withOb @arr @d $
            par (run f) (run g)
  run Swap = swap
  bind :: forall arr' arr a b. (Law Sym arr', Bind Sym arr, Ob arr a, Ob arr b, Ob arr' a, Ob arr' b) => (arr :~> arr') -> Sym arr a b -> arr' a b
  bind h (Lift f) = h f
  bind h (Compose @_ @b1 g f) = withOb @arr' @b1 (bind h g . bind h f)
  bind h (Par (f :: Sym arr a1 b1) (g :: Sym arr c d)) =
    withOb @arr @a1 $
      withOb @arr @b1 $
        withOb @arr @c $
          withOb @arr @d $
            withOb @arr' @a1 $
              withOb @arr' @b1 $
                withOb @arr' @c $
                  withOb @arr' @d $
                    par (bind h f) (bind h g)
  bind _ Swap = swap

-- | Lift the 'Strength' structure through 'Sym'.
instance (Strength t arr, Action (,) arr, Discrete arr) => Strength t (Sym arr) where
  strength = Lift . strength . run

-- | Lift the 'Traced' structure through 'Sym'.
--
-- Loop bodies are 'run' into the base arrow before tracing, just as for
-- @Free@.
instance (Traced t arr, Action (,) arr, Discrete arr) => Traced t (Sym arr) where
  trace = Lift . trace . run