pub struct HyperbolicTensorNetwork { /* private fields */ }Expand description
Hyperbolic Tensor Network for tree data representation.
Embeds hierarchical data into the Poincaré disk model using Sarkar’s cone-based construction. Each node occupies a point in hyperbolic space, with children placed at hyperbolic distance τ from their parent using Möbius reflections to preserve the tree structure as a Delaunay graph.
Internal maps use DashMap for lock-free concurrent reads, preparing for multi-threaded access in later phases.
Implementations§
Source§impl HyperbolicTensorNetwork
impl HyperbolicTensorNetwork
Sourcepub fn new(dimension: usize, tau: FixedPoint) -> Self
pub fn new(dimension: usize, tau: FixedPoint) -> Self
Create a new hyperbolic tensor network with the given Sarkar scale factor τ.
Sourcepub fn add_node_data_only(
&self,
metadata: NodeMetadata,
value: Vec<u8>,
_level: u32,
) -> String
pub fn add_node_data_only( &self, metadata: NodeMetadata, value: Vec<u8>, _level: u32, ) -> String
Add a node to the DashMap without computing geometric embedding.
Creates a key-derived unique_id for path_map/id_to_path lookups. Semantic queries (nearest_semantic, neighbors_semantic, get/set) work normally. Spatial queries (nearest, neighbors) will not find this node until it is embedded.
Sourcepub fn add_node(
&self,
metadata: NodeMetadata,
value: Vec<u8>,
parent_signature: Option<&GeometricSignature>,
level: u32,
) -> Option<GeometricSignature>
pub fn add_node( &self, metadata: NodeMetadata, value: Vec<u8>, parent_signature: Option<&GeometricSignature>, level: u32, ) -> Option<GeometricSignature>
Add a node to the tensor network.
Computes a position in the Poincaré disk using Sarkar’s cone construction: children are placed at hyperbolic distance τ from their parent, at golden-angle-spaced angles in the parent’s reflected frame.
Sourcepub fn add_node_positioned(
&self,
metadata: NodeMetadata,
value: Vec<u8>,
parent_signature: Option<&GeometricSignature>,
level: u32,
child_index: u32,
) -> Option<GeometricSignature>
pub fn add_node_positioned( &self, metadata: NodeMetadata, value: Vec<u8>, parent_signature: Option<&GeometricSignature>, level: u32, child_index: u32, ) -> Option<GeometricSignature>
Add a node with an explicit child_index for deterministic Sarkar reconstruction.
Used during snapshot replay: the stored child_index ensures the node gets the same geometric position regardless of replay order.
Sourcepub fn max_degree(&self) -> u32
pub fn max_degree(&self) -> u32
The largest node degree for which PROOF.md’s Delaunay hypothesis holds
at this network’s tau. Exceeding it is a spacing-quality matter, not a
correctness one — see docs/GEOMETRY_TRACK.md.
Sourcepub fn get_node_by_signature(
&self,
signature: &GeometricSignature,
) -> Option<CompressedNode>
pub fn get_node_by_signature( &self, signature: &GeometricSignature, ) -> Option<CompressedNode>
Get a node by its signature (returns cloned value).
Sourcepub fn update_node_value(&self, unique_id: &str, value: Vec<u8>) -> bool
pub fn update_node_value(&self, unique_id: &str, value: Vec<u8>) -> bool
Update the value of a node by its unique_id.
Sourcepub fn set_node_metadata_entry(
&self,
unique_id: &str,
key: &str,
val: &str,
) -> bool
pub fn set_node_metadata_entry( &self, unique_id: &str, key: &str, val: &str, ) -> bool
Set a metadata key-value pair on a node by its unique_id.
Sourcepub fn set_node_semantic(&self, unique_id: &str, coords: Vec<u8>) -> bool
pub fn set_node_semantic(&self, unique_id: &str, coords: Vec<u8>) -> bool
Set semantic coordinates on a node by its unique_id.
Sourcepub fn get_node_semantic(&self, unique_id: &str) -> Option<Vec<u8>>
pub fn get_node_semantic(&self, unique_id: &str) -> Option<Vec<u8>>
Get semantic coordinates for a node by its unique_id.
Sourcepub fn root_node(&self) -> Option<CompressedNode>
pub fn root_node(&self) -> Option<CompressedNode>
Get the root node (cloned).
Sourcepub fn root_signature(&self) -> Option<GeometricSignature>
pub fn root_signature(&self) -> Option<GeometricSignature>
Get the root node signature (cloned).
Sourcepub fn children_of(&self, signature: &GeometricSignature) -> Vec<CompressedNode>
pub fn children_of(&self, signature: &GeometricSignature) -> Vec<CompressedNode>
Get the children of a node by its signature (cloned).
Sourcepub fn get_point(&self, unique_id: &str) -> Option<HyperbolicPoint>
pub fn get_point(&self, unique_id: &str) -> Option<HyperbolicPoint>
Get the hyperbolic point for a node (cloned).
Sourcepub fn semantic_epoch(&self) -> u64
pub fn semantic_epoch(&self) -> u64
Monotone counter of semantic-relevant mutations (coordinate writes, inserts, deletes). External caches — like the semantic disk’s derived-position index — use it exactly as the internal per-slice cache does: tag on build, rebuild when it has advanced.
Sourcepub fn tau(&self) -> FixedPoint
pub fn tau(&self) -> FixedPoint
The Sarkar scale factor: the hyperbolic distance from any node to each of its children.
Sourcepub fn cell_index(&self) -> &CellIndex
pub fn cell_index(&self) -> &CellIndex
The spatial index, for tests that compare it against brute force.
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Get the number of nodes in the network.
Sourcepub fn remove_detached_node(&self, unique_id: &str)
pub fn remove_detached_node(&self, unique_id: &str)
Remove a node-map entry that has NO geometric registration — the
data-only entry retired by embed_existing after its embedded
replacement went live under a new signature-derived id. Not for
embedded nodes: those need Self::unregister_node_with_parent.
Sourcepub fn unregister_node(&self, unique_id: &str)
pub fn unregister_node(&self, unique_id: &str)
Unregister a node from the spatial index (for deletion).
Prefer Self::unregister_node_with_parent: the parent is not derivable
here, so the parent’s child list keeps a dangling entry.
Sourcepub fn unregister_node_with_parent(
&self,
unique_id: &str,
parent_uid: Option<&str>,
)
pub fn unregister_node_with_parent( &self, unique_id: &str, parent_uid: Option<&str>, )
Unregister a node, removing it from the node map and from its parent’s
child list. The caller supplies parent_uid; it is resolved from the
path map, which is authoritative for parentage.
Sourcepub fn find_descendants_spatial(
&self,
signature: &GeometricSignature,
) -> Vec<(String, FixedPoint)>
pub fn find_descendants_spatial( &self, signature: &GeometricSignature, ) -> Vec<(String, FixedPoint)>
Find all descendants of a node using the spatial index.
Uses the parent’s stored point + a τ-based radius to find all nodes within the Sarkar cone. Radius = 3·τ covers ~3 levels of descendants.
Sourcepub fn nearest_neighbor_point(
&self,
query_poincare: &HyperbolicPoint,
) -> Option<(String, FixedPoint)>
pub fn nearest_neighbor_point( &self, query_poincare: &HyperbolicPoint, ) -> Option<(String, FixedPoint)>
The nearest stored node to an arbitrary point, exactly.
Delegates to the cell index: the cell is computed from the query’s coordinates, and the ring expands until a proven lower bound says nothing closer remains. No candidate cap, no window, no count-based stopping rule.
Complexity: O(1) cell lookup plus a bounded ring. Measured on 5 461 nodes: 1.7 cells and ~27 points scanned per k=1 query, against 7 563 µs for the bucket layer this replaced.
Sourcepub fn nearest_neighbor_point_k(
&self,
query_poincare: &HyperbolicPoint,
k: usize,
) -> Vec<(String, FixedPoint)>
pub fn nearest_neighbor_point_k( &self, query_poincare: &HyperbolicPoint, k: usize, ) -> Vec<(String, FixedPoint)>
The k nearest stored nodes to an arbitrary point, ascending by
(distance, unique_id).
Sourcepub fn nodes_in_radius(
&self,
centre: &HyperbolicPoint,
radius: FixedPoint,
) -> Vec<(String, FixedPoint)>
pub fn nodes_in_radius( &self, centre: &HyperbolicPoint, radius: FixedPoint, ) -> Vec<(String, FixedPoint)>
Every stored node within radius of centre, ascending by
(distance, unique_id). Same expansion as nearest_neighbor_point_k
with a fixed threshold instead of a moving k-th distance.
Sourcepub fn semantic_distance(
coords_a: &[u8],
coords_b: &[u8],
dim_range: &Range<usize>,
) -> FixedPoint
pub fn semantic_distance( coords_a: &[u8], coords_b: &[u8], dim_range: &Range<usize>, ) -> FixedPoint
Compute Euclidean distance between two semantic coordinate vectors across a dimensional slice (specified dimension range).
Each dimension is 16 bytes (i128 LE, Q64.64 fixed-point). Dimensions outside the vectors are treated as zero.
Uses gMath’s fused kernel: differences, squares, and the accumulator all live at the compute tier, so the sum cannot wrap the way a storage-tier Q64.64 accumulator would for large coordinates or many dimensions.
Sourcepub fn decode_semantic_slice(
coords: &[u8],
dim_range: &Range<usize>,
) -> Vec<FixedPoint>
pub fn decode_semantic_slice( coords: &[u8], dim_range: &Range<usize>, ) -> Vec<FixedPoint>
Decode a dimension slice of a raw Q64.64 coordinate vector.
Dimensions beyond the end of coords decode as zero — short vectors
are zero-extended, matching Self::semantic_distance semantics.
Sourcepub fn nearest_semantic(
&self,
query_coords: &[u8],
k: usize,
dim_range: &Range<usize>,
) -> Vec<(String, FixedPoint)>
pub fn nearest_semantic( &self, query_coords: &[u8], k: usize, dim_range: &Range<usize>, ) -> Vec<(String, FixedPoint)>
Find the k nearest nodes by Euclidean distance in semantic dimension space.
query_coords: raw Q64.64 byte vector representing the query point.
k: number of nearest neighbors to return.
dim_range: which semantic dimensions to compare (the “dimensional slice”).
Returns Vec<(key, distance)> sorted ascending by (distance, key) —
ties break deterministically by the user-visible node key, both for
ordering and for which ties survive the k-boundary.
Routing (docs/SEMANTIC_INDEX.md): stores below
constants::SEMANTIC_INDEX_MIN_NODES use the brute-force scan;
larger stores query a lazily built per-dim_range VP-tree, rebuilt
when the semantic epoch has advanced (any coord write, insert, or
delete). Warm-index queries are O(log n) expected on low-dimensional
slices; the first query for a slice after a mutation pays the
O(n log n) build. Results are identical to the scan path.
Sourcepub fn nearest_semantic_scan(
&self,
query_coords: &[u8],
k: usize,
dim_range: &Range<usize>,
) -> Vec<(String, FixedPoint)>
pub fn nearest_semantic_scan( &self, query_coords: &[u8], k: usize, dim_range: &Range<usize>, ) -> Vec<(String, FixedPoint)>
Reference brute-force path for Self::nearest_semantic:
O(n × d) scan over every node with semantic coordinates.
Same ordering contract as the indexed path — ascending
(distance, key). Public so tests and benchmarks can compare
the two paths directly; prefer nearest_semantic, which picks.
Sourcepub fn validate_network(&self) -> bool
pub fn validate_network(&self) -> bool
Check if the network has a valid structure.
Verifies structural invariants across all internal data structures: nodes, point_map, child_counts, and the spatial index.
Sourcepub fn verify_index_locates_all_nodes(&self) -> bool
pub fn verify_index_locates_all_nodes(&self) -> bool
Functional integrity: can the spatial index actually answer a query about what it holds?
Every other check in this file is
referential — it asks whether these maps point at things that exist.
A structure can pass all of them and still be unable to find anything,
which is exactly what happened: the bucket layer was referentially
perfect while nearest returned the wrong node for 25 of 42 nodes in a
deep tree. No check asked it to locate a node it had itself indexed.
This one does. point_map is the authority on where a node is; the
index is derived from it. Querying at a node’s own stored position must
return that node, because distance 0 is the global minimum of a metric
— an expected answer known without any oracle.
Ties are respected: several nodes may share a position, so the check is that something at distance zero comes back, not that a particular id does.
O(n) queries, so it is a diagnostic rather than a hot path.