Skip to content

Commit

Permalink
Merge pull request #169 from BlueBrain/json-nan-to-null
Browse files Browse the repository at this point in the history
- Fix NaN values in JSON
- Ensure 'current' array operations use float64 to prevent casting error
- Refactor sAHP unit test
- Add APThreshold unit test
  • Loading branch information
ilkilic authored Jan 26, 2024
2 parents 0ae5960 + 9d587e8 commit e438278
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 150 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ MouseCells/
.ipynb_checkpoints/
coverage.xml
MouseCells_sAHP/
MouseCells_APThreshold/
tests/exp_data/X/
6 changes: 3 additions & 3 deletions bluepyefe/ecode/DeHyperPol.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ def generate(self):
toff = int(self.toff / self.dt)

time = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(time.shape, self.hypamp)
current[ton:tmid] += self.amp
current[tmid:toff] += self.amp2
current = numpy.full(time.shape, numpy.float64(self.hypamp))
current[ton:tmid] += numpy.float64(self.amp)
current[tmid:toff] += numpy.float64(self.amp2)

return time, current

Expand Down
6 changes: 3 additions & 3 deletions bluepyefe/ecode/HyperDePol.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ def generate(self):
toff = int(self.toff / self.dt)

time = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(time.shape, self.hypamp)
current[ton:tmid] += self.amp
current[tmid:toff] += self.amp2
current = numpy.full(time.shape, numpy.float64(self.hypamp))
current[ton:tmid] += numpy.float64(self.amp)
current[tmid:toff] += numpy.float64(self.amp2)

return time, current

Expand Down
6 changes: 3 additions & 3 deletions bluepyefe/ecode/SpikeRec.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,16 @@ def generate(self):
"""Generate the step current array from the parameters of the ecode"""

t = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(t.shape, self.hypamp)
current = numpy.full(t.shape, numpy.float64(self.hypamp))

spike_start = int(self.tspike[0] / self.dt)
spike_end = int((self.tspike[0] + self.spike_duration) / self.dt)
current[spike_start:spike_end] += self.amp
current[spike_start:spike_end] += numpy.float64(self.amp)

for i in range(1, len(self.tspike)):
spike_start = int(spike_end + (self.delta / self.dt))
spike_end = spike_start + int(self.spike_duration / self.dt)
current[spike_start:spike_end] += self.amp
current[spike_start:spike_end] += numpy.float64(self.amp)

return t, current

Expand Down
2 changes: 1 addition & 1 deletion bluepyefe/ecode/negCheops.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def generate(self):
toff = int(self.toff / self.dt)

time = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(time.shape, self.hypamp)
current = numpy.full(time.shape, numpy.float64(self.hypamp))

# First peak
mid = int(0.5 * (ton + t1))
Expand Down
2 changes: 1 addition & 1 deletion bluepyefe/ecode/posCheops.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def generate(self):
toff = int(self.toff / self.dt)

time = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(time.shape, self.hypamp)
current = numpy.full(time.shape, numpy.float64(self.hypamp))

# First peak
mid = int(0.5 * (self.ton + self.t1) / self.dt)
Expand Down
2 changes: 1 addition & 1 deletion bluepyefe/ecode/ramp.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def generate(self):
toff_idx = int(self.toff / self.dt)

t = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(t.shape, self.hypamp)
current = numpy.full(t.shape, numpy.float64(self.hypamp))
current[ton_idx:toff_idx] += numpy.linspace(
0.0, self.amp, toff_idx - ton_idx
)
Expand Down
8 changes: 4 additions & 4 deletions bluepyefe/ecode/sAHP.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,10 @@ def generate(self):
toff = int(self.toff / self.dt)

time = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(time.shape, self.hypamp)
current[ton:tmid] += self.amp
current[tmid2:toff] += self.amp
current[tmid:tmid2] += self.amp2
current = numpy.full(time.shape, numpy.float64(self.hypamp))
current[ton:tmid] += numpy.float64(self.amp)
current[tmid2:toff] += numpy.float64(self.amp)
current[tmid:tmid2] += numpy.float64(self.amp2)

return time, current

Expand Down
4 changes: 2 additions & 2 deletions bluepyefe/ecode/step.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def generate(self):
toff_idx = int(self.toff / self.dt)

t = numpy.arange(0.0, self.tend, self.dt)
current = numpy.full(t.shape, self.hypamp)
current[ton_idx:toff_idx] += self.amp
current = numpy.full(t.shape, numpy.float64(self.hypamp))
current[ton_idx:toff_idx] += numpy.float64(self.amp)

