class test_CSRConnectivity(unittest.TestCase): """ This class tests the functionality of the connectivity patterns within *Projections*. """ @classmethod def setUpClass(self): """ Compile the network for this test """ self.test_net = Network() self.test_net.add([pop1, pop2, proj]) self.test_net.compile(silent=True, debug_build=True, clean=True) def setUp(self): """ In our *setUp()* function we call *reset()* to reset the network before every test. """ self.test_net.reset() def test_all_to_all(self): """ Tests the *all_to_all* connectivity pattern, in which every pre-synaptic neuron is connected to every post-synaptic neuron. We test correctness of ranks and weight values. """ tmp = self.test_net.get(proj) #self.assertEqual(tmp.dendrite(3).rank, [0, 1, 2, 3, 4, 5, 6, 7, 8]) self.assertTrue(numpy.allclose(tmp.dendrite(3).w, numpy.ones((8, 1)) * 0.1))
class test_CSRConnectivity(unittest.TestCase): """ This class tests the functionality of the connectivity patterns within *Projections*. """ @classmethod def setUpClass(self): """ Compile the network for this test """ self.test_net = Network() self.test_net.add([pop1, pop2, proj]) self.test_net.compile(silent=True, debug_build=True, clean=True) def setUp(self): """ In our *setUp()* function we call *reset()* to reset the network before every test. """ self.test_net.reset() def test_all_to_all(self): """ Tests the *all_to_all* connectivity pattern, in which every pre-synaptic neuron is connected to every post-synaptic neuron. We test correctness of ranks and weight values. """ tmp = self.test_net.get(proj) #self.assertEqual(tmp.dendrite(3).rank, [0, 1, 2, 3, 4, 5, 6, 7, 8]) self.assertTrue( numpy.allclose(tmp.dendrite(3).w, numpy.ones((8, 1)) * 0.1))
class test_CurrentInjection(unittest.TestCase): """ Test the implementation of the specialized projection 'CurrentInjection'. Based on the example in the documentation. """ @classmethod def setUpClass(self): """ Compile the network for this test. Adapted the example from documentation. """ SimpleSpike = Neuron(equations="mp=g_exc", spike="mp >= 1.0", reset="") inp = Population(1, neuron=Neuron(equations="r=sin(t)")) out = Population(1, neuron=SimpleSpike) m = Monitor(out, "mp") proj = CurrentInjection(inp, out, 'exc') proj.connect_current() self.test_net = Network() self.test_net.add([inp, out, proj, m]) self.test_net.compile(silent=True) self.output = self.test_net.get(out) self.m = self.test_net.get(m) def setUp(self): """ Automatically called before each test method, basically to reset the network after every test. """ self.test_net.reset() def test_compile(self): """ Enforce compilation of the network. """ pass def test_run_one_loop(self): self.test_net.simulate(11) rec_data = self.m.get("mp")[:, 0] # there is 1 dt delay between the input and output target = [0] + [sin(x) for x in range(10)] self.assertTrue(np.allclose(rec_data, target))
class test_SynapticAccess(unittest.TestCase): """ ANNarchy support several global operations, there are always applied on variables of *Population* objects. This particular test focuses on the usage of them in synaptic learning rules (for instance covariance). """ @classmethod def setUpClass(self): """ Compile the network for this test """ neuron = Neuron(parameters=""" r=0 """) cov = Synapse(parameters=""" tau = 5000.0 """, equations=""" tau * dw/dt = (pre.r - mean(pre.r) ) * (post.r - mean(post.r) ) """) pre = Population(6, neuron) post = Population(1, neuron) proj = Projection(pre, post, "exc", synapse=cov).connect_all_to_all(weights=1.0) self.test_net = Network() self.test_net.add([pre, post, proj]) self.test_net.compile(silent=True) self.net_pop = self.test_net.get(post) @classmethod def tearDownClass(cls): del cls.test_net def test_compile(self): """ Tests the result of *norm1(r)* for *pop*. """ pass
class test_GlobalOps_1D(unittest.TestCase): """ ANNarchy support several global operations, there are always applied on variables of *Population* objects. Currently the following methods are supported: * mean() * max() * min() * norm1() * norm2() They are used in the equations of our neuron definition. This particular test focuses on a one-dimensional *Population*. """ @classmethod def setUpClass(self): """ Compile the network for this test """ neuron = Neuron(parameters=""" r=0 """, equations=""" mean_r = mean(r) max_r = max(r) min_r = min(r) l1 = norm1(r) l2 = norm2(r) """) pop = Population(6, neuron) self.test_net = Network() self.test_net.add([pop]) self.test_net.compile(silent=True) self.net_pop = self.test_net.get(pop) @classmethod def tearDownClass(cls): del cls.test_net def setUp(self): """ In our *setUp()* function we set the variable *r*. We also call *simulate()* to calculate mean/max/min. """ # reset() set all variables to init value (default 0), which is # unfortunately meaningless for mean/max/min. So we set here some # better values self.net_pop.r = [2.0, 1.0, 0.0, -5.0, -3.0, -1.0] # 1st step: calculate mean/max/min and store in intermediate # variables # 2nd step: write intermediate variables to accessible variables. self.test_net.simulate(2) def tearDown(self): """ After each test we call *reset()* to reset the network. """ self.test_net.reset() def test_get_mean_r(self): """ Tests the result of *mean(r)* for *pop*. """ self.assertTrue(numpy.allclose(self.net_pop.mean_r, -1.0)) def test_get_max_r(self): """ Tests the result of *max(r)* for *pop*. """ self.assertTrue(numpy.allclose(self.net_pop.max_r, 2.0)) def test_get_min_r(self): """ Tests the result of *min(r)* for *pop*. """ self.assertTrue(numpy.allclose(self.net_pop.min_r, -5.0)) def test_get_l1_norm(self): """ Tests the result of *norm1(r)* (L1 norm) for *pop*. """ self.assertTrue(numpy.allclose(self.net_pop.l1, 12.0)) def test_get_l2_norm(self): """ Tests the result of *norm2(r)* (L2 norm) for *pop*. """ # compute control value l2norm = numpy.linalg.norm(self.net_pop.r, ord=2) # test self.assertTrue(numpy.allclose(self.net_pop.l2, l2norm))
class test_GlobalOps_1D_Large(unittest.TestCase): @classmethod def setUpClass(self): """ Compile the network for this test """ neuron = Neuron(parameters=""" r=0 """, equations=""" mean_r = mean(r) max_r = max(r) min_r = min(r) l1 = norm1(r) l2 = norm2(r) """) pop = Population(500, neuron) self.test_net = Network() self.test_net.add([pop]) self.test_net.compile(silent=True) self.net_pop = self.test_net.get(pop) @classmethod def tearDownClass(cls): del cls.test_net def tearDown(self): """ After each test we call *reset()* to reset the network. """ self.test_net.reset() def test_mean_r(self): """ """ rand_val = numpy.random.random(500) self.net_pop.r = rand_val self.test_net.simulate(2) self.assertTrue( numpy.allclose(self.net_pop.mean_r, numpy.mean(rand_val))) def test_min_r(self): """ """ rand_val = numpy.random.random(500) self.net_pop.r = rand_val self.test_net.simulate(2) self.assertTrue( numpy.allclose(self.net_pop.min_r, numpy.amin(rand_val))) def test_max_r(self): """ """ rand_val = numpy.random.random(500) self.net_pop.r = rand_val self.test_net.simulate(2) self.assertTrue( numpy.allclose(self.net_pop.max_r, numpy.amax(rand_val)))
#inp_i = Projection(pre=I_i, post=I, target='exc') #inp_e.connect_one_to_one(1.0) #inp_i.connect_one_to_one(1.0) E.i_offset = 5.0 I.i_offset = 2.0 # monitoring obs_e = Monitor(E, variables=['spike', 'v'], start=True) obs_i = Monitor(I, variables=['spike', 'v'], start=True) # simulation ############ # annarchy simulation net = Network(everything=True) net.compile() net.simulate(duration=T) # conversion to pyrates rate_e = pyrates_from_annarchy(monitors=[net.get(obs_e)], vars=['spike'], pop_average=True) rate_i = pyrates_from_annarchy(monitors=[net.get(obs_i)], vars=['spike'], pop_average=True) v_e = pyrates_from_annarchy(monitors=[net.get(obs_e)], vars=['v'], pop_average=False) v_i = pyrates_from_annarchy(monitors=[net.get(obs_i)], vars=['v'], pop_average=False)
def grid_search_annarchy(param_grid: dict, param_map: dict, dt: float, simulation_time: float, inputs: dict, outputs: dict, sampling_step_size: Optional[float] = None, permute_grid: bool = False, circuit=None, **kwargs) -> DataFrame: """Function that runs multiple parametrizations of the same circuit in parallel and returns a combined output. Parameters ---------- param_grid Key-value pairs for each circuit parameter that should be altered over different circuit parametrizations. param_map Key-value pairs that map the keys of param_grid to concrete circuit variables. dt Simulation step-size in s. simulation_time Simulation time in s. inputs Inputs as provided to the `run` method of `:class:ComputeGraph`. outputs Outputs as provided to the `run` method of `:class:ComputeGraph`. sampling_step_size Sampling step-size as provided to the `run` method of `:class:ComputeGraph`. permute_grid If true, all combinations of the provided param_grid values will be realized. If false, the param_grid values will be traversed pairwise. circuit Instance of ANNarchy network. kwargs Additional keyword arguments passed to the `:class:ComputeGraph` initialization. Returns ------- DataFrame Simulation results stored in a multi-index data frame where each index lvl refers to one of the parameters of param_grid. """ from ANNarchy import Population, Projection, Network, TimedArray, Monitor, ANNarchyException # linearize parameter grid if necessary if type(param_grid) is dict: param_grid = linearize_grid(param_grid, permute_grid) # create annarchy net if necessary if circuit is None: circuit = Network(everything=True) # assign parameter updates to each circuit and combine them to unconnected network circuit_names = [] param_info = [] param_split = "__" val_split = "--" comb = "_" populations, projections = {}, {} for n in range(param_grid.shape[0]): # copy and re-parametrize populations try: for p in circuit.get_populations(): name = f'net{n}/{p.name}' p_new = Population(geometry=p.geometry, neuron=p.neuron_type, name=name, stop_condition=p.stop_condition, storage_order=p._storage_order, copied=False) p_new = adapt_pop(p_new, param_grid.iloc[n, :], param_map) populations[name] = p_new # add input to population for node, inp in inputs.items(): if node in name: inp_name = f'{name}_inp' inp = TimedArray(rates=inp, name=inp_name) proj = Projection(pre=inp, post=p_new, target='exc') proj.connect_one_to_one(1.0) populations[inp_name] = inp projections[inp_name] = proj except ANNarchyException: pass # copy and re-parametrize projections try: for c in circuit.get_projections(): source = c.pre if type(c.pre) is str else c.pre.name target = c.post if type(c.post) is str else c.post.name source = f'net{n}/{source}' target = f'net{n}/{target}' name = f'{source}/{target}/{c.name}' c_new = Projection(pre=source, post=target, target=c.target, synapse=c.synapse_type, name=name, copied=False) c_new._store_connectivity(c._connection_method, c._connection_args, c._connection_delay, c._storage_format) c_new = adapt_proj(c_new, param_grid.iloc[n, :], param_map) projections[name] = c_new except ANNarchyException: pass # collect parameter and circuit name infos circuit_names.append(f'net{n}') param_names = list(param_grid.columns.values) param_info_tmp = [f"{param_names[i]}{val_split}{val}" for i, val in enumerate(param_grid.iloc[n, :])] param_info.append(param_split.join(param_info_tmp)) net = Network() for p in populations.values(): net.add(p) for c in projections.values(): net.add(c) # adjust output of simulation to combined network nodes = [p.name for p in circuit.get_populations()] out_names, var_names, out_lens, monitors, monitor_names = [], [], [], [], [] for out_key, out in outputs.copy().items(): out_names_tmp, out_lens_tmp = [], [] if out[0] in nodes: for i, name in enumerate(param_info): out_tmp = list(out) out_tmp[0] = f'{circuit_names[i]}/{out_tmp[0]}' p = net.get_population(out_tmp[0]) monitors.append(Monitor(p, variables=out_tmp[-1], period=sampling_step_size, start=True, net_id=net.id)) monitor_names.append(f'{name}{param_split}out_var{val_split}{out_key}{comb}{out[0]}') var_names.append(out_tmp[-1]) out_names_tmp.append(f'{out_key}{comb}{out[0]}') out_lens_tmp.append(p.geometry[0]) elif out[0] == 'all': for node in nodes: for i, name in enumerate(param_info): out_tmp = list(out) out_tmp[0] = f'{circuit_names[i]}/{node}' p = net.get_population(out_tmp[0]) monitors.append(Monitor(p, variables=out_tmp[-1], period=sampling_step_size, start=True, net_id=net.id)) monitor_names.append(f'{name}{param_split}out_var{val_split}{out_key}{comb}{node}') var_names.append(out_tmp[-1]) out_names_tmp.append(f'{out_key}{comb}{node}') out_lens_tmp.append(p.geometry[0]) else: node_found = False for node in nodes: if out[0] in node: node_found = True for i, name in enumerate(param_info): out_tmp = list(out) out_tmp[0] = f'{circuit_names[i]}/{node}' p = net.get_population(out_tmp[0]) monitors.append(Monitor(p, variables=out_tmp[-1], period=sampling_step_size, start=True, net_id=net.id)) monitor_names.append(f'{name}{param_split}out_var{val_split}{out_key}{comb}{node}') var_names.append(out_tmp[-1]) out_names_tmp.append(f'{out_key}{comb}{node}') out_lens_tmp.append(p.geometry[0]) if not node_found: raise ValueError(f'Invalid output identifier in output: {out_key}. ' f'Node {out[0]} is not part of this network') out_names += list(set(out_names_tmp)) out_lens += list(set(out_lens_tmp)) #net.add(monitors) # simulate the circuits behavior net.compile() net.simulate(duration=simulation_time) # transform output into pyrates-compatible data format results = pyrates_from_annarchy(monitors, vars=list(set(var_names)), monitor_names=monitor_names, **kwargs) # transform results into long-form dataframe with changed parameters as columns multi_idx = [param_grid[key].values for key in param_grid.keys()] n_iters = len(multi_idx[0]) outs = [] for out_name, out_len in zip(out_names, out_lens): outs += [f'{out_name}_n{i}' for i in range(out_len)] * n_iters multi_idx_final = [] for idx in multi_idx: for val in idx: for out_len in out_lens: multi_idx_final += [val]*len(out_names)*out_len index = MultiIndex.from_arrays([multi_idx_final, outs], names=list(param_grid.keys()) + ["out_var"]) index = MultiIndex.from_tuples(list(set(index)), names=list(param_grid.keys()) + ["out_var"]) results_final = DataFrame(columns=index, data=np.zeros_like(results.values), index=results.index) for col in results.keys(): params = col.split(param_split) indices = [None] * len(results_final.columns.names) for param in params: var, val = param.split(val_split)[:2] idx = list(results_final.columns.names).index(var) try: indices[idx] = float(val) except ValueError: indices[idx] = val results_final.loc[:, tuple(indices)] = results[col].values return results_final
class test_CustomFunc(unittest.TestCase): """ This class tests the definition of custom functions, they can defined on three levels: * globally * within neurons * within synapses """ @classmethod def setUpClass(self): """ Compile the network for this test """ neuron = Neuron( equations = "r = transfer_function(sum(exc), 0.0)", functions = "transfer_function(x, t) = if x > t: if x > 2*t : (x - 2*t)^2 else: x - t else: 0." ) neuron2 = Neuron( equations = "r = glob_pos(sum(exc))" ) synapse = Synapse( equations="w += hebb(pre.r, post.r)", functions="hebb(x, y) = x * y" ) pop = Population(10, neuron) pop2 = Population(10, neuron2) proj = Projection(pop, pop, 'exc', synapse).connect_all_to_all(1.0) self.test_net = Network() self.test_net.add([pop, pop2, proj]) self.test_net.compile(silent=True) self.net_pop = self.test_net.get(pop) self.net_proj = self.test_net.get(proj) @classmethod def tearDownClass(cls): del cls.test_net def setUp(self): """ In our *setUp()* function we call *reset()* to reset the network. """ self.test_net.reset() def test_neuron(self): """ Custom func defined within a neuron object, providing numpy.array data. """ self.assertTrue(numpy.allclose(self.net_pop.transfer_function(numpy.array([0., 1., 2., 3.]), numpy.array([2., 2., 2., 2.])), [0, 0, 0, 1])) def test_neuron2(self): """ Custom func defined within a neuron object, providing simple lists. """ self.assertTrue(numpy.allclose(self.net_pop.transfer_function([0., 1., 2., 3.], [2., 2., 2., 2.]), [0, 0, 0, 1])) def test_synapse(self): """ Custom func defined within a synapse object, providing simple lists. """ self.assertTrue(numpy.allclose(self.net_proj.hebb(numpy.array([0., 1., 2., 3.]), numpy.array([0., 1., 2., 3.])), [0, 1, 4, 9])) def test_synapse2(self): """ Custom func defined within a synapse object, providing simple lists. """ self.assertTrue(numpy.allclose(self.net_proj.hebb([0., 1., 2., 3.], [0., 1., 2., 3.]), [0, 1, 4, 9]))
class test_BuiltinFunctions(unittest.TestCase): """ Test the correct evaluation of builtin functions """ @classmethod def setUpClass(self): """ Compile the network for this test """ BuiltinFuncs = Neuron(parameters=""" base = 2.0 """, equations=""" r = modulo(t,3) pr = power(base,3) clip_below = clip(-2, -1, 1) clip_within = clip(0, -1, 1) clip_above = clip(2, -1, 1) """) pop1 = Population(1, BuiltinFuncs) mon = Monitor(pop1, ['r', 'pr', 'clip_below', 'clip_within', 'clip_above']) self.test_net = Network() self.test_net.add([pop1, mon]) self.test_net.compile(silent=True) self.test_mon = self.test_net.get(mon) @classmethod def tearDownClass(cls): """ All tests of this class are done. We can destroy the network. """ del cls.test_net def setUp(self): """ Automatically called before each test method, basically to reset the network after every test. """ self.test_net.reset() def tearDown(self): """ Since all tests are independent, after every test we use the *get()* method for every monotor to clear all recordings. """ self.test_mon.get() def test_modulo(self): """ Test modulo function. """ self.test_net.simulate(10) data_m = self.test_mon.get('r') self.assertTrue( np.allclose(data_m, [[0.0], [1.0], [2.0], [0.0], [1.0], [2.0], [0.0], [1.0], [2.0], [0.0]])) def test_integer_power(self): """ Test integer power function. """ self.test_net.simulate(1) data_m = self.test_mon.get('pr') self.assertTrue(np.allclose(data_m, [[8.0]])) def test_clip_below(self): """ The clip(x, a, b) method ensures that x is within range [a,b]. This tests validates that x = -2 is clipped to -1 """ data_clip_below = self.test_mon.get('clip_below') self.assertTrue(np.allclose(data_clip_below, [[-1.0]])) def test_clip_within(self): """ The clip(x, a, b) method ensures that x is within range [a,b]. This tests validates that x = 0 retains. """ data_clip_within = self.test_mon.get('clip_within') self.assertTrue(np.allclose(data_clip_within, [[0.0]])) def test_clip_above(self): """ The clip(x, a, b) method ensures that x is within range [a,b]. This tests validates that x = 2 is clipped to 1. """ data_clip_above = self.test_mon.get('clip_above') self.assertTrue(np.allclose(data_clip_above, [[1.0]]))
class test_Dendrite(unittest.TestCase): """ This class tests the *Dendrite* object, which gathers all synapses belonging to a post-synaptic neuron in a *Projection*: * access to parameters * the *rank* method * the *size* method """ @classmethod def setUpClass(self): """ Compile the network for this test """ neuron = Neuron(parameters="tau = 10", equations="r += 1/tau * t") neuron2 = Neuron(parameters="tau = 10: population", equations="r += 1/tau * t: init = 1.0") Oja = Synapse(parameters=""" tau = 5000.0 : postsynaptic alpha = 8.0 """, equations=""" tau * dw/dt = pre.r * post.r - alpha * post.r^2 * w """) pop1 = Population(5, neuron) pop2 = Population(8, neuron2) proj = Projection(pre=pop1, post=pop2, target="exc", synapse=Oja) proj.connect_all_to_all(weights=1.0) self.test_net = Network() self.test_net.add([pop1, pop2, proj]) self.test_net.compile(silent=True) self.net_proj = self.test_net.get(proj) @classmethod def tearDownClass(cls): del cls.test_net def setUp(self): """ In our *setUp()* function we call *reset()* to reset the network. """ self.test_net.reset() def test_none(self): """ If a non-existent *Dendrite* is accessed, an error should be thrown. This is tested here. """ from ANNarchy.core.Global import ANNarchyException with self.assertRaises(ANNarchyException) as cm: d = self.net_proj.dendrite(14) # self.assertEqual(cm.exception.code, 1) def test_pre_ranks(self): """ Tests the *pre_ranks* method, which returns the ranks of the pre-synaptic neurons belonging to the accessed *Dendrite*. """ self.assertEqual(self.net_proj.dendrite(5).pre_ranks, [0, 1, 2, 3, 4]) def test_dendrite_size(self): """ Tests the *size* method, which returns the number of pre-synaptic neurons belonging to the accessed *Dendrite*. """ self.assertEqual(self.net_proj.dendrite(3).size, 5) def test_get_dendrite_tau(self): """ Tests the direct access of the parameter *tau* of a *Dendrite*. """ self.assertTrue(numpy.allclose(self.net_proj.dendrite(1).tau, 5000.0)) def test_get_dendrite_alpha(self): """ Tests the direct access of the variable *alpha* of a *Dendrite*. """ self.assertTrue( numpy.allclose( self.net_proj.dendrite(0).alpha, [8.0, 8.0, 8.0, 8.0, 8.0])) def test_get_dendrite_weights(self): """ Tests the direct access of the parameter *w* (weights) of a *Dendrite*. """ self.assertTrue( numpy.allclose( self.net_proj.dendrite(7).w, [1.0, 1.0, 1.0, 1.0, 1.0])) def test_set_tau(self): """ Tests the setting of the parameter *tau* for the whole *Projection* through a single value. """ self.net_proj.tau = 6000.0 self.assertTrue(numpy.allclose(self.net_proj.dendrite(0).tau, 6000.0)) def test_set_tau_2(self): """ Tests the setting of the parameter *tau* for a single dendrite with a single value. """ old_value = self.net_proj.tau old_value[1] = 7000.0 self.net_proj.dendrite(1).tau = 7000.0 self.assertTrue(numpy.allclose(self.net_proj.dendrite(1).tau, 7000.0)) self.assertTrue(numpy.allclose(self.net_proj.tau, old_value)) def test_set_alpha(self): """ Tests the setting of the parameter *alpha* of a *Dendrite*. """ self.net_proj.dendrite(4).alpha = 9.0 self.assertTrue( numpy.allclose( self.net_proj.dendrite(4).alpha, [9.0, 9.0, 9.0, 9.0, 9.0])) def test_set_alpha_2(self): """ Tests the setting of the parameter *alpha* of a specific synapse in a *Dendrite*. """ self.net_proj.dendrite(4)[1].alpha = 10.0 self.assertTrue( numpy.allclose( self.net_proj.dendrite(4).alpha, [9.0, 10.0, 9.0, 9.0, 9.0])) def test_set_weights(self): """ Tests the setting of the parameter *w* (weights) of a *Dendrite*. """ self.net_proj.dendrite(6).w = 2.0 self.assertTrue( numpy.allclose( self.net_proj.dendrite(6).w, [2.0, 2.0, 2.0, 2.0, 2.0])) def test_set_weights_2(self): """ Tests the setting of the parameter *w* (weights) of a specific synapse in a *Dendrite*. """ self.net_proj.dendrite(6)[2].w = 3.0 self.assertTrue( numpy.allclose( self.net_proj.dendrite(6).w, [2.0, 2.0, 3.0, 2.0, 2.0])) def test_set_with_dict(self): """ Test the setting of attributes using a dictionary. """ new_value = self.net_proj.tau new_value[1] = 7000.0 update = dict({'tau': 7000}) self.net_proj.dendrite(1).set(update) self.assertTrue(numpy.allclose(self.net_proj.tau, new_value)) def test_get_by_name(self): """ Test the retrieval of an attribute by the name. """ val = self.net_proj.dendrite(1).get('tau') self.assertTrue(numpy.allclose(val, 5000.0))