Skip to main content

haagenti_hct/
lib.rs

1// Test modules have minor lints that don't affect production code
2#![cfg_attr(test, allow(clippy::assertions_on_constants))]
3#![cfg_attr(test, allow(clippy::needless_range_loop))]
4#![cfg_attr(test, allow(clippy::op_ref))]
5#![cfg_attr(test, allow(unused_variables))]
6#![cfg_attr(test, allow(unused_mut))]
7
8//! # Haagenti Compressed Tensor (HCT) Format
9//!
10//! High-performance compressed tensor storage for neural network weights,
11//! with HoloTensor holographic compression support for progressive loading.
12//!
13//! ## Overview
14//!
15//! HCT provides two complementary storage modes:
16//!
17//! - **Standard HCT**: Block-compressed tensor storage with random access
18//! - **HoloTensor**: Holographic compression enabling progressive reconstruction
19//!
20//! ## Standard HCT Format
21//!
22//! Block-based compression with LZ4 or Zstd for fast random access:
23//!
24//! ```ignore
25//! use haagenti_hct::{HctWriter, HctReader, CompressionAlgorithm, DType};
26//! use std::fs::File;
27//!
28//! // Write compressed tensor
29//! let mut writer = HctWriter::new(
30//!     File::create("weights.hct")?,
31//!     CompressionAlgorithm::Zstd,
32//!     DType::F16,
33//!     &[4096, 4096],
34//! )?;
35//! writer.write_data(&weight_data)?;
36//! writer.finish()?;
37//!
38//! // Read tensor
39//! let mut reader = HctReader::open("weights.hct")?;
40//! let data = reader.read_all()?;
41//! ```
42//!
43//! ## HoloTensor Format
44//!
45//! Holographic compression enables progressive reconstruction from partial data:
46//!
47//! ```ignore
48//! use haagenti_hct::{
49//!     HoloTensorEncoder, HoloTensorDecoder,
50//!     HolographicEncoding, DType,
51//! };
52//!
53//! // Encode with spectral holography (8 fragments)
54//! let encoder = HoloTensorEncoder::new(HolographicEncoding::Spectral)
55//!     .with_fragments(8);
56//! let (header, fragments) = encoder.encode_1d(&weights)?;
57//!
58//! // Reconstruct from partial fragments (any 4 of 8 for ~90% quality)
59//! let mut decoder = HoloTensorDecoder::new(header);
60//! decoder.add_fragment(fragments[0].clone())?;
61//! decoder.add_fragment(fragments[3].clone())?;
62//! decoder.add_fragment(fragments[5].clone())?;
63//! decoder.add_fragment(fragments[7].clone())?;
64//!
65//! let approx_data = decoder.reconstruct()?;
66//! ```
67//!
68//! ## Encoding Schemes
69//!
70//! | Scheme | Best For | Min Quality | Progressive |
71//! |--------|----------|-------------|-------------|
72//! | Spectral (DCT) | Dense MLP weights | 60% | Smooth curve |
73//! | Random Projection | High-dimensional | 10% | Linear curve |
74//! | Low-Rank Distributed | Attention layers | 30% | Sharp knee |
75//!
76//! ## Feature Flags
77//!
78//! - `lz4` - LZ4 compression for base blocks
79//! - `zstd` - Zstd compression for better ratios
80//! - `full` - All features (default)
81
82// Allow various lints in experimental holotensor code
83#![allow(clippy::needless_range_loop)]
84
85// Local modules (implementations owned by haagenti-hct)
86pub mod holotensor;
87pub mod tensor;
88
89// Re-export core error types
90pub use haagenti_core::{Error, Result};
91
92// ==================== HCT Tensor Format ====================
93
94// Format constants
95pub use tensor::{
96    DEFAULT_BLOCK_SIZE, FLAG_BLOCK_CHECKSUMS, FLAG_HEADER_CHECKSUM, FLAG_HOLOGRAPHIC,
97    FLAG_QUANTIZATION, FLAG_TENSOR_NAME, HCT_MAGIC, HCT_VERSION, HCT_VERSION_V2,
98};
99
100// Core types
101pub use tensor::{BlockIndex, CompressionAlgorithm, DType, HctHeader};
102
103// V2 types (with quantization support)
104pub use tensor::{BlockIndexV2, QuantizationMetadata, QuantizationScheme};
105
106// Reader/Writer
107pub use tensor::{HctReader, HctReaderV2, HctWriter, HctWriterV2};
108
109// Utilities
110pub use tensor::{compress_file, ChecksumError, CompressionStats as HctCompressionStats};
111
112// ==================== HoloTensor Holographic Compression ====================
113
114// Format constants
115pub use holotensor::{
116    HOLO_FLAG_ESSENTIAL_FIRST, HOLO_FLAG_FRAGMENT_CHECKSUMS, HOLO_FLAG_HEADER_CHECKSUM,
117    HOLO_FLAG_INTERLEAVED, HOLO_FLAG_QUALITY_CURVE, HOLO_FLAG_QUANTIZATION, HOLO_MAGIC,
118    HOLO_VERSION,
119};
120
121// Core types
122pub use holotensor::{FragmentIndexEntry, HoloFragment, HolographicEncoding, QualityCurve};
123
124// Header
125pub use holotensor::HoloTensorHeader;
126
127// DCT primitives (for advanced use)
128pub use holotensor::{dct_1d, dct_2d, idct_1d, idct_2d};
129
130// Seeded RNG (for reproducible random projections)
131pub use holotensor::SeededRng;
132
133// Spectral (DCT-based) encoder/decoder
134pub use holotensor::{SpectralDecoder, SpectralEncoder};
135
136// Random Projection (JL-based) encoder/decoder
137pub use holotensor::{RphDecoder, RphEncoder};
138
139// Low-Rank Distributed (SVD-based) encoder/decoder
140pub use holotensor::{LrdfDecoder, LrdfEncoder};
141
142// Unified encoder/decoder API
143pub use holotensor::{HoloTensorDecoder, HoloTensorEncoder};
144
145// File I/O
146pub use holotensor::{HoloTensorReader, HoloTensorWriter};
147
148// Convenience functions
149pub use holotensor::{
150    decode_from_file, decode_from_file_progressive, encode_to_file, open_holotensor,
151    read_holotensor, write_holotensor,
152};
153
154/// Prelude module for common imports.
155///
156/// Convenient imports for common HCT operations.
157///
158/// ```ignore
159/// use haagenti_hct::prelude::*;
160/// ```
161pub mod prelude {
162    // Error handling
163    pub use crate::{Error, Result};
164
165    // Core HCT types
166    pub use crate::{CompressionAlgorithm, DType, HctReader, HctReaderV2, HctWriter, HctWriterV2};
167
168    // HoloTensor types
169    pub use crate::{
170        HoloFragment, HoloTensorDecoder, HoloTensorEncoder, HoloTensorHeader, HoloTensorReader,
171        HoloTensorWriter, HolographicEncoding, QualityCurve,
172    };
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_dtype_values() {
181        assert_eq!(DType::F32 as u8, 0);
182        assert_eq!(DType::F16 as u8, 1);
183        assert_eq!(DType::BF16 as u8, 2);
184        assert_eq!(DType::I8 as u8, 3);
185        assert_eq!(DType::I4 as u8, 4);
186    }
187
188    #[test]
189    fn test_compression_algorithm_values() {
190        assert_eq!(CompressionAlgorithm::Lz4 as u8, 0);
191        assert_eq!(CompressionAlgorithm::Zstd as u8, 1);
192    }
193
194    #[test]
195    fn test_holographic_encoding_values() {
196        assert_eq!(HolographicEncoding::Spectral as u8, 0);
197        assert_eq!(HolographicEncoding::RandomProjection as u8, 1);
198        assert_eq!(HolographicEncoding::LowRankDistributed as u8, 2);
199    }
200
201    #[test]
202    fn test_holographic_encoding_names() {
203        assert_eq!(HolographicEncoding::Spectral.name(), "Spectral (DCT)");
204        assert_eq!(
205            HolographicEncoding::RandomProjection.name(),
206            "Random Projection (JL)"
207        );
208        assert_eq!(
209            HolographicEncoding::LowRankDistributed.name(),
210            "Low-Rank Distributed (SVD)"
211        );
212    }
213
214    #[test]
215    fn test_default_quality_curves() {
216        let spectral = HolographicEncoding::Spectral.default_quality_curve();
217        assert_eq!(spectral.min_fragments, 1);
218        assert_eq!(spectral.sufficient_fragments, 6);
219
220        let rph = HolographicEncoding::RandomProjection.default_quality_curve();
221        assert_eq!(rph.min_fragments, 2);
222
223        let lrdf = HolographicEncoding::LowRankDistributed.default_quality_curve();
224        assert_eq!(lrdf.sufficient_fragments, 4);
225    }
226
227    #[test]
228    fn test_quality_curve_predict() {
229        let curve = QualityCurve {
230            coefficients: [0.0, 1.0, 0.0, 0.0],
231            min_fragments: 1,
232            sufficient_fragments: 8,
233        };
234
235        // Linear curve: quality = k/N
236        let q = curve.predict(4, 8);
237        assert!((q - 0.5).abs() < 0.001);
238
239        let q = curve.predict(8, 8);
240        assert!((q - 1.0).abs() < 0.001);
241    }
242
243    #[test]
244    fn test_holo_fragment_creation() {
245        let data = vec![1, 2, 3, 4, 5];
246        let fragment = HoloFragment::new(3, data.clone());
247
248        assert_eq!(fragment.index, 3);
249        assert_eq!(fragment.data, data);
250        assert!(fragment.checksum != 0);
251    }
252
253    #[test]
254    fn test_holo_fragment_checksum_consistency() {
255        let data = vec![42u8; 100];
256        let f1 = HoloFragment::new(0, data.clone());
257        let f2 = HoloFragment::new(0, data);
258
259        assert_eq!(f1.checksum, f2.checksum);
260    }
261
262    #[test]
263    fn test_seeded_rng_determinism() {
264        let mut rng1 = SeededRng::new(12345);
265        let mut rng2 = SeededRng::new(12345);
266
267        for _ in 0..100 {
268            assert_eq!(rng1.next_u64(), rng2.next_u64());
269        }
270    }
271
272    #[test]
273    fn test_dct_roundtrip_1d() {
274        let signal = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
275        let mut dct_output = vec![0.0f32; 8];
276        dct_1d(&signal, &mut dct_output);
277
278        let mut reconstructed = vec![0.0f32; 8];
279        idct_1d(&dct_output, &mut reconstructed);
280
281        for (a, b) in signal.iter().zip(reconstructed.iter()) {
282            assert!((a - b).abs() < 1e-5);
283        }
284    }
285
286    #[test]
287    fn test_dct_roundtrip_2d() {
288        let data = vec![
289            1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
290        ];
291        let mut dct_output = vec![0.0f32; 16];
292        dct_2d(&data, &mut dct_output, 4, 4);
293
294        let mut reconstructed = vec![0.0f32; 16];
295        idct_2d(&dct_output, &mut reconstructed, 4, 4);
296
297        for (a, b) in data.iter().zip(reconstructed.iter()) {
298            assert!((a - b).abs() < 1e-4, "Expected {}, got {}", a, b);
299        }
300    }
301
302    #[test]
303    fn test_spectral_encoder_decoder_roundtrip() {
304        // Create small test matrix
305        let data: Vec<f32> = (0..64).map(|i| i as f32).collect();
306
307        let encoder = SpectralEncoder::new(4);
308        let fragments = encoder.encode_2d(&data, 8, 8).expect("encode failed");
309
310        assert_eq!(fragments.len(), 4);
311
312        let mut decoder = SpectralDecoder::new(8, 8, 4);
313        for frag in &fragments {
314            decoder.add_fragment(frag).expect("add fragment failed");
315        }
316
317        let reconstructed = decoder.reconstruct();
318        assert_eq!(reconstructed.len(), 64);
319
320        // Should reconstruct with high quality from all fragments
321        let mse: f32 = data
322            .iter()
323            .zip(reconstructed.iter())
324            .map(|(a, b)| (a - b).powi(2))
325            .sum::<f32>()
326            / data.len() as f32;
327
328        assert!(mse < 1.0, "MSE too high: {}", mse);
329    }
330
331    #[test]
332    fn test_rph_encoder_decoder_roundtrip() {
333        // Create small test vector
334        let data: Vec<f32> = (0..32).map(|i| i as f32).collect();
335
336        let encoder = RphEncoder::new(4, 42);
337        let fragments = encoder.encode(&data).expect("encode failed");
338
339        assert_eq!(fragments.len(), 4);
340
341        let mut decoder = RphDecoder::new(32, 4);
342        for frag in &fragments {
343            decoder.add_fragment(frag).expect("add fragment failed");
344        }
345
346        let reconstructed = decoder.reconstruct();
347        assert_eq!(reconstructed.len(), 32);
348
349        // RPH provides approximate reconstruction
350        let correlation: f32 = data
351            .iter()
352            .zip(reconstructed.iter())
353            .map(|(a, b)| a * b)
354            .sum::<f32>();
355
356        let norm_orig: f32 = data.iter().map(|x| x * x).sum::<f32>().sqrt();
357        let norm_recon: f32 = reconstructed.iter().map(|x| x * x).sum::<f32>().sqrt();
358
359        let cos_sim = correlation / (norm_orig * norm_recon + 1e-10);
360        assert!(cos_sim > 0.5, "Cosine similarity too low: {}", cos_sim);
361    }
362
363    #[test]
364    fn test_lrdf_encoder_decoder_roundtrip() {
365        // Create small test matrix with low-rank structure
366        let data: Vec<f32> = (0..64).map(|i| ((i / 8) + (i % 8)) as f32).collect();
367
368        let encoder = LrdfEncoder::new(4);
369        let fragments = encoder.encode_2d(&data, 8, 8).expect("encode failed");
370
371        assert_eq!(fragments.len(), 4);
372
373        let mut decoder = LrdfDecoder::new(8, 8, 4);
374        for frag in &fragments {
375            decoder.add_fragment(frag).expect("add fragment failed");
376        }
377
378        let reconstructed = decoder.reconstruct();
379        assert_eq!(reconstructed.len(), 64);
380
381        // Should reconstruct low-rank matrix well
382        let mse: f32 = data
383            .iter()
384            .zip(reconstructed.iter())
385            .map(|(a, b)| (a - b).powi(2))
386            .sum::<f32>()
387            / data.len() as f32;
388
389        assert!(mse < 10.0, "MSE too high: {}", mse);
390    }
391
392    #[test]
393    fn test_unified_encoder_spectral() {
394        let data: Vec<f32> = (0..64).map(|i| i as f32 * 0.1).collect();
395
396        let encoder = HoloTensorEncoder::new(HolographicEncoding::Spectral)
397            .with_fragments(4)
398            .with_seed(42);
399
400        let (header, fragments) = encoder.encode_1d(&data).expect("encode failed");
401        assert_eq!(fragments.len(), 4);
402        assert_eq!(header.total_fragments, 4);
403    }
404
405    #[test]
406    fn test_unified_encoder_decoder_roundtrip() {
407        let data: Vec<f32> = (0..64).map(|i| i as f32 * 0.1).collect();
408
409        let encoder = HoloTensorEncoder::new(HolographicEncoding::Spectral)
410            .with_fragments(4)
411            .with_seed(42);
412
413        let (header, fragments) = encoder.encode_1d(&data).expect("encode failed");
414
415        let mut decoder = HoloTensorDecoder::new(header);
416        for frag in fragments {
417            decoder.add_fragment(frag).expect("add fragment failed");
418        }
419
420        assert!(decoder.can_reconstruct());
421        let reconstructed = decoder.reconstruct().expect("reconstruct failed");
422        assert_eq!(reconstructed.len(), 64);
423    }
424
425    #[test]
426    fn test_format_flags() {
427        // Verify flag values don't overlap
428        let flags = [
429            FLAG_HEADER_CHECKSUM,
430            FLAG_BLOCK_CHECKSUMS,
431            FLAG_QUANTIZATION,
432            FLAG_TENSOR_NAME,
433            FLAG_HOLOGRAPHIC,
434        ];
435
436        for (i, &a) in flags.iter().enumerate() {
437            for (j, &b) in flags.iter().enumerate() {
438                if i != j {
439                    assert_eq!(a & b, 0, "Flags {} and {} overlap", a, b);
440                }
441            }
442        }
443    }
444
445    #[test]
446    fn test_holo_flags() {
447        // Verify holo flag values don't overlap
448        let flags = [
449            HOLO_FLAG_HEADER_CHECKSUM,
450            HOLO_FLAG_FRAGMENT_CHECKSUMS,
451            HOLO_FLAG_QUANTIZATION,
452            HOLO_FLAG_QUALITY_CURVE,
453            HOLO_FLAG_ESSENTIAL_FIRST,
454            HOLO_FLAG_INTERLEAVED,
455        ];
456
457        for (i, &a) in flags.iter().enumerate() {
458            for (j, &b) in flags.iter().enumerate() {
459                if i != j {
460                    assert_eq!(a & b, 0, "Holo flags {} and {} overlap", a, b);
461                }
462            }
463        }
464    }
465
466    #[test]
467    fn test_progressive_reconstruction_quality() {
468        // Test that partial fragment loading gives partial quality
469        let data: Vec<f32> = (0..256).map(|i| (i as f32 * 0.01).sin()).collect();
470
471        let encoder = SpectralEncoder::new(8);
472        let fragments = encoder.encode_1d(&data).expect("encode failed");
473
474        // Test with 2 of 8 fragments
475        let mut decoder = SpectralDecoder::new(256, 1, 8);
476        decoder.add_fragment(&fragments[0]).expect("add fragment 0");
477        decoder.add_fragment(&fragments[4]).expect("add fragment 4");
478
479        let partial = decoder.reconstruct();
480        assert_eq!(partial.len(), 256);
481
482        // Partial reconstruction should have some similarity
483        let correlation: f32 = data
484            .iter()
485            .zip(partial.iter())
486            .map(|(a, b)| a * b)
487            .sum::<f32>();
488
489        let norm_orig: f32 = data.iter().map(|x| x * x).sum::<f32>().sqrt();
490        let norm_partial: f32 = partial.iter().map(|x| x * x).sum::<f32>().sqrt();
491
492        let cos_sim = correlation / (norm_orig * norm_partial + 1e-10);
493        // With essential coefficients replicated, should have decent similarity
494        assert!(
495            cos_sim > 0.3,
496            "Cosine similarity from partial reconstruction too low: {}",
497            cos_sim
498        );
499    }
500}