/*
 *  test_hpc_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_hpc_synapse - Basic tests on hpc synapses

Synopsis: (test_hpc_synapse) run -> NEST exits if test fails

Description: 
Test basic properties of HPC synapses as follows:

1. For all known synapses with _hpc ending and counterparts without _hpc
   connect spike generator to one neuron with normal, one with _hpc synapse,
   ensure that simulation yields identical membrane potentials.
   
2. Check that adding and freezing/thawing of nodes either is blocked or
   does not affect results, i.e., that the TargetIdentifierIndicies are ok.
   These tests proceed as follows:
   1. Connect spike_generator to N neurons with different weights,
      disable spiking in receptors. We use membrane potential after simulation
      as indicator for through which synpse a neuron received input.
   2. Build network once with plain static_synapse to get reference data.
   3. Then build with respective test cases and compare.
   
Author: Hans E Plesser, 2014-11-14
 */

(unittest) run
/unittest using

skip_if_not_threaded

M_ERROR setverbosity

% convert name with _hpc to equivalent without
/hpctoplain
{
  cvs dup length 4 sub Take cvlit
} bind def


% Test if synapse can be used without additional setup; stdp_dopamine_synpase does not
/synapse_works  % expects lit specifying hpc synapse
{
  /syn Set
  mark
  {
    /iaf_neuron Create dup syn Connect
    10 Simulate
  } 
  stopped
  dup /failed Set
  {
    % error, need to clean up
    counttomark npop % pop all but mark
    errordict begin /newerror false def end    
  }
  if
  pop % mark
  failed not
} def


% find all hpc synapses with non-hpc counterparts that can be used without
% additional setup
/synmodels synapsedict keys def
/hpcsyns synmodels { cvs -4 Take (_hpc) eq } Select def
/hpcwithpartner hpcsyns { dup hpctoplain synmodels exch MemberQ exch synapse_works and } Select def


% first set of tests: compare hpc to plain variant
/run_sim_test_one  % expects synapse model, returns membrane potential
{
  /syn Set
  
  ResetKernel
  0 << /local_num_threads 4 >> SetStatus

  % we connect via a parrot neuron, so we can test plastic synapses, too 
  /sg /spike_generator << /spike_times [ 5.0 ] >> Create def
  /pn /parrot_neuron Create def
  /nrn /iaf_neuron << /V_th 100000. >> Create def 
  sg pn Connect
  pn nrn syn Connect
  10 Simulate

  nrn /V_m get
  
} def
  
hpcwithpartner
{
  /hpc_syn Set
  (Test 1: ) =only hpc_syn ==
  /plain_syn hpc_syn hpctoplain def
  
  { 
    hpc_syn run_sim_test_one
    plain_syn run_sim_test_one
    eq
  } assert_or_die
} forall 


% second test: build in some neurons, connect, then some more neurons, connect
/test_two_step_build  % expects syntype
{
  /syn Set
  
  ResetKernel
  0 << /local_num_threads 4 >> SetStatus
  /sg /spike_generator << /spike_times [ 1.0 ] >> Create def
  /pn /parrot_neuron Create def

  /iaf_neuron 2 << /V_th 100000. >> Create ;
  sg pn Connect
  [3 4] { /tgid Set [pn] [tgid] /one_to_one << /model syn /weight tgid 100. mul >> Connect } forall

  /iaf_neuron 2 << /V_th 100000. >> Create ;
  [5 6] { /tgid Set [pn] [tgid] /one_to_one << /model syn /weight tgid 100. mul >> Connect } forall

  10. Simulate
  [3 4 5 6] { /V_m get } Map
} def

{
  /static_synapse test_two_step_build
  (Test 2 plain: ) =only dup ==
  /static_synapse_hpc test_two_step_build
  (Test 2 hpc  : ) =only dup ==
  eq
} assert_or_die



% third test: build, freeze one, then connect non-frozen, should work
/test_freeze_before_connect_nonfrozen  % expects syntype
{
  /syn Set
  
  ResetKernel
  0 << /local_num_threads 4 >> SetStatus
  /sg /spike_generator << /spike_times [ 1.0 ] >> Create def
  /pn /parrot_neuron Create def

  /iaf_neuron 4 << /V_th 100000. >> Create ;
  3 << /frozen true >> SetStatus
  sg pn Connect
  [4 5 6] { /tgid Set [pn] [tgid] /one_to_one << /model syn /weight tgid 100. mul >> Connect } forall

  10. Simulate
  [4 5 6] { /V_m get } Map
} def

{
  /static_synapse test_freeze_before_connect_nonfrozen
  (Test 3 plain: ) =only dup ==
  /static_synapse_hpc test_freeze_before_connect_nonfrozen
  (Test 3 hpc  : ) =only dup ==
  eq
} assert_or_die

% fourth test: build, freeze one, then connect all, should also work
% because frozen synapse has invalid Target ID
/test_freeze_before_connect_all  % expects syntype
{
  /syn Set
  
  ResetKernel
  0 << /local_num_threads 4 >> SetStatus
  /sg /spike_generator << /spike_times [ 1.0 ] >> Create def
  /pn /parrot_neuron Create def

  /iaf_neuron 4 << /V_th 100000. >> Create ;
  3 << /frozen true >> SetStatus
  sg pn Connect
  [3 4 5 6] { /tgid Set [pn] [tgid] /one_to_one << /model syn /weight tgid 100. mul >> Connect } forall

  10. Simulate
  [3 4 5 6] { /V_m get } Map
} def

{
  /static_synapse test_freeze_before_connect_all
  /static_synapse_hpc test_freeze_before_connect_all
  eq
} assert_or_die

% fifth test: build, then connect all, then freeze one 
/test_freeze_after_connect_all  % expects syntype
{
  /syn Set
  
  ResetKernel
  0 << /local_num_threads 4 >> SetStatus
  /sg /spike_generator << /spike_times [ 1.0 ] >> Create def
  /pn /parrot_neuron Create def

  /iaf_neuron 4 << /V_th 100000. >> Create ;
  sg pn Connect
  [3 4 5 6] { /tgid Set [pn] [tgid] /one_to_one << /model syn /weight tgid 100. mul >> Connect } forall
  3 << /frozen true >> SetStatus

  10. Simulate
  [3 4 5 6] { /V_m get } Map
} def

{
  /static_synapse test_freeze_after_connect_all  
  (Test 5 plain: ) =only dup ==
  /static_synapse_hpc test_freeze_after_connect_all
  (Test 5 hpc  : ) =only dup ==
  eq
} assert_or_die


 
endusing
