Skip to content

NBLAST

See Concepts › NBLAST for how the scoring works and what the Rust pipeline does under the hood. This page covers the Python API.

A "dotprop" is any object exposing points (an (N, 3) array of coordinates) and vect (an (N, 3) array of unit tangent vectors). When use_alpha is enabled it must additionally expose alpha (an (N,) array).

All-by-all

import navis_fastcore as fastcore

import numpy as np
from collections import namedtuple

Dotprop = namedtuple("Dotprop", ["points", "vect"])

# 10 example dotprops (use real tangent vectors in practice)
dps = [Dotprop(np.random.rand(100, 3), np.random.rand(100, 3)) for _ in range(10)]

# (10, 10) float32 matrix; row = query, column = target, diagonal = 1.0
scores = fastcore.nblast_allbyall(dps)

# Symmetric scores (also 'min' / 'max')
scores_sym = fastcore.nblast_allbyall(dps, symmetry="mean")

Query vs target

# (len(query), len(target)) score matrix
scores = fastcore.nblast(query_dps, target_dps)

# 'mean' / 'min' / 'max' also combine with the reverse (target-vs-query) NBLAST
scores = fastcore.nblast(query_dps, target_dps, symmetry="mean")

Smart NBLAST

nblast_smart is a two-pass approximation for large comparisons. It first runs a cheap "pre-NBLAST" on downsampled dotprops, then keeps only the best-scoring targets per query and recomputes those pairs at full resolution. Unselected cells keep their coarse pre-pass score. This mirrors navis' nblast_smart (its scores argument is spelled symmetry here, matching the other functions).

# Keep the top 10% of targets per query (percentile 90) for the full pass
scores = fastcore.nblast_smart(query_dps, target_dps, t=90, criterion="percentile")

# All-by-all; also return the boolean mask of cells recomputed at full resolution
scores, mask = fastcore.nblast_smart(dps, t=90, return_mask=True)

# Other selection criteria: an absolute score threshold, or a fixed number per query
scores = fastcore.nblast_smart(dps, t=0.3, criterion="score")
scores = fastcore.nblast_smart(dps, t=10, criterion="N")

Extra options beyond the shared ones below: t / criterion select the candidate targets ("percentile", "score" or "N"), downsample (default 10) sets the pre-pass point stride, and return_mask additionally returns which cells were recomputed. Because fastcore's dense NBLAST is already fast, nblast_smart pays off mainly for large all-by-all comparisons where the full-resolution scoring dominates; on small inputs the extra pre-pass can make it a wash.

syNBLAST (synapse-based)

synblast compares neurons by their synapses (connectors) instead of their skeleton points: for every query connector it finds the nearest target connector of the same type and scores that distance through the same lookup matrix with the dot product fixed at 1 (synapses carry no tangent vector). This mirrors navis' synblast (its scores argument is spelled symmetry here).

A "synapse cloud" is any object exposing connectors — an (N, 3) or (N, 4) array of [x, y, z, (type)], where the optional 4th column is a numeric connector type (e.g. 0 = presynapse, 1 = postsynapse). The Synapses namedtuple is a minimal container for one.

import navis_fastcore as fastcore
from navis_fastcore import Synapses

import numpy as np

# 5 neurons; each connector is [x, y, z, type] with type in {0, 1}
neurons = [
    Synapses(np.hstack([np.random.rand(200, 3) * 10, np.random.randint(0, 2, (200, 1))]))
    for _ in range(5)
]

# (5, 5) all-by-all matrix; diagonal = 1.0
scores = fastcore.synblast(neurons)

# Only compare like-typed synapses (pre-vs-pre, post-vs-post)
scores = fastcore.synblast(neurons, by_type=True)

# Query vs target, symmetric, restricted to presynapses
scores = fastcore.synblast(neurons[:2], neurons, symmetry="mean", cn_types=[0])

synblast shares smat, normalize, symmetry, n_cores, precision and progress with nblast (see below), plus two synapse-specific options: by_type (default False) restricts matches to same-type connectors, and cn_types keeps only connectors whose type is in the given set before scoring. It does not take use_alpha or limit_dist (neither applies to synapses).

Options

