Skip to content

Commit

Permalink
[BBPBGLIB-1122] Removing legacy RNGs, leaving only Random123 (#136)
Browse files Browse the repository at this point in the history
Removing legacy RNGs and the ability to configure RNGs. Random123 is
chosen everywhere as the only option.

## Context

RNG types differing from Random123 are no longer used and removed from
everywhere (including configuration).

## Scope

RNGSettings.hoc (removed RNGmode and all related constants),
configuration.py, random.py, and corresponding usages.

## Testing

Write a test that raises exception in attempt to configure RNG mode.

## Review
* [X] PR description is complete
* [X] Coding style (imports, function length, New functions, classes or
files) are good
* [ ] Unit/Scientific test added
* [ ] Updated Readme, in-code, developer documentation
  • Loading branch information
atemerev authored May 28, 2024
1 parent 650f872 commit b302ede
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 212 deletions.
30 changes: 3 additions & 27 deletions neurodamus/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
2 changes: 0 additions & 2 deletions neurodamus/core/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 10 additions & 31 deletions neurodamus/core/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
seed = kw.get("seed")
return Neuron.h.Random() if seed is None else Neuron.h.Random(seed)
def __new__(cls, **kw):
"""Creates a default RNG (Random123 with ids of 0,1,2)"""
seed = kw.get("seed", 0)
return Random123(0, 1, 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):
"""
Expand Down
150 changes: 6 additions & 144 deletions neurodamus/data/hoc/RNGSettings.hoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
{load_file("fileUtils.hoc")}

rngMode = 0 // corresponds to COMPATIBILITY defined below
rngMode = 1 // corresponds to RANDOM123 defined below
ionchannelSeed = 0
synapseSeed = 0
stimulusSeed = 0
Expand All @@ -17,15 +17,13 @@ globalSeed = 0
begintemplate RNGSettings

public init, interpret, getRNGMode, getIonChannelSeed, getSynapseSeed, getStimulusSeed, getMinisSeed, getGlobalSeed
public COMPATIBILITY, RANDOM123, UPMCELLRAN4
public RANDOM123

external rngMode, ionchannelSeed, synapseSeed, stimulusSeed, minisSeed, globalSeed, terminate

proc init() {
// consts for random number handling
COMPATIBILITY = 0
RANDOM123 = 1
UPMCELLRAN4 = 2
}

/**
Expand All @@ -50,40 +48,21 @@ proc interpret() { local coreNeuronUsed localobj runInfo, pc, rng

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 ) {
if( strcmp( rngModeField, "Random123" ) == 0 ) {
rngMode = RANDOM123
} else {
terminate( "Invalid RNGMode ", rngModeField )
terminate( "Invalid RNGMode (only Random123 is currently supported) ", 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" )
}
Expand Down Expand Up @@ -123,120 +102,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
}
}
}
10 changes: 3 additions & 7 deletions neurodamus/stimulus_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,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)
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit b302ede

Please sign in to comment.