/*
 *  test_stdp_synapse.sli
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST 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.
 *
 *  NEST 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 NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


/* BeginDocumentation
Name: testsuite::test_stdp_synapse - basic test of stdp_synapse

Synopsis: (test_stdp_synapse) run

Description:
  A parrot_neuron that repeats the spikes from a poisson generator is
  connected to an iaf_psc_alpha that is driven by inh. and exc. poisson input.
  The synapse is an stdp_synapse. After the simulation, we go through the pre-
  and postsyn. spike-trains spike by spike and try to reproduce the STDP
  results. The final weight obtained after simulation is compared to the final
  weight obtained from the test.

Author: Kunkel, Nov 2010
*/


/unittest (8831) require
/unittest using
 
ResetKernel
 
/resolution 0.1 def %1.0 def %2.0 -4 pow def  % simulation step size
0 << /resolution resolution >> SetStatus %/tics_per_ms 10000.0 >> SetStatus


%%% input parameters %%%

/K_exc         8000.0 def  % number of exc. inputs
/K_inh         2000.0 def  % number of inh. inputs
/nu              10.0 def  % equil. firing rate
/nu_x             1.7 def  % external rate
/w_exc           45.0 def  % strength of exc. connections
/w_inh w_exc -5.0 mul def  % strength of inh. connections
/delay            1.0 def  % synaptic transmission delay

/axonal_delay 0.0 def
/backpr_delay delay axonal_delay sub def


%%% STDP parameters %%%

/alpha              1.1  def
/lambda             0.01 def
/tau_plus          20.0  def
/tau_minus         30.0  def
/mu_plus            1.0  def  % multiplicative
/mu_minus           1.0  def  % multiplicative
/w_max     w_exc 2.0 mul def


%%% create poisson generators, neurons and spike detector %%%

/pg_exc /poisson_generator << /rate K_exc nu nu_x add mul >> Create def
/pg_inh /poisson_generator << /rate K_inh nu mul          >> Create def

/pg_pre /poisson_generator << /rate nu >> Create def

/parrot /parrot_neuron Create def
/neuron /iaf_psc_alpha << /tau_minus tau_minus >> Create def

/spike_detector << /to_file   false
                   /to_memory true
		   /precision 15
                >> SetDefaults
/sd_pre  /spike_detector Create def
/sd_post /spike_detector Create def


%%% connect %%%

/stdp_synapse << /alpha    alpha
                 /lambda   lambda
		 /tau_plus tau_plus
		 /mu_plus  mu_plus
		 /mu_minus mu_minus
		 /Wmax     w_max
              >> SetDefaults

pg_exc neuron w_exc delay Connect
pg_inh neuron w_inh delay Connect
pg_pre parrot w_exc delay Connect

parrot neuron w_exc delay /stdp_synapse Connect

parrot sd_pre  Connect
neuron sd_post Connect


%%% simulate and get data %%%

10000.0 Simulate

/pre_spikes  sd_pre  GetStatus /events get /times get cva { axonal_delay add } Map def
/post_spikes sd_post GetStatus /events get /times get cva { backpr_delay add } Map def

/final_weight << /source parrot /target neuron >> FindConnections 0 get /weight get def


%%% check final weight %%%

cout 15 setprecision

/K_plus    0.0 def
/K_minus   0.0 def
/last_pre  0   def
/last_post 0   def
/j         0   def
/i         0   def

/post_spike post_spikes i get def
/pre_spike  pre_spikes  j get def
/w          w_exc w_max div   def

/update_K_plus
{
  last_pre pre_spike sub tau_plus div exp K_plus mul 1.0 add /K_plus Set
}
def

/update_K_minus
{
  last_post post_spike sub tau_minus div exp K_minus mul 1.0 add /K_minus Set
}
def

/next_pre_spike
{
  j 1 add /j Set
  pre_spike /last_pre Set
  pre_spikes j get /pre_spike Set  
}
def

/next_post_spike
{
  i 1 add /i Set
  post_spike /last_post Set
  post_spikes i get /post_spike Set
}
def

/facilitate
{
  ( w + lambda * (1.0-w)**mu_plus * K_plus * exp((last_pre-post_spike)/tau_plus) ) ExecMath
  dup 1.0 lt { /w Set } { pop 1.0 /w Set } ifelse
  %(facilitation) =only (\t) =only last_pre =only (\t) =only post_spike =only (\t) =only w w_max mul =
}
def

/depress
{
  ( w - lambda * alpha * w**mu_minus * K_minus * exp((last_post-pre_spike)/tau_minus) ) ExecMath
  dup 0.0 gt { /w Set } { pop 0.0 /w Set } ifelse
  %(depression) =only (\t) =only last_post =only (\t) =only pre_spike =only (\t) =only w w_max mul =
}
def

{
  pre_spike post_spike eq
  { % pre- and post-syn. spike at the same time
    last_post post_spike neq { facilitate } if
    last_pre pre_spike neq { depress } if
    %(pre == post) =only (\t) =only pre_spike =only (\t) =only post_spike =only (\t) =only w w_max mul =
    j 1 add pre_spikes length lt
    {
      update_K_plus
      next_pre_spike
      i 1 add post_spikes length lt
      {
        update_K_minus
	next_post_spike
      }
      if
    }
    {
      exit  
    }
    ifelse
  }
  {
    pre_spike post_spike lt
    { % next spike is a pre-syn. spike
      depress
      update_K_plus
      j 1 add pre_spikes length lt
      {
        next_pre_spike
      }
      {
        %(last presyn spike) =
        % we don't consider the post-syn. spikes after the last pre-syn. spike
        exit
      }
      ifelse
    }
    { % next spike is a post-syn. spike
      facilitate
      update_K_minus
      i 1 add post_spikes length lt
      {
        next_post_spike
      }
      {
        %(last postsyn spike) =
        % we DO consider the pre-syn. spikes after the last post-syn. spike
        post_spike /last_post Set
        pre_spikes dup length 1 sub get resolution add /post_spike Set  % to make sure we don't come here again
      }
      ifelse
    }
    ifelse
  }
  ifelse
}
loop

%final_weight w w_max mul sub abs dup = 1e-13 leq =

final_weight w w_max mul sub abs 1e-13 leq assert_or_die