From 62ebfe0345d5c5ac2e3cf25d3417916e2475f0a0 Mon Sep 17 00:00:00 2001 From: Alexander Temerev Date: Thu, 29 Feb 2024 14:30:53 +0100 Subject: [PATCH 1/5] All RNG usages except Random123 are removed. All non-Random123 configuration options removed --- neurodamus/connection.py | 30 +---- neurodamus/core/configuration.py | 2 - neurodamus/core/random.py | 37 ++----- neurodamus/data/hoc/RNGSettings.hoc | 164 +--------------------------- neurodamus/stimulus_manager.py | 10 +- tests/integration/test_neuron.py | 2 +- 6 files changed, 19 insertions(+), 226 deletions(-) diff --git a/neurodamus/connection.py b/neurodamus/connection.py index e60634c0..8d89ee3d 100644 --- a/neurodamus/connection.py +++ b/neurodamus/connection.py @@ -714,36 +714,12 @@ def create_on(self, conn, sec, position, syn_obj, syn_params, base_seed, _rate_v self._store(ips, netcon) src_pop_id, dst_pop_id = conn.population_id - rng_mode = self._rng_info.getRNGMode() rng_seed = self._rng_info.getMinisSeed() tgid_seed = conn.tgid + 250 - if rng_mode == self._rng_info.RANDOM123: - seed2 = (src_pop_id * 65536 + dst_pop_id + rng_seed) - ips.setRNGs(syn_obj.synapseID + 200, tgid_seed, seed2 + 300, - syn_obj.synapseID + 200, tgid_seed, seed2 + 350) - else: - seed2 = src_pop_id * 16777216 - exprng = Nd.Random() - if rng_mode == self._rng_info.COMPATIBILITY: - exprng.MCellRan4(syn_obj.synapseID * 100000 + 200, - tgid_seed + base_seed + rng_seed) - else: # if ( rngIndo.getRNGMode()== rng_info.UPMCELLRAN4 ): - exprng.MCellRan4(syn_obj.synapseID * 1000 + 200, - seed2 + tgid_seed + base_seed + rng_seed) - - exprng.negexp(1) - uniformrng = Nd.Random() - if rng_mode == self._rng_info.COMPATIBILITY: - uniformrng.MCellRan4(syn_obj.synapseID * 100000 + 300, - tgid_seed + base_seed + rng_seed) - else: # if ( rngIndo.getRNGMode()== rng_info.UPMCELLRAN4 ): - uniformrng.MCellRan4(syn_obj.synapseID * 1000 + 300, - seed2 + tgid_seed + base_seed + rng_seed) - - uniformrng.uniform(0.0, 1.0) - ips.setRNGs(exprng, uniformrng) - self._keep_alive += (exprng, uniformrng) + seed2 = (src_pop_id * 65536 + dst_pop_id + rng_seed) + ips.setRNGs(syn_obj.synapseID + 200, tgid_seed, seed2 + 300, + syn_obj.synapseID + 200, tgid_seed, seed2 + 350) def __bool__(self): """object is considered False in case rate is not positive""" diff --git a/neurodamus/core/configuration.py b/neurodamus/core/configuration.py index 53adfc44..c67d8240 100644 --- a/neurodamus/core/configuration.py +++ b/neurodamus/core/configuration.py @@ -108,8 +108,6 @@ class CircuitConfig(ConfigT): class RNGConfig(ConfigT): - Modes = Enum("Mode", "COMPATIBILITY RANDOM123 UPMCELLRAN4") - mode = Modes.COMPATIBILITY global_seed = None IonChannelSeed = None StimulusSeed = None diff --git a/neurodamus/core/random.py b/neurodamus/core/random.py index 82af0db0..92133156 100644 --- a/neurodamus/core/random.py +++ b/neurodamus/core/random.py @@ -3,57 +3,36 @@ """ from __future__ import absolute_import from . import Neuron -from neurodamus.core.configuration import RNGConfig -# NOTE: These are pure wrappers, in the sense they dont create Python objects. Instead +# NOTE: These are pure wrappers, in the sense they don't create Python objects. Instead # Neuron objects are returned (using __new__) class RNG(object): def __new__(cls, *ids, **kw): - """Creates a default RNG, currently based on ACG""" + """Creates a default RNG (Random123) """ seed = kw.get("seed") - return Neuron.h.Random() if seed is None else Neuron.h.Random(seed) + return Random123(ids[0], ids[1], ids[2], seed) @classmethod - def create(cls, rng_mode, ids, seed=None): - # type: (RNGConfig, tuple, object) -> RNG - if rng_mode == RNGConfig.Modes.RANDOM123: - assert len(ids) == 3, "Random123 requires three ids (as a tuple)" - obj = Random123(ids[0], ids[1], ids[2], seed) - elif rng_mode == RNGConfig.Modes.UPMCELLRAN4: - assert len(ids) == 1 - obj = MCellRan4(ids[0], seed) - else: - obj = RNG(seed) + def create(cls, ids, seed=None): + # type: (tuple, object) -> RNG + assert len(ids) == 3, "Random123 requires three ids (as a tuple)" + obj = Random123(ids[0], ids[1], ids[2], seed) return obj -class ACG(RNG): - def __new__(cls, size=None, seed=None): - rng = RNG(seed=seed) - rng.ACG(seed, size) - return rng - - class Random123(RNG): def __new__(cls, id1, id2, id3, seed=None): - rng = RNG() + rng = Neuron.h.Random() if seed is None else Neuron.h.Random(seed) if seed is not None: rng.Random123_globalindex(seed) rng.Random123(id1, id2, id3) return rng -class MCellRan4(RNG): - def __new__(cls, high_i, seed=None, low_i=0): - rng = RNG(seed=seed) - rng.MCellRan4(high_i, low_i) - return rng - - # Gamma-distributed sample generator (not available in NEURON) def gamma(rng, a, b, N=1): """ diff --git a/neurodamus/data/hoc/RNGSettings.hoc b/neurodamus/data/hoc/RNGSettings.hoc index 51819462..f097c929 100644 --- a/neurodamus/data/hoc/RNGSettings.hoc +++ b/neurodamus/data/hoc/RNGSettings.hoc @@ -7,7 +7,6 @@ */ {load_file("fileUtils.hoc")} -rngMode = 0 // corresponds to COMPATIBILITY defined below ionchannelSeed = 0 synapseSeed = 0 stimulusSeed = 0 @@ -16,16 +15,11 @@ globalSeed = 0 begintemplate RNGSettings -public init, interpret, getRNGMode, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed -public COMPATIBILITY, RANDOM123, UPMCELLRAN4 +public init, interpret, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed -external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate +external ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate proc init() { - // consts for random number handling - COMPATIBILITY = 0 - RANDOM123 = 1 - UPMCELLRAN4 = 2 } /** @@ -37,7 +31,6 @@ proc init() { proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng strdef rngModeField runInfo = $o1 - rngMode = RANDOM123 coreNeuronUsed = $2 if (numarg() < 2) { coreNeuronUsed = 0 @@ -48,42 +41,14 @@ proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng print "As of Oct 2020 Neurodamus uses Random123 as default" } - if( runInfo.exists( "RNGMode" ) ) { - rngModeField = runInfo.get( "RNGMode" ).s - if( strcmp( rngModeField, "Compatibility" ) == 0 ) { - rngMode = COMPATIBILITY - } else if( strcmp( rngModeField, "UpdatedMCell" ) == 0 ) { - rngMode = UPMCELLRAN4 - } else if( strcmp( rngModeField, "Random123" ) == 0 ) { - rngMode = RANDOM123 - } else { - terminate( "Invalid RNGMode ", rngModeField ) - } - if( pc.id() == 0 ) { - print "RNGMode set to ", rngMode, " [COMPATIBILITY=0, RANDOM123=1, UPMCELLRAN4=2]" - } - } - - if( coreNeuronUsed && rngMode != RANDOM123) { - // Given R123 is now the default there's no sense to allow an explicit invalid option - terminate( "Config Error: CoreNEURON requires Random123 RNG" ) - } - if( runInfo.exists("BaseSeed") ) { globalSeed = runInfo.valueOf( "BaseSeed" ) // random123 can set the global index now - if( rngMode == RANDOM123 ) { - rng = new Random() - rng.Random123_globalindex( globalSeed ) - } + rng = new Random() + rng.Random123_globalindex( globalSeed ) } - // don't search for indicidual seeds if we are in compatibility mode - if( rngMode == COMPATIBILITY ) { - return - } - if( runInfo.exists("IonChannelSeed") ) { ionchannelSeed = runInfo.valueOf( "IonChannelSeed" ) } @@ -98,10 +63,6 @@ proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng } } -func getRNGMode() { - return rngMode -} - func getIonChannelSeed() { return ionchannelSeed } @@ -123,120 +84,3 @@ func getGlobalSeed() { } endtemplate RNGSettings - - -/////////////////////////////////////////////////////////////////////// -/// -/// These are legacy functions to keep compaltibility -/// -/////////////////////////////////////////////////////////////////////// - -/*! - * In place of using a CCell's re_init_rng function, we will check for cells that define the re_init_rng function, - * but then setRNG using global seed as well - * - * @param $o1 CCell which is to be checked for setRNG - */ -obfunc rngForStochKvInit() { local channelID, hasStochKv localobj CCell, rng, rngInfo, rngList, pc - CCell = $o1 - //print "Replace rng for stochKv in gid ", CCell.CellRef.gid - - // quick check to verify this object contains StochKv - hasStochKv = 0 - CCell.CellRef.soma { - if( ismembrane( "StochKv" ) ) { - hasStochKv = 1 - } - } - - if( !hasStochKv ) { - return - } - - rngList = new List() - - channelID = 0 - forsec CCell.CellRef.somatic { - for (x, 0) { - setdata_StochKv(x) - rng = new Random() - rng.MCellRan4( channelID*10000+100, globalSeed + ionchannelSeed + CCell.CellRef.gid*10000+250+1 ) - channelID = channelID + 1 - rng.uniform(0,1) - setRNG_StochKv(rng) - rngList.append(rng) - } - } - forsec CCell.CellRef.basal { - for (x, 0) { - setdata_StochKv(x) - rng = new Random() - rng.MCellRan4( channelID*10000+100, globalSeed + ionchannelSeed + CCell.CellRef.gid*10000+250+1 ) - channelID = channelID + 1 - rng.uniform(0,1) - setRNG_StochKv(rng) - rngList.append(rng) - } - } - forsec CCell.CellRef.apical { - for (x, 0) { - setdata_StochKv(x) - rng = new Random() - rng.MCellRan4( channelID*10000+100, globalSeed + ionchannelSeed + CCell.CellRef.gid*10000+250+1 ) - channelID = channelID + 1 - rng.uniform(0,1) - setRNG_StochKv(rng) - rngList.append(rng) - } - } - - return rngList -} - -//----------------------------------------------------------------------------------------------- - -/*! - * In place of using a CCell's re_init_rng function, we will check for cells that define the re_init_rng function, - * but then setRNG using random123 - * - * @param $o1 CCell which is to be checked for setRNG - */ -proc rng123ForStochKvInit() { local channelID, hasStochKv localobj CCell - CCell = $o1 - //print "Replace rng for stochKv in gid ", CCell.CellRef.gid - - // quick check to verify this object contains StochKv - hasStochKv = 0 - CCell.CellRef.soma { - if( ismembrane( "StochKv" ) ) { - hasStochKv = 1 - } - } - - if( !hasStochKv ) { - return - } - - channelID = 0 - forsec CCell.CellRef.somatic { - for (x, 0) { - setdata_StochKv(x) - setRNG_StochKv(ionchannelSeed, channelID, CCell.CellRef.gid + 1) - channelID = channelID + 1 - } - } - forsec CCell.CellRef.basal { - for (x, 0) { - setdata_StochKv(x) - setRNG_StochKv(ionchannelSeed, channelID, CCell.CellRef.gid + 1) - channelID = channelID + 1 - } - } - forsec CCell.CellRef.apical { - for (x, 0) { - setdata_StochKv(x) - setRNG_StochKv(ionchannelSeed, channelID, CCell.CellRef.gid + 1) - channelID = channelID + 1 - } - } -} diff --git a/neurodamus/stimulus_manager.py b/neurodamus/stimulus_manager.py index 169e7a86..fd5b1c9a 100644 --- a/neurodamus/stimulus_manager.py +++ b/neurodamus/stimulus_manager.py @@ -551,15 +551,11 @@ def __init__(self, target, stim_info: dict, cell_manager): self.parse_check_all_parameters(stim_info) sim_dt = float(SimConfig.run_conf["Dt"]) # simulation time-step [ms] - rng_mode = SimConfig.rng_info.getRNGMode() # simulation RNGMode # setup RNG - if rng_mode == SimConfig.rng_info.RANDOM123: - rand = lambda gid: random.Random123(Noise.stimCount + 100, - SimConfig.rng_info.getStimulusSeed() + 500, - gid + 300) - else: - raise Exception("rng_mod is not RANDOM123") + rand = lambda gid: random.Random123(Noise.stimCount + 100, + SimConfig.rng_info.getStimulusSeed() + 500, + gid + 300) # apply stim to each point in target tpoints = target.getPointList(cell_manager) diff --git a/tests/integration/test_neuron.py b/tests/integration/test_neuron.py index 1d062094..b7554477 100644 --- a/tests/integration/test_neuron.py +++ b/tests/integration/test_neuron.py @@ -15,4 +15,4 @@ def test_base_h(): def neurodamus(): from neurodamus.core import NeurodamusCore as Nd rng_conf = Nd.RNGSettings() - assert rng_conf.RANDOM123 == 1 + assert rng_conf.getGlobalSeed() == 0 From a6baddd1c8b70f2bb271610bf8c2e4e0ce7bb27b Mon Sep 17 00:00:00 2001 From: Alexander Temerev Date: Thu, 29 Feb 2024 16:16:06 +0100 Subject: [PATCH 2/5] Keep rngMode in RNGSettings.hoc --- neurodamus/data/hoc/RNGSettings.hoc | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/neurodamus/data/hoc/RNGSettings.hoc b/neurodamus/data/hoc/RNGSettings.hoc index f097c929..86801e9b 100644 --- a/neurodamus/data/hoc/RNGSettings.hoc +++ b/neurodamus/data/hoc/RNGSettings.hoc @@ -7,6 +7,7 @@ */ {load_file("fileUtils.hoc")} +rngMode = 1 // corresponds to RANDOM123 defined below ionchannelSeed = 0 synapseSeed = 0 stimulusSeed = 0 @@ -15,11 +16,14 @@ globalSeed = 0 begintemplate RNGSettings -public init, interpret, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed +public init, interpret, getRNGMode, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed +public RANDOM123 -external ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate +external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate proc init() { + // consts for random number handling + RANDOM123 = 1 } /** @@ -31,6 +35,7 @@ proc init() { proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng strdef rngModeField runInfo = $o1 + rngMode = RANDOM123 coreNeuronUsed = $2 if (numarg() < 2) { coreNeuronUsed = 0 @@ -41,6 +46,15 @@ proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng print "As of Oct 2020 Neurodamus uses Random123 as default" } + if( runInfo.exists( "RNGMode" ) ) { + rngModeField = runInfo.get( "RNGMode" ).s + if( strcmp( rngModeField, "Random123" ) == 0 ) { + rngMode = RANDOM123 + } else { + terminate( "Invalid RNGMode (only Random123 is currently supported) ", rngModeField ) + } + } + if( runInfo.exists("BaseSeed") ) { globalSeed = runInfo.valueOf( "BaseSeed" ) @@ -63,6 +77,10 @@ proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng } } +func getRNGMode() { + return rngMode +} + func getIonChannelSeed() { return ionchannelSeed } From ed9eb83144e60037609ea537128865c67d1c50cc Mon Sep 17 00:00:00 2001 From: Alexander Temerev Date: Fri, 24 May 2024 15:24:04 +0200 Subject: [PATCH 3/5] default (P)RNG (Random123 with ids 0,1,2 and seed 0) --- neurodamus/core/random.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/neurodamus/core/random.py b/neurodamus/core/random.py index 92133156..971293a3 100644 --- a/neurodamus/core/random.py +++ b/neurodamus/core/random.py @@ -10,10 +10,9 @@ class RNG(object): - def __new__(cls, *ids, **kw): - """Creates a default RNG (Random123) """ - seed = kw.get("seed") - return Random123(ids[0], ids[1], ids[2], seed) + def __new__(cls): + """Creates a default RNG (Random123)""" + return Random123(0, 1, 2, 0) @classmethod def create(cls, ids, seed=None): From f5ae24535568175bc88f76cdaba7d352cc4c9e3e Mon Sep 17 00:00:00 2001 From: Alexander Temerev Date: Fri, 24 May 2024 15:30:54 +0200 Subject: [PATCH 4/5] RNG() default constructor as it was in the main branch (ignoring the ids) --- neurodamus/core/random.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/neurodamus/core/random.py b/neurodamus/core/random.py index 971293a3..5644a353 100644 --- a/neurodamus/core/random.py +++ b/neurodamus/core/random.py @@ -10,9 +10,10 @@ class RNG(object): - def __new__(cls): - """Creates a default RNG (Random123)""" - return Random123(0, 1, 2, 0) + def __new__(cls, **kw): + """Creates a default RNG, currently based on ACG""" + seed = kw.get("seed", 0) + return Random123(0, 1, 2, seed) @classmethod def create(cls, ids, seed=None): From 07f0e061f393dcd5d387a59ab9860102e5e00150 Mon Sep 17 00:00:00 2001 From: Alexander Temerev Date: Fri, 24 May 2024 21:58:04 +0200 Subject: [PATCH 5/5] RNG() docstring --- neurodamus/core/random.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neurodamus/core/random.py b/neurodamus/core/random.py index 5644a353..b3e161d9 100644 --- a/neurodamus/core/random.py +++ b/neurodamus/core/random.py @@ -11,7 +11,7 @@ class RNG(object): def __new__(cls, **kw): - """Creates a default RNG, currently based on ACG""" + """Creates a default RNG (Random123 with ids of 0,1,2)""" seed = kw.get("seed", 0) return Random123(0, 1, 2, seed)