quantrs2_ml/recommender/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5// SciRS2 Policy: Unified imports
6use crate::error::{MLError, Result};
7use crate::optimization::OptimizationMethod;
8use crate::qnn::{QNNLayerType, QuantumNeuralNetwork};
9use scirs2_core::ndarray::*;
10use scirs2_core::random::prelude::*;
11use scirs2_core::random::{ChaCha20Rng, Rng, SeedableRng};
12use scirs2_core::{Complex32, Complex64};
13use std::collections::{HashMap, HashSet};
14use std::f64::consts::PI;
15
16/// Item features
17pub trait RecommendationEngine: std::fmt::Debug {
18    /// Generate recommendations for a user
19    fn recommend(
20        &self,
21        user_id: usize,
22        n_items: usize,
23        exclude_seen: bool,
24    ) -> Result<Vec<Recommendation>>;
25    /// Update model with new interaction
26    fn update(&mut self, user_id: usize, item_id: usize, rating: f64) -> Result<()>;
27    /// Compute similarity between users or items
28    fn compute_similarity(
29        &self,
30        id1: usize,
31        id2: usize,
32        similarity_type: SimilarityType,
33    ) -> Result<f64>;
34    /// Get model parameters
35    fn parameters(&self) -> &Array1<f64>;
36    /// Clone the engine
37    fn clone_box(&self) -> Box<dyn RecommendationEngine>;
38}
39impl Clone for Box<dyn RecommendationEngine> {
40    fn clone(&self) -> Self {
41        self.clone_box()
42    }
43}
44
45#[derive(Debug, Clone)]
46pub struct ItemFeatures {
47    /// Item ID
48    pub item_id: usize,
49    /// Feature vector
50    pub features: Array1<f64>,
51    /// Categories
52    pub categories: Vec<String>,
53    /// Attributes
54    pub attributes: HashMap<String, AttributeValue>,
55    /// Quantum representation
56    pub quantum_features: Array1<f64>,
57}
58/// Quantum recommender system configuration
59#[derive(Debug, Clone)]
60pub struct QuantumRecommenderConfig {
61    /// Number of qubits for quantum processing
62    pub num_qubits: usize,
63    /// Recommendation algorithm type
64    pub algorithm: RecommendationAlgorithm,
65    /// Number of latent factors
66    pub num_factors: usize,
67    /// Regularization strength
68    pub regularization: f64,
69    /// Learning rate
70    pub learning_rate: f64,
71    /// Quantum enhancement level
72    pub quantum_enhancement: QuantumEnhancementLevel,
73    /// Similarity measure
74    pub similarity_measure: SimilarityMeasure,
75}
76impl QuantumRecommenderConfig {
77    /// Create default configuration
78    pub fn default() -> Self {
79        Self {
80            num_qubits: 10,
81            algorithm: RecommendationAlgorithm::QuantumMatrixFactorization {
82                optimization_method: OptimizationMethod::Adam,
83                num_iterations: 100,
84            },
85            num_factors: 50,
86            regularization: 0.01,
87            learning_rate: 0.001,
88            quantum_enhancement: QuantumEnhancementLevel::Medium,
89            similarity_measure: SimilarityMeasure::QuantumFidelity,
90        }
91    }
92    /// Configuration for collaborative filtering
93    pub fn collaborative_filtering() -> Self {
94        Self {
95            num_qubits: 12,
96            algorithm: RecommendationAlgorithm::QuantumCollaborativeFiltering {
97                neighborhood_size: 50,
98                min_common_items: 3,
99            },
100            num_factors: 100,
101            regularization: 0.001,
102            learning_rate: 0.01,
103            quantum_enhancement: QuantumEnhancementLevel::High,
104            similarity_measure: SimilarityMeasure::EntanglementSimilarity,
105        }
106    }
107    /// Configuration for content-based filtering
108    pub fn content_based() -> Self {
109        Self {
110            num_qubits: 10,
111            algorithm: RecommendationAlgorithm::QuantumContentBased {
112                feature_extraction: FeatureExtractionMethod::QuantumEmbeddings {
113                    embedding_dim: 128,
114                },
115                profile_learning: ProfileLearningMethod::QuantumSuperposition,
116            },
117            num_factors: 64,
118            regularization: 0.005,
119            learning_rate: 0.005,
120            quantum_enhancement: QuantumEnhancementLevel::Medium,
121            similarity_measure: SimilarityMeasure::Cosine,
122        }
123    }
124    /// Configuration for hybrid recommender
125    pub fn hybrid() -> Self {
126        Self {
127            num_qubits: 14,
128            algorithm: RecommendationAlgorithm::HybridQuantum {
129                cf_weight: 0.6,
130                cb_weight: 0.3,
131                knowledge_weight: 0.1,
132            },
133            num_factors: 80,
134            regularization: 0.01,
135            learning_rate: 0.001,
136            quantum_enhancement: QuantumEnhancementLevel::High,
137            similarity_measure: SimilarityMeasure::Hybrid {
138                classical_weight: 0.4,
139                quantum_weight: 0.6,
140            },
141        }
142    }
143}
144/// Recommendation explanation
145#[derive(Debug, Clone)]
146pub struct RecommendationExplanation {
147    /// Similar users who liked this item
148    pub similar_users: Vec<(usize, f64)>,
149    /// Similar items to user's history
150    pub similar_items: Vec<(usize, f64)>,
151    /// Feature-based reasons
152    pub feature_reasons: Vec<String>,
153    /// Quantum state information
154    pub quantum_state: Option<QuantumStateInfo>,
155}
156/// Similarity computation type
157#[derive(Debug, Clone)]
158pub enum SimilarityType {
159    UserToUser,
160    ItemToItem,
161    UserToItem,
162}
163/// Trend indicator
164#[derive(Debug, Clone)]
165pub struct TrendIndicator {
166    /// Category or feature
167    pub feature: String,
168    /// Trend direction (-1 to 1)
169    pub direction: f64,
170    /// Trend strength
171    pub strength: f64,
172    /// Time window
173    pub window: f64,
174}
175/// Recommendation algorithm types
176#[derive(Debug, Clone)]
177pub enum RecommendationAlgorithm {
178    /// Quantum collaborative filtering
179    QuantumCollaborativeFiltering {
180        neighborhood_size: usize,
181        min_common_items: usize,
182    },
183    /// Quantum matrix factorization
184    QuantumMatrixFactorization {
185        optimization_method: OptimizationMethod,
186        num_iterations: usize,
187    },
188    /// Quantum content-based filtering
189    QuantumContentBased {
190        feature_extraction: FeatureExtractionMethod,
191        profile_learning: ProfileLearningMethod,
192    },
193    /// Hybrid quantum recommender
194    HybridQuantum {
195        cf_weight: f64,
196        cb_weight: f64,
197        knowledge_weight: f64,
198    },
199    /// Quantum neural collaborative filtering
200    QuantumNeuralCF {
201        embedding_dim: usize,
202        hidden_layers: Vec<usize>,
203    },
204    /// Quantum graph-based recommendations
205    QuantumGraphRecommender {
206        walk_length: usize,
207        num_walks: usize,
208        teleportation_prob: f64,
209    },
210}
211/// Interaction matrix for user-item data
212#[derive(Debug, Clone)]
213pub struct InteractionMatrix {
214    /// Sparse user-item ratings
215    ratings: HashMap<(usize, usize), f64>,
216    /// User indices
217    user_ids: HashSet<usize>,
218    /// Item indices
219    item_ids: HashSet<usize>,
220    /// Implicit feedback
221    implicit_feedback: Option<HashMap<(usize, usize), ImplicitFeedback>>,
222    /// Temporal information
223    timestamps: Option<HashMap<(usize, usize), f64>>,
224}
225impl InteractionMatrix {
226    /// Create new interaction matrix
227    pub fn new() -> Self {
228        Self {
229            ratings: HashMap::new(),
230            user_ids: HashSet::new(),
231            item_ids: HashSet::new(),
232            implicit_feedback: None,
233            timestamps: None,
234        }
235    }
236    /// Add rating
237    pub fn add_rating(&mut self, user_id: usize, item_id: usize, rating: f64) {
238        self.ratings.insert((user_id, item_id), rating);
239        self.user_ids.insert(user_id);
240        self.item_ids.insert(item_id);
241    }
242    /// Get users who rated an item
243    pub fn get_item_users(&self, item_id: usize) -> Result<Vec<usize>> {
244        Ok(self
245            .ratings
246            .keys()
247            .filter_map(
248                |(user, item)| {
249                    if *item == item_id {
250                        Some(*user)
251                    } else {
252                        None
253                    }
254                },
255            )
256            .collect())
257    }
258    /// Get item rating count
259    pub fn get_item_rating_count(&self, item_id: usize) -> Result<usize> {
260        Ok(self
261            .ratings
262            .keys()
263            .filter(|(_, item)| *item == item_id)
264            .count())
265    }
266}
267/// Recommendation result
268#[derive(Debug, Clone)]
269pub struct Recommendation {
270    /// Item ID
271    pub item_id: usize,
272    /// Predicted score
273    pub score: f64,
274    /// Confidence interval
275    pub confidence: (f64, f64),
276    /// Explanation
277    pub explanation: Option<RecommendationExplanation>,
278    /// Quantum contribution
279    pub quantum_contribution: f64,
280}
281/// Evaluation metrics
282#[derive(Debug, Clone, Default)]
283pub struct EvaluationMetrics {
284    pub mae: f64,
285    pub rmse: f64,
286}
287impl EvaluationMetrics {
288    fn compute(predictions: &[f64], actuals: &[f64]) -> Self {
289        let n = predictions.len() as f64;
290        let mae = predictions
291            .iter()
292            .zip(actuals)
293            .map(|(p, a)| (p - a).abs())
294            .sum::<f64>()
295            / n;
296        let rmse = (predictions
297            .iter()
298            .zip(actuals)
299            .map(|(p, a)| (p - a).powi(2))
300            .sum::<f64>()
301            / n)
302            .sqrt();
303        Self { mae, rmse }
304    }
305}
306/// Quantum matrix factorization engine
307#[derive(Debug, Clone)]
308pub struct QuantumMFEngine {
309    config: QuantumRecommenderConfig,
310    pub parameters: Array1<f64>,
311}
312impl QuantumMFEngine {
313    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
314        Ok(Self {
315            config: config.clone(),
316            parameters: Array1::zeros(100),
317        })
318    }
319}
320/// Training history
321#[derive(Debug, Clone)]
322pub struct TrainingHistory {
323    pub epochs: Vec<usize>,
324    pub train_losses: Vec<f64>,
325    pub val_metrics: Vec<EvaluationMetrics>,
326}
327impl TrainingHistory {
328    fn new() -> Self {
329        Self {
330            epochs: Vec::new(),
331            train_losses: Vec::new(),
332            val_metrics: Vec::new(),
333        }
334    }
335    fn add_epoch(&mut self, epoch: usize, train_loss: f64, val_metrics: EvaluationMetrics) {
336        self.epochs.push(epoch);
337        self.train_losses.push(train_loss);
338        self.val_metrics.push(val_metrics);
339    }
340    fn should_stop_early(&self) -> bool {
341        if self.val_metrics.len() < 4 {
342            return false;
343        }
344        let recent = &self.val_metrics[self.val_metrics.len() - 3..];
345        recent[0].rmse < recent[1].rmse && recent[1].rmse < recent[2].rmse
346    }
347}
348/// Interaction context
349#[derive(Debug, Clone)]
350pub struct InteractionContext {
351    /// Device type
352    pub device: String,
353    /// Location (anonymized)
354    pub location_cluster: usize,
355    /// Session length
356    pub session_duration: f64,
357    /// Previous actions
358    pub action_sequence: Vec<String>,
359}
360/// Implicit feedback data
361#[derive(Debug, Clone)]
362pub struct ImplicitFeedback {
363    /// View count
364    pub views: usize,
365    /// Interaction duration
366    pub duration: f64,
367    /// Click-through rate
368    pub ctr: f64,
369    /// Conversion indicator
370    pub converted: bool,
371}
372/// Entanglement generator for quantum similarity
373#[derive(Debug, Clone)]
374pub struct EntanglementGenerator {
375    /// Entanglement patterns
376    patterns: Vec<EntanglementPattern>,
377    /// Circuit parameters
378    circuit_params: Vec<f64>,
379}
380impl EntanglementGenerator {
381    /// Create new entanglement generator
382    pub fn new(num_qubits: usize) -> Self {
383        let patterns = vec![
384            EntanglementPattern {
385                qubit_pairs: (0..num_qubits - 1).map(|i| (i, i + 1)).collect(),
386                strength: 0.8,
387                pattern_type: PatternType::Bell,
388            },
389            EntanglementPattern {
390                qubit_pairs: vec![(0, num_qubits - 1)],
391                strength: 0.5,
392                pattern_type: PatternType::GHZ,
393            },
394        ];
395        Self {
396            patterns,
397            circuit_params: vec![0.0; num_qubits * 3],
398        }
399    }
400    /// Create entangled state
401    pub fn create_entangled_state(
402        &self,
403        vec1: &Array1<f64>,
404        vec2: &Array1<f64>,
405    ) -> Result<Array1<f64>> {
406        let combined = Array1::from_iter(vec1.iter().chain(vec2.iter()).cloned());
407        let mut entangled = combined.clone();
408        for pattern in &self.patterns {
409            for &(q1, q2) in &pattern.qubit_pairs {
410                if q1 < entangled.len() && q2 < entangled.len() {
411                    let v1 = entangled[q1];
412                    let v2 = entangled[q2];
413                    entangled[q1] = v1 * pattern.strength.cos() - v2 * pattern.strength.sin();
414                    entangled[q2] = v1 * pattern.strength.sin() + v2 * pattern.strength.cos();
415                }
416            }
417        }
418        let norm = entangled.dot(&entangled).sqrt();
419        Ok(entangled / (norm + 1e-10))
420    }
421}
422/// Diversity metrics
423#[derive(Debug, Clone)]
424pub struct DiversityMetrics {
425    /// Intra-list diversity
426    pub intra_list_diversity: f64,
427    /// Inter-list diversity
428    pub inter_list_diversity: f64,
429    /// Category coverage
430    pub category_coverage: f64,
431    /// Novelty score
432    pub novelty: f64,
433}
434/// Temporal preference patterns
435#[derive(Debug, Clone)]
436pub struct TemporalPatterns {
437    /// Time of day preferences
438    pub hourly_distribution: Array1<f64>,
439    /// Day of week preferences
440    pub weekly_distribution: Array1<f64>,
441    /// Seasonal preferences
442    pub seasonal_factors: Array1<f64>,
443    /// Trend indicators
444    pub trends: Vec<TrendIndicator>,
445}
446/// Business rules for recommendations
447#[derive(Debug, Clone)]
448pub struct BusinessRules {
449    /// Required categories
450    pub required_categories: Option<HashSet<String>>,
451    /// Boost new items
452    pub boost_new_items: Option<f64>,
453    /// Maximum price filter
454    pub max_price: Option<f64>,
455}
456/// Quantum neural collaborative filtering engine
457#[derive(Debug, Clone)]
458pub struct QuantumNCFEngine {
459    config: QuantumRecommenderConfig,
460    pub parameters: Array1<f64>,
461}
462impl QuantumNCFEngine {
463    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
464        Ok(Self {
465            config: config.clone(),
466            parameters: Array1::zeros(100),
467        })
468    }
469}
470/// Similarity measures
471#[derive(Debug, Clone)]
472pub enum SimilarityMeasure {
473    /// Cosine similarity
474    Cosine,
475    /// Pearson correlation
476    Pearson,
477    /// Quantum state fidelity
478    QuantumFidelity,
479    /// Entanglement-based similarity
480    EntanglementSimilarity,
481    /// Hybrid similarity
482    Hybrid {
483        classical_weight: f64,
484        quantum_weight: f64,
485    },
486}
487/// User profile
488#[derive(Debug, Clone)]
489pub struct UserProfile {
490    /// User ID
491    pub user_id: usize,
492    /// Feature vector
493    pub features: Array1<f64>,
494    /// Preference history
495    pub preferences: PreferenceHistory,
496    /// Quantum state representation
497    pub quantum_state: Array1<f64>,
498    /// Profile metadata
499    pub metadata: ProfileMetadata,
500}
501impl UserProfile {
502    /// Create new user profile
503    pub fn new(user_id: usize) -> Self {
504        Self {
505            user_id,
506            features: Array1::zeros(128),
507            preferences: PreferenceHistory::new(),
508            quantum_state: Array1::zeros(64),
509            metadata: ProfileMetadata {
510                created_at: 0.0,
511                updated_at: 0.0,
512                num_interactions: 0,
513                completeness: 0.0,
514            },
515        }
516    }
517    /// Update profile with new interaction
518    pub fn update_with_interaction(
519        &mut self,
520        item_id: usize,
521        rating: f64,
522        context: Option<InteractionContext>,
523    ) {
524        self.preferences.rated_items.push((item_id, rating));
525        if let Some(ctx) = context {
526            self.preferences.contexts.push(ctx);
527        }
528        self.metadata.num_interactions += 1;
529        self.metadata.updated_at = self.metadata.num_interactions as f64;
530    }
531}
532/// Entanglement pattern
533#[derive(Debug, Clone)]
534pub struct EntanglementPattern {
535    /// Qubit pairs
536    pub qubit_pairs: Vec<(usize, usize)>,
537    /// Entanglement strength
538    pub strength: f64,
539    /// Pattern type
540    pub pattern_type: PatternType,
541}
542/// Accuracy metrics
543#[derive(Debug, Clone)]
544pub struct AccuracyMetrics {
545    /// Mean Absolute Error
546    pub mae: f64,
547    /// Root Mean Square Error
548    pub rmse: f64,
549    /// Precision at K
550    pub precision_at_k: HashMap<usize, f64>,
551    /// Recall at K
552    pub recall_at_k: HashMap<usize, f64>,
553    /// NDCG at K
554    pub ndcg_at_k: HashMap<usize, f64>,
555}
556/// Coverage metrics
557#[derive(Debug, Clone)]
558pub struct CoverageMetrics {
559    /// Item coverage
560    pub item_coverage: f64,
561    /// User coverage
562    pub user_coverage: f64,
563    /// Cold start performance
564    pub cold_start_performance: f64,
565}
566/// Profile learning methods
567#[derive(Debug, Clone)]
568pub enum ProfileLearningMethod {
569    /// Weighted average
570    WeightedAverage,
571    /// Quantum superposition
572    QuantumSuperposition,
573    /// Adaptive quantum learning
574    AdaptiveQuantum { learning_rate: f64 },
575}
576/// Quantum enhancement levels
577#[derive(Debug, Clone)]
578pub enum QuantumEnhancementLevel {
579    /// Minimal quantum processing
580    Low,
581    /// Balanced quantum-classical
582    Medium,
583    /// Maximum quantum advantage
584    High,
585    /// Custom quantum configuration
586    Custom {
587        entanglement_strength: f64,
588        coherence_time: f64,
589        circuit_depth: usize,
590    },
591}
592/// Main quantum recommender system
593#[derive(Debug, Clone)]
594pub struct QuantumRecommender {
595    /// Configuration
596    config: QuantumRecommenderConfig,
597    /// User-item interaction matrix
598    interaction_matrix: InteractionMatrix,
599    /// Quantum processor
600    quantum_processor: QuantumProcessor,
601    /// Recommendation engine
602    engine: Box<dyn RecommendationEngine>,
603    /// User profiles
604    pub user_profiles: HashMap<usize, UserProfile>,
605    /// Item features
606    item_features: HashMap<usize, ItemFeatures>,
607    /// Model parameters
608    parameters: ModelParameters,
609    /// Performance metrics
610    metrics: RecommenderMetrics,
611}
612impl QuantumRecommender {
613    /// Create new quantum recommender system
614    pub fn new(config: QuantumRecommenderConfig) -> Result<Self> {
615        let interaction_matrix = InteractionMatrix::new();
616        let quantum_processor = QuantumProcessor::new(config.num_qubits)?;
617        let engine: Box<dyn RecommendationEngine> = match &config.algorithm {
618            RecommendationAlgorithm::QuantumCollaborativeFiltering { .. } => {
619                Box::new(QuantumCFEngine::new(&config)?)
620            }
621            RecommendationAlgorithm::QuantumMatrixFactorization { .. } => {
622                Box::new(QuantumMFEngine::new(&config)?)
623            }
624            RecommendationAlgorithm::QuantumContentBased { .. } => {
625                Box::new(QuantumCBEngine::new(&config)?)
626            }
627            RecommendationAlgorithm::HybridQuantum { .. } => {
628                Box::new(HybridQuantumEngine::new(&config)?)
629            }
630            RecommendationAlgorithm::QuantumNeuralCF { .. } => {
631                Box::new(QuantumNCFEngine::new(&config)?)
632            }
633            RecommendationAlgorithm::QuantumGraphRecommender { .. } => {
634                Box::new(QuantumGraphEngine::new(&config)?)
635            }
636        };
637        let parameters = ModelParameters::new(1000, 1000, config.num_factors);
638        Ok(Self {
639            config,
640            interaction_matrix,
641            quantum_processor,
642            engine,
643            user_profiles: HashMap::new(),
644            item_features: HashMap::new(),
645            parameters,
646            metrics: RecommenderMetrics::new(),
647        })
648    }
649    /// Add user-item interaction
650    pub fn add_interaction(
651        &mut self,
652        user_id: usize,
653        item_id: usize,
654        rating: f64,
655        context: Option<InteractionContext>,
656    ) -> Result<()> {
657        self.interaction_matrix.add_rating(user_id, item_id, rating);
658        if let Some(profile) = self.user_profiles.get_mut(&user_id) {
659            profile.update_with_interaction(item_id, rating, context);
660        } else {
661            let mut profile = UserProfile::new(user_id);
662            profile.update_with_interaction(item_id, rating, context);
663            self.user_profiles.insert(user_id, profile);
664        }
665        self.engine.update(user_id, item_id, rating)?;
666        Ok(())
667    }
668    /// Get recommendations for a user
669    pub fn recommend(
670        &self,
671        user_id: usize,
672        n_items: usize,
673        options: RecommendationOptions,
674    ) -> Result<Vec<Recommendation>> {
675        let mut recommendations = self
676            .engine
677            .recommend(user_id, n_items, options.exclude_seen)?;
678        if options.diversify {
679            recommendations =
680                self.diversify_recommendations(recommendations, options.diversity_weight)?;
681        }
682        if options.explain {
683            for rec in &mut recommendations {
684                rec.explanation = Some(self.generate_explanation(user_id, rec.item_id)?);
685            }
686        }
687        if let Some(rules) = options.business_rules {
688            recommendations = self.apply_business_rules(recommendations, rules)?;
689        }
690        Ok(recommendations)
691    }
692    /// Train the recommender system
693    pub fn train(
694        &mut self,
695        train_data: &[(usize, usize, f64)],
696        val_data: Option<&[(usize, usize, f64)]>,
697        epochs: usize,
698    ) -> Result<TrainingHistory> {
699        let mut history = TrainingHistory::new();
700        for epoch in 0..epochs {
701            let mut train_loss = 0.0;
702            for &(user_id, item_id, rating) in train_data {
703                let prediction = self.predict(user_id, item_id)?;
704                let loss = (prediction - rating).powi(2);
705                train_loss += loss;
706                self.update_model(user_id, item_id, rating, prediction)?;
707            }
708            train_loss /= train_data.len() as f64;
709            let val_metrics = if let Some(val) = val_data {
710                self.evaluate(val)?
711            } else {
712                EvaluationMetrics::default()
713            };
714            history.add_epoch(epoch, train_loss, val_metrics);
715            if history.should_stop_early() {
716                break;
717            }
718        }
719        Ok(history)
720    }
721    /// Predict rating for user-item pair
722    pub fn predict(&self, user_id: usize, item_id: usize) -> Result<f64> {
723        let user_embedding = self.parameters.get_user_embedding(user_id)?;
724        let item_embedding = self.parameters.get_item_embedding(item_id)?;
725        let quantum_similarity = self.quantum_processor.compute_similarity(
726            &user_embedding,
727            &item_embedding,
728            &self.config.similarity_measure,
729        )?;
730        let prediction = self.parameters.global_bias
731            + self.parameters.user_bias[user_id]
732            + self.parameters.item_bias[item_id]
733            + quantum_similarity;
734        Ok(prediction.max(1.0).min(5.0))
735    }
736    /// Update model parameters
737    fn update_model(
738        &mut self,
739        user_id: usize,
740        item_id: usize,
741        true_rating: f64,
742        predicted_rating: f64,
743    ) -> Result<()> {
744        let error = true_rating - predicted_rating;
745        let lr = self.config.learning_rate;
746        let reg = self.config.regularization;
747        self.parameters.user_bias[user_id] +=
748            lr * (error - reg * self.parameters.user_bias[user_id]);
749        self.parameters.item_bias[item_id] +=
750            lr * (error - reg * self.parameters.item_bias[item_id]);
751        let quantum_gradient = self
752            .quantum_processor
753            .compute_gradient(user_id, item_id, error)?;
754        self.parameters
755            .update_embeddings(user_id, item_id, &quantum_gradient, lr, reg)?;
756        Ok(())
757    }
758    /// Diversify recommendations
759    fn diversify_recommendations(
760        &self,
761        mut recommendations: Vec<Recommendation>,
762        diversity_weight: f64,
763    ) -> Result<Vec<Recommendation>> {
764        let mut diversified = Vec::new();
765        let mut selected_items = HashSet::new();
766        while !recommendations.is_empty() && diversified.len() < recommendations.len() {
767            let mut best_score = f64::NEG_INFINITY;
768            let mut best_idx = 0;
769            for (idx, rec) in recommendations.iter().enumerate() {
770                let relevance_score = rec.score;
771                let diversity_score = self.compute_diversity_score(rec.item_id, &selected_items)?;
772                let combined_score =
773                    (1.0 - diversity_weight) * relevance_score + diversity_weight * diversity_score;
774                if combined_score > best_score {
775                    best_score = combined_score;
776                    best_idx = idx;
777                }
778            }
779            let selected = recommendations.remove(best_idx);
780            selected_items.insert(selected.item_id);
781            diversified.push(selected);
782        }
783        Ok(diversified)
784    }
785    /// Compute diversity score
786    fn compute_diversity_score(
787        &self,
788        item_id: usize,
789        selected_items: &HashSet<usize>,
790    ) -> Result<f64> {
791        if selected_items.is_empty() {
792            return Ok(1.0);
793        }
794        let mut min_similarity: f64 = 1.0;
795        for &selected_id in selected_items {
796            let similarity =
797                self.engine
798                    .compute_similarity(item_id, selected_id, SimilarityType::ItemToItem)?;
799            min_similarity = min_similarity.min(similarity);
800        }
801        Ok(1.0 - min_similarity)
802    }
803    /// Generate explanation for recommendation
804    fn generate_explanation(
805        &self,
806        user_id: usize,
807        item_id: usize,
808    ) -> Result<RecommendationExplanation> {
809        let similar_users = self.find_similar_users_for_item(user_id, item_id, 5)?;
810        let similar_items = self.find_similar_items_to_history(user_id, item_id, 5)?;
811        let feature_reasons = self.extract_feature_reasons(user_id, item_id)?;
812        let quantum_state = Some(self.quantum_processor.get_state_info(user_id, item_id)?);
813        Ok(RecommendationExplanation {
814            similar_users,
815            similar_items,
816            feature_reasons,
817            quantum_state,
818        })
819    }
820    /// Find similar users who liked an item
821    fn find_similar_users_for_item(
822        &self,
823        user_id: usize,
824        item_id: usize,
825        n: usize,
826    ) -> Result<Vec<(usize, f64)>> {
827        let mut similar_users = Vec::new();
828        let item_users = self.interaction_matrix.get_item_users(item_id)?;
829        for &other_user in &item_users {
830            if other_user != user_id {
831                let similarity = self.engine.compute_similarity(
832                    user_id,
833                    other_user,
834                    SimilarityType::UserToUser,
835                )?;
836                similar_users.push((other_user, similarity));
837            }
838        }
839        similar_users.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
840        similar_users.truncate(n);
841        Ok(similar_users)
842    }
843    /// Find similar items to user's history
844    fn find_similar_items_to_history(
845        &self,
846        user_id: usize,
847        target_item: usize,
848        n: usize,
849    ) -> Result<Vec<(usize, f64)>> {
850        let mut similar_items = Vec::new();
851        if let Some(profile) = self.user_profiles.get(&user_id) {
852            for &(item_id, _) in &profile.preferences.rated_items {
853                if item_id != target_item {
854                    let similarity = self.engine.compute_similarity(
855                        target_item,
856                        item_id,
857                        SimilarityType::ItemToItem,
858                    )?;
859                    similar_items.push((item_id, similarity));
860                }
861            }
862        }
863        similar_items.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
864        similar_items.truncate(n);
865        Ok(similar_items)
866    }
867    /// Extract feature-based reasons
868    fn extract_feature_reasons(&self, user_id: usize, item_id: usize) -> Result<Vec<String>> {
869        let mut reasons = Vec::new();
870        if let (Some(user_profile), Some(item_features)) = (
871            self.user_profiles.get(&user_id),
872            self.item_features.get(&item_id),
873        ) {
874            for category in &item_features.categories {
875                if let Some(&pref_score) =
876                    user_profile.preferences.preferred_categories.get(category)
877                {
878                    if pref_score > 0.7 {
879                        reasons.push(format!("Matches your interest in {}", category));
880                    }
881                }
882            }
883            for (attr_name, attr_value) in &item_features.attributes {
884                match attr_value {
885                    AttributeValue::Categorical(val) => {
886                        reasons.push(format!("Features {}: {}", attr_name, val));
887                    }
888                    AttributeValue::Numeric(val) => {
889                        if *val > 0.8 {
890                            reasons.push(format!("High {} score", attr_name));
891                        }
892                    }
893                    _ => {}
894                }
895            }
896        }
897        Ok(reasons)
898    }
899    /// Apply business rules
900    fn apply_business_rules(
901        &self,
902        recommendations: Vec<Recommendation>,
903        rules: BusinessRules,
904    ) -> Result<Vec<Recommendation>> {
905        let mut filtered = recommendations;
906        if let Some(categories) = rules.required_categories {
907            filtered = self.filter_by_categories(filtered, categories)?;
908        }
909        if let Some(boost_new) = rules.boost_new_items {
910            filtered = self.boost_new_items(filtered, boost_new)?;
911        }
912        if let Some(max_price) = rules.max_price {
913            filtered = self.filter_by_price(filtered, max_price)?;
914        }
915        Ok(filtered)
916    }
917    /// Filter recommendations by categories
918    fn filter_by_categories(
919        &self,
920        recommendations: Vec<Recommendation>,
921        categories: HashSet<String>,
922    ) -> Result<Vec<Recommendation>> {
923        Ok(recommendations
924            .into_iter()
925            .filter(|rec| {
926                if let Some(features) = self.item_features.get(&rec.item_id) {
927                    features
928                        .categories
929                        .iter()
930                        .any(|cat| categories.contains(cat))
931                } else {
932                    false
933                }
934            })
935            .collect())
936    }
937    /// Boost new items
938    fn boost_new_items(
939        &self,
940        mut recommendations: Vec<Recommendation>,
941        boost_factor: f64,
942    ) -> Result<Vec<Recommendation>> {
943        for rec in &mut recommendations {
944            if self.is_new_item(rec.item_id)? {
945                rec.score *= boost_factor;
946            }
947        }
948        recommendations.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap());
949        Ok(recommendations)
950    }
951    /// Check if item is new
952    fn is_new_item(&self, item_id: usize) -> Result<bool> {
953        let num_ratings = self.interaction_matrix.get_item_rating_count(item_id)?;
954        Ok(num_ratings < 10)
955    }
956    /// Filter by price
957    fn filter_by_price(
958        &self,
959        recommendations: Vec<Recommendation>,
960        max_price: f64,
961    ) -> Result<Vec<Recommendation>> {
962        Ok(recommendations
963            .into_iter()
964            .filter(|rec| {
965                if let Some(features) = self.item_features.get(&rec.item_id) {
966                    if let Some(AttributeValue::Numeric(price)) = features.attributes.get("price") {
967                        *price <= max_price
968                    } else {
969                        true
970                    }
971                } else {
972                    true
973                }
974            })
975            .collect())
976    }
977    /// Evaluate on test data
978    pub fn evaluate(&self, test_data: &[(usize, usize, f64)]) -> Result<EvaluationMetrics> {
979        let mut predictions = Vec::new();
980        let mut actuals = Vec::new();
981        for &(user_id, item_id, rating) in test_data {
982            let prediction = self.predict(user_id, item_id)?;
983            predictions.push(prediction);
984            actuals.push(rating);
985        }
986        Ok(EvaluationMetrics::compute(&predictions, &actuals))
987    }
988    /// Get performance metrics
989    pub fn metrics(&self) -> &RecommenderMetrics {
990        &self.metrics
991    }
992}
993/// Preference history
994#[derive(Debug, Clone)]
995pub struct PreferenceHistory {
996    /// Rated items with scores
997    pub rated_items: Vec<(usize, f64)>,
998    /// Item categories
999    pub preferred_categories: HashMap<String, f64>,
1000    /// Temporal preferences
1001    pub temporal_patterns: TemporalPatterns,
1002    /// Interaction context
1003    pub contexts: Vec<InteractionContext>,
1004}
1005impl PreferenceHistory {
1006    /// Create new preference history
1007    pub fn new() -> Self {
1008        Self {
1009            rated_items: Vec::new(),
1010            preferred_categories: HashMap::new(),
1011            temporal_patterns: TemporalPatterns {
1012                hourly_distribution: Array1::zeros(24),
1013                weekly_distribution: Array1::zeros(7),
1014                seasonal_factors: Array1::zeros(4),
1015                trends: Vec::new(),
1016            },
1017            contexts: Vec::new(),
1018        }
1019    }
1020}
1021/// Quantum-specific metrics
1022#[derive(Debug, Clone)]
1023pub struct QuantumMetrics {
1024    /// Quantum advantage ratio
1025    pub quantum_advantage: f64,
1026    /// Entanglement utilization
1027    pub entanglement_utilization: f64,
1028    /// Coherence preservation
1029    pub coherence_preservation: f64,
1030    /// Circuit efficiency
1031    pub circuit_efficiency: f64,
1032}
1033/// Entanglement pattern types
1034#[derive(Debug, Clone)]
1035pub enum PatternType {
1036    /// Bell states
1037    Bell,
1038    /// GHZ states
1039    GHZ,
1040    /// Cluster states
1041    Cluster,
1042    /// Custom pattern
1043    Custom(Vec<f64>),
1044}
1045/// Quantum processor for recommendation computations
1046#[derive(Debug, Clone)]
1047pub struct QuantumProcessor {
1048    /// Number of qubits
1049    num_qubits: usize,
1050    /// Quantum circuits for similarity computation
1051    similarity_circuits: Vec<Vec<f64>>,
1052    /// Quantum neural network for embeddings
1053    embedding_network: QuantumNeuralNetwork,
1054    /// Entanglement generator
1055    entanglement_generator: EntanglementGenerator,
1056}
1057impl QuantumProcessor {
1058    /// Create new quantum processor
1059    pub fn new(num_qubits: usize) -> Result<Self> {
1060        let layers = vec![
1061            QNNLayerType::EncodingLayer { num_features: 128 },
1062            QNNLayerType::VariationalLayer { num_params: 64 },
1063            QNNLayerType::EntanglementLayer {
1064                connectivity: "circular".to_string(),
1065            },
1066            QNNLayerType::MeasurementLayer {
1067                measurement_basis: "computational".to_string(),
1068            },
1069        ];
1070        let embedding_network = QuantumNeuralNetwork::new(layers, num_qubits, 128, 64)?;
1071        let entanglement_generator = EntanglementGenerator::new(num_qubits);
1072        Ok(Self {
1073            num_qubits,
1074            similarity_circuits: Vec::new(),
1075            embedding_network,
1076            entanglement_generator,
1077        })
1078    }
1079    /// Compute quantum similarity
1080    pub fn compute_similarity(
1081        &self,
1082        vec1: &Array1<f64>,
1083        vec2: &Array1<f64>,
1084        measure: &SimilarityMeasure,
1085    ) -> Result<f64> {
1086        match measure {
1087            SimilarityMeasure::Cosine => {
1088                let dot = vec1.dot(vec2);
1089                let norm1 = vec1.dot(vec1).sqrt();
1090                let norm2 = vec2.dot(vec2).sqrt();
1091                Ok(dot / (norm1 * norm2 + 1e-10))
1092            }
1093            SimilarityMeasure::QuantumFidelity => {
1094                let state1 = self.encode_as_quantum_state(vec1)?;
1095                let state2 = self.encode_as_quantum_state(vec2)?;
1096                let fidelity = state1.dot(&state2).abs();
1097                Ok(fidelity * fidelity)
1098            }
1099            SimilarityMeasure::EntanglementSimilarity => {
1100                let entangled = self
1101                    .entanglement_generator
1102                    .create_entangled_state(vec1, vec2)?;
1103                let entanglement = self.measure_entanglement(&entangled)?;
1104                Ok(entanglement)
1105            }
1106            _ => Ok(0.5),
1107        }
1108    }
1109    /// Encode vector as quantum state
1110    fn encode_as_quantum_state(&self, vec: &Array1<f64>) -> Result<Array1<f64>> {
1111        let norm = vec.dot(vec).sqrt();
1112        let normalized = vec / (norm + 1e-10);
1113        let quantum_dim = 2_usize.pow(self.num_qubits as u32);
1114        let mut quantum_state = Array1::zeros(quantum_dim);
1115        for i in 0..normalized.len().min(quantum_dim) {
1116            quantum_state[i] = normalized[i];
1117        }
1118        Ok(quantum_state)
1119    }
1120    /// Measure entanglement
1121    fn measure_entanglement(&self, state: &Array1<f64>) -> Result<f64> {
1122        let entropy = -state
1123            .iter()
1124            .filter(|&&x| x.abs() > 1e-10)
1125            .map(|&x| {
1126                let p = x * x;
1127                p * p.ln()
1128            })
1129            .sum::<f64>();
1130        Ok((entropy / (self.num_qubits as f64).ln()).min(1.0))
1131    }
1132    /// Compute quantum gradient
1133    pub fn compute_gradient(
1134        &self,
1135        user_id: usize,
1136        item_id: usize,
1137        error: f64,
1138    ) -> Result<Array1<f64>> {
1139        let gradient_dim = 64;
1140        let mut gradient = Array1::zeros(gradient_dim);
1141        for i in 0..gradient_dim {
1142            gradient[i] = error
1143                * (0.1 * (i as f64 * 0.1 + user_id as f64 * 0.01 + item_id as f64 * 0.001).sin());
1144        }
1145        Ok(gradient)
1146    }
1147    /// Get quantum state information
1148    pub fn get_state_info(&self, user_id: usize, item_id: usize) -> Result<QuantumStateInfo> {
1149        Ok(QuantumStateInfo {
1150            entanglement: 0.7 + 0.3 * (user_id as f64 * 0.1).sin(),
1151            superposition_weights: vec![0.5, 0.3, 0.2],
1152            phase: PI * (item_id as f64 * 0.01).sin(),
1153        })
1154    }
1155}
1156/// Recommendation options
1157#[derive(Debug, Clone)]
1158pub struct RecommendationOptions {
1159    /// Exclude already seen items
1160    pub exclude_seen: bool,
1161    /// Diversify recommendations
1162    pub diversify: bool,
1163    /// Diversity weight (0-1)
1164    pub diversity_weight: f64,
1165    /// Include explanations
1166    pub explain: bool,
1167    /// Business rules to apply
1168    pub business_rules: Option<BusinessRules>,
1169}
1170/// Model parameters
1171#[derive(Debug, Clone)]
1172pub struct ModelParameters {
1173    /// User embeddings
1174    pub user_embeddings: Array2<f64>,
1175    /// Item embeddings
1176    pub item_embeddings: Array2<f64>,
1177    /// Quantum circuit parameters
1178    pub quantum_params: Vec<f64>,
1179    /// Bias terms
1180    pub user_bias: Array1<f64>,
1181    pub item_bias: Array1<f64>,
1182    pub global_bias: f64,
1183}
1184impl ModelParameters {
1185    /// Create new model parameters
1186    pub fn new(num_users: usize, num_items: usize, num_factors: usize) -> Self {
1187        Self {
1188            user_embeddings: Array2::from_shape_fn((num_users, num_factors), |(_, _)| {
1189                0.01 * (fastrand::f64() - 0.5)
1190            }),
1191            item_embeddings: Array2::from_shape_fn((num_items, num_factors), |(_, _)| {
1192                0.01 * (fastrand::f64() - 0.5)
1193            }),
1194            quantum_params: vec![0.0; num_factors * 10],
1195            user_bias: Array1::zeros(num_users),
1196            item_bias: Array1::zeros(num_items),
1197            global_bias: 3.5,
1198        }
1199    }
1200    /// Get user embedding
1201    pub fn get_user_embedding(&self, user_id: usize) -> Result<Array1<f64>> {
1202        if user_id < self.user_embeddings.nrows() {
1203            Ok(self.user_embeddings.row(user_id).to_owned())
1204        } else {
1205            Ok(Array1::zeros(self.user_embeddings.ncols()))
1206        }
1207    }
1208    /// Get item embedding
1209    pub fn get_item_embedding(&self, item_id: usize) -> Result<Array1<f64>> {
1210        if item_id < self.item_embeddings.nrows() {
1211            Ok(self.item_embeddings.row(item_id).to_owned())
1212        } else {
1213            Ok(Array1::zeros(self.item_embeddings.ncols()))
1214        }
1215    }
1216    /// Update embeddings
1217    pub fn update_embeddings(
1218        &mut self,
1219        user_id: usize,
1220        item_id: usize,
1221        gradient: &Array1<f64>,
1222        lr: f64,
1223        reg: f64,
1224    ) -> Result<()> {
1225        if user_id < self.user_embeddings.nrows() && item_id < self.item_embeddings.nrows() {
1226            let user_emb = self.user_embeddings.row(user_id).to_owned();
1227            let item_emb = self.item_embeddings.row(item_id).to_owned();
1228            self.user_embeddings
1229                .row_mut(user_id)
1230                .zip_mut_with(&user_emb, |param, &old| {
1231                    *param = old + lr * (gradient[0] - reg * old);
1232                });
1233            self.item_embeddings
1234                .row_mut(item_id)
1235                .zip_mut_with(&item_emb, |param, &old| {
1236                    *param = old + lr * (gradient[0] - reg * old);
1237                });
1238        }
1239        Ok(())
1240    }
1241}
1242/// Quantum collaborative filtering engine
1243#[derive(Debug, Clone)]
1244pub struct QuantumCFEngine {
1245    config: QuantumRecommenderConfig,
1246    similarity_cache: HashMap<(usize, usize), f64>,
1247    pub parameters: Array1<f64>,
1248}
1249impl QuantumCFEngine {
1250    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1251        Ok(Self {
1252            config: config.clone(),
1253            similarity_cache: HashMap::new(),
1254            parameters: Array1::zeros(100),
1255        })
1256    }
1257}
1258/// Attribute value types
1259#[derive(Debug, Clone)]
1260pub enum AttributeValue {
1261    Numeric(f64),
1262    Categorical(String),
1263    Binary(bool),
1264    Vector(Vec<f64>),
1265}
1266/// Recommender performance metrics
1267#[derive(Debug, Clone)]
1268pub struct RecommenderMetrics {
1269    /// Recommendation accuracy metrics
1270    pub accuracy_metrics: AccuracyMetrics,
1271    /// Diversity metrics
1272    pub diversity_metrics: DiversityMetrics,
1273    /// Coverage metrics
1274    pub coverage_metrics: CoverageMetrics,
1275    /// Quantum metrics
1276    pub quantum_metrics: QuantumMetrics,
1277}
1278impl RecommenderMetrics {
1279    /// Create new metrics
1280    pub fn new() -> Self {
1281        Self {
1282            accuracy_metrics: AccuracyMetrics {
1283                mae: 0.0,
1284                rmse: 0.0,
1285                precision_at_k: HashMap::new(),
1286                recall_at_k: HashMap::new(),
1287                ndcg_at_k: HashMap::new(),
1288            },
1289            diversity_metrics: DiversityMetrics {
1290                intra_list_diversity: 0.0,
1291                inter_list_diversity: 0.0,
1292                category_coverage: 0.0,
1293                novelty: 0.0,
1294            },
1295            coverage_metrics: CoverageMetrics {
1296                item_coverage: 0.0,
1297                user_coverage: 0.0,
1298                cold_start_performance: 0.0,
1299            },
1300            quantum_metrics: QuantumMetrics {
1301                quantum_advantage: 1.0,
1302                entanglement_utilization: 0.0,
1303                coherence_preservation: 0.0,
1304                circuit_efficiency: 0.0,
1305            },
1306        }
1307    }
1308}
1309/// Feature extraction methods for content-based filtering
1310#[derive(Debug, Clone)]
1311pub enum FeatureExtractionMethod {
1312    /// TF-IDF with quantum enhancement
1313    QuantumTFIDF,
1314    /// Circuit depth-based features
1315    CircuitDepth,
1316    /// Quantum embeddings
1317    QuantumEmbeddings { embedding_dim: usize },
1318    /// Quantum autoencoders
1319    QuantumAutoencoder { latent_dim: usize },
1320    /// Deep quantum features
1321    DeepQuantumFeatures { layer_dims: Vec<usize> },
1322}
1323/// Hybrid quantum engine
1324#[derive(Debug, Clone)]
1325pub struct HybridQuantumEngine {
1326    config: QuantumRecommenderConfig,
1327    pub parameters: Array1<f64>,
1328}
1329impl HybridQuantumEngine {
1330    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1331        Ok(Self {
1332            config: config.clone(),
1333            parameters: Array1::zeros(100),
1334        })
1335    }
1336}
1337/// Quantum graph-based engine
1338#[derive(Debug, Clone)]
1339pub struct QuantumGraphEngine {
1340    config: QuantumRecommenderConfig,
1341    pub parameters: Array1<f64>,
1342}
1343impl QuantumGraphEngine {
1344    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1345        Ok(Self {
1346            config: config.clone(),
1347            parameters: Array1::zeros(100),
1348        })
1349    }
1350}
1351/// Quantum state information for explanations
1352#[derive(Debug, Clone)]
1353pub struct QuantumStateInfo {
1354    /// Entanglement measure
1355    pub entanglement: f64,
1356    /// Superposition weights
1357    pub superposition_weights: Vec<f64>,
1358    /// Quantum phase
1359    pub phase: f64,
1360}
1361/// Profile metadata
1362#[derive(Debug, Clone)]
1363pub struct ProfileMetadata {
1364    /// Profile creation time
1365    pub created_at: f64,
1366    /// Last update time
1367    pub updated_at: f64,
1368    /// Number of interactions
1369    pub num_interactions: usize,
1370    /// Profile completeness
1371    pub completeness: f64,
1372}
1373/// Quantum content-based engine
1374#[derive(Debug, Clone)]
1375pub struct QuantumCBEngine {
1376    config: QuantumRecommenderConfig,
1377    pub parameters: Array1<f64>,
1378}
1379impl QuantumCBEngine {
1380    fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1381        Ok(Self {
1382            config: config.clone(),
1383            parameters: Array1::zeros(100),
1384        })
1385    }
1386}
1387
1388#[cfg(test)]
1389mod tests {
1390    use super::*;
1391    #[test]
1392    fn test_recommender_creation() {
1393        let config = QuantumRecommenderConfig::default();
1394        let recommender = QuantumRecommender::new(config).unwrap();
1395        assert!(recommender.user_profiles.is_empty());
1396    }
1397    #[test]
1398    fn test_add_interaction() {
1399        let config = QuantumRecommenderConfig::default();
1400        let mut recommender = QuantumRecommender::new(config).unwrap();
1401        recommender.add_interaction(1, 10, 4.5, None).unwrap();
1402        assert_eq!(recommender.user_profiles.len(), 1);
1403    }
1404    #[test]
1405    fn test_recommendations() {
1406        let config = QuantumRecommenderConfig::default();
1407        let mut recommender = QuantumRecommender::new(config).unwrap();
1408        recommender.add_interaction(1, 10, 4.5, None).unwrap();
1409        recommender.add_interaction(1, 20, 3.5, None).unwrap();
1410        let options = RecommendationOptions::default();
1411        let recommendations = recommender.recommend(1, 5, options).unwrap();
1412        assert_eq!(recommendations.len(), 5);
1413        assert!(recommendations[0].score >= recommendations[1].score);
1414    }
1415    #[test]
1416    fn test_similarity_measures() {
1417        let processor = QuantumProcessor::new(8).unwrap();
1418        let vec1 = Array1::from_vec(vec![1.0, 0.0, 0.0, 0.0]);
1419        let vec2 = Array1::from_vec(vec![0.0, 1.0, 0.0, 0.0]);
1420        let cosine_sim = processor
1421            .compute_similarity(&vec1, &vec2, &SimilarityMeasure::Cosine)
1422            .unwrap();
1423        assert!(cosine_sim.abs() < 1e-10);
1424    }
1425}