sPyNNaker neural_modelling  7.4.2
synapse_dynamics_external_weight_change.c
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 
20 #include <neuron/synapses.h>
22 #include <stddef.h>
23 
24 typedef struct limits {
25  weight_t min;
26  weight_t max;
27 } limits;
28 
29 typedef struct change_params {
30  uint32_t n_limits;
31  limits weight_limits[];
33 
34 typedef struct updatable_synapse_t {
35  weight_t weight;
37 
39  uint32_t pre_spike: 31;
40  uint32_t is_update: 1;
41 
42  // This is only present if is_update is false
44 };
45 
46 typedef struct fixed_stdp_synapse {
47  uint32_t delay;
48  uint32_t type;
49  uint32_t index;
50  uint32_t ring_buffer_index;
52 
55 
57 static uint32_t num_plastic_pre_synaptic_events = 0;
58 
60 static uint32_t plastic_saturation_count = 0;
61 
64 
66  address_t address, uint32_t n_neurons,
67  uint32_t n_synapse_types,
68  UNUSED uint32_t *ring_buffer_to_input_buffer_left_shifts) {
69 
70  change_params *sdram_params = (change_params *) address;
71  uint32_t size = sizeof(change_params) + (n_synapse_types * sizeof(limits));
72  params = spin1_malloc(size);
73  if (params == NULL) {
74  log_error("Unable to allocate memory for params");
75  return false;
76  }
77  spin1_memcpy(params, sdram_params, size);
78  for (uint32_t i = 0; i < n_synapse_types; i++) {
79  log_info("Synapse type %u: min = %d, max = %d", i,
80  params->weight_limits[i].min, params->weight_limits[i].max);
81  }
82 
84  if (post_event_history == NULL) {
85  log_error("Failed to allocate post_event_history");
86  return false;
87  }
88 
89  return true;
90 }
91 
92 //---------------------------------------
94  UNUSED uint32_t time, UNUSED index_t neuron_index) {
95  // Do Nothing - not needed here!
96 }
97 
98 static inline fixed_stdp_synapse synapse_dynamics_stdp_get_fixed(
99  uint32_t control_word, uint32_t time, uint32_t colour_delay) {
100  // Extract control-word components
101  // **NOTE** cunningly, control word is just the same as lower
102  // 16-bits of 32-bit fixed synapse so same functions can be used
103  uint32_t delay = synapse_row_sparse_delay(control_word,
105  uint32_t type_index = synapse_row_sparse_type_index(control_word,
107  uint32_t type = synapse_row_sparse_type(control_word, synapse_index_bits,
109  uint32_t index = synapse_row_sparse_index(control_word, synapse_index_mask);
110  return (fixed_stdp_synapse) {
111  .delay = delay,
112  .type = type,
113  .index = index,
114  .ring_buffer_index = synapse_row_get_ring_buffer_index_combined(
115  (delay + time) - colour_delay, type_index,
117  };
118 }
119 
120 static inline void synapse_dynamics_stdp_update_ring_buffers(
121  weight_t *ring_buffers, fixed_stdp_synapse s, int32_t weight) {
122  uint32_t accumulation = ring_buffers[s.ring_buffer_index] + weight;
123 
124  uint32_t sat_test = accumulation & 0xFFFF0000;
125  if (sat_test) {
126  accumulation = 0xFFFF;
128  }
129 
130  ring_buffers[s.ring_buffer_index] = accumulation;
131 }
132 
133 //---------------------------------------
134 static inline updatable_synapse_t process_plastic_synapse(
135  uint32_t pre_spike, uint32_t control_word, weight_t *ring_buffers,
136  uint32_t time, uint32_t colour_delay, updatable_synapse_t synapse,
137  uint32_t *changed) {
138  fixed_stdp_synapse s = synapse_dynamics_stdp_get_fixed(control_word, time,
139  colour_delay);
140 
141 
142  // Work out if the weight needs to be updated
143  post_event_history_t *history = &post_event_history[s.index];
144  weight_t min_weight = params->weight_limits[s.type].min;
145  weight_t max_weight = params->weight_limits[s.type].max;
146  log_debug(" Looking at change weight history 0x%08x of %u items to post"
147  " neuron index %u", history, history->count, s.index);
148  for (uint32_t i = 0; i < history->count; i++) {
149  update_post_trace_t *trace = &history->traces[i];
150  log_debug(
151  " Checking history item %u, weight change %d for"
152  " pre-neuron %u, synapse_type = %u",
153  i, trace->weight_change, trace->pre_spike, trace->synapse_type);
154  if (trace->pre_spike == pre_spike && s.type == trace->synapse_type) {
155  int32_t new_weight = synapse.weight + trace->weight_change;
156  if (new_weight < min_weight) {
157  synapse.weight = min_weight;
158  } else if (new_weight > max_weight) {
159  synapse.weight = max_weight;
160  } else {
161  synapse.weight = (weight_t) new_weight;
162  }
163  log_debug(" Weight now %d", synapse.weight);
164  *changed = 1;
165 
166  // Remove the done item from history and then go back to make sure
167  // we do the next one!
168  if (post_events_remove(history, i)) {
169  i--;
170  }
171  }
172  }
173 
174  // Add weight to ring-buffer entry, but only if not too late
175  if (s.delay > colour_delay) {
176  synapse_dynamics_stdp_update_ring_buffers(ring_buffers, s,
177  synapse.weight);
178  } else {
180  }
181 
182  return synapse;
183 }
184 
185 static inline int16_t change_sign(weight_t weight) {
186  union {
187  weight_t weight;
188  int16_t value;
189  } converter;
190  converter.weight = weight;
191  return converter.value;
192 }
193 
194 static inline void process_weight_update(
195  synapse_row_plastic_data_t *plastic_region_address,
196  synapse_row_fixed_part_t *fixed_region) {
197  uint32_t n_synapses = synapse_row_num_plastic_controls(fixed_region);
198  const uint32_t *words = (uint32_t *) synapse_row_plastic_controls(fixed_region);
199  uint32_t pre_spike = plastic_region_address->pre_spike;
200 
201  log_debug("Weight change update for pre-neuron %u", pre_spike);
202 
203  // Loop through synapses
204  for (; n_synapses > 0; n_synapses--) {
205  // Get next control word (auto incrementing)
206  uint32_t word = *words++;
207  int32_t weight_change = change_sign(synapse_row_sparse_weight(word));
208  uint32_t synapse_type = synapse_row_sparse_type(word,
210  uint32_t neuron_index = synapse_row_sparse_index(word,
212 
213  log_debug(" Adding weight change %d to post-neuron %u",
214  weight_change, neuron_index);
215 
216  // Get post event history of this neuron
217  post_event_history_t *history = &post_event_history[neuron_index];
218 
219  // Add a new history trace into the buffer of post synaptic events
220  post_events_add(history, weight_change, pre_spike, synapse_type);
221  }
222 }
223 
225  synapse_row_plastic_data_t *plastic_region_address,
226  synapse_row_fixed_part_t *fixed_region,
227  weight_t *ring_buffers, uint32_t time, uint32_t colour_delay,
228  bool *write_back) {
229 
230  // If the flag is set, this is neuromodulation
231  if (plastic_region_address->is_update) {
232  process_weight_update(plastic_region_address, fixed_region);
233  *write_back = false;
234  return true;
235  }
236 
237  // Extract separate arrays of plastic synapses (from plastic region),
238  // Control words (from fixed region) and number of plastic synapses
239  updatable_synapse_t *plastic_words = plastic_region_address->synapses;
240  const control_t *control_words = synapse_row_plastic_controls(fixed_region);
241  size_t n_plastic_synapses = synapse_row_num_plastic_controls(fixed_region);
242 
243  num_plastic_pre_synaptic_events += n_plastic_synapses;
244  uint32_t pre_spike = plastic_region_address->pre_spike;
245 
246  log_debug("Checking for weight changes for pre-neuron %u", pre_spike);
247 
248  // Loop through plastic synapses
249  for (; n_plastic_synapses > 0; n_plastic_synapses--) {
250  // Get next control word (auto incrementing)
251  uint32_t control_word = *control_words++;
252  uint32_t changed = 0;
253  plastic_words[0] = process_plastic_synapse(pre_spike, control_word,
254  ring_buffers, time, colour_delay, plastic_words[0], &changed);
255  plastic_words++;
256  if (changed) {
257  *write_back = true;
258  }
259  }
260  return true;
261 }
262 
263 input_t synapse_dynamics_get_intrinsic_bias(
264  UNUSED uint32_t time, UNUSED index_t neuron_index) {
265  return ZERO;
266 }
267 
270 }
271 
274 }
static weight_t * ring_buffers
The ring buffers to be used in the simulation.
Definition: c_main.c:118
static uint32_t time
Simulation time.
uint32_t synapse_delay_mask
The mask to get the synaptic delay from a "synapse".
Definition: local_only.c:71
uint32_t synapse_type_index_bits
The number of bits used by the synapse type and post-neuron index.
Definition: local_only.c:74
uint32_t synapse_index_bits
The number of bits used by just the post-neuron index.
Definition: local_only.c:77
#define ZERO
A REAL 0.0.
Definition: maths-util.h:123
REAL input_t
The type of an input.
static uint32_t n_neurons
The number of neurons on the core.
Definition: neuron.c:45
static uint32_t n_synapse_types
The number of synapse types.
Definition: neuron.c:51
static post_event_history_t * post_events_init_buffers(uint32_t n_neurons)
Initialise an array of post-synaptic event histories.
Definition: post_events.h:83
static void post_events_add(uint32_t time, post_event_history_t *events, post_trace_t trace)
Add a post-synaptic event to the history.
Definition: post_events.h:172
uint16_t synapse_type
The synapse type.
uint32_t count
Number of events stored.
int16_t weight_change
The amount to change the weight by (positive or negative)
uint32_t pre_spike
The pre-spike to look out for in doing the update.
post_trace_t traces[MAX_POST_SYNAPTIC_EVENTS]
Event traces.
Definition: post_events.h:45
Trace history of post-synaptic events.
Definition: post_events.h:39
API for synapse dynamics.
uint32_t synapse_dynamics_get_plastic_pre_synaptic_events(void)
Get the counters for plastic pre synaptic events based on (if the model was compiled with SYNAPSE_BEN...
bool synapse_dynamics_initialise(address_t address, uint32_t n_neurons, uint32_t n_synapse_types, uint32_t *ring_buffer_to_input_buffer_left_shifts)
Initialise the synapse dynamics.
void synapse_dynamics_process_post_synaptic_event(uint32_t time, index_t neuron_index)
Inform the synapses that the neuron fired.
uint32_t synapse_dynamics_get_plastic_saturation_count(void)
Get the number of ring buffer saturation events due to adding plastic weights.
static uint32_t plastic_saturation_count
Count of times that the plastic math became saturated.
plastic_synapse_t synapses[]
The per-synapse information.
static post_event_history_t * post_event_history
The history data of post-events.
static uint32_t num_plastic_pre_synaptic_events
Count of pre-synaptic events relevant to plastic processing.
static change_params * params
Parameters.
bool synapse_dynamics_process_plastic_synapses(synapse_row_plastic_data_t *plastic_region_address, synapse_row_fixed_part_t *fixed_region, weight_t *ring_buffers, uint32_t time, uint32_t colour_delay, bool *write_back)
Process the dynamics of the synapses.
The format of the plastic data region of a synaptic row.
uint32_t skipped_synapses
Definition: synapses.c:84
static size_t synapse_row_num_plastic_controls(const synapse_row_fixed_part_t *fixed)
Get the number of plastic controls in the row.
Definition: synapse_row.h:164
static index_t synapse_row_sparse_type(uint32_t x, uint32_t synapse_index_bits, uint32_t synapse_type_mask)
Get the type code.
Definition: synapse_row.h:201
static index_t synapse_row_get_ring_buffer_index_combined(uint32_t simulation_timestep, uint32_t combined_synapse_neuron_index, uint32_t synapse_type_index_bits, uint32_t synapse_delay_mask)
Get the index of the ring buffer for a given timestep and combined synapse type and neuron index (as ...
Definition: synapse_row.h:298
static index_t synapse_row_sparse_index(uint32_t x, uint32_t synapse_index_mask)
Get the index.
Definition: synapse_row.h:190
static control_t * synapse_row_plastic_controls(synapse_row_fixed_part_t *fixed)
Get the array of plastic controls in the row.
Definition: synapse_row.h:172
static index_t synapse_row_sparse_delay(uint32_t x, uint32_t synapse_type_index_bits, uint32_t synapse_delay_mask)
Get the delay from an encoded synapse descriptor.
Definition: synapse_row.h:222
static index_t synapse_row_sparse_type_index(uint32_t x, uint32_t synapse_type_index_mask)
Get the type and index.
Definition: synapse_row.h:211
uint16_t control_t
Define the type of the control data.
Definition: synapse_row.h:106
static weight_t synapse_row_sparse_weight(uint32_t x)
Get the weight from an encoded synapse descriptor.
Definition: synapse_row.h:230
The type of the fixed part of the row. The fixed-plastic part follows.
Definition: synapse_row.h:118
uint32_t synapse_index_mask
Mask to pick out the synapse index.
Definition: synapses.c:69
uint32_t synapse_type_index_mask
Mask to pick out the synapse type and index.
Definition: synapses.c:65
uint32_t synapse_type_mask
Mask to pick out the synapse type.
Definition: synapses.c:73
Operations on synapses.