Measuring Energy Consumption
get_chip_consumption(model, spks, dt) estimates the average power (in watts) that
the network would draw on the Neuronova chip.
What drives consumption:
- Spike count — more spikes mean more synaptic operations and higher power
- Number of active connections — a synapse is considered active if |weight| > 0.1;
only active connections contribute to power (weights below the threshold are treated
as pruned). The cost per active synapse is a fixed hardware constant — weight
magnitude beyond the threshold does not scale consumption.
- Layer type — H1v1 and H1v2 have different hardware cost constants
spks must be a list of spike tensors (batch, timesteps, neurons), one per layer
that contributes to power (i.e., layers with incoming synaptic weights).
import torch
import torch.nn as nn
from nwavesdk.layers import H1v2Synapse, H1v2Layer, prepare_net
from nwavesdk.metrics import get_chip_consumption
N_IN, N_HIDDEN, N_OUT, DT = 16, 20, 8, 1e-3
class TwoLayerNet(nn.Module):
def __init__(self):
super().__init__()
self.syn1 = H1v2Synapse(N_IN, N_HIDDEN, device="cpu")
self.lyr1 = H1v2Layer(N_HIDDEN, taus=10e-3, dt=DT, layer_topology="FF", device="cpu")
self.syn2 = H1v2Synapse(N_HIDDEN, N_OUT, device="cpu")
self.lyr2 = H1v2Layer(N_OUT, taus=10e-3, dt=DT, layer_topology="FF", device="cpu")
def forward(self, x):
# x: (batch, timesteps, n_in)
prepare_net(self)
spk1_list, spk2_list = [], []
for t in range(x.shape[1]):
cur1 = self.syn1(x[:, t, :])
spk1, _ = self.lyr1(cur1)
cur2 = self.syn2(spk1)
spk2, _ = self.lyr2(cur2)
spk1_list.append(spk1)
spk2_list.append(spk2)
# Return one tensor per layer: (batch, timesteps, neurons)
return torch.stack(spk1_list, dim=1), torch.stack(spk2_list, dim=1)
model = TwoLayerNet()
model.eval()
TwoLayerNet(
(syn1): H1v2Synapse()
(lyr1): H1v2Layer(
(spike_grad): FastSigmoid(slope=25.0)
)
(syn2): H1v2Synapse()
(lyr2): H1v2Layer(
(spike_grad): FastSigmoid(slope=25.0)
)
)
# Collect spike tensors over a batch of synthetic inputs.
BATCH, T = 32, 50
x = (torch.rand(BATCH, T, N_IN) > 0.8).float()
with torch.no_grad():
spk_hidden, spk_out = model(x)
# Pass the spike tensors for every layer that has incoming synaptic weights.
spks = [spk_hidden, spk_out]
power_W = get_chip_consumption(model, spks, dt=DT)
energy_per_inference_nJ = power_W * T * DT * 1e9 # convert J → nJ
print(f"Average power: {power_W * 1e6:.3f} µW")
print(f"Energy per inference: {energy_per_inference_nJ:.3f} nJ")
print(f"\nMean hidden firing rate: {spk_hidden.mean().item():.3f} spk/ts/neuron")
print(f"Mean output firing rate: {spk_out.mean().item():.3f} spk/ts/neuron")
Average power: 0.011 µW
Energy per inference: 0.549 nJ
Mean hidden firing rate: 0.077 spk/ts/neuron
Mean output firing rate: 0.064 spk/ts/neuron
Reducing consumption
Two levers control power:
1. Reduce spike count — use firing_rate_target_mse_loss to penalise high activity:
from nwavesdk.loss import firing_rate_target_mse_loss
activity_loss = firing_rate_target_mse_loss(
spikes_list=[spk_hidden, spk_out],
offsets=[0.05, 0.05], # target 5% firing rate per layer
multipliers=[1.0, 1.0],
)
2. Prune connections — synaptic weights with |w| ≤ 0.1 are treated as inactive
by get_chip_consumption and contribute nothing to power. Training networks to have
fewer active connections (more weights near zero) directly reduces consumption.
Further reading
- Reference → Metrics: full documentation of
get_chip_consumptionand other network metrics →../metrics/metrics.md - Basics → Checking Deployment Readiness: consumption is one of the checks before chip upload →
./deployment-readiness.md - Tutorial 6 — end-to-end H1v2 audio classification including consumption analysis →
../tutorials/Tutorial6_audio_classification_on_H1v2.md