Source code for axon_sdk.networks.functional.divider

from axon_sdk.networks import SubtractorNetwork, ExponentialNetwork
from axon_sdk.primitives import DataEncoder, SpikingNetworkModule

from typing import Optional


[docs] class LogAndSyncNetwork(SpikingNetworkModule): """ > IMPORTANT: Do not use this network outside of this file since it's tailor made for the div module. Inspired by the log network and multiplier network (discussed in the STICK paper), it computes the log of both inputs and outputs them synced in time. For each of its inputs (x1 and x2, in [0,1]), the output is a pair of spikes with a time interval of Tmin + tf * (-ln(xi)) (Note ln(xi) < 0) """ def __init__(self, encoder: DataEncoder, module_name: Optional[str] = None): super().__init__(module_name) self.encoder = encoder # Parameters Vt = 10.0 tm = 100.0 tf = 20.0 Tsyn = 1.0 Tneu = 0.01 Tmin = encoder.Tmin we = Vt wi = -Vt wacc = Vt * tm / encoder.Tmax wacc_bar = Vt * tm / encoder.Tcod gmult = Vt * tm / tf # First input self.input1 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="input1") self.first1 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="first1") self.last1 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="last1") self.acc1 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="acc1") self.output1 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="output1") self.connect_neurons(self.input1, self.first1, "V", we, Tsyn) self.connect_neurons(self.first1, self.first1, "V", wi, Tsyn) self.connect_neurons(self.input1, self.last1, "V", 0.5 * we, Tsyn) self.connect_neurons(self.first1, self.acc1, "ge", wacc_bar, Tsyn + Tmin) self.connect_neurons(self.last1, self.acc1, "ge", -wacc_bar, Tsyn) # Second input self.input2 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="input2") self.first2 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="first2") self.last2 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="last2") self.acc2 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="acc2") self.output2 = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="output2") self.connect_neurons(self.input2, self.first2, "V", we, Tsyn) self.connect_neurons(self.first2, self.first2, "V", wi, Tsyn) self.connect_neurons(self.input2, self.last2, "V", 0.5 * we, Tsyn) self.connect_neurons(self.first2, self.acc2, "ge", wacc_bar, Tsyn + Tmin) self.connect_neurons(self.last2, self.acc2, "ge", -wacc_bar, Tsyn) # Sync neuron self.sync = self.add_neuron(Vt=Vt, tm=tm, tf=tf, neuron_name="sync") # sync neuron isused like in the multiplier module. A Synchronizer module cannot # be used since the intervals could be larger than Tmax) self.connect_neurons(self.last1, self.sync, "V", 0.5 * we, Tsyn) self.connect_neurons(self.last2, self.sync, "V", 0.5 * we, Tsyn) self.connect_neurons(self.sync, self.output1, "V", we, 2 * Tsyn) self.connect_neurons(self.sync, self.output2, "V", we, 2 * Tsyn) self.connect_neurons(self.sync, self.acc1, "gf", gmult, Tsyn) self.connect_neurons(self.sync, self.acc1, "gate", 1, Tsyn) self.connect_neurons(self.acc1, self.output1, "V", we, Tsyn + Tmin) self.connect_neurons(self.sync, self.acc2, "gf", gmult, Tsyn) self.connect_neurons(self.sync, self.acc2, "gate", 1, Tsyn) self.connect_neurons(self.acc2, self.output2, "V", we, Tsyn + Tmin)
[docs] class DivNetwork(SpikingNetworkModule): """ DivNetworkModule: Given inputs x1 and x2 between [0, 1] such that x1 <= x2, it computes x1 / x2. Experiments show 4 decimal places of precision. > IMPORTANT: It's the user responsability to guarantee that x1 <= x2. Otherwise, The network will malfunction (it will not output any spikes) Its working principle is based on the identity x1/x2 = exp(ln(x1) - ln(x2)). It uses two log modules whose outputs are synced, a sub network to generate the difference and a exp network to compute back the exponent. """ def __init__(self, encoder: DataEncoder, module_name: Optional[str] = None): super().__init__(module_name) self.encoder = encoder # Parameters Vt = 10.0 tm = 100.0 tf = 20.0 Tsyn = 1.0 Tneu = 0.01 Tmin = encoder.Tmin we = Vt wi = -Vt wacc = Vt * tm / encoder.Tmax wacc_bar = Vt * tm / encoder.Tcod gmult = Vt * tm / tf logsync = LogAndSyncNetwork(encoder=encoder, module_name="log_and_sync_net") sub = SubtractorNetwork(encoder=encoder, module_name="sub_net") exp = ExponentialNetwork(encoder=encoder, module_name="exp_net") self.add_subnetwork(logsync) self.add_subnetwork(sub) self.add_subnetwork(exp) self.input1 = logsync.input1 self.input2 = logsync.input2 self.output = exp.output self.connect_neurons(logsync.output1, sub.input1, "V", we, Tsyn) self.connect_neurons(logsync.output2, sub.input2, "V", we, Tsyn) # Since x1 < x2 (by construction), sub always spikes on output plus self.connect_neurons(sub.output_plus, exp.input, "V", we, Tsyn)
if __name__ == "__main__": from axon_sdk import Simulator encoder = DataEncoder(Tmin=10.0, Tcod=100.0) net = DivNetwork(encoder, module_name="div") sim = Simulator(net, encoder, dt=0.001) x1 = 0.0001 x2 = 0.5 assert x1 < x2, f"x1 must be smaller than x2!!, but x1: {x1} and x2 {x2}" sim.apply_input_value(value=x1, neuron=net.input1, t0=0) sim.apply_input_value(value=x2, neuron=net.input2, t0=0) sim.simulate(simulation_time=300) # --- Output Decoding --- out_spikes = sim.spike_log[net.output.uid] assert len(out_spikes) == 2, "Didn't get 2 output spikes" print(f"Input x1: {x1}, input x2: {x2}") print(f"Expected {(x1 / x2):.4f}") print(f"Decoded: {encoder.decode_interval(out_spikes[1] - out_spikes[0])}")