814 lines
33 KiB
Julia
814 lines
33 KiB
Julia
module forward
|
|
|
|
# export
|
|
|
|
using Flux, CUDA
|
|
using GeneralUtils
|
|
using ..type, ..snnUtil
|
|
|
|
#------------------------------------------------------------------------------------------------100
|
|
|
|
""" kfn forward
|
|
input (row, col, batch)
|
|
"""
|
|
function (kfn::kfn_1)(input::AbstractArray)
|
|
|
|
kfn.timeStep .+= 1
|
|
|
|
#TODO time step forward
|
|
if view(kfn.learningStage, 1)[1] == 1
|
|
# reset learning params
|
|
kfn.lif_vt .= 0
|
|
kfn.lif_wRecChange .= 0
|
|
kfn.lif_epsilonRec .= 0
|
|
|
|
kfn.alif_vt .= 0
|
|
kfn.alif_epsilonRec .= 0
|
|
kfn.alif_wRecChange .= 0
|
|
|
|
kfn.on_vt .= 0
|
|
kfn.on_epsilonRec .= 0
|
|
kfn.on_wOutChange .= 0
|
|
|
|
kfn.learningStage = [2]
|
|
end
|
|
|
|
# update activation matrix with "lif_zt1" and "alif_zt1" by concatenating
|
|
# (input, lif_zt1, alif_zt1) to form activation matrix
|
|
_zit = cat(reshape(input, (size(input, 1), size(input, 2), 1, size(input, 3))),
|
|
reshape(kfn.lif_zt, (size(input, 1), :, 1, size(input, 3))),
|
|
reshape(kfn.alif_zt, (size(input, 1), :, 1, size(input, 3))), dims=2)
|
|
kfn.zit .= reshape(_zit, (size(input, 1), :, size(input, 3)))
|
|
|
|
@sync begin
|
|
@async begin
|
|
# project 3D kfn zit into 4D lif zit
|
|
i1, i2, i3, i4 = size(kfn.lif_zit)
|
|
kfn.lif_zit .= reshape(kfn.zit, (i1, i2, 1, i4)) .* kfn.lif_arrayProjection4d
|
|
|
|
lifForward( kfn.lif_zit,
|
|
kfn.lif_wRec,
|
|
kfn.lif_vt,
|
|
kfn.lif_vth,
|
|
kfn.lif_vRest,
|
|
kfn.lif_zt4d,
|
|
kfn.lif_alpha,
|
|
kfn.lif_phi,
|
|
kfn.lif_epsilonRec,
|
|
kfn.lif_refractoryCounter,
|
|
kfn.lif_refractoryDuration,
|
|
kfn.lif_gammaPd,
|
|
kfn.lif_firingCounter,
|
|
kfn.lif_recSignal,)
|
|
end
|
|
@async begin
|
|
# project 3D kfn zit into 4D alif zit
|
|
i1, i2, i3, i4 = size(kfn.alif_zit)
|
|
kfn.alif_zit .= reshape(kfn.zit, (i1, i2, 1, i4)) .* kfn.alif_arrayProjection4d
|
|
|
|
alifForward(kfn.alif_zit,
|
|
kfn.alif_wRec,
|
|
kfn.alif_vt,
|
|
kfn.alif_vth,
|
|
kfn.alif_vRest,
|
|
kfn.alif_zt4d,
|
|
kfn.alif_alpha,
|
|
kfn.alif_phi,
|
|
kfn.alif_epsilonRec,
|
|
kfn.alif_refractoryCounter,
|
|
kfn.alif_refractoryDuration,
|
|
kfn.alif_gammaPd,
|
|
kfn.alif_firingCounter,
|
|
kfn.alif_recSignal,
|
|
kfn.alif_epsilonRecA,
|
|
kfn.alif_a,
|
|
kfn.alif_avth,
|
|
kfn.alif_beta,
|
|
kfn.alif_rho,)
|
|
end
|
|
end
|
|
|
|
# reduce lif_zt4d and alif_zt4d into lif_zt, alif_zt (4d -> 1d)
|
|
kfn.lif_zt .= reduce(max, kfn.lif_zt4d, dims=(1,2))
|
|
kfn.alif_zt .= reduce(max, kfn.alif_zt4d, dims=(1,2))
|
|
|
|
# update activation matrix with "lif_zt1" and "alif_zt1" by concatenating
|
|
# (input, lif_zt1, alif_zt1) to form activation matrix
|
|
_zit = cat(reshape(input, (size(input, 1), size(input, 2), 1, size(input, 3))),
|
|
reshape(kfn.lif_zt, (size(input, 1), :, 1, size(input, 3))),
|
|
reshape(kfn.alif_zt, (size(input, 1), :, 1, size(input, 3))), dims=2)
|
|
kfn.zit .= reshape(_zit, (size(input, 1), :, size(input, 3)))
|
|
|
|
# project 3D kfn zit into 4D on zit
|
|
i1, i2, i3, i4 = size(kfn.on_zit)
|
|
kfn.on_zit .= reshape(kfn.zit, (i1, i2, 1, i4)) .* kfn.on_arrayProjection4d
|
|
|
|
# read out
|
|
onForward( kfn.on_zit,
|
|
kfn.on_wOut,
|
|
kfn.on_vt,
|
|
kfn.on_vth,
|
|
kfn.on_vRest,
|
|
kfn.on_zt4d,
|
|
kfn.on_alpha,
|
|
kfn.on_phi,
|
|
kfn.on_epsilonRec,
|
|
kfn.on_refractoryCounter,
|
|
kfn.on_refractoryDuration,
|
|
kfn.on_gammaPd,
|
|
kfn.on_firingCounter,
|
|
kfn.on_recSignal,)
|
|
# error("DEBUG -> kfn forward")
|
|
logit = reshape(kfn.on_zt, (size(input, 1), :))
|
|
|
|
return logit,
|
|
kfn.zit
|
|
end
|
|
|
|
|
|
function lifForward(kfn_zit::Array{T},
|
|
zit::Array{T},
|
|
wRec::Array{T},
|
|
vt0::Array{T},
|
|
vt1::Array{T},
|
|
vth::Array{T},
|
|
vRest::Array{T},
|
|
zt1::Array{T},
|
|
alpha::Array{T},
|
|
phi::Array{T},
|
|
epsilonRec::Array{T},
|
|
refractoryCounter::Array{T},
|
|
refractoryDuration::Array{T},
|
|
gammaPd::Array{T},
|
|
firingCounter::Array{T},
|
|
arrayProjection4d::Array{T},
|
|
recSignal::Array{T},
|
|
decayed_vt0::Array{T},
|
|
decayed_epsilonRec::Array{T},
|
|
vt1_diff_vth::Array{T},
|
|
vt1_diff_vth_div_vth::Array{T},
|
|
gammaPd_div_vth::Array{T},
|
|
phiActivation::Array{T},
|
|
) where T<:Number
|
|
|
|
# project 3D kfn zit into 4D lif zit
|
|
i1, i2, i3, i4 = size(alif_wRec)
|
|
lif_zit .= reshape(kfn_zit, (i1, i2, 1, i4)) .* lif_arrayProjection4d
|
|
|
|
for j in 1:size(wRec, 4), i in 1:size(wRec, 3) # compute along neurons axis of every batch
|
|
if sum(@view(refractoryCounter[:,:,i,j])) > 0 # refractory period is active
|
|
@. @views refractoryCounter[:,:,i,j] -= 1
|
|
@. @views zt1[:,:,i,j] = 0
|
|
@. @views vt1[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@. @views phi[:,:,i,j] = 0
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j]
|
|
else # refractory period is inactive
|
|
@. @views recSignal[:,:,i,j] = zit[:,:,i,j] * wRec[:,:,i,j]
|
|
@. @views decayed_vt0[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@view(vt1[:,:,i,j]) .= @view(decayed_vt0[:,:,i,j]) .+ sum(@view(recSignal[:,:,i,j]))
|
|
|
|
if sum(@view(vt1[:,:,i,j])) > sum(@view(vth[:,:,i,j]))
|
|
@. @views zt1[:,:,i,j] = 1
|
|
@. @views refractoryCounter[:,:,i,j] = refractoryDuration[:,:,i,j]
|
|
@. @views firingCounter[:,:,i,j] += 1
|
|
@. @views vt1[:,:,i,j] = vRest[:,:,i,j]
|
|
else
|
|
@. @views zt1[:,:,i,j] = 0
|
|
end
|
|
|
|
# compute phi, there is a difference from alif formula
|
|
@. @views gammaPd_div_vth[:,:,i,j] = gammaPd[:,:,i,j] / vth[:,:,i,j]
|
|
@. @views vt1_diff_vth[:,:,i,j] = vt1[:,:,i,j] - vth[:,:,i,j]
|
|
@. @views vt1_diff_vth_div_vth[:,:,i,j] = vt1_diff_vth[:,:,i,j] / vth[:,:,i,j]
|
|
@view(phiActivation[:,:,i,j]) .= max(0, 1 - sum(@view(vt1_diff_vth_div_vth[:,:,i,j])))
|
|
@. @views phi[:,:,i,j] = gammaPd_div_vth[:,:,i,j] * phiActivation[:,:,i,j]
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j] + zit[:,:,i,j]
|
|
end
|
|
end
|
|
end
|
|
|
|
# gpu launcher
|
|
function lifForward( lif_zit::CuArray,
|
|
lif_wRec::CuArray,
|
|
lif_vt::CuArray,
|
|
lif_vth::CuArray,
|
|
lif_vRest::CuArray,
|
|
lif_zt::CuArray,
|
|
lif_alpha::CuArray,
|
|
lif_phi::CuArray,
|
|
lif_epsilonRec::CuArray,
|
|
lif_refractoryCounter::CuArray,
|
|
lif_refractoryDuration::CuArray,
|
|
lif_gammaPd::CuArray,
|
|
lif_firingCounter::CuArray,
|
|
lif_recSignal::CuArray,)
|
|
|
|
kernel = @cuda launch=false lifForward( lif_zit,
|
|
lif_wRec,
|
|
lif_vt,
|
|
lif_vth,
|
|
lif_vRest,
|
|
lif_zt,
|
|
lif_alpha,
|
|
lif_phi,
|
|
lif_epsilonRec,
|
|
lif_refractoryCounter,
|
|
lif_refractoryDuration,
|
|
lif_gammaPd,
|
|
lif_firingCounter,
|
|
lif_recSignal,
|
|
GeneralUtils.linear_to_cartesian)
|
|
config = launch_configuration(kernel.fun)
|
|
|
|
|
|
# threads to be launched. Since one can't launch exact thread number the kernel needs,
|
|
# one just launch threads more than this kernel needs then use a guard inside the kernel
|
|
# to prevent unused threads to access memory.
|
|
threads = min(1024, config.threads) # depend on gpu. Most NVIDIA gpu has 1024 threads per block
|
|
|
|
# total desired threads to launch to gpu. Usually 1 thread per 1 matrix element
|
|
totalThreads = length(lif_wRec)
|
|
|
|
blocks = cld(totalThreads, threads)
|
|
# println("launching gpu kernel")
|
|
CUDA.@sync begin
|
|
kernel( lif_zit,
|
|
lif_wRec,
|
|
lif_vt,
|
|
lif_vth,
|
|
lif_vRest,
|
|
lif_zt,
|
|
lif_alpha,
|
|
lif_phi,
|
|
lif_epsilonRec,
|
|
lif_refractoryCounter,
|
|
lif_refractoryDuration,
|
|
lif_gammaPd,
|
|
lif_firingCounter,
|
|
lif_recSignal,
|
|
GeneralUtils.linear_to_cartesian; threads, blocks)
|
|
end
|
|
end
|
|
|
|
# gpu kernel
|
|
function lifForward( zit,
|
|
wRec,
|
|
vt,
|
|
vth,
|
|
vRest,
|
|
zt,
|
|
alpha,
|
|
phi,
|
|
epsilonRec,
|
|
refractoryCounter,
|
|
refractoryDuration,
|
|
gammaPd,
|
|
firingCounter,
|
|
recSignal,
|
|
linear_to_cartesian)
|
|
i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # gpu threads index
|
|
|
|
if i <= length(wRec)
|
|
# cartesian index
|
|
i1, i2, i3, i4 = linear_to_cartesian(i, size(wRec))
|
|
# @cuprintln("gpu thread $i $i1 $i2 $i3 $i4")
|
|
|
|
refractoryCounter[i] -= 1
|
|
|
|
if refractoryCounter[i] > 0 # refractory period is active
|
|
refractoryCounter[i] -= 1
|
|
zt[i] = 0
|
|
vt[i] = alpha[i] * vt[i]
|
|
phi[i] = 0
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
|
|
else # refractory period is inactive
|
|
recSignal[i] = zit[i] * wRec[i]
|
|
vt[i] = (alpha[i] * vt[i]) + sum(@view(recSignal[:,:,i3,i4]))
|
|
|
|
# fires if membrane potential exceed threshold
|
|
if vt[i] > vth[i]
|
|
zt[i] = 1
|
|
refractoryCounter[i] = refractoryDuration[i]
|
|
firingCounter[i] += 1
|
|
vt[i] = vRest[i]
|
|
else
|
|
zt[i] = 0
|
|
end
|
|
|
|
# compute phi, there is a difference from lif formula
|
|
phi[i] = (gammaPd[i] / vth[i]) * max(0, 1 - ((vt[i] - vth[i]) / vth[i]))
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
end
|
|
end
|
|
return nothing
|
|
end
|
|
|
|
function alifForward(zit::Array{T},
|
|
wRec::Array{T},
|
|
vt0::Array{T},
|
|
vt1::Array{T},
|
|
vth::Array{T},
|
|
vRest::Array{T},
|
|
zt1::Array{T},
|
|
alpha::Array{T},
|
|
phi::Array{T},
|
|
epsilonRec::Array{T},
|
|
refractoryCounter::Array{T},
|
|
refractoryDuration::Array{T},
|
|
gammaPd::Array{T},
|
|
firingCounter::Array{T},
|
|
recSignal::Array{T},
|
|
decayed_vt0::Array{T},
|
|
decayed_epsilonRec::Array{T},
|
|
vt1_diff_vth::Array{T},
|
|
vt1_diff_vth_div_vth::Array{T},
|
|
gammaPd_div_vth::Array{T},
|
|
phiActivation::Array{T},
|
|
|
|
epsilonRecA::Array{T},
|
|
avth::Array{T},
|
|
a::Array{T},
|
|
beta::Array{T},
|
|
rho::Array{T},
|
|
phi_x_epsilonRec::Array{T},
|
|
phi_x_beta::Array{T},
|
|
rho_diff_phi_x_beta::Array{T},
|
|
rho_div_phi_x_beta_x_epsilonRecA::Array{T},
|
|
beta_x_a::Array{T},
|
|
) where T<:Number
|
|
|
|
for j in 1:size(wRec, 4), i in 1:size(wRec, 3) # compute along neurons axis of every batch
|
|
if sum(@view(refractoryCounter[:,:,i,j])) > 0 # refractory period is active
|
|
@. @views refractoryCounter[:,:,i,j] -= 1
|
|
@. @views zt1[:,:,i,j] = 0
|
|
@. @views vt1[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@. @views phi[:,:,i,j] = 0
|
|
@. @views a[:,:,i,j] = rho[:,:,i,j] * a[:,:,i,j]
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j]
|
|
|
|
# compute epsilonRecA
|
|
@. @views phi_x_epsilonRec[:,:,i,j] = phi[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views phi_x_beta[:,:,i,j] = phi[:,:,i,j] * beta[:,:,i,j]
|
|
@. @views rho_diff_phi_x_beta[:,:,i,j] = rho[:,:,i,j] - phi_x_beta[:,:,i,j]
|
|
@. @views rho_div_phi_x_beta_x_epsilonRecA[:,:,i,j] = rho_diff_phi_x_beta[:,:,i,j] * epsilonRecA[:,:,i,j]
|
|
@. @views epsilonRecA[:,:,i,j] = phi_x_epsilonRec[:,:,i,j] + rho_div_phi_x_beta_x_epsilonRecA[:,:,i,j]
|
|
|
|
# compute avth
|
|
@. @views beta_x_a[:,:,i,j] = beta[:,:,i,j] * a[:,:,i,j]
|
|
@. @views avth[:,:,i,j] = vth[:,:,i,j] + beta_x_a[:,:,i,j]
|
|
|
|
else # refractory period is inactive
|
|
@. @views recSignal[:,:,i,j] = zit[:,:,i,j] * wRec[:,:,i,j]
|
|
@. @views decayed_vt0[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@view(vt1[:,:,i,j]) .= @view(decayed_vt0[:,:,i,j]) .+ sum(@view(recSignal[:,:,i,j]))
|
|
|
|
# compute avth
|
|
@. @views beta_x_a[:,:,i,j] = beta[:,:,i,j] * a[:,:,i,j]
|
|
@. @views avth[:,:,i,j] = vth[:,:,i,j] + beta_x_a[:,:,i,j]
|
|
|
|
if sum(@view(vt1[:,:,i,j])) > sum(@view(avth[:,:,i,j]))
|
|
@. @views zt1[:,:,i,j] = 1
|
|
@. @views refractoryCounter[:,:,i,j] = refractoryDuration[:,:,i,j]
|
|
@. @views firingCounter[:,:,i,j] += 1
|
|
@. @views vt1[:,:,i,j] = vRest[:,:,i,j]
|
|
@. @views a[:,:,i,j] = rho[:,:,i,j] * a[:,:,i,j]
|
|
@. @views a[:,:,i,j] = a[:,:,i,j] += 1
|
|
else
|
|
@. @views zt1[:,:,i,j] = 0
|
|
@. @views a[:,:,i,j] = rho[:,:,i,j] * a[:,:,i,j]
|
|
end
|
|
|
|
# compute phi, there is a difference from alif formula
|
|
@. @views gammaPd_div_vth[:,:,i,j] = gammaPd[:,:,i,j] / vth[:,:,i,j]
|
|
@. @views vt1_diff_vth[:,:,i,j] = vt1[:,:,i,j] - vth[:,:,i,j]
|
|
@. @views vt1_diff_vth_div_vth[:,:,i,j] = vt1_diff_vth[:,:,i,j] / vth[:,:,i,j]
|
|
@view(phiActivation[:,:,i,j]) .= max(0, 1 - sum(@view(vt1_diff_vth_div_vth[:,:,i,j])))
|
|
@. @views phi[:,:,i,j] = gammaPd_div_vth[:,:,i,j] * phiActivation[:,:,i,j]
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j] + zit[:,:,i,j]
|
|
|
|
# compute epsilonRecA
|
|
@. @views phi_x_epsilonRec[:,:,i,j] = phi[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views phi_x_beta[:,:,i,j] = phi[:,:,i,j] * beta[:,:,i,j]
|
|
@. @views rho_diff_phi_x_beta[:,:,i,j] = rho[:,:,i,j] - phi_x_beta[:,:,i,j]
|
|
@. @views rho_div_phi_x_beta_x_epsilonRecA[:,:,i,j] = rho_diff_phi_x_beta[:,:,i,j] * epsilonRecA[:,:,i,j]
|
|
@. @views epsilonRecA[:,:,i,j] = phi_x_epsilonRec[:,:,i,j] + rho_div_phi_x_beta_x_epsilonRecA[:,:,i,j]
|
|
end
|
|
end
|
|
end
|
|
|
|
# gpu launcher
|
|
function alifForward( alif_zit::CuArray,
|
|
alif_wRec::CuArray,
|
|
alif_vt::CuArray,
|
|
alif_vth::CuArray,
|
|
alif_vRest::CuArray,
|
|
alif_zt::CuArray,
|
|
alif_alpha::CuArray,
|
|
alif_phi::CuArray,
|
|
alif_epsilonRec::CuArray,
|
|
alif_refractoryCounter::CuArray,
|
|
alif_refractoryDuration::CuArray,
|
|
alif_gammaPd::CuArray,
|
|
alif_firingCounter::CuArray,
|
|
alif_recSignal::CuArray,
|
|
alif_epsilonRecA::CuArray,
|
|
alif_a::CuArray,
|
|
alif_avth::CuArray,
|
|
alif_beta::CuArray,
|
|
alif_rho::CuArray,
|
|
)
|
|
|
|
kernel = @cuda launch=false alifForward( alif_zit,
|
|
alif_wRec,
|
|
alif_vt,
|
|
alif_vth,
|
|
alif_vRest,
|
|
alif_zt,
|
|
alif_alpha,
|
|
alif_phi,
|
|
alif_epsilonRec,
|
|
alif_refractoryCounter,
|
|
alif_refractoryDuration,
|
|
alif_gammaPd,
|
|
alif_firingCounter,
|
|
alif_recSignal,
|
|
alif_epsilonRecA,
|
|
alif_a,
|
|
alif_avth,
|
|
alif_beta,
|
|
alif_rho,
|
|
GeneralUtils.linear_to_cartesian)
|
|
config = launch_configuration(kernel.fun)
|
|
|
|
# threads to be launched. Since one can't launch exact thread number the kernel needs,
|
|
# one just launch threads more than this kernel needs then use a guard inside the kernel
|
|
# to prevent unused threads to access memory.
|
|
threads = min(1024, config.threads) # depend on gpu. Most NVIDIA gpu has 1024 threads per block
|
|
|
|
# total desired threads to launch to gpu. Usually 1 thread per 1 matrix element
|
|
totalThreads = length(alif_wRec)
|
|
|
|
blocks = cld(totalThreads, threads)
|
|
# println("launching gpu kernel")
|
|
CUDA.@sync begin
|
|
kernel( alif_zit,
|
|
alif_wRec,
|
|
alif_vt,
|
|
alif_vth,
|
|
alif_vRest,
|
|
alif_zt,
|
|
alif_alpha,
|
|
alif_phi,
|
|
alif_epsilonRec,
|
|
alif_refractoryCounter,
|
|
alif_refractoryDuration,
|
|
alif_gammaPd,
|
|
alif_firingCounter,
|
|
alif_recSignal,
|
|
alif_epsilonRecA,
|
|
alif_a,
|
|
alif_avth,
|
|
alif_beta,
|
|
alif_rho,
|
|
GeneralUtils.linear_to_cartesian; threads, blocks)
|
|
end
|
|
end
|
|
|
|
# gpu kernel
|
|
function alifForward( zit,
|
|
wRec,
|
|
vt,
|
|
vth,
|
|
vRest,
|
|
zt,
|
|
alpha,
|
|
phi,
|
|
epsilonRec,
|
|
refractoryCounter,
|
|
refractoryDuration,
|
|
gammaPd,
|
|
firingCounter,
|
|
recSignal,
|
|
epsilonRecA,
|
|
a,
|
|
avth,
|
|
beta,
|
|
rho,
|
|
linear_to_cartesian)
|
|
i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # gpu threads index
|
|
|
|
if i <= length(wRec)
|
|
# cartesian index
|
|
i1, i2, i3, i4 = linear_to_cartesian(i, size(wRec))
|
|
# @cuprintln("gpu thread $i $i1 $i2 $i3 $i4")
|
|
|
|
refractoryCounter[i] -= 1
|
|
|
|
if refractoryCounter[i] > 0 # refractory period is active
|
|
refractoryCounter[i] -= 1
|
|
zt[i] = 0
|
|
vt[i] = alpha[i] * vt[i]
|
|
phi[i] = 0
|
|
a[i] = rho[i] * a[i]
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
|
|
# compute epsilonRecA
|
|
epsilonRecA[i] = (phi[i] * epsilonRec[i]) +
|
|
((rho[i] - (phi[i] * beta[i])) * epsilonRecA[i])
|
|
|
|
# compute avth
|
|
avth[i] = vth[i] + (beta[i] * a[i])
|
|
|
|
else # refractory period is inactive
|
|
recSignal[i] = zit[i] * wRec[i]
|
|
vt[i] = (alpha[i] * vt[i]) + sum(@view(recSignal[:,:,i3,i4]))
|
|
|
|
# compute avth
|
|
avth[i] = vth[i] + (beta[i] * a[i])
|
|
|
|
# fires if membrane potential exceed threshold
|
|
if vt[i] > avth[i]
|
|
zt[i] = 1
|
|
refractoryCounter[i] = refractoryDuration[i]
|
|
firingCounter[i] += 1
|
|
vt[i] = vRest[i]
|
|
a[i] = (rho[i] * a[i]) + 1
|
|
else
|
|
zt[i] = 0
|
|
a[i] = (rho[i] * a[i])
|
|
end
|
|
|
|
# compute phi, there is a difference from alif formula
|
|
phi[i] = (gammaPd[i] / vth[i]) * max(0, 1 - ((vt[i] - vth[i]) / vth[i]))
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
|
|
# compute epsilonRecA
|
|
epsilonRecA[i] = (phi[i] * epsilonRec[i]) +
|
|
((rho[i] - (phi[i] * beta[i])) * epsilonRecA[i])
|
|
end
|
|
end
|
|
return nothing
|
|
end
|
|
|
|
function onForward(kfn_zit::Array{T},
|
|
zit::Array{T},
|
|
wOut::Array{T},
|
|
vt0::Array{T},
|
|
vt1::Array{T},
|
|
vth::Array{T},
|
|
vRest::Array{T},
|
|
zt1::Array{T},
|
|
alpha::Array{T},
|
|
phi::Array{T},
|
|
epsilonRec::Array{T},
|
|
refractoryCounter::Array{T},
|
|
refractoryDuration::Array{T},
|
|
gammaPd::Array{T},
|
|
firingCounter::Array{T},
|
|
arrayProjection4d::Array{T},
|
|
recSignal::Array{T},
|
|
decayed_vt0::Array{T},
|
|
decayed_epsilonRec::Array{T},
|
|
vt1_diff_vth::Array{T},
|
|
vt1_diff_vth_div_vth::Array{T},
|
|
gammaPd_div_vth::Array{T},
|
|
phiActivation::Array{T},
|
|
) where T<:Number
|
|
|
|
# project 3D kfn zit into 4D lif zit
|
|
zit .= reshape(kfn_zit,
|
|
(size(wOut, 1), size(wOut, 2), 1, size(wOut, 4))) .* arrayProjection4d
|
|
|
|
for j in 1:size(wOut, 4), i in 1:size(wOut, 3) # compute along neurons axis of every batch
|
|
if sum(@view(refractoryCounter[:,:,i,j])) > 0 # refractory period is active
|
|
@. @views refractoryCounter[:,:,i,j] -= 1
|
|
@. @views zt1[:,:,i,j] = 0
|
|
@. @views vt1[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@. @views phi[:,:,i,j] = 0
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j]
|
|
else # refractory period is inactive
|
|
@. @views recSignal[:,:,i,j] = zit[:,:,i,j] * wOut[:,:,i,j]
|
|
@. @views decayed_vt0[:,:,i,j] = alpha[:,:,i,j] * vt0[:,:,i,j]
|
|
@view(vt1[:,:,i,j]) .= @view(decayed_vt0[:,:,i,j]) .+ sum(@view(recSignal[:,:,i,j]))
|
|
|
|
if sum(@view(vt1[:,:,i,j])) > sum(@view(vth[:,:,i,j]))
|
|
@. @views zt1[:,:,i,j] = 1
|
|
@. @views refractoryCounter[:,:,i,j] = refractoryDuration[:,:,i,j]
|
|
@. @views firingCounter[:,:,i,j] += 1
|
|
@. @views vt1[:,:,i,j] = vRest[:,:,i,j]
|
|
else
|
|
@. @views zt1[:,:,i,j] = 0
|
|
end
|
|
|
|
# compute phi, there is a difference from alif formula
|
|
@. @views gammaPd_div_vth[:,:,i,j] = gammaPd[:,:,i,j] / vth[:,:,i,j]
|
|
@. @views vt1_diff_vth[:,:,i,j] = vt1[:,:,i,j] - vth[:,:,i,j]
|
|
@. @views vt1_diff_vth_div_vth[:,:,i,j] = vt1_diff_vth[:,:,i,j] / vth[:,:,i,j]
|
|
@view(phiActivation[:,:,i,j]) .= max(0, 1 - sum(@view(vt1_diff_vth_div_vth[:,:,i,j])))
|
|
@. @views phi[:,:,i,j] = gammaPd_div_vth[:,:,i,j] * phiActivation[:,:,i,j]
|
|
|
|
# compute epsilonRec
|
|
@. @views decayed_epsilonRec[:,:,i,j] = alpha[:,:,i,j] * epsilonRec[:,:,i,j]
|
|
@. @views epsilonRec[:,:,i,j] = decayed_epsilonRec[:,:,i,j] + zit[:,:,i,j]
|
|
end
|
|
end
|
|
end
|
|
|
|
# gpu launcher
|
|
function onForward( on_zit::CuArray,
|
|
on_wOut::CuArray,
|
|
on_vt::CuArray,
|
|
on_vth::CuArray,
|
|
on_vRest::CuArray,
|
|
on_zt::CuArray,
|
|
on_alpha::CuArray,
|
|
on_phi::CuArray,
|
|
on_epsilonRec::CuArray,
|
|
on_refractoryCounter::CuArray,
|
|
on_refractoryDuration::CuArray,
|
|
on_gammaPd::CuArray,
|
|
on_firingCounter::CuArray,
|
|
on_recSignal::CuArray)
|
|
|
|
kernel = @cuda launch=false onForward( on_zit,
|
|
on_wOut,
|
|
on_vt,
|
|
on_vth,
|
|
on_vRest,
|
|
on_zt,
|
|
on_alpha,
|
|
on_phi,
|
|
on_epsilonRec,
|
|
on_refractoryCounter,
|
|
on_refractoryDuration,
|
|
on_gammaPd,
|
|
on_firingCounter,
|
|
on_recSignal,
|
|
GeneralUtils.linear_to_cartesian)
|
|
config = launch_configuration(kernel.fun)
|
|
|
|
# threads to be launched. Since one can't launch exact thread number the kernel needs,
|
|
# one just launch threads more than this kernel needs then use a guard inside the kernel
|
|
# to prevent unused threads to access memory.
|
|
threads = min(1024, config.threads) # depend on gpu. Most NVIDIA gpu has 1024 threads per block
|
|
|
|
# total desired threads to launch to gpu. Usually 1 thread per 1 matrix element
|
|
totalThreads = length(on_wOut)
|
|
|
|
blocks = cld(totalThreads, threads)
|
|
# println("launching gpu kernel")
|
|
CUDA.@sync begin
|
|
kernel( on_zit,
|
|
on_wOut,
|
|
on_vt,
|
|
on_vth,
|
|
on_vRest,
|
|
on_zt,
|
|
on_alpha,
|
|
on_phi,
|
|
on_epsilonRec,
|
|
on_refractoryCounter,
|
|
on_refractoryDuration,
|
|
on_gammaPd,
|
|
on_firingCounter,
|
|
on_recSignal,
|
|
GeneralUtils.linear_to_cartesian; threads, blocks)
|
|
end
|
|
end
|
|
|
|
# gpu kernel
|
|
function onForward( zit,
|
|
wOut,
|
|
vt,
|
|
vth,
|
|
vRest,
|
|
zt,
|
|
alpha,
|
|
phi,
|
|
epsilonRec,
|
|
refractoryCounter,
|
|
refractoryDuration,
|
|
gammaPd,
|
|
firingCounter,
|
|
recSignal,
|
|
linear_to_cartesian)
|
|
i = (blockIdx().x - 1) * blockDim().x + threadIdx().x # gpu threads index
|
|
|
|
if i <= length(wOut)
|
|
# cartesian index
|
|
i1, i2, i3, i4 = linear_to_cartesian(i, size(wOut))
|
|
# @cuprintln("gpu thread $i $i1 $i2 $i3 $i4")
|
|
|
|
refractoryCounter[i] -= 1
|
|
|
|
if refractoryCounter[i] > 0 # refractory period is active
|
|
refractoryCounter[i] -= 1
|
|
zt[i] = 0
|
|
vt[i] = alpha[i] * vt[i]
|
|
phi[i] = 0
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
|
|
else # refractory period is inactive
|
|
recSignal[i] = zit[i] * wOut[i]
|
|
vt[i] = (alpha[i] * vt[i]) + sum(@view(recSignal[:,:,i3,i4]))
|
|
|
|
# fires if membrane potential exceed threshold
|
|
if vt[i] > vth[i]
|
|
zt[i] = 1
|
|
refractoryCounter[i] = refractoryDuration[i]
|
|
firingCounter[i] += 1
|
|
vt[i] = vRest[i]
|
|
else
|
|
zt[i] = 0
|
|
end
|
|
|
|
# compute phi, there is a difference from on formula
|
|
phi[i] = (gammaPd[i] / vth[i]) * max(0, 1 - ((vt[i] - vth[i]) / vth[i]))
|
|
|
|
# compute epsilonRec
|
|
epsilonRec[i] = (alpha[i] * epsilonRec[i]) + zit[i]
|
|
end
|
|
end
|
|
return nothing
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end # module |