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 Manhattan (L1) distance between two vectors through the SIMD dispatcher.
94 /// Returns `f32::MAX` for unequal-length vectors, matching [`euclidean_distance`].
95 /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for dispatch and examples.
96 #[inline]
97 pub fn manhattan_distance(a: &[f32], b: &[f32]) -> f32 {
98 simd::manhattan_distance(a, b)
99 }
100
101 /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
102 ///
103 /// Computes cosine similarities for vector pairs in input order.
104 /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for batch-dispatch behavior.
105 #[inline]
106 pub fn batch_cosine_similarity(pairs: &[(&[f32], &[f32])]) -> Vec<f32> {
107 simd::batch_cosine_similarity(pairs)
108 }
109
110 /// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
111 ///
112 /// Computes dot products for vector pairs in input order.
113 /// See [`docs/design.md`](../docs/design.md#vector-utility-facade) for batch-dispatch behavior.
114 #[inline]
115 pub fn batch_dot_product(pairs: &[(&[f32], &[f32])]) -> Vec<f32> {
116 simd::batch_dot_product(pairs)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_cosine_similarity_identical() {
126 let a = vec![1.0, 2.0, 3.0];
127 let b = vec![1.0, 2.0, 3.0];
128 let sim = utils::cosine_similarity(&a, &b);
129 assert!((sim - 1.0).abs() < 0.0001);
130 }
131
132 #[test]
133 fn test_cosine_similarity_orthogonal() {
134 let a = vec![1.0, 0.0];
135 let b = vec![0.0, 1.0];
136 let sim = utils::cosine_similarity(&a, &b);
137 assert!(sim.abs() < 0.0001);
138 }
139
140 #[test]
141 fn test_cosine_similarity_opposite() {
142 let a = vec![1.0, 0.0];
143 let b = vec![-1.0, 0.0];
144 let sim = utils::cosine_similarity(&a, &b);
145 assert!((sim + 1.0).abs() < 0.0001);
146 }
147
148 #[test]
149 fn test_normalize() {
150 let mut v = vec![3.0, 4.0];
151 utils::normalize(&mut v);
152 let magnitude: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
153 assert!((magnitude - 1.0).abs() < 0.0001);
154 }
155
156 #[test]
157 fn test_euclidean_distance() {
158 let a = vec![0.0, 0.0, 0.0];
159 let b = vec![1.0, 0.0, 0.0];
160 let dist = utils::euclidean_distance(&a, &b);
161 assert!((dist - 1.0).abs() < 0.0001);
162 }
163
164 #[test]
165 fn test_model_default() {
166 let model = EmbeddingModel::default();
167 assert_eq!(model.dimensions(), 384);
168 }
169}