From cba27bdb01d6f377b922aefb498b8694878f4889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Wed, 9 Aug 2023 10:56:32 +0200 Subject: [PATCH 1/9] allow numpy in scalers --- bluepyopt/ephys/parameterscalers/parameterscalers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bluepyopt/ephys/parameterscalers/parameterscalers.py b/bluepyopt/ephys/parameterscalers/parameterscalers.py index 47f5189a..2b7eb40b 100644 --- a/bluepyopt/ephys/parameterscalers/parameterscalers.py +++ b/bluepyopt/ephys/parameterscalers/parameterscalers.py @@ -199,6 +199,7 @@ def scale(self, values, segment, sim=None): # Find something to generalise this import math # pylint:disable=W0611 #NOQA + import numpy # This eval is unsafe (but is it ever dangerous ?) # pylint: disable=W0123 From d506c7c597f37a8d3e9954360fe7c681f3c7ee3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Thu, 24 Aug 2023 13:18:19 +0200 Subject: [PATCH 2/9] Add NrnSegmentSomaDistanceStepScaler --- .../parameterscalers/parameterscalers.py | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/bluepyopt/ephys/parameterscalers/parameterscalers.py b/bluepyopt/ephys/parameterscalers/parameterscalers.py index 2b7eb40b..8fbcafd7 100644 --- a/bluepyopt/ephys/parameterscalers/parameterscalers.py +++ b/bluepyopt/ephys/parameterscalers/parameterscalers.py @@ -161,9 +161,8 @@ def inst_distribution(self): # Use this special formatting to bypass missing keys return string.Formatter().vformat(self.distribution, (), dist_dict) - def eval_dist(self, values, distance): - """Create the final dist string""" - + def scale_dict(self, values, distance): + """Create scale disctionary""" scale_dict = {} if isinstance(values, dict): for k, v in values.items(): @@ -172,6 +171,12 @@ def eval_dist(self, values, distance): scale_dict["value"] = format_float(values) scale_dict["distance"] = format_float(distance) + return scale_dict + + def eval_dist(self, values, distance): + """Create the final dist string""" + scale_dict = self.scale_dict(values, distance) + return self.inst_distribution.format(**scale_dict) def scale(self, values, segment, sim=None): @@ -262,3 +267,51 @@ def acc_scale_iexpr(self, value, constant_formatter=format_float): ArbFileMorphology.region_labels['somatic'].ref ) return generate_acc_scale_iexpr(iexpr, variables, constant_formatter) + + +class NrnSegmentSomaDistanceStepScaler(NrnSegmentSomaDistanceScaler, + ParameterScaler, DictMixin): + + """Scaler based on distance from soma with a step function""" + SERIALIZED_FIELDS = ('name', 'comment', 'distribution', ) + + def __init__( + self, + name=None, + distribution=None, + comment='', + dist_param_names=None, + soma_ref_location=0.5, + step_begin=None, + step_end=None): + """Constructor + Args: + name (str): name of this object + distribution (str): distribution of parameter dependent on distance + from soma. string can contain `distance` and/or `value` as + placeholders for the distance to the soma and parameter value + respectivily + dist_param_names (list): list of names of parameters that + parametrise the distribution. These names will become + attributes of this object. + The distribution string should contain these names, and they + will be replaced by values of the corresponding attributes + soma_ref_location (float): location along the soma used as origin + from which to compute the distances. Expressed as a fraction + (between 0.0 and 1.0). + step_begin (float): distance at which the step begins + step_end (float): distance at which the step ends + """ + + super(NrnSegmentSomaDistanceStepScaler, self).__init__( + name, distribution, comment, dist_param_names, + soma_ref_location=soma_ref_location) + self.step_begin = step_begin + self.step_end = step_end + + def scale_dict(self, values, distance): + scale_dict = super().scale_dict(values, distance) + scale_dict["step_begin"] = self.step_begin + scale_dict["step_end"] = self.step_end + + return scale_dict From 130d990d27104e2fafc1791a10c18ea51c5924a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Thu, 5 Oct 2023 16:20:38 +0200 Subject: [PATCH 3/9] allow to use '&' and the distance twice in hoc distribution --- bluepyopt/ephys/create_hoc.py | 1 + bluepyopt/ephys/templates/cell_template.jinja2 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bluepyopt/ephys/create_hoc.py b/bluepyopt/ephys/create_hoc.py index a287571b..f397d3c0 100644 --- a/bluepyopt/ephys/create_hoc.py +++ b/bluepyopt/ephys/create_hoc.py @@ -105,6 +105,7 @@ def range_exprs_to_hoc(range_params): for param in range_params: value = param.value_scaler.inst_distribution value = re.sub(r'math\.', '', value) + value = re.sub(r'\&', '&&', value) value = re.sub('{distance}', FLOAT_FORMAT, value) value = re.sub('{value}', format_float(param.value), value) ret.append(Range(param.location, param.name, value)) diff --git a/bluepyopt/ephys/templates/cell_template.jinja2 b/bluepyopt/ephys/templates/cell_template.jinja2 index 33210a3c..2b964641 100644 --- a/bluepyopt/ephys/templates/cell_template.jinja2 +++ b/bluepyopt/ephys/templates/cell_template.jinja2 @@ -120,7 +120,7 @@ proc distribute_distance(){local x localobj sl this.soma[0] distance(0, 0.5) sprint(distfunc, "%%s %s(%%f) = %s", mech, distfunc) forsec sl for(x, 0) { - sprint(stmp, distfunc, secname(), x, distance(x)) + sprint(stmp, distfunc, secname(), x, distance(x), distance(x)) execute(stmp) } } From 635528fb91598824263eefcd42f120316895a952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Thu, 25 Jan 2024 14:09:46 +0100 Subject: [PATCH 4/9] Use step_begin, step_end when exporting to hoc if present --- bluepyopt/ephys/create_hoc.py | 3 +++ bluepyopt/ephys/parameterscalers/parameterscalers.py | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bluepyopt/ephys/create_hoc.py b/bluepyopt/ephys/create_hoc.py index f397d3c0..3c7552b6 100644 --- a/bluepyopt/ephys/create_hoc.py +++ b/bluepyopt/ephys/create_hoc.py @@ -108,6 +108,9 @@ def range_exprs_to_hoc(range_params): value = re.sub(r'\&', '&&', value) value = re.sub('{distance}', FLOAT_FORMAT, value) value = re.sub('{value}', format_float(param.value), value) + if hasattr(param.value_scaler, "step_begin"): + value = re.sub('{step_begin}', format_float(param.value_scaler.step_begin), value) + value = re.sub('{step_end}', format_float(param.value_scaler.step_end), value) ret.append(Range(param.location, param.name, value)) return ret diff --git a/bluepyopt/ephys/parameterscalers/parameterscalers.py b/bluepyopt/ephys/parameterscalers/parameterscalers.py index 8fbcafd7..aed29f6f 100644 --- a/bluepyopt/ephys/parameterscalers/parameterscalers.py +++ b/bluepyopt/ephys/parameterscalers/parameterscalers.py @@ -162,7 +162,7 @@ def inst_distribution(self): return string.Formatter().vformat(self.distribution, (), dist_dict) def scale_dict(self, values, distance): - """Create scale disctionary""" + """Create scale dictionary""" scale_dict = {} if isinstance(values, dict): for k, v in values.items(): @@ -204,7 +204,6 @@ def scale(self, values, segment, sim=None): # Find something to generalise this import math # pylint:disable=W0611 #NOQA - import numpy # This eval is unsafe (but is it ever dangerous ?) # pylint: disable=W0123 @@ -290,7 +289,7 @@ def __init__( distribution (str): distribution of parameter dependent on distance from soma. string can contain `distance` and/or `value` as placeholders for the distance to the soma and parameter value - respectivily + respectivily. It can also contain step_begin and step_end. dist_param_names (list): list of names of parameters that parametrise the distribution. These names will become attributes of this object. From 92071023eb30a5c56697fb900e01b15f91409822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 26 Jan 2024 09:23:41 +0100 Subject: [PATCH 5/9] style fix --- bluepyopt/ephys/create_hoc.py | 10 ++++++++-- bluepyopt/ephys/parameterscalers/parameterscalers.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bluepyopt/ephys/create_hoc.py b/bluepyopt/ephys/create_hoc.py index 3c7552b6..b1a340c1 100644 --- a/bluepyopt/ephys/create_hoc.py +++ b/bluepyopt/ephys/create_hoc.py @@ -109,8 +109,14 @@ def range_exprs_to_hoc(range_params): value = re.sub('{distance}', FLOAT_FORMAT, value) value = re.sub('{value}', format_float(param.value), value) if hasattr(param.value_scaler, "step_begin"): - value = re.sub('{step_begin}', format_float(param.value_scaler.step_begin), value) - value = re.sub('{step_end}', format_float(param.value_scaler.step_end), value) + value = re.sub( + '{step_begin}', + format_float(param.value_scaler.step_begin), + value + ) + value = re.sub( + '{step_end}', format_float(param.value_scaler.step_end), value + ) ret.append(Range(param.location, param.name, value)) return ret diff --git a/bluepyopt/ephys/parameterscalers/parameterscalers.py b/bluepyopt/ephys/parameterscalers/parameterscalers.py index aed29f6f..14fcb94c 100644 --- a/bluepyopt/ephys/parameterscalers/parameterscalers.py +++ b/bluepyopt/ephys/parameterscalers/parameterscalers.py @@ -269,7 +269,7 @@ def acc_scale_iexpr(self, value, constant_formatter=format_float): class NrnSegmentSomaDistanceStepScaler(NrnSegmentSomaDistanceScaler, - ParameterScaler, DictMixin): + ParameterScaler, DictMixin): """Scaler based on distance from soma with a step function""" SERIALIZED_FIELDS = ('name', 'comment', 'distribution', ) From 656580bf0b9cbc0eef822b011f08106c7aad34f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 26 Jan 2024 09:42:45 +0100 Subject: [PATCH 6/9] update stochkv test data --- examples/stochkv/stochkv3cell.hoc | 2 +- examples/stochkv/stochkv3cell_det.hoc | 2 +- examples/stochkv/stochkvcell.hoc | 2 +- examples/stochkv/stochkvcell_det.hoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/stochkv/stochkv3cell.hoc b/examples/stochkv/stochkv3cell.hoc index b019b4a8..4224e882 100644 --- a/examples/stochkv/stochkv3cell.hoc +++ b/examples/stochkv/stochkv3cell.hoc @@ -98,7 +98,7 @@ proc distribute_distance(){local x localobj sl this.soma[0] distance(0, 0.5) sprint(distfunc, "%%s %s(%%f) = %s", mech, distfunc) forsec sl for(x, 0) { - sprint(stmp, distfunc, secname(), x, distance(x)) + sprint(stmp, distfunc, secname(), x, distance(x), distance(x)) execute(stmp) } } diff --git a/examples/stochkv/stochkv3cell_det.hoc b/examples/stochkv/stochkv3cell_det.hoc index 54b14b08..186179a2 100644 --- a/examples/stochkv/stochkv3cell_det.hoc +++ b/examples/stochkv/stochkv3cell_det.hoc @@ -98,7 +98,7 @@ proc distribute_distance(){local x localobj sl this.soma[0] distance(0, 0.5) sprint(distfunc, "%%s %s(%%f) = %s", mech, distfunc) forsec sl for(x, 0) { - sprint(stmp, distfunc, secname(), x, distance(x)) + sprint(stmp, distfunc, secname(), x, distance(x), distance(x)) execute(stmp) } } diff --git a/examples/stochkv/stochkvcell.hoc b/examples/stochkv/stochkvcell.hoc index f3296d15..a777a6c2 100644 --- a/examples/stochkv/stochkvcell.hoc +++ b/examples/stochkv/stochkvcell.hoc @@ -98,7 +98,7 @@ proc distribute_distance(){local x localobj sl this.soma[0] distance(0, 0.5) sprint(distfunc, "%%s %s(%%f) = %s", mech, distfunc) forsec sl for(x, 0) { - sprint(stmp, distfunc, secname(), x, distance(x)) + sprint(stmp, distfunc, secname(), x, distance(x), distance(x)) execute(stmp) } } diff --git a/examples/stochkv/stochkvcell_det.hoc b/examples/stochkv/stochkvcell_det.hoc index 63244811..6e90e6a1 100644 --- a/examples/stochkv/stochkvcell_det.hoc +++ b/examples/stochkv/stochkvcell_det.hoc @@ -98,7 +98,7 @@ proc distribute_distance(){local x localobj sl this.soma[0] distance(0, 0.5) sprint(distfunc, "%%s %s(%%f) = %s", mech, distfunc) forsec sl for(x, 0) { - sprint(stmp, distfunc, secname(), x, distance(x)) + sprint(stmp, distfunc, secname(), x, distance(x), distance(x)) execute(stmp) } } From 86307ecbac018f3fe3a82439a41da9764b59c2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 26 Jan 2024 10:30:21 +0100 Subject: [PATCH 7/9] add tests for new scaler and modified create_hoc --- bluepyopt/tests/test_ephys/test_create_hoc.py | 32 +++++++++++++++++++ .../tests/test_ephys/test_parameterscalers.py | 16 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/bluepyopt/tests/test_ephys/test_create_hoc.py b/bluepyopt/tests/test_ephys/test_create_hoc.py index 4666d4e3..19153a52 100644 --- a/bluepyopt/tests/test_ephys/test_create_hoc.py +++ b/bluepyopt/tests/test_ephys/test_create_hoc.py @@ -5,7 +5,9 @@ import os from bluepyopt.ephys.acc import ArbLabel +from bluepyopt.ephys.locations import NrnSomaDistanceCompLocation from bluepyopt.ephys.parameterscalers import NrnSegmentSomaDistanceScaler +from bluepyopt.ephys.parameterscalers import NrnSegmentSomaDistanceStepScaler from . import utils from bluepyopt.ephys import create_acc, create_hoc @@ -151,3 +153,33 @@ def test_range_exprs_to_hoc(): assert hoc[0].param_name == 'gkbar_hh' val_gt = '(-0.8696 + 2.087*exp((%.17g)*0.0031))*0.025000000000000001' assert hoc[0].value == val_gt + + +@pytest.mark.unit +def test_range_exprs_to_hoc_step_scaler(): + """ephys.create_hoc: Test range_exprs_to_hoc with step scaler""" + # apical_region = ArbLabel("region", "apic", "(tag 4)") + apical_location = NrnSomaDistanceCompLocation( + name='apic100', + soma_distance=100, + seclist_name='apical', + ) + param_scaler = NrnSegmentSomaDistanceStepScaler( + name='soma-distance-step-scaler', + distribution='{value} * (0.1 + 0.9 * int(' + '({distance} > {step_begin}) & (' + '{distance} < {step_end})))', + step_begin=300, + step_end=500) + + range_expr = create_hoc.RangeExpr( + location=apical_location, + name="gCa_LVAstbar_Ca_LVAst", + value=1, + value_scaler=param_scaler + ) + + hoc = create_hoc.range_exprs_to_hoc([range_expr]) + assert hoc[0].param_name == 'gCa_LVAstbar_Ca_LVAst' + val_gt = '1 * (0.1 + 0.9 * int((%.17g > 300) && (%.17g < 500)))' + assert hoc[0].value == val_gt diff --git a/bluepyopt/tests/test_ephys/test_parameterscalers.py b/bluepyopt/tests/test_ephys/test_parameterscalers.py index 7d0380e9..a10d88d2 100644 --- a/bluepyopt/tests/test_ephys/test_parameterscalers.py +++ b/bluepyopt/tests/test_ephys/test_parameterscalers.py @@ -49,6 +49,22 @@ def test_NrnSegmentSectionDistanceScaler_eval_dist_with_dict(): assert (scaler.eval_dist(values=_values, distance=10) == '0.5 + (1 - (abs(10 - 8) / 4)) * 1') + + +@pytest.mark.unit +def test_NrnSegmentSomaDistanceStepScaler_eval_dist_with_dict(): + """ephys.parameterscalers: eval_dist of NrnSegmentSomaDistanceStepScaler""" + + dist = '{value} * (0.1 + 0.9 * int(' \ + '({distance} > {step_begin}) & ({distance} < {step_end})))' + + scaler = ephys.parameterscalers.NrnSegmentSomaDistanceStepScaler( + distribution=dist, step_begin=300, step_end=500) + + _values = {'value': 1} + + assert (scaler.eval_dist(values=_values, distance=10) + == '1 * (0.1 + 0.9 * int((10 > 300) & (10 < 500)))') @pytest.mark.unit From 37162af13e98cf5b379cbb77fafab6c065c49f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 26 Jan 2024 10:31:03 +0100 Subject: [PATCH 8/9] style fix --- bluepyopt/tests/test_ephys/test_parameterscalers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluepyopt/tests/test_ephys/test_parameterscalers.py b/bluepyopt/tests/test_ephys/test_parameterscalers.py index a10d88d2..846bf023 100644 --- a/bluepyopt/tests/test_ephys/test_parameterscalers.py +++ b/bluepyopt/tests/test_ephys/test_parameterscalers.py @@ -49,7 +49,7 @@ def test_NrnSegmentSectionDistanceScaler_eval_dist_with_dict(): assert (scaler.eval_dist(values=_values, distance=10) == '0.5 + (1 - (abs(10 - 8) / 4)) * 1') - + @pytest.mark.unit def test_NrnSegmentSomaDistanceStepScaler_eval_dist_with_dict(): From 21ebe168d6ab1675a9e3478a8042af84efa87ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 26 Jan 2024 11:18:28 +0100 Subject: [PATCH 9/9] fix typo --- bluepyopt/ephys/parameterscalers/parameterscalers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bluepyopt/ephys/parameterscalers/parameterscalers.py b/bluepyopt/ephys/parameterscalers/parameterscalers.py index 14fcb94c..342cb61f 100644 --- a/bluepyopt/ephys/parameterscalers/parameterscalers.py +++ b/bluepyopt/ephys/parameterscalers/parameterscalers.py @@ -289,7 +289,7 @@ def __init__( distribution (str): distribution of parameter dependent on distance from soma. string can contain `distance` and/or `value` as placeholders for the distance to the soma and parameter value - respectivily. It can also contain step_begin and step_end. + respectively. It can also contain step_begin and step_end. dist_param_names (list): list of names of parameters that parametrise the distribution. These names will become attributes of this object.