Skip to content

Commit

Permalink
Merge pull request #1491 from SpiNNakerManchester/shift_connector
Browse files Browse the repository at this point in the history
Add a shift connector
  • Loading branch information
rowleya authored Oct 1, 2024
2 parents 9b1ae39 + c3df1d7 commit 0ce7505
Show file tree
Hide file tree
Showing 7 changed files with 569 additions and 4 deletions.
8 changes: 7 additions & 1 deletion neural_modelling/src/synapse_expander/connection_generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "connection_generators/connection_generator_fixed_post.h"
#include "connection_generators/connection_generator_kernel.h"
#include "connection_generators/connection_generator_all_but_me.h"
#include "connection_generators/connection_generator_one_to_one_offset.h"

//! \brief Known "hashes" of connection generators
//!
Expand All @@ -45,6 +46,7 @@ enum {
FIXED_POST, //!< Fixed post-size connection generator
KERNEL, //!< Convolution kernel connection generator
ALL_BUT_ME, //!< AllButMe connection generator
ONE_TO_ONE_OFFSET, //!< One-to-one offset connection generator
N_CONNECTION_GENERATORS//!< The number of known generators
};

Expand Down Expand Up @@ -102,7 +104,11 @@ static const connection_generator_info connection_generators[] = {
{ALL_BUT_ME,
connection_generator_all_but_me_initialise,
connection_generator_all_but_me_generate,
connection_generator_all_but_me_free}
connection_generator_all_but_me_free},
{ONE_TO_ONE_OFFSET,
connection_generator_one_to_one_offset_initialise,
connection_generator_one_to_one_offset_generate,
connection_generator_one_to_one_offset_free}
};

