SNN Layers (sanafe.layers)

The sanafe.layers module provides higher-level machine learning abstractions that simplify the construction of deep spiking neural network applications in SANA-FE. These layers wrap the low-level C++ kernel with PyTorch/Keras-style interfaces.

Quick Start

import sanafe
from sanafe import layers
import numpy as np

# Create network
net = sanafecpp.Network()

# Build a simple CNN
input_layer = layers.Input2D(net, 28, 28, 1)

# Add convolutional layer
conv_weights = np.random.random((3, 3, 1, 32))
conv1 = layers.Conv2D(net, input_layer, conv_weights, threshold=1.0)

# Add dense layer
dense_weights = np.random.random((conv1.width * conv1.height * conv1.channels, 10))
output = layers.Dense(net, conv1, 10, dense_weights)

For a more in-depth real-world example, see our tutorial implementing DVS gesture categorization

Layer Base Class

class sanafe.layers.Layer[source]

Bases: object

Base class for all neural network layers.

Provides common functionality for layer objects including indexing, iteration, and length operations that delegate to the underlying neuron group.

group

Underlying SANA-FE neuron group

Type:

NeuronGroup

Example

>>> # Access individual neurons
>>> neuron = layer[0]
>>>
>>> # Iterate over neurons
>>> for neuron in layer:
...     neuron.set_attributes(log_spikes=True)
>>>
>>> # Get layer size
>>> print(f"Layer has {len(layer)} neurons")
__init__()[source]

Initialize base layer with empty neuron group.

__getitem__(key)[source]

Access neuron(s) by index or slice.

Parameters:

key (int or slice) – Index or slice for neuron access

Returns:

Single neuron or list of neurons

Return type:

Neuron or list

__len__()[source]

Get number of neurons in this layer.

Returns:

Number of neurons in the layer

Return type:

int

__iter__()[source]

Iterate over all neurons in the layer.

Yields:

Neuron – Individual neurons in the layer

Input Layers

class sanafe.layers.Input2D(snn, width, height, channels=1, **kwargs)[source]

Bases: Layer

2D input layer for image-like data with configurable dimensions.

Creates a flattened neuron group representing a 2D input space with optional channel dimension. Neurons are arranged in row-major order (channels-last format).

width

Input width in pixels

Type:

int

height

Input height in pixels

Type:

int

channels

Number of input channels

Type:

int

group

Underlying neuron group

Type:

NeuronGroup

Example

>>> # Create 28x28 grayscale input layer
>>> input_layer = Input2D(net, 28, 28, 1, threshold=1.0)
>>>
>>> # Create 32x32 RGB input layer
>>> rgb_input = Input2D(net, 32, 32, 3, leak=0.01)
>>>
>>> # Access specific neuron at position (y=10, x=15, channel=0)
>>> neuron_idx = 10 * input_layer.width + 15
>>> neuron = input_layer[neuron_idx]
__init__(snn, width, height, channels=1, **kwargs)[source]

Create 2D input layer with specified dimensions.

Parameters:
  • snn (Network) – SANA-FE network to add this layer to

  • width (int) – Input width in pixels

  • height (int) – Input height in pixels

  • channels (int, optional) – Number of input channels. Defaults to 1.

  • **kwargs – Additional neuron model parameters (threshold, leak, etc.)

Raises:

ValueError – If width, height, or channels are non-positive

Convolutional Layers

class sanafe.layers.Conv2D(snn, prev_layer, weights, stride_width=1, stride_height=1, pad_width=0, pad_height=0, **kwargs)[source]

Bases: Layer

2D convolutional layer implementing CNN-style feature extraction.

Applies learnable filters across the input to produce feature maps. Automatically calculates output dimensions based on input size, kernel size, stride, and padding parameters.

width

Output feature map width

Type:

int

height

Output feature map height

Type:

int

channels

Number of output channels (filters)

Type:

int

group

Underlying neuron group

Type:

NeuronGroup

Example

>>> # Create 3x3 convolution with 32 filters (3*3*1*32 = 288 weights)
>>> conv_weights = [0.1 if i % 2 == 0 else -0.1 for i in range(288)]
>>> conv_layer = Conv2D(net, input_layer, conv_weights,
...                     stride_width=1, stride_height=1)
>>>
>>> # Create 5x5 convolution with stride 2 (5*5*32*64 = 51200 weights)
>>> conv_weights = [0.05] * 51200
>>> conv2 = Conv2D(net, conv_layer, conv_weights,
...                 stride_width=2, stride_height=2,
...                 threshold=1.2, leak=0.05)
__init__(snn, prev_layer, weights, stride_width=1, stride_height=1, pad_width=0, pad_height=0, **kwargs)[source]

Create 2D convolutional layer.

