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 navisLookup2d, or a(values, dist_edges, dot_edges)tuple where the edges are the ascending left bin boundaries.normalize(defaultTrue): divide each score by the query's self-hit so a perfect self-match scores 1.0.symmetry(defaultNone):None/"forward"returns the raw forward (asymmetric) matrix;"mean","min"or"max"combine it with the reverse direction (its transpose fornblast_allbyall, an explicit reverse NBLAST for the rectangularnblast).use_alpha(defaultFalse): weight each point's dot product bysqrt(alpha_query * alpha_target), emphasising locally linear (backbone) regions. Requires each dotprop to expose a per-pointalpha. With no explicitsmat, this auto-selects the alpha-calibrated FCWB matrix (as navis does); an explicitsmatis used as given.limit_dist(defaultNone): a distance upper bound. A query point whose nearest neighbour is farther than this is scored at the matrix's "far + orthogonal" corner (aannprunes such searches). Pass a number, or"auto"for1.05 ×the last distance-bin edge (as in navis).n_cores(defaultNone): cap the number of worker threads.Noneuses all available cores.precision(default32): dtype of the returned matrix —16,32or64(or"half"/"single"/"double"). The scoring math always runs in float64;precisiononly sets the storage width of the result.progress(defaultFalse): show a progress bar over the scoring pairs (drawn from Rust to stderr; plain reprinted text under Jupyter).
API¶
nblast(query, target, smat=None, normalize=True, symmetry=None, use_alpha=False, limit_dist=None, n_cores=None, precision=32, progress=False)
¶
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
|
TYPE:
|
target
|
TYPE:
|
smat
|
TYPE:
|
normalize
|
TYPE:
|
symmetry
|
TYPE:
|
use_alpha
|
TYPE:
|
limit_dist
|
TYPE:
|
n_cores
|
TYPE:
|
precision
|
TYPE:
|
progress
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
np.ndarray
|
(n_query, n_target) score matrix; row = query, column = target. |
navis_fastcore.nblast_allbyall(dotprops, smat=None, normalize=True, symmetry=None, use_alpha=False, limit_dist=None, n_cores=None, precision=32, progress=False)
¶
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
|
TYPE:
|
smat
|
TYPE:
|
normalize
|
TYPE:
|
symmetry
|
TYPE:
|
use_alpha
|
TYPE:
|
limit_dist
|
TYPE:
|
n_cores
|
TYPE:
|
precision
|
TYPE:
|
progress
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
np.ndarray
|
(n, n) score matrix; row = query, column = target. |
navis_fastcore.nblast_smart(query, target=None, t=90, criterion='percentile', downsample=10, smat=None, normalize=True, symmetry=None, use_alpha=False, limit_dist=None, n_cores=None, precision=32, progress=False, return_mask=False)
¶
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
|
TYPE:
|
target
|
TYPE:
|
t
|
TYPE:
|
criterion
|
TYPE:
|
downsample
|
TYPE:
|
smat
|
DEFAULT:
|
normalize
|
DEFAULT:
|
symmetry
|
DEFAULT:
|
use_alpha
|
DEFAULT:
|
limit_dist
|
DEFAULT:
|
n_cores
|
DEFAULT:
|
precision
|
DEFAULT:
|
progress
|
DEFAULT:
|
return_mask
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
np.ndarray | (np.ndarray, np.ndarray)
|
The (n_query, n_target) score matrix, or |
navis_fastcore.synblast(query, target=None, by_type=False, cn_types=None, smat=None, normalize=True, symmetry=None, n_cores=None, precision=32, progress=False)
¶
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
|
TYPE:
|
target
|
TYPE:
|
by_type
|
TYPE:
|
cn_types
|
TYPE:
|
smat
|
TYPE:
|
normalize
|
TYPE:
|
symmetry
|
TYPE:
|
n_cores
|
TYPE:
|
precision
|
TYPE:
|
progress
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
np.ndarray
|
(n_query, n_target) score matrix; row = query, column = target. |
navis_fastcore.Synapses = namedtuple('Synapses', ['connectors'])
module-attribute
¶
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=1gives matches per target instead of per query. It costs no more thanaxis=0and 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=Trueif lower is better.NaNis never a match. A query with no valid scores yields-1/NaNslots (top_matches) or an empty group (matches_above).- Ties break toward the lower index, so results don't depend on the thread count.
matches_abovecounts before it allocates, somax_matchescan refuse an over-broad cutoff instead of exhausting the machine. Usecount_matchesto size a result first.
navis_fastcore.top_matches(scores, n, axis=0, distances=False, skip_self=False, n_cores=None, progress=False)
¶
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
|
TYPE:
|
n
|
TYPE:
|
axis
|
TYPE:
|
distances
|
TYPE:
|
skip_self
|
TYPE:
|
n_cores
|
TYPE:
|
progress
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
indices
|
Index along the other axis, best match first. -1 where the group had
fewer than
TYPE:
|
values
|
The matching scores, in the dtype of
TYPE:
|
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:
navis_fastcore.matches_above(scores, threshold=None, percentage=None, axis=0, distances=False, skip_self=False, max_matches=None, n_cores=None, progress=False)
¶
Extract every match clearing a cutoff. Ragged, so returned CSR-style.
Give exactly one of:
threshold- an absolute cutoff: keep every cell>= threshold(<=ifdistances).percentage- a band around each group's own best value:percentage=0.05keeps everything within 5% of that group's top match. Note this is "within X% of the best", not "the top X%".
| PARAMETER | DESCRIPTION |
|---|---|
scores
|
TYPE:
|
threshold
|
TYPE:
|
percentage
|
TYPE:
|
axis
|
TYPE:
|
distances
|
TYPE:
|
skip_self
|
TYPE:
|
max_matches
|
TYPE:
|
n_cores
|
TYPE:
|
progress
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
offsets
|
Group
TYPE:
|
indices
|
Index along the other axis, best first within each group.
TYPE:
|
values
|
The matching scores, in the dtype of
TYPE:
|
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:
navis_fastcore.count_matches(scores, threshold=None, percentage=None, axis=0, distances=False, skip_self=False, n_cores=None)
¶
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
|
TYPE:
|
threshold
|
TYPE:
|
percentage
|
TYPE:
|
axis
|
TYPE:
|
distances
|
TYPE:
|
skip_self
|
TYPE:
|
n_cores
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
counts
|
TYPE:
|
Examples: