/*
    cuba_stdp.sli

    Copyright (C) 2004 The NEST Initiative
    This file is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This file is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

/*
   The fifth simulator review benchmark is implemented as a variation of the
      * Brunel Network *

   (from the docu of brunel.sli)
   The SLI code in this file creates a sparsely coupled network of
   excitatory and inhibitory neurons.  Connections within and across
   both populations are created at random.  Both neuron populations
   receive Poissonian background input.  The spike output of 1000
   neurons are recorded.  Neurons are modeled as leaky
   integrate-and-fire neurons with current-injecting synapses
   (exp functions). Excitatory-excitatory synapses implement
   multiplicative STDP.
   Marc-Oliver Gewaltig, Abigail Morrison, Wiebke Potjans, Tobias Potjans

   Usage:
   This file (cuba_stdp.sli) is used on its own, e.g.

     nest cuba_stdp.sli

   If virtual_processes is set to a value greater than one, the benchmarks can
   will be run with that number of virtual processes. If you do not use MPI, each
   virtual process is mapped to one POSIX thread. You should not use more POSIX
   threads per machine than there are processor cores. If you use MPI, you can
   run the benchmark in a distributed fashion using mpirun, e.g.

     mpirun -np 2 nest cuba_stdp.sli

   In this case, the number of MPI processes must divide the number of virtual
   processes without remainder. You could, e.g. use 4 virtual processes on a
   mini-cluster of 2 machines with 2 CPUs each.


*/

%%% PARAMETER SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% define all relevant parameters: changes should be made here
% all data is place in the userdict dictionary

% A dictionary is a list of name value pairs, enclosed in << and >>
% Here we use dictionaries to encapsulate the parameters for the different
% benchmarks

/plastic_brunel_params
<<

  /virtual_processes 1  % number of virtual processes to use
  /simtime 2000.0 ms % simulated time
  /dt         0.1 ms % simulation step


  /NE 9000       % number of excitatory neurons
  /NI 2250       % number of inhibitory neurons


  /model  /iaf_psc_exp  % the neuron model to use
  /model_params
   <<
   % Set variables for iaf_psc_exp
    /E_L     0.0  mV  % Resting membrane potential [mV]
    /V_m     0.0  mV  % Initial membrane potential [mV]
    /V_th   20.0  mV  % Threshold [mV]
    /V_reset 0.0  mV  % Reset Potential [mV]
    /C_m   250.0  pF  % Capacity of the membrane [pF]
    /tau_m  20.0  ms  % Membrane time constant [ms]
                      % V randomly initialized see below
   >>

  /mean_potential 7.0 mV
  /sigma_potential 5.0 mV

  /epsilon 0.1           % Connection probability
  /delay  1.5 ms         % synaptic delay, static connections [ms]
  /JE 35.0 pA            % peak amplitude of PSC [pA]
  /g  5.0                % relative strength of inhib. connections

  /plastic_synapses true
  /stdp_params
  <<
    /delay 1.5 ms
    /alpha 1.05      %STDP asymmetry parameter
  >>
  /sigma_w 3.0       %initial standard dev. of E->E synapses [pA]

  /stimulus /poisson_generator
  /stimulus_params
  <<
    /rate 900.0 4.5 mul Hz % rate of inital poisson stimulus
  >>

  /detector /spike_detector
  /detector_params
  <<
   /withtime true
   /withgid true
   /to_file true
   /label (cuba_stdp)
  >>


  /Nrec 500    % number of neurons per population to record from

>> def

% Here we resolve parameter dependencies, by making the independent
% values visible
plastic_brunel_params dup using
<<
 /E_synapse_params
  <<
     /weight JE          % excitatory synaptic conductance
  >>

  /I_synapse_params
  <<
     /weight JE g mul -1.0 mul  % inhibitory synaptic current [pA]
  >>
>> join

stdp_params
<<
  /weight JE
  /Wmax 2.0 JE mul       %max strength of E->E synapses [pA]
>> join

% create one single random number generator
rngdict /knuthlfg get 238 CreateRNG /myrng Set

% normal distribution to draw several quantities from
myrng rdevdict /normal_clipped_right get CreateRDV /normal_dv Set
normal_dv
<<
 /mu  mean_potential
 /std sigma_potential
 /max model_params /V_th get
>> SetStatus





%%% FUNCTION SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Take spike detector, find total number of spikes registered,
% return average rate per neuron in Hz.
% NOTE: If you are running with several MPI processes, this
%       function only gives an approximation to the true rate.
%
% spike_det ComputeRate -> rate
/ComputeRate
{
  GetStatus /n_events get /nspikes Set

  % We need to guess how many neurons we record from.
  % This assumes an even distribution of nodes across
  % processes, as well as homogeneous activity in the
  % network. This is ok for this type of symmetric
  % network but should not be considered a general
  % solution.
  plastic_brunel_params /Nrec get cvd NumProcesses div
  /nnrn Set

  nspikes nnrn plastic_brunel_params /simtime get mul div
  1000 mul         % convert from mHz to Hz, leave on stack
  end
} bind             % optional, improves performance
def

