昨天的问题顺利解决,原因是我对指针的操作不熟悉,但至于详细原因是什么我也不知道,所以我就放弃了指针的操作,下面给出神经元的代码
#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号神经元也发射一个脉冲,而一号神经元处于静默期,所以没有收到,符合流程