diff --git a/bluepyopt/ephys/create_hoc.py b/bluepyopt/ephys/create_hoc.py index a287571b..b1a340c1 100644 --- a/bluepyopt/ephys/create_hoc.py +++ b/bluepyopt/ephys/create_hoc.py @@ -105,8 +105,18 @@ 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) + 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 47f5189a..342cb61f 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 dictionary""" 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): @@ -261,3 +266,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 + 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. + 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 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) } } 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..846bf023 100644 --- a/bluepyopt/tests/test_ephys/test_parameterscalers.py +++ b/bluepyopt/tests/test_ephys/test_parameterscalers.py @@ -51,6 +51,22 @@ def test_NrnSegmentSectionDistanceScaler_eval_dist_with_dict(): == '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 def test_serialize(): """ephys.parameterscalers: test serialization""" 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) } }