packages feed

multilinear-io-0.2.1: test/Spec.hs

module Main where

import           Control.Exception.Base
import           Control.Monad.Trans.Either
import           Control.Monad.Trans.Class
import           Multilinear.Class          as Multilinear
import           Multilinear.Generic
import           Multilinear.Generic.Serialize
import qualified Multilinear.Matrix         as Matrix
import qualified Multilinear.Tensor         as Tensor
import qualified Multilinear.Vector         as Vector

-- PARAMETRY SKRYPTU
fi     = signum  -- funkcja aktywacji perceptronu
layers = 10      -- liczba warstw perceptronu

mlp_input         = "test/data/mlp_input.csv"          -- dane uczące dla perceptronu
mlp_expected      = "test/data/mlp_expected.csv"       -- dane oczekiwane dla percepttronu
mlp_classify      = "test/data/mlp_classify.csv"       -- dane do klasyfikacji na nauczonym perceptronie
mlp_output        = "test/data/mlp_output.csv"         -- wyjście perceptronu
hopfield_input    = "test/data/hopfield_input.csv"     -- wzorce do zpamiętania dla sieci Hopfielda
hopfield_classify = "test/data/hopfield_classify.csv"  -- dane do klasyfikacji dla sieci Hopfielda
hopfield_output   = "test/data/hopfield_output.csv"    -- wyjście sieci Hopfielda


-- PERCEPTRON WIELOWARSTWOWY
perceptron :: Int                    -- ns:  liczba neuronów w warstwie
           -> Int                    -- ks:  liczba warstw 
           -> Int                    -- ps:  liczba wektorów uczących
           -> Int                    -- cs:  liczba wektorów do klasyfikacji
           -> (Int -> Tensor Double) -- x t: wejścia uczące w funkcji czasu
           -> (Int -> Tensor Double) -- e t: wyjścia oczekiwane w funkcji czasu
           -> (Int -> Tensor Double) -- c t: dane do klasyfikacji w funkcji czasu
           -> Tensor Double          -- Tensor ("i","t"): zaklasyfikowane dane

perceptron ns ks ps cs x e c =
  let -- wagi startowe
      zero = Tensor.const ("ki",[ks,ns]) ("j",[ns]) 0
      -- wagi w następnym kroku uczącym
      nextWeights w x e =
        let ygen [k] [] = -- tensor wyjść
              if k == 0 then x $| ("j","") 
              else fi <$> w $$| ("k",[k - 1]) $| ("i","j") * ygen [k-1] [] $| ("j","")
            y = Tensor.generate ("k",[ks + 1]) ("",[]) $ \[k] [] -> ygen [k] []
            -- tensor wejścia-wyjścia omega
            om = Tensor.generate ("k",[ks]) ("",[]) $ 
              \[k] [] -> ygen [k + 1] [] $| ("i","") * ygen [k] [] $| ("j","") \/ "j"
            incWgen [k] [] = -- inkrementacyjna propagacja wsteczna
              if k == ks - 1 then x $| ("j","") \/ "j" * (y $$| ("k",[ks-1]) $| ("i","") - e $| ("i",""))
              else Multilinear.transpose (w $$| ("k",[k+1])) $| ("i","b") * 
                   incWgen [k+1] [] $| ("b","c") * 
                   om $$| ("k",[k]) $| ("c","j")
            incW = Tensor.generate ("k",[ks]) ("",[]) $ \[k] [] -> incWgen [k] []
        in  w $| ("ki","j") + incW $| ("ki","j")
      xl = take 2 $ x <$> [0 .. ps - 1]
      el = take 2 $ e <$> [0 .. ps - 1]
      -- uczenie sieci
      learnedNetwork = foldr (\(x,e) w -> nextWeights w x e) zero $ zip xl el
      -- praca nauczonej sieci
      out t = fi <$> learnedNetwork $$| ("k",[ks-1]) $| ("i","j") * c t $| ("j","")
  in  Tensor.generate ("",[]) ("t",[cs]) $ \[] [t] -> out t

-- SIEĆ HOPFIELDA
hopfield :: Int        -- ns: liczba neuronów w sieci
        -> Int         -- ps: liczba wzorców do zapamiętania
        -> Int         -- cs: liczba wzorców do klasyfikacji
        -> Tensor Int  --  x: macierz wektorów do zapamiętania
        -> Tensor Int  --  c: macierz wektorów do sklasyfikowania
        -> Tensor Int  --  macierz sklafyfikowanych wektorów
hopfield ns ps cs x c = 
  let -- 1 - deltaKroneckera
      delta = Matrix.fromIndices "ij" ns ns $ \i j -> if i == j then 0 else 1
      -- wagi sieci ze wzorców
      w = delta * x $| ("i","t") * (x $| ("j","t") \/ "j") * Vector.const "t" ps 1
      -- wyjście sieci: sieć działa rekurencyjnie aż do osiągnięcia stanu stabilnego
      y inp =
        let out = (\x -> if x > 0 then 1 else 0) <$> w $| ("i","j") * inp $| ("j","") 
        in  if out $| ("i","") == inp $| ("i","") then out else y out
      -- klasyfikacja zadanych wektorów
  in  Tensor.generate ("",[]) ("t",[cs]) $ \[] [t] -> y ((c $| ("i","t")) $$| ("t",[t]))

-- OPERACJE WEJŚCIA/WYJŚCIA
prog :: EitherT SomeException IO ()
prog = do
  -- wczytywanie danych
  mlpInput <- fromCSV "tj" mlp_input ';'
  mlpExp   <- fromCSV "tj" mlp_expected ';'
  mlpClas  <- fromCSV "tj" mlp_classify ';'
  hopInput <- fromCSV "tj" hopfield_input ';'
  hopClas  <- fromCSV "tj" hopfield_classify ';'
  let mx t = Multilinear.transpose $ mlpInput $$| ("t",[t])
  let me t = Multilinear.transpose $ mlpExp $$| ("t",[t])
  let mc t = Multilinear.transpose $ mlpClas $$| ("t",[t])
  let hx = Multilinear.transpose $ hopInput $| ("it",[])
  let hc = Multilinear.transpose $ hopClas $| ("it",[])
  let (ns_mlp,ps_mlp,cs_mlp,ns_hop,ps_hop,cs_hop) = 
         (mlpInput `size` "j", mlpExp `size` "t", mlpClas `size` "t", 
         hopInput `size` "j", hopInput `size` "t", hopClas `size` "t")
  -- perceptron
  let mlp_net = perceptron ns_mlp layers ps_mlp cs_mlp mx me mc
  smlp <- lift $ toCSV mlp_net mlp_output ';'
  -- hopfield
  let hop_net = hopfield ns_hop ps_hop cs_hop hx hc
  shop <- lift $ toCSV hop_net hopfield_output ';'
  lift $ putStrLn $ "Perceptron: " ++ show smlp ++ " vectors saved to '" ++ mlp_output ++ "'."
  lift $ putStrLn $ "Hopfield: " ++ show shop ++ " vectors saved to '" ++ hopfield_output ++ "'."
  return ()

-- ENTRY POINT
main :: IO (Either SomeException ())
main = runEitherT prog