Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Lava

Lava is a Python framework for building neural networks that is developed and maintained by Intel. It offers deployment options to Intel’s Loihi chips, particularly the newest Loihi 2 chip.

Lava-dl is an extension of Lava that focuses on deep learning applications and adds more neuron models and also PyTorch-based training algorithms (SLAYER) and model conversions from ANNs.

NIR has been tested with Lava, and Lava-dl. The following code will show how to import a NIR graph into Lava and Lava-dl. It uses the NIR2Lava script from the NIR repository.

Supported Primitives in Lava

This library does not support conversion of any nodes to NIR.

This library supports conversion of the following nodes from NIR:

  • Conv2d

  • Flatten

  • Affine

  • Linear

  • CubaLIF

  • IF

  • LIF

  • SumPool2d

NIR to Lava

import numpy as np
import nir
import matplotlib.pyplot as plt

ng = nir.NIRGraph(
    nodes=[
        nir.Input(input_type=np.array([3])),
        nir.Affine(weight=np.array([[8, 2, 10], [14, 3, 14]]).T, bias=np.array([1, 2])),
        nir.LIF(
            tau=np.array([1] * 2),
            r=np.array([1] * 2),
            v_leak=np.array([0] * 2),
            v_threshold=np.array([1] * 2),
        ),
        nir.Output(input_type=np.array([3])),
    ],
    edges=[(0, 1), (1, 2), (2, 3)],
)

We can use the library_preference argument to specify that we want to convert our NIR graph to Lava (instead of Lava-dl). Additionally, we can also specify a timestep dt that is used to discretize the neuron equations, and we can specify if we want the Lava network to run on_chip or not (if you don’t have access to a Loihi chip, set this to False and use Lava’s bit-accurate simulator instead), and also whether to use fixed precision (integers) or floating precision.

from nir_to_lava import ImportConfig, LavaLibrary, import_from_nir

config = ImportConfig(
    dt=1e-4, fixed_pt=False, on_chip=False, library_preference=LavaLibrary.Lava
)
lava_nodes, start_nodes, end_nodes = import_from_nir(ng, config)

We now configure the input and output to/from the NIR graph:

input_data = None  # here you can specify the input data to test your network
from lava.proc.monitor.process import Monitor
from lava.magma.core.run_conditions import RunSteps
from lava.magma.core.run_configs import Loihi2SimCfg, Loihi2HwCfg
from lava.proc.io.source import RingBuffer

if config.on_chip:
    from lava.proc.io.sink import RingBuffer as Sink
    from lava.proc.embedded_io.spike import PyToNxAdapter, NxToPyAdapter
    from lava.utils.loihi2_state_probes import StateProbe

ring_buffer = RingBuffer(data=input_data)
first_node = lava_nodes['0']  # point this to the node that should receive the input
last_node = lava_nodes['1'][1]  # point this to the node that should output the result

if config.on_chip:
    sink = Sink(shape=(1,), buffer=input_data.shape[1])
    py2nx = PyToNxAdapter(shape=(1,))
    nx2py = NxToPyAdapter(shape=(1,))

    ring_buffer.s_out.connect(py2nx.inp)
    py2nx.out.connect(first_node.s_in)
    first_node.a_out.connect(last_node.a_in)
    last_node.s_out.connect(nx2py.inp)
    nx2py.out.connect(sink.a_in)
else:
    ring_buffer.s_out.connect(first_node.s_in)

Now we can run the network in Lava (here we assume that the last node is a LIF):

n_steps = 1000

if config.on_chip:
    probe_v = StateProbe(last_node.v)
    probe_u = StateProbe(last_node.u)
    callbacks = [probe_v, probe_u]
    run_cfg = Loihi2HwCfg(callback_fxs=callbacks)
else:
    mon_volt = Monitor()
    mon_curr = Monitor()
    mon_spk = Monitor()
    mon_inp = Monitor()
    mon_volt.probe(last_node.v, n_steps)
    mon_curr.probe(last_node.u, n_steps)
    mon_spk.probe(last_node.s_out, n_steps)
    mon_inp.probe(ring_buffer.s_out, n_steps)
    tag = "fixed_pt" if config.fixed_pt else "floating_pt"
    run_cfg = Loihi2SimCfg(select_tag=tag)

last_node.run(condition=RunSteps(num_steps=n_steps), run_cfg=run_cfg)
if config.on_chip:
    sink_data = sink.data.get()
    last_node.stop()

And finally we plot the results:

inp_spikes = input_data.reshape(-1)

if config.on_chip:
    spikes = sink_data.reshape(-1)
    voltage = probe_v.time_series.reshape(-1)
    print(f'spikes: {spikes.shape}, voltage: {voltage.shape}')
else:
    spikes = mon_spk.get_data()['lif']['s_out'].reshape(-1)
    voltage = mon_volt.get_data()['lif']['v'].reshape(-1)
    print(f'spikes: {spikes.shape}, voltage: {voltage.shape}')

sfx = 'fixed' if config.fixed_pt else 'float'
device = 'loihi' if config.on_chip else 'cpu'
with open(f'lif_lava_{device}_{sfx}.csv', 'w') as fw:
    for idx in range(inp_spikes.shape[0]):
        fw.write(f'{inp_spikes[idx]},{voltage[idx]},{spikes[idx]}\n')

fig, axs = plt.subplots(3, 1, figsize=(10, 7), sharex=True)
fig.suptitle(f'LIF ({sfx} pt) on {device}')
# fig.suptitle(f'LIF ({sfx} pt) on {device} with du={du}, dv={dv}, vth={vthr}')
mon_volt.plot(axs[0], last_node.v)
mon_curr.plot(axs[1], last_node.u)
mon_inp.plot(axs[2], ring_buffer.s_out, label='ring buffer output')
axs[2].eventplot(np.argwhere(input_data.reshape(-1) > 0).reshape(-1), color='r', label='data')
axs[2].legend()
# axs[2].set_xticks(np.arange(0, n_steps, 1), minor=True)
axs[0].set_title('LIF voltage')
axs[1].set_title('LIF current')
axs[2].set_title('Input spikes')
plt.tight_layout()
plt.savefig('output.png')
plt.show()
from IPython.display import Image

Image(filename='output.png')
<IPython.core.display.Image object>

NIR to Lava-dl

The conversion from NIR to Lava-dl is comparatively simpler, as the resulting object is a PyTorch module that you can run. Here’s the code snippet to convert the same NIR graph to a PyTorch network using Lava-dl components. Please see the lava-dl docs for more information about lava-dl and how to deply your lava-dl network to a Loihi chip.

import_config = ImportConfig(library_preference=LavaLibrary.LavaDl)
net = import_from_nir(ng, import_config)