Both nblast_allbyall and nblast accept the same options:

  • smat: the scoring matrix. None (default) uses the embedded FCWB matrix. You may also pass a navis Lookup2d, or a (values, dist_edges, dot_edges) tuple where the edges are the ascending left bin boundaries.
  • normalize (default True): divide each score by the query's self-hit so a perfect self-match scores 1.0.
  • symmetry (default None): None / "forward" returns the raw forward (asymmetric) matrix; "mean", "min" or "max" combine it with the reverse direction (its transpose for nblast_allbyall, an explicit reverse NBLAST for the rectangular nblast).
  • use_alpha (default False): weight each point's dot product by sqrt(alpha_query * alpha_target), emphasising locally linear (backbone) regions. Requires each dotprop to expose a per-point alpha. With no explicit smat, this auto-selects the alpha-calibrated FCWB matrix (as navis does); an explicit smat is used as given.
  • limit_dist (default None): a distance upper bound. A query point whose nearest neighbour is farther than this is scored at the matrix's "far + orthogonal" corner (aann prunes such searches). Pass a number, or "auto" for 1.05 × the last distance-bin edge (as in navis).
  • n_cores (default None): cap the number of worker threads. None uses all available cores.
  • precision (default 32): dtype of the returned matrix — 16, 32 or 64 (or "half" / "single" / "double"). The scoring math always runs in float64; precision only sets the storage width of the result.
  • progress (default False): show a progress bar over the scoring pairs (drawn from Rust to stderr; plain reprinted text under Jupyter).

API

NBLAST every query neuron against every target neuron.

A long run can be interrupted with Ctrl-C / the Jupyter interrupt button; it stops promptly and raises KeyboardInterrupt.

PARAMETER DESCRIPTION
query
        Each must expose `points` (N, 3) and unit tangent `vect`
        (N, 3); also `alpha` (N,) when ``use_alpha`` is set.

TYPE: iterable of dotprop-likes

target
        Each must expose `points` (N, 3) and unit tangent `vect`
        (N, 3); also `alpha` (N,) when ``use_alpha`` is set.

TYPE: iterable of dotprop-likes

smat
        Scoring matrix. ``None`` uses the embedded FCWB matrix.

TYPE: None | navis Lookup2d | (values, dist_edges, dot_edges) DEFAULT: None

normalize
        Divide each score by the query's self-hit.

TYPE: bool DEFAULT: True

symmetry
        ``None`` / ``'forward'`` returns the raw forward matrix.
        The others combine it with the reverse (target-vs-query)
        NBLAST, matching navis' ``scores`` argument.

TYPE: None | 'forward' | 'mean' | 'min' | 'max' DEFAULT: None

use_alpha
        Weight each dot product by ``sqrt(alpha_query * alpha_target)``.

TYPE: bool DEFAULT: False

limit_dist
        Distance upper bound (see ``nblast_allbyall``).

TYPE: None | float | 'auto' DEFAULT: None

n_cores
        Cap the number of worker threads. ``None`` uses all cores.

TYPE: int | None DEFAULT: None

precision
        Dtype of the returned matrix. Math is always float64.

TYPE: 16 | 32 | 64 DEFAULT: 32

progress
        Show progress bars over index building and scoring.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
np.ndarray

(n_query, n_target) score matrix; row = query, column = target.

All-by-all NBLAST.

A long run can be interrupted with Ctrl-C / the Jupyter interrupt button; it stops promptly and raises KeyboardInterrupt.

PARAMETER DESCRIPTION
dotprops
     Each must expose `points` (N, 3) and unit tangent `vect` (N, 3).
     When ``use_alpha`` is set, each must also expose `alpha` (N,).

TYPE: iterable of dotprop-likes

smat
     Scoring matrix. ``None`` uses the embedded FCWB matrix.

TYPE: None | navis Lookup2d | (values, dist_edges, dot_edges) DEFAULT: None

normalize
     Divide each score by the query's self-hit (self-match == 1.0).

TYPE: bool DEFAULT: True

symmetry
     Combine the forward matrix with its transpose. ``None`` /
     ``'forward'`` returns the raw forward (asymmetric) matrix.

TYPE: None | 'forward' | 'mean' | 'min' | 'max' DEFAULT: None

