module RC.NTC
( new
, learn
, predict
, par0
, NTCParameters (..)
, DDEModel.Par (..)
, DDEModel.BandpassFiltering (..)
) where
import Numeric.LinearAlgebra
import System.Random ( StdGen
, split
)
import qualified Data.Vector.Storable as V
import qualified Learning
import qualified Numeric.DDE as DDE
import qualified Numeric.DDE.Model as DDEModel
import qualified RC.Helpers as H
newtype Reservoir = Reservoir { _transform :: Matrix Double -> Matrix Double }
data NTCParameters = Par
{ _preprocess :: Matrix Double -> Matrix Double
, _inputWeightsRange :: (Double, Double)
, _inputWeightsGenerator :: StdGen -> (Int, Int) -> (Double, Double) -> Matrix Double
, _postprocess :: Matrix Double -> Matrix Double
, _reservoirModel :: DDEModel.Par
}
data NTC = NTC
{ _inputWeights :: Matrix Double
, _reservoir :: Reservoir
, _outputWeights :: Maybe (Matrix Double)
, _par :: NTCParameters
}
new
:: StdGen
-> NTCParameters
-> (Int, Int, Int)
-> NTC
new g par (ind, nodes, out) =
let iwgen = _inputWeightsGenerator par
iw = iwgen g (nodes, ind) (_inputWeightsRange par)
ntc = NTC { _inputWeights = iw
, _reservoir = genReservoir (_reservoirModel par)
, _outputWeights = Nothing
, _par = par
}
in ntc
par0 :: NTCParameters
par0 = Par
{ _preprocess = id
, _inputWeightsGenerator = H.randMatrix
, _postprocess = H.addBiases
, _inputWeightsRange = undefined
, _reservoirModel = DDEModel.RC { DDEModel._filt = filt'
, DDEModel._rho = 3.25
, DDEModel._fnl = H.hsigmoid (1.09375, 1.5, 0.0)
}
}
where
filt' = DDEModel.BandpassFiltering {
DDEModel._tau = 7.8125e-3
, DDEModel._theta = recip 0.34375
}
genReservoir :: DDEModel.Par -> Reservoir
genReservoir par@DDEModel.RC {
DDEModel._filt = DDEModel.BandpassFiltering { DDEModel._tau = tau }
} = Reservoir _r
where
_r sample = unflatten response
where
flatten' = flatten. tr
unflatten = tr. reshape nodes
oversampling = 1 :: Int
detuning = 1.0 :: Double
nodes = rows sample
delaySamples = round $ detuning * fromIntegral (oversampling * nodes)
trace1 = flatten' sample
trace = trace1 V.++ V.singleton (V.last trace1)
hStep = tau / 2
!(_, !response) = DDE.integHeun2_2D delaySamples hStep (DDEModel.rhs par) (DDE.Input trace)
genReservoir _ = error "Unsupported DDE model"
forwardPass :: NTC
-> Matrix Double
-> Matrix Double
forwardPass NTC { _par = Par { _preprocess = prep, _postprocess = post }
, _inputWeights = iw
, _reservoir = Reservoir res
} sample =
let pipeline = post. res. (iw <>). prep
in pipeline sample
learn
:: NTC
-> Int
-> Matrix Double
-> Matrix Double
-> Either String NTC
learn ntc forgetPts inp out = ntc'
where
state' = (forwardPass ntc inp) ?? (All, Drop forgetPts)
teacher' = out ?? (All, Drop forgetPts)
ntc' = case Learning.learn' state' teacher' of
Nothing -> Left "Cannot create a readout matrix"
w -> Right $ ntc { _outputWeights = w }
predict :: NTC
-> Int
-> Matrix Double
-> Either String (Matrix Double)
predict ntc@NTC { _outputWeights = ow
} forgetPts inp =
case ow of
Nothing -> Left "Please train the NTC first"
Just w -> let y = forwardPass ntc inp
y2 = y ?? (All, Drop forgetPts)
prediction = w <> y2
in Right prediction