Skip to main content

Tutorial: talonviz — Visualization

talonviz provides interactive visualization tools for TALON IR graphs and neuromorphic event data.

What You'll Learn​

  1. Rendering interactive graph visualizations (HTML)
  2. Processing neuromorphic events into frames, rasters, and grids
  3. Exporting animated event visualizations
  4. Detecting architectural patterns (skip connections, SPP)

Prerequisites​

pip install t1c-talon

1. Setup​

import os
import numpy as np
from talon import ir, viz

os.makedirs("viz", exist_ok=True)

print(f"talonviz version: {viz.__version__}")
print(f"Tonic available: {viz.TONIC_AVAILABLE}")
print(f"PIL available: {viz.PIL_AVAILABLE}")

Output:

talonviz version: 0.0.1
Tonic available: False
PIL available: True

2. Graph Visualization​

talonviz renders TALON IR graphs as interactive HTML with zoomable/pannable views, node details on hover, and architecture pattern highlighting.

Create a Graph​

nodes = {
"input": ir.Input(np.array([784])),
"fc1": ir.Affine(
weight=np.random.randn(256, 784).astype(np.float32) * 0.01,
bias=np.zeros(256, dtype=np.float32),
),
"lif1": ir.LIF(
tau=np.ones(256, dtype=np.float32) * 10.0,
r=np.ones(256, dtype=np.float32),
v_leak=np.zeros(256, dtype=np.float32),
v_threshold=np.ones(256, dtype=np.float32),
),
"fc2": ir.Affine(
weight=np.random.randn(10, 256).astype(np.float32) * 0.01,
bias=np.zeros(10, dtype=np.float32),
),
"lif2": ir.LIF(
tau=np.ones(10, dtype=np.float32) * 10.0,
r=np.ones(10, dtype=np.float32),
v_leak=np.zeros(10, dtype=np.float32),
v_threshold=np.ones(10, dtype=np.float32),
),
"output": ir.Output(np.array([10])),
}

edges = [
("input", "fc1"),
("fc1", "lif1"),
("lif1", "fc2"),
("fc2", "lif2"),
("lif2", "output"),
]

graph = ir.Graph(nodes=nodes, edges=edges)
print(f"Created graph: {len(graph.nodes)} nodes, {len(graph.edges)} edges")

Output:

Created graph: 6 nodes, 5 edges

Export to Interactive HTML​

viz.export_html(graph, "viz/snn_visualization.html", title="Simple SNN")
print("Exported to viz/snn_visualization.html")

Output:

Exported to viz/snn_visualization.html
Localhost Serving

Use viz.serve(graph) to launch an interactive visualization on a local HTTP server, similar to Plotly's approach.

Serialize Graph Data​

data = viz.graph_to_dict(graph)

print(f"Serialized data keys: {list(data.keys())}")
print(f"\nSummary:")
print(f" Node count: {data['summary']['node_count']}")
print(f" Edge count: {data['summary']['edge_count']}")
print(f" Total params: {data['summary']['total_params']:,}")

Output:

Serialized data keys: ['version', 'nodes', 'edges', 'metadata', 'summary', 'groups', 'node_to_group']

Summary:
Node count: 6
Edge count: 5
Total params: 204,594

3. Event/Spike Visualization​

talonviz processes neuromorphic event data into three representations:

RepresentationShapeDescription
Frames(n_frames, 2, H, W)Accumulated event counts per time bin (ON/OFF channels)
Rasters(time_bins, n_neurons)Binary spike matrix per neuron per time bin
Grids(n_bins, H, W, 3)RGB spatial grids for larger sensors

Generate Synthetic Events​

n_events = 5000
sensor_size = (28, 28)

events = np.zeros(n_events, dtype=[
('x', np.int32),
('y', np.int32),
('t', np.float64),
('p', np.int32),
])

events['x'] = np.random.randint(0, sensor_size[0], n_events)
events['y'] = np.random.randint(0, sensor_size[1], n_events)
events['t'] = np.sort(np.random.uniform(0, 100000, n_events))
events['p'] = np.random.choice([-1, 1], n_events)

print(f"Generated {n_events} events")
print(f"Time range: {events['t'].min():.0f} - {events['t'].max():.0f} μs")
print(f"Sensor size: {sensor_size}")

Output:

Generated 5000 events
Time range: 44 - 99985 μs
Sensor size: (28, 28)

Convert to Frames​

frames = viz.events_to_frames(
events,
sensor_size=sensor_size,
n_frames=25,
)

print(f"Frame shape: {frames.shape}")
print(f" (n_frames, channels, height, width)")
print(f" Channels: 2 (ON/OFF polarity)")

Output:

Frame shape: (25, 2, 28, 28)
(n_frames, channels, height, width)
Channels: 2 (ON/OFF polarity)

Convert to Raster​

n_neurons = sensor_size[0] * sensor_size[1]

raster = viz.events_to_raster(
events,
n_neurons=n_neurons,
time_bins=100,
)

