Skip to content

Commit

Permalink
Use morphio for the morphologies (#134)
Browse files Browse the repository at this point in the history
* removing the MORPH_CACHE_SIZE
* removing neurom as the main reader for morphologies
* adding morphio as the main reader for morphologies
* remove the self._load
  • Loading branch information
tomdele committed Apr 23, 2021
1 parent 9480da5 commit 1750be7
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 69 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
Changelog
=========

Version v0.11.1
Version v0.12.0
---------------

Improvements
~~~~~~~~~~~~~~
- Pinned major versions of neurom to >=2.0.0.
- removing the MORPH_CACHE_SIZE
- removing neurom as the main reader for morphologies
- adding morphio as the main reader for the morphologies


Version v0.11.0
Expand Down
20 changes: 9 additions & 11 deletions bluepysnap/morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
from pathlib import Path

import numpy as np
import neurom as nm
from morphio.mut import Morphology
import morph_tool.transform as transformations


from bluepysnap.settings import MORPH_CACHE_SIZE
from bluepysnap.sonata_constants import Node
from bluepysnap.exceptions import BluepySnapError
from bluepysnap.circuit_ids import CircuitNodeId
Expand All @@ -47,10 +48,6 @@ def __init__(self, morph_dir, population):
# all nodes from a population must have the same model type
if not self._is_biophysical(0):
raise BluepySnapError("Node population does not contain biophysical nodes.")
self._load = nm.load_neuron
if MORPH_CACHE_SIZE is not None:
from functools import lru_cache
self._load = lru_cache(maxsize=MORPH_CACHE_SIZE)(self._load)

def _is_biophysical(self, node_id):
return self._population.get(node_id, Node.MODEL_TYPE) == "biophysical"
Expand All @@ -75,9 +72,10 @@ def get(self, node_id, transform=False):
according to `node_id` position in the circuit.
"""
filepath = self.get_filepath(node_id)
result = self._load(filepath)
result = Morphology(filepath)
if transform:
A_t = self._population.orientations(node_id).transpose()
B = self._population.positions(node_id).values
result = result.transform(lambda p: np.dot(p, A_t) + B)
return result
T = np.eye(4)
T[:3, :3] = self._population.orientations(node_id) # rotations
T[:3, 3] = self._population.positions(node_id).values # translations
transformations.transform(result, T)
return result.as_immutable()
6 changes: 0 additions & 6 deletions bluepysnap/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@

import os

# NeuroM morphology loader cache size
MORPH_CACHE_SIZE = None

# All possible checks enabled / deprecated methods disallowed
STRICT_MODE = False

Expand All @@ -37,9 +34,6 @@ def str2bool(value):
def load_env():
"""Load settings from environment variables."""
# pylint: disable=global-statement
if 'BLUESNAP_MORPH_CACHE_SIZE' in os.environ:
global MORPH_CACHE_SIZE
MORPH_CACHE_SIZE = int(os.environ['BLUESNAP_MORPH_CACHE_SIZE'])
if 'BLUESNAP_STRICT_MODE' in os.environ:
global STRICT_MODE
STRICT_MODE = str2bool(os.environ['BLUESNAP_STRICT_MODE'])
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def __init__(self, *args, **kwargs):
'cached_property>=1.0',
'h5py>=3.0.1,<4.0.0',
'libsonata>=0.1.6,<1.0.0',
'neurom>=2.0.0,<3.0.0',
'morphio>=3.0.0,<4.0.0',
'morph-tool>=2.4.3,<3.0.0',
'numpy>=1.8,<2.0.0',
'pandas>=1.0.0,<2.0.0',
'click>=7.0,<8.0.0',
Expand Down
57 changes: 14 additions & 43 deletions tests/test_morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import numpy.testing as npt
import pandas as pd

from mock import Mock, patch
import pandas.testing as pdt
import pytest

Expand Down Expand Up @@ -76,13 +75,14 @@ def test_get_filepath(self):
self.test_obj.get_filepath([0, 1])

def test_get_morphology(self):
actual = self.test_obj.get(0).points
assert len(actual) == 13
actual = self.test_obj.get(0)
assert len(actual.points) == 13
expected = [
[0., 5., 0., 1.],
[2., 9., 0., 1.],
[0., 5., 0.],
[2., 9., 0.],
]
npt.assert_almost_equal(expected, actual[:2])
npt.assert_almost_equal(expected, actual.points[:2])
npt.assert_almost_equal([2., 2.], actual.diameters[:2])

with pytest.raises(BluepySnapError):
self.test_obj.get([0, 1])
Expand All @@ -103,19 +103,20 @@ def test_get_morphology_simple_rotation(self):
decimal=6
)

actual = self.test_obj.get(node_id, transform=True).points
assert len(actual) == 13
actual = self.test_obj.get(node_id, transform=True)
assert len(actual.points) == 13
# swc file
# index type X Y Z radius parent
# 22 2 0.000000 5.000000 0.000000 1.000000 1
# 23 2 2.000000 9.000000 0.000000 1.000000 22
# rotation around the x axis 90 degrees counter clockwise (swap Y and Z)
# x = X + 101, y = Z + 102, z = Y + 103, radius does not change
expected = [
[101., 102., 108., 1.],
[103., 102., 112., 1.]
[101., 102., 108.],
[103., 102., 112.]
]
npt.assert_almost_equal(actual[:2], expected)
npt.assert_almost_equal(actual.points[:2], expected)
npt.assert_almost_equal(actual.diameters[:2], [2., 2.])

def test_get_morphology_standard_rotation(self):
nodes = create_node_population(
Expand All @@ -142,37 +143,7 @@ def test_get_morphology_standard_rotation(self):

assert len(actual) == 13
expected = [
[101., 107., 103., 1.],
[102.47644, 111., 101.65088, 1.]
[101., 107., 103.],
[102.47644, 111., 101.65088]
]
npt.assert_almost_equal(actual[:2], expected, decimal=6)


@patch(test_module.__name__ + '.MORPH_CACHE_SIZE', 1)
@patch('neurom.load_neuron')
def test_MorphHelper_cache_1(nm_load):
nodes = Mock()
nodes.get.side_effect = ['morph-A', 'morph-A', 'morph-B', 'morph-A']
with patch.object(test_module.MorphHelper, '_is_biophysical', return_value=True):
test_obj = test_module.MorphHelper('morph-dir', nodes)
nm_load.side_effect = Mock
morph0 = test_obj.get(0)
# should get cached object for 'morph-A'
assert test_obj.get(1) is morph0
# should get new object ('morph-B')
assert test_obj.get(2) is not morph0
# 'morph-A' was evicted from cache
assert test_obj.get(3) is not morph0


@patch(test_module.__name__ + '.MORPH_CACHE_SIZE', None)
@patch('neurom.load_neuron')
def test_MorphHelper_cache_2(nm_load):
nodes = Mock()
nodes.get.side_effect = ['morph-A', 'morph-A']
with patch.object(test_module.MorphHelper, '_is_biophysical', return_value=True):
test_obj = test_module.MorphHelper('morph-dir', nodes)
nm_load.side_effect = Mock
morph1 = test_obj.get(0)
# same morphology, but no caching
assert test_obj.get(1) is not morph1
6 changes: 0 additions & 6 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ def test_str2bool():
assert not test_module.str2bool(None)


def test_MORPH_CACHE_SIZE():
with patch.dict(os.environ, {'BLUESNAP_MORPH_CACHE_SIZE': '42'}):
test_module.load_env()
assert test_module.MORPH_CACHE_SIZE == 42


def test_STRICT_MODE():
with patch.dict(os.environ, {'BLUESNAP_STRICT_MODE': '1'}):
test_module.load_env()
Expand Down

0 comments on commit 1750be7

Please sign in to comment.