/*
 *  test_multimeter_freeze_thaw.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_multimeter_freeze_thaw - test if multimeter handles frozen nodes properly

Synopsis: (test_multimeter_freeze_thaw.sli) run -> dies if assertion fails

Description:
1. Record from neuron initially frozen, then thawed, then frozen, then thawed. See
   that correct number of data points collected.
2. Same as above, simultaneously record from thawed node.
3. Record from two synchrounsly frozen/thawed nodes in accumulator mode.
4. Same as above, but one node permanently thawed.

Author: Plesser
FirstVersion: 2011-02-11
*/

/unittest (8831) require
/unittest using

/arr (9161) require

M_ERROR setverbosity


/clear_error
{ 
  counttomark npop % pop all but mark
  errordict begin /newerror false def end
} def

% integer_data --- return array with only unique entries in ascending order
%                  data must be intergers
%           
/unique_ascending
{
  /d << >> def
  { cvs cvlit d exch 0 put } forall
  d keys { cvs cvi } Map Sort
}
def

% sender data --- split data by sender
/split_data
{
  /data Set
  dup { cvs cvlit } Map /senders Set
  unique_ascending { cvs cvlit } Map /us Set
  /res << >> def
  us { res exch [] put } forall
  [ data senders ] { /key Set res key get exch append res exch key exch put  } ScanThread
  res
}
def

% times data --- times and data have corresponding entries; entries in 
%                data with equalt times are summed.
/sum_by_times
{
  /data Set
  dup { cvs cvlit } Map /times Set
  unique_ascending { cvs cvlit } Map /utimes Set
  /res << >> def
  utimes { res exch 0 put } forall
  [ data times ] { /key Set res key get add res exch key exch put  } ScanThread
  [ ] utimes { res exch get append } forall
}
def


% first test:
% one noded, cycled
{
  << >> begin
   /build_net { 
     /iaf_neuron << /I_e 50.0 >> SetDefaults 
     /multimeter << /record_from [ /V_m ] /interval 0.5 >> SetDefaults
     /n  /iaf_neuron Create def  
     /mm /multimeter Create def  
     mm n Connect
   } def
 
   % sim with freeze/thaw
   ResetKernel
   build_net
   n << /frozen true >> SetStatus
   5. Simulate
   n << /frozen false >> SetStatus
   5. Simulate
   n << /frozen true >> SetStatus
   5. Simulate
   n << /frozen false >> SetStatus
   5. Simulate
   mm /events get /V_m get cva /fV Set

   % simulate for same equivalent time (ie thawed time only) without freezing
   ResetKernel
   build_net
   10. Simulate
   mm /events get /V_m get cva /rV Set
     
   fV rV eq  % both should give same result

  end
} assert_or_die

% second test:
% two nodes, one cycled
{
  << >> begin
   /build_net { 
     /iaf_neuron << /I_e 50.0 >> SetDefaults 
     /multimeter << /record_from [ /V_m ] /interval 0.5 /withgid true 
                    /time_in_steps true >> SetDefaults
     /nc /iaf_neuron Create def  
     /np /iaf_neuron Create def  
     /mm /multimeter Create def  
     mm nc Connect
     mm np Connect
   } def
 
   % sim with freeze/thaw
   ResetKernel
   build_net
   nc << /frozen true >> SetStatus
   5. Simulate
   nc << /frozen false >> SetStatus
   5. Simulate
   nc << /frozen true >> SetStatus
   5. Simulate
   nc << /frozen false >> SetStatus
   5. Simulate

   mm /events get /ev Set
   ev /times   get cva /tall Set
   ev /senders get cva /sall Set
   ev /V_m     get cva /Vall Set

   % check event times by sender
   sall tall split_data dup
   np cvs cvlit get [5 190 5] Range eq exch
   nc cvs cvlit get [55 100 5] Range [155 190 5] Range join eq
   and

   % check event data by sender---frozen data equal to first points in non-frozen
   sall Vall split_data dup
   nc cvs cvlit get /Vc Set
   np cvs cvlit get Vc length Take Vc eq
   and
  
  end
} assert_or_die


% third test:
% several nodes, synchronously thawed and frozen
{
  ResetKernel
  << >> begin
    /recvars [ /V_m ] def
    /N 10 def

        /nnet /subnet Create def
        nnet ChangeSubnet
	/iaf_psc_alpha N Create ;
        0 ChangeSubnet
        /nrns nnet GetGlobalLeaves def

        /multimeter << /record_from recvars /withgid false /withtime true 
                       /interval 0.7 /start 4.8 /stop 233.1 /time_in_steps true >> SetDefaults
        /mm /multimeter Create def	
        /ac /multimeter << /to_memory false /to_accumulator true >> Create def	
        mm nrns DivergentConnect
        ac nrns DivergentConnect

        /pg /poisson_generator << /rate 1000. >> Create def
        /static_synapse /exc << /weight  1.0 >> CopyModel 
        /static_synapse /inh << /weight -1.0 >> CopyModel  
        pg nrns /exc DivergentConnect
        pg nrns /inh DivergentConnect

        nrns { << /frozen true >> SetStatus } forall
        20. Simulate
        nrns { << /frozen false >> SetStatus } forall
        20. Simulate
        nrns { << /frozen true >> SetStatus } forall
        20. Simulate
        nrns { << /frozen false >> SetStatus } forall
        20. Simulate

        /detevs mm /events get def
        /dettimes detevs /times get cva def          
        /accevs ac /events get def	
        /acctimes accevs /times get cva def          
        
        recvars 
        { 
          /var Set
          % get data from individual recordings
	  detevs var get cva
          % compute mean for error estimate
          dup Mean /varmean Set
          % sum results for equal times
          dettimes exch sum_by_times

          % data from accumulated recording
          accevs var get cva

          % test difference relative to mean	  
          sub true exch { varmean div abs 1e-15 lt and } Fold 
	} Map 

        true exch { and } Fold 

        % check that times are equal  
        dettimes unique_ascending acctimes eq and  

  end
} assert_or_die


% Fourth test:
% As second, but run in accumulator mode. This does NOT MAKE SENSE, really ...
{
  << >> begin
   /build_net { 
     /iaf_neuron << /I_e 0.0 >> SetDefaults 
     /multimeter << /record_from [ /V_m ] /interval 0.5 /withgid false 
                    /time_in_steps true /record_to [ /accumulator ] >> SetDefaults
     /nc /iaf_neuron Create def  
     /np /iaf_neuron Create def  
     /mm /multimeter Create def  
     mm nc Connect
     mm np Connect
   } def
 
   % sim with freeze/thaw
   ResetKernel
   build_net
   nc << /frozen true >> SetStatus
   5. Simulate
   nc << /frozen false >> SetStatus
   5. Simulate
   nc << /frozen true >> SetStatus
   5. Simulate
   nc << /frozen false >> SetStatus
   5. Simulate

   mm /events get /ev Set
   ev /times   get cva [5 190 5] Range eq % should be all time points

   % for voltage, we can't sort by sender, but all points where nc is not frozen
   % should have double the value from the others
   ev /V_m get cva /Vall Set
   /fp [ 1 10] Range [21 30] Range join def
   /tp [11 20] Range [31 38] Range join def
   /Vref /iaf_neuron GetDefaults /E_L get def
   nc /V_m get Vref eq np /V_m get Vref eq and assert % ascertain V_m has not changed

   true fp { Vall exch 1 sub get   Vref     eq and } Fold
   true tp { Vall exch 1 sub get 2 Vref mul eq and } Fold
   and
  
   and

  end
} assert_or_die

endusing