1use crate::error::{MLError, Result};
8use crate::optimization::OptimizationMethod;
9use crate::qnn::{QNNLayerType, QuantumNeuralNetwork};
10use ndarray::{s, Array1, Array2, Array3, Axis};
11use std::collections::{HashMap, HashSet};
12use std::f64::consts::PI;
13
14#[derive(Debug, Clone)]
16pub struct QuantumRecommenderConfig {
17 pub num_qubits: usize,
19
20 pub algorithm: RecommendationAlgorithm,
22
23 pub num_factors: usize,
25
26 pub regularization: f64,
28
29 pub learning_rate: f64,
31
32 pub quantum_enhancement: QuantumEnhancementLevel,
34
35 pub similarity_measure: SimilarityMeasure,
37}
38
39#[derive(Debug, Clone)]
41pub enum RecommendationAlgorithm {
42 QuantumCollaborativeFiltering {
44 neighborhood_size: usize,
45 min_common_items: usize,
46 },
47
48 QuantumMatrixFactorization {
50 optimization_method: OptimizationMethod,
51 num_iterations: usize,
52 },
53
54 QuantumContentBased {
56 feature_extraction: FeatureExtractionMethod,
57 profile_learning: ProfileLearningMethod,
58 },
59
60 HybridQuantum {
62 cf_weight: f64,
63 cb_weight: f64,
64 knowledge_weight: f64,
65 },
66
67 QuantumNeuralCF {
69 embedding_dim: usize,
70 hidden_layers: Vec<usize>,
71 },
72
73 QuantumGraphRecommender {
75 walk_length: usize,
76 num_walks: usize,
77 teleportation_prob: f64,
78 },
79}
80
81#[derive(Debug, Clone)]
83pub enum FeatureExtractionMethod {
84 QuantumTFIDF,
86
87 CircuitDepth,
89
90 QuantumEmbeddings { embedding_dim: usize },
92
93 QuantumAutoencoder { latent_dim: usize },
95
96 DeepQuantumFeatures { layer_dims: Vec<usize> },
98}
99
100#[derive(Debug, Clone)]
102pub enum ProfileLearningMethod {
103 WeightedAverage,
105
106 QuantumSuperposition,
108
109 AdaptiveQuantum { learning_rate: f64 },
111}
112
113#[derive(Debug, Clone)]
115pub enum QuantumEnhancementLevel {
116 Low,
118
119 Medium,
121
122 High,
124
125 Custom {
127 entanglement_strength: f64,
128 coherence_time: f64,
129 circuit_depth: usize,
130 },
131}
132
133#[derive(Debug, Clone)]
135pub enum SimilarityMeasure {
136 Cosine,
138
139 Pearson,
141
142 QuantumFidelity,
144
145 EntanglementSimilarity,
147
148 Hybrid {
150 classical_weight: f64,
151 quantum_weight: f64,
152 },
153}
154
155#[derive(Debug, Clone)]
157pub struct QuantumRecommender {
158 config: QuantumRecommenderConfig,
160
161 interaction_matrix: InteractionMatrix,
163
164 quantum_processor: QuantumProcessor,
166
167 engine: Box<dyn RecommendationEngine>,
169
170 user_profiles: HashMap<usize, UserProfile>,
172
173 item_features: HashMap<usize, ItemFeatures>,
175
176 parameters: ModelParameters,
178
179 metrics: RecommenderMetrics,
181}
182
183#[derive(Debug, Clone)]
185pub struct InteractionMatrix {
186 ratings: HashMap<(usize, usize), f64>,
188
189 user_ids: HashSet<usize>,
191
192 item_ids: HashSet<usize>,
194
195 implicit_feedback: Option<HashMap<(usize, usize), ImplicitFeedback>>,
197
198 timestamps: Option<HashMap<(usize, usize), f64>>,
200}
201
202#[derive(Debug, Clone)]
204pub struct ImplicitFeedback {
205 pub views: usize,
207
208 pub duration: f64,
210
211 pub ctr: f64,
213
214 pub converted: bool,
216}
217
218#[derive(Debug, Clone)]
220pub struct QuantumProcessor {
221 num_qubits: usize,
223
224 similarity_circuits: Vec<Vec<f64>>,
226
227 embedding_network: QuantumNeuralNetwork,
229
230 entanglement_generator: EntanglementGenerator,
232}
233
234pub trait RecommendationEngine: std::fmt::Debug {
236 fn recommend(
238 &self,
239 user_id: usize,
240 n_items: usize,
241 exclude_seen: bool,
242 ) -> Result<Vec<Recommendation>>;
243
244 fn update(&mut self, user_id: usize, item_id: usize, rating: f64) -> Result<()>;
246
247 fn compute_similarity(
249 &self,
250 id1: usize,
251 id2: usize,
252 similarity_type: SimilarityType,
253 ) -> Result<f64>;
254
255 fn parameters(&self) -> &Array1<f64>;
257
258 fn clone_box(&self) -> Box<dyn RecommendationEngine>;
260}
261
262impl Clone for Box<dyn RecommendationEngine> {
263 fn clone(&self) -> Self {
264 self.clone_box()
265 }
266}
267
268#[derive(Debug, Clone)]
270pub enum SimilarityType {
271 UserToUser,
272 ItemToItem,
273 UserToItem,
274}
275
276#[derive(Debug, Clone)]
278pub struct Recommendation {
279 pub item_id: usize,
281
282 pub score: f64,
284
285 pub confidence: (f64, f64),
287
288 pub explanation: Option<RecommendationExplanation>,
290
291 pub quantum_contribution: f64,
293}
294
295#[derive(Debug, Clone)]
297pub struct RecommendationExplanation {
298 pub similar_users: Vec<(usize, f64)>,
300
301 pub similar_items: Vec<(usize, f64)>,
303
304 pub feature_reasons: Vec<String>,
306
307 pub quantum_state: Option<QuantumStateInfo>,
309}
310
311#[derive(Debug, Clone)]
313pub struct QuantumStateInfo {
314 pub entanglement: f64,
316
317 pub superposition_weights: Vec<f64>,
319
320 pub phase: f64,
322}
323
324#[derive(Debug, Clone)]
326pub struct UserProfile {
327 pub user_id: usize,
329
330 pub features: Array1<f64>,
332
333 pub preferences: PreferenceHistory,
335
336 pub quantum_state: Array1<f64>,
338
339 pub metadata: ProfileMetadata,
341}
342
343#[derive(Debug, Clone)]
345pub struct PreferenceHistory {
346 pub rated_items: Vec<(usize, f64)>,
348
349 pub preferred_categories: HashMap<String, f64>,
351
352 pub temporal_patterns: TemporalPatterns,
354
355 pub contexts: Vec<InteractionContext>,
357}
358
359#[derive(Debug, Clone)]
361pub struct TemporalPatterns {
362 pub hourly_distribution: Array1<f64>,
364
365 pub weekly_distribution: Array1<f64>,
367
368 pub seasonal_factors: Array1<f64>,
370
371 pub trends: Vec<TrendIndicator>,
373}
374
375#[derive(Debug, Clone)]
377pub struct TrendIndicator {
378 pub feature: String,
380
381 pub direction: f64,
383
384 pub strength: f64,
386
387 pub window: f64,
389}
390
391#[derive(Debug, Clone)]
393pub struct InteractionContext {
394 pub device: String,
396
397 pub location_cluster: usize,
399
400 pub session_duration: f64,
402
403 pub action_sequence: Vec<String>,
405}
406
407#[derive(Debug, Clone)]
409pub struct ProfileMetadata {
410 pub created_at: f64,
412
413 pub updated_at: f64,
415
416 pub num_interactions: usize,
418
419 pub completeness: f64,
421}
422
423#[derive(Debug, Clone)]
425pub struct ItemFeatures {
426 pub item_id: usize,
428
429 pub features: Array1<f64>,
431
432 pub categories: Vec<String>,
434
435 pub attributes: HashMap<String, AttributeValue>,
437
438 pub quantum_features: Array1<f64>,
440}
441
442#[derive(Debug, Clone)]
444pub enum AttributeValue {
445 Numeric(f64),
446 Categorical(String),
447 Binary(bool),
448 Vector(Vec<f64>),
449}
450
451#[derive(Debug, Clone)]
453pub struct ModelParameters {
454 pub user_embeddings: Array2<f64>,
456
457 pub item_embeddings: Array2<f64>,
459
460 pub quantum_params: Vec<f64>,
462
463 pub user_bias: Array1<f64>,
465 pub item_bias: Array1<f64>,
466 pub global_bias: f64,
467}
468
469#[derive(Debug, Clone)]
471pub struct EntanglementGenerator {
472 patterns: Vec<EntanglementPattern>,
474
475 circuit_params: Vec<f64>,
477}
478
479#[derive(Debug, Clone)]
481pub struct EntanglementPattern {
482 pub qubit_pairs: Vec<(usize, usize)>,
484
485 pub strength: f64,
487
488 pub pattern_type: PatternType,
490}
491
492#[derive(Debug, Clone)]
494pub enum PatternType {
495 Bell,
497
498 GHZ,
500
501 Cluster,
503
504 Custom(Vec<f64>),
506}
507
508#[derive(Debug, Clone)]
510pub struct RecommenderMetrics {
511 pub accuracy_metrics: AccuracyMetrics,
513
514 pub diversity_metrics: DiversityMetrics,
516
517 pub coverage_metrics: CoverageMetrics,
519
520 pub quantum_metrics: QuantumMetrics,
522}
523
524#[derive(Debug, Clone)]
526pub struct AccuracyMetrics {
527 pub mae: f64,
529
530 pub rmse: f64,
532
533 pub precision_at_k: HashMap<usize, f64>,
535
536 pub recall_at_k: HashMap<usize, f64>,
538
539 pub ndcg_at_k: HashMap<usize, f64>,
541}
542
543#[derive(Debug, Clone)]
545pub struct DiversityMetrics {
546 pub intra_list_diversity: f64,
548
549 pub inter_list_diversity: f64,
551
552 pub category_coverage: f64,
554
555 pub novelty: f64,
557}
558
559#[derive(Debug, Clone)]
561pub struct CoverageMetrics {
562 pub item_coverage: f64,
564
565 pub user_coverage: f64,
567
568 pub cold_start_performance: f64,
570}
571
572#[derive(Debug, Clone)]
574pub struct QuantumMetrics {
575 pub quantum_advantage: f64,
577
578 pub entanglement_utilization: f64,
580
581 pub coherence_preservation: f64,
583
584 pub circuit_efficiency: f64,
586}
587
588impl QuantumRecommenderConfig {
589 pub fn default() -> Self {
591 Self {
592 num_qubits: 10,
593 algorithm: RecommendationAlgorithm::QuantumMatrixFactorization {
594 optimization_method: OptimizationMethod::Adam,
595 num_iterations: 100,
596 },
597 num_factors: 50,
598 regularization: 0.01,
599 learning_rate: 0.001,
600 quantum_enhancement: QuantumEnhancementLevel::Medium,
601 similarity_measure: SimilarityMeasure::QuantumFidelity,
602 }
603 }
604
605 pub fn collaborative_filtering() -> Self {
607 Self {
608 num_qubits: 12,
609 algorithm: RecommendationAlgorithm::QuantumCollaborativeFiltering {
610 neighborhood_size: 50,
611 min_common_items: 3,
612 },
613 num_factors: 100,
614 regularization: 0.001,
615 learning_rate: 0.01,
616 quantum_enhancement: QuantumEnhancementLevel::High,
617 similarity_measure: SimilarityMeasure::EntanglementSimilarity,
618 }
619 }
620
621 pub fn content_based() -> Self {
623 Self {
624 num_qubits: 10,
625 algorithm: RecommendationAlgorithm::QuantumContentBased {
626 feature_extraction: FeatureExtractionMethod::QuantumEmbeddings {
627 embedding_dim: 128,
628 },
629 profile_learning: ProfileLearningMethod::QuantumSuperposition,
630 },
631 num_factors: 64,
632 regularization: 0.005,
633 learning_rate: 0.005,
634 quantum_enhancement: QuantumEnhancementLevel::Medium,
635 similarity_measure: SimilarityMeasure::Cosine,
636 }
637 }
638
639 pub fn hybrid() -> Self {
641 Self {
642 num_qubits: 14,
643 algorithm: RecommendationAlgorithm::HybridQuantum {
644 cf_weight: 0.6,
645 cb_weight: 0.3,
646 knowledge_weight: 0.1,
647 },
648 num_factors: 80,
649 regularization: 0.01,
650 learning_rate: 0.001,
651 quantum_enhancement: QuantumEnhancementLevel::High,
652 similarity_measure: SimilarityMeasure::Hybrid {
653 classical_weight: 0.4,
654 quantum_weight: 0.6,
655 },
656 }
657 }
658}
659
660impl QuantumRecommender {
661 pub fn new(config: QuantumRecommenderConfig) -> Result<Self> {
663 let interaction_matrix = InteractionMatrix::new();
665
666 let quantum_processor = QuantumProcessor::new(config.num_qubits)?;
668
669 let engine: Box<dyn RecommendationEngine> = match &config.algorithm {
671 RecommendationAlgorithm::QuantumCollaborativeFiltering { .. } => {
672 Box::new(QuantumCFEngine::new(&config)?)
673 }
674 RecommendationAlgorithm::QuantumMatrixFactorization { .. } => {
675 Box::new(QuantumMFEngine::new(&config)?)
676 }
677 RecommendationAlgorithm::QuantumContentBased { .. } => {
678 Box::new(QuantumCBEngine::new(&config)?)
679 }
680 RecommendationAlgorithm::HybridQuantum { .. } => {
681 Box::new(HybridQuantumEngine::new(&config)?)
682 }
683 RecommendationAlgorithm::QuantumNeuralCF { .. } => {
684 Box::new(QuantumNCFEngine::new(&config)?)
685 }
686 RecommendationAlgorithm::QuantumGraphRecommender { .. } => {
687 Box::new(QuantumGraphEngine::new(&config)?)
688 }
689 };
690
691 let parameters = ModelParameters::new(1000, 1000, config.num_factors);
693
694 Ok(Self {
695 config,
696 interaction_matrix,
697 quantum_processor,
698 engine,
699 user_profiles: HashMap::new(),
700 item_features: HashMap::new(),
701 parameters,
702 metrics: RecommenderMetrics::new(),
703 })
704 }
705
706 pub fn add_interaction(
708 &mut self,
709 user_id: usize,
710 item_id: usize,
711 rating: f64,
712 context: Option<InteractionContext>,
713 ) -> Result<()> {
714 self.interaction_matrix.add_rating(user_id, item_id, rating);
716
717 if let Some(profile) = self.user_profiles.get_mut(&user_id) {
719 profile.update_with_interaction(item_id, rating, context);
720 } else {
721 let mut profile = UserProfile::new(user_id);
722 profile.update_with_interaction(item_id, rating, context);
723 self.user_profiles.insert(user_id, profile);
724 }
725
726 self.engine.update(user_id, item_id, rating)?;
728
729 Ok(())
730 }
731
732 pub fn recommend(
734 &self,
735 user_id: usize,
736 n_items: usize,
737 options: RecommendationOptions,
738 ) -> Result<Vec<Recommendation>> {
739 let mut recommendations = self
740 .engine
741 .recommend(user_id, n_items, options.exclude_seen)?;
742
743 if options.diversify {
745 recommendations =
746 self.diversify_recommendations(recommendations, options.diversity_weight)?;
747 }
748
749 if options.explain {
751 for rec in &mut recommendations {
752 rec.explanation = Some(self.generate_explanation(user_id, rec.item_id)?);
753 }
754 }
755
756 if let Some(rules) = options.business_rules {
758 recommendations = self.apply_business_rules(recommendations, rules)?;
759 }
760
761 Ok(recommendations)
762 }
763
764 pub fn train(
766 &mut self,
767 train_data: &[(usize, usize, f64)],
768 val_data: Option<&[(usize, usize, f64)]>,
769 epochs: usize,
770 ) -> Result<TrainingHistory> {
771 let mut history = TrainingHistory::new();
772
773 for epoch in 0..epochs {
774 let mut train_loss = 0.0;
776
777 for &(user_id, item_id, rating) in train_data {
778 let prediction = self.predict(user_id, item_id)?;
780 let loss = (prediction - rating).powi(2);
781 train_loss += loss;
782
783 self.update_model(user_id, item_id, rating, prediction)?;
785 }
786
787 train_loss /= train_data.len() as f64;
788
789 let val_metrics = if let Some(val) = val_data {
791 self.evaluate(val)?
792 } else {
793 EvaluationMetrics::default()
794 };
795
796 history.add_epoch(epoch, train_loss, val_metrics);
797
798 if history.should_stop_early() {
800 break;
801 }
802 }
803
804 Ok(history)
805 }
806
807 pub fn predict(&self, user_id: usize, item_id: usize) -> Result<f64> {
809 let user_embedding = self.parameters.get_user_embedding(user_id)?;
811 let item_embedding = self.parameters.get_item_embedding(item_id)?;
812
813 let quantum_similarity = self.quantum_processor.compute_similarity(
815 &user_embedding,
816 &item_embedding,
817 &self.config.similarity_measure,
818 )?;
819
820 let prediction = self.parameters.global_bias
822 + self.parameters.user_bias[user_id]
823 + self.parameters.item_bias[item_id]
824 + quantum_similarity;
825
826 Ok(prediction.max(1.0).min(5.0)) }
828
829 fn update_model(
831 &mut self,
832 user_id: usize,
833 item_id: usize,
834 true_rating: f64,
835 predicted_rating: f64,
836 ) -> Result<()> {
837 let error = true_rating - predicted_rating;
838 let lr = self.config.learning_rate;
839 let reg = self.config.regularization;
840
841 self.parameters.user_bias[user_id] +=
843 lr * (error - reg * self.parameters.user_bias[user_id]);
844 self.parameters.item_bias[item_id] +=
845 lr * (error - reg * self.parameters.item_bias[item_id]);
846
847 let quantum_gradient = self
849 .quantum_processor
850 .compute_gradient(user_id, item_id, error)?;
851
852 self.parameters
853 .update_embeddings(user_id, item_id, &quantum_gradient, lr, reg)?;
854
855 Ok(())
856 }
857
858 fn diversify_recommendations(
860 &self,
861 mut recommendations: Vec<Recommendation>,
862 diversity_weight: f64,
863 ) -> Result<Vec<Recommendation>> {
864 let mut diversified = Vec::new();
865 let mut selected_items = HashSet::new();
866
867 while !recommendations.is_empty() && diversified.len() < recommendations.len() {
868 let mut best_score = f64::NEG_INFINITY;
869 let mut best_idx = 0;
870
871 for (idx, rec) in recommendations.iter().enumerate() {
872 let relevance_score = rec.score;
873 let diversity_score = self.compute_diversity_score(rec.item_id, &selected_items)?;
874
875 let combined_score =
876 (1.0 - diversity_weight) * relevance_score + diversity_weight * diversity_score;
877
878 if combined_score > best_score {
879 best_score = combined_score;
880 best_idx = idx;
881 }
882 }
883
884 let selected = recommendations.remove(best_idx);
885 selected_items.insert(selected.item_id);
886 diversified.push(selected);
887 }
888
889 Ok(diversified)
890 }
891
892 fn compute_diversity_score(
894 &self,
895 item_id: usize,
896 selected_items: &HashSet<usize>,
897 ) -> Result<f64> {
898 if selected_items.is_empty() {
899 return Ok(1.0);
900 }
901
902 let mut min_similarity: f64 = 1.0;
903
904 for &selected_id in selected_items {
905 let similarity =
906 self.engine
907 .compute_similarity(item_id, selected_id, SimilarityType::ItemToItem)?;
908 min_similarity = min_similarity.min(similarity);
909 }
910
911 Ok(1.0 - min_similarity)
912 }
913
914 fn generate_explanation(
916 &self,
917 user_id: usize,
918 item_id: usize,
919 ) -> Result<RecommendationExplanation> {
920 let similar_users = self.find_similar_users_for_item(user_id, item_id, 5)?;
922
923 let similar_items = self.find_similar_items_to_history(user_id, item_id, 5)?;
925
926 let feature_reasons = self.extract_feature_reasons(user_id, item_id)?;
928
929 let quantum_state = Some(self.quantum_processor.get_state_info(user_id, item_id)?);
931
932 Ok(RecommendationExplanation {
933 similar_users,
934 similar_items,
935 feature_reasons,
936 quantum_state,
937 })
938 }
939
940 fn find_similar_users_for_item(
942 &self,
943 user_id: usize,
944 item_id: usize,
945 n: usize,
946 ) -> Result<Vec<(usize, f64)>> {
947 let mut similar_users = Vec::new();
948
949 let item_users = self.interaction_matrix.get_item_users(item_id)?;
951
952 for &other_user in &item_users {
953 if other_user != user_id {
954 let similarity = self.engine.compute_similarity(
955 user_id,
956 other_user,
957 SimilarityType::UserToUser,
958 )?;
959 similar_users.push((other_user, similarity));
960 }
961 }
962
963 similar_users.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
965 similar_users.truncate(n);
966
967 Ok(similar_users)
968 }
969
970 fn find_similar_items_to_history(
972 &self,
973 user_id: usize,
974 target_item: usize,
975 n: usize,
976 ) -> Result<Vec<(usize, f64)>> {
977 let mut similar_items = Vec::new();
978
979 if let Some(profile) = self.user_profiles.get(&user_id) {
981 for &(item_id, _) in &profile.preferences.rated_items {
982 if item_id != target_item {
983 let similarity = self.engine.compute_similarity(
984 target_item,
985 item_id,
986 SimilarityType::ItemToItem,
987 )?;
988 similar_items.push((item_id, similarity));
989 }
990 }
991 }
992
993 similar_items.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
995 similar_items.truncate(n);
996
997 Ok(similar_items)
998 }
999
1000 fn extract_feature_reasons(&self, user_id: usize, item_id: usize) -> Result<Vec<String>> {
1002 let mut reasons = Vec::new();
1003
1004 if let (Some(user_profile), Some(item_features)) = (
1006 self.user_profiles.get(&user_id),
1007 self.item_features.get(&item_id),
1008 ) {
1009 for category in &item_features.categories {
1011 if let Some(&pref_score) =
1012 user_profile.preferences.preferred_categories.get(category)
1013 {
1014 if pref_score > 0.7 {
1015 reasons.push(format!("Matches your interest in {}", category));
1016 }
1017 }
1018 }
1019
1020 for (attr_name, attr_value) in &item_features.attributes {
1022 match attr_value {
1023 AttributeValue::Categorical(val) => {
1024 reasons.push(format!("Features {}: {}", attr_name, val));
1025 }
1026 AttributeValue::Numeric(val) => {
1027 if *val > 0.8 {
1028 reasons.push(format!("High {} score", attr_name));
1029 }
1030 }
1031 _ => {}
1032 }
1033 }
1034 }
1035
1036 Ok(reasons)
1037 }
1038
1039 fn apply_business_rules(
1041 &self,
1042 recommendations: Vec<Recommendation>,
1043 rules: BusinessRules,
1044 ) -> Result<Vec<Recommendation>> {
1045 let mut filtered = recommendations;
1046
1047 if let Some(categories) = rules.required_categories {
1049 filtered = self.filter_by_categories(filtered, categories)?;
1050 }
1051
1052 if let Some(boost_new) = rules.boost_new_items {
1053 filtered = self.boost_new_items(filtered, boost_new)?;
1054 }
1055
1056 if let Some(max_price) = rules.max_price {
1057 filtered = self.filter_by_price(filtered, max_price)?;
1058 }
1059
1060 Ok(filtered)
1061 }
1062
1063 fn filter_by_categories(
1065 &self,
1066 recommendations: Vec<Recommendation>,
1067 categories: HashSet<String>,
1068 ) -> Result<Vec<Recommendation>> {
1069 Ok(recommendations
1070 .into_iter()
1071 .filter(|rec| {
1072 if let Some(features) = self.item_features.get(&rec.item_id) {
1073 features
1074 .categories
1075 .iter()
1076 .any(|cat| categories.contains(cat))
1077 } else {
1078 false
1079 }
1080 })
1081 .collect())
1082 }
1083
1084 fn boost_new_items(
1086 &self,
1087 mut recommendations: Vec<Recommendation>,
1088 boost_factor: f64,
1089 ) -> Result<Vec<Recommendation>> {
1090 for rec in &mut recommendations {
1091 if self.is_new_item(rec.item_id)? {
1092 rec.score *= boost_factor;
1093 }
1094 }
1095
1096 recommendations.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap());
1098
1099 Ok(recommendations)
1100 }
1101
1102 fn is_new_item(&self, item_id: usize) -> Result<bool> {
1104 let num_ratings = self.interaction_matrix.get_item_rating_count(item_id)?;
1106 Ok(num_ratings < 10)
1107 }
1108
1109 fn filter_by_price(
1111 &self,
1112 recommendations: Vec<Recommendation>,
1113 max_price: f64,
1114 ) -> Result<Vec<Recommendation>> {
1115 Ok(recommendations
1116 .into_iter()
1117 .filter(|rec| {
1118 if let Some(features) = self.item_features.get(&rec.item_id) {
1119 if let Some(AttributeValue::Numeric(price)) = features.attributes.get("price") {
1120 *price <= max_price
1121 } else {
1122 true
1123 }
1124 } else {
1125 true
1126 }
1127 })
1128 .collect())
1129 }
1130
1131 pub fn evaluate(&self, test_data: &[(usize, usize, f64)]) -> Result<EvaluationMetrics> {
1133 let mut predictions = Vec::new();
1134 let mut actuals = Vec::new();
1135
1136 for &(user_id, item_id, rating) in test_data {
1137 let prediction = self.predict(user_id, item_id)?;
1138 predictions.push(prediction);
1139 actuals.push(rating);
1140 }
1141
1142 Ok(EvaluationMetrics::compute(&predictions, &actuals))
1143 }
1144
1145 pub fn metrics(&self) -> &RecommenderMetrics {
1147 &self.metrics
1148 }
1149}
1150
1151impl InteractionMatrix {
1152 pub fn new() -> Self {
1154 Self {
1155 ratings: HashMap::new(),
1156 user_ids: HashSet::new(),
1157 item_ids: HashSet::new(),
1158 implicit_feedback: None,
1159 timestamps: None,
1160 }
1161 }
1162
1163 pub fn add_rating(&mut self, user_id: usize, item_id: usize, rating: f64) {
1165 self.ratings.insert((user_id, item_id), rating);
1166 self.user_ids.insert(user_id);
1167 self.item_ids.insert(item_id);
1168 }
1169
1170 pub fn get_item_users(&self, item_id: usize) -> Result<Vec<usize>> {
1172 Ok(self
1173 .ratings
1174 .keys()
1175 .filter_map(
1176 |(user, item)| {
1177 if *item == item_id {
1178 Some(*user)
1179 } else {
1180 None
1181 }
1182 },
1183 )
1184 .collect())
1185 }
1186
1187 pub fn get_item_rating_count(&self, item_id: usize) -> Result<usize> {
1189 Ok(self
1190 .ratings
1191 .keys()
1192 .filter(|(_, item)| *item == item_id)
1193 .count())
1194 }
1195}
1196
1197impl QuantumProcessor {
1198 pub fn new(num_qubits: usize) -> Result<Self> {
1200 let layers = vec![
1202 QNNLayerType::EncodingLayer { num_features: 128 },
1203 QNNLayerType::VariationalLayer { num_params: 64 },
1204 QNNLayerType::EntanglementLayer {
1205 connectivity: "circular".to_string(),
1206 },
1207 QNNLayerType::MeasurementLayer {
1208 measurement_basis: "computational".to_string(),
1209 },
1210 ];
1211
1212 let embedding_network = QuantumNeuralNetwork::new(layers, num_qubits, 128, 64)?;
1213
1214 let entanglement_generator = EntanglementGenerator::new(num_qubits);
1216
1217 Ok(Self {
1218 num_qubits,
1219 similarity_circuits: Vec::new(),
1220 embedding_network,
1221 entanglement_generator,
1222 })
1223 }
1224
1225 pub fn compute_similarity(
1227 &self,
1228 vec1: &Array1<f64>,
1229 vec2: &Array1<f64>,
1230 measure: &SimilarityMeasure,
1231 ) -> Result<f64> {
1232 match measure {
1233 SimilarityMeasure::Cosine => {
1234 let dot = vec1.dot(vec2);
1235 let norm1 = vec1.dot(vec1).sqrt();
1236 let norm2 = vec2.dot(vec2).sqrt();
1237 Ok(dot / (norm1 * norm2 + 1e-10))
1238 }
1239 SimilarityMeasure::QuantumFidelity => {
1240 let state1 = self.encode_as_quantum_state(vec1)?;
1242 let state2 = self.encode_as_quantum_state(vec2)?;
1243
1244 let fidelity = state1.dot(&state2).abs();
1246 Ok(fidelity * fidelity)
1247 }
1248 SimilarityMeasure::EntanglementSimilarity => {
1249 let entangled = self
1251 .entanglement_generator
1252 .create_entangled_state(vec1, vec2)?;
1253
1254 let entanglement = self.measure_entanglement(&entangled)?;
1256 Ok(entanglement)
1257 }
1258 _ => Ok(0.5), }
1260 }
1261
1262 fn encode_as_quantum_state(&self, vec: &Array1<f64>) -> Result<Array1<f64>> {
1264 let norm = vec.dot(vec).sqrt();
1266 let normalized = vec / (norm + 1e-10);
1267
1268 let quantum_dim = 2_usize.pow(self.num_qubits as u32);
1270 let mut quantum_state = Array1::zeros(quantum_dim);
1271
1272 for i in 0..normalized.len().min(quantum_dim) {
1273 quantum_state[i] = normalized[i];
1274 }
1275
1276 Ok(quantum_state)
1277 }
1278
1279 fn measure_entanglement(&self, state: &Array1<f64>) -> Result<f64> {
1281 let entropy = -state
1283 .iter()
1284 .filter(|&&x| x.abs() > 1e-10)
1285 .map(|&x| {
1286 let p = x * x;
1287 p * p.ln()
1288 })
1289 .sum::<f64>();
1290
1291 Ok((entropy / (self.num_qubits as f64).ln()).min(1.0))
1292 }
1293
1294 pub fn compute_gradient(
1296 &self,
1297 user_id: usize,
1298 item_id: usize,
1299 error: f64,
1300 ) -> Result<Array1<f64>> {
1301 let gradient_dim = 64;
1303 let mut gradient = Array1::zeros(gradient_dim);
1304
1305 for i in 0..gradient_dim {
1306 gradient[i] = error
1307 * (0.1 * (i as f64 * 0.1 + user_id as f64 * 0.01 + item_id as f64 * 0.001).sin());
1308 }
1309
1310 Ok(gradient)
1311 }
1312
1313 pub fn get_state_info(&self, user_id: usize, item_id: usize) -> Result<QuantumStateInfo> {
1315 Ok(QuantumStateInfo {
1316 entanglement: 0.7 + 0.3 * (user_id as f64 * 0.1).sin(),
1317 superposition_weights: vec![0.5, 0.3, 0.2],
1318 phase: PI * (item_id as f64 * 0.01).sin(),
1319 })
1320 }
1321}
1322
1323impl EntanglementGenerator {
1324 pub fn new(num_qubits: usize) -> Self {
1326 let patterns = vec![
1327 EntanglementPattern {
1328 qubit_pairs: (0..num_qubits - 1).map(|i| (i, i + 1)).collect(),
1329 strength: 0.8,
1330 pattern_type: PatternType::Bell,
1331 },
1332 EntanglementPattern {
1333 qubit_pairs: vec![(0, num_qubits - 1)],
1334 strength: 0.5,
1335 pattern_type: PatternType::GHZ,
1336 },
1337 ];
1338
1339 Self {
1340 patterns,
1341 circuit_params: vec![0.0; num_qubits * 3],
1342 }
1343 }
1344
1345 pub fn create_entangled_state(
1347 &self,
1348 vec1: &Array1<f64>,
1349 vec2: &Array1<f64>,
1350 ) -> Result<Array1<f64>> {
1351 let combined = Array1::from_iter(vec1.iter().chain(vec2.iter()).cloned());
1352
1353 let mut entangled = combined.clone();
1355
1356 for pattern in &self.patterns {
1357 for &(q1, q2) in &pattern.qubit_pairs {
1358 if q1 < entangled.len() && q2 < entangled.len() {
1359 let v1 = entangled[q1];
1360 let v2 = entangled[q2];
1361
1362 entangled[q1] = v1 * pattern.strength.cos() - v2 * pattern.strength.sin();
1363 entangled[q2] = v1 * pattern.strength.sin() + v2 * pattern.strength.cos();
1364 }
1365 }
1366 }
1367
1368 let norm = entangled.dot(&entangled).sqrt();
1370 Ok(entangled / (norm + 1e-10))
1371 }
1372}
1373
1374impl UserProfile {
1375 pub fn new(user_id: usize) -> Self {
1377 Self {
1378 user_id,
1379 features: Array1::zeros(128),
1380 preferences: PreferenceHistory::new(),
1381 quantum_state: Array1::zeros(64),
1382 metadata: ProfileMetadata {
1383 created_at: 0.0,
1384 updated_at: 0.0,
1385 num_interactions: 0,
1386 completeness: 0.0,
1387 },
1388 }
1389 }
1390
1391 pub fn update_with_interaction(
1393 &mut self,
1394 item_id: usize,
1395 rating: f64,
1396 context: Option<InteractionContext>,
1397 ) {
1398 self.preferences.rated_items.push((item_id, rating));
1399
1400 if let Some(ctx) = context {
1401 self.preferences.contexts.push(ctx);
1402 }
1403
1404 self.metadata.num_interactions += 1;
1405 self.metadata.updated_at = self.metadata.num_interactions as f64; }
1407}
1408
1409impl PreferenceHistory {
1410 pub fn new() -> Self {
1412 Self {
1413 rated_items: Vec::new(),
1414 preferred_categories: HashMap::new(),
1415 temporal_patterns: TemporalPatterns {
1416 hourly_distribution: Array1::zeros(24),
1417 weekly_distribution: Array1::zeros(7),
1418 seasonal_factors: Array1::zeros(4),
1419 trends: Vec::new(),
1420 },
1421 contexts: Vec::new(),
1422 }
1423 }
1424}
1425
1426impl ModelParameters {
1427 pub fn new(num_users: usize, num_items: usize, num_factors: usize) -> Self {
1429 Self {
1430 user_embeddings: Array2::from_shape_fn((num_users, num_factors), |(_, _)| {
1431 0.01 * (fastrand::f64() - 0.5)
1432 }),
1433 item_embeddings: Array2::from_shape_fn((num_items, num_factors), |(_, _)| {
1434 0.01 * (fastrand::f64() - 0.5)
1435 }),
1436 quantum_params: vec![0.0; num_factors * 10],
1437 user_bias: Array1::zeros(num_users),
1438 item_bias: Array1::zeros(num_items),
1439 global_bias: 3.5, }
1441 }
1442
1443 pub fn get_user_embedding(&self, user_id: usize) -> Result<Array1<f64>> {
1445 if user_id < self.user_embeddings.nrows() {
1446 Ok(self.user_embeddings.row(user_id).to_owned())
1447 } else {
1448 Ok(Array1::zeros(self.user_embeddings.ncols()))
1449 }
1450 }
1451
1452 pub fn get_item_embedding(&self, item_id: usize) -> Result<Array1<f64>> {
1454 if item_id < self.item_embeddings.nrows() {
1455 Ok(self.item_embeddings.row(item_id).to_owned())
1456 } else {
1457 Ok(Array1::zeros(self.item_embeddings.ncols()))
1458 }
1459 }
1460
1461 pub fn update_embeddings(
1463 &mut self,
1464 user_id: usize,
1465 item_id: usize,
1466 gradient: &Array1<f64>,
1467 lr: f64,
1468 reg: f64,
1469 ) -> Result<()> {
1470 if user_id < self.user_embeddings.nrows() && item_id < self.item_embeddings.nrows() {
1471 let user_emb = self.user_embeddings.row(user_id).to_owned();
1472 let item_emb = self.item_embeddings.row(item_id).to_owned();
1473
1474 self.user_embeddings
1476 .row_mut(user_id)
1477 .zip_mut_with(&user_emb, |param, &old| {
1478 *param = old + lr * (gradient[0] - reg * old);
1479 });
1480
1481 self.item_embeddings
1483 .row_mut(item_id)
1484 .zip_mut_with(&item_emb, |param, &old| {
1485 *param = old + lr * (gradient[0] - reg * old);
1486 });
1487 }
1488
1489 Ok(())
1490 }
1491}
1492
1493impl RecommenderMetrics {
1494 pub fn new() -> Self {
1496 Self {
1497 accuracy_metrics: AccuracyMetrics {
1498 mae: 0.0,
1499 rmse: 0.0,
1500 precision_at_k: HashMap::new(),
1501 recall_at_k: HashMap::new(),
1502 ndcg_at_k: HashMap::new(),
1503 },
1504 diversity_metrics: DiversityMetrics {
1505 intra_list_diversity: 0.0,
1506 inter_list_diversity: 0.0,
1507 category_coverage: 0.0,
1508 novelty: 0.0,
1509 },
1510 coverage_metrics: CoverageMetrics {
1511 item_coverage: 0.0,
1512 user_coverage: 0.0,
1513 cold_start_performance: 0.0,
1514 },
1515 quantum_metrics: QuantumMetrics {
1516 quantum_advantage: 1.0,
1517 entanglement_utilization: 0.0,
1518 coherence_preservation: 0.0,
1519 circuit_efficiency: 0.0,
1520 },
1521 }
1522 }
1523}
1524
1525#[derive(Debug, Clone)]
1527pub struct RecommendationOptions {
1528 pub exclude_seen: bool,
1530
1531 pub diversify: bool,
1533
1534 pub diversity_weight: f64,
1536
1537 pub explain: bool,
1539
1540 pub business_rules: Option<BusinessRules>,
1542}
1543
1544impl Default for RecommendationOptions {
1545 fn default() -> Self {
1546 Self {
1547 exclude_seen: true,
1548 diversify: false,
1549 diversity_weight: 0.3,
1550 explain: false,
1551 business_rules: None,
1552 }
1553 }
1554}
1555
1556#[derive(Debug, Clone)]
1558pub struct BusinessRules {
1559 pub required_categories: Option<HashSet<String>>,
1561
1562 pub boost_new_items: Option<f64>,
1564
1565 pub max_price: Option<f64>,
1567}
1568
1569#[derive(Debug, Clone)]
1571pub struct TrainingHistory {
1572 pub epochs: Vec<usize>,
1573 pub train_losses: Vec<f64>,
1574 pub val_metrics: Vec<EvaluationMetrics>,
1575}
1576
1577impl TrainingHistory {
1578 fn new() -> Self {
1579 Self {
1580 epochs: Vec::new(),
1581 train_losses: Vec::new(),
1582 val_metrics: Vec::new(),
1583 }
1584 }
1585
1586 fn add_epoch(&mut self, epoch: usize, train_loss: f64, val_metrics: EvaluationMetrics) {
1587 self.epochs.push(epoch);
1588 self.train_losses.push(train_loss);
1589 self.val_metrics.push(val_metrics);
1590 }
1591
1592 fn should_stop_early(&self) -> bool {
1593 if self.val_metrics.len() < 4 {
1595 return false;
1596 }
1597
1598 let recent = &self.val_metrics[self.val_metrics.len() - 3..];
1599 recent[0].rmse < recent[1].rmse && recent[1].rmse < recent[2].rmse
1600 }
1601}
1602
1603#[derive(Debug, Clone, Default)]
1605pub struct EvaluationMetrics {
1606 pub mae: f64,
1607 pub rmse: f64,
1608}
1609
1610impl EvaluationMetrics {
1611 fn compute(predictions: &[f64], actuals: &[f64]) -> Self {
1612 let n = predictions.len() as f64;
1613
1614 let mae = predictions
1615 .iter()
1616 .zip(actuals)
1617 .map(|(p, a)| (p - a).abs())
1618 .sum::<f64>()
1619 / n;
1620
1621 let rmse = (predictions
1622 .iter()
1623 .zip(actuals)
1624 .map(|(p, a)| (p - a).powi(2))
1625 .sum::<f64>()
1626 / n)
1627 .sqrt();
1628
1629 Self { mae, rmse }
1630 }
1631}
1632
1633#[derive(Debug, Clone)]
1637struct QuantumCFEngine {
1638 config: QuantumRecommenderConfig,
1639 similarity_cache: HashMap<(usize, usize), f64>,
1640 parameters: Array1<f64>,
1641}
1642
1643impl QuantumCFEngine {
1644 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1645 Ok(Self {
1646 config: config.clone(),
1647 similarity_cache: HashMap::new(),
1648 parameters: Array1::zeros(100),
1649 })
1650 }
1651}
1652
1653impl RecommendationEngine for QuantumCFEngine {
1654 fn recommend(
1655 &self,
1656 _user_id: usize,
1657 n_items: usize,
1658 _exclude_seen: bool,
1659 ) -> Result<Vec<Recommendation>> {
1660 let mut recommendations = Vec::new();
1662
1663 for i in 0..n_items {
1664 recommendations.push(Recommendation {
1665 item_id: i,
1666 score: 4.0 - 0.1 * i as f64,
1667 confidence: (3.5, 4.5),
1668 explanation: None,
1669 quantum_contribution: 0.3,
1670 });
1671 }
1672
1673 Ok(recommendations)
1674 }
1675
1676 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1677 Ok(())
1678 }
1679
1680 fn compute_similarity(
1681 &self,
1682 _id1: usize,
1683 _id2: usize,
1684 _similarity_type: SimilarityType,
1685 ) -> Result<f64> {
1686 Ok(0.8)
1687 }
1688
1689 fn parameters(&self) -> &Array1<f64> {
1690 &self.parameters
1691 }
1692
1693 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1694 Box::new(self.clone())
1695 }
1696}
1697
1698#[derive(Debug, Clone)]
1700struct QuantumMFEngine {
1701 config: QuantumRecommenderConfig,
1702 parameters: Array1<f64>,
1703}
1704
1705impl QuantumMFEngine {
1706 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1707 Ok(Self {
1708 config: config.clone(),
1709 parameters: Array1::zeros(100),
1710 })
1711 }
1712}
1713
1714impl RecommendationEngine for QuantumMFEngine {
1715 fn recommend(
1716 &self,
1717 _user_id: usize,
1718 n_items: usize,
1719 _exclude_seen: bool,
1720 ) -> Result<Vec<Recommendation>> {
1721 let mut recommendations = Vec::new();
1722
1723 for i in 0..n_items {
1724 recommendations.push(Recommendation {
1725 item_id: i * 2,
1726 score: 4.2 - 0.05 * i as f64,
1727 confidence: (3.8, 4.6),
1728 explanation: None,
1729 quantum_contribution: 0.4,
1730 });
1731 }
1732
1733 Ok(recommendations)
1734 }
1735
1736 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1737 Ok(())
1738 }
1739
1740 fn compute_similarity(
1741 &self,
1742 _id1: usize,
1743 _id2: usize,
1744 _similarity_type: SimilarityType,
1745 ) -> Result<f64> {
1746 Ok(0.75)
1747 }
1748
1749 fn parameters(&self) -> &Array1<f64> {
1750 &self.parameters
1751 }
1752
1753 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1754 Box::new(self.clone())
1755 }
1756}
1757
1758#[derive(Debug, Clone)]
1760struct QuantumCBEngine {
1761 config: QuantumRecommenderConfig,
1762 parameters: Array1<f64>,
1763}
1764
1765impl QuantumCBEngine {
1766 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1767 Ok(Self {
1768 config: config.clone(),
1769 parameters: Array1::zeros(100),
1770 })
1771 }
1772}
1773
1774impl RecommendationEngine for QuantumCBEngine {
1775 fn recommend(
1776 &self,
1777 _user_id: usize,
1778 n_items: usize,
1779 _exclude_seen: bool,
1780 ) -> Result<Vec<Recommendation>> {
1781 let mut recommendations = Vec::new();
1782
1783 for i in 0..n_items {
1784 recommendations.push(Recommendation {
1785 item_id: i * 3,
1786 score: 3.9 - 0.08 * i as f64,
1787 confidence: (3.4, 4.4),
1788 explanation: None,
1789 quantum_contribution: 0.35,
1790 });
1791 }
1792
1793 Ok(recommendations)
1794 }
1795
1796 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1797 Ok(())
1798 }
1799
1800 fn compute_similarity(
1801 &self,
1802 _id1: usize,
1803 _id2: usize,
1804 _similarity_type: SimilarityType,
1805 ) -> Result<f64> {
1806 Ok(0.7)
1807 }
1808
1809 fn parameters(&self) -> &Array1<f64> {
1810 &self.parameters
1811 }
1812
1813 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1814 Box::new(self.clone())
1815 }
1816}
1817
1818#[derive(Debug, Clone)]
1820struct HybridQuantumEngine {
1821 config: QuantumRecommenderConfig,
1822 parameters: Array1<f64>,
1823}
1824
1825impl HybridQuantumEngine {
1826 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1827 Ok(Self {
1828 config: config.clone(),
1829 parameters: Array1::zeros(100),
1830 })
1831 }
1832}
1833
1834impl RecommendationEngine for HybridQuantumEngine {
1835 fn recommend(
1836 &self,
1837 _user_id: usize,
1838 n_items: usize,
1839 _exclude_seen: bool,
1840 ) -> Result<Vec<Recommendation>> {
1841 let mut recommendations = Vec::new();
1842
1843 for i in 0..n_items {
1844 recommendations.push(Recommendation {
1845 item_id: i,
1846 score: 4.3 - 0.06 * i as f64,
1847 confidence: (3.9, 4.7),
1848 explanation: None,
1849 quantum_contribution: 0.5,
1850 });
1851 }
1852
1853 Ok(recommendations)
1854 }
1855
1856 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1857 Ok(())
1858 }
1859
1860 fn compute_similarity(
1861 &self,
1862 _id1: usize,
1863 _id2: usize,
1864 _similarity_type: SimilarityType,
1865 ) -> Result<f64> {
1866 Ok(0.85)
1867 }
1868
1869 fn parameters(&self) -> &Array1<f64> {
1870 &self.parameters
1871 }
1872
1873 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1874 Box::new(self.clone())
1875 }
1876}
1877
1878#[derive(Debug, Clone)]
1880struct QuantumNCFEngine {
1881 config: QuantumRecommenderConfig,
1882 parameters: Array1<f64>,
1883}
1884
1885impl QuantumNCFEngine {
1886 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1887 Ok(Self {
1888 config: config.clone(),
1889 parameters: Array1::zeros(100),
1890 })
1891 }
1892}
1893
1894impl RecommendationEngine for QuantumNCFEngine {
1895 fn recommend(
1896 &self,
1897 _user_id: usize,
1898 n_items: usize,
1899 _exclude_seen: bool,
1900 ) -> Result<Vec<Recommendation>> {
1901 let mut recommendations = Vec::new();
1902
1903 for i in 0..n_items {
1904 recommendations.push(Recommendation {
1905 item_id: i * 4,
1906 score: 4.1 - 0.07 * i as f64,
1907 confidence: (3.7, 4.5),
1908 explanation: None,
1909 quantum_contribution: 0.45,
1910 });
1911 }
1912
1913 Ok(recommendations)
1914 }
1915
1916 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1917 Ok(())
1918 }
1919
1920 fn compute_similarity(
1921 &self,
1922 _id1: usize,
1923 _id2: usize,
1924 _similarity_type: SimilarityType,
1925 ) -> Result<f64> {
1926 Ok(0.82)
1927 }
1928
1929 fn parameters(&self) -> &Array1<f64> {
1930 &self.parameters
1931 }
1932
1933 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1934 Box::new(self.clone())
1935 }
1936}
1937
1938#[derive(Debug, Clone)]
1940struct QuantumGraphEngine {
1941 config: QuantumRecommenderConfig,
1942 parameters: Array1<f64>,
1943}
1944
1945impl QuantumGraphEngine {
1946 fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
1947 Ok(Self {
1948 config: config.clone(),
1949 parameters: Array1::zeros(100),
1950 })
1951 }
1952}
1953
1954impl RecommendationEngine for QuantumGraphEngine {
1955 fn recommend(
1956 &self,
1957 _user_id: usize,
1958 n_items: usize,
1959 _exclude_seen: bool,
1960 ) -> Result<Vec<Recommendation>> {
1961 let mut recommendations = Vec::new();
1962
1963 for i in 0..n_items {
1964 recommendations.push(Recommendation {
1965 item_id: i * 5,
1966 score: 4.0 - 0.09 * i as f64,
1967 confidence: (3.5, 4.5),
1968 explanation: None,
1969 quantum_contribution: 0.42,
1970 });
1971 }
1972
1973 Ok(recommendations)
1974 }
1975
1976 fn update(&mut self, _user_id: usize, _item_id: usize, _rating: f64) -> Result<()> {
1977 Ok(())
1978 }
1979
1980 fn compute_similarity(
1981 &self,
1982 _id1: usize,
1983 _id2: usize,
1984 _similarity_type: SimilarityType,
1985 ) -> Result<f64> {
1986 Ok(0.78)
1987 }
1988
1989 fn parameters(&self) -> &Array1<f64> {
1990 &self.parameters
1991 }
1992
1993 fn clone_box(&self) -> Box<dyn RecommendationEngine> {
1994 Box::new(self.clone())
1995 }
1996}
1997
1998#[cfg(test)]
1999mod tests {
2000 use super::*;
2001
2002 #[test]
2003 fn test_recommender_creation() {
2004 let config = QuantumRecommenderConfig::default();
2005 let recommender = QuantumRecommender::new(config).unwrap();
2006 assert!(recommender.user_profiles.is_empty());
2007 }
2008
2009 #[test]
2010 fn test_add_interaction() {
2011 let config = QuantumRecommenderConfig::default();
2012 let mut recommender = QuantumRecommender::new(config).unwrap();
2013
2014 recommender.add_interaction(1, 10, 4.5, None).unwrap();
2015 assert_eq!(recommender.user_profiles.len(), 1);
2016 }
2017
2018 #[test]
2019 fn test_recommendations() {
2020 let config = QuantumRecommenderConfig::default();
2021 let mut recommender = QuantumRecommender::new(config).unwrap();
2022
2023 recommender.add_interaction(1, 10, 4.5, None).unwrap();
2025 recommender.add_interaction(1, 20, 3.5, None).unwrap();
2026
2027 let options = RecommendationOptions::default();
2029 let recommendations = recommender.recommend(1, 5, options).unwrap();
2030
2031 assert_eq!(recommendations.len(), 5);
2032 assert!(recommendations[0].score >= recommendations[1].score);
2033 }
2034
2035 #[test]
2036 fn test_similarity_measures() {
2037 let processor = QuantumProcessor::new(8).unwrap();
2038 let vec1 = Array1::from_vec(vec![1.0, 0.0, 0.0, 0.0]);
2039 let vec2 = Array1::from_vec(vec![0.0, 1.0, 0.0, 0.0]);
2040
2041 let cosine_sim = processor
2042 .compute_similarity(&vec1, &vec2, &SimilarityMeasure::Cosine)
2043 .unwrap();
2044
2045 assert!(cosine_sim.abs() < 1e-10); }
2047}