fastcore-rs¶
Fast implementations of the algorithms that navis
and nat need most: traversal and geometry on
rooted trees (i.e. neuron skeletons), plus
NBLAST neuron similarity.
The project is a single Rust core with bindings on top:
| Package | Language | |
|---|---|---|
fastcore |
Rust | the core crate |
navis-fastcore |
Python (PyO3) | docs |
nat.fastcore |
R (extendr) | docs |
The bindings are deliberately generalized: they know nothing about navis or
nat themselves, so third-party libraries can use them without those rather
heavy dependencies. navis-fastcore only requires numpy.
Install¶
Pre-compiled wheels are on PyPI. navis will use fastcore automatically
where appropriate; see the Python docs if you want to call
it directly.
install.packages(
"nat.fastcore",
repos = c("https://schlegelp.r-universe.dev", "https://cloud.r-project.org")
)
Pre-compiled binaries for Windows and macOS come from R-universe, so no Rust toolchain is needed. On Linux this builds from source. See the R docs.
The crate is not published to crates.io — depend on it via git. See the Rust docs.
What's available where¶
Every capability lives in the Rust core; the bindings differ only in what they
surface and how they spell it. — means that surface doesn't expose it directly.
Rooted trees (skeletons)¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
| Geodesic distance matrix | dag::geodesic_distances_all_by_all, dag::geodesic_distances_partial |
geodesic_matrix |
geodesic_distances |
| Geodesic distances for given pairs | dag::geodesic_pairs |
geodesic_pairs |
geodesic_pairs |
| Distance to the nearest target | dag::geodesic_nearest |
geodesic_nearest |
geodesic_nearest |
| Distance to the farthest target | dag::geodesic_farthest |
geodesic_farthest |
geodesic_farthest |
| Distances to the root | dag::all_dists_to_root, dag::dist_to_root |
dist_to_root |
all_dists_to_root, dist_to_root |
| Subtree height (distance down to the farthest leaf) | dag::subtree_height |
subtree_height |
subtree_height |
| Connected components | dag::connected_components |
connected_components |
connected_components |
| Break into linear segments | dag::generate_segments, dag::break_segments |
generate_segments, break_segments, segment_coords |
generate_segments, break_segments |
| Strahler index | dag::strahler_index |
strahler_index |
strahler_index |
| Prune twigs | dag::prune_twigs |
prune_twigs |
prune_twigs1 |
| Classify nodes (root/leaf/branch/slab) | dag::classify_nodes |
classify_nodes |
classify_nodes |
| Synapse flow centrality | dag::synapse_flow_centrality |
synapse_flow_centrality |
synapse_flow_centrality |
| Cycle detection | dag::has_cycles |
— | has_cycles |
| Node ID → index mapping | — | — (done internally) | node_indices |
| Child → parent distances | — | — (see parent_dist) |
child_to_parent_dists |
Healing fragmented skeletons¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
| Reconnect a broken skeleton | (composed in the bindings) | heal_skeleton |
heal_skeleton |
| Minimal reconnecting edges | topo::stitch_fragments |
stitch_fragments |
stitch_fragments |
| Regenerate the parent vector | topo::reroot_rewire |
— (used by heal_skeleton) |
reroot_rewire |
Meshes¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
| Connected components of a triangle mesh | mesh::mesh_connected_components |
mesh_connected_components |
mesh_connected_components |
| Geodesic distances across a mesh | mesh::geodesic_matrix_mesh |
geodesic_matrix_mesh |
geodesic_matrix_mesh |
| Nearest / farthest target vertex | mesh::geodesic_nearest_mesh, mesh::geodesic_farthest_mesh |
geodesic_nearest_mesh, geodesic_farthest_mesh |
geodesic_nearest_mesh, geodesic_farthest_mesh |
| Geodesic distances across any graph | mesh::geodesic_matrix_graph |
geodesic_matrix_graph |
geodesic_matrix_graph |
Neuron similarity¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
| NBLAST, query vs target | nblast::nblast_query_target |
nblast |
nblast |
| NBLAST, all-by-all | nblast::nblast_allbyall |
nblast_allbyall |
nblast_allbyall |
| NBLAST for explicit index pairs | nblast::nblast_pairs |
— | nblast_pairs |
| Two-pass approximate NBLAST | — | nblast_smart |
— |
| synNBLAST (synapse-based) | synblast::synblast_query_target, synblast::synblast_allbyall |
synblast |
synblast, synblast_allbyall |
limit_dist heuristic for a scoring matrix |
nblast::Smat |
— (via limit_dist="auto") |
smat_auto_limit |
Match extraction¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
| Top-N matches per query | matches::top_matches |
top_matches |
— |
| Matches above a threshold / percentage band | matches::matches_above |
matches_above |
— |
| Count matches without allocating them | matches::count_matches |
count_matches |
— |
Spatial transforms¶
| Capability | Rust (fastcore) |
Python (navis-fastcore) |
R (nat.fastcore) |
|---|---|---|---|
Read a CMTK *.list registration |
cmtk::Registration::from_path, cmtk::Chain |
CmtkRegistration, load_cmtk_registration |
cmtk_read |
| Apply it to points (forward / inverse) | cmtk::transform_points, cmtk::inverse_transform_points |
CmtkRegistration.xform, .xform_inv |
cmtk_xform, cmtk_xform_inv |
| CMTK registration properties | fields on Registration / SplineWarp |
.affine, .dims, .spacing |
cmtk_affine, cmtk_domain, cmtk_dims, cmtk_spacing |
Read an Elastix TransformParameters file |
elastix::ElastixTransform::from_path, elastix::Chain |
ElastixTransform, load_elastix_transform |
elastix_read |
| Apply it to points (forward / inverse) | elastix::transform_points, elastix::inverse_transform_points |
ElastixTransform.xform, .xform_inv |
elastix_xform, elastix_xform_inv |
| Elastix transform properties | fields on Linear / BSpline |
.affine, .kinds, .grid_size, .grid_spacing, .grid_origin |
elastix_affine, elastix_kinds, elastix_grid_size, elastix_grid_spacing, elastix_grid_origin |
| Is an Elastix file invertible? (without parsing it) | elastix::probe_invertible |
probe_elastix_invertible |
elastix_probe_invertible |
Neither CMTK nor Elastix needs to be installed — see CMTK transforms and
Elastix transforms. The two differ in one important respect: outside the
region it was fitted on, CMTK fails a point (we return NaN) while Elastix returns it
unchanged. Each binding follows its own tool. Elastix additionally gains an inverse,
which Elastix itself cannot compute.
Both work the same way: a transform object holds only the parse, and direction is chosen
per call (invert=, or the _inv method), so one object serves every direction rather than
re-reading the file to walk it the other way.
Python only¶
navis_fastcore.wrappers.csgraph provides drop-in replacements for some
scipy.sparse.csgraph routines (dijkstra, connected_components) that are
faster when your graph happens to be a rooted tree — see
Scipy wrappers.
A note on input conventions¶
The three surfaces take the same tree in different shapes:
- Rust is index-based: a parent-index vector in which roots are negative.
- Python takes
node_idsandparent_idsarrays with arbitrary IDs and maps them to indices for you. - R takes a precomputed
parentsindex vector (build one withnode_indices) plus separatex/y/zvectors.
So what reads geodesic_matrix(node_ids, parent_ids) in Python reads
geodesic_distances(parents, ...) in R, with node_indices called first.
-
R's
prune_twigshas nomaskargument — extendr cannot take aVec<bool>. ↩