Struct TemporalHnsw

Source
pub struct TemporalHnsw<D: DistanceMetric> {
    graph: HnswGraph<D>,
    timestamps: Vec<i64>,
    entity_ids: Vec<u64>,
    entity_index: BTreeMap<u64, Vec<(i64, u32)>>,
    min_timestamp: i64,
    max_timestamp: i64,
    metadata_store: Option<MetadataStore>,
    centroid: Option<Vec<f32>>,
    rewards: Vec<f32>,
}
Expand description

Spatiotemporal HNSW index.

Each inserted point has an entity_id, a timestamp, and a vector. Internal node IDs are assigned sequentially.

Fields§

§graph: HnswGraph<D>§timestamps: Vec<i64>

node_id → timestamp

§entity_ids: Vec<u64>

node_id → entity_id

§entity_index: BTreeMap<u64, Vec<(i64, u32)>>

entity_id → sorted vec of (timestamp, node_id)

§min_timestamp: i64

Global temporal range for normalization

§max_timestamp: i64§metadata_store: Option<MetadataStore>

Optional per-node metadata store.

§centroid: Option<Vec<f32>>

Optional centroid for anisotropy correction (RFC-012 Part B).

When set, all distance computations center vectors by subtracting this mean vector, amplifying the discriminative signal that is otherwise compressed by the dominant “average text” direction.

§rewards: Vec<f32>

Optional per-node reward for outcome-aware search (RFC-012 P4).

NaN means “no reward assigned”. Stored parallel to timestamps/entity_ids.

Implementations§

Source§

impl<D: DistanceMetric> TemporalHnsw<D>

Source

pub fn new(config: HnswConfig, metric: D) -> Self

Create a new empty spatiotemporal index.

Source

pub fn len(&self) -> usize

Number of points in the index.

Source

pub fn is_empty(&self) -> bool

Whether the index is empty.

Source

pub fn entity_last_node(&self, entity_id: u64) -> Option<u32>

Get the last (most recent) node_id for an entity, or None if not found.

Source

pub fn set_ef_construction(&mut self, ef: usize)

Set ef_construction at runtime (lower for bulk load, higher for quality).

Set ef_search at runtime.

Source

pub fn config(&self) -> &HnswConfig

Get the current configuration.

Source

pub fn enable_scalar_quantization(&mut self, min_val: f32, max_val: f32)

Enable scalar quantization for faster distance computation.

Source

pub fn disable_scalar_quantization(&mut self)

Disable scalar quantization.

Source

pub fn is_quantized(&self) -> bool

Whether scalar quantization is active.

Source

pub fn insert(&mut self, entity_id: u64, timestamp: i64, vector: &[f32]) -> u32

Insert a temporal point into the index.

Returns the internal node_id assigned to this point.

Source

pub fn bulk_insert_parallel( &mut self, entity_ids: &[u64], timestamps: &[i64], vectors: &[&[f32]], ) -> usize

Bulk insert multiple points with parallel distance computation (RFC-012 P9).

Faster than sequential insert() calls for large batches. Uses rayon to parallelize neighbor search across chunks while keeping graph modifications sequential.

Returns the number of points inserted.

Source

pub fn insert_with_reward( &mut self, entity_id: u64, timestamp: i64, vector: &[f32], reward: f32, ) -> u32

Insert a temporal point with an outcome reward.

reward annotates this point with an outcome signal (e.g., 0.0-1.0). Use f32::NAN for “no reward assigned”.

Source

pub fn insert_with_metadata( &mut self, entity_id: u64, timestamp: i64, vector: &[f32], metadata: HashMap<String, String>, ) -> u32

Insert a temporal point with metadata.

Source

pub fn node_metadata(&self, node_id: u32) -> HashMap<String, String>

Get metadata for a node. Returns empty map if metadata store not enabled.

Source

pub fn build_filter_bitmap(&self, filter: &TemporalFilter) -> RoaringBitmap

Build a Roaring Bitmap of node IDs matching the temporal filter.

Source

pub fn temporal_distance_normalized(&self, t1: i64, t2: i64) -> f32

Compute normalized temporal distance between two timestamps.

Returns a value in [0.0, 1.0] where 0 = same timestamp, 1 = max range.

Source

pub(crate) fn normalize_semantic_distance(&self, d: f32) -> f32

Normalize semantic distance to [0, 1] range (RFC-012 P8).

Cosine distance ∈ [0, 2], L2 distance ∈ [0, ∞). This clamps and scales to [0, 1] so it’s comparable with temporal distance [0, 1].

Source

pub(crate) fn recency_penalty( &self, node_timestamp: i64, recency_lambda: f32, ) -> f32

Compute recency penalty for a node (RFC-012 P7).

Returns a value in [0.0, 1.0] where 0 = most recent, 1 = oldest. Uses exponential decay: 1 - exp(-λ · age) where age is normalized.

