001    package cnslab.cnsnetwork;
002    
003    import java.io.*;
004    
005    import org.slf4j.Logger;
006    import org.slf4j.LoggerFactory;
007    
008    import cnslab.cnsmath.*;
009    import edu.jhu.mb.ernst.model.Synapse;
010    import edu.jhu.mb.ernst.util.slot.Slot;
011    
012    /***********************************************************************
013    * Implemented simple integrate and fire neuron. Only two channels are
014    * allowed, one excitatory and one inhibitory, and the decay of
015    * inhibitory channel should be longer than the excitatory one.
016    * Look up table is used for quick spike test.
017    * 
018    * @version
019    *   $Date: 2012-08-04 13:43:22 -0500 (Sat, 04 Aug 2012) $
020    *   $Rev: 104 $
021    *   $Author: croft $
022    * @author
023    *   Yi Dong
024    * @author
025    *   David Wallace Croft
026    ***********************************************************************/
027    public class  SIFNeuron
028      implements Neuron, Serializable
029    ////////////////////////////////////////////////////////////////////////
030    ////////////////////////////////////////////////////////////////////////
031    {
032
033    private static final long  serialVersionUID = 0L;
034    
035    public static double  maxWeight = 0;
036
037    /** For table size */
038    public static int TABLESIZE_o = 40;
039    
040    /** For table size */
041    public static int TABLESIZE_c = 500;
042    
043    /** For table min boundary  */
044    public static double  memMin = -0.10; // min -100 mV membrane potential
045    
046    /** For table  max boundary */
047    public static double  memMax = -0.04; // max -40 mV membrane potential
048    
049    /** For table min boundary */
050    public static double [ ]
051      currMin = new double [ ] { 0, -maxWeight }; // minimum Current
052    
053    /** For table max boundary */
054    public static double [ ]
055      currMax = new double [ ] { maxWeight, 0 };
056    
057    /** For table step size */
058    public static double  memStep = ( memMax - memMin ) / TABLESIZE_o;
059    
060    /** For table step size */
061    public static double currStep = ( maxWeight ) / TABLESIZE_c;
062
063    // absoulte refractory period    
064    // public static final double para.ABSREF = 0.000;
065
066    // voltage from threshold for which the neuron is considered to fire
067    // public static final double para.EPSV = 1E-14;
068    
069    // used for absoulte refractory period    
070    // public double timeOffset=0.0;
071
072    /** long imposes a constraint on the maximum number of hosts to be 64 */
073    public long  tHost;
074    
075    // private static variables
076    
077    private static final Class<SIFNeuron>
078      CLASS = SIFNeuron.class;
079  
080    private static final Logger
081      LOGGER = LoggerFactory.getLogger ( CLASS );
082    
083    // private final variables
084
085    private final SIFNeuronPara  para;
086    
087    // private non-final variables
088
089    private double [ ]  curr;
090    
091    /** true then this neuron will be recorded */
092    private boolean  record;
093
094    /** the neuron membrane potential */
095    private double  memV;
096
097    /** time of the neuron's next fire */
098    private double  timeOfNextFire = -1;
099    
100    /** time of the neuron's last update */
101    private double  timeOfLastUpdate;
102    
103    ////////////////////////////////////////////////////////////////////////
104    // static methods
105    ////////////////////////////////////////////////////////////////////////
106    
107/*
108    public static double maxWeight()
109    ////////////////////////////////////////////////////////////////////////
110    {
111      return para.CAP*(para.THRESHOLD-para.VRESET)/para.MINRISETIME;
112    }
113*/
114    
115    ////////////////////////////////////////////////////////////////////////
116    // constructor methods
117    ////////////////////////////////////////////////////////////////////////
118
119    public  SIFNeuron ( final SIFNeuronPara  para )
120    ////////////////////////////////////////////////////////////////////////
121    {
122      if ( maxWeight
123        < para.CAP * ( para.THRESHOLD - para.VRESET ) / para.MINRISETIME )
124      {
125        maxWeight
126          = para.CAP * ( para.THRESHOLD - para.VRESET ) / para.MINRISETIME;
127        
128        currStep = maxWeight / TABLESIZE_c;
129      }
130
131      this.para = para;
132      
133      this.curr = new double [ 2 ];
134      
135      curr[0]=0.0;
136      
137      curr[1]=0.0;
138
139      if ( !para.GlobalInit )
140      {
141        para.FIRETABLE = new boolean
142          [ TABLESIZE_o + 1 ]
143          [ TABLESIZE_c + 1 ]
144          [ TABLESIZE_c + 1 ];
145
146        for ( int  i = 0; i < TABLESIZE_o + 1; i++ )
147        {
148          for ( int  k = 0; k < TABLESIZE_c + 1; k++ )
149          {
150            for ( int  m = 0; m < TABLESIZE_c + 1; m++ )
151            {
152              this.memV = i * memStep + memMin;
153              
154              this.curr = new double [ ] {
155                k * currStep + currMin [ 0 ],
156                m * currStep + currMin [ 1 ] };
157              
158              if ( this.memV >= para.THRESHOLD )
159              {
160                para.FIRETABLE [ i ] [ k ] [ m ] = true;
161              }
162              else
163              {
164                if ( doesFire ( ) )
165                {
166                  para.FIRETABLE [ i ] [ k ] [ m ] = true;
167                }
168                else
169                {
170                  para.FIRETABLE [ i ] [ k ] [ m ] = false;
171                }
172              }
173            }
174          }
175        }
176        
177        para.GlobalInit = true;
178      }
179    }
180
181    ////////////////////////////////////////////////////////////////////////
182    // interface Neuron accessor methods
183    ////////////////////////////////////////////////////////////////////////
184    
185/*
186    public Axon getAxon()
187    ////////////////////////////////////////////////////////////////////////
188    {
189      return axon;
190    }
191*/
192
193    @Override
194    public double [ ]  getCurr ( double  currTime )
195    ////////////////////////////////////////////////////////////////////////
196    {
197      return new double [ ] {
198        curr [ 0 ] * Math.exp (
199          -para.DECAY_E * ( currTime - timeOfLastUpdate ) ),
200        curr [ 1 ] * Math.exp (
201          -para.DECAY_I * ( currTime - timeOfLastUpdate ) ) };
202    }
203
204    @Override
205    public double  getMemV ( final double  currTime )
206    ////////////////////////////////////////////////////////////////////////
207    {
208      // if input comes inside the refractory period, memV not changed
209      
210      if ( currTime < timeOfLastUpdate )
211      {
212        return this.memV; 
213      }
214      
215      return voltageFunOfTime ( currTime - timeOfLastUpdate );
216    }
217    
218    @Override
219    public boolean  getRecord ( )
220    ////////////////////////////////////////////////////////////////////////
221    {
222      return this.record;
223    }
224
225    @Override
226    public long  getTHost ( )
227    ////////////////////////////////////////////////////////////////////////
228    {
229      return tHost;
230    }
231
232    /***********************************************************************
233    * get the value of timeOfNextFire
234    * 
235    * @return
236    *   the value of timeOfNextFire
237    ***********************************************************************/
238    @Override
239    public synchronized double  getTimeOfNextFire ( )
240    ////////////////////////////////////////////////////////////////////////
241    {
242      return this.timeOfNextFire;
243    }
244
245    @Override
246    public boolean  isSensory ( )
247    ////////////////////////////////////////////////////////////////////////
248    {
249      return false;
250    }
251
252    @Override
253    public boolean realFire()
254    ////////////////////////////////////////////////////////////////////////
255    {
256      return true;
257    }
258    
259    ////////////////////////////////////////////////////////////////////////
260    // additional accessor methods
261    ////////////////////////////////////////////////////////////////////////
262
263    /***********************************************************************
264    * It looks this was originally a method in the Neuron interface which
265    * has since been removed.  Probably can be deleted.    
266    ***********************************************************************/
267    public double getSensoryWeight ( )
268    ////////////////////////////////////////////////////////////////////////
269    {
270      throw new RuntimeException("LIF neurons can't run this function");
271    }
272    
273    /***********************************************************************
274    * get the value of timeOfLastUpdate
275    * 
276    * @return
277    *   the value of timeOfLastUpdate
278    ***********************************************************************/
279    public double  getTimeOfLastUpdate ( )
280    ////////////////////////////////////////////////////////////////////////
281    {
282      return this.timeOfLastUpdate;
283    }
284    
285    ////////////////////////////////////////////////////////////////////////
286    // interface Neuron mutator methods
287    ////////////////////////////////////////////////////////////////////////
288    
289    /***********************************************************************
290    * set a new value to memV
291    * 
292    * @param memV
293    *   the new value to be used
294    ***********************************************************************/
295    public void  setMemV ( final double  memV )
296    ////////////////////////////////////////////////////////////////////////
297    {
298      this.memV = memV;
299    }
300
301    /***********************************************************************
302    * set the neuron record or not.
303    ***********************************************************************/
304    @Override
305    public void setRecord ( final boolean  record )
306    ////////////////////////////////////////////////////////////////////////
307    {
308      this.record = record;
309    }
310
311    /***********************************************************************
312    * set the target host id //0 means none , 1 means yes 
313    ***********************************************************************/
314    @Override
315    public void setTHost(long id)
316    ////////////////////////////////////////////////////////////////////////
317    {
318      this.tHost=id;
319    }
320
321    /***********************************************************************
322    * set a new value to timeOfNextFire
323    * 
324    * @param timeOfNextFire
325    *   the new value to be used
326    ***********************************************************************/
327    @Override
328    public synchronized void  setTimeOfNextFire (
329      final double  timeOfNextFire )
330    ////////////////////////////////////////////////////////////////////////
331    {
332      this.timeOfNextFire = timeOfNextFire;
333    }
334
335    ////////////////////////////////////////////////////////////////////////
336    // additional mutator methods
337    ////////////////////////////////////////////////////////////////////////
338    
339    /***********************************************************************
340    * set a new value to timeOfLastUpdate
341    * 
342    * @param timeOfLastUpdate
343    *   the new value to be used
344    ***********************************************************************/
345    public void  setTimeOfLastUpdate ( final double  timeOfLastUpdate )
346    ////////////////////////////////////////////////////////////////////////
347    {
348      this.timeOfLastUpdate = timeOfLastUpdate;
349    }
350
351    ////////////////////////////////////////////////////////////////////////
352    // interface Neuron lifecycle methods
353    ////////////////////////////////////////////////////////////////////////
354    
355    @Override
356    public void  init (
357      int expid,
358      int trialid,
359      Seed idum,
360      Network net,
361      int id)
362    ////////////////////////////////////////////////////////////////////////
363    {
364      this.timeOfLastUpdate=0.0;
365      
366      this.timeOfNextFire=-1;
367      
368      this.memV = para.ini_mem+para.ini_memVar*Cnsran.ran2(idum);
369      
370      this.curr[0]=0.0;
371      
372      this.curr[1]=0.0;
373      
374      double fireTime;
375      
376      // net.p.println("My name is"+net.getClass().getName());
377      
378      if ( doesFire ( ) )
379      {
380        final Slot<FireEvent>  fireEventSlot = net.getFireEventSlot ( );
381        
382        if ( net.getClass ( ).getName ( ).equals (
383          "cnslab.cnsnetwork.ANetwork" ) )
384        {
385          fireEventSlot.offer (
386            new AFireEvent (
387              id,
388              fireTime = timeOfFire ( 0 ),
389              net.info.idIndex,
390              ( ( ANetwork ) net ).aData.getId ( ( ANetwork ) net ) ) );
391        }
392        else if ( net.getClass ( ).getName ( ).equals (
393          "cnslab.cnsnetwork.Network" ) )
394        {
395          fireEventSlot.offer (
396            new FireEvent (
397              id,
398              fireTime = timeOfFire ( 0 ) ) );
399        }
400        else
401        {
402          throw new RuntimeException (
403            "Other Network Class doesn't exist" );
404        }
405        
406        timeOfNextFire = fireTime;
407      }
408    }
409
410    @Override
411    public double  updateFire ( )
412    ////////////////////////////////////////////////////////////////////////
413    {
414      // LOGGER.trace ( "updateFire() entered" );
415      
416      memV = para.VRESET;
417      
418      // LOGGER.debug ( "time of next fire:  {}", timeOfNextFire );
419      
420      // LOGGER.debug ( "time of last update:  {}", timeOfLastUpdate );
421      
422      // LOGGER.debug ( "excitatory current:  {}", curr [ 0 ] );
423      
424      curr [ 0 ]
425        = curr [ 0 ]
426        * Math.exp (
427          -para.DECAY_E * ( timeOfNextFire - timeOfLastUpdate ) )
428        - para.REFCURR;
429      
430      // LOGGER.debug ( "excitatory current:  {}", curr [ 0 ] );
431      
432      // LOGGER.debug ( "inhibitory current:  {}", curr [ 1 ] );
433      
434      curr [ 1 ] *= Math.exp (
435        -para.DECAY_I * ( timeOfNextFire - timeOfLastUpdate ) );
436      
437      // LOGGER.debug ( "inhibitory current:  {}", curr [ 1 ] );
438      
439      timeOfLastUpdate = timeOfNextFire + para.ABSREF;
440
441      curr [ 0 ] *= Math.exp ( -para.DECAY_E * para.ABSREF );
442      
443      // LOGGER.debug ( "excitatory current:  {}", curr [ 0 ] );
444      
445      curr [ 1 ] *= Math.exp ( -para.DECAY_I * para.ABSREF );
446
447      // LOGGER.debug ( "inhibitory current:  {}", curr [ 1 ] );
448      
449      //curr[a]=para.IRATIO[a]*curr[a]*Math.exp(
450      //  -para.DECAY[a]*(timeOfNextFire-timeOfLastUpdate+para.ABSREF))
451      //  +para.IADD[a]*Math.exp(-para.DECAY[a]*para.ABSREF);
452
453      // for excitatory neuron
454      //    curr[0] =0;
455
456      final int
457        memI = ( int ) ( Math.floor ( ( memV - memMin ) / memStep ) );
458      
459      final int [ ]  curI = new int [ curr.length ];
460
461      for ( int  i = 0; i < curr.length; i++ )
462      {
463        curI [ i ] = ( int )
464          ( Math.floor ( ( curr [ i ] - currMin [ i ] ) / currStep ) );
465      }
466
467      int [ ]  memList = null;
468      
469      final int [ ] [ ]  currList = new int [ curr.length ] [ ];
470      
471      boolean  fullcheck = false;
472
473      if ( memI < 0)
474      {  
475        fullcheck = true;
476        //      memList = new int [] {0} ;
477      }
478      else if ( memI >= TABLESIZE_o )
479      {
480        fullcheck = true;
481        //      memList = new int [] {TABLESIZE_o} ;
482      }
483      else
484      {
485        memList = new int [ ] { memI, memI + 1 };
486      }
487
488      for ( int  i = 0; i < curr.length; i++ )
489      {
490        if ( curI [ i ] < 0 )
491        {  
492          fullcheck = true;
493          //        currList[i] = new int [] {0} ;
494        }
495        else if ( curI [ i ] == TABLESIZE_c )
496        {
497          if ( i == 0 )
498          {
499            fullcheck = true; //excitatory currents needs to be checked
500          }
501          else
502          {
503            // inhibitory currents is fine
504            
505            currList [ i ] = new int [ ] { TABLESIZE_c };
506          }
507        }
508        else if ( curI [ i ] > TABLESIZE_c )
509        {
510          fullcheck = true;
511        }
512        else
513        {
514          currList [ i ] = new int [ ] { curI [ i ], curI [ i ] + 1 };
515        }
516      }
517      
518      if ( fullcheck )
519      {
520        if ( doesFire ( ) )
521        {
522          return timeOfFire ( timeOfNextFire );
523        }
524        
525        return -1.0;
526      }
527      
528      final boolean  fireS = para.FIRETABLE
529        [ memList [ 0 ] ]
530        [ currList [ 0 ] [ 0 ] ]
531        [ currList [ 1 ] [ 0 ] ];
532
533      for ( int  ii = 0; ii < memList.length; ii++ )
534      {
535        for ( int  kk = 0; kk < currList [ 0 ].length; kk++ )
536        {
537          for ( int  mm = 0; mm < currList [ 1 ].length; mm++ )
538          {
539            if ( !( ii == 0
540                 && kk == 0
541                 && mm == 0 ) )
542            {
543              if ( ( para.FIRETABLE
544                [ memList [ ii ] ]
545                [ currList [ 0 ] [ kk ] ]
546                [ currList [ 1 ] [ mm ] ] ^ fireS ) )
547              {
548                // ambiguous firing case, do a full check
549                
550                if ( doesFire ( ) )
551                {
552                  return timeOfFire ( timeOfNextFire );
553                }
554                
555                return -1.0;
556              }
557            }
558          }
559        }
560      }
561      
562      // pass through this, it is non ambiguous now.
563      
564      if ( fireS ) //do fire
565      {
566        return timeOfFire(timeOfNextFire);
567      }
568      
569      // not fire
570      
571      return -1.0;
572    }              
573
574    @Override
575    public double  updateInput (
576      final double   time,
577      final Synapse  inputSynapse )                             
578    ////////////////////////////////////////////////////////////////////////
579    {
580      // if input comes inside the refractory period only update current to
581      // the last update time.
582      
583      if ( time < timeOfLastUpdate )
584      {
585        switch ( inputSynapse.getType ( ) )
586        {
587          case 0:
588            
589            curr [ 0 ] = curr [ 0 ]
590              + Math.exp ( -para.DECAY_E * ( timeOfLastUpdate - time ) )
591              * inputSynapse.getWeight ( );
592            
593            // if(timeOfLastUpdate - time <=0 )
594            // throw new RuntimeException(
595            // "reversed time at "+time+"lastUPdate "+timeOfLastUpdate
596            // +" input:"+input.toString());
597            
598            // if( curr[0]>1.0 )
599            // throw new RuntimeException(
600            // "two high activity time at "+time+"lastUPdate "
601            // +timeOfLastUpdate+" input:"+input.toString());
602            
603            break;
604            
605          case 1:
606            
607            curr [ 1 ] = curr [ 1 ]
608              + Math.exp ( -para.DECAY_I * ( timeOfLastUpdate - time ) )
609              * inputSynapse.getWeight ( );
610            
611            break;
612            
613          default:
614            
615            System.out.println("no other types available");
616            
617            break;
618        }
619      }
620      else
621      {
622        memV = voltageFunOfTime(time - timeOfLastUpdate);
623        
624        if(memV-para.THRESHOLD>0)
625        {
626          System.out.println(
627            "current time:"+time+" last update: "+timeOfLastUpdate
628            +" time of next firing: "+timeOfNextFire);
629          
630          System.out.println("time diff:"+(time - timeOfLastUpdate));
631          
632          System.out.println("curr0:"+curr[0]+" curr1:"+curr[1]);
633          
634          System.out.println("Membrane voltage: "+memV);
635          
636          throw new RuntimeException (
637            "current time:"+time
638            +" last update: "+timeOfLastUpdate
639            +" time of next firing: "+timeOfNextFire + "\n"
640            +"time diff:"+(time - timeOfLastUpdate)+"\n"
641            +"curr0:"+curr[0]+" curr1:"+curr[1]+"\n"
642            +"Membrane voltage: "+memV+"\n"
643            +"already fires in updateinput" );
644        }
645        
646        switch ( inputSynapse.getType ( ) )
647        {
648          case 0:
649            
650            curr[0]=curr[0]
651              *Math.exp(-para.DECAY_E*(time - timeOfLastUpdate))
652              +inputSynapse.getWeight ( );
653            
654            curr[1]=curr[1]
655              *Math.exp(-para.DECAY_I*(time - timeOfLastUpdate));
656            
657            break;
658            
659          case 1:
660            
661            curr[0] = curr[0]
662              * Math.exp(-para.DECAY_E*(time - timeOfLastUpdate));
663            
664            curr[1] = curr[1]
665              * Math.exp(-para.DECAY_I*(time - timeOfLastUpdate))
666              + inputSynapse.getWeight ( );
667            
668            break;
669            
670          default:
671            
672            System.out.println("no other types available");
673            
674            break;
675        }
676        
677        timeOfLastUpdate = time;
678      }
679
680      int memI = (int)(Math.floor((memV-memMin)/memStep));
681      
682      int [] curI = new int [curr.length];
683
684      for(int i=0; i < curr.length; i++)
685      {
686        curI[i] = (int)(Math.floor((curr[i]-currMin[i])/currStep));
687      }
688
689      int [] memList=null;
690      
691      int [][] currList = new int [curr.length][];
692      
693      boolean fullcheck = false;
694
695      if(memI<0)
696      {  
697        fullcheck = true;
698        
699        // memList = new int [] {0} ;
700      }
701      else if( memI >= TABLESIZE_o )
702      {
703        fullcheck = true;
704        
705        // memList = new int [] {TABLESIZE_o} ;
706      }
707      else
708      {
709        memList = new int [] {memI, memI+1} ;
710      }
711
712      for(int i=0; i < curr.length; i++)
713      {
714        if(curI[i]<0)
715        {  
716          fullcheck = true;
717          
718          // currList[i] = new int [] {0} ;
719        }
720        else if( curI[i] == TABLESIZE_c )
721        {
722          if(i==0)
723          {
724            fullcheck = true; //excitatory currents needs to be checked
725          }
726          else
727          {
728            // inhibitory currents is fine
729            currList[i] = new int [] {TABLESIZE_c} ;
730          }
731        }
732        else if( curI[i] > TABLESIZE_c )
733        {
734          fullcheck = true;
735        }
736        else
737        {
738          currList[i] = new int [] {curI[i],curI[i]+1} ;
739        }
740      }
741      
742      if(fullcheck)
743      {
744        if(doesFire())
745        {
746          return timeOfFire(time);
747        }
748        return -1.0;
749      }
750      
751      final boolean  fireS = para.FIRETABLE
752        [ memList [ 0 ] ]
753        [ currList [ 0 ] [ 0 ] ]
754        [ currList [ 1 ] [ 0 ] ];
755
756      for(int ii=0; ii < memList.length; ii ++)
757      {
758        for(int kk=0; kk< currList[0].length; kk++)
759        {
760          for(int mm=0; mm< currList[1].length; mm++)
761          {
762            if(!(ii==0 && kk==0 && mm==0))
763            {
764              if ( ( para.FIRETABLE
765                [ memList [ ii ] ]
766                [ currList [ 0 ] [ kk ] ]
767                [ currList [ 1 ] [ mm ] ] ^ fireS ) )                
768              {
769                // ambiguous firing case, do a full check
770                
771                if(doesFire())
772                {
773                  return timeOfFire(time);
774                }
775                
776                return -1.0;
777              }
778            }
779          }
780        }
781      }
782      
783      //pass through this, it is non ambiguous now.
784      
785      if(fireS) //do fire
786      {
787        return timeOfFire(time);
788      }
789      
790      // not fire
791      
792      return -1.0;
793    }
794
795    ////////////////////////////////////////////////////////////////////////
796    // additional methods
797    ////////////////////////////////////////////////////////////////////////
798    
799    /***********************************************************************
800    * @return
801    *   true if the neuron will fire in future
802    ***********************************************************************/
803    public boolean  doesFire ( )
804    ////////////////////////////////////////////////////////////////////////
805    {
806      double ts = 0.0;
807
808      double dt, tsmax;
809      
810      int  i = 0, imax;
811
812      // maximum time to look for firing
813      
814      tsmax = 3.0 * ( para.DECAY_E > para.CAP / para.GL
815        ? para.DECAY_E : para.CAP / para.GL );
816      
817      // tsmax=2*(para.CAP/para.GL);//maximum time to look for firing
818      
819      // max number of steps to look for firing
820      
821      imax = 100000;
822
823// TODO:  Should this be >= instead of >?  memV >= para.THRESHOLD
824      
825      if ( memV - para.THRESHOLD > 0 )
826      {
827        System.out.println (
828          "last update "
829          + timeOfLastUpdate
830          + "time of next firing "
831          + timeOfNextFire );
832        
833        System.out.println ( "Membrane voltage: " + memV );
834        
835        throw new RuntimeException ( "already fires in doesfire" );
836      }
837      
838      // if(derivvoltageFunOfTime(ts)<0)return false;
839      
840      while ( ts < tsmax )
841      {
842        if ( deriVoltageFunOfTime ( ts ) <= 0 )
843        {
844          return false;
845        }
846        
847        dt = ( para.THRESHOLD - voltageFunOfTime ( ts ) )
848          / deriVoltageFunOfTime ( ts );
849        
850        if ( voltageFunOfTime ( ts + 2 * dt ) > para.THRESHOLD )
851        {
852          // guess=ts+2*dt;
853          
854          return true;
855        }
856        
857        ts += dt;
858        
859        if ( voltageFunOfTime ( ts ) > para.THRESHOLD )
860        {
861          // guess = ts;
862          
863          return true;
864        }
865        
866        i++;
867        
868        if ( i > imax )
869        {
870          throw new RuntimeException (
871            "max steps reached in doesfire, memV="
872            + memV + " curr=" + curr [ 0 ] + " " + curr [ 1 ] );
873        }
874      }
875      
876      return false;
877    }
878
879    /***********************************************************************
880    * @return
881    *   the time when the neuron fires
882    ***********************************************************************/
883    public double  timeOfFire ( final double  currTime )
884    ////////////////////////////////////////////////////////////////////////
885    {
886      double  ts = 0;
887      
888      // double  dt;
889      
890      int
891        i = 0,
892        imax = 100000;
893
894      while ( i < imax )
895      {
896        ts += ( para.THRESHOLD - voltageFunOfTime ( ts ) )
897          / deriVoltageFunOfTime ( ts );
898        
899        if ( Math.abs ( 1 - ( voltageFunOfTime ( ts ) / para.THRESHOLD ) )
900          < para.EPSV )
901        {
902          return ts + ( ( timeOfLastUpdate - currTime ) < 0 ? 0.0
903            : ( timeOfLastUpdate - currTime ) );
904        }
905        
906        i++;
907      }
908
909      throw new RuntimeException (
910        "Too many iteratations were encounted in timeOfFire currtime:"
911        + currTime
912        + " timeOfLastUpdate:"
913        + timeOfLastUpdate
914        + " curr[0]:"
915        + curr [ 0 ]
916        + " curr[1]:"
917        + curr [ 1 ]
918        + " memV:"
919        + memV
920        + " timeOfnextFire:"
921        + timeOfNextFire );
922    }
923
924    ////////////////////////////////////////////////////////////////////////
925    // overridden Object methods
926    ////////////////////////////////////////////////////////////////////////
927    
928    @Override
929    public String  toString ( )
930    ////////////////////////////////////////////////////////////////////////
931    {
932      String  tmp = "";
933      
934      tmp = tmp + "MemVol:" + memV + "\n";
935      
936      tmp = tmp + "Current:" + "\n";
937      
938      for ( int  i = 0; i < curr.length; i++)
939      {
940        tmp = tmp + "       curr" + i + " " + curr [ 0 ] + "\n";
941      }
942      
943      // tmp = tmp + axon;
944      
945      return tmp;
946    }
947
948    ////////////////////////////////////////////////////////////////////////
949    // private methods
950    ////////////////////////////////////////////////////////////////////////
951    
952    /***********************************************************************
953    * @param _diff
954    *   time difference between current time and future time
955    * @return
956    *   derivative of membrane voltage with respect to time
957    ***********************************************************************/
958    private double  deriVoltageFunOfTime ( final double  _diff )
959    ////////////////////////////////////////////////////////////////////////
960    {
961      return (
962        ( curr [ 0 ] * para.DECAY_E )
963          / ( Math.exp ( para.DECAY_E * _diff )
964          * ( para.CAP * para.DECAY_E - para.GL ) )
965        + ( curr [ 1 ] * para.DECAY_I )
966          / ( Math.exp ( para.DECAY_I * _diff )
967          * ( para.CAP * para.DECAY_I - para.GL ) )
968        - ( para.GL
969          * ( curr [ 0 ] / ( para.CAP * para.DECAY_E - para.GL )
970          + curr [ 1 ] / ( para.CAP * para.DECAY_I - para.GL )
971          + memV
972          - para.VREST ) )
973          / ( para.CAP * Math.exp ( ( para.GL * _diff ) / para.CAP ) ) );
974    }
975
976    /***********************************************************************
977    * @param _diff
978    *   time difference between current time and future time
979    * @return
980    *   membrane voltage
981    ***********************************************************************/
982    private double  voltageFunOfTime ( double  _diff )
983    ////////////////////////////////////////////////////////////////////////
984    {                       
985      return (
986        -curr [ 0 ]
987          / ( Math.exp ( para.DECAY_E * _diff )
988            * ( para.CAP * para.DECAY_E - para.GL ) )
989        - curr [ 1 ]
990          / ( Math.exp ( para.DECAY_I * _diff )
991            * ( para.CAP * para.DECAY_I - para.GL ) )
992        + ( curr [ 0 ] / ( para.CAP * para.DECAY_E - para.GL )
993          + curr [ 1 ] / ( para.CAP * para.DECAY_I - para.GL )
994          + memV - para.VREST )
995          / Math.exp ( ( para.GL * _diff ) / para.CAP )
996        + para.VREST );
997    }    
998
999    ////////////////////////////////////////////////////////////////////////
1000    ////////////////////////////////////////////////////////////////////////
1001    }