重新设计仿真方案

文章介绍了LIF神经元模型的实现,包括膜电位、突触电流、时间常数等参数,以及使用环形缓冲机制处理脉冲事件和模拟神经元间的通信。作者展示了如何用C++实现神经元更新和脉冲传递过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天的问题顺利解决,原因是我对指针的操作不熟悉,但至于详细原因是什么我也不知道,所以我就放弃了指针的操作,下面给出神经元的代码

#ifndef NEURON_H
#define NEURON_H

#include "Params.h"
#include "Ringbuffer.h"

class Neuron
{
private:
    double v;               //膜电压
    double last_fired;      //上次发放脉冲的的时间
    int gid;                //神经元全局id
    double i_e;             //累加电流
    double dt;              //时间精度

    RingBuffer buffer;

    struct NeuronParams
    {
        // LIF神经元的参数
        float v_rest;			// 静息电位
        float v_reset;			// 重置电位
        float tau_m;			// 膜时间常数
        float refractory;       //不应期时间
        float c_m;				//膜电容
        float r_m;              //膜电阻
        float v_thresh;		   // 脉冲发放阈值
        float i_offset;	    // 注入电流
        float dt;

        /// @brief 初始化参数
        NeuronParams();
    };

    NeuronParams params;

    /// @brief 神经元的初始化操作
    /// @param v_init 初始电压
    void init(double v_init,int gid_);

public:

    /// @brief 有参构造函数
    /// @param gid_ 神经元的全局id
    Neuron(double v_init,int gid_);
    ~Neuron() = default;

    /// @brief 神经元更新的主要函数
    /// @param current_t 当前的时间
    /// @param spike_evnet 用来收集脉冲时间的,主要是发射脉冲的神经元和发射的时间
    void update(int from,int to,float current_t,std::vector<SpikeEvent>& spike_evnet);

    /// @brief 处理脉冲事件
    /// @param se 脉冲事件
    void handle(SpikeEvent se);

    
};


Neuron::NeuronParams::NeuronParams()
    :tau_m(10.0f),
    refractory(2.0f),
    v_rest(-45.0f),
    v_thresh(-50.0f),
    v_reset(-65.0f),
    i_offset(0.95f),
    c_m(0.250f),
    r_m(40.0f),
    dt(0.1)
{
}


Neuron::Neuron(double v_init,int gid_)
{
    init(v_init,gid_);
}

void Neuron::init(double v_init,int gid_)
{
    
    v = v_init;
    i_e = 0;
    last_fired = -params.refractory;
    gid = gid_;
    dt = params.dt;
    buffer.resize(10);

    printf("%d init successful!\n",gid);

    if(gid = 1)
    {
        params.i_offset = 0.0;
    }
}

void Neuron::update(int from,int to,float current_t_,std::vector<SpikeEvent>& spike_evnet)
{
    double current_t = current_t_;
    
    for(int lag; lag < to; ++lag)
    {
        
        if(current_t >= params.refractory + last_fired) //判断是否在不应期
        {
            
            double I_syn = buffer.get_value(lag); //获取突触电流
            if (I_syn != 0)
            {
                printf("post neuron receive time is %lf\n",current_t);
            }
            
            i_e = params.i_offset + I_syn; //输入电流和突触电流的累加

            v += dt * ((params.v_rest - v + i_e * params.r_m)/params.tau_m); //计算膜电位


            if(v > params.v_thresh)  //判断是否发射脉冲
            {
				v = params.v_reset;
				last_fired = current_t; //记录本次发放脉冲的时间

                SpikeEvent se; //脉冲事件
                se.preneuron = gid;
                se.lag = lag;
                spike_evnet.push_back(se);
                printf("spike happenned in %lf\n",current_t);
			}
            current_t += dt;
        }
        else
        {
            buffer.reset();
        }
        
        i_e = 0; //重置电流

    }
}

void Neuron::handle(SpikeEvent se)
{
    buffer.add_value(se.lag,se.weight);
}
#endif

每个神经元的缓冲机制为

#ifndef RING_BUFFER_H
#define RING_BUFFER_H


#include<vector>