recency_lambda controls decay speed:

  • λ = 0: no recency effect
  • λ = 1: moderate decay
  • λ = 3: strong decay (old nodes heavily penalized)
Source

pub fn search_with_recency( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, recency_lambda: f32, recency_weight: f32, ) -> Vec<(u32, f32)>

Search with full composite scoring (RFC-012 P7 + P8).

Enhanced distance: d = α·d_sem_norm + (1-α)·d_temporal + γ·recency

  • alpha: semantic vs temporal weight (1.0 = pure semantic)
  • recency_lambda: recency decay strength (0.0 = off, 1.0 = moderate, 3.0 = strong)
  • recency_weight: weight of recency term in composite score (0.0-1.0)
Source

pub fn reward(&self, node_id: u32) -> f32

Get the reward for a node. Returns NaN if no reward was assigned.

Source

pub fn set_reward(&mut self, node_id: u32, reward: f32)

Set the reward for a node retroactively.

Useful for annotating outcomes after an episode completes.

Source

pub fn build_reward_bitmap(&self, min_reward: f32) -> RoaringBitmap

Build a bitmap of node_ids with reward >= min_reward.

Source

pub fn search_with_reward( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, min_reward: f32, ) -> Vec<(u32, f32)>

Search with reward filtering: only return nodes with reward >= min_reward.

Combines temporal filter + reward filter as bitmap pre-filter.

Source

pub fn compute_centroid(&self) -> Option<Vec<f32>>

Compute the centroid (mean vector) of all indexed vectors.

Single O(N×D) pass over stored vectors. Returns None if the index is empty.

Source

pub fn set_centroid(&mut self, centroid: Vec<f32>)

Set the centroid for anisotropy correction.

Once set, centered_vector() subtracts this from any vector, and search operations use centered distances. The centroid is serialized with the index snapshot.

You can provide an externally computed centroid (e.g., from a larger corpus) or use compute_centroid() for the index contents.

Source

pub fn clear_centroid(&mut self)

Clear the centroid, reverting to raw (uncentered) distances.

Source

pub fn centroid(&self) -> Option<&[f32]>

Get the current centroid, if set.

Source

pub fn centered_vector(&self, vec: &[f32]) -> Vec<f32>

Return a centered copy of the given vector (vec - centroid).

If no centroid is set, returns the vector unchanged (cloned).

Source

pub fn search( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, ) -> Vec<(u32, f32)>

Search for the k nearest neighbors with temporal filtering and composite scoring.

  • query: the query vector
  • k: number of results
  • filter: temporal constraint (Snapshot, Range, Before, After, All)
  • alpha: weight for semantic distance (1.0 = pure semantic, 0.0 = pure temporal)
  • query_timestamp: reference timestamp for temporal distance computation

Returns (node_id, combined_score) sorted by combined score ascending.

Source

pub fn trajectory( &self, entity_id: u64, filter: TemporalFilter, ) -> Vec<(i64, u32)>

Retrieve the full trajectory for an entity within a time range.

Returns (timestamp, node_id) pairs sorted by timestamp ascending.

Source

pub fn timestamp(&self, node_id: u32) -> i64

Get the timestamp for a node.

Source

pub fn entity_id(&self, node_id: u32) -> u64

Get the entity_id for a node.

Source

pub fn vector(&self, node_id: u32) -> &[f32]

Get the vector for a node.

Source

pub fn bitmap_memory_bytes(&self) -> usize

Approximate memory usage of the Roaring Bitmaps for a full-index filter.

Useful for verifying the < 1 byte/vector target.

Source

pub fn graph(&self) -> &HnswGraph<D>

Access the underlying HNSW graph (for recall comparisons, etc.).

Source

pub fn regions(&self, level: usize) -> Vec<(u32, Vec<f32>, usize)>

Get semantic regions at a given HNSW level.

Returns (hub_node_id, hub_vector, n_assigned_nodes) for each region. Use level 2-3 for interpretable granularity (~N/M^L regions).

Source

pub fn region_trajectory( &self, entity_id: u64, level: usize, window_days: i64, alpha: f32, ) -> Vec<(i64, Vec<f32>)>

Compute smoothed region-distribution trajectory for an entity (RFC-004).

  • level: HNSW level for region granularity
  • window_days: sliding window in timestamp units (same scale as ingested timestamps)
  • alpha: EMA smoothing factor (0.3 typical, higher = more reactive)

Returns (timestamp, distribution) where distribution is a Vec of length K (number of regions) that sums to ~1.0.

Source

pub fn region_members( &self, region_hub: u32, level: usize, filter: TemporalFilter, ) -> Vec<(u32, u64, i64)>

Get points assigned to a specific region, optionally time-filtered (RFC-004, RFC-005).

