packages feed

gasp-1.4.0.0: Algebra/Category/BlockMatrix.hs

{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Algebra.Category.BlockMatrix where

import Algebra.Category
import Algebra.Category.Laws
import Algebra.Category.Objects (Trivial,forallSumType)
import Algebra.Types
import Algebra.Classes
import Prelude (Int,Bool(..),Show(..),($),Semigroup(..),error)
import Test.QuickCheck hiding (scale)
import Test.QuickCheck.Property
import Data.Constraint
import Control.Applicative


data M s a b where
  Zero :: M s a b
  Diag :: s -> M s a a
  (:▵)  :: M s a b -> M s a c ->  M s a (b ⊕ c)
  (:▿)  :: M s b a -> M s c a ->  M s (b ⊕ c) a
  EmptyL :: M s Zero a -- no elements
  EmptyR :: M s a Zero -- no elements
  -- EmptyR and EmptyL are there to make the law unitorR . unitorR_ pass
deriving instance Show s => Show (M s a b)

    
instance (Show s, Additive s, TestEqual s) => TestEqual (M s a b) where
  EmptyR =.= _ = property True
  EmptyL =.= _ = property True
  _ =.= EmptyR = property True
  _ =.= EmptyL = property True
  (a :▵ b) =.= c = case findSplit c of
    (a',b') -> (a =.= a') * (b =.= b')
  (a :▿ b) =.= c = case findSplit' c of
    (a',b') -> (a =.= a') * (b =.= b')
  c =.= (a :▵ b) = case findSplit c of
    (a',b') -> (a =.= a') * (b =.= b')
  c =.= (a :▿ b) = case findSplit' c of
    (a',b') -> (a =.= a') * (b =.= b')
  Zero =.= c = testZero c
  c =.= Zero = testZero c
  (Diag x) =.= (Diag y) = x =.= y

testZero :: (Additive s, TestEqual s) => M s a b -> Property
testZero = \case
     EmptyL -> property True
     EmptyR -> property True
     Zero -> property True
     Diag s -> s =.= zero
     a :▵ b -> testZero a * testZero b
     a :▿ b -> testZero a * testZero b

instance Ring s => Scalable s (M s a b) where
  s *^ c = case c of
    EmptyR -> EmptyR
    EmptyL -> EmptyL
    Zero -> Zero
    Diag x -> Diag (s*x)
    a :▿ b -> (s *^ a) :▿ (s *^ b)
    a :▵ b -> (s *^ a) :▵ (s *^ b)
  
instance Ring s => Category (M s) where
  EmptyL . EmptyR = Zero -- adding zero elements together for each position in the matrix
  EmptyR . _ = EmptyR
  _ . EmptyL = EmptyL
  Zero . _ = Zero
  _ . Zero = Zero
  Diag s . m = s *^ m
  m . Diag s = s *^ m
  (a1 :▵ a2) . b = (a1 . b) :▵ (a2 . b)
  a . (b1 :▿ b2) = (a . b1) :▿ (a . b2)
  (a1 :▿ a2) . (b1 :▵ b2) = a1 . b1 + a2 . b2

  type Obj (M s) = Trivial
  id = Diag one

instance Ring s => Monoidal (⊕) Zero (M s) where
  (⊗) = cartesianCross -- a potential optimisation is that two diagonals will be a new diagonal. Represent diagonals as (sparse) vectors?
  assoc = cartesianAssoc
  assoc_ = cartesianAssoc_
  unitorR = cartesianUnitor
  unitorR_ = id ▿ new

instance Ring s => Symmetric (⊕) Zero (M s)
instance Ring s => Braided (⊕) Zero (M s) where
  swap = (zero ▵ id) ▿ (id ▵ zero)
instance Ring s => Cartesian (⊕) Zero (M s) where
  (▵) = (:▵)
  dis = EmptyR
  exl = id ▿ Zero
  exr = Zero ▿ id

instance Ring s => CoCartesian (⊕) Zero (M s) where
  (▿) = (:▿)
  new = EmptyL
  inl = id ▵ Zero
  inr = Zero ▵ id

instance Additive s => Additive (M s a b) where
  zero = Zero
  Zero + a = a
  a + Zero = a
  EmptyL + _ = EmptyL
  _ + EmptyL = EmptyL
  EmptyR + _ = EmptyR
  _ + EmptyR = EmptyR
  (a :▵ b) + m  = (a + d) :▵ (b + c) where (d,c) = findSplit  m
  m  + (a :▵ b) = (a + d) :▵ (b + c) where (d,c) = findSplit  m
  (a :▿ b) + m  = (a + d) :▿ (b + c) where (d,c) = findSplit' m
  m  + (a :▿ b) = (a + d) :▿ (b + c) where (d,c) = findSplit' m
  Diag s + Diag t = Diag (s + t)

instance Group s => Group (M s a b) where
  negate = \case
    EmptyL -> EmptyL
    EmptyR -> EmptyR
    Zero -> Zero
    Diag d -> Diag (negate d)
    f :▵ g -> negate f :▵ negate g
    f :▿ g -> negate f :▿ negate g

findSplit :: M s a (b ⊕ c) -> (M s a b, M s a c)
findSplit EmptyL = (EmptyL, EmptyL)
findSplit Zero = (Zero,Zero)
findSplit (Diag s) = (Diag s:▿Zero,Zero :▿ Diag s)
findSplit (a :▵ b) = (a,b)
findSplit ((findSplit -> (a1,a2)) :▿ (findSplit -> (b1,b2))) = (a1:▿b1,a2:▿b2)

findSplit' :: M s (b ⊕ c) a -> (M s b a, M s c a)
findSplit' EmptyR = (EmptyR, EmptyR)
findSplit' Zero = (Zero,Zero)
findSplit' (Diag s) = (Diag s:▵Zero,Zero :▵ Diag s)
findSplit' (a :▿ b) = (a,b)
findSplit' ((findSplit' -> (a1,a2)) :▵ (findSplit' -> (b1,b2))) = (a1:▵b1,a2:▵b2)


transpose :: M s a b -> M s b a
transpose = \case
  EmptyL -> EmptyR
  EmptyR -> EmptyL
  Zero -> Zero
  (Diag s) -> Diag s
  (a :▿ b) -> transpose a :▵ transpose b
  (a :▵ b) -> transpose a :▿ transpose b

genMorphism :: Arbitrary s => Ring s => Repr (⊗) One (⊕) Zero a -> Repr (⊗) One (⊕) Zero b -> Gen (M s a b)
genMorphism RZero _ = pure EmptyL
genMorphism _ RZero = pure EmptyR
genMorphism (RPlus x y) b = transpose <$> ((▵) <$> (genMorphism b x) <*> (genMorphism b y))
genMorphism ROne (RPlus x y) = (▵) <$> genMorphism ROne x <*> genMorphism ROne y
genMorphism ROne ROne = Diag <$> arbitrary
genMorphism x _ = error ("genMorphism: " <> show x)


prop_block_matrix :: Property
prop_block_matrix =
  laws_bicartesian @(M Int)
  (testableCat
     (\k -> forallSumType @(⊗) @One @(⊕) @Zero (\t -> k t))
     (\tx ty k -> property $ do
         x <- genMorphism tx ty
         unProperty (k x))
     (\_ _ -> Dict)
     RPlus
     RZero)