Моделирование электричесой активности нейронов C# реализация

Данный класс представляет из себя реализацию модели описанной в статье автора esir_pavel

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace SpikeNN
{
    class SpikeNN
    {
        const float h     = .5f;    // временной шаг интегрирования в мс
        const int   Tsim  = 500; // время симуляции в дискретных отсчетах
        const int   Nexc  = 100;    // Количество возбуждающих (excitatory) нейронов
        const int   Ninh  = 25;     // Количество тормозных (inhibitory) нейронов
        const int   Nneur = Nexc + Ninh;
        const int   Ncon  = (int)(Nneur*Nneur*0.1f); // Количество сязей, 0.1 это вероятность связи между 2-мя случайными нейронами
 
        float[,] Vms = new float[Nneur,Tsim]; // мембранные потенциалы
        float[,] Ums = new float[Nneur,Tsim]; // вспомогательные переменные модели Ижикевича
        float[] Iex = new float [Nneur];   // внешний постоянный ток приложенный к нейрону
        float[] Isyn= new float [Nneur];  // синаптический ток на каждый нейрон
 
        int[] pre_conns= new int [Ncon];     // индексы пресинаптических нейронов
        int[] post_conns= new int [Ncon];   // индексы постсинаптических нейронов
        float[] weights = new float[Ncon];  // веса связей
        float[,] y = new float[Ncon,Tsim];    // переменная модулирующая синаптический ток в зависимости от спайков на пресинапсе
 
        float psc_excxpire_time = 4.0f; // характерное вермя спадания постсинаптического тока, мс
        float minWeight = 50.0f; // веса, размерность пкА
        float maxWeight = 100.0f;
 
        // Параметры нейрона
        float Iex_max = 40.0f; // максимальный приложенный к нейрону ток 50 пкА
        float a    = 0.02f;
        float b    = 0.5f;
        float c    = -40.0f; // значение мембранного потенциала до которого он сбрасываеться после спайка
        float d    = 100.0f;
        float k    = 0.5f;
        float Vr   = -60.0f;
        float Vt   = -45.0f;
        float Vpeak   = 35.0f;  // максимальное значение мембранного потенциала, при котором происходит сброс до значения с
        float V0   = -60.0f; // начальное значение для мембранного потенциала
        float U0   = 0.0f;   // начальное значение для вспомогательной переменной
        float Cm      = 50.0f;  // электрическая ёмкость нейрона, размерность пкФ
 
        float[] spike_times=new float[Nneur*Tsim];    // времена возникновения спайков
        int[] spike_neurons = new int[Nneur * Tsim];  // индексы нейронов на которых происходят спайки
        int spike_num = 0;
 
        Random rnd = new Random();
        void init_connections()
        {
            for (int con_idx = 0; con_idx < Ncon; )
            {
                // случайно выбираем постсипантические и пресинаптические нейроны
                int pre = rnd.Next(Nneur);
                int post = rnd.Next(Nneur);
                pre_conns[con_idx] = pre;
                post_conns[con_idx] = post;
                weights[con_idx] = rnd.Next((int)(((int)(maxWeight - minWeight) * 10) / 10.0f)) + minWeight;
                if (pre >= Nexc)
                {
                    // если пресинаптический нейрон тормозный то вес связи идет со знаком минус
                    weights[con_idx] = -weights[con_idx];
                }
                y[con_idx,0] = 0.0f;
                con_idx++;
            }
        }
 
        void init_neurons()
        {
            for (int neur_idx = 0; neur_idx < Nneur; neur_idx++)
            {
                // случайно разбрасываем приложенные токи
                Iex[neur_idx] = (int)(rnd.Next( (int)(Iex_max * 10)) / 10.0f);
                Isyn[neur_idx] = 0.0f;
                Vms[neur_idx,0] = V0;
                Ums[neur_idx,0] = U0;
            }
        }
 
 
        float izhik_Vm(int neuron, int time)
        {
            return (k * (Vms[neuron,time] - Vr) * (Vms[neuron,time] - Vt) - Ums[neuron,time] + Iex[neuron] + Isyn[neuron]) / Cm;
        }
 
        float izhik_Um(int neuron, int time)
        {
            return a * (b * (Vms[neuron,time] - Vr) - Ums[neuron,time]);
        }
 
        void save2file()
        {
            List<string> rastr = new List<string>();
            string s="";
            for (int k = 0; k < spike_num; k++)
            {
                s=spike_times[k]+"; " + (spike_neurons[k] + 1).ToString() + "; \n" ;
                rastr.Add(s);
            }
            File.WriteAllLines("rastr.csv", rastr);
 
            // Вычисление среднего по всей сети мембранного потенциала в каждый момент времени
            // нечто наподобие электроэнцефалографии
            List<string> oscill = new List<string>();
            for (int t = 0; t < Tsim; t++)
            {
                float Vm_mean = 0.0f;
                for (int m = 0; m < Nneur; m++)
                {
                    Vm_mean += Vms[m,t];
                }
                Vm_mean /= Nneur;
                s = (t * h).ToString() + "; " + Vm_mean.ToString() + "; \n";
                oscill.Add(s);
            }
            File.WriteAllLines("oscill.csv", oscill);
        }
 
       public  void Start()
        {
            init_connections();
            init_neurons();
            float expire_coeff = -h / psc_excxpire_time;
            expire_coeff =(float) Math.Exp(Convert.ToDouble(expire_coeff));
            for (int t = 1; t < Tsim; t++)
            {
                // проходим по всем нейронам
                for (int neur = 0; neur < Nneur; neur++)
                {
                    Vms[neur,t] = Vms[neur,t - 1] + h * izhik_Vm(neur, t - 1);
                    Ums[neur,t] = Ums[neur,t - 1] + h * izhik_Um(neur, t - 1);
                    Isyn[neur] = 0.0f;
 
                    if (Vms[neur,t - 1] > Vpeak)
                    {
                        Vms[neur,t] = c;
                        Ums[neur,t] = Ums[neur,t - 1] + d;
                        spike_times[spike_num] = t * h;
                        spike_neurons[spike_num] = neur;
                        spike_num++;
                    }
                }
 
                // проходим по всем связям
                for (int con = 0; con < Ncon; con++)
                {
                    y[con,t] = y[con,t - 1] * expire_coeff;
 
                    if (Vms[pre_conns[con],t - 1] > Vpeak)
                    {
                        y[con,t] = 1.0f;
                    }
                    Isyn[post_conns[con]] += y[con,t] * weights[con];
                }
            }
            save2file();
        }
    }
}
 

Комментарии

Популярные сообщения