Skip to content

Commit

Permalink
Merge pull request #150 from navis-org/fastcore
Browse files Browse the repository at this point in the history
Integrate fastcore functions
  • Loading branch information
schlegelp authored Aug 15, 2024
2 parents 9d513c2 + 7dc5011 commit 5d21d13
Show file tree
Hide file tree
Showing 32 changed files with 1,436 additions and 859 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
pip install k3d
pip install pyarrow
- name: Install navis
run: pip install -e .[dev,vispy-pyqt5,pathos,cloudvolume]
run: pip install -e .[dev,vispy-pyqt5,pathos,cloudvolume,fastcore]
- run: pip install python-igraph
if: ${{ matrix.igraph == 'igraph' }}
- name: Report dependency versions
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ NAVis is on [ReadTheDocs](http://navis.readthedocs.io/ "NAVis ReadTheDocs").
* **render**: use Blender 3D for high quality [visualizations](https://youtu.be/wl3sFG7WQJc)
* **R** neuron libraries: interfaces with [nat](https://github.com/jefferis/nat), [rcatmaid](https://github.com/jefferis/rcatmaid), [elmr](https://github.com/jefferis/elmr) and more
* **import-export**: read/write SWCs, neuroglancer's ["*precomputed*"](https://github.com/google/neuroglancer/tree/master/src/datasource/precomputed) format, NMX/NML, NRRD, mesh-files and more
* **fast**: uses functions compiled in Rust under-the-hood (see [fastcore](https://github.com/schlegelp/fastcore-rs))
* **scalable**: out-of-the-box support for multiprocessing
* **extensible**: build your own package on top of navis - see [pymaid](https://pymaid.readthedocs.io/en/latest/) for example

Expand Down
24 changes: 12 additions & 12 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ def pytest_ignore_collect(path, config):
more specific hooks.
"""
path = str(path)
if 'interfaces' in path:
return True
if '/docs' in path:
return True
if '/stubs' in path:
return True
if '/examples' in path:
return True
if '/dist/' in path:
return True
if '/binder' in path:
return True
for pattern in (
"interfaces",
"/docs",
"/stubs",
"/examples",
"/dist/",
"/binder",
"/site",
"/scripts",
):
if pattern in path:
return True
15 changes: 14 additions & 1 deletion docs/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Install
=======

NAVis currently requires Python 3.8 or later. Below instructions assume that
NAVis currently requires Python 3.9 or later. Below instructions assume that
you have already installed Python and its package manager ``pip``.

.. topic:: By the way
Expand Down Expand Up @@ -67,6 +67,19 @@ These extras can be installed directly, or along with navis with
The user-facing extras, the dependencies they install,
and how to install those dependencies directly, are below.

.. _fastcore:

``fastcore``: `navis-fastcore <https://github.com/schlegelp/fastcore-rs>`_
``navis-fastcore`` re-implements a bunch of low-level functions in Rust
and wraps them in Python. ``navis`` will use ``fastcore`` under-the-hood
if it is available. This is a highly recommended extra as it will
speed up certain operations such as geodesic distances, Strahler Index
or pruning by several orders of magnitude.

::

pip3 install navis-fastcore


.. _pykd:

Expand Down
4 changes: 2 additions & 2 deletions docs/source/tutorials/morph_analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@
"import networkx as nx\n",
"\n",
"# First we need to find the path between the soma and the terminal node\n",
"path = nx.shortest_path(n.graph.to_undirected(), n.soma[0], end)\n",
"path = nx.shortest_path(n.graph.to_undirected(), n.soma, end)\n",
"\n",
"# Get coordinates for the path\n",
"path_co = n.nodes.set_index('node_id').loc[path, ['x', 'y', 'z']].copy()\n",
Expand All @@ -754,7 +754,7 @@
"\n",
"# Add Euclidian path\n",
"end_loc = n.nodes.set_index('node_id').loc[end, ['x', 'y', 'z']]\n",
"soma_loc = n.nodes.set_index('node_id').loc[n.soma[0], ['x', 'y', 'z']]\n",
"soma_loc = n.nodes.set_index('node_id').loc[n.soma, ['x', 'y', 'z']]\n",
"ax.plot([soma_loc.x, end_loc.x], [-soma_loc.z, -end_loc.z], c='g', ls='--')\n",
"\n",
"d_eucl = np.sqrt(np.sum((end_loc - soma_loc)**2)) * n.units\n",
Expand Down
2 changes: 1 addition & 1 deletion navis/connectivity/adjacency.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def to_adjacency(self, include_other=True) -> pd.DataFrame:
data = np.zeros((len(index), len(index)), np.uint64)
df = pd.DataFrame(data, index, index)
for _, src, tgt, _, _ in self.edges(include_other):
df[tgt][src] += 1
df.loc[src, tgt] += 1

return df

Expand Down
1 change: 0 additions & 1 deletion navis/core/dotprop.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import numbers
import pint
import types
import uuid
import warnings

import numpy as np
Expand Down
21 changes: 6 additions & 15 deletions navis/core/skeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,13 @@ def __truediv__(self, other, copy=True):

# If a number, consider this an offset for coordinates
n = self.copy() if copy else self
n.nodes.loc[:, ['x', 'y', 'z', 'radius']] /= other
n.nodes[['x', 'y', 'z', 'radius']] /= other

# At this point we can ditch any 4th unit
if utils.is_iterable(other):
other = other[:3]
if n.has_connectors:
n.connectors.loc[:, ['x', 'y', 'z']] /= other
n.connectors[['x', 'y', 'z']] /= other

if hasattr(n, 'soma_radius'):
if isinstance(n.soma_radius, numbers.Number):
Expand Down Expand Up @@ -254,13 +254,13 @@ def __mul__(self, other, copy=True):

# If a number, consider this an offset for coordinates
n = self.copy() if copy else self
n.nodes.loc[:, ['x', 'y', 'z', 'radius']] *= other
n.nodes[['x', 'y', 'z', 'radius']] *= other

# At this point we can ditch any 4th unit
if utils.is_iterable(other):
other = other[:3]
if n.has_connectors:
n.connectors.loc[:, ['x', 'y', 'z']] *= other
n.connectors[['x', 'y', 'z']] *= other

if hasattr(n, 'soma_radius'):
if isinstance(n.soma_radius, numbers.Number):
Expand Down Expand Up @@ -606,17 +606,8 @@ def n_leafs(self) -> Optional[int]:
@temp_property
def cable_length(self) -> Union[int, float]:
"""Cable length."""
if hasattr(self, '_cable_length'):
return self._cable_length

# The by far fastest way to get the cable length is to work on the node table
# Using the igraph representation is about the same speed - if it is already calculated!
# However, one problem with the graph representation is that with large neuronlists
# it adds a lot to the memory footprint.
not_root = (self.nodes.parent_id >= 0).values
xyz = self.nodes[['x', 'y', 'z']].values[not_root]
xyz_parent = self.nodes.set_index('node_id').loc[self.nodes.parent_id.values[not_root], ['x', 'y', 'z']].values
self._cable_length = np.sum(np.linalg.norm(xyz - xyz_parent, axis=1))
if not hasattr(self, '_cable_length'):
self._cable_length = morpho.cable_length(self)
return self._cable_length

@property
Expand Down
Loading

0 comments on commit 5d21d13

Please sign in to comment.