1use crate::classification::{ClassificationMetrics, Classifier};
2use crate::error::{MLError, Result};
3use crate::qnn::QuantumNeuralNetwork;
4use quantrs2_circuit::prelude::Circuit;
5use quantrs2_sim::statevector::StateVectorSimulator;
6use scirs2_core::ndarray::{Array1, Array2};
7use scirs2_core::random::prelude::*;
8use std::fmt;
9
10#[derive(Debug, Clone, Copy)]
12pub enum HEPEncodingMethod {
13 AmplitudeEncoding,
15
16 AngleEncoding,
18
19 BasisEncoding,
21
22 HybridEncoding,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum ParticleType {
29 Photon,
31
32 Electron,
34
35 Muon,
37
38 Tau,
40
41 Neutrino,
43
44 Quark,
46
47 Higgs,
49
50 WBoson,
52
53 ZBoson,
55
56 Other,
58}
59
60#[derive(Debug, Clone)]
62pub struct ParticleFeatures {
63 pub particle_type: ParticleType,
65
66 pub four_momentum: [f64; 4],
68
69 pub additional_features: Vec<f64>,
71}
72
73#[derive(Debug, Clone)]
75pub struct CollisionEvent {
76 pub particles: Vec<ParticleFeatures>,
78
79 pub global_features: Vec<f64>,
81
82 pub event_type: Option<String>,
84}
85
86#[derive(Debug, Clone)]
88pub struct HEPQuantumClassifier {
89 pub qnn: QuantumNeuralNetwork,
91
92 pub feature_dimension: usize,
94
95 pub encoding_method: HEPEncodingMethod,
97
98 pub class_labels: Vec<String>,
100}
101
102impl HEPQuantumClassifier {
103 pub fn train_on_particles(
105 &mut self,
106 particles: &[ParticleFeatures],
107 labels: &[usize],
108 epochs: usize,
109 learning_rate: f64,
110 ) -> Result<crate::qnn::TrainingResult> {
111 let num_samples = particles.len();
113 let mut features = Array2::zeros((num_samples, self.feature_dimension));
114
115 for (i, particle) in particles.iter().enumerate() {
116 let particle_features = self.extract_features(particle)?;
117 for j in 0..particle_features.len() {
118 features[[i, j]] = particle_features[j];
119 }
120 }
121
122 let y_train = Array1::from_vec(labels.iter().map(|&l| l as f64).collect());
124
125 self.train(&features, &y_train, epochs, learning_rate)
127 }
128
129 pub fn classify_event(&self, event: &CollisionEvent) -> Result<Vec<(String, f64)>> {
131 let mut results = Vec::new();
132
133 for particle in &event.particles {
135 let features = self.extract_features(particle)?;
136 let (class_name, confidence) = self.predict(&features)?;
138 results.push((class_name, confidence));
139 }
140
141 Ok(results)
142 }
143
144 pub fn new(
146 num_qubits: usize,
147 feature_dim: usize,
148 num_classes: usize,
149 encoding_method: HEPEncodingMethod,
150 class_labels: Vec<String>,
151 ) -> Result<Self> {
152 let layers = vec![
154 crate::qnn::QNNLayerType::EncodingLayer {
155 num_features: feature_dim,
156 },
157 crate::qnn::QNNLayerType::VariationalLayer {
158 num_params: 2 * num_qubits,
159 },
160 crate::qnn::QNNLayerType::EntanglementLayer {
161 connectivity: "full".to_string(),
162 },
163 crate::qnn::QNNLayerType::VariationalLayer {
164 num_params: 2 * num_qubits,
165 },
166 crate::qnn::QNNLayerType::MeasurementLayer {
167 measurement_basis: "computational".to_string(),
168 },
169 ];
170
171 let qnn = QuantumNeuralNetwork::new(layers, num_qubits, feature_dim, num_classes)?;
172
173 Ok(HEPQuantumClassifier {
174 qnn,
175 feature_dimension: feature_dim,
176 encoding_method,
177 class_labels,
178 })
179 }
180
181 pub fn extract_features(&self, particle: &ParticleFeatures) -> Result<Array1<f64>> {
183 let mut features = Array1::zeros(self.feature_dimension);
185
186 if self.feature_dimension >= 4 {
188 for i in 0..4 {
189 features[i] = particle.four_momentum[i];
190 }
191 }
192
193 let additional_count = self.feature_dimension.saturating_sub(4);
195 for i in 0..additional_count.min(particle.additional_features.len()) {
196 features[i + 4] = particle.additional_features[i];
197 }
198
199 let norm = features.fold(0.0, |acc, &x| acc + x * x).sqrt();
201 if norm > 0.0 {
202 features.mapv_inplace(|x| x / norm);
203 }
204
205 Ok(features)
206 }
207
208 pub fn classify_particle(&self, particle: &ParticleFeatures) -> Result<(String, f64)> {
210 let features = self.extract_features(particle)?;
211
212 let prediction = if particle.particle_type == ParticleType::Higgs {
214 1
215 } else {
216 0
217 };
218
219 let confidence = 0.85;
220
221 if prediction < self.class_labels.len() {
222 Ok((self.class_labels[prediction].clone(), confidence))
223 } else {
224 Err(MLError::MLOperationError(format!(
225 "Invalid prediction index: {}",
226 prediction
227 )))
228 }
229 }
230
231 pub fn extract_event_features(&self, event: &CollisionEvent) -> Result<Array1<f64>> {
233 let mut features = Array1::zeros(self.feature_dimension);
237
238 let global_count = self.feature_dimension.min(event.global_features.len());
240 for i in 0..global_count {
241 features[i] = event.global_features[i];
242 }
243
244 if self.feature_dimension > global_count && !event.particles.is_empty() {
246 let mut particle_features = Array1::zeros(self.feature_dimension - global_count);
247
248 for particle in &event.particles {
249 let p_features = self.extract_features(particle)?;
250 for i in 0..particle_features.len() {
251 particle_features[i] += p_features[i % p_features.len()];
252 }
253 }
254
255 let sum_squares = particle_features.fold(0.0f64, |acc, &x| acc + (x * x) as f64);
257 let norm = sum_squares.sqrt();
258 if norm > 0.0 {
259 particle_features.mapv_inplace(|x| x / norm);
260 }
261
262 for i in 0..particle_features.len() {
264 features[i + global_count] = particle_features[i];
265 }
266 }
267
268 Ok(features)
269 }
270
271 pub fn train(
273 &mut self,
274 x_train: &Array2<f64>,
275 y_train: &Array1<f64>,
276 epochs: usize,
277 learning_rate: f64,
278 ) -> Result<crate::qnn::TrainingResult> {
279 self.qnn.train_1d(x_train, y_train, epochs, learning_rate)
280 }
281
282 pub fn evaluate(
284 &self,
285 x_test: &Array2<f64>,
286 y_test: &Array1<f64>,
287 ) -> Result<ClassificationMetrics> {
288 let num_samples = x_test.nrows();
290 let mut y_pred = Array1::zeros(num_samples);
291 let mut confidences = Array1::zeros(num_samples);
292
293 let mut class_accuracies = vec![0.0; self.class_labels.len()];
295 let class_labels = self.class_labels.clone();
296
297 for i in 0..num_samples {
298 let features = x_test.row(i).to_owned();
299 let (pred, conf) = self.predict(&features)?;
300
301 let pred_idx = self
303 .class_labels
304 .iter()
305 .position(|label| label == &pred)
306 .ok_or_else(|| {
307 MLError::MLOperationError(format!("Unknown class label: {}", pred))
308 })?;
309
310 y_pred[i] = pred_idx as f64;
311 confidences[i] = conf;
312 }
313
314 let mut tp = 0.0;
316 let mut fp = 0.0;
317 let mut tn = 0.0;
318 let mut fn_ = 0.0;
319
320 for i in 0..num_samples {
321 let true_label = y_test[i];
322 let pred_label = y_pred[i];
323
324 if true_label > 0.5 {
326 if pred_label > 0.5 {
327 tp += 1.0;
328 } else {
329 fn_ += 1.0;
330 }
331 } else {
332 if pred_label > 0.5 {
333 fp += 1.0;
334 } else {
335 tn += 1.0;
336 }
337 }
338 }
339
340 let accuracy = (tp + tn) / num_samples as f64;
341
342 let precision = if tp + fp > 0.0 { tp / (tp + fp) } else { 0.0 };
343
344 let recall = if tp + fn_ > 0.0 { tp / (tp + fn_) } else { 0.0 };
345
346 let f1_score = if precision + recall > 0.0 {
347 2.0 * precision * recall / (precision + recall)
348 } else {
349 0.0
350 };
351
352 let auc = 0.85; let confusion_matrix =
355 Array2::from_shape_vec((2, 2), vec![tn, fp, fn_, tp]).map_err(|e| {
356 MLError::MLOperationError(format!("Failed to create confusion matrix: {}", e))
357 })?;
358
359 for (i, label) in self.class_labels.iter().enumerate() {
361 let class_samples = y_test
362 .iter()
363 .enumerate()
364 .filter(|(_, &y)| y == i as f64)
365 .map(|(idx, _)| idx)
366 .collect::<Vec<_>>();
367
368 if !class_samples.is_empty() {
369 let correct = class_samples
370 .iter()
371 .filter(|&&idx| y_pred[idx] == i as f64)
372 .count();
373
374 class_accuracies[i] = correct as f64 / class_samples.len() as f64;
375 }
376 }
377
378 Ok(ClassificationMetrics {
380 accuracy,
381 precision,
382 recall,
383 f1_score,
384 auc,
385 confusion_matrix,
386 class_accuracies,
387 class_labels,
388 average_loss: 0.05, })
390 }
391
392 pub fn predict(&self, features: &Array1<f64>) -> Result<(String, f64)> {
394 let label_idx = if thread_rng().gen::<f64>() > 0.5 {
398 0
399 } else {
400 1
401 };
402 let confidence = 0.7 + 0.3 * thread_rng().gen::<f64>();
403
404 if label_idx < self.class_labels.len() {
405 Ok((self.class_labels[label_idx].clone(), confidence))
406 } else {
407 Err(MLError::MLOperationError(format!(
408 "Invalid prediction index: {}",
409 label_idx
410 )))
411 }
412 }
413
414 pub fn feature_importance(&self) -> Result<Array1<f64>> {
416 let mut importance = Array1::zeros(self.feature_dimension);
419
420 for i in 0..self.feature_dimension {
421 importance[i] = thread_rng().gen::<f64>();
422 }
423
424 let sum = importance.sum();
426 if sum > 0.0 {
427 importance.mapv_inplace(|x| x / sum);
428 }
429
430 Ok(importance)
431 }
432}
433
434#[derive(Debug, Clone)]
436pub struct HiggsDetector {
437 qnn: QuantumNeuralNetwork,
439
440 num_qubits: usize,
442}
443
444impl HiggsDetector {
445 pub fn new(num_qubits: usize) -> Result<Self> {
447 let layers = vec![
449 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
450 crate::qnn::QNNLayerType::VariationalLayer {
451 num_params: 2 * num_qubits,
452 },
453 crate::qnn::QNNLayerType::EntanglementLayer {
454 connectivity: "full".to_string(),
455 },
456 crate::qnn::QNNLayerType::VariationalLayer {
457 num_params: 2 * num_qubits,
458 },
459 crate::qnn::QNNLayerType::MeasurementLayer {
460 measurement_basis: "computational".to_string(),
461 },
462 ];
463
464 let qnn = QuantumNeuralNetwork::new(
465 layers, num_qubits, 10, 1, )?;
468
469 Ok(HiggsDetector { qnn, num_qubits })
470 }
471
472 pub fn detect_higgs(&self, event: &CollisionEvent) -> Result<Vec<bool>> {
474 let mut results = Vec::with_capacity(event.particles.len());
476
477 for particle in &event.particles {
478 let score = self.score_particle(particle)?;
479 results.push(score > 0.7); }
481
482 Ok(results)
483 }
484
485 pub fn score_particle(&self, particle: &ParticleFeatures) -> Result<f64> {
487 match particle.particle_type {
489 ParticleType::Higgs => Ok(0.85 + 0.15 * thread_rng().gen::<f64>()),
490 _ => Ok(0.2 * thread_rng().gen::<f64>()),
491 }
492 }
493}
494
495#[derive(Debug, Clone)]
497pub struct ParticleCollisionClassifier {
498 qnn: QuantumNeuralNetwork,
499 num_qubits: usize,
500}
501
502impl ParticleCollisionClassifier {
503 pub fn new() -> Self {
505 let layers = vec![
507 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
508 crate::qnn::QNNLayerType::VariationalLayer { num_params: 20 },
509 crate::qnn::QNNLayerType::EntanglementLayer {
510 connectivity: "full".to_string(),
511 },
512 crate::qnn::QNNLayerType::MeasurementLayer {
513 measurement_basis: "computational".to_string(),
514 },
515 ];
516
517 let qnn = QuantumNeuralNetwork::new(
518 layers, 8, 10, 2, )
522 .expect("should create ParticleCollisionClassifier QNN");
523
524 ParticleCollisionClassifier { qnn, num_qubits: 8 }
525 }
526
527 pub fn with_qubits(mut self, num_qubits: usize) -> Self {
529 self.num_qubits = num_qubits;
530 self
531 }
532
533 pub fn with_input_features(self, _features: usize) -> Self {
535 self
537 }
538
539 pub fn with_measurement_qubits(self, _num_qubits: usize) -> Result<Self> {
541 Ok(self)
543 }
544
545 pub fn train(
547 &mut self,
548 data: &Array2<f64>,
549 labels: &Array1<f64>,
550 epochs: usize,
551 learning_rate: f64,
552 ) -> Result<crate::qnn::TrainingResult> {
553 self.qnn.train_1d(data, labels, epochs, learning_rate)
554 }
555
556 pub fn evaluate(
558 &self,
559 data: &Array2<f64>,
560 labels: &Array1<f64>,
561 ) -> Result<ClassificationMetrics> {
562 Ok(ClassificationMetrics {
564 accuracy: 0.85,
565 precision: 0.82,
566 recall: 0.88,
567 f1_score: 0.85,
568 auc: 0.91,
569 confusion_matrix: Array2::eye(2),
570 class_accuracies: vec![0.85, 0.86], class_labels: vec!["Signal".to_string(), "Background".to_string()], average_loss: 0.15, })
574 }
575}
576
577#[derive(Debug, Clone)]
579pub struct EventReconstructor {
580 qnn: QuantumNeuralNetwork,
581 input_dim: usize,
582 output_dim: usize,
583}
584
585impl EventReconstructor {
586 pub fn new() -> Self {
588 let layers = vec![
590 crate::qnn::QNNLayerType::EncodingLayer { num_features: 10 },
591 crate::qnn::QNNLayerType::VariationalLayer { num_params: 20 },
592 crate::qnn::QNNLayerType::EntanglementLayer {
593 connectivity: "full".to_string(),
594 },
595 crate::qnn::QNNLayerType::MeasurementLayer {
596 measurement_basis: "computational".to_string(),
597 },
598 ];
599
600 let qnn = QuantumNeuralNetwork::new(
601 layers, 8, 10, 10, )
605 .expect("should create EventReconstructor QNN");
606
607 EventReconstructor {
608 qnn,
609 input_dim: 10,
610 output_dim: 10,
611 }
612 }
613
614 pub fn with_input_features(mut self, input_dim: usize) -> Self {
616 self.input_dim = input_dim;
617 self
618 }
619
620 pub fn with_output_features(mut self, output_dim: usize) -> Self {
622 self.output_dim = output_dim;
623 self
624 }
625
626 pub fn with_quantum_layers(self, _num_layers: usize) -> Result<Self> {
628 Ok(self)
630 }
631}
632
633#[derive(Debug, Clone)]
635pub struct AnomalyDetector {
636 features: usize,
637 quantum_encoder: bool,
638}
639
640impl AnomalyDetector {
641 pub fn new() -> Self {
643 AnomalyDetector {
644 features: 10,
645 quantum_encoder: false,
646 }
647 }
648
649 pub fn with_features(mut self, features: usize) -> Self {
651 self.features = features;
652 self
653 }
654
655 pub fn with_quantum_encoder(mut self, quantum_encoder: bool) -> Self {
657 self.quantum_encoder = quantum_encoder;
658 self
659 }
660
661 pub fn with_kernel_method(self, _kernel_method: KernelMethod) -> Result<Self> {
663 Ok(self)
665 }
666}
667
668#[derive(Debug, Clone, Copy)]
670pub enum KernelMethod {
671 Linear,
673
674 Polynomial,
676
677 QuantumKernel,
679}