use_alpha
     Weight each dot product by ``sqrt(alpha_query * alpha_target)``,
     emphasising locally linear (backbone) regions.

TYPE: bool DEFAULT: False

limit_dist
     Distance upper bound: a query point whose nearest neighbour is
     farther than this is scored at the "far + orthogonal" corner of
     the matrix. ``'auto'`` uses ``1.05 *`` the last distance bin edge.

TYPE: None | float | auto DEFAULT: None

n_cores
     Cap the number of worker threads. ``None`` uses all cores.

TYPE: int | None DEFAULT: None

precision
     Dtype of the returned matrix. The scoring math is always float64.

TYPE: 16 | 32 | 64 DEFAULT: 32

progress
     Show progress bars (drawn from Rust to stderr): first over
     building the neuron indices, then over the scoring pairs.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
np.ndarray

(n, n) score matrix; row = query, column = target.

Smart(er) NBLAST: a fast two-pass approximation of an all-by-all / query-target.

A cheap "pre-NBLAST" is run on downsampled dotprops; for each query the best-scoring targets are then kept (per criterion) and the full-resolution NBLAST is computed only for those query-target pairs. Unselected cells keep their coarse pre-pass score. This matches navis' nblast_smart (its scores argument is spelled symmetry here, as in nblast/nblast_allbyall).

A long run can be interrupted with Ctrl-C / the Jupyter interrupt button (during either pass); it stops promptly and raises KeyboardInterrupt.

PARAMETER DESCRIPTION
query
     Each must expose `points` (N, 3) and unit tangent `vect` (N, 3);
     also `alpha` (N,) when ``use_alpha`` is set.

TYPE: iterable of dotprop-likes

target
     Targets to compare against. ``None`` runs an all-by-all
     (``target = query``).

TYPE: iterable of dotprop-likes | None DEFAULT: None

t
     Threshold for ``criterion``: the percentile (default, ``90`` keeps
     the top 10% per query), an absolute score, or the number of targets.

TYPE: int | float DEFAULT: 90

criterion
     How ``t`` selects the candidate targets kept for the full pass.

TYPE: 'percentile' | 'score' | 'N' DEFAULT: 'percentile'

downsample
     Pre-pass keeps every ``downsample``-th point (default ``10``).

TYPE: int DEFAULT: 10

smat
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

normalize
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

symmetry
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

use_alpha
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

limit_dist
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

n_cores
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

precision
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

progress
     As in `nblast` / `nblast_allbyall`.

DEFAULT: None

return_mask
     If ``True``, also return the boolean mask of cells that were
     recomputed at full resolution.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
np.ndarray | (np.ndarray, np.ndarray)

The (n_query, n_target) score matrix, or (scores, mask) when return_mask is set.

Synapse-based NBLAST (syNBLAST).

Compares neurons by their synapses (connectors) rather than their skeleton points: for every query connector the nearest target connector of the same type is found, and the euclidean distance is scored through the NBLAST lookup matrix with the dot product fixed at 1 (synapses have no tangent vector). This matches navis' synblast (navis' scores argument is spelled symmetry here, as in nblast / nblast_allbyall).

A long run can be interrupted with Ctrl-C / the Jupyter interrupt button; it stops promptly and raises KeyboardInterrupt.

PARAMETER DESCRIPTION
query
     Each item exposes ``connectors`` (or is itself an array) of shape
     ``(N, 3)`` or ``(N, 4)`` — ``[x, y, z, (type)]``. The 4th column is
     a numeric connector type, required when ``by_type`` / ``cn_types``
     are set. See `Synapses`.

TYPE: iterable of synapse-bearing neurons

target
     Targets to compare against. ``None`` runs an all-by-all
     (``target = query``).

TYPE: iterable of synapse-bearing neurons | None DEFAULT: None

