from __future__ import annotations
from typing import Optional, Tuple, Union
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import torch
from ._utils import _to_numpy
__all__ = ["plot_1d_spikes", "plot_one_neuron_v_s"]
[文档]
def plot_1d_spikes(
spikes: Union[np.ndarray, torch.Tensor],
title: str,
xlabel: str,
ylabel: str,
int_x_ticks: bool = True,
int_y_ticks: bool = True,
plot_firing_rate: bool = True,
firing_rate_map_title: str = "firing rate",
figsize: Tuple[float, float] = (12, 8),
dpi: int = 200,
) -> Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]:
r"""
**API Language** - :ref:`中文 <plot_1d_spikes-cn>` | :ref:`English <plot_1d_spikes-en>`
----
.. _plot_1d_spikes-cn:
* **中文**
画出 N 个时长为 T 的脉冲数据。可以用来画 N 个神经元在 T 个时刻的脉冲发放情况。
:param spikes: shape=[T, N] 的数组,元素只能为 0 或 1,表示 N 个时长为 T 的脉冲数据。
支持 ``np.ndarray`` 或 ``torch.Tensor``
:type spikes: Union[np.ndarray, torch.Tensor]
:param title: 图的标题
:type title: str
:param xlabel: x轴标签
:type xlabel: str
:param ylabel: y轴标签
:type ylabel: str
:param int_x_ticks: x轴是否只显示整数刻度
:type int_x_ticks: bool
:param int_y_ticks: y轴是否只显示整数刻度
:type int_y_ticks: bool
:param plot_firing_rate: 是否画出各脉冲发放频率
:type plot_firing_rate: bool
:param firing_rate_map_title: 脉冲频率发放图的标题
:type firing_rate_map_title: str
:param figsize: 图片尺寸
:type figsize: Tuple[float, float]
:param dpi: 绘图 dpi
:type dpi: int
:return: ``(fig, ax)`` 元组,其中 ``ax`` 是脉冲图的 axes
:rtype: Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]
:raises ValueError: 当 ``spikes`` 不是二维数组时
----
.. _plot_1d_spikes-en:
* **English**
Plot spike data for N neurons over T time steps.
:param spikes: Array of shape=[T, N] with values in {0, 1}, representing spike trains
of N neurons over T steps. Accepts ``np.ndarray`` or ``torch.Tensor``.
:type spikes: Union[np.ndarray, torch.Tensor]
:param title: Title of the plot.
:type title: str
:param xlabel: Label of the x-axis.
:type xlabel: str
:param ylabel: Label of the y-axis.
:type ylabel: str
:param int_x_ticks: Whether to show only integer ticks on the x-axis.
:type int_x_ticks: bool
:param int_y_ticks: Whether to show only integer ticks on the y-axis.
:type int_y_ticks: bool
:param plot_firing_rate: Whether to draw a firing rate bar beside the spike plot.
:type plot_firing_rate: bool
:param firing_rate_map_title: Title of the firing rate subplot.
:type firing_rate_map_title: str
:param figsize: Figure size.
:type figsize: Tuple[float, float]
:param dpi: Dots per inch.
:type dpi: int
:return: ``(fig, ax)`` tuple where ``ax`` is the spike plot axes.
:rtype: Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]
:raises ValueError: If ``spikes`` is not 2-dimensional.
----
* **代码示例 | Example**
.. code-block:: python
import torch
from spikingjelly.activation_based import neuron
from spikingjelly import visualizing
from matplotlib import pyplot as plt
lif = neuron.LIFNode(tau=100.0)
x = torch.rand(size=[32]) * 4
T = 50
s_list = []
for t in range(T):
s_list.append(lif(x).unsqueeze(0))
s_list = torch.cat(s_list)
fig, ax = visualizing.plot_1d_spikes(
spikes=s_list,
title="Spikes",
xlabel="Simulating Step",
ylabel="Neuron Index",
dpi=200,
)
plt.show()
.. image:: ../_static/API/visualizing/plot_1d_spikes.*
:width: 100%
"""
spikes = _to_numpy(spikes)
if spikes.ndim != 2:
raise ValueError(f"Expected 2D array, got {spikes.ndim}D array instead")
spikes_T = spikes.T
if plot_firing_rate:
fig = plt.figure(tight_layout=True, figsize=figsize, dpi=dpi)
gs = matplotlib.gridspec.GridSpec(1, 5)
spikes_map = fig.add_subplot(gs[0, 0:4])
firing_rate_map = fig.add_subplot(gs[0, 4])
else:
fig, spikes_map = plt.subplots(figsize=figsize, dpi=dpi)
spikes_map.set_title(title)
spikes_map.set_xlabel(xlabel)
spikes_map.set_ylabel(ylabel)
spikes_map.xaxis.set_major_locator(
matplotlib.ticker.MaxNLocator(integer=int_x_ticks)
)
spikes_map.yaxis.set_major_locator(
matplotlib.ticker.MaxNLocator(integer=int_y_ticks)
)
spikes_map.xaxis.set_minor_locator(matplotlib.ticker.NullLocator())
spikes_map.yaxis.set_minor_locator(matplotlib.ticker.NullLocator())
spikes_map.set_xlim(-0.5, spikes_T.shape[1] - 0.5)
spikes_map.set_ylim(-0.5, spikes_T.shape[0] - 0.5)
spikes_map.invert_yaxis()
N = spikes_T.shape[0]
T = spikes_T.shape[1]
t = np.arange(0, T)
t_spike = spikes_T * t
mask = spikes_T == 1
colormap = plt.get_cmap("tab10")
for i in range(N):
spikes_map.eventplot(
t_spike[i][mask[i]], lineoffsets=i, colors=colormap(i % 10)
)
if plot_firing_rate:
firing_rate = np.mean(spikes_T, axis=1, keepdims=True)
max_rate = firing_rate.max()
min_rate = firing_rate.min()
firing_rate_map.yaxis.set_major_locator(
matplotlib.ticker.MaxNLocator(integer=True)
)
firing_rate_map.yaxis.set_minor_locator(matplotlib.ticker.NullLocator())
firing_rate_map.imshow(firing_rate, cmap="magma", aspect="auto")
for i in range(firing_rate.shape[0]):
firing_rate_map.text(
0,
i,
f"{firing_rate[i][0]:.2f}",
ha="center",
va="center",
color="w"
if firing_rate[i][0] < 0.7 * max_rate or min_rate == max_rate
else "black",
)
firing_rate_map.get_xaxis().set_visible(False)
firing_rate_map.set_title(firing_rate_map_title)
return fig, spikes_map
[文档]
def plot_one_neuron_v_s(
v: Union[np.ndarray, torch.Tensor],
s: Union[np.ndarray, torch.Tensor],
v_threshold: float = 1.0,
v_reset: Optional[float] = 0.0,
title: str = "$V[t]$ and $S[t]$ of the neuron",
figsize: Tuple[float, float] = (12, 8),
dpi: int = 200,
) -> Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes, matplotlib.axes.Axes]:
r"""
**API Language** - :ref:`中文 <plot_one_neuron_v_s-cn>` | :ref:`English <plot_one_neuron_v_s-en>`
----
.. _plot_one_neuron_v_s-cn:
* **中文**
绘制单个神经元的电压、脉冲随着时间的变化情况。
:param v: shape=[T] 的数组,存放神经元不同时刻的电压。支持 ``np.ndarray`` 或 ``torch.Tensor``
:type v: Union[np.ndarray, torch.Tensor]
:param s: shape=[T] 的数组,存放神经元不同时刻释放的脉冲。支持 ``np.ndarray`` 或 ``torch.Tensor``
:type s: Union[np.ndarray, torch.Tensor]
:param v_threshold: 神经元的阈值电压
:type v_threshold: float
:param v_reset: 神经元的重置电压。可以为 ``None``
:type v_reset: Optional[float]
:param title: 图的标题
:type title: str
:param figsize: 图片尺寸
:type figsize: Tuple[float, float]
:param dpi: 绘图 dpi
:type dpi: int
:return: ``(fig, ax_voltage, ax_spike)`` 三元组
:rtype: Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes, matplotlib.axes.Axes]
:raises ValueError: 当 ``v`` 或 ``s`` 不是一维数组时
----
.. _plot_one_neuron_v_s-en:
* **English**
Plot the membrane voltage and spike train of a single neuron over time.
:param v: Array of shape=[T] storing membrane voltage at each time step.
Accepts ``np.ndarray`` or ``torch.Tensor``.
:type v: Union[np.ndarray, torch.Tensor]
:param s: Array of shape=[T] storing spikes emitted at each time step.
Accepts ``np.ndarray`` or ``torch.Tensor``.
:type s: Union[np.ndarray, torch.Tensor]
:param v_threshold: Threshold voltage of the neuron.
:type v_threshold: float
:param v_reset: Reset voltage of the neuron. Can be ``None``.
:type v_reset: Optional[float]
:param title: Title of the plot.
:type title: str
:param figsize: Figure size.
:type figsize: Tuple[float, float]
:param dpi: Dots per inch.
:type dpi: int
:return: ``(fig, ax_voltage, ax_spike)`` triple.
:rtype: Tuple[matplotlib.figure.Figure, matplotlib.axes.Axes, matplotlib.axes.Axes]
:raises ValueError: If ``v`` or ``s`` is not 1-dimensional.
----
* **代码示例 | Example**
.. code-block:: python
import torch
from spikingjelly.activation_based import neuron
from spikingjelly import visualizing
from matplotlib import pyplot as plt
lif = neuron.LIFNode(tau=100.0)
x = torch.Tensor([2.0])
T = 150
s_list = []
v_list = []
for t in range(T):
s_list.append(lif(x))
v_list.append(lif.v)
fig, ax_v, ax_s = visualizing.plot_one_neuron_v_s(
v_list, s_list, v_threshold=lif.v_threshold, v_reset=lif.v_reset
)
plt.show()
.. image:: ../_static/API/visualizing/plot_one_neuron_v_s.*
:width: 100%
"""
v = _to_numpy(v)
s = _to_numpy(s)
if v.ndim != 1:
raise ValueError(f"Expected 1D array for v, got {v.ndim}D array instead")
if s.ndim != 1:
raise ValueError(f"Expected 1D array for s, got {s.ndim}D array instead")
fig = plt.figure(figsize=figsize, dpi=dpi)
ax0 = plt.subplot2grid((3, 1), (0, 0), rowspan=2)
ax0.set_title(title)
T = s.shape[0]
t = np.arange(0, T)
ax0.plot(t, v)
ax0.set_xlim(-0.5, T - 0.5)
ax0.set_ylabel("voltage")
ax0.axhline(v_threshold, label="$V_{threshold}$", linestyle="-.", c="r")
if v_reset is not None:
ax0.axhline(v_reset, label="$V_{reset}$", linestyle="-.", c="g")
ax0.legend(frameon=True)
t_spike = s * t
mask = s == 1
ax1 = plt.subplot2grid((3, 1), (2, 0))
ax1.eventplot(t_spike[mask], lineoffsets=0, colors="r")
ax1.set_xlim(-0.5, T - 0.5)
ax1.set_xlabel("simulating step")
ax1.set_ylabel("spike")
ax1.set_yticks([])
ax1.xaxis.set_major_locator(matplotlib.ticker.MaxNLocator(integer=True))
return fig, ax0, ax1