sPyNNaker neural_modelling  development
connection_generator_all_but_me.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024 The University of Manchester
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
24 #include <stdbool.h>
26 
29  // How many values there are in each WTA group
30  uint32_t n_values;
31 
32  // Whether there are weight values specified or not
33  uint32_t has_weights;
34 
35  // The weight values if specified.
36  // If so, there must be (n_values * n_values - 1) weights
37  accum weights[];
38 };
39 
41 struct all_but_me {
42  // How many neurons there are in each WTA group
43  uint32_t n_neurons_per_group;
44 
45  // The weight values if specified.
46  // If so, there must be (n_values * n_values - 1) weights
47  accum *weights;
48 };
49 
50 
58  // Get the SDRAM params
59  struct all_but_me_conf *params_sdram = *region;
60 
61  // Allocate the data structure for parameters
62  struct all_but_me *params = spin1_malloc(sizeof(struct all_but_me));
63 
64  // Copy the parameters
65  params->n_neurons_per_group = params_sdram->n_values;
66  if (params_sdram->has_weights) {
67  uint32_t n_per_group = params->n_neurons_per_group;
68  uint32_t weight_size = n_per_group * (n_per_group - 1) * sizeof(accum);
69  params->weights = spin1_malloc(weight_size);
70  if (params->weights == NULL) {
71  // If we can't copy, just reference the SDRAM
72  params->weights = &params_sdram->weights[0];
73  } else {
74  spin1_memcpy(&params->weights[0], &params_sdram->weights[0], weight_size);
75  }
76  *region = &params_sdram->weights[n_per_group * (n_per_group - 1)];
77  } else {
78  params->weights = NULL;
79  *region = &params_sdram->weights[0];
80  }
81 
82  log_info("allButMe connector, n_values = %u, has_weights = %u", params->n_neurons_per_group,
83  params_sdram->has_weights);
84 
85  return params;
86 }
87 
92 static void connection_generator_all_but_me_free(void *generator) {
93  sark_free(generator);
94 }
95 
96 static inline bool make_all_but_me_conn(accum weight,
97  param_generator_t delay_generator, matrix_generator_t matrix_generator,
98  uint32_t pre, uint32_t post, unsigned long accum weight_scale,
99  accum timestep_per_delay) {
100  uint16_t delay = rescale_delay(
101  param_generator_generate(delay_generator), timestep_per_delay);
103  weight, delay, weight_scale)) {
104  log_error("Matrix not sized correctly!");
105  return false;
106  }
107  return true;
108 }
109 
110 static inline void div_mod(uint32_t dividend, uint32_t divisor, uint32_t *div,
111  uint32_t *mod) {
112  uint32_t remainder = dividend;
113  uint32_t count = 0;
114  while (remainder >= divisor) {
115  remainder -= divisor;
116  count++;
117  }
118  *div = count;
119  *mod = remainder;
120 }
121 
125 static inline accum get_weight(struct all_but_me *obj,
126  param_generator_t weight_generator, uint32_t pre_value,
127  uint32_t post_value) {
128  // Get the post position rather than the post value. Because each "row" in
129  // the table has the diagonal removed, we need to adjust where we get the
130  // value from depending on the relative pre and post values (which must not
131  // be the same - this isn't checked here though).
132  uint32_t post_pos = post_value;
133  if (post_value >= pre_value) {
134  post_pos -= 1;
135  }
136  if (obj->weights != NULL) {
137  uint32_t weight_index = (pre_value * (obj->n_neurons_per_group - 1)) + post_pos;
138  return obj->weights[weight_index];
139  } else {
140  return param_generator_generate(weight_generator);
141  }
142 }
143 
157  void *generator, uint32_t pre_lo, uint32_t pre_hi,
158  uint32_t post_lo, uint32_t post_hi, UNUSED uint32_t post_index,
159  uint32_t post_slice_start, uint32_t post_slice_count,
160  unsigned long accum weight_scale, accum timestep_per_delay,
161  param_generator_t weight_generator, param_generator_t delay_generator,
162  matrix_generator_t matrix_generator) {
163  struct all_but_me *obj = generator;
164 
165  // Get the actual ranges to generate within
166  uint32_t post_start = max(post_slice_start, post_lo);
167  uint32_t post_end = min(post_slice_start + post_slice_count - 1, post_hi);
168 
169  // Work out where we are in the generation
170  // We need to connect each pre-neuron to each post-neuron in each group
171  // (but not to itself). We are currently generating a subset of the post
172  // neurons, so we need to work out which group we are in within that subset,
173  // and which is the first post-neuron in the group that we are generating
174  // for now.
175  uint32_t post_group;
176  uint32_t post_value;
177  div_mod(post_start, obj->n_neurons_per_group, &post_group, &post_value);
178 
179  // Work out where the pre-neurons start and end for the group that we are
180  // in at the start of the post-neurons. The group might not have enough
181  // neurons in it, so we check just in case.
182  uint32_t pre_start = pre_lo + post_group * obj->n_neurons_per_group;
183  uint32_t pre_end = min(pre_start + obj->n_neurons_per_group, pre_hi + 1);
184  uint32_t n_values = pre_end - pre_start;
185 
186  // Go through the post neurons in this slice
187  for (uint32_t post = post_start; post <= post_end; post++) {
188  uint32_t local_post = post - post_slice_start;
189 
190  // Go through each of the "values" in this group that can target this
191  // post neuron (each of which is a pre-neuron)
192  for (uint32_t pre_value = 0; pre_value < n_values; pre_value++) {
193  if (pre_value != post_value) {
194  uint32_t pre = pre_start + pre_value;
195  accum weight = get_weight(obj, weight_generator, pre_value, post_value);
196  if (!make_all_but_me_conn(weight, delay_generator,
197  matrix_generator, pre, local_post, weight_scale,
198  timestep_per_delay)) {
199  return false;
200  }
201  }
202  }
203 
204  // Work out next loop iteration. If we have reached the end of a group
205  // of values, we need to move onto the next group.
206  post_value += 1;
207  if (post_value == obj->n_neurons_per_group) {
208  post_value = 0;
209  pre_start += obj->n_neurons_per_group;
210  pre_end = min(pre_start + obj->n_neurons_per_group, pre_hi + 1);
211  if (pre_start >= pre_hi) {
212  break;
213  }
214  n_values = pre_end - pre_start;
215  }
216  }
217 
218  return true;
219 }
static void connection_generator_all_but_me_free(void *generator)
Free the All But Me connection generator.
static void * connection_generator_all_but_me_initialise(void **region)
Initialise the all but me connection generator.
static accum get_weight(struct all_but_me *obj, param_generator_t weight_generator, uint32_t pre_value, uint32_t post_value)
static bool connection_generator_all_but_me_generate(void *generator, uint32_t pre_lo, uint32_t pre_hi, uint32_t post_lo, uint32_t post_hi, uint32_t post_index, uint32_t post_slice_start, uint32_t post_slice_count, unsigned long accum weight_scale, accum timestep_per_delay, param_generator_t weight_generator, param_generator_t delay_generator, matrix_generator_t matrix_generator)
Generate connections with the all but me connection generator.
The parameters to be passed around for this connector.
The parameters to be passed around for this connector.
General types associated with generators.
static uint16_t rescale_delay(accum delay, accum timestep_per_delay)
Rescales a delay to account for timesteps and type-converts it.
bool matrix_generator_write_synapse(matrix_generator_t generator, uint32_t pre_index, uint16_t post_index, accum weight, uint16_t delay, unsigned long accum weight_scale)
Write a synapse with a matrix generator.
The data for a matrix generator.
accum param_generator_generate(param_generator_t generator)
Generate value with a parameter generator.
region
spike source array region IDs in human readable form
static stdp_params params
Configuration parameters.