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:
objectBase 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:
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")
- __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
Input Layers
- class sanafe.layers.Input2D(snn, width, height, channels=1, **kwargs)[source]
Bases:
Layer2D 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:
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:
Layer2D 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:
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:
LayerFully-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:
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)
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
Core API Reference (sanafe) - API reference