[test] tiny initial test

This commit is contained in:
Jan Grewe 2021-02-06 13:38:01 +01:00
parent 420db5327e
commit feefa81cf8
5 changed files with 452 additions and 0 deletions

0
nixview/test/__init__.py Normal file
View File

View File

@ -0,0 +1,329 @@
import nixio as nix
import lif
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
from PIL import Image as img
from IPython import embed
def bivariate_normal(X, Y, sigmax=1.0, sigmay=1.0,
mux=0.0, muy=0.0, sigmaxy=0.0):
"""
Bivariate Gaussian distribution for equal shape *X*, *Y*.
See `bivariate normal
<http://mathworld.wolfram.com/BivariateNormalDistribution.html>`_
at mathworld.
"""
Xmu = X-mux
Ymu = Y-muy
rho = sigmaxy/(sigmax*sigmay)
z = Xmu**2/sigmax**2 + Ymu**2/sigmay**2 - 2*rho*Xmu*Ymu/(sigmax*sigmay)
denom = 2*np.pi*sigmax*sigmay*np.sqrt(1-rho**2)
return np.exp(-z/(2*(1-rho**2))) / denom
def fake_neuron():
lif_model = lif.lif()
t, v, spike_times = lif_model.run_const_stim(10000, 0.005)
return t, v, spike_times
def create_1d_sampled(f, b):
sample_interval = 0.00005
t = np.arange(0., 0.5, sample_interval)
f1 = 100.
f2 = 200.
a1 = 0.825
a2 = 0.4
eod = a1 * np.sin(t * f1 * 2 * np.pi) + a2 * np.sin(f2 * t * 2 * np.pi)
da = b.create_data_array('eod', 'nix.regular_sampled', data=eod)
da.definition = "Recording of an electric fish's electric organ discharge. \
Demontrates the use of DataArrays to store 1-D data that is \
regularly sampled in time. The DataArray contains one dimension \
descriptor that defines how the time-axis is resolved."
da.unit = 'mV/cm'
da.label = 'electric field'
da.description = ""
d = da.append_sampled_dimension(sample_interval)
d.unit = 's'
d.label = 'time'
sec = f.create_section('in vivo 1', 'setup')
hw = sec.create_section('amplifier', 'hardware.amplifier')
hw['model'] = 'EXT 2F'
hw['manufacturer'] = 'npi electronics'
hw['gain'] = 1000
subj = f.create_section('2015_albi_10', 'subject.animal')
subj['species'] = 'Apteronotus albifrons'
subj['sex'] = 'male'
src1 = b.create_source('setup', 'nix.source.setup')
src1.metadata = sec
src2 = b.create_source('subject', 'nix.source.subject')
src2.metadata = subj
def create_2d(f, b, trials=10):
# create multiple responses of a lif model neuron
voltages = []
for t in range(trials):
time, voltage, _ = fake_neuron()
voltages.append(voltage)
voltages = np.asarray(voltages).T
da = b.create_data_array("membrane voltages", "nix.regular_sampled.series",
dtype=nix.DataType.Double, data=voltages)
d = da.append_sampled_dimension(time[1]-time[0])
d.label = "time"
d.unit = "s"
da.label = "voltage"
da.unit = "mV"
da.append_set_dimension()
average_v = b.create_data_array("average response", "nix.regular_sampled",
dtype=nix.DataType.Double, data=np.mean(voltages, axis=1))
average_v.label = "voltage"
average_v.unit = "mV"
dim = average_v.append_sampled_dimension(time[1] - time[0])
dim.unit = "s"
dim.label = "time"
tag = b.create_tag("average response", "nix.epoch", [0.0])
tag.extent = [time[-1]]
tag.definition = "Average repsonse of the model neuron. The original responses\
are referenced and the average response is linked as a feature of these."
tag.references.append(da)
tag.create_feature(average_v, nix.LinkType.Untagged)
def create_3d(f, b):
# taken from nix tutorial
image = img.open('lena.bmp')
img_data = np.array(image)
channels = list(image.mode)
data = b.create_data_array("lena", "nix.image.rgb", data=img_data)
# add descriptors for width, height and channels
height_dim = data.append_sampled_dimension(1)
height_dim.label = "height"
width_dim = data.append_sampled_dimension(1)
width_dim.label = "width"
color_dim = data.append_set_dimension()
color_dim.labels = channels
def create_1d_range(f, b):
da = b.data_arrays['eod']
eod = da[:]
time = np.asarray(da.dimensions[0].axis(len(eod)))
shift_eod = np.roll(eod, 1)
xings = time[(eod > 0) & (shift_eod < 0)]
range_da = b.create_data_array('zero crossings', 'nix.event', data=xings)
range_da.definition = "1-D data that is irregularly sampled in time. That \
is, the time between consecutive sampling points is not regular. Here we \
store the times at which a signal crossed the zero line. The content of the \
DataArray itself defines the time-axis the only dimension descriptor is thus \
an \"aliasRange\" dimension."
d = range_da.append_alias_range_dimension()
d.unit = 's'
d.label = 'time'
def create_1d_set(f, b):
temp = [13.7, 16.3, 14.6, 11.6, 8.6, 5.7, 4., 2.6, 3., 4., 8.5, 13.1]
labels = ["Sep", "Aug", "Jul", "Jun", "Mai", "April", "Mar", "Feb", "Jan", "Dec", "Nov", "Okt"]
da = b.create_data_array("average temperature", "nix.catergorical", data=temp)
da.definition = "1-D categorical data can also be stored in a DataArray entity. The dimension\
descriptor is in this case a SetDimension. The labels stored in this dimension are used to \
label the ticks of the x-axis."
da.label = "temperature"
da.unit = "C"
d = da.append_set_dimension()
d.labels = labels
src = b.create_source("Data source", "nix.source")
da.sources.append(src)
s = f.create_section("Helgoland Weather data", "data_origin")
s["period"] = "201509 - 201410"
s["url"] = "http://www.dwd.de/DE/leistungen/klimadatendeutschland/klimadatendeutschland.html"
src.metadata = s
return src
def create_2d_set(f, b, source):
temp = [13.7, 16.3, 14.6, 11.6, 8.6, 5.7, 4., 2.6, 3., 4., 8.5, 13.1]
temp_min = [12.3, 13.8, 12.1, 9.9, 6.6, 1.4, 1.5, -0.2, -1.5, -1.4, 0.5, 9.4]
temp_max = [18.7, 23.6, 25.9, 20., 16.6, 11.7, 9.5, 7.2, 9.8, 10.5, 15.8, 18.8]
xlabels = ["Sep", "Aug", "Jul", "Jun", "Mai", "April", "Mar", "Feb", "Jan", "Dec", "Nov", "Okt"]
ylabels = ["Min", "Avg", "Max"]
da = b.create_data_array("2D set of temperatures", "nix.catergorical.series",
data=np.vstack([temp_min, temp, temp_max]))
da.label = "temperature"
da.unit = "C"
d1 = da.append_set_dimension()
d1.labels = ylabels
d2 = da.append_set_dimension()
d2.labels = xlabels
da.sources.append(source)
def create_2d_sample(f, b):
# stolen fom matplolib examples http://matplotlib.org/examples/pylab_examples/image_demo.html
delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1 # difference of Gaussians
da = b.create_data_array("difference of Gaussians", "nix.2d.heatmap", data=Z)
d1 = da.append_sampled_dimension(delta)
d1.label = "x"
d1.offset = -3.
d2 = da.append_sampled_dimension(delta)
d2.label = "y"
d2.offset = -3.
def create_m_tag(f, b):
trace = b.data_arrays["eod"]
event_times = b.data_arrays["zero crossings"]
mt = b.create_multi_tag("special events", "nix.event_times", event_times)
mt.definition = "A MultiTag entity is used to annotate multiple events or segments \
in a number of referenced DataArrays. In this example, the events are the zero \
crossings (see 1-D DataArrays) in the EOD. Thus, the one DataArray (zeros crossings) \
is used to mark the time points in the other (EOD)."
mt.references.append(trace)
positions = b.create_data_array('epoch_starts', 'nix.event', data=[0.05, 0.35])
positions.append_set_dimension()
extents = b.create_data_array('epoch_ends', 'nix.event', data=[0.1, 0.1])
extents.append_set_dimension()
mtag = b.create_multi_tag("epochs", "nix.event_epochs", positions)
mtag.references.append(trace)
mtag.extents = extents
feature_1 = b.create_data_array("feature 1", "nix.feature",
data=np.sin(100 * 2 * np.pi * np.arange(0, 0.1, 0.00005)))
d = feature_1.append_sampled_dimension(0.00005)
d.unit = "s"
d.label = "time"
feature_1.unit = "mV"
feature_1.label = "voltage"
feature_2 = b.create_data_array("feature 2", "nix.feature",
data=np.cos(150 * 2 * np.pi * np.arange(0, 0.1, 0.00005)))
d = feature_2.append_sampled_dimension(0.00005)
d.unit = "s"
d.label = "time"
feature_2.unit = "mV"
feature_2.label = "voltage"
mtag.create_feature(feature_1, nix.LinkType.Untagged)
mtag.create_feature(feature_2, nix.LinkType.Untagged)
def create_m_tag_3d(f, b):
data = [da for da in b.data_arrays if da.name == 'lena'][0]
# some space for three regions-of-interest
roi_starts = np.zeros((3, 3))
roi_starts[0, :] = [250, 245, 0]
roi_starts[1, :] = [250, 315, 0]
roi_starts[2, :] = [340, 260, 0]
roi_extents = np.zeros((3, 3))
roi_extents[0, :] = [30, 45, 3]
roi_extents[1, :] = [30, 40, 3]
roi_extents[2, :] = [25, 65, 3]
# create the positions DataArray
positions = b.create_data_array("ROI positions", "nix.positions", data=roi_starts)
positions.append_set_dimension() # these can be empty
positions.append_set_dimension()
# create the extents DataArray
extents = b.create_data_array("ROI extents", "nix.extents", data=roi_extents)
extents.append_set_dimension()
extents.append_set_dimension()
# create a MultiTag
multi_tag = b.create_multi_tag("Regions of interest", "nix.roi", positions)
multi_tag.extents = extents
multi_tag.references.append(data)
def create_epoch_tag(f, b):
trace = b.data_arrays["eod"]
p, f = mlab.psd(trace[:], Fs=1./trace.dimensions[0].sampling_interval, NFFT=4096,
noverlap=2048, sides="twosided")
power = b.create_data_array("power spectrum", "nix.sampled.spectrum.psd", data=p)
power.label = "power"
power.unit = "mV^2/cm^2*Hz^-1"
dim = power.append_sampled_dimension(np.mean(np.diff(f)))
dim.offset = f[0]
dim.label = "frequency"
dim.unit = "Hz"
tag = b.create_tag("interesting epoch", "nix.epoch", [0.1])
tag.extent = [0.3]
tag.definition = "This tag tags a region in the referenced DataArray (EOD). One feature\
of the referenced epoch, or region, is the power spectrum of the EOD signal in that region."
tag.references.append(trace)
tag.create_feature(power, nix.LinkType.Untagged)
def create_point_tag(f, b):
trace = b.data_arrays["eod"]
tag = b.create_tag("interesting point", "nix.point", [0.05])
tag.references.append(trace)
def create_test_file(filename):
nix_file = nix.File.open(filename, nix.FileMode.Overwrite)
s = nix_file.create_section('Recording session', 'recording')
s['date'] = '2015-10-21'
s['experimenter'] = 'John Doe'
b = nix_file.create_block("1D data", "nix.recording_session")
b.definition = "This Block contains 1D datasets and links between them. \
These datasets show the use of regualrly sampled and irregularly sampled (range) dimensions."
b.metadata = s
b2 = nix_file.create_block("Categorical data", "nix.analysis_session")
b2.definition = "This Block contains categorical data demonstrating the use of SetDimensions."
create_1d_sampled(nix_file, b)
create_1d_range(nix_file, b)
src = create_1d_set(nix_file, b2)
create_2d_set(nix_file, b2, src)
create_m_tag(nix_file, b)
create_epoch_tag(nix_file, b)
create_point_tag(nix_file, b)
s2 = nix_file.create_section('Lif recording', 'recording')
s2['date'] = '2015-10-21'
s2['experimenter'] = 'John Doe'
s2['neuron'] = 'Leaky integrate and fire neuron'
b2 = nix_file.create_block("2D data", "nix.recording_session")
b2.metadata = s2
b2.definition = "2-dimensional datasets e.g. for storing multiple time-series or image data."
create_2d(nix_file, b2)
create_2d_sample(nix_file, b2)
b3 = nix_file.create_block("3D data", "nix.image_data")
b3.metadata = s
b3.definition = "3-D datasets like RGB image data and links into such datasets."
create_3d(nix_file, b3)
create_m_tag_3d(nix_file, b3)
nix_file.close()
if __name__ == "__main__":
create_test_file('nix_test.h5')

