diff --git a/neural_modelling/makefiles/neuron/IF_cond_alpha/Makefile b/neural_modelling/makefiles/neuron/IF_cond_alpha/Makefile new file mode 100644 index 0000000000..99f7033f24 --- /dev/null +++ b/neural_modelling/makefiles/neuron/IF_cond_alpha/Makefile @@ -0,0 +1,25 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +APP = $(notdir $(CURDIR)) + +NEURON_MODEL = $(NEURON_DIR)/neuron/models/neuron_model_lif_impl.c +NEURON_MODEL_H = $(NEURON_DIR)/neuron/models/neuron_model_lif_impl.h +INPUT_TYPE_H = $(NEURON_DIR)/neuron/input_types/input_type_conductance.h +NEURON_IMPL_H = $(NEURON_DIR)/neuron/implementations/neuron_impl_standard.h +THRESHOLD_TYPE_H = $(NEURON_DIR)/neuron/threshold_types/threshold_type_static.h +SYNAPSE_TYPE_H = $(NEURON_DIR)/neuron/synapse_types/synapse_types_alpha_impl.h +SYNAPSE_DYNAMICS = $(NEURON_DIR)/neuron/plasticity/synapse_dynamics_static_impl.c + +include ../neural_build.mk diff --git a/neural_modelling/src/common/send_mc.h b/neural_modelling/src/common/send_mc.h index 809b61e5de..91ed8325b2 100644 --- a/neural_modelling/src/common/send_mc.h +++ b/neural_modelling/src/common/send_mc.h @@ -32,10 +32,10 @@ static inline void wait_for_cc(void) { spin1_delay_us(1); n_loops++; } - if (!(cc[CC_TCR] & TX_NOT_FULL_MASK)) { - log_error("Couldn't send spike; TCR=0x%08x\n", cc[CC_TCR]); - rt_error(RTE_SWERR); - } +// if (!(cc[CC_TCR] & TX_NOT_FULL_MASK)) { +// log_error("Couldn't send spike; TCR=0x%08x\n", cc[CC_TCR]); +// rt_error(RTE_SWERR); +// } } //! \brief Perform direct spike sending with hardware for speed diff --git a/neural_modelling/src/delay_extension/delay_extension.h b/neural_modelling/src/delay_extension/delay_extension.h index e0975ee067..9a7d06ba95 100644 --- a/neural_modelling/src/delay_extension/delay_extension.h +++ b/neural_modelling/src/delay_extension/delay_extension.h @@ -24,6 +24,9 @@ #include +//! Constants +#define DELAY_STAGE_LENGTH 64 + //! region identifiers typedef enum region_identifiers { //! General simulation system control diff --git a/neural_modelling/src/neuron/additional_inputs/additional_input_ca2_adaptive_impl.h b/neural_modelling/src/neuron/additional_inputs/additional_input_ca2_adaptive_impl.h index 35253a4ee5..6cf5402dd1 100644 --- a/neural_modelling/src/neuron/additional_inputs/additional_input_ca2_adaptive_impl.h +++ b/neural_modelling/src/neuron/additional_inputs/additional_input_ca2_adaptive_impl.h @@ -76,7 +76,7 @@ static inline input_t additional_input_get_input_value_as_current( additional_input->i_ca2 *= additional_input->exp_tau_ca2; // Return the Ca2 - return -additional_input->i_ca2; + return additional_input->i_ca2; } //! \brief Notifies the additional input type that the neuron has spiked diff --git a/neural_modelling/src/neuron/c_main.c b/neural_modelling/src/neuron/c_main.c index 40a98fe808..2a6173ffe7 100644 --- a/neural_modelling/src/neuron/c_main.c +++ b/neural_modelling/src/neuron/c_main.c @@ -37,8 +37,14 @@ #include "c_main_common.h" #include "regions.h" #include "profile_tags.h" +//#include "spike_profiling.h" #include "spike_processing.h" +//struct spike_holder_t spike_counter; +//struct spike_holder_t spike_cache; +//struct spike_holder_t spike_counter_inh; +//struct spike_holder_t spike_cache_inh; + //! The combined provenance from synapses and neurons struct combined_provenance { struct neuron_provenance neuron_provenance; @@ -70,6 +76,7 @@ const struct common_priorities COMMON_PRIORITIES = { .timer = TIMER }; + //! From the regions, extract those that are neuron-specific const struct neuron_regions NEURON_REGIONS = { .core_params = CORE_PARAMS_REGION, @@ -153,9 +160,9 @@ static inline void process_ring_buffers(void) { neuron_transfer(&ring_buffers[first_index]); // Print the neuron inputs. - #if LOG_LEVEL >= LOG_DEBUG - neuron_print_inputs(); - #endif // LOG_LEVEL >= LOG_DEBUG +#if LOG_LEVEL >= LOG_DEBUG + neuron_print_inputs(); +#endif // LOG_LEVEL >= LOG_DEBUG } //! \brief Background activities called from timer @@ -179,6 +186,18 @@ void background_callback(uint timer_count, uint local_time) { //! executed since start of simulation //! \param[in] unused: unused parameter kept for API consistency void timer_callback(uint timer_count, UNUSED uint unused) { + +// // Get number of spikes in last tick, and reset spike counter +// spike_processing_get_and_reset_spikes_this_tick(); +// spike_processing_get_and_reset_dmas_this_tick(); +// spike_processing_get_and_reset_pipeline_restarts_this_tick(); +// +// // cache and flush spike counters +// spike_profiling_cache_and_flush_spike_holder(&spike_counter, +// &spike_cache); +// spike_profiling_cache_and_flush_spike_holder(&spike_counter_inh, +// &spike_cache_inh); + // Disable interrupts to stop DMAs and MC getting in the way of this bit uint32_t state = spin1_int_disable(); diff --git a/neural_modelling/src/neuron/c_main_synapses.c b/neural_modelling/src/neuron/c_main_synapses.c index 6ce1e3a754..5b34931d60 100644 --- a/neural_modelling/src/neuron/c_main_synapses.c +++ b/neural_modelling/src/neuron/c_main_synapses.c @@ -35,9 +35,20 @@ #include "c_main_synapse_common.h" #include "c_main_common.h" #include "spike_processing_fast.h" +#include "spike_profiling.h" #include "structural_plasticity/synaptogenesis_dynamics.h" #include +//! spike profiling +struct spike_holder_t spike_counter; +struct spike_holder_t spike_cache; +struct spike_holder_t spike_counter_inh; +struct spike_holder_t spike_cache_inh; + +//// FLUSH SPIKES ?? +//bool timer_callback_active = false; +//extern volatile bool dma_busy; + //! values for the priority for each callback typedef enum callback_priorities { MC = -1, DMA = -2, TIMER = 0, SDP = 0 @@ -129,6 +140,17 @@ void resume_callback(void) { //! \param[in] unused0: unused //! \param[in] unused1: unused void timer_callback(UNUSED uint unused0, UNUSED uint unused1) { + // Get number of spikes in last tick, and reset spike counter + spike_processing_get_and_reset_spikes_this_tick(); + spike_processing_get_and_reset_dmas_this_tick(); + spike_processing_get_and_reset_pipeline_restarts_this_tick(); + + // cache and flush spike counters + spike_profiling_cache_and_flush_spike_holder(&spike_counter, + &spike_cache); + spike_profiling_cache_and_flush_spike_holder(&spike_counter_inh, + &spike_cache_inh); + time++; if (simulation_is_finished()) { // Enter pause and resume state to avoid another tick diff --git a/neural_modelling/src/neuron/current_sources/current_source.h b/neural_modelling/src/neuron/current_sources/current_source.h index f1fd058dd5..60f067017c 100644 --- a/neural_modelling/src/neuron/current_sources/current_source.h +++ b/neural_modelling/src/neuron/current_sources/current_source.h @@ -58,7 +58,7 @@ SOMETIMES_UNUSED // Marked unused as only used sometimes static bool current_source_initialise(address_t cs_address, uint32_t n_neurons) { // Avoid the loops if no current sources #if !defined(_CURRENT_SOURCE_DC_H_) && !defined(_CURRENT_SOURCE_AC_H) && \ - !defined(_CURRENT_SOURCE_STEP_H_) && !defined(_CURRENT_SOURCE_NOISY_H_) + !defined(_CURRENT_SOURCE_STEP_H_) && !defined(_CURRENT_SOURCE_NOISY_H_) return true; #else @@ -150,7 +150,6 @@ static bool current_source_load_parameters(address_t cs_address) { // Avoid the loops if no current sources #if !defined(_CURRENT_SOURCE_DC_H_) && !defined(_CURRENT_SOURCE_AC_H) && \ !defined(_CURRENT_SOURCE_STEP_H_) && !defined(_CURRENT_SOURCE_NOISY_H_) - io_printf(IO_BUF, "no current sources defined \n"); return true; #else @@ -207,7 +206,7 @@ SOMETIMES_UNUSED // Marked unused as only used sometimes static inline REAL current_source_get_offset(uint32_t time, uint32_t neuron_index) { // Avoid the loops if no current sources defined #if !defined(_CURRENT_SOURCE_DC_H_) && !defined(_CURRENT_SOURCE_AC_H) && \ - !defined(_CURRENT_SOURCE_STEP_H_) && !defined(_CURRENT_SOURCE_NOISY_H_) + !defined(_CURRENT_SOURCE_STEP_H_) && !defined(_CURRENT_SOURCE_NOISY_H_) return ZERO; #else diff --git a/neural_modelling/src/neuron/implementations/neuron_impl_standard.h b/neural_modelling/src/neuron/implementations/neuron_impl_standard.h index c94bd25027..cfa866145c 100644 --- a/neural_modelling/src/neuron/implementations/neuron_impl_standard.h +++ b/neural_modelling/src/neuron/implementations/neuron_impl_standard.h @@ -164,7 +164,6 @@ SOMETIMES_UNUSED // Marked unused as only used sometimes static void neuron_impl_load_neuron_parameters( address_t address, uint32_t next, uint32_t n_neurons, address_t save_initial_state) { - // Read the number of steps per timestep n_steps_per_timestep = address[next++]; if (n_steps_per_timestep == 0) { @@ -252,6 +251,8 @@ static void neuron_impl_do_timestep_update( additional_input_t *additional_inputs = &additional_input_array[neuron_index]; synapse_types_t *the_synapse_type = &synapse_types_array[neuron_index]; +// bool spike = false; + // Loop however many times requested; do this in reverse for efficiency, // and because the index doesn't actually matter for (uint32_t i_step = n_steps_per_timestep; i_step > 0; i_step--) { @@ -265,26 +266,25 @@ static void neuron_impl_do_timestep_update( input_t inh_values[NUM_INHIBITORY_RECEPTORS]; input_t *inh_syn_values = synapse_types_get_inhibitory_input(inh_values, the_synapse_type); - // Call functions to obtain exc_input and inh_input input_t *exc_input_values = input_type_get_input_value( exc_syn_values, input_types, NUM_EXCITATORY_RECEPTORS); input_t *inh_input_values = input_type_get_input_value( inh_syn_values, input_types, NUM_INHIBITORY_RECEPTORS); - // Sum g_syn contributions from all receptors for recording - REAL total_exc = ZERO; - REAL total_inh = ZERO; - - for (int i = 0; i < NUM_EXCITATORY_RECEPTORS; i++) { - total_exc += exc_input_values[i]; - } - for (int i = 0; i < NUM_INHIBITORY_RECEPTORS; i++) { - total_inh += inh_input_values[i]; - } - // Do recording if on the first step if (i_step == n_steps_per_timestep) { + // Sum g_syn contributions from all receptors for recording + REAL total_exc = ZERO; + REAL total_inh = ZERO; + + for (int i = 0; i < NUM_EXCITATORY_RECEPTORS; i++) { + total_exc += exc_input_values[i]; + } + for (int i = 0; i < NUM_INHIBITORY_RECEPTORS; i++) { + total_inh += inh_input_values[i]; + } + neuron_recording_record_accum( V_RECORDING_INDEX, neuron_index, soma_voltage); neuron_recording_record_accum( @@ -319,6 +319,8 @@ static void neuron_impl_do_timestep_update( // If spike occurs, communicate to relevant parts of model if (spike_now) { +// spike = true; + // Call relevant model-based functions // Tell the neuron model neuron_model_has_spiked(this_neuron); @@ -340,6 +342,14 @@ static void neuron_impl_do_timestep_update( #if LOG_LEVEL >= LOG_DEBUG neuron_model_print_state_variables(this_neuron); #endif // LOG_LEVEL >= LOG_DEBUG + +// if (spike) { +// // Record the spike +// neuron_recording_record_bit(SPIKE_RECORDING_BITFIELD, neuron_index); +// +// // Send the spike +// send_spike(timer_count, time, neuron_index); +// } } } diff --git a/neural_modelling/src/neuron/input_types/input_type_conductance.h b/neural_modelling/src/neuron/input_types/input_type_conductance.h index a1854cbe69..53d7896bb9 100644 --- a/neural_modelling/src/neuron/input_types/input_type_conductance.h +++ b/neural_modelling/src/neuron/input_types/input_type_conductance.h @@ -20,6 +20,7 @@ #define _INPUT_TYPE_CONDUCTANCE_H_ #include "input_type.h" +//#include "round.h" //! Conductance input parameters struct input_type_params_t { @@ -59,7 +60,7 @@ static inline input_t *input_type_get_input_value( input_t *restrict value, UNUSED input_type_t *input_type, uint16_t num_receptors) { for (int i = 0; i < num_receptors; i++) { - value[i] = value[i] >> 10; + value[i] = value[i] >> 5; } return &value[0]; } @@ -74,8 +75,12 @@ static inline void input_type_convert_excitatory_input_to_current( input_t *restrict exc_input, const input_type_t *input_type, state_t membrane_voltage) { for (int i=0; i < NUM_EXCITATORY_RECEPTORS; i++) { + // accum = accum * (accum - accum) exc_input[i] = exc_input[i] * (input_type->V_rev_E - membrane_voltage); + // RTN accum +// exc_input[i] = MULT_ROUND_NEAREST_ACCUM(exc_input[i], +// (input_type->V_rev_E - membrane_voltage)); } } @@ -89,8 +94,12 @@ static inline void input_type_convert_inhibitory_input_to_current( input_t *restrict inh_input, const input_type_t *input_type, state_t membrane_voltage) { for (int i=0; i < NUM_INHIBITORY_RECEPTORS; i++) { + // accum = accum * (accum - accum) inh_input[i] = -inh_input[i] * (input_type->V_rev_I - membrane_voltage); + // RTN accum +// inh_input[i] = MULT_ROUND_NEAREST_ACCUM(-inh_input[i], +// (input_type->V_rev_I - membrane_voltage)); } } diff --git a/neural_modelling/src/neuron/models/neuron_model_izh_impl.h b/neural_modelling/src/neuron/models/neuron_model_izh_impl.h index 5b04806179..aa425cad01 100644 --- a/neural_modelling/src/neuron/models/neuron_model_izh_impl.h +++ b/neural_modelling/src/neuron/models/neuron_model_izh_impl.h @@ -178,7 +178,7 @@ static inline state_t neuron_model_state_update( } input_t input_this_timestep = total_exc - total_inh - + external_bias + neuron->I_offset + current_offset; + - external_bias + neuron->I_offset + current_offset; // the best AR update so far rk2_kernel_midpoint(neuron->this_h, neuron, input_this_timestep); diff --git a/neural_modelling/src/neuron/models/neuron_model_lif_impl.h b/neural_modelling/src/neuron/models/neuron_model_lif_impl.h index 80a249cd8f..ab77650afe 100644 --- a/neural_modelling/src/neuron/models/neuron_model_lif_impl.h +++ b/neural_modelling/src/neuron/models/neuron_model_lif_impl.h @@ -20,6 +20,7 @@ #define _NEURON_MODEL_LIF_CURR_IMPL_H_ #include "neuron_model.h" +#include "round.h" //! definition for LIF neuron parameters struct neuron_params_t { @@ -118,10 +119,16 @@ static inline void neuron_model_save_state(neuron_t *state, neuron_params_t *par //! \param[in] input_this_timestep: The input to apply static inline void lif_neuron_closed_form( neuron_t *neuron, REAL V_prev, input_t input_this_timestep) { - REAL alpha = input_this_timestep * neuron->R_membrane + neuron->V_rest; + // accum = accum * accum + accum +// REAL alpha = (input_this_timestep * neuron->R_membrane) + neuron->V_rest; + REAL alpha = MULT_ROUND_NEAREST_ACCUM( + input_this_timestep, neuron->R_membrane) + neuron->V_rest; // update membrane voltage - neuron->V_membrane = alpha - (neuron->exp_TC * (alpha - V_prev)); + // accum - (ufract * (accum - accum)) +// neuron->V_membrane = alpha - (neuron->exp_TC * (alpha - V_prev)); + neuron->V_membrane = alpha - MULT_ROUND_NEAREST_ACCUM( + neuron->exp_TC, (alpha - V_prev)); } //! \brief primary function called in timer loop after synaptic updates @@ -157,7 +164,7 @@ static inline state_t neuron_model_state_update( } // Get the input in nA input_t input_this_timestep = - total_exc - total_inh + external_bias + neuron->I_offset + current_offset; + total_exc - total_inh - external_bias + neuron->I_offset + current_offset; lif_neuron_closed_form( neuron, neuron->V_membrane, input_this_timestep); diff --git a/neural_modelling/src/neuron/population_table/population_table_binary_search_impl.c b/neural_modelling/src/neuron/population_table/population_table_binary_search_impl.c index 258b7336f7..a00237a239 100644 --- a/neural_modelling/src/neuron/population_table/population_table_binary_search_impl.c +++ b/neural_modelling/src/neuron/population_table/population_table_binary_search_impl.c @@ -155,7 +155,6 @@ bool population_table_load_bitfields(filter_region_t *filter_region) { return false; } uint32_t useful = !(filters[mp_i].merged || filters[mp_i].all_ones); - if (useful) { // Try to allocate all the bitfields for this entry uint32_t n_words = get_bit_field_size(filters[mp_i].n_atoms); @@ -337,5 +336,3 @@ bool population_table_get_next_address(spike_t *spike, pop_table_lookup_result_t return is_valid; } - -//! \} diff --git a/neural_modelling/src/neuron/spike_processing.c b/neural_modelling/src/neuron/spike_processing.c index c32b61f632..b4f634e21e 100644 --- a/neural_modelling/src/neuron/spike_processing.c +++ b/neural_modelling/src/neuron/spike_processing.c @@ -108,6 +108,19 @@ static uint32_t biggest_fill_size_of_input_buffer; //! end of a timer tick. static bool clear_input_buffers_of_late_packets; +//static uint32_t max_spikes_in_a_tick; +//static uint32_t max_dmas_in_a_tick; +//static uint32_t dma_complete_count; +//static uint32_t max_pipeline_restarts; +//static uint32_t spike_pipeline_deactivation_time = 0; +//static uint32_t timer_callback_completed = 0; +//static uint32_t spikes_this_time_step = 0; // needed because packets gets reset? +//static uint32_t dmas_this_time_step = 0; +//static uint32_t pipeline_restarts = 0; +// +//static uint32_t max_flushed_spikes = 0; +//static uint32_t total_flushed_spikes = 0; + //! the number of packets received this time step static struct { uint32_t time; @@ -300,6 +313,9 @@ static inline void start_dma_loop(void) { //! \param[in] unused: Only specified to match API static void multicast_packet_received_callback(uint key, UNUSED uint unused) { p_per_ts_struct.packets_this_time_step += 1; + // Increment the count of number of spikes received this tick by this core +// spikes_this_time_step++; +// log_debug("Received spike %x at %d, DMA Busy = %d", key, time, dma_busy); if (in_spikes_add_spike(key)) { start_dma_loop(); } @@ -310,6 +326,10 @@ static void multicast_packet_received_callback(uint key, UNUSED uint unused) { //! \param[in] payload: the payload of the packet. The count. static void multicast_packet_pl_received_callback(uint key, uint payload) { p_per_ts_struct.packets_this_time_step += 1; + // Increment the count of number of spikes received this tick by this core +// spikes_this_time_step++; +// log_debug("Received spike %x with payload %d at %d, DMA Busy = %d", +// key, payload, time, dma_busy); // cycle through the packet insertion bool added = false; @@ -329,6 +349,12 @@ static void dma_complete_callback(UNUSED uint unused, UNUSED uint tag) { // increment the dma complete count for provenance generation dma_complete_count++; +// log_debug("DMA transfer complete at time %u with tag %u", time, tag); +// +// // Increment the counter tracking the number of DMAs completed this +// // timestep on a particular core +// dmas_this_time_step++; + // Get pointer to current buffer uint32_t current_buffer_index = buffer_being_read; dma_buffer *current_buffer = &dma_buffers[current_buffer_index]; @@ -398,6 +424,9 @@ void user_event_callback(UNUSED uint unused0, UNUSED uint unused1) { dma_n_rewires = 0; dma_n_spikes = 0; + // Increment counter for spike processing pipeline restarts +// pipeline_restarts++; + if (buffer_being_read < N_DMA_BUFFERS) { // If the DMA buffer is full of valid data, attempt to reuse it on the // next data to be used, as this might be able to make use of the buffer @@ -441,8 +470,7 @@ bool spike_processing_initialise( // EXPORTED } } dma_busy = false; - clear_input_buffers_of_late_packets = - clear_input_buffers_of_late_packets_init; + clear_input_buffers_of_late_packets = clear_input_buffers_of_late_packets_init; next_buffer_to_fill = 0; buffer_being_read = N_DMA_BUFFERS; p_per_ts_region = packets_per_timestep_region; @@ -471,6 +499,13 @@ void spike_processing_store_provenance(struct spike_processing_provenance *prov) prov->n_rewires = n_successful_rewires; prov->n_packets_dropped_from_lateness = count_input_buffer_packets_late; prov->max_filled_input_buffer_size = biggest_fill_size_of_input_buffer; +// prov->max_spikes_in_a_tick = max_spikes_in_a_tick; +// prov->max_dmas_in_a_tick = max_dmas_in_a_tick; +// prov->max_pipeline_restarts = max_pipeline_restarts; +// prov->timer_callback_completed = timer_callback_completed; +// prov->spike_pipeline_deactivated = spike_pipeline_deactivation_time; +// prov->max_flushed_spikes = max_flushed_spikes; +// prov->total_flushed_spikes = total_flushed_spikes; } bool spike_processing_do_rewiring(int number_of_rewires) { @@ -482,3 +517,38 @@ bool spike_processing_do_rewiring(int number_of_rewires) { spin1_mode_restore(cpsr); return true; } + +//// Custom provenance from SpiNNCer +//void spike_processing_get_and_reset_spikes_this_tick(void ) { +// if (spikes_this_time_step > max_spikes_in_a_tick) { +// max_spikes_in_a_tick = spikes_this_time_step; +// } +// spikes_this_time_step = 0; +//} +// +//void spike_processing_get_and_reset_dmas_this_tick(void) { +// if (dmas_this_time_step > max_dmas_in_a_tick){ +// max_dmas_in_a_tick = dmas_this_time_step; +// } +// dmas_this_time_step = 0; +//} +// +//void spike_processing_get_and_reset_pipeline_restarts_this_tick(void) { +// if (pipeline_restarts > max_pipeline_restarts) { +// max_pipeline_restarts = pipeline_restarts; +// } +// pipeline_restarts = 0; +//} +// +//uint32_t spike_processing_get_pipeline_deactivation_time(){ +// return spike_pipeline_deactivation_time; +//} +// +//// FLUSH SPIKES +//uint32_t spike_processing_get_total_flushed_spikes(){ +// return total_flushed_spikes; +//} +// +//uint32_t spike_processing_get_max_flushed_spikes(){ +// return max_flushed_spikes; +//} diff --git a/neural_modelling/src/neuron/spike_processing.h b/neural_modelling/src/neuron/spike_processing.h index d8fddfcacb..a4ffc1f761 100644 --- a/neural_modelling/src/neuron/spike_processing.h +++ b/neural_modelling/src/neuron/spike_processing.h @@ -37,6 +37,21 @@ struct spike_processing_provenance { uint32_t n_packets_dropped_from_lateness; //! The maximum size of the input buffer uint32_t max_filled_input_buffer_size; +// //! SpiNNCer-related provenance +// //! The maximum number of spikes in a tick +// uint32_t max_spikes_in_a_tick; +// //! The maximum number of DMAs in a tick +// uint32_t max_dmas_in_a_tick; +// //! The maximum number of pipeline restarts +// uint32_t max_pipeline_restarts; +// //! Was the timer callback completed? +// uint32_t timer_callback_completed; +// //! Was the spike pipeline deactivated? +// uint32_t spike_pipeline_deactivated; +// //! The maximum number of flushed spikes in one step +// uint32_t max_flushed_spikes; +// //! Thet total number of flushed spikes +// uint32_t total_flushed_spikes; }; //! \brief Initialise the spike processing system @@ -69,4 +84,33 @@ bool spike_processing_do_rewiring(int number_of_rewires); //! \param[in] time: The current timestep void spike_processing_clear_input_buffer(timer_t time); + +//// Custom provenance from SpiNNCer +// +////! \brief get number of spikes received since last timer event +////! \return uint32_t number of spikes +//void spike_processing_get_and_reset_spikes_this_tick(void); +// +////! \brief get number of dmas completed since last timer event +////! \return uint32_t number of DMAs +//void spike_processing_get_and_reset_dmas_this_tick(void); +// +////! \brief get number of time pipeline was restarted since last timer event +////! \return uint32_t number of pipeline restarts +//void spike_processing_get_and_reset_pipeline_restarts_this_tick(void); +// +////! \brief get time from T1 clock at which spike pipeline completed +////! \return uint32_t pipeline deactivation time +//uint32_t spike_processing_get_pipeline_deactivation_time(); +// +//// FLUSH SPIKES +////! \brief returns the total unprocessed spikes from a simulation +////! \return total unprocessed spikes +//uint32_t spike_processing_get_total_flushed_spikes(); +// +////! \brief returns the maximum unprocessed spikes from a single +////! simulation timestep. +////! \return maximum unprocessed spikes from a single timestep. +//uint32_t spike_processing_get_max_flushed_spikes(); + #endif // _SPIKE_PROCESSING_H_ diff --git a/neural_modelling/src/neuron/spike_processing_fast.c b/neural_modelling/src/neuron/spike_processing_fast.c index 14e134f463..0024b1c818 100644 --- a/neural_modelling/src/neuron/spike_processing_fast.c +++ b/neural_modelling/src/neuron/spike_processing_fast.c @@ -114,6 +114,19 @@ static uint32_t earliest_spike_received_time = 0; //! The maximum number of spikes left at the end of a time step static uint32_t max_spikes_overflow = 0; +static uint32_t max_spikes_in_a_tick; +static uint32_t max_dmas_in_a_tick; +static uint32_t dma_complete_count; +static uint32_t max_pipeline_restarts; +static uint32_t spike_pipeline_deactivation_time = 0; +static uint32_t timer_callback_completed = 0; +static uint32_t spikes_this_time_step = 0; // needed because packets gets reset? +static uint32_t dmas_this_time_step = 0; +static uint32_t pipeline_restarts = 0; + +static uint32_t max_flushed_spikes = 0; +static uint32_t total_flushed_spikes = 0; + //! The number of packets received this time step for recording static struct { uint32_t time; @@ -656,4 +669,46 @@ void spike_processing_fast_store_provenance( prov->earliest_receive = earliest_spike_received_time; prov->latest_receive = latest_spike_received_time; prov->max_spikes_overflow = max_spikes_overflow; + prov->max_spikes_in_a_tick = max_spikes_in_a_tick; + prov->max_dmas_in_a_tick = max_dmas_in_a_tick; + prov->max_pipeline_restarts = max_pipeline_restarts; + prov->timer_callback_completed = timer_callback_completed; + prov->spike_pipeline_deactivated = spike_pipeline_deactivation_time; + prov->max_flushed_spikes = max_flushed_spikes; + prov->total_flushed_spikes = total_flushed_spikes; +} + +// Custom provenance from SpiNNCer +void spike_processing_get_and_reset_spikes_this_tick(void ) { + if (spikes_this_time_step > max_spikes_in_a_tick) { + max_spikes_in_a_tick = spikes_this_time_step; + } + spikes_this_time_step = 0; +} + +void spike_processing_get_and_reset_dmas_this_tick(void) { + if (dmas_this_time_step > max_dmas_in_a_tick){ + max_dmas_in_a_tick = dmas_this_time_step; + } + dmas_this_time_step = 0; +} + +void spike_processing_get_and_reset_pipeline_restarts_this_tick(void) { + if (pipeline_restarts > max_pipeline_restarts) { + max_pipeline_restarts = pipeline_restarts; + } + pipeline_restarts = 0; +} + +uint32_t spike_processing_get_pipeline_deactivation_time(){ + return spike_pipeline_deactivation_time; +} + +// FLUSH SPIKES +uint32_t spike_processing_get_total_flushed_spikes(){ + return total_flushed_spikes; +} + +uint32_t spike_processing_get_max_flushed_spikes(){ + return max_flushed_spikes; } diff --git a/neural_modelling/src/neuron/spike_processing_fast.h b/neural_modelling/src/neuron/spike_processing_fast.h index 16f656eb80..38d79255b1 100644 --- a/neural_modelling/src/neuron/spike_processing_fast.h +++ b/neural_modelling/src/neuron/spike_processing_fast.h @@ -79,6 +79,21 @@ struct spike_processing_fast_provenance { uint32_t latest_receive; //! The most spikes left at the end of any time step uint32_t max_spikes_overflow; + //! SpiNNCer-related provenance + //! The maximum number of spikes in a tick + uint32_t max_spikes_in_a_tick; + //! The maximum number of DMAs in a tick + uint32_t max_dmas_in_a_tick; + //! The maximum number of pipeline restarts + uint32_t max_pipeline_restarts; + //! Was the timer callback completed? + uint32_t timer_callback_completed; + //! Was the spike pipeline deactivated? + uint32_t spike_pipeline_deactivated; + //! The maximum number of flushed spikes in one step + uint32_t max_flushed_spikes; + //! Thet total number of flushed spikes + uint32_t total_flushed_spikes; }; //! \brief Set up spike processing @@ -112,4 +127,32 @@ void spike_processing_fast_time_step_loop(uint32_t time, uint32_t n_rewires); void spike_processing_fast_store_provenance( struct spike_processing_fast_provenance *prov); +// Custom provenance from SpiNNCer + +//! \brief get number of spikes received since last timer event +//! \return uint32_t number of spikes +void spike_processing_get_and_reset_spikes_this_tick(void); + +//! \brief get number of dmas completed since last timer event +//! \return uint32_t number of DMAs +void spike_processing_get_and_reset_dmas_this_tick(void); + +//! \brief get number of time pipeline was restarted since last timer event +//! \return uint32_t number of pipeline restarts +void spike_processing_get_and_reset_pipeline_restarts_this_tick(void); + +//! \brief get time from T1 clock at which spike pipeline completed +//! \return uint32_t pipeline deactivation time +uint32_t spike_processing_get_pipeline_deactivation_time(); + +// FLUSH SPIKES +//! \brief returns the total unprocessed spikes from a simulation +//! \return total unprocessed spikes +uint32_t spike_processing_get_total_flushed_spikes(); + +//! \brief returns the maximum unprocessed spikes from a single +//! simulation timestep. +//! \return maximum unprocessed spikes from a single timestep. +uint32_t spike_processing_get_max_flushed_spikes(); + #endif // _SPIKE_PROCESSING_FAST_H_ diff --git a/neural_modelling/src/neuron/spike_profiling.h b/neural_modelling/src/neuron/spike_profiling.h new file mode 100644 index 0000000000..225d479c7b --- /dev/null +++ b/neural_modelling/src/neuron/spike_profiling.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 The University of Manchester + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +typedef struct spike_holder_t { + uint8_t spikes_a; + uint8_t spikes_b; + uint8_t spikes_c; + uint8_t spikes_d; +} spike_holder_t; + +static inline void spike_profiling_cache_and_flush_spike_holder( + struct spike_holder_t* counter_spikes, + struct spike_holder_t* cache_levels) { + + cache_levels->spikes_a = counter_spikes->spikes_a; + cache_levels->spikes_b = counter_spikes->spikes_b; + cache_levels->spikes_c = counter_spikes->spikes_c; + cache_levels->spikes_d = counter_spikes->spikes_d; + + // zero counters + counter_spikes->spikes_a = 0; + counter_spikes->spikes_b = 0; + counter_spikes->spikes_c = 0; + counter_spikes->spikes_d = 0; +} + +static inline void spike_profiling_add_count(uint32_t row_length, + struct spike_holder_t* spike_counter) { + + uint32_t a = 0; + uint32_t b = 1; + uint32_t c = 5; + + if (row_length <= a) { + spike_counter->spikes_a++; + } else if (row_length > a && row_length <= b) { + spike_counter->spikes_b++; + } else if (row_length > b && row_length <= c) { + spike_counter->spikes_c++; + } else if (row_length > c) { + spike_counter->spikes_d++; + } +} + +static inline int32_t spike_profiling_get_spike_holder_as_int( + struct spike_holder_t spikes) { + + union { + int32_t inty; + struct spike_holder_t sh; + } x; + + x.sh = spikes; + + return x.inty; +} + +static inline accum spike_profiling_get_spike_holder_as_accum( + struct spike_holder_t spikes) { + union { + accum acc; + struct spike_holder_t sh; + } x; + x.sh = spikes; + + return x.acc; +} + +#if LOG_LEVEL >= LOG_DEBUG +static inline void spike_profiling_print_spikes_from_spike_holder( + struct spike_holder_t spikes_orig) { + io_printf(IO_BUF, "Spikes from input: a %u, b %u, c %u, d %u \n", + spikes_orig.spikes_a, spikes_orig.spikes_b, spikes_orig.spikes_c, + spikes_orig.spikes_d); +} + +static inline void spike_profiling_print_spikes_from_int(int32_t output) { + io_printf(IO_BUF, "Spikes from output: a %d, b %d, c %d, d %d \n", + (output & 0xFF), (output >> 8 & 0xFF), (output >> 16 & 0xFF), + (output >> 24 & 0xFF)); +} +#endif diff --git a/neural_modelling/src/neuron/synapse_row.h b/neural_modelling/src/neuron/synapse_row.h index a8ad535744..81e5ca1ff7 100644 --- a/neural_modelling/src/neuron/synapse_row.h +++ b/neural_modelling/src/neuron/synapse_row.h @@ -95,6 +95,15 @@ #define SYNAPSE_WEIGHT_BITS 16 #endif +//! how many bits the synapse delay will take +#ifndef SYNAPSE_DELAY_BITS +#define SYNAPSE_DELAY_BITS 6 +#endif + +// Create some masks based on the number of bits +//! the mask for the synapse delay in the row +#define SYNAPSE_DELAY_MASK ((1 << SYNAPSE_DELAY_BITS) - 1) + #ifdef SYNAPSE_WEIGHTS_SIGNED //! Define the type of the weights typedef __int_t(SYNAPSE_WEIGHT_BITS) weight_t; diff --git a/neural_modelling/src/neuron/synapse_types/exp_synapse_utils.h b/neural_modelling/src/neuron/synapse_types/exp_synapse_utils.h index b5e8b5ef58..4ff7cbb181 100644 --- a/neural_modelling/src/neuron/synapse_types/exp_synapse_utils.h +++ b/neural_modelling/src/neuron/synapse_types/exp_synapse_utils.h @@ -21,6 +21,7 @@ #include #include +//#include "round.h" //! The type of exponential decay parameters typedef struct exp_params_t { @@ -61,6 +62,9 @@ static inline void exp_shaping(exp_state_t *exp_param) { // decay value according to decay constant exp_param->synaptic_input_value = decay_s1615(exp_param->synaptic_input_value, exp_param->decay); +// exp_param->synaptic_input_value = +// MULT_ROUND_NEAREST_ACCUM(exp_param->synaptic_input_value, +// exp_param->decay); } //! \brief helper function to add input for a given timer period to a given @@ -70,4 +74,6 @@ static inline void exp_shaping(exp_state_t *exp_param) { static inline void add_input_exp(exp_state_t *parameter, input_t input) { parameter->synaptic_input_value = parameter->synaptic_input_value + decay_s1615(input, parameter->init); +// parameter->synaptic_input_value = parameter->synaptic_input_value + +// MULT_ROUND_NEAREST_ACCUM(input, parameter->init); } diff --git a/neural_modelling/src/neuron/synapse_types/synapse_types_alpha_impl.h b/neural_modelling/src/neuron/synapse_types/synapse_types_alpha_impl.h index 708b52586e..0134c2af07 100644 --- a/neural_modelling/src/neuron/synapse_types/synapse_types_alpha_impl.h +++ b/neural_modelling/src/neuron/synapse_types/synapse_types_alpha_impl.h @@ -144,9 +144,9 @@ static inline void add_input_alpha(alpha_state_t *a_params, input_t input) { a_params->exp_buff = decay_s1615(a_params->exp_buff, a_params->decay) + ONE; - a_params->lin_buff = - (a_params->lin_buff + (input * a_params->dt_divided_by_tau_sqr)) - * (ONE - kdivk(ONE, a_params->exp_buff)); + REAL exp_temp = ONE - kdivk(ONE, a_params->exp_buff); + a_params->lin_buff = (a_params->lin_buff + ( + input * a_params->dt_divided_by_tau_sqr)) * exp_temp; } //! \brief adds the inputs for a give timer period to a given neuron that is @@ -221,7 +221,7 @@ static inline const char *synapse_types_get_type_char( //! \param[in] parameters: the pointer to the parameters to print static inline void synapse_types_print_input( synapse_types_t *parameters) { - io_printf(IO_BUF, "%12.6k - %12.6k", + log_debug("%12.6k - %12.6k", parameters->exc.lin_buff * parameters->exc.exp_buff, parameters->inh.lin_buff * parameters->inh.exp_buff); } diff --git a/spynnaker/pyNN/external_devices_models/external_device_lif_control.py b/spynnaker/pyNN/external_devices_models/external_device_lif_control.py index c338e7881a..f772370c43 100644 --- a/spynnaker/pyNN/external_devices_models/external_device_lif_control.py +++ b/spynnaker/pyNN/external_devices_models/external_device_lif_control.py @@ -99,7 +99,7 @@ def create_vertex( self, n_neurons, label, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, n_steps_per_timestep, drop_late_spikes, splitter, seed, - n_colour_bits): + n_colour_bits, rb_left_shifts): if n_neurons != len(self._devices): raise ConfigurationException( "Number of neurons does not match number of " @@ -110,4 +110,4 @@ def create_vertex( self._devices, self._create_edges, max_atoms, self._model, self, self._translator, spikes_per_second, label, ring_buffer_sigma, incoming_spike_buffer_size, drop_late_spikes, splitter, seed, - n_colour_bits) + n_colour_bits, rb_left_shifts) diff --git a/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py b/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py index aa5220f194..06c6464e4f 100644 --- a/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py +++ b/spynnaker/pyNN/external_devices_models/external_device_lif_control_vertex.py @@ -44,7 +44,7 @@ def __init__( pynn_model, translator=None, spikes_per_second=None, label=None, ring_buffer_sigma=None, incoming_spike_buffer_size=None, drop_late_spikes=None, splitter=None, seed=None, - n_colour_bits=None): + n_colour_bits=None, rb_left_shifts=None): """ :param list(AbstractMulticastControllableDevice) devices: The AbstractMulticastControllableDevice instances to be controlled @@ -73,7 +73,7 @@ def __init__( len(devices), label, max_atoms_per_core, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, neuron_impl, pynn_model, drop_late_spikes, splitter, seed, - n_colour_bits) + n_colour_bits, rb_left_shifts) if not devices: raise ConfigurationException("No devices specified") diff --git a/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_fixed.py b/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_fixed.py index f4488e7071..34245b7a78 100644 --- a/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_fixed.py +++ b/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_fixed.py @@ -19,6 +19,7 @@ from pacman.model.partitioner_splitters import AbstractSplitterCommon from pacman.utilities.algorithm_utilities\ .partition_algorithm_utilities import get_multidimensional_slices +from pacman.utilities.utility_calls import get_n_bits_for_fields from spynnaker.pyNN.models.neuron import ( AbstractPopulationVertex, PopulationMachineVertex, PopulationMachineLocalOnlyCombinedVertex, LocalOnlyProvenance) @@ -35,6 +36,8 @@ from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex from spynnaker.pyNN.models.neuron.synaptic_matrices import SynapticMatrices from spynnaker.pyNN.models.neuron.neuron_data import NeuronData +from spynnaker.pyNN.utilities.utility_calls import get_n_bits +from spynnaker.pyNN.exceptions import SynapticConfigurationException from .abstract_spynnaker_splitter_delay import AbstractSpynnakerSplitterDelay # The maximum number of bits for the ring buffer index that are likely to @@ -76,13 +79,46 @@ def set_governed_app_vertex(self, app_vertex): @overrides(AbstractSplitterCommon.create_machine_vertices) def create_machine_vertices(self, chip_counter): app_vertex = self.governed_app_vertex + + # Do some checks to make sure everything is likely to fit + field_sizes = [ + min(max_atoms, n) for max_atoms, n in zip( + app_vertex.get_max_atoms_per_dimension_per_core(), + app_vertex.atoms_shape)] + n_atom_bits = get_n_bits_for_fields(field_sizes) + n_synapse_types = app_vertex.neuron_impl.get_n_synapse_types() + if (n_atom_bits + get_n_bits(n_synapse_types) + + get_n_bits(self.max_support_delay())) > MAX_RING_BUFFER_BITS: + raise SynapticConfigurationException( + "The combination of the number of neurons per core ({}), " + "the number of synapse types ({}), and the maximum delay per " + "core ({}) will require too much DTCM. Please reduce one or " + "more of these values.".format( + field_sizes, n_synapse_types, self.max_support_delay())) + app_vertex.synapse_recorder.add_region_offset( len(app_vertex.neuron_recorder.get_recordable_variables())) max_atoms_per_core = min( app_vertex.get_max_atoms_per_core(), app_vertex.n_atoms) - ring_buffer_shifts = app_vertex.get_ring_buffer_shifts() + ring_buffer_shifts = None + app_vertex = self._governed_app_vertex + if (hasattr(app_vertex, "rb_left_shifts") and + app_vertex.rb_left_shifts is not None): + print("=" * 80) + print("Using given values for RB left shifts.") + ring_buffer_shifts = app_vertex.rb_left_shifts + print("RB left shifts for {:20}".format(app_vertex.label), + "=", ring_buffer_shifts) + print("-" * 80) + else: + print("=" * 80) + print("Computing RB left shifts for", app_vertex.label) + ring_buffer_shifts = app_vertex.get_ring_buffer_shifts() + print("RB left shifts for {:20}".format(app_vertex.label), + "=", ring_buffer_shifts) + weight_scales = app_vertex.get_weight_scales(ring_buffer_shifts) all_syn_block_sz = app_vertex.get_synapses_size( max_atoms_per_core) diff --git a/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_neurons_synapses.py b/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_neurons_synapses.py index f0b54bbcba..90748d0e82 100644 --- a/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_neurons_synapses.py +++ b/spynnaker/pyNN/extra_algorithms/splitter_components/splitter_abstract_pop_vertex_neurons_synapses.py @@ -209,7 +209,22 @@ def create_machine_vertices(self, chip_counter): app_vertex.get_max_atoms_per_core(), app_vertex.n_atoms) # Work out the ring buffer shifts based on all incoming things - rb_shifts = app_vertex.get_ring_buffer_shifts() + rb_shifts = None + if (hasattr(app_vertex, "rb_left_shifts") and + app_vertex.rb_left_shifts is not None): + print("=" * 80) + print("Using given values for RB left shifts.") + rb_shifts = app_vertex.rb_left_shifts + print("RB left shifts for {:20}".format(app_vertex.label), + "=", rb_shifts) + print("-" * 80) + else: + print("=" * 80) + print("Computing RB left shifts for", app_vertex.label) + rb_shifts = app_vertex.get_ring_buffer_shifts() + print("RB left shifts for {:20}".format(app_vertex.label), + "=", rb_shifts) + weight_scales = app_vertex.get_weight_scales(rb_shifts) # We add the SDRAM edge SDRAM to the neuron resources so it is diff --git a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py index 9ada2fa356..e0a8346846 100644 --- a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py +++ b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py @@ -165,6 +165,7 @@ class AbstractPopulationVertex( "__ring_buffer_sigma", "__spikes_per_second", "__drop_late_spikes", + "__rb_left_shifts", "__incoming_projections", "__incoming_poisson_projections", "__synapse_dynamics", @@ -205,7 +206,7 @@ def __init__( self, n_neurons, label, max_atoms_per_core, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, neuron_impl, pynn_model, drop_late_spikes, splitter, seed, - n_colour_bits): + n_colour_bits, rb_left_shifts): """ :param int n_neurons: The number of neurons in the population :param str label: The label on the population @@ -260,6 +261,8 @@ def __init__( self.__drop_late_spikes = get_config_bool( "Simulation", "drop_late_spikes") + self.__rb_left_shifts = rb_left_shifts + self.__neuron_impl = neuron_impl self.__pynn_model = pynn_model self.__parameters = RangeDictionary(n_neurons) @@ -556,6 +559,16 @@ def drop_late_spikes(self): """ return self.__drop_late_spikes + @property + def rb_left_shifts(self): + """ ring buffer left shifts (for use by user) + :rtype: bool + """ + return self.__rb_left_shifts + + def set_rb_left_shifts(self, rb_left_shifts): + self.__rb_left_shifts = rb_left_shifts + def get_sdram_usage_for_core_neuron_params(self, n_atoms): """ :param int n_atoms: The number of atoms per core @@ -1074,7 +1087,7 @@ def get_ring_buffer_shifts(self): # Convert these to powers; we could use int.bit_length() for this if # they were integers, but they aren't... max_weight_powers = ( - 0 if w <= 0 else int(math.ceil(max(0, math.log2(w)))) + 0 if w <= 1 else int(math.ceil(max(0, math.log2(w)))) for w in max_weights) # If 2^max_weight_power equals the max weight, we have to add another @@ -1593,6 +1606,7 @@ class _Stats(object): "biggest_weight", "rate_stats", "steps_per_second", + "min_max_weight", "default_spikes_per_second", "ring_buffer_sigma" ] @@ -1613,6 +1627,7 @@ def __init__( self.steps_per_second = ( SpynnakerDataView.get_simulation_time_step_per_s()) + self.min_max_weight = numpy.ones(n_synapse_types) * 2 ** 32 self.default_spikes_per_second = default_spikes_per_second self.ring_buffer_sigma = ring_buffer_sigma @@ -1666,6 +1681,7 @@ def __add_details( self, proj, s_type, n_conns, w_mean, w_var, w_max, d_var): self.running_totals[s_type].add_items( w_mean * self.w_scale, w_var * self.w_scale_sq, n_conns) + self.min_max_weight[s_type] = min(self.min_max_weight[s_type], w_max) self.biggest_weight[s_type] = max( self.biggest_weight[s_type], w_max * self.w_scale) self.delay_running_totals[s_type].add_items(0.0, d_var, n_conns) @@ -1689,14 +1705,24 @@ def __pre_spike_stats(self, proj): def get_max_weight(self, s_type): if self.delay_running_totals[s_type].variance == 0.0: - return max(self.total_weights[s_type], self.biggest_weight[s_type]) + w_max = max(self.total_weights[s_type], + self.biggest_weight[s_type]) + else: + stats = self.running_totals[s_type] + rates = self.rate_stats[s_type] + # pylint: disable=protected-access + w_max = AbstractPopulationVertex._ring_buffer_expected_upper_bound( + stats.mean, stats.standard_deviation, rates.mean, + stats.n_items, self.ring_buffer_sigma) + w_max = min(w_max, self.total_weights[s_type]) + w_max = max(w_max, self.biggest_weight[s_type]) + + # This is to deal with very small weights that are floored to zero + if self.min_max_weight[s_type] != 0: + mmw = 2**math.floor(math.log(self.min_max_weight[s_type], 2)) + else: # if it is zero then can't take logs... + small = 1.0 / 65536.0 + mmw = 2**math.floor(math.log(small, 2)) + w_max = min(mmw * 2 ** 15, w_max) - stats = self.running_totals[s_type] - rates = self.rate_stats[s_type] - # pylint: disable=protected-access - w_max = AbstractPopulationVertex._ring_buffer_expected_upper_bound( - stats.mean, stats.standard_deviation, rates.mean, - stats.n_items, self.ring_buffer_sigma) - w_max = min(w_max, self.total_weights[s_type]) - w_max = max(w_max, self.biggest_weight[s_type]) return w_max diff --git a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py index 79d7b41741..375d2fa219 100644 --- a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py +++ b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model.py @@ -24,7 +24,8 @@ _population_parameters = { "spikes_per_second": None, "ring_buffer_sigma": None, "incoming_spike_buffer_size": None, "drop_late_spikes": None, - "splitter": None, "seed": None, "n_colour_bits": None + "splitter": None, "seed": None, "n_colour_bits": None, + "rb_left_shifts": None } @@ -48,7 +49,7 @@ def _model(self): def create_vertex( self, n_neurons, label, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, drop_late_spikes, splitter, seed, - n_colour_bits): + n_colour_bits, rb_left_shifts): """ :param float spikes_per_second: :param float ring_buffer_sigma: @@ -59,13 +60,14 @@ def create_vertex( ~pacman.model.partitioner_splitters.AbstractSplitterCommon or None :param float seed: :param int n_colour_bits: + :param rb_left_shifts: """ # pylint: disable=arguments-differ max_atoms = self.get_model_max_atoms_per_dimension_per_core() return AbstractPopulationVertex( n_neurons, label, max_atoms, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, self.__model, self, drop_late_spikes, - splitter, seed, n_colour_bits) + splitter, seed, n_colour_bits, rb_left_shifts) @property @overrides(AbstractPyNNModel.name) diff --git a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py index bb64a9691d..0c26342a76 100644 --- a/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py +++ b/spynnaker/pyNN/models/neuron/abstract_pynn_neuron_model_standard.py @@ -58,7 +58,7 @@ def create_vertex( self, n_neurons, label, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, n_steps_per_timestep, drop_late_spikes, splitter, seed, - n_colour_bits): + n_colour_bits, rb_left_shifts): """ :param int n_steps_per_timestep: """ @@ -67,4 +67,4 @@ def create_vertex( return super().create_vertex( n_neurons, label, spikes_per_second, ring_buffer_sigma, incoming_spike_buffer_size, drop_late_spikes, - splitter, seed, n_colour_bits) + splitter, seed, n_colour_bits, rb_left_shifts) diff --git a/spynnaker/pyNN/models/neuron/builds/if_cond_alpha.py b/spynnaker/pyNN/models/neuron/builds/if_cond_alpha.py index 62c3dd89a8..727f4e2262 100644 --- a/spynnaker/pyNN/models/neuron/builds/if_cond_alpha.py +++ b/spynnaker/pyNN/models/neuron/builds/if_cond_alpha.py @@ -12,25 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -from spynnaker.pyNN.exceptions import SpynnakerException +from spynnaker.pyNN.models.neuron import AbstractPyNNNeuronModelStandard from spynnaker.pyNN.models.defaults import defaults, default_initial_values +from spynnaker.pyNN.models.neuron.neuron_models import ( + NeuronModelLeakyIntegrateAndFire) +from spynnaker.pyNN.models.neuron.synapse_types import SynapseTypeAlpha +from spynnaker.pyNN.models.neuron.input_types import InputTypeConductance +from spynnaker.pyNN.models.neuron.threshold_types import ThresholdTypeStatic @defaults -class IFCondAlpha(object): +class IFCondAlpha(AbstractPyNNNeuronModelStandard): """ Leaky integrate and fire neuron with an alpha-shaped current input. - .. warning:: - Not currently supported by the tool chain. + TODO: add parameter documentation + """ + __slots__ = [] # noinspection PyPep8Naming - @default_initial_values({"v", "gsyn_exc", "gsyn_inh"}) + @default_initial_values({"v", "exc_response", + "exc_exp_response", "inh_response", + "inh_exp_response"}) def __init__( self, tau_m=20, cm=1.0, e_rev_E=0.0, e_rev_I=-70.0, v_rest=-65.0, v_reset=-65.0, v_thresh=-50.0, tau_syn_E=0.3, tau_syn_I=0.5, - tau_refrac=0.1, i_offset=0, v=-65.0, gsyn_exc=0.0, gsyn_inh=0.0): - # pylint: disable=too-many-locals, unused-argument - raise SpynnakerException( - "This neuron model is currently not supported by the tool chain") + tau_refrac=0.1, i_offset=0, v=-65.0, + exc_response=0.0, exc_exp_response=0.0, inh_response=0.0, + inh_exp_response=0.0): + # pylint: disable=too-many-arguments, too-many-locals, unused-argument + neuron_model = NeuronModelLeakyIntegrateAndFire( + v, v_rest, tau_m, cm, i_offset, v_reset, tau_refrac) + + synapse_type = SynapseTypeAlpha( + exc_response=exc_response, exc_exp_response=exc_exp_response, + tau_syn_E=tau_syn_E, inh_response=inh_response, + inh_exp_response=inh_exp_response, tau_syn_I=tau_syn_I) + + input_type = InputTypeConductance(e_rev_E, e_rev_I) + threshold_type = ThresholdTypeStatic(v_thresh) + + super(IFCondAlpha, self).__init__( + model_name="IF_cond_alpha", binary="IF_cond_alpha.aplx", + neuron_model=neuron_model, input_type=input_type, + synapse_type=synapse_type, threshold_type=threshold_type) diff --git a/spynnaker/pyNN/models/neuron/input_types/input_type_conductance.py b/spynnaker/pyNN/models/neuron/input_types/input_type_conductance.py index 3efe45dddc..05ab73134e 100644 --- a/spynnaker/pyNN/models/neuron/input_types/input_type_conductance.py +++ b/spynnaker/pyNN/models/neuron/input_types/input_type_conductance.py @@ -58,7 +58,8 @@ def add_state_variables(self, state_variables): @overrides(AbstractInputType.get_global_weight_scale) def get_global_weight_scale(self): - return 1024.0 + # IMPLICIT WEIGHT SCALING -- the default in main branch is 2**10 + return float(2**5) @property def e_rev_E(self): diff --git a/spynnaker/pyNN/models/neuron/population_machine_vertex.py b/spynnaker/pyNN/models/neuron/population_machine_vertex.py index 5033f72e79..b15fbb0ff0 100644 --- a/spynnaker/pyNN/models/neuron/population_machine_vertex.py +++ b/spynnaker/pyNN/models/neuron/population_machine_vertex.py @@ -42,6 +42,20 @@ class SpikeProcessingProvenance(ctypes.LittleEndianStructure): ("n_late_packets", ctypes.c_uint32), # The maximum size of the spike input buffer during simulation ("max_size_input_buffer", ctypes.c_uint32) + # # Custom provenance from SpiNNCer - max spikes in a tick + # ("max_spikes_in_a_tick", ctypes.c_uint32), + # # max dmas in a tick + # ("max_dmas_in_a_tick", ctypes.c_uint32), + # # max pipeline restarts + # ("max_pipeline_restarts", ctypes.c_uint32), + # # timer callback completed? + # ("timer_callback_completed", ctypes.c_uint32), + # # spikes pipeline activated? + # ("spikes_pipeline_activated", ctypes.c_uint32), + # # Max flushed spikes in a timestep + # ("max_flushed_spikes", ctypes.c_uint32), + # # Total flushed spikes + # ("total_flushed_spikes", ctypes.c_uint32) ] N_ITEMS = len(_fields_) @@ -91,6 +105,15 @@ class PopulationMachineVertex( MAX_FILLED_SIZE_OF_INPUT_BUFFER_NAME = "Max_filled_size_input_buffer" BACKGROUND_OVERLOADS_NAME = "Times_the_background_queue_overloaded" BACKGROUND_MAX_QUEUED_NAME = "Max_backgrounds_queued" + # # Custom provenance from SpiNNCer + # MAX_SPIKES_IN_A_TICK = "Maximum number of spikes in a timer tick" + # MAX_DMAS_IN_A_TICK = "Maximum number of DMAs in a timer tick" + # MAX_PIPELINE_RESTARTS = "Maximum pipeline restarts" + # TIMER_CALLBACK_COMPLETED = "Was the timer callback completed?" + # SPIKES_PIPELINE_ACTIVATED = "Was the spikes pipeline activated?" + # # Flushed spikes + # MAX_FLUSHED_SPIKES = "Maximum number of spikes flushed in a timer tick" + # TOTAL_FLUSHED_SPIKES = "Total number of spikes flushed" class REGIONS(Enum): """ @@ -390,6 +413,50 @@ def _parse_spike_processing_provenance( x, y, p, self.MAX_FILLED_SIZE_OF_INPUT_BUFFER_NAME, prov.max_size_input_buffer) + # # SpiNNCer + # db.insert_core( + # x, y, p, self.MAX_SPIKES_IN_A_TICK, + # prov.max_spikes_in_a_tick) + # if prov.max_spikes_in_a_tick > 200: + # db.insert_report( + # f"Max number of spikes for {label} was " + # f"{prov.max_spikes_in_a_tick}. Empirically, we " + # f"can deal with ~200 for real time performance using a " + # f"1.0 ms timestep.") + # + # db.insert_core( + # x, y, p, self.MAX_DMAS_IN_A_TICK, + # prov.max_dmas_in_a_tick) + # + # db.insert_core( + # x, y, p, self.MAX_PIPELINE_RESTARTS, + # prov.max_pipeline_restarts) + # + # db.insert_core( + # x, y, p, self.TIMER_CALLBACK_COMPLETED, + # prov.timer_callback_completed) + # + # db.insert_core( + # x, y, p, self.SPIKES_PIPELINE_ACTIVATED, + # prov.spikes_pipeline_activated) + # + # # FLUSHED SPIKES + # db.insert_core( + # x, y, p, self.MAX_FLUSHED_SPIKES, + # prov.max_flushed_spikes) + # if prov.max_flushed_spikes > 0: + # db.insert_report( + # f"Max number of flushed spikes for {label} was " + # f"was {prov.max_flushed_spikes}.") + # + # db.insert_core( + # x, y, p, self.TOTAL_FLUSHED_SPIKES, + # prov.total_flushed_spikes) + # if prov.total_flushed_spikes > 0: + # db.insert_report( + # f"Total number of flushed spikes for {label} was " + # f"{prov.total_flushed_spikes}.") + @overrides(PopulationMachineNeurons.set_do_neuron_regeneration) def set_do_neuron_regeneration(self): self.__regenerate_neuron_data = True diff --git a/spynnaker/pyNN/models/neuron/population_synapses_machine_vertex_common.py b/spynnaker/pyNN/models/neuron/population_synapses_machine_vertex_common.py index 10aa023823..567f4924d7 100644 --- a/spynnaker/pyNN/models/neuron/population_synapses_machine_vertex_common.py +++ b/spynnaker/pyNN/models/neuron/population_synapses_machine_vertex_common.py @@ -67,7 +67,21 @@ class SpikeProcessingFastProvenance(ctypes.LittleEndianStructure): # The latest time a spike was received ("latest_receive", ctypes.c_uint32), # The maximum overflow of spikes in a time step - ("max_spikes_overflow", ctypes.c_uint32) + ("max_spikes_overflow", ctypes.c_uint32), + # Custom provenance from SpiNNCer - max spikes in a tick + ("max_spikes_in_a_tick", ctypes.c_uint32), + # max dmas in a tick + ("max_dmas_in_a_tick", ctypes.c_uint32), + # max pipeline restarts + ("max_pipeline_restarts", ctypes.c_uint32), + # timer callback completed? + ("timer_callback_completed", ctypes.c_uint32), + # spikes pipeline activated? + ("spikes_pipeline_activated", ctypes.c_uint32), + # Max flushed spikes in a timestep + ("max_flushed_spikes", ctypes.c_uint32), + # Total flushed spikes + ("total_flushed_spikes", ctypes.c_uint32) ] N_ITEMS = len(_fields_) @@ -95,6 +109,16 @@ class PopulationSynapsesMachineVertexCommon( LATEST_RECEIVE = "Latest_receive_time" MAX_SPIKE_OVERFLOW = "Max_spike_overflow_in_time_step" + # Custom provenance from SpiNNCer + MAX_SPIKES_IN_A_TICK = "Maximum number of spikes in a timer tick" + MAX_DMAS_IN_A_TICK = "Maximum number of DMAs in a timer tick" + MAX_PIPELINE_RESTARTS = "Maximum pipeline restarts" + TIMER_CALLBACK_COMPLETED = "Was the timer callback completed?" + SPIKES_PIPELINE_ACTIVATED = "Was the spikes pipeline activated?" + # Flushed spikes + MAX_FLUSHED_SPIKES = "Maximum number of spikes flushed in a timer tick" + TOTAL_FLUSHED_SPIKES = "Total number of spikes flushed" + __slots__ = [ "__sdram_partition", "__neuron_vertex", @@ -374,3 +398,47 @@ def _parse_spike_processing_fast_provenance( x, y, p, self.LATEST_RECEIVE, prov.latest_receive) db.insert_core( x, y, p, self.MAX_SPIKE_OVERFLOW, prov.max_spikes_overflow) + + # SpiNNCer + db.insert_core( + x, y, p, self.MAX_SPIKES_IN_A_TICK, + prov.max_spikes_in_a_tick) + if prov.max_spikes_in_a_tick > 200: + db.insert_report( + f"Max number of spikes for {label} was " + f"{prov.max_spikes_in_a_tick}. Empirically, we " + f"can deal with ~200 for real time performance using a " + f"1.0 ms timestep.") + + db.insert_core( + x, y, p, self.MAX_DMAS_IN_A_TICK, + prov.max_dmas_in_a_tick) + + db.insert_core( + x, y, p, self.MAX_PIPELINE_RESTARTS, + prov.max_pipeline_restarts) + + db.insert_core( + x, y, p, self.TIMER_CALLBACK_COMPLETED, + prov.timer_callback_completed) + + db.insert_core( + x, y, p, self.SPIKES_PIPELINE_ACTIVATED, + prov.spikes_pipeline_activated) + + # FLUSHED SPIKES + db.insert_core( + x, y, p, self.MAX_FLUSHED_SPIKES, + prov.max_flushed_spikes) + if prov.max_flushed_spikes > 0: + db.insert_report( + f"Max number of flushed spikes for {label} was " + f"was {prov.max_flushed_spikes}.") + + db.insert_core( + x, y, p, self.TOTAL_FLUSHED_SPIKES, + prov.total_flushed_spikes) + if prov.total_flushed_spikes > 0: + db.insert_report( + f"Total number of flushed spikes for {label} was " + f"{prov.total_flushed_spikes}.") diff --git a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py index de761eee88..f7775641e8 100644 --- a/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py +++ b/spynnaker/pyNN/models/neuron/synapse_dynamics/synapse_dynamics_static.py @@ -22,6 +22,7 @@ from .synapse_dynamics_neuromodulation import SynapseDynamicsNeuromodulation from spynnaker.pyNN.exceptions import SynapticConfigurationException from spynnaker.pyNN.utilities.utility_calls import get_n_bits +from spynnaker.pyNN.utilities.constants import DELAY_MASK from spinn_front_end_common.utilities.constants import BYTES_PER_WORD @@ -106,7 +107,7 @@ def get_static_synaptic_data( fixed_fixed = ( ((numpy.rint(numpy.abs(connections["weight"])).astype("uint32") & 0xFFFF) << 16) | - (connections["delay"].astype("uint32") << + ((connections["delay"].astype("uint32") & DELAY_MASK) << (n_neuron_id_bits + n_synapse_type_bits)) | (connections["synapse_type"].astype( "uint32") << n_neuron_id_bits) | @@ -169,8 +170,9 @@ def read_static_synaptic_data( connections["target"] = ( (data & neuron_id_mask) + post_vertex_slice.lo_atom) connections["weight"] = (data >> 16) & 0xFFFF - connections["delay"] = (data & 0xFFFF) >> ( - n_neuron_id_bits + n_synapse_type_bits) + connections["delay"] = (data >> (n_neuron_id_bits + + n_synapse_type_bits)) & DELAY_MASK + connections["delay"][connections["delay"] == 0] = 16 return connections diff --git a/spynnaker/pyNN/models/populations/population.py b/spynnaker/pyNN/models/populations/population.py index 754d28eb85..c3b95eb66a 100644 --- a/spynnaker/pyNN/models/populations/population.py +++ b/spynnaker/pyNN/models/populations/population.py @@ -827,6 +827,17 @@ def __create_vertex( "Model must be either an AbstractPyNNModel or a" " PopulationApplicationVertex") + # Getting custom RB LS + if (additional_parameters is not None and + "rb_left_shifts" in additional_parameters.keys()): + rb_left_shifts = additional_parameters['rb_left_shifts'] + else: + rb_left_shifts = None + + # Setting custom RB LS + if rb_left_shifts is not None: + self.__vertex.set_rb_left_shifts(rb_left_shifts) + @staticmethod def create(cellclass, cellparams=None, n=1): """ diff --git a/spynnaker/pyNN/spinnaker.py b/spynnaker/pyNN/spinnaker.py index d6c2faf73b..cc4daf0d1d 100644 --- a/spynnaker/pyNN/spinnaker.py +++ b/spynnaker/pyNN/spinnaker.py @@ -97,6 +97,9 @@ def __init__( # pynn demanded objects self.__recorders = set([]) + # Structured provenance_items + self.structured_provenance_filename = None + # main pynn interface inheritance pynn_control.BaseState.__init__(self) @@ -337,6 +340,29 @@ def _set_up_timings(self, timestep, min_delay, time_scale_factor): math.ceil(timestep * MICRO_TO_MILLISECOND_CONVERSION), time_scale_factor, min_delay) + # TODO: work out if this max_delay code is necessary or not + # + # # Sort out the maximum delay + # natively_supported_delay_for_models = \ + # constants.MAX_SUPPORTED_DELAY_TICS + # delay_extension_max_supported_delay = ( + # constants.MAX_DELAY_BLOCKS * + # constants.MAX_TIMER_TICS_SUPPORTED_PER_BLOCK) + # max_delay_tics_supported = \ + # natively_supported_delay_for_models + \ + # delay_extension_max_supported_delay + # if (max_delay is not None and max_delay * 1000.0 > + # max_delay_tics_supported * machine_time_step): + # raise ConfigurationException( + # "Pacman does not support max delays above {} ms with the " + # "current machine time step".format( + # max_delay_tics_supported * self.machine_time_step_ms)) + # if max_delay is not None: + # self.__max_delay = max_delay + # else: + # self.__max_delay = ( + # max_delay_tics_supported * self.machine_time_step_ms) + # Check the combination of machine time step and time scale factor if (self._data_writer.get_simulation_time_step_ms() * self._data_writer.get_time_scale_factor() < 1): diff --git a/spynnaker/pyNN/utilities/constants.py b/spynnaker/pyNN/utilities/constants.py index de224c3c59..bbaa063dec 100644 --- a/spynnaker/pyNN/utilities/constants.py +++ b/spynnaker/pyNN/utilities/constants.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. + +import math from spinn_front_end_common.utilities.constants import ( BYTES_PER_WORD, BYTES_PER_KB) @@ -53,6 +55,12 @@ WEIGHT_FLOAT_TO_FIXED_SCALE = 16.0 SCALE = WEIGHT_FLOAT_TO_FIXED_SCALE * NA_TO_PA_SCALE +#: natively supported delays for all abstract_models +MAX_SUPPORTED_DELAY_TICS = 64 +MAX_DELAY_BLOCKS = 64 +DELAY_MASK = (1 << int(math.log2(MAX_SUPPORTED_DELAY_TICS))) - 1 +MAX_TIMER_TICS_SUPPORTED_PER_BLOCK = 16 + #: the minimum supported delay slot between two neurons MIN_SUPPORTED_DELAY = 1 diff --git a/unittests/model_tests/neuron/test_synaptic_manager.py b/unittests/model_tests/neuron/test_synaptic_manager.py index 441abb62b0..f1eb1dbc67 100644 --- a/unittests/model_tests/neuron/test_synaptic_manager.py +++ b/unittests/model_tests/neuron/test_synaptic_manager.py @@ -231,7 +231,7 @@ def test_set_synapse_dynamics(): n_neurons=10, label="post", spikes_per_second=None, ring_buffer_sigma=None, incoming_spike_buffer_size=None, n_steps_per_timestep=1, drop_late_spikes=True, splitter=None, - seed=None, n_colour_bits=None) + seed=None, n_colour_bits=None, rb_left_shifts=None) static = SynapseDynamicsStatic() stdp = SynapseDynamicsSTDP( @@ -342,7 +342,7 @@ def test_set_synapse_dynamics(): n_neurons=10, label="post", spikes_per_second=None, ring_buffer_sigma=None, incoming_spike_buffer_size=None, n_steps_per_timestep=1, drop_late_spikes=True, splitter=None, - seed=None, n_colour_bits=None) + seed=None, n_colour_bits=None, rb_left_shifts=None) # STDP followed by structural STDP should result in Structural STDP post_app_vertex.synapse_dynamics = stdp @@ -365,7 +365,7 @@ def test_set_synapse_dynamics(): n_neurons=10, label="post", spikes_per_second=None, ring_buffer_sigma=None, incoming_spike_buffer_size=None, n_steps_per_timestep=1, drop_late_spikes=True, splitter=None, - seed=None, n_colour_bits=None) + seed=None, n_colour_bits=None, rb_left_shifts=None) # Static followed by static structural should result in static # structural @@ -403,7 +403,7 @@ def test_set_synapse_dynamics(): n_neurons=10, label="post", spikes_per_second=None, ring_buffer_sigma=None, incoming_spike_buffer_size=None, n_steps_per_timestep=1, drop_late_spikes=True, splitter=None, - seed=None, n_colour_bits=None) + seed=None, n_colour_bits=None, rb_left_shifts=None) post_app_vertex.synapse_dynamics = static_struct post_app_vertex.synapse_dynamics = stdp_struct diff --git a/unittests/test_populations/test_vertex.py b/unittests/test_populations/test_vertex.py index fde6e56eec..41c8e3eb50 100644 --- a/unittests/test_populations/test_vertex.py +++ b/unittests/test_populations/test_vertex.py @@ -106,7 +106,7 @@ def __init__(self): ring_buffer_sigma=None, incoming_spike_buffer_size=None, neuron_impl=foo_bar.model, pynn_model=foo_bar, drop_late_spikes=True, splitter=None, seed=None, - n_colour_bits=None) + n_colour_bits=None, rb_left_shifts=None) def test_initializable():