Hi,
I am trying to implement a fast event-based numerically exact simulation of a sparse large spiking neural network using a priority queue. It is fast, but not fast enough. Profiling indicates that the bottleneck seem to be the dictionary operations keyindex and setindex! when changing priority of an existing key (3rd line of function ptc! in code below). Is there a way to avoid this? Is is possible to set up a priority queue with integer keys instead? More generally, is there a smarter data structure than priority queue, if I want to find the smallest element of an large array and change a small fraction array entries (say 10^2 out of 10^8) each iteration?
Best regards
Rainer
## For those interested into the implementation: We map the membrane
potential to a phase variable phi \in [-1, inf) and solve the network
evolution of an purely inhibitory homogeneous pulse-coupled leaky integrate and fire
network analytically between network spikes. This is just a toy example, some important steps are missing.
##### code ###
function ptc!(phi, postid, phishift, w, c) #phase transition curve
@inbounds for i = postid
phi[i] = w*log(exp((phi[i]-phishift)/w)+c) + phishift
end
end
function lifnet(n,nstep,k,j0,ratewnt,tau,seedic,seedtopo)
iext = tau*sqrt(k)*j0*ratewnt # iext given by balance equation
w = 1/log(1. + 1/iext) # phase velocity LIF
c = j0/sqrt(k)/(1. + iext) # effective coupling in PTC for LIF
phith, phireset = 1., 0. # reset and threshold for LIF
srand(seedic) # initialize random number generator (Mersenne Twister)
phi = Collections.PriorityQueue(UInt32,Float64)
@inbounds for i = 1:n #set initial phases for all neurons
phi[i] = -rand()
end
spikeidx, spiketimes = Int64[], Float64[] # initialize time & spike raster
postidx = Array{UInt32,1}(k) # idices of postsynaptic neurons
t = phishift = 0. # initial time and initial global phase shift
for s = 1 : nstep #
j, phimax = Collections.peek(phi) # next spiking neuron
dphi = phith+phimax-phishift # calculate next spike time
phishift += dphi # global backshift instead of shifting all phases
t += dphi # update time
state = UInt(j+seedtopo) # spiking neuron index is seed of rng
@inbounds for i = 1:k # idea by R.Rosenbaum to reduce memory
postidx[i], state = randfast(n,state) # get postsyn. neurons
end
ptc!(phi,postidx,phishift,w,c) # evaluate PTC
phi[j] = phireset + phishift # reset spiking neuron to 0
push!(spiketimes,t) # store spiketimes
push!(spikeidx,j) # store spiking neuron
end
nstep/t/n/tau*w, spikeidx, spiketimes*tau/w
end
function randfast(m, state::UInt) # linear congruential generator
state = lcg(state) # faster then Mersenne-Twister
1 + rem(state, m) % Int, state # but can be problematic
end
function lcg(oldstate) #
tinyurl.com/fastrand by M. Schauer
a = unsigned(2862933555777941757)
b = unsigned
(3037000493)
a*oldstate + b
end
#n: # of neurons, k: synapses/neuron, j0: syn. strength, tau: membr. time const.
n,nstep,k,j0,ratewnt,tau,seedic,seedtopo = 10^6,10^4,10^2,1,1,.01,1,1
@time lifnet(100, 1, 10, j0, ratewnt, tau, seedic, seedtopo); #warmup
gc()
@time rate,spidx,sptimes = lifnet(n,nstep,k,j0,ratewnt,tau,seedic,seedtopo);
#using PyPlot;plot(sptimes,spidx,".k");ylabel("Neuron Index");xlabel("Time (s)")