Source code for devices.dynapse.hardware.config.from_spec

"""
Dynap-SE2 samna configuration getter
Process a quantized specification dictionary and obtain a deployable object

See also `devices.dynapse.mapper`
See also `devices.dynapse.autoencoder_quantization`
"""
from __future__ import annotations
import logging
import numpy as np

from typing import Any, Dict, List, Optional, Tuple
from rockpool.devices.dynapse.samna_alias import Dynapse2Configuration

from rockpool.typehints import FloatVector, IntVector
from rockpool.devices.dynapse.parameters import DynapSimCore

from rockpool.devices.dynapse.lookup import NUM_CORES, NUM_NEURONS, CHIP_MAP, CHIP_POS

# Try to import samna for device interfacing
SAMNA_AVAILABLE = True
try:
    import samna
except:
    samna = Any
    logging.warning(
        "Device interface requires `samna` package which is not installed on the system"
    )
    SAMNA_AVAILABLE = False

from .allocator import WeightAllocator

# - Configure exports
__all__ = ["config_from_specification"]


[docs]def config_from_specification( n_cluster: int, core_map: List[int], weights_in: Optional[List[Optional[IntVector]]], weights_rec: Optional[List[Optional[IntVector]]], # params Idc: List[FloatVector], If_nmda: List[FloatVector], Igain_ahp: List[FloatVector], Igain_mem: List[FloatVector], Igain_syn: List[FloatVector], Ipulse_ahp: List[FloatVector], Ipulse: List[FloatVector], Iref: List[FloatVector], Ispkthr: List[FloatVector], Itau_ahp: List[FloatVector], Itau_mem: List[FloatVector], Itau_syn: List[FloatVector], Iw_ahp: List[FloatVector], # Optionals sign_in: Optional[List[Optional[IntVector]]] = None, sign_rec: Optional[List[Optional[IntVector]]] = None, Iw_0: Optional[List[FloatVector]] = None, Iw_1: Optional[List[FloatVector]] = None, Iw_2: Optional[List[FloatVector]] = None, Iw_3: Optional[List[FloatVector]] = None, # definitions chip_map: Dict[int, int] = CHIP_MAP, chip_pos: Dict[int, Tuple[int]] = CHIP_POS, num_cores: int = NUM_CORES, num_neurons: int = NUM_NEURONS, *args, **kwargs, ) -> Dynapse2Configuration: """ config_from_specification gets a specification and creates a samna configuration object for Dynap-SE2 chip. All the parameteres and weight matrices are provided as lists, indices indicating the exact cluster(core id). :param n_cluster: total number of clusters, neural cores allocated :type n_cluster: int :param core_map: core map (neuron_id : core_id) for in-device neurons, defaults to CORE_MAP :type core_map: List[int] :param weights_in: a list of quantized input weight matrices :type weights_in: Optional[List[Optional[IntVector]]] :param weights_rec: a list of quantized recurrent weight matrices :type weights_rec: Optional[List[Optional[IntVector]]] :param Idc: a list of Constant DC current injected to membrane in Amperes :type Idc: List[FloatVector] :param If_nmda: a list of NMDA gate soft cut-off current setting the NMDA gating voltage in Amperes :type If_nmda: List[FloatVector] :param Igain_ahp: a list of gain bias current of the spike frequency adaptation block in Amperes :type Igain_ahp: List[FloatVector] :param Igain_mem: a list of gain bias current for neuron membrane in Amperes :type Igain_mem: List[FloatVector] :param Igain_syn: a list of gain bias current of synaptic gates (AMPA, GABA, NMDA, SHUNT) combined in Amperes :type Igain_syn: List[FloatVector] :param Ipulse_ahp: a list of bias current setting the pulse width for spike frequency adaptation block ``t_pulse_ahp`` in Amperes :type Ipulse_ahp: List[FloatVector] :param Ipulse: a list of bias current setting the pulse width for neuron membrane ``t_pulse`` in Amperes :type Ipulse: List[FloatVector] :param Iref: a list of bias current setting the refractory period ``t_ref`` in Amperes :type Iref: List[FloatVector] :param Ispkthr: a list of spiking threshold current, neuron spikes if :math:`I_{mem} > I_{spkthr}` in Amperes :type Ispkthr: List[FloatVector] :param Itau_ahp: a list of Spike frequency adaptation leakage current setting the time constant ``tau_ahp`` in Amperes :type Itau_ahp: List[FloatVector] :param Itau_mem: a list of Neuron membrane leakage current setting the time constant ``tau_mem`` in Amperes :type Itau_mem: List[FloatVector] :param Itau_syn: a list of (AMPA, GABA, NMDA, SHUNT) synapses combined leakage current setting the time constant ``tau_syn`` in Amperes :type Itau_syn: List[FloatVector] :param Iw_ahp: a list of spike frequency adaptation weight current of the neurons of the core in Amperes :type Iw_ahp: List[FloatVector] :param sign_in: a list of input weight directions (+1 : excitatory, -1 : inhibitory) matrices, defaults to None :type sign_in: Optional[List[Optional[IntVector]]] :param sign_rec: a list of recurrent weight directions (+1 : excitatory, -1 : inhibitory) matrices, defaults to None :type sign_rec: Optional[List[Optional[IntVector]]] :param Iw_0: a list of weight bit 0 current of the neurons of the core in Amperes, defaults to None :type Iw_0: Optional[List[FloatVector]] :param Iw_1: a list of weight bit 1 current of the neurons of the core in Amperes, defaults to None :type Iw_1: Optional[List[FloatVector]] :param Iw_2: a list of weight bit 2 current of the neurons of the core in Amperes, defaults to None :type Iw_2: Optional[List[FloatVector]] :param Iw_3: a list of weight bit 3 current of the neurons of the core in Amperes, defaults to None :type Iw_3: Optional[List[FloatVector]] :param chip_map: chip map (core_id : chip_id) for all cores, defaults to CHIP_MAP :type chip_map: Dict[int, int], optional :param chip_pos: global chip position dictionary (chip_id : (xpos,ypos)), defaults to CHIP_POS :type chip_pos: Dict[int, Tuple[int]], optional :param num_cores: the number of cores per chip, defaults to NUM_CORES :type num_cores: int, optional :param num_neurons: the number of neurons per core, defaults to NUM_NEURONS :type num_neurons: int, optional :return: config, input_channel_map :config: a modified samna ``Dynapse2Configuration`` object :input_channel_map: the mapping between input timeseries channels and the destinations :rtype: Tuple[Dynapse2Configuration, Dict[int, Dynapse2Destination]] """ new_config = samna.dynapse2.Dynapse2Configuration() core_map = np.array(core_map) if len(core_map.shape) != 1: raise ValueError("Core_map should be one dimensional!") ## -- Get cores one by one -- ## for c in range(n_cluster): # Get the right chip and the indicated core config ch = chip_map[c] core_config = new_config.chips[ch].cores[c % num_cores] # Convert the core parameters core = DynapSimCore( Idc=Idc[c], If_nmda=If_nmda[c], Igain_ahp=Igain_ahp[c], Igain_ampa=Igain_syn[c], Igain_gaba=Igain_syn[c], Igain_nmda=Igain_syn[c], Igain_shunt=Igain_syn[c], Igain_mem=Igain_mem[c], Ipulse_ahp=Ipulse_ahp[c], Ipulse=Ipulse[c], Iref=Iref[c], Ispkthr=Ispkthr[c], Itau_ahp=Itau_ahp[c], Itau_ampa=Itau_syn[c], Itau_gaba=Itau_syn[c], Itau_nmda=Itau_syn[c], Itau_shunt=Itau_syn[c], Itau_mem=Itau_mem[c], Iw_ahp=Iw_ahp[c], Iw_0=Iw_0[c] if Iw_0 is not None else 0.0, Iw_1=Iw_1[c] if Iw_1 is not None else 0.0, Iw_2=Iw_2[c] if Iw_2 is not None else 0.0, Iw_3=Iw_3[c] if Iw_3 is not None else 0.0, ) # Allocate memory for the weights allocator = WeightAllocator( weights_in=weights_in[c] if weights_in is not None else None, weights_rec=weights_rec[c] if weights_rec is not None else None, sign_in=sign_in[c] if sign_in is not None else None, sign_rec=sign_rec[c] if sign_rec is not None else None, core_map=core_map, chip_map=chip_map, chip_pos=chip_pos, ) # SRAM blocks are responsible for storing the destinations of the neurons sram = allocator.SRAM_content( use_samna=True, monitor_neurons=list(range(0, num_neurons)), ) # CAM blocks are responsible for storing the incoming connections of the neurons cam = allocator.CAM_content(use_samna=True) # Neural parameters are shared across all neurons inside the core params = core.export_Dynapse2Parameters() # Update the configuration object ## DC excitation if Idc[c] > 0: nidx = np.where(core_map == c)[0] for n in nidx: core_config.neurons[n].latch_so_dc = True ## Receiving connections for n, cam_content in cam.items(): core_config.neurons[n].synapses = cam_content ## Broadcasting connections for n, sram_content in sram.items(): core_config.neurons[n].destinations = sram_content ## Parameters for key, (coarse, fine) in params.items(): core_config.parameters[key].coarse_value = coarse core_config.parameters[key].fine_value = fine ## Returns input_channel_map = allocator.input_channel_map() return {"config": new_config, "input_channel_map": input_channel_map}