print(f"Raster shape: {raster.shape}")
print(f" (time_bins, n_neurons)")
print(f" n_neurons = {sensor_size[0]} * {sensor_size[1]} = {n_neurons}")

Output:

Raster shape: (100, 784)
(time_bins, n_neurons)
n_neurons = 28 * 28 = 784

Convert to Grid​

grid = viz.events_to_grid(
events,
sensor_size=sensor_size,
n_bins=5,
)

print(f"Grid shape: {grid.shape}")
print(f" (n_bins, height, width, channels)")

Output:

Grid shape: (5, 28, 28, 3)
(n_bins, height, width, channels)

Export Event Visualization​

viz.export_events_html(
events,
"viz/tutorial_events.html",
sensor_size=sensor_size,
title="Tutorial: Synthetic Events",
n_frames=30,
)

print("Exported to viz/tutorial_events.html")
print("Features:")
print(" - Animated frame playback")
print(" - Spike raster plot")
print(" - Event statistics")

Output:

Exported to viz/tutorial_events.html
Features:
- Animated frame playback
- Spike raster plot
- Event statistics

4. Pattern Detection​

talonviz can detect common architectural patterns in graphs:

PatternDescription
SkipConnectionResNet-style residual blocks
SPPSpatial Pyramid Pooling
SPPFFast SPP (YOLOv5+)
RepConvReparameterizable convolutions

Create a Graph with Skip Connections​

skip_nodes = {
"input": ir.Input(np.array([64, 14, 14])),
"conv1": ir.Conv2d(
weight=np.random.randn(64, 64, 3, 3).astype(np.float32) * 0.01,
bias=np.zeros(64, dtype=np.float32),
stride=(1, 1), padding=(1, 1),
),
"lif1": ir.LIF(
tau=np.ones(64, dtype=np.float32) * 10.0,
r=np.ones(64, dtype=np.float32),
v_leak=np.zeros(64, dtype=np.float32),
v_threshold=np.ones(64, dtype=np.float32),
),
"conv2": ir.Conv2d(
weight=np.random.randn(64, 64, 3, 3).astype(np.float32) * 0.01,
bias=np.zeros(64, dtype=np.float32),
stride=(1, 1), padding=(1, 1),
),
"skip": ir.Skip(
input_type={"input": np.array([64, 14, 14])},
skip_type="residual",
),
"lif2": ir.LIF(
tau=np.ones(64, dtype=np.float32) * 10.0,
r=np.ones(64, dtype=np.float32),
v_leak=np.zeros(64, dtype=np.float32),
v_threshold=np.ones(64, dtype=np.float32),
),
"output": ir.Output(np.array([64, 14, 14])),
}

skip_edges = [
("input", "conv1"),
("conv1", "lif1"),
("lif1", "conv2"),
("conv2", "skip"),
("input", "skip"),
("skip", "lif2"),
("lif2", "output"),
]

skip_graph = ir.Graph(nodes=skip_nodes, edges=skip_edges)
print(f"Created ResNet-style graph: {len(skip_graph.nodes)} nodes")

Output:

Created ResNet-style graph: 7 nodes

Detect Patterns​

patterns = viz.detect_all_patterns(skip_graph)

print(f"Patterns detected: {len(patterns)}")
for pattern in patterns:
print(f" - {pattern.pattern_type}: {pattern.group_id}")
print(f" Nodes: {pattern.nodes}")

Output:

Patterns detected: 1
- SkipConnection: skip_input_skip
Nodes: ['skip', 'conv1', 'lif1', 'conv2']

Export with Pattern Highlighting​

viz.export_html(
skip_graph,
"viz/resnet_block.html",
title="ResNet Block with Skip Connection",
)
print("Exported to viz/resnet_block.html")
print("The skip connection pattern will be highlighted in the visualization.")

Output:

Exported to viz/resnet_block.html
The skip connection pattern will be highlighted in the visualization.

5. Performance Considerations​

ImplementationSpeedRequirements
NumPy (default)~23M events/secNone
Tonic/Numba~30M events/secpip install tonic
print(f"Tonic available: {viz.TONIC_AVAILABLE}")

if viz.TONIC_AVAILABLE:
print("Using Tonic/Numba implementation (faster)")
else:
print("Using NumPy implementation")
print("Install tonic for ~30% speedup: pip install tonic")

Output:

Tonic available: False
Using NumPy implementation
Install tonic for ~30% speedup: pip install tonic

Summary​

FunctionPurpose
viz.export_html()Interactive graph visualization
viz.graph_to_dict()Serialize graph for visualization
viz.events_to_frames()Convert events to frame arrays
viz.events_to_raster()Convert events to spike rasters
viz.events_to_grid()Convert events to spatial grids
viz.export_events_html()Interactive spike visualization
viz.detect_all_patterns()Find architectural patterns
viz.serve()Localhost HTTP visualization server

What's Next​