return t, current
3 changes: 3 additions & 0 deletions bluepyefe/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ def reduce_ecode(self, ecode, operator):
else:
mean_param = operator([numpy.nan if c[key] is None else c[key] for c in params])

if numpy.isnan(mean_param):
mean_param = None

setattr(ecode, key, mean_param)

return ecode
Expand Down
142 changes: 142 additions & 0 deletions tests/ecode/test_apthresh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""bluepyefe.ecode.APThreshold tests"""

import unittest
import pytest
import glob
import json

import bluepyefe.extract
import bluepyefe.tools
from tests.utils import download_apthresh_datafiles


def get_apthresh_config(absolute_amplitude=False):
download_apthresh_datafiles()

interesting_efeatures = {
"Spikecount": {},
"mean_frequency": {},
"ISI_CV": {},
"AP1_amp": {},
"AP_width": {},
}

files_metadata1 = []
for file in glob.glob("./tests/exp_data/X/X_APThreshold_ch0_*.ibw"):
files_metadata1.append(
{
"i_file": file,
"v_file": file.replace("ch0", "ch1"),
"i_unit": "A",
"v_unit": "V",
"t_unit": "ms",
"dt": 0.25,
"ljp": 14,
"ton": 10, # in ms
"tmid": 260, # in ms
"tmid2": 360, # in ms
"toff": 1360, # in ms
}
)
files_metadata2 = []
for file in glob.glob("./tests/exp_data/X/X_IDthresh_ch0_*.ibw"):
files_metadata2.append(
{
"i_file": file,
"v_file": file.replace("ch0", "ch1"),
"i_unit": "A",
"v_unit": "V",
"t_unit": "ms",
"dt": 0.25,
"ljp": 14,
}
)

files_metadata = {
"MouseNeuron1": {"APThreshold": files_metadata1, "IDthresh": files_metadata2},
}

if absolute_amplitude:
targets = {
"APThreshold": {
"amplitudes": [0.0, 0.225, 0.5, 0.69, 0.41, 0.595],
"tolerances": [0.01],
"efeatures": interesting_efeatures,
"location": "soma",
}
}

else:
targets = {
"APThreshold": {
"amplitudes": [150],
"tolerances": [10.0],
"efeatures": interesting_efeatures,
"location": "soma",
}
}

return files_metadata, bluepyefe.extract.convert_legacy_targets(targets)

class APThreshTest(unittest.TestCase):
def test_extract_apthresh(self):
for absolute_amplitude in [True, False]:
with self.subTest(absolute_amplitude=absolute_amplitude):
self.run_test_with_absolute_amplitude(absolute_amplitude)

def run_test_with_absolute_amplitude(self, absolute_amplitude):
files_metadata, targets = get_apthresh_config(absolute_amplitude)

cells = bluepyefe.extract.read_recordings(files_metadata=files_metadata)

cells = bluepyefe.extract.extract_efeatures_at_targets(
cells=cells, targets=targets
)

bluepyefe.extract.compute_rheobase(cells, protocols_rheobase=["IDthresh"])

self.assertEqual(len(cells), 1)
self.assertEqual(len(cells[0].recordings), 21)
self.assertLess(abs(cells[0].rheobase - 0.1103), 0.01)

# amplitude test for one recording
# sort the recordings because they can be in any order,
# and we want to select the same one each time we test
apthresh_recs = [rec for rec in cells[0].recordings if rec.protocol_name == "APThreshold"]
rec1 = sorted(apthresh_recs, key=lambda x: x.amp)[1]
self.assertLess(abs(rec1.amp - 0.1740), 0.01)
self.assertLess(abs(rec1.amp_rel - 157.7), 0.1)


protocols = bluepyefe.extract.group_efeatures(
cells,
targets,
use_global_rheobase=True,
protocol_mode="mean",
absolute_amplitude=absolute_amplitude
)

_ = bluepyefe.extract.create_feature_protocol_files(
cells=cells, protocols=protocols, output_directory="MouseCells_APThreshold"
)

for protocol in protocols:
if protocol.name == "APThreshold" and protocol.amplitude == 150:
for target in protocol.feature_targets:
if target.efel_feature_name == "Spikecount":
self.assertEqual(target.mean, 14)
break

bluepyefe.extract.plot_all_recordings_efeatures(
cells, protocols, output_dir="MouseCells_APThreshold/"
)

with open("MouseCells_APThreshold/features.json") as fp:
features = json.load(fp)
with open("MouseCells_APThreshold/protocols.json") as fp:
protocols = json.load(fp)

self.assertEqual(len(features), len(protocols))

if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit e438278

Please sign in to comment.