Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
5a94dae
started implementing spikes as a fuseable thing
neworderofjamie Nov 27, 2023
aefdc3c
use to build fused list of spike conditions
neworderofjamie Nov 27, 2023
adcde8a
caught a stray use of ugly function-pointer-to-member call syntax
neworderofjamie Nov 27, 2023
e22dfb9
taking a NeuronGroup* to determine pre or postsynapticness spreads th…
neworderofjamie Nov 27, 2023
cda5b41
started implementing code generation and runtime - synapse group targ…
neworderofjamie Nov 27, 2023
b5c44b8
slightly ugly means of getting correct fused target from synapse group
neworderofjamie Nov 27, 2023
fc4ba1d
getting closer - spike queue and previous spike updating should happe…
neworderofjamie Nov 28, 2023
be6f104
started calcualting some hashes
neworderofjamie Nov 28, 2023
8efe650
Added NeuronUpdateGroupMerged::generateSpikes which sets up environme…
neworderofjamie Nov 29, 2023
90f03a1
generate weight update outside of spike generation
neworderofjamie Nov 29, 2023
6440abd
WIP spike event implementation
neworderofjamie Nov 29, 2023
da11972
more spike-event work
neworderofjamie Nov 29, 2023
9e20495
spike event generation compiles
neworderofjamie Nov 30, 2023
256e7cc
added start of spike event feature test
neworderofjamie Nov 30, 2023
d32c72e
initialisation for spike events
neworderofjamie Nov 30, 2023
6df1faa
fixed a couple of typos
neworderofjamie Nov 30, 2023
fd6fc7a
marked children of ``NeuronUpdateGroupMerged`` for ``GENN_EXPORT``
neworderofjamie Nov 30, 2023
32b474b
change array name
neworderofjamie Nov 30, 2023
5fcb7a9
adjusted some backendbase environment-building code
neworderofjamie Nov 30, 2023
6a5423d
made names more consistent - "event" rather than "evnt"
neworderofjamie Nov 30, 2023
9c43cd0
one missing!
neworderofjamie Nov 30, 2023
2914376
removed unused function
neworderofjamie Nov 30, 2023
3048d69
extended test
neworderofjamie Nov 30, 2023
75fe297
spike event hashes should include whether times are required
neworderofjamie Nov 30, 2023
f478457
no need to get fused targets in child groups as these are, by definit…
neworderofjamie Nov 30, 2023
3bf3139
moved lots of spike-event related stuff from ``NeuronGroup`` to ``Syn…
neworderofjamie Nov 30, 2023
1dd9842
fixed up initialisation of spike event times
neworderofjamie Nov 30, 2023
a2aa7fa
removed some legacy code
neworderofjamie Nov 30, 2023
e3f1282
update spike event times on event emission
neworderofjamie Nov 30, 2023
8fe14a1
Revert "moved lots of spike-event related stuff from ``NeuronGroup`` …
neworderofjamie Nov 30, 2023
16bed82
per-fused group spike times
neworderofjamie Nov 30, 2023
dc20373
fixed typo
neworderofjamie Nov 30, 2023
5251e0a
fixed warning
neworderofjamie Nov 30, 2023
964d0a0
removed tests for spike times in neuron code
neworderofjamie Nov 30, 2023
8011832
existing tests run
neworderofjamie Nov 30, 2023
ec43e40
renamed feature test module
neworderofjamie Nov 30, 2023
4c47770
added test_spike_event_times feature test
neworderofjamie Nov 30, 2023
5baf6bd
fixed a couple of small bugs
neworderofjamie Nov 30, 2023
d351ee5
renamed another feature test
neworderofjamie Dec 1, 2023
eb7609c
added propagation tests
neworderofjamie Dec 1, 2023
caa35c6
started unit test for fusing spike event conditions
neworderofjamie Dec 1, 2023
080b3bb
fixed small bugs
neworderofjamie Dec 1, 2023
bb9ddcb
slight extension of test
neworderofjamie Dec 1, 2023
fe86ae6
WIP spike-event recording implemenation
neworderofjamie Dec 1, 2023
7cfed37
moved PyGeNN spike-event recording functionality to SynapseGroup
neworderofjamie Dec 2, 2023
d797bc5
started adding spike event recording to feature test
neworderofjamie Dec 2, 2023
b5f9f29
correctly zero all spike-event recording buffers in CPU
neworderofjamie Dec 2, 2023
fe5db96
update name of test
neworderofjamie Dec 2, 2023
a58e3ee
fixed various SIMT recording bugs
neworderofjamie Dec 2, 2023
6c69bb6
fixed a couple of minor bugs
neworderofjamie Dec 2, 2023
7826911
rename field
neworderofjamie Dec 2, 2023
a54d42e
tweaked test
neworderofjamie Dec 2, 2023
e2237d6
fixed one more typo
neworderofjamie Dec 2, 2023
fbb4f22
**REVERT** commented out bits of test to expose bug
neworderofjamie Dec 4, 2023
2ef0ed5
started fixing nasty bug
neworderofjamie Dec 4, 2023
5d4b7ce
improved logging of creation of fused synapse variables in runtime
neworderofjamie Dec 4, 2023
4936a2a
add more debugging information to runtime log
neworderofjamie Dec 4, 2023
c8c32b1
deleted commented out function from CodeGenUtils
neworderofjamie Dec 4, 2023
b12056c
fixed bug and tidied resultant complexity a bit
neworderofjamie Dec 4, 2023
6a5b8e0
renamed spike-event to pre-spike event wherever it was found!
neworderofjamie Dec 4, 2023
448c145
and spike-event-threshold-condition code
neworderofjamie Dec 4, 2023
5b81484
added postsynaptic spike-like events
neworderofjamie Dec 4, 2023
6784000
pygenn wrapper with deprecations
neworderofjamie Dec 4, 2023
4044b69
uncomment bits of test_spike_times
neworderofjamie Dec 4, 2023
e5cec9f
actually setup substitutions to access variables in synaptic code
neworderofjamie Dec 4, 2023
5ab0e00
completed implementing test
neworderofjamie Dec 4, 2023
073c3bb
some small fixes
neworderofjamie Dec 4, 2023
8506402
fixed unit tests
neworderofjamie Dec 5, 2023
9fabbb5
fixed some issues relating to ``remap``:
neworderofjamie Dec 5, 2023
af2f13e
speculative fix for unit test building
neworderofjamie Dec 5, 2023
507d207
totally broken logic for generating SIMT previuous spike times
neworderofjamie Dec 5, 2023
6cc163a
use the right threshold condition
neworderofjamie Dec 5, 2023
3c15ae3
added test of propagating activity via postsynaptic spike-like events
neworderofjamie Dec 5, 2023
9caae9c
updated spike-event recording
neworderofjamie Dec 5, 2023
2d07bc3
tidy of single-threaded CPU code
neworderofjamie Dec 5, 2023
73031f7
tidy of SIMT previous event time update and copying events from share…
neworderofjamie Dec 6, 2023
967a777
tidied up SIMT spike emission
neworderofjamie Dec 6, 2023
d41bca8
tidied up event data structure initialisation
neworderofjamie Dec 6, 2023
7b7de36
remove last of C++ feature tests
neworderofjamie Dec 7, 2023
7ea28e3
fixed some bugs resulting from tidying
neworderofjamie Dec 8, 2023
c7daf07
VarAccessDim should be exported
neworderofjamie Dec 8, 2023
285ec8e
so should ``SynapseMatrixWeight`` and ``VarAccessModeAttribute``
neworderofjamie Dec 8, 2023
0a97097
and some operators on ``VarAccessModeAttribute``
neworderofjamie Dec 8, 2023
f7a1dc7
misspelling of flaky!
neworderofjamie Dec 8, 2023
f457ce4
make sure std out isn't lost from pytestr
neworderofjamie Dec 9, 2023
d1bdda7
create GeNNModel via fixture to ensure it always gets unloaded properly
neworderofjamie Dec 11, 2023
31049d4
fixed typo
neworderofjamie Dec 11, 2023
ba718fc
deleted manual test entry points (they no longer work with fixtures)
neworderofjamie Dec 12, 2023
79567f2
no need for C++ test utils
neworderofjamie Dec 12, 2023
6c3417a
reinstated check tested by CustomUpdates/NeuronSharedCustomUpdateWU
neworderofjamie Dec 12, 2023
8821725
reverted dumb feature test change
neworderofjamie Dec 12, 2023
f74de29
added C++ defaults to addXXPopulation methods and save typing in unit…
neworderofjamie Dec 12, 2023
c6e19ee
tracked down bug in structural plasticity branch version of test and …
neworderofjamie Dec 12, 2023
fe91df5
fix for model ownership issues
neworderofjamie Dec 16, 2023
9731d17
fixed tiny typo
neworderofjamie Dec 18, 2023
cfd9c01
expose SynapseMatrixConnectivity in top-level module
neworderofjamie Dec 18, 2023
989fac7
fixed really nasty bug with CUDA backend - no idea what I was thinkin…
neworderofjamie Dec 18, 2023
6e30d44
fix to ownership fix
neworderofjamie Dec 18, 2023
bfd3988
typo in average pool + conv2d fused model
neworderofjamie Dec 18, 2023
5f2f33a
exposed definitions of internal type checking and pretty printing env…
neworderofjamie Dec 18, 2023
bb5b01a
provide the internal environment for global variables via the code ge…
neworderofjamie Dec 18, 2023
ee8b27e
define internal environments for neuron outside so it can be shared b…
neworderofjamie Dec 18, 2023
74ef9f3
kernel size fields shouldn't be const - is it worth just stripping co…
neworderofjamie Dec 18, 2023
259d6e7
whitespace
neworderofjamie Dec 18, 2023
a62098a
remove commented out dregs of event retesting in presynaptic update s…
neworderofjamie Dec 18, 2023
622bbf7
not sure if this is actually a good idea - expose allowDuplicates eve…
neworderofjamie Dec 18, 2023
eb59209
add batch size to environment
neworderofjamie Dec 18, 2023
1f940b4
Revert "not sure if this is actually a good idea - expose allowDuplic…
neworderofjamie Dec 18, 2023
c9795b3
Revert "add batch size to environment"
neworderofjamie Dec 18, 2023
34c1aea
just made allowDuplicates true by default - saved a lot of complexity
neworderofjamie Dec 18, 2023
153e252
expose num_batch
neworderofjamie Dec 18, 2023
0cb4a0c
tidied up exposure of non-variable arrays like out_post
neworderofjamie Dec 18, 2023
e28af4e
fixed type-checker unit tests
neworderofjamie Dec 18, 2023
7f6a0c1
removed all legacy $() syntax from C++ library and tests
neworderofjamie Dec 19, 2023
6dc79ec
missing array suffix for Toeplitz and Procedural connect init EGPs
neworderofjamie Dec 19, 2023
ab20044
currently un-integrated Python code-string upgrading function
neworderofjamie Dec 19, 2023
4de93aa
broken code upgrading
neworderofjamie Dec 19, 2023
45fa652
missing escape
neworderofjamie Dec 19, 2023
f26f0cf
update weirdly-cases sT_pre etc
neworderofjamie Dec 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ for(b = 0; b < builderNodes.size(); b++) {
${env.PYTHON} -m venv ${WORKSPACE}/venv
. ${WORKSPACE}/venv/bin/activate
pip install -U pip
pip install numpy scipy pybind11 pytest flakey pytest-cov wheel flake8 bitarray
pip install numpy scipy pybind11 pytest flaky pytest-cov wheel flake8 bitarray
""";
}

Expand Down Expand Up @@ -234,7 +234,7 @@ for(b = 0; b < builderNodes.size(); b++) {
// Run ML GeNN test suite
def commandsTest = """
. ${WORKSPACE}/venv/bin/activate
pytest -v --cov ../../pygenn --cov-report=xml:${coveragePython} --junitxml test_results_feature.xml 1>> "${outputFilename}" 2>&1
pytest -s -v --cov ../../pygenn --cov-report=xml:${coveragePython} --junitxml test_results_feature.xml 1>> "${outputFilename}" 2>&1
""";
def statusTests = sh script:commandsTest, returnStatus:true;
if (statusTests != 0) {
Expand Down
7 changes: 6 additions & 1 deletion include/genn/backends/single_threaded_cpu/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,13 @@ class BACKEND_EXPORT Backend : public BackendBase
//--------------------------------------------------------------------------
void genPresynapticUpdate(EnvironmentExternalBase &env, PresynapticUpdateGroupMerged &sg,
double dt, bool trueSpike) const;
void genPostsynapticUpdate(EnvironmentExternalBase &env, PostsynapticUpdateGroupMerged &sg,
double dt, bool trueSpike) const;

void genEmitSpike(EnvironmentExternalBase &env, NeuronUpdateGroupMerged &ng, bool trueSpike, bool recordingEnabled) const;
void genPrevEventTimeUpdate(EnvironmentExternalBase &env, NeuronPrevSpikeTimeUpdateGroupMerged &ng,
bool trueSpike) const;

void genEmitEvent(EnvironmentExternalBase &env, NeuronUpdateGroupMerged &ng, bool trueSpike) const;

//! Helper to generate code to copy reduced custom update group variables back to memory
/*! Because reduction operations are unnecessary in unbatched single-threaded CPU models so there's no need to actually reduce */
Expand Down
15 changes: 11 additions & 4 deletions include/genn/genn/code_generator/backendSIMT.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,13 +460,20 @@ class GENN_EXPORT BackendSIMT : public BackendBase
}
}

void genEmitSpike(EnvironmentExternalBase &env, const std::string &suffix,
bool spikeRequired, bool recordingEnabled) const;

void genRecordingSharedMemInit(CodeStream &os, const std::string &suffix) const;
void genRecordingSharedMemInit(CodeStream &os, const std::string &suffix, size_t numArrays) const;

void genSynapseVariableRowInit(EnvironmentExternalBase &env, HandlerEnv handler) const;

void genPostsynapticUpdate(EnvironmentExternalBase &env, PostsynapticUpdateGroupMerged &sg,
double dt, unsigned int batchSize, bool trueSpike) const;

void genPrevEventTimeUpdate(EnvironmentExternalBase &env, NeuronPrevSpikeTimeUpdateGroupMerged &ng,
unsigned int batchSize, bool trueSpike) const;
void genEmitEvent(EnvironmentExternalBase &env, NeuronUpdateGroupMerged &ng,
size_t index, bool trueSpike) const;
void genCopyEventToGlobal(EnvironmentExternalBase &env, NeuronUpdateGroupMerged &ng,
unsigned int batchSize, size_t index, bool trueSpike) const;

// Get appropriate presynaptic update strategy to use for this synapse group
const PresynapticUpdateStrategySIMT::Base *getPresynapticUpdateStrategy(const SynapseGroupInternal &sg) const
{
Expand Down
73 changes: 13 additions & 60 deletions include/genn/genn/code_generator/codeGenUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,75 +55,28 @@ inline size_t padSize(size_t size, size_t blockSize)

GENN_EXPORT void genTypeRange(CodeStream &os, const Type::ResolvedType &type, const std::string &prefix);

//--------------------------------------------------------------------------
/*! \brief This function uses the transpiler to parse, type check and pretty print previously scanned vector of tokens representing an expression
*/
//--------------------------------------------------------------------------
//! Parse, type check and pretty print previously scanned vector of tokens representing an expression
GENN_EXPORT void prettyPrintExpression(const std::vector<Transpiler::Token> &tokens, const Type::TypeContext &typeContext,
Transpiler::TypeChecker::EnvironmentInternal &typeCheckEnv, Transpiler::PrettyPrinter::EnvironmentInternal &prettyPrintEnv,
Transpiler::ErrorHandler &errorHandler);

//! Parse, type check and pretty print previously scanned vector of tokens representing an expression
GENN_EXPORT void prettyPrintExpression(const std::vector<Transpiler::Token> &tokens, const Type::TypeContext &typeContext,
EnvironmentExternalBase &env, Transpiler::ErrorHandler &errorHandler);

//--------------------------------------------------------------------------
/*! \brief This function uses the transpiler to parse, type check and pretty print previously scanned vector of tokens representing a statemebt
*/
//--------------------------------------------------------------------------
//! Parse, type check and pretty print previously scanned vector of tokens representing a statement
GENN_EXPORT void prettyPrintStatements(const std::vector<Transpiler::Token> &tokens, const Type::TypeContext &typeContext,
Transpiler::TypeChecker::EnvironmentInternal &typeCheckEnv, Transpiler::PrettyPrinter::EnvironmentInternal &prettyPrintEnv,
Transpiler::ErrorHandler &errorHandler, Transpiler::TypeChecker::StatementHandler forEachSynapseTypeCheckHandler = nullptr,
Transpiler::PrettyPrinter::StatementHandler forEachSynapsePrettyPrintHandler = nullptr);

//! Parse, type check and pretty print previously scanned vector of tokens representing a statement
GENN_EXPORT void prettyPrintStatements(const std::vector<Transpiler::Token> &tokens, const Type::TypeContext &typeContext, EnvironmentExternalBase &env,
Transpiler::ErrorHandler &errorHandler, Transpiler::TypeChecker::StatementHandler forEachSynapseTypeCheckHandler = nullptr,
Transpiler::PrettyPrinter::StatementHandler forEachSynapsePrettyPrintHandler = nullptr);

GENN_EXPORT std::string printSubs(const std::string &format, Transpiler::PrettyPrinter::EnvironmentBase &env);

//-------------------------------------------------------------------------
/*!
\brief Function for performing the code and value substitutions necessary to insert neuron related variables, parameters, and extraGlobal parameters into synaptic code.
*/
//-------------------------------------------------------------------------
/*template<typename P, typename D, typename V, typename S>
void neuronSubstitutionsInSynapticCode(CodeGenerator::Substitutions &substitutions, const NeuronGroupInternal *archetypeNG,
const std::string &delayOffset, const std::string &sourceSuffix, const std::string &destSuffix,
const std::string &varPrefix, const std::string &varSuffix, bool useLocalNeuronVars,
P isParamHeterogeneousFn, D isDerivedParamHeterogeneousFn, V getVarIndexFn, S getPrevSpikeTimeIndexFn)
{

// Substitute spike times
const bool delay = archetypeNG->isDelayRequired();
const std::string spikeTimeVarIndex = getVarIndexFn(delay, VarAccessDuplication::DUPLICATE);
const std::string prevSpikeTimeVarIndex = getPrevSpikeTimeIndexFn(delay, VarAccessDuplication::DUPLICATE);
substitutions.addVarSubstitution("sT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->sT" + destSuffix + "[" + spikeTimeVarIndex + "]" + varSuffix + ")");
substitutions.addVarSubstitution("prev_sT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->prevST" + destSuffix + "[" + prevSpikeTimeVarIndex + "]" + varSuffix + ")");

// Substitute spike-like-event times
substitutions.addVarSubstitution("seT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->seT" + destSuffix + "[" + spikeTimeVarIndex + "]" + varSuffix + ")");
substitutions.addVarSubstitution("prev_seT" + sourceSuffix,
"(" + delayOffset + varPrefix + "group->prevSET" + destSuffix + "[" + prevSpikeTimeVarIndex + "]" + varSuffix + ")");

// Substitute neuron variables
const auto *nm = archetypeNG->getNeuronModel();
if(useLocalNeuronVars) {
substitutions.addVarNameSubstitution(nm->getVars(), sourceSuffix, "l");
}
else {
for(const auto &v : nm->getVars()) {
const std::string varIdx = getVarIndexFn(delay && archetypeNG->isVarQueueRequired(v.name),
getVarAccessDuplication(v.access));

substitutions.addVarSubstitution(v.name + sourceSuffix,
varPrefix + "group->" + v.name + destSuffix + "[" + varIdx + "]" + varSuffix);
}
}

// Substitute (potentially heterogeneous) parameters and derived parameters from neuron model
substitutions.addParamValueSubstitution(nm->getParamNames(), archetypeNG->getParams(), isParamHeterogeneousFn,
sourceSuffix, "group->", destSuffix);
substitutions.addVarValueSubstitution(nm->getDerivedParams(), archetypeNG->getDerivedParams(), isDerivedParamHeterogeneousFn,
sourceSuffix, "group->", destSuffix);

// Substitute extra global parameters from neuron model
substitutions.addVarNameSubstitution(nm->getExtraGlobalParams(), sourceSuffix, "group->", destSuffix);
}*/

template<typename G>
bool isKernelSizeHeterogeneous(const G &group, size_t dimensionIndex)
{
Expand Down
39 changes: 18 additions & 21 deletions include/genn/genn/code_generator/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class EnvironmentFieldPolicy
const G &getGroup() const{ return m_Group; }

protected:
using Payload = std::tuple<bool, bool, LazyString, std::optional<typename G::Field>>;
using Payload = std::tuple<bool, LazyString, std::optional<typename G::Field>>;

EnvironmentFieldPolicy(G &group, F &fieldGroup)
: m_Group(group), m_FieldGroup(fieldGroup)
Expand All @@ -180,15 +180,15 @@ class EnvironmentFieldPolicy
std::string getNameInternal(const Payload &payload)
{
// If a field is specified
const auto str = std::get<2>(payload).str();
if(std::get<3>(payload)) {
const auto str = std::get<1>(payload).str();
if(std::get<2>(payload)) {
// If there is no value specified, access field directly
if(str.empty()) {
return "group->" + std::get<3>(payload).value().name;
return "group->" + std::get<2>(payload).value().name;
}
// Otherwise, treat value as index
else {
return "group->" + std::get<3>(payload).value().name + "[" + str + "]";
return "group->" + std::get<2>(payload).value().name + "[" + str + "]";
}
}
// Otherwise, use value directly
Expand All @@ -200,9 +200,9 @@ class EnvironmentFieldPolicy
void setRequired(Payload &payload)
{
// If a field is specified but it hasn't already been added
if (std::get<3>(payload) && !std::get<0>(payload)) {
if (std::get<2>(payload) && !std::get<0>(payload)) {
// Extract field from payload
const auto &field = std::get<3>(payload).value();
const auto &field = std::get<2>(payload).value();
const auto &group = getGroup();

// Add to field group using lambda function to potentially map from group to field
Expand All @@ -212,7 +212,7 @@ class EnvironmentFieldPolicy
{
return field.getValue(runtime, group.getGroups().at(i), i);
},
field.fieldType, std::get<1>(payload));
field.fieldType);

// Set flag so field doesn't get re-added
std::get<0>(payload) = true;
Expand Down Expand Up @@ -432,26 +432,26 @@ class EnvironmentGroupMergedField : public EnvironmentExternalDynamicBase<Enviro
void add(const GeNN::Type::ResolvedType &type, const std::string &name, const std::string &value,
const std::vector<size_t> &initialisers = {})
{
this->addInternal(type, name, std::make_tuple(false, false, LazyString{value, *this}, std::nullopt), initialisers);
this->addInternal(type, name, std::make_tuple(false, LazyString{value, *this}, std::nullopt), initialisers);
}

//! Map a type (for type-checking) and a group merged field to back it to an identifier
void addField(const GeNN::Type::ResolvedType &type, const std::string &name,
const GeNN::Type::ResolvedType &fieldType, const std::string &fieldName, typename G::GetFieldValueFunc getFieldValue,
const std::string &indexSuffix = "", GroupMergedFieldType mergedFieldType = GroupMergedFieldType::STANDARD,
bool allowDuplicates = false, const std::vector<size_t> &initialisers = {})
const std::vector<size_t> &initialisers = {})
{
typename G::Field field{fieldName, fieldType, mergedFieldType, getFieldValue};
this->addInternal(type, name, std::make_tuple(false, allowDuplicates, LazyString{indexSuffix, *this}, std::make_optional(field)),
this->addInternal(type, name, std::make_tuple(false, LazyString{indexSuffix, *this}, std::make_optional(field)),
initialisers);
}

//! Map a type (for type-checking) and a group merged field to back it to an identifier
void addField(const GeNN::Type::ResolvedType &type, const std::string &name, const std::string &fieldName,
typename G::GetFieldValueFunc getFieldValue, const std::string &indexSuffix = "", GroupMergedFieldType mergedFieldType = GroupMergedFieldType::STANDARD,
bool allowDuplicates = false, const std::vector<size_t> &initialisers = {})
const std::vector<size_t> &initialisers = {})
{
addField(type, name, type, fieldName, getFieldValue, indexSuffix, mergedFieldType, allowDuplicates, initialisers);
addField(type, name, type, fieldName, getFieldValue, indexSuffix, mergedFieldType, initialisers);
}

void addParams(const Snippet::Base::ParamVec &params, const std::string &fieldSuffix,
Expand Down Expand Up @@ -722,7 +722,7 @@ class EnvironmentGroupMergedField : public EnvironmentExternalDynamicBase<Enviro
}

template<typename A>
void addVarPointers(const std::string &fieldSuffix = "", bool hidden = false, bool allowDuplicates = false)
void addVarPointers(const std::string &fieldSuffix = "", bool hidden = false)
{
// Loop through variables and add private pointer field
const A archetypeAdaptor(this->getGroup().getArchetype());
Expand All @@ -733,8 +733,7 @@ class EnvironmentGroupMergedField : public EnvironmentExternalDynamicBase<Enviro
[v](auto &runtime, const auto &g, size_t)
{
return runtime.getArray(g, v.name);
},
"", GroupMergedFieldType::STANDARD, allowDuplicates);
});
}
}

Expand Down Expand Up @@ -924,10 +923,10 @@ class EnvironmentLocalCacheBase : public EnvironmentExternalBase, public P
template<typename... PolicyArgs>
EnvironmentLocalCacheBase(G &group, F &fieldGroup, const Type::TypeContext &context, EnvironmentExternalBase &enclosing,
const std::string &fieldSuffix, const std::string &localPrefix,
bool hidden, bool allowDuplicates, PolicyArgs&&... policyArgs)
bool hidden, PolicyArgs&&... policyArgs)
: EnvironmentExternalBase(enclosing), P(std::forward<PolicyArgs>(policyArgs)...), m_Group(group),
m_FieldGroup(fieldGroup), m_Context(context), m_Contents(m_ContentsStream),
m_FieldSuffix(fieldSuffix), m_LocalPrefix(localPrefix), m_AllowDuplicates(allowDuplicates)
m_FieldSuffix(fieldSuffix), m_LocalPrefix(localPrefix)
{
// Copy variables into variables referenced, alongside boolean
const auto defs = A(m_Group.get().getArchetype()).getDefs();
Expand Down Expand Up @@ -961,8 +960,7 @@ class EnvironmentLocalCacheBase : public EnvironmentExternalBase, public P
[v, &group, this](auto &runtime, const typename F::GroupInternal &, size_t i)
{
return this->getArray(runtime, group.getGroups().at(i), v);
},
GroupMergedFieldType::STANDARD, m_AllowDuplicates);
});

if(getVarAccessMode(v.access) == VarAccessMode::READ_ONLY) {
getContextStream() << "const ";
Expand Down Expand Up @@ -1050,7 +1048,6 @@ class EnvironmentLocalCacheBase : public EnvironmentExternalBase, public P
CodeStream m_Contents;
std::string m_FieldSuffix;
std::string m_LocalPrefix;
bool m_AllowDuplicates;
std::unordered_map<std::string, std::pair<bool, AdapterDef>> m_VariablesReferenced;
};

Expand Down
20 changes: 5 additions & 15 deletions include/genn/genn/code_generator/groupMerged.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,24 +362,14 @@ class GroupMerged : public ChildGroupMerged<G>
}

void addField(const Type::ResolvedType &type, const std::string &name, typename ChildGroupMerged<G>::GetFieldValueFunc getFieldValue,
GroupMergedFieldType fieldType = GroupMergedFieldType::STANDARD, bool allowDuplicate = false)
GroupMergedFieldType fieldType = GroupMergedFieldType::STANDARD)
{
// Add field to data structure
auto r = m_Fields.insert({name, type, fieldType, getFieldValue});

// If field wasn't successfully inserted
if(!r.second) {
// If duplicate fields are allowed
if(allowDuplicate) {
// If other properties of the field don't match
if(r.first->type != type || r.first->fieldType != fieldType) {
throw std::runtime_error("Unable to add duplicate field '" + name + "' with different properties to merged group");
}
}
// Otherwise, give error
else {
throw std::runtime_error("Unable to add duplicate field '" + name + "' to merged group");
}
// If field wasn't successfully and inserted other properties of the field don't match
if(!r.second && (r.first->type != type || r.first->fieldType != fieldType)) {
throw std::runtime_error("Unable to add duplicate field '" + name + "' with different properties to merged group");
}
}

Expand Down Expand Up @@ -450,7 +440,7 @@ class GENN_EXPORT NeuronGroupMergedBase : public GroupMerged<NeuronGroupInternal
// Loop through children and add them and their digests to vector
childDigests.clear();
for(auto *c : groupChildren) {
childDigests.emplace_back((c->*getHashDigestFunc)(), c);
childDigests.emplace_back(std::invoke(getHashDigestFunc, c, &g.get()), c);
}

// Sort by digest
Expand Down
Loading