{-# LANGUAGE AllowAmbiguousTypes #-}
module Proarrow.Tools.Einsum
( Tensor
, einsum
, Einsum
, EinsumType
, Inputs
, Output
) where
import Data.Containers.ListUtils (nubOrd)
import Data.Kind (Constraint, Type)
import Data.Map.Strict qualified as M
import Data.Proxy (Proxy (..))
import Data.Type.Bool (type (&&), type (||))
import Data.Type.Equality (type (==))
import GHC.TypeLits (CmpChar, ErrorMessage (..), KnownChar, Symbol, TypeError, UnconsSymbol, charVal)
import GHC.TypeNats (Nat, type (+))
import Prelude (Bool (..), Char, Maybe (..), Ordering (..), type (~))
import Prelude qualified as P
import Proarrow.Category.Instance.OpenHypergraph
( Box (..)
, SIMPLIFY
, SomeArrow (..)
, SortList
, Wires
, simplifyHypergraph
, someArrow
, unsafeOpenHypergraph
, unsafePrim
)
import Proarrow.Category.Instance.Product (Fst, Snd)
import Proarrow.Category.Monoidal (State)
import Proarrow.Category.Monoidal.Hypergraph (Hypergraph, Sized)
import Proarrow.Category.Monoidal.Strictified (Fold, type (++))
import Proarrow.Core (CategoryOf (..), Kind)
import Proarrow.Functor (FunctorForRep (..))
import Proarrow.Object (KnownListOf (..), mapListOf, someOfList)
type Tensor :: forall k. [k] -> Type
type Tensor xs = State xs
type Parse :: Symbol -> ([[Char]], [Char])
type Parse s = ParseInputs (UnconsSymbol s) '[] '[]
type Inputs :: Symbol -> [[Char]]
type Inputs s = Fst @ Parse s
type Output :: Symbol -> [Char]
type Output s = Snd @ Parse s
type ParseInputs :: Maybe (Char, Symbol) -> [Char] -> [[Char]] -> ([[Char]], [Char])
type family ParseInputs m cur acc where
ParseInputs 'Nothing cur acc = Implicit (Finish cur acc)
ParseInputs ('Just '( ',', s)) cur acc = ParseInputs (UnconsSymbol s) '[] (Reverse cur ': acc)
ParseInputs ('Just '( ' ', s)) cur acc = ParseInputs (UnconsSymbol s) cur acc
ParseInputs ('Just '( '-', s)) cur acc = ParseArrow (UnconsSymbol s) (Finish cur acc)
ParseInputs ('Just '(c, s)) cur acc = ParseInputs (UnconsSymbol s) (c ': cur) acc
type Finish :: [Char] -> [[Char]] -> [[Char]]
type Finish cur acc = Reverse (Reverse cur ': acc)
type ParseArrow :: Maybe (Char, Symbol) -> [[Char]] -> ([[Char]], [Char])
type family ParseArrow m ins where
ParseArrow ('Just '( '>', s)) ins = '(ins, ParseOutput (UnconsSymbol s) '[])
ParseArrow _ _ = TypeError (Text "Proarrow.Tools.Einsum: expected > after - in the specification")
type Implicit :: [[Char]] -> ([[Char]], [Char])
type Implicit ins = '(ins, Sort (Once (Fold ins) (Fold ins)))
type Once :: [Char] -> [Char] -> [Char]
type family Once cs all where
Once '[] all = '[]
Once (c ': cs) all = OnceIf (Count c all) c (Once cs all)
type OnceIf :: Nat -> Char -> [Char] -> [Char]
type family OnceIf n c cs where
OnceIf 1 c cs = c ': cs
OnceIf _ c cs = cs
type Count :: Char -> [Char] -> Nat
type family Count c cs where
Count c '[] = 0
Count c (c ': cs) = 1 + Count c cs
Count c (d ': cs) = Count c cs
type Sort :: [Char] -> [Char]
type family Sort cs where
Sort '[] = '[]
Sort (c ': cs) = InsertSorted c (Sort cs)
type InsertSorted :: Char -> [Char] -> [Char]
type family InsertSorted c cs where
InsertSorted c '[] = '[c]
InsertSorted c (d ': ds) = InsertOrd (CmpChar c d) c d ds
type InsertOrd :: Ordering -> Char -> Char -> [Char] -> [Char]
type family InsertOrd o c d ds where
InsertOrd 'GT c d ds = d ': InsertSorted c ds
InsertOrd _ c d ds = c ': d ': ds
type ParseOutput :: Maybe (Char, Symbol) -> [Char] -> [Char]
type family ParseOutput m cur where
ParseOutput 'Nothing cur = Reverse cur
ParseOutput ('Just '( ' ', s)) cur = ParseOutput (UnconsSymbol s) cur
ParseOutput ('Just '(c, s)) cur = ParseOutput (UnconsSymbol s) (c ': cur)
type Reverse :: [a] -> [a]
type Reverse xs = ReverseOnto xs '[]
type ReverseOnto :: [a] -> [a] -> [a]
type family ReverseOnto xs acc where
ReverseOnto '[] acc = acc
ReverseOnto (x ': xs) acc = ReverseOnto xs (x ': acc)
type Env :: Kind -> Kind
type Env k = [(Char, k)]
type BindAll :: forall k. [([Char], [k])] -> Env k -> Env k
type family BindAll ts env where
BindAll '[] env = env
BindAll ('(ls, xs) ': ts) env = BindAll ts (Bind ls xs env)
type Bind :: forall k. [Char] -> [k] -> Env k -> Env k
type family Bind ls xs env where
Bind (c ': ls) (x ': xs) env = Bind ls xs (Insert c x env)
Bind _ _ env = env
type Insert :: forall k. Char -> k -> Env k -> Env k
type family Insert c x env where
Insert c x '[] = '[ '(c, x)]
Insert c x ('(c, y) ': env) = '(c, y) ': env
Insert c x (p ': env) = p ': Insert c x env
type Lookup :: forall k. Char -> Env k -> k
type family Lookup c env where
Lookup c ('(c, x) ': env) = x
Lookup c (p ': env) = Lookup c env
type Check :: forall k. [([Char], [k])] -> [Char] -> Env k -> Constraint
type Check ts out env = Letters (LettersOf ts ++ out) (Arities ts (Agreements ts env, CheckOutput out env))
type LettersOf :: forall k. [([Char], [k])] -> [Char]
type family LettersOf ts where
LettersOf '[] = '[]
LettersOf ('(ls, xs) ': ts) = ls ++ LettersOf ts
type Letters :: [Char] -> Constraint -> Constraint
type family Letters ls c where
Letters '[] c = c
Letters (l ': ls) c = LetterIf (IsLetter l) l (Letters ls c)
type LetterIf :: Bool -> Char -> Constraint -> Constraint
type family LetterIf ok l c where
LetterIf 'True l c = c
LetterIf 'False l c =
TypeError (Text "Proarrow.Tools.Einsum: " :<>: ShowType l :<>: Text " is not a letter, so it cannot be an index")
type IsLetter :: Char -> Bool
type IsLetter c = Within 'a' c 'z' || Within 'A' c 'Z'
type Within :: Char -> Char -> Char -> Bool
type Within lo c hi = NotGT (CmpChar lo c) && NotGT (CmpChar c hi)
type NotGT :: Ordering -> Bool
type family NotGT o where
NotGT 'GT = 'False
NotGT _ = 'True
type Arities :: forall k. [([Char], [k])] -> Constraint -> Constraint
type family Arities ts c where
Arities '[] c = c
Arities ('(ls, xs) ': ts) c = ArityError (Len ls == Len xs) ls xs (Arities ts c)
type ArityError :: forall k. Bool -> [Char] -> [k] -> Constraint -> Constraint
type family ArityError ok ls xs c where
ArityError 'True ls xs c = c
ArityError 'False ls xs c =
TypeError
( Text "Proarrow.Tools.Einsum: the tensor with indices "
:<>: ShowType xs
:<>: Text " has letters "
:<>: ShowType ls
)
type Agreements :: forall k. [([Char], [k])] -> Env k -> Constraint
type family Agreements ts env where
Agreements '[] env = ()
Agreements ('(c ': ls, x ': xs) ': ts) env = (Agrees c x env, Agreements ('(ls, xs) ': ts) env)
Agreements ('(ls, xs) ': ts) env = Agreements ts env
type Agrees :: forall k. Char -> k -> Env k -> Constraint
type family Agrees c x env where
Agrees c x ('(c, x) ': env) = ()
Agrees c x ('(c, y) ': env) =
TypeError
( Text "Proarrow.Tools.Einsum: the index "
:<>: ShowType c
:<>: Text " is used with both "
:<>: ShowType y
:<>: Text " and "
:<>: ShowType x
)
Agrees c x (p ': env) = Agrees c x env
type CheckOutput :: forall k. [Char] -> Env k -> Constraint
type family CheckOutput out env where
CheckOutput '[] env = ()
CheckOutput (c ': out) env = (Bound c env, CheckOutput out env)
type Bound :: forall k. Char -> Env k -> Constraint
type family Bound c env where
Bound c ('(c, x) ': env) = ()
Bound c (p ': env) = Bound c env
Bound c '[] =
TypeError
(Text "Proarrow.Tools.Einsum: the output letter " :<>: ShowType c :<>: Text " is not an index of any input")
type Objs :: forall k. [Char] -> Env k -> [k]
type family Objs ls env where
Objs '[] env = '[]
Objs (c ': ls) env = Lookup c env ': Objs ls env
type Len :: [a] -> Nat
type family Len xs where
Len '[] = 0
Len (x ': xs) = 1 + Len xs
type KnownChars :: [Char] -> Constraint
type KnownChars ls = KnownListOf KnownChar ls
chars :: forall ls. (KnownChars ls) => [Char]
chars :: forall (ls :: [Char]). KnownChars ls => [Char]
chars = forall {k} (c :: OB k) (xs :: [k]) r.
(forall (x :: k). c x => r) -> ListOf c xs -> [r]
forall (c :: OB Char) (xs :: [Char]) r.
(forall (x :: Char). c x => r) -> ListOf c xs -> [r]
mapListOf @KnownChar (\ @c -> Proxy x -> Char
forall (n :: Char) (proxy :: Char -> Type).
KnownChar n =>
proxy n -> Char
charVal (forall (t :: Char). Proxy t
forall {k} (t :: k). Proxy t
Proxy @c)) (forall {k} (c :: OB k) (xs :: [k]). KnownListOf c xs => ListOf c xs
forall (c :: OB Char) (xs :: [Char]).
KnownListOf c xs =>
ListOf c xs
listOf @KnownChar @ls)
network
:: forall {k} (os :: [k])
. (SortList os)
=> [([Char], SomeArrow k)]
-> [Char]
-> Wires '[] ~> (Wires os :: SIMPLIFY k)
network :: forall {k} (os :: [k]).
SortList os =>
[([Char], SomeArrow k)] -> [Char] -> Wires '[] ~> Wires os
network [([Char], SomeArrow k)]
tensors [Char]
out =
[SomeSort k]
-> [Int]
-> [Int]
-> [Box (Prim k) Int]
-> DC (SUB (FH (Port '[]))) ~> DC (SUB (FH (Port os)))
forall {s} l (as :: [s]) (bs :: [s]).
(SortList as, SortList bs) =>
[SomeSort s]
-> [Int] -> [Int] -> [Box l Int] -> Wires as ~> Wires bs
unsafeOpenHypergraph
((Char -> SomeSort k) -> [Char] -> [SomeSort k]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
P.fmap (Map Char (SomeSort k)
sortOfLetter Map Char (SomeSort k) -> Char -> SomeSort k
forall k a. Ord k => Map k a -> k -> a
M.!) [Char]
letters)
[]
((Char -> Int) -> [Char] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
P.fmap (Map Char Int
index Map Char Int -> Char -> Int
forall k a. Ord k => Map k a -> k -> a
M.!) [Char]
out)
[Prim k -> [Int] -> [Int] -> Box (Prim k) Int
forall l n. l -> [n] -> [n] -> Box l n
Box (SomeArrow k -> Prim k
forall k. SomeArrow k -> Prim k
unsafePrim SomeArrow k
t) [] ((Char -> Int) -> [Char] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
P.fmap (Map Char Int
index Map Char Int -> Char -> Int
forall k a. Ord k => Map k a -> k -> a
M.!) [Char]
ls) | ([Char]
ls, SomeArrow k
t) <- [([Char], SomeArrow k)]
tensors]
where
letters :: [Char]
letters = [Char] -> [Char]
forall a. Ord a => [a] -> [a]
nubOrd ((([Char], SomeArrow k) -> [Char])
-> [([Char], SomeArrow k)] -> [Char]
forall (t :: Type -> Type) a b.
Foldable t =>
(a -> [b]) -> t a -> [b]
P.concatMap ([Char], SomeArrow k) -> [Char]
forall a b. (a, b) -> a
P.fst [([Char], SomeArrow k)]
tensors)
sortOfLetter :: Map Char (SomeSort k)
sortOfLetter = [(Char, SomeSort k)] -> Map Char (SomeSort k)
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(Char
l, SomeSort k
x) | ([Char]
ls, SomeArrow Sorts as
_ Sorts bs
ys as ~> bs
_) <- [([Char], SomeArrow k)]
tensors, (Char
l, SomeSort k
x) <- [Char] -> [SomeSort k] -> [(Char, SomeSort k)]
forall a b. [a] -> [b] -> [(a, b)]
P.zip [Char]
ls (Sorts bs -> [SomeSort k]
forall {k} (c :: OB k) (xs :: [k]). ListOf c xs -> [SomeOf c]
someOfList Sorts bs
ys)]
index :: Map Char Int
index = [(Char, Int)] -> Map Char Int
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([Char] -> [Int] -> [(Char, Int)]
forall a b. [a] -> [b] -> [(a, b)]
P.zip [Char]
letters [Int
0 ..])
type Einsum :: forall {k}. [[Char]] -> [Char] -> [([Char], [k])] -> Type -> Constraint
class Einsum ins out (ts :: [([Char], [k])]) r where
collect :: [([Char], SomeArrow k)] -> r
instance
(r ~ (Tensor xs -> r'), KnownChars ls, SortList xs, Einsum ins out ('(ls, xs) ': ts) r')
=> Einsum (ls ': ins) out (ts :: [([Char], [k])]) r
where
collect :: [([Char], SomeArrow k)] -> r
collect [([Char], SomeArrow k)]
acc Strictified '[] xs
t = forall (ins :: [[Char]]) (out :: [Char]) (ts :: [([Char], [k])]) r.
Einsum ins out ts r =>
[([Char], SomeArrow k)] -> r
forall {k} (ins :: [[Char]]) (out :: [Char])
(ts :: [([Char], [k])]) r.
Einsum ins out ts r =>
[([Char], SomeArrow k)] -> r
collect @ins @out @('(ls, xs) ': ts) ((forall (ls :: [Char]). KnownChars ls => [Char]
chars @ls, ('[] ~> xs) -> SomeArrow k
forall {s} (as :: [s]) (bs :: [s]).
(SortList as, SortList bs) =>
(as ~> bs) -> SomeArrow s
someArrow '[] ~> xs
Strictified '[] xs
t) ([Char], SomeArrow k)
-> [([Char], SomeArrow k)] -> [([Char], SomeArrow k)]
forall a. a -> [a] -> [a]
: [([Char], SomeArrow k)]
acc)
instance
( env ~ BindAll (Reverse ts) '[]
, Check ts out env
, Hypergraph k
, Sized k
, os ~ Objs out env
, r ~ Tensor os
, SortList os
, KnownChars out
)
=> Einsum '[] out (ts :: [([Char], [k])]) r
where
collect :: [([Char], SomeArrow k)] -> r
collect [([Char], SomeArrow k)]
acc = (Wires '[] ~> Wires os)
-> WireSorts (Wires '[]) ~> WireSorts (Wires os)
forall {k} (a :: SIMPLIFY k) (b :: SIMPLIFY k).
(Hypergraph k, Sized k) =>
(a ~> b) -> WireSorts a ~> WireSorts b
simplifyHypergraph (forall (os :: [k]).
SortList os =>
[([Char], SomeArrow k)] -> [Char] -> Wires '[] ~> Wires os
forall {k} (os :: [k]).
SortList os =>
[([Char], SomeArrow k)] -> [Char] -> Wires '[] ~> Wires os
network @os ([([Char], SomeArrow k)] -> [([Char], SomeArrow k)]
forall a. [a] -> [a]
P.reverse [([Char], SomeArrow k)]
acc) (forall (ls :: [Char]). KnownChars ls => [Char]
chars @out))
einsum :: forall {k} (s :: Symbol) r. (Einsum (Inputs s) (Output s) ('[] :: [([Char], [k])]) r) => r
einsum :: forall {k} (s :: Symbol) r. Einsum (Inputs s) (Output s) '[] r => r
einsum = forall (ins :: [[Char]]) (out :: [Char]) (ts :: [([Char], [k])]) r.
Einsum ins out ts r =>
[([Char], SomeArrow k)] -> r
forall {k} (ins :: [[Char]]) (out :: [Char])
(ts :: [([Char], [k])]) r.
Einsum ins out ts r =>
[([Char], SomeArrow k)] -> r
collect @(Inputs s) @(Output s) @('[] :: [([Char], [k])]) []
type EinsumType :: forall k. Symbol -> [[k]] -> Type
type EinsumType s xss = Arrows (Inputs s) (Output s) (BindAll (Zip (Inputs s) xss) '[])
type Zip :: [a] -> [b] -> [(a, b)]
type family Zip as bs where
Zip (a ': as) (b ': bs) = '(a, b) ': Zip as bs
Zip _ _ = '[]
type Arrows :: forall k. [[Char]] -> [Char] -> Env k -> Type
type family Arrows ins out env where
Arrows '[] out env = Tensor (Objs out env)
Arrows (ls ': ins) out env = Tensor (Objs ls env) -> Arrows ins out env