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