Skip to main content

lattice_embed/
lib.rs

1//! # lattice-embed
2//!
3//! Pure-Rust local embedding generation, caching, SIMD vector operations, and model migration.
4//! Most of the crate, including model loading and SIMD dispatch, is **Unstable**; consumers
5//! should normally use it through `lattice-engine`.
6//!
7//! The exception is the stable 0.4.x ANN contract in `simd`: squared/ordinary L2 distance,
8//! dot product, and cosine similarity retain their `(&[f32], &[f32]) -> f32` APIs and
9//! documented degenerate-input behavior. SIMD is not bit-identical to scalar, so near-tie
10//! ordering is not guaranteed.
11//!
12//! See `docs/design.md` for the architecture, service lifecycle, cache identity, and migration
13//! boundaries; use `docs/INDEX.md` to find subsystem references.
14
15#![warn(missing_docs)]
16#![allow(clippy::clone_on_copy)]
17
18pub mod backfill;
19mod cache;
20mod error;
21pub mod migration;
22mod model;
23pub mod service;
24pub mod simd;
25pub mod types;
26#[cfg(feature = "native")]
27pub mod vision;
28// Keep this gate aligned with wasm-bindgen's wasm32-only dependency — see docs/design.md.
29#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
30pub mod wasm;
31
32pub use cache::{CacheStats, DEFAULT_CACHE_CAPACITY, EmbeddingCache, ShardStats};
33pub use error::{EmbedError, Result};
34pub use model::{EmbeddingModel, MIN_MRL_OUTPUT_DIM, ModelConfig, ModelProvenance};
35#[allow(deprecated)]
36pub use service::MAX_TEXT_CHARS;
37pub use service::{DEFAULT_MAX_BATCH_SIZE, EmbeddingRole, EmbeddingService, MAX_TEXT_BYTES};
38pub use simd::{SimdConfig, simd_config};
39
40#[cfg(feature = "native")]
41pub use service::{CachedEmbeddingService, NativeEmbeddingService};
42
43/// Utility functions for vector operations.
44///
45/// All functions in this module are SIMD-accelerated when available (AVX2 on x86_64, NEON on aarch64).
46/// Runtime feature detection ensures automatic fallback to scalar implementations
47/// on systems without SIMD support.
48pub mod utils {
49    use crate::simd;
50
51    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
52    ///
53    /// Computes cosine similarity through the SIMD dispatcher.
54    /// Returns `0.0` for empty, unequal-length, or zero-norm vectors.
55    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for dispatch and performance details.
56    #[inline]
57    pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
58        simd::cosine_similarity(a, b)
59    }
60
61    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
62    ///
63    /// Computes the dot product of two vectors through the SIMD dispatcher.
64    /// Returns `0.0` for unequal-length vectors; for unit vectors it equals cosine similarity.
65    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for dispatch and performance details.
66    #[inline]
67    pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
68        simd::dot_product(a, b)
69    }
70
71    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
72    ///
73    /// L2-normalizes a vector in place through the SIMD dispatcher.
74    /// Leaves zero- or NaN-norm vectors unchanged.
75    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for dispatch and examples.
76    #[inline]
77    pub fn normalize(vector: &mut [f32]) {
78        simd::normalize(vector)
79    }
80
81    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
82    ///
83    /// Computes Euclidean distance between two vectors through the SIMD dispatcher.
84    /// Returns `f32::MAX` for unequal-length vectors.
85    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for dispatch and examples.
86    #[inline]
87    pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
88        simd::euclidean_distance(a, b)
89    }
90
91    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
92    ///
93    /// Computes cosine similarities for vector pairs in input order.
94    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for batch-dispatch behavior.
95    #[inline]
96    pub fn batch_cosine_similarity(pairs: &[(&[f32], &[f32])]) -> Vec<f32> {
97        simd::batch_cosine_similarity(pairs)
98    }
99
100    /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
101    ///
102    /// Computes dot products for vector pairs in input order.
103    /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for batch-dispatch behavior.
104    #[inline]
105    pub fn batch_dot_product(pairs: &[(&[f32], &[f32])]) -> Vec<f32> {
106        simd::batch_dot_product(pairs)
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_cosine_similarity_identical() {
116        let a = vec![1.0, 2.0, 3.0];
117        let b = vec![1.0, 2.0, 3.0];
118        let sim = utils::cosine_similarity(&a, &b);
119        assert!((sim - 1.0).abs() < 0.0001);
120    }
121
122    #[test]
123    fn test_cosine_similarity_orthogonal() {
124        let a = vec![1.0, 0.0];
125        let b = vec![0.0, 1.0];
126        let sim = utils::cosine_similarity(&a, &b);
127        assert!(sim.abs() < 0.0001);
128    }
129
130    #[test]
131    fn test_cosine_similarity_opposite() {
132        let a = vec![1.0, 0.0];
133        let b = vec![-1.0, 0.0];
134        let sim = utils::cosine_similarity(&a, &b);
135        assert!((sim + 1.0).abs() < 0.0001);
136    }
137
138    #[test]
139    fn test_normalize() {
140        let mut v = vec![3.0, 4.0];
141        utils::normalize(&mut v);
142        let magnitude: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
143        assert!((magnitude - 1.0).abs() < 0.0001);
144    }
145
146    #[test]
147    fn test_euclidean_distance() {
148        let a = vec![0.0, 0.0, 0.0];
149        let b = vec![1.0, 0.0, 0.0];
150        let dist = utils::euclidean_distance(&a, &b);
151        assert!((dist - 1.0).abs() < 0.0001);
152    }
153
154    #[test]
155    fn test_model_default() {
156        let model = EmbeddingModel::default();
157        assert_eq!(model.dimensions(), 384);
158    }
159}