fastcore (Rust)¶
The core crate. Everything the Python and R bindings do is implemented here; they are thin adapters that translate each language's idioms into the core's index-based API.
Modules¶
| Module | What it does |
|---|---|
dag |
Traversal and geometry on rooted trees: geodesic distances, linear segments, Strahler index, twig pruning, node classification, connected components, synapse flow centrality, cycle detection. |
topo |
Repairing fragmented skeletons: stitch_fragments finds the minimal-length edges that reconnect the pieces (optionally preferring fragments of similar calibre), reroot_rewire re-derives the parent vector afterwards. |
mesh |
Triangle meshes as vertex graphs: mesh_connected_components (and mesh_face_components, the same question read across shared edges rather than shared corners — optionally only across the edges exactly two faces carry, which makes the components surfaces rather than merely connected triangles) and unique_edges, a parallel Dijkstra/BFS behind geodesic_matrix_mesh, geodesic_nearest_mesh, geodesic_farthest_mesh and — for arbitrary (cyclic) graphs given as an edge list — geodesic_matrix_graph, geodesic_predecessors_graph and geodesic_path_graph, plus the traversal primitives that go with them: connected_components_graph, level_set_components, contract_vertices, minimum_spanning_tree, parents_from_edges (orient an edge list into a rooted forest, breaking cycles), bridges (Tarjan cut-edges), geodesic_mst_mesh / geodesic_mst_graph (span a subset of nodes by geodesic distance without materialising the k x k matrix) and geodesic_clusters. Plus GeodesicGraph, which is the search half of that list again as methods with the per-call adjacency build hoisted out (distances, nearest, farthest, predecessors, path, clusters, components, and subset for an induced subgraph), together with the two operations that only make sense against a graph you keep: grow, a connected region of a fixed number of nodes (or of cloud points attached to them), and farthest_seed, evenly-spread seeds by incremental, pruned farthest-point sampling. Everything bar GeodesicGraph is generic over mesh::Weight, so distances are accumulated and returned at f32 or f64 as the caller's weights dictate. |
simplify |
Decimating a triangle mesh by quadric-error edge collapse — simplify_mesh to a face budget, simplify_mesh_lossless to a fixed point under an error bound — and, uniquely among implementations of this algorithm, reporting through Simplified::vertex_map which vertex of the simplified mesh every vertex of the original ended up in. Also takes a locked mask of vertices that must survive at exactly their input coordinates. A port of Sven Forstmann's Simplify.h (MIT), the algorithm behind pyfqmr: flat index arrays rather than a halfedge structure, so non-manifold input is data rather than an error, and no C toolchain in the dependency tree. |
points |
Raw 3D point clouds: dotprops derives the unit tangent vector and alpha of every point's local k-neighbourhood — the representation nblast consumes — with an exact k-d tree k-NN and a Jacobi 3x3 eigensolve in place of cKDTree plus N SVDs. |
nblast |
The NBLAST pipeline — build_index, score_pair, nblast_query_target, nblast_allbyall, nblast_pairs, plus the Smat scoring matrix and Opts. |
nblast_knn |
Each neuron's k nearest neighbours without the n x n matrix — nblast_knn, nblast_knn_query_target, plus build_signatures / candidate_pairs and the Symmetry combine. |
synblast |
Synapse-based NBLAST: synblast_query_target, synblast_allbyall. |
matches |
Pulling the top matches back out of a score matrix — top_matches (top-N), matches_above (absolute threshold or a percentage band around each group's best), count_matches — without copying or transposing a matrix that may be tens of GB. |
cmtk |
CMTK spatial transforms: Registration::from_path reads a *.list registration (12-DOF affine + cubic B-spline warp), transform_points / inverse_transform_points apply it. Matches CMTK's streamxform to ~4e-7 without needing CMTK installed. |
elastix |
Elastix spatial transforms: ElastixTransform::from_path reads a TransformParameters file and the initial-transform chain hanging off it (affine / Euler / similarity / translation, plus cubic B-spline warps), transform_points / inverse_transform_points apply it. Matches transformix to 5e-7 without needing Elastix installed — and adds an inverse, which Elastix itself cannot compute. probe_invertible answers whether a file inverts without reading its coefficients, ~20x faster than a full parse. |
tps |
Thin-plate spline warps from landmark pairs: TpsTransform::fit solves for the coefficients (blocked LU, no BLAS dependency), xform applies them. The n_points x n_landmarks distance matrix is fused into the accumulation rather than built, so peak memory is the output and the landmark count is unbounded. |
mls |
Moving least squares warps (Schaefer et al. 2006, affine flavour): MlsTransform::xform solves a locally weighted affine per point. No fit step. Same fusion as tps, which is what makes landmark counts the reference implementation cannot allocate for tractable here. |
See Concepts › Rooted trees and Concepts › NBLAST for the ideas behind them, and the capability matrix for how each module maps onto the Python and R functions.
Using the crate¶
fastcore is not published to crates.io, so depend on it via git:
API reference¶
There is no docs.rs page (see above). Build the reference locally:
Shape of the API¶
The core is index-based and ndarray-typed. Where the
Python bindings accept node_ids and parent_ids with arbitrary IDs and map them
for you, fastcore expects the mapping to have happened already:
- A tree is an
ArrayView1<i32>of parent indices, with roots encoded as negative values. - Edge weights, coordinates and masks are passed as separate arrays.
- NBLAST takes prepared point clouds (
build_index) rather than dotprop objects.
That mapping step is exactly what navis_fastcore's internal _ids_to_indices
and nat.fastcore's public node_indices exist to do.
Parallelism¶
dag and the NBLAST modules parallelise with rayon.
Opts::threads caps the pool for NBLAST; the Python bindings surface this as
n_cores and release the GIL around the call.