/BuildNetwork
{

  tic % start timer on construction
  % set global kernel parameters
  0
  <<
     /resolution  dt
     /total_num_virtual_procs virtual_processes
     /overwrite_files true
  >> SetStatus


  model    model_params    SetDefaults
  M_INFO (BuildNetwork)
  (Creating excitatory population.) message  % show message

  /E_net model [ NE ] LayoutNetwork def

  M_INFO (BuildNetwork)
  (Creating inhibitory population.) message  % show message
  /I_net model [ NI ] LayoutNetwork def

  M_INFO (BuildNetwork)
  (Creating excitatory stimulus generator.) message
  /E_stimulus stimulus Create def
  E_stimulus stimulus_params SetStatus

  /I_stimulus stimulus Create def
   I_stimulus stimulus_params SetStatus

  % one detector would in principle be enough,
  % but by recording the populations separately,
  % we save ourselves a lot of sorting work later

  M_INFO (BuildNetwork)
  (Creating excitatory spike detector.) message
  /E_detector detector Create def
  E_detector detector_params SetStatus

  M_INFO (BuildNetwork)
  (Creating inhibitory spike detector.) message
  /I_detector detector Create def
  I_detector detector_params SetStatus

  % some connecting functions need lists (arrays) over all
  % neurons they should work on, so we need to extract these
  % lists from the subnetworks

  % obtain array with GIDs of all excitatory neurons
  /E_neurons E_net GetLocalNodes def

  % obtain array with GIDs of all inhibitory neurons
  /I_neurons I_net GetLocalNodes def

  % all neurons
  /allNeurons E_neurons I_neurons join def

  /N allNeurons length def

  /CE NE epsilon mul iround def %number of incoming excitatory connections
  /CI NI epsilon mul iround def %number of incomining inhibitory connections

  M_INFO (BuildNetwork)
  (Configuring neuron parameters.) message
  allNeurons
  {
   << /V_m  normal_dv Random >> SetStatus
  } forall

  % Create custom synapse types with appropriate values for
  % our excitatory and inhibitory connections
  /static_synapse << /delay delay >> SetDefaults
  /static_synapse /syn_ex E_synapse_params CopyModel
  /static_synapse /syn_in I_synapse_params CopyModel
  /stdp_synapse stdp_params SetDefaults

  plastic_synapses
  {
    /synapse_model /stdp_synapse def% plastic
  }
  {
    /synapse_model /syn_ex def
  } ifelse

  % create a dictionary containing the initialization function
  % InitSynapse, which will be called by RandomConvergentConnect
  % before each connection to be established.
  /init_dict
  <<
     % call: i InitSynapse
     /InitSynapse
     {
       /i Set

       synapse_model
       <<
         /weight weights i get  % weights will be added to the dict later
       >> SetDefaults
     }
  >> def

  M_INFO (BuildNetwork)
  (Connecting stimulus generators.) message

  % Connect Poisson generator to neuron
  E_stimulus E_neurons /syn_ex DivergentConnect
  I_stimulus I_neurons /syn_ex DivergentConnect

  M_INFO (BuildNetwork)
  (Connecting excitatory -> excitatory population.) message
  E_neurons
  {
    /target Set

     % create an array of CE elements and store it in 'rnd_weights';
     % each element is chosen from a normal distribution with mean
     % value JE and standard deviation sigma_w, add to init dictionary
     CE array { ; normal_dv Random sigma_w mul JE add } Map
     /rnd_weights Set
    init_dict cvo << /weights rnd_weights >> SetStatus

    % E -> E connections
    E_neurons   % source population [we pick from this]
    target      % target neuron
    CE          % number of source neurons to pick
    init_dict   % initialization dictionary
    synapse_model
    RandomConvergentConnect

  } bind forall

  M_INFO (BuildNetwork)
  (Connecting excitatory -> inhibitory population.) message

  I_neurons
  {
    /target Set
        E_neurons target CE /syn_ex RandomConvergentConnect
  } bind forall

  M_INFO (BuildNetwork)
  (Connecting inhibitory -> excitatory population.) message

  E_neurons
  {
    /target Set

        % I -> E connections
        % as above, but on a single line
        I_neurons target CI /syn_in RandomConvergentConnect
  } bind forall

  M_INFO (BuildNetwork)
  (Connecting inhibitory -> inhibitory population.) message

  I_neurons
  {
    /target Set
        I_neurons target CI /syn_in RandomConvergentConnect
  } bind forall

  % Spike detectors are connected to the first Nrec neurons in each
  % population.  Since neurons are equal and connectivity is homogeneously
  % randomized, this is equivalent to picking Nrec neurons at random
  % from each population
  M_INFO (BuildNetwork)
  (Connecting spike detectors.) message

  E_neurons Nrec Take E_detector ConvergentConnect
  I_neurons Nrec Take I_detector ConvergentConnect

  % read out time used for building
  toc /BuildCPUTime Set
 } def % end of buildnetwork

{
  ResetKernel
  M_INFO setverbosity

  BuildNetwork
  tic

  simtime Simulate

  toc /SimCPUTime Set
   % number of synapses---just so we know
  /Nsyn
    CE CI add  % internal synapses
    N mul
    Nrec 2 mul % "synapses" to spike detectors
    add
    N add      % "synapses" from poisson generator
  def

  (\nBrunel Network Simulation) =
  (Building time     : ) =only BuildCPUTime =only ( s) =
  (Simulation time   : ) =only SimCPUTime   =only ( s\n) =
  (Number of Neurons : ) =only N =
  (Number of Synapses: ) =only Nsyn =
  (Excitatory rate   : ) =only E_detector ComputeRate =only ( Hz) =
  (Inhibitory rate   : ) =only I_detector ComputeRate =only ( Hz) =

} exec