OSBModelValidation 0.2.18__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OSBModelValidation-0.2.18.dist-info/METADATA +184 -0
- OSBModelValidation-0.2.18.dist-info/RECORD +98 -0
- OSBModelValidation-0.2.18.dist-info/WHEEL +5 -0
- OSBModelValidation-0.2.18.dist-info/entry_points.txt +2 -0
- OSBModelValidation-0.2.18.dist-info/top_level.txt +1 -0
- omv/__init__.py +8 -0
- omv/analyzers/__init__.py +21 -0
- omv/analyzers/activation.py +24 -0
- omv/analyzers/analyzer.py +80 -0
- omv/analyzers/dryrun.py +13 -0
- omv/analyzers/input_resistance.py +47 -0
- omv/analyzers/morphology.py +16 -0
- omv/analyzers/rates.py +97 -0
- omv/analyzers/resting.py +37 -0
- omv/analyzers/spikes.py +113 -0
- omv/analyzers/temperature.py +12 -0
- omv/analyzers/timeseries.py +57 -0
- omv/analyzers/utils/__init__.py +0 -0
- omv/analyzers/utils/filenode.py +73 -0
- omv/analyzers/utils/timeseries.py +293 -0
- omv/autogen.py +106 -0
- omv/common/__init__.py +0 -0
- omv/common/inout.py +154 -0
- omv/engines/__init__.py +53 -0
- omv/engines/arbor_.py +57 -0
- omv/engines/brian1.py +59 -0
- omv/engines/brian2_.py +57 -0
- omv/engines/eden_.py +63 -0
- omv/engines/engine.py +111 -0
- omv/engines/genesis.py +86 -0
- omv/engines/getarbor.py +15 -0
- omv/engines/getbrian1.py +42 -0
- omv/engines/getbrian2.py +17 -0
- omv/engines/geteden.py +17 -0
- omv/engines/getgenesis.py +56 -0
- omv/engines/getjlems.py +20 -0
- omv/engines/getjnml.py +48 -0
- omv/engines/getlibsbml.py +13 -0
- omv/engines/getmoose.py +15 -0
- omv/engines/getnest.py +62 -0
- omv/engines/getnetpyne.py +52 -0
- omv/engines/getneuroconstruct.py +31 -0
- omv/engines/getnml2.py +26 -0
- omv/engines/getnrn.py +60 -0
- omv/engines/getoctave.py +11 -0
- omv/engines/getpylems.py +13 -0
- omv/engines/getpyneuroml.py +14 -0
- omv/engines/getpynn.py +40 -0
- omv/engines/jlems.py +48 -0
- omv/engines/jneuroml.py +101 -0
- omv/engines/jneuromlbrian.py +65 -0
- omv/engines/jneuromlbrian2.py +61 -0
- omv/engines/jneuromleden.py +61 -0
- omv/engines/jneuromlmoose.py +63 -0
- omv/engines/jneuromlnetpyne.py +73 -0
- omv/engines/jneuromlnetpyne_np2.py +45 -0
- omv/engines/jneuromlnetpyne_np4.py +45 -0
- omv/engines/jneuromlnrn.py +78 -0
- omv/engines/jneuromlpynnnrn.py +75 -0
- omv/engines/jneuromlvalidate.py +62 -0
- omv/engines/jneuromlvalidatev1.py +58 -0
- omv/engines/moose_.py +86 -0
- omv/engines/nestsli.py +84 -0
- omv/engines/netpyne_.py +120 -0
- omv/engines/netpyne__np2.py +47 -0
- omv/engines/netpyne__np4.py +47 -0
- omv/engines/neuron_.py +205 -0
- omv/engines/octave.py +51 -0
- omv/engines/pylems.py +57 -0
- omv/engines/pylemsnml2.py +59 -0
- omv/engines/pynest.py +111 -0
- omv/engines/pyneuroconstruct.py +67 -0
- omv/engines/pyneuroml_.py +55 -0
- omv/engines/pyneuromlvalidatesbml.py +83 -0
- omv/engines/pyneuron.py +94 -0
- omv/engines/pynn.py +63 -0
- omv/engines/pynnbrian1.py +59 -0
- omv/engines/pynnbrian2.py +60 -0
- omv/engines/pynnnest.py +56 -0
- omv/engines/pynnneuroml.py +57 -0
- omv/engines/pynnneuron.py +94 -0
- omv/engines/utils/__init__.py +21 -0
- omv/engines/utils/genesis_utils.g +26 -0
- omv/engines/utils/wdir.py +15 -0
- omv/experiment.py +18 -0
- omv/find_tests.py +126 -0
- omv/omt_mep_parser.py +59 -0
- omv/omv_util.py +439 -0
- omv/parse_omt.py +118 -0
- omv/tally.py +112 -0
- omv/test/__init__.py +0 -0
- omv/test/test_rates.py +38 -0
- omv/test/test_types.py +29 -0
- omv/validation/__init__.py +0 -0
- omv/validation/rx_validator.py +35 -0
- omv/validation/utils.py +51 -0
- omv/validation/validate.py +53 -0
- omv/validation/validate_mep.py +15 -0
omv/analyzers/spikes.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from omv.analyzers.analyzer import OMVAnalyzer
|
|
2
|
+
from omv.analyzers.utils import timeseries as ts
|
|
3
|
+
from omv.analyzers.utils import filenode as fn
|
|
4
|
+
from omv.common.inout import inform
|
|
5
|
+
from os import getcwd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SpikeAnalyzer(OMVAnalyzer):
|
|
9
|
+
def before_running(self):
|
|
10
|
+
if "file" in self.observable:
|
|
11
|
+
self.f = fn.FileNodeHelper(self.observable["file"], self.omt_root)
|
|
12
|
+
if self.f.tstamp:
|
|
13
|
+
inform(
|
|
14
|
+
"Attention! Preexistent datafile: ",
|
|
15
|
+
self.f.filename,
|
|
16
|
+
indent=2,
|
|
17
|
+
verbosity=1,
|
|
18
|
+
)
|
|
19
|
+
if "spiketimes file" in self.observable:
|
|
20
|
+
self.f = fn.SpikeFileNodeHelper(
|
|
21
|
+
self.observable["spiketimes file"], self.omt_root
|
|
22
|
+
)
|
|
23
|
+
if self.f.tstamp:
|
|
24
|
+
inform(
|
|
25
|
+
"Attention! Preexistent datafile: ",
|
|
26
|
+
self.f.filename,
|
|
27
|
+
indent=2,
|
|
28
|
+
verbosity=1,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def parse_spikes(self, to_parse):
|
|
32
|
+
spikes = []
|
|
33
|
+
if isinstance(to_parse, list):
|
|
34
|
+
spikes = to_parse
|
|
35
|
+
elif "file" in to_parse or "spiketimes file" in to_parse:
|
|
36
|
+
if not self.f.exists():
|
|
37
|
+
inform(
|
|
38
|
+
"ERROR! Datafile %s does not exist (relative to %s)!"
|
|
39
|
+
% (self.f.filename, getcwd()),
|
|
40
|
+
indent=2,
|
|
41
|
+
verbosity=0,
|
|
42
|
+
underline="-",
|
|
43
|
+
)
|
|
44
|
+
spikes = None
|
|
45
|
+
elif not self.f.has_changed():
|
|
46
|
+
inform(
|
|
47
|
+
"ERROR! Datafile %s has not changed!" % self.f.filename,
|
|
48
|
+
indent=2,
|
|
49
|
+
verbosity=0,
|
|
50
|
+
underline="-",
|
|
51
|
+
)
|
|
52
|
+
spikes = None
|
|
53
|
+
elif self.f.has_changed():
|
|
54
|
+
if "file" in to_parse:
|
|
55
|
+
try:
|
|
56
|
+
tv = self.f.get_timeseries()
|
|
57
|
+
except Exception as e:
|
|
58
|
+
inform(
|
|
59
|
+
"ERROR! Could not read spikes from datafile %s (%s)!"
|
|
60
|
+
% (self.f.filename, e),
|
|
61
|
+
indent=2,
|
|
62
|
+
verbosity=0,
|
|
63
|
+
underline="-",
|
|
64
|
+
)
|
|
65
|
+
spikes = []
|
|
66
|
+
return spikes
|
|
67
|
+
inform("Reading timeseries from: ", self.f, indent=1, verbosity=1)
|
|
68
|
+
if "spike detection" in to_parse:
|
|
69
|
+
sd = to_parse["spike detection"]
|
|
70
|
+
method = sd.get("method", "threshold")
|
|
71
|
+
threshold = float(sd.get("threshold", 0))
|
|
72
|
+
inform(
|
|
73
|
+
"Detecting spikes with method: ",
|
|
74
|
+
method,
|
|
75
|
+
indent=2,
|
|
76
|
+
verbosity=1,
|
|
77
|
+
)
|
|
78
|
+
spikes = ts.spikes_from_timeseries(
|
|
79
|
+
tv, method=method, threshold=threshold
|
|
80
|
+
)
|
|
81
|
+
else: # file contains spike times
|
|
82
|
+
spikes = tv
|
|
83
|
+
|
|
84
|
+
elif "spiketimes file" in to_parse:
|
|
85
|
+
inform("Reading spiketimes from: ", self.f, indent=1, verbosity=1)
|
|
86
|
+
all_spikes = self.f.get_spike_times()
|
|
87
|
+
if len(all_spikes) != 1:
|
|
88
|
+
inform(
|
|
89
|
+
"ERROR! Spike times file %s should contain only spikes for one cell!"
|
|
90
|
+
% (self.f.filename),
|
|
91
|
+
indent=2,
|
|
92
|
+
verbosity=0,
|
|
93
|
+
underline="-",
|
|
94
|
+
)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
spikes = list(all_spikes.values())[0]
|
|
98
|
+
else:
|
|
99
|
+
inform(
|
|
100
|
+
"ERROR! Preexistent datafile %s has not been updated!"
|
|
101
|
+
% self.f.filename,
|
|
102
|
+
indent=2,
|
|
103
|
+
verbosity=0,
|
|
104
|
+
underline="-",
|
|
105
|
+
)
|
|
106
|
+
spikes = []
|
|
107
|
+
return spikes
|
|
108
|
+
|
|
109
|
+
def parse_expected(self):
|
|
110
|
+
return self.parse_spikes(self.expected)
|
|
111
|
+
|
|
112
|
+
def parse_observable(self):
|
|
113
|
+
return self.parse_spikes(self.observable)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from omv.analyzers.analyzer import OMVAnalyzer
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TemperatureAnalyzer(OMVAnalyzer):
|
|
5
|
+
def before_running(self):
|
|
6
|
+
self.query = self.engine.query_temperature()
|
|
7
|
+
|
|
8
|
+
def parse_expected(self):
|
|
9
|
+
return float(self.expected)
|
|
10
|
+
|
|
11
|
+
def parse_observable(self):
|
|
12
|
+
return float(self.engine.fetch_query(self.query))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from omv.analyzers.analyzer import OMVAnalyzer
|
|
2
|
+
from omv.analyzers.utils import timeseries as ts
|
|
3
|
+
from omv.analyzers.utils import filenode as fn
|
|
4
|
+
from omv.common.inout import inform
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TimeSeriesAnalyzer(OMVAnalyzer):
|
|
8
|
+
def before_running(self):
|
|
9
|
+
if "file" in self.observable:
|
|
10
|
+
self.fo = fn.FileNodeHelper(self.observable["file"], self.omt_root)
|
|
11
|
+
if self.fo.tstamp:
|
|
12
|
+
inform(
|
|
13
|
+
"Attention! Preexistent datafile: ",
|
|
14
|
+
self.fo.filename,
|
|
15
|
+
indent=2,
|
|
16
|
+
verbosity=1,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def parse_ts(self, to_parse):
|
|
20
|
+
return ts
|
|
21
|
+
|
|
22
|
+
def parse_expected(self):
|
|
23
|
+
to_parse = self.expected
|
|
24
|
+
if isinstance(to_parse, list):
|
|
25
|
+
ts = to_parse
|
|
26
|
+
elif "file" in to_parse:
|
|
27
|
+
f = fn.FileNodeHelper(to_parse["file"], self.mep_root)
|
|
28
|
+
ts = f.get_timeseries()
|
|
29
|
+
inform("Reading timeseries from: ", f, indent=1, verbosity=1)
|
|
30
|
+
return ts
|
|
31
|
+
|
|
32
|
+
def parse_observable(self):
|
|
33
|
+
to_parse = self.observable
|
|
34
|
+
if isinstance(to_parse, list):
|
|
35
|
+
ts = to_parse
|
|
36
|
+
elif "file" in to_parse:
|
|
37
|
+
if not self.fo.has_changed():
|
|
38
|
+
inform(
|
|
39
|
+
"ERROR! Datafile %s does not exist!" % self.fo.filename,
|
|
40
|
+
indent=2,
|
|
41
|
+
verbosity=0,
|
|
42
|
+
underline="-",
|
|
43
|
+
)
|
|
44
|
+
ts = []
|
|
45
|
+
elif self.fo.has_changed():
|
|
46
|
+
ts = self.fo.get_timeseries()
|
|
47
|
+
inform("Reading timeseries from: ", self.fo, indent=1, verbosity=1)
|
|
48
|
+
else:
|
|
49
|
+
inform(
|
|
50
|
+
"ERROR! Preexistent datafile %s has not been updated!"
|
|
51
|
+
% self.fo.filename,
|
|
52
|
+
indent=2,
|
|
53
|
+
verbosity=0,
|
|
54
|
+
underline="-",
|
|
55
|
+
)
|
|
56
|
+
ts = []
|
|
57
|
+
return ts
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from omv.analyzers.utils.timeseries import load_data_file, load_spike_file
|
|
2
|
+
from os.path import join, getmtime
|
|
3
|
+
from os.path import exists as os_exists
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FileNode(object):
|
|
7
|
+
def get_timestamp(self):
|
|
8
|
+
try:
|
|
9
|
+
return getmtime(self.filename)
|
|
10
|
+
except OSError:
|
|
11
|
+
# No preexisting file
|
|
12
|
+
return None
|
|
13
|
+
|
|
14
|
+
def exists(self):
|
|
15
|
+
return os_exists(self.filename)
|
|
16
|
+
|
|
17
|
+
def has_changed(self):
|
|
18
|
+
return self.get_timestamp() != self.tstamp
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FileNodeHelper(FileNode):
|
|
22
|
+
def __init__(self, fn, root_dir):
|
|
23
|
+
self.filename = join(root_dir, fn["path"])
|
|
24
|
+
self.columns = fn.get("columns", (0, 1))
|
|
25
|
+
self.header = fn.get("header", 0)
|
|
26
|
+
self.scaling = fn.get("scaling", [1.0] * len(self.columns))
|
|
27
|
+
self.tstamp = self.get_timestamp()
|
|
28
|
+
|
|
29
|
+
def get_timestamp(self):
|
|
30
|
+
try:
|
|
31
|
+
return getmtime(self.filename)
|
|
32
|
+
except OSError:
|
|
33
|
+
# No preexisting file
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def exists(self):
|
|
37
|
+
return os_exists(self.filename)
|
|
38
|
+
|
|
39
|
+
def has_changed(self):
|
|
40
|
+
return self.get_timestamp() != self.tstamp
|
|
41
|
+
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return str(
|
|
44
|
+
{
|
|
45
|
+
"file name": str(self.filename),
|
|
46
|
+
"columns(time, voltage)": str(self.columns),
|
|
47
|
+
"initial rows to disregard": str(self.header),
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def get_timeseries(self):
|
|
52
|
+
return load_data_file(self.filename, self.columns, self.header, self.scaling)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SpikeFileNodeHelper(FileNode):
|
|
56
|
+
def __init__(self, fn, root_dir):
|
|
57
|
+
self.filename = join(root_dir, fn["path"])
|
|
58
|
+
self.tstamp = self.get_timestamp()
|
|
59
|
+
self.format = fn.get("format", 0)
|
|
60
|
+
self.ids = fn.get("ids", 0)
|
|
61
|
+
self.scaling = fn.get("scaling", 1)
|
|
62
|
+
|
|
63
|
+
def __str__(self):
|
|
64
|
+
return str(
|
|
65
|
+
{
|
|
66
|
+
"spike file name": str(self.filename),
|
|
67
|
+
"format": str(self.format),
|
|
68
|
+
"ids": str(self.ids),
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def get_spike_times(self):
|
|
73
|
+
return load_spike_file(self.filename, self.format, self.ids, self.scaling)
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
from omv.common.inout import inform
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def detect_spikes(v, method="threshold", threshold=0.0):
|
|
5
|
+
from numpy import flatnonzero, bitwise_and, roll, diff, array
|
|
6
|
+
|
|
7
|
+
extrema = array([])
|
|
8
|
+
if method == "threshold":
|
|
9
|
+
extrema = 1 + flatnonzero(
|
|
10
|
+
bitwise_and((v[:-1] <= threshold), (roll(v, -1)[:-1] > threshold))
|
|
11
|
+
)
|
|
12
|
+
elif method == "derivative":
|
|
13
|
+
# This should only work for noiseless cases!
|
|
14
|
+
dx = diff(v)
|
|
15
|
+
extrema = 1 + flatnonzero(bitwise_and((dx[:-1] >= 0), (roll(dx, -1)[:-1] < 0)))
|
|
16
|
+
else:
|
|
17
|
+
print("still need to implement fancier spike detectors...")
|
|
18
|
+
# see for example scipy.signal.find_peaks_cwt
|
|
19
|
+
return extrema
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def load_data_file(fname, columns=(0, 1), header_lines=0, scaling=1):
|
|
23
|
+
from numpy import loadtxt
|
|
24
|
+
|
|
25
|
+
ts = loadtxt(fname, usecols=columns, skiprows=header_lines)
|
|
26
|
+
return ts * scaling
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def load_spike_file(fname, format="ID_TIME", ids=0, scaling=1.0):
|
|
30
|
+
from numpy import loadtxt
|
|
31
|
+
|
|
32
|
+
ts = loadtxt(fname, skiprows=3) if format == "ID_TIME_NEST_DAT" else loadtxt(fname)
|
|
33
|
+
spike_map = {}
|
|
34
|
+
for l in ts:
|
|
35
|
+
# print('Interpreting: %s'%l)
|
|
36
|
+
if format == "ID_TIME" or format == "ID_TIME_NEST_DAT":
|
|
37
|
+
t = l[1] * scaling
|
|
38
|
+
id = l[0]
|
|
39
|
+
elif format == "TIME_ID":
|
|
40
|
+
t = l[0] * scaling
|
|
41
|
+
id = l[1]
|
|
42
|
+
if ids == id or ids == "*":
|
|
43
|
+
if not id in spike_map:
|
|
44
|
+
spike_map[id] = []
|
|
45
|
+
spike_map[id].append(t)
|
|
46
|
+
return spike_map
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def compare_arrays(arrays, tolerance):
|
|
50
|
+
from numpy import allclose, array, max, abs, atleast_1d
|
|
51
|
+
|
|
52
|
+
# if array conversion fails, also fail test
|
|
53
|
+
try:
|
|
54
|
+
a1, a2 = array(arrays)
|
|
55
|
+
except ValueError as e:
|
|
56
|
+
print(e)
|
|
57
|
+
return (False, 0)
|
|
58
|
+
|
|
59
|
+
if (hasattr(a1, "__len__") or hasattr(a2, "__len__")) and len(a1) != len(
|
|
60
|
+
a2
|
|
61
|
+
): # Different lengths!!
|
|
62
|
+
return (False, 0)
|
|
63
|
+
|
|
64
|
+
best_tol = None
|
|
65
|
+
try:
|
|
66
|
+
comp = allclose(a1, a2, tolerance)
|
|
67
|
+
if len(atleast_1d(a1)) > 0:
|
|
68
|
+
best_tol = max(abs((a1 - a2) / a2))
|
|
69
|
+
except ValueError:
|
|
70
|
+
comp = False
|
|
71
|
+
return (comp, best_tol)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def compare_dictionaries(d1, d2, tolerance=0.1):
|
|
75
|
+
from numpy import allclose, array
|
|
76
|
+
|
|
77
|
+
ks = d1.keys() if len(d1) < len(d2) else d2.keys()
|
|
78
|
+
a1 = array([d1[k] for k in ks if k in d1])
|
|
79
|
+
a2 = array([d2[k] for k in ks if k in d2])
|
|
80
|
+
return allclose(a1, a2, tolerance)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def load_spike_times(spiketime):
|
|
84
|
+
if isinstance(spiketime, list):
|
|
85
|
+
return spiketime
|
|
86
|
+
else:
|
|
87
|
+
return load_data_file(spiketime, [0])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def spikes_from_timeseries(ts, **kwargs):
|
|
91
|
+
method = kwargs.get("method", "threshold")
|
|
92
|
+
threshold = kwargs.get("threshold", 0)
|
|
93
|
+
t, v = ts.T
|
|
94
|
+
spk_idx = detect_spikes(v, method, threshold)
|
|
95
|
+
return t[spk_idx]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def spikes_from_datafile(
|
|
99
|
+
path, columns=(0, 1), header=0, method="threshold", threshold=0
|
|
100
|
+
):
|
|
101
|
+
t, v = load_data_file(path, columns, header)
|
|
102
|
+
spk_idx = detect_spikes(v, method, threshold)
|
|
103
|
+
return t[spk_idx]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def average_resting(tv, window, col=1):
|
|
107
|
+
from numpy import mean
|
|
108
|
+
|
|
109
|
+
return mean(tv[-window:, col])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def input_resistance(tv, ti, h_window, cmd_window, voltages, col=1):
|
|
113
|
+
from numpy import mean, where
|
|
114
|
+
|
|
115
|
+
t = ti[:, 0]
|
|
116
|
+
# print(cmd_window
|
|
117
|
+
t_holding = where((t >= h_window[0]) & (t <= h_window[1]))
|
|
118
|
+
t_cmd = where((t >= cmd_window[0]) & (t <= cmd_window[1]))
|
|
119
|
+
holding_i = mean(ti[t_holding, col])
|
|
120
|
+
command_i = mean(ti[t_cmd, col])
|
|
121
|
+
holding_v = mean(tv[t_holding, col])
|
|
122
|
+
command_v = mean(tv[t_cmd, col])
|
|
123
|
+
i_step = command_i - holding_i
|
|
124
|
+
v_step = command_v - holding_v # voltages[0]-voltages[1]
|
|
125
|
+
# print(v_step, i_step, holding_i, command_i
|
|
126
|
+
input_resistance = v_step / i_step
|
|
127
|
+
return input_resistance
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def all_within_bounds(ts, bounds=(0, 1)):
|
|
131
|
+
from numpy import all
|
|
132
|
+
|
|
133
|
+
# print(type(bounds[0]),bounds[0],bounds[1], ts.size
|
|
134
|
+
return all((ts[:, 1:] >= bounds[0]) & (ts[:, 1:] <= bounds[1]))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def all_nonzero(ts):
|
|
138
|
+
from numpy import all
|
|
139
|
+
|
|
140
|
+
return all(ts[:, 1:])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def pretty_print_copypaste(obs, exp):
|
|
144
|
+
from numpy import atleast_1d
|
|
145
|
+
|
|
146
|
+
ob = atleast_1d(obs)
|
|
147
|
+
ex = atleast_1d(exp)
|
|
148
|
+
suggest_tol = False
|
|
149
|
+
try: # making it easier to copy/paste lists
|
|
150
|
+
pretty_obs = [float(el) for el in ob]
|
|
151
|
+
pretty_exp = [float(el) for el in ex]
|
|
152
|
+
suggest_tol = len(ob) == len(ex)
|
|
153
|
+
|
|
154
|
+
except (
|
|
155
|
+
Exception
|
|
156
|
+
) as e: # obs, exp can be rank > 1. Not sure if we would ever c&p those
|
|
157
|
+
pretty_obs, pretty_exp = (str(ob), str(ex))
|
|
158
|
+
|
|
159
|
+
return pretty_obs, pretty_exp
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_detect_spikes():
|
|
163
|
+
from numpy import array, all, arange
|
|
164
|
+
|
|
165
|
+
x = array([-1, 0, 1, 0] * 10)
|
|
166
|
+
|
|
167
|
+
spk_idx = detect_spikes(x, method="derivative")
|
|
168
|
+
assert all(spk_idx == arange(2, len(x), 4))
|
|
169
|
+
|
|
170
|
+
spk_idx = detect_spikes(x, method="threshold", threshold=0.1)
|
|
171
|
+
assert all(spk_idx == arange(2, len(x), 4))
|
|
172
|
+
|
|
173
|
+
xx = -x # edge case: first point > threshold
|
|
174
|
+
spk_idx = detect_spikes(xx, method="derivative")
|
|
175
|
+
assert all(spk_idx == arange(4, len(xx), 4))
|
|
176
|
+
|
|
177
|
+
spk_idx = detect_spikes(xx, method="threshold", threshold=0.1)
|
|
178
|
+
assert all(spk_idx == arange(4, len(xx), 4))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _get_single_spike_rate(spiketimes, method, start_time, end_time):
|
|
182
|
+
if method == ISI_BASED_SPIKERATE_CALC:
|
|
183
|
+
isis = []
|
|
184
|
+
tot_isi = 0
|
|
185
|
+
for si in range(len(spiketimes) - 1):
|
|
186
|
+
if (
|
|
187
|
+
spiketimes[si + 1] >= start_time
|
|
188
|
+
and spiketimes[si + 1] <= end_time
|
|
189
|
+
and spiketimes[si] >= start_time
|
|
190
|
+
and spiketimes[si] <= end_time
|
|
191
|
+
):
|
|
192
|
+
isi = spiketimes[si + 1] - spiketimes[si]
|
|
193
|
+
isis.append(isi)
|
|
194
|
+
tot_isi += isi
|
|
195
|
+
if len(isis) > 0:
|
|
196
|
+
rate = len(isis) / float(tot_isi)
|
|
197
|
+
else:
|
|
198
|
+
rate = 0
|
|
199
|
+
inform(
|
|
200
|
+
"Spikes (%i): %s, qualifying ISIs: %s, rate: %s"
|
|
201
|
+
% (len(spiketimes), spiketimes, isis, rate),
|
|
202
|
+
verbosity=2,
|
|
203
|
+
indent=2,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
elif method == DURATION_BASED_SPIKERATE_CALC:
|
|
207
|
+
dur = float(end_time - start_time)
|
|
208
|
+
spikes_in = 0
|
|
209
|
+
for s in spiketimes:
|
|
210
|
+
if s >= start_time and s <= end_time:
|
|
211
|
+
spikes_in += 1
|
|
212
|
+
rate = spikes_in / dur
|
|
213
|
+
inform(
|
|
214
|
+
"Spikes %i of %i inside range %s->%s, so rate %s"
|
|
215
|
+
% (spikes_in, len(spiketimes), start_time, end_time, rate),
|
|
216
|
+
verbosity=2,
|
|
217
|
+
indent=2,
|
|
218
|
+
)
|
|
219
|
+
return rate
|
|
220
|
+
|
|
221
|
+
else:
|
|
222
|
+
raise Exception("Unknown method for calculating rates: %s" % method)
|
|
223
|
+
|
|
224
|
+
return rate
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
ISI_BASED_SPIKERATE_CALC = "isi based"
|
|
228
|
+
DURATION_BASED_SPIKERATE_CALC = "duration based"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_spike_rate(
|
|
232
|
+
spikes, method=ISI_BASED_SPIKERATE_CALC, start_time=0, end_time=float("inf")
|
|
233
|
+
):
|
|
234
|
+
inform(
|
|
235
|
+
'Calculating average of %i spike rate(s) with method "%s" from %s->%s'
|
|
236
|
+
% (len(spikes), method, start_time, end_time),
|
|
237
|
+
verbosity=1,
|
|
238
|
+
indent=2,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if isinstance(spikes, list):
|
|
242
|
+
return _get_single_spike_rate(spikes, method, start_time, end_time)
|
|
243
|
+
|
|
244
|
+
if isinstance(spikes, dict):
|
|
245
|
+
tot_rates = 0
|
|
246
|
+
all_rates = []
|
|
247
|
+
if len(spikes) == 0:
|
|
248
|
+
avg_rate = 0
|
|
249
|
+
else:
|
|
250
|
+
for s in spikes.values():
|
|
251
|
+
r = _get_single_spike_rate(s, method, start_time, end_time)
|
|
252
|
+
all_rates.append(r)
|
|
253
|
+
tot_rates += r
|
|
254
|
+
avg_rate = float(tot_rates) / len(spikes)
|
|
255
|
+
inform(
|
|
256
|
+
'Calculated average of %i spike rate(s) with method "%s": %s %s'
|
|
257
|
+
% (len(spikes), method, avg_rate, all_rates),
|
|
258
|
+
verbosity=1,
|
|
259
|
+
indent=2,
|
|
260
|
+
)
|
|
261
|
+
return avg_rate
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if __name__ == "__main__":
|
|
265
|
+
from omv.common.inout import set_verbosity
|
|
266
|
+
|
|
267
|
+
set_verbosity(2)
|
|
268
|
+
|
|
269
|
+
tsNone = []
|
|
270
|
+
tsA = [0.1]
|
|
271
|
+
tsB = [0.1, 0.2]
|
|
272
|
+
|
|
273
|
+
ts1 = [0.1, 0.2, 0.3, 0.40]
|
|
274
|
+
ts2 = [0.1, 0.3, 0.50]
|
|
275
|
+
|
|
276
|
+
methods = [ISI_BASED_SPIKERATE_CALC, DURATION_BASED_SPIKERATE_CALC]
|
|
277
|
+
|
|
278
|
+
for method in methods:
|
|
279
|
+
opts = [tsNone, tsA, tsB, ts1, ts2, {"0": ts1}, {"0": ts1, "1": ts2}]
|
|
280
|
+
for opt in opts:
|
|
281
|
+
start_time = 0
|
|
282
|
+
end_time = 0.25
|
|
283
|
+
inform(
|
|
284
|
+
" > Rate in %s->%s (%s) for %s: %s\n"
|
|
285
|
+
% (
|
|
286
|
+
start_time,
|
|
287
|
+
end_time,
|
|
288
|
+
method,
|
|
289
|
+
opt,
|
|
290
|
+
get_spike_rate(opt, method, start_time, end_time),
|
|
291
|
+
),
|
|
292
|
+
indent=2,
|
|
293
|
+
)
|
omv/autogen.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from os.path import isdir, join, split
|
|
2
|
+
from glob import glob
|
|
3
|
+
import yaml
|
|
4
|
+
|
|
5
|
+
# ugly hack to get .travis.yaml closer to manual version
|
|
6
|
+
from collections import OrderedDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UnsortableList(list):
|
|
10
|
+
def sort(self, *args, **kwargs):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UnsortableOrderedDict(OrderedDict):
|
|
15
|
+
def items(self, *args, **kwargs):
|
|
16
|
+
return UnsortableList(OrderedDict.items(self, *args, **kwargs))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
yaml.add_representer(
|
|
20
|
+
UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def read_option(options, default=0):
|
|
25
|
+
for i, opt in enumerate(options):
|
|
26
|
+
print("\t\t", i, opt)
|
|
27
|
+
opt = None
|
|
28
|
+
while opt is None:
|
|
29
|
+
try:
|
|
30
|
+
sel = int(raw_input("Select option number [default: %s]: " % default))
|
|
31
|
+
opt = options[sel]
|
|
32
|
+
except IndexError:
|
|
33
|
+
print("invalid index!")
|
|
34
|
+
except ValueError:
|
|
35
|
+
print("selecting default: " + default)
|
|
36
|
+
opt = options[default]
|
|
37
|
+
return opt
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_targets(auto=False):
|
|
41
|
+
# TODO: should belong to engine
|
|
42
|
+
dirs_to_engines_exts = {
|
|
43
|
+
"NEURON": {"engine": "NEURON", "extension": ".hoc"},
|
|
44
|
+
"NeuroML2": {"engine": "jNeuroML", "extension": ".nml"},
|
|
45
|
+
}
|
|
46
|
+
targets = []
|
|
47
|
+
for d, eng_ext in dirs_to_engines_exts.items():
|
|
48
|
+
if isdir(d):
|
|
49
|
+
engine = dirs_to_engines_exts[d]["engine"]
|
|
50
|
+
ext = dirs_to_engines_exts[d]["extension"]
|
|
51
|
+
print("Default directory for {0} engine found.".format(engine))
|
|
52
|
+
print(" Will look for scripts with {0} extension".format(ext))
|
|
53
|
+
scripts = glob(join(d, "*" + ext))
|
|
54
|
+
if scripts:
|
|
55
|
+
if auto:
|
|
56
|
+
print("selecting default: ", scripts[0])
|
|
57
|
+
script = scripts[0]
|
|
58
|
+
else:
|
|
59
|
+
script = read_option(scripts)
|
|
60
|
+
targets.append((engine, script))
|
|
61
|
+
return targets
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def create_dryrun(engine, target):
|
|
65
|
+
print(
|
|
66
|
+
" ".join(("Generating dry run test for file", target, ", using engine", engine))
|
|
67
|
+
)
|
|
68
|
+
dirname, fname = split(target)
|
|
69
|
+
omt = {"target": fname, "engine": engine}
|
|
70
|
+
with open(target + ".dry.omt", "w") as fh:
|
|
71
|
+
yaml.dump(omt, fh, default_flow_style=False)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def generate_dottravis(targets):
|
|
75
|
+
engines = [t[0] for t in targets]
|
|
76
|
+
engines = ["OMV_ENGINE=" + be for be in engines]
|
|
77
|
+
# TODO: softcode
|
|
78
|
+
repo = "git+https://github.com/borismarin/osb-model-validation.git"
|
|
79
|
+
|
|
80
|
+
travis = UnsortableOrderedDict(
|
|
81
|
+
[
|
|
82
|
+
("language", "python"),
|
|
83
|
+
("python", 2.7),
|
|
84
|
+
("env", engines),
|
|
85
|
+
("install", ["pip install " + repo]),
|
|
86
|
+
("script", ["omv all"]),
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
with open(".travis.yml", "w") as fh:
|
|
91
|
+
fh.write(yaml.dump(travis, default_flow_style=False))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def autogen(auto=False, dry=True):
|
|
95
|
+
targets = find_targets(auto)
|
|
96
|
+
if targets:
|
|
97
|
+
for engine, target in targets:
|
|
98
|
+
if dry:
|
|
99
|
+
create_dryrun(engine, target)
|
|
100
|
+
generate_dottravis(targets)
|
|
101
|
+
else:
|
|
102
|
+
print("No target scripts found!")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
autogen()
|
omv/common/__init__.py
ADDED
|
File without changes
|