1use 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
16pub trait RecommendationEngine: std::fmt::Debug {
18 fn recommend(
20 &self,
21 user_id: usize,
22 n_items: usize,
23 exclude_seen: bool,
24 ) -> Result<Vec<Recommendation>>;
25 fn update(&mut self, user_id: usize, item_id: usize, rating: f64) -> Result<()>;
27 fn compute_similarity(
29 &self,
30 id1: usize,
31 id2: usize,
32 similarity_type: SimilarityType,
33 ) -> Result<f64>;
34 fn parameters(&self) -> &Array1<f64>;
36 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 pub item_id: usize,
49 pub features: Array1<f64>,
51 pub categories: Vec<String>,
53 pub attributes: HashMap<String, AttributeValue>,
55 pub quantum_features: Array1<f64>,
57}
58#[derive(Debug, Clone)]
60pub struct QuantumRecommenderConfig {
61 pub num_qubits: usize,
63 pub algorithm: RecommendationAlgorithm,
65 pub num_factors: usize,
67 pub regularization: f64,
69 pub learning_rate: f64,
71 pub quantum_enhancement: QuantumEnhancementLevel,
73 pub similarity_measure: SimilarityMeasure,
75}
76impl QuantumRecommenderConfig {
77 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 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 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 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#[derive(Debug, Clone)]
146pub struct RecommendationExplanation {
147 pub similar_users: Vec<(usize, f64)>,
149 pub similar_items: Vec<(usize, f64)>,
151 pub feature_reasons: Vec<String>,
153 pub quantum_state: Option<QuantumStateInfo>,
155}
156#[derive(Debug, Clone)]
158pub enum SimilarityType {
159 UserToUser,
160 ItemToItem,
161 UserToItem,
162}
163#[derive(Debug, Clone)]
165pub struct TrendIndicator {
166 pub feature: String,
168 pub direction: f64,
170 pub strength: f64,
172 pub window: f64,
174}
175#[derive(Debug, Clone)]
177pub enum RecommendationAlgorithm {
178 QuantumCollaborativeFiltering {
180 neighborhood_size: usize,
181 min_common_items: usize,
182 },
183 QuantumMatrixFactorization {
185 optimization_method: OptimizationMethod,
186 num_iterations: usize,
187 },
188 QuantumContentBased {
190 feature_extraction: FeatureExtractionMethod,
191 profile_learning: ProfileLearningMethod,
192 },
193 HybridQuantum {
195 cf_weight: f64,
196 cb_weight: f64,
197 knowledge_weight: f64,
198 },
199 QuantumNeuralCF {
201 embedding_dim: usize,
202 hidden_layers: Vec<usize>,
203 },
204 QuantumGraphRecommender {
206 walk_length: usize,
207 num_walks: usize,
208 teleportation_prob: f64,
209 },
210}
211#[derive(Debug, Clone)]
213pub struct InteractionMatrix {
214 ratings: HashMap<(usize, usize), f64>,
216 user_ids: HashSet<usize>,
218 item_ids: HashSet<usize>,
220 implicit_feedback: Option<HashMap<(usize, usize), ImplicitFeedback>>,
222 timestamps: Option<HashMap<(usize, usize), f64>>,
224}
225impl InteractionMatrix {
226 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 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 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 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#[derive(Debug, Clone)]
269pub struct Recommendation {
270 pub item_id: usize,
272 pub score: f64,
274 pub confidence: (f64, f64),
276 pub explanation: Option<RecommendationExplanation>,
278 pub quantum_contribution: f64,
280}
281#[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#[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#[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#[derive(Debug, Clone)]
350pub struct InteractionContext {
351 pub device: String,
353 pub location_cluster: usize,
355 pub session_duration: f64,
357 pub action_sequence: Vec<String>,
359}
360#[derive(Debug, Clone)]
362pub struct ImplicitFeedback {
363 pub views: usize,
365 pub duration: f64,
367 pub ctr: f64,
369 pub converted: bool,
371}
372#[derive(Debug, Clone)]
374pub struct EntanglementGenerator {
375 patterns: Vec<EntanglementPattern>,
377 circuit_params: Vec<f64>,
379}
380impl EntanglementGenerator {
381 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 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#[derive(Debug, Clone)]
424pub struct DiversityMetrics {
425 pub intra_list_diversity: f64,
427 pub inter_list_diversity: f64,
429 pub category_coverage: f64,
431 pub novelty: f64,
433}
434#[derive(Debug, Clone)]
436pub struct TemporalPatterns {
437 pub hourly_distribution: Array1<f64>,
439 pub weekly_distribution: Array1<f64>,
441 pub seasonal_factors: Array1<f64>,
443 pub trends: Vec<TrendIndicator>,
445}
446#[derive(Debug, Clone)]
448pub struct BusinessRules {
449 pub required_categories: Option<HashSet<String>>,
451 pub boost_new_items: Option<f64>,
453 pub max_price: Option<f64>,
455}
456#[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#[derive(Debug, Clone)]
472pub enum SimilarityMeasure {
473 Cosine,
475 Pearson,
477 QuantumFidelity,
479 EntanglementSimilarity,
481 Hybrid {
483 classical_weight: f64,
484 quantum_weight: f64,
485 },
486}
487#[derive(Debug, Clone)]
489pub struct UserProfile {
490 pub user_id: usize,
492 pub features: Array1<f64>,
494 pub preferences: PreferenceHistory,
496 pub quantum_state: Array1<f64>,
498 pub metadata: ProfileMetadata,
500}
501impl UserProfile {
502 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 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#[derive(Debug, Clone)]
534pub struct EntanglementPattern {
535 pub qubit_pairs: Vec<(usize, usize)>,
537 pub strength: f64,
539 pub pattern_type: PatternType,
541}
542#[derive(Debug, Clone)]
544pub struct AccuracyMetrics {
545 pub mae: f64,
547 pub rmse: f64,
549 pub precision_at_k: HashMap<usize, f64>,
551 pub recall_at_k: HashMap<usize, f64>,
553 pub ndcg_at_k: HashMap<usize, f64>,
555}
556#[derive(Debug, Clone)]
558pub struct CoverageMetrics {
559 pub item_coverage: f64,
561 pub user_coverage: f64,
563 pub cold_start_performance: f64,
565}
566#[derive(Debug, Clone)]
568pub enum ProfileLearningMethod {
569 WeightedAverage,
571 QuantumSuperposition,
573 AdaptiveQuantum { learning_rate: f64 },
575}
576#[derive(Debug, Clone)]
578pub enum QuantumEnhancementLevel {
579 Low,
581 Medium,
583 High,
585 Custom {
587 entanglement_strength: f64,
588 coherence_time: f64,
589 circuit_depth: usize,
590 },
591}
592#[derive(Debug, Clone)]
594pub struct QuantumRecommender {
595 config: QuantumRecommenderConfig,
597 interaction_matrix: InteractionMatrix,
599 quantum_processor: QuantumProcessor,
601 engine: Box<dyn RecommendationEngine>,
603 pub user_profiles: HashMap<usize, UserProfile>,
605 item_features: HashMap<usize, ItemFeatures>,
607 parameters: ModelParameters,
609 metrics: RecommenderMetrics,
611}
612impl QuantumRecommender {
613 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn metrics(&self) -> &RecommenderMetrics {
990 &self.metrics
991 }
992}
993#[derive(Debug, Clone)]
995pub struct PreferenceHistory {
996 pub rated_items: Vec<(usize, f64)>,
998 pub preferred_categories: HashMap<String, f64>,
1000 pub temporal_patterns: TemporalPatterns,
1002 pub contexts: Vec<InteractionContext>,
1004}
1005impl PreferenceHistory {
1006 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#[derive(Debug, Clone)]
1023pub struct QuantumMetrics {
1024 pub quantum_advantage: f64,
1026 pub entanglement_utilization: f64,
1028 pub coherence_preservation: f64,
1030 pub circuit_efficiency: f64,
1032}
1033#[derive(Debug, Clone)]
1035pub enum PatternType {
1036 Bell,
1038 GHZ,
1040 Cluster,
1042 Custom(Vec<f64>),
1044}
1045#[derive(Debug, Clone)]
1047pub struct QuantumProcessor {
1048 num_qubits: usize,
1050 similarity_circuits: Vec<Vec<f64>>,
1052 embedding_network: QuantumNeuralNetwork,
1054 entanglement_generator: EntanglementGenerator,
1056}
1057impl QuantumProcessor {
1058 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 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 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 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 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 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#[derive(Debug, Clone)]
1158pub struct RecommendationOptions {
1159 pub exclude_seen: bool,
1161 pub diversify: bool,
1163 pub diversity_weight: f64,
1165 pub explain: bool,
1167 pub business_rules: Option<BusinessRules>,
1169}
1170#[derive(Debug, Clone)]
1172pub struct ModelParameters {
1173 pub user_embeddings: Array2<f64>,
1175 pub item_embeddings: Array2<f64>,
1177 pub quantum_params: Vec<f64>,
1179 pub user_bias: Array1<f64>,
1181 pub item_bias: Array1<f64>,
1182 pub global_bias: f64,
1183}
1184impl ModelParameters {
1185 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 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 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 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#[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#[derive(Debug, Clone)]
1260pub enum AttributeValue {
1261 Numeric(f64),
1262 Categorical(String),
1263 Binary(bool),
1264 Vector(Vec<f64>),
1265}
1266#[derive(Debug, Clone)]
1268pub struct RecommenderMetrics {
1269 pub accuracy_metrics: AccuracyMetrics,
1271 pub diversity_metrics: DiversityMetrics,
1273 pub coverage_metrics: CoverageMetrics,
1275 pub quantum_metrics: QuantumMetrics,
1277}
1278impl RecommenderMetrics {
1279 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#[derive(Debug, Clone)]
1311pub enum FeatureExtractionMethod {
1312 QuantumTFIDF,
1314 CircuitDepth,
1316 QuantumEmbeddings { embedding_dim: usize },
1318 QuantumAutoencoder { latent_dim: usize },
1320 DeepQuantumFeatures { layer_dims: Vec<usize> },
1322}
1323#[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#[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#[derive(Debug, Clone)]
1353pub struct QuantumStateInfo {
1354 pub entanglement: f64,
1356 pub superposition_weights: Vec<f64>,
1358 pub phase: f64,
1360}
1361#[derive(Debug, Clone)]
1363pub struct ProfileMetadata {
1364 pub created_at: f64,
1366 pub updated_at: f64,
1368 pub num_interactions: usize,
1370 pub completeness: f64,
1372}
1373#[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}