/// @brief 此类是每个神经元的事件缓冲区,
//在每个时间步长,可以读取和擦除当前值。在一个时间步长结束时,所有事件缓冲区都旋转一个段,以便在下一个时间步长中可以读取下一个值。
class RingBuffer
{

public:
	RingBuffer() = default;
	~RingBuffer() = default;

	/// @brief 将突触输入的电流,累加到固定的延迟后
	void add_value(int offset, double val);
	
	/// @brief 获取offset位置的值
	/// @param offset 
	/// @return 
	double get_value(int offset);
	
	/// @brief 返回缓冲区长度 
	size_t size();
    
    /// @brief 设置缓冲区大小
    void resize(int len);

	/// @brief 重置buffer
	void reset();

	/// @brief 清楚缓冲区
	void clear();
private:
	std::vector<double> buffer;
};


void RingBuffer::add_value(int offset, double val)
{
	buffer[offset] += val;
}

double RingBuffer::get_value(int offset)
{
	double val = buffer[offset];
	buffer[offset] = 0.0;
	return val;
}

size_t RingBuffer::size()
{
	return buffer.size();
}

void RingBuffer::resize(int len)
{
    buffer.resize(len,0.0);
}

void RingBuffer::clear()
{
	buffer.clear();
}

void RingBuffer::reset()
{
	std::fill(buffer.begin(),buffer.end(),0.0);
}
#endif


脉冲事件的数据结构为

/// @brief 脉冲事件的描述
struct SpikeEvent
{
    int preneuron;      //后神经元的gid
    int lag;            //发射脉冲的位置
    double delay;       //读取到的突触上的延迟
    double weight;      //读取到的突触上的权重
};

我只模拟了两个神经元,突触的延迟设置为1ms,仿真精度为0.1

给出模拟代码

#include <iostream>
#include <vector>
#include <random>


#include "Neuron.h"
#include "Synapse.h"
#include "Params.h"


int main()
{
	//设置网络  定义邻接表
    std::vector<std::vector<int>> adjList = {{1},{}};

    //实例化神经元


    // 创建随机数生成器
    std::random_device rd;
    std::mt19937 gen(rd());
    // 生成随机浮点数
    std::uniform_real_distribution<double> realDist(-65.0, -50.0);  // 生成范围为-65.0到-55.0的浮点数

    std::vector<Neuron> localNeuron;
    for (int i = 0; i < adjList.size(); i++)
    {
        double v_init = realDist(gen);
        localNeuron.push_back(Neuron(v_init,i));
    }

    printf("localNeuron size is %d\n",localNeuron.size());


    
    //实例化突触
    std::vector<std::vector<Synapse>> localSynapse(adjList.size());
    for (int i = 0; i < adjList.size(); i++)
    {
        for (int j = 0; j < adjList[i].size(); j++)
        {
            localSynapse[i].push_back(Synapse(1,0.2,&localNeuron[adjList[i][j]]));
        }
        
    }
    

    
    //设置仿真的参数
    double dt = 0.1; //时间戳
    double delay = 1;
    double sim_time = 10; //仿真时间
    int slice = static_cast<int>(sim_time / delay);//总共要执行循环数
    int timespan = delay /dt;
    double current_time = 0;    


    std::vector<SpikeEvent> spike_event;
    
    //开始仿真
    for (int timestep = 0; timestep < slice; timestep++)
    {

        current_time = timestep * delay;

        printf("current time is: %lf\n",current_time);

        //更新神经元
        for (auto node = localNeuron.begin(); node != localNeuron.end(); node++)
        {
            (*node).update(0,10,current_time,spike_event);
        }
        
        //传递脉冲
        for (int i = 0; i < spike_event.size(); i++)
        {
            int pre_gid = spike_event[i].preneuron; //前神经元脉冲的gid

            for (int j = 0; j < localSynapse[i].size(); j++)
            {
                localSynapse[i][j].send(spike_event[i]);//找到对应的突触将脉冲发送给后神经元
            }
            
        }

        spike_event.clear();  //清楚本回合的脉冲

        
    }
    

    
}

执行结果为

在2.2时刻0号神经元也发射一个脉冲,而一号神经元处于静默期,所以没有收到,符合流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值