Skip to content

Commit

Permalink
Fix the morphology/model access using a numpy int (#137)
Browse files Browse the repository at this point in the history
* Fix the morphology/model access using a numpy int
* Add tests for the int accesses
  • Loading branch information
tomdele committed May 26, 2021
1 parent d632efe commit ce650f2
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 7 deletions.
4 changes: 2 additions & 2 deletions bluepysnap/morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from bluepysnap.sonata_constants import Node
from bluepysnap.exceptions import BluepySnapError
from bluepysnap.circuit_ids import CircuitNodeId
from bluepysnap.utils import is_node_id


class MorphHelper:
Expand Down Expand Up @@ -58,7 +58,7 @@ def get_filepath(self, node_id):
Args:
node_id (int/CircuitNodeId): could be a int or CircuitNodeId.
"""
if not isinstance(node_id, (int, CircuitNodeId)):
if not is_node_id(node_id):
raise BluepySnapError("node_id must be a int or a CircuitNodeId")
name = self._population.get(node_id, Node.MORPHOLOGY)
return Path(self._morph_dir, f"{name}.swc")
Expand Down
4 changes: 2 additions & 2 deletions bluepysnap/neuron_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

from pathlib import Path

from bluepysnap.circuit_ids import CircuitNodeId
from bluepysnap.exceptions import BluepySnapError
from bluepysnap.sonata_constants import Node
from bluepysnap.utils import is_node_id


class NeuronModelsHelper:
Expand Down Expand Up @@ -54,7 +54,7 @@ def get_filepath(self, node_id):
Returns:
Path: path to the model file of neuron
"""
if not isinstance(node_id, (int, CircuitNodeId)):
if not is_node_id(node_id):
raise BluepySnapError("node_id must be a int or a CircuitNodeId")
node = self._population.get(node_id, [Node.MODEL_TYPE, Node.MODEL_TEMPLATE])
if node[Node.MODEL_TYPE] == "biophysical":
Expand Down
2 changes: 1 addition & 1 deletion bluepysnap/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ def _get_values(prop):
return result.get(prop, np.zeros((result.shape[0],)))

args = [_get_values(prop) for prop in props]
if isinstance(group, (int, np.integer, CircuitNodeId)):
if utils.is_node_id(group):
return trans(*args)[0]
return pd.Series(trans(*args), index=result.index, name='orientation')

Expand Down
9 changes: 9 additions & 0 deletions bluepysnap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ def load_json(filepath):
return json.load(f)


def is_node_id(node_id):
"""Check if node_id can be a valid node id.
Returns:
bool: true is node_id is a int, np.integer or CircuitNodeId. False for anything else.
"""
return isinstance(node_id, (int, np.integer, CircuitNodeId))


def is_iterable(v):
"""Check if `v` is any iterable (strings are considered scalar and CircuitNode/EdgeId also)."""
return isinstance(v, Iterable) and not isinstance(v, (str, CircuitNodeId, CircuitEdgeId))
Expand Down
88 changes: 87 additions & 1 deletion tests/test_edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ def test_ids(self):
tested = self.test_obj.ids(0)
assert tested == expected
npt.assert_equal(tested.get_ids().dtype, IDS_DTYPE)
tested = self.test_obj.ids(np.int64(0))
assert tested == expected
npt.assert_equal(tested.get_ids().dtype, IDS_DTYPE)
tested = self.test_obj.ids(np.uint64(0))
assert tested == expected
npt.assert_equal(tested.get_ids().dtype, IDS_DTYPE)
tested = self.test_obj.ids(np.int32(0))
assert tested == expected
npt.assert_equal(tested.get_ids().dtype, IDS_DTYPE)
tested = self.test_obj.ids(np.uint32(0))
assert tested == expected
npt.assert_equal(tested.get_ids().dtype, IDS_DTYPE)

# single edge ID list --> CircuitEdgeIds return populations with the 0 id
expected = CircuitEdgeIds.from_tuples([("default", 0), ("default2", 0)])
Expand Down Expand Up @@ -217,7 +229,6 @@ def test_ids(self):
expected = CircuitEdgeIds.from_dict({"default": [1, 2, 3], "default2": [1, 2, 3]})
assert tested == expected


def test_get(self):
with pytest.raises(BluepySnapError):
self.test_obj.get(properties=["other2", "unknown"])
Expand Down Expand Up @@ -280,6 +291,10 @@ def test_properties(self):

def test_afferent_nodes(self):
assert self.test_obj.afferent_nodes(0) == CircuitNodeIds.from_arrays(["default"], [2])
assert self.test_obj.afferent_nodes(np.int64(0)) == CircuitNodeIds.from_arrays(["default"], [2])
assert self.test_obj.afferent_nodes(np.uint64(0)) == CircuitNodeIds.from_arrays(["default"], [2])
assert self.test_obj.afferent_nodes(np.int32(0)) == CircuitNodeIds.from_arrays(["default"], [2])
assert self.test_obj.afferent_nodes(np.int32(0)) == CircuitNodeIds.from_arrays(["default"], [2])
assert self.test_obj.afferent_nodes(CircuitNodeId("default", 0)) == CircuitNodeIds.from_arrays(
["default"], [2])
assert self.test_obj.afferent_nodes([0, 1]) == CircuitNodeIds.from_dict({"default": [2, 0]})
Expand All @@ -294,6 +309,10 @@ def test_afferent_nodes(self):

def test_efferent_nodes(self):
assert self.test_obj.efferent_nodes(0) == CircuitNodeIds.from_arrays(["default"], [1])
assert self.test_obj.efferent_nodes(np.int64(0)) == CircuitNodeIds.from_arrays(["default"], [1])
assert self.test_obj.efferent_nodes(np.uint64(0)) == CircuitNodeIds.from_arrays(["default"], [1])
assert self.test_obj.efferent_nodes(np.int32(0)) == CircuitNodeIds.from_arrays(["default"], [1])
assert self.test_obj.efferent_nodes(np.uint32(0)) == CircuitNodeIds.from_arrays(["default"], [1])
assert self.test_obj.efferent_nodes(CircuitNodeId("default", 0)) == CircuitNodeIds.from_arrays(
["default"], [1])
assert self.test_obj.efferent_nodes([0, 2]) == CircuitNodeIds.from_dict({"default": [0, 1]})
Expand Down Expand Up @@ -684,6 +703,10 @@ def test_ids(self):
npt.assert_equal(tested.dtype, IDS_DTYPE)

assert self.test_obj.ids(0) == [0]
assert self.test_obj.ids(np.int64(0)) == [0]
assert self.test_obj.ids(np.uint64(0)) == [0]
assert self.test_obj.ids(np.int32(0)) == [0]
assert self.test_obj.ids(np.uint32(0)) == [0]
npt.assert_equal(self.test_obj.ids([0, 1]), np.array([0, 1]))
npt.assert_equal(self.test_obj.ids(np.array([0, 1])), np.array([0, 1]))
npt.assert_equal(self.test_obj.ids(CircuitEdgeId("default", 0)), [0])
Expand Down Expand Up @@ -753,6 +776,11 @@ def test_properties(self):

def test_get_all_edge_ids_types(self):
assert self.test_obj.get(0, Synapse.PRE_GID).tolist() == [2]
assert self.test_obj.get(np.int64(0), Synapse.PRE_GID).tolist() == [2]
assert self.test_obj.get(np.uint64(0), Synapse.PRE_GID).tolist() == [2]
assert self.test_obj.get(np.int32(0), Synapse.PRE_GID).tolist() == [2]
assert self.test_obj.get(np.uint32(0), Synapse.PRE_GID).tolist() == [2]

assert self.test_obj.get([0], Synapse.PRE_GID).tolist() == [2]
assert self.test_obj.get([0, 1], Synapse.PRE_GID).tolist() == [2, 0]
assert self.test_obj.get(CircuitEdgeId("default", 0), Synapse.PRE_GID).tolist() == [
Expand Down Expand Up @@ -784,6 +812,11 @@ def test_positions_1(self):
columns=['x', 'y', 'z']
)
pdt.assert_frame_equal(actual, expected)
pdt.assert_frame_equal(self.test_obj.positions(0, 'afferent', 'center'), actual)
pdt.assert_frame_equal(self.test_obj.positions(np.int64(0), 'afferent', 'center'), actual)
pdt.assert_frame_equal(self.test_obj.positions(np.uint64(0), 'afferent', 'center'), actual)
pdt.assert_frame_equal(self.test_obj.positions(np.int32(0), 'afferent', 'center'), actual)
pdt.assert_frame_equal(self.test_obj.positions(np.uint32(0), 'afferent', 'center'), actual)

def test_positions_2(self):
actual = self.test_obj.positions([1], 'afferent', 'surface')
Expand Down Expand Up @@ -828,6 +861,22 @@ def test_afferent_nodes(self):
npt.assert_equal(tested, [2])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.afferent_nodes(np.int64(0))
npt.assert_equal(tested, [2])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.afferent_nodes(np.uint64(0))
npt.assert_equal(tested, [2])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.afferent_nodes(np.int32(0))
npt.assert_equal(tested, [2])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.afferent_nodes(np.uint32(0))
npt.assert_equal(tested, [2])
npt.assert_equal(tested.dtype, IDS_DTYPE)

npt.assert_equal(self.test_obj.afferent_nodes(0, unique=False), [2])

npt.assert_equal(self.test_obj.afferent_nodes(1), [0, 2])
Expand All @@ -852,6 +901,22 @@ def test_efferent_nodes(self):
npt.assert_equal(tested, [1])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.efferent_nodes(np.int64(0))
npt.assert_equal(tested, [1])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.efferent_nodes(np.uint64(0))
npt.assert_equal(tested, [1])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.efferent_nodes(np.int32(0))
npt.assert_equal(tested, [1])
npt.assert_equal(tested.dtype, IDS_DTYPE)

tested = self.test_obj.efferent_nodes(np.uint32(0))
npt.assert_equal(tested, [1])
npt.assert_equal(tested.dtype, IDS_DTYPE)

npt.assert_equal(self.test_obj.efferent_nodes(0, unique=False), [1, 1])

npt.assert_equal(self.test_obj.efferent_nodes(1), [])
Expand Down Expand Up @@ -883,6 +948,27 @@ def test_afferent_edges_1(self):
[1, 2, 3]
)

