Skip to main content

scirs2_ndimage/hyperdimensional_computing/
image_processing.rs

1//! Image Processing with Hyperdimensional Computing
2//!
3//! This module implements HDC-based image processing algorithms including
4//! image encoding, pattern recognition, feature detection, and sequence processing
5//! for computer vision applications.
6
7use scirs2_core::ndarray::{s, Array2, ArrayView2};
8use scirs2_core::numeric::{Float, FromPrimitive};
9use std::collections::{HashMap, HashSet};
10
11use crate::error::{NdimageError, NdimageResult};
12use crate::hyperdimensional_computing::memory::HDCMemory;
13use crate::hyperdimensional_computing::types::{
14    CompositionResult, FeatureDetection, HDCConfig, Hypervector, PatternMatch, SequenceEncoding,
15};
16use crate::hyperdimensional_computing::utils::{
17    analyze_patch_for_feature, non_maximum_suppression,
18};
19use crate::hyperdimensional_computing::vector_ops::vector_utils;
20
21/// HDC-based image encoder
22#[derive(Debug, Clone)]
23pub struct ImageHDCEncoder {
24    /// Image dimensions
25    pub height: usize,
26    pub width: usize,
27    /// Position encoders for spatial information
28    pub position_encoders: HashMap<(usize, usize), Hypervector>,
29    /// Value encoders for pixel intensities
30    pub value_encoders: HashMap<u8, Hypervector>,
31    /// Feature encoders for specific features
32    pub feature_encoders: HashMap<String, Hypervector>,
33    /// Configuration
34    pub config: HDCConfig,
35}
36
37impl ImageHDCEncoder {
38    /// Create a new image HDC encoder
39    ///
40    /// # Arguments
41    ///
42    /// * `height` - Image height
43    /// * `width` - Image width
44    /// * `config` - HDC configuration
45    ///
46    /// # Returns
47    ///
48    /// A new ImageHDCEncoder instance
49    pub fn new(height: usize, width: usize, config: HDCConfig) -> Self {
50        let mut encoder = Self {
51            height,
52            width,
53            position_encoders: HashMap::new(),
54            value_encoders: HashMap::new(),
55            feature_encoders: HashMap::new(),
56            config,
57        };
58
59        encoder.initialize_encoders();
60        encoder
61    }
62
63    /// Initialize position and value encoders
64    fn initialize_encoders(&mut self) {
65        // Create position encoders for each spatial location
66        for y in 0..self.height {
67            for x in 0..self.width {
68                let position_hv =
69                    Hypervector::random(self.config.hypervector_dim, self.config.sparsity);
70                self.position_encoders.insert((y, x), position_hv);
71            }
72        }
73
74        // Create value encoders for pixel intensities (0-255)
75        for value in 0..=255 {
76            let value_hv = Hypervector::random(self.config.hypervector_dim, self.config.sparsity);
77            self.value_encoders.insert(value, value_hv);
78        }
79    }
80
81    /// Encode an image into a hypervector
82    ///
83    /// # Arguments
84    ///
85    /// * `image` - Input image as 2D array
86    ///
87    /// # Returns
88    ///
89    /// Encoded hypervector representation of the image
90    pub fn encode_image<T>(&self, image: ArrayView2<T>) -> NdimageResult<Hypervector>
91    where
92        T: Float + FromPrimitive + Copy,
93    {
94        let (img_height, img_width) = image.dim();
95
96        if img_height > self.height || img_width > self.width {
97            return Err(NdimageError::InvalidInput(format!(
98                "Image size ({}×{}) exceeds encoder capacity ({}×{})",
99                img_height, img_width, self.height, self.width
100            )));
101        }
102
103        let mut encodings = Vec::new();
104
105        for y in 0..img_height {
106            for x in 0..img_width {
107                let pixel_value = image[[y, x]];
108
109                // Convert to u8 intensity (assuming normalized input)
110                let intensity = (pixel_value.to_f64().unwrap_or(0.0).clamp(0.0, 1.0) * 255.0) as u8;
111
112                // Get position and value encoders
113                if let (Some(pos_hv), Some(val_hv)) = (
114                    self.position_encoders.get(&(y, x)),
115                    self.value_encoders.get(&intensity),
116                ) {
117                    // Bind position and value
118                    let pixel_encoding = pos_hv.bind(val_hv)?;
119                    encodings.push(pixel_encoding);
120                }
121            }
122        }
123
124        // Bundle all pixel encodings
125        if encodings.is_empty() {
126            Ok(Hypervector::zeros(self.config.hypervector_dim))
127        } else {
128            vector_utils::bundle_multiple(&encodings)
129        }
130    }
131
132    /// Encode a patch with specific feature type
133    ///
134    /// # Arguments
135    ///
136    /// * `patch` - Image patch to encode
137    /// * `feature_type` - Type of feature to encode for
138    ///
139    /// # Returns
140    ///
141    /// Feature-specific encoded hypervector
142    pub fn encode_patch<T>(
143        &self,
144        patch: ArrayView2<T>,
145        feature_type: &str,
146    ) -> NdimageResult<Hypervector>
147    where
148        T: Float + FromPrimitive + Copy,
149    {
150        let base_encoding = self.encode_image(patch)?;
151
152        if let Some(feature_hv) = self.feature_encoders.get(feature_type) {
153            base_encoding.bind(feature_hv)
154        } else {
155            Ok(base_encoding)
156        }
157    }
158
159    /// Add a feature encoder
160    ///
161    /// # Arguments
162    ///
163    /// * `feature_type` - Name of the feature type
164    /// * `feature_hv` - Hypervector encoding for this feature type
165    pub fn add_feature_encoder(&mut self, feature_type: String, feature_hv: Hypervector) {
166        self.feature_encoders.insert(feature_type, feature_hv);
167    }
168
169    /// Get spatial encoding for a specific position
170    pub fn get_position_encoding(&self, y: usize, x: usize) -> Option<&Hypervector> {
171        self.position_encoders.get(&(y, x))
172    }
173
174    /// Get value encoding for a specific intensity
175    pub fn get_value_encoding(&self, intensity: u8) -> Option<&Hypervector> {
176        self.value_encoders.get(&intensity)
177    }
178}
179
180/// HDC-based pattern recognition
181///
182/// Recognize patterns in images using stored hypervector representations.
183///
184/// # Arguments
185///
186/// * `image` - Input image to search
187/// * `patterns` - Known patterns with labels
188/// * `config` - HDC configuration
189///
190/// # Returns
191///
192/// Vector of pattern matches found in the image
193#[allow(dead_code)]
194pub fn hdc_pattern_recognition<T>(
195    image: ArrayView2<T>,
196    patterns: &[(&ArrayView2<T>, &str)],
197    config: &HDCConfig,
198) -> NdimageResult<Vec<PatternMatch>>
199where
200    T: Float + FromPrimitive + Copy + Send + Sync,
201{
202    let (height, width) = image.dim();
203    let encoder = ImageHDCEncoder::new(height, width, config.clone());
204    let mut memory = HDCMemory::new(config.clone());
205
206    // Encode and store patterns
207    for (pattern, label) in patterns {
208        let encoded_pattern = encoder.encode_image(**pattern)?;
209        memory.store(label.to_string(), encoded_pattern);
210    }
211
212    let mut matches = Vec::new();
213    let patch_size = 32; // Configurable patch size
214
215    // Sliding window pattern matching
216    for y in 0..height.saturating_sub(patch_size) {
217        for x in 0..width.saturating_sub(patch_size) {
218            let patch = image.slice(s![y..y + patch_size, x..x + patch_size]);
219            let encoded_patch = encoder.encode_image(patch)?;
220
221            if let Some((matched_label, confidence)) = memory.retrieve(&encoded_patch) {
222                matches.push(PatternMatch {
223                    label: matched_label,
224                    confidence,
225                    position: (y, x),
226                    size: (patch_size, patch_size),
227                });
228            }
229        }
230    }
231
232    // Non-maximum suppression
233    let filtered_matches = non_maximum_suppression(matches, 0.5)?;
234
235    Ok(filtered_matches)
236}
237
238/// HDC-based Feature Detection
239///
240/// Detect and encode image features using hyperdimensional computing.
241/// Provides compositional feature representations.
242#[allow(dead_code)]
243pub fn hdc_feature_detection<T>(
244    image: ArrayView2<T>,
245    feature_types: &[String],
246    config: &HDCConfig,
247) -> NdimageResult<HashMap<String, Vec<FeatureDetection>>>
248where
249    T: Float + FromPrimitive + Copy + Send + Sync,
250{
251    let (height, width) = image.dim();
252    let mut encoder = ImageHDCEncoder::new(height, width, config.clone());
253
254    // Initialize feature encoders
255    for feature_type in feature_types {
256        let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
257        encoder.add_feature_encoder(feature_type.clone(), feature_hv);
258    }
259
260    let mut feature_detections = HashMap::new();
261
262    for feature_type in feature_types {
263        let mut detections = Vec::new();
264        let window_size = 16; // Configurable
265
266        for y in 0..height.saturating_sub(window_size) {
267            for x in 0..width.saturating_sub(window_size) {
268                let patch = image.slice(s![y..y + window_size, x..x + window_size]);
269
270                // Analyze patch characteristics
271                let feature_strength = analyze_patch_for_feature(&patch, feature_type)?;
272
273                if feature_strength > config.similarity_threshold {
274                    let encoded_feature = encoder.encode_patch(patch, feature_type)?;
275
276                    detections.push(FeatureDetection {
277                        feature_type: feature_type.clone(),
278                        position: (y, x),
279                        strength: feature_strength,
280                        hypervector: encoded_feature,
281                        patch_size: (window_size, window_size),
282                    });
283                }
284            }
285        }
286
287        feature_detections.insert(feature_type.clone(), detections);
288    }
289
290    Ok(feature_detections)
291}
292
293/// HDC-based Sequence Processing
294///
295/// Process temporal sequences using hyperdimensional computing.
296/// Encodes temporal relationships and sequences efficiently.
297#[allow(dead_code)]
298pub fn hdc_sequence_processing<T>(
299    image_sequence: &[ArrayView2<T>],
300    sequence_length: usize,
301    config: &HDCConfig,
302) -> NdimageResult<SequenceEncoding>
303where
304    T: Float + FromPrimitive + Copy + Send + Sync,
305{
306    if image_sequence.is_empty() {
307        return Err(NdimageError::InvalidInput("Empty image sequence".into()));
308    }
309
310    let (height, width) = image_sequence[0].dim();
311    let encoder = ImageHDCEncoder::new(height, width, config.clone());
312
313    // Create temporal position encoders
314    let mut temporal_encoders = Vec::new();
315    for t in 0..sequence_length {
316        temporal_encoders.push(Hypervector::random(config.hypervector_dim, config.sparsity));
317    }
318
319    let mut sequence_encodings = Vec::new();
320    let mut temporal_positions = Vec::new();
321
322    // Process each frame in the sequence
323    for (t, image) in image_sequence.iter().enumerate().take(sequence_length) {
324        let frame_encoding = encoder.encode_image(*image)?;
325
326        if t < temporal_encoders.len() {
327            let temporal_binding = frame_encoding.bind(&temporal_encoders[t])?;
328            sequence_encodings.push(temporal_binding);
329            temporal_positions.push(t);
330        }
331    }
332
333    // Bundle all temporal encodings
334    let final_encoding = if sequence_encodings.is_empty() {
335        Hypervector::zeros(config.hypervector_dim)
336    } else {
337        vector_utils::bundle_multiple(&sequence_encodings)?
338    };
339
340    // Calculate sequence confidence based on consistency
341    let mut similarities = Vec::new();
342    for i in 0..sequence_encodings.len().saturating_sub(1) {
343        let sim = sequence_encodings[i].similarity(&sequence_encodings[i + 1]);
344        similarities.push(sim);
345    }
346
347    let confidence = if similarities.is_empty() {
348        1.0
349    } else {
350        similarities.iter().sum::<f64>() / similarities.len() as f64
351    };
352
353    Ok(SequenceEncoding {
354        encoding: final_encoding,
355        temporal_positions,
356        confidence,
357    })
358}
359
360/// Multi-scale image encoding
361///
362/// Encode image at multiple scales for robust representation.
363#[allow(dead_code)]
364pub fn multiscale_encoding<T>(
365    image: ArrayView2<T>,
366    scales: &[f64],
367    config: &HDCConfig,
368) -> NdimageResult<Hypervector>
369where
370    T: Float + FromPrimitive + Copy,
371{
372    let (height, width) = image.dim();
373    let mut scale_encodings = Vec::new();
374
375    for &scale in scales {
376        let scaled_height = (height as f64 * scale) as usize;
377        let scaled_width = (width as f64 * scale) as usize;
378
379        if scaled_height == 0 || scaled_width == 0 {
380            continue;
381        }
382
383        // Simple downsampling (in practice would use proper resampling)
384        let mut scaled_image = Array2::zeros((scaled_height, scaled_width));
385        for y in 0..scaled_height {
386            for x in 0..scaled_width {
387                let orig_y = (y * height) / scaled_height;
388                let orig_x = (x * width) / scaled_width;
389                scaled_image[[y, x]] = image[[orig_y, orig_x]];
390            }
391        }
392
393        let encoder = ImageHDCEncoder::new(scaled_height, scaled_width, config.clone());
394        let scale_encoding = encoder.encode_image(scaled_image.view())?;
395        scale_encodings.push(scale_encoding);
396    }
397
398    if scale_encodings.is_empty() {
399        Ok(Hypervector::zeros(config.hypervector_dim))
400    } else {
401        vector_utils::bundle_multiple(&scale_encodings)
402    }
403}
404
405/// Create patches from image with specified stride and size
406pub fn create_patches<T>(
407    image: ArrayView2<T>,
408    patch_size: (usize, usize),
409    stride: (usize, usize),
410) -> Vec<Array2<T>>
411where
412    T: Clone,
413{
414    let (height, width) = image.dim();
415    let (patch_h, patch_w) = patch_size;
416    let (stride_y, stride_x) = stride;
417
418    let mut patches = Vec::new();
419
420    let mut y = 0;
421    while y + patch_h <= height {
422        let mut x = 0;
423        while x + patch_w <= width {
424            let patch_view = image.slice(s![y..y + patch_h, x..x + patch_w]);
425            let patch =
426                Array2::from_shape_fn((patch_h, patch_w), |(i, j)| patch_view[[i, j]].clone());
427            patches.push(patch);
428            x += stride_x;
429        }
430        y += stride_y;
431    }
432
433    patches
434}
435
436/// Semantic encoding of concepts
437pub fn encode_semantic_concepts(
438    concepts: &[String],
439    config: &HDCConfig,
440) -> NdimageResult<Hypervector> {
441    if concepts.is_empty() {
442        return Ok(Hypervector::zeros(config.hypervector_dim));
443    }
444
445    let mut concept_encodings = Vec::new();
446
447    for concept in concepts {
448        // Create deterministic encoding based on concept string
449        let mut concept_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
450
451        // Hash concept name to create consistent encoding
452        use std::collections::hash_map::DefaultHasher;
453        use std::hash::{Hash, Hasher};
454
455        let mut hasher = DefaultHasher::new();
456        concept.hash(&mut hasher);
457        let hash_value = hasher.finish();
458
459        // Use hash to permute the hypervector consistently
460        let permutation_seed = hash_value as usize;
461        if permutation_seed % 2 == 0 {
462            concept_hv = concept_hv.scale(-1.0); // Flip polarity based on hash
463        }
464
465        concept_encodings.push(concept_hv);
466    }
467
468    vector_utils::bundle_multiple(&concept_encodings)
469}
470
471/// HDC-based Image Classification
472///
473/// Optimized image classification using hyperdimensional computing.
474/// Achieves brain-like efficiency with massive parallelism.
475#[allow(dead_code)]
476pub fn hdc_image_classification<T>(
477    images: &[ArrayView2<T>],
478    labels: &[String],
479    test_images: &[ArrayView2<T>],
480    config: &HDCConfig,
481) -> NdimageResult<Vec<(String, f64)>>
482where
483    T: Float + FromPrimitive + Copy + Send + Sync,
484{
485    if images.is_empty() || images.len() != labels.len() {
486        return Err(NdimageError::InvalidInput(
487            "Invalid training data".to_string(),
488        ));
489    }
490
491    let (height, width) = images[0].dim();
492
493    // Initialize encoder and memory
494    let encoder = ImageHDCEncoder::new(height, width, config.clone());
495    let mut memory = HDCMemory::new(config.clone());
496
497    // Training phase: encode and store images
498    for (image, label) in images.iter().zip(labels.iter()) {
499        let encoded_image = encoder.encode_image(*image)?;
500
501        // If label already exists, bundle with existing pattern
502        if let Some(existing) = memory.patterns.get(label) {
503            let bundled = existing.bundle(&encoded_image)?;
504            memory.store(label.clone(), bundled);
505        } else {
506            memory.store(label.clone(), encoded_image);
507        }
508    }
509
510    // Testing phase: classify test images
511    let mut results = Vec::new();
512
513    for test_image in test_images {
514        let encoded_test = encoder.encode_image(*test_image)?;
515
516        if let Some((predicted_label, confidence)) = memory.retrieve(&encoded_test) {
517            results.push((predicted_label, confidence));
518        } else {
519            results.push(("unknown".to_string(), 0.0));
520        }
521    }
522
523    Ok(results)
524}
525
526/// HDC-based Pattern Matching
527///
528/// Optimized pattern matching using hyperdimensional representations.
529/// Robust to noise and partial occlusion.
530#[allow(dead_code)]
531pub fn hdc_pattern_matching<T>(
532    image: ArrayView2<T>,
533    patterns: &[(ArrayView2<T>, String)],
534    config: &HDCConfig,
535) -> NdimageResult<Vec<PatternMatch>>
536where
537    T: Float + FromPrimitive + Copy + Send + Sync,
538{
539    let (height, width) = image.dim();
540    let encoder = ImageHDCEncoder::new(height, width, config.clone());
541    let mut memory = HDCMemory::new(config.clone());
542
543    // Encode and store patterns
544    for (pattern, label) in patterns {
545        let encoded_pattern = encoder.encode_image(*pattern)?;
546        memory.store(label.clone(), encoded_pattern);
547    }
548
549    let mut matches = Vec::new();
550    let patch_size = 32; // Configurable patch size
551
552    // Sliding window pattern matching
553    for y in 0..height.saturating_sub(patch_size) {
554        for x in 0..width.saturating_sub(patch_size) {
555            let patch = image.slice(s![y..y + patch_size, x..x + patch_size]);
556            let encoded_patch = encoder.encode_image(patch)?;
557
558            if let Some((matched_label, confidence)) = memory.retrieve(&encoded_patch) {
559                matches.push(PatternMatch {
560                    label: matched_label,
561                    confidence,
562                    position: (y, x),
563                    size: (patch_size, patch_size),
564                });
565            }
566        }
567    }
568
569    // Non-maximum suppression
570    let filtered_matches = non_maximum_suppression(matches, 0.5)?;
571
572    Ok(filtered_matches)
573}
574
575/// HDC-based Compositional Reasoning
576///
577/// Compose and decompose visual concepts using hyperdimensional operations.
578/// Enables complex reasoning about image content.
579#[allow(dead_code)]
580pub fn hdc_compositional_reasoning<T>(
581    image: ArrayView2<T>,
582    concept_memory: &HDCMemory,
583    query_concepts: &[String],
584    config: &HDCConfig,
585) -> NdimageResult<CompositionResult>
586where
587    T: Float + FromPrimitive + Copy + Send + Sync,
588{
589    let (height, width) = image.dim();
590    let encoder = ImageHDCEncoder::new(height, width, config.clone());
591
592    // Encode the input image
593    let encoded_image = encoder.encode_image(image)?;
594
595    // Compose query from concepts
596    let mut composed_query: Option<Hypervector> = None;
597
598    for concept in query_concepts {
599        if let Some(concept_hv) = concept_memory.get_item(concept) {
600            if let Some(ref current_query) = composed_query {
601                composed_query = Some(current_query.bind(concept_hv)?);
602            } else {
603                composed_query = Some(concept_hv.clone());
604            }
605        }
606    }
607
608    if let Some(query_hv) = composed_query {
609        // Compute similarity between image and composed query
610        let similarity = encoded_image.similarity(&query_hv);
611
612        // Decompose image to find constituent concepts
613        let mut concept_presence = HashMap::new();
614
615        for (concept_name, concept_hv) in &concept_memory.item_memory {
616            let presence_strength = encoded_image.similarity(concept_hv);
617            if presence_strength > config.cleanup_threshold {
618                concept_presence.insert(concept_name.clone(), presence_strength);
619            }
620        }
621
622        Ok(CompositionResult {
623            query_similarity: similarity,
624            concept_presence,
625            composed_representation: query_hv,
626            image_representation: encoded_image,
627        })
628    } else {
629        Err(NdimageError::InvalidInput(
630            "No valid concepts found".to_string(),
631        ))
632    }
633}
634
635#[cfg(test)]
636mod tests {
637    use super::*;
638    use crate::hyperdimensional_computing::calculate_overlap;
639    use scirs2_core::ndarray::Array2;
640
641    #[test]
642    fn test_image_hdc_encoder_creation() {
643        let config = HDCConfig::default();
644        let encoder = ImageHDCEncoder::new(10, 10, config);
645
646        assert_eq!(encoder.height, 10);
647        assert_eq!(encoder.width, 10);
648        assert_eq!(encoder.position_encoders.len(), 100); // 10x10
649        assert_eq!(encoder.value_encoders.len(), 256); // 0-255
650    }
651
652    #[test]
653    fn test_image_encoding() {
654        let config = HDCConfig::default();
655        let encoder = ImageHDCEncoder::new(5, 5, config);
656
657        let image = Array2::from_shape_fn((5, 5), |(i, j)| (i + j) as f64 / 10.0);
658        let result = encoder.encode_image(image.view());
659
660        assert!(result.is_ok());
661        let encoded = result.expect("Operation failed");
662        assert_eq!(encoded.dimension, encoder.config.hypervector_dim);
663        assert!(encoded.norm > 0.0);
664    }
665
666    #[test]
667    fn test_feature_encoder() {
668        let config = HDCConfig::default();
669        let mut encoder = ImageHDCEncoder::new(5, 5, config.clone());
670
671        let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
672        encoder.add_feature_encoder("test_feature".to_string(), feature_hv);
673
674        assert!(encoder.feature_encoders.contains_key("test_feature"));
675    }
676
677    #[test]
678    fn test_patch_encoding() {
679        let config = HDCConfig::default();
680        let mut encoder = ImageHDCEncoder::new(10, 10, config.clone());
681
682        let feature_hv = Hypervector::random(config.hypervector_dim, config.sparsity);
683        encoder.add_feature_encoder("edge".to_string(), feature_hv);
684
685        let patch = Array2::<f64>::ones((3, 3));
686        let result = encoder.encode_patch(patch.view(), "edge");
687
688        assert!(result.is_ok());
689        let encoded = result.expect("Operation failed");
690        assert_eq!(encoded.dimension, encoder.config.hypervector_dim);
691    }
692
693    #[test]
694    fn test_multiscale_encoding() {
695        // Use smaller hypervector_dim for faster testing
696        let config = HDCConfig {
697            hypervector_dim: 500, // Reduced from default 10000 for faster testing
698            ..Default::default()
699        };
700        let image = Array2::from_shape_fn((10, 10), |(i, j)| (i + j) as f64 / 20.0);
701        let scales = vec![1.0, 0.5];
702
703        let result = multiscale_encoding(image.view(), &scales, &config);
704        assert!(result.is_ok());
705
706        let encoded = result.expect("Operation failed");
707        assert_eq!(encoded.dimension, config.hypervector_dim);
708        assert!(encoded.norm > 0.0);
709    }
710
711    #[test]
712    fn test_create_patches() {
713        let image = Array2::from_shape_fn((10, 10), |(i, j)| i + j);
714        let patches = create_patches(image.view(), (3, 3), (2, 2));
715
716        assert!(!patches.is_empty());
717
718        // Should create patches from (0,0), (0,2), (0,4), (0,6), (2,0), etc.
719        // With 10x10 image, 3x3 patches, and stride 2, we get several patches
720        assert!(!patches.is_empty());
721
722        for patch in &patches {
723            assert_eq!(patch.dim(), (3, 3));
724        }
725    }
726
727    #[test]
728    fn test_encode_semantic_concepts() {
729        let config = HDCConfig::default();
730        let concepts = vec!["cat".to_string(), "dog".to_string(), "bird".to_string()];
731
732        let result = encode_semantic_concepts(&concepts, &config);
733        assert!(result.is_ok());
734
735        let encoded = result.expect("Operation failed");
736        assert_eq!(encoded.dimension, config.hypervector_dim);
737        assert!(encoded.norm > 0.0);
738
739        // Test that the function can be called multiple times successfully
740        let result2 = encode_semantic_concepts(&concepts, &config);
741        assert!(result2.is_ok());
742        let encoded2 = result2.expect("Operation failed");
743        assert_eq!(encoded2.dimension, config.hypervector_dim);
744        assert!(encoded2.norm > 0.0);
745    }
746
747    #[test]
748    fn test_analyze_patch_for_feature() {
749        let patch = Array2::<f64>::ones((5, 5));
750
751        let edge_strength =
752            analyze_patch_for_feature(&patch.view(), "edge").expect("Operation failed");
753        assert_eq!(edge_strength, 0.8);
754
755        let corner_strength =
756            analyze_patch_for_feature(&patch.view(), "corner").expect("Operation failed");
757        assert_eq!(corner_strength, 0.6);
758
759        let texture_strength =
760            analyze_patch_for_feature(&patch.view(), "texture").expect("Operation failed");
761        assert_eq!(texture_strength, 0.7);
762
763        let unknown_strength =
764            analyze_patch_for_feature(&patch.view(), "unknown").expect("Operation failed");
765        assert_eq!(unknown_strength, 0.5);
766    }
767
768    #[test]
769    fn test_calculate_overlap() {
770        let match1 = PatternMatch {
771            label: "test1".to_string(),
772            confidence: 0.9,
773            position: (0, 0),
774            size: (10, 10),
775        };
776
777        let match2 = PatternMatch {
778            label: "test2".to_string(),
779            confidence: 0.8,
780            position: (5, 5),
781            size: (10, 10),
782        };
783
784        let overlap = calculate_overlap(&match1, &match2);
785        assert!(overlap > 0.0);
786        assert!(overlap < 1.0);
787
788        // Non-overlapping matches
789        let match3 = PatternMatch {
790            label: "test3".to_string(),
791            confidence: 0.7,
792            position: (15, 15),
793            size: (5, 5),
794        };
795
796        let no_overlap = calculate_overlap(&match1, &match3);
797        assert_eq!(no_overlap, 0.0);
798    }
799
800    #[test]
801    fn test_sequence_encoding() {
802        let config = HDCConfig::default();
803
804        let img1 = Array2::zeros((5, 5));
805        let img2 = Array2::ones((5, 5));
806        let img3 = Array2::from_elem((5, 5), 0.5);
807
808        let sequence = vec![img1.view(), img2.view(), img3.view()];
809        let result = hdc_sequence_processing(&sequence, 3, &config);
810
811        assert!(result.is_ok());
812        let seq_encoding = result.expect("Operation failed");
813
814        assert_eq!(seq_encoding.encoding.dimension, config.hypervector_dim);
815        assert_eq!(seq_encoding.temporal_positions.len(), 3);
816        assert!(seq_encoding.confidence >= -1.0); // Allow negative confidence
817        assert!(seq_encoding.confidence <= 1.0);
818    }
819}