on master help sign in
Added LAoP matrix type safe induction definition - Nat and Type dimensions / Utils / Dist
commitd9a4707c18ab374516633596ebc657d76dbb826f authorArmando Santos <armandoifsantos@gmail.com> dateFri 27 Dec 2019, 23:20 (7 years ago)
16 files changed+1207
.gitignore+3 ChangeLog.md+3 LICENSE+30 README.md+1 Setup.hs+2 app/Main.hs+43 package.yaml+48 src/Dist.hs+13 src/Dist/Internal.hs+54 src/Matrix/Nat.hs+392 src/Matrix/Type.hs+471 src/Utils.hs+8 src/Utils/Internal.hs+59 stack.yaml+66 stack.yaml.lock+12 test/Spec.hs+2
.gitignorenew file+3
@@ -0,0 +1,3 @@
1+.stack-work/
2+laop.cabal
3+*~
ChangeLog.mdnew file+3
@@ -0,0 +1,3 @@
1+# Changelog for laop
2+
3+## Unreleased changes
LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright Armando Santos (c) 2019
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Armando Santos nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.mdnew file+1
@@ -0,0 +1 @@
1+# laop
Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
app/Main.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE TypeApplications #-}
3+{-# LANGUAGE DataKinds #-}
4+
5+module Main where
6+
7+import Matrix.Nat
8+import Utils
9+import Dist
10+import GHC.TypeLits
11+import Data.Coerce
12+
13+-- Monty Hall Problem
14+data Outcome = Win | Lose
15+ deriving (Bounded, Enum, Eq, Show)
16+
17+switch :: Outcome -> Outcome
18+switch Win = Lose
19+switch Lose = Win
20+
21+firstChoice :: Dist Outcome 2
22+firstChoice = choose (1/3)
23+
24+secondChoice :: Matrix Double 2 2
25+secondChoice = fromF switch
26+
27+-- Dice sum
28+
29+sumSS :: Natural 6 -> Natural 6 -> Natural 12
30+sumSS = coerceNat (+)
31+
32+sumSSM :: Matrix Double 36 12
33+sumSSM = fromF (uncurry sumSS)
34+
35+die :: Dist (Natural 6) 6
36+die = uniform [(nat 1)..(nat @6 6)]
37+
38+main :: IO ()
39+main = do
40+ prettyPrint (p1 @Double @1 `comp` secondChoice `comp` firstChoice)
41+ prettyPrint (sumSSM `comp` (die >< die))
42+ prettyPrint (bang `comp` sumSSM `comp` (die >< die))
43+
package.yamlnew file+48
@@ -0,0 +1,48 @@
1+name: laop
2+version: 0.1.0.0
3+github: "bolt12/laop"
4+license: BSD3
5+author: "Armando Santos"
6+maintainer: "armandoifsantos@gmail.com"
7+copyright: "MIT"
8+
9+extra-source-files:
10+- README.md
11+- ChangeLog.md
12+
13+# Metadata used when publishing your package
14+# synopsis: Short description of your package
15+# category: Web
16+
17+# To avoid duplicated efforts in documentation and dealing with the
18+# complications of embedding Haddock markup inside cabal files, it is
19+# common to point users to the README.md file.
20+description: Please see the README on GitHub at <https://github.com/bolt12/laop#readme>
21+
22+dependencies:
23+- base >= 4.7 && < 5
24+
25+library:
26+ source-dirs: src
27+
28+executables:
29+ laop-exe:
30+ main: Main.hs
31+ source-dirs: app
32+ ghc-options:
33+ - -threaded
34+ - -rtsopts
35+ - -with-rtsopts=-N
36+ dependencies:
37+ - laop
38+
39+tests:
40+ laop-test:
41+ main: Spec.hs
42+ source-dirs: test
43+ ghc-options:
44+ - -threaded
45+ - -rtsopts
46+ - -with-rtsopts=-N
47+ dependencies:
48+ - laop
src/Dist.hsnew file+13
@@ -0,0 +1,13 @@
1+module Dist
2+ (
3+ Dist(..),
4+ choose,
5+ shape,
6+ linear,
7+ uniform,
8+ negExp,
9+ normal
10+ )
11+ where
12+
13+import Dist.Internal
src/Dist/Internal.hsnew file+54
@@ -0,0 +1,54 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE DataKinds #-}
4+
5+module Dist.Internal
6+ (
7+ Dist(..),
8+ choose,
9+ shape,
10+ linear,
11+ uniform,
12+ negExp,
13+ normal
14+ )
15+ where
16+
17+import Matrix.Nat
18+import Utils
19+
20+type Prob = Double
21+type Dist a m = (FromLists Prob () (FromNat m)) => Matrix Prob 1 m
22+
23+choose :: Prob -> Dist a m
24+choose prob = col [prob, 1 - prob]
25+
26+shape :: (Prob -> Prob) -> [a] -> Dist a m
27+shape _ [] = error "Probability.shape: empty list"
28+shape f xs =
29+ let incr = 1 / fromIntegral (length xs - 1)
30+ ps = map f (iterate (+incr) 0)
31+ in fromFreqs (zip xs ps)
32+
33+linear :: [a] -> Dist a m
34+linear = shape id
35+
36+uniform :: [a] -> Dist a m
37+uniform = shape (const 1)
38+
39+negExp :: [a] -> Dist a m
40+negExp = shape (\x -> exp (-x))
41+
42+normal :: [a] -> Dist a m
43+normal = shape (normalCurve 0.5 0.5)
44+
45+---
46+
47+fromFreqs :: [(a,Prob)] -> Dist a m
48+fromFreqs xs = col (map (\(x,p) -> p/q) xs)
49+ where q = sum $ map snd xs
50+
51+normalCurve :: Prob -> Prob -> Prob -> Prob
52+normalCurve mean dev x =
53+ let u = (x - mean) / dev
54+ in exp (-1/2 * u^(2::Int)) / sqrt (2 * pi)
src/Matrix/Nat.hsnew file+392
@@ -0,0 +1,392 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DerivingVia #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE KindSignatures #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE NoStarIsType #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+
16+module Matrix.Nat
17+ ( -- | LAoP (Linear Algebra of Programming) Inductive Matrix definition.
18+ --
19+ -- LAoP generalises relations and functions treating them as
20+ -- Boolean matrices and in turn consider these as arrows.
21+ -- This library offers many of the combinators mentioned in the work of
22+ -- Macedo (2012) and Oliveira (2012).
23+ --
24+ -- This definition is a wrapper around 'Matrix.Type' but
25+ -- dimensions are type level Naturals. Type inference might not
26+ -- be as desired.
27+ --
28+ -- There exists two type families that make it easier to write
29+ -- matrix dimensions: 'FromNat' and 'Count'. This approach
30+ -- leads to a very straightforward implementation
31+ -- of LAoP combinators.
32+
33+ -- * Type safe matrix representation
34+ Matrix (..),
35+
36+ -- * Auxiliary type families
37+ I.FromNat,
38+ I.Count,
39+ I.Normalize,
40+
41+ -- * Matrix construction and conversion
42+ I.FromLists,
43+ fromLists,
44+ toLists,
45+ toList,
46+ matrixBuilder,
47+ row,
48+ col,
49+ zeros,
50+ ones,
51+ bang,
52+ constant,
53+
54+ -- * Misc
55+ -- ** Get dimensions
56+ columns,
57+ rows,
58+
59+ -- ** Matrix Transposition
60+ tr,
61+
62+ -- ** Matrix "abiding"
63+ abideJS,
64+ abideSJ,
65+
66+ -- * Biproduct approach
67+ -- ** Split
68+ split,
69+ -- *** Projections
70+ p1,
71+ p2,
72+ -- ** Junc
73+ junc,
74+ -- *** Injections
75+ i1,
76+ i2,
77+ -- ** Bifunctors
78+ (-|-),
79+ (><),
80+
81+ -- ** Applicative matrix combinators
82+
83+ -- | Note that given the restrictions imposed it is not possible to
84+ -- implement the standard type classes present in standard Haskell.
85+ -- *** Matrix pairing projections
86+ KhatriP1,
87+ kp1,
88+ KhatriP2,
89+ kp2,
90+
91+ -- *** Matrix pairing
92+ khatri,
93+
94+ -- * Matrix composition and lifting
95+
96+ -- ** Arrow matrix combinators
97+
98+ -- | Note that given the restrictions imposed it is not possible to
99+ -- implement the standard type classes present in standard Haskell.
100+ identity,
101+ comp,
102+ fromF,
103+
104+ -- * Matrix printing
105+ pretty,
106+ prettyPrint
107+ )
108+where
109+
110+import Data.Proxy
111+import GHC.TypeLits
112+import qualified Matrix.Type as I
113+
114+newtype Matrix e (cols :: Nat) (rows :: Nat) = M (I.Matrix e (I.FromNat cols) (I.FromNat rows))
115+ deriving (Show, Num, Eq) via (I.Matrix e (I.FromNat cols) (I.FromNat rows))
116+
117+-- Primitives
118+
119+empty :: Matrix e 0 0
120+empty = M I.Empty
121+
122+one :: e -> Matrix e 1 1
123+one = M . I.One
124+
125+junc ::
126+ (I.FromNat cols3 ~ Either (I.FromNat cols1) (I.FromNat cols2)) =>
127+ Matrix e cols1 rows ->
128+ Matrix e cols2 rows ->
129+ Matrix e cols3 rows
130+junc (M a) (M b) = M (I.Junc a b)
131+
132+split ::
133+ (I.FromNat rows3 ~ Either (I.FromNat rows1) (I.FromNat rows2)) =>
134+ Matrix e cols rows1 ->
135+ Matrix e cols rows2 ->
136+ Matrix e cols rows3
137+split (M a) (M b) = M (I.Split a b)
138+
139+-- Construction
140+
141+fromLists :: (I.FromLists e (I.FromNat cols) (I.FromNat rows)) => [[e]] -> Matrix e cols rows
142+fromLists = M . I.fromLists
143+
144+matrixBuilder ::
145+ (I.FromLists e (I.FromNat cols) (I.FromNat rows), KnownNat (I.Count (I.FromNat cols)), KnownNat (I.Count (I.FromNat rows))) =>
146+ ((Int, Int) -> e) ->
147+ Matrix e cols rows
148+matrixBuilder = M . I.matrixBuilder
149+
150+col :: (I.FromLists e () (I.FromNat rows)) => [e] -> Matrix e 1 rows
151+col = M . I.col
152+
153+row :: (I.FromLists e (I.FromNat cols) ()) => [e] -> Matrix e cols 1
154+row = M . I.row
155+
156+fromF ::
157+ ( Bounded a,
158+ Bounded b,
159+ Enum a,
160+ Enum b,
161+ Eq b,
162+ Num e,
163+ Ord e,
164+ KnownNat (I.Count (I.FromNat cols)),
165+ KnownNat (I.Count (I.FromNat rows)),
166+ I.FromLists e (I.FromNat rows) (I.FromNat cols)
167+ ) =>
168+ (a -> b) ->
169+ Matrix e cols rows
170+fromF = M . I.fromF
171+
172+-- Conversion
173+
174+toLists :: Matrix e cols rows -> [[e]]
175+toLists (M m) = I.toLists m
176+
177+toList :: Matrix e cols rows -> [e]
178+toList (M m) = I.toList m
179+
180+-- Zeros Matrix
181+
182+zeros ::
183+ (Num e, I.FromLists e (I.FromNat cols) (I.FromNat rows), KnownNat (I.Count (I.FromNat cols)), KnownNat (I.Count (I.FromNat rows))) =>
184+ Matrix e cols rows
185+zeros = M I.zeros
186+
187+-- Ones Matrix
188+
189+ones ::
190+ (Num e, I.FromLists e (I.FromNat cols) (I.FromNat rows), KnownNat (I.Count (I.FromNat cols)), KnownNat (I.Count (I.FromNat rows))) =>
191+ Matrix e cols rows
192+ones = M I.ones
193+
194+-- Const Matrix
195+
196+constant ::
197+ (Num e, I.FromLists e (I.FromNat cols) (I.FromNat rows), KnownNat (I.Count (I.FromNat cols)), KnownNat (I.Count (I.FromNat rows))) =>
198+ e ->
199+ Matrix e cols rows
200+constant = M . I.constant
201+
202+-- Bang Matrix
203+
204+bang ::
205+ forall e cols.
206+ (Num e, Enum e, I.FromLists e (I.FromNat cols) (), KnownNat (I.Count (I.FromNat cols))) =>
207+ Matrix e cols 1
208+bang = M I.bang
209+
210+-- Identity Matrix
211+
212+identity ::
213+ (Num e, I.FromLists e (I.FromNat cols) (I.FromNat cols), KnownNat (I.Count (I.FromNat cols))) =>
214+ Matrix e cols cols
215+identity = M I.identity
216+
217+-- Matrix composition (MMM)
218+
219+comp :: (Num e) => Matrix e cr rows -> Matrix e cols cr -> Matrix e cols rows
220+comp (M a) (M b) = M (I.comp a b)
221+
222+p1 ::
223+ ( Num e,
224+ KnownNat (I.Count (I.FromNat n)),
225+ KnownNat (I.Count (I.FromNat m)),
226+ I.FromLists e (I.FromNat n) (I.FromNat m),
227+ I.FromLists e (I.FromNat m) (I.FromNat m),
228+ I.FromNat (m + n) ~ Either (I.FromNat m) (I.FromNat n)
229+ ) =>
230+ Matrix e (m + n) m
231+p1 = M I.p1
232+
233+p2 ::
234+ ( Num e,
235+ KnownNat (I.Count (I.FromNat n)),
236+ KnownNat (I.Count (I.FromNat m)),
237+ I.FromLists e (I.FromNat m) (I.FromNat n),
238+ I.FromLists e (I.FromNat n) (I.FromNat n),
239+ I.FromNat (m + n) ~ Either (I.FromNat m) (I.FromNat n)
240+ ) =>
241+ Matrix e (m + n) n
242+p2 = M I.p2
243+
244+-- Injections
245+
246+i1 ::
247+ ( Num e,
248+ KnownNat (I.Count (I.FromNat n)),
249+ KnownNat (I.Count (I.FromNat rows)),
250+ I.FromLists e (I.FromNat n) (I.FromNat rows),
251+ I.FromLists e (I.FromNat rows) (I.FromNat rows),
252+ I.FromNat (rows + n) ~ Either (I.FromNat rows) (I.FromNat n)
253+ ) =>
254+ Matrix e rows (rows + n)
255+i1 = tr p1
256+
257+i2 ::
258+ ( Num e,
259+ KnownNat (I.Count (I.FromNat rows)),
260+ KnownNat (I.Count (I.FromNat m)),
261+ I.FromLists e (I.FromNat m) (I.FromNat rows),
262+ I.FromLists e (I.FromNat rows) (I.FromNat rows),
263+ I.FromNat (m + rows) ~ Either (I.FromNat m) (I.FromNat rows)
264+ ) =>
265+ Matrix e rows (m + rows)
266+i2 = tr p2
267+
268+-- Dimensions
269+
270+rows :: (KnownNat (I.Count (I.FromNat rows))) => Matrix e cols rows -> Int
271+rows (M m) = I.rows m
272+
273+columns :: (KnownNat (I.Count (I.FromNat cols))) => Matrix e cols rows -> Int
274+columns (M m) = I.columns m
275+
276+-- Coproduct Bifunctor
277+
278+infixl 5 -|-
279+
280+(-|-) ::
281+ ( Num e,
282+ KnownNat (I.Count (I.FromNat j)),
283+ KnownNat (I.Count (I.FromNat k)),
284+ I.FromLists e (I.FromNat k) (I.FromNat k),
285+ I.FromLists e (I.FromNat j) (I.FromNat k),
286+ I.FromLists e (I.FromNat k) (I.FromNat j),
287+ I.FromLists e (I.FromNat j) (I.FromNat j),
288+ I.FromNat (n + m) ~ Either (I.FromNat n) (I.FromNat m),
289+ I.FromNat (k + j) ~ Either (I.FromNat k) (I.FromNat j)
290+ ) =>
291+ Matrix e n k ->
292+ Matrix e m j ->
293+ Matrix e (n + m) (k + j)
294+(-|-) (M a) (M b) = M ((I.-|-) a b)
295+
296+-- Khatri Rao Product and projections
297+
298+class KhatriP1 e (m :: Nat) (k :: Nat) where
299+ kp1 :: Matrix e (m * k) m
300+
301+instance KhatriP1 e 0 k where
302+ kp1 = empty
303+
304+instance
305+ forall e m k.
306+ ( Num e,
307+ KnownNat k,
308+ I.FromLists e (I.FromNat (m * k)) (I.FromNat m),
309+ KnownNat (I.Count (I.FromNat m)),
310+ KnownNat (I.Count (I.FromNat (m * k)))
311+ ) =>
312+ KhatriP1 e m k
313+ where
314+ kp1 = matrixBuilder f
315+ where
316+ offset = fromInteger (natVal (Proxy :: Proxy k))
317+ f (x, y)
318+ | y >= (x * offset) && y <= (x * offset + offset - 1) = 1
319+ | otherwise = 0
320+
321+class KhatriP2 e (m :: Nat) (k :: Nat) where
322+ kp2 :: Matrix e (m * k) k
323+
324+instance KhatriP2 e m 0 where
325+ kp2 = empty
326+
327+instance
328+ forall e m k.
329+ ( Num e,
330+ KnownNat k,
331+ KnownNat (I.Count (I.FromNat k)),
332+ I.FromLists e (I.FromNat (m * k)) (I.FromNat k),
333+ KnownNat (I.Count (I.FromNat m)),
334+ KnownNat (I.Count (I.FromNat (m * k)))
335+ ) =>
336+ KhatriP2 e m k
337+ where
338+ kp2 = matrixBuilder f
339+ where
340+ offset = fromInteger (natVal (Proxy :: Proxy k))
341+ f (x, y)
342+ | x == y || mod (y - x) offset == 0 = 1
343+ | otherwise = 0
344+
345+khatri ::
346+ forall e cols a b.
347+ (Num e, KhatriP1 e a b, KhatriP2 e a b) =>
348+ Matrix e cols a ->
349+ Matrix e cols b ->
350+ Matrix e cols (a * b)
351+khatri a b =
352+ let kp1' = kp1 @e @a @b
353+ kp2' = kp2 @e @a @b
354+ in comp (tr kp1') a * comp (tr kp2') b
355+
356+-- Product Bifunctor (Kronecker)
357+
358+infixl 4 ><
359+
360+(><) ::
361+ forall e m p n q.
362+ (Num e, KhatriP1 e m n, KhatriP2 e m n, KhatriP1 e p q, KhatriP2 e p q) =>
363+ Matrix e m p ->
364+ Matrix e n q ->
365+ Matrix e (m * n) (p * q)
366+(><) a b =
367+ let kp1' = kp1 @e @m @n
368+ kp2' = kp2 @e @m @n
369+ in khatri (comp a kp1') (comp b kp2')
370+
371+-- Matrix abide Junc Split
372+
373+abideJS :: Matrix e cols rows -> Matrix e cols rows
374+abideJS (M m) = M (I.abideJS m)
375+
376+-- Matrix abide Split Junc
377+
378+abideSJ :: Matrix e cols rows -> Matrix e cols rows
379+abideSJ (M m) = M (I.abideSJ m)
380+
381+-- Matrix transposition
382+
383+tr :: Matrix e cols rows -> Matrix e rows cols
384+tr (M m) = M (I.tr m)
385+
386+-- Pretty print
387+
388+pretty :: Show e => Matrix e cols rows -> String
389+pretty (M m) = I.pretty m
390+
391+prettyPrint :: Show e => Matrix e cols rows -> IO ()
392+prettyPrint (M m) = I.prettyPrint m
src/Matrix/Type.hsnew file+471
@@ -0,0 +1,471 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoStarIsType #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE StandaloneDeriving #-}
11+{-# LANGUAGE TypeApplications #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+
16+module Matrix.Type
17+ ( -- | LAoP (Linear Algebra of Programming) Inductive Matrix definition.
18+ --
19+ -- LAoP generalises relations and functions treating them as
20+ -- Boolean matrices and in turn consider these as arrows.
21+ -- This library offers many of the combinators mentioned in the work of
22+ -- Macedo (2012) and Oliveira (2012).
23+ --
24+ -- This definition makes use of the fact that 'Void' is
25+ -- isomorphic to 0 and '()' to 1 and captures matrix
26+ -- dimensions as stacks of 'Either's.
27+ --
28+ -- There exists two type families that make it easier to write
29+ -- matrix dimensions: 'FromNat' and 'Count'. This approach
30+ -- leads to a very straightforward implementation
31+ -- of LAoP combinators.
32+
33+ -- * Type safe matrix representation
34+ Matrix (..),
35+
36+ -- * Auxiliary type families
37+ FromNat,
38+ Count,
39+ Normalize,
40+
41+ -- * Matrix construction and conversion
42+ FromLists,
43+ fromLists,
44+ toLists,
45+ toList,
46+ matrixBuilder,
47+ row,
48+ col,
49+ zeros,
50+ ones,
51+ bang,
52+ constant,
53+
54+ -- * Misc
55+ -- ** Get dimensions
56+ columns,
57+ rows,
58+
59+ -- ** Matrix Transposition
60+ tr,
61+
62+ -- ** Matrix "abiding"
63+ abideJS,
64+ abideSJ,
65+
66+ -- * Biproduct approach
67+ -- ** Split
68+ split,
69+ -- *** Projections
70+ p1,
71+ p2,
72+ -- ** Junc
73+ junc,
74+ -- *** Injections
75+ i1,
76+ i2,
77+ -- ** Bifunctors
78+ (-|-),
79+ (><),
80+
81+ -- ** Applicative matrix combinators
82+
83+ -- | Note that given the restrictions imposed it is not possible to
84+ -- implement the standard type classes present in standard Haskell.
85+ -- *** Matrix pairing projections
86+ KhatriP1,
87+ kp1,
88+ KhatriP2,
89+ kp2,
90+
91+ -- *** Matrix pairing
92+ khatri,
93+
94+ -- * Matrix composition and lifting
95+
96+ -- ** Arrow matrix combinators
97+
98+ -- | Note that given the restrictions imposed it is not possible to
99+ -- implement the standard type classes present in standard Haskell.
100+ identity,
101+ comp,
102+ fromF,
103+
104+ -- * Matrix printing
105+ pretty,
106+ prettyPrint
107+ )
108+ where
109+
110+import Data.Bool
111+import Data.Kind
112+import Data.List
113+import Data.Proxy
114+import Data.Void
115+import GHC.TypeLits
116+
117+-- | Matrix data type
118+data Matrix e cols rows where
119+ Empty :: Matrix e Void Void
120+ One :: e -> Matrix e () ()
121+ Junc :: Matrix e a rows -> Matrix e b rows -> Matrix e (Either a b) rows
122+ Split :: Matrix e cols a -> Matrix e cols b -> Matrix e cols (Either a b)
123+
124+deriving instance (Show e) => Show (Matrix e cols rows)
125+
126+-- | Type family that computes the cardinality of a given type dimension
127+type family Count (d :: Type) where
128+ Count Void = 0
129+ Count () = 1
130+ Count (Either a b) = (+) (Count a) (Count b)
131+ Count (a, b) = (*) (Count a) (Count b)
132+ Count (a -> b) = (^) (Count b) (Count a)
133+
134+-- | Type family that computes of a given type dimension from a fiven natural
135+type family FromNat (n :: Nat) where
136+ FromNat 0 = Void
137+ FromNat 1 = ()
138+ FromNat n = Either () (FromNat (n - 1))
139+
140+-- | Type family that normalizes the representation of a given data
141+-- structure
142+type family Normalize (d :: Type) where
143+ Normalize d = FromNat (Count d)
144+
145+instance Eq e => Eq (Matrix e cols rows) where
146+ Empty == Empty = True
147+ (One a) == (One b) = a == b
148+ (Junc a b) == (Junc c d) = a == c && b == d
149+ (Split a b) == (Split c d) = a == c && b == d
150+
151+instance Num e => Num (Matrix e cols rows) where
152+
153+ Empty + Empty = Empty
154+ (One a) + (One b) = One (a + b)
155+ (Junc a b) + (Junc c d) = Junc (a + c) (b + d)
156+ (Split a b) + (Split c d) = Split (a + c) (b + d)
157+
158+ Empty - Empty = Empty
159+ (One a) - (One b) = One (a - b)
160+ (Junc a b) - (Junc c d) = Junc (a - c) (b - d)
161+ (Split a b) - (Split c d) = Split (a - c) (b - d)
162+
163+ Empty * Empty = Empty
164+ (One a) * (One b) = One (a * b)
165+ (Junc a b) * (Junc c d) = Junc (a * c) (b * d)
166+ (Split a b) * (Split c d) = Split (a * c) (b * d)
167+
168+ abs Empty = Empty
169+ abs (One a) = One (abs a)
170+ abs (Junc a b) = Junc (abs a) (abs b)
171+ abs (Split a b) = Split (abs a) (abs b)
172+
173+ signum Empty = Empty
174+ signum (One a) = One (signum a)
175+ signum (Junc a b) = Junc (signum a) (signum b)
176+ signum (Split a b) = Split (signum a) (signum b)
177+
178+-- Primitives
179+
180+empty :: Matrix e Void Void
181+empty = Empty
182+
183+one :: e -> Matrix e () ()
184+one = One
185+
186+junc :: Matrix e a rows -> Matrix e b rows -> Matrix e (Either a b) rows
187+junc = Junc
188+
189+split :: Matrix e cols a -> Matrix e cols b -> Matrix e cols (Either a b)
190+split = Split
191+
192+-- Construction
193+
194+class FromLists e cols rows where
195+ fromLists :: [[e]] -> Matrix e cols rows
196+
197+instance FromLists e Void Void where
198+ fromLists [] = Empty
199+ fromLists _ = error "Wrong dimensions"
200+
201+instance {-# OVERLAPPING #-} FromLists e () () where
202+ fromLists [[e]] = One e
203+ fromLists _ = error "Wrong dimensions"
204+
205+instance (FromLists e cols ()) => FromLists e (Either () cols) () where
206+ fromLists [(h : t)] = Junc (One h) (fromLists [t])
207+ fromLists _ = error "Wrong dimensions"
208+
209+instance (FromLists e () rows) => FromLists e () (Either () rows) where
210+ fromLists ([h] : t) = Split (One h) (fromLists t)
211+ fromLists _ = error "Wrong dimensions"
212+
213+instance {-# OVERLAPPABLE #-} (FromLists e cols c, FromLists e cols d) => FromLists e cols (Either c d) where
214+ fromLists (h : t) =
215+ let lh = length h
216+ condition = all (== lh) (map length t)
217+ in if lh > 0 && condition
218+ then Split (fromLists [h]) (fromLists t)
219+ else error "Not all rows have the same length"
220+
221+matrixBuilder ::
222+ forall e cols rows.
223+ ( FromLists e cols rows,
224+ KnownNat (Count cols),
225+ KnownNat (Count rows)
226+ ) =>
227+ ((Int, Int) -> e) ->
228+ Matrix e cols rows
229+matrixBuilder f =
230+ let c = fromInteger $ natVal (Proxy :: Proxy (Count cols))
231+ r = fromInteger $ natVal (Proxy :: Proxy (Count rows))
232+ positions = [(a, b) | a <- [0 .. (r - 1)], b <- [0 .. (c - 1)]]
233+ in fromLists . map (map f) . groupBy (\(x, _) (w, _) -> x == w) $ positions
234+
235+col :: (FromLists e () rows) => [e] -> Matrix e () rows
236+col = fromLists . map (: [])
237+
238+row :: (FromLists e cols ()) => [e] -> Matrix e cols ()
239+row = fromLists . (: [])
240+
241+fromF ::
242+ forall a b cols rows e.
243+ ( Bounded a,
244+ Bounded b,
245+ Enum a,
246+ Enum b,
247+ Eq b,
248+ Num e,
249+ Ord e,
250+ KnownNat (Count cols),
251+ KnownNat (Count rows),
252+ FromLists e rows cols
253+ ) =>
254+ (a -> b) ->
255+ Matrix e cols rows
256+fromF f =
257+ let minA = minBound @a
258+ maxA = maxBound @a
259+ minB = minBound @b
260+ maxB = maxBound @b
261+ ccols = fromInteger $ natVal (Proxy :: Proxy (Count cols))
262+ rrows = fromInteger $ natVal (Proxy :: Proxy (Count rows))
263+ elementsA = take ccols [minA .. maxA]
264+ elementsB = take rrows [minB .. maxB]
265+ combinations = (,) <$> elementsA <*> elementsB
266+ combAp = map snd . sort . map (\(a, b) -> if f a == b
267+ then ((fromEnum a, fromEnum b), 1)
268+ else ((fromEnum a, fromEnum b), 0)) $ combinations
269+ mList = buildList combAp rrows
270+ in tr $ fromLists mList
271+ where
272+ buildList [] _ = []
273+ buildList l r = take r l : buildList (drop r l) r
274+
275+-- Conversion
276+
277+toLists :: Matrix e cols rows -> [[e]]
278+toLists Empty = []
279+toLists (One e) = [[e]]
280+toLists (Split l r) = toLists l ++ toLists r
281+toLists (Junc l r) = zipWith (++) (toLists l) (toLists r)
282+
283+toList :: Matrix e cols rows -> [e]
284+toList = concat . toLists
285+
286+-- Zeros Matrix
287+
288+zeros :: (Num e, FromLists e cols rows, KnownNat (Count cols), KnownNat (Count rows)) => Matrix e cols rows
289+zeros = matrixBuilder (const 0)
290+
291+-- Ones Matrix
292+
293+ones :: (Num e, FromLists e cols rows, KnownNat (Count cols), KnownNat (Count rows)) => Matrix e cols rows
294+ones = matrixBuilder (const 1)
295+
296+-- Const Matrix
297+
298+constant :: (Num e, FromLists e cols rows, KnownNat (Count cols), KnownNat (Count rows)) => e -> Matrix e cols rows
299+constant e = matrixBuilder (const e)
300+
301+-- Bang Matrix
302+
303+bang :: forall e cols. (Num e, Enum e, FromLists e cols (), KnownNat (Count cols)) => Matrix e cols ()
304+bang =
305+ let c = fromInteger $ natVal (Proxy :: Proxy (Count cols))
306+ in fromLists [take c [1, 1 ..]]
307+
308+-- Identity Matrix
309+
310+identity :: (Num e, FromLists e cols cols, KnownNat (Count cols)) => Matrix e cols cols
311+identity = matrixBuilder (bool 0 1 . uncurry (==))
312+
313+-- Matrix composition (MMM)
314+
315+comp :: (Num e) => Matrix e cr rows -> Matrix e cols cr -> Matrix e cols rows
316+comp Empty Empty = Empty
317+comp (One a) (One b) = One (a * b)
318+comp (Junc a b) (Split c d) = comp a c + comp b d
319+comp (Split a b) c = Split (comp a c) (comp b c)
320+comp c (Junc a b) = Junc (comp c a) (comp c b)
321+
322+-- Projections
323+
324+p1 :: forall e m n. (Num e, KnownNat (Count n), KnownNat (Count m), FromLists e n m, FromLists e m m) => Matrix e (Either m n) m
325+p1 =
326+ let iden = identity :: Matrix e m m
327+ zero = zeros :: Matrix e n m
328+ in junc iden zero
329+
330+p2 :: forall e m n. (Num e, KnownNat (Count n), KnownNat (Count m), FromLists e m n, FromLists e n n) => Matrix e (Either m n) n
331+p2 =
332+ let iden = identity :: Matrix e n n
333+ zero = zeros :: Matrix e m n
334+ in junc zero iden
335+
336+-- Injections
337+
338+i1 :: (Num e, KnownNat (Count n), KnownNat (Count m), FromLists e n m, FromLists e m m) => Matrix e m (Either m n)
339+i1 = tr p1
340+
341+i2 :: (Num e, KnownNat (Count n), KnownNat (Count m), FromLists e m n, FromLists e n n) => Matrix e n (Either m n)
342+i2 = tr p2
343+
344+-- Dimensions
345+
346+rows :: forall e cols rows. (KnownNat (Count rows)) => Matrix e cols rows -> Int
347+rows _ = fromInteger $ natVal (Proxy :: Proxy (Count rows))
348+
349+columns :: forall e cols rows. (KnownNat (Count cols)) => Matrix e cols rows -> Int
350+columns _ = fromInteger $ natVal (Proxy :: Proxy (Count cols))
351+
352+-- Coproduct Bifunctor
353+
354+infixl 5 -|-
355+
356+(-|-) ::
357+ forall e n k m j.
358+ ( Num e,
359+ KnownNat (Count j),
360+ KnownNat (Count k),
361+ FromLists e k k,
362+ FromLists e j k,
363+ FromLists e k j,
364+ FromLists e j j
365+ ) =>
366+ Matrix e n k ->
367+ Matrix e m j ->
368+ Matrix e (Either n m) (Either k j)
369+(-|-) a b = Junc (comp i1 a) (comp i2 b)
370+
371+-- Khatri Rao Product and projections
372+
373+class KhatriP1 e m k where
374+ kp1 :: Matrix e (Normalize (m, k)) m
375+
376+instance KhatriP1 e Void k where
377+ kp1 = Empty
378+
379+instance
380+ ( Num e,
381+ KnownNat (Count k),
382+ FromLists e (FromNat (Count m * Count k)) m,
383+ KnownNat (Count m),
384+ KnownNat (Count (Normalize (m, k)))
385+ ) =>
386+ KhatriP1 e m k
387+ where
388+ kp1 = matrixBuilder f
389+ where
390+ offset = fromInteger (natVal (Proxy :: Proxy (Count k)))
391+ f (x, y)
392+ | y >= (x * offset) && y <= (x * offset + offset - 1) = 1
393+ | otherwise = 0
394+
395+class KhatriP2 e m k where
396+ kp2 :: Matrix e (Normalize (m, k)) k
397+
398+instance KhatriP2 e m Void where
399+ kp2 = Empty
400+
401+instance
402+ ( Num e,
403+ KnownNat (Count k),
404+ FromLists e (FromNat (Count m * Count k)) k,
405+ KnownNat (Count m),
406+ KnownNat (Count (Normalize (m, k)))
407+ ) =>
408+ KhatriP2 e m k
409+ where
410+ kp2 = matrixBuilder f
411+ where
412+ offset = fromInteger (natVal (Proxy :: Proxy (Count k)))
413+ f (x, y)
414+ | x == y || mod (y - x) offset == 0 = 1
415+ | otherwise = 0
416+
417+khatri :: forall e cols a b. (Num e, KhatriP1 e a b, KhatriP2 e a b) => Matrix e cols a -> Matrix e cols b -> Matrix e cols (Normalize (a, b))
418+khatri a b =
419+ let kp1' = kp1 @e @a @b
420+ kp2' = kp2 @e @a @b
421+ in comp (tr kp1') a * comp (tr kp2') b
422+
423+-- Product Bifunctor (Kronecker)
424+
425+infixl 4 ><
426+
427+(><) :: forall e m p n q. (Num e, KhatriP1 e m n, KhatriP2 e m n, KhatriP1 e p q, KhatriP2 e p q) => Matrix e m p -> Matrix e n q -> Matrix e (Normalize (m, n)) (Normalize (p, q))
428+(><) a b =
429+ let kp1' = kp1 @e @m @n
430+ kp2' = kp2 @e @m @n
431+ in khatri (comp a kp1') (comp b kp2')
432+
433+-- Matrix abide Junc Split
434+
435+abideJS :: Matrix e cols rows -> Matrix e cols rows
436+abideJS (Junc (Split a c) (Split b d)) = Split (Junc (abideJS a) (abideJS b)) (Junc (abideJS c) (abideJS d))
437+abideJS Empty = Empty
438+abideJS (One e) = (One e)
439+abideJS (Junc a b) = Junc (abideJS a) (abideJS b)
440+abideJS (Split a b) = Split (abideJS a) (abideJS b)
441+
442+-- Matrix abide Split Junc
443+
444+abideSJ :: Matrix e cols rows -> Matrix e cols rows
445+abideSJ (Split (Junc a b) (Junc c d)) = Junc (Split (abideSJ a) (abideSJ c)) (Split (abideSJ b) (abideSJ d))
446+abideSJ Empty = Empty
447+abideSJ (One e) = (One e)
448+abideSJ (Junc a b) = Junc (abideSJ a) (abideSJ b)
449+abideSJ (Split a b) = Split (abideSJ a) (abideSJ b)
450+
451+-- Matrix transposition
452+
453+tr :: Matrix e cols rows -> Matrix e rows cols
454+tr Empty = Empty
455+tr (One e) = One e
456+tr (Junc a b) = Split (tr a) (tr b)
457+tr (Split a b) = Junc (tr a) (tr b)
458+
459+-- Pretty print
460+
461+prettyAux :: Show e => [[e]] -> String
462+prettyAux [] = ""
463+prettyAux [[e]] = show e
464+prettyAux [h] = concat $ intersperse " " (map show h)
465+prettyAux (h : t) = concat (intersperse " " (map show h)) ++ "\n" ++ prettyAux t
466+
467+pretty :: Show e => Matrix e cols rows -> String
468+pretty = prettyAux . toLists
469+
470+prettyPrint :: Show e => Matrix e cols rows -> IO ()
471+prettyPrint = putStrLn . pretty
src/Utils.hsnew file+8
@@ -0,0 +1,8 @@
1+module Utils
2+ ( Natural(..),
3+ nat,
4+ coerceNat
5+ )
6+ where
7+
8+import Utils.Internal
src/Utils/Internal.hsnew file+59
@@ -0,0 +1,59 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+
7+module Utils.Internal
8+ ( Natural(..),
9+ nat,
10+ coerceNat
11+ )
12+ where
13+
14+import GHC.TypeLits
15+import Data.Proxy
16+import Data.Coerce
17+
18+newtype Natural (nat :: Nat) = Nat Int
19+ deriving (Show, Read, Eq, Num)
20+
21+nat :: forall n . (KnownNat n) => Int -> Natural n
22+nat i = let nat = fromInteger (natVal (Proxy :: Proxy n))
23+ in if i <= nat
24+ then Nat i
25+ else error "Off limits"
26+
27+coerceNat :: (Int -> Int -> Int) -> (Natural a -> Natural b -> Natural c)
28+coerceNat = coerce
29+
30+instance KnownNat n => Bounded (Natural n) where
31+ minBound = Nat 0
32+ maxBound = Nat $ fromInteger (natVal (Proxy :: Proxy n))
33+
34+instance KnownNat n => Enum (Natural n) where
35+ toEnum = nat
36+ fromEnum (Nat nat) = let val = fromInteger (natVal (Proxy :: Proxy n))
37+ in if nat > val
38+ then error "Off limits"
39+ else nat
40+
41+instance
42+ ( Enum a,
43+ Enum b,
44+ Bounded b
45+ ) =>
46+ Enum (a, b)
47+ where
48+ toEnum i =
49+ let (listB :: [b]) = [minBound .. maxBound]
50+ lengthB = length listB
51+ fstI = div i lengthB
52+ sndI = mod i lengthB
53+ in (toEnum fstI, toEnum sndI)
54+ fromEnum (a, b) =
55+ let (listB :: [b]) = [minBound .. maxBound]
56+ lengthB = length listB
57+ fstI = fromEnum a
58+ sndI = fromEnum b
59+ in fstI * lengthB + sndI
stack.yamlnew file+66
@@ -0,0 +1,66 @@
1+# This file was automatically generated by 'stack init'
2+#
3+# Some commonly used options have been documented as comments in this file.
4+# For advanced use and comprehensive documentation of the format, please see:
5+# https://docs.haskellstack.org/en/stable/yaml_configuration/
6+
7+# Resolver to choose a 'specific' stackage snapshot or a compiler version.
8+# A snapshot resolver dictates the compiler version and the set of packages
9+# to be used for project dependencies. For example:
10+#
11+# resolver: lts-3.5
12+# resolver: nightly-2015-09-21
13+# resolver: ghc-7.10.2
14+#
15+# The location of a snapshot can be provided as a file or url. Stack assumes
16+# a snapshot provided as a file might change, whereas a url resource does not.
17+#
18+# resolver: ./custom-snapshot.yaml
19+# resolver: https://example.com/snapshots/2018-01-01.yaml
20+resolver: lts-14.18
21+
22+# User packages to be built.
23+# Various formats can be used as shown in the example below.
24+#
25+# packages:
26+# - some-directory
27+# - https://example.com/foo/bar/baz-0.0.2.tar.gz
28+# subdirs:
29+# - auto-update
30+# - wai
31+packages:
32+- .
33+# Dependency packages to be pulled from upstream that are not in the resolver.
34+# These entries can reference officially published versions as well as
35+# forks / in-progress versions pinned to a git hash. For example:
36+#
37+# extra-deps:
38+# - acme-missiles-0.3
39+# - git: https://github.com/commercialhaskell/stack.git
40+# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
41+#
42+# extra-deps: []
43+
44+# Override default flag values for local packages and extra-deps
45+# flags: {}
46+
47+# Extra package databases containing global packages
48+# extra-package-dbs: []
49+
50+# Control whether we use the GHC we find on the path
51+# system-ghc: true
52+#
53+# Require a specific version of stack, using version ranges
54+# require-stack-version: -any # Default
55+# require-stack-version: ">=2.1"
56+#
57+# Override the architecture used by stack, especially useful on Windows
58+# arch: i386
59+# arch: x86_64
60+#
61+# Extra directories used by stack for building
62+# extra-include-dirs: [/path/to/dir]
63+# extra-lib-dirs: [/path/to/dir]
64+#
65+# Allow a newer minor version of GHC than the snapshot specifies
66+# compiler-check: newer-minor
stack.yaml.locknew file+12
@@ -0,0 +1,12 @@
1+# This file was autogenerated by Stack.
2+# You should not edit this file by hand.
3+# For more information, please see the documentation at:
4+# https://docs.haskellstack.org/en/stable/lock_files
5+
6+packages: []
7+snapshots:
8+- completed:
9+ size: 524789
10+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/14/18.yaml
11+ sha256: 646be71223e08234131c6989912e6011e01b9767bc447b6d466a35e14360bdf2
12+ original: lts-14.18
test/Spec.hsnew file+2
@@ -0,0 +1,2 @@
1+main :: IO ()
2+main = putStrLn "Test suite not yet implemented"