Skip to content

Commit

Permalink
Merge "Add / correct diameter models, fix relevant tests."
Browse files Browse the repository at this point in the history
  • Loading branch information
lidakanari authored and BBP code review committed Feb 8, 2019
2 parents 93ff23b + 4c145fa commit 7a7fb91
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 147 deletions.
21 changes: 21 additions & 0 deletions test_data/diam_simple.swc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SWC structure for testing:
# index, type, x, y, z, radius, parent

1 1 0 0 0 1. -1
2 3 1 0 0 2. 1
3 3 2 0 0 1.9 2
4 3 3 0 0 1.8 3
5 3 4 0 0 1.7 4
6 3 5 0 0 1.6 5
7 3 6 0 0 1.5 6
8 3 7 0 0 1.4 7
9 3 7 1 0 1.4 8
10 3 7 2 0 1.3 9
11 3 7 3 0 1.2 10
12 3 7 4 0 1.1 11
13 3 7 5 0 1.0 12
14 3 7 -1 0 1.0 8
15 3 7 -2 0 1.2 14
16 3 7 -3 0 1.0 15
17 3 7 -4 0 1.1 16
18 3 7 -5 0 1.0 17
91 changes: 75 additions & 16 deletions tests/test_diametrizer.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,82 @@
import neurom
import os

from numpy.testing import assert_array_equal

'''Test tns.generate.diametrizer code'''
import os
from nose import tools as nt
import numpy as np
from numpy.testing import assert_array_almost_equal
from tns.generate import diametrizer
import morphio
import tns.generate.diametrizer as test_module
from tns import extract_input

_path = os.path.dirname(os.path.abspath(__file__))
NEU_PATH1 = os.path.join(_path, '../test_data/diam_simple.swc')
NEU_PATH2 = os.path.join(_path, '../test_data/simple.swc')

MODEL = {3: {'Rall_ratio': 2./3.,
'siblings_ratio': 1.0,
'taper': [0.1],
'term': [0.6],
'trunk': [4., 3.],
'trunk_taper': [0.2]}}

def test_sample():
assert(diametrizer.sample([0.]) == 0.)
np.random.seed(0)
assert(diametrizer.sample([1., 1., 1., 2., 2., 3.]) == 2.)

def test_bifurcator():
d1 = diametrizer.bifurcator(1.0, 2., 3./2., 1.)
assert(d1 == 0.6299605249474366)
d1 = diametrizer.bifurcator(1.0, 2., 1., 1.)
assert(d1 == 0.5)
d1 = diametrizer.bifurcator(1.0, 2., 1., 0.5)
assert(d1 == 0.6666666666666666)


def test_taper_section_diam():
neu1 = morphio.mut.Morphology(NEU_PATH1)
section = neu1.root_sections[0]
diametrizer.taper_section_diam(section, 4., 0.3, 0.07, 100.)
assert_array_almost_equal(section.diameters,
[4. , 3.8, 3.6, 3.4, 3.2, 3. , 2.8])

def test_diametrize_constant_per_section():
neu2 = morphio.mut.Morphology(NEU_PATH2) # has to be loaded to start clean
diametrizer.diametrize_constant_per_section(neu2)
assert_array_almost_equal(morphio.Morphology(neu2).diameters,
[2., 2., 2.5, 2.5, 2.5, 2.5, 2., 2., 3., 3., 3., 3.])

def test_diametrize_constant_all():
neuron = morphio.mut.Morphology(os.path.join(_path, '..', 'test_data', 'simple.swc'))
test_module.diametrize_constant_all(neuron)
def test_diametrize_constant_per_neurite():
neu2 = morphio.mut.Morphology(NEU_PATH2) # has to be loaded to start clean
diametrizer.diametrize_constant_per_neurite(neu2)
assert_array_almost_equal(morphio.Morphology(neu2).diameters,
[2.333333, 2.333333, 2.333333, 2.333333, 2.333333, 2.333333,
2.666667, 2.666667, 2.666667, 2.666667, 2.666667, 2.666667])

# assert_array_equal(morphio.Morphology(neuron).diameters,
# [2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5])
def test_diametrize_smoothing():
neu1 = morphio.mut.Morphology(NEU_PATH1) # has to be loaded to start clean
diametrizer.diametrize_smoothing(neu1)
assert_array_almost_equal(morphio.Morphology(neu1).diameters,
[4. , 3.6 , 3.2 , 2.8 , 2.4 , 2. ,
1.599999, 2.8 , 2.8 , 2.5312 , 2.2624 , 1.9936 ,
1.7248 , 2.8 , 2.8 , 2.688 , 2.576 , 2.464 ,
2.352])