npt.assert_equal(
self.test_obj.afferent_edges(np.int64(1), None),
[1, 2, 3]
)

npt.assert_equal(
self.test_obj.afferent_edges(np.uint64(1), None),
[1, 2, 3]
)

npt.assert_equal(
self.test_obj.afferent_edges(np.int32(1), None),
[1, 2, 3]
)

npt.assert_equal(
self.test_obj.afferent_edges(np.uint32(1), None),
[1, 2, 3]
)


def test_afferent_edges_2(self):
properties = [Synapse.AXONAL_DELAY]
pdt.assert_frame_equal(
Expand Down
14 changes: 14 additions & 0 deletions tests/test_morph.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,23 @@ def test_not_biophysical_population(self):
def test_get_filepath(self):
node_id = 0
assert self.nodes.get(node_id, properties="morphology") == "morph-A"

actual = self.test_obj.get_filepath(node_id)
expected = self.morph_path / 'morph-A.swc'
assert actual == expected

actual = self.test_obj.get_filepath(np.int64(node_id))
assert actual == expected

actual = self.test_obj.get_filepath(np.uint64(node_id))
assert actual == expected

actual = self.test_obj.get_filepath(np.int32(node_id))
assert actual == expected

actual = self.test_obj.get_filepath(np.uint32(node_id))
assert actual == expected

node_id = CircuitNodeId("default", 0)
assert self.nodes.get(node_id, properties="morphology") == "morph-A"
actual = self.test_obj.get_filepath(node_id)
Expand Down
12 changes: 12 additions & 0 deletions tests/test_neuron_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pathlib import Path

import h5py
import numpy as np

import bluepysnap.neuron_models as test_module
from bluepysnap.circuit import Circuit, Config
from bluepysnap.circuit_ids import CircuitNodeId
Expand Down Expand Up @@ -50,6 +53,15 @@ def test_get_filepath_biophysical():
expected = Path(config['components']['biophysical_neuron_models_dir'], 'small_bio-A.hoc')
assert actual == expected

actual = test_obj.get_filepath(np.int64(node_id))
assert actual == expected
actual = test_obj.get_filepath(np.uint64(node_id))
assert actual == expected
actual = test_obj.get_filepath(np.int32(node_id))
assert actual == expected
actual = test_obj.get_filepath(np.uint32(node_id))
assert actual == expected

node_id = CircuitNodeId('default', 0)
assert nodes.get(node_id, properties=Node.MODEL_TEMPLATE) == "hoc:small_bio-A"
actual = test_obj.get_filepath(node_id)
Expand Down
4 changes: 4 additions & 0 deletions tests/test_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,10 @@ def test_ids(self):
npt.assert_equal(_call(group=[], sample=2), [])
npt.assert_equal(_call(group={Cell.MTYPE: "unknown"}, sample=2), [])
npt.assert_equal(_call(0), [0])
npt.assert_equal(_call(np.int64(0)), [0])
npt.assert_equal(_call(np.uint64(0)), [0])
npt.assert_equal(_call(np.int32(0)), [0])
npt.assert_equal(_call(np.uint32(0)), [0])
npt.assert_equal(_call([0, 1]), [0, 1])
npt.assert_equal(_call([1, 0, 1]), [1, 0, 1]) # order and duplicates preserved
npt.assert_equal(_call(np.array([1, 0, 1])), np.array([1, 0, 1]))
Expand Down
18 changes: 17 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
import numpy.testing as npt
import pytest

import bluepysnap.utils as test_module
from bluepysnap.sonata_constants import DYNAMICS_PREFIX
from bluepysnap.circuit_ids import CircuitNodeId, CircuitEdgeId
from bluepysnap.exceptions import BluepySnapError, BluepySnapDeprecationError, BluepySnapDeprecationWarning

from utils import TEST_DATA_DIR

import bluepysnap.utils as test_module


def test_load_json():
actual = test_module.load_json(str(TEST_DATA_DIR / 'circuit_config.json'))
Expand All @@ -21,6 +23,20 @@ def test_is_iterable():
assert not test_module.is_iterable('abc')


def test_is_node_id():
assert test_module.is_node_id(1)
assert test_module.is_node_id(np.int(1))
assert test_module.is_node_id(np.int32(1))
assert test_module.is_node_id(np.uint32(1))
assert test_module.is_node_id(np.int64(1))
assert test_module.is_node_id(np.uint64(1))
assert test_module.is_node_id(CircuitNodeId("default", 1))

assert not test_module.is_node_id([1])
assert not test_module.is_node_id(np.array([1], dtype=np.int64))
assert not test_module.is_node_id(CircuitEdgeId("default", 1))


def test_ensure_list():
assert test_module.ensure_list(1) == [1]
assert test_module.ensure_list([1]) == [1]
Expand Down

0 comments on commit ce650f2

Please sign in to comment.