Skip to content

core_utils

Class representing a failed run.

Source code in navis/core/core_utils.py
794
795
796
797
798
799
800
801
802
803
804
805
806
807
class FailedRun:
    """Class representing a failed run."""
    def __init__(self, func, args, kwargs, exception='NA'):
        self.args = args
        self.func = func
        self.kwargs = kwargs
        self.exception = exception

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        return (f'Failed run(function={self.func}, args={self.args}, '
                f'kwargs={self.kwargs}, exception={self.exception})')

Add neuron units (if present) to output of function.

Source code in navis/core/core_utils.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def add_units(compact=True, power=1):
    """Add neuron units (if present) to output of function."""
    def outer(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            self = args[0]
            res = func(*args, **kwargs)

            if config.add_units and self.has_units and not self.units.dimensionless:
                res = res * np.power(self.units, power)
                if compact:
                    res = res.to_compact()

            return res

        return wrapper

    return outer

Compute tangent vectors and alpha from a point cloud.

For each point: take its k nearest neighbours (itself included), form the scatter matrix of that neighbourhood about its centroid, and return the principal direction plus (l1 - l2) / (l1 + l2 + l3) for its eigenvalues.

Uses navis_fastcore.dotprops if available - it fuses the k-NN and the eigendecomposition into one parallel Rust pass and is ~15x faster than the cKDTree + N-SVDs route below - and falls back to scipy/numpy otherwise.

Note the two agree exactly except where the k-NN search hits a tied distance, which grid-quantised coordinates produce readily: there the k-th neighbour is ambiguous and the two trees may pick different points. That affects ~0.3% of points on the example neurons and moves NBLAST scores by ~1e-4 without changing match ranking.

PARAMETER DESCRIPTION
points

TYPE: (N, 3) array

k
    Number of nearest neighbours, *including the point itself*.

TYPE: int

RETURNS DESCRIPTION
vect

Unit tangent vectors. The sign is arbitrary (an eigenvector is only defined up to sign, and NBLAST scores on |dot|) but deterministic within a backend.

TYPE: (N, 3) array

alpha

TYPE: (N, ) array

Source code in navis/core/core_utils.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def tangents_and_alpha(points, k):
    """Compute tangent vectors and alpha from a point cloud.

    For each point: take its `k` nearest neighbours (itself included), form the
    scatter matrix of that neighbourhood about its centroid, and return the
    principal direction plus `(l1 - l2) / (l1 + l2 + l3)` for its eigenvalues.

    Uses `navis_fastcore.dotprops` if available - it fuses the k-NN and the
    eigendecomposition into one parallel Rust pass and is ~15x faster than the
    `cKDTree` + N-SVDs route below - and falls back to scipy/numpy otherwise.

    Note the two agree exactly except where the k-NN search hits a *tied*
    distance, which grid-quantised coordinates produce readily: there the k-th
    neighbour is ambiguous and the two trees may pick different points. That
    affects ~0.3% of points on the example neurons and moves NBLAST scores by
    ~1e-4 without changing match ranking.

    Parameters
    ----------
    points :    (N, 3) array
    k :         int
                Number of nearest neighbours, *including the point itself*.

    Returns
    -------
    vect :      (N, 3) array
                Unit tangent vectors. The sign is arbitrary (an eigenvector is
                only defined up to sign, and NBLAST scores on `|dot|`) but
                deterministic within a backend.
    alpha :     (N, ) array

    """
    if utils.fastcore is not None and hasattr(utils.fastcore, 'dotprops'):
        return utils.fastcore.dotprops(points, k=k)

    tree = cKDTree(points)
    _, ix = tree.query(points, k=k)

    # This makes sure we have a (N, k) shaped array even if k = 1
    ix = ix.reshape(points.shape[0], k)

    # Get points: array of (N, k, 3)
    pt = points[ix]

    # Generate centers for each cloud of k nearest neighbors
    centers = np.mean(pt, axis=1)

    # Generate vector from center
    cpt = pt - centers.reshape((pt.shape[0], 1, 3))

    # Get inertia (N, 3, 3)
    inertia = cpt.transpose((0, 2, 1)) @ cpt

    # Extract vector and alpha
    u, s, vh = np.linalg.svd(inertia)
    vect = vh[:, 0, :]
    with np.errstate(invalid='ignore'):
        alpha = (s[:, 0] - s[:, 1]) / np.sum(s, axis=1)

    # A neighbourhood of coincident points has a zero scatter matrix, so alpha
    # is 0/0. Report it as 0 with an arbitrary unit vector - matching fastcore -
    # rather than a NaN that would silently poison every downstream score.
    degen = ~np.isfinite(alpha)
    if degen.any():
        alpha = np.where(degen, 0.0, alpha)
        vect = np.where(degen[:, None], np.array([1.0, 0.0, 0.0]), vect)

    return vect, alpha

Check if neuron is stale. Clear cached temporary attributes if it is.

Source code in navis/core/core_utils.py
139
140
141
142
143
144
145
146
147
148
149
def temp_property(func):
    """Check if neuron is stale. Clear cached temporary attributes if it is."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        self = args[0]
        # Do nothing if neurons is locked
        if not self.is_locked:
            if self.is_stale:
                self._clear_temp_attr()
        return func(*args, **kwargs)
    return wrapper