def test_diametrize_constant():
neuron = morphio.mut.Morphology(os.path.join(_path, '..', 'test_data', 'simple.swc'))
test_module.diametrize_constant(neuron)
def test_diametrize_from_root():
neu1 = morphio.mut.Morphology(NEU_PATH1) # has to be loaded to start clean
np.random.seed(0) # ensure constant random number for sampling
diametrizer.diametrize_from_root(neu1, MODEL)
assert_array_almost_equal(morphio.Morphology(neu1).diameters,
[3. , 2.9 , 2.8 , 2.7 , 2.6 , 2.5 ,
2.4 , 2.4 , 0.848528, 0.831558, 0.814587, 0.797616,
0.780646, 2.4 , 0.848528, 0.831558, 0.814587, 0.797616,
0.780646])

assert_array_equal(morphio.Morphology(neuron).diameters,
[2., 2., 2.5, 2.5, 2.5, 2.5, 2., 2., 3., 3., 3., 3.])
def test_diametrize_from_tips():
neu1 = morphio.mut.Morphology(NEU_PATH1) # has to be loaded to start clean
np.random.seed(0) # ensure constant random number for sampling
diametrizer.diametrize_from_tips(neu1, MODEL)
assert_array_almost_equal(morphio.Morphology(neu1).diameters,
[1.770291, 1.711282, 1.652272, 1.593262, 1.534253, 1.475243,
1.416233, 1.416233, 0.62 , 0.6076 , 0.6 , 0.6 ,
0.6 , 1.416233, 0.62 , 0.6076 , 0.6 , 0.6 ,
0.6])
30 changes: 18 additions & 12 deletions tests/test_extract_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
from nose.tools import assert_dict_equal
import neurom
import numpy as np
from numpy.testing import assert_array_almost_equal
from numpy.testing import assert_array_almost_equal, assert_array_equal, assert_equal
from tns import extract_input
import os
import json
import tns.extract_input as test_module


_path = os.path.dirname(os.path.abspath(__file__))
POP_PATH = os.path.join(_path, '../test_data/bio/')
NEU_PATH = os.path.join(_path, '../test_data/diam_simple.swc')

POPUL = neurom.load_neurons(POP_PATH)
NEU = neurom.load_neurons(NEU_PATH)