Returns (node_id, entity_id, timestamp) for all points in the region. This is the “SELECT * FROM points WHERE region = R” equivalent.

Performance warning: This does a full scan of all nodes. For multiple regions, use region_assignments() instead (single scan for all regions).

Source

pub fn region_assignments( &self, level: usize, filter: TemporalFilter, ) -> HashMap<u32, Vec<(u64, i64)>>

Assign ALL nodes to their regions in a single pass, optionally time-filtered.

Returns a HashMap from hub_id → Vec<(entity_id, timestamp)>. This is O(N) — one full scan instead of O(N × K) for K region_members calls.

Source§

impl<D: DistanceMetric> TemporalHnsw<D>

Source

pub fn save(&self, path: &Path) -> Result<()>

Save the index to a file using postcard binary encoding.

The distance metric is NOT serialized (it’s stateless). On load, you must provide the same metric type.

Source

pub fn load(path: &Path, metric: D) -> Result<Self>

Load an index from a file, providing the distance metric.

Supports all snapshot versions. Unknown future versions produce a clear error instead of silent corruption.

Trait Implementations§

Source§

impl<D: DistanceMetric> TemporalIndexAccess for TemporalHnsw<D>

Source§

fn search_raw( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, ) -> Vec<(u32, f32)>

Search with temporal filtering, returning (node_id, score) pairs.
Source§

fn trajectory(&self, entity_id: u64, filter: TemporalFilter) -> Vec<(i64, u32)>

Retrieve trajectory for an entity: (timestamp, node_id) pairs.
Source§

fn vector(&self, node_id: u32) -> Vec<f32>

Get the vector for a node. Returns owned vec for thread safety.
Source§

fn entity_id(&self, node_id: u32) -> u64

Get the entity_id for a node.
Source§

fn timestamp(&self, node_id: u32) -> i64

Get the timestamp for a node.
Source§

fn len(&self) -> usize

Number of points in the index.
Source§

fn regions(&self, level: usize) -> Vec<(u32, Vec<f32>, usize)>

Get semantic regions at a given HNSW level (RFC-004). Returns (hub_node_id, hub_vector, n_assigned) per region.
Source§

fn region_members( &self, region_hub: u32, level: usize, filter: TemporalFilter, ) -> Vec<(u32, u64, i64)>

Get points belonging to a specific region, optionally time-filtered (RFC-005). Returns (node_id, entity_id, timestamp) per member.
Source§

fn region_trajectory( &self, entity_id: u64, level: usize, window_days: i64, alpha: f32, ) -> Vec<(i64, Vec<f32>)>

Smoothed region-distribution trajectory for an entity (RFC-004).
Source§

fn metadata(&self, node_id: u32) -> HashMap<String, String>

Get metadata for a node. Returns empty map if not available.
Source§

fn search_with_metadata( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, metadata_filter: &MetadataFilter, ) -> Vec<(u32, f32)>

Search with metadata filtering (post-filter on search results). Default: ignores metadata filter and delegates to search_raw.
Source§

fn is_empty(&self) -> bool

Whether the index is empty.
Source§

fn region_assignments( &self, _level: usize, _filter: TemporalFilter, ) -> HashMap<u32, Vec<(u64, i64)>>

Assign all nodes to regions in a single O(N) pass, optionally time-filtered. Returns HashMap<hub_id, Vec<(entity_id, timestamp)>>.

Auto Trait Implementations§

§

impl<D> Freeze for TemporalHnsw<D>
where D: Freeze,

§

impl<D> RefUnwindSafe for TemporalHnsw<D>
where D: RefUnwindSafe,

§

impl<D> Send for TemporalHnsw<D>

§

impl<D> Sync for TemporalHnsw<D>

§

impl<D> Unpin for TemporalHnsw<D>
where D: Unpin,

§

impl<D> UnwindSafe for TemporalHnsw<D>
where D: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> TemporalSearch for T

Source§

fn search_raw( &self, query: &[f32], k: usize, filter: TemporalFilter, alpha: f32, query_timestamp: i64, ) -> Vec<(u32, f32)>

Search with temporal filtering, returning (node_id, score) pairs.
Source§

impl<T> TrajectoryAccess for T

Source§

fn trajectory(&self, entity_id: u64, filter: TemporalFilter) -> Vec<(i64, u32)>

Retrieve trajectory for an entity: (timestamp, node_id) pairs.
Source§

fn vector(&self, node_id: u32) -> Vec<f32>

Get the vector for a node. Returns owned vec for thread safety.
Source§

fn entity_id(&self, node_id: u32) -> u64

Get the entity_id for a node.
Source§

fn timestamp(&self, node_id: u32) -> i64

Get the timestamp for a node.
Source§

fn len(&self) -> usize

Number of points in the index.
Source§

fn is_empty(&self) -> bool

Whether the index is empty.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V