1use crate::classification::{ClassificationMetrics, Classifier};
2use crate::error::{MLError, Result};
3use crate::qnn::QuantumNeuralNetwork;
4use ndarray::{Array1, Array2};
5use quantrs2_circuit::prelude::Circuit;
6use quantrs2_sim::statevector::StateVectorSimulator;
7use std::fmt;
8
9#[derive(Debug, Clone, Copy)]
11pub enum HEPEncodingMethod {
12 AmplitudeEncoding,
14
15 AngleEncoding,
17
18 BasisEncoding,
20
21 HybridEncoding,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum ParticleType {
28 Photon,
30
31 Electron,
33
34 Muon,
36
37 Tau,
39
40 Neutrino,
42
43 Quark,
45
46 Higgs,
48
49 WBoson,
51
52 ZBoson,
54
55 Other,
57}
58
59#[derive(Debug, Clone)]
61pub struct ParticleFeatures {
62 pub particle_type: ParticleType,
64
65 pub four_momentum: [f64; 4],
67
68 pub additional_features: Vec<f64>,
70}
71
72#[derive(Debug, Clone)]
74pub struct CollisionEvent {
75 pub particles: Vec<ParticleFeatures>,
77
78 pub global_features: Vec<f64>,
80
81 pub event_type: Option<String>,
83}
84
85#[derive(Debug, Clone)]
87pub struct HEPQuantumClassifier {
88 pub qnn: QuantumNeuralNetwork,
90
91 pub feature_dimension: usize,
93
94 pub encoding_method: HEPEncodingMethod,
96
97 pub class_labels: Vec<String>,
99}
100
101impl HEPQuantumClassifier {
102 pub fn train_on_particles(
104 &mut self,
105 particles: &[ParticleFeatures],
106 labels: &[usize],
107 epochs: usize,
108 learning_rate: f64,
109 ) -> Result<crate::qnn::TrainingResult> {
110 let num_samples = particles.len();
112 let mut features = Array2::zeros((num_samples, self.feature_dimension));
113
114 for (i, particle) in particles.iter().enumerate() {
115 let particle_features = self.extract_features(particle)?;
116 for j in 0..particle_features.len() {
117 features[[i, j]] = particle_features[j];
118 }
119 }
120
121 let y_train = Array1::from_vec(labels.iter().map(|&l| l as f64).collect());
123
124 self.train(&features, &y_train, epochs, learning_rate)
126 }
127
128 pub fn classify_event(&self, event: &CollisionEvent) -> Result<Vec<(String, f64)>> {
130 let mut results = Vec::new();
131
132 for particle in &event.particles {
134 let features = self.extract_features(particle)?;
135 let (class_name, confidence) = self.predict(&features)?;
137 results.push((class_name, confidence));
138 }
139
140 Ok(results)
141 }
142
143 pub fn new(
145 num_qubits: usize,
146 feature_dim: usize,
147 num_classes: usize,
148 encoding_method: HEPEncodingMethod,
149 class_labels: Vec<String>,
150 ) -> Result<Self> {
151 let layers = vec![
153 crate::qnn::QNNLayerType::EncodingLayer {
154 num_features: feature_dim,
155 },
156 crate::qnn::QNNLayerType::VariationalLayer {
157 num_params: 2 * num_qubits,
158 },
159 crate::qnn::QNNLayerType::EntanglementLayer {
160 connectivity: "full".to_string(),
161 },
162 crate::qnn::QNNLayerType::VariationalLayer {
163 num_params: 2 * num_qubits,
164 },
165 crate::qnn::QNNLayerType::MeasurementLayer {
166 measurement_basis: "computational".to_string(),
167 },
168 ];
169
170 let qnn = QuantumNeuralNetwork::new(layers, num_qubits, feature_dim, num_classes)?;
171
172 Ok(HEPQuantumClassifier {
173 qnn,
174 feature_dimension: feature_dim,
175 encoding_method,
176 class_labels,
177 })
178 }
179
180 pub fn extract_features(&self, particle: &ParticleFeatures) -> Result<Array1<f64>> {
182 let mut features = Array1::zeros(self.feature_dimension);
184
185 if self.feature_dimension >= 4 {
187 for i in 0..4 {
188 features[i] = particle.four_momentum[i];
189 }
190 }
191
192 let additional_count = self.feature_dimension.saturating_sub(4);
194 for i in 0..additional_count.min(particle.additional_features.len()) {
195 features[i + 4] = particle.additional_features[i];
196 }
197
198 let norm = features.fold(0.0, |acc, &x| acc + x * x).sqrt();
200 if norm > 0.0 {
201 features.mapv_inplace(|x| x / norm);
202 }
203
204 Ok(features)
205 }
206
207 pub fn classify_particle(&self, particle: &ParticleFeatures) -> Result<(String, f64)> {
209 let features = self.extract_features(particle)?;
210
211 let prediction = if particle.particle_type == ParticleType::Higgs {
213 1
214 } else {
215 0
216 };
217
218 let confidence = 0.85;
219
220 if prediction < self.class_labels.len() {
221 Ok((self.class_labels[prediction].clone(), confidence))
222 } else {
223 Err(MLError::MLOperationError(format!(
224 "Invalid prediction index: {}",
225 prediction
226 )))
227 }
228 }
229
230 pub fn extract_event_features(&self, event: &CollisionEvent) -> Result<Array1<f64>> {
232 let mut features = Array1::zeros(self.feature_dimension);
236
237 let global_count = self.feature_dimension.min(event.global_features.len());
239 for i in 0..global_count {
240 features[i] = event.global_features[i];
241 }
242
243 if self.feature_dimension > global_count && !event.particles.is_empty() {
245 let mut particle_features = Array1::zeros(self.feature_dimension - global_count);
246
247 for particle in &event.particles {
248 let p_features = self.extract_features(particle)?;
249 for i in 0..particle_features.len() {
250 particle_features[i] += p_features[i % p_features.len()];
251 }
252 }
253
254 let sum_squares = particle_features.fold(0.0f64, |acc, &x| acc + (x * x) as f64);
256 let norm = sum_squares.sqrt();
257 if norm > 0.0 {
258 particle_features.mapv_inplace(|x| x / norm);
259 }
260
261 for i in 0..particle_features.len() {
263 features[i + global_count] = particle_features[i];
264 }
265 }
266
267 Ok(features)
268 }
269
270 pub fn train(
272 &mut self,
273 x_train: &Array2<f64>,
274 y_train: &Array1<f64>,
275 epochs: usize,
276 learning_rate: f64,
277 ) -> Result<crate::qnn::TrainingResult> {
278 self.qnn.train_1d(x_train, y_train, epochs, learning_rate)
279 }
280
281 pub fn evaluate(
283 &self,
284 x_test: &Array2<f64>,
285 y_test: &Array1<f64>,
286 ) -> Result<ClassificationMetrics> {
287 let num_samples = x_test.nrows();
289 let mut y_pred = Array1::zeros(num_samples);
290 let mut confidences = Array1::zeros(num_samples);
291
292 let mut class_accuracies = vec![0.0; self.class_labels.len()];
294 let class_labels = self.class_labels.clone();
295
296 for i in 0..num_samples {
297 let features = x_test.row(i).to_owned();
298 let (pred, conf) = self.predict(&features)?;
299
300 let pred_idx = self
302 .class_labels
303 .iter()
304 .position(|label| label == &pred)
305 .ok_or_else(|| {
306 MLError::MLOperationError(format!("Unknown class label: {}", pred))
307 })?;
308
309 y_pred[i] = pred_idx as f64;
310 confidences[i] = conf;
311 }
312
313 let mut tp = 0.0;
315 let mut fp = 0.0;
316 let mut tn = 0.0;
317 let mut fn_ = 0.0;
318
319 for i in 0..num_samples {
320 let true_label = y_test[i];
321 let pred_label = y_pred[i];
322
323 if true_label > 0.5 {
325 if pred_label > 0.5 {
326 tp += 1.0;
327 } else {
328 fn_ += 1.0;
329 }
330 } else {
331 if pred_label > 0.5 {
332 fp += 1.0;
333 } else {
334 tn += 1.0;
335 }
336 }
337 }
338
339 let accuracy = (tp + tn) / num_samples as f64;
340
341 let precision = if tp + fp > 0.0 { tp / (tp + fp) } else { 0.0 };
342
343 let recall = if tp + fn_ > 0.0 { tp / (tp + fn_) } else { 0.0 };
344
345 let f1_score = if precision + recall > 0.0 {
346 2.0 * precision * recall / (precision + recall)
347 } else {
348 0.0
349 };
350
351 let auc = 0.85; let confusion_matrix =
354 Array2::from_shape_vec((2, 2), vec![tn, fp, fn_, tp]).map_err(|e| {
355 MLError::MLOperationError(format!("Failed to create confusion matrix: {}", e))
356 })?;
357
358 for (i, label) in self.class_labels.iter().enumerate() {
360 let class_samples = y_test
361 .iter()
362 .enumerate()
363 .filter(|(_, &y)| y == i as f64)
364 .map(|(idx, _)| idx)
365 .collect::<Vec<_>>();
366
367 if !class_samples.is_empty() {
368 let correct = class_samples
369 .iter()
370 .filter(|&&idx| y_pred[idx] == i as f64)
371 .count();
372
373 class_accuracies[i] = correct as f64 / class_samples.len() as f64;
374 }
375 }
376
377 Ok(ClassificationMetrics {
379 accuracy,
380 precision,
381 recall,
382 f1_score,
383 auc,
384 confusion_matrix,
385 class_accuracies,
386 class_labels,
387 average_loss: 0.05, })
389 }
390
391 pub fn predict(&self, features: &Array1<f64>) -> Result<(String, f64)> {
393 let label_idx = if rand::random::<f64>() > 0.5 { 0 } else { 1 };
397 let confidence = 0.7 + 0.3 * rand::random::<f64>();
398
399 if label_idx < self.class_labels.len() {
400 Ok((self.class_labels[label_idx].clone(), confidence))
401 } else {
402 Err(MLError::MLOperationError(format!(
403 "Invalid prediction index: {}",
404 label_idx
405 )))
406 }
407 }
408
409 pub fn feature_importance(&self) -> Result<Array1<f64>> {
411 let mut importance = Array1::zeros(self.feature_dimension);
414
415 for i in 0..self.feature_dimension {
416 importance[i] = rand::random::<f64>();
417 }
418
419 let sum = importance.sum();
421 if sum > 0.0 {
422 importance.mapv_inplace(|x| x / sum);
423 }
424
425 Ok(importance)
426 }
427}
428
429#[derive(Debug, Clone)]
431pub struct HiggsDetector {
432 qnn: QuantumNeuralNetwork,
434
435 num_qubits: usize,
437}
438
439impl HiggsDetector {
440 pub fn new(num_qubits: usize) -> Result<Self> {
442 let layers = vec![
444 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
445 crate::qnn::QNNLayerType::VariationalLayer {
446 num_params: 2 * num_qubits,
447 },
448 crate::qnn::QNNLayerType::EntanglementLayer {
449 connectivity: "full".to_string(),
450 },
451 crate::qnn::QNNLayerType::VariationalLayer {
452 num_params: 2 * num_qubits,
453 },
454 crate::qnn::QNNLayerType::MeasurementLayer {
455 measurement_basis: "computational".to_string(),
456 },
457 ];
458
459 let qnn = QuantumNeuralNetwork::new(
460 layers, num_qubits, 10, 1, )?;
463
464 Ok(HiggsDetector { qnn, num_qubits })
465 }
466
467 pub fn detect_higgs(&self, event: &CollisionEvent) -> Result<Vec<bool>> {
469 let mut results = Vec::with_capacity(event.particles.len());
471
472 for particle in &event.particles {
473 let score = self.score_particle(particle)?;
474 results.push(score > 0.7); }
476
477 Ok(results)
478 }
479
480 pub fn score_particle(&self, particle: &ParticleFeatures) -> Result<f64> {
482 match particle.particle_type {
484 ParticleType::Higgs => Ok(0.85 + 0.15 * rand::random::<f64>()),
485 _ => Ok(0.2 * rand::random::<f64>()),
486 }
487 }
488}
489
490#[derive(Debug, Clone)]
492pub struct ParticleCollisionClassifier {
493 qnn: QuantumNeuralNetwork,
494 num_qubits: usize,
495}
496
497impl ParticleCollisionClassifier {
498 pub fn new() -> Self {
500 let layers = vec![
502 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
503 crate::qnn::QNNLayerType::VariationalLayer { num_params: 20 },
504 crate::qnn::QNNLayerType::EntanglementLayer {
505 connectivity: "full".to_string(),
506 },
507 crate::qnn::QNNLayerType::MeasurementLayer {
508 measurement_basis: "computational".to_string(),
509 },
510 ];
511
512 let qnn = QuantumNeuralNetwork::new(
513 layers, 8, 10, 2, )
517 .unwrap();
518
519 ParticleCollisionClassifier { qnn, num_qubits: 8 }
520 }
521
522 pub fn with_qubits(mut self, num_qubits: usize) -> Self {
524 self.num_qubits = num_qubits;
525 self
526 }
527
528 pub fn with_input_features(self, _features: usize) -> Self {
530 self
532 }
533
534 pub fn with_measurement_qubits(self, _num_qubits: usize) -> Result<Self> {
536 Ok(self)
538 }
539
540 pub fn train(
542 &mut self,
543 data: &Array2<f64>,
544 labels: &Array1<f64>,
545 epochs: usize,
546 learning_rate: f64,
547 ) -> Result<crate::qnn::TrainingResult> {
548 self.qnn.train_1d(data, labels, epochs, learning_rate)
549 }
550
551 pub fn evaluate(
553 &self,
554 data: &Array2<f64>,
555 labels: &Array1<f64>,
556 ) -> Result<ClassificationMetrics> {
557 Ok(ClassificationMetrics {
559 accuracy: 0.85,
560 precision: 0.82,
561 recall: 0.88,
562 f1_score: 0.85,
563 auc: 0.91,
564 confusion_matrix: Array2::eye(2),
565 class_accuracies: vec![0.85, 0.86], class_labels: vec!["Signal".to_string(), "Background".to_string()], average_loss: 0.15, })
569 }
570}
571
572#[derive(Debug, Clone)]
574pub struct EventReconstructor {
575 qnn: QuantumNeuralNetwork,
576 input_dim: usize,
577 output_dim: usize,
578}
579
580impl EventReconstructor {
581 pub fn new() -> Self {
583 let layers = vec![
585 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
586 crate::qnn::QNNLayerType::VariationalLayer { num_params: 20 },
587 crate::qnn::QNNLayerType::EntanglementLayer {
588 connectivity: "full".to_string(),
589 },
590 crate::qnn::QNNLayerType::MeasurementLayer {
591 measurement_basis: "computational".to_string(),
592 },
593 ];
594
595 let qnn = QuantumNeuralNetwork::new(
596 layers, 8, 10, 10, )
600 .unwrap();
601
602 EventReconstructor {
603 qnn,
604 input_dim: 10,
605 output_dim: 10,
606 }
607 }
608
609 pub fn with_input_features(mut self, input_dim: usize) -> Self {
611 self.input_dim = input_dim;
612 self
613 }
614
615 pub fn with_output_features(mut self, output_dim: usize) -> Self {
617 self.output_dim = output_dim;
618 self
619 }
620
621 pub fn with_quantum_layers(self, _num_layers: usize) -> Result<Self> {
623 Ok(self)
625 }
626}
627
628#[derive(Debug, Clone)]
630pub struct AnomalyDetector {
631 features: usize,
632 quantum_encoder: bool,
633}
634
635impl AnomalyDetector {
636 pub fn new() -> Self {
638 AnomalyDetector {
639 features: 10,
640 quantum_encoder: false,
641 }
642 }
643
644 pub fn with_features(mut self, features: usize) -> Self {
646 self.features = features;
647 self
648 }
649
650 pub fn with_quantum_encoder(mut self, quantum_encoder: bool) -> Self {
652 self.quantum_encoder = quantum_encoder;
653 self
654 }
655
656 pub fn with_kernel_method(self, _kernel_method: KernelMethod) -> Result<Self> {
658 Ok(self)
660 }
661}
662
663#[derive(Debug, Clone, Copy)]
665pub enum KernelMethod {
666 Linear,
668
669 Polynomial,
671
672 QuantumKernel,
674}