def test_num_trees():
target_numBAS = {'num_trees': {'data': {'bins': [4, 5, 6, 7, 8, 9],
Expand All @@ -18,8 +23,8 @@ def test_num_trees():

numBAS = extract_input.from_neurom.number_neurites(POPUL)
numAX = extract_input.from_neurom.number_neurites(POPUL, neurite_type=neurom.AXON)
assert_dict_equal(numBAS, target_numBAS)
assert_dict_equal(numAX, target_numAX)
assert_equal(numBAS, target_numBAS)
assert_equal(numAX, target_numAX)

def test_trunk_distr():
target_trunkBAS = {'trunk':
Expand All @@ -40,18 +45,19 @@ def test_trunk_distr():

trunkAP = extract_input.from_neurom.trunk_neurite(POPUL, neurite_type=neurom.APICAL_DENDRITE, bins=1)
trunkBAS = extract_input.from_neurom.trunk_neurite(POPUL, bins=10)
assert_dict_equal(trunkBAS, target_trunkBAS)
assert_dict_equal(trunkAP, target_trunkAPIC)

import os
from numpy.testing import assert_equal
import tns.extract_input as test_module
import numpy as np
from numpy.testing import assert_array_almost_equal, assert_array_equal
assert_equal(trunkBAS, target_trunkBAS)
assert_equal(trunkAP, target_trunkAPIC)

def test_diameter_extract():
model0 = {3: {'Rall_ratio': 0.6666666666666666,
'siblings_ratio': 1.0,
'taper': [0.24000000000000005, 0.1],
'term': [2.0, 2.0],
'trunk': [3.9],
'trunk_taper': [0.3000000000000001]}}
assert_equal(model0, extract_input.from_diameter.model(NEU))


import json
class NeuromJSON(json.JSONEncoder):
'''JSON encoder that handles numpy types
Expand Down
43 changes: 28 additions & 15 deletions tns/extract_input/from_diameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,47 @@
from itertools import chain

import numpy as np
from neurom import get, morphmath
from neurom import get
from neurom.core import Tree, iter_neurites
from neurom.core.types import tree_type_checker as is_type
from neurom.morphmath import segment_length, segment_radius

default_model = {'Rall_ratio': 3. / 2.,
'siblings_ratio': [1. / 3.]}
default_model = {'Rall_ratio': 2. / 3.,
'siblings_ratio': 1.}


def section_mean_taper(s):
'''Computes the mean tapering of a section'''
initial_diam = min(s.points[:, 3])
min_diam = min(s.points[:, 3])

di_li = sum([segment_radius([s.points[i], s.points[i + 1]]) * segment_length(
[s.points[i], s.points[i + 1]]) for i in range(len(s.points) - 1)])

i_li = sum([i * morphmath.segment_length([s.points[i], s.points[i + 1]])
for i in range(len(s.points) - 1)])

return (di_li - initial_diam * s.length) / i_li
return (di_li - min_diam * s.length) / s.length


def terminal_diam(tree):
"""Returns the model for the terminations"""
term_diam = [2. * t.points[-1, 3] for t in Tree.ileaf(tree.iter_sections().next())]
term_diam = [2. * t.points[-1, 3] for t in Tree.ileaf(next(tree.iter_sections()))]

return term_diam


def section_taper(tree):
"""Returns the tapering of the *diameters* within
the sections of a tree"""
# Return non-leafs and exclude the trunk = first section
tapers = [2 * section_mean_taper(s) if Tree.is_leaf(s)
else None for s in tree.iter_sections()]
# Exclude the trunk = first section, taper should not be x2 because it is relative
tapers = [section_mean_taper(s) for s in tree.iter_sections()]
return [taper for taper in tapers if taper][1:]


def section_trunk_taper(tree):
"""Returns the tapering of the *diameters* within
the sections of a tree"""
# Exclude the trunk = first section, taper should not be x2 because it is relative
return section_mean_taper(next(tree.iter_sections()))


def model(neuron):
"""Measures the statistical properties of a neuron's
diameters and outputs a diameter_model"""
Expand All @@ -52,15 +55,25 @@ def model(neuron):
for typee in types_to_process:

neurite_type = types_to_process[typee]
taper = [section_taper(tree) for tree in iter_neurites(neuron, filt=is_type(neurite_type))]
taper = [section_taper(tree) for tree in iter_neurites(neuron,
filt=is_type(neurite_type))]
trunk_taper = np.array([section_trunk_taper(tree)
for tree in iter_neurites(neuron,
filt=is_type(neurite_type))])
taper_c = np.array(list(chain(*taper)))
# Keep only positive, non-zero taper rates
taper_c = taper_c[np.where(taper_c > 0)[0]]
trunk_taper = trunk_taper[np.where(trunk_taper > 0.0)[0]]

term_diam = [terminal_diam(tree)
for tree in iter_neurites(neuron, filt=is_type(neurite_type))]
trunk_diam = [2. * np.max(get('segment_radii', tree))
for tree in iter_neurites(neuron, filt=is_type(neurite_type))]

values[typee - 1] = {"taper": [c for c in chain(*taper)],
values[typee - 1] = {"taper": taper_c,
"term": [c for c in chain(*term_diam)],
"trunk": trunk_diam}
"trunk": trunk_diam,
"trunk_taper": trunk_taper}

values[typee - 1].update(default_model)

Expand Down
14 changes: 10 additions & 4 deletions tns/extract_input/from_neurom.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ def trunk_neurite(pop, neurite_type=nm.BASAL_DENDRITE, bins=30):
actual_bins = (bins[1:] + bins[:-1]) / 2.

return {"trunk": {"orientation_deviation": {"data":
{"bins": actual_bins.tolist(),
"weights": heights.tolist()}},
{"bins": actual_bins,
"weights": heights}},
"azimuth": {"uniform": {"min": np.pi, "max": 0.0}}}}


Expand All @@ -51,9 +51,15 @@ def number_neurites(pop, neurite_type=nm.BASAL_DENDRITE):
# The output is given in integer numbers which are
# the permitted values for the number of trees.
nneurites = nm.get('number_of_neurites', pop, neurite_type=neurite_type)
# Clean the data from single basal trees cells
if neurite_type == nm.BASAL_DENDRITE and len(np.where(nneurites == 1)[0]) > 0:
nneurites[np.where(nneurites == 1)[0]] = 2
print("Warning, input population includes cells with single basal trees! " +
"The distribution has been altered to include 2 basals minimum.")

heights, bins = np.histogram(nneurites, bins=np.arange(np.min(nneurites),
np.max(nneurites) + 2))

# pylint: disable=no-member
return {"num_trees": {"data": {"bins": bins[:-1].tolist(),
"weights": heights.tolist()}}}
return {"num_trees": {"data": {"bins": bins[:-1],
"weights": heights}}}
Loading

0 comments on commit 7a7fb91

Please sign in to comment.