by_type
     If ``True``, only connectors of the same type compare against each
     other (navis' ``by_type``); requires a type column. Default
     ``False`` treats all connectors as one group.

TYPE: bool DEFAULT: False

cn_types
     If given, keep only connectors whose type is in this set before
     scoring (navis' ``cn_types``); requires a type column.

TYPE: iterable | None DEFAULT: None

smat
     Scoring matrix. ``None`` uses the embedded FCWB matrix (navis'
     ``smat="auto"``); only its last (aligned) dot-product column is
     used.

TYPE: None | navis Lookup2d | (values, dist_edges, dot_edges) DEFAULT: None

normalize
     Divide each score by the query's self-hit (self-match == 1.0).

TYPE: bool DEFAULT: True

symmetry
     ``None`` / ``'forward'`` returns the raw forward matrix; the others
     combine it with the reverse (target-vs-query) syNBLAST.

TYPE: None | 'forward' | 'mean' | 'min' | 'max' DEFAULT: None

n_cores
     Cap the number of worker threads. ``None`` uses all cores.

TYPE: int | None DEFAULT: None

precision
     Dtype of the returned matrix. The scoring math is always float64.

TYPE: 16 | 32 | 64 DEFAULT: 32

progress
     Show progress bars over index building and scoring.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
np.ndarray

(n_query, n_target) score matrix; row = query, column = target.

Extracting matches

Once you have a score matrix, pulling the best matches back out of it is its own problem — the matrices are big (a few 100k on a side is tens of GB), so the extraction must not copy or transpose them. Three criteria, all reading the matrix at its native width (float16, float32 or float64) and returning plain numpy arrays:

import numpy as np
import navis_fastcore as fastcore

scores = fastcore.nblast_allbyall(dps)          # (n, n) float32

# The 5 best targets per query, best first. `skip_self` drops the diagonal, which
# would otherwise be every neuron's own top hit.
indices, values = fastcore.top_matches(scores, 5, skip_self=True)

# Everything above an absolute cutoff, or within 5% of each query's own best match.
# Ragged, so returned CSR-style: query `q` owns indices[offsets[q]:offsets[q + 1]].
offsets, indices, values = fastcore.matches_above(scores, threshold=0.5)
offsets, indices, values = fastcore.matches_above(scores, percentage=0.05)

Expand the ragged result into a long-format table without a Python loop:

counts = np.diff(offsets)
query = np.repeat(np.arange(len(counts)), counts)
rank = np.arange(len(indices)) - np.repeat(offsets[:-1], counts)

Notes:

  • axis=1 gives matches per target instead of per query. It costs no more than axis=0 and does not transpose: the kernel walks column stripes of the row-major buffer, so the matrix is read exactly once either way. Passing a transposed view (scores.T) is likewise free.
  • The matrix is borrowed, never copied — including a np.memmap. Nothing here will cast your dtype behind your back, so a strided view or an unsupported dtype raises rather than silently materialising tens of GB.
  • distances=True if lower is better.
  • NaN is never a match. A query with no valid scores yields -1/NaN slots (top_matches) or an empty group (matches_above).
  • Ties break toward the lower index, so results don't depend on the thread count.
  • matches_above counts before it allocates, so max_matches can refuse an over-broad cutoff instead of exhausting the machine. Use count_matches to size a result first.

Extract the n best matches for each query (or target).

The matrix is neither copied nor transposed, whichever axis you ask for - matches along axis=1 are served by striding the row-major buffer in column blocks, so it is read exactly once either way. That matters: these matrices run to tens of GB.

PARAMETER DESCRIPTION
scores
        Score matrix, float16/32/64. Must be C- or F-contiguous. Not copied.

TYPE: (n_query, n_target) np.ndarray

n
        How many matches to extract per group. Must be <= the length of the
        scanned axis.

TYPE: int

axis
        0 = one row of matches per query (per row of `scores`); 1 = per target.

TYPE: 0 | 1 DEFAULT: 0

distances
        If True, *lower* is better (a distance matrix rather than similarity).

TYPE: bool DEFAULT: False

skip_self
        Exclude each group's self-match. True uses the diagonal (requires a
        square matrix); an array gives the index to skip per group, -1 for none.

TYPE: bool | (n_groups,) array DEFAULT: False

n_cores
        Cap the worker count. Default: all cores.

TYPE: int DEFAULT: None

progress

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
indices

Index along the other axis, best match first. -1 where the group had fewer than n valid (non-NaN) cells.

TYPE: (n_groups, n) int64

values

The matching scores, in the dtype of scores. NaN where the paired index is -1.

TYPE: (n_groups, n)

Examples:

>>> import numpy as np
>>> import navis_fastcore as fastcore
>>> scores = np.array([[1.0, 0.2, 0.9],
...                    [0.2, 1.0, 0.4],
...                    [0.9, 0.4, 1.0]], dtype=np.float32)
>>> idx, val = fastcore.top_matches(scores, 2)
>>> idx
array([[0, 2],
       [1, 2],
       [2, 0]])

Ignore the self-hits on the diagonal:

>>> idx, val = fastcore.top_matches(scores, 1, skip_self=True)
>>> idx.ravel()
array([2, 2, 0])
>>> val.ravel()
array([0.9, 0.4, 0.9], dtype=float32)

Extract every match clearing a cutoff. Ragged, so returned CSR-style.

Give exactly one of:

  • threshold - an absolute cutoff: keep every cell >= threshold (<= if distances).
  • percentage - a band around each group's own best value: percentage=0.05 keeps everything within 5% of that group's top match. Note this is "within X% of the best", not "the top X%".
PARAMETER DESCRIPTION
scores
        Score matrix, float16/32/64. Must be C- or F-contiguous. Not copied.

TYPE: (n_query, n_target) np.ndarray

threshold

TYPE: float DEFAULT: None

percentage
        In [0, 1].

TYPE: float DEFAULT: None

axis

TYPE: 0 | 1 DEFAULT: 0

distances
        If True, *lower* is better.

TYPE: bool DEFAULT: False

skip_self
        See [`top_matches`][navis_fastcore.top_matches].

TYPE: bool | (n_groups,) array DEFAULT: False

max_matches
        Refuse to allocate more than this many matches. The count is known
        before anything is allocated, so an over-broad cutoff raises instead of
        taking the machine down with it. See
        [`count_matches`][navis_fastcore.count_matches] to size a result first.

TYPE: int DEFAULT: None

n_cores

TYPE: int DEFAULT: None

progress

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
offsets

Group g's matches are indices[offsets[g]:offsets[g + 1]].

TYPE: (n_groups + 1,) int64

indices

Index along the other axis, best first within each group.

TYPE: (total,) uint32

values

The matching scores, in the dtype of scores.

TYPE: (total,)

Examples:

>>> import numpy as np
>>> import navis_fastcore as fastcore
>>> scores = np.array([[1.0, 0.2, 0.9],
...                    [0.2, 1.0, 0.4],
...                    [0.9, 0.4, 1.0]], dtype=np.float32)
>>> offsets, indices, values = fastcore.matches_above(scores, threshold=0.5)
>>> offsets
array([0, 2, 3, 5])
>>> indices[offsets[0]:offsets[1]]   # query 0's matches, best first
array([0, 2], dtype=uint32)

Expand to a long-format table without a Python loop:

>>> counts = np.diff(offsets)
>>> query = np.repeat(np.arange(len(counts)), counts)
>>> rank = np.arange(len(indices)) - np.repeat(offsets[:-1], counts)
>>> query
array([0, 0, 1, 2, 2])
>>> rank
array([0, 1, 0, 0, 1])

Count the matches each group would yield, without materialising them.

The counting half of matches_above on its own. Use it to size a result - or to pick a cutoff - on a matrix you cannot afford to guess wrong about.

PARAMETER DESCRIPTION
scores
        Score matrix, float16/32/64. Must be C- or F-contiguous. Not copied.

TYPE: (n_query, n_target) np.ndarray

threshold

TYPE: float DEFAULT: None

percentage
        Exactly one of the two, as for `matches_above`.

TYPE: float DEFAULT: None

axis

TYPE: 0 | 1 DEFAULT: 0

distances

TYPE: bool DEFAULT: False

skip_self

TYPE: bool | (n_groups,) array DEFAULT: False

n_cores

TYPE: int DEFAULT: None

RETURNS DESCRIPTION
counts

TYPE: (n_groups,) int64

Examples:

>>> import numpy as np
>>> import navis_fastcore as fastcore
>>> scores = np.array([[1.0, 0.2, 0.9],
...                    [0.2, 1.0, 0.4],
...                    [0.9, 0.4, 1.0]], dtype=np.float32)
>>> fastcore.count_matches(scores, threshold=0.5)
array([2, 1, 2])