BIN
nixview/test/lena.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

92
nixview/test/lif.py Normal file
View File

@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
"""
Copyright © 2014 German Neuroinformatics Node (G-Node)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted under the terms of the BSD License. See
LICENSE file in the root of the Project.
Author: Jan Grewe <jan.grewe@g-node.org>
"""
import numpy as np
class lif:
def __init__(self, stepsize=0.0001, offset=1.6, tau_m=0.025, tau_a=0.02, da=0.0, D=3.5):
self.stepsize = stepsize # simulation stepsize [s]
self.offset = offset # offset curent [nA]
self.tau_m = tau_m # membrane time_constant [s]
self.tau_a = tau_a # adaptation time_constant [s]
self.da = da # increment in adaptation current [nA]
self.D = D # noise intensity
self.v_threshold = 1.0 # spiking threshold
self.v_reset = 0.0 # reset voltage after spiking
self.i_a = 0.0 # current adaptation current
self.v = self.v_reset # current membrane voltage
self.t = 0.0 # current time [s]
self.membrane_voltage = []
self.spike_times = []
def _reset(self):
self.i_a = 0.0
self.v = self.v_reset
self.t = 0.0
self.membrane_voltage = []
self.spike_times = []
def _lif(self, stimulus, noise):
"""
euler solution of the membrane equation with adaptation current and noise
"""
self.i_a -= self.i_a - self.stepsize/self.tau_a * (self.i_a)
self.v += self.stepsize * (-self.v + stimulus + noise + self.offset - self.i_a) / self.tau_m
self.membrane_voltage.append(self.v)
def _next(self, stimulus):
"""
working horse which delegates to the euler and gets the spike times
"""
noise = self.D * (float(np.random.randn() % 10000) - 5000.0)/10000
self._lif(stimulus, noise)
self.t += self.stepsize
if self.v > self.v_threshold and len(self.membrane_voltage) > 1:
self.v = self.v_reset
self.membrane_voltage[len(self.membrane_voltage)-1] = 2.0
self.spike_times.append(self.t)
self.i_a += self.da
def run_const_stim(self, steps, stimulus):
"""
lif simulation with constant stimulus.
"""
self._reset()
for i in range(steps):
self._next(stimulus)
time = np.arange(len(self.membrane_voltage))*self.stepsize
return time, np.array(self.membrane_voltage), np.array(self.spike_times)
def run_stimulus(self, stimulus):
"""
lif simulation with a predefined stimulus trace.
"""
self._reset()
for s in stimulus:
self._next(s)
time = np.arange(len(self.membrane_voltage))*self.stepsize
return time, np.array(self.membrane_voltage), np.array(self.spike_times)
def __str__(self):
out = '\n'.join(["stepsize: \t" + str(self.stepsize),
"offset:\t\t" + str(self.offset),
"tau_m:\t\t" + str(self.tau_m),
"tau_a:\t\t" + str(self.tau_a),
"da:\t\t" + str(self.da),
"D:\t\t" + str(self.D),
"v_threshold:\t" + str(self.v_threshold),
"v_reset:\t" + str(self.v_reset)])
return out
def __repr__(self):
return self.__str__()

31
nixview/test/test_main.py Normal file
View File

@ -0,0 +1,31 @@
import pytest
from PyQt5 import QtCore
import os
from nixview.ui.mainwindow import NixView
test_file = os.path.join(".", "nix_test.h5")
@pytest.fixture
def app(qtbot):
if not os.path.exists(test_file):
from nixview.test.create_test_file import create_test_file
create_test_file()
test_nv = NixView()
qtbot.addWidget(test_nv)
return test_nv
def test_file_open(app, qtbot):
assert not app._file_handler.is_valid
app.open_file(test_file)
assert app._file_handler.is_valid
assert app._help_action.isEnabled()
def test_file_handler(app):
assert app._file_handler.is_valid
assert not app._plot_action.isEnabled()
assert not app._table_action.isEnabled()
assert app._file_handler._entity_buffer is not None
assert app._file_handler.file_descriptor is not None