use crate::error::{MLError, Result};
use crate::optimization::OptimizationMethod;
use crate::qnn::{QNNLayerType, QuantumNeuralNetwork};
use scirs2_core::ndarray::*;
use scirs2_core::random::prelude::*;
use scirs2_core::random::{ChaCha20Rng, Rng, SeedableRng};
use scirs2_core::{Complex32, Complex64};
use std::collections::{HashMap, HashSet};
use std::f64::consts::PI;
pub trait RecommendationEngine: std::fmt::Debug {
fn recommend(
&self,
user_id: usize,
n_items: usize,
exclude_seen: bool,
) -> Result<Vec<Recommendation>>;
fn update(&mut self, user_id: usize, item_id: usize, rating: f64) -> Result<()>;
fn compute_similarity(
&self,
id1: usize,
id2: usize,
similarity_type: SimilarityType,
) -> Result<f64>;
fn parameters(&self) -> &Array1<f64>;
fn clone_box(&self) -> Box<dyn RecommendationEngine>;
}
impl Clone for Box<dyn RecommendationEngine> {
fn clone(&self) -> Self {
self.clone_box()
}
}
#[derive(Debug, Clone)]
pub struct ItemFeatures {
pub item_id: usize,
pub features: Array1<f64>,
pub categories: Vec<String>,
pub attributes: HashMap<String, AttributeValue>,
pub quantum_features: Array1<f64>,
}
#[derive(Debug, Clone)]
pub struct QuantumRecommenderConfig {
pub num_qubits: usize,
pub algorithm: RecommendationAlgorithm,
pub num_factors: usize,
pub regularization: f64,
pub learning_rate: f64,
pub quantum_enhancement: QuantumEnhancementLevel,
pub similarity_measure: SimilarityMeasure,
}
impl QuantumRecommenderConfig {
pub fn default() -> Self {
Self {
num_qubits: 10,
algorithm: RecommendationAlgorithm::QuantumMatrixFactorization {
optimization_method: OptimizationMethod::Adam,
num_iterations: 100,
},
num_factors: 50,
regularization: 0.01,
learning_rate: 0.001,
quantum_enhancement: QuantumEnhancementLevel::Medium,
similarity_measure: SimilarityMeasure::QuantumFidelity,
}
}
pub fn collaborative_filtering() -> Self {
Self {
num_qubits: 12,
algorithm: RecommendationAlgorithm::QuantumCollaborativeFiltering {
neighborhood_size: 50,
min_common_items: 3,
},
num_factors: 100,
regularization: 0.001,
learning_rate: 0.01,
quantum_enhancement: QuantumEnhancementLevel::High,
similarity_measure: SimilarityMeasure::EntanglementSimilarity,
}
}
pub fn content_based() -> Self {
Self {
num_qubits: 10,
algorithm: RecommendationAlgorithm::QuantumContentBased {
feature_extraction: FeatureExtractionMethod::QuantumEmbeddings {
embedding_dim: 128,
},
profile_learning: ProfileLearningMethod::QuantumSuperposition,
},
num_factors: 64,
regularization: 0.005,
learning_rate: 0.005,
quantum_enhancement: QuantumEnhancementLevel::Medium,
similarity_measure: SimilarityMeasure::Cosine,
}
}
pub fn hybrid() -> Self {
Self {
num_qubits: 14,
algorithm: RecommendationAlgorithm::HybridQuantum {
cf_weight: 0.6,
cb_weight: 0.3,
knowledge_weight: 0.1,
},
num_factors: 80,
regularization: 0.01,
learning_rate: 0.001,
quantum_enhancement: QuantumEnhancementLevel::High,
similarity_measure: SimilarityMeasure::Hybrid {
classical_weight: 0.4,
quantum_weight: 0.6,
},
}
}
}
#[derive(Debug, Clone)]
pub struct RecommendationExplanation {
pub similar_users: Vec<(usize, f64)>,
pub similar_items: Vec<(usize, f64)>,
pub feature_reasons: Vec<String>,
pub quantum_state: Option<QuantumStateInfo>,
}
#[derive(Debug, Clone)]
pub enum SimilarityType {
UserToUser,
ItemToItem,
UserToItem,
}
#[derive(Debug, Clone)]
pub struct TrendIndicator {
pub feature: String,
pub direction: f64,
pub strength: f64,
pub window: f64,
}
#[derive(Debug, Clone)]
pub enum RecommendationAlgorithm {
QuantumCollaborativeFiltering {
neighborhood_size: usize,
min_common_items: usize,
},
QuantumMatrixFactorization {
optimization_method: OptimizationMethod,
num_iterations: usize,
},
QuantumContentBased {
feature_extraction: FeatureExtractionMethod,
profile_learning: ProfileLearningMethod,
},
HybridQuantum {
cf_weight: f64,
cb_weight: f64,
knowledge_weight: f64,
},
QuantumNeuralCF {
embedding_dim: usize,
hidden_layers: Vec<usize>,
},
QuantumGraphRecommender {
walk_length: usize,
num_walks: usize,
teleportation_prob: f64,
},
}
#[derive(Debug, Clone)]
pub struct InteractionMatrix {
ratings: HashMap<(usize, usize), f64>,
user_ids: HashSet<usize>,
item_ids: HashSet<usize>,
implicit_feedback: Option<HashMap<(usize, usize), ImplicitFeedback>>,
timestamps: Option<HashMap<(usize, usize), f64>>,
}
impl InteractionMatrix {
pub fn new() -> Self {
Self {
ratings: HashMap::new(),
user_ids: HashSet::new(),
item_ids: HashSet::new(),
implicit_feedback: None,
timestamps: None,
}
}
pub fn add_rating(&mut self, user_id: usize, item_id: usize, rating: f64) {
self.ratings.insert((user_id, item_id), rating);
self.user_ids.insert(user_id);
self.item_ids.insert(item_id);
}
pub fn get_item_users(&self, item_id: usize) -> Result<Vec<usize>> {
Ok(self
.ratings
.keys()
.filter_map(
|(user, item)| {
if *item == item_id {
Some(*user)
} else {
None
}
},
)
.collect())
}
pub fn get_item_rating_count(&self, item_id: usize) -> Result<usize> {
Ok(self
.ratings
.keys()
.filter(|(_, item)| *item == item_id)
.count())
}
}
#[derive(Debug, Clone)]
pub struct Recommendation {
pub item_id: usize,
pub score: f64,
pub confidence: (f64, f64),
pub explanation: Option<RecommendationExplanation>,
pub quantum_contribution: f64,
}
#[derive(Debug, Clone, Default)]
pub struct EvaluationMetrics {
pub mae: f64,
pub rmse: f64,
}
impl EvaluationMetrics {
fn compute(predictions: &[f64], actuals: &[f64]) -> Self {
let n = predictions.len() as f64;
let mae = predictions
.iter()
.zip(actuals)
.map(|(p, a)| (p - a).abs())
.sum::<f64>()
/ n;
let rmse = (predictions
.iter()
.zip(actuals)
.map(|(p, a)| (p - a).powi(2))
.sum::<f64>()
/ n)
.sqrt();
Self { mae, rmse }
}
}
#[derive(Debug, Clone)]
pub struct QuantumMFEngine {
config: QuantumRecommenderConfig,
pub parameters: Array1<f64>,
}
impl QuantumMFEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
parameters: Array1::zeros(100),
})
}
}
#[derive(Debug, Clone)]
pub struct TrainingHistory {
pub epochs: Vec<usize>,
pub train_losses: Vec<f64>,
pub val_metrics: Vec<EvaluationMetrics>,
}
impl TrainingHistory {
fn new() -> Self {
Self {
epochs: Vec::new(),
train_losses: Vec::new(),
val_metrics: Vec::new(),
}
}
fn add_epoch(&mut self, epoch: usize, train_loss: f64, val_metrics: EvaluationMetrics) {
self.epochs.push(epoch);
self.train_losses.push(train_loss);
self.val_metrics.push(val_metrics);
}
fn should_stop_early(&self) -> bool {
if self.val_metrics.len() < 4 {
return false;
}
let recent = &self.val_metrics[self.val_metrics.len() - 3..];
recent[0].rmse < recent[1].rmse && recent[1].rmse < recent[2].rmse
}
}
#[derive(Debug, Clone)]
pub struct InteractionContext {
pub device: String,
pub location_cluster: usize,
pub session_duration: f64,
pub action_sequence: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct ImplicitFeedback {
pub views: usize,
pub duration: f64,
pub ctr: f64,
pub converted: bool,
}
#[derive(Debug, Clone)]
pub struct EntanglementGenerator {
patterns: Vec<EntanglementPattern>,
circuit_params: Vec<f64>,
}
impl EntanglementGenerator {
pub fn new(num_qubits: usize) -> Self {
let patterns = vec![
EntanglementPattern {
qubit_pairs: (0..num_qubits - 1).map(|i| (i, i + 1)).collect(),
strength: 0.8,
pattern_type: PatternType::Bell,
},
EntanglementPattern {
qubit_pairs: vec![(0, num_qubits - 1)],
strength: 0.5,
pattern_type: PatternType::GHZ,
},
];
Self {
patterns,
circuit_params: vec![0.0; num_qubits * 3],
}
}
pub fn create_entangled_state(
&self,
vec1: &Array1<f64>,
vec2: &Array1<f64>,
) -> Result<Array1<f64>> {
let combined = Array1::from_iter(vec1.iter().chain(vec2.iter()).cloned());
let mut entangled = combined.clone();
for pattern in &self.patterns {
for &(q1, q2) in &pattern.qubit_pairs {
if q1 < entangled.len() && q2 < entangled.len() {
let v1 = entangled[q1];
let v2 = entangled[q2];
entangled[q1] = v1 * pattern.strength.cos() - v2 * pattern.strength.sin();
entangled[q2] = v1 * pattern.strength.sin() + v2 * pattern.strength.cos();
}
}
}
let norm = entangled.dot(&entangled).sqrt();
Ok(entangled / (norm + 1e-10))
}
}
#[derive(Debug, Clone)]
pub struct DiversityMetrics {
pub intra_list_diversity: f64,
pub inter_list_diversity: f64,
pub category_coverage: f64,
pub novelty: f64,
}
#[derive(Debug, Clone)]
pub struct TemporalPatterns {
pub hourly_distribution: Array1<f64>,
pub weekly_distribution: Array1<f64>,
pub seasonal_factors: Array1<f64>,
pub trends: Vec<TrendIndicator>,
}
#[derive(Debug, Clone)]
pub struct BusinessRules {
pub required_categories: Option<HashSet<String>>,
pub boost_new_items: Option<f64>,
pub max_price: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct QuantumNCFEngine {
config: QuantumRecommenderConfig,
pub parameters: Array1<f64>,
}
impl QuantumNCFEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
parameters: Array1::zeros(100),
})
}
}
#[derive(Debug, Clone)]
pub enum SimilarityMeasure {
Cosine,
Pearson,
QuantumFidelity,
EntanglementSimilarity,
Hybrid {
classical_weight: f64,
quantum_weight: f64,
},
}
#[derive(Debug, Clone)]
pub struct UserProfile {
pub user_id: usize,
pub features: Array1<f64>,
pub preferences: PreferenceHistory,
pub quantum_state: Array1<f64>,
pub metadata: ProfileMetadata,
}
impl UserProfile {
pub fn new(user_id: usize) -> Self {
Self {
user_id,
features: Array1::zeros(128),
preferences: PreferenceHistory::new(),
quantum_state: Array1::zeros(64),
metadata: ProfileMetadata {
created_at: 0.0,
updated_at: 0.0,
num_interactions: 0,
completeness: 0.0,
},
}
}
pub fn update_with_interaction(
&mut self,
item_id: usize,
rating: f64,
context: Option<InteractionContext>,
) {
self.preferences.rated_items.push((item_id, rating));
if let Some(ctx) = context {
self.preferences.contexts.push(ctx);
}
self.metadata.num_interactions += 1;
self.metadata.updated_at = self.metadata.num_interactions as f64;
}
}
#[derive(Debug, Clone)]
pub struct EntanglementPattern {
pub qubit_pairs: Vec<(usize, usize)>,
pub strength: f64,
pub pattern_type: PatternType,
}
#[derive(Debug, Clone)]
pub struct AccuracyMetrics {
pub mae: f64,
pub rmse: f64,
pub precision_at_k: HashMap<usize, f64>,
pub recall_at_k: HashMap<usize, f64>,
pub ndcg_at_k: HashMap<usize, f64>,
}
#[derive(Debug, Clone)]
pub struct CoverageMetrics {
pub item_coverage: f64,
pub user_coverage: f64,
pub cold_start_performance: f64,
}
#[derive(Debug, Clone)]
pub enum ProfileLearningMethod {
WeightedAverage,
QuantumSuperposition,
AdaptiveQuantum { learning_rate: f64 },
}
#[derive(Debug, Clone)]
pub enum QuantumEnhancementLevel {
Low,
Medium,
High,
Custom {
entanglement_strength: f64,
coherence_time: f64,
circuit_depth: usize,
},
}
#[derive(Debug, Clone)]
pub struct QuantumRecommender {
config: QuantumRecommenderConfig,
interaction_matrix: InteractionMatrix,
quantum_processor: QuantumProcessor,
engine: Box<dyn RecommendationEngine>,
pub user_profiles: HashMap<usize, UserProfile>,
item_features: HashMap<usize, ItemFeatures>,
parameters: ModelParameters,
metrics: RecommenderMetrics,
}
impl QuantumRecommender {
pub fn new(config: QuantumRecommenderConfig) -> Result<Self> {
let interaction_matrix = InteractionMatrix::new();
let quantum_processor = QuantumProcessor::new(config.num_qubits)?;
let engine: Box<dyn RecommendationEngine> = match &config.algorithm {
RecommendationAlgorithm::QuantumCollaborativeFiltering { .. } => {
Box::new(QuantumCFEngine::new(&config)?)
}
RecommendationAlgorithm::QuantumMatrixFactorization { .. } => {
Box::new(QuantumMFEngine::new(&config)?)
}
RecommendationAlgorithm::QuantumContentBased { .. } => {
Box::new(QuantumCBEngine::new(&config)?)
}
RecommendationAlgorithm::HybridQuantum { .. } => {
Box::new(HybridQuantumEngine::new(&config)?)
}
RecommendationAlgorithm::QuantumNeuralCF { .. } => {
Box::new(QuantumNCFEngine::new(&config)?)
}
RecommendationAlgorithm::QuantumGraphRecommender { .. } => {
Box::new(QuantumGraphEngine::new(&config)?)
}
};
let parameters = ModelParameters::new(1000, 1000, config.num_factors);
Ok(Self {
config,
interaction_matrix,
quantum_processor,
engine,
user_profiles: HashMap::new(),
item_features: HashMap::new(),
parameters,
metrics: RecommenderMetrics::new(),
})
}
pub fn add_interaction(
&mut self,
user_id: usize,
item_id: usize,
rating: f64,
context: Option<InteractionContext>,
) -> Result<()> {
self.interaction_matrix.add_rating(user_id, item_id, rating);
if let Some(profile) = self.user_profiles.get_mut(&user_id) {
profile.update_with_interaction(item_id, rating, context);
} else {
let mut profile = UserProfile::new(user_id);
profile.update_with_interaction(item_id, rating, context);
self.user_profiles.insert(user_id, profile);
}
self.engine.update(user_id, item_id, rating)?;
Ok(())
}
pub fn recommend(
&self,
user_id: usize,
n_items: usize,
options: RecommendationOptions,
) -> Result<Vec<Recommendation>> {
let mut recommendations = self
.engine
.recommend(user_id, n_items, options.exclude_seen)?;
if options.diversify {
recommendations =
self.diversify_recommendations(recommendations, options.diversity_weight)?;
}
if options.explain {
for rec in &mut recommendations {
rec.explanation = Some(self.generate_explanation(user_id, rec.item_id)?);
}
}
if let Some(rules) = options.business_rules {
recommendations = self.apply_business_rules(recommendations, rules)?;
}
Ok(recommendations)
}
pub fn train(
&mut self,
train_data: &[(usize, usize, f64)],
val_data: Option<&[(usize, usize, f64)]>,
epochs: usize,
) -> Result<TrainingHistory> {
let mut history = TrainingHistory::new();
for epoch in 0..epochs {
let mut train_loss = 0.0;
for &(user_id, item_id, rating) in train_data {
let prediction = self.predict(user_id, item_id)?;
let loss = (prediction - rating).powi(2);
train_loss += loss;
self.update_model(user_id, item_id, rating, prediction)?;
}
train_loss /= train_data.len() as f64;
let val_metrics = if let Some(val) = val_data {
self.evaluate(val)?
} else {
EvaluationMetrics::default()
};
history.add_epoch(epoch, train_loss, val_metrics);
if history.should_stop_early() {
break;
}
}
Ok(history)
}
pub fn predict(&self, user_id: usize, item_id: usize) -> Result<f64> {
let user_embedding = self.parameters.get_user_embedding(user_id)?;
let item_embedding = self.parameters.get_item_embedding(item_id)?;
let quantum_similarity = self.quantum_processor.compute_similarity(
&user_embedding,
&item_embedding,
&self.config.similarity_measure,
)?;
let prediction = self.parameters.global_bias
+ self.parameters.user_bias[user_id]
+ self.parameters.item_bias[item_id]
+ quantum_similarity;
Ok(prediction.max(1.0).min(5.0))
}
fn update_model(
&mut self,
user_id: usize,
item_id: usize,
true_rating: f64,
predicted_rating: f64,
) -> Result<()> {
let error = true_rating - predicted_rating;
let lr = self.config.learning_rate;
let reg = self.config.regularization;
self.parameters.user_bias[user_id] +=
lr * (error - reg * self.parameters.user_bias[user_id]);
self.parameters.item_bias[item_id] +=
lr * (error - reg * self.parameters.item_bias[item_id]);
let quantum_gradient = self
.quantum_processor
.compute_gradient(user_id, item_id, error)?;
self.parameters
.update_embeddings(user_id, item_id, &quantum_gradient, lr, reg)?;
Ok(())
}
fn diversify_recommendations(
&self,
mut recommendations: Vec<Recommendation>,
diversity_weight: f64,
) -> Result<Vec<Recommendation>> {
let mut diversified = Vec::new();
let mut selected_items = HashSet::new();
while !recommendations.is_empty() && diversified.len() < recommendations.len() {
let mut best_score = f64::NEG_INFINITY;
let mut best_idx = 0;
for (idx, rec) in recommendations.iter().enumerate() {
let relevance_score = rec.score;
let diversity_score = self.compute_diversity_score(rec.item_id, &selected_items)?;
let combined_score =
(1.0 - diversity_weight) * relevance_score + diversity_weight * diversity_score;
if combined_score > best_score {
best_score = combined_score;
best_idx = idx;
}
}
let selected = recommendations.remove(best_idx);
selected_items.insert(selected.item_id);
diversified.push(selected);
}
Ok(diversified)
}
fn compute_diversity_score(
&self,
item_id: usize,
selected_items: &HashSet<usize>,
) -> Result<f64> {
if selected_items.is_empty() {
return Ok(1.0);
}
let mut min_similarity: f64 = 1.0;
for &selected_id in selected_items {
let similarity =
self.engine
.compute_similarity(item_id, selected_id, SimilarityType::ItemToItem)?;
min_similarity = min_similarity.min(similarity);
}
Ok(1.0 - min_similarity)
}
fn generate_explanation(
&self,
user_id: usize,
item_id: usize,
) -> Result<RecommendationExplanation> {
let similar_users = self.find_similar_users_for_item(user_id, item_id, 5)?;
let similar_items = self.find_similar_items_to_history(user_id, item_id, 5)?;
let feature_reasons = self.extract_feature_reasons(user_id, item_id)?;
let quantum_state = Some(self.quantum_processor.get_state_info(user_id, item_id)?);
Ok(RecommendationExplanation {
similar_users,
similar_items,
feature_reasons,
quantum_state,
})
}
fn find_similar_users_for_item(
&self,
user_id: usize,
item_id: usize,
n: usize,
) -> Result<Vec<(usize, f64)>> {
let mut similar_users = Vec::new();
let item_users = self.interaction_matrix.get_item_users(item_id)?;
for &other_user in &item_users {
if other_user != user_id {
let similarity = self.engine.compute_similarity(
user_id,
other_user,
SimilarityType::UserToUser,
)?;
similar_users.push((other_user, similarity));
}
}
similar_users.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
similar_users.truncate(n);
Ok(similar_users)
}
fn find_similar_items_to_history(
&self,
user_id: usize,
target_item: usize,
n: usize,
) -> Result<Vec<(usize, f64)>> {
let mut similar_items = Vec::new();
if let Some(profile) = self.user_profiles.get(&user_id) {
for &(item_id, _) in &profile.preferences.rated_items {
if item_id != target_item {
let similarity = self.engine.compute_similarity(
target_item,
item_id,
SimilarityType::ItemToItem,
)?;
similar_items.push((item_id, similarity));
}
}
}
similar_items.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
similar_items.truncate(n);
Ok(similar_items)
}
fn extract_feature_reasons(&self, user_id: usize, item_id: usize) -> Result<Vec<String>> {
let mut reasons = Vec::new();
if let (Some(user_profile), Some(item_features)) = (
self.user_profiles.get(&user_id),
self.item_features.get(&item_id),
) {
for category in &item_features.categories {
if let Some(&pref_score) =
user_profile.preferences.preferred_categories.get(category)
{
if pref_score > 0.7 {
reasons.push(format!("Matches your interest in {}", category));
}
}
}
for (attr_name, attr_value) in &item_features.attributes {
match attr_value {
AttributeValue::Categorical(val) => {
reasons.push(format!("Features {}: {}", attr_name, val));
}
AttributeValue::Numeric(val) => {
if *val > 0.8 {
reasons.push(format!("High {} score", attr_name));
}
}
_ => {}
}
}
}
Ok(reasons)
}
fn apply_business_rules(
&self,
recommendations: Vec<Recommendation>,
rules: BusinessRules,
) -> Result<Vec<Recommendation>> {
let mut filtered = recommendations;
if let Some(categories) = rules.required_categories {
filtered = self.filter_by_categories(filtered, categories)?;
}
if let Some(boost_new) = rules.boost_new_items {
filtered = self.boost_new_items(filtered, boost_new)?;
}
if let Some(max_price) = rules.max_price {
filtered = self.filter_by_price(filtered, max_price)?;
}
Ok(filtered)
}
fn filter_by_categories(
&self,
recommendations: Vec<Recommendation>,
categories: HashSet<String>,
) -> Result<Vec<Recommendation>> {
Ok(recommendations
.into_iter()
.filter(|rec| {
if let Some(features) = self.item_features.get(&rec.item_id) {
features
.categories
.iter()
.any(|cat| categories.contains(cat))
} else {
false
}
})
.collect())
}
fn boost_new_items(
&self,
mut recommendations: Vec<Recommendation>,
boost_factor: f64,
) -> Result<Vec<Recommendation>> {
for rec in &mut recommendations {
if self.is_new_item(rec.item_id)? {
rec.score *= boost_factor;
}
}
recommendations.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(recommendations)
}
fn is_new_item(&self, item_id: usize) -> Result<bool> {
let num_ratings = self.interaction_matrix.get_item_rating_count(item_id)?;
Ok(num_ratings < 10)
}
fn filter_by_price(
&self,
recommendations: Vec<Recommendation>,
max_price: f64,
) -> Result<Vec<Recommendation>> {
Ok(recommendations
.into_iter()
.filter(|rec| {
if let Some(features) = self.item_features.get(&rec.item_id) {
if let Some(AttributeValue::Numeric(price)) = features.attributes.get("price") {
*price <= max_price
} else {
true
}
} else {
true
}
})
.collect())
}
pub fn evaluate(&self, test_data: &[(usize, usize, f64)]) -> Result<EvaluationMetrics> {
let mut predictions = Vec::new();
let mut actuals = Vec::new();
for &(user_id, item_id, rating) in test_data {
let prediction = self.predict(user_id, item_id)?;
predictions.push(prediction);
actuals.push(rating);
}
Ok(EvaluationMetrics::compute(&predictions, &actuals))
}
pub fn metrics(&self) -> &RecommenderMetrics {
&self.metrics
}
}
#[derive(Debug, Clone)]
pub struct PreferenceHistory {
pub rated_items: Vec<(usize, f64)>,
pub preferred_categories: HashMap<String, f64>,
pub temporal_patterns: TemporalPatterns,
pub contexts: Vec<InteractionContext>,
}
impl PreferenceHistory {
pub fn new() -> Self {
Self {
rated_items: Vec::new(),
preferred_categories: HashMap::new(),
temporal_patterns: TemporalPatterns {
hourly_distribution: Array1::zeros(24),
weekly_distribution: Array1::zeros(7),
seasonal_factors: Array1::zeros(4),
trends: Vec::new(),
},
contexts: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct QuantumMetrics {
pub quantum_advantage: f64,
pub entanglement_utilization: f64,
pub coherence_preservation: f64,
pub circuit_efficiency: f64,
}
#[derive(Debug, Clone)]
pub enum PatternType {
Bell,
GHZ,
Cluster,
Custom(Vec<f64>),
}
#[derive(Debug, Clone)]
pub struct QuantumProcessor {
num_qubits: usize,
similarity_circuits: Vec<Vec<f64>>,
embedding_network: QuantumNeuralNetwork,
entanglement_generator: EntanglementGenerator,
}
impl QuantumProcessor {
pub fn new(num_qubits: usize) -> Result<Self> {
let layers = vec![
QNNLayerType::EncodingLayer { num_features: 128 },
QNNLayerType::VariationalLayer { num_params: 64 },
QNNLayerType::EntanglementLayer {
connectivity: "circular".to_string(),
},
QNNLayerType::MeasurementLayer {
measurement_basis: "computational".to_string(),
},
];
let embedding_network = QuantumNeuralNetwork::new(layers, num_qubits, 128, 64)?;
let entanglement_generator = EntanglementGenerator::new(num_qubits);
Ok(Self {
num_qubits,
similarity_circuits: Vec::new(),
embedding_network,
entanglement_generator,
})
}
pub fn compute_similarity(
&self,
vec1: &Array1<f64>,
vec2: &Array1<f64>,
measure: &SimilarityMeasure,
) -> Result<f64> {
match measure {
SimilarityMeasure::Cosine => {
let dot = vec1.dot(vec2);
let norm1 = vec1.dot(vec1).sqrt();
let norm2 = vec2.dot(vec2).sqrt();
Ok(dot / (norm1 * norm2 + 1e-10))
}
SimilarityMeasure::QuantumFidelity => {
let state1 = self.encode_as_quantum_state(vec1)?;
let state2 = self.encode_as_quantum_state(vec2)?;
let fidelity = state1.dot(&state2).abs();
Ok(fidelity * fidelity)
}
SimilarityMeasure::EntanglementSimilarity => {
let entangled = self
.entanglement_generator
.create_entangled_state(vec1, vec2)?;
let entanglement = self.measure_entanglement(&entangled)?;
Ok(entanglement)
}
_ => Ok(0.5),
}
}
fn encode_as_quantum_state(&self, vec: &Array1<f64>) -> Result<Array1<f64>> {
let norm = vec.dot(vec).sqrt();
let normalized = vec / (norm + 1e-10);
let quantum_dim = 2_usize.pow(self.num_qubits as u32);
let mut quantum_state = Array1::zeros(quantum_dim);
for i in 0..normalized.len().min(quantum_dim) {
quantum_state[i] = normalized[i];
}
Ok(quantum_state)
}
fn measure_entanglement(&self, state: &Array1<f64>) -> Result<f64> {
let entropy = -state
.iter()
.filter(|&&x| x.abs() > 1e-10)
.map(|&x| {
let p = x * x;
p * p.ln()
})
.sum::<f64>();
Ok((entropy / (self.num_qubits as f64).ln()).min(1.0))
}
pub fn compute_gradient(
&self,
user_id: usize,
item_id: usize,
error: f64,
) -> Result<Array1<f64>> {
let gradient_dim = 64;
let mut gradient = Array1::zeros(gradient_dim);
for i in 0..gradient_dim {
gradient[i] = error
* (0.1 * (i as f64 * 0.1 + user_id as f64 * 0.01 + item_id as f64 * 0.001).sin());
}
Ok(gradient)
}
pub fn get_state_info(&self, user_id: usize, item_id: usize) -> Result<QuantumStateInfo> {
Ok(QuantumStateInfo {
entanglement: 0.7 + 0.3 * (user_id as f64 * 0.1).sin(),
superposition_weights: vec![0.5, 0.3, 0.2],
phase: PI * (item_id as f64 * 0.01).sin(),
})
}
}
#[derive(Debug, Clone)]
pub struct RecommendationOptions {
pub exclude_seen: bool,
pub diversify: bool,
pub diversity_weight: f64,
pub explain: bool,
pub business_rules: Option<BusinessRules>,
}
#[derive(Debug, Clone)]
pub struct ModelParameters {
pub user_embeddings: Array2<f64>,
pub item_embeddings: Array2<f64>,
pub quantum_params: Vec<f64>,
pub user_bias: Array1<f64>,
pub item_bias: Array1<f64>,
pub global_bias: f64,
}
impl ModelParameters {
pub fn new(num_users: usize, num_items: usize, num_factors: usize) -> Self {
Self {
user_embeddings: Array2::from_shape_fn((num_users, num_factors), |(_, _)| {
0.01 * (fastrand::f64() - 0.5)
}),
item_embeddings: Array2::from_shape_fn((num_items, num_factors), |(_, _)| {
0.01 * (fastrand::f64() - 0.5)
}),
quantum_params: vec![0.0; num_factors * 10],
user_bias: Array1::zeros(num_users),
item_bias: Array1::zeros(num_items),
global_bias: 3.5,
}
}
pub fn get_user_embedding(&self, user_id: usize) -> Result<Array1<f64>> {
if user_id < self.user_embeddings.nrows() {
Ok(self.user_embeddings.row(user_id).to_owned())
} else {
Ok(Array1::zeros(self.user_embeddings.ncols()))
}
}
pub fn get_item_embedding(&self, item_id: usize) -> Result<Array1<f64>> {
if item_id < self.item_embeddings.nrows() {
Ok(self.item_embeddings.row(item_id).to_owned())
} else {
Ok(Array1::zeros(self.item_embeddings.ncols()))
}
}
pub fn update_embeddings(
&mut self,
user_id: usize,
item_id: usize,
gradient: &Array1<f64>,
lr: f64,
reg: f64,
) -> Result<()> {
if user_id < self.user_embeddings.nrows() && item_id < self.item_embeddings.nrows() {
let user_emb = self.user_embeddings.row(user_id).to_owned();
let item_emb = self.item_embeddings.row(item_id).to_owned();
self.user_embeddings
.row_mut(user_id)
.zip_mut_with(&user_emb, |param, &old| {
*param = old + lr * (gradient[0] - reg * old);
});
self.item_embeddings
.row_mut(item_id)
.zip_mut_with(&item_emb, |param, &old| {
*param = old + lr * (gradient[0] - reg * old);
});
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct QuantumCFEngine {
config: QuantumRecommenderConfig,
similarity_cache: HashMap<(usize, usize), f64>,
pub parameters: Array1<f64>,
}
impl QuantumCFEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
similarity_cache: HashMap::new(),
parameters: Array1::zeros(100),
})
}
}
#[derive(Debug, Clone)]
pub enum AttributeValue {
Numeric(f64),
Categorical(String),
Binary(bool),
Vector(Vec<f64>),
}
#[derive(Debug, Clone)]
pub struct RecommenderMetrics {
pub accuracy_metrics: AccuracyMetrics,
pub diversity_metrics: DiversityMetrics,
pub coverage_metrics: CoverageMetrics,
pub quantum_metrics: QuantumMetrics,
}
impl RecommenderMetrics {
pub fn new() -> Self {
Self {
accuracy_metrics: AccuracyMetrics {
mae: 0.0,
rmse: 0.0,
precision_at_k: HashMap::new(),
recall_at_k: HashMap::new(),
ndcg_at_k: HashMap::new(),
},
diversity_metrics: DiversityMetrics {
intra_list_diversity: 0.0,
inter_list_diversity: 0.0,
category_coverage: 0.0,
novelty: 0.0,
},
coverage_metrics: CoverageMetrics {
item_coverage: 0.0,
user_coverage: 0.0,
cold_start_performance: 0.0,
},
quantum_metrics: QuantumMetrics {
quantum_advantage: 1.0,
entanglement_utilization: 0.0,
coherence_preservation: 0.0,
circuit_efficiency: 0.0,
},
}
}
}
#[derive(Debug, Clone)]
pub enum FeatureExtractionMethod {
QuantumTFIDF,
CircuitDepth,
QuantumEmbeddings { embedding_dim: usize },
QuantumAutoencoder { latent_dim: usize },
DeepQuantumFeatures { layer_dims: Vec<usize> },
}
#[derive(Debug, Clone)]
pub struct HybridQuantumEngine {
config: QuantumRecommenderConfig,
pub parameters: Array1<f64>,
}
impl HybridQuantumEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
parameters: Array1::zeros(100),
})
}
}
#[derive(Debug, Clone)]
pub struct QuantumGraphEngine {
config: QuantumRecommenderConfig,
pub parameters: Array1<f64>,
}
impl QuantumGraphEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
parameters: Array1::zeros(100),
})
}
}
#[derive(Debug, Clone)]
pub struct QuantumStateInfo {
pub entanglement: f64,
pub superposition_weights: Vec<f64>,
pub phase: f64,
}
#[derive(Debug, Clone)]
pub struct ProfileMetadata {
pub created_at: f64,
pub updated_at: f64,
pub num_interactions: usize,
pub completeness: f64,
}
#[derive(Debug, Clone)]
pub struct QuantumCBEngine {
config: QuantumRecommenderConfig,
pub parameters: Array1<f64>,
}
impl QuantumCBEngine {
fn new(config: &QuantumRecommenderConfig) -> Result<Self> {
Ok(Self {
config: config.clone(),
parameters: Array1::zeros(100),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_recommender_creation() {
let config = QuantumRecommenderConfig::default();
let recommender =
QuantumRecommender::new(config).expect("Failed to create quantum recommender");
assert!(recommender.user_profiles.is_empty());
}
#[test]
fn test_add_interaction() {
let config = QuantumRecommenderConfig::default();
let mut recommender =
QuantumRecommender::new(config).expect("Failed to create quantum recommender");
recommender
.add_interaction(1, 10, 4.5, None)
.expect("Failed to add interaction");
assert_eq!(recommender.user_profiles.len(), 1);
}
#[test]
fn test_recommendations() {
let config = QuantumRecommenderConfig::default();
let mut recommender =
QuantumRecommender::new(config).expect("Failed to create quantum recommender");
recommender
.add_interaction(1, 10, 4.5, None)
.expect("Failed to add first interaction");
recommender
.add_interaction(1, 20, 3.5, None)
.expect("Failed to add second interaction");
let options = RecommendationOptions::default();
let recommendations = recommender
.recommend(1, 5, options)
.expect("Failed to get recommendations");
assert_eq!(recommendations.len(), 5);
assert!(recommendations[0].score >= recommendations[1].score);
}
#[test]
fn test_similarity_measures() {
let processor = QuantumProcessor::new(8).expect("Failed to create quantum processor");
let vec1 = Array1::from_vec(vec![1.0, 0.0, 0.0, 0.0]);
let vec2 = Array1::from_vec(vec![0.0, 1.0, 0.0, 0.0]);
let cosine_sim = processor
.compute_similarity(&vec1, &vec2, &SimilarityMeasure::Cosine)
.expect("Failed to compute cosine similarity");
assert!(cosine_sim.abs() < 1e-10);
}
}