Parameters:
  • snn (Network) – SANA-FE network to add this layer to

  • prev_layer (Layer) – Previous layer to connect from

  • weights (np.ndarray) – 4D weight tensor with shape (W, H, C_in, C_out) where W=kernel width, H=kernel height, C_in=input channels, C_out=output channels

  • stride_width (int, optional) – Horizontal stride. Defaults to 1.

  • stride_height (int, optional) – Vertical stride. Defaults to 1.

  • pad_width (int, optional) – Horizontal padding. Defaults to 0.

  • pad_height (int, optional) – Vertical padding. Defaults to 0.

  • **kwargs – Additional neuron model parameters

Raises:
  • ValueError – If weights don’t have expected 4D shape

  • ValueError – If stride or padding values are invalid

Dense Layers

class sanafe.layers.Dense(snn, prev_layer, neuron_count, weights, **kwargs)[source]

Bases: Layer

Fully-connected (dense) layer with all-to-all connectivity.

Connects every neuron in the previous layer to every neuron in this layer, implementing a standard neural network dense layer.

group

Underlying neuron group

Type:

NeuronGroup

Example

>>> # Create dense layer with 128 neurons
>>> prev_size = len(prev_layer)
>>> weights = [0.1 if i % 3 == 0 else 0.05 for i in range(prev_size * 128)]
>>> dense_layer = Dense(net, prev_layer, 128, weights,
...                     threshold=1.0, leak=0.1)
>>>
>>> # Create output layer with 10 classes
>>> output_weights = [0.08] * (128 * 10)
>>> output_layer = Dense(net, dense_layer, 10, output_weights)
__init__(snn, prev_layer, neuron_count, weights, **kwargs)[source]

Create fully-connected layer.

Parameters:
  • snn (Network) – SANA-FE network to add this layer to

  • prev_layer (Layer) – Previous layer to connect from

  • neuron_count (int) – Number of neurons in this layer

  • weights (np.ndarray) – 2D weight matrix with shape (prev_layer_size, neuron_count)

  • **kwargs – Additional neuron model parameters

Raises:
  • ValueError – If weight matrix dimensions don’t match layer sizes

  • ValueError – If neuron_count is non-positive

Application Examples

MNIST CNN

import sanafecpp
from sanafe import layers
import numpy as np

def create_mnist_cnn(net):
    """Create a simple CNN for MNIST classification."""

    # Input layer: 28x28 grayscale images
    input_layer = layers.Input2D(net, 28, 28, 1, threshold=1.0)

    # First conv layer: 3x3 kernels, 32 filters
    conv1_weights = np.random.normal(0, 0.1, (3, 3, 1, 32))
    conv1 = layers.Conv2D(net, input_layer, conv1_weights,
                         stride_width=1, stride_height=1,
                         threshold=1.2, leak=0.05)

    # Second conv layer: 3x3 kernels, 64 filters
    conv2_weights = np.random.normal(0, 0.1, (3, 3, 32, 64))
    conv2 = layers.Conv2D(net, conv1, conv2_weights,
                         stride_width=2, stride_height=2,
                         threshold=1.1, leak=0.05)

    # Flatten and classify
    flatten_size = conv2.width * conv2.height * conv2.channels
    dense_weights = np.random.normal(0, 0.1, (flatten_size, 10))
    output = layers.Dense(net, conv2, 10, dense_weights, threshold=2.0)

    return input_layer, conv1, conv2, output

Multi-Layer Perceptron

def create_mlp(net, input_size, hidden_sizes, output_size):
    """Create a multi-layer perceptron."""

    # Input layer
    input_layer = layers.Input2D(net, input_size, 1, 1, threshold=0.5)

    prev_layer = input_layer
    layers_list = [input_layer]

    # Hidden layers
    for i, hidden_size in enumerate(hidden_sizes):
        weights = np.random.normal(0, 0.1, (len(prev_layer), hidden_size))
        hidden = layers.Dense(net, prev_layer, hidden_size, weights,
                             threshold=1.0, leak=0.1)
        layers_list.append(hidden)
        prev_layer = hidden

    # Output layer
    output_weights = np.random.normal(0, 0.1, (len(prev_layer), output_size))
    output = layers.Dense(net, prev_layer, output_size, output_weights,
                         threshold=1.5)
    layers_list.append(output)

    return layers_list

Error Handling

The layers module includes error checking:

  • Dimension validation: Ensures weight matrices match layer sizes

  • Parameter validation: Checks for positive dimensions and valid strides

  • Channel compatibility: Verifies input/output channel consistency

  • Output size validation: Prevents zero or negative output dimensions

Common error scenarios:

# This will raise ValueError: weight matrix size mismatch
try:
    wrong_weights = np.random.random((100, 50))  # Wrong input size
    layer = layers.Dense(net, prev_layer, 50, wrong_weights)
except ValueError as e:
    print(f"Error: {e}")

# This will raise ValueError: invalid stride
try:
    layer = layers.Conv2D(net, input_layer, weights, stride_width=0)
except ValueError as e:
    print(f"Error: {e}")

See Also