connection_generator_t connection_generator_init(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2024 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.
*/

/**
* \file
* \brief one_to_one_offset Connection generator implementation
*/

#include <synapse_expander/generator_types.h>

//! \brief The parameters to be passed around for this connector
struct one_to_one_offset {
// Amount to add to the pre by to get the post
int32_t offset;
// Whether to wrap around the post values or just clip
uint32_t wrap;
// The group size to consider for the offset
uint32_t n_neurons_per_group;

};


/**
* \brief Initialise the one_to_one_offset connection generator
* \param[in,out] region: Region to read parameters from. Should be updated
* to position just after parameters after calling.
* \return A data item to be passed in to other functions later on
*/
static void *connection_generator_one_to_one_offset_initialise(UNUSED void **region) {
// Allocate the data structure for parameters
struct one_to_one_offset *params = spin1_malloc(sizeof(struct one_to_one_offset));
struct one_to_one_offset *params_sdram = *region;

// Copy the parameters into the data structure
*params = *params_sdram;
*region = &params_sdram[1];

log_debug("one_to_one_offset connector, one_to_one_offset = %u, wrap = %u, "
"n_neurons_per_group = %u",
params->offset, params->wrap, params->n_neurons_per_group);

return params;
}

/**
* \brief Free the one_to_one_offset connection generator
* \param[in] generator: The generator to free
*/
static void connection_generator_one_to_one_offset_free(UNUSED void *generator) {
// Nothing to do
}

/**
* \brief Generate connections with the one_to_one_offset connection generator
* \param[in] generator: The generator to use to generate connections
* \param[in] pre_slice_start: The start of the slice of the pre-population
* being generated
* \param[in] pre_slice_count: The number of neurons in the slice of the
* pre-population being generated
* \param[in] pre_neuron_index: The index of the neuron in the pre-population
* being generated
* \param[in] post_slice_start: The start of the slice of the post-population
* being generated
* \param[in] post_slice_count: The number of neurons in the slice of the
* post-population being generated
* \param[in] max_row_length: The maximum number of connections to generate
* \param[in,out] indices: An array into which the core-relative post-indices
* should be placed. This will be initialised to be
* \p max_row_length in size
* \return The number of connections generated
*/
static bool connection_generator_one_to_one_offset_generate(
void *generator, uint32_t pre_lo, uint32_t pre_hi,
uint32_t post_lo, uint32_t post_hi, UNUSED 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) {

struct one_to_one_offset *obj = generator;

// Get the actual ranges to generate within
uint32_t post_start = max(post_slice_start, post_lo);
uint32_t post_end = min(post_slice_start + post_slice_count - 1, post_hi);

// Work out where we are in the generation
// We need to connect each pre-neuron to each post-neuron in each group
// (but not to itself). We are currently generating a subset of the post
// neurons, so we need to work out which group we are in within that subset,
// and which is the first post-neuron in the group that we are generating
// for now.
uint32_t post_group;
uint32_t post_value;
div_mod(post_start, obj->n_neurons_per_group, &post_group, &post_value);

// Work out where the pre-neurons start and end for the group that we are
// in at the start of the post-neurons.
uint32_t pre_start = pre_lo + post_group * obj->n_neurons_per_group;
uint32_t pre_end = min(pre_start + obj->n_neurons_per_group - 1, pre_hi);

// Go through the post neurons in this slice
for (uint32_t post = post_start; post <= post_end; post++) {
uint32_t local_post = post - post_slice_start;

// Find the pre that occurs after offset; as the offset is post from
// pre, we subtract it to get pre from post (note it might be negative already)
int32_t pre = post - obj->offset;
bool use = true;
if (pre < (int32_t) pre_start) {
if (obj->wrap) {
pre += obj->n_neurons_per_group;
} else {
use = false;
}
} else if (pre > (int32_t) pre_end) {
if (obj->wrap) {
pre -= obj->n_neurons_per_group;
} else {
use = false;
}
}

if (use) {
accum weight = param_generator_generate(weight_generator);
uint16_t delay = rescale_delay(
param_generator_generate(delay_generator), timestep_per_delay);
if (!matrix_generator_write_synapse(matrix_generator, (uint32_t) pre,
local_post, weight, delay, weight_scale)) {
log_error("Matrix not sized correctly!");
return false;
}
}

// Work out next loop iteration. If we have reached the end of a group
// of values, we need to move onto the next group.
post_value += 1;
if (post_value == obj->n_neurons_per_group) {
post_value = 0;
pre_start += obj->n_neurons_per_group;
pre_end = min(pre_start + obj->n_neurons_per_group - 1, pre_hi);
if (pre_start > pre_hi) {
break;
}
}
}

return true;
}
4 changes: 2 additions & 2 deletions spynnaker/pyNN/extra_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
IFCurrDeltaCa2Adaptive, StocExp, StocExpStable, StocSigma, IFTruncDelta,
IFCurrDeltaFixedProb)
from spynnaker.pyNN.models.neural_projections.connectors import (
AllButMeConnector)
AllButMeConnector, OneToOneOffsetConnector)

# Variable rate poisson
from spynnaker.pyNN.models.spike_source import SpikeSourcePoissonVariable
Expand Down Expand Up @@ -64,7 +64,7 @@
'IFTruncDelta',

# Connectors
'AllButMeConnector',
'AllButMeConnector', 'OneToOneOffsetConnector',

# Weight changeable synapse dynamics
'WeightChangeable', 'WeightChanger'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .kernel_connector import KernelConnector
from .convolution_connector import ConvolutionConnector
from .pool_dense_connector import PoolDenseConnector
from .one_to_one_offset_connector import OneToOneOffsetConnector

__all__ = ["AbstractConnector", "AbstractGenerateConnectorOnMachine",
"AbstractGenerateConnectorOnHost", "AllButMeConnector",
Expand All @@ -44,4 +45,5 @@
"FromFileConnector",
"FromListConnector", "IndexBasedProbabilityConnector",
"KernelConnector", "ConvolutionConnector", "PoolDenseConnector",
"MultapseConnector", "OneToOneConnector", "SmallWorldConnector"]
"MultapseConnector", "OneToOneConnector", "SmallWorldConnector",
"OneToOneOffsetConnector"]
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ConnectorIDs(Enum):
FIXED_NUMBER_POST_CONNECTOR = 5
KERNEL_CONNECTOR = 6
WTA_CONNECTOR = 7
ONE_TO_ONE_OFFSET_CONNECTOR = 8


class AbstractGenerateConnectorOnMachine(
Expand Down
Loading

0 comments on commit 0ce